// boost\math\distributions\binomial.hpp

// Copyright John Maddock 2006.
// Copyright Paul A. Bristow 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)

// http://en.wikipedia.org/wiki/binomial_distribution

// Binomial distribution is the discrete probability distribution of
// the number (k) of successes, in a sequence of
// n independent (yes or no, success or failure) Bernoulli trials.

// It expresses the probability of a number of events occurring in a fixed time
// if these events occur with a known average rate (probability of success),
// and are independent of the time since the last event.

// The number of cars that pass through a certain point on a road during a given period of time.
// The number of spelling mistakes a secretary makes while typing a single page.
// The number of phone calls at a call center per minute.
// The number of times a web server is accessed per minute.
// The number of light bulbs that burn out in a certain amount of time.
// The number of roadkill found per unit length of road

// http://en.wikipedia.org/wiki/binomial_distribution

// Given a sample of N measured values k[i],
// we wish to estimate the value of the parameter x (mean)
// of the binomial population from which the sample was drawn.
// To calculate the maximum likelihood value = 1/N sum i = 1 to N of k[i]

// Also may want a function for EXACTLY k.

// And probability that there are EXACTLY k occurrences is
// exp(-x) * pow(x, k) / factorial(k)
// where x is expected occurrences (mean) during the given interval.
// For example, if events occur, on average, every 4 min,
// and we are interested in number of events occurring in 10 min,
// then x = 10/4 = 2.5

// http://www.itl.nist.gov/div898/handbook/eda/section3/eda366i.htm

// The binomial distribution is used when there are
// exactly two mutually exclusive outcomes of a trial.
// These outcomes are appropriately labeled "success" and "failure".
// The binomial distribution is used to obtain
// the probability of observing x successes in N trials,
// with the probability of success on a single trial denoted by p.
// The binomial distribution assumes that p is fixed for all trials.

// P(x, p, n) = n!/(x! * (n-x)!) * p^x * (1-p)^(n-x)

// http://mathworld.wolfram.com/BinomialCoefficient.html

// The binomial coefficient (n; k) is the number of ways of picking
// k unordered outcomes from n possibilities,
// also known as a combination or combinatorial number.
// The symbols _nC_k and (n; k) are used to denote a binomial coefficient,
// and are sometimes read as "n choose k."
// (n; k) therefore gives the number of k-subsets  possible out of a set of n distinct items.

// For example:
//  The 2-subsets of {1,2,3,4} are the six pairs {1,2}, {1,3}, {1,4}, {2,3}, {2,4}, and {3,4}, so (4; 2)==6.

// http://functions.wolfram.com/GammaBetaErf/Binomial/ for evaluation.

// But note that the binomial distribution
// (like others including the poisson, negative binomial & Bernoulli)
// is strictly defined as a discrete function: only integral values of k are envisaged.
// However because of the method of calculation using a continuous gamma function,
// it is convenient to treat it as if a continous function,
// and permit non-integral values of k.
// To enforce the strict mathematical model, users should use floor or ceil functions
// on k outside this function to ensure that k is integral.

#ifndef BOOST_MATH_SPECIAL_BINOMIAL_HPP
#define BOOST_MATH_SPECIAL_BINOMIAL_HPP

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/special_functions/beta.hpp> // for incomplete beta.
#include <boost/math/distributions/complement.hpp> // complements
#include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
#include <boost/math/distributions/detail/inv_discrete_quantile.hpp> // error checks
#include <boost/math/special_functions/fpclassify.hpp> // isnan.
#include <boost/math/tools/roots.hpp> // for root finding.

#include <utility>

namespace boost
{
  namespace math
  {

     template <class RealType, class Policy>
     class binomial_distribution;

     namespace binomial_detail{
        // common error checking routines for binomial distribution functions:
        template <class RealType, class Policy>
        inline bool check_N(const char* function, const RealType& N, RealType* result, const Policy& pol)
        {
           if((N < 0) || !(boost::math::isfinite)(N))
           {
               *result = policies::raise_domain_error<RealType>(
                  function,
                  "Number of Trials argument is %1%, but must be >= 0 !", N, pol);
               return false;
           }
           return true;
        }
        template <class RealType, class Policy>
        inline bool check_success_fraction(const char* function, const RealType& p, RealType* result, const Policy& pol)
        {
           if((p < 0) || (p > 1) || !(boost::math::isfinite)(p))
           {
               *result = policies::raise_domain_error<RealType>(
                  function,
                  "Success fraction argument is %1%, but must be >= 0 and <= 1 !", p, pol);
               return false;
           }
           return true;
        }
        template <class RealType, class Policy>
        inline bool check_dist(const char* function, const RealType& N, const RealType& p, RealType* result, const Policy& pol)
        {
           return check_success_fraction(
              function, p, result, pol)
              && check_N(
               function, N, result, pol);
        }
        template <class RealType, class Policy>
        inline bool check_dist_and_k(const char* function, const RealType& N, const RealType& p, RealType k, RealType* result, const Policy& pol)
        {
           if(check_dist(function, N, p, result, pol) == false)
              return false;
           if((k < 0) || !(boost::math::isfinite)(k))
           {
               *result = policies::raise_domain_error<RealType>(
                  function,
                  "Number of Successes argument is %1%, but must be >= 0 !", k, pol);
               return false;
           }
           if(k > N)
           {
               *result = policies::raise_domain_error<RealType>(
                  function,
                  "Number of Successes argument is %1%, but must be <= Number of Trials !", k, pol);
               return false;
           }
           return true;
        }
        template <class RealType, class Policy>
        inline bool check_dist_and_prob(const char* function, const RealType& N, RealType p, RealType prob, RealType* result, const Policy& pol)
        {
           if(check_dist(function, N, p, result, pol) && detail::check_probability(function, prob, result, pol) == false)
              return false;
           return true;
        }

         template <class T, class Policy>
         T inverse_binomial_cornish_fisher(T n, T sf, T p, T q, const Policy& pol)
         {
            BOOST_MATH_STD_USING
            // mean:
            T m = n * sf;
            // standard deviation:
            T sigma = sqrt(n * sf * (1 - sf));
            // skewness
            T sk = (1 - 2 * sf) / sigma;
            // kurtosis:
            // T k = (1 - 6 * sf * (1 - sf) ) / (n * sf * (1 - sf));
            // Get the inverse of a std normal distribution:
            T x = boost::math::erfc_inv(p > q ? 2 * q : 2 * p, pol) * constants::root_two<T>();
            // Set the sign:
            if(p < 0.5)
               x = -x;
            T x2 = x * x;
            // w is correction term due to skewness
            T w = x + sk * (x2 - 1) / 6;
            /*
            // Add on correction due to kurtosis.
            // Disabled for now, seems to make things worse?
            //
            if(n >= 10)
               w += k * x * (x2 - 3) / 24 + sk * sk * x * (2 * x2 - 5) / -36;
               */
            w = m + sigma * w;
            if(w < tools::min_value<T>())
               return sqrt(tools::min_value<T>());
            if(w > n)
               return n;
            return w;
         }

      template <class RealType, class Policy>
      RealType quantile_imp(const binomial_distribution<RealType, Policy>& dist, const RealType& p, const RealType& q)
      { // Quantile or Percent Point Binomial function.
        // Return the number of expected successes k,
        // for a given probability p.
        //
        // Error checks:
        BOOST_MATH_STD_USING  // ADL of std names
        RealType result;
        RealType trials = dist.trials();
        RealType success_fraction = dist.success_fraction();
        if(false == binomial_detail::check_dist_and_prob(
           "boost::math::quantile(binomial_distribution<%1%> const&, %1%)",
           trials,
           success_fraction,
           p,
           &result, Policy()))
        {
           return result;
        }

        // Special cases:
        //
        if(p == 0)
        {  // There may actually be no answer to this question,
           // since the probability of zero successes may be non-zero,
           // but zero is the best we can do:
           return 0;
        }
        if(p == 1)
        {  // Probability of n or fewer successes is always one,
           // so n is the most sensible answer here:
           return trials;
        }
        if (p <= pow(1 - success_fraction, trials))
        { // p <= pdf(dist, 0) == cdf(dist, 0)
          return 0; // So the only reasonable result is zero.
        } // And root finder would fail otherwise.

        // Solve for quantile numerically:
        //
        RealType guess = binomial_detail::inverse_binomial_cornish_fisher(trials, success_fraction, p, q, Policy());
        RealType factor = 8;
        if(trials > 100)
           factor = 1.01f; // guess is pretty accurate
        else if((trials > 10) && (trials - 1 > guess) && (guess > 3))
           factor = 1.15f; // less accurate but OK.
        else if(trials < 10)
        {
           // pretty inaccurate guess in this area:
           if(guess > trials / 64)
           {
              guess = trials / 4;
              factor = 2;
           }
           else
              guess = trials / 1024;
        }
        else
           factor = 2; // trials largish, but in far tails.

        typedef typename Policy::discrete_quantile_type discrete_quantile_type;
        boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
        return detail::inverse_discrete_quantile(
            dist,
            p,
            q,
            guess,
            factor,
            RealType(1),
            discrete_quantile_type(),
            max_iter);
      } // quantile

     }

    template <class RealType = double, class Policy = policies::policy<> >
    class binomial_distribution
    {
    public:
      typedef RealType value_type;
      typedef Policy policy_type;

      binomial_distribution(RealType n = 1, RealType p = 0.5) : m_n(n), m_p(p)
      { // Default n = 1 is the Bernoulli distribution
        // with equal probability of 'heads' or 'tails.
         RealType r;
         binomial_detail::check_dist(
            "boost::math::binomial_distribution<%1%>::binomial_distribution",
            m_n,
            m_p,
            &r, Policy());
      } // binomial_distribution constructor.

      RealType success_fraction() const
      { // Probability.
        return m_p;
      }
      RealType trials() const
      { // Total number of trials.
        return m_n;
      }

      enum interval_type{
         clopper_pearson_exact_interval,
         jeffreys_prior_interval
      };

      //
      // Estimation of the success fraction parameter.
      // The best estimate is actually simply successes/trials,
      // these functions are used
      // to obtain confidence intervals for the success fraction.
      //
      static RealType find_lower_bound_on_p(
         RealType trials,
         RealType successes,
         RealType probability,
         interval_type t = clopper_pearson_exact_interval)
      {
        static const char* function = "boost::math::binomial_distribution<%1%>::find_lower_bound_on_p";
        // Error checks:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           function, trials, RealType(0), successes, &result, Policy())
            &&
           binomial_detail::check_dist_and_prob(
           function, trials, RealType(0), probability, &result, Policy()))
        { return result; }

        if(successes == 0)
           return 0;

        // NOTE!!! The Clopper Pearson formula uses "successes" not
        // "successes+1" as usual to get the lower bound,
        // see http://www.itl.nist.gov/div898/handbook/prc/section2/prc241.htm
        return (t == clopper_pearson_exact_interval) ? ibeta_inv(successes, trials - successes + 1, probability, static_cast<RealType*>(0), Policy())
           : ibeta_inv(successes + 0.5f, trials - successes + 0.5f, probability, static_cast<RealType*>(0), Policy());
      }
      static RealType find_upper_bound_on_p(
         RealType trials,
         RealType successes,
         RealType probability,
         interval_type t = clopper_pearson_exact_interval)
      {
        static const char* function = "boost::math::binomial_distribution<%1%>::find_upper_bound_on_p";
        // Error checks:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           function, trials, RealType(0), successes, &result, Policy())
            &&
           binomial_detail::check_dist_and_prob(
           function, trials, RealType(0), probability, &result, Policy()))
        { return result; }

        if(trials == successes)
           return 1;

        return (t == clopper_pearson_exact_interval) ? ibetac_inv(successes + 1, trials - successes, probability, static_cast<RealType*>(0), Policy())
           : ibetac_inv(successes + 0.5f, trials - successes + 0.5f, probability, static_cast<RealType*>(0), Policy());
      }
      // Estimate number of trials parameter:
      //
      // "How many trials do I need to be P% sure of seeing k events?"
      //    or
      // "How many trials can I have to be P% sure of seeing fewer than k events?"
      //
      static RealType find_minimum_number_of_trials(
         RealType k,     // number of events
         RealType p,     // success fraction
         RealType alpha) // risk level
      {
        static const char* function = "boost::math::binomial_distribution<%1%>::find_minimum_number_of_trials";
        // Error checks:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           function, k, p, k, &result, Policy())
            &&
           binomial_detail::check_dist_and_prob(
           function, k, p, alpha, &result, Policy()))
        { return result; }

