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

#include "required_defines.hpp"

#include "performance_measure.hpp"

#include <boost/math/distributions.hpp>

double probabilities[] = {
   1e-5,
   1e-4,
   1e-3,
   1e-2,
   0.05,
   0.1,
   0.2,
   0.3,
   0.4,
   0.5,
   0.6,
   0.7,
   0.8,
   0.9,
   0.95,
   1-1e-5,
   1-1e-4,
   1-1e-3,
   1-1e-2
};

int int_values[] = {
   1,
   2,
   3,
   5,
   10,
   20,
   50,
   100,
   1000,
   10000,
   100000
};

int small_int_values[] = {
   1,
   2,
   3,
   5,
   10,
   15,
   20,
   30,
   50,
   100,
   150
};

double real_values[] = {
   1e-5,
   1e-4,
   1e-2,
   1e-1,
   1,
   10,
   100,
   1000,
   10000,
   100000
};

#define BOOST_MATH_DISTRIBUTION3_TEST(name, param1_table, param2_table, param3_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" BOOST_STRINGIZE(name) "-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
   unsigned d_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            for(unsigned l = 0; l < d_size; ++l)\
            {\
               result += cdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j], param3_table[k]), random_variable_table[l]);\
            }\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size * d_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" BOOST_STRINGIZE(name) "-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
   unsigned d_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            for(unsigned l = 0; l < d_size; ++l)\
            {\
               result += pdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j], param3_table[k]), random_variable_table[l]);\
            }\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size * d_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" BOOST_STRINGIZE(name) "-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
      unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
      unsigned d_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
         for(unsigned j = 0; j < b_size; ++j)\
         {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               for(unsigned l = 0; l < d_size; ++l)\
               {\
                  result += quantile(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j], param3_table[k]), probability_table[l]);\
               }\
            }\
         }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * b_size * c_size * d_size);\
   }

#define BOOST_MATH_DISTRIBUTION2_TEST(name, param1_table, param2_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" BOOST_STRINGIZE(name) "-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += cdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j]), random_variable_table[k]);\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" BOOST_STRINGIZE(name) "-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += pdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j]), random_variable_table[k]);\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" BOOST_STRINGIZE(name) "-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
         for(unsigned j = 0; j < b_size; ++j)\
         {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += quantile(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i], param2_table[j]), probability_table[k]);\
            }\
         }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * b_size * c_size);\
   }

#define BOOST_MATH_DISTRIBUTION1_TEST(name, param1_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" BOOST_STRINGIZE(name) "-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += cdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i]), random_variable_table[k]);\
         }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" BOOST_STRINGIZE(name) "-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += pdf(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i]), random_variable_table[k]);\
         }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" BOOST_STRINGIZE(name) "-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += quantile(boost::math:: BOOST_JOIN(name, _distribution) <>(param1_table[i]), probability_table[k]);\
            }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * c_size);\
   }

BOOST_MATH_DISTRIBUTION2_TEST(beta, probabilities, probabilities, probabilities, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(binomial, int_values, probabilities, int_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(cauchy, int_values, real_values, int_values, probabilities)
BOOST_MATH_DISTRIBUTION1_TEST(chi_squared, int_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION1_TEST(exponential, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(fisher_f, int_values, int_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(gamma, real_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(lognormal, real_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(negative_binomial, int_values, probabilities, int_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(normal, real_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION1_TEST(poisson, real_values, int_values, probabilities)
BOOST_MATH_DISTRIBUTION1_TEST(students_t, int_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(weibull, real_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(non_central_chi_squared, int_values, int_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION3_TEST(non_central_beta, int_values, int_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION3_TEST(non_central_f, int_values, int_values, real_values, real_values, probabilities)
BOOST_MATH_DISTRIBUTION2_TEST(non_central_t, int_values, small_int_values, real_values, probabilities)

#ifdef TEST_R

#define MATHLIB_STANDALONE 1

extern "C" {
#include "Rmath.h"
/*
double qnchisq(double, double, double, int, int)
{
   return 0;
}
*/
}

#define BOOST_MATH_R_DISTRIBUTION3_TEST(name, param1_table, param2_table, param3_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" #name "-R-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
   unsigned d_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            for(unsigned l = 0; l < d_size; ++l)\
            {\
               result += p##name (random_variable_table[l], param1_table[i], param2_table[j], param3_table[k], 1, 0);\
            }\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size * d_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" #name "-R-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
   unsigned d_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            for(unsigned l = 0; l < d_size; ++l)\
            {\
               result += d##name (random_variable_table[l], param1_table[i], param2_table[j], param3_table[k], 0);\
            }\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size * d_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" #name "-R-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
      unsigned c_size = sizeof(param3_table)/sizeof(param3_table[0]);\
      unsigned d_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
         for(unsigned j = 0; j < b_size; ++j)\
         {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               for(unsigned l = 0; l < d_size; ++l)\
               {\
                  result += q##name (probability_table[l], param1_table[i], param2_table[j], param3_table[k], 1, 0);\
               }\
            }\
         }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * b_size * c_size * d_size);\
   }

#define BOOST_MATH_R_DISTRIBUTION2_TEST(name, param1_table, param2_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" #name "-R-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += p##name (random_variable_table[k], param1_table[i], param2_table[j], 1, 0);\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" #name "-R-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += d##name (random_variable_table[k], param1_table[i], param2_table[j], 0);\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" #name "-R-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
         for(unsigned j = 0; j < b_size; ++j)\
         {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += q##name (probability_table[k], param1_table[i], param2_table[j], 1, 0);\
            }\
         }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * b_size * c_size);\
   }

#define BOOST_MATH_R_DISTRIBUTION1_TEST(name, param1_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" #name "-R-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += p##name (random_variable_table[k], param1_table[i], 1, 0);\
         }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_pdf_, name), "dist-" #name "-R-pdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += d##name (random_variable_table[k], param1_table[i], 0);\
         }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" #name "-R-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += q##name (probability_table[k], param1_table[i], 1, 0);\
            }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * c_size);\
   }

BOOST_MATH_R_DISTRIBUTION2_TEST(beta, probabilities, probabilities, probabilities, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(binom, int_values, probabilities, int_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(cauchy, int_values, real_values, int_values, probabilities)
BOOST_MATH_R_DISTRIBUTION1_TEST(chisq, int_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION1_TEST(exp, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(f, int_values, int_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(gamma, real_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(lnorm, real_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(nbinom, int_values, probabilities, int_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(norm, real_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION1_TEST(pois, real_values, int_values, probabilities)
BOOST_MATH_R_DISTRIBUTION1_TEST(t, int_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(weibull, real_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(nchisq, int_values, int_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION3_TEST(nf, int_values, int_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION3_TEST(nbeta, int_values, int_values, real_values, real_values, probabilities)
BOOST_MATH_R_DISTRIBUTION2_TEST(nt, int_values, small_int_values, real_values, probabilities)

#endif

#ifdef TEST_CEPHES

extern "C"{

double bdtr(int k, int n, double p);
double bdtri(int k, int n, double p);

double chdtr(double df, double x);
double chdtri(double df, double p);

double fdtr(int k, int n, double p);
double fdtri(int k, int n, double p);

double nbdtr(int k, int n, double p);
double nbdtri(int k, int n, double p);

}

#define BOOST_MATH_CEPHES_DISTRIBUTION2_TEST(name, param1_table, param2_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" #name "-cephes-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
      for(unsigned j = 0; j < b_size; ++j)\
      {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += name##dtr (param1_table[i], param2_table[j], random_variable_table[k]);\
         }\
      }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * b_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" #name "-cephes-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned b_size = sizeof(param2_table)/sizeof(param2_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
         for(unsigned j = 0; j < b_size; ++j)\
         {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += name##dtri (param1_table[i], param2_table[j], probability_table[k]);\
            }\
         }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * b_size * c_size);\
   }

#define BOOST_MATH_CEPHES_DISTRIBUTION1_TEST(name, param1_table, random_variable_table, probability_table) \
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist, name), "dist-" #name "-cephes-cdf")\
   {\
   double result = 0;\
   unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
   unsigned c_size = sizeof(random_variable_table)/sizeof(random_variable_table[0]);\
   \
   for(unsigned i = 0; i < a_size; ++i)\
   {\
         for(unsigned k = 0; k < c_size; ++k)\
         {\
            result += name##dtr (param1_table[i], random_variable_table[k]);\
         }\
   }\
   \
   consume_result(result);\
   set_call_count(a_size * c_size);\
   }\
   BOOST_MATH_PERFORMANCE_TEST(BOOST_JOIN(dist_quant, name), "dist-" #name "-cephes-quantile")\
   {\
      double result = 0;\
      unsigned a_size = sizeof(param1_table)/sizeof(param1_table[0]);\
      unsigned c_size = sizeof(probability_table)/sizeof(probability_table[0]);\
      \
      for(unsigned i = 0; i < a_size; ++i)\
      {\
            for(unsigned k = 0; k < c_size; ++k)\
            {\
               result += name##dtri (param1_table[i], probability_table[k]);\
            }\
      }\
      \
      consume_result(result);\
      set_call_count(a_size * c_size);\
   }
// Cephes inverse doesn't actually calculate the quantile!!!
// BOOST_MATH_CEPHES_DISTRIBUTION2_TEST(b, int_values, int_values, probabilities, probabilities)
BOOST_MATH_CEPHES_DISTRIBUTION1_TEST(ch, int_values, real_values, probabilities)
BOOST_MATH_CEPHES_DISTRIBUTION2_TEST(f, int_values, int_values, real_values, probabilities)
// Cephes inverse doesn't calculate the quantile!!!
// BOOST_MATH_CEPHES_DISTRIBUTION2_TEST(nb, int_values, int_values, probabilities, probabilities)

#endif


#include "UniformDistributionModel.h"

#include <boost/random/uniform_smallint.hpp>

UniformDistributionModel::UniformDistributionModel( SearchSpace* ss )
    : searchSpace( ss ) {
    rng.seed( std::time( 0 ) );
}

TuningSpecification* UniformDistributionModel::sample() const {
    std::vector<TuningParameter*>   tuningParams;
    std::map<TuningParameter*, int> sample_;

    tuningParams = searchSpace->getVariantSpace()->getTuningParameters();
    for( int i = 0; i < tuningParams.size(); i++ ) {
        TuningParameter* tp = tuningParams[ i ];

        int k = ( tp->getRangeTo() - tp->getRangeFrom() ) / tp->getRangeStep();

        boost::uniform_smallint<> choice( 0, k );
        sample_[ tp ] = tp->getRangeFrom() + choice( rng ) * tp->getRangeStep();
    }

    vector<Region*> regionVec  = searchSpace->getRegions();
    list<Region*>*  regionList = new std::list<Region*>( regionVec.begin(), regionVec.end() );
    return new TuningSpecification( new Variant( sample_ ), regionList );
}


#include "functions/operators.hh"
#include <boost/test/unit_test.hpp>
#include "functions/std_functions.hh"

BOOST_AUTO_TEST_CASE(operators_test) {
  using namespace manifolds;
  Sin s;
  Tan t;
  BOOST_CHECK_EQUAL(s + t, (Addition<Sin, Tan>{}));
  BOOST_CHECK_EQUAL(s - t, (Addition<Sin, UnaryMinus<Tan> >{}));
  BOOST_CHECK_EQUAL(-s, (UnaryMinus<Sin>{}));
  BOOST_CHECK_EQUAL(s * t, (Multiplication<Sin, Tan>{}));
  BOOST_CHECK_EQUAL(s / t, (Division<Sin, Tan>{}));
}


/**
 * \ file VariableDelayLineFilter.cpp
 */

#include <iostream>

#include <ATK/Delay/VariableDelayLineFilter.h>

#include <ATK/Core/InPointerFilter.h>
#include <ATK/Core/OutPointerFilter.h>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp>

#include <boost/math/constants/constants.hpp>

#define PROCESSSIZE (1024*64)

BOOST_AUTO_TEST_CASE( VariableDelayLineFilter_sinus_line16000_delaysinus_test )
{
  std::vector<double> data(PROCESSSIZE);
  for(gsl::index i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = std::sin(2 * boost::math::constants::pi<double>() * (i+1.)/48000 * 1000);
  }
  std::vector<double> datadelay(PROCESSSIZE);
  for(gsl::index i = 0; i < PROCESSSIZE; ++i)
  {
    datadelay[i] = i * .25f;
  }

  ATK::InPointerFilter<double> generator(data.data(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);
  ATK::InPointerFilter<double> generatordelay(datadelay.data(), 1, PROCESSSIZE, false);
  generatordelay.set_output_sampling_rate(48000);

  std::vector<double> outdata(PROCESSSIZE);

  ATK::VariableDelayLineFilter<double> filter(20000);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_input_port(1, &generatordelay, 0);

  ATK::OutPointerFilter<double> output(outdata.data(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(1);
  output.process(49);
  output.process(51);
  output.process(PROCESSSIZE - 1 - 49 -51);

  for(gsl::index i = 0; i < PROCESSSIZE/4; ++i)
  {
    BOOST_REQUIRE_EQUAL(data[i*4 - i], outdata[i*4]);
  }
  for(gsl::index i = 0; i < PROCESSSIZE/4; ++i)
  {
    gsl::index current = 4*i+1;
    BOOST_REQUIRE_EQUAL((data[current - i]*3 + data[current - i-1])/4, outdata[current]);
  }
  for(gsl::index i = 0; i < PROCESSSIZE/4; ++i)
  {
    gsl::index current = 4*i+2;
    BOOST_REQUIRE_EQUAL((data[current - i] + data[current - i-1])/2, outdata[current]);
  }
  for(gsl::index i = 0; i < PROCESSSIZE/4; ++i)
  {
    gsl::index current = 4*i+3;
    BOOST_REQUIRE_EQUAL((data[current - i] + data[current - i-1]*3)/4, outdata[current]);
  }
}


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include "Mean.hpp"
#include "backendsCommon/WorkloadData.hpp"

#include <boost/numeric/conversion/cast.hpp>

#include <cmath>
#include <cstddef>
#include <functional>
#include <limits>

namespace armnn
{
bool NextIndex(const unsigned int numDims, const armnn::TensorShape& dims, std::vector<unsigned int>& current)
{
    unsigned int carry = 1;

    for (unsigned int idx = numDims; idx-- > 0; )
    {
        unsigned int current_val = current[idx] + carry;
        if (dims[idx] == current_val)
        {
            current[idx] = 0;
        }
        else
        {
            current[idx] = current_val;
            carry = 0;
            break;
        }
    }
    return (carry == 0);
}

std::size_t ReducedOutputOffset(const unsigned int numDims, const armnn::TensorShape& dims,
                                std::vector<unsigned int>& index, const unsigned int numAxis,
                                const std::vector<unsigned int>& axis) {
    std::size_t offset = 0;
    for (unsigned int idx = 0; idx < numDims; ++idx)
    {
        bool isAxis = false;
        if (!axis.empty())
        {
            for (unsigned int axisIdx = 0; axisIdx < numAxis; ++axisIdx)
            {
                if (idx == axis[axisIdx])
                {
                    isAxis = true;
                    break;
                }
            }
        }
        if (!isAxis)
        {
            offset = offset * boost::numeric_cast<size_t>(dims[idx]) + boost::numeric_cast<size_t>(index[idx]);
        }
    }
    return offset;
}
} // namespace

namespace armnn
{
void Mean(const armnn::TensorInfo& inputInfo,
          const armnn::TensorInfo& outputInfo,
          const std::vector<unsigned int>& axis,
          const float* inputData,
          float* outputData) {

    unsigned int inputNumDims = inputInfo.GetNumDimensions();
    unsigned int outputNumDims = outputInfo.GetNumDimensions();

    armnn::TensorShape outputDims = outputInfo.GetShape();
    armnn::TensorShape inputDims = inputInfo.GetShape();

    // Initialise output data.
    size_t numOutputs = 1;
    for (unsigned int idx = 0; idx < outputNumDims; ++idx)
    {
        numOutputs *= boost::numeric_cast<size_t>(outputDims[idx]);
    }

    std::vector<float> tempSum(numOutputs);
    for (size_t idx = 0; idx < numOutputs; ++idx)
    {
        outputData[idx] = 0.0f;
        tempSum[idx] = 0.0f;
    }

    // Initialise temp index.
    std::vector<unsigned int> tempIndex(inputNumDims);
    for (unsigned int idx = 0; idx < inputNumDims; ++idx)
    {
        tempIndex[idx] = 0;
    }

    std::vector<unsigned int> resolvedAxis = axis;
    if (resolvedAxis.empty())
    {
      for (unsigned int idx = 0; idx < inputNumDims; ++idx)
      {
          resolvedAxis.push_back(idx);
      }
    }
    unsigned int numResolvedAxis = boost::numeric_cast<unsigned int>(resolvedAxis.size());

    // Iterates through input_data and sum up the reduced axis.
    for (bool hasNext = true; hasNext; hasNext = NextIndex(inputNumDims, inputDims, tempIndex))
    {
        size_t inputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, 0, {});
        size_t outputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex,
                                                  numResolvedAxis, resolvedAxis);
        tempSum[outputOffset] += inputData[inputOffset];
    }

    // Takes average by num of elements added to get mean.
    size_t numElementsInAxis = 1;
    for (unsigned int idx = 0; idx < numResolvedAxis; ++idx)
    {
        size_t current = boost::numeric_cast<size_t>(inputDims[resolvedAxis[idx]]);
        BOOST_ASSERT(boost::numeric_cast<float>(current) <
              (std::numeric_limits<float>::max() / boost::numeric_cast<float>(numElementsInAxis)));
        numElementsInAxis *= current;
    }
    if (numElementsInAxis > 0) {
        for (size_t idx = 0; idx < numOutputs; ++idx)
        {
            outputData[idx] = tempSum[idx] / boost::numeric_cast<float>(numElementsInAxis);
        }
    }
}
} //namespace armnn


#include <iostream>
#include <iomanip>
#include <string>

#define CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE

//#define CGAL_STRAIGHT_SKELETON_ENABLE_TRACE 100
//#define CGAL_STRAIGHT_SKELETON_TRAITS_ENABLE_TRACE 10000000
//#define CGAL_POLYGON_OFFSET_ENABLE_TRACE 10000000

void Straight_skeleton_external_trace(std::string m)
{
  std::cout << std::setprecision(19) << m << std::endl << std::endl ;
}

void Straight_skeleton_traits_external_trace(std::string m)
{
  std::cout << std::setprecision(19) << m << std::endl << std::endl ;
}

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel_with_sqrt.h>

#include <CGAL/compute_outer_frame_margin.h>
#include <CGAL/create_offset_polygons_2.h>
#include <CGAL/create_offset_polygons_from_polygon_with_holes_2.h>
#include <CGAL/draw_straight_skeleton_2.h>
#include <CGAL/Min_circle_2.h>
#include <CGAL/Min_circle_2_traits_2.h>
#include <CGAL/Straight_skeleton_builder_2.h>
#include <CGAL/Polygon_offset_builder_2.h>
#include "print.h"

#include <CGAL/Bbox_2.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/draw_polygon_2.h>

#include <boost/shared_ptr.hpp>

#include <cassert>
#include <iostream>
#include <string>
#include <vector>

typedef CGAL::Exact_predicates_inexact_constructions_kernel          EPICK;
typedef CGAL::Exact_predicates_exact_constructions_kernel            EPECK;
typedef CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt  EPECK_w_sqrt;

template <typename K>
void test_API()
{
  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;

  typedef CGAL::Polygon_2<EPICK>                                     Polygon_2_EPICK;
  typedef CGAL::Polygon_with_holes_2<EPICK>                          Polygon_with_holes_2_EPICK;

  Polygon_2 p;
  Polygon_with_holes_2 pwh;

  std::vector< boost::shared_ptr<Polygon_2> > res;
  std::vector< boost::shared_ptr<Polygon_2_EPICK> > res_EPICK;
  std::vector< boost::shared_ptr<Polygon_with_holes_2> > res_w;
  std::vector< boost::shared_ptr<Polygon_with_holes_2_EPICK> > res_w_EPICK;

  // First kernel is the offset construction (and thus output kernel), second kernel is the skeleton construction

  // simple interior, no holes
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0.1, p) ;
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0.1, p, EPICK()) ;
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0, p, EPICK(), K()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0.1, p, K()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0, p, K(), EPICK()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0.1, p, K(), K()) ;

  // simple interior, holes
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0.1, pwh) ;
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0.1, pwh, EPICK()) ;
  res_EPICK = create_interior_skeleton_and_offset_polygons_2(0, pwh, EPICK(), K()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0.1, pwh, K()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0, pwh, K(), EPICK()) ;
  res = create_interior_skeleton_and_offset_polygons_2(0.1, pwh, K(), K()) ;

  // simple exterior, no holes
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0.1, p) ;
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0.1, p, EPICK()) ;
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0, p, EPICK(), K()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0.1, p, K()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0, p, K(), EPICK()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0.1, p, K(), K()) ;

  // simple exterior, holes
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0.1, pwh) ;
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0.1, pwh, EPICK()) ;
  res_EPICK = create_exterior_skeleton_and_offset_polygons_2(0, pwh, EPICK(), K()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0.1, pwh, K()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0, pwh, K(), EPICK()) ;
  res = create_exterior_skeleton_and_offset_polygons_2(0.1, pwh, K(), K()) ;

  // Same, but the result has holes --------------------

  // arranged interior, no holes
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, p) ;
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, p, EPICK()) ;
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0, p, EPICK(), K()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, p, K()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0, p, K(), EPICK()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, p, K(), K()) ;

  // arranged interior, holes
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh) ;
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, EPICK()) ;
  res_w_EPICK = create_interior_skeleton_and_offset_polygons_with_holes_2(0, pwh, EPICK(), K()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, K()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0, pwh, K(), EPICK()) ;
  res_w = create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, K(), K()) ;

  // arranged exterior, no holes
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, p) ;
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, p, EPICK()) ;
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0, p, EPICK(), K()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, p, K()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0, p, K(), EPICK()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, p, K(), K()) ;

  // arranged exterior, holes
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh) ;
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, EPICK()) ;
  res_w_EPICK = create_exterior_skeleton_and_offset_polygons_with_holes_2(0, pwh, EPICK(), K()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, K()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0, pwh, K(), EPICK()) ;
  res_w = create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, pwh, K(), K()) ;
}

template <typename K, typename StraightSkeleton>
bool is_valid(const boost::shared_ptr<StraightSkeleton>& ss)
{
  typedef typename StraightSkeleton::Traits::Point_2 Point;

  if(!ss)
    return false;

  if(!ss->is_valid())
    return false;

  assert(static_cast<std::size_t>(std::distance(ss->vertices_begin(), ss->vertices_end())) ==
           static_cast<std::size_t>(ss->size_of_vertices()));

  std::set<Point> unique_vertices;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
    unique_vertices.insert(vit->point());

  std::cout << unique_vertices.size() << " unique vertices (" << ss->size_of_vertices() << ")" << std::endl;

  // Can't guarantee that the embedding is correct with EPICK
  if(!std::is_same<K, EPICK>::value)
  {
    if(unique_vertices.size() != ss->size_of_vertices())
      return false;

    for(auto hit=ss->halfedges_begin(); hit!=ss->halfedges_end(); ++hit)
      if(hit->vertex()->point() == hit->opposite()->vertex()->point())
        return false;
  }

  return true;
}

// Test what happens when the offset is a single point
template <typename K>
void test_offset_square()
{
  std::cout << " --- Test Square, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::Point_2                                                  Point;
  typedef CGAL::Polygon_2<K>                                                   Polygon_2;
  typedef boost::shared_ptr<Polygon_2>                                         Polygon_ptr;
  typedef std::vector<Polygon_ptr>                                             Polygon_ptr_container;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  std::vector<Point> square;
  square.push_back(Point(0, 0));
  square.push_back(Point(1, 0));
  square.push_back(Point(1, 1));
  square.push_back(Point(0, 1));

  Skeleton_builder ssb;
  ssb.enter_contour(square.begin(), square.end());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  Polygon_ptr_container offset_polys =
    CGAL::create_interior_skeleton_and_offset_polygons_2(0.5, Polygon_2(square.begin(), square.end()), K());

  std::cout << offset_polys.size() << " polygons" << std::endl;
  for(const auto& offp : offset_polys)
    print_polygon(*offp);

  assert(offset_polys.empty());
}

// Test what happens when the offset is a single point
template <typename K>
void test_offset_four_square_holes()
{
  std::cout << " --- Test square with four holes, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::Point_2                                        Point;

  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                    Polygon_with_holes_2_ptr;
  typedef std::vector<Polygon_with_holes_2_ptr>                      Polygon_with_holes_2_ptr_container;

  Polygon_2 outer, hole1, hole2, hole3, hole4;
  outer.push_back(Point( 0,  0));
  outer.push_back(Point(10,  0));
  outer.push_back(Point(10, 10));
  outer.push_back(Point(0,  10));

  hole1.push_back(Point(1, 1));
  hole1.push_back(Point(1, 4.5));
  hole1.push_back(Point(4.5, 4.5));
  hole1.push_back(Point(4.5, 1));

  hole2.push_back(Point(5.5, 1));
  hole2.push_back(Point(5.5, 4.5));
  hole2.push_back(Point(9, 4.5));
  hole2.push_back(Point(9, 1));

  hole3.push_back(Point(1, 5.5));
  hole3.push_back(Point(1, 9));
  hole3.push_back(Point(4.5, 9));
  hole3.push_back(Point(4.5, 5.5));

  hole4.push_back(Point(5.5, 5.5));
  hole4.push_back(Point(5.5, 9));
  hole4.push_back(Point(9, 9));
  hole4.push_back(Point(9, 5.5));

  Polygon_with_holes_2 poly(outer);
  poly.add_hole(hole1);
  poly.add_hole(hole2);
  poly.add_hole(hole3);
  poly.add_hole(hole4);

  // Generic case, offset of the outer boundary and of the hole are distinct
  std::cout << "Interior skeleton and offset, value: 2" << std::endl;
  Polygon_with_holes_2_ptr_container offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(0.5, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  // @todo
//  assert(offset_poly_with_holes.size() == 1);
//  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
//  assert(offset_poly_with_holes[0]->number_of_holes() == 1);
//  assert(offset_poly_with_holes[0]->holes_begin()->size() == 4);
}

// Test what happens when part an offset polygon collapses
template <typename K>
void test_offset_L()
{
  std::cout << " --- Test L, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::Point_2                                                  Point;
  typedef CGAL::Polygon_2<K>                                                   Polygon_2;
  typedef boost::shared_ptr<Polygon_2>                                         Polygon_ptr;
  typedef std::vector<Polygon_ptr>                                             Polygon_ptr_container;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  std::vector<Point> L;
  L.push_back(Point( 0  ,  0));
  L.push_back(Point( 0.2,  0));
  L.push_back(Point(10  ,  0));
  L.push_back(Point(10  , 10));
  L.push_back(Point( 4  , 10));
  L.push_back(Point( 4  ,  2));
  L.push_back(Point( 0.2,  2));
  L.push_back(Point( 0  ,  2));

  Skeleton_builder ssb;
  ssb.enter_contour(L.begin(), L.end());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  Polygon_ptr_container offset_polys =
    CGAL::create_interior_skeleton_and_offset_polygons_2(int(1), Polygon_2(L.begin(), L.end()), K());

  std::cout << offset_polys.size() << " polygons" << std::endl;
//  for(const auto& offp : offset_polys)
//    print_polygon(*offp);

  assert(offset_polys.size() == 1);
  assert(offset_polys[0]->size() == 4);
}

template <typename K>
void test_offset_polygon_with_hole()
{
  std::cout << " --- Test Polygon with Hole, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                             FT;
  typedef typename K::Point_2                                        Point;

  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                    Polygon_with_holes_2_ptr;
  typedef std::vector<Polygon_with_holes_2_ptr>                      Polygon_with_holes_2_ptr_container;

  // Square with a non-centered square hole
  Polygon_2 outer, hole;
  outer.push_back(Point(-45, -45));
  outer.push_back(Point(  5, -45));
  outer.push_back(Point(  5,   5));
  outer.push_back(Point(-45,   5));

  hole.push_back(Point( -5, -30));
  hole.push_back(Point(  0, -30));
  hole.push_back(Point(  0, -35));
  hole.push_back(Point( -5, -35));

  Polygon_with_holes_2 poly(outer);
  poly.add_hole(hole);

  // Generic case, offset of the outer boundary and of the hole are distinct
  std::cout << "Interior skeleton and offset, value: 2" << std::endl;
  Polygon_with_holes_2_ptr_container offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(int(2), poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);
  assert(offset_poly_with_holes[0]->holes_begin()->size() == 4);

  // The offset value is such that
  // - skeleton bisectors and offset edge overlap
  // - switch from 2 CCs to 1 CC
  std::cout << "Interior skeleton and offset, value: 2.5" << std::endl;
  offset_poly_with_holes = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(2.5, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 8);
  assert(offset_poly_with_holes[0]->number_of_holes() == 0);

  // Same, but using a different skeleton kernel
  std::cout << "Interior skeleton and offset, value: 2.5 (EPICK)" << std::endl;
  offset_poly_with_holes = create_interior_skeleton_and_offset_polygons_with_holes_2(2.5, poly, K(), EPICK());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 8);
  assert(offset_poly_with_holes[0]->number_of_holes() == 0);

  // Single CC in the offset
  std::cout << "Interior skeleton and offset, value: 3.5" << std::endl;
  offset_poly_with_holes = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(3.5, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 8);
  assert(offset_poly_with_holes[0]->number_of_holes() == 0);

  // Very large value, no offset polygon
  std::cout << "Interior skeleton and offset, value: 100" << std::endl;
  offset_poly_with_holes = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(FT(100), poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 0);
}

template <typename K>
void test_offset_pinched()
{
  std::cout << " --- Test Pinched, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                                       FT;
  typedef typename K::Point_2                                                  Point;
  typedef CGAL::Polygon_2<K>                                                   Polygon_2;
  typedef boost::shared_ptr<Polygon_2>                                         Polygon_ptr;
  typedef std::vector<Polygon_ptr>                                             Polygon_ptrs;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  typedef CGAL::Polygon_offset_builder_traits_2<K>                             Offset_builder_traits;
  typedef CGAL::Polygon_offset_builder_2<Ss, Offset_builder_traits, Polygon_2> Offset_builder;

  // This looks like this:
  // ---------------------------      ---------------------------
  // |                          \    /                          |
  // |                           \  /                           |
  // |                            \/                            |
  // |                                                          |
  // |                                                          |
  // |                            /\                            |
  // |                           /  \                           |
  // |                          /    \                          |
  // |_________________________/      \_________________________|

  Polygon_2 input;
  input.push_back(Point(0, 0));
  input.push_back(Point(20, 0));
  input.push_back(Point(30, 10));
  input.push_back(Point(40, 0));
  input.push_back(Point(60, 0));
  input.push_back(Point(60, 40));
  input.push_back(Point(40, 40));
  input.push_back(Point(30, 30));
  input.push_back(Point(20, 40));
  input.push_back(Point(0, 40));

  Skeleton_builder ssb;
  ssb.enter_contour(input.begin(), input.end());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  // The two splitting fronts meet in the middle, and at that time,
  // we go from a single offset polygon to two polygons
  FT time_at_split = 1;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
    if(vit->is_split())
      time_at_split = vit->time();

  Polygon_ptrs offset_polys;
  Offset_builder ob(*ss);

  // Before, 1 polygon
  ob.construct_offset_contours(time_at_split - FT(1), std::back_inserter(offset_polys));

//  for(const Polygon_ptr p : offset_polys)
//    print_polygon(*p);

  assert(offset_polys.size() == 1);
  assert(offset_polys[0]->size() == 10);

  // At the exact splitting time
  offset_polys.clear();
  ob.construct_offset_contours(time_at_split, std::back_inserter(offset_polys));

//  for(const Polygon_ptr p : offset_polys)
//    print_polygon(*p);

  // This splits into two polygons, but might have just created a pinched polygon too...
//  assert(offset_polys.size() == 2);
//  assert(offset_polys[0]->size() == 5);
//  assert(offset_polys[1]->size() == 5);
}

template <typename K>
void test_offset_multiple_CCs()
{
  std::cout << " --- Test Multi CC, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                                       FT;
  typedef typename K::Point_2                                                  Point_2;
  typedef CGAL::Polygon_2<K>                                                   Contour;
  typedef boost::shared_ptr<Contour>                                           ContourPtr;
  typedef std::vector<ContourPtr>                                              Contour_sequence;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  typedef CGAL::Polygon_offset_builder_traits_2<K>                             Offset_builder_traits;
  typedef CGAL::Polygon_offset_builder_2<Ss, Offset_builder_traits, Contour>   Offset_builder;

  Point_2 pts[] = { Point_2(  0,   0),
                    Point_2(700,   0),
                    Point_2(700, 600),
                    Point_2(  0, 600),
                    Point_2(  0, 300),
                    Point_2(300, 300),
                    Point_2(300, 400),
                    Point_2(600, 400),
                    Point_2(600, 100),
                    Point_2(300, 100),
                    Point_2(300, 200),
                    Point_2(  0, 200)
                  };
  std::vector<Point_2> input(pts, pts+12);

  const FT offset = 50;
  boost::optional<FT> margin = CGAL::compute_outer_frame_margin(input.begin(), input.end(), offset);
  assert(margin);

  // Get the bbox of the polygon
  CGAL::Bbox_2 bbox = CGAL::bbox_2(input.begin(), input.end());

  // Compute the boundaries of the frame
  double m = CGAL::to_double(*margin);
  double fxmin = bbox.xmin() - m;
  double fxmax = bbox.xmax() + m;
  double fymin = bbox.ymin() - m;
  double fymax = bbox.ymax() + m;

  // Create the rectangular frame
  Point_2 frame[4] = { Point_2(fxmin, fymin),
                       Point_2(fxmax, fymin),
                       Point_2(fxmax, fymax),
                       Point_2(fxmin, fymax)
                     };

  Skeleton_builder ssb;

  ssb.enter_contour(frame, frame+4);
  ssb.enter_contour(input.rbegin(), input.rend());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  Contour_sequence offset_contours;
  Offset_builder ob(*ss);
  ob.construct_offset_contours(offset, std::back_inserter(offset_contours));

//  for(const ContourPtr p : offset_contours)
//    print_polygon(*p);

  assert(offset_contours.size() == 3);
}

template <typename K>
void test_offset_non_manifold()
{
  std::cout << " --- Test Non Manifold #1, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                                       FT;
  typedef typename K::Point_2                                                  Point;
  typedef CGAL::Polygon_2<K>                                                   Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                                        Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                              Polygon_with_holes_ptr;
  typedef std::vector<Polygon_with_holes_ptr>                                  Polygon_with_holes_ptrs;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  // Square with a diamond
  std::vector<Point> outer, hole;
  outer.push_back(Point( 0,  0));
  outer.push_back(Point(10,  0));
  outer.push_back(Point(10, 10));
  outer.push_back(Point( 0, 10));

  hole.push_back(Point( 2,  6));
  hole.push_back(Point( 5,  9));
  hole.push_back(Point( 8,  6));
  hole.push_back(Point( 5,  3));

  Skeleton_builder ssb;
  ssb.enter_contour(outer.begin(), outer.end());
  ssb.enter_contour(hole.begin(), hole.end());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  // The two splitting fronts meet in the middle, and at that time,
  // we go from a single offset polygon to two polygons
  std::set<FT> times;

  FT time_at_split = 100;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
  {
    times.insert(vit->time());
    if(vit->is_split())
      if(vit->time() < time_at_split)
        time_at_split = vit->time();
  }

  Polygon_with_holes_2 poly(Polygon_2(outer.begin(), outer.end()));
  poly.add_hole(Polygon_2(hole.begin(), hole.end()));

  // Basic, one polygon with a hole
  Polygon_with_holes_ptrs offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  // The way the algorithm currently works, this creates a non-simple polygon
  // and not a square offset with a diamond-hole tangent to its border
  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);
  assert(offset_poly_with_holes[0]->holes_begin()->size() == 4);

  // Outer and hole polygons merge into a single polygon
  std::cout << "time_at_split: " << time_at_split << std::endl;
  offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(time_at_split, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  // The way the algorithm currently works, this sometimes creates a non-simple (but strictly simple) polygon
  // and not a square offset with a diamond-hole tangent to its border
  assert(offset_poly_with_holes.size() == 1);
//  assert(offset_poly_with_holes[0]->outer_boundary().size() == 9);
//  assert(offset_poly_with_holes[0]->number_of_holes() == 0);
}

template <typename K>
void test_offset_non_manifold_2()
{
  std::cout << " --- Test Non Manifold #2, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                                       FT;
  typedef typename K::Point_2                                                  Point;
  typedef CGAL::Polygon_2<K>                                                   Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                                        Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                              Polygon_with_holes_ptr;
  typedef std::vector<Polygon_with_holes_ptr>                                  Polygon_with_holes_ptrs;

  typedef CGAL::Straight_skeleton_2<K>                                         Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                          Skeleton_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Skeleton_builder_traits, Ss>       Skeleton_builder;

  std::vector<Point> outer, hole;
  outer.push_back(Point(15, 10));
  outer.push_back(Point(20, 20));
  outer.push_back(Point(25, 10));
  outer.push_back(Point(25,  0));
  outer.push_back(Point(40,  0));
  outer.push_back(Point(40, 45));
  outer.push_back(Point( 0, 45));
  outer.push_back(Point( 0,  0));
  outer.push_back(Point(15,  0));

  hole.push_back(Point( 5,  5));
  hole.push_back(Point( 5, 40));
  hole.push_back(Point(35, 40));
  hole.push_back(Point(35,  5));
  hole.push_back(Point(30,  5));
  hole.push_back(Point(30, 25));
  hole.push_back(Point(10, 25));
  hole.push_back(Point(10,  5));

  Skeleton_builder ssb;
  ssb.enter_contour(outer.begin(), outer.end());
  ssb.enter_contour(hole.begin(), hole.end());

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  // Similar to the previous function, a split event happens and at that particular time,
  // the offset of the hole and of the outer border merge into a single polygon
  FT time_at_split = 100;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
  {
    if(vit->is_split())
      if(vit->time() < time_at_split)
        time_at_split = vit->time();
  }

  Polygon_with_holes_2 poly(Polygon_2(outer.begin(), outer.end()));
  poly.add_hole(Polygon_2(hole.begin(), hole.end()));

  // Before, one polygon with a hole
  Polygon_with_holes_ptrs offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(0.1, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 9);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);
  assert(offset_poly_with_holes[0]->holes_begin()->size() == 8);

  // At the split
  std::cout << "time_at_split: " << time_at_split << std::endl;
  offset_poly_with_holes =
    CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(time_at_split, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  // Current implementation will give a pinched polygon, but it could be a polygon with one incident hole
  assert(offset_poly_with_holes.size() == 1);
//  assert(offset_poly_with_holes[0]->outer_boundary().size() == 18);
//  assert(offset_poly_with_holes[0]->number_of_holes() == 0);
}

template <typename K>
void test_offset_polygon_exterior()
{
  std::cout << " --- Test Polygon exterior, kernel: " << typeid(K).name() << std::endl;

  typedef typename K::FT                                             FT;
  typedef typename K::Point_2                                        Point;

  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                    Polygon_with_holes_2_ptr;
  typedef std::vector<Polygon_with_holes_2_ptr>                      Polygon_with_holes_2_ptr_container;

  Polygon_2 poly;
  poly.push_back(Point( 0,  0));
  poly.push_back(Point(40,  0));
  poly.push_back(Point(40, 50));
  poly.push_back(Point( 0, 50));
  poly.push_back(Point( 0, 30));
  poly.push_back(Point(10, 30));
  poly.push_back(Point(10, 40));
  poly.push_back(Point(30, 40));
  poly.push_back(Point(30, 10));
  poly.push_back(Point(10, 10));
  poly.push_back(Point(10, 20));
  poly.push_back(Point( 0, 20));

  // -----------------------------------------------------------------------------------------------
  // Outer skeleton and offset
  std::cout << "Outer skeleton and offset, value: 0.1" << std::endl;
  Polygon_with_holes_2_ptr_container offset_poly_with_holes =
    create_exterior_skeleton_and_offset_polygons_with_holes_2(0.1, poly, K());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 1);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);
  assert(offset_poly_with_holes[0]->holes_begin()->size() == 12);

  // -----------------------------------------------------------------------------------------------
  // Value such that it is clearly separated into two contours
  std::cout << "Outer skeleton and offset, value: 7" << std::endl;
  offset_poly_with_holes =
    create_exterior_skeleton_and_offset_polygons_with_holes_2(FT(7), poly, K(), EPICK());

//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 2);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);

  // Technically both polygons below should be rectangles, but the algorithm puts a 5th vertex collinear.
  // Tolerating it for now...

  // assert(offset_poly_with_holes[0]->holes_begin()->size() == 4);
  // assert(offset_poly_with_holes[1]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->holes_begin()->is_simple());
  assert(offset_poly_with_holes[1]->outer_boundary().is_simple());

  // -----------------------------------------------------------------------------------------------
  // Border value between a single contour and two contours
  std::cout << "Outer skeleton and offset, value: 5" << std::endl;
  offset_poly_with_holes =
    create_exterior_skeleton_and_offset_polygons_with_holes_2(5., poly, K(), EPICK());

  std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//  for(const auto& offp : offset_poly_with_holes)
//    print_polygon_with_holes(*offp);

  assert(offset_poly_with_holes.size() == 2);
  assert(offset_poly_with_holes[0]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->number_of_holes() == 1);

  // Technically both polygons below should be rectangles, but the algorithm puts a 5th vertex collinear.
  // Tolerating it for now...

  // assert(offset_poly_with_holes[0]->holes_begin()->size() == 4);
  // assert(offset_poly_with_holes[1]->outer_boundary().size() == 4);
  assert(offset_poly_with_holes[0]->holes_begin()->is_simple());
  assert(offset_poly_with_holes[1]->outer_boundary().is_simple());
}

template <typename K>
void test_offset(const char* filename)
{
  std::cout << "Construct inner offset of input: " << filename
            << ", Kernel = " << typeid(K).name() << std::endl;

  typedef typename K::FT                                             FT;
  typedef typename K::Point_2                                        Point;

  typedef CGAL::Polygon_2<K>                                         Polygon_2;
  typedef CGAL::Polygon_with_holes_2<K>                              Polygon_with_holes_2;
  typedef boost::shared_ptr<Polygon_with_holes_2>                    Polygon_with_holes_2_ptr;
  typedef std::vector<Polygon_with_holes_2_ptr>                      Polygon_with_holes_2_ptr_container;

  typedef CGAL::Straight_skeleton_2<K>                               Ss;

  typedef CGAL::Straight_skeleton_builder_traits_2<K>                Ss_builder_traits;
  typedef CGAL::Straight_skeleton_builder_2<Ss_builder_traits, Ss>   Skeleton_builder;

  typedef CGAL::Min_circle_2_traits_2<K>                             Traits;
  typedef CGAL::Min_circle_2<Traits>                                 Min_circle;

  std::ifstream in(filename);
  assert(in);

  CGAL::set_ascii_mode(in);

  std::vector<Point> points;
  std::vector<Polygon_2> polys;

  int ccb_count = 0;
  in >> ccb_count;
  for(int i=0; i<ccb_count && in; ++i)
  {
    std::vector<Point> poly;

    int v_count = 0;
    in >> v_count;
    for(int j=0; j<v_count && in; ++j)
    {
      double x = 0., y = 0.;
      in >> x >> y;
      points.emplace_back(x, y);
      poly.push_back(points.back());
    }

    if(poly.size() >= 3)
    {
      bool is_simple = CGAL::is_simple_2(poly.begin(), poly.end(), K());
      if(!is_simple)
        std::cerr << "Input polygon not simple (hopefully it is strictly simple...)" << std::endl;

      CGAL::Orientation expected = (i == 0 ? CGAL::COUNTERCLOCKWISE : CGAL::CLOCKWISE);

      const double area = CGAL::to_double(CGAL::polygon_area_2(poly.begin(), poly.end(), K()));
      CGAL::Orientation orientation = area > 0 ? CGAL::COUNTERCLOCKWISE : area < 0 ? CGAL::CLOCKWISE : CGAL::COLLINEAR;

      if(orientation == expected)
        polys.push_back(Polygon_2(poly.begin(), poly.end()));
      else
        polys.push_back(Polygon_2(poly.rbegin(), poly.rend()));
    }
  }

  assert(!polys.empty());

  Polygon_with_holes_2 p(polys[0]);
  Skeleton_builder ssb;
  ssb.enter_contour(polys[0].begin(), polys[0].end());

  for(std::size_t i=0; i<polys.size()-1; ++i)
  {
    p.add_hole(polys[i+1]);
    ssb.enter_contour(polys[i+1].begin(), polys[i+1].end());
  }

  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
  assert(is_valid<K>(ss));

  std::set<FT> offset_times;
  for(auto vit=ss->vertices_begin(); vit!=ss->vertices_end(); ++vit)
    if(vit->time() != 0)
      offset_times.insert(vit->time());

  Min_circle mc(points.begin(), points.end(), true /*randomize*/);
  const FT r = CGAL::approximate_sqrt(mc.circle().squared_radius());
  const FT offt = 0.05 * r; // 10% of the radius of the min enclosing circle
  offset_times.insert(offt);

  // Offset construction
  int i = 0;
  for(const FT& ot : offset_times)
  {
    std::cout << "Offset #" << i++ << " = " << ot << std::endl;
    Polygon_with_holes_2_ptr_container offset_poly_with_holes =
      CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(ot, p, K());

    std::cout << offset_poly_with_holes.size() << " polygons with holes" << std::endl;
//    for(const auto& offp : offset_poly_with_holes)
//      print_polygon_with_holes(*offp);
    CGAL::set_use_polygon_assertions(false);
    for(const auto& offp : offset_poly_with_holes){
      (void)offp;
      assert(offp->outer_boundary().is_counterclockwise_oriented());
    }
    CGAL::set_use_polygon_assertions(true);
#ifdef CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE
    if(i > 2)
      break;
#endif
  }
}

template <typename K>
void test_kernel()
{
  std::cout.precision(17);
  std::cerr.precision(17);

#ifndef CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE
  test_API<K>();
#endif

  // Artificial data
  test_offset_square<K>();
  test_offset_four_square_holes<K>();
  test_offset_L<K>();
  test_offset_polygon_with_hole<K>();
  test_offset_pinched<K>();
  test_offset_non_manifold<K>();
  test_offset_non_manifold_2<K>();
  test_offset_polygon_exterior<K>();
  test_offset_multiple_CCs<K>();

  // Real data
  test_offset<K>("data/1_Example.poly");
  test_offset<K>("data/1_Example_Working.poly");
  test_offset<K>("data/2_Example.poly");
  test_offset<K>("data/5-SPOKE2.poly");
  test_offset<K>("data/5-SPOKE.poly");
  test_offset<K>("data/7-SPOKE.poly");
  test_offset<K>("data/AlmostClosed.poly");
  test_offset<K>("data/A.poly");
  test_offset<K>("data/closer_edge_event_0.poly");
  test_offset<K>("data/closer_edge_event_1.poly");
  test_offset<K>("data/consecutive_coincident_vertices_0.poly");
  test_offset<K>("data/consecutive_coincident_vertices_1.poly");
  test_offset<K>("data/consecutive_coincident_vertices_2.poly");
  test_offset<K>("data/consecutive_coincident_vertices_3.poly");
  test_offset<K>("data/consecutive_coincident_vertices_4.poly");
  test_offset<K>("data/degenerate0a.poly");
  test_offset<K>("data/degenerate0.poly");
  test_offset<K>("data/degenerate2.poly");
  test_offset<K>("data/degenerate3.poly");
  test_offset<K>("data/degenerate4.poly");
  test_offset<K>("data/degenerate5a.poly");
  test_offset<K>("data/degenerate5.poly");
  test_offset<K>("data/degenerate6.poly");
  test_offset<K>("data/degenerate7.poly");
  test_offset<K>("data/degenerate8.poly");
  test_offset<K>("data/degenerate9.poly");
  test_offset<K>("data/degenerate10.poly");
  test_offset<K>("data/degenerate11.poly");
  test_offset<K>("data/degenerate12.poly");
  test_offset<K>("data/degenerate13.poly");
  test_offset<K>("data/degenerate1.poly");
  test_offset<K>("data/degenerate20.poly");
  test_offset<K>("data/degenerate21.poly");
  test_offset<K>("data/degenerate22b.poly");
  test_offset<K>("data/degenerate22c.poly");
  test_offset<K>("data/degenerate22.poly");
  test_offset<K>("data/degenerate24.poly");
  test_offset<K>("data/degenerate25.poly");
  test_offset<K>("data/degenerate26.poly");
  test_offset<K>("data/degenerate27b.poly");
  test_offset<K>("data/degenerate27c.poly");
  test_offset<K>("data/degenerate27d.poly");
  test_offset<K>("data/degenerate27e.poly");
  test_offset<K>("data/degenerate27.poly");
  test_offset<K>("data/degenerate28aa.poly");
  test_offset<K>("data/degenerate28a.poly");
  test_offset<K>("data/degenerate28b.poly");
  test_offset<K>("data/degenerate28c.poly");
  test_offset<K>("data/degenerate28x.poly");
  test_offset<K>("data/degenerate_multinode0.poly");
  test_offset<K>("data/Detmier_b.poly");
  test_offset<K>("data/Detmier_c.poly");
  test_offset<K>("data/Detmier_d.poly");
  test_offset<K>("data/Detmier_e.poly");
  test_offset<K>("data/Detmier.poly");
  test_offset<K>("data/double_edge_0.poly");
  test_offset<K>("data/double_edge_1.poly");
  test_offset<K>("data/double_edge_2.poly");
  test_offset<K>("data/double_edge.poly");
  test_offset<K>("data/double_split.poly");
  test_offset<K>("data/equal_times_0.poly");
  test_offset<K>("data/ExtraEdge_1.poly");
  test_offset<K>("data/ExtraEdge_2.poly");
  test_offset<K>("data/hole.poly");
  test_offset<K>("data/inputcircle.poly");
  test_offset<K>("data/inputsquare2.poly");
  test_offset<K>("data/inputsquare.poly");
  test_offset<K>("data/many_holes.poly");
  test_offset<K>("data/masked_double_split.poly");
  test_offset<K>("data/multinode0.poly");
  test_offset<K>("data/multinode1.poly");
  test_offset<K>("data/near_degenerate_0.poly");
  test_offset<K>("data/near_degenerate_1.poly");
  test_offset<K>("data/nearly_collinear.poly");
  test_offset<K>("data/parallels0_b.poly");
  test_offset<K>("data/parallels0.poly");
  test_offset<K>("data/parallels_1.poly");
  test_offset<K>("data/poly4b.poly");
  test_offset<K>("data/poly4.poly");
  test_offset<K>("data/poly6.poly");
  test_offset<K>("data/pseudo_split_0.poly");
  test_offset<K>("data/pseudo_split_10.poly");
  test_offset<K>("data/pseudo_split_11.poly");
  test_offset<K>("data/pseudo_split_12.poly");
  test_offset<K>("data/pseudo_split_13b.poly");
  test_offset<K>("data/pseudo_split_13.poly");
  test_offset<K>("data/pseudo_split_1.poly");
  test_offset<K>("data/pseudo_split_2.poly");
  test_offset<K>("data/pseudo_split_3.poly");
  test_offset<K>("data/pseudo_split_4.poly");
  test_offset<K>("data/pseudo_split_5b.poly");
  test_offset<K>("data/pseudo_split_5.poly");
  test_offset<K>("data/pseudo_split_6.poly");
  test_offset<K>("data/pseudo_split_7.poly");
  test_offset<K>("data/pseudo_split_8.poly");
  test_offset<K>("data/pseudo_split_9.poly");
  test_offset<K>("data/rect_4_spokes.poly");
  test_offset<K>("data/rectangle.poly");
  test_offset<K>("data/region_4.poly");
  test_offset<K>("data/rombus_4_spokes.poly");

#ifndef CGAL_SLS_TEST_SPEED_THINGS_UP_FOR_THE_TESTSUITE
  test_offset<K>("data/sample_0.poly");
  test_offset<K>("data/sample_101.poly");
  test_offset<K>("data/sample_102.poly");
  test_offset<K>("data/sample_147.poly");
  test_offset<K>("data/sample_1.poly");
  test_offset<K>("data/sample_235.poly");
  test_offset<K>("data/sample_298.poly");
  test_offset<K>("data/sample_2.poly");
  test_offset<K>("data/sample2.poly");
  test_offset<K>("data/sample_319.poly");
  test_offset<K>("data/sample_325.poly");
  test_offset<K>("data/sample_333.poly");
  test_offset<K>("data/sample_3.poly");
  test_offset<K>("data/sample3.poly");
  test_offset<K>("data/sample_46.poly");
  test_offset<K>("data/sample_4.poly");
  test_offset<K>("data/sample_5.poly");
  test_offset<K>("data/sample_638.poly");
  test_offset<K>("data/sample_698.poly");
  test_offset<K>("data/sample_6.poly");
  test_offset<K>("data/sample_73.poly");
  test_offset<K>("data/sample_85.poly");
  test_offset<K>("data/sample.poly");
  test_offset<K>("data/simple_0.poly");
  test_offset<K>("data/simple_1.poly");
  test_offset<K>("data/simple_2.poly");
  test_offset<K>("data/simple_3.poly");
  test_offset<K>("data/single_split.poly");
  test_offset<K>("data/split_at_end_0.poly");
  test_offset<K>("data/split_at_end_1.poly");
  test_offset<K>("data/split_at_end_2.poly");
  test_offset<K>("data/split_at_zero_0.poly");
  test_offset<K>("data/square.poly");
  test_offset<K>("data/star.poly");
  test_offset<K>("data/StrayCenterlines.poly");
  test_offset<K>("data/triangle.poly");
  test_offset<K>("data/wheel_13_spokes.poly");
  test_offset<K>("data/wheel_14_spokes.poly");
  test_offset<K>("data/wheel_15_spokes.poly");
  test_offset<K>("data/wheel_16_spokes_b.poly");
  test_offset<K>("data/wheel_16_spokes.poly");
  test_offset<K>("data/wiggly_03_cgal.poly");
  test_offset<K>("data/WingChiu.poly");

  // Below is particularly long
  test_offset<K>("data/alley_0.poly");
  test_offset<K>("data/alley_1.poly");
  test_offset<K>("data/alley_2.poly");
  test_offset<K>("data/alley_3.poly");
  test_offset<K>("data/inputc.poly");
  test_offset<K>("data/inputd1.poly");
  test_offset<K>("data/inputd.poly");
  test_offset<K>("data/inputG.poly");
  test_offset<K>("data/input_K.poly");
  test_offset<K>("data/inputPa.poly");
  test_offset<K>("data/inputP.poly");
  test_offset<K>("data/inputq1.poly");
  test_offset<K>("data/inputq.poly");
  test_offset<K>("data/inputT.poly");
  test_offset<K>("data/inputu.poly");
  test_offset<K>("data/large_1.poly");
  test_offset<K>("data/large_2.poly");
  test_offset<K>("data/large_3.poly");
  test_offset<K>("data/large_4.poly");
  test_offset<K>("data/complex_0.poly");
  test_offset<K>("data/complex_1.poly");
  test_offset<K>("data/complex_2.poly");
  test_offset<K>("data/complex_3.poly");
  test_offset<K>("data/complex_4.poly");
  test_offset<K>("data/complex_5.poly");
  test_offset<K>("data/wheel_128_spokes.poly");
#endif
}

int main(int, char**)
{
  test_kernel<EPICK>();
  test_kernel<EPECK>();
  test_kernel<EPECK_w_sqrt>();

  std::cout << "Done!" << std::endl;

  return EXIT_SUCCESS;
}


//
// Created by devi on 1/9/20.
//
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream>
#include <fstream>
#include "Node.h"
#include <boost/graph/astar_search.hpp>
#include <boost/graph/random.hpp>
#include <boost/random.hpp>
#include <sys/time.h>
#include <math.h>

namespace boost {
    enum vertex_Node_t { vertex_Node = 0 };
    BOOST_INSTALL_PROPERTY(vertex, Node);
}
struct VertexProps { };
struct EdgeProps { float weight; };
typedef float cost;
// euclidean distance heuristic
template <class Graph, class CostType, class LocMap>
class distance_heuristic : public boost::astar_heuristic<Graph, CostType>
{
public:
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    distance_heuristic(LocMap l, Vertex goal)
            :m_location(l), m_goal(goal) {}
    CostType operator()(Vertex u)
    {
        CostType dx = m_location[m_goal].weight - m_location[u].weight;
        return dx;
    }
private:
    LocMap m_location;
    Vertex m_goal;
};

struct found_goal {}; // exception for termination

// visitor that terminates when we find the goal
template <class Vertex>
class astar_goal_visitor : public boost::default_astar_visitor
{
public:
    astar_goal_visitor(Vertex goal) : m_goal(goal) {}
    template <class Graph>
    void examine_vertex(Vertex u, Graph& g) {
        if(u == m_goal)
            throw found_goal();
    }
private:
    Vertex m_goal;
};
enum vertex_properties_t { vertex_properties };
enum edge_properties_t { edge_properties };
namespace boost {
    BOOST_INSTALL_PROPERTY(vertex, properties);
    BOOST_INSTALL_PROPERTY(edge, properties);
}
int main() {
        typedef boost::adjacency_list< boost::listS, boost::vecS, boost::undirectedS,
        boost::property<vertex_properties_t, std::shared_ptr<Node>>,
        boost::property<boost::edge_weight_t, cost>> Graph;
        typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
        //typedef boost::property<boost::edge_weight_t, std::shared_ptr<Node>> EdgeWeightProperty;

        typedef boost::graph_traits <Graph>::edge_descriptor Edge;
        typedef boost::graph_traits <Graph>::vertex_descriptor Vertex;
        typedef std::pair<Vertex, Vertex > E;

        typedef Graph::vertex_descriptor vertex;
        typedef Graph::edge_descriptor edge_descriptor;
        typedef Graph::vertex_iterator vertex_iterator;

        const int num_nodes = 6;

        // Create Shared pointer for Node Class
        std::shared_ptr<Node> object_one = std::make_shared<Node>(123, "obj1");
        std::shared_ptr<Node> object_two = std::make_shared<Node>(456, "obj2");
        std::shared_ptr<Node> object_three = std::make_shared<Node>(789, "obj3");
        std::shared_ptr<Node> object_four = std::make_shared<Node>(101, "obj4");
        std::shared_ptr<Node> object_five = std::make_shared<Node>(121, "obj5");
        std::shared_ptr<Node> object_six = std::make_shared<Node>(145, "obj6");

        // Saving all the objects to an array
        std::shared_ptr<Node> objs [] = {object_one, object_two, object_three,
                                         object_four, object_five, object_six};

        // Lets create a graph with n nodes
        Graph g(num_nodes);

        // Create BOOST::VERTEX from the shared objects for the graph g
        Vertex vertex_one = boost::add_vertex(object_one, g);
        Vertex vertex_two = boost::add_vertex(object_two, g);
        Vertex vertex_three = boost::add_vertex(object_three, g);
        Vertex vertex_four = boost::add_vertex(object_four, g);
        Vertex vertex_five = boost::add_vertex(object_five, g);
        Vertex vertex_six = boost::add_vertex(object_six, g);

        // Weights to add to the edge
        int weights[] = { 1, 1, 2, 7, 3, 1, 1, 1 };

        EdgeProps edge_weights[] = {
                {1}, {1}, {2}, {7}, {3}, {1}
        };

        // weight_map weights for the edge.
        //WeightMap weightmap = get(&EdgeProps::weight, g);

        //auto weight_map = get(boost::edge_weight_t, g);

        // Edge array with all the pairs to be joined with a path
        E edge_array[] = {
                E(vertex_one, vertex_two), E(vertex_one, vertex_three), E(vertex_two, vertex_six),
                E(vertex_three, vertex_five), E(vertex_five, vertex_two), E(vertex_six, vertex_one)
        };

        // Get the num_edges
        std::size_t num_edges = sizeof(edge_array) / sizeof(E);

        // Now lets add edges and change their weights
        WeightMap weightmap = get(boost::edge_weight, g);
        for(std::size_t j = 0; j < num_edges; ++j) {
            edge_descriptor e; bool inserted;
            boost::tie(e, inserted) = add_edge(edge_array[j].first,
                                               edge_array[j].second, g);
            weightmap[e] = weights[j];
        }

//        // Now lets add edges and change their weights
//        for(std::size_t j = 0; j < num_edges; ++j) {
//            add_edge(edge_array[j].first,
//                     edge_array[j].second, g);
//            weight_map[j] = weights[j];
//        }

        // get the updated edges weights
//        weight_map = get(&EdgeProps::weight, g);
//
//        // traverse through them and get print the weights
//        for(std::size_t j = 0; j < num_edges; ++j) {
//            std::cout<<weight_map[j];
//        }

        // pick random start/goal
        boost::mt19937 gen(time(0));
        vertex start = random_vertex(g, gen);
        vertex goal = random_vertex(g, gen);

        // Printing the start and goal vertex's name
        std::cout << "Start vertex: " << objs[start]->get_name() << std::endl;
        std::cout << "Goal vertex: " << objs[goal]->get_name() << std::endl;

        std::vector<Graph::vertex_descriptor> p(num_vertices(g));
        std::vector<std::shared_ptr<Node>> d(num_vertices(g));
        try {
            // call astar named parameter interface
            astar_search_tree
                    (
                         g, start,
                         distance_heuristic<Graph, cost ,  EdgeProps*>
                                 (edge_weights, goal),
                         boost::predecessor_map(&p[0]).distance_map(&d[0]).
                                 visitor(astar_goal_visitor<vertex>(goal))
                     );

        } catch(found_goal fg) { // found a path to the goal
            std::list<vertex> shortest_path;
            for(vertex v = goal;; v = p[v]) {
                shortest_path.push_front(v);
                if(p[v] == v)
                    break;
            }
            std::cout << "Shortest path from " << objs[start]->get_name() << " to "
                 << objs[goal]->get_name() << ": ";
            std::list<vertex>::iterator spi = shortest_path.begin();
            std::cout << objs[start]->get_name();
            for(++spi; spi != shortest_path.end(); ++spi)
                std::cout << " -> " << objs[*spi]->get_name();
            std::cout << std::endl << "Total travel time: " << d[goal]->get_name() << std::endl;
            return 0;
        }

        std::cout << "Didn't find a path from " << objs[start]->get_name() << "to"
             << objs[goal]->get_name() << "!" << std::endl;
        return 0;
}


#include <assert.h>
#include <algorithm>
#include <boost/math/distributions/normal.hpp>

#include "process.hh"
#include "mypoisson.hh"

using namespace boost::math;

Process::Process( const double maximum_rate, const double s_brownian_motion_rate, const double s_outage_escape_rate, const int bins )
  : _probability_mass_function( bins, maximum_rate, 0 ),
    _gaussian( maximum_rate, bins * 128 ),
    _brownian_motion_rate( s_brownian_motion_rate ),
    _outage_escape_rate( s_outage_escape_rate ),
    _normalized( false )
{
  normalize();
}

void Process::observe( const double time, const int counts )
{
  _normalized = false;

  /* multiply by likelihood function */
  _probability_mass_function.for_each( [&]
				       ( const double midpoint, double & value, const unsigned int index __attribute((unused)) )
				       {
					 value *= poissonpdf( midpoint * time, counts );
				       } );
}

void Process::normalize( void )
{
  if ( _normalized ) {
    return;
  }

  double sum( 0 );

  /* find the total */
  _probability_mass_function.for_each( [&sum]
				       ( const double, const double & value, const unsigned int )
				       {
					 sum += value;
				       } );

  /* normalize */
  _probability_mass_function.for_each( [&sum]
				       ( const double, double & value, const unsigned int )
				       {
					 value /= sum;
				       } );

  _normalized = true;
}

void Process::evolve( const double time )
{
  _normalized = false;

  /* initialize brownian motion */
  double stddev = _brownian_motion_rate * sqrt( time );
  _gaussian.calculate( stddev );

  /* initialize new pmf */
  SampledFunction new_pmf( _probability_mass_function );
  new_pmf.for_each( [] ( const double, double & value, const unsigned int ) { value = 0; } );

  const double zero_escape_probability = 1 - poissonpdf( time * _outage_escape_rate, 0 );
  assert( zero_escape_probability >= 0 );
  assert( zero_escape_probability <= 1.0 );

  _probability_mass_function.for_each( [&]
				       ( const double old_rate, const double & old_prob, const unsigned int old_index )
				       {
					 new_pmf.for_range( old_rate - 5 * stddev,
							    old_rate + 5 * stddev,
							    [&]
							    ( const double new_rate, double & new_prob, const unsigned int new_index )
							    {
							      double zfactor = 1.0;

							      if ( old_index == 0 ) {
								zfactor = ( new_index != 0 ) ? zero_escape_probability : (1 - zero_escape_probability);
							      }

							      double contribution = zfactor * old_prob
								* ( _gaussian.cdf( new_pmf.sample_ceil( new_rate ) - old_rate )
								    - _gaussian.cdf( new_pmf.sample_floor( new_rate ) - old_rate ) );

							      assert( !isnan( zfactor ) );
							      assert( !isnan( old_prob ) );
							      assert( !isnan( new_rate ) );
							      assert( !isnan( old_rate ) );
							      assert( !isnan( _gaussian.cdf( new_pmf.sample_ceil( new_rate ) - old_rate ) ) );
							      assert( !isnan( _gaussian.cdf( new_pmf.sample_floor( new_rate ) - old_rate ) ) );

							      assert( contribution >= 0.0 );
							      assert( contribution <= 1.0 );

							      new_prob += contribution;
							    } );
				       } );

  _probability_mass_function = new_pmf;
}

Process::GaussianCache::GaussianCache( const double maximum_rate, const int bins )
  : _cdf( bins, maximum_rate, -maximum_rate ),
    _stddev( -1 )
{}

void Process::GaussianCache::calculate( const double s_stddev )
{
  if ( s_stddev == _stddev ) {
    return;
  }

  _stddev = s_stddev;
  normal diffdist( 0, _stddev );

  _cdf.for_each( [&] ( const double x, double & value, const unsigned int ) { value = boost::math::cdf( diffdist, x ); } );
}

void Process::set_certain( const double rate )
{
  _normalized = false;

  unsigned int index_to_hit = _probability_mass_function.index( rate );

  _probability_mass_function.for_each( [&] ( const double,
					     double & value,
					     const unsigned int index )
				       {
					 if ( index == index_to_hit ) {
					   value = 1.0;
					 } else {
					   value = 0.0;
					 }
				       } );

  normalize();

  assert( _probability_mass_function[ rate ] == 1.0 );
}

Process & Process::operator=( const Process & other )
{
  _probability_mass_function = other._probability_mass_function;
  _gaussian = other._gaussian;
  _normalized = other._normalized;
  *( const_cast< double * >( &_brownian_motion_rate ) ) = other._brownian_motion_rate;

  return *this;
}

double Process::count_probability( const double time, const int counts )
{
  double ret = 0.0;

  _probability_mass_function.for_each( [&] ( const double rate,
					     const double & rate_probability,
					     const unsigned int )
				       {
					 ret += rate_probability * poissonpdf( rate * time, counts );
				       } );

  return ret;
}