/* boost random/hyperexponential_distribution.hpp header file
 *
 * Copyright Marco Guazzone 2014
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * Much of the code here taken by boost::math::hyperexponential_distribution.
 * To this end, we would like to thank Paul Bristow and John Maddock for their
 * valuable feedback.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 */

#ifndef BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP
#define BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP

#include <boost/config.hpp>
#include <boost/core/cmath.hpp>
#include <boost/random/detail/operators.hpp>
#include <boost/random/detail/vector_io.hpp>
#include <boost/random/discrete_distribution.hpp>
#include <boost/random/exponential_distribution.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>
#include <boost/type_traits/has_pre_increment.hpp>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <iterator>
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
# include <initializer_list>
#endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST
#include <iostream>
#include <limits>
#include <numeric>
#include <vector>

namespace boost { namespace random {

namespace hyperexp_detail {

template <typename T>
std::vector<T>& normalize(std::vector<T>& v)
{
    if (v.size() == 0)
    {
        return v;
    }

    const T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
    T final_sum = 0;

    const typename std::vector<T>::iterator end = --v.end();
    for (typename std::vector<T>::iterator it = v.begin();
         it != end;
         ++it)
    {
        *it /= sum;
        final_sum += *it;
    }
    *end = 1-final_sum; // avoids round off errors thus ensuring the probabilities really sum to 1

    return v;
}

template <typename RealT>
bool check_probabilities(std::vector<RealT> const& probabilities)
{
    const std::size_t n = probabilities.size();
    RealT sum = 0;
    for (std::size_t i = 0; i < n; ++i)
    {
        if (probabilities[i] < 0
            || probabilities[i] > 1
            || !(boost::core::isfinite)(probabilities[i]))
        {
            return false;
        }
        sum += probabilities[i];
    }

    //NOTE: the check below seems to fail on some architectures.
    //      So we commented it.
    //// - We try to keep phase probabilities correctly normalized in the distribution constructors
    //// - However in practice we have to allow for a very slight divergence from a sum of exactly 1:
    ////if (std::abs(sum-1) > (std::numeric_limits<RealT>::epsilon()*2))
    //// This is from Knuth "The Art of Computer Programming: Vol.2, 3rd Ed", and can be used to
    //// check is two numbers are approximately equal
    //const RealT one = 1;
    //const RealT tol = std::numeric_limits<RealT>::epsilon()*2.0;
    //if (std::abs(sum-one) > (std::max(std::abs(sum), std::abs(one))*tol))
    //{
    //    return false;
    //}

    return true;
}

template <typename RealT>
bool check_rates(std::vector<RealT> const& rates)
{
    const std::size_t n = rates.size();
    for (std::size_t i = 0; i < n; ++i)
    {
        if (rates[i] <= 0
            || !(boost::core::isfinite)(rates[i]))
        {
            return false;
        }
    }
    return true;
}

template <typename RealT>
bool check_params(std::vector<RealT> const& probabilities, std::vector<RealT> const& rates)
{
    if (probabilities.size() != rates.size())
    {
        return false;
    }

    return check_probabilities(probabilities)
           && check_rates(rates);
}

} // Namespace hyperexp_detail

/**
 * The hyperexponential distribution is a real-valued continuous distribution
 * with two parameters, the phase probability vector \c probs and the
 * rate vector \c rates.
 *
 * A \f$k\f$-phase hyperexponential distribution is a mixture of \f$k\f$
 * exponential distributions.
 * For this reason, it is also referred to as mixed exponential
 * distribution or parallel \f$k\f$-phase exponential
 * distribution.
 *
 * A \f$k\f$-phase hyperexponential distribution is characterized by two
 * parameters, namely a phase probability vector \f$\mathbf{\alpha}=(\alpha_1,\ldots,\alpha_k)\f$ and a rate vector \f$\mathbf{\lambda}=(\lambda_1,\ldots,\lambda_k)\f$.
 *
 * A \f$k\f$-phase hyperexponential distribution is frequently used in
 * queueing theory to model the distribution of the superposition of
 * \f$k\f$ independent events, like, for instance, the  service time distribution
 * of a queueing station with \f$k\f$ servers in parallel where the \f$i\f$-th
 * server is chosen with probability \f$\alpha_i\f$ and its service time
 * distribution is an exponential distribution with rate \f$\lambda_i\f$
 * (Allen,1990; Papadopolous et al.,1993; Trivedi,2002).
 *
 * For instance, CPUs service-time distribution in a computing system has often
 * been observed to possess such a distribution (Rosin,1965).
 * Also, the arrival of different types of customer to a single queueing station
 * is often modeled as a hyperexponential distribution (Papadopolous et al.,1993).
 * Similarly, if a product manufactured in several parallel assemply lines and
 * the outputs are merged, the failure density of the overall product is likely
 * to be hyperexponential (Trivedi,2002).
 *
 * Finally, since the hyperexponential distribution exhibits a high Coefficient
 * of Variation (CoV), that is a CoV > 1, it is especially suited to fit
 * empirical data with large CoV (Feitelson,2014; Wolski et al.,2013) and to
 * approximate long-tail probability distributions (Feldmann et al.,1998).
 *
 * See (Boost,2014) for more information and examples.
 *
 * A \f$k\f$-phase hyperexponential distribution has a probability density
 * function
 * \f[
 *  f(x) = \sum_{i=1}^k \alpha_i \lambda_i e^{-x\lambda_i}
 * \f]
 * where:
 * - \f$k\f$ is the number of phases and also the size of the input
 *   vector parameters,
 * - \f$\mathbf{\alpha}=(\alpha_1,\ldots,\alpha_k)\f$ is the phase probability
 *   vector parameter, and
 * - \f$\mathbf{\lambda}=(\lambda_1,\ldots,\lambda_k)\f$ is the rate vector
 *   parameter.
 * .
 *
 * Given a \f$k\f$-phase hyperexponential distribution with phase probability
 * vector \f$\mathbf{\alpha}\f$ and rate vector \f$\mathbf{\lambda}\f$, the
 * random variate generation algorithm consists of the following steps (Tyszer,1999):
 * -# Generate a random variable \f$U\f$ uniformly distribution on the interval \f$(0,1)\f$.
 * -# Use \f$U\f$ to select the appropriate \f$\lambda_i\f$ (e.g., the
 *  alias method can possibly be used for this step).
 * -# Generate an exponentially distributed random variable \f$X\f$ with rate parameter \f$\lambda_i\f$.
 * -# Return \f$X\f$.
 * .
 *
 * References:
 * -# A.O. Allen, Probability, Statistics, and Queuing Theory with Computer Science Applications, Second Edition, Academic Press, 1990.
 * -# Boost C++ Libraries, Boost.Math / Statistical Distributions: Hyperexponential Distribution, Online: http://www.boost.org/doc/libs/release/libs/math/doc/html/dist.html , 2014.
 * -# D.G. Feitelson, Workload Modeling for Computer Systems Performance Evaluation, Cambridge University Press, 2014
 * -# A. Feldmann and W. Whitt, Fitting mixtures of exponentials to long-tail distributions to analyze network performance models, Performance Evaluation 31(3-4):245, doi:10.1016/S0166-5316(97)00003-5, 1998.
 * -# H.T. Papadopolous, C. Heavey and J. Browne, Queueing Theory in Manufacturing Systems Analysis and Design, Chapman & Hall/CRC, 1993, p. 35.
 * -# R.F. Rosin, Determining a computing center environment, Communications of the ACM 8(7):463-468, 1965.
 * -# K.S. Trivedi, Probability and Statistics with Reliability, Queueing, and Computer Science Applications, John Wiley & Sons, Inc., 2002.
 * -# J. Tyszer, Object-Oriented Computer Simulation of Discrete-Event Systems, Springer, 1999.
 * -# Wikipedia, Hyperexponential Distribution, Online: http://en.wikipedia.org/wiki/Hyperexponential_distribution , 2014.
 * -# Wolfram Mathematica, Hyperexponential Distribution, Online: http://reference.wolfram.com/language/ref/HyperexponentialDistribution.html , 2014.
 * .
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 */
template<class RealT = double>
class hyperexponential_distribution
{
    public: typedef RealT result_type;
    public: typedef RealT input_type;

    /**
     * The parameters of a hyperexponential distribution.
     *
     * Stores the phase probability vector and the rate vector
     * of the hyperexponential distribution.
     *
     * \author Marco Guazzone (marco.guazzone@gmail.com)
     */
    public: class param_type
    {
        public: typedef hyperexponential_distribution distribution_type;

        /**
         * Constructs a \c param_type with the default parameters
         * of the distribution.
         */
        public: param_type()
        : probs_(1, 1),
          rates_(1, 1)
        {
        }

        /**
         * Constructs a \c param_type from the phase probability vector
         * and rate vector parameters of the distribution.
         *
         * The phase probability vector parameter is given by the range
         * defined by [\a prob_first, \a prob_last) iterator pair, and the
         * rate vector parameter is given by the range defined by
         * [\a rate_first, \a rate_last) iterator pair.
         *
         * \tparam ProbIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
         * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
         *
         * \param prob_first The iterator to the beginning of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
         * \param prob_last The iterator to the ending of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
         * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
         * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
         *
         * References:
         * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
         * .
         */
        public: template <typename ProbIterT, typename RateIterT>
                param_type(ProbIterT prob_first, ProbIterT prob_last,
                           RateIterT rate_first, RateIterT rate_last)
        : probs_(prob_first, prob_last),
          rates_(rate_first, rate_last)
        {
            hyperexp_detail::normalize(probs_);

            assert( hyperexp_detail::check_params(probs_, rates_) );
        }

        /**
         * Constructs a \c param_type from the phase probability vector
         * and rate vector parameters of the distribution.
         *
         * The phase probability vector parameter is given by the range
         * defined by \a prob_range, and the rate vector parameter is
         * given by the range defined by \a rate_range.
         *
         * \tparam ProbRangeT Must meet the requirements of Range concept.
         * \tparam RateRangeT Must meet the requirements of Range concept.
         *
         * \param prob_range The range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
         * \param rate_range The range of positive real elements representing the rates.
         *
         * \note
         *  The final \c disable_if parameter is an implementation detail that
         *  differentiates between this two argument constructor and the
         *  iterator-based two argument constructor described below.
         */
        //  We SFINAE this out of existance if either argument type is
        //  incrementable as in that case the type is probably an iterator:
        public: template <typename ProbRangeT, typename RateRangeT>
                param_type(ProbRangeT const& prob_range,
                           RateRangeT const& rate_range,
                           typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
        : probs_(boost::begin(prob_range), boost::end(prob_range)),
          rates_(boost::begin(rate_range), boost::end(rate_range))
        {
            hyperexp_detail::normalize(probs_);

            assert( hyperexp_detail::check_params(probs_, rates_) );
        }

        /**
         * Constructs a \c param_type from the rate vector parameter of
         * the distribution and with equal phase probabilities.
         *
         * The rate vector parameter is given by the range defined by
         * [\a rate_first, \a rate_last) iterator pair, and the phase
         * probability vector parameter is set to the equal phase
         * probabilities (i.e., to a vector of the same length \f$k\f$ of the
         * rate vector and with each element set to \f$1.0/k\f$).
         *
         * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
         * \tparam RateIterT2 Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
         *
         * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
         * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
         *
         * \note
         *  The final \c disable_if parameter is an implementation detail that
         *  differentiates between this two argument constructor and the
         *  range-based two argument constructor described above.
         *
         * References:
         * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
         * .
         */
        //  We SFINAE this out of existance if the argument type is
        //  incrementable as in that case the type is probably an iterator.
        public: template <typename RateIterT>
                param_type(RateIterT rate_first,
                           RateIterT rate_last,
                           typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value>::type* = 0)
        : probs_(std::distance(rate_first, rate_last), 1), // will be normalized below
          rates_(rate_first, rate_last)
        {
            assert(probs_.size() == rates_.size());
        }

        /**
         * Constructs a @c param_type from the "rates" parameters
         * of the distribution and with equal phase probabilities.
         *
         * The rate vector parameter is given by the range defined by
         * \a rate_range, and the phase probability vector parameter is
         * set to the equal phase probabilities (i.e., to a vector of the same
         * length \f$k\f$ of the rate vector and with each element set
         * to \f$1.0/k\f$).
         *
         * \tparam RateRangeT Must meet the requirements of Range concept.
         *
         * \param rate_range The range of positive real elements representing the rates.
         */
        public: template <typename RateRangeT>
                param_type(RateRangeT const& rate_range)
        : probs_(boost::size(rate_range), 1), // Will be normalized below
          rates_(boost::begin(rate_range), boost::end(rate_range))
        {
            hyperexp_detail::normalize(probs_);

            assert( hyperexp_detail::check_params(probs_, rates_) );
        }

#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
        /**
         * Constructs a \c param_type from the phase probability vector
         * and rate vector parameters of the distribution.
         *
         * The phase probability vector parameter is given by the
         * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
         * defined by \a l1, and the rate vector parameter is given by the
         * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
         * defined by \a l2.
         *
         * \param l1 The initializer list for inizializing the phase probability vector.
         * \param l2 The initializer list for inizializing the rate vector.
         *
         * References:
         * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
         * .
         */
        public: param_type(std::initializer_list<RealT> l1, std::initializer_list<RealT> l2)
        : probs_(l1.begin(), l1.end()),
          rates_(l2.begin(), l2.end())
        {
            hyperexp_detail::normalize(probs_);

            assert( hyperexp_detail::check_params(probs_, rates_) );
        }

        /**
         * Constructs a \c param_type from the rate vector parameter
         * of the distribution and with equal phase probabilities.
         *
         * The rate vector parameter is given by the
         * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
         * defined by \a l1, and the phase probability vector parameter is
         * set to the equal phase probabilities (i.e., to a vector of the same
         * length \f$k\f$ of the rate vector and with each element set
         * to \f$1.0/k\f$).
         *
         * \param l1 The initializer list for inizializing the rate vector.
         *
         * References:
         * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
         * .
         */
        public: param_type(std::initializer_list<RealT> l1)
        : probs_(std::distance(l1.begin(), l1.end()), 1), // Will be normalized below
          rates_(l1.begin(), l1.end())
        {
            hyperexp_detail::normalize(probs_);

            assert( hyperexp_detail::check_params(probs_, rates_) );
        }
#endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST

        /**
         * Gets the phase probability vector parameter of the distribtuion.
         *
         * \return The phase probability vector parameter of the distribution.
         *
         * \note
         *  The returned probabilities are the normalized version of the ones
         *  passed at construction time.
         */
        public: std::vector<RealT> probabilities() const
        {
            return probs_;
        }

        /**
         * Gets the rate vector parameter of the distribtuion.
         *
         * \return The rate vector parameter of the distribution.
         */
        public: std::vector<RealT> rates() const
        {
            return rates_;
        }

        /** Writes a \c param_type to a \c std::ostream. */
        public: BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, param)
        {
            detail::print_vector(os, param.probs_);
            os << ' ';
            detail::print_vector(os, param.rates_);

            return os;
        }

        /** Reads a \c param_type from a \c std::istream. */
        public: BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, param)
        {
            // NOTE: if \c std::ios_base::exceptions is set, the code below may
            //       throw in case of a I/O failure.
            //       To prevent leaving the state of \c param inconsistent:
            //       - if an exception is thrown, the state of \c param is left
            //         unchanged (i.e., is the same as the one at the beginning
            //         of the function's execution), and
            //       - the state of \c param only after reading the whole input.

            std::vector<RealT> probs;
            std::vector<RealT> rates;

            // Reads probability and rate vectors
            detail::read_vector(is, probs);
            if (!is)
            {
                return is;
            }
            is >> std::ws;
            detail::read_vector(is, rates);
            if (!is)
            {
                return is;
            }

            // Update the state of the param_type object
            if (probs.size() > 0)
            {
                param.probs_.swap(probs);
                probs.clear();
            }
            if (rates.size() > 0)
            {
                param.rates_.swap(rates);
                rates.clear();
            }

            // Adjust vector sizes (if needed)
            if (param.probs_.size() != param.rates_.size()
                || param.probs_.size() == 0)
            {
                const std::size_t np = param.probs_.size();
                const std::size_t nr = param.rates_.size();

                if (np > nr)
                {
                    param.rates_.resize(np, 1);
                }
                else if (nr > np)
                {
                    param.probs_.resize(nr, 1);
                }
                else
                {
                    param.probs_.resize(1, 1);
                    param.rates_.resize(1, 1);
                }
            }

            // Normalize probabilities
            // NOTE: this cannot be done earlier since the probability vector
            //       can be changed due to size conformance
            hyperexp_detail::normalize(param.probs_);

            //post: vector size conformance
            assert(param.probs_.size() == param.rates_.size());

            return is;
        }

        /** Returns true if the two sets of parameters are the same. */
        public: BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
        {
            return lhs.probs_ == rhs.probs_
                   && lhs.rates_ == rhs.rates_;
        }

        /** Returns true if the two sets of parameters are the different. */
        public: BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)

        private: std::vector<RealT> probs_; ///< The phase probability vector parameter of the distribution
        private: std::vector<RealT> rates_; ///< The rate vector parameter of the distribution
    }; // param_type

    /**
     * Constructs a 1-phase \c hyperexponential_distribution (i.e., an
     * exponential distribution) with rate 1.
     */
    public: hyperexponential_distribution()
    : dd_(std::vector<RealT>(1, 1)),
      rates_(1, 1)
    {
        // empty
    }

    /**
     * Constructs a \c hyperexponential_distribution from the phase
     * probability vector and rate vector parameters of the
     * distribution.
     *
     * The phase probability vector parameter is given by the range
     * defined by [\a prob_first, \a prob_last) iterator pair, and the
     * rate vector parameter is given by the range defined by
     * [\a rate_first, \a rate_last) iterator pair.
     *
     * \tparam ProbIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
     * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
     *
     * \param prob_first The iterator to the beginning of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
     * \param prob_last The iterator to the ending of the range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
     * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
     * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
     *
     * References:
     * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
     * .
     */
    public: template <typename ProbIterT, typename RateIterT>
            hyperexponential_distribution(ProbIterT prob_first, ProbIterT prob_last,
                                          RateIterT rate_first, RateIterT rate_last)
    : dd_(prob_first, prob_last),
      rates_(rate_first, rate_last)
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

    /**
     * Constructs a \c hyperexponential_distribution from the phase
     * probability vector and rate vector parameters of the
     * distribution.
     *
     * The phase probability vector parameter is given by the range
     * defined by \a prob_range, and the rate vector parameter is
     * given by the range defined by \a rate_range.
     *
     * \tparam ProbRangeT Must meet the requirements of Range concept.
     * \tparam RateRangeT Must meet the requirements of Range concept.
     *
     * \param prob_range The range of non-negative real elements representing the phase probabilities; if elements don't sum to 1, they are normalized.
     * \param rate_range The range of positive real elements representing the rates.
     *
     * \note
     *  The final \c disable_if parameter is an implementation detail that
     *  differentiates between this two argument constructor and the
     *  iterator-based two argument constructor described below.
     */
    //  We SFINAE this out of existance if either argument type is
    //  incrementable as in that case the type is probably an iterator:
    public: template <typename ProbRangeT, typename RateRangeT>
            hyperexponential_distribution(ProbRangeT const& prob_range,
                                          RateRangeT const& rate_range,
                                          typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
    : dd_(prob_range),
      rates_(boost::begin(rate_range), boost::end(rate_range))
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

    /**
     * Constructs a \c hyperexponential_distribution from the rate
     * vector parameter of the distribution and with equal phase
     * probabilities.
     *
     * The rate vector parameter is given by the range defined by
     * [\a rate_first, \a rate_last) iterator pair, and the phase
     * probability vector parameter is set to the equal phase
     * probabilities (i.e., to a vector of the same length \f$k\f$ of the
     * rate vector and with each element set to \f$1.0/k\f$).
     *
     * \tparam RateIterT Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
     * \tparam RateIterT2 Must meet the requirements of \c InputIterator concept (ISO,2014,sec. 24.2.3 [input.iterators]).
     *
     * \param rate_first The iterator to the beginning of the range of non-negative real elements representing the rates.
     * \param rate_last The iterator to the ending of the range of non-negative real elements representing the rates.
     *
     * \note
     *  The final \c disable_if parameter is an implementation detail that
     *  differentiates between this two argument constructor and the
     *  range-based two argument constructor described above.
     *
     * References:
     * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
     * .
     */
    //  We SFINAE this out of existance if the argument type is
    //  incrementable as in that case the type is probably an iterator.
    public: template <typename RateIterT>
            hyperexponential_distribution(RateIterT rate_first,
                                          RateIterT rate_last,
                                          typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value>::type* = 0)
    : dd_(std::vector<RealT>(std::distance(rate_first, rate_last), 1)),
      rates_(rate_first, rate_last)
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

    /**
     * Constructs a @c param_type from the "rates" parameters
     * of the distribution and with equal phase probabilities.
     *
     * The rate vector parameter is given by the range defined by
     * \a rate_range, and the phase probability vector parameter is
     * set to the equal phase probabilities (i.e., to a vector of the same
     * length \f$k\f$ of the rate vector and with each element set
     * to \f$1.0/k\f$).
     *
     * \tparam RateRangeT Must meet the requirements of Range concept.
     *
     * \param rate_range The range of positive real elements representing the rates.
     */
    public: template <typename RateRangeT>
            hyperexponential_distribution(RateRangeT const& rate_range)
    : dd_(std::vector<RealT>(boost::size(rate_range), 1)),
      rates_(boost::begin(rate_range), boost::end(rate_range))
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

    /**
     * Constructs a \c hyperexponential_distribution from its parameters.
     *
     * \param param The parameters of the distribution.
     */
    public: explicit hyperexponential_distribution(param_type const& param)
    : dd_(param.probabilities()),
      rates_(param.rates())
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
    /**
     * Constructs a \c hyperexponential_distribution from the phase
     * probability vector and rate vector parameters of the
     * distribution.
     *
     * The phase probability vector parameter is given by the
     * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
     * defined by \a l1, and the rate vector parameter is given by the
     * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
     * defined by \a l2.
     *
     * \param l1 The initializer list for inizializing the phase probability vector.
     * \param l2 The initializer list for inizializing the rate vector.
     *
     * References:
     * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
     * .
     */
    public: hyperexponential_distribution(std::initializer_list<RealT> const& l1, std::initializer_list<RealT> const& l2)
    : dd_(l1.begin(), l1.end()),
      rates_(l2.begin(), l2.end())
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }

    /**
     * Constructs a \c hyperexponential_distribution from the rate
     * vector parameter of the distribution and with equal phase
     * probabilities.
     *
     * The rate vector parameter is given by the
     * brace-init-list (ISO,2014,sec. 8.5.4 [dcl.init.list])
     * defined by \a l1, and the phase probability vector parameter is
     * set to the equal phase probabilities (i.e., to a vector of the same
     * length \f$k\f$ of the rate vector and with each element set
     * to \f$1.0/k\f$).
     *
     * \param l1 The initializer list for inizializing the rate vector.
     *
     * References:
     * -# ISO, ISO/IEC 14882-2014: Information technology - Programming languages - C++, 2014
     * .
     */
    public: hyperexponential_distribution(std::initializer_list<RealT> const& l1)
    : dd_(std::vector<RealT>(std::distance(l1.begin(), l1.end()), 1)),
      rates_(l1.begin(), l1.end())
    {
        assert( hyperexp_detail::check_params(dd_.probabilities(), rates_) );
    }
#endif

    /**
     * Gets a random variate distributed according to the
     * hyperexponential distribution.
     *
     * \tparam URNG Must meet the requirements of \uniform_random_number_generator.
     *
     * \param urng A uniform random number generator object.
     *
     * \return A random variate distributed according to the hyperexponential distribution.
     */
    public: template<class URNG>\
            RealT operator()(URNG& urng) const
    {
        const int i = dd_(urng);

        return boost::random::exponential_distribution<RealT>(rates_[i])(urng);
    }

    /**
     * Gets a random variate distributed according to the hyperexponential
     * distribution with parameters specified by \c param.
     *
     * \tparam URNG Must meet the requirements of \uniform_random_number_generator.
     *
     * \param urng A uniform random number generator object.
     * \param param A distribution parameter object.
     *
     * \return A random variate distributed according to the hyperexponential distribution.
     *  distribution with parameters specified by \c param.
     */
    public: template<class URNG>
            RealT operator()(URNG& urng, const param_type& param) const
    {
        return hyperexponential_distribution(param)(urng);
    }

    /** Returns the number of phases of the distribution. */
    public: std::size_t num_phases() const
    {
        return rates_.size();
    }

    /** Returns the phase probability vector parameter of the distribution. */
    public: std::vector<RealT> probabilities() const
    {
        return dd_.probabilities();
    }

    /** Returns the rate vector parameter of the distribution. */
    public: std::vector<RealT> rates() const
    {
        return rates_;
    }

    /** Returns the smallest value that the distribution can produce. */
    public: RealT min BOOST_PREVENT_MACRO_SUBSTITUTION () const
    {
        return 0;
    }

    /** Returns the largest value that the distribution can produce. */
    public: RealT max BOOST_PREVENT_MACRO_SUBSTITUTION () const
    {
        return std::numeric_limits<RealT>::infinity();
    }

    /** Returns the parameters of the distribution. */
    public: param_type param() const
    {
        std::vector<RealT> probs = dd_.probabilities();

        return param_type(probs.begin(), probs.end(), rates_.begin(), rates_.end());
    }

    /** Sets the parameters of the distribution. */
    public: void param(param_type const& param)
    {
        dd_.param(typename boost::random::discrete_distribution<int,RealT>::param_type(param.probabilities()));
        rates_ = param.rates();
    }

    /**
     * Effects: Subsequent uses of the distribution do not depend
     * on values produced by any engine prior to invoking reset.
     */
    public: void reset()
    {
        // empty
    }

    /** Writes an @c hyperexponential_distribution to a @c std::ostream. */
    public: BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, hyperexponential_distribution, hd)
    {
        os << hd.param();
        return os;
    }

    /** Reads an @c hyperexponential_distribution from a @c std::istream. */
    public: BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, hyperexponential_distribution, hd)
    {
        param_type param;
        if(is >> param)
        {
            hd.param(param);
        }
        return is;
    }

    /**
     * Returns true if the two instances of @c hyperexponential_distribution will
     * return identical sequences of values given equal generators.
     */
    public: BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(hyperexponential_distribution, lhs, rhs)
    {
        return lhs.dd_ == rhs.dd_
               && lhs.rates_ == rhs.rates_;
    }

    /**
     * Returns true if the two instances of @c hyperexponential_distribution will
     * return different sequences of values given equal generators.
     */
    public: BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(hyperexponential_distribution)

    private: boost::random::discrete_distribution<int,RealT> dd_; ///< The \c discrete_distribution used to sample the phase probability and choose the rate
    private: std::vector<RealT> rates_; ///< The rate vector parameter of the distribution
}; // hyperexponential_distribution

}} // namespace boost::random

#endif // BOOST_RANDOM_HYPEREXPONENTIAL_DISTRIBUTION_HPP


//  Copyright John Maddock 2007.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// Note that this file contains quickbook mark-up as well as code
// and comments, don't change any of the special comment mark-ups!

#include <iostream>

//[policy_eg_5

#include <boost/math/special_functions.hpp>

namespace {

using namespace boost::math::policies;

typedef policy<
   domain_error<errno_on_error>,
   pole_error<errno_on_error>,
   overflow_error<errno_on_error>,
   evaluation_error<errno_on_error>
> c_policy;

BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(c_policy)

} // close unnamed namespace

int main()
{
   errno = 0;
   std::cout << "Result of tgamma(30000) is: "
      << tgamma(30000) << std::endl;
   std::cout << "errno = " << errno << std::endl;
   std::cout << "Result of tgamma(-10) is: "
      << tgamma(-10) << std::endl;
   std::cout << "errno = " << errno << std::endl;
}

//]


////////////////////////////////////////////////////////////////////////////////
// ElasticityTensor.hh
////////////////////////////////////////////////////////////////////////////////
/*! @file
//      Implements a rank 4 tensor with the symmetries of an elasticity tensor:
//          E_ijkl = E_jikl = E_ijlk = E_klij
//      This allows the tensor to be stored as a symmetric 6x6 matrix "D"
//      See doc/meshless_fem/TensorFlattening.pdf
//      for details of this transformation.
//
//      When _MajorSymmetry == true, symmetry is enforced by only storing the
//      upper triangle of D internally. This means matrix element accesses must
//      be done through method D(i, j), and matrix operations need to be
//      performed with Eigen's "selfadjointView<Eigen::Upper>" view. For safety,
//      because of this complexity, m_d is kept entirely private, with no direct
//      accessor.
//
//      When _MajorSymmetry == false, major symmetry is not enforced, and the
//      datastructure can be used to store non-major-symmetric tensors (though
//      these shouldn't be used as true elasticity tensors). This can be useful
//      to store intermediate computation results on rank 4 tensors: e.g. the
//      double contraction of two elasticity tensors.
*/
//  Author:  Julian Panetta (jpanetta), julian.panetta@gmail.com
//  Company:  New York University
//  Created:  05/11/2014 15:14:13
////////////////////////////////////////////////////////////////////////////////
#ifndef ELASTICITYTENSOR_HH
#define ELASTICITYTENSOR_HH

#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <stdexcept>

#include <tuple>
#include "Flattening.hh"
#include "SymmetricMatrix.hh"

#include "Algebra.hh"

// Eigenvalues/vectors are sorted in order of increasing eigenvalue.
struct ETensorEigenDecomposition {
    Eigen::MatrixXd strains;
    Eigen::VectorXd lambdas;
};

template<typename Real, size_t _Dim, bool _MajorSymmetry = true>
class ElasticityTensor : public VectorSpace<Real, ElasticityTensor<Real, _Dim, _MajorSymmetry>> {
    // We need access to other major symmetry types' members (for double
    // contraction operator), but unfortunately we can't friend a partial
    // template specialization, so...
    template<typename _Real2, size_t _Dim2, bool _MajorSymmetry2>
    friend class ElasticityTensor;
public:
    typedef Eigen::Matrix<Real, flatLen(_Dim), flatLen(_Dim)> DType;
    typedef typename DType::RowXpr                            RowXpr;
    typedef typename DType::ConstRowXpr                       ConstRowXpr;
    typedef typename DType::ColXpr                            ColXpr;
    typedef typename DType::ConstColXpr                       ConstColXpr;
    // Wraps a row of the flattened elasticity tensor with a symmetric matrix
    // interface--useful for periodic homogenization formulas where rows of the
    // flattened homogenized elasticity tensor are modulated by flattened
    // fluctuation stresses.
    typedef      SymmetricMatrixRef<_Dim, RowXpr, ConstRowXpr>      SMRowWrapper;
    typedef ConstSymmetricMatrixRef<_Dim,         ConstRowXpr> ConstSMRowWrapper;
    typedef      SymmetricMatrixRef<_Dim, ColXpr, ConstColXpr>      SMColWrapper;
    typedef ConstSymmetricMatrixRef<_Dim,         ConstColXpr> ConstSMColWrapper;

    typedef Eigen::Matrix<Real, flatLen(_Dim), 1>       FlattenedRank2Tensor;
    typedef SymmetricMatrix<_Dim, FlattenedRank2Tensor> SMatrix;

    static const bool MajorSymmetry = _MajorSymmetry;
    static const size_t Dim = _Dim;

    ElasticityTensor() : m_d(DType::Zero()) { }
    // Construct the elasticity tensor with a Young's modulus and Poisson ratio
    ElasticityTensor(Real E, Real nu) { setIsotropic(E, nu); }

    // Allow converting constructor to and from major symmetric tensors.
    // Converting to a major symmetric tensor verifies that the input is indeed
    // major symmetric.
    template<bool _MajorSymmetry2>
    ElasticityTensor(const ElasticityTensor<Real, _Dim, _MajorSymmetry2> &b) {
        if ( _MajorSymmetry == _MajorSymmetry2) m_d = b.m_d;
        if (!_MajorSymmetry && _MajorSymmetry2) {
            m_d = b.m_d.template selfadjointView<Eigen::Upper>();
        }
        if ( _MajorSymmetry && !_MajorSymmetry2) {
            if (!b.hasMajorSymmetry())
                throw std::runtime_error("Attempting to copy construct major-symmetric tensor from non-major symmetric tensor");
            m_d = b.m_d.template selfadjointView<Eigen::Upper>();
        }
    }

    // The symmetric rank 4 identity tensor corresponds to lambda = 0, mu = 1/2
    void setIdentity() { setIsotropicLame(0, 0.5); }

    // Configure the elasticity tensor with a Young's modulus and Poisson ratio
    void setIsotropic(Real E, Real nu) {
        // Lame formula:
        // stress = lamda * trace(strain) + mu (strain + strain^T)
        // (We write it this way so that the implied elasticity tensor has the
        //  correct symmetries.)
        Real lambda = (nu * E) / ((1.0 + nu) * (1.0 - 2.0 * nu));
        Real mu = E / (2.0 + 2.0 * nu);

        // For 2D (plane stress), lambda is actually different...
        // This can be found by inverting the 2D orthotropic compliance
        // tensor with Ex = Ey = E.
        if (_Dim == 2)
            lambda = (nu * E) / (1.0 - nu * nu);

        setIsotropicLame(lambda, mu);
    }

    // Configure the elasticity tensor with Lame parameters
    void setIsotropicLame(Real lambda, Real mu) {
        m_d =  DType::Zero();
        if (_Dim == 3) {
            m_d(0, 0) = lambda + 2 * mu; m_d(0, 1) = lambda;          m_d(0, 2) = lambda;
                                         m_d(1, 1) = lambda + 2 * mu; m_d(1, 2) = lambda;
                                                                      m_d(2, 2) = lambda + 2 * mu;
            m_d(3, 3) = m_d(4, 4) = m_d(5, 5) = mu;
        }
        else {
            assert(_Dim == 2);
            m_d(0, 0) = lambda + 2 * mu; m_d(0, 1) = lambda;
                                         m_d(1, 1) = lambda + 2 * mu;
            m_d(2, 2) = mu;
        }

        m_d = m_d.template selfadjointView<Eigen::Upper>();
    }

    void setOrthotropic3D(Real   Ex, Real   Ey, Real   Ez,
                          Real nuYX, Real nuZX, Real nuZY,
                          Real muYZ, Real muZX, Real muXY) {
        if (_Dim != 3)
            throw std::runtime_error("setOrthotropic3D call on non-3D tensor");
        // Note: this isn't the flattened compliance tensor! Rather, it is the
        // matrix inverse of the flattened elasticity tensor. See the tensor
        // flattening writeup.
        m_d << 1.0 / Ex, -nuYX / Ey, -nuZX / Ez,        0.0,        0.0,        0.0,
                    0.0,   1.0 / Ey, -nuZY / Ez,        0.0,        0.0,        0.0,
                    0.0,        0.0,   1.0 / Ez,        0.0,        0.0,        0.0,
                    0.0,        0.0,        0.0, 1.0 / muYZ,        0.0,        0.0,
                    0.0,        0.0,        0.0,        0.0, 1.0 / muZX,        0.0,
                    0.0,        0.0,        0.0,        0.0,        0.0, 1.0 / muXY;
        m_d = m_d.template selfadjointView<Eigen::Upper>();
        m_d = m_d.inverse().eval();
    }

    void setOrthotropic2D(Real Ex, Real Ey, Real nuYX, Real muXY) {
        if (_Dim != 2)
            throw std::runtime_error("setOrthotropic2D call on non-2D tensor");
        // Note: this isn't the flattened compliance tensor! Rather, it is the
        // matrix inverse of the flattened elasticity tensor.
        m_d << 1.0 / Ex, -nuYX / Ey,        0.0,
                    0.0,   1.0 / Ey,        0.0,
                    0.0,        0.0, 1.0 / muXY;
        m_d = m_d.template selfadjointView<Eigen::Upper>();
        m_d = m_d.inverse().eval();
    }

    // Get the orthotropic material parameters (assuming the material is
    // in fact orthotropic).
    void getOrthotropicParameters(std::vector<Real> &moduli) const {
        if (_Dim == 2) {
            moduli.resize(4);
            // Ex Ey nuYX nuXY
            getOrthotropic2D(moduli[0], moduli[1], moduli[2], moduli[3]);
        }
        else if (_Dim == 3) {
            moduli.resize(9);
            // Ex Ey Ez nuYX nuZX nuZY muYZ muZX muXY
            getOrthotropic3D(moduli[0], moduli[1], moduli[2],
                             moduli[3], moduli[4], moduli[5],
                             moduli[6], moduli[7], moduli[8]);
        }
        else {
            throw std::runtime_error("Invalid instance dimension.");
        }
    }

    std::vector<Real> getOrthotropicParameters() const {
        std::vector<Real> moduli;
        getOrthotropicParameters(moduli);
        return moduli;
    }

    // Get the orthotropic material parameters (assuming the material is in fact
    // 3D orthotropic)
    void getOrthotropic3D(Real &  Ex, Real &  Ey, Real &  Ez,
                          Real &nuYX, Real &nuZX, Real &nuZY,
                          Real &muYZ, Real &muZX, Real &muXY) const {
        if (_Dim != 3)
            throw std::runtime_error("getOrthotropic3D call on non-3D tensor");
        ElasticityTensor Einv = this->inverse();
        Ex = 1.0 / Einv.D(0, 0);
        Ey = 1.0 / Einv.D(1, 1);
        Ez = 1.0 / Einv.D(2, 2);
        nuYX = -Einv.D(0, 1) * Ey;
        nuZX = -Einv.D(0, 2) * Ez;
        nuZY = -Einv.D(1, 2) * Ez;
        // Recall: shear terms in the compliance tensor are actually 1/(4mu)
        // (See Tensor Flatteneing writeup)
        muYZ = 0.25 / Einv.D(3, 3);
        muZX = 0.25 / Einv.D(4, 4);
        muXY = 0.25 / Einv.D(5, 5);
    }

    // Get the orthotropic material parameters (assuming the material is in fact
    // 2D orthotropic)
    void getOrthotropic2D(Real &Ex, Real &Ey, Real &nuYX, Real &muXY) const {
        if (_Dim != 2)
            throw std::runtime_error("getOrthotropic2D call on non-2D tensor");
        ElasticityTensor Einv = this->inverse();
        Ex = 1.0 / Einv.D(0, 0);
        Ey = 1.0 / Einv.D(1, 1);
        nuYX = -Einv.D(0, 1) * Ey;
        // Recall: shear terms in the compliance tensor are actually 1/(4mu)
        // (See Tensor Flattening writeup)
        muXY = 0.25 / Einv.D(2, 2);
    }

	void getUpperRight2D(std::vector<Real> & components) const {
        if (_Dim != 2)
            throw std::runtime_error("getOrthotropic2D call on non-2D tensor");
        for (size_t i = 0; i < flatLen(_Dim); ++i)
        	for (size_t j = i; j < flatLen(_Dim); ++j)
        		components.push_back(m_d(i, j));
    }

    void printOrthotropic(std::ostream &os) const {
        if (_Dim == 2) {
            Real Ex, Ey, nuYX, muXY;
            getOrthotropic2D(Ex, Ey, nuYX, muXY);
            os << Ex << "\t" << Ey << "\t" << nuYX << "\t" << muXY << std::endl;
        }
        else {
            Real Ex, Ey, Ez, nuYX, nuZX, nuZY, muYZ, muZX, muXY;
            getOrthotropic3D(Ex, Ey, Ez, nuYX, nuZX, nuZY, muYZ, muZX, muXY);
            os << Ex << "\t" << Ey << "\t" << Ez << "\t"
               << nuYX << "\t" << nuZX << "\t" << nuZY << "\t"
               << muYZ << "\t" << muZX << "\t" << muXY << std::endl;
        }
    }

    Real anisotropy() const {
        Real mu_avg, E_avg, nu_avg;
        if (_Dim == 2) {
            Real Ex, Ey, nuYX, muXY;
            getOrthotropic2D(Ex, Ey, nuYX, muXY);
            E_avg = (Ex + Ey) / 2.0;
            nu_avg = nuYX;
            mu_avg = muXY;
        }
        else {
            Real Ex, Ey, Ez, nuYX, nuZX, nuZY, muYZ, muZX, muXY;
            getOrthotropic3D(Ex, Ey, Ez, nuYX, nuZX, nuZY, muYZ, muZX, muXY);
            E_avg = (Ex + Ey + Ez) / 3.0;
            nu_avg = (nuYX + nuZX + nuZY) / 3.0;
            mu_avg = (muYZ + muZX + muXY) / 3.0;
        }
        return mu_avg / (E_avg / (2 * (1 + nu_avg)));
    }

    void clear() {
        m_d =  DType::Zero();
    }

    Real operator()(size_t i, size_t j, size_t k, size_t l) const {
        assert((i < _Dim) && (j < _Dim) && (k < _Dim) && (l < _Dim));
        return D(flattenIndices<_Dim>(i, j), flattenIndices<_Dim>(k, l));
    }

    Real D(size_t i, size_t j) const {
        assert((i < (size_t) m_d.rows()) && (j < (size_t) m_d.cols()));
        if (_MajorSymmetry) return (i <= j) ? m_d(i, j) : m_d(j, i);
        else                return m_d(i, j);
    }

    Real &D(size_t i, size_t j) {
        assert((i < (size_t) m_d.rows()) && (j < (size_t) m_d.cols()));
        if (_MajorSymmetry) return (i <= j) ? m_d(i, j) : m_d(j, i);
        else                return m_d(i, j);
    }

    ConstRowXpr DRow(size_t i) const { assert(i < (size_t) m_d.rows()); return m_d.row(i); }
    RowXpr      DRow(size_t i)       { assert(i < (size_t) m_d.rows()); return m_d.row(i); }
    ConstSMRowWrapper DRowAsSymMatrix(size_t i) const { return ConstSMRowWrapper(DRow(i)); }
         SMRowWrapper DRowAsSymMatrix(size_t i)       { return      SMRowWrapper(DRow(i)); }

    ConstColXpr DCol(size_t i) const { assert(i < (size_t) m_d.cols()); return m_d.col(i); }
    ColXpr      DCol(size_t i)       { assert(i < (size_t) m_d.cols()); return m_d.col(i); }
    ConstSMColWrapper DColAsSymMatrix(size_t i) const { return ConstSMColWrapper(DCol(i)); }
         SMColWrapper DColAsSymMatrix(size_t i)       { return      SMColWrapper(DCol(i)); }

    // Get the flattened tensor's diagonal
    Eigen::Matrix<Real, flatLen(_Dim), 1> diag() const {
        return m_d.diagonal();
    }

    ////////////////////////////////////////////////////////////////////////////
    // VectorSpace requirements
    ////////////////////////////////////////////////////////////////////////////
    void Add(const ElasticityTensor &b) { m_d += b.m_d; }
    void Scale(Real s)                  { m_d *= s; }

    // Get the tensor Einv such that E : Einv = Identity
    // Note this is different from just inverting the flattened representation:
    // F(E^-1) = S^-1 F(E)^-1 S^-1
    ElasticityTensor inverse() const {
        ElasticityTensor result;
        if (_MajorSymmetry) result.m_d = m_d.template selfadjointView<Eigen::Upper>();
        else                result.m_d = m_d;
        result.m_d = result.m_d.inverse().eval();
         leftApplyShearDoublerInverse(result.m_d);
        rightApplyShearDoublerInverse(result.m_d);
        return result;
    }

    // For rank deficient elasticity tensors, get the tensor Epinv such that
    // E : (Epinv : E) = E.
    ElasticityTensor pseudoinverse() const {
        auto eigs = computeEigenstrains();
        // note: computeEigenstrains() performs the decomposition
        // D^1/2 F(E) D^1/2 = Q Lambda Q^T
        // And actually returns D^(-1/2) Q as the "eigenstrains".
        // We need to compute
        // F(Epinv) = [D^(-1/2) Q] Lambda_pinv [D^(-1/2) Q]^T
        for (int i = 0; i < eigs.lambdas.size(); ++i) {
            // TODO: think more about threshold here
            if (std::abs(eigs.lambdas[i]) > 1e-8)
                eigs.lambdas[i] = 1.0 / eigs.lambdas[i];
            else eigs.lambdas[i] = 0;
        }

        ElasticityTensor result;
        result.m_d = eigs.strains * (eigs.lambdas.asDiagonal() * eigs.strains.transpose());
        return result;
    }

    // Get the ***major*** transposed tensor E^T:
    // E^T_ijkl = E_klij
    // For major-symmetric tensors, this is an identity operation--returns copy.
    ElasticityTensor transpose() const {
        if (_MajorSymmetry) return *this;
        ElasticityTensor result;
        result.m_d = m_d.transpose();
        return result;
    }

    ////////////////////////////////////////////////////////////////////////////
    // In-place application of shear doubling matrices (both left and right)
    ////////////////////////////////////////////////////////////////////////////
    template<class T>
    void leftApplyShearDoubler(T &val) const {
        // Applying on right doubles "shear rows" of a matrix or vector
        assert(size_t(val.rows()) == flatLen(_Dim));
        for (size_t i = _Dim; i < flatLen(_Dim); ++i)
            for (size_t j = 0; j < (size_t) val.cols(); ++j)
                val(i, j) *= 2.0;
    }

    template<class T>
    void rightApplyShearDoubler(T &val) const {
        // Applying on left doubles "shear columns" of a matrix or row vector
        assert(size_t(val.cols()) == flatLen(_Dim));
        for (size_t j = _Dim; j < flatLen(_Dim); ++j)
            for (size_t i = 0; i < (size_t) val.rows(); ++i)
                val(i, j) *= 2.0;
    }

    template<class T>
    void leftApplyShearDoublerInverse(T &val) const {
        // Applying on right halves "shear rows" of a matrix or vector
        assert(size_t(val.rows()) == flatLen(_Dim));
        for (size_t i = _Dim; i < flatLen(_Dim); ++i)
            for (size_t j = 0; j < (size_t) val.cols(); ++j)
                val(i, j) *= 0.5;
    }

    template<class T>
    void rightApplyShearDoublerInverse(T &val) const {
        // Applying on left halves "shear columns" of a matrix or row vector
        assert(size_t(val.cols()) == flatLen(_Dim));
        for (size_t j = _Dim; j < flatLen(_Dim); ++j)
            for (size_t i = 0; i < (size_t) val.rows(); ++i)
                val(i, j) *= 0.5;
    }

    ////////////////////////////////////////////////////////////////////////////
    // In place application of *square root* of shear doubling matrices
    ////////////////////////////////////////////////////////////////////////////
    template<class T>
    void leftApplySqrtShearDoubler(T &val) const {
        // Applying on right doubles "shear rows" of a matrix or vector
        assert(size_t(val.rows()) == flatLen(_Dim));
        for (size_t i = _Dim; i < flatLen(_Dim); ++i)
            for (size_t j = 0; j < (size_t) val.cols(); ++j)
                val(i, j) *= sqrt(2.0);
    }

    template<class T>
    void rightApplySqrtShearDoubler(T &val) const {
        // Applying on left doubles "shear columns" of a matrix or row vector
        assert(size_t(val.cols()) == flatLen(_Dim));
        for (size_t j = _Dim; j < flatLen(_Dim); ++j)
            for (size_t i = 0; i < (size_t) val.rows(); ++i)
                val(i, j) *= sqrt(2.0);
    }

    template<class T>
    void leftApplySqrtShearDoublerInverse(T &val) const {
        // Applying on right halves "shear rows" of a matrix or vector
        assert(size_t(val.rows()) == flatLen(_Dim));
        for (size_t i = _Dim; i < flatLen(_Dim); ++i)
            for (size_t j = 0; j < (size_t) val.cols(); ++j)
                val(i, j) *= sqrt(0.5);
    }

    template<class T>
    void rightApplySqrtShearDoublerInverse(T &val) const {
        // Applying on left halves "shear columns" of a matrix or row vector
        assert(size_t(val.cols()) == flatLen(_Dim));
        for (size_t j = _Dim; j < flatLen(_Dim); ++j)
            for (size_t i = 0; i < (size_t) val.rows(); ++i)
                val(i, j) *= sqrt(0.5);
    }

    // Doubles the off-diagonal entries of a flattened symmetric rank 2 tensor.
    FlattenedRank2Tensor shearDoubled(FlattenedRank2Tensor t) const {
        for (size_t i = _Dim; i < (size_t) t.rows(); ++i)
            t[i] *= 2.0;
        return t;
    }

    // The operation is D * S * strain, where S is the "Shear doubling" matrix
    // needed to implement contraction E_ijkl e_kl
    // (see doc/meshless_fem/TensorFlattening.pdf)
    FlattenedRank2Tensor doubleContract(const FlattenedRank2Tensor &in) const {
        if (_MajorSymmetry) return m_d.template selfadjointView<Eigen::Upper>() * shearDoubled(in);
        else                return m_d * shearDoubled(in);
    }

    // Apply matrix D itself to a vector or a matrix. For this to have physical
    // meaning, "in" should represent a (collection of) flattened engineering
    // strains.
    template<typename FlattenedType>
    FlattenedType applyD(const FlattenedType &in) const {
        if (_MajorSymmetry) return m_d.template selfadjointView<Eigen::Upper>() * in;
        else                return m_d * in;
    }

    template<typename Real2, size_t N, class _Storage, class _ConstRef>
    SymmetricMatrix<N, FlattenedRank2Tensor>
    doubleContract(const ConstSymmetricMatrixBase<Real2, N, _Storage, _ConstRef> &b) const {
        return SymmetricMatrix<N, FlattenedRank2Tensor>(applyD(shearDoubled(b.flattened())));
    }

    // The following operation that we call "double double contract" maintains
    // major symmetry (result is major-symmetric if **both** A and B are):
    //      A : B : A       (A_ijpq B_pqrs A_rskl)
    // Tensor A is "this", B is passed as an argument.
    // The operation is implemented as:
    // F(A) S F(B) S F(A)
    template<bool _MajorSymmetry2>
    ElasticityTensor<Real, _Dim, _MajorSymmetry && _MajorSymmetry2>
    doubleDoubleContract(const ElasticityTensor<Real, _Dim, _MajorSymmetry2> &B) const {
        ElasticityTensor<Real, _Dim, _MajorSymmetry && _MajorSymmetry2> result;
        if (_MajorSymmetry) result.m_d = m_d.template selfadjointView<Eigen::Upper>();
        else                result.m_d = m_d;
        leftApplyShearDoubler(result.m_d);
        result.m_d = B.applyD(result.m_d);
        leftApplyShearDoubler(result.m_d);
        result.m_d = applyD(result.m_d);
        return result;
    }

    // NOTE: In general, double contraction:
    //      A : B       (A_ijpq B_pqkl)
    // gives a tensor without major symmetry. We implement this operation as:
    // F(A : B) = F(A) S F(B)
    template<bool _MajorSymmetry2>
    ElasticityTensor<Real, _Dim, false> doubleContract(const ElasticityTensor<Real, _Dim, _MajorSymmetry2> &B) const {
        ElasticityTensor<Real, _Dim, false> result;
        if (_MajorSymmetry2) result.m_d = B.m_d.template selfadjointView<Eigen::Upper>();
        else                 result.m_d = B.m_d;
        leftApplyShearDoubler(result.m_d);
        result.m_d = applyD(result.m_d);
        return result;
    }

    template<bool _MajorSymmetry2>
    Real quadrupleContract(const ElasticityTensor<Real, _Dim, _MajorSymmetry2> &b) const {
        Real result = 0;
        for (size_t i = 0; i < _Dim; ++i)
            for (size_t j = 0; j < _Dim; ++j)
                for (size_t k = 0; k < _Dim; ++k)
                    for (size_t l = 0; l < _Dim; ++l)
                        result += (*this)(i, j, k, l) * b(i, j, k, l);
        return result;
    }

    Real frobeniusNormSq() const { return this->quadrupleContract(*this); }

    // Applies an change of coordinates to this tensor using the
    // tensor transformation rule:
    // E_ijlk' = E_pqrs R_ip R_jq R_kr R_ls
    // (When R is a rotation or reflection, this is the correct transformation
    // rule for cartesian tensors).
    ElasticityTensor transform(const Eigen::Matrix<Real, _Dim, _Dim> &R) const {
        ElasticityTensor result;
        for (size_t i = 0; i < _Dim; ++i) {
            for (size_t j = 0; j < _Dim; ++j) {
                for (size_t k = 0; k < _Dim; ++k) {
                    for (size_t l = 0; l < _Dim; ++l) {
                        Real comp = 0;
                        for (size_t p = 0; p < _Dim; ++p)
                            for (size_t q = 0; q < _Dim; ++q)
                                for (size_t r = 0; r < _Dim; ++r)
                                    for (size_t s = 0; s < _Dim; ++s)
                                        comp += (*this)(p, q, r, s) * R(i, p) * R(j, q) * R(k, r) * R(l, s);
                        if (_MajorSymmetry) {
                            // Validate major symmetry (when we should have it).
                            Real existing = result(i, j, k, l);
                            assert((existing == 0) || (std::abs(existing - comp) < 1e-10));
                        }

                        size_t ij = flattenIndices<_Dim>(i, j);
                        size_t kl = flattenIndices<_Dim>(k, l);
                        result.m_d(ij, kl) = comp;
                    }
                }
            }
        }
        return result;
    }

    // If major symmetry isn't enforced, check whether it exists.
    bool hasMajorSymmetry() const {
        if (_MajorSymmetry) return true;
        Real absDiff = (m_d - DType(m_d.template selfadjointView<Eigen::Upper>())).norm();
        // Permit small relative or absolute deviations.
        return (absDiff < 1e-10) || (absDiff < 1e-10 * m_d.norm());
    }

    // Compute all the eigenstrains and corresponding eigenvalues. In other words,
    // find the (s, lambda) pairs satisfying:
    //      E : s = lambda s
    // Should only be used on major-symmetric tensors.
    ETensorEigenDecomposition computeEigenstrains() const {
        assert(hasMajorSymmetry());

        // We are solving the problem:
        //    lambda = max_||s||^2_F=1   s : E : s
        // In flattened form, this is:
        //    lambda = max_(F(s)^T D F(s) = 1) F(s)^T D F(E) D F(s)
        // Where D is the shear-doubling matrix. We could solve this as a
        // generalized eigenvalue problem, but instead we transform:
        //    e = D^(1/2) F(s) ==>
        //    lambda = max_(||e||^2 = 1) e^T D^(1/2) F(E) D^(1/2) e
        // i.e. an ordinary eigenvalue problem for D^(1/2) F(E) D^(1/2).
        // We then retrieve eigenstrain F(s) = D^(-1/2) e.
        //
        DType mat(m_d.template selfadjointView<Eigen::Upper>());
        leftApplySqrtShearDoubler(mat);
        rightApplySqrtShearDoubler(mat);
        Eigen::SelfAdjointEigenSolver<DType> solver;
        solver.compute(mat);

        Eigen::MatrixXd Q = solver.eigenvectors();
        Eigen::VectorXd Lambda = solver.eigenvalues();
        leftApplySqrtShearDoublerInverse(Q);
        return ETensorEigenDecomposition{Q, Lambda};
    }

    // Computes the eigenstrains with maximum eigenvalue (and this eigenvalue).
    // In otherwords, we find the (s, lambda) satisfying:
    //     E : s = lambda s
    // for greatest lambda.
    // Should only be used on major-symmetric tensors.
    std::tuple<SMatrix, Real> maxEigenstrain() const {
        auto eigs = computeEigenstrains();
        // Eigenvalues sorted in increasing order
        constexpr size_t largestIdx = flatLen(_Dim) - 1;
        return std::make_tuple(SMatrix(eigs.strains.col(largestIdx)), eigs.lambdas[largestIdx]);
    }

    // Same as above, but also approximate the algebraic multiplicity of the
    // maximum eigenvalue (within the specified tolerance) and also return the
    // second largest eigenvalue.
    std::tuple<SMatrix, Real, int, Real>  maxEigenstrainMultiplicity(Real tol = 1e-3) const {
        auto eigs = computeEigenstrains();

        // Eigenvalues sorted in increasing order
        constexpr size_t largestIdx = flatLen(_Dim) - 1;
        Real lambda = eigs.lambdas[largestIdx];

        // Approximate multiplicity
        int multiplicity = 1;
        for (size_t i = largestIdx; i > 0; --i) {
            if ((lambda - eigs.lambdas[i - 1]) < lambda * tol) ++multiplicity;
            else break;
        }
        return std::make_tuple(SMatrix(eigs.strains.col(largestIdx)), lambda, multiplicity,
                               eigs.lambdas[largestIdx - 1]);
    }

    // Write unflattened tensor in Mathematica array syntax.
    void writeUnflattened(std::ostream &os) const {
        os << "{";
        for (size_t i = 0; i < _Dim; ++i) {
            os << "{";
            for (size_t j = 0; j < _Dim; ++j) {
                os << "{";
                for (size_t k = 0; k < _Dim; ++k) {
                    os << "{";
                    for (size_t l = 0; l < _Dim; ++l) {
                        os << (*this)(i, j, k, l);
                        if (l < _Dim - 1) os << ", ";
                    }
                    os << ((k < _Dim - 1) ? "}, " : "}");
                }
                os << ((j < _Dim - 1) ? "}, " : "}");
            }
            os << ((i < _Dim - 1) ? "}, " : "}");
        }
        os << "}";
    }

    // For serialization into a json file
    std::vector<typename DType::Scalar> getCoefficients() const {
        std::vector<typename DType::Scalar> coeffs;
        if (_MajorSymmetry) {
            auto M = (DType) m_d.template selfadjointView<Eigen::Upper>();
            for (int i = 0; i < M.rows(); ++i) {
                for (int j = 0; j < M.cols(); ++j) {
                    coeffs.push_back(M(i, j));
                }
            }
        } else {
            auto M = m_d;
            for (int i = 0; i < M.rows(); ++i) {
                for (int j = 0; j < M.cols(); ++j) {
                    coeffs.push_back(M(i, j));
                }
            }
        }
        return coeffs;
    }

    // For debug purposes only
    void writeD(std::ostream &os) const {
        os << m_d;
    }

    void readD(std::istream &is) const {
        is >> m_d;
    }

private:
    DType m_d;

    friend std::ostream &operator<<(std::ostream &os, const ElasticityTensor &E) {
        if (_MajorSymmetry) os << (DType) E.m_d.template selfadjointView<Eigen::Upper>();
        else                os << E.m_d;
        return os;
    }
};

#include "LinearIndexer.hh"

// Index the distinct components (after accounting for symmetries).
// Only major symmetric tensors are currently supported
template<typename Real, size_t N>
struct LinearIndexerImpl<ElasticityTensor<Real, N, true>> {
    using tensor_type = ElasticityTensor<Real, N, true>;
    using scalar_type = Real;

    static Real &index(      tensor_type &val, size_t i) { size_t ij, kl; linearIndexTo2D(i, ij, kl); return val.D(ij, kl); }
    static Real  index(const tensor_type &val, size_t i) { size_t ij, kl; linearIndexTo2D(i, ij, kl); return val.D(ij, kl); }
    static constexpr size_t size() { return (flatLen(N) * (flatLen(N) + 1)) / 2; }

    // Compute the flattened tensor row and column index corresponding to a
    // 1D index. This is the inverse of the following map:
    //    r(ij, kl) = kl + flatlen * ij - (ij * (ij + 1)) / 2
    // Not a closed form inverse, but likely faster than sqrt version anyway
    static void linearIndexTo2D(size_t idx, size_t &ij, size_t &kl) {
        assert(idx < size());
        kl = flatLen(N) + 1; // invalid
        for (ij = 0; ij < flatLen(N); ++ij) {
            size_t rowSize = flatLen(N) - ij;
            if (idx < rowSize) { kl = ij + idx; break;}
            idx -= rowSize;
        }
        assert((ij < flatLen(N)) && (kl < flatLen(N)));
    }
};

#endif /* end of include guard: ELASTICITYTENSOR_HH */


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/mlog10two2nmb.hpp>
#include <boost/simd/as.hpp>
#include <simd_test.hpp>

STF_CASE_TPL( "Check mlog10two2nmb behavior for integral types"
            , (std::uint32_t)(std::uint64_t)
              (std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mlog10two2nmb;
  using boost::simd::Mlog10two2nmb;
  T ref = T(0);
  STF_TYPE_IS(decltype(Mlog10two2nmb<T>()), T);
  STF_EQUAL(Mlog10two2nmb<T>(), ref);
  STF_EQUAL(mlog10two2nmb( as(T{}) ),ref);
}

STF_CASE_TPL( "Check mlog10two2nmb behavior for double"
            , (double)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mlog10two2nmb;
  using boost::simd::Mlog10two2nmb;
  T ref = T(-15.9545897701910033463281614204);

  STF_TYPE_IS(decltype(Mlog10two2nmb<T>()), T);
  STF_IEEE_EQUAL(Mlog10two2nmb<T>(), ref);
  STF_IEEE_EQUAL(mlog10two2nmb( as(T{}) ), ref);
}

STF_CASE_TPL( "Check mlog10two2nmb behavior for float"
            , (float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::mlog10two2nmb;
  using boost::simd::Mlog10two2nmb;
  T ref = T(-6.92368990027156748991599457866);
  STF_TYPE_IS(decltype(Mlog10two2nmb<T>()), T);
  STF_IEEE_EQUAL(Mlog10two2nmb<T>(), ref);
  STF_IEEE_EQUAL(mlog10two2nmb( as(T{}) ), ref);
}


/*
 * Hélène Perrier helene.perrier@liris.cnrs.fr
 * and David Coeurjolly david.coeurjolly@liris.cnrs.fr
 *
 * Copyright (c) 2018 CNRS Université de Lyon
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the UTK project.
 */
#ifndef _TRIANGLE_ARRANGEMENT_H_
#define _TRIANGLE_ARRANGEMENT_H_

#include <random>
#include <CGAL/Exact_rational.h>
#include <CGAL/Cartesian.h>
#include <CGAL/Env_triangle_traits_3.h>
#include <CGAL/Env_surface_data_traits_3.h>
#include <CGAL/envelope_3.h>
#include <CGAL/Arr_naive_point_location.h>
#include <CGAL/Arr_walk_along_line_point_location.h>
#include <CGAL/Arr_landmarks_point_location.h>
#include <boost/variant/get.hpp>
#include <CGAL/Triangle_3.h>
#include "DrawTriangleArrangement.hpp"
#include "../../io/messageStream.hpp"

#ifdef CIMG_ON
	#include "../../externals/CImg-1.6.9/CImg.h"
	using namespace cimg_library;
#endif

typedef CGAL::Exact_rational                             Number_type;
typedef CGAL::Cartesian<Number_type>                     Kernel;
typedef Kernel::FT                                       FT;
typedef CGAL::Env_triangle_traits_3<Kernel>              Traits_3;
typedef Traits_3::Point_3                                  Point_3;
typedef Traits_3::Surface_3                              Triangle_3;
typedef CGAL::Env_surface_data_traits_3<Traits_3, float>  Data_traits_3;
typedef Data_traits_3::Surface_3                         Data_triangle_3;
typedef CGAL::Envelope_diagram_2<Data_traits_3>          Envelope_diagram_2;
typedef Envelope_diagram_2::Point_2                      Point_2;
typedef CGAL::Arr_naive_point_location<Envelope_diagram_2> Naive_pl;
typedef CGAL::Arr_walk_along_line_point_location<Envelope_diagram_2> Walk_pl;

std::vector<std::pair<float, float>> luminance_histogram;
std::vector<std::pair<float, float>> orientation_histogram;

//MAXSIZE is 2^16
const int TRIANGLE_MAXSIZE = 65536;

namespace utk
{

class TriangleArrangement
{

public:
	TriangleArrangement() {};
	~TriangleArrangement() {};

	virtual void generateTriangles(const unsigned int seed, std::string histogram_lumi="") = 0;

	void computeEnveloppe() { CGAL::upper_envelope_3 (triangles.begin(), triangles.end(), diagram); }

	double exact_integration();
	double integration(std::list<Point_2> &pointset);
	void rasterizerPS(const unsigned int Width,
                const char *filename,
                const bool displayProgressBar=true);
	double sample(const Point_2& p);

protected:
	float generateTag();
	float generateAngle();

	void load_histogram(std::string luminancehistogram_file="");
	void load_orientations(float min, float max, float mean, float stddev, float step=0.01);

	float getId(Walk_pl& pl, const Point_2 &p);
	FT measureTriangle(const Envelope_diagram_2::Ccb_halfedge_const_circulator &a,
                            const Envelope_diagram_2::Ccb_halfedge_const_circulator &b,
                            const Envelope_diagram_2::Ccb_halfedge_const_circulator &c);
	FT measureFace(const Envelope_diagram_2::Face_const_iterator &fit);

protected:
	Envelope_diagram_2 diagram;
	std::list<Data_triangle_3> triangles;
};

/*******************************************/
/** Statistics over the image to generate **/
/*******************************************/

/*Load the luminance histogram*/
void TriangleArrangement::load_histogram(std::string luminancehistogram_file)
{
  if (!luminancehistogram_file.empty())
  {
	std::ifstream file;
	file.open(luminancehistogram_file.c_str(), std::ifstream::in);
	do
	{
		float val;
		float cdf;
		file >> val;
		file >> cdf;
		luminance_histogram.push_back(std::pair<float, float>(val, cdf));
	}
	while(!file.eof());
	luminance_histogram.resize(luminance_histogram.size()-1);

	file.close();
  }
  else
  {
	for(int i=1; i<=TRIANGLE_MAXSIZE; i++)
		luminance_histogram.push_back(std::pair<float, float>((double)i/(double)TRIANGLE_MAXSIZE, (double)i/(double)TRIANGLE_MAXSIZE));
  }
}

/*returns a random tag value following the distribution in luminance_histogram*/
float TriangleArrangement::generateTag()
{
	double a = drand48();
	if (luminance_histogram[0].second > a)
		return luminance_histogram[0].first;

	for(unsigned int i=0; i<luminance_histogram.size(); i++)
	{
		if (luminance_histogram[i].second < a && luminance_histogram[i+1].second > a)
			return luminance_histogram[i].first;
	}

	return luminance_histogram[luminance_histogram.size()-1].first;
}

/*Load the orientations histogram*/
float gaussianCDF(float x, float mean, float stddev)
{
	float num = x - mean;
	float denom = stddev*sqrt(2);

	float erf_val = std::erf( num/denom );

	return 0.5*(1 + erf_val);
}
void TriangleArrangement::load_orientations(float min, float max, float mean, float stddev, float step)
{
	for(float i=min; i<=max; i+=step)
		orientation_histogram.push_back(std::pair<float,float>(i, gaussianCDF(i, mean, stddev)) );
}

/*returns a random angle in radians following the distribution in orientation_histogram*/
float TriangleArrangement::generateAngle()
{
	double a = drand48();
	if (orientation_histogram[0].second > a)
		return orientation_histogram[0].first;

	for(unsigned int i=0; i<orientation_histogram.size(); i++)
	{
		if (orientation_histogram[i].second < a && orientation_histogram[i+1].second > a)
			return orientation_histogram[i].first;
	}

	return orientation_histogram[orientation_histogram.size()-1].first;
}

/*******************************************/
/******** Integration over the soup ********/
/*******************************************/

//Return the triangle Id  associated to the arrangement cell containing a given point*
//The walker defines the point location strategy
//
// BE CAREFULL: the returned Id  0 corresponds to the infinite face (not a triangle).
//
float TriangleArrangement::getId(Walk_pl& pl, const Point_2 &p)
{
  typedef typename Envelope_diagram_2::Vertex_const_handle   Vertex_const_handle;
  typedef typename Envelope_diagram_2::Halfedge_const_handle Halfedge_const_handle;
  typedef typename Envelope_diagram_2::Face_const_handle     Face_const_handle;
  const Vertex_const_handle*   v;
  const Halfedge_const_handle* e;
  const Face_const_handle*     f;

  typename CGAL::Arr_point_location_result<Envelope_diagram_2>::Type obj = pl.locate( p );
  if ((f = boost::get<Face_const_handle>(&obj))) //point in a face
  {
    if (!((*f)->is_unbounded())) //not in unbounded
    {
      Envelope_diagram_2::Face_const_iterator it(*f);
      if (it->surfaces_begin() == it->surfaces_end() )
      {
        return 0; //should be a hole or a face with no data
      }
      else
        return it->surfaces_begin()->data();
     }
    else
      return 0;  // unbounded face
  }

  if ((e = boost::get<Halfedge_const_handle>(&obj))) // located on an edge
  {
    Envelope_diagram_2::Halfedge_const_iterator it(*e);
    return it->surfaces_begin()->data();
  }

  if ((v = boost::get<Vertex_const_handle>(&obj))) // located on an edge
  {
    Envelope_diagram_2::Vertex_const_iterator it(*v);
    return it->surfaces_begin()->data();
  }
  std::cerr << "Oops something goes wrong while locating a point " << p << std::endl;
  exit(2);
  return 0;
}

//Measure of a signed triangle
FT TriangleArrangement::measureTriangle(const Envelope_diagram_2::Ccb_halfedge_const_circulator &a,
                            const Envelope_diagram_2::Ccb_halfedge_const_circulator &b,
                            const Envelope_diagram_2::Ccb_halfedge_const_circulator &c)
{
  assert(a->target()->point().x().to_double() > 0 && a->target()->point().x().to_double() < TRIANGLE_MAXSIZE);
  assert(b->target()->point().x().to_double() > 0 && b->target()->point().x().to_double() < TRIANGLE_MAXSIZE);
  assert(c->target()->point().x().to_double() > 0 && c->target()->point().x().to_double() < TRIANGLE_MAXSIZE);

  assert(a->target()->point().y().to_double() > 0 && a->target()->point().y().to_double() < TRIANGLE_MAXSIZE);
  assert(b->target()->point().y().to_double() > 0 && b->target()->point().y().to_double() < TRIANGLE_MAXSIZE);
  assert(c->target()->point().y().to_double() > 0 && c->target()->point().y().to_double() < TRIANGLE_MAXSIZE);

  FT area = CGAL::area( a->target()->point(), b->target()->point(), c->target()->point());
  return area;
}

//Measure of a face
// Area of the face x the tagId of the face
// (i.e. constant function with value tagId on the face)
FT TriangleArrangement::measureFace(const Envelope_diagram_2::Face_const_iterator &fit)
{
  Envelope_diagram_2::Ccb_halfedge_const_circulator  ccb,nextccb,origin;

  //Face without any data
  if (fit->surfaces_begin() == fit->surfaces_end())
    return 0;

  const float faceId = fit->surfaces_begin()->data();

  // Print the vertices along the outer boundary of the face.
  ccb = fit->outer_ccb();
  origin = ccb;
  ++ccb;
  nextccb = ccb;
  ++nextccb;
  FT measure = 0;
  do
  {
    measure += measureTriangle( origin, ccb, nextccb );
    ++ccb;
    ++nextccb;
  } while (nextccb != origin);

  return measure * (faceId);
}

// Exact Integration
// normalized to stay in [0,1]
double TriangleArrangement::exact_integration()
{
  FT integral = 0;
  for(Envelope_diagram_2::Face_const_iterator faceit = diagram.faces_begin(), faceend = diagram.faces_end();
      faceit != faceend; ++faceit)
  {
    if (! (faceit->is_unbounded()))
      integral += measureFace( faceit );
  }
  FT scale = TRIANGLE_MAXSIZE;
  integral = integral / scale  / scale;
  return CGAL::to_double(integral);
}

// MonteCarlo Integration
double TriangleArrangement::integration(std::list<Point_2> &pointset)
{
  double sum = 0.0;
  Walk_pl pl(diagram);
  const double N = static_cast<double>(pointset.size());

  for(auto it = pointset.begin(), itend = pointset.end() ; it != itend; ++it)
  {
	float faceId =  getId(pl, *it);
	double quantity = (faceId);
    sum += quantity;
  }

  return 1.0/N * sum;
}

double TriangleArrangement::sample(const Point_2 &pt)
{
	Walk_pl pl(diagram);
	return getId(pl, pt);
}

/*******************************************/
/************* Rasterization ***************/
/*******************************************/

void TriangleArrangement::rasterizerPS(const unsigned int Width,
                const char *filename,
                const bool displayProgressBar)
{
	utk::Drawer_Postscript d(filename, Width);
	double normalisation_factor = 1.0/TRIANGLE_MAXSIZE;

	for(Envelope_diagram_2::Face_const_iterator faceit = diagram.faces_begin(), faceend = diagram.faces_end();
      faceit != faceend; ++faceit)
	{
		Envelope_diagram_2::Ccb_halfedge_const_circulator  ccb,nextccb,origin;
		//Face without any data
		if (faceit->surfaces_begin() == faceit->surfaces_end())
			continue;

		std::vector< utk::Vector<2, double> > points;
		const float color  = faceit->surfaces_begin()->data() / luminance_histogram[luminance_histogram.size()-1].first;

		ccb = faceit->outer_ccb();
		origin = ccb;
		++ccb;
		nextccb = ccb;
		++nextccb;

		double pt[2];
    pt[0] = CGAL::to_double(origin->target()->point().x())*normalisation_factor;
		pt[1] = CGAL::to_double(origin->target()->point().y())*normalisation_factor;
		points.push_back(utk::Vector<2, double>(pt));

		pt[0] = CGAL::to_double(ccb->target()->point().x())*normalisation_factor;
		pt[1] = CGAL::to_double(ccb->target()->point().y())*normalisation_factor;
		points.push_back(utk::Vector<2, double>(pt));
		do
		{
			pt[0] = CGAL::to_double(nextccb->target()->point().x())*normalisation_factor;
			pt[1] = CGAL::to_double(nextccb->target()->point().y())*normalisation_factor;
			points.push_back(utk::Vector<2, double>(pt));
			++nextccb;
		} while (nextccb != origin);

		d.drawPolygon(points, color);
	}
}

/*******************************************/
/***************** Utils *******************/
/*******************************************/

bool backToBounds(double& x, double& y)
{
	bool hasmoved = false;
	if (x <= 0) {
		hasmoved = true;
		x = drand48()*1000+1;
	}
	if (y <= 0){
		hasmoved = true;
		y = drand48()*1000+1;
	}
	if (x >= TRIANGLE_MAXSIZE){
		hasmoved = true;
		x = TRIANGLE_MAXSIZE-(drand48()*1000)-1;
	}
	if (y >= TRIANGLE_MAXSIZE){
		hasmoved = true;
		y = TRIANGLE_MAXSIZE-(drand48()*1000)-1;
	}
	return hasmoved;
}

} //end namespace utk

#endif