        result = ibetac_invb(k + 1, p, alpha, Policy());  // returns n - k
        return result + k;
      }

      static RealType find_maximum_number_of_trials(
         RealType k,     // number of events
         RealType p,     // success fraction
         RealType alpha) // risk level
      {
        static const char* function = "boost::math::binomial_distribution<%1%>::find_maximum_number_of_trials";
        // Error checks:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           function, k, p, k, &result, Policy())
            &&
           binomial_detail::check_dist_and_prob(
           function, k, p, alpha, &result, Policy()))
        { return result; }

        result = ibeta_invb(k + 1, p, alpha, Policy());  // returns n - k
        return result + k;
      }

    private:
        RealType m_n; // Not sure if this shouldn't be an int?
        RealType m_p; // success_fraction
      }; // template <class RealType, class Policy> class binomial_distribution

      typedef binomial_distribution<> binomial;
      // typedef binomial_distribution<double> binomial;
      // IS now included since no longer a name clash with function binomial.
      //typedef binomial_distribution<double> binomial; // Reserved name of type double.

      template <class RealType, class Policy>
      const std::pair<RealType, RealType> range(const binomial_distribution<RealType, Policy>& dist)
      { // Range of permissible values for random variable k.
        using boost::math::tools::max_value;
        return std::pair<RealType, RealType>(static_cast<RealType>(0), dist.trials());
      }

      template <class RealType, class Policy>
      const std::pair<RealType, RealType> support(const binomial_distribution<RealType, Policy>& dist)
      { // Range of supported values for random variable k.
        // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
        return std::pair<RealType, RealType>(0,  dist.trials());
      }

      template <class RealType, class Policy>
      inline RealType mean(const binomial_distribution<RealType, Policy>& dist)
      { // Mean of Binomial distribution = np.
        return  dist.trials() * dist.success_fraction();
      } // mean

      template <class RealType, class Policy>
      inline RealType variance(const binomial_distribution<RealType, Policy>& dist)
      { // Variance of Binomial distribution = np(1-p).
        return  dist.trials() * dist.success_fraction() * (1 - dist.success_fraction());
      } // variance

      template <class RealType, class Policy>
      RealType pdf(const binomial_distribution<RealType, Policy>& dist, const RealType& k)
      { // Probability Density/Mass Function.
        BOOST_FPU_EXCEPTION_GUARD

        BOOST_MATH_STD_USING // for ADL of std functions

        RealType n = dist.trials();

        // Error check:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           "boost::math::pdf(binomial_distribution<%1%> const&, %1%)",
           n,
           dist.success_fraction(),
           k,
           &result, Policy()))
        {
           return result;
        }

        // Special cases of success_fraction, regardless of k successes and regardless of n trials.
        if (dist.success_fraction() == 0)
        {  // probability of zero successes is 1:
           return static_cast<RealType>(k == 0 ? 1 : 0);
        }
        if (dist.success_fraction() == 1)
        {  // probability of n successes is 1:
           return static_cast<RealType>(k == n ? 1 : 0);
        }
        // k argument may be integral, signed, or unsigned, or floating point.
        // If necessary, it has already been promoted from an integral type.
        if (n == 0)
        {
          return 1; // Probability = 1 = certainty.
        }
        if (k == 0)
        { // binomial coeffic (n 0) = 1,
          // n ^ 0 = 1
          return pow(1 - dist.success_fraction(), n);
        }
        if (k == n)
        { // binomial coeffic (n n) = 1,
          // n ^ 0 = 1
          return pow(dist.success_fraction(), k);  // * pow((1 - dist.success_fraction()), (n - k)) = 1
        }

        // Probability of getting exactly k successes
        // if C(n, k) is the binomial coefficient then:
        //
        // f(k; n,p) = C(n, k) * p^k * (1-p)^(n-k)
        //           = (n!/(k!(n-k)!)) * p^k * (1-p)^(n-k)
        //           = (tgamma(n+1) / (tgamma(k+1)*tgamma(n-k+1))) * p^k * (1-p)^(n-k)
        //           = p^k (1-p)^(n-k) / (beta(k+1, n-k+1) * (n+1))
        //           = ibeta_derivative(k+1, n-k+1, p) / (n+1)
        //
        using boost::math::ibeta_derivative; // a, b, x
        return ibeta_derivative(k+1, n-k+1, dist.success_fraction(), Policy()) / (n+1);

      } // pdf

      template <class RealType, class Policy>
      inline RealType cdf(const binomial_distribution<RealType, Policy>& dist, const RealType& k)
      { // Cumulative Distribution Function Binomial.
        // The random variate k is the number of successes in n trials.
        // k argument may be integral, signed, or unsigned, or floating point.
        // If necessary, it has already been promoted from an integral type.

        // Returns the sum of the terms 0 through k of the Binomial Probability Density/Mass:
        //
        //   i=k
        //   --  ( n )   i      n-i
        //   >   |   |  p  (1-p)
        //   --  ( i )
        //   i=0

        // The terms are not summed directly instead
        // the incomplete beta integral is employed,
        // according to the formula:
        // P = I[1-p]( n-k, k+1).
        //   = 1 - I[p](k + 1, n - k)

        BOOST_MATH_STD_USING // for ADL of std functions

        RealType n = dist.trials();
        RealType p = dist.success_fraction();

        // Error check:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           "boost::math::cdf(binomial_distribution<%1%> const&, %1%)",
           n,
           p,
           k,
           &result, Policy()))
        {
           return result;
        }
        if (k == n)
        {
          return 1;
        }

        // Special cases, regardless of k.
        if (p == 0)
        {  // This need explanation:
           // the pdf is zero for all cases except when k == 0.
           // For zero p the probability of zero successes is one.
           // Therefore the cdf is always 1:
           // the probability of k or *fewer* successes is always 1
           // if there are never any successes!
           return 1;
        }
        if (p == 1)
        { // This is correct but needs explanation:
          // when k = 1
          // all the cdf and pdf values are zero *except* when k == n,
          // and that case has been handled above already.
          return 0;
        }
        //
        // P = I[1-p](n - k, k + 1)
        //   = 1 - I[p](k + 1, n - k)
        // Use of ibetac here prevents cancellation errors in calculating
        // 1-p if p is very small, perhaps smaller than machine epsilon.
        //
        // Note that we do not use a finite sum here, since the incomplete
        // beta uses a finite sum internally for integer arguments, so
        // we'll just let it take care of the necessary logic.
        //
        return ibetac(k + 1, n - k, p, Policy());
      } // binomial cdf

      template <class RealType, class Policy>
      inline RealType cdf(const complemented2_type<binomial_distribution<RealType, Policy>, RealType>& c)
      { // Complemented Cumulative Distribution Function Binomial.
        // The random variate k is the number of successes in n trials.
        // k argument may be integral, signed, or unsigned, or floating point.
        // If necessary, it has already been promoted from an integral type.

        // Returns the sum of the terms k+1 through n of the Binomial Probability Density/Mass:
        //
        //   i=n
        //   --  ( n )   i      n-i
        //   >   |   |  p  (1-p)
        //   --  ( i )
        //   i=k+1

        // The terms are not summed directly instead
        // the incomplete beta integral is employed,
        // according to the formula:
        // Q = 1 -I[1-p]( n-k, k+1).
        //   = I[p](k + 1, n - k)

        BOOST_MATH_STD_USING // for ADL of std functions

        RealType const& k = c.param;
        binomial_distribution<RealType, Policy> const& dist = c.dist;
        RealType n = dist.trials();
        RealType p = dist.success_fraction();

        // Error checks:
        RealType result;
        if(false == binomial_detail::check_dist_and_k(
           "boost::math::cdf(binomial_distribution<%1%> const&, %1%)",
           n,
           p,
           k,
           &result, Policy()))
        {
           return result;
        }

        if (k == n)
        { // Probability of greater than n successes is necessarily zero:
          return 0;
        }

        // Special cases, regardless of k.
        if (p == 0)
        {
           // This need explanation: the pdf is zero for all
           // cases except when k == 0.  For zero p the probability
           // of zero successes is one.  Therefore the cdf is always
           // 1: the probability of *more than* k successes is always 0
           // if there are never any successes!
           return 0;
        }
        if (p == 1)
        {
          // This needs explanation, when p = 1
          // we always have n successes, so the probability
          // of more than k successes is 1 as long as k < n.
          // The k == n case has already been handled above.
          return 1;
        }
        //
        // Calculate cdf binomial using the incomplete beta function.
        // Q = 1 -I[1-p](n - k, k + 1)
        //   = I[p](k + 1, n - k)
        // Use of ibeta here prevents cancellation errors in calculating
        // 1-p if p is very small, perhaps smaller than machine epsilon.
        //
        // Note that we do not use a finite sum here, since the incomplete
        // beta uses a finite sum internally for integer arguments, so
        // we'll just let it take care of the necessary logic.
        //
        return ibeta(k + 1, n - k, p, Policy());
      } // binomial cdf

      template <class RealType, class Policy>
      inline RealType quantile(const binomial_distribution<RealType, Policy>& dist, const RealType& p)
      {
         return binomial_detail::quantile_imp(dist, p, RealType(1-p));
      } // quantile

      template <class RealType, class Policy>
      RealType quantile(const complemented2_type<binomial_distribution<RealType, Policy>, RealType>& c)
      {
         return binomial_detail::quantile_imp(c.dist, RealType(1-c.param), c.param);
      } // quantile

      template <class RealType, class Policy>
      inline RealType mode(const binomial_distribution<RealType, Policy>& dist)
      {
         BOOST_MATH_STD_USING // ADL of std functions.
         RealType p = dist.success_fraction();
         RealType n = dist.trials();
         return floor(p * (n + 1));
      }

      template <class RealType, class Policy>
      inline RealType median(const binomial_distribution<RealType, Policy>& dist)
      { // Bounds for the median of the negative binomial distribution
        // VAN DE VEN R. ; WEBER N. C. ;
        // Univ. Sydney, school mathematics statistics, Sydney N.S.W. 2006, AUSTRALIE
        // Metrika  (Metrika)  ISSN 0026-1335   CODEN MTRKA8
        // 1993, vol. 40, no3-4, pp. 185-189 (4 ref.)

        // Bounds for median and 50 percetage point of binomial and negative binomial distribution
        // Metrika, ISSN   0026-1335 (Print) 1435-926X (Online)
        // Volume 41, Number 1 / December, 1994, DOI   10.1007/BF01895303
         BOOST_MATH_STD_USING // ADL of std functions.
         RealType p = dist.success_fraction();
         RealType n = dist.trials();
         // Wikipedia says one of floor(np) -1, floor (np), floor(np) +1
         return floor(p * n); // Chose the middle value.
      }

      template <class RealType, class Policy>
      inline RealType skewness(const binomial_distribution<RealType, Policy>& dist)
      {
         BOOST_MATH_STD_USING // ADL of std functions.
         RealType p = dist.success_fraction();
         RealType n = dist.trials();
         return (1 - 2 * p) / sqrt(n * p * (1 - p));
      }

      template <class RealType, class Policy>
      inline RealType kurtosis(const binomial_distribution<RealType, Policy>& dist)
      {
         RealType p = dist.success_fraction();
         RealType n = dist.trials();
         return 3 - 6 / n + 1 / (n * p * (1 - p));
      }

      template <class RealType, class Policy>
      inline RealType kurtosis_excess(const binomial_distribution<RealType, Policy>& dist)
      {
         RealType p = dist.success_fraction();
         RealType q = 1 - p;
         RealType n = dist.trials();
         return (1 - 6 * p * q) / (n * p * q);
      }

    } // namespace math
  } // namespace boost

// This include must be at the end, *after* the accessors
// for this distribution have been defined, in order to
// keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_MATH_SPECIAL_BINOMIAL_HPP


#include <fstream>
#include <boost/config.hpp>
#include <boost/version.hpp>

// CGAL headers
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>

#include <CGAL/point_generators_2.h>

// Qt headers
#include <QtGui>
#include <QString>
#include <QActionGroup>
#include <QFileDialog>
#include <QInputDialog>

// GraphicsView items and event filters (input classes)
#include "TriangulationCircumcircle.h"
#include "TriangulationMovingPoint.h"
#include "TriangulationConflictZone.h"
#include "TriangulationRemoveVertex.h"
#include "TriangulationPointInputAndConflictZone.h"
#include <CGAL/Qt/TriangulationGraphicsItem.h>
#include <CGAL/Qt/VoronoiGraphicsItem.h>
#if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
#include <CGAL/IO/WKT.h>
#endif

// for viewportsBbox
#include <CGAL/Qt/utility.h>

// the two base classes
#include "ui_Delaunay_triangulation_2.h"
#include <CGAL/Qt/DemosMainWindow.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef K::Iso_rectangle_2 Iso_rectangle_2;

typedef CGAL::Delaunay_triangulation_2<K> Delaunay;

class MainWindow :
  public CGAL::Qt::DemosMainWindow,
  public Ui::Delaunay_triangulation_2
{
  Q_OBJECT

private:
  Delaunay dt;
  QGraphicsScene scene;

  CGAL::Qt::TriangulationGraphicsItem<Delaunay> * dgi;
  CGAL::Qt::VoronoiGraphicsItem<Delaunay> * vgi;

  CGAL::Qt::TriangulationMovingPoint<Delaunay> * mp;
  CGAL::Qt::TriangulationConflictZone<Delaunay> * cz;
  CGAL::Qt::TriangulationRemoveVertex<Delaunay> * trv;
  CGAL::Qt::TriangulationPointInputAndConflictZone<Delaunay> * pi;
  CGAL::Qt::TriangulationCircumcircle<Delaunay> *tcc;
public:
  MainWindow();

public Q_SLOTS:

  void processInput(CGAL::Object o);

  void on_actionMovingPoint_toggled(bool checked);

  void on_actionShowConflictZone_toggled(bool checked);

  void on_actionCircumcenter_toggled(bool checked);

  void on_actionShowDelaunay_toggled(bool checked);

  void on_actionShowVoronoi_toggled(bool checked);

  void on_actionInsertPoint_toggled(bool checked);

  void on_actionInsertRandomPoints_triggered();

  void on_actionLoadPoints_triggered();

  void on_actionSavePoints_triggered();

  void on_actionClear_triggered();

  void on_actionRecenter_triggered();

  virtual void open(QString fileName);

Q_SIGNALS:
  void changed();
};

MainWindow::MainWindow()
  : DemosMainWindow()
{
  setupUi(this);

  this->graphicsView->setAcceptDrops(false);

  // Add a GraphicItem for the Delaunay triangulation
  dgi = new CGAL::Qt::TriangulationGraphicsItem<Delaunay>(&dt);

  QObject::connect(this, SIGNAL(changed()),
                   dgi, SLOT(modelChanged()));

  dgi->setVerticesPen(QPen(Qt::red, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  scene.addItem(dgi);

  // Add a GraphicItem for the Voronoi diagram
  vgi = new CGAL::Qt::VoronoiGraphicsItem<Delaunay>(&dt);

  QObject::connect(this, SIGNAL(changed()),
                   vgi, SLOT(modelChanged()));

  vgi->setEdgesPen(QPen(Qt::blue, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  scene.addItem(vgi);
  vgi->hide();

  // Setup input handlers. They get events before the scene gets them
  // and the input they generate is passed to the triangulation with
  // the signal/slot mechanism
  pi = new CGAL::Qt::TriangulationPointInputAndConflictZone<Delaunay>(&scene, &dt, this );

  QObject::connect(pi, SIGNAL(generate(CGAL::Object)),
                   this, SLOT(processInput(CGAL::Object)));

  mp = new CGAL::Qt::TriangulationMovingPoint<Delaunay>(&dt, this);
  // TriangulationMovingPoint<Delaunay> emits a modelChanged() signal each
  // time the moving point moves.
  // The following connection is for the purpose of emitting changed().
  QObject::connect(mp, SIGNAL(modelChanged()),
                   this, SIGNAL(changed()));

  trv = new CGAL::Qt::TriangulationRemoveVertex<Delaunay>(&dt, this);
  QObject::connect(trv, SIGNAL(modelChanged()),
                   this, SIGNAL(changed()));

  tcc = new CGAL::Qt::TriangulationCircumcircle<Delaunay>(&scene, &dt, this);
  tcc->setPen(QPen(Qt::red, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));

  cz = new CGAL::Qt::TriangulationConflictZone<Delaunay>(&scene, &dt, this);

  //
  // Manual handling of actions
  //

  QObject::connect(this->actionQuit, SIGNAL(triggered()),
                   this, SLOT(close()));

  // We put mutually exclusive actions in an QActionGroup
  QActionGroup* ag = new QActionGroup(this);
  ag->addAction(this->actionInsertPoint);
  ag->addAction(this->actionMovingPoint);
  ag->addAction(this->actionCircumcenter);
  ag->addAction(this->actionShowConflictZone);

  // Check two actions
  this->actionInsertPoint->setChecked(true);
  this->actionShowDelaunay->setChecked(true);

  //
  // Setup the scene and the view
  //
  scene.setItemIndexMethod(QGraphicsScene::NoIndex);
  scene.setSceneRect(-100, -100, 100, 100);
  this->graphicsView->setScene(&scene);
  this->graphicsView->setMouseTracking(true);

  // Turn the vertical axis upside down
  this->graphicsView->transform().scale(1, -1);

  // The navigation adds zooming and translation functionality to the
  // QGraphicsView
  this->addNavigation(this->graphicsView);

  this->setupStatusBar();
  this->setupOptionsMenu();
  this->addAboutDemo(":/cgal/help/about_Delaunay_triangulation_2.html");
  this->addAboutCGAL();
  this->setupExportSVG(actionExport_SVG, graphicsView);

  this->addRecentFiles(this->menuFile, this->actionQuit);
  connect(this, SIGNAL(openRecentFile(QString)),
          this, SLOT(open(QString)));
}

void
MainWindow::processInput(CGAL::Object o)
{
  Point_2 p;
  if(CGAL::assign(p, o)){
    dt.insert(p);
  }
  Q_EMIT( changed());
}

/*
 *  Qt Automatic Connections
 *  https://doc.qt.io/qt-5/designer-using-a-ui-file.html#automatic-connections
 *
 *  setupUi(this) generates connections to the slots named
 *  "on_<action_name>_<signal_name>"
 */
void
MainWindow::on_actionInsertPoint_toggled(bool checked)
{
  if(checked){
    scene.installEventFilter(pi);
    scene.installEventFilter(trv);
  } else {
    scene.removeEventFilter(pi);
    scene.removeEventFilter(trv);
  }
}

void
MainWindow::on_actionMovingPoint_toggled(bool checked)
{

  if(checked){
    scene.installEventFilter(mp);
  } else {
    scene.removeEventFilter(mp);
  }
}

void
MainWindow::on_actionShowConflictZone_toggled(bool checked)
{

  if(checked){
    scene.installEventFilter(cz);
  } else {
    scene.removeEventFilter(cz);
  }
}

void
MainWindow::on_actionCircumcenter_toggled(bool checked)
{
  if(checked){
    scene.installEventFilter(tcc);
    tcc->show();
  } else {
    scene.removeEventFilter(tcc);
    tcc->hide();
  }
}

void
MainWindow::on_actionShowDelaunay_toggled(bool checked)
{
  dgi->setVisibleEdges(checked);
}

void
MainWindow::on_actionShowVoronoi_toggled(bool checked)
{
  vgi->setVisible(checked);
}

void
MainWindow::on_actionClear_triggered()
{
  dt.clear();
  Q_EMIT( changed());
}

void
MainWindow::on_actionInsertRandomPoints_triggered()
{
  QRectF rect = CGAL::Qt::viewportsBbox(&scene);
  CGAL::Qt::Converter<K> convert;
  Iso_rectangle_2 isor = convert(rect);
  CGAL::Random_points_in_iso_rectangle_2<Point_2> pg((isor.min)(), (isor.max)());
  bool ok = false;

  const int number_of_points =
    QInputDialog::getInt(this,
                             tr("Number of random points"),
                             tr("Enter number of random points"),
                             100,
                             0,
                             (std::numeric_limits<int>::max)(),
                             1,
                             &ok);

  if(!ok) {
    return;
  }

  // wait cursor
  QApplication::setOverrideCursor(Qt::WaitCursor);
  std::vector<Point_2> points;
  points.reserve(number_of_points);
  for(int i = 0; i < number_of_points; ++i){
    points.push_back(*pg++);
  }
  dt.insert(points.begin(), points.end());
  // default cursor
  QApplication::restoreOverrideCursor();
  Q_EMIT( changed());
}

void
MainWindow::on_actionLoadPoints_triggered()
{
  QString fileName = QFileDialog::getOpenFileName(this,
                                                  tr("Open Points file"),
                                                  ".",
                                                  tr("CGAL files (*.pts.cgal);;"
                                                   #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
                                                     "WKT files (*.WKT *.wkt);;"
                                                   #endif
                                                     "All files (*)"));
  if(! fileName.isEmpty()){
    open(fileName);
  }
}

void
MainWindow::open(QString fileName)
{
  // wait cursor
  QApplication::setOverrideCursor(Qt::WaitCursor);
  std::ifstream ifs(qPrintable(fileName));

  K::Point_2 p;
  std::vector<K::Point_2> points;
  if(fileName.endsWith(".wkt", Qt::CaseInsensitive))
  {
#if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
    CGAL::IO::read_multi_point_WKT(ifs, points);
#endif
  }
  else
    while(ifs >> p) {
      // ignore whatever comes after x and y
      ifs.ignore((std::numeric_limits<std::streamsize>::max)(), '\n');
      points.push_back(p);
    }
  dt.insert(points.begin(), points.end());

  // default cursor
  QApplication::restoreOverrideCursor();
  this->addToRecentFiles(fileName);
  actionRecenter->trigger();
  Q_EMIT( changed());

}

void
MainWindow::on_actionSavePoints_triggered()
{
  QString fileName = QFileDialog::getSaveFileName(this,
                                                  tr("Save points"),
                                                  ".",
                                                  tr("CGAL files (*.pts.cgal);;"
                                                   #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
                                                     "WKT files (*.WKT *.wkt);;"
                                                   #endif
                                                     "All files (*)"));
  if(! fileName.isEmpty()){
    std::ofstream ofs(qPrintable(fileName));
    if(fileName.endsWith(".wkt", Qt::CaseInsensitive))
    {
#if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
      std::vector<K::Point_2> points;
      points.reserve(dt.number_of_vertices());
      for(Delaunay::Finite_vertices_iterator
          vit = dt.finite_vertices_begin(),
          end = dt.finite_vertices_end();
          vit!= end; ++vit)
      {
        points.push_back(vit->point());
      }
      CGAL::IO::write_multi_point_WKT(ofs, points);
#endif
    }
    else
      for(Delaunay::Finite_vertices_iterator
          vit = dt.finite_vertices_begin(),
          end = dt.finite_vertices_end();
          vit!= end; ++vit)
      {
        ofs << vit->point() << std::endl;
      }
  }
}

void
MainWindow::on_actionRecenter_triggered()
{
  this->graphicsView->setSceneRect(dgi->boundingRect());
  this->graphicsView->fitInView(dgi->boundingRect(), Qt::KeepAspectRatio);
}

#include "Delaunay_triangulation_2.moc"
#include <CGAL/Qt/resources.h>

int main(int argc, char **argv)
{
  QApplication app(argc, argv);

  app.setOrganizationDomain("geometryfactory.com");
  app.setOrganizationName("GeometryFactory");
  app.setApplicationName("Delaunay_triangulation_2 demo");

  // Import resources from libCGAL (QT5).
  CGAL_QT_INIT_RESOURCES;

  MainWindow mainWindow;
  mainWindow.show();

  QStringList args = app.arguments();
  args.removeAt(0);
  Q_FOREACH(QString filename, args) {
    mainWindow.open(filename);
  }

  return app.exec();
}


#include "gru_cell.hpp"
#include "generic/activity.hpp"
#include "generic/utils.hpp"
#include <Eigen/SVD>

namespace rnn {
gru_cell::gru_cell(const int inputDim, const int hiddenDim) {
  this->Wxr = MatD(hiddenDim, inputDim);
  this->Whr = MatD(hiddenDim, hiddenDim);
  this->br = VecD::Zero(hiddenDim);

  this->Wxz = MatD(hiddenDim, inputDim);
  this->Whz = MatD(hiddenDim, hiddenDim);
  this->bz = VecD::Zero(hiddenDim);

  this->Wxu = MatD(hiddenDim, inputDim);
  this->Whu = MatD(hiddenDim, hiddenDim);
  this->bu = VecD::Zero(hiddenDim);
}

void gru_cell::init(rnn::generic::rand &rnd, const real scale) {
  rnd.uniform(this->Wxr, scale);
  rnd.uniform(this->Whr, scale);

  rnd.uniform(this->Wxz, scale);
  rnd.uniform(this->Whz, scale);

  rnd.uniform(this->Wxu, scale);
  rnd.uniform(this->Whu, scale);

  this->Whr = Eigen::JacobiSVD<MatD>(this->Whr, Eigen::ComputeFullV |
      Eigen::ComputeFullU).matrixU();
  this->Whz = Eigen::JacobiSVD<MatD>(this->Whz, Eigen::ComputeFullV |
      Eigen::ComputeFullU).matrixU();
  this->Whu = Eigen::JacobiSVD<MatD>(this->Whu, Eigen::ComputeFullV |
      Eigen::ComputeFullU).matrixU();
}
void gru_cell::forward(const VecD &xt, const gru_cell::State *prev, gru_cell::State *cur) {

  cur->r = this->br + this->Wxr * xt + this->Whr * prev->h;
  cur->z = this->bz + this->Wxz * xt + this->Whz * prev->h;

  activity::logistic(cur->r);
  activity::logistic(cur->z);

  cur->rh = cur->r.array() * prev->h.array();
  cur->u = this->bu + this->Wxu * xt + this->Whu * cur->rh;
  activity::tanh(cur->u);
  cur->h = (1.0 - cur->z.array()) * prev->h.array() +
      cur->z.array() * cur->u.array();
}

void gru_cell::backward(gru_cell::State *prev, gru_cell::State *cur, gru_cell::Grad &grad,
                        const VecD &xt) {
  VecD delr, delz, delu, delrh;

  delz = activity::logisticPrime(cur->z).array() * cur->delh.array() *
      (cur->u - prev->h).array();
  delu =
      activity::tanhPrime(cur->u).array() * cur->delh.array() * cur->z.array();
  delrh = this->Whu.transpose() * delu;
  delr =
      activity::logisticPrime(cur->r).array() * delrh.array() * prev->h.array();

  cur->delx =
      this->Wxr.transpose() * delr +
          this->Wxz.transpose() * delz +
          this->Wxu.transpose() * delu;

  prev->delh.noalias() +=
      this->Whr.transpose() * delr +
          this->Whz.transpose() * delz;
  prev->delh.array() +=
      delrh.array() * cur->r.array() +
          cur->delh.array() * (1.0 - cur->z.array());

  grad.Wxr.noalias() += delr * xt.transpose();
  grad.Whr.noalias() += delr * prev->h.transpose();

  grad.Wxz.noalias() += delz * xt.transpose();
  grad.Whz.noalias() += delz * prev->h.transpose();

  grad.Wxu.noalias() += delu * xt.transpose();
  grad.Whu.noalias() += delu * cur->rh.transpose();

  grad.br += delr;
  grad.bz += delz;
  grad.bu += delu;
}

void gru_cell::sgd(const gru_cell::Grad &grad, const real learningRate) {
  this->Wxr -= learningRate * grad.Wxr;
  this->Whr -= learningRate * grad.Whr;
  this->br -= learningRate * grad.br;

  this->Wxz -= learningRate * grad.Wxz;
  this->Whz -= learningRate * grad.Whz;
  this->bz -= learningRate * grad.bz;

  this->Wxu -= learningRate * grad.Wxu;
  this->Whu -= learningRate * grad.Whu;
  this->bu -= learningRate * grad.bu;
}

void gru_cell::save(std::ofstream &ofs) {
  rnn::generic::save(ofs, this->Wxr);
  rnn::generic::save(ofs, this->Whr);
  rnn::generic::save(ofs, this->br);
  rnn::generic::save(ofs, this->Wxz);
  rnn::generic::save(ofs, this->Whz);
  rnn::generic::save(ofs, this->bz);
  rnn::generic::save(ofs, this->Wxu);
  rnn::generic::save(ofs, this->Whu);
  rnn::generic::save(ofs, this->bu);
}

void gru_cell::load(std::ifstream &ifs) {
  rnn::generic::load(ifs, this->Wxr);
  rnn::generic::load(ifs, this->Whr);
  rnn::generic::load(ifs, this->br);
  rnn::generic::load(ifs, this->Wxz);
  rnn::generic::load(ifs, this->Whz);
  rnn::generic::load(ifs, this->bz);
  rnn::generic::load(ifs, this->Wxu);
  rnn::generic::load(ifs, this->Whu);
  rnn::generic::load(ifs, this->bu);
}

void gru_cell::State::clear() {
  this->h = VecD();
  this->u = VecD();
  this->r = VecD();
  this->z = VecD();
  this->rh = VecD();
  this->delh = VecD();
  this->delx = VecD();
}

gru_cell::Grad::Grad(const gru_cell &gru) {
  this->Wxr = MatD::Zero(gru.Wxr.rows(), gru.Wxr.cols());
  this->Whr = MatD::Zero(gru.Whr.rows(), gru.Whr.cols());
  this->br = VecD::Zero(gru.br.rows());

  this->Wxz = MatD::Zero(gru.Wxz.rows(), gru.Wxz.cols());
  this->Whz = MatD::Zero(gru.Whz.rows(), gru.Whz.cols());
  this->bz = VecD::Zero(gru.bz.rows());

  this->Wxu = MatD::Zero(gru.Wxu.rows(), gru.Wxu.cols());
  this->Whu = MatD::Zero(gru.Whu.rows(), gru.Whu.cols());
  this->bu = VecD::Zero(gru.bu.rows());
};

void gru_cell::Grad::init() {
  this->Wxr.setZero();
  this->Whr.setZero();
  this->br.setZero();
  this->Wxz.setZero();
  this->Whz.setZero();
  this->bz.setZero();
  this->Wxu.setZero();
  this->Whu.setZero();
  this->bu.setZero();
}

real gru_cell::Grad::norm() {
  return
      this->Wxr.squaredNorm() + this->Whr.squaredNorm() +
          this->br.squaredNorm() +
          this->Wxz.squaredNorm() + this->Whz.squaredNorm() +
          this->bz.squaredNorm() +
          this->Wxu.squaredNorm() + this->Whu.squaredNorm() +
          this->bu.squaredNorm();
}

void gru_cell::Grad::operator+=(const gru_cell::Grad &grad) {
  this->Wxr += grad.Wxr;
  this->Whr += grad.Whr;
  this->br += grad.br;
  this->Wxz += grad.Wxz;
  this->Whz += grad.Whz;
  this->bz += grad.bz;
  this->Wxu += grad.Wxu;
  this->Whu += grad.Whu;
  this->bu += grad.bu;
}
}


#ifndef APP_ND_GRID_SIMPLEX
#define APP_ND_GRID_SIMPLEX

#include "Point.hpp"
#include "Triangulator.hpp"

#include <stdlib.h>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/lu.hpp>

class Simplex {
public:
    Triangulator& triangulator;
    unsigned int num_dimensions;
    std::vector<Point> points;
    std::vector<Point> lines;

    Simplex(unsigned int num_dims, std::vector<std::vector<double>>& _points, Triangulator& _triangulator):
    num_dimensions(num_dims),
    points(_points.size()),
    lines(0),
    triangulator(_triangulator) {
        for (unsigned int i=0; i<_points.size(); i++) {
            points[i] = Point(_points[i]);
        }

        lines = generateLines();
    }

    Simplex(unsigned int num_dims, std::vector<Point> _points, Triangulator& _triangulator):
    num_dimensions(num_dims),
    points(_points),
    lines(0),
    triangulator(_triangulator) {

        lines = generateLines();
    }

    Simplex(const Simplex& other) :
    num_dimensions(other.num_dimensions),
    triangulator(other.triangulator) {
        points = std::vector<Point>(other.points.size());
        for(unsigned int i=0; i<other.points.size(); i++) {
            points[i] = other.points[i];
        }

        lines = std::vector<Point>(other.lines.size());
        for(unsigned int i=0; i<other.lines.size(); i++) {
            lines[i] = other.lines[i];
        }
    }

    Simplex& operator=(const Simplex &other) {
        num_dimensions = other.num_dimensions;
        triangulator = other.triangulator;
        points = std::vector<Point>(other.points.size());
        for(unsigned int i=0; i<other.points.size(); i++) {
            points[i] = other.points[i];
        }

        lines = std::vector<Point>(other.lines.size());
        for(unsigned int i=0; i<other.lines.size(); i++) {
            lines[i] = other.lines[i];
        }

        return *this;
    }

    std::vector<Point> generateLines() {
        std::vector<Point> lines(num_dimensions);
        for(unsigned int p=0; p<points.size()-1; p++) {
            std::vector<double> coords(num_dimensions);
            for (unsigned int c=0; c<num_dimensions; c++)
                coords[c] = points[p+1].coords[c] - points[0].coords[c];
            lines[p] = Point(coords);
        }
        return lines;
    }

    // CalcDeterminant by Richel Bilderbeek : http://www.richelbilderbeek.nl/CppUblasMatrixExample7.htm
    double CalcDeterminant(boost::numeric::ublas::matrix<double> m)
    {
        assert(m.size1() == m.size2() && "Can only calculate the determinant of square matrices");
        boost::numeric::ublas::permutation_matrix<std::size_t> pivots(m.size1() );

        const int is_singular = boost::numeric::ublas::lu_factorize(m, pivots);

        if (is_singular) return 0.0;

        double d = 1.0;
        const std::size_t sz = pivots.size();
        for (std::size_t i=0; i != sz; ++i)
        {
            if (pivots(i) != i)
            {
            d *= -1.0;
            }
            d *= m(i,i);
        }
        return d;
    }

    double getVolume() {
        boost::numeric::ublas::matrix<double> m(num_dimensions,num_dimensions);
        for (unsigned int l=0; l<num_dimensions; l++) {
            for(unsigned int c=0; c<num_dimensions; c++) {
                m(l,c) = lines[l].coords[c];
            }
        }

        unsigned int dim_fac = 0;
        for(unsigned int n=0; n<num_dimensions; n++)
            dim_fac += n;

        return std::abs(CalcDeterminant(m)/dim_fac)/2;
    }

    std::vector<std::vector<Simplex>> intersectWithHyperplane(unsigned int dim_index, double dim) {
        double eps = 0.00000000001;

        std::vector<Point*> lower;
        std::vector<Point*> upper;
        std::vector<Point*> equal;
        for (unsigned int i=0; i<points.size(); i++) {
            if(points[i].coords[dim_index] < dim - eps) lower.push_back(&points[i]);
            else if(points[i].coords[dim_index] > dim + eps) upper.push_back(&points[i]);
            else equal.push_back(&points[i]);
        }

        std::vector<Point> p_outs;
        for (Point* p0 : lower){
            for (Point* p1 : upper) {
                double t = (dim - p0->coords[dim_index]) / (p1->coords[dim_index] - p0->coords[dim_index]);
                std::vector<double> coords(num_dimensions);
                for (unsigned int i=0; i<num_dimensions; i++){
                    coords[i] = p0->coords[i] + ((p1->coords[i] - p0->coords[i])*t);
                }
                Point np(coords);
                np.hyper = true;
                p_outs.push_back(np);
            }
        }

        if (p_outs.size() == 0) {
            std::vector<std::vector<Simplex>> out;
            bool points_above = true;
            for(Point p : points)
                points_above &= p.coords[dim_index] >= dim - eps;

            std::vector<Simplex> less;
            std::vector<Simplex> greater;

            if (!points_above){
                less.push_back(Simplex(num_dimensions, points, triangulator));
                out.push_back(less);
                out.push_back(std::vector<Simplex>());
            } else {
                greater.push_back(Simplex(num_dimensions, points, triangulator));
                out.push_back(std::vector<Simplex>());
                out.push_back(greater);
            }
            return out;
        }

        unsigned int index = 0;
        std::vector<unsigned int> i_less(lower.size());
        for (unsigned int i=0; i<lower.size(); i++) {
            i_less[i] = i + index;
        }
        index += lower.size();

        std::vector<unsigned int> i_greater(upper.size());
        for (unsigned int i=0; i<upper.size(); i++) {
            i_greater[i] = i + index;
        }
        index += upper.size();

        std::vector<unsigned int> i_hyp(p_outs.size());
        for (unsigned int i=0; i<p_outs.size(); i++) {
            i_hyp[i] = i + index;
        }
        index += p_outs.size();

        for (unsigned int i=0; i<equal.size(); i++) i_hyp.push_back(i + index);

        std::vector<Point> p_total(lower.size()+upper.size()+p_outs.size()+equal.size());
        for(unsigned int i=0; i<lower.size(); i++){
            p_total[i] = *(lower[i]);
        }
        for(unsigned int i=0; i<upper.size(); i++){
            p_total[lower.size()+i] = *(upper[i]);
        }
        for(unsigned int i=0; i<p_outs.size(); i++){
            p_total[lower.size()+upper.size()+i] = p_outs[i];
        }
        for(unsigned int i=0; i<equal.size(); i++){
            p_total[lower.size()+upper.size()+p_outs.size()+i] = *(equal[i]);
        }

        std::vector<Simplex> simplices = triangulator.chooseTriangulation(num_dimensions, p_total, i_less, i_greater, i_hyp);

        std::vector<Simplex> less;
        std::vector<Simplex> greater;
        for (Simplex s : simplices){
            bool all_above = true;
            bool all_below = true;
            for (Point p : s.points) {
                all_above &= p.coords[dim_index] >= dim-eps;
                all_below &= p.coords[dim_index] <= dim+eps;
            }

            if (all_above)
                greater.push_back(s);

            if (all_below)
                less.push_back(s);
        }

        std::vector<std::vector<Simplex>> out;
        out.push_back(less);
        out.push_back(greater);
        return out;
    }
};

#endif


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://svn.win.tue.nl/trac/MCRL2/browser/trunk/COPYING
//
// 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)
//
/// \file monotonicity_test.cpp
/// \brief Tests for the is_monotonous function for pbes expressions.

#include <iostream>
#include <string>

#include <boost/test/included/unit_test_framework.hpp>

#include "mcrl2/pbes/is_monotonous.h"
#include "mcrl2/pbes/parse.h"
#include "mcrl2/pbes/txt2pbes.h"

using namespace mcrl2;
using namespace mcrl2::pbes_system;

void run_monotonicity_test_case(const pbes_expression& x, bool expected_result)
{
  bool result = is_monotonous(x);
  if (result != expected_result)
  {
    std::cerr << "--- Failing monotonicity test case ---\n";
    std::cerr << " x = " << pbes_system::pp(x) << std::endl;
    std::cerr << " expected_result = " << std::boolalpha << expected_result << std::endl;
  }
  BOOST_CHECK(result == expected_result);
}

BOOST_AUTO_TEST_CASE(test_monotonicity)
{
  std::string text =
    "pbes                                                 \n"
    " nu X0(m, n: Nat) = val(n == m) && X0(m + 1, n + 1); \n"
    " nu X1            = !X1;                             \n"
    " nu X2            = !!X2;                            \n"
    " nu X3            = !X2 => X1;                       \n"
    " nu X4            = !(forall n:Nat . (X2 => !X1));   \n"
    " nu X5            = !(forall n:Nat . (X2 || !X1));   \n"
    "                                                     \n"
    " init X0(0, 0);                                      \n"
    ;
  bool normalize = false;
  pbes p = txt2pbes(text, normalize);
  std::vector<pbes_equation> eqn = p.equations();

  std::vector<bool> expected_results(eqn.size(), true);
  expected_results[0] = true;
  expected_results[1] = false;
  expected_results[2] = true;
  expected_results[3] = true;
  expected_results[4] = true;
  expected_results[5] = false;

  for (std::size_t i = 0; i < eqn.size(); ++i)
  {
    run_monotonicity_test_case(eqn[i].formula(), expected_results[i]);
  }
}

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[])
{
  return nullptr;
}


/**
 * \file boost/numeric/ublasx/operation/illcond.hpp
 *
 * \brief Check if a matrix is ill-conditioned.
 *
 * Copyright (c) 2012, Marco Guazzone
 *
 * 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)
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 */

#ifndef BOOST_NUMERIC_UBLASX_OPERATION_ILLCOND_HPP
#define BOOST_NUMERIC_UBLASX_OPERATION_ILLCOND_HPP

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublasx/operation/rcond.hpp>
#include <cmath>

namespace boost { namespace numeric { namespace ublasx {

using namespace ::boost::numeric::ublas;

template <typename MatrixExprT>
BOOST_UBLAS_INLINE
bool illcond(matrix_expression<MatrixExprT> const& A)
{
	double r = rcond(A);
	volatile double rp1 = r + 1.0;

	return (rp1 == 1.0) || ::std::isnan(r);
}

}}} // Namespace boost::numeric::ublasx

#endif // BOOST_NUMERIC_UBLASX_OPERATION_ILLCOND_HPP


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

namespace la3dm {
  class FeatureArray {
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;

    FeatureArray(int num_channel, bool need_normalizing=false):
      dimension (num_channel),
      need_normalizing(need_normalizing){

      feature.resize(num_channel);
      feature.setZero();
      for (int i = 0; i < num_channel; i++)
        feature(i) = 1.0 / num_channel;
      if (need_normalizing == false)
        feature = (feature * 255.0).eval();
      //feature.colwise() += 1.0 / num_channel ;
      counter = 1;

    };

    FeatureArray(const FeatureArray & other):
      dimension(other.dimension),
      counter(other.counter),
      need_normalizing(other.need_normalizing)
    {
      feature  = other.feature;

    }

    FeatureArray &operator=(const FeatureArray &other) {
      dimension = other.dimension;
      counter = other.counter;
      need_normalizing = other.need_normalizing;
      feature = other.feature;
      return *this;
    }

    int add_observation_by_averaging(const Eigen::VectorXf & f) {
      if (f.size() == feature.size()){
        Eigen::VectorXf to_add = f;
        if (need_normalizing && f.sum() != 0)
          to_add = (to_add / f.sum()).eval();
        feature = ((feature * counter + f)/(counter+1)).eval();
        counter++;
        if (need_normalizing && feature.sum() != 0)
          feature = (feature / feature.sum()).eval();
        return 0;
      } else {
        return -1;
      }

    }

    int get_counter() const {return counter;}

    int overwrite_feature(const Eigen::VectorXf & f) {
      if (f.size() != feature.size())
        return -1;

      feature = f;
      if (need_normalizing && feature.sum()!= 0 )
        feature = (feature / feature.sum()) .eval();
      return 0;
    }

    int dimension;
    Eigen::VectorXf get_feature()  const {return feature;}

  private:
    Eigen::VectorXf feature;
    int counter;
    bool need_normalizing;
  };

}


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

  Copyright 2016 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/ratio.hpp>
#include <scalar_test.hpp>

STF_CASE_TPL( "Check constant behavior for ratio", STF_NUMERIC_TYPES)
{
  using boost::simd::Ratio;

  STF_EXPR_IS   ( (Ratio<T,7,21>()  ) , T         );
  STF_IEEE_EQUAL( (Ratio<T,7,21>()  ) , T(7./21.) );

  STF_EXPR_IS   ( (Ratio<T,7>()  ) , T    );
  STF_IEEE_EQUAL( (Ratio<T,7>()  ) , T(7) );
}


//    Copyright 2017 Rainer Gemulla
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
/** \file
 *
 * Illustrates matrix factorization with Lee's GKL method. We first creates factors and then a data matrix
 * from these factors. THis process ensures that we know the best factorization of the input.
 * We then try to reconstruct the factors.
 */
#include <iostream>
#include <numeric>

#include <boost/math/distributions/normal.hpp>
#include <boost/random/uniform_real.hpp>

#include <util/evaluation.h>
#include <util/io.h>

#include <mpi2/mpi2.h>
#include <mf/mf.h>

using namespace std;
using namespace mf;
using namespace rg;
using namespace boost::numeric::ublas;

log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("main"));

int main(int argc, char* argv[]) {
	boost::mpi::communicator& world = mfInit(argc, argv);

	// parameters for the factorization
	mf_size_type size1 = 10000;
	mf_size_type size2 = 10000;
	mf_size_type nnz = 5000000;
	mf_size_type rank = 10;

	// parameters for distribution
	int tasksPerRank = 2;
	mf_size_type blocks = world.size() * tasksPerRank;

	// parameters for Lee01
	unsigned epochs = 20;

	// initialize mf library and mpi2
	mfStart();

	if (world.rank() == 0) {
#ifndef NDEBUG
	LOG4CXX_WARN(logger, "Warning: Debug mode activated (runtimes may be slow).");
#endif
		// generate original factors by sampling from a uniform[0,1] distribution
		Random32 random; // note: this takes a default seed (not randomized!)
		DenseMatrix wIn(size1, rank);
		DenseMatrixCM hIn(rank, size2);
		generateRandom(wIn, random,  boost::uniform_real<>(0, 1));
		generateRandom(hIn, random, boost::uniform_real<>(0, 1));
		// div2(wIn, sums2(wIn));
		// div1(hIn, sums1(hIn));

		// generate a sparse matrix by selecting random entries from the generated factors
		// and sample from a Poisson with mean equal to the entry
		// TODO: this generation process does not match the factorization model since we sample
		//       from the Poisson only at some entries of wh
		SparseMatrix v;
		generateRandom(v, nnz, wIn, hIn, random);
		applyPoisson(v, random);
		SparseMatrixCM vc;
		copyCm(v, vc);
		LOG4CXX_INFO(logger, "Data matrix: "
			<< v.size1() << " x " << v.size2() << ", " << v.nnz() << " nonzeros");
		LOG4CXX_INFO(logger, "Loss with original factors: " << gkl(v, wIn, hIn));

		// generate initial factors by sampling from a uniform[0,1] distribution
		DenseMatrix w(size1, rank);
		DenseMatrixCM h(rank, size2);
		generateRandom(w, random, boost::uniform_real<>(0, 1));
		generateRandom(h, random, boost::uniform_real<>(0, 1));
		div2(w, sums2(w));
		div1(h, sums1(h));
		double scaleFactor = sqrt(sum(v));
		mult(w, scaleFactor);
		mult(h, scaleFactor);

		// distribute the input matrices and test matrix
		DistributedSparseMatrix dv = distributeMatrix("V", blocks, 1, true, v);
		LOG4CXX_INFO(logger, "Distributed data matrix: "
				<< dv.blocks1() << " x " << dv.blocks2() << " blocks");
		DistributedSparseMatrixCM dvc = distributeMatrix("VC", 1, blocks, false, vc);
		LOG4CXX_INFO(logger, "Distributed data matrix (CM): "
				<< dvc.blocks1() << " x " << dvc.blocks2() << " blocks");
		DistributedDenseMatrix dw = distributeMatrix("W", blocks, 1, true, w);
		DistributedDenseMatrixCM dh = distributeMatrix("H", 1, blocks, false, h);
		LOG4CXX_INFO(logger, "Distributed factor matrices");

		// perform the factorization
		DapFactorizationData<> data(dv, dw, dh, tasksPerRank, &dvc);
		Trace trace;
		dlee01Gkl(data, epochs, trace);

		// write the trace
		LOG4CXX_INFO(logger, "Writing trace to " << "/tmp/dlee01-gkl-trace.R");
		trace.toRfile("/tmp/dlee01-gkl-trace.R", "dlee01.gkl");
	}

	mfStop();
	mfFinalize();

	return 0;
}


/** \file oatest.cpp

C++ program: oatest

oatest: tool for testing new algorithms

Author: Pieter Eendebak <pieter.eendebak@gmail.com>, (C) 2014

Copyright: See LICENSE.txt file that comes with this distribution
*/

#include <algorithm>
#include <iostream>
#include <map>
#include <stdio.h>
#include <stdlib.h>

#include "anyoption.h"
#include "arrayproperties.h"
#include "arraytools.h"
#include "extend.h"
#include "graphtools.h"
#include "unittests.h"
#include "tools.h"

#include "evenodd.h"
#include "lmc.h"

#include "conference.h"

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/LU>
#include <Eigen/SVD>

#include "Deff.h"
#include "arraytools.h"
#include "strength.h"

#ifdef HAVE_BOOST
#include <boost/filesystem.hpp>
#include <string>
#endif

using namespace Eigen;

#include "graphtools.h"

/// show information about Pareto criteria for conference matrix
void paretoInfo (const array_link &alx) {
        std::vector< int > j5 = alx.Jcharacteristics (5);
        int j5max = vectormax (j5, 0);

        int v1 = (j5max == alx.n_rows);
        int v2 = 1 - v1;

        int N = alx.n_rows;
        int rank = array2xf (alx).rank ();
        std::vector< int > F4 = alx.Fvalues (4);
        std::vector< double > gwlp = alx.GWLP ();
        printf ("pareto data: %d ; ", rank);
        printf (" %d ", (int)(N * N * gwlp[4]));
        printf (" ; ");
        display_vector (F4);
        printf (" ; ");
        printf (" %d ; %d", v1, v2);
        printf ("\n");
}

/// check whether an array is contained in a Pareto set
int arrayInPareto (const Pareto< mvalue_t< long >, array_link > &pset, const array_link &al, int verbose = 1) {

        std::vector< array_link > llx = pset.allindices ();
        arraylist_t ll (llx.begin (), llx.end ());
        int jx = arrayInList (al, ll, 0);
        if (verbose)
                myprintf ("arrayInPareto: index in pareto list %d\n", jx);
        return jx;
}

int main(int argc, char* argv[]) {
    AnyOption opt;
    /* parse command line options */
    opt.setFlag("help", 'h'); /* a flag (takes no argument), supporting long and short form */
    opt.setOption("output", 'o');
    opt.setOption("input", 'I');
    opt.setOption("rand", 'r');
    opt.setOption("verbose", 'v');
    opt.setOption("ii", 'i');
    opt.setOption("jj");
    opt.setOption("xx", 'x');
    opt.setOption("dverbose", 'd');
    opt.setOption("rows");
    opt.setOption("cols");
    opt.setOption("nrestarts");
    opt.setOption("niter");
    opt.setOption("mdebug", 'm');
    opt.setOption("oaconfig", 'c'); /* file that specifies the design */

    opt.addUsage("Orthonal Array: oatest: testing platform");
    opt.addUsage("Usage: oatest [OPTIONS] [FILE]");
    opt.addUsage("");
    opt.addUsage(" -h --help  			Prints this help ");
    opt.processCommandArgs(argc, argv);

    double t0 = get_time_ms(), dt = 0;
    int randvalseed = opt.getIntValue('r', 1);
    int ix = opt.getIntValue('i', 1);
    int r = opt.getIntValue('r', 1);
    int jj = opt.getIntValue("jj", 5);

    int xx = opt.getIntValue('x', 0);
    int niter = opt.getIntValue("niter", 10);
    int verbose = opt.getIntValue("verbose", 1);

    const char* input = opt.getValue('I');
    if (input == 0)
        input = "test.oa";

    array_link G(4, 4, 0);
    G.at(0, 0) = 1;
    G.at(1, 0) = 1;
    G.at(1, 2) = 1;
    G.at(1, 3) = 1;
    G.at(2, 2) = 1;
    G.at(3, 1) = 1;

    if (xx) {
    G.at(0, 1) = 1; G.at(2, 1) = G.at(3, 1) = G.at(1, 3) = 1; // symmetric
}
        G.showarraycompact();

        std::vector<int> colors(4);

        std::vector<int> perm = nauty::reduceNauty(G, colors, 4);
        myprintf("perm: ");  print_perm(perm);
        return 0;

        printf("test! %ld\n", choose(6,4));
        for (int i = 4; i < 12; i++)
            printf("choose(%d, %d): %ld\n", i, i-2, choose(i, i - 2) - ncombs(i, i - 2));
        fflush(0);

        array_link A = exampleArray(56, 1);
        ndarray<double> D = distance_distribution_mixed(A, 2);
        D.show();

        t0 = get_time_ms();
        for(int x=0; x<100000; x++)
            choose(15, 11);
        dt = get_time_ms() - t0;
        printf("dt: %f\n", dt);

        t0 = get_time_ms();
        for (int x = 0; x < 100000; x++)
            ncombs(15, 11);
        dt = get_time_ms() - t0;
        printf("dt: %f\n", dt);
        return 0;

        srand (randvalseed);
        if (randvalseed == -1) {
                randvalseed = time (NULL);
                printf ("random seed %d\n", randvalseed);
                srand (randvalseed);
        }

		array_link array = exampleArray(0);
		lmc_t lmc_type = LMCcheck(array);

		array = array.randomperm();
		array.showarray();
		array_link reduced_array = reduceLMCform(array);
		reduced_array.showarray();
		exit(0);

		try {
			array_link al = exampleArray(r);
			al.show();
			al.showarray();

			std::vector<int> sizes = array2modelmatrix_sizes(al);
			display_vector(sizes); myprintf("\n");
			MatrixFloat modelmatrix = array2modelmatrix(al, "i", 1);
			array_link modelmatrixx = modelmatrix;
			modelmatrixx.show();
			modelmatrixx.showarray();

			modelmatrix = array2modelmatrix(al, "main", 1);
			modelmatrixx = modelmatrix;
			modelmatrixx.show();
			modelmatrixx.showarray();

			exit(0);

		}
		catch (const std::exception &e) {
			std::cerr << e.what() << std::endl;
			throw;
		}

        {
                array_link al = exampleArray (r);

                array_transformation_t tt = reduceOAnauty (al);
                array_link alx = tt.apply (al);
                exit (0);
        }

        return 0;
}

// kate: indent-mode cstyle; indent-width 5; replace-tabs on;


/*
 *  (C) Copyright Nick Thompson 2018.
 *  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)
 */
#include <cmath>
#include <vector>
#include <array>
#include <forward_list>
#include <algorithm>
#include <random>
#include <limits>
#include <boost/core/lightweight_test.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/math/tools/norms.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_complex.hpp>

using std::abs;
using std::pow;
using std::sqrt;
using boost::multiprecision::cpp_bin_float_50;
using boost::multiprecision::cpp_complex_50;
using boost::math::tools::lp_norm;
using boost::math::tools::l1_norm;
using boost::math::tools::l2_norm;
using boost::math::tools::sup_norm;
using boost::math::tools::lp_distance;
using boost::math::tools::l1_distance;
using boost::math::tools::l2_distance;
using boost::math::tools::sup_distance;
using boost::math::tools::total_variation;

/*
 * Test checklist:
 * 1) Does it work with multiprecision?
 * 2) Does it work with .cbegin()/.cend() if the data is not altered?
 * 3) Does it work with ublas and std::array? (Checking Eigen and Armadillo will make the CI system really unhappy.)
 * 4) Does it work with std::forward_list if a forward iterator is all that is required?
 * 5) Does it work with complex data if complex data is sensible?
 */

// To stress test, set global_seed = 0, global_size = huge.
static const constexpr size_t global_seed = 834;
static const constexpr size_t global_size = 64;

template<class T>
std::vector<T> generate_random_vector(size_t size, size_t seed)
{
    if (seed == 0)
    {
        std::random_device rd;
        seed = rd();
    }
    std::vector<T> v(size);

    std::mt19937 gen(seed);

    if constexpr (std::is_floating_point<T>::value)
    {
        std::normal_distribution<T> dis(0, 1);
        for (size_t i = 0; i < v.size(); ++i)
        {
            v[i] = dis(gen);
        }
        return v;
    }
    else if constexpr (std::is_integral<T>::value)
    {
        // Rescaling by larger than 2 is UB!
        std::uniform_int_distribution<T> dis(std::numeric_limits<T>::lowest()/2, std::numeric_limits<T>::max()/2);
        for (size_t i = 0; i < v.size(); ++i)
        {
            v[i] = dis(gen);
        }
        return v;
    }
    else if constexpr (boost::is_complex<T>::value)
    {
        std::normal_distribution<typename T::value_type> dis(0, 1);
        for (size_t i = 0; i < v.size(); ++i)
        {
            v[i] = {dis(gen), dis(gen)};
        }
        return v;
    }
    else if constexpr (boost::multiprecision::number_category<T>::value == boost::multiprecision::number_kind_complex)
    {
        std::normal_distribution<long double> dis(0, 1);
        for (size_t i = 0; i < v.size(); ++i)
        {
            v[i] = {dis(gen), dis(gen)};
        }
        return v;
    }
    else if constexpr (boost::multiprecision::number_category<T>::value == boost::multiprecision::number_kind_floating_point)
    {
        std::normal_distribution<long double> dis(0, 1);
        for (size_t i = 0; i < v.size(); ++i)
        {
            v[i] = dis(gen);
        }
        return v;
    }
    else
    {
        BOOST_ASSERT_MSG(false, "Could not identify type for random vector generation.");
        return v;
    }
}

template<class Real>
void test_lp()
{
    Real tol = 50*std::numeric_limits<Real>::epsilon();

    std::array<Real, 3> u{1,0,0};
    Real l3 = lp_norm(u.begin(), u.end(), 3);
    BOOST_TEST(abs(l3 - 1) < tol);

    u[0] = -8;
    l3 = lp_norm(u.cbegin(), u.cend(), 3);
    BOOST_TEST(abs(l3 - 8) < tol);

    std::vector<Real> v(500);
    for (size_t i = 0; i < v.size(); ++i) {
        v[i] = 7;
    }
    Real l8 = lp_norm(v, 8);
    Real expected = 7*pow(v.size(), static_cast<Real>(1)/static_cast<Real>(8));
    BOOST_TEST(abs(l8 - expected) < tol*abs(expected));

    // Does it work with ublas vectors?
    // Does it handle the overflow of intermediates?
    boost::numeric::ublas::vector<Real> w(4);
    Real bignum = sqrt(std::numeric_limits<Real>::max())/256;
    for (size_t i = 0; i < w.size(); ++i)
    {
        w[i] = bignum;
    }
    Real l20 = lp_norm(w.cbegin(), w.cend(), 4);
    expected = bignum*pow(w.size(), static_cast<Real>(1)/static_cast<Real>(4));
    BOOST_TEST(abs(l20 - expected) < tol*expected);

    v = generate_random_vector<Real>(global_size, global_seed);
    Real scale = 8;
    Real l7 = scale*lp_norm(v, 7);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l7_ = lp_norm(v, 7);
    BOOST_TEST(abs(l7_ - l7) < tol*l7);
}

template<class Complex>
void test_complex_lp()
{
    typedef typename Complex::value_type Real;
    Real tol = 50*std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,0}, {0,0}};
    Real l3 = lp_norm(v.cbegin(), v.cend(), 3);
    BOOST_TEST(abs(l3 - 1) < tol);

    l3 = lp_norm(v, 3);
    BOOST_TEST(abs(l3 - 1) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    Real scale = 8;
    Real l7 = scale*lp_norm(v, 7);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l7_ = lp_norm(v, 7);
    BOOST_TEST(abs(l7_ - l7) < tol*l7);
}

template<class Z>
void test_integer_lp()
{
    double tol = 100*std::numeric_limits<double>::epsilon();

    std::array<Z, 3> u{1,0,0};
    double l3 = lp_norm(u.begin(), u.end(), 3);
    BOOST_TEST(abs(l3 - 1) < tol);

    auto v = generate_random_vector<Z>(global_size, global_seed);
    Z scale = 2;
    double l7 = scale*lp_norm(v, 7);
    for (auto & x : v)
    {
        x *= scale;
    }
    double l7_ = lp_norm(v, 7);
    BOOST_TEST(abs(l7_ - l7) < tol*l7);
}

template<class Real>
void test_lp_distance()
{
    Real tol = 100*std::numeric_limits<Real>::epsilon();

    std::vector<Real> u{1,0,0};
    std::vector<Real> v{0,0,0};

    Real dist = lp_distance(u,u, 3);
    BOOST_TEST(abs(dist) < tol);

    dist = lp_distance(u,v, 3);
    BOOST_TEST(abs(dist - 1) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    u = generate_random_vector<Real>(global_size, global_seed+1);
    Real dist1 = lp_distance(u, v, 7);
    Real dist2 = lp_distance(v, u, 7);

    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Complex>
void test_complex_lp_distance()
{
    using Real = typename Complex::value_type;
    Real tol = 100*std::numeric_limits<Real>::epsilon();

    std::vector<Complex> u{{1,0},{0,0},{0,0}};
    std::vector<Complex> v{{0,0},{0,0},{0,0}};

    Real dist = boost::math::tools::lp_distance(u,u, 3);
    BOOST_TEST(abs(dist) < tol);

    dist = boost::math::tools::lp_distance(u,v, 3);
    BOOST_TEST(abs(dist - 1) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    u = generate_random_vector<Complex>(global_size, global_seed + 1);
    Real dist1 = lp_distance(u, v, 7);
    Real dist2 = lp_distance(v, u, 7);

    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Z>
void test_integer_lp_distance()
{
    double tol = 100*std::numeric_limits<double>::epsilon();

    std::array<Z, 3> u{1,0,0};
    std::array<Z, 3> w{0,0,0};
    double l3 = lp_distance(u, w, 3);
    BOOST_TEST(abs(l3 - 1) < tol);

    auto v = generate_random_vector<Z>(global_size, global_seed);
    Z scale = 2;
    for (auto & x : v)
    {
        x *= scale;
    }
    auto s = generate_random_vector<Z>(global_size, global_seed + 1);
    double dist1 = lp_distance(v, s, 7);
    double dist2 = lp_distance(s, v, 7);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist2);
}

template<class Z>
void test_integer_total_variation()
{
    double eps = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,1};
    double tv = boost::math::tools::total_variation(v);
    BOOST_TEST_EQ(tv, 0);

    v[1] = 2;
    tv = boost::math::tools::total_variation(v.begin(), v.end());
    BOOST_TEST_EQ(tv, 1);

    v.resize(16);
    for (size_t i = 0; i < v.size(); ++i) {
        v[i] = i;
    }

    tv = boost::math::tools::total_variation(v);
    BOOST_TEST_EQ(tv, v.size() -1);

    for (size_t i = 0; i < v.size(); ++i)
    {
        v[i] = i*i;
    }

    tv = boost::math::tools::total_variation(v);
    BOOST_TEST_EQ(tv, (v.size() - 1)*(v.size() - 1));

    // Work with std::array?
    std::array<Z, 2> w{1,1};
    tv = boost::math::tools::total_variation(w);
    BOOST_TEST_EQ(tv,0);

    std::array<Z, 4> u{1, 2, 1, 2};
    tv = boost::math::tools::total_variation(u);
    BOOST_TEST_EQ(tv, 3);

    v = generate_random_vector<Z>(global_size, global_seed);
    double tv1 = 2*total_variation(v);
    Z scale = 2;
    for (auto & x : v)
    {
        x *= scale;
    }
    double tv2 = total_variation(v);
    BOOST_TEST(abs(tv1 - tv2) < tv1*eps);
}

template<class Real>
void test_total_variation()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,1};
    Real tv = total_variation(v.begin(), v.end());
    BOOST_TEST(tv >= 0 && abs(tv) < tol);

    tv = total_variation(v);
    BOOST_TEST(tv >= 0 && abs(tv) < tol);

    v[1] = 2;
    tv = total_variation(v.begin(), v.end());
    BOOST_TEST(abs(tv - 1) < tol);

    v.resize(50);
    for (size_t i = 0; i < v.size(); ++i) {
        v[i] = i;
    }

    tv = total_variation(v.begin(), v.end());
    BOOST_TEST(abs(tv - (v.size() -1)) < tol);

    for (size_t i = 0; i < v.size(); ++i) {
        v[i] = i*i;
    }

    tv = total_variation(v.begin(), v.end());
    BOOST_TEST(abs(tv - (v.size() - 1)*(v.size() - 1)) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    Real scale = 8;
    Real tv1 = scale*total_variation(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real tv2 = total_variation(v);
    BOOST_TEST(abs(tv1 - tv2) < tol*tv1);
}

template<class Real>
void test_sup_norm()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{-2,1,0};
    Real s = boost::math::tools::sup_norm(v.begin(), v.end());
    BOOST_TEST(abs(s - 2) < tol);

    s = boost::math::tools::sup_norm(v);
    BOOST_TEST(abs(s - 2) < tol);

    // Work with std::array?
    std::array<Real, 3> w{-2,1,0};
    s = boost::math::tools::sup_norm(w);
    BOOST_TEST(abs(s - 2) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    Real scale = 8;
    Real sup1 = scale*sup_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real sup2 = sup_norm(v);
    BOOST_TEST(abs(sup1 - sup2) < tol*sup1);
}

template<class Z>
void test_integer_sup_norm()
{
    double eps = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{2,1,0};
    Z s = sup_norm(v.begin(), v.end());
    BOOST_TEST_EQ(s, 2);

    s = sup_norm(v);
    BOOST_TEST_EQ(s,2);

    v = generate_random_vector<Z>(global_size, global_seed);
    double sup1 = 2*sup_norm(v);
    Z scale = 2;
    for (auto & x : v)
    {
        x *= scale;
    }
    double sup2 = sup_norm(v);
    BOOST_TEST(abs(sup1 - sup2) < sup1*eps);
}

template<class Complex>
void test_complex_sup_norm()
{
    typedef typename Complex::value_type Real;
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Complex> w{{0,-8}, {1,1}, {3,2}};
    Real s = sup_norm(w.cbegin(), w.cend());
    BOOST_TEST(abs(s-8) < tol);

    s = sup_norm(w);
    BOOST_TEST(abs(s-8) < tol);

    auto v = generate_random_vector<Complex>(global_size, global_seed);
    Real scale = 8;
    Real sup1 = scale*sup_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real sup2 = sup_norm(v);
    BOOST_TEST(abs(sup1 - sup2) < tol*sup1);
}

template<class Real>
void test_l0_pseudo_norm()
{
    std::vector<Real> v{0,0,1};
    size_t count = boost::math::tools::l0_pseudo_norm(v.begin(), v.end());
    BOOST_TEST_EQ(count, 1);

    // Compiles with cbegin()/cend()?
    count = boost::math::tools::l0_pseudo_norm(v.cbegin(), v.cend());
    BOOST_TEST_EQ(count, 1);

    count = boost::math::tools::l0_pseudo_norm(v);
    BOOST_TEST_EQ(count, 1);

    std::array<Real, 3> w{0,0,1};
    count = boost::math::tools::l0_pseudo_norm(w);
    BOOST_TEST_EQ(count, 1);
}

template<class Complex>
void test_complex_l0_pseudo_norm()
{
    std::vector<Complex> v{{0,0}, {0,0}, {1,0}};
    size_t count = boost::math::tools::l0_pseudo_norm(v.begin(), v.end());
    BOOST_TEST_EQ(count, 1);

    count = boost::math::tools::l0_pseudo_norm(v);
    BOOST_TEST_EQ(count, 1);
}

template<class Z>
void test_hamming_distance()
{
    std::vector<Z> v{1,2,3};
    std::vector<Z> w{1,2,4};
    size_t count = boost::math::tools::hamming_distance(v, w);
    BOOST_TEST_EQ(count, 1);

    count = boost::math::tools::hamming_distance(v, v);
    BOOST_TEST_EQ(count, 0);
}

template<class Real>
void test_l1_norm()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,1,1};
    Real l1 = l1_norm(v.begin(), v.end());
    BOOST_TEST(abs(l1 - 3) < tol);

    l1 = l1_norm(v);
    BOOST_TEST(abs(l1 - 3) < tol);

    std::array<Real, 3> w{1,1,1};
    l1 = l1_norm(w);
    BOOST_TEST(abs(l1 - 3) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    Real scale = 8;
    Real l1_1 = scale*l1_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l1_2 = l1_norm(v);
    BOOST_TEST(abs(l1_1 - l1_2) < tol*l1_1);
}

template<class Z>
void test_integer_l1_norm()
{
    double eps = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,1,1};
    Z l1 = boost::math::tools::l1_norm(v.begin(), v.end());
    BOOST_TEST_EQ(l1, 3);

    v = generate_random_vector<Z>(global_size, global_seed);
    double l1_1 = 2*l1_norm(v);
    Z scale = 2;
    for (auto & x : v)
    {
        x *= scale;
    }
    double l1_2 = l1_norm(v);
    BOOST_TEST(l1_1 > 0);
    BOOST_TEST(l1_2 > 0);
    if (abs(l1_1 - l1_2) > 2*l1_1*eps)
    {
        std::cout << std::setprecision(std::numeric_limits<double>::digits10);
        std::cout << "L1_1 = " << l1_1 << "\n";
        std::cout << "L1_2 = " << l1_2 << "\n";
        BOOST_TEST(abs(l1_1 - l1_2) < 2*l1_1*eps);
    }
}

template<class Complex>
void test_complex_l1_norm()
{
    typedef typename Complex::value_type Real;
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,1},{0,-1}};
    Real l1 = l1_norm(v.begin(), v.end());
    BOOST_TEST(abs(l1 - 3) < tol);

    l1 = l1_norm(v);
    BOOST_TEST(abs(l1 - 3) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    Real scale = 8;
    Real l1_1 = scale*l1_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l1_2 = l1_norm(v);
    BOOST_TEST(abs(l1_1 - l1_2) < tol*l1_1);
}

template<class Real>
void test_l1_distance()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,2,3};
    std::vector<Real> w{1,1,1};
    Real l1 = boost::math::tools::l1_distance(v, v);
    BOOST_TEST(abs(l1) < tol);

    l1 = boost::math::tools::l1_distance(w, v);
    BOOST_TEST(abs(l1 - 3) < tol);

    l1 = boost::math::tools::l1_distance(v, w);
    BOOST_TEST(abs(l1 - 3) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    w = generate_random_vector<Real>(global_size, global_seed+1);
    Real dist1 = l1_distance(v, w);
    Real dist2 = l1_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Z>
void test_integer_l1_distance()
{
    double tol = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,2,3};
    std::vector<Z> w{1,1,1};
    double l1 = boost::math::tools::l1_distance(v, v);
    BOOST_TEST(abs(l1) < tol);

    l1 = boost::math::tools::l1_distance(w, v);
    BOOST_TEST(abs(l1 - 3) < tol);

    l1 = boost::math::tools::l1_distance(v, w);
    BOOST_TEST(abs(l1 - 3) < tol);

    v = generate_random_vector<Z>(global_size, global_seed);
    w = generate_random_vector<Z>(global_size, global_seed + 1);
    double dist1 = l1_distance(v, w);
    double dist2 = l1_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Complex>
void test_complex_l1_distance()
{
    typedef typename Complex::value_type Real;
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,1},{0,-1}};
    Real l1 = boost::math::tools::l1_distance(v, v);
    BOOST_TEST(abs(l1) < tol);

    std::vector<Complex> w{{2,0}, {0,1},{0,-1}};
    l1 = boost::math::tools::l1_distance(v.cbegin(), v.cend(), w.cbegin());
    BOOST_TEST(abs(l1 - 1) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    w = generate_random_vector<Complex>(global_size, global_seed + 1);
    Real dist1 = l1_distance(v, w);
    Real dist2 = l1_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Real>
void test_l2_norm()
{
    using std::sqrt;
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,1,1,1};
    Real l2 = boost::math::tools::l2_norm(v.begin(), v.end());
    BOOST_TEST(abs(l2 - 2) < tol);

    l2 = boost::math::tools::l2_norm(v);
    BOOST_TEST(abs(l2 - 2) < tol);

    std::array<Real, 4> w{1,1,1,1};
    l2 = boost::math::tools::l2_norm(w);
    BOOST_TEST(abs(l2 - 2) < tol);

    Real bignum = 4*sqrt(std::numeric_limits<Real>::max());
    v[0] = bignum;
    v[1] = 0;
    v[2] = 0;
    v[3] = 0;
    l2 = boost::math::tools::l2_norm(v.begin(), v.end());
    BOOST_TEST(abs(l2 - bignum) < tol*l2);

    v = generate_random_vector<Real>(global_size, global_seed);
    Real scale = 8;
    Real l2_1 = scale*l2_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l2_2 = l2_norm(v);
    BOOST_TEST(l2_1 > 0);
    BOOST_TEST(l2_2 > 0);
    BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1);
}

template<class Z>
void test_integer_l2_norm()
{
    double tol = 100*std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,1,1,1};
    double l2 = boost::math::tools::l2_norm(v.begin(), v.end());
    BOOST_TEST(abs(l2 - 2) < tol);

    v = generate_random_vector<Z>(global_size, global_seed);
    Z scale = 2;
    double l2_1 = scale*l2_norm(v);
    for (auto & x : v)
    {
        x *= scale;
    }
    double l2_2 = l2_norm(v);
    BOOST_TEST(l2_1 > 0);
    BOOST_TEST(l2_2 > 0);
    BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1);
}

template<class Complex>
void test_complex_l2_norm()
{
    typedef typename Complex::value_type Real;
    Real tol = 100*std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,1},{0,-1}, {1,0}};
    Real l2 = boost::math::tools::l2_norm(v.begin(), v.end());
    BOOST_TEST(abs(l2 - 2) < tol);

    l2 = boost::math::tools::l2_norm(v);
    BOOST_TEST(abs(l2 - 2) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    Real scale = 8;
    Real l2_1 = scale*l2_norm(v);
    for (auto & x : v)
    {
        x *= -scale;
    }
    Real l2_2 = l2_norm(v);
    BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1);
}

template<class Real>
void test_l2_distance()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,1,1,1};
    Real l2 = boost::math::tools::l2_distance(v, v);
    BOOST_TEST(abs(l2) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    auto w = generate_random_vector<Real>(global_size, global_seed + 1);
    Real dist1 = l2_distance(v, w);
    Real dist2 = l2_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Z>
void test_integer_l2_distance()
{
    double tol = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,1,1,1};
    double l2 = boost::math::tools::l2_distance(v, v);
    BOOST_TEST(abs(l2) < tol);

    v = generate_random_vector<Z>(global_size, global_seed);
    auto w = generate_random_vector<Z>(global_size, global_seed + 1);
    double dist1 = l2_distance(v, w);
    double dist2 = l2_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Complex>
void test_complex_l2_distance()
{
    typedef typename Complex::value_type Real;
    Real tol = 100*std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,1},{0,-1}, {1,0}};
    Real l2 = boost::math::tools::l2_distance(v, v);
    BOOST_TEST(abs(l2) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    auto w = generate_random_vector<Complex>(global_size, global_seed + 1);
    Real dist1 = l2_distance(v, w);
    Real dist2 = l2_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Real>
void test_sup_distance()
{
    Real tol = std::numeric_limits<Real>::epsilon();
    std::vector<Real> v{1,1,1,1};
    std::vector<Real> w{0,0,0,0};
    Real sup = boost::math::tools::sup_distance(v, v);
    BOOST_TEST(abs(sup) < tol);
    sup = boost::math::tools::sup_distance(v, w);
    BOOST_TEST(abs(sup -1) < tol);

    v = generate_random_vector<Real>(global_size, global_seed);
    w = generate_random_vector<Real>(global_size, global_seed + 1);
    Real dist1 = sup_distance(v, w);
    Real dist2 = sup_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Z>
void test_integer_sup_distance()
{
    double tol = std::numeric_limits<double>::epsilon();
    std::vector<Z> v{1,1,1,1};
    std::vector<Z> w{0,0,0,0};
    double sup = boost::math::tools::sup_distance(v, v);
    BOOST_TEST(abs(sup) < tol);

    sup = boost::math::tools::sup_distance(v, w);
    BOOST_TEST(abs(sup -1) < tol);

    v = generate_random_vector<Z>(global_size, global_seed);
    w = generate_random_vector<Z>(global_size, global_seed + 1);
    double dist1 = sup_distance(v, w);
    double dist2 = sup_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

template<class Complex>
void test_complex_sup_distance()
{
    typedef typename Complex::value_type Real;
    Real tol = 100*std::numeric_limits<Real>::epsilon();
    std::vector<Complex> v{{1,0}, {0,1},{0,-1}, {1,0}};
    Real sup = boost::math::tools::sup_distance(v, v);
    BOOST_TEST(abs(sup) < tol);

    v = generate_random_vector<Complex>(global_size, global_seed);
    auto w = generate_random_vector<Complex>(global_size, global_seed + 1);
    Real dist1 = sup_distance(v, w);
    Real dist2 = sup_distance(w, v);
    BOOST_TEST(abs(dist1 - dist2) < tol*dist1);
}

int main()
{
    test_l0_pseudo_norm<unsigned>();
    test_l0_pseudo_norm<int>();
    test_l0_pseudo_norm<float>();
    test_l0_pseudo_norm<double>();
    test_l0_pseudo_norm<long double>();
    test_l0_pseudo_norm<cpp_bin_float_50>();

    test_complex_l0_pseudo_norm<std::complex<float>>();
    test_complex_l0_pseudo_norm<std::complex<double>>();
    test_complex_l0_pseudo_norm<std::complex<long double>>();
    test_complex_l0_pseudo_norm<cpp_complex_50>();

    test_hamming_distance<int>();
    test_hamming_distance<unsigned>();

    test_l1_norm<float>();
    test_l1_norm<double>();
    test_l1_norm<long double>();
    test_l1_norm<cpp_bin_float_50>();

    test_integer_l1_norm<int>();
    test_integer_l1_norm<unsigned>();

    test_complex_l1_norm<std::complex<float>>();
    test_complex_l1_norm<std::complex<double>>();
    test_complex_l1_norm<std::complex<long double>>();
    test_complex_l1_norm<cpp_complex_50>();

    test_l1_distance<float>();
    test_l1_distance<cpp_bin_float_50>();

    test_integer_l1_distance<int>();
    test_integer_l1_distance<unsigned>();

    test_complex_l1_distance<std::complex<float>>();
    test_complex_l1_distance<cpp_complex_50>();

    test_complex_l2_norm<std::complex<float>>();
    test_complex_l2_norm<std::complex<double>>();
    test_complex_l2_norm<std::complex<long double>>();
    test_complex_l2_norm<cpp_complex_50>();

    test_l2_norm<float>();
    test_l2_norm<double>();
    test_l2_norm<long double>();
    test_l2_norm<cpp_bin_float_50>();

    test_integer_l2_norm<int>();
    test_integer_l2_norm<unsigned>();

    test_l2_distance<double>();
    test_l2_distance<cpp_bin_float_50>();

    test_integer_l2_distance<int>();
    test_integer_l2_distance<unsigned>();

    test_complex_l2_distance<std::complex<double>>();
    test_complex_l2_distance<cpp_complex_50>();

    test_lp<float>();
    test_lp<double>();
    test_lp<long double>();
    test_lp<cpp_bin_float_50>();

    test_complex_lp<std::complex<float>>();
    test_complex_lp<std::complex<double>>();
    test_complex_lp<std::complex<long double>>();
    test_complex_lp<cpp_complex_50>();

    test_integer_lp<int>();
    test_integer_lp<unsigned>();

    test_lp_distance<double>();
    test_lp_distance<cpp_bin_float_50>();

    test_complex_lp_distance<std::complex<double>>();
    test_complex_lp_distance<cpp_complex_50>();

    test_integer_lp_distance<int>();
    test_integer_lp_distance<unsigned>();

    test_sup_norm<float>();
    test_sup_norm<double>();
    test_sup_norm<long double>();
    test_sup_norm<cpp_bin_float_50>();

    test_integer_sup_norm<int>();
    test_integer_sup_norm<unsigned>();

    test_complex_sup_norm<std::complex<float>>();
    test_complex_sup_norm<std::complex<double>>();
    test_complex_sup_norm<std::complex<long double>>();
    test_complex_sup_norm<cpp_complex_50>();

    test_sup_distance<double>();
    test_sup_distance<cpp_bin_float_50>();

    test_integer_sup_distance<int>();
    test_integer_sup_distance<unsigned>();

    test_complex_sup_distance<std::complex<double>>();
    test_complex_sup_distance<cpp_complex_50>();

    test_total_variation<float>();
    test_total_variation<double>();
    test_total_variation<long double>();
    test_total_variation<cpp_bin_float_50>();

    test_integer_total_variation<uint32_t>();
    test_integer_total_variation<int>();

    return boost::report_errors();
}


//
// Created by prostoichelovek on 12.06.19.
//

#ifndef VISUALODOMETRY_WRAPPER_HPP
#define VISUALODOMETRY_WRAPPER_HPP

#include <Eigen/Dense>

#include <opencv2/opencv.hpp>
#include <opencv2/core/eigen.hpp>

#include <iostream>
#include <iterator>
#include <vector>

#include "feature.h"
#include "utils.h"
#include "visualOdometry.h"

using namespace cv;
using namespace std;

class Wrapper {
public:
    Mat projMatrl, projMatrr;
    Mat rotation = Mat::eye(3, 3, CV_64F);
    Mat translation_stereo = Mat::zeros(3, 1, CV_64F);

    Mat Rpose = Mat::eye(3, 3, CV_64F);

    Mat frame_pose = Mat::eye(4, 4, CV_64F);
    Mat frame_pose32 = Mat::eye(4, 4, CV_32F);

    FeatureSet currentVOFeatures;
    Mat points4D, points3D;

    vector<Point2f> pointsLeft_t0, pointsRight_t0, pointsLeft_t1, pointsRight_t1;

    Wrapper(Mat &projMatrl, Mat &projMatrr)
            : projMatrl(projMatrl), projMatrr(projMatrr) {

    }

    Point computePos(Mat &imageLeft, Mat &imageRight) {
        if (imageLeft_t0.cols == 0) {
            imageLeft_t0 = imageLeft;
            imageRight_t0 = imageRight;
            return {-1, -1};
        }

        vector<Point2f> oldPointsLeft_t0 = currentVOFeatures.points;
        matchingFeatures(imageLeft_t0, imageRight_t0,
                         imageLeft, imageRight,
                         currentVOFeatures,
                         pointsLeft_t0, pointsRight_t0,
                         pointsLeft_t1, pointsRight_t1);

        imageLeft_t0 = imageLeft;
        imageRight_t0 = imageRight;

        vector<Point2f> &currentPointsLeft_t0 = pointsLeft_t0;
        vector<Point2f> &currentPointsLeft_t1 = pointsLeft_t1;

        vector<Point2f> newPoints;
        vector<bool> valid; // valid new points are true

        // ---------------------
        // Triangulate 3D Points
        // ---------------------
        Mat points3D_t0, points4D_t0;
        triangulatePoints(projMatrl, projMatrr, pointsLeft_t0, pointsRight_t0, points4D_t0);
        convertPointsFromHomogeneous(points4D_t0.t(), points3D_t0);

        Mat points3D_t1, points4D_t1;

        triangulatePoints(projMatrl, projMatrr, pointsLeft_t1, pointsRight_t1, points4D_t1);
        convertPointsFromHomogeneous(points4D_t1.t(), points3D_t1);

        // -----------------------
        // Tracking transformation
        // -----------------------
        trackingFrame2Frame(projMatrl, projMatrr, pointsLeft_t0, pointsLeft_t1, points3D_t0, rotation,
                            translation_stereo);

        points4D = points4D_t0;
        frame_pose.convertTo(frame_pose32, CV_32F);
        points4D = frame_pose32 * points4D;
        convertPointsFromHomogeneous(points4D.t(), points3D);

        // -----------
        // Integrating
        // -----------

        Vec3f rotation_euler = rotationMatrixToEulerAngles(rotation);

        Mat rigid_body_transformation;

        if (abs(rotation_euler[1]) < 0.1 && abs(rotation_euler[0]) < 0.1
            && abs(rotation_euler[2]) < 0.1) {
            integrateOdometryStereo(rigid_body_transformation, frame_pose, rotation,
                                    translation_stereo);
        }

        Rpose = frame_pose(Range(0, 3), Range(0, 3));
        Vec3f Rpose_euler = rotationMatrixToEulerAngles(Rpose);

        int x = int(frame_pose.col(3).at<double>(0));
        int y = int(frame_pose.col(3).at<double>(2));

        return Point(x, y);
    }

private:
    Mat imageLeft_t0, imageRight_t0;

};

#endif //VISUALODOMETRY_WRAPPER_HPP