//  Copyright John Maddock 2015.
//  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)

#ifndef BOOST_MATH_BINDINGS
#define BOOST_MATH_BINDINGS

#define ERROR_REPORTING_MODE
#include <stdexcept>

#if TEST_CXX17_CMATH

#include <cmath>

#define TEST_LIBRARY_NAME "<cmath>"

#define LOG1P_FUNCTION_TO_TEST std::log1p
#define EXPM1_FUNCTION_TO_TEST std::expm1

#define CBRT_FUNCTION_TO_TEST std::cbrt
#define ERF_FUNCTION_TO_TEST std::erf
#define ERFC_FUNCTION_TO_TEST std::erfc

#define LGAMMA_FUNCTION_TO_TEST std::lgamma
#define TGAMMA_FUNCTION_TO_TEST std::tgamma

#define BESSEL_I_FUNCTION_TO_TEST std::cyl_bessel_i
#define BESSEL_IN_FUNCTION_TO_TEST std::cyl_bessel_i
#define BESSEL_J_FUNCTION_TO_TEST std::cyl_bessel_j
#define BESSEL_JN_FUNCTION_TO_TEST std::cyl_bessel_j
#define BESSEL_JS_FUNCTION_TO_TEST std::sph_bessel
#define BESSEL_K_FUNCTION_TO_TEST std::cyl_bessel_k
#define BESSEL_KN_FUNCTION_TO_TEST std::cyl_bessel_k
#define BESSEL_Y_FUNCTION_TO_TEST std::cyl_neumann
#define BESSEL_YN_FUNCTION_TO_TEST std::cyl_neumann
#define BESSEL_YS_FUNCTION_TO_TEST std::sph_neumann

#define BETA_FUNCTION_TO_TEST std::beta

#define ELLINT_1_FUNCTION_TO_TEST std::ellint_1
#define ELLINT_1C_FUNCTION_TO_TEST std::comp_ellint_1
#define ELLINT_2_FUNCTION_TO_TEST std::ellint_2
#define ELLINT_2C_FUNCTION_TO_TEST std::comp_ellint_2
#define ELLINT_3_FUNCTION_TO_TEST std::ellint_3
#define ELLINT_3C_FUNCTION_TO_TEST std::comp_ellint_3

#define EI_FUNCTION_TO_TEST std::expint

#define LAGUERRE_FUNCTION_TO_TEST std::laguerre
#define ASSOC_LAGUERRE_FUNCTION_TO_TEST std::assoc_laguerre

inline long double legendre_p_binder(int i, long double d)
{
   if(i < 0)
      throw std::domain_error("order parameters less than 0 not supported in TR1");
   return std::legendre(i, d);
}
inline long double assoc_legendre_p_binder(int i, int j, long double d)
{
   if((i < 0) || (j < 0))
      throw std::domain_error("order parameters less than 0 not supported in TR1");
   return std::assoc_legendre(i, j, d);
}

#define LEGENDRE_P_FUNCTION_TO_TEST legendre_p_binder
#define LEGENDRE_PA_FUNCTION_TO_TEST assoc_legendre_p_binder
#define ZETA_FUNCTION_TO_TEST std::riemann_zeta

#define TYPE_TO_TEST long double

#elif defined(TEST_C99)

#include <math.h>

#define TEST_LIBRARY_NAME "<math.h>"

#ifdef _MSC_VER

#define LOG1P_FUNCTION_TO_TEST ::log1p
#define EXPM1_FUNCTION_TO_TEST ::expm1

#define CBRT_FUNCTION_TO_TEST ::cbrt
#define ERF_FUNCTION_TO_TEST ::erf
#define ERFC_FUNCTION_TO_TEST ::erfc

#define LGAMMA_FUNCTION_TO_TEST ::lgamma
#define TGAMMA_FUNCTION_TO_TEST ::tgamma
#define BESSEL_JN_FUNCTION_TO_TEST ::jn
#define BESSEL_YN_FUNCTION_TO_TEST ::yn

#define TYPE_TO_TEST double

#else

#define LOG1P_FUNCTION_TO_TEST ::log1pl
#define EXPM1_FUNCTION_TO_TEST ::expm1l

#define CBRT_FUNCTION_TO_TEST ::cbrtl
#define ERF_FUNCTION_TO_TEST ::erfl
#define ERFC_FUNCTION_TO_TEST ::erfcl

#define LGAMMA_FUNCTION_TO_TEST ::lgammal
#define TGAMMA_FUNCTION_TO_TEST ::tgammal
//#define BESSEL_JN_FUNCTION_TO_TEST ::jnl
//#define BESSEL_JN_FUNCTION_TO_TEST ::ynl

#define TYPE_TO_TEST long double
#endif

#elif defined(TEST_GSL)

#include <stdexcept>

#include <gsl/gsl_sf.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_version.h>

#define TEST_LIBRARY_NAME "GSL " GSL_VERSION

void gsl_handler(const char * reason, const char * file, int line, int gsl_errno)
{
   if(gsl_errno == GSL_ERANGE) return; // handle zero or infinity in our test code.
   throw std::domain_error(reason);
}

struct gsl_error_handler_setter
{
   gsl_error_handler_t * old_handler;
   gsl_error_handler_setter()
   {
      old_handler = gsl_set_error_handler(gsl_handler);
   }
   ~gsl_error_handler_setter()
   {
      gsl_set_error_handler(old_handler);
   }
};

static const gsl_error_handler_setter handler;

inline double gsl_bessel_ys(unsigned i, double d)
{
   return gsl_sf_bessel_yl(i, d);
}

inline double gsl_bessel_js(unsigned i, double d)
{
   return gsl_sf_bessel_jl(i, d);
}

//#define CBRT_FUNCTION_TO_TEST boost::cbrt
#define ERF_FUNCTION_TO_TEST gsl_sf_erf
#define ERFC_FUNCTION_TO_TEST gsl_sf_erfc
//#define ERF_INV_FUNCTION_TO_TEST boost::math::erf_inv
//#define ERFC_INV_FUNCTION_TO_TEST boost::math::erfc_inv

#define LGAMMA_FUNCTION_TO_TEST gsl_sf_lngamma
#define TGAMMA_FUNCTION_TO_TEST gsl_sf_gamma
//#define TGAMMA1PM1_FUNCTION_TO_TEST boost::math::tgamma1pm1

#define BESSEL_I_FUNCTION_TO_TEST gsl_sf_bessel_Inu
#define BESSEL_IN_FUNCTION_TO_TEST gsl_sf_bessel_In
//#define BESSEL_IP_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
//#define BESSEL_IPN_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
#define BESSEL_J_FUNCTION_TO_TEST  gsl_sf_bessel_Jnu
#define BESSEL_JN_FUNCTION_TO_TEST  gsl_sf_bessel_Jn
#define BESSEL_JS_FUNCTION_TO_TEST  gsl_bessel_js
//#define BESSEL_JP_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPN_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPS_FUNCTION_TO_TEST boost::math::sph_bessel_prime
#define BESSEL_K_FUNCTION_TO_TEST  gsl_sf_bessel_Knu
#define BESSEL_KN_FUNCTION_TO_TEST gsl_sf_bessel_Kn
//#define BESSEL_KP_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
//#define BESSEL_KPN_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
#define BESSEL_Y_FUNCTION_TO_TEST gsl_sf_bessel_Ynu
#define BESSEL_YN_FUNCTION_TO_TEST gsl_sf_bessel_Yn
#define BESSEL_YS_FUNCTION_TO_TEST gsl_bessel_ys
//#define BESSEL_YP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YNP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YSP_FUNCTION_TO_TEST boost::math::sph_neumann_prime

#define BETA_FUNCTION_TO_TEST gsl_sf_beta
//#define BINOMIAL_FUNCTION_TO_TEST boost::math::binomial_coefficient<T>

inline double RC(double a, double b)
{
   return gsl_sf_ellint_RC(a, b, GSL_PREC_DOUBLE);
}
inline double RD(double a, double b, double c)
{
   return gsl_sf_ellint_RD(a, b, c, GSL_PREC_DOUBLE);
}
inline double RF(double a, double b, double c)
{
   return gsl_sf_ellint_RF(a, b, c, GSL_PREC_DOUBLE);
}
inline double RJ(double a, double b, double c, double d)
{
   return gsl_sf_ellint_RJ(a, b, c, d, GSL_PREC_DOUBLE);
}

#define ELLINT_RC_FUNCTION_TO_TEST RC
#define ELLINT_RD_FUNCTION_TO_TEST RD
#define ELLINT_RF_FUNCTION_TO_TEST RF
//#define ELLINT_RG_FUNCTION_TO_TEST boost::math::ellint_rg
#define ELLINT_RJ_FUNCTION_TO_TEST RJ

#define DIGAMMA_FUNCTION_TO_TEST  gsl_sf_psi

inline double ellintK(double a) { return gsl_sf_ellint_Kcomp(a, GSL_PREC_DOUBLE); }
inline double ellintE(double a) { return gsl_sf_ellint_Ecomp(a, GSL_PREC_DOUBLE); }
inline double ellintP(double a, double b) { return gsl_sf_ellint_Pcomp(a, -b, GSL_PREC_DOUBLE); }

inline double ellintF(double a, double b) { return gsl_sf_ellint_F(b, a, GSL_PREC_DOUBLE); }
inline double ellintE2(double a, double b) { return gsl_sf_ellint_E(b, a, GSL_PREC_DOUBLE); }
inline double ellintP3(double a, double b, double c) { return gsl_sf_ellint_P(c, a, -b, GSL_PREC_DOUBLE); }
inline double ellintD2(double a, double b) { return gsl_sf_ellint_D(b, a, GSL_PREC_DOUBLE); }

#define ELLINT_1_FUNCTION_TO_TEST ellintF
#define ELLINT_1C_FUNCTION_TO_TEST ellintK
#define ELLINT_2_FUNCTION_TO_TEST ellintE2
#define ELLINT_2C_FUNCTION_TO_TEST ellintE
#define ELLINT_3_FUNCTION_TO_TEST ellintP3
#define ELLINT_3C_FUNCTION_TO_TEST ellintP
#define ELLINT_D2_FUNCTION_TO_TEST ellintD2
//#define ELLINT_D1_FUNCTION_TO_TEST boost::math::ellint_d

#define EI_FUNCTION_TO_TEST gsl_sf_expint_Ei
#define EN_FUNCTION_TO_TEST  gsl_sf_expint_En

//#define HERMITE_FUNCTION_TO_TEST boost::math::hermite
//#define HEUMAN_LAMBDA_FUNCTION_TO_TEST boost::math::heuman_lambda

//#define BETA_INC_FUNCTION_TO_TEST boost::math::beta
//#define BETAC_INC_FUNCTION_TO_TEST boost::math::betac
#define IBETA_FUNCTION_TO_TEST gsl_sf_beta_inc
//#define IBETAC_FUNCTION_TO_TEST boost::math::ibetac
//#define IBETA_INV_FUNCTION_TO_TEST boost::math::ibeta_inv
//#define IBETAC_INV_FUNCTION_TO_TEST boost::math::ibetac_inv
//#define IBETA_INVA_FUNCTION_TO_TEST boost::math::ibeta_inva
//#define IBETAC_INVA_FUNCTION_TO_TEST boost::math::ibetac_inva
//#define IBETA_INVB_FUNCTION_TO_TEST boost::math::ibeta_invb
//#define IBETAC_INVB_FUNCTION_TO_TEST boost::math::ibetac_invb

#define IGAMMA_FUNCTION_TO_TEST gsl_sf_gamma_inc
//#define IGAMMAL_FUNCTION_TO_TEST boost::math::tgamma_lower
#define GAMMAP_FUNCTION_TO_TEST gsl_sf_gamma_inc_P
#define GAMMAQ_FUNCTION_TO_TEST gsl_sf_gamma_inc_Q
//#define GAMMAP_INV_FUNCTION_TO_TEST boost::math::gamma_p_inv
//#define GAMMAQ_INV_FUNCTION_TO_TEST boost::math::gamma_q_inv
//#define GAMMAP_INVA_FUNCTION_TO_TEST boost::math::gamma_p_inva
//#define GAMMAQ_INVA_FUNCTION_TO_TEST boost::math::gamma_q_inva

inline double sn(double k, double u)
{
   double s, c, d;
   gsl_sf_elljac_e(u, k * k, &s, &c, &d);
   return s;
}
inline double cn(double k, double u)
{
   double s, c, d;
   gsl_sf_elljac_e(u, k * k, &s, &c, &d);
   return c;
}
inline double dn(double k, double u)
{
   double s, c, d;
   gsl_sf_elljac_e(u, k * k, &s, &c, &d);
   return d;
}

#define SN_FUNCTION_TO_TEST sn
#define CN_FUNCTION_TO_TEST cn
#define DN_FUNCTION_TO_TEST dn
//#define JACOBI_ZETA_FUNCTION_TO_TEST boost::math::jacobi_zeta

inline double laguerre(unsigned n, unsigned m, double x){ return gsl_sf_laguerre_n(n, m, x); }
inline double laguerre_0(unsigned n, double x){ return gsl_sf_laguerre_n(n, 0, x); }

#define LAGUERRE_FUNCTION_TO_TEST laguerre_0
#define ASSOC_LAGUERRE_FUNCTION_TO_TEST laguerre

inline double legendre_q(unsigned n, double x) { return gsl_sf_legendre_Ql(n, x); }

#define LEGENDRE_P_FUNCTION_TO_TEST gsl_sf_legendre_Pl
#define LEGENDRE_Q_FUNCTION_TO_TEST  legendre_q
#define LEGENDRE_PA_FUNCTION_TO_TEST  gsl_sf_legendre_Plm

#define POLYGAMMA_FUNCTION_TO_TEST  gsl_sf_psi_n
//#define TGAMMA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_ratio
//#define TGAMMA_DELTA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_delta_ratio
//#define SIN_PI_RATIO_FUNCTION_TO_TEST boost::math::sin_pi
//#define COS_PI_RATIO_FUNCTION_TO_TEST boost::math::cos_pi
#define TRIGAMMA_RATIO_FUNCTION_TO_TEST gsl_sf_psi_1
#define ZETA_FUNCTION_TO_TEST gsl_sf_zeta

#define TYPE_TO_TEST double

#elif defined(TEST_RMATH)

#define MATHLIB_STANDALONE
#include <Rmath.h>

#undef trunc

#define TEST_LIBRARY_NAME "Rmath " R_VERSION_STRING

#define LOG1P_FUNCTION_TO_TEST log1p
#define EXPM1_FUNCTION_TO_TEST expm1

//#define CBRT_FUNCTION_TO_TEST boost::math::cbrt
//#define ERF_FUNCTION_TO_TEST boost::math::erf
//#define ERFC_FUNCTION_TO_TEST boost::math::erfc
//#define ERF_INV_FUNCTION_TO_TEST boost::math::erf_inv
//#define ERFC_INV_FUNCTION_TO_TEST boost::math::erfc_inv

#define LGAMMA_FUNCTION_TO_TEST lgammafn
#define TGAMMA_FUNCTION_TO_TEST gammafn
//#define TGAMMA1PM1_FUNCTION_TO_TEST boost::math::tgamma1pm1

inline double I(double n, double x)
{
   if (x < 0)
      throw std::domain_error("Unsupported domain");
   return bessel_i(x, n, 1);
}
inline double K(double n, double x) { return bessel_k(x, n, 1); }
inline double J(double n, double x)
{
   if (x < 0)
      throw std::domain_error("Unsupported domain");
   return bessel_j(x, n);
}
inline double Y(double n, double x) { return bessel_y(x, n); }

#define BESSEL_I_FUNCTION_TO_TEST I
#define BESSEL_IN_FUNCTION_TO_TEST I
//#define BESSEL_IP_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
//#define BESSEL_IPN_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
#define BESSEL_J_FUNCTION_TO_TEST J
#define BESSEL_JN_FUNCTION_TO_TEST J
//#define BESSEL_JS_FUNCTION_TO_TEST boost::math::sph_bessel
//#define BESSEL_JP_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPN_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPS_FUNCTION_TO_TEST boost::math::sph_bessel_prime
#define BESSEL_K_FUNCTION_TO_TEST K
#define BESSEL_KN_FUNCTION_TO_TEST K
//#define BESSEL_KP_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
//#define BESSEL_KPN_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
#define BESSEL_Y_FUNCTION_TO_TEST Y
#define BESSEL_YN_FUNCTION_TO_TEST Y
//#define BESSEL_YS_FUNCTION_TO_TEST boost::math::sph_neumann
//#define BESSEL_YP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YNP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YSP_FUNCTION_TO_TEST boost::math::sph_neumann_prime

#define BETA_FUNCTION_TO_TEST beta
//#define BINOMIAL_FUNCTION_TO_TEST boost::math::binomial_coefficient<T>

//#define ELLINT_RC_FUNCTION_TO_TEST boost::math::ellint_rc
//#define ELLINT_RD_FUNCTION_TO_TEST boost::math::ellint_rd
//#define ELLINT_RF_FUNCTION_TO_TEST boost::math::ellint_rf
//#define ELLINT_RG_FUNCTION_TO_TEST boost::math::ellint_rg
//#define ELLINT_RJ_FUNCTION_TO_TEST boost::math::ellint_rj

#define DIGAMMA_FUNCTION_TO_TEST digamma

//#define ELLINT_1_FUNCTION_TO_TEST boost::math::ellint_1
//#define ELLINT_1C_FUNCTION_TO_TEST boost::math::ellint_1
//#define ELLINT_2_FUNCTION_TO_TEST boost::math::ellint_2
//#define ELLINT_2C_FUNCTION_TO_TEST boost::math::ellint_2
//#define ELLINT_3_FUNCTION_TO_TEST boost::math::ellint_3
//#define ELLINT_3C_FUNCTION_TO_TEST boost::math::ellint_3
//#define ELLINT_D2_FUNCTION_TO_TEST boost::math::ellint_d
//#define ELLINT_D1_FUNCTION_TO_TEST boost::math::ellint_d

//#define EI_FUNCTION_TO_TEST boost::math::expint
//#define EN_FUNCTION_TO_TEST boost::math::expint

//#define HERMITE_FUNCTION_TO_TEST boost::math::hermite
//#define HEUMAN_LAMBDA_FUNCTION_TO_TEST boost::math::heuman_lambda

inline double ibeta(double a, double b, double x) { return pbeta(x, a, b, 1, 0); }
inline double ibetac(double a, double b, double x) { return pbeta(x, a, b, 0, 0); }
inline double ibeta_inv(double a, double b, double x) { return qbeta(x, a, b, 1, 0); }
inline double ibetac_inv(double a, double b, double x) { return qbeta(x, a, b, 0, 0); }

//#define BETA_INC_FUNCTION_TO_TEST boost::math::beta
//#define BETAC_INC_FUNCTION_TO_TEST boost::math::betac
#define IBETA_FUNCTION_TO_TEST ibeta
#define IBETAC_FUNCTION_TO_TEST ibetac
#define IBETA_INV_FUNCTION_TO_TEST ibeta_inv
#define IBETAC_INV_FUNCTION_TO_TEST ibetac_inv
//#define IBETA_INVA_FUNCTION_TO_TEST boost::math::ibeta_inva
//#define IBETAC_INVA_FUNCTION_TO_TEST boost::math::ibetac_inva
//#define IBETA_INVB_FUNCTION_TO_TEST boost::math::ibeta_invb
//#define IBETAC_INVB_FUNCTION_TO_TEST boost::math::ibetac_invb

inline double gamma_p(double a, double x) { return pgamma(x, a, 1.0, 1, 0); }
inline double gamma_q(double a, double x) { return pgamma(x, a, 1.0, 0, 0); }
inline double gamma_p_inv(double a, double x) { return qgamma(x, a, 1.0, 1, 0); }
inline double gamma_q_inv(double a, double x) { return qgamma(x, a, 1.0, 0, 0); }

//#define IGAMMA_FUNCTION_TO_TEST boost::math::tgamma
//#define IGAMMAL_FUNCTION_TO_TEST boost::math::tgamma_lower
#define GAMMAP_FUNCTION_TO_TEST gamma_p
#define GAMMAQ_FUNCTION_TO_TEST gamma_q
#define GAMMAP_INV_FUNCTION_TO_TEST gamma_p_inv
#define GAMMAQ_INV_FUNCTION_TO_TEST gamma_q_inv
//#define GAMMAP_INVA_FUNCTION_TO_TEST boost::math::gamma_p_inva
//#define GAMMAQ_INVA_FUNCTION_TO_TEST boost::math::gamma_q_inva

//#define SN_FUNCTION_TO_TEST boost::math::jacobi_sn
//#define CN_FUNCTION_TO_TEST boost::math::jacobi_cn
//#define DN_FUNCTION_TO_TEST boost::math::jacobi_dn
//#define JACOBI_ZETA_FUNCTION_TO_TEST boost::math::jacobi_zeta

//#define LAGUERRE_FUNCTION_TO_TEST boost::math::laguerre
//#define ASSOC_LAGUERRE_FUNCTION_TO_TEST boost::math::laguerre

//#define LEGENDRE_P_FUNCTION_TO_TEST boost::math::legendre_p
//#define LEGENDRE_Q_FUNCTION_TO_TEST boost::math::legendre_q
//#define LEGENDRE_PA_FUNCTION_TO_TEST boost::math::legendre_p

inline double polygamma(int n, double x)
{
   if (x < 0)
      throw std::domain_error("Outside supported domain");
   return psigamma(x, n);
}

#define POLYGAMMA_FUNCTION_TO_TEST polygamma
//#define TGAMMA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_ratio
//#define TGAMMA_DELTA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_delta_ratio
//#define SIN_PI_RATIO_FUNCTION_TO_TEST sinpi
//#define COS_PI_RATIO_FUNCTION_TO_TEST cospi
#define TRIGAMMA_RATIO_FUNCTION_TO_TEST trigamma
//#define ZETA_FUNCTION_TO_TEST boost::math::zeta

//#define SQRT1PM1_FUNCTION_TO_TEST boost::math::sqrt1pm1
//#define POWM1_FUNCTION_TO_TEST boost::math::powm1
//#define OWENS_T_FUNCTION_TO_TEST boost::math::owens_t
//#define SPHERICAL_HARMONIC_R_FUNCTION_TO_TEST boost::math::spherical_harmonic_r
//#define SPHERICAL_HARMONIC_I_FUNCTION_TO_TEST boost::math::spherical_harmonic_i

template <class T> T do_nc_beta_cdf(T a, T b, T nc, T x){ return pnbeta(x, a, b, nc, 1, 0); }
template <class T> T do_nc_beta_ccdf(T a, T b, T nc, T x){ return pnbeta(x, a, b, nc, 0, 0); }
template <class T> T do_nc_chi_squared_cdf(T df, T nc, T x){ return pnchisq(x, df, nc, 1, 0); }
template <class T> T do_nc_chi_squared_ccdf(T df, T nc, T x){ return pnchisq(x, df, nc, 0, 0); }
template <class T> T do_nc_t_cdf(T df, T nc, T x){ return pnt(x, df, nc, 1, 0); }
template <class T> T do_nc_t_ccdf(T df, T nc, T x){ return pnt(x, df, nc, 0, 0); }

#define NC_BETA_CDF_FUNCTION_TO_TEST do_nc_beta_cdf
#define NC_BETA_CCDF_FUNCTION_TO_TEST do_nc_beta_ccdf
#define NC_CHI_SQUARED_CDF_FUNCTION_TO_TEST do_nc_chi_squared_cdf
#define NC_CHI_SQUARED_CCDF_FUNCTION_TO_TEST do_nc_chi_squared_ccdf
#define NC_T_CDF_FUNCTION_TO_TEST do_nc_t_cdf
#define NC_T_CCDF_FUNCTION_TO_TEST do_nc_t_ccdf

#define TYPE_TO_TEST double

#elif defined(TEST_CEPHES)

#define TEST_LIBRARY_NAME "Cephes"
#define TYPE_TO_TEST double

extern "C" {

   double log1p(double) throw();
   double expm1(double) throw();
   double cbrt(double) throw();
   double erf(double) throw();
   double erfc(double) throw();
   double gamma(double) throw();
   double lgam(double) throw();

   double iv(double, double) throw();
   double jv(double, double) throw();
   double jn(int, double) throw();
   double kn(int, double) throw();
   double yn(int, double) throw();

   double beta(double, double)throw();
   double psi(double);

   double ellik(double, double);
   double ellpk(double);
   double ellie(double, double);
   double ellpe(double);

   double ei(double);
   // Can't get any sensible values from Cephes expn???
   //double expn(double, double);

   double incbet(double, double, double);
   double incbi(double, double, double);

   double igam(double, double);
   double igamc(double, double);
   double igami(double, double);

   double ellpj(double u, double m, double *sn, double *cn, double *dn, double *phi);

   double zetac(double);

}

inline double ellint_1(double k, double phi) { return ellik(phi, k * k); }
inline double ellint_2(double k, double phi) { return ellie(phi, k * k); }
inline double ellint_1(double k) { return ellpk(k * k); }
inline double ellint_2(double k) { return ellpe(k * k); }

inline double sn(double k, double u)
{
   double sn, cn, dn, phi;
   ellpj(u, k * k, &sn, &cn, &dn, &phi);
   return sn;
}
inline double cn(double k, double u)
{
   double sn, cn, dn, phi;
   ellpj(u, k * k, &sn, &cn, &dn, &phi);
   return cn;
}

inline double dn(double k, double u)
{
   double sn, cn, dn, phi;
   ellpj(u, k * k, &sn, &cn, &dn, &phi);
   return dn;
}

#define LOG1P_FUNCTION_TO_TEST log1p
#define EXPM1_FUNCTION_TO_TEST expm1

#define CBRT_FUNCTION_TO_TEST cbrt
#define ERF_FUNCTION_TO_TEST erf
#define ERFC_FUNCTION_TO_TEST erfc
//#define ERF_INV_FUNCTION_TO_TEST boost::math::erf_inv
//#define ERFC_INV_FUNCTION_TO_TEST boost::math::erfc_inv

#define LGAMMA_FUNCTION_TO_TEST lgam
#define TGAMMA_FUNCTION_TO_TEST gamma
//#define TGAMMA1PM1_FUNCTION_TO_TEST boost::math::tgamma1pm1

#define BESSEL_I_FUNCTION_TO_TEST iv
#define BESSEL_IN_FUNCTION_TO_TEST iv
//#define BESSEL_IP_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
//#define BESSEL_IPN_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
#define BESSEL_J_FUNCTION_TO_TEST jv
#define BESSEL_JN_FUNCTION_TO_TEST jn
//#define BESSEL_JS_FUNCTION_TO_TEST boost::math::sph_bessel
//#define BESSEL_JP_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPN_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
//#define BESSEL_JPS_FUNCTION_TO_TEST boost::math::sph_bessel_prime
//#define BESSEL_K_FUNCTION_TO_TEST boost::math::cyl_bessel_k
#define BESSEL_KN_FUNCTION_TO_TEST kn
//#define BESSEL_KP_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
//#define BESSEL_KPN_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
//#define BESSEL_Y_FUNCTION_TO_TEST boost::math::cyl_neumann
#define BESSEL_YN_FUNCTION_TO_TEST yn
//#define BESSEL_YS_FUNCTION_TO_TEST boost::math::sph_neumann
//#define BESSEL_YP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YNP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
//#define BESSEL_YSP_FUNCTION_TO_TEST boost::math::sph_neumann_prime

#define BETA_FUNCTION_TO_TEST beta
//#define BINOMIAL_FUNCTION_TO_TEST boost::math::binomial_coefficient<T>

//#define ELLINT_RC_FUNCTION_TO_TEST boost::math::ellint_rc
//#define ELLINT_RD_FUNCTION_TO_TEST boost::math::ellint_rd
//#define ELLINT_RF_FUNCTION_TO_TEST boost::math::ellint_rf
//#define ELLINT_RG_FUNCTION_TO_TEST boost::math::ellint_rg
//#define ELLINT_RJ_FUNCTION_TO_TEST boost::math::ellint_rj

#define DIGAMMA_FUNCTION_TO_TEST psi

#define ELLINT_1_FUNCTION_TO_TEST ellint_1
// Can't seem to get sensible answers from Cephes complete elliptic integrals???
//#define ELLINT_1C_FUNCTION_TO_TEST ellint_1
#define ELLINT_2_FUNCTION_TO_TEST ellint_2
//#define ELLINT_2C_FUNCTION_TO_TEST ellint_2
//#define ELLINT_3_FUNCTION_TO_TEST boost::math::ellint_3
//#define ELLINT_3C_FUNCTION_TO_TEST boost::math::ellint_3
//#define ELLINT_D2_FUNCTION_TO_TEST boost::math::ellint_d
//#define ELLINT_D1_FUNCTION_TO_TEST boost::math::ellint_d

#define EI_FUNCTION_TO_TEST ei
//#define EN_FUNCTION_TO_TEST expn

//#define HERMITE_FUNCTION_TO_TEST boost::math::hermite
//#define HEUMAN_LAMBDA_FUNCTION_TO_TEST boost::math::heuman_lambda

//#define BETA_INC_FUNCTION_TO_TEST incbet
//#define BETAC_INC_FUNCTION_TO_TEST boost::math::betac
#define IBETA_FUNCTION_TO_TEST incbet
//#define IBETAC_FUNCTION_TO_TEST boost::math::ibetac
#define IBETA_INV_FUNCTION_TO_TEST incbi
//#define IBETAC_INV_FUNCTION_TO_TEST boost::math::ibetac_inv
//#define IBETA_INVA_FUNCTION_TO_TEST boost::math::ibeta_inva
//#define IBETAC_INVA_FUNCTION_TO_TEST boost::math::ibetac_inva
//#define IBETA_INVB_FUNCTION_TO_TEST boost::math::ibeta_invb
//#define IBETAC_INVB_FUNCTION_TO_TEST boost::math::ibetac_invb

//#define IGAMMA_FUNCTION_TO_TEST boost::math::tgamma
//#define IGAMMAL_FUNCTION_TO_TEST boost::math::tgamma_lower
#define GAMMAP_FUNCTION_TO_TEST igam
#define GAMMAQ_FUNCTION_TO_TEST igamc
//#define GAMMAP_INV_FUNCTION_TO_TEST boost::math::gamma_p_inv
#define GAMMAQ_INV_FUNCTION_TO_TEST igami
//#define GAMMAP_INVA_FUNCTION_TO_TEST boost::math::gamma_p_inva
//#define GAMMAQ_INVA_FUNCTION_TO_TEST boost::math::gamma_q_inva

#define SN_FUNCTION_TO_TEST sn
#define CN_FUNCTION_TO_TEST cn
#define DN_FUNCTION_TO_TEST dn

#define ZETA_FUNCTION_TO_TEST zetac

#else

#include <boost/math/distributions/non_central_beta.hpp>
#include <boost/math/distributions/non_central_chi_squared.hpp>
#include <boost/math/distributions/non_central_t.hpp>

#define TEST_LIBRARY_NAME "boost"

#define LOG1P_FUNCTION_TO_TEST boost::math::log1p
#define EXPM1_FUNCTION_TO_TEST boost::math::expm1

#define CBRT_FUNCTION_TO_TEST boost::math::cbrt
#define ERF_FUNCTION_TO_TEST boost::math::erf
#define ERFC_FUNCTION_TO_TEST boost::math::erfc
#define ERF_INV_FUNCTION_TO_TEST boost::math::erf_inv
#define ERFC_INV_FUNCTION_TO_TEST boost::math::erfc_inv

#define LGAMMA_FUNCTION_TO_TEST boost::math::lgamma
#define TGAMMA_FUNCTION_TO_TEST boost::math::tgamma
#define TGAMMA1PM1_FUNCTION_TO_TEST boost::math::tgamma1pm1

#define BESSEL_I_FUNCTION_TO_TEST boost::math::cyl_bessel_i
#define BESSEL_IN_FUNCTION_TO_TEST boost::math::cyl_bessel_i
#define BESSEL_IP_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
#define BESSEL_IPN_FUNCTION_TO_TEST boost::math::cyl_bessel_i_prime
#define BESSEL_J_FUNCTION_TO_TEST boost::math::cyl_bessel_j
#define BESSEL_JN_FUNCTION_TO_TEST boost::math::cyl_bessel_j
#define BESSEL_JS_FUNCTION_TO_TEST boost::math::sph_bessel
#define BESSEL_JP_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
#define BESSEL_JPN_FUNCTION_TO_TEST boost::math::cyl_bessel_j_prime
#define BESSEL_JPS_FUNCTION_TO_TEST boost::math::sph_bessel_prime
#define BESSEL_K_FUNCTION_TO_TEST boost::math::cyl_bessel_k
#define BESSEL_KN_FUNCTION_TO_TEST boost::math::cyl_bessel_k
#define BESSEL_KP_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
#define BESSEL_KPN_FUNCTION_TO_TEST boost::math::cyl_bessel_k_prime
#define BESSEL_Y_FUNCTION_TO_TEST boost::math::cyl_neumann
#define BESSEL_YN_FUNCTION_TO_TEST boost::math::cyl_neumann
#define BESSEL_YS_FUNCTION_TO_TEST boost::math::sph_neumann
#define BESSEL_YP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
#define BESSEL_YNP_FUNCTION_TO_TEST boost::math::cyl_neumann_prime
#define BESSEL_YSP_FUNCTION_TO_TEST boost::math::sph_neumann_prime

#define BETA_FUNCTION_TO_TEST boost::math::beta
#define BINOMIAL_FUNCTION_TO_TEST boost::math::binomial_coefficient<T>

#define ELLINT_RC_FUNCTION_TO_TEST boost::math::ellint_rc
#define ELLINT_RD_FUNCTION_TO_TEST boost::math::ellint_rd
#define ELLINT_RF_FUNCTION_TO_TEST boost::math::ellint_rf
#define ELLINT_RG_FUNCTION_TO_TEST boost::math::ellint_rg
#define ELLINT_RJ_FUNCTION_TO_TEST boost::math::ellint_rj

#define DIGAMMA_FUNCTION_TO_TEST boost::math::digamma

#define ELLINT_1_FUNCTION_TO_TEST boost::math::ellint_1
#define ELLINT_1C_FUNCTION_TO_TEST boost::math::ellint_1
#define ELLINT_2_FUNCTION_TO_TEST boost::math::ellint_2
#define ELLINT_2C_FUNCTION_TO_TEST boost::math::ellint_2
#define ELLINT_3_FUNCTION_TO_TEST boost::math::ellint_3
#define ELLINT_3C_FUNCTION_TO_TEST boost::math::ellint_3
#define ELLINT_D2_FUNCTION_TO_TEST boost::math::ellint_d
#define ELLINT_D1_FUNCTION_TO_TEST boost::math::ellint_d

#define EI_FUNCTION_TO_TEST boost::math::expint
#define EN_FUNCTION_TO_TEST boost::math::expint

#define HERMITE_FUNCTION_TO_TEST boost::math::hermite
#define HEUMAN_LAMBDA_FUNCTION_TO_TEST boost::math::heuman_lambda

#define BETA_INC_FUNCTION_TO_TEST boost::math::beta
#define BETAC_INC_FUNCTION_TO_TEST boost::math::betac
#define IBETA_FUNCTION_TO_TEST boost::math::ibeta
#define IBETAC_FUNCTION_TO_TEST boost::math::ibetac
#define IBETA_INV_FUNCTION_TO_TEST boost::math::ibeta_inv
#define IBETAC_INV_FUNCTION_TO_TEST boost::math::ibetac_inv
#define IBETA_INVA_FUNCTION_TO_TEST boost::math::ibeta_inva
#define IBETAC_INVA_FUNCTION_TO_TEST boost::math::ibetac_inva
#define IBETA_INVB_FUNCTION_TO_TEST boost::math::ibeta_invb
#define IBETAC_INVB_FUNCTION_TO_TEST boost::math::ibetac_invb

#define IGAMMA_FUNCTION_TO_TEST boost::math::tgamma
#define IGAMMAL_FUNCTION_TO_TEST boost::math::tgamma_lower
#define GAMMAP_FUNCTION_TO_TEST boost::math::gamma_p
#define GAMMAQ_FUNCTION_TO_TEST boost::math::gamma_q
#define GAMMAP_INV_FUNCTION_TO_TEST boost::math::gamma_p_inv
#define GAMMAQ_INV_FUNCTION_TO_TEST boost::math::gamma_q_inv
#define GAMMAP_INVA_FUNCTION_TO_TEST boost::math::gamma_p_inva
#define GAMMAQ_INVA_FUNCTION_TO_TEST boost::math::gamma_q_inva

#define SN_FUNCTION_TO_TEST boost::math::jacobi_sn
#define CN_FUNCTION_TO_TEST boost::math::jacobi_cn
#define DN_FUNCTION_TO_TEST boost::math::jacobi_dn
#define JACOBI_ZETA_FUNCTION_TO_TEST boost::math::jacobi_zeta

#define LAGUERRE_FUNCTION_TO_TEST boost::math::laguerre
#define ASSOC_LAGUERRE_FUNCTION_TO_TEST boost::math::laguerre

#define LEGENDRE_P_FUNCTION_TO_TEST boost::math::legendre_p
#define LEGENDRE_Q_FUNCTION_TO_TEST boost::math::legendre_q
#define LEGENDRE_PA_FUNCTION_TO_TEST boost::math::legendre_p

#define POLYGAMMA_FUNCTION_TO_TEST boost::math::polygamma
#define TGAMMA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_ratio
#define TGAMMA_DELTA_RATIO_FUNCTION_TO_TEST boost::math::tgamma_delta_ratio
#define SIN_PI_RATIO_FUNCTION_TO_TEST boost::math::sin_pi
#define COS_PI_RATIO_FUNCTION_TO_TEST boost::math::cos_pi
#define TRIGAMMA_RATIO_FUNCTION_TO_TEST boost::math::trigamma
#define ZETA_FUNCTION_TO_TEST boost::math::zeta

#define SQRT1PM1_FUNCTION_TO_TEST boost::math::sqrt1pm1
#define POWM1_FUNCTION_TO_TEST boost::math::powm1
#define OWENS_T_FUNCTION_TO_TEST boost::math::owens_t
#define SPHERICAL_HARMONIC_R_FUNCTION_TO_TEST boost::math::spherical_harmonic_r
#define SPHERICAL_HARMONIC_I_FUNCTION_TO_TEST boost::math::spherical_harmonic_i

template <class T> T do_nc_beta_cdf(T a, T b, T nc, T x){ return cdf(boost::math::non_central_beta_distribution<T>(a, b, nc), x); }
template <class T> T do_nc_beta_ccdf(T a, T b, T nc, T x){ return cdf(complement(boost::math::non_central_beta_distribution<T>(a, b, nc), x)); }
template <class T> T do_nc_chi_squared_cdf(T df, T nc, T x){ return cdf(boost::math::non_central_chi_squared_distribution<T>(df, nc), x); }
template <class T> T do_nc_chi_squared_ccdf(T df, T nc, T x){ return cdf(complement(boost::math::non_central_chi_squared_distribution<T>(df, nc), x)); }
template <class T> T do_nc_t_cdf(T df, T nc, T x){ return cdf(boost::math::non_central_t_distribution<T>(df, nc), x); }
template <class T> T do_nc_t_ccdf(T df, T nc, T x){ return cdf(complement(boost::math::non_central_t_distribution<T>(df, nc), x)); }

#define NC_BETA_CDF_FUNCTION_TO_TEST do_nc_beta_cdf
#define NC_BETA_CCDF_FUNCTION_TO_TEST do_nc_beta_ccdf
#define NC_CHI_SQUARED_CDF_FUNCTION_TO_TEST do_nc_chi_squared_cdf
#define NC_CHI_SQUARED_CCDF_FUNCTION_TO_TEST do_nc_chi_squared_ccdf
#define NC_T_CDF_FUNCTION_TO_TEST do_nc_t_cdf
#define NC_T_CCDF_FUNCTION_TO_TEST do_nc_t_ccdf

#endif

#if defined(TYPE_TO_TEST) && !defined(NAME_OF_TYPE_TO_TEST)
#define NAME_OF_TYPE_TO_TEST BOOST_STRINGIZE(TYPE_TO_TEST)
#endif

//
// This include has to come at the end after all the setup is done:
//
#include "handle_test_result.hpp"

#endif


//
// Created by Sergej Krivonos on 25.02.18.
//
#define BOOST_TEST_MODULE Stasis test
#include <boost/test/unit_test.hpp>

#include "System.h"

using namespace omnn::math;
using namespace boost::unit_test;
using namespace std;

BOOST_AUTO_TEST_CASE(Stasis
                     ,*disabled()
                     ) // Solve magic square
{
    constexpr auto Sz = 5;
    constexpr auto m = (Sz*(Sz*Sz+1))/2;
    constexpr auto mm = -m;
    auto& x = "x"_va;
    auto& y = "y"_va;
    auto& v = "v"_va;

    auto at = [&](const Valuable& xx, const Valuable& yy, const Valuable& vv) -> Valuable {
        return x.Equals(xx).sq() + y.Equals(yy).sq() + v.Equals(vv).sq();
    };

    System world;
	world << at(0, 0, 1);

    auto getat = [&](const Valuable& xx, const Valuable& yy) {
        /*constexpr*/static Variable t = "t"_va;
        return at(xx,yy,t)(t);
    };

    Sum sumd0{mm}, sumd1{mm};
    Valuable::optimizations = {};
    for (auto xx=Sz; xx--;) {
        Sum sumx{mm}, sumy{mm};
        for (auto yy=Sz; yy--; ) {
            sumx.Add(getat(xx,yy));
            sumy.Add(getat(xx,yy));
        }
        world << sumx;
        world << sumy;
        sumd0.Add(getat(xx,xx));
        sumd1.Add(getat(Sz-xx, Sz-xx));
    }
    world << sumd0;
    world << sumd1;

    for (auto xx=Sz; xx--;) {
        auto copy = world;
        copy.Eval(x, xx);
        for (auto yy=Sz; yy--; ) {
            auto co = copy;
            co.Eval(y, yy);
            auto xyValues = co.Solve(v);
            for (auto& s : xyValues) {
            if (!s.IsInt()
//                s.size()!=1
                ) {
                IMPLEMENT
            }else{
                    std::cout << s
//                    << *s.begin()
                    << ' ';
            }
        }
        }
        std::cout << std::endl;
    }
}

BOOST_AUTO_TEST_CASE(Stasis_empty_test){} // to success end if all tests disabled


/*
arg min Sum_i Ei(Ri)
  s.t. ~Ri Ri = 1

Ei = Sum_j wij |Ri qji ~Ri - pji|^2 + A Sum_j cij |Ri - Rj|^2 + ei |Ri - R_{i-1}|^2

where:  Sum_j wij = 1    and     ei + Sum_j cij = 1

Ei = Sum_j wij |Ri qji - pji Ri|^2 + A Sum_j cij |Ri - Rj|^2 + ei |Ri - R_{i-1}|^2

Since: Sum_j wij |Ri qji - pji Ri|^2 = Ri^T Hi Ri

Ei = Ri^T Hi Ri + A Sum_j cij |Ri - Rj|^2 + ei |Ri - R_{i-1}|^2

dEi/Ri = 2 Hi Ri + 2 A sum_j(cij Ri - Rj) + 2 ei (Ri - R_{i-1})
dEi/Ri = Hi Ri + A sum_j(cij Ri) - A sum_j(cij Rj) + ei Ri - ei R_{i-1}
dEi/Ri = Hi Ri + A Ri sum_j(cij) - A sum_j(cij Rj) + ei Ri - ei R_{i-1}
dEi/Ri = (Hi + A sum_j(cij) I + ei I) Ri - sum_j(A cij Rj) - ei R_{i-1}

Setting dEi/Ri = 0

(Hi + A sum_j(cij) I + ei I) Ri - sum_j(A cij Rj) - ei R_{i-1} = 0

Setting Mi = Hi + (A sum_j(cij) + ei) I

And imposing: sum_j(cij) + ei = 1

Mi = Hi + A I

Mi Ri = sum_j(A cij Rj) + ei R_{i-1}

M R = L R + ei R_prev

Where
   M is stacking Mi at the diagonal
   L is laplacian matrix from sum_j(A cij Rj)
   R_prev is stacking feedback R_{i-1}

Solve (M - L) R = ei R_prev
s.t R = R_const,  at the constrained points.
*/
#ifdef WIN32
#define NOMINMAX
#include <windows.h>
#endif

#if defined (__APPLE__) || defined (OSX)
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
#include <GL/gl.h>
#include <GL/glut.h>
#endif

#include "GA/c3ga.h"
#include "GA/c3ga_util.h"
#include "GA/gl_util.h"

#include "primitivedraw.h"
#include "gahelper.h"
#include "Laplacian.h"

#include <memory>

#include <vector>
#include <queue>
#include <map>
#include "numerics.h"
#include "HalfEdge/Mesh.h"
#include "GARotorEstimator.h"

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <Eigen/Geometry>

// #include <ppl.h>

#include "Benchs.h"

const char *WINDOW_TITLE = "Interactive 3D Shape Deformation using Conformal Geometric Algebra";

// GLUT state information
int g_viewportWidth = 800;
int g_viewportHeight = 600;

void display();
void reshape(GLint width, GLint height);
void MouseButton(int button, int state, int x, int y);
void MouseMotion(int x, int y);
void KeyboardUpFunc(unsigned char key, int x, int y);
void SpecialFunc(int key, int x, int y);
void SpecialUpFunc(int key, int x, int y);
void Idle();
void DestroyWindow();

//using namespace boost;
using namespace c3ga;
using namespace std;
using namespace numerics;

class Camera
{
public:
	float		pos[3];
	float		fw[3];
	float		up[3];
	float		translateVel;
	float		rotateVel;

	Camera()
	{
		float		_pos[] = { 0, 0, -2};
		float		_fw[] = { 0, 0, 1 };
		float		_up[] = { 0, 1, 0 };

		translateVel = 0.005;
		rotateVel = 0.005;
		memcpy(pos, _pos, sizeof(float)*3);
		memcpy(fw, _fw, sizeof(float)*3);
		memcpy(up, _up, sizeof(float)*3);
	}

	void glLookAt()
	{
		gluLookAt( pos[0], pos[1], pos[2], fw[0],  fw[1],  fw[2], up[0],  up[1],  up[2] );
	}
};

class VertexBuffer
{
public:
	std::vector<Eigen::Vector3d> deformedPositions; //deformed mesh positions
	std::map<int, Eigen::Vector3d> constrainedPositions; //positional constraints
	std::vector<Eigen::Vector3d> laplacianCoordinates; //laplacian Coordinates
	std::vector<Eigen::Vector3d> normals; //for rendering (lighting)
	std::vector<Eigen::Vector3d> normalsOrig; //original normals
	std::vector<Eigen::Quaterniond> rotors;
	int size;

	VertexBuffer() : size(0)
	{
	}

	void resize(int size)
	{
		this->size = size;
		deformedPositions.resize(size);
		laplacianCoordinates.resize(size);
		normals.resize(size);
		normalsOrig.resize(size);
		rotors.resize(size);
	}
	int get_size() { return size; }

};

class IndexBuffer {
public:
	std::vector<int> faces;
	int size;

	IndexBuffer() : size(0)
	{
	}

	void resize(int size)
	{
		this->size = size;
		faces.resize(size);
	}
	int get_size() { return size; }

};

class Handle
{
public:
	rotor R;
	translator T;
	translator Tcenter;
	dualSphere dS;
	std::set<int> constraints;

	Handle() {
		T = _rotor(1.0);
		R = _translator(1.0);
		Tcenter = _translator(1.0);
	}
	Handle(dualSphere dS)
	{
		normalizedPoint x = DualSphereCenter(dS);
		Tcenter = exp(-0.5 * _vectorE3GA(x) * ni);
		T = _translator(1.0);
		R = _rotor(1.0);
		this->dS = dS;
	}

	TRversor GetTRVersor()
	{
		return _TRversor(T * _TRversor(Tcenter * R * inverse(Tcenter)));
	}
};

Camera g_camera;
Mesh mesh, meshLow;
map<int, int> mapping;
vectorE3GA g_prevMousePos;
bool g_rotateModel = false;
bool g_rotateModelOutOfPlane = false;
rotor g_modelRotor = _rotor(1.0);
bool g_rotateKeyRotors = false;
bool g_translateKeyRotors = false;
bool g_computeBasis = false;
float g_dragDistance = -1.0f;
int g_dragObject;
std::shared_ptr<SparseMatrix> A;
std::shared_ptr<SparseMatrix> AHi;
Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> solverLow, solverHi;
Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> solverRotations;
Eigen::SparseMatrix<double> RotationMatrix;
vector<Eigen::Triplet<double>> triplets;
int systemType = LaplaceBeltrami; //MeanValue; //LaplaceBeltrami
bool g_showSpheres = true;
bool g_showWires = false;
bool g_iterateManyTimes = false;
Eigen::MatrixXd b3Low;
Eigen::MatrixXd xyzLow;
Eigen::MatrixXd b3Hi;
Eigen::MatrixXd xyzHi;
Eigen::VectorXd Rknown;
Eigen::MatrixXd RknownHi;
Eigen::MatrixXd RbestHi;

double g_meshArea = 0.0;

bool g_automaticAnimation = false;
bool g_convergence = false;
Benchmarks benchs;

VertexBuffer vertexDescriptors;
VertexBuffer vertexDescriptorsLow;
IndexBuffer trianglesHi;
std::vector<Handle> handles;
std::set<int> allconstraints;

Eigen::Affine3d MotorToMatrix(const TRversor& R) {
	TRversor Ri = inverse(R);

	// compute images of basis vectors:
	c3ga::flatPoint imageOfE1NI = _flatPoint(R * c3ga::e1ni * Ri);
	c3ga::flatPoint imageOfE2NI = _flatPoint(R * c3ga::e2ni * Ri);
	c3ga::flatPoint imageOfE3NI = _flatPoint(R * c3ga::e3ni * Ri);
	c3ga::flatPoint imageOfNONI = _flatPoint(R * c3ga::noni * Ri);

	// create matrix representation:
	Eigen::Affine3d M;
	M(0, 0) = imageOfE1NI.m_c[0];
	M(1, 0) = imageOfE1NI.m_c[1];
	M(2, 0) = imageOfE1NI.m_c[2];
	M(3, 0) = imageOfE1NI.m_c[3];
	M(0, 1) = imageOfE2NI.m_c[0];
	M(1, 1) = imageOfE2NI.m_c[1];
	M(2, 1) = imageOfE2NI.m_c[2];
	M(3, 1) = imageOfE2NI.m_c[3];
	M(0, 2) = imageOfE3NI.m_c[0];
	M(1, 2) = imageOfE3NI.m_c[1];
	M(2, 2) = imageOfE3NI.m_c[2];
	M(3, 2) = imageOfE3NI.m_c[3];
	M(0, 3) = imageOfNONI.m_c[0];
	M(1, 3) = imageOfNONI.m_c[1];
	M(2, 3) = imageOfNONI.m_c[2];
	M(3, 3) = imageOfNONI.m_c[3];
	return M;
}

void ComputeLaplacianCoordinates(std::shared_ptr<SparseMatrix> A, Mesh* mesh, std::vector<Eigen::Vector3d>& laplacianCoordinates)
{
	std::fill(laplacianCoordinates.begin(), laplacianCoordinates.end(), Eigen::Vector3d(0, 0, 0));

	auto numRows = A->numRows();

	for (int i = 0; i < numRows; ++i)
	{
		SparseMatrix::RowIterator aIter = A->iterator(i);
		for (; !aIter.end(); ++aIter)
		{
			auto j = aIter.columnIndex();
			laplacianCoordinates[i] += mesh->vertexAt(j).p * aIter.value();
		}
	}
}

bool is_constrained(std::set<int>& constraints, int vertex)
{
	return constraints.find(vertex) != constraints.end();
}

void PreFactor(std::shared_ptr<SparseMatrix> A, std::set<int>& constraints, Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>>& solver)
{

	Eigen::SparseMatrix<double> Lc = Eigen::SparseMatrix<double>(A->numRows(), A->numColumns());

	auto numRows = A->numRows();
	for (int i = 0; i < numRows; ++i)
	{
		if (!is_constrained(constraints, i))
		{
			SparseMatrix::RowIterator aIter = A->iterator(i);
			for (; !aIter.end(); ++aIter)
			{
				auto j = aIter.columnIndex();
				Lc.insert(i, j) = (*A)(i, j);
			}
		}
		else
		{
			Lc.insert(i, i) = 1.0;
		}
	}

	Lc.makeCompressed();
	solver.compute(Lc);
	if (solver.info() != Eigen::Success) {
		// TODO: error handling
	}

}

double meshArea(Mesh *mesh)
{
	double area = 0.0;
	for(Face& face :  mesh->getFaces())
	{
		Eigen::Vector3d	u = face.edge->next->vertex->p - face.edge->vertex->p;
		Eigen::Vector3d	v = face.edge->next->next->vertex->p - face.edge->vertex->p;
		area += 0.5 * u.cross(v).norm();
	}
	return area;
}

void meshMapping(Mesh *meshLow, Mesh *mesh, map<int, int>& mapping)
{
	for (Vertex& vertexLow : meshLow->getVertices())
	{
		double minDist = 1e38;
		int mappedPoint = -1;
		Eigen::Vector3d& pLow = vertexLow.p;
		for (Vertex& vertexHi : mesh->getVertices())
		{
			Eigen::Vector3d& p = vertexHi.p;
			double dist = (pLow - p).norm();
			if (dist < minDist) {
				minDist = dist;
				mappedPoint = vertexHi.ID;
			}
		}
		mapping[vertexLow.ID] = mappedPoint;
	}
}

int main(int argc, char* argv[])
{
	mesh.readOBJ("cactus2.obj"); //cactus2.obj  armadillo.obj
	mesh.CenterAndNormalize();
	mesh.computeNormals();

	meshLow.readOBJ("cactus1.obj"); //cactus1.obj armadillo1.obj
	meshLow.CenterAndNormalize();
	meshLow.computeNormals();

	meshMapping(&meshLow, &mesh, mapping);

	// GLUT Window Initialization:
	glutInit (&argc, argv);
	glutInitWindowSize(g_viewportWidth, g_viewportHeight);
	glutInitDisplayMode( GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE | GLUT_DEPTH);
	glutCreateWindow(WINDOW_TITLE);

	// Register callbacks:
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMouseFunc(MouseButton);
	glutMotionFunc(MouseMotion);
	glutKeyboardUpFunc(KeyboardUpFunc);
	glutSpecialFunc(SpecialFunc);
	glutSpecialUpFunc(SpecialUpFunc);
	glutIdleFunc(Idle);
	atexit(DestroyWindow);

	InitializeDrawing();

	//cactus.obj
	handles.push_back(Handle(_dualSphere(c3gaPoint(.0, .04, 0.7) - 0.5*SQR(0.15)*ni)));
	handles.push_back(Handle(_dualSphere(c3gaPoint(.0, .04, -0.8) - 0.5*SQR(0.15)*ni)));

	////cylinder.obj
	//handles.push_back(Handle(_dualSphere(c3gaPoint(.0, 0.9, .0) - 0.5*SQR(0.25)*ni)));
	//handles.push_back(Handle(_dualSphere(c3gaPoint(.0, -0.9, .0) - 0.5*SQR(0.25)*ni)));

	// //Armadillo foot and hand
	// handles.push_back(Handle(_dualSphere(c3gaPoint(-.5, 0.45, -.3) - 0.5*SQR(0.15)*ni))); // hand
	// handles.push_back(Handle(_dualSphere(c3gaPoint(.2, -0.6, .1) - 0.5*SQR(0.15)*ni))); // foot
	// handles.push_back(Handle(_dualSphere(c3gaPoint(.45, 0.45, -.3) - 0.5*SQR(0.15)*ni))); // hand
	// handles.push_back(Handle(_dualSphere(c3gaPoint(-.3, -0.6, .1) - 0.5*SQR(0.15)*ni))); // foot
	// //Armadillo hip y head
	// handles.push_back(Handle(_dualSphere(c3gaPoint(.0, 0.4, -.2) - 0.5*SQR(0.15)*ni)));
	// handles.push_back(Handle(_dualSphere(c3gaPoint(.0, -0.05, .1) - 0.5*SQR(0.15)*ni)));

	vertexDescriptorsLow.resize(meshLow.numVertices());
	vertexDescriptors.resize(mesh.numVertices());
	trianglesHi.resize(mesh.numFaces() * 3);

	for( Vertex& vertexLow : meshLow.getVertices())
	{
		vertexDescriptorsLow.rotors[vertexLow.ID] = Eigen::Quaterniond::Identity();
		normalizedPoint position = c3gaPoint(vertexLow.p.x(), vertexLow.p.y(), vertexLow.p.z());

		for (Handle& handle : handles)
		{
			TRversor TR = handle.GetTRVersor();

			if (_double(position << (TR * handle.dS * inverse(TR))) > 0) //inside the sphere
			{
				handle.constraints.insert(vertexLow.ID);
				vertexDescriptorsLow.constrainedPositions[vertexLow.ID] = vertexLow.p;
			}
		}
	}

	A = CreateLaplacianMatrix( &meshLow, systemType );

	ComputeLaplacianCoordinates(A, &meshLow, vertexDescriptorsLow.laplacianCoordinates);

	b3Low = Eigen::MatrixXd(A->numRows(), 3);

	for (Handle& handle : handles) {
		allconstraints.insert(handle.constraints.begin(), handle.constraints.end());
	}
	PreFactor(A, allconstraints, solverLow);

	g_meshArea = meshArea(&meshLow);

	for (Vertex& vertexHi : mesh.getVertices()) {
		vertexDescriptors.normalsOrig[vertexHi.ID] = vertexHi.n;
		vertexDescriptors.rotors[vertexHi.ID] = Eigen::Quaterniond::Identity();
	}

	for (Face& face : mesh.getFaces()) {
		int i = face.ID;
		int	v1 = face.edge->vertex->ID;
		int	v2 = face.edge->next->vertex->ID;
		int	v3 = face.edge->next->next->vertex->ID;
		trianglesHi.faces[i * 3 + 0] = v1;
		trianglesHi.faces[i * 3 + 1] = v2;
		trianglesHi.faces[i * 3 + 2] = v3;
	}

	AHi = CreateLaplacianMatrix(&mesh, systemType);
	ComputeLaplacianCoordinates(AHi, &mesh, vertexDescriptors.laplacianCoordinates);
	std::set<int> constraintsHi;
	for (map<int, int>::iterator iter = mapping.begin(); iter != mapping.end(); iter++){
		constraintsHi.insert(iter->second);
	}
	b3Hi = Eigen::MatrixXd(AHi->numRows(), 3);
	PreFactor(AHi, constraintsHi, solverHi);

  RotationMatrix = Eigen::SparseMatrix<double>(A->numRows()*4, A->numColumns()*4);
  triplets.resize(A->NonZeros()*16);
  Rknown = Eigen::VectorXd(A->numRows()*4);
  Rknown.setZero();
  RknownHi = Eigen::MatrixXd(AHi->numRows(), 4);
  RknownHi.setZero();

	glutMainLoop();

	return 0;
}

void SolvePoissonEquation(VertexBuffer& vertexDescriptors)
{
	int n = vertexDescriptors.get_size();

	for (int i = 0; i < n; ++i) {
		b3Low.row(i) = vertexDescriptors.laplacianCoordinates[i];
	}

	for (Handle& handle : handles) {
		Eigen::Affine3d M = MotorToMatrix(handle.GetTRVersor());
		for (int i : handle.constraints) {
			b3Low.row(i) = M * vertexDescriptors.constrainedPositions[i];
		}
	}

	xyzLow = solverLow.solve(b3Low);

	for( int i = 0 ; i < n ; ++i ) {
		vertexDescriptors.deformedPositions[i] = xyzLow.row(i);
	}
}

void SolvePoissonEquationHi(VertexBuffer& vertexDescriptors, VertexBuffer& vertexDescriptorsLow)
{
	int n = vertexDescriptors.get_size();

	for (int i = 0; i < n; ++i) {
		b3Hi.row(i) = vertexDescriptors.laplacianCoordinates[i];
	}

	for (auto&& pair : mapping){
		b3Hi.row(pair.second) = vertexDescriptorsLow.deformedPositions[pair.first];
	}

	xyzHi = solverHi.solve(b3Hi);

	for (int i = 0; i < n; ++i) {
		vertexDescriptors.deformedPositions[i] = xyzHi.row(i);
		vertexDescriptors.normals[i] = vertexDescriptors.rotors[i]._transformVector(vertexDescriptors.normalsOrig[i]);
	}
}

/*
Laplacian: L(Ri) => Ri = Sum_j wij Rj

Solve Sum_i wij (Ri - Rj) = 0
s.t Ri = R_const,  at the constrained points.

L Ri = 0
s.t Ri = R_const

Where L is discrete laplacian operator
*/
void LaplacianRotationsInterpolation(VertexBuffer& vertexDescriptors, VertexBuffer& vertexDescriptorsLow)
{
	int n = vertexDescriptors.get_size();

	for (auto&& pair : mapping){
		RknownHi.row(pair.second) = vertexDescriptorsLow.rotors[pair.first].coeffs();
	}

	RbestHi = solverHi.solve(RknownHi);

	for (int i = 0; i < n; ++i) {
		vertexDescriptors.rotors[i] = Eigen::Quaterniond((Eigen::Vector4d)RbestHi.row(i)).normalized();
	}
}

/*
Ei = sum_j |Ri qij ~Ri - pij|^2
Ei = sum_j |Ri qij - pij Ri|^2

Ri = w + B

(w + B) qij - pij (w + B)
w qij + B qij - pij w - pij B
w qij + B qij - pij w - pij B

pij B = pij . B + pij x B
B qij = B . qij + B x qij = qij . B - qij x B

w qij + (qij . B - qij x B) - pij w - (pij . B + pij x B)
w qij + qij . B - qij x B - pij w - pij . B - pij x B

(qij - pij) w + (qij - pij) . B - (qij + pij) x B

(qij - pij) w - (qij - pij)^T B - [qij + pij]_x B

|0            -(qij - pij)^T  | |w| = |-(qij - pij)^T B               |
|(qij - pij)   [qij + pij]^T_x| |B|   |(qij - pij) w - [qij + pij]_x B|

d = (qij - pij)
s = (qij + pij)
d w - s^T B - [s]_x B

|0    -d^T  | |w| = |-d^T B       |
|d   [s]^T_x| |B|   |d w - [s]_x B|

|0    -d^T  |^T |0    -d^T  | =
|d   [s]^T_x|   |d   [s]^T_x|

|0    d^T  | |0    -d^T  | = |d^T d    d^T [s]^T_x              |
|-d   [s]_x| |d   [s]^T_x|   |[s]_x d  -d (-d^T) + [s]_x [s]^T_x|

|0    d^T  | |0    -d^T  | = |d^T d    d^T [s]^T_x    |
|-d   [s]_x| |d   [s]^T_x|   |[s]_x d  d d^T - [s]^2_x|

| ||d||^2   (s x d)^T      |
| s x d     d d^T - [s]^2_x|

| ||d||^2   (s x d)^T                |
| s x d     d d^T - s s^T + ||s||^2 I|

d = (qij - pij)
s = (qij + pij)

||d||^2 = - 2 pij^T qij + ||pij||^2 + ||qij||^2
||s||^2 = + 2 pij^T qij + ||pij||^2 + ||qij||^2
s x d = 2 pij x qij
d d^T = qij qij^T - pij qij^T - qij pij^T + pij pij^T
s s^T = qij qij^T + pij qij^T + qij pij^T + pij pij^T
d d^T - s s^T = - 2 pij qij^T - 2 qij pij^T

| ||d||^2   (s x d)^T                |
| s x d     d d^T - s s^T + ||s||^2 I|

-2 | qij^T pij   (qij x pij)^T                      | + ||pij||^2 + ||qij||^2 I4x4
   | qij x pij   qij pij^T + pij qij^T - qij^T pij I|

COORDINATES:

qij pij^T = | qx | |px py pz | = | qx px  qx py  qx pz |
            | qy |               | qy px  qy py  qy pz |
            | qz |               | qz px  qz py  qz pz |

Trace(qij pij^T) = qij^T pij
qij x pij = [(qy pz - qz py) e2e3, (qz px - qx pz) e3e1, (qx py - qy px) e1e2]
pij qij^T = (qij pij^T)^T
qij pij^T + pij qij^T = qij pij^T + (qij pij^T)^T =

| qx px  qx py  qx pz | | qx px  qy px  qz px |   | qx px+qx px  qx py+qy px  qx pz+qz px |
| qy px  qy py  qy pz | | qx py  qy py  qz py | = | qy px+qx py  qy py+qy py  qy pz+qz py |
| qz px  qz py  qz pz | | qx pz  qy pz  qz pz |   | qz px+qx pz  qz py+qy pz  qz pz+qz pz |

qij = a e1 + b e2 + c e3
pij = d e1 + e e2 + f e3
(b f - c e) e2e3 + (c d - a f) e3e1 + (a e - b d) e1e2
(qy pz - qz py) e2e3 + (qz px - qx pz) e3e1 + (qx py - qy px) e1e2
*/
void E3GA_Prep(const vector<Eigen::Vector3d>& P, const vector<Eigen::Vector3d>& Q, const vector<double>& w, const int N, Eigen::Matrix4d &JtJ)
{
	Eigen::Matrix3d Sx;
	double wj;
	Sx.setZero();
	double S = 0;
	for (size_t j = 0; j < N; ++j) {
		wj = w[j];
		const Eigen::Vector3d& Qj = Q[j];
		const Eigen::Vector3d& Pj = P[j];
		S += wj * Pj.dot(Pj);
		S += wj * Qj.dot(Qj);
		Sx.noalias() += (wj * Pj) * Qj.transpose();
	}

	wj = 0.5 * S;
 	JtJ(0,0) = wj - Sx.trace();
	wj = wj + Sx.trace();
	JtJ(0,1) = -(Sx(1, 2) - Sx(2, 1));
	JtJ(0,2) = -(Sx(2, 0) - Sx(0, 2));
	JtJ(0,3) = -(Sx(0, 1) - Sx(1, 0));
	JtJ(1,1) = -2.0 * Sx(0, 0) + wj;
	JtJ(1,2) = -(Sx(0, 1) + Sx(1, 0));
	JtJ(1,3) = -(Sx(2, 0) + Sx(0, 2));
	JtJ(2,2) = -2.0 * Sx(1, 1) + wj;
	JtJ(2,3) = -(Sx(1, 2) + Sx(2, 1));
	JtJ(3,3) = -2.0 * Sx(2, 2) + wj;
  JtJ.selfadjointView<Eigen::Upper>().evalTo(JtJ);
}

/*
Ei = sum_j cij |Ri qij ~Ri - pij|^2 + alphaA sum_j wij |Rj - Ri|^2
Ei = sum_j cij |Ri qij - pij Ri|^2 + alphaA sum_j wij |Rj - Ri|^2

Ei = Ri^T Mi Ri + alphaA sum_j wij (Rj^T Rj - Rj^T Ri + Ri^T Ri)
d/Ri Ei = Mi Ri + sum_j alphaA wij (-Rj + Ri)

Mi Ri + sum_j alphaA wij (-Rj + Ri) = 0
Mi Ri - sum_j alphaA wij (Rj) + sum_j alphaA wij (Ri) = 0

Since sum_j alphaA wij = alphaA

Mi Ri + alphaA Ri - sum_j alphaA wij (Rj) = 0
(Mi + alphaA I) Ri = sum_j alphaA wij Rj
*/
Eigen::Quaterniond E3GA_Fast4(const vector<Eigen::Vector3d>& P, const vector<Eigen::Vector3d>& Q, const vector<double>& w, double alphaA, const vector<Eigen::Quaterniond>& Rq, const int N)
{
	Eigen::Matrix4d JtJ;
	Eigen::Matrix3d Sx;
  Eigen::Vector4d b;

  b.setZero();
  for (int i = 0; i < N; ++i)
	{
		const Eigen::Quaterniond& Qi = Rq[i];
		b(0) += Qi.w(); // 1.0
		b(1) += Qi.x(); // e1 ^ e2
		b(2) += Qi.y(); // e1 ^ e3
		b(3) += Qi.z(); // e2 ^ e3
	}

  b *= alphaA / (double) N;

	double wj;
	Sx.setZero();
	double S = 0;
	for (size_t j = 0; j < N; ++j) {
		wj = w[j];
		const Eigen::Vector3d& Qj = Q[j];
		const Eigen::Vector3d& Pj = P[j];
		S += wj * Pj.dot(Pj);
		S += wj * Qj.dot(Qj);
		Sx.noalias() += (wj * Pj) * Qj.transpose();
	}

	wj = 0.5 * S;
 	JtJ(0,0) = wj - Sx.trace() + alphaA;
	wj = wj + Sx.trace() + alphaA;
	JtJ(0,1) = -(Sx(1, 2) - Sx(2, 1));
	JtJ(0,2) = -(Sx(2, 0) - Sx(0, 2));
	JtJ(0,3) = -(Sx(0, 1) - Sx(1, 0));
	JtJ(1,1) = -2.0 * Sx(0, 0) + wj;
	JtJ(1,2) = -(Sx(0, 1) + Sx(1, 0));
	JtJ(1,3) = -(Sx(2, 0) + Sx(0, 2));
	JtJ(2,2) = -2.0 * Sx(1, 1) + wj;
	JtJ(2,3) = -(Sx(1, 2) + Sx(2, 1));
	JtJ(3,3) = -2.0 * Sx(2, 2) + wj;
  JtJ.selfadjointView<Eigen::Upper>().evalTo(JtJ);

  Eigen::Vector4d x = JtJ.inverse().eval() * b;
  return Eigen::Quaterniond(x(0), x(1), x(2), x(3)).normalized();
}

void SolveRotationsSystem(Mesh *mesh, std::shared_ptr<SparseMatrix> A, VertexBuffer& vertexDescriptors, std::set<int>& constraints, bool analyzeSystem)
{
	vector<Eigen::Vector3d> P;
	vector<Eigen::Vector3d> Q;
  vector<double> w;
  vector<int> neighbors;
  Eigen::VectorXd Rbest;
  Eigen::Matrix4d JtJ;
  vector<Eigen::Quaterniond> Rq;

	P.resize(32);
	Q.resize(32);
  neighbors.resize(32);
  triplets.reserve(A->NonZeros()*16);
  w.resize(32);
  Rq.resize(32);
	const double alpha = 0.14;
	const double ei = 0.2;
  const double cij = 1.0;
  const double alphaA = alpha * g_meshArea;
  int nnz = 0;

	for (Vertex& vertex : mesh->getVertices())
	{
		int i = vertex.ID;
    int ii = i*4;

		Eigen::Vector3d &pi = vertex.p;
		Eigen::Vector3d &tpi = vertexDescriptors.deformedPositions[i];

		int vertexDegree = 0;
		for (Vertex::EdgeAroundIterator edgeAroundIter = vertex.iterator(); vertexDegree < 32 && !edgeAroundIter.end(); edgeAroundIter++, vertexDegree++)
		{
			int j = edgeAroundIter.edge_out()->pair->vertex->ID;

			Eigen::Vector3d &tpj = vertexDescriptors.deformedPositions[j];
			Eigen::Vector3d &pj = mesh->vertexAt(j).p;
			P[vertexDegree] = pj - pi;
			Q[vertexDegree] = tpj - tpi;
      neighbors[vertexDegree] = j;
      w[vertexDegree] = (*A)(i, j);
		}
    if (!is_constrained(constraints, i)) {
  		double invVertexDegree = (alphaA * cij) / ((double)vertexDegree + ei); // Plus ei since we consider the feedback.
      for(int v = 0; v < vertexDegree; ++v) {
        int jj = neighbors[v]*4;
        triplets[nnz++] = Eigen::Triplet<double>(ii+0, jj+0, -invVertexDegree);
        triplets[nnz++] = Eigen::Triplet<double>(ii+1, jj+1, -invVertexDegree);
        triplets[nnz++] = Eigen::Triplet<double>(ii+2, jj+2, -invVertexDegree);
        triplets[nnz++] = Eigen::Triplet<double>(ii+3, jj+3, -invVertexDegree);
      }
      E3GA_Prep(P, Q, w, vertexDegree, JtJ);
      JtJ(0, 0) += alphaA + 1e-6;
      JtJ(1, 1) += alphaA + 1e-6;
      JtJ(2, 2) += alphaA + 1e-6;
      JtJ(3, 3) += alphaA + 1e-6;
      triplets[nnz++] = Eigen::Triplet<double>(ii+0, ii+0, JtJ(0, 0));
      triplets[nnz++] = Eigen::Triplet<double>(ii+0, ii+1, JtJ(0, 1));
      triplets[nnz++] = Eigen::Triplet<double>(ii+0, ii+2, JtJ(0, 2));
      triplets[nnz++] = Eigen::Triplet<double>(ii+0, ii+3, JtJ(0, 3));
      triplets[nnz++] = Eigen::Triplet<double>(ii+1, ii+0, JtJ(1, 0));
      triplets[nnz++] = Eigen::Triplet<double>(ii+1, ii+1, JtJ(1, 1));
      triplets[nnz++] = Eigen::Triplet<double>(ii+1, ii+2, JtJ(1, 2));
      triplets[nnz++] = Eigen::Triplet<double>(ii+1, ii+3, JtJ(1, 3));
      triplets[nnz++] = Eigen::Triplet<double>(ii+2, ii+0, JtJ(2, 0));
      triplets[nnz++] = Eigen::Triplet<double>(ii+2, ii+1, JtJ(2, 1));
      triplets[nnz++] = Eigen::Triplet<double>(ii+2, ii+2, JtJ(2, 2));
      triplets[nnz++] = Eigen::Triplet<double>(ii+2, ii+3, JtJ(2, 3));
      triplets[nnz++] = Eigen::Triplet<double>(ii+3, ii+0, JtJ(3, 0));
      triplets[nnz++] = Eigen::Triplet<double>(ii+3, ii+1, JtJ(3, 1));
      triplets[nnz++] = Eigen::Triplet<double>(ii+3, ii+2, JtJ(3, 2));
      triplets[nnz++] = Eigen::Triplet<double>(ii+3, ii+3, JtJ(3, 3));
      // Feedback added on the RHS.
      invVertexDegree *= ei;
      Rknown[ii+0] = invVertexDegree * vertexDescriptors.rotors[i].w();
      Rknown[ii+1] = invVertexDegree * vertexDescriptors.rotors[i].x();
      Rknown[ii+2] = invVertexDegree * vertexDescriptors.rotors[i].y();
      Rknown[ii+3] = invVertexDegree * vertexDescriptors.rotors[i].z();
    }
    else
    {
      for(int v = 0; v < vertexDegree; ++v) {
        Rq[v] = vertexDescriptors.rotors[neighbors[v]];
      }

      Eigen::Quaterniond M = E3GA_Fast4(P, Q, w, alphaA, Rq, vertexDegree);
      Rknown[ii+0] = M.w();
      Rknown[ii+1] = M.x();
      Rknown[ii+2] = M.y();
      Rknown[ii+3] = M.z();
      triplets[nnz++] = Eigen::Triplet<double>(ii+0, ii+0, 1.0);
      triplets[nnz++] = Eigen::Triplet<double>(ii+1, ii+1, 1.0);
      triplets[nnz++] = Eigen::Triplet<double>(ii+2, ii+2, 1.0);
      triplets[nnz++] = Eigen::Triplet<double>(ii+3, ii+3, 1.0);
    }
	}
  RotationMatrix.setFromTriplets(triplets.begin(), triplets.begin() + nnz);
  if(analyzeSystem) {
    solverRotations.analyzePattern(RotationMatrix);
  }
  solverRotations.factorize(RotationMatrix);
	if (solverRotations.info() != Eigen::Success) {
		// TODO: error handling
	}
	Rbest = solverRotations.solve(Rknown);

	for (int i = 0; i < vertexDescriptors.get_size(); ++i) {
    int ii = i*4;
    Eigen::Quaterniond M = Eigen::Quaterniond(Rbest(ii+0), Rbest(ii+1), Rbest(ii+2), Rbest(ii+3));
		vertexDescriptors.rotors[i] = M.normalized();
	}

  std::fill(vertexDescriptors.laplacianCoordinates.begin(), vertexDescriptors.laplacianCoordinates.end(), Eigen::Vector3d(0, 0, 0));

	//concurrency::parallel_for_each(mesh->getVertices().begin(), mesh->getVertices().end(), [&](Vertex& vertex)
	for (Vertex& vertex : mesh->getVertices())
	{
		int i = vertex.ID;
		for (Vertex::EdgeAroundIterator edgeAroundIter = vertex.iterator(); !edgeAroundIter.end(); edgeAroundIter++)
		{
			int j = edgeAroundIter.edge_out()->pair->vertex->ID;
			double wij = (*A)(i, j);
			Eigen::Quaterniond&Ri = vertexDescriptors.rotors[i];
			Eigen::Quaterniond&Rj = vertexDescriptors.rotors[j];
			Eigen::Vector3d V = mesh->vertexAt(j).p - mesh->vertexAt(i).p;
			vertexDescriptors.laplacianCoordinates[i] += 0.5 * wij * (Ri._transformVector(V) + Rj._transformVector(V));;
		}
	}
}

void transferRotations(Mesh *mesh, std::shared_ptr<SparseMatrix> A, VertexBuffer& vertexDescriptors, VertexBuffer& vertexDescriptorsLow)
{
  LaplacianRotationsInterpolation(vertexDescriptors, vertexDescriptorsLow);

	std::fill(vertexDescriptors.laplacianCoordinates.begin(), vertexDescriptors.laplacianCoordinates.end(), Eigen::Vector3d(0, 0, 0));

	//concurrency::parallel_for_each(mesh->getVertices().begin(), mesh->getVertices().end(), [&](Vertex& vertex)
	for (Vertex& vertex : mesh->getVertices())
	{
		int i = vertex.ID;
		for (Vertex::EdgeAroundIterator edgeAroundIter = vertex.iterator(); !edgeAroundIter.end(); edgeAroundIter++)
		{
			int j = edgeAroundIter.edge_out()->pair->vertex->ID;
			double wij = (*A)(i, j);
			Eigen::Quaterniond &Ri = vertexDescriptors.rotors[i];
			Eigen::Quaterniond &Rj = vertexDescriptors.rotors[j];
			Eigen::Vector3d V = mesh->vertexAt(j).p - mesh->vertexAt(i).p;
			vertexDescriptors.laplacianCoordinates[i] += 0.5 * wij * (Ri._transformVector(V) + Rj._transformVector(V));;
		}
	}
	//});
}

double ComputeEnergy(Mesh *mesh, std::shared_ptr<SparseMatrix> A, VertexBuffer& vertexDescriptors)
{
	double arapEnergy = 0.0;
	const double alpha = 0.28;
	//concurrency::parallel_for_each(mesh->vertices.begin(), mesh->vertices.end(), [&](Vertex * vertex)
	for (Vertex& vertex : mesh->getVertices())
	{
		int i = vertex.ID;

		Eigen::Vector3d& pi = vertex.p;
		Eigen::Vector3d &tpi = vertexDescriptors.deformedPositions[i];
		auto& Ri = vertexDescriptors.rotors[i];

		int vertexDegree = 0;
		for (Vertex::EdgeAroundIterator edgeAroundIter = vertex.iterator(); !edgeAroundIter.end(); edgeAroundIter++)
		{
			vertexDegree++;
		}

		double invVertexDegree = alpha * g_meshArea / (double)vertexDegree;

		for (Vertex::EdgeAroundIterator edgeAroundIter = vertex.iterator(); !edgeAroundIter.end(); edgeAroundIter++)
		{
			int j = edgeAroundIter.edge_out()->pair->vertex->ID;

			Eigen::Vector3d &tpj = vertexDescriptors.deformedPositions[j];
			Eigen::Vector3d &pj = mesh->vertexAt(j).p;

			Eigen::Vector3d eij = (pj - pi);
			auto q_ij = tpj - tpi;
			auto p_ij = eij;
			auto& Rj = vertexDescriptors.rotors[j];

			arapEnergy += (*A)(i, j) * (Ri._transformVector(p_ij) - q_ij).squaredNorm();
			arapEnergy += invVertexDegree * (Rj.coeffs() - Ri.coeffs()).squaredNorm();
		}
	}//);

	return arapEnergy;
}

void display()
{
	/*
	 *	matrices
	 */
	glViewport( 0, 0, g_viewportWidth, g_viewportHeight );
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	pickLoadMatrix();
	GLpick::g_frustumFar = 1000.0;
	GLpick::g_frustumNear = .1;
	gluPerspective( 60.0, (double)g_viewportWidth/(double)g_viewportHeight, GLpick::g_frustumNear, GLpick::g_frustumFar );
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glShadeModel(GL_SMOOTH);	//gouraud shading
	glClearDepth(1.0f);
	glClearColor( .75f, .75f, .75f, .0f );
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

	/*
	 *	estados
	 */
	glEnable(GL_CULL_FACE);		//face culling
	glCullFace( GL_BACK );
	glFrontFace( GL_CCW );
	glEnable(GL_DEPTH_TEST);	//z-buffer
	glDepthFunc(GL_LEQUAL);

	/*
	 *	iluminacion
	 */
	float		ambient[] = { .3f, .3f, .3f, 1.f };
	float		diffuse[] = { .3f, .3f, .3f, 1.f };
	float		position[] = { .0f, 0.f, -150.f, 1.f };
	float		specular[] = { 1.f, 1.f, 1.f };

	glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
	glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0);
	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0125);
	glEnable(  GL_LIGHT0   );
	glEnable(  GL_LIGHTING );
	glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular );
	glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 50.f );

	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glLoadIdentity();

	g_camera.glLookAt();

	glLightfv( GL_LIGHT0, /*GL_POSITION*/GL_SPOT_DIRECTION, position );

	glPushMatrix();

	rotorGLMult(g_modelRotor);

	benchs.Begin();

	if(!g_computeBasis)
	{
		static bool oneTime = true;
		if (oneTime || (g_rotateKeyRotors || g_translateKeyRotors || g_automaticAnimation))
		{
			if(oneTime == true)
			{
				SolvePoissonEquation(vertexDescriptorsLow);
			}

			for(int i = 0 ; i < 3 ; ++i)
			{
        SolveRotationsSystem(&meshLow, A, vertexDescriptorsLow, allconstraints, oneTime == true);
				SolvePoissonEquation(vertexDescriptorsLow);
			}
			transferRotations(&mesh, AHi, vertexDescriptors, vertexDescriptorsLow);
			SolvePoissonEquationHi(vertexDescriptors, vertexDescriptorsLow);

      oneTime = false;
		}
	}
	if(g_iterateManyTimes)
	{
		if (g_convergence)
		{
			clock_t begin;
			clock_t end;
			double avgTime = 0.0;

			auto arapEnergy = ComputeEnergy(&meshLow, A, vertexDescriptorsLow);
			int count = 0;
			for (; count < 2500; ++count)
			{
				begin = clock();

        SolveRotationsSystem(&meshLow, A, vertexDescriptorsLow, allconstraints, false);
				SolvePoissonEquation(vertexDescriptorsLow);

				end = clock();

				double secDeform = (double)(end - begin) / CLOCKS_PER_SEC;

				avgTime += secDeform;

				auto newArapEnergy = ComputeEnergy(&meshLow, A, vertexDescriptorsLow);
				if (fabs(arapEnergy - newArapEnergy) < 1e-4)
				{
					std::cout << "convergence after " << count + 1 << " iterations" << std::endl;
					break;
				}
				arapEnergy = newArapEnergy;
			}
			arapEnergy = ComputeEnergy(&meshLow, A, vertexDescriptorsLow);
			std::cout << arapEnergy << std::endl;
			std::cout << "Average iteration time (sec): " << avgTime / (double)(count + 1) << endl;
			std::cout << "Total accum time (sec): " << avgTime << endl;

			transferRotations(&mesh, AHi, vertexDescriptors, vertexDescriptorsLow);
			SolvePoissonEquationHi(vertexDescriptors, vertexDescriptorsLow);
		}
		else
		{
			for (int i = 0; i < 100; ++i)
			{
        SolveRotationsSystem(&meshLow, A, vertexDescriptorsLow, allconstraints, false);
				SolvePoissonEquation(vertexDescriptorsLow);
			}
			transferRotations(&mesh, AHi, vertexDescriptors, vertexDescriptorsLow);
			SolvePoissonEquationHi(vertexDescriptors, vertexDescriptorsLow);
		}
		g_iterateManyTimes = false;
	}

	benchs.End();

	if (GLpick::g_pickActive) glLoadName((GLuint)-1);

	double alpha = 1.0;

	//glEnable (GL_BLEND);
	//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//alpha = 0.5;

	//Mesh-Faces Rendering
	glPolygonMode( GL_FRONT_AND_BACK, GL_FILL /*GL_LINE GL_FILL GL_POINT*/);
	glEnable (GL_POLYGON_OFFSET_FILL);
	glPolygonOffset (1., 1.);
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable( GL_COLOR_MATERIAL );
	if (GLpick::g_pickActive) glLoadName((GLuint)10);

	glColor4d(1, 1, 1, alpha);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_DOUBLE, 0, &vertexDescriptors.deformedPositions[0]);
	glNormalPointer(GL_DOUBLE, 0, &vertexDescriptors.normals[0]);
	// draw the model
	glDrawElements(GL_TRIANGLES, trianglesHi.get_size(), GL_UNSIGNED_INT, &trianglesHi.faces[0]);
	// deactivate vertex arrays after drawing
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);

	if (g_showWires)
	{
		if (!GLpick::g_pickActive)
		{
			//Mesh-Edges Rendering (superimposed to faces)
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE /*GL_LINE GL_FILL GL_POINT*/);
			glColor4d(.5, .5, .5, alpha);
			glDisable(GL_LIGHTING);
			glEnableClientState(GL_VERTEX_ARRAY);
			glVertexPointer(3, GL_DOUBLE, 0, &vertexDescriptors.deformedPositions[0]);
			// draw the model
			glDrawElements(GL_TRIANGLES, trianglesHi.get_size(), GL_UNSIGNED_INT, &trianglesHi.faces[0]);
			// deactivate vertex arrays after drawing
			glDisableClientState(GL_VERTEX_ARRAY);
			glEnable(GL_LIGHTING);
		}
	}

	//for (map<int, int>::iterator iter = mapping.begin(); iter != mapping.end(); iter++){
	//	DrawPoint(c3gaPoint(vertexDescriptors[iter->second]->deformedPosition));
	//}

	glDisable( GL_COLOR_MATERIAL );
	glDisable(GL_POLYGON_OFFSET_FILL);

	//glDisable (GL_BLEND);

	if(g_showSpheres)
	{
		//Handles rendering
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL /*GL_LINE GL_FILL GL_POINT*/);

		float	turcoise[] = { .0f, .5f, .5f, 0.3f };
		float	red[] = { .5f, .0f, .0f, 0.3f };

		for( int k = 0 ; k < handles.size() ; ++k)
		{
			if (GLpick::g_pickActive) glLoadName((GLuint)k);
			TRversor R = handles[k].GetTRVersor();
			DrawTransparentDualSphere( _dualSphere( R * handles[k].dS * inverse(R) ), turcoise );
		}
	}

	glPopMatrix();

	glutSwapBuffers();
}

void reshape(GLint width, GLint height)
{
	g_viewportWidth = width;
	g_viewportHeight = height;

	// redraw viewport
	glutPostRedisplay();
}

vectorE3GA mousePosToVector(int x, int y) {
	x -= g_viewportWidth / 2;
	y -= g_viewportHeight / 2;
	return _vectorE3GA((float)-x * e1 - (float)y * e2);
}

void MouseButton(int button, int state, int x, int y)
{
	g_rotateModel = false;
	g_rotateKeyRotors = false;
	g_translateKeyRotors = false;

	if (button == GLUT_LEFT_BUTTON)
	{
		g_prevMousePos = mousePosToVector(x, y);

		GLpick::g_pickWinSize = 1;
		g_dragObject = pick(x, g_viewportHeight - y, display, &g_dragDistance);

		if(g_dragObject == -1 || g_dragObject == 10 )
		{
			vectorE3GA mousePos = mousePosToVector(x, y);
			g_rotateModel = true;

			if ((_Float(norm_e(mousePos)) / _Float(norm_e(g_viewportWidth * e1 + g_viewportHeight * e2))) < 0.2)
				g_rotateModelOutOfPlane = true;
			else g_rotateModelOutOfPlane = false;
		}
		else if(g_dragObject >= 0 && g_dragObject < handles.size())
		{
			g_rotateKeyRotors = true;
		}
	}

	if (button == GLUT_RIGHT_BUTTON)
	{
		g_prevMousePos = mousePosToVector(x, y);

		GLpick::g_pickWinSize = 1;
		g_dragObject = pick(x, g_viewportHeight - y, display, &g_dragDistance);

		if(g_dragObject >= 0 && g_dragObject < handles.size())
			g_translateKeyRotors = true;
	}
}

void MouseMotion(int x, int y)
{
	if (g_rotateModel || g_rotateKeyRotors || g_translateKeyRotors )
	{
		// get mouse position, motion
		vectorE3GA mousePos = mousePosToVector(x, y);
		vectorE3GA motion = mousePos - g_prevMousePos;

		if (g_rotateModel)
		{
			// update rotor
			if (g_rotateModelOutOfPlane)
				g_modelRotor = exp(g_camera.rotateVel * (motion ^ e3) ) * g_modelRotor;
			else
				g_modelRotor = exp(0.00001f * (motion ^ mousePos) ) * g_modelRotor;
		}
		if(g_rotateKeyRotors)
		{
			rotor R1 =  _rotor( inverse(g_modelRotor) * exp(-g_camera.rotateVel * (motion ^ e3) ) * g_modelRotor);
			//rotor R1 =  _rotor( exp(-g_camera.rotateVel * (motion ^ e3) ) );
			if(g_dragObject < handles.size())
			{
				rotor R = handles[g_dragObject].R;
				handles[g_dragObject].R = normalize(_TRversor( R1 * R  ) );
			}
		}

		if(g_translateKeyRotors)
		{
			normalizedTranslator T1 = _normalizedTranslator(inverse(g_modelRotor) * exp( _freeVector(-g_camera.translateVel*motion*ni) ) * g_modelRotor);
			if(g_dragObject < handles.size())
			{
				translator T = handles[g_dragObject].T;
				handles[g_dragObject].T = normalize(_TRversor( T1 * T ));
			}
		}

		// remember mouse pos for next motion:
		g_prevMousePos = mousePos;

		// redraw viewport
		glutPostRedisplay();
	}
}

void SpecialFunc(int key, int x, int y)
{
	switch(key) {
		case GLUT_KEY_F1 :
			{
				int mod = glutGetModifiers();
				if(mod == GLUT_ACTIVE_CTRL || mod == GLUT_ACTIVE_SHIFT )
				{
				}
			}
			break;
		case GLUT_KEY_UP:
			{
				if(g_rotateKeyRotors)
				{
					handles[g_dragObject].dS = ChangeDualSphereRadiusSize(handles[g_dragObject].dS, 0.025);

					// redraw viewport
					glutPostRedisplay();
				}
			}
			break;
		case GLUT_KEY_DOWN:
			{
				if(g_rotateKeyRotors)
				{
					handles[g_dragObject].dS = ChangeDualSphereRadiusSize(handles[g_dragObject].dS, -0.025);

					// redraw viewport
					glutPostRedisplay();
				}
			}
			break;
	}
}

void SpecialUpFunc(int key, int x, int y)
{
}

void KeyboardUpFunc(unsigned char key, int x, int y)
{
	if (key == 'c' || key == 'C')
	{
		g_convergence = !g_convergence;
		vectorE3GA motion = _vectorE3GA(-70.0, -60.0, 0);
		normalizedTranslator T1 = _normalizedTranslator(inverse(g_modelRotor) * exp(_freeVector(-g_camera.translateVel*motion*ni)) * g_modelRotor);
		translator T = handles[0].T;
		handles[0].T = normalize(_TRversor(T1 * T));

		glutPostRedisplay();
	}

	if (key == 'a' || key == 'A')
	{
		g_automaticAnimation = !g_automaticAnimation;
		if (g_automaticAnimation)
		{
			benchs.Start();
		}
		else
		{
			benchs.Stop();
		}

		glutPostRedisplay();
	}

	if(key == 'w' || key == 'W')
	{
		g_showWires = !g_showWires;
		glutPostRedisplay();
	}

	if( key == 'h' || key == 'H' )
	{
		g_showSpheres = !g_showSpheres;
		glutPostRedisplay();
	}

	if( key == 'x' || key == 'X' )
	{
		g_iterateManyTimes = true;
		glutPostRedisplay();
	}
}

void Idle()
{
	// redraw viewport
	if (g_automaticAnimation)
	{
		const int minCount = -50;
		const int maxCount = 50;
		static int mycount = 0;
		static int direction = 0;
		vectorE3GA motion = _vectorE3GA(1.0, 0, 0);
		if (mycount == maxCount)
		{
			direction = 1;
		}
		if (mycount == minCount)
		{
			direction = 0;
		}

		if (mycount < maxCount && direction == 0)
		{
			motion = _vectorE3GA(1.0, 0, 0);
			mycount++;
		}
		if (mycount > minCount && direction == 1)
		{
			motion = _vectorE3GA(-1.0, 0, 0);
			mycount--;
		}

		normalizedTranslator T1 = _normalizedTranslator(inverse(g_modelRotor) * exp(_freeVector(-g_camera.translateVel*motion*ni)) * g_modelRotor);
		translator T = handles[0].T;
		handles[0].T = normalize(_TRversor(T1 * T));

		if (!benchs.IsStarted())
		{
			benchs.Start();
		}

		glutPostRedisplay();
	}
}

void DestroyWindow()
{
	ReleaseDrawing();
}


//
// Created by Lubos Ilcik on 27/01/2018.
//

#include <boost/test/unit_test.hpp>

#include <graphene/protocol/fee_schedule.hpp>
#include <graphene/protocol/operations_permissions.hpp>

using namespace graphene::protocol;

BOOST_AUTO_TEST_SUITE(transfer_fee_calculation_tests)

BOOST_AUTO_TEST_CASE(calculate_flat_transfer_fee)
{
    // fee calculation input parameters
    uint64_t fee = 1000;
    uint32_t scale = GRAPHENE_100_PERCENT; // the rationale for scale is unknown
                                           // fee value is multiplied by (scale / GRAPHENE_100_PERCENT)
    uint64_t base_amount  = 2; // the base asset is the asset being sold
    uint64_t quote_amount = 1; // the quote asset is the asset being purchased
                              // exchange_rate = quote_amount / base_amount

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = fee;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // set scale parameter
    schedule.scale = scale; // schedule.scale default value is GRAPHENE_100_PERCENT

    // set price parameters
    price price(asset(base_amount, asset_id_type(0)), asset(quote_amount, asset_id_type(1)));

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    auto calculatedFee = schedule.calculate_fee(operation, price);

    // expected value
    uint64_t expected_fee = fee * quote_amount * scale / (base_amount * GRAPHENE_100_PERCENT);
    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), expected_fee);
}

BOOST_AUTO_TEST_CASE(when_transfer_amount_is_0_calculated_fee_is_flat_fee)
{
    uint64_t flat_fee = 1000;

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = flat_fee;
    transfer_fee.percentage = (GRAPHENE_100_PERCENT/1000); // 0.1%
    transfer_fee.percentage_max_fee = 300 * GRAPHENE_BLOCKCHAIN_PRECISION;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    operation.amount = asset();
    auto calculatedFee = schedule.calculate_fee(operation, price::unit_price());

    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), flat_fee);
}

BOOST_AUTO_TEST_CASE(when_percentage_is_0_calculated_fee_is_flat_fee)
{
    uint64_t flat_fee = 1000;

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = flat_fee;
    transfer_fee.percentage = 0;
    transfer_fee.percentage_max_fee = 300 * GRAPHENE_BLOCKCHAIN_PRECISION;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    operation.amount = asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION);
    auto calculatedFee = schedule.calculate_fee(operation, price::unit_price());

    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), flat_fee);
}

BOOST_AUTO_TEST_CASE(when_percentage_and_transfer_amount_are_not_0_calculated_fee_is_percentage_fee)
{
    uint64_t min_fee = 1000;
    uint16_t percentage = GRAPHENE_1_PERCENT / 10; // 0.1%
    uint64_t max_fee = 300 * GRAPHENE_BLOCKCHAIN_PRECISION;
    uint64_t amount = 1000 * GRAPHENE_BLOCKCHAIN_PRECISION;

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = min_fee;
    transfer_fee.percentage = percentage;
    transfer_fee.percentage_max_fee = max_fee;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    operation.amount = asset(amount);
    auto calculatedFee = schedule.calculate_fee(operation, price::unit_price());

    uint64_t expected_fee = amount * percentage / GRAPHENE_100_PERCENT;
    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), expected_fee);
}

BOOST_AUTO_TEST_CASE(when_calculated_percentage_fee_is_more_than_max_fee_return_max_fee)
{
    uint64_t min_fee = 1000;
    uint16_t percentage = GRAPHENE_1_PERCENT;
    uint64_t max_fee = 300 * GRAPHENE_BLOCKCHAIN_PRECISION;
    uint64_t amount = 100000 * GRAPHENE_BLOCKCHAIN_PRECISION;

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = min_fee;
    transfer_fee.percentage = percentage;
    transfer_fee.percentage_max_fee = max_fee;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    operation.amount = asset(amount);
    auto calculatedFee = schedule.calculate_fee(operation, price::unit_price());

    uint64_t expected_fee = max_fee;
    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), expected_fee);
}

BOOST_AUTO_TEST_CASE(when_calculated_percentage_fee_is_less_than_min_fee_return_min_fee)
{
    uint64_t min_fee = 10000000;
    uint16_t percentage = GRAPHENE_1_PERCENT / 100; // 0.0001
    uint64_t max_fee = 300 * GRAPHENE_BLOCKCHAIN_PRECISION;
    uint64_t amount = 10 * GRAPHENE_BLOCKCHAIN_PRECISION;

    fee_schedule schedule;

    // set fee parameter
    transfer_operation::fee_parameters_type transfer_fee;
    transfer_fee.fee = min_fee;
    transfer_fee.percentage = percentage;
    transfer_fee.percentage_max_fee = max_fee;
    schedule.parameters.insert(transfer_fee); // overwrite default value

    // calculate operation fee with given input parameters
    auto operation = transfer_operation();
    operation.amount = asset(amount);
    auto calculatedFee = schedule.calculate_fee(operation, price::unit_price());

    uint64_t expected_fee = min_fee;
    BOOST_CHECK_EQUAL(static_cast<uint64_t>(calculatedFee.amount.value), expected_fee);
}

BOOST_AUTO_TEST_SUITE_END()


#ifndef __PARTICLE_FILTER_HAND_LIKELIHOOD_HPP
#define __PARTICLE_FILTER_HAND_LIKELIHOOD_HPP

#include <cmath>
#include <ctime>
#include <vector>
#include <algorithm>
#include <Eigen/Dense>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>

#include <padenti/image.hpp>
#include <padenti/mh_rtree.hpp>
#include <padenti/cl_regressor.hpp>

#include "rchord_meanshift.hpp"

#include "particle_filter_hand.hpp"
#include "particle_filter_base.hpp"

// Random Forest parameters
#define RF_FEAT_SIZE (2)
#define RF_VAL_SIZE  (4)
#define RF_N_HYPH    (2)

// Meanshift parameters
#define MS_N_HYPH    (10)
#define MS_SIGMA    (1.6223)
#define MS_STEPSIZE (0.2)
#define MS_STEPTHR  (0.025)
#define MS_SAMECTHR (0.15)
#define MS_NITERTHR (10u)
#define MS_SAMPLED_PIXELS (200u)

// Likelihood parameters
#define EVAL_SIGMA  (MS_SIGMA/3)

// Commodity typedefs to avoid long class type declaration
typedef Image<unsigned short, 1> DepthT;
typedef Image<unsigned char, 1> MaskT;
typedef Image<int, 1> PredictionT;
typedef MHRTree<short int, RF_FEAT_SIZE, RF_VAL_SIZE, RF_N_HYPH> MHRTreeT;
typedef RTreeNode<short int, RF_FEAT_SIZE, RF_VAL_SIZE> RTreeNodeT;
typedef CLRegressor<unsigned short, 1, short, RF_FEAT_SIZE, RF_VAL_SIZE> RegressorT;

class HandLikelihoodModel: public LikelihoodModel<HAND_STATE_SIZE>
{
private:
  RegressorT &regressor;
  Particle<HAND_STATE_SIZE> prevState;
  std::vector<Eigen::Matrix3f> modes;
  std::vector<float> modesW;
  std::vector<int> modesCount;
  std::vector<float> modesVar;
  int nModes;
  float evalSigma;
  float deltaT;
public:

  HandLikelihoodModel(RegressorT &regressor,
		      float evalSigma=EVAL_SIGMA):
    regressor(regressor),
    modes(std::vector<Eigen::Matrix3f>(MS_N_HYPH)),
    modesW(std::vector<float>(MS_N_HYPH)),
    modesCount(std::vector<int>(MS_N_HYPH)),
    modesVar(std::vector<float>(MS_N_HYPH)),
    nModes(0),
    evalSigma(evalSigma)
  {}

  const std::vector<Eigen::Matrix3f> &getModes() const {return modes;}
  const std::vector<float> &getModeWeights() const {return modesW;}
  const std::vector<int> &getModeCount() const {return modesCount;}
  const std::vector<float> &getModeVar() const {return modesVar;}
  int getNModes() const {return nModes;}

  void eval(std::vector<Particle<HAND_STATE_SIZE> > &particles) const
  {
    Eigen::Quaternion<float> prevq(prevState.state[0],
				   prevState.state[1],
				   prevState.state[2],
				   prevState.state[3]);
    Eigen::Vector3d prevV(prevState.state[4],
			  prevState.state[5],
			  prevState.state[6]);

    // Compute the particles weight as the Panzer window estimate on rotation chordal
    // distance values using a Gaussian kernel
    // TODO: per-mode bandwidth as in Herdtweek paper?
    for (int i=0; i<particles.size(); i++)
    {
      Eigen::Quaternion<float> q(particles.at(i).state[0],
				 particles.at(i).state[1],
				 particles.at(i).state[2],
				 particles.at(i).state[3]);
      Eigen::Vector3d currV(particles.at(i).state[4],
			    particles.at(i).state[5],
			    particles.at(i).state[6]);
      Eigen::Matrix3f R(q);

      // Compute the likelihood
      double l = 0;
      for (int j=0; j<nModes; j++)
      {
	Eigen::Matrix3f rotDiff = R-modes.at(j);
	l += modesW.at(j) * exp(-rotDiff.cwiseProduct(rotDiff).sum()/(evalSigma*evalSigma));
	//l += modesCount.at(j)/sqrt(modesVar.at(j)) *
	//  exp(-rotDiff.cwiseProduct(rotDiff).sum()/(modesVar.at(j)));
      }

     // Compute the velocity with respect to the previous best particle rotation
      // TODO: parameterize velocity bound
      Eigen::Quaternion<float> deltaq;
      Eigen::AngleAxisf axang;
      float V;
      deltaq = q * prevq.inverse();
      axang = deltaq;
      V = axang.angle()/deltaT;
      particles.at(i).likelihood = V<10*M_PI ? l : 0.;

      //particles.at(i).likelihood = l;
      //double diffV = (currV-prevV).norm();
      //particles.at(i).likelihood *= exp(-(diffV*diffV)/(M_PI*M_PI));
      //particles.at(i).likelihood *= exp(-(V*V)/(25*M_PI*M_PI));

    }
  }

  void updateModel(const Particle<HAND_STATE_SIZE> &_prevState,
		   const MHRTreeT &rtree, const DepthT &depthmap,
		   float _deltaT=1./30)
  {
    // Perform RF prediction on the current depthmap. The depthmap is supposed to
    // be already segmented
    // - Mask as non-zero pixels
    MaskT mask(depthmap.getWidth(), depthmap.getHeight());
    cv::Mat cvDepth(depthmap.getHeight(), depthmap.getWidth(), CV_16U,
		    reinterpret_cast<unsigned char*>(depthmap.getData()));
    cv::Mat cvMask(depthmap.getHeight(), depthmap.getWidth(), CV_8U,
		   reinterpret_cast<unsigned char*>(mask.getData()));
    cvMask.setTo(0);
    cvMask.setTo(1, cvDepth>0);

    // - perform prediction
    // TODO: handle multiple trees
    PredictionT prediction(depthmap.getWidth(), depthmap.getHeight());
    regressor.predict(0, depthmap, prediction, mask);

    // - sample image pixels, i.e. work on a subset of the per-pixel prediction
    // in order to guarantee real-time execution
    unsigned int *nonNullPixelsBuff =
      new unsigned int[prediction.getWidth()*prediction.getHeight()];
    unsigned int nonNullPixels=0, nSamples=0;
    for (unsigned int i=0; i<prediction.getWidth()*prediction.getHeight(); i++)
    {
      if (mask.getData()[i]) nonNullPixelsBuff[nonNullPixels++]=i;
    }
    nSamples = std::min(MS_SAMPLED_PIXELS, nonNullPixels);

    if (!nSamples)
    {
      // TODO: how to handle the lack of samples?
      return;
    }

    unsigned int *samples = new unsigned int[nSamples];
    if (nSamples<MS_SAMPLED_PIXELS)
    {
      // Not enough samples (i.e. non-null pixels) to perform sampling.
      // Simply copy the whole set of samples
      std::copy(nonNullPixelsBuff, nonNullPixelsBuff+nSamples, samples);
    }
    else
    {
      boost::random::mt19937 gen(time(NULL));
      boost::random::uniform_int_distribution<> U(0, nonNullPixels-1);
      for (int i=0; i<nSamples; i++) samples[i] = nonNullPixelsBuff[U(gen)];
    }

    // Build the list of votes (i.e. Euler angles) and perform meanshift
    std::vector<Eigen::Vector3f> votes;
    std::vector<float> votesW;
    for (int i=0; i<nSamples; i++)
    {
      int nodeID = prediction.getData()[samples[i]];
      unsigned int nVotes = rtree.getNVotes()[nodeID];

      for (int j=0; j<nVotes; j++)
      {
	Eigen::Vector3f vote;
	float *votesPtr =
	  &rtree.getVotes()[nodeID*RF_VAL_SIZE*RF_N_HYPH + j*RF_VAL_SIZE];
	float *weightPtr =
	  &rtree.getVoteWeights()[nodeID*RF_N_HYPH+j];

	vote.z() = votesPtr[0]*M_PI/180.; //static_cast<double>(votesPtr[0])*M_PI/180.;
        vote.y() = votesPtr[1]*M_PI/180.; //static_cast<double>(votesPtr[1])*M_PI/180.;
	vote.x() = votesPtr[2]*M_PI/180.; //static_cast<double>(votesPtr[2])*M_PI/180.;
	votes.push_back(vote);
	votesW.push_back(*weightPtr);
	//votesW.push_back(static_cast<double>(*weightPtr));
      }
    }

    std::vector<Eigen::Vector3f> eulModes(MS_N_HYPH);
    std::vector<int> voteModeID(votes.size());
    nModes = MultiGuessRChordMeanShift(votes, votesW, votes, eulModes, voteModeID,
				       MS_SIGMA, MS_STEPSIZE, MS_STEPTHR,
				       MS_NITERTHR, MS_SAMECTHR);

    // Store modes found as rotation matrices
    for (int i=0; i<nModes; i++)
    {
      modes.at(i) = Eigen::AngleAxisf(eulModes.at(i).x(), Eigen::Vector3f::UnitZ()) *
	            Eigen::AngleAxisf(eulModes.at(i).y(), Eigen::Vector3f::UnitY()) *
	            Eigen::AngleAxisf(eulModes.at(i).z(), Eigen::Vector3f::UnitX());
    }

    // Update the mode weights as the sum of (weights of the) votes that converge to
    // each specific mode
    std::fill(modesW.begin(), modesW.end(), 0.);
    std::fill(modesCount.begin(), modesCount.end(), 0);
    std::fill(modesVar.begin(), modesVar.end(), 0.);
    for (int i=0; i<votes.size(); i++)
    {
      if (voteModeID.at(i)!=-1)
      {
	modesW.at(voteModeID.at(i)) += votesW.at(i);
	modesCount.at(voteModeID.at(i))++;

	Eigen::Matrix3f voteMat(Eigen::AngleAxisf(votes.at(i).x()*M_PI/180,
						  Eigen::Vector3f::UnitZ()) *
				Eigen::AngleAxisf(votes.at(i).y()*M_PI/180,
						  Eigen::Vector3f::UnitY()) *
				Eigen::AngleAxisf(votes.at(i).z()*M_PI/180,
						  Eigen::Vector3f::UnitX()));
	Eigen::Quaternion<float> modeQ(modes.at(voteModeID.at(i)));
	Eigen::Quaternion<float> voteQ(voteMat);

	Eigen::Quaternion<double> tmp(voteQ.inverse() * modeQ);
	float distQ = 2.*acos(fabs(tmp.w()));
	modesVar.at(voteModeID.at(i)) += distQ*distQ;
      }
    }

    for (int i=0; i<nModes; i++) modesVar.at(i)/=modesCount.at(i);

    // Finally, save the previous state and time delta (they will be used to disregard unplausible rotations)
    prevState = _prevState;
    deltaT = _deltaT;

    // Done
    delete []samples;
    delete []nonNullPixelsBuff;
  }
};

#endif // __PARTICLE_FILTER_HAND_LIKELIHOOD_HPP


//=======================================================================
// Copyright (c)
//
// 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 local_search_multi_solution_test.cpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-02-04
 */

#include "test_utils/logger.hpp"

#include "paal/local_search/local_search.hpp"
#include "paal/data_structures/combine_iterator.hpp"

#include <boost/test/unit_test.hpp>

#include <vector>
#include <string>
#include <cstdlib>

using std::string;
using std::vector;
using namespace paal;
const int DIM = 3;
const float LOWER_BOUND = 0;
const float UPPER_BOUND = 1;

typedef std::vector<float> Solution;
typedef float SolutionElement;
typedef SolutionElement Move;

struct value_diff {
    value_diff(SolutionElement &value, float diff)
        : m_value(&value), m_diff(diff) {}
    value_diff() = default;
    SolutionElement *m_value;
    float m_diff;
};

namespace {
float f(const Solution &x) {
    float x1(x.at(0)), x2(x.at(1)), x3(x.at(2));
    return x1 * x2 + x2 * x3 + x3 * x1 - 3 * x1 * x2 * x3;
}

struct make_value_diff {
    value_diff operator()(SolutionElement &value, float diff) const {
        return value_diff{ value, diff };
    }
};

SolutionElement normalize(SolutionElement el) {
    return std::min(std::max(el, LOWER_BOUND), UPPER_BOUND);
}

struct get_moves {
    typedef std::vector<Move> Neigh;
    using Iter = paal::data_structures::combine_iterator<make_value_diff,
                                                         Solution, const Neigh>;
    using IterPair = boost::iterator_range<Iter>;

    const Neigh neighb;

  public:

    get_moves() : neighb{ .01, -.01, .001, -.001 } {}

    IterPair operator()(Solution &s) {
        auto b = data_structures::make_combine_iterator(make_value_diff{}, s,
                                                        neighb);
        return boost::make_iterator_range(b, decltype(b) {});
    }
};

struct Gain {
    float operator()(Solution &s, value_diff vd) {
        auto &el = *vd.m_value;
        auto old = el;
        auto val = f(s);
        el = normalize(*vd.m_value + vd.m_diff);
        auto valMove = f(s);
        el = old;
        return valMove - val;
    }
};

struct Commit {
    bool operator()(Solution &s, value_diff vd) {
        *vd.m_value = normalize(*vd.m_value + vd.m_diff);
        return true;
    }
};

typedef local_search::search_components<get_moves, Gain, Commit> search_comps;

void fill_rand(Solution &s) {
    const int MAX_VAL = 10000;
    for (float &el : s) {
        el = float(std::rand() % MAX_VAL) / float(MAX_VAL);
    }
}

ON_LOG(int i;)

bool logger(const Solution &s) {
    // printing
    LOGLN("f(");
    LOG_COPY_RANGE_DEL(s, ",");
    LOGLN(") \t" << f(s) << " after " << i++);
    return true;
}

} // anonymous namespace

BOOST_AUTO_TEST_CASE(local_search_first_improving_test) {
    // creating local search
    Solution sol(DIM, 0);
    fill_rand(sol);

    // printing
    LOGLN("f(");
    LOG_COPY_RANGE_DEL(sol, ",");
    LOGLN(") \t" << f(sol));
    ON_LOG(i = 0);

    // search
    local_search::local_search(sol, local_search::first_improving_strategy{},
                               logger, utils::always_false(), search_comps());
    BOOST_CHECK_EQUAL(f(sol), 1.);
}

BOOST_AUTO_TEST_CASE(local_search_best_improving_test) {
    // creating local search
    Solution sol(DIM, 0);
    fill_rand(sol);

    // printing
    LOGLN("f(");
    LOG_COPY_RANGE_DEL(sol, ",");
    LOGLN(") \t" << f(sol));
    ON_LOG(i = 0);

    // search
    local_search::local_search(sol, local_search::best_improving_strategy{},
                               logger, utils::always_false(), search_comps());
    BOOST_CHECK_EQUAL(f(sol), 1.);
}


// Copyright 2010 The Trustees of Indiana University.

// 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)

//  Authors: Jeremiah Willcock
//           Andrew Lumsdaine

#ifndef BOOST_GRAPH_RANDOM_SPANNING_TREE_HPP
#define BOOST_GRAPH_RANDOM_SPANNING_TREE_HPP

#include <vector>
#include <boost/assert.hpp>
#include <boost/graph/loop_erased_random_walk.hpp>
#include <boost/graph/random.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/named_function_params.hpp>

namespace boost {

  namespace detail {
    // Use Wilson's algorithm (based on loop-free random walks) to generate a
    // random spanning tree.  The distribution of edges used is controlled by
    // the next_edge() function, so this version allows either weighted or
    // unweighted selection of trees.
    // Algorithm is from http://en.wikipedia.org/wiki/Uniform_spanning_tree
    template <typename Graph, typename PredMap, typename ColorMap, typename NextEdge>
    void random_spanning_tree_internal(const Graph& g, typename graph_traits<Graph>::vertex_descriptor s, PredMap pred, ColorMap color, NextEdge next_edge) {
      typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
      typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;

      BOOST_ASSERT (num_vertices(g) >= 1); // g must also be undirected (or symmetric) and connected

      typedef color_traits<typename property_traits<ColorMap>::value_type> color_gen;
      BGL_FORALL_VERTICES_T(v, g, Graph) put(color, v, color_gen::white());

      std::vector<vertex_descriptor> path;

      put(color, s, color_gen::black());
      put(pred, s, graph_traits<Graph>::null_vertex());

      BGL_FORALL_VERTICES_T(v, g, Graph) {
        if (get(color, v) != color_gen::white()) continue;
        loop_erased_random_walk(g, v, next_edge, color, path);
        for (typename std::vector<vertex_descriptor>::const_reverse_iterator i = path.rbegin();
             boost::next(i) !=
               (typename std::vector<vertex_descriptor>::const_reverse_iterator)path.rend();
             ++i) {
          typename std::vector<vertex_descriptor>::const_reverse_iterator j = i;
          ++j;
          BOOST_ASSERT (get(color, *j) == color_gen::gray());
          put(color, *j, color_gen::black());
          put(pred, *j, *i);
        }
      }
    }
  }

  // Compute a uniformly-distributed spanning tree on a graph.  Use Wilson's algorithm:
  // @inproceedings{wilson96generating,
  //    author = {Wilson, David Bruce},
  //    title = {Generating random spanning trees more quickly than the cover time},
  //    booktitle = {STOC '96: Proceedings of the twenty-eighth annual ACM symposium on Theory of computing},
  //    year = {1996},
  //    isbn = {0-89791-785-5},
  //    pages = {296--303},
  //    location = {Philadelphia, Pennsylvania, United States},
  //    doi = {http://doi.acm.org/10.1145/237814.237880},
  //    publisher = {ACM},
  //    address = {New York, NY, USA},
  //  }
  //
  template <typename Graph, typename Gen, typename PredMap, typename ColorMap>
  void random_spanning_tree(const Graph& g, Gen& gen, typename graph_traits<Graph>::vertex_descriptor root,
                            PredMap pred, static_property_map<double>, ColorMap color) {
    unweighted_random_out_edge_gen<Graph, Gen> random_oe(gen);
    detail::random_spanning_tree_internal(g, root, pred, color, random_oe);
  }

  // Compute a weight-distributed spanning tree on a graph.
  template <typename Graph, typename Gen, typename PredMap, typename WeightMap, typename ColorMap>
  void random_spanning_tree(const Graph& g, Gen& gen, typename graph_traits<Graph>::vertex_descriptor root,
                            PredMap pred, WeightMap weight, ColorMap color) {
    weighted_random_out_edge_gen<Graph, WeightMap, Gen> random_oe(weight, gen);
    detail::random_spanning_tree_internal(g, root, pred, color, random_oe);
  }

  template <typename Graph, typename Gen, typename P, typename T, typename R>
  void random_spanning_tree(const Graph& g, Gen& gen, const bgl_named_params<P, T, R>& params) {
    using namespace boost::graph::keywords;
    typedef bgl_named_params<P, T, R> params_type;
    BOOST_GRAPH_DECLARE_CONVERTED_PARAMETERS(params_type, params)
    random_spanning_tree(g,
                         gen,
                         arg_pack[_root_vertex | *vertices(g).first],
                         arg_pack[_predecessor_map],
                         arg_pack[_weight_map | static_property_map<double>(1.)],
                         boost::detail::make_color_map_from_arg_pack(g, arg_pack));
  }
}

#include <boost/graph/iteration_macros_undef.hpp>

#endif // BOOST_GRAPH_RANDOM_SPANNING_TREE_HPP


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2003, 2004 Ferdinando Ametrano

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#ifndef quantlib_test_lowdiscrepancysequences_hpp
#define quantlib_test_lowdiscrepancysequences_hpp

#include <boost/test/unit_test.hpp>

/* remember to document new and/or updated tests in the Doxygen
   comment block of the corresponding class */

class LowDiscrepancyTest {
  public:
    static void testSeedGenerator();

    static void testPolynomialsModuloTwo();

    static void testSobol();
    static void testHalton();
    static void testFaure();

    static void testMersenneTwisterDiscrepancy();
    static void testPlainHaltonDiscrepancy();
    static void testRandomStartHaltonDiscrepancy();
    static void testRandomShiftHaltonDiscrepancy();
    static void testRandomStartRandomShiftHaltonDiscrepancy();
    static void testJackelSobolDiscrepancy();
    static void testSobolLevitanSobolDiscrepancy();
    static void testSobolLevitanLemieuxSobolDiscrepancy();
    static void testUnitSobolDiscrepancy();

    static void testRandomizedLowDiscrepancySequence();

    static void testSobolSkipping();

    static void testRandomizedLattices();

    static boost::unit_test_framework::test_suite* suite();
};

#endif


#ifndef qlex_ibor_ois_basis_swap_hpp
#define qlex_ibor_ois_basis_swap_hpp

#include <ql/instruments/swap.hpp>
#include <ql/indexes/iborindex.hpp>
#include <ql/time/daycounter.hpp>
#include <ql/time/schedule.hpp>
#include <boost/optional.hpp>

using namespace QuantLib;

namespace QLExtension {

	class QuantLib::IborIndex;
	class QuantLib::OvernightIndex;

	//! Ibor OIS basis swap: ibor vs compounded overnight rate + spread
	class IBOROISBasisSwap : public Swap {
	public:
		enum Type { Receiver = -1, Payer = 1 };
		IBOROISBasisSwap(
			Type type,
			Real nominal,
			const Schedule& floatSchedule,
			const boost::shared_ptr<IborIndex>& iborIndex,
			const DayCounter& floatingDayCount,
			const Schedule& overnightSchedule,
			const boost::shared_ptr<OvernightIndex>& overnightIndex,
			Spread spread,
			const DayCounter& overnightDayCount,
			boost::optional<BusinessDayConvention> paymentConvention =
			boost::none,
			bool arithmeticAveragedCoupon = true);
		IBOROISBasisSwap(
			Type type,
			std::vector<Real> nominals,
			const Schedule& floatSchedule,
			const boost::shared_ptr<IborIndex>& iborIndex,
			const DayCounter& floatingDayCount,
			const Schedule& overnightSchedule,
			const boost::shared_ptr<OvernightIndex>& overnightIndex,
			Spread spread,
			const DayCounter& overnightDayCount,
			boost::optional<BusinessDayConvention> paymentConvention =
			boost::none,
			bool arithmeticAveragedCoupon = true);
		//! \name Inspectors
		//@{
		Type type() const { return type_; }
		Real nominal() const;
		std::vector<Real> nominals() const { return nominals_; }

		const Schedule& floatingSchedule() const;
		const boost::shared_ptr<IborIndex>& iborIndex() const { return iborIndex_; }
		const DayCounter& floatingDayCount() const;

		const Schedule& overnightSchedule() { return overnightSchedule_; }
		const boost::shared_ptr<OvernightIndex>& overnightIndex() { return overnightIndex_; }
		Spread spread() { return spread_; }

		BusinessDayConvention paymentConvention() const { return paymentConvention_; }

		const Leg& floatingLeg() const { return legs_[0]; }
		const Leg& overnightLeg() const { return legs_[1]; }
		//@}

		//! \name Results
		//@{
		Real floatingLegBPS() const;
		Real floatingLegNPV() const;

		Real overnightLegBPS() const;
		Real overnightLegNPV() const;
		Spread fairSpread() const;
		//@}
	private:
		void initialize();
		Type type_;
		std::vector<Real> nominals_;

		Schedule floatingSchedule_;
		boost::shared_ptr<IborIndex> iborIndex_;
		DayCounter floatingDayCount_;

		Schedule overnightSchedule_;
		boost::shared_ptr<OvernightIndex> overnightIndex_;
		Spread spread_;
		DayCounter overnightDayCount_;

		BusinessDayConvention paymentConvention_;

		bool arithmeticAveragedCoupon_;
	};

	// inline

	inline Real IBOROISBasisSwap::nominal() const {
		QL_REQUIRE(nominals_.size() == 1, "varying nominals");
		return nominals_[0];
	}
}

#endif