/********************************************************************************
*  This file is part of CinoLib                                                 *
*  Copyright(C) 2016: Marco Livesu                                              *
*                                                                               *
*  The MIT License                                                              *
*                                                                               *
*  Permission is hereby granted, free of charge, to any person obtaining a      *
*  copy of this software and associated documentation files (the "Software"),   *
*  to deal in the Software without restriction, including without limitation    *
*  the rights to use, copy, modify, merge, publish, distribute, sublicense,     *
*  and/or sell copies of the Software, and to permit persons to whom the        *
*  Software is furnished to do so, subject to the following conditions:         *
*                                                                               *
*  The above copyright notice and this permission notice shall be included in   *
*  all copies or substantial portions of the Software.                          *
*                                                                               *
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   *
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *
*  FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE *
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       *
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      *
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS *
*  IN THE SOFTWARE.                                                             *
*                                                                               *
*  Author(s):                                                                   *
*                                                                               *
*     Marco Livesu (marco.livesu@gmail.com)                                     *
*     http://pers.ge.imati.cnr.it/livesu/                                       *
*                                                                               *
*     Italian National Research Council (CNR)                                   *
*     Institute for Applied Mathematics and Information Technologies (IMATI)    *
*     Via de Marini, 6                                                          *
*     16149 Genoa,                                                              *
*     Italy                                                                     *
*********************************************************************************/
#include <cinolib/matrix.h>
#include <Eigen/Eigenvalues>

namespace cinolib
{

// http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
//
CINO_INLINE
void eigen_decomposition_2x2(const float   a00,
                             const float   a01,
                             const float   a10,
                             const float   a11,
                                   vec2f  & v_min, // eigenvectors
                                   vec2f  & v_max,
                                   float & min,   // eigenvalues
                                   float & max)
{
    eigenvalues_2x2(a00,a01,a10,a11,min,max);

    if(std::fabs(a10)>1e-5)
    {
        v_max = vec2f(max-a11,a10);
        v_min = vec2f(min-a11,a10);
    }
    else if(std::fabs(a01)>1e-5)
    {
        v_max = vec2f(a01,max-a00);
        v_min = vec2f(a01,min-a00);
    }
    else
    {
        v_max = (a00>=a11) ? vec2f(1,0) : vec2f(0,1);
        v_min = (a00>=a11) ? vec2f(0,1) : vec2f(1,0);
    }

    v_max.normalize();
    v_min.normalize();
}

// http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
//
CINO_INLINE
void eigenvalues_2x2(const float   a00,
                     const float   a01,
                     const float   a10,
                     const float   a11,
                           float & min,
                           float & max)
{
    float T = a00 + a11; // trace
    float D = determinant_2x2(a00,a01,a10,a11);

    min = T/2.0 - sqrt(T*T/4.0-D);
    max = T/2.0 + sqrt(T*T/4.0-D);
}

CINO_INLINE
void eigenvectors_2x2(const float   a00,
                      const float   a01,
                      const float   a10,
                      const float   a11,
                            vec2f  & v_min,
                            vec2f  & v_max)
{
    float min, max;
    eigen_decomposition_2x2(a00, a01, a10, a11, v_min, v_max, min, max);
}

CINO_INLINE
float determinant_2x2(const float a00, const float a01, const float a10, const float a11)
{
    return ((a00*a11) - (a10*a01));
}

CINO_INLINE
float determinant_2x2(const vec2f a0, const vec2f a1)
{
    return determinant_2x2(a0[0], a0[1], a1[0], a1[1]);
}

CINO_INLINE
void eigen_decomposition_3x3(const float   a[3][3],
                                   vec3f  & v_min, // eigenvectors
                                   vec3f  & v_mid,
                                   vec3f  & v_max,
                                   float & min,   // eigenvalues
                                   float & mid,
                                   float & max)
{
    eigen_decomposition_3x3(a[0][0], a[0][1], a[0][2],
                            a[1][0], a[1][1], a[1][2],
                            a[2][0], a[2][1], a[2][2],
                            v_min, v_mid, v_max,
                            min, mid, max);
}

CINO_INLINE
void eigen_decomposition_3x3(const float   a00,
                             const float   a01,
                             const float   a02,
                             const float   a10,
                             const float   a11,
                             const float   a12,
                             const float   a20,
                             const float   a21,
                             const float   a22,
                                   vec3f  & v_min, // eigenvectors
                                   vec3f  & v_mid,
                                   vec3f  & v_max,
                                   float & min,   // eigenvalues
                                   float & mid,
                                   float & max)
{
    Eigen::Matrix3d m;
    m << a00, a01, a02,
         a10, a11, a12,
         a20, a21, a22;

    bool symmetric = (a10==a01) && (a20==a02) && (a21==a12);

    if(symmetric)
    {
        // eigen decomposition for self-adjoint matrices
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(m);
        assert(eig.info() == Eigen::Success);

        v_min = vec3f(eig.eigenvectors()(0,0), eig.eigenvectors()(1,0), eig.eigenvectors()(2,0));
        v_mid = vec3f(eig.eigenvectors()(0,1), eig.eigenvectors()(1,1), eig.eigenvectors()(2,1));
        v_max = vec3f(eig.eigenvectors()(0,2), eig.eigenvectors()(1,2), eig.eigenvectors()(2,2));

        min = eig.eigenvalues()[0];
        mid = eig.eigenvalues()[1];
        max = eig.eigenvalues()[2];
    }
    else
    {
        // eigen decomposition for general matrices
        Eigen::EigenSolver<Eigen::Matrix3d> eig(m);
        assert(eig.info() == Eigen::Success);

        // WARNING: I am taking only the real part!
        v_min = vec3f(eig.eigenvectors()(0,0).real(), eig.eigenvectors()(1,0).real(), eig.eigenvectors()(2,0).real());
        v_mid = vec3f(eig.eigenvectors()(0,1).real(), eig.eigenvectors()(1,1).real(), eig.eigenvectors()(2,1).real());
        v_max = vec3f(eig.eigenvectors()(0,2).real(), eig.eigenvectors()(1,2).real(), eig.eigenvectors()(2,2).real());

        // WARNING: I am taking only the real part!
        min = eig.eigenvalues()[0].real();
        mid = eig.eigenvalues()[1].real();
        max = eig.eigenvalues()[2].real();
    }
}

CINO_INLINE
void eigenvalues_3x3(const float   a00,
                     const float   a01,
                     const float   a02,
                     const float   a10,
                     const float   a11,
                     const float   a12,
                     const float   a20,
                     const float   a21,
                     const float   a22,
                           float & min,
                           float & mid,
                           float & max)
{
    vec3f v_min, v_mid, v_max;
    eigen_decomposition_3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22, v_min, v_mid, v_max, min, mid, max);
}

CINO_INLINE
void eigenvectors_3x3(const float   a00,
                      const float   a01,
                      const float   a02,
                      const float   a10,
                      const float   a11,
                      const float   a12,
                      const float   a20,
                      const float   a21,
                      const float   a22,
                            vec3f  & v_min,
                            vec3f  & v_mid,
                            vec3f  & v_max)
{
    float min, mid, max;
    eigen_decomposition_3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22, v_min, v_mid, v_max, min, mid, max);
}

CINO_INLINE
float determinant_3x3(const float a00, const float a01, const float a02,
                       const float a10, const float a11, const float a12,
                       const float a20, const float a21, const float a22)
{
    return a00 * determinant_2x2(a11, a12, a21, a22) -
           a01 * determinant_2x2(a10, a12, a20, a22) +
           a02 * determinant_2x2(a10, a11, a20, a21);
}

CINO_INLINE
void from_std_3x3_to_Eigen_3x3(const float stdM[3][3], Eigen::Matrix3d & eigenM)
{
    eigenM.coeffRef(0,0) = stdM[0][0];  eigenM.coeffRef(0,1) = stdM[0][1];  eigenM.coeffRef(0,2) = stdM[0][2];
    eigenM.coeffRef(1,0) = stdM[1][0];  eigenM.coeffRef(1,1) = stdM[1][1];  eigenM.coeffRef(1,2) = stdM[1][2];
    eigenM.coeffRef(2,0) = stdM[2][0];  eigenM.coeffRef(2,1) = stdM[2][1];  eigenM.coeffRef(2,2) = stdM[2][2];
}

CINO_INLINE
void from_eigen_3x3_to_std_3x3(const Eigen::Matrix3d & eigenM, float stdM[3][3])
{
    stdM[0][0] = eigenM.coeffRef(0,0);  stdM[0][1] = eigenM.coeffRef(0,1);  stdM[0][2] = eigenM.coeffRef(0,2);
    stdM[1][0] = eigenM.coeffRef(1,0);  stdM[1][1] = eigenM.coeffRef(1,1);  stdM[1][2] = eigenM.coeffRef(1,2);
    stdM[2][0] = eigenM.coeffRef(2,0);  stdM[2][1] = eigenM.coeffRef(2,1);  stdM[2][2] = eigenM.coeffRef(2,2);
}
}


// Copyright Nick Thompson, 2017
// 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_INTERPOLATORS_CARDINAL_CUBIC_B_SPLINE_DETAIL_HPP
#define BOOST_MATH_INTERPOLATORS_CARDINAL_CUBIC_B_SPLINE_DETAIL_HPP

#include <limits>
#include <cmath>
#include <vector>
#include <memory>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/fpclassify.hpp>

namespace boost{ namespace math{ namespace interpolators{ namespace detail{

template <class Real>
class cardinal_cubic_b_spline_imp
{
public:
    // If you don't know the value of the derivative at the endpoints, leave them as nans and the routine will estimate them.
    // f[0] = f(a), f[length -1] = b, step_size = (b - a)/(length -1).
    template <class BidiIterator>
    cardinal_cubic_b_spline_imp(BidiIterator f, BidiIterator end_p, Real left_endpoint, Real step_size,
                       Real left_endpoint_derivative = std::numeric_limits<Real>::quiet_NaN(),
                       Real right_endpoint_derivative = std::numeric_limits<Real>::quiet_NaN());

    Real operator()(Real x) const;

    Real prime(Real x) const;

    Real double_prime(Real x) const;

private:
    std::vector<Real> m_beta;
    Real m_h_inv;
    Real m_a;
    Real m_avg;
};

template <class Real>
Real b3_spline(Real x)
{
    using std::abs;
    Real absx = abs(x);
    if (absx < 1)
    {
        Real y = 2 - absx;
        Real z = 1 - absx;
        return boost::math::constants::sixth<Real>()*(y*y*y - 4*z*z*z);
    }
    if (absx < 2)
    {
        Real y = 2 - absx;
        return boost::math::constants::sixth<Real>()*y*y*y;
    }
    return (Real) 0;
}

template<class Real>
Real b3_spline_prime(Real x)
{
    if (x < 0)
    {
        return -b3_spline_prime(-x);
    }

    if (x < 1)
    {
        return x*(3*boost::math::constants::half<Real>()*x - 2);
    }
    if (x < 2)
    {
        return -boost::math::constants::half<Real>()*(2 - x)*(2 - x);
    }
    return (Real) 0;
}

template<class Real>
Real b3_spline_double_prime(Real x)
{
    if (x < 0)
    {
        return b3_spline_double_prime(-x);
    }

    if (x < 1)
    {
        return 3*x - 2;
    }
    if (x < 2)
    {
        return (2 - x);
    }
    return (Real) 0;
}

template <class Real>
template <class BidiIterator>
cardinal_cubic_b_spline_imp<Real>::cardinal_cubic_b_spline_imp(BidiIterator f, BidiIterator end_p, Real left_endpoint, Real step_size,
                                             Real left_endpoint_derivative, Real right_endpoint_derivative) : m_a(left_endpoint), m_avg(0)
{
    using boost::math::constants::third;

    std::size_t length = end_p - f;

    if (length < 5)
    {
        if (boost::math::isnan(left_endpoint_derivative) || boost::math::isnan(right_endpoint_derivative))
        {
            throw std::logic_error("Interpolation using a cubic b spline with derivatives estimated at the endpoints requires at least 5 points.\n");
        }
        if (length < 3)
        {
            throw std::logic_error("Interpolation using a cubic b spline requires at least 3 points.\n");
        }
    }

    if (boost::math::isnan(left_endpoint))
    {
        throw std::logic_error("Left endpoint is NAN; this is disallowed.\n");
    }
    if (left_endpoint + length*step_size >= (std::numeric_limits<Real>::max)())
    {
        throw std::logic_error("Right endpoint overflows the maximum representable number of the specified precision.\n");
    }
    if (step_size <= 0)
    {
        throw std::logic_error("The step size must be strictly > 0.\n");
    }
    // Storing the inverse of the stepsize does provide a measurable speedup.
    // It's not huge, but nonetheless worthwhile.
    m_h_inv = 1/step_size;

    // Following Kress's notation, s'(a) = a1, s'(b) = b1
    Real a1 = left_endpoint_derivative;
    // See the finite-difference table on Wikipedia for reference on how
    // to construct high-order estimates for one-sided derivatives:
    // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Forward_and_backward_finite_difference
    // Here, we estimate then to O(h^4), as that is the maximum accuracy we could obtain from this method.
    if (boost::math::isnan(a1))
    {
        // For simple functions (linear, quadratic, so on)
        // almost all the error comes from derivative estimation.
        // This does pairwise summation which gives us another digit of accuracy over naive summation.
        Real t0 = 4*(f[1] + third<Real>()*f[3]);
        Real t1 = -(25*third<Real>()*f[0] + f[4])/4  - 3*f[2];
        a1 = m_h_inv*(t0 + t1);
    }

    Real b1 = right_endpoint_derivative;
    if (boost::math::isnan(b1))
    {
        size_t n = length - 1;
        Real t0 = 4*(f[n-3] + third<Real>()*f[n - 1]);
        Real t1 = -(25*third<Real>()*f[n - 4] + f[n])/4  - 3*f[n - 2];

        b1 = m_h_inv*(t0 + t1);
    }

    // s(x) = \sum \alpha_i B_{3}( (x- x_i - a)/h )
    // Of course we must reindex from Kress's notation, since he uses negative indices which make C++ unhappy.
    m_beta.resize(length + 2, std::numeric_limits<Real>::quiet_NaN());

    // Since the splines have compact support, they decay to zero very fast outside the endpoints.
    // This is often very annoying; we'd like to evaluate the interpolant a little bit outside the
    // boundary [a,b] without massive error.
    // A simple way to deal with this is just to subtract the DC component off the signal, so we need the average.
    // This algorithm for computing the average is recommended in
    // http://www.heikohoffmann.de/htmlthesis/node134.html
    Real t = 1;
    for (size_t i = 0; i < length; ++i)
    {
        if (boost::math::isnan(f[i]))
        {
            std::string err = "This function you are trying to interpolate is a nan at index " + std::to_string(i) + "\n";
            throw std::logic_error(err);
        }
        m_avg += (f[i] - m_avg) / t;
        t += 1;
    }

    // Now we must solve an almost-tridiagonal system, which requires O(N) operations.
    // There are, in fact 5 diagonals, but they only differ from zero on the first and last row,
    // so we can patch up the tridiagonal row reduction algorithm to deal with two special rows.
    // See Kress, equations 8.41
    // The the "tridiagonal" matrix is:
    // 1  0 -1
    // 1  4  1
    //    1  4  1
    //       1  4  1
    //          ....
    //          1  4  1
    //          1  0 -1
    // Numerical estimate indicate that as N->Infinity, cond(A) -> 6.9, so this matrix is good.
    std::vector<Real> rhs(length + 2, std::numeric_limits<Real>::quiet_NaN());
    std::vector<Real> super_diagonal(length + 2, std::numeric_limits<Real>::quiet_NaN());

    rhs[0] = -2*step_size*a1;
    rhs[rhs.size() - 1] = -2*step_size*b1;

    super_diagonal[0] = 0;

    for(size_t i = 1; i < rhs.size() - 1; ++i)
    {
        rhs[i] = 6*(f[i - 1] - m_avg);
        super_diagonal[i] = 1;
    }

    // One step of row reduction on the first row to patch up the 5-diagonal problem:
    // 1 0 -1 | r0
    // 1 4 1  | r1
    // mapsto:
    // 1 0 -1 | r0
    // 0 4 2  | r1 - r0
    // mapsto
    // 1 0 -1 | r0
    // 0 1 1/2| (r1 - r0)/4
    super_diagonal[1] = 0.5;
    rhs[1] = (rhs[1] - rhs[0])/4;

    // Now do a tridiagonal row reduction the standard way, until just before the last row:
    for (size_t i = 2; i < rhs.size() - 1; ++i)
    {
        Real diagonal = 4 - super_diagonal[i - 1];
        rhs[i] = (rhs[i] - rhs[i - 1])/diagonal;
        super_diagonal[i] /= diagonal;
    }

    // Now the last row, which is in the form
    // 1 sd[n-3] 0      | rhs[n-3]
    // 0  1     sd[n-2] | rhs[n-2]
    // 1  0     -1      | rhs[n-1]
    Real final_subdiag = -super_diagonal[rhs.size() - 3];
    rhs[rhs.size() - 1] = (rhs[rhs.size() - 1] - rhs[rhs.size() - 3])/final_subdiag;
    Real final_diag = -1/final_subdiag;
    // Now we're here:
    // 1 sd[n-3] 0         | rhs[n-3]
    // 0  1     sd[n-2]    | rhs[n-2]
    // 0  1     final_diag | (rhs[n-1] - rhs[n-3])/diag

    final_diag = final_diag - super_diagonal[rhs.size() - 2];
    rhs[rhs.size() - 1] = rhs[rhs.size() - 1] - rhs[rhs.size() - 2];

    // Back substitutions:
    m_beta[rhs.size() - 1] = rhs[rhs.size() - 1]/final_diag;
    for(size_t i = rhs.size() - 2; i > 0; --i)
    {
        m_beta[i] = rhs[i] - super_diagonal[i]*m_beta[i + 1];
    }
    m_beta[0] = m_beta[2] + rhs[0];
}

template<class Real>
Real cardinal_cubic_b_spline_imp<Real>::operator()(Real x) const
{
    // See Kress, 8.40: Since B3 has compact support, we don't have to sum over all terms,
    // just the (at most 5) whose support overlaps the argument.
    Real z = m_avg;
    Real t = m_h_inv*(x - m_a) + 1;

    using std::max;
    using std::min;
    using std::ceil;
    using std::floor;

    size_t k_min = (size_t) (max)(static_cast<long>(0), boost::math::ltrunc(ceil(t - 2)));
    size_t k_max = (size_t) (max)((min)(static_cast<long>(m_beta.size() - 1), boost::math::ltrunc(floor(t + 2))), (long) 0);

    for (size_t k = k_min; k <= k_max; ++k)
    {
        z += m_beta[k]*b3_spline(t - k);
    }

    return z;
}

template<class Real>
Real cardinal_cubic_b_spline_imp<Real>::prime(Real x) const
{
    Real z = 0;
    Real t = m_h_inv*(x - m_a) + 1;

    using std::max;
    using std::min;
    using std::ceil;
    using std::floor;

    size_t k_min = (size_t) (max)(static_cast<long>(0), boost::math::ltrunc(ceil(t - 2)));
    size_t k_max = (size_t) (min)(static_cast<long>(m_beta.size() - 1), boost::math::ltrunc(floor(t + 2)));

    for (size_t k = k_min; k <= k_max; ++k)
    {
        z += m_beta[k]*b3_spline_prime(t - k);
    }
    return z*m_h_inv;
}

template<class Real>
Real cardinal_cubic_b_spline_imp<Real>::double_prime(Real x) const
{
    Real z = 0;
    Real t = m_h_inv*(x - m_a) + 1;

    using std::max;
    using std::min;
    using std::ceil;
    using std::floor;

    size_t k_min = (size_t) (max)(static_cast<long>(0), boost::math::ltrunc(ceil(t - 2)));
    size_t k_max = (size_t) (min)(static_cast<long>(m_beta.size() - 1), boost::math::ltrunc(floor(t + 2)));

    for (size_t k = k_min; k <= k_max; ++k)
    {
        z += m_beta[k]*b3_spline_double_prime(t - k);
    }
    return z*m_h_inv*m_h_inv;
}

}}}}
#endif


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/integral.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/monoid/laws.hpp>
using namespace boost::hana;

int main() {
    BOOST_HANA_CONSTANT_ASSERT(zero<Integral> == int_<0>);
    BOOST_HANA_CONSTANT_ASSERT(plus(int_<3>, int_<4>) == int_<3 + 4>);
    BOOST_HANA_CONSTEXPR_ASSERT(plus(int_<3>, 4) == 3 + 4);
    BOOST_HANA_CONSTEXPR_ASSERT(plus(3, int_<4>) == 3 + 4);

    BOOST_HANA_CONSTEXPR_ASSERT(Monoid::laws::check(list(
        int_<1>, short_<2>, long_<3>, ullong<4>, 5, 6ull
    )));
}


//          Copyright Gavin Band 2008 - 2012.
// 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)

#include <string>
#include <map>
#include <cmath>
#include <cassert>
#include <boost/function.hpp>
#include <Eigen/Core>
#include "genfile/SingleSNPGenotypeProbabilities.hpp"
#include "components/SNPSummaryComponent/GenotypeFrequencyTestCallComparer.hpp"
#include "metro/likelihood/Multinomial.hpp"
#include "metro/likelihood/ProductOfMultinomials.hpp"
#include "metro/rBF.hpp"

#define GENOTYPE_FREQUENCY_TEST_CALL_COMPARER_DEBUG 0

namespace {
	typedef Eigen::VectorXd Vector ;
	typedef Eigen::MatrixXd Matrix ;
	typedef metro::likelihood::Multinomial< double, Vector, Matrix > Multinomial ;
	typedef metro::likelihood::ProductOfMultinomials< double, Vector, Matrix > ProductOfIndependentMultinomials ;
}

GenotypeFrequencyTestCallComparer::GenotypeFrequencyTestCallComparer():
	m_threshhold( 0.9 ),
 	m_chi_squared( 2.0 )
{}

void GenotypeFrequencyTestCallComparer::compare(
	Eigen::MatrixXd const& left,
	Eigen::MatrixXd const& right,
	Callback callback
) const {
	// Make table of counts
	assert( left.rows() == right.rows() ) ;
	assert( left.cols() == 3 ) ;
	assert( right.cols() == 3 ) ;

	std::size_t const N = left.rows() ;

	Matrix table = Matrix::Zero( 2, 3 ) ;

	for( std::size_t i = 0; i < N; ++i ) {
		for( int g = 0; g < 3; ++g ) {
			if( left( i, g ) > m_threshhold ) {
				++table( 0, g ) ;
				break ;
			}
		}

		for( int g = 0; g < 3; ++g ) {
			if( right( i, g ) > m_threshhold ) {
				++table( 1, g ) ;
				break ;
			}
		}
	}

	ProductOfIndependentMultinomials alt_model( table ) ;
	Multinomial null_model( table.row(0) + table.row(1) ) ;

	null_model.evaluate_at( null_model.get_MLE() ) ;
	alt_model.evaluate_at( alt_model.get_MLE() ) ;

	double likelihood_ratio_statistic = 2.0 * ( alt_model.get_value_of_function() - null_model.get_value_of_function() ) ;
	double p_value = std::numeric_limits< double >::quiet_NaN() ;
	if( likelihood_ratio_statistic != likelihood_ratio_statistic || likelihood_ratio_statistic < 0.0 ) {
		likelihood_ratio_statistic = std::numeric_limits< double >::quiet_NaN() ;
	}
	else {
		p_value = boost::math::cdf(
			boost::math::complement(
				m_chi_squared,
				likelihood_ratio_statistic
			)
		) ;
	}

#if GENOTYPE_FREQUENCY_TEST_CALL_COMPARER_DEBUG
	std::cerr << "Table is:\n" << table << ".\n" ;
#endif

	callback( "likelihood_ratio_test_statistic", likelihood_ratio_statistic ) ;
	callback( "pvalue", p_value ) ;
	callback( "lambda=60/rBF", metro::compute_rBF( table, 60 )) ;
}


#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "helpers.hpp"
#include <Eigen/Dense>
#include <celerite2/celerite2.h>

using namespace celerite2::test;
using namespace celerite2::core;

TEMPLATE_LIST_TEST_CASE("check the results of factor", "[factor]", TestKernels) {
  SETUP_TEST(50);

  Vector d;
  LowRank W;
  Matrix K, S;
  to_dense(x, c, a, U, V, K);

  // Brute force the Cholesky factorization
  Eigen::LDLT<Matrix> LDLT(K);
  Eigen::MatrixXd matrixL = LDLT.matrixL();

  SECTION("general") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, d, W, S);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, W, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - d).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }

  SECTION("no grad") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, d, W);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, W, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - d).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }

  SECTION("inplace") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, a, V, S);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, V, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - a).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }
}


///////////////////////////////////////////////////////////////////////////////
// accumulator::statistics::proportion_less_than.hpp                         //
//                                                                           //
//  Copyright 2008 Erwann Rogard. 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)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_ACCUMULATOR_STATISTICS_PROPORTION_LESS_THAN_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_ACCUMULATOR_STATISTICS_PROPORTION_LESS_THAN_HPP_ER_2009
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/parameter/binding.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics_fwd.hpp>

#include <boost/statistics/detail/accumulator/statistics/count_less_than.hpp>

namespace boost {
namespace statistics{
namespace detail{
namespace accumulator{

namespace impl
{

    template<typename T>
    class proportion_less_than : public boost::accumulators::accumulator_base
    {
        typedef boost::accumulators::dont_care dont_care_;
        typedef tag::count_less_than tag_m_;
        typedef boost::accumulators::tag::count tag_n_;
        typedef boost::accumulators::tag::accumulator tag_acc_;

        public:

        typedef T result_type;

        proportion_less_than(dont_care_){}

        void operator()(dont_care_)const{}

        template<typename Args>
        result_type result(const Args& args) const
        {
            typedef boost::parameter::binding<Args,tag_acc_> bind_;
            typedef typename bind_::type cref_;
            cref_ acc = args[boost::accumulators::accumulator];
            T res =  boost::accumulators::extract_result<tag_m_>( acc );
            res /= static_cast<T>(
                boost::accumulators::extract_result<tag_n_>( acc )
            );
            return res;
        }
    };

}//impl

///////////////////////////////////////////////////////////////////////////////
// tag::proportion_less_than
namespace tag
{
    struct proportion_less_than
      : boost::accumulators::depends_on<
        boost::accumulators::tag::count,
        tag::count_less_than
    >
    {
      typedef statistics::detail::accumulator::
      	impl::proportion_less_than<boost::mpl::_1> impl;
    };
}

///////////////////////////////////////////////////////////////////////////////
// extract::proportion_less_than
namespace extract
{

  	template<typename AccSet>
  	typename boost::mpl::apply<
    	AccSet,
        statistics::detail::accumulator::tag::proportion_less_than
	>::type::result_type
  	proportion_less_than(AccSet const& acc){
    	typedef statistics::detail::accumulator
        	::tag::proportion_less_than the_tag;
    	return boost::accumulators::extract_result<the_tag>(acc);
  	}

}

using extract::proportion_less_than;

}// accumulator
}// detail
}// statistics
}// boost

#endif


/*
* compile with flags:   g++ test.cc tree_classifier.cc   -std=c++14 -larmadillo -I ../../include/
* author: Yuzhen Liu
* Date: 2019.5.8 17:35
*/

#include <iostream>
#include <armadillo>
#include <cmath>
#include <trees/treenode.h>
#include <trees/tree_classifier.h>
#include <trees/random_forest.h>
#include <trees/cart.h>
#include <trees/gradient_boosting_dt.h>
#include <stdlib.h>
#include <datasets/datasets.h>

using namespace std;
using namespace arma;

pair<umat, uvec> generate_data() {
    int n_sample = 100;
    int n_feature = 4;
    umat x(n_feature, n_sample);
    uvec y(n_sample);

    // generate n samples iteratively
    srand((unsigned)time(NULL));
    for(int i =0; i < n_sample; i++) {
        x(0, i) = rand() % 10;
        x(1, i) = rand() % 10 + 100;
        x(2, i) = rand() % 10;
        x(3, i) = rand() % 10;

        if (x(0, i) <= 5 && x(1, i) > 105 && x(2, i) > 5 )  y(i) = 0;
        else if (x(0, i) > 5 && x(1, i) == 106 && x(2, i) <= 5 && x(3, i) > 5)  y(i) = 1;
        else if (x(0, i) <= 5 && x(1, i) <= 105 && x(2, i) < 5)  y(i) = 2;
        else y(i) = 3;
    }
    return make_pair(x, y);
}

int main() {
    // pair<umat, uvec> p = generate_data();
    // umat x = p.first;
    // uvec y = p.second;
    // x.print();
    // y.print();

    /*******************************
    * Tree classifier testing
    *******************************/
    // Tree_Classifier tree_classifier = Tree_Classifier(20/*max_depth=10, max_entropy=1.0*/);
    // tree_classifier.train(x, y);
    // uvec res = tree_classifier.predict(x);
    // cout << "=======================================\n";
    // res.print();
    // cout << "=======================================\n";
    // uvec dis = res - y;
    // int count = 0;
    // for (int i =0; i < dis.n_elem; i++) {
    //     count += dis(i) == 0 ? 1 : 0;
    // }
    // cout << (float)count / dis.n_elem <<endl;

    /******************************
     * random forest tesitng
    *******************************/
    // Random_Forest rf(50, 3, 0.7, 1);
    // rf.train(x, y);
    // uvec res = rf.predict(x);

    // cout << "=======================================\n";
    // res.print();
    // cout << "=======================================\n";

    // uvec dis = res - y;
    // int count = 0;
    // for (int i =0; i < dis.n_elem; i++) {
    //     count += dis(i) == 0 ? 1 : 0;
    // }
    // cout << (float)count / dis.n_elem <<endl;

    /*******************************
    * cart regression testing
    *******************************/
    // Datasets dataset = Datasets("boston");
    // mat x = dataset.x;
    // vec y = dataset.y;
    // Cart_Regression cart(100);
    // cart.train(x, y);
    // vec res = cart.predict(x);
    // join_rows(res, y).print();
    // vec dis = res - y;
    // // dis.print();
    // cout << "The standard deviation is: " << stddev(dis) << endl;

    /*******************************
    * GBDT testing
    *******************************/
    Datasets dataset = Datasets("boston");
    mat x = dataset.x;
    vec y = dataset.y;
    Gradient_Boosting_DT gbdt(6, 0.8, 80);
    gbdt.train(x, y);
    vec res = gbdt.predict(x);
    join_rows(res, y).print();
    vec dis = res - y;
    // dis.print();
    cout << "The standard deviation is: " << stddev(dis) << endl;

    return 0;
}


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
|  Phycas: Python software for phylogenetic analysis                          |
|  Copyright (C) 2010 Mark T. Holder, Paul O. Lewis and David L. Swofford     |
|                                                                             |
|  This program is free software; you can redistribute it and/or modify       |
|  it under the terms of the GNU General Public License as published by       |
|  the Free Software Foundation; either version 2 of the License, or          |
|  (at your option) any later version.                                        |
|                                                                             |
|  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              |
|  GNU General Public License for more details.                               |
|                                                                             |
|  You should have received a copy of the GNU General Public License along    |
|  with this program; if not, write to the Free Software Foundation, Inc.,    |
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                |
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

#if !defined(RELATIVE_RATE_DISTRIBUTION_HPP)
#define RELATIVE_RATE_DISTRIBUTION_HPP

#if defined(_MSC_VER)
#	pragma warning(disable: 4267)	// warning about loss of data when converting size_t to int
#endif

#include <cmath>
#include "ncl/nxsdefs.h"

#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>

#include "subset_proportions.hpp"
#include "dirichlet_distribution.hpp"

namespace phycas
{

/*------------------------------------------------------------------------------------------------------------------------------------------------------------------
|	This is the distribution of a relative rates vector X = (x1, x2, ..., xn) when the vector Y = (p1 x1, p2 x2, ..., pn xn) ~ Dirichlet(c1, c2, ..., cn). It is
|	useful as a prior for relative rates of subsets in a partition model, where the coefficients p1, p2, ..., pn are the relative sizes of the subsets.
*/
class RelativeRateDistribution : public DirichletDistribution
	{
	public:
											RelativeRateDistribution();
											RelativeRateDistribution(const std::vector<double> & params, const double_vect_t & coeffs);
                        					RelativeRateDistribution(const RelativeRateDistribution & other);
		virtual								~RelativeRateDistribution() {}

        RelativeRateDistribution * 			cloneAndSetLot(Lot * other) const;
        RelativeRateDistribution * 			Clone() const;

		virtual std::string 						GetDistributionName() const;
		virtual std::string 						GetDistributionDescription() const;
		std::string                                 GetDescriptionForPython() const;
		virtual std::vector<double>					GetMean() const;
		virtual std::vector<double> 				GetVar() const;
		virtual std::vector<double> 				GetStdDev() const;
		virtual std::vector<double>					Sample() const;
		virtual double								ApproxCDF(const std::vector<double> &x, unsigned nsamples = 10000) const;
		virtual double								GetLnPDF(const std::vector<double> &x) const;
		virtual double								GetRelativeLnPDF(const std::vector<double> &x) const;
		virtual void 								SetMeanAndVariance(const std::vector<double> &m, const std::vector<double> &v);
		// virtual void 								SetCoefficients(const std::vector<double> & coeff);
		virtual unsigned							GetNParams() const;
#		if defined(PYTHON_ONLY)
		//void								AltSetMeanAndVariance(std::vector<double> m, std::vector<double> v);
		double_vect_t						GetVarCovarMatrix();
#		endif

        void                                setSubsetProportions(SubsetProportionsShPtr subset_proportions);

    private:

		unsigned							dim;                    /**< The dimension, which equals the number of parameters (used to initialze the coefficients) */
        SubsetProportionsShPtr              _subset_proportions;    /**< The coefficients used to weight the relative rates */
		double								sum_params;             /**< The sum of the dirichlet parameters stored in the data member `dirParams', which is provided by the base class */
	};

typedef boost::shared_ptr<RelativeRateDistribution> RelativeRateDistributionShPtr;

} // namespace phycas

#endif


#include <boost/program_options.hpp>
#include <iostream>
#include <random>

namespace po = boost::program_options;

using Options = std::tuple<int, double, double, size_t>;

Options get_options(int argc, char* argv[]);

int main(int argc, char* argv[]) {
    int n;
    double a, b;
    size_t seed;
    std::tie(n, a, b, seed) = get_options(argc, argv);

    std::mt19937 engine(seed);
    std::uniform_real_distribution<double> distr(a, b);
    for (int i = 0; i < n; ++i)
        std::cout << distr(engine) << std::endl;

    return 0;
}

Options get_options(int argc, char* argv[]) {
    const int default_n {1};
    const double default_a {0.0};
    const double default_b {1.0};
    int n;
    double a, b;
    size_t seed;
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("n", po::value<int>(&n)->default_value(default_n),
         "number of random values to generate")
        ("a", po::value<double>(&a)->default_value(default_a),
         "minimum value")
        ("b", po::value<double>(&b)->default_value(default_b),
         "maximum value")
        ("seed", po::value<size_t>(&seed), "seed to use")
    ;
    po::positional_options_description pos_desc;
    pos_desc.add("n", -1);

    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv)
                  .options(desc).positional(pos_desc).run(), vm);
    po::notify(vm);

    if (vm.count("help")) {
        std::cout << desc << std::endl;
        std::exit(0);
    }

    if (!vm.count("seed")) {
        std::random_device device;
        seed = device();
    }
    return std::make_tuple(n, a, b, seed);
}


//
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
//
// 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_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP
#define BOOST_GIL_IMAGE_PROCESSING_HISTOGRAM_MATCHING_HPP

#include <boost/gil/algorithm.hpp>
#include <boost/gil/histogram.hpp>
#include <boost/gil/image.hpp>

#include <algorithm>
#include <cmath>
#include <map>
#include <vector>

namespace boost { namespace gil {

/////////////////////////////////////////
/// Histogram Matching(HM)
/////////////////////////////////////////
/// \defgroup HM HM
/// \brief Contains implementation and description of the algorithm used to compute
///        global histogram matching of input images.
///
///        Algorithm :-
///        1. Calculate histogram A(pixel) of input image and G(pixel) of reference image.
///        2. Compute the normalized cumulative(CDF) histograms of A and G.
///        3. Match the histograms using transofrmation  => CDF(A(px)) = CDF(G(px'))
///                                                      => px' = Inv-CDF (CDF(px))
///

/// \fn histogram_matching
/// \ingroup HM
/// \tparam SrcKeyType Key Type of input histogram
/// @param src_hist INPUT Input source histogram
/// @param ref_hist INPUT Input reference histogram
/// \brief Overload for histogram matching algorithm, takes in a single source histogram &
///        reference histogram and returns the color map used for histogram matching.
///
template <typename SrcKeyType, typename RefKeyType>
std::map<SrcKeyType, SrcKeyType>
    histogram_matching(histogram<SrcKeyType> const& src_hist, histogram<RefKeyType> const& ref_hist)
{
    histogram<SrcKeyType> dst_hist;
    return histogram_matching(src_hist, ref_hist, dst_hist);
}

/// \overload histogram_matching
/// \ingroup HM
/// \tparam SrcKeyType Key Type of input histogram
/// \tparam RefKeyType Key Type of reference histogram
/// \tparam DstKeyType Key Type of output histogram
/// @param src_hist INPUT source histogram
/// @param ref_hist INPUT reference histogram
/// @param dst_hist OUTPUT Output histogram
/// \brief Overload for histogram matching algorithm, takes in source histogram, reference
///        histogram & destination histogram and returns the color map used for histogram
///        matching as well as transforming the destination histogram.
///
template <typename SrcKeyType, typename RefKeyType, typename DstKeyType>
std::map<SrcKeyType, DstKeyType> histogram_matching(
    histogram<SrcKeyType> const& src_hist,
    histogram<RefKeyType> const& ref_hist,
    histogram<DstKeyType>& dst_hist)
{
    static_assert(
        std::is_integral<SrcKeyType>::value &&
        std::is_integral<RefKeyType>::value &&
        std::is_integral<DstKeyType>::value,
        "Source, Refernce or Destination histogram type is not appropriate.");

    using value_t = typename histogram<SrcKeyType>::value_type;
    dst_hist.clear();
    double src_sum      = src_hist.sum();
    double ref_sum      = ref_hist.sum();
    auto cumltv_srchist = cumulative_histogram(src_hist);
    auto cumltv_refhist = cumulative_histogram(ref_hist);
    std::map<SrcKeyType, RefKeyType> inverse_mapping;

    std::vector<typename histogram<RefKeyType>::key_type> src_keys, ref_keys;
    src_keys             = src_hist.sorted_keys();
    ref_keys             = ref_hist.sorted_keys();
    std::ptrdiff_t start = ref_keys.size() - 1;
    RefKeyType ref_max;
    if (start >= 0)
        ref_max = std::get<0>(ref_keys[start]);

    for (std::ptrdiff_t j = src_keys.size() - 1; j >= 0; --j)
    {
        double src_val = (cumltv_srchist[src_keys[j]] * ref_sum) / src_sum;
        while (cumltv_refhist[ref_keys[start]] > src_val && start > 0)
        {
            start--;
        }
        if (std::abs(cumltv_refhist[ref_keys[start]] - src_val) >
            std::abs(cumltv_refhist(std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]))) -
                src_val))
        {
            inverse_mapping[std::get<0>(src_keys[j])] =
                std::min<RefKeyType>(ref_max, std::get<0>(ref_keys[start + 1]));
        }
        else
        {
            inverse_mapping[std::get<0>(src_keys[j])] = std::get<0>(ref_keys[start]);
        }
        if (j == 0)
            break;
    }
    std::for_each(src_hist.begin(), src_hist.end(), [&](value_t const& v) {
        dst_hist[inverse_mapping[std::get<0>(v.first)]] += v.second;
    });
    return inverse_mapping;
}

/// \overload histogram_matching
/// \ingroup HM
/// @param src_view  INPUT source image view
/// @param ref_view  INPUT Reference image view
/// @param dst_view  OUTPUT Output image view
/// @param bin_width INPUT Histogram bin width
/// @param mask      INPUT Specify is mask is to be used
/// @param src_mask  INPUT Mask vector over input image
/// @param ref_mask  INPUT Mask vector over reference image
/// \brief Overload for histogram matching algorithm, takes in both source, reference &
///        destination image views and histogram matches the input image using the
///        reference image.
///
template <typename SrcView, typename ReferenceView, typename DstView>
void histogram_matching(
    SrcView const& src_view,
    ReferenceView const& ref_view,
    DstView const& dst_view,
    std::size_t bin_width = 1,
    bool mask = false,
    std::vector<std::vector<bool>> src_mask = {},
    std::vector<std::vector<bool>> ref_mask = {})
{
    gil_function_requires<ImageViewConcept<SrcView>>();
    gil_function_requires<ImageViewConcept<ReferenceView>>();
    gil_function_requires<MutableImageViewConcept<DstView>>();

    static_assert(
        color_spaces_are_compatible<
            typename color_space_type<SrcView>::type,
            typename color_space_type<ReferenceView>::type>::value,
        "Source and reference view must have same color space");

    static_assert(
        color_spaces_are_compatible<
            typename color_space_type<SrcView>::type,
            typename color_space_type<DstView>::type>::value,
        "Source and destination view must have same color space");

    // Defining channel type
    using source_channel_t = typename channel_type<SrcView>::type;
    using ref_channel_t    = typename channel_type<ReferenceView>::type;
    using dst_channel_t    = typename channel_type<DstView>::type;
    using coord_t          = typename SrcView::x_coord_t;

    std::size_t const channels     = num_channels<SrcView>::value;
    coord_t const width            = src_view.width();
    coord_t const height           = src_view.height();
    source_channel_t src_pixel_min = std::numeric_limits<source_channel_t>::min();
    source_channel_t src_pixel_max = std::numeric_limits<source_channel_t>::max();
    ref_channel_t ref_pixel_min    = std::numeric_limits<ref_channel_t>::min();
    ref_channel_t ref_pixel_max    = std::numeric_limits<ref_channel_t>::max();
    dst_channel_t dst_pixel_min    = std::numeric_limits<dst_channel_t>::min();
    dst_channel_t dst_pixel_max    = std::numeric_limits<dst_channel_t>::max();

    for (std::size_t i = 0; i < channels; i++)
    {
        histogram<source_channel_t> src_histogram;
        histogram<ref_channel_t> ref_histogram;
        fill_histogram(
            nth_channel_view(src_view, i), src_histogram, bin_width, false, false, mask, src_mask,
            std::tuple<source_channel_t>(src_pixel_min),
            std::tuple<source_channel_t>(src_pixel_max), true);
        fill_histogram(
            nth_channel_view(ref_view, i), ref_histogram, bin_width, false, false, mask, ref_mask,
            std::tuple<ref_channel_t>(ref_pixel_min), std::tuple<ref_channel_t>(ref_pixel_max),
            true);
        auto inverse_mapping = histogram_matching(src_histogram, ref_histogram);
        for (std::ptrdiff_t src_y = 0; src_y < height; ++src_y)
        {
            auto src_it = nth_channel_view(src_view, i).row_begin(src_y);
            auto dst_it = nth_channel_view(dst_view, i).row_begin(src_y);
            for (std::ptrdiff_t src_x = 0; src_x < width; ++src_x)
            {
                if (mask && !src_mask[src_y][src_x])
                    dst_it[src_x][0] = src_it[src_x][0];
                else
                    dst_it[src_x][0] =
                        static_cast<dst_channel_t>(inverse_mapping[src_it[src_x][0]]);
            }
        }
    }
}

}}  //namespace boost::gil

#endif


// tests

//local headers
#include "exception_assert.h"
#include "math.h"
#include "test.h"

//third party headers
#include <boost/math/special_functions/binomial.hpp>
#include "boost/multiprecision/cpp_int.hpp"

//standard headers
#include <cstdint>
#include <iostream>
#include <vector>

TEST(test_get_mid)
{
    std::cout << "Testing get_mid<T>()\n";

    EXCEPTION_ASSERT_MSG(get_mid<int>(-1,-1) == -1, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(-1,0) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(0,-1) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(1,0) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(0,1) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(1,1) == 1, "test failed");

    EXCEPTION_ASSERT_MSG(get_mid<int>(-4,-2) == -3, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(4,2) == 3, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<int>(5,2) == 3, "test failed");

    EXCEPTION_ASSERT_MSG(get_mid<double>(5.0,2.0) < 3.51, "test failed");
    EXCEPTION_ASSERT_MSG(get_mid<double>(5.0,2.0) > 3.49, "test failed");

    // overflow test
    EXCEPTION_ASSERT_MSG(get_mid<std::uint32_t>(
        std::numeric_limits<std::uint32_t>::max(),
        std::numeric_limits<std::uint32_t>::max() - 2) ==
        std::numeric_limits<std::uint32_t>::max() - 1,
        "test failed");
} TEST_END()

TEST(test_sqrt_integral)
{
    std::cout << "Testing sqrt_integral<T>()\n";

    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(-1) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(0) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(1) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(2) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(3) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(4) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(5) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(6) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(7) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(8) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<int>(9) == 3, "test failed");

    EXCEPTION_ASSERT_MSG(sqrt_integral<std::uint32_t>(10000) == 100, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<std::uint32_t>(123456789) == 11111, "test failed");

    EXCEPTION_ASSERT_MSG(sqrt_integral<std::uint16_t>(65535) == 255, "test failed");
    EXCEPTION_ASSERT_MSG(sqrt_integral<std::uint32_t>(4294967295ul) == 65535, "test failed");
} TEST_END()

TEST(test_get_primes_up_to)
{
    std::cout << "Testing get_primes_up_to()\n";

    std::vector<std::uint16_t> primes_to_300{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
        37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
        113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
        199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293};

    std::vector<std::uint16_t> primes_to_200_test{get_primes_up_to(200)};
    std::vector<std::uint16_t> primes_to_300_test{get_primes_up_to(300)};
    std::vector<std::uint16_t> primes_to_400_test{get_primes_up_to(400)};

    EXCEPTION_ASSERT_MSG(primes_to_300 != primes_to_200_test, "test failed");
    EXCEPTION_ASSERT_MSG(primes_to_300 == primes_to_300_test, "test failed");
    EXCEPTION_ASSERT_MSG(primes_to_300 != primes_to_400_test, "test failed");

    EXCEPTION_ASSERT_MSG(get_primes_up_to(0).size() == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(1).size() == 0, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(2).size() == 1, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(2)[0] == 2, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(3).size() == 2, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(3)[0] == 2, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(3)[1] == 3, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(4).size() == 2, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(4)[0] == 2, "test failed");
    EXCEPTION_ASSERT_MSG(get_primes_up_to(4)[1] == 3, "test failed");
} TEST_END()

TEST(test_prime_factors)
{
    std::cout << "Testing prime_factors<T>()\n";

    auto rebuild_num_f{
            [](const std::vector<uint16_t> &primes) -> std::uint16_t
            {
                if (primes.size() == 0)
                    return 0;

                std::uint16_t result{1};

                for (const auto prime : primes)
                    result *= prime;

                return result;
            }
        };

    // factorization is complete (can reconstruct original number)
    for (std::uint16_t i{0}; i < 200; ++i)
        EXCEPTION_ASSERT_MSG(rebuild_num_f(prime_factors<std::uint16_t>(i)) == i, "test failed");

    // prime factors are actually primes
    std::vector<std::uint16_t> primes_to_150{get_primes_up_to(150)};
    std::vector<std::uint16_t> prime_factors_300{prime_factors<std::uint16_t>(300)};

    for (const auto factor : prime_factors_300)
        EXCEPTION_ASSERT_MSG(std::find(primes_to_150.begin(), primes_to_150.end(), factor) != primes_to_150.end(), "test failed");
} TEST_END()

TEST(test_n_choose_k)
{
    std::cout << "Testing n_choose_k<T>()\n";

    EXCEPTION_ASSERT_MSG(n_choose_k<int>(-1,-1, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(-1,0, true) == 0, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k<int>(0,-1, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(0,0, true) == 1, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k<int>(1,0, true) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(1,1, true) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(1,2, true) == 0, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k<int>(2,-1, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(2,0, true) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(2,1, true) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(2,2, true) == 1, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,1, true) == 10, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,2, true) == 45, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,3, true) == 120, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,4, true) == 210, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,5, true) == 252, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,6, true) == 210, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,7, true) == 120, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,8, true) == 45, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,9, true) == 10, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<int>(10,10, true) == 1, "test failed");

    // numerical limits
    using boost::multiprecision::cpp_int;
    cpp_int bigint_limit = (cpp_int{1} << 650) - 1;
    std::size_t bigint_size = 650/8 + (650 % 8 ? 1 : 0);
    cpp_int bigint;

    // std::uint16_t
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint16_t>(18,9, true) != 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint16_t>(19,9, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(sizeof(std::uint16_t)) == 9, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(18, 9, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<std::uint16_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(19, 9, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<std::uint16_t>::max(), "test failed");

    // std::uint32_t
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint32_t>(34,17, true) != 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint32_t>(35,17, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(sizeof(std::uint32_t)) == 17, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(34, 17, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<std::uint32_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(35, 17, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<std::uint32_t>::max(), "test failed");

    // std::uint64_t
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint64_t>(67,33, true) != 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k<std::uint64_t>(68,34, true) == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(sizeof(std::uint64_t)) == 33, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(67, 33, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<std::uint64_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(68, 34, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<std::uint64_t>::max(), "test failed");

    using boost::multiprecision::uint128_t;
    using boost::multiprecision::uint256_t;
    using boost::multiprecision::uint512_t;

    // uint128_t
    uint128_t a1 = n_choose_k_impl<uint128_t>(131, 65, std::numeric_limits<uint128_t>::max(), 128/8, true);
    uint128_t a2 = n_choose_k_impl<uint128_t>(132, 66, std::numeric_limits<uint128_t>::max(), 128/8, true);
    EXCEPTION_ASSERT_MSG(a1 != 0, "test failed");
    EXCEPTION_ASSERT_MSG(a2 == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(128/8) == 65, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(131, 65, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<uint128_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(132, 66, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<uint128_t>::max(), "test failed");

    // uint256_t
    uint256_t b1 = n_choose_k_impl<uint256_t>(260, 130, std::numeric_limits<uint256_t>::max(), 256/8, true);
    uint256_t b2 = n_choose_k_impl<uint256_t>(261, 130, std::numeric_limits<uint256_t>::max(), 256/8, true);
    EXCEPTION_ASSERT_MSG(b1 != 0, "test failed");
    EXCEPTION_ASSERT_MSG(b2 == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(256/8) == 130, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(260, 130, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<uint256_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(261, 130, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<uint256_t>::max(), "test failed");

    // uint512_t
    uint512_t c1 = n_choose_k_impl<uint512_t>(516, 258, std::numeric_limits<uint512_t>::max(), 512/8, true);
    uint512_t c2 = n_choose_k_impl<uint512_t>(517, 258, std::numeric_limits<uint512_t>::max(), 512/8, true);
    EXCEPTION_ASSERT_MSG(c1 != 0, "test failed");
    EXCEPTION_ASSERT_MSG(c2 == 0, "test failed");
    EXCEPTION_ASSERT_MSG(bin_coeff_get_max_k(512/8) == 258, "test failed");

    // valid 'n' is below max size
    bigint = n_choose_k_impl<cpp_int>(516, 258, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint <= std::numeric_limits<uint512_t>::max(), "test failed");

    // invalid 'n' is above max size
    bigint = n_choose_k_impl<cpp_int>(517, 258, bigint_limit, bigint_size, true);
    EXCEPTION_ASSERT_MSG(bigint > std::numeric_limits<uint512_t>::max(), "test failed");

    // cpp_int with 600 bits
    // test that bin_coeff_get_max_k() returns a value k such that k + 1 will fail
    cpp_int limit600 = (cpp_int{1} << 600) - 1;
    std::size_t max_expected_k = bin_coeff_get_max_k(600/8 + (600 % 8 ? 1 : 0));
    cpp_int r = n_choose_k_impl<cpp_int>((max_expected_k + 1)*2, (max_expected_k + 1), limit600, 600/8 + (600 % 8 ? 1 : 0), true);
    EXCEPTION_ASSERT_MSG(r == 0, "test failed");

    // maximum input value exceeded
    using boost::multiprecision::cpp_int;
    cpp_int limit = (cpp_int{1} << std::size_t{80000}) - 1;
    cpp_int x = n_choose_k_impl<cpp_int>(cpp_int{200000}, cpp_int{100000}, limit, std::size_t{10000}, true);
    EXCEPTION_ASSERT_MSG(x == 0, "test failed");
} TEST_END()

TEST(test_n_choose_k_bwrap)
{
    std::cout << "Testing n_choose_k_bwrap()\n";

    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(0,0) == 1, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(1,0) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(1,1) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(1,2) == 0, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(2,0) == 1, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(2,1) == 2, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(2,2) == 1, "test failed");

    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,1) == 10, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,2) == 45, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,3) == 120, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,4) == 210, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,5) == 252, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,6) == 210, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,7) == 120, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,8) == 45, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,9) == 10, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(10,10) == 1, "test failed");

    // numerical limits
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(33,16) != 0, "test failed");
    EXCEPTION_ASSERT_MSG(n_choose_k_bwrap(34,16) == 0, "test failed");

    double fp_result_good = boost::math::binomial_coefficient<double>(33, 16);
    double fp_result_overflow = boost::math::binomial_coefficient<double>(34, 16);

    EXCEPTION_ASSERT_MSG(fp_result_good <= std::numeric_limits<std::int32_t>::max(), "test failed");
    EXCEPTION_ASSERT_MSG(fp_result_overflow > std::numeric_limits<std::int32_t>::max(), "test failed");
} TEST_END()

void run_tests()
{
    test_get_mid();
    test_sqrt_integral();
    test_get_primes_up_to();
    test_prime_factors();
    test_n_choose_k();
    test_n_choose_k_bwrap();
}


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

#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xview.hpp"
#include "xtensor/xaxis_slice_iterator.hpp"
#include "xtensor/xadapt.hpp"

#define H5_USE_XTENSOR
#include <highfive/H5Easy.hpp>
#include <highfive/H5Group.hpp>

#include <algorithm>
#include <map>

// #include <bits/stdc++.h>
// #include <boost/algorithm/string.hpp>

using namespace std;
using namespace xt;

struct Sum
{
   void operator()(const xarray<int>& a) { sum += xt::sum(a)(); }
   int sum {0};
};

template <typename T>
void print_shape(xt::xarray<T>& a){
    const auto& s = a.shape();
    std::copy(s.cbegin(), s.cend(), std::ostream_iterator<double>(std::cout, " "));
}

int main(int argc, char** argv) {
    xarray<int> a = {{{1, 2, 3, 4},
                  {5, 6, 7, 8},
                  {9, 10, 11, 12}},
                 {{13, 14, 15, 16},
                  {17, 18, 19, 20},
                  {21, 22, 23, 24}}};

    auto iter = axis_slice_begin(a, 0);
    auto end = axis_slice_end(a, 0);

    cout << "print 1 " << endl;
    auto print = [](const xarray<int>& a) {cout << a;};
    std::for_each(iter, end, print);

    // iter = axis_slice_begin(a, 0);
    // Sum s1 = std::for_each(iter, end, Sum());
    // cout << endl << "Sum is " << s1.sum << endl;

    cout << "print 2" << endl;
    const auto& s = a.shape();
    std::copy(s.cbegin(), s.cend(), std::ostream_iterator<double>(std::cout, " "));

    xarray<int> b = {{1, 2, 3, 4},
                    {5, 6, 7, 8}};
    cout << xt::col(b, 0) << endl;
    xt::col(b, 0) = xt::xarray<int>({6, 7});
    cout << xt::col(b, 0) << endl;

    vector<int> tmp {12, 13};
    vector<size_t> shape = {2};
    xt::col(b, 0) = xt::adapt(tmp, shape);
    cout << xt::col(b, 0) << endl;

	cout << "begin experiment 3" << endl;
	std::vector<size_t> shape3d = {2, 4, 4};
	xt::xarray<int> array3d(shape3d, 0);
    // auto v2 = xt::view(array3d, xt::all() , xt::all(), 1);
    // auto v2 = xt::view(array3d, 1, xt::all(), xt::range(1, 3));

	// auto v2 = xt::view(array3d, xt::all(), xt::all(), 1); // failed
	auto v2 = xt::view(array3d, 0, xt::all(), xt::all()); // OK
    // auto v2 = xt::view(array3d, xt::all(), xt::all(), xt::range(0, 1)); // OK
    // auto v2 = xt::view(array3d, xt::all(), xt::range(1, 3), xt::all()); // OK
    // auto v2 = xt::view(array3d, 0, xt::all(), xt::range(1, 3));
    cout << v2.dimension() << endl;
	v2(0, 0) = 1;
	v2 = 3;
    cout << v2 << endl;
	std::vector<size_t> shape2d = {4, 2};
	xt::xarray<int> array2d(shape2d, 0);
	array2d = v2;
    cout << array2d << endl;
	cout << "end experiment 3" << endl;

    xarray<double> a1 = {{0., 1., 2.}, {3., 4., 5.}};
    double b1 = 1.2;
    auto tr = view(a1, 0, all());
    tr = b1;
    a1(0, 0) = 12.0;
    cout << tr << endl;

	// a map of xarray
    using arr2d_t = xt::xarray<double>;
    std::map<std::string, arr2d_t > data_map;
    vector<string> field_keys = {"Sumw2", "Sumw"};
    long unsigned int n_bins = 2;
    long unsigned int n_variations = 3;
    for(auto field_key: field_keys) {
        data_map[field_key] = xt::xarray<double>::from_shape({n_bins, n_variations});
    }
	cout << data_map["Sumw2"] << endl;
	data_map["Sumw2"](0, 0) = 12.0;
	cout << data_map["Sumw2"] << endl;

	cout << "start 5 " << endl;
	int icc = 12;
	cout << icc++ << endl;
	cout << icc << endl;
	cout << ++icc << endl;
	cout << icc << endl;
	cout << "end 5 " << endl;

    return 0;
}


#pragma once
#include "base/assert.hpp"

#include "std/algorithm.hpp"
#include "std/cmath.hpp"
#include "std/functional.hpp"
#include "std/limits.hpp"
#include "std/type_traits.hpp"

#include <boost/integer.hpp>

namespace my
{

template <typename T> inline T Abs(T x)
{
  return (x < 0 ? -x : x);
}

// Compare floats or doubles for almost equality.
// maxULPs - number of closest floating point values that are considered equal.
// Infinity is treated as almost equal to the largest possible floating point values.
// NaN produces undefined result.
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
// for details.
template <typename TFloat>
bool AlmostEqualULPs(TFloat x, TFloat y, unsigned int maxULPs = 256)
{
  static_assert(is_floating_point<TFloat>::value, "");
  static_assert(numeric_limits<TFloat>::is_iec559, "");

  // Make sure maxUlps is non-negative and small enough that the
  // default NaN won't compare as equal to anything.
  ASSERT_LESS(maxULPs, 4 * 1024 * 1024, ());

  int const bits = CHAR_BIT * sizeof(TFloat);
  typedef typename boost::int_t<bits>::exact IntType;
  typedef typename boost::uint_t<bits>::exact UIntType;

  IntType xInt = *reinterpret_cast<IntType const *>(&x);
  IntType yInt = *reinterpret_cast<IntType const *>(&y);

  // Make xInt and yInt lexicographically ordered as a twos-complement int
  IntType const highestBit = IntType(1) << (bits - 1);
  if (xInt < 0)
    xInt = highestBit - xInt;
  if (yInt < 0)
    yInt = highestBit - yInt;

  UIntType const diff = Abs(xInt - yInt);

  return diff <= maxULPs;
}

// Returns true if x and y are equal up to the absolute difference eps.
// Does not produce a sensible result if any of the arguments is NaN or infinity.
// The default value for eps is deliberately not provided: the intended usage
// is for the client to choose the precision according to the problem domain,
// explicitly define the precision constant and call this function.
template <typename TFloat>
inline bool AlmostEqualAbs(TFloat x, TFloat y, TFloat eps)
{
  return fabs(x - y) < eps;
}

// Returns true if x and y are equal up to the relative difference eps.
// Does not produce a sensible result if any of the arguments is NaN, infinity or zero.
// The same considerations as in AlmostEqualAbs apply.
template <typename TFloat>
inline bool AlmostEqualRel(TFloat x, TFloat y, TFloat eps)
{
  return fabs(x - y) < eps * max(fabs(x), fabs(y));
}

template <typename TFloat> inline TFloat DegToRad(TFloat deg)
{
  return deg * TFloat(math::pi) / TFloat(180);
}

template <typename TFloat> inline TFloat RadToDeg(TFloat rad)
{
  return rad * TFloat(180) / TFloat(math::pi);
}

template <typename T> inline T id(T const & x)
{
  return x;
}

template <typename T> inline T sq(T const & x)
{
  return x * x;
}

template <typename T, typename TMin, typename TMax>
inline T clamp(T x, TMin xmin, TMax xmax)
{
  if (x > xmax)
    return xmax;
  if (x < xmin)
    return xmin;
  return x;
}

template <typename T> inline bool between_s(T a, T b, T x)
{
  return (a <= x && x <= b);
}
template <typename T> inline bool between_i(T a, T b, T x)
{
  return (a < x && x < b);
}

inline int rounds(double x)
{
  return (x > 0.0 ? int(x + 0.5) : int(x - 0.5));
}

inline size_t SizeAligned(size_t size, size_t align)
{
  // static_cast    .
  return size + (static_cast<size_t>(-static_cast<ptrdiff_t>(size)) & (align - 1));
}

template <typename T>
bool IsIntersect(T const & x0, T const & x1, T const & x2, T const & x3)
{
  return !((x1 < x2) || (x3 < x0));
}

// Computes x^n.
template <typename T> inline T PowUint(T x, uint64_t n)
{
  T res = 1;
  for (T t = x; n > 0; n >>= 1, t *= t)
    if (n & 1)
      res *= t;
  return res;
}

template <typename T> inline T NextModN(T x, T n)
{
  return x + 1 == n ? 0 : x + 1;
}

template <typename T> inline T PrevModN(T x, T n)
{
  return x == 0 ? n - 1 : x - 1;
}

inline uint32_t NextPowOf2(uint32_t v)
{
  v = v - 1;
  v |= (v >> 1);
  v |= (v >> 2);
  v |= (v >> 4);
  v |= (v >> 8);
  v |= (v >> 16);

  return v + 1;
}

// Greatest Common Divisor
template <typename T> T GCD(T a, T b)
{
  T multiplier = 1;
  T gcd = 1;
  while (true)
  {
    if (a == 0 || b == 0)
    {
      gcd = max(a, b);
      break;
    }

    if (a == 1 || b == 1)
    {
      gcd = 1;
      break;
    }

    if ((a & 0x1) == 0 && (b & 0x1) == 0)
    {
      multiplier <<= 1;
      a >>= 1;
      b >>= 1;
      continue;
    }

    if ((a & 0x1) != 0 && (b & 0x1) != 0)
    {
      T const minV = min(a, b);
      T const maxV = max(a, b);
      a = (maxV - minV) >> 1;
      b = minV;
      continue;
    }

    if ((a & 0x1) != 0)
      swap(a, b);

    a >>= 1;
  }

  return multiplier * gcd;
}

/// Calculate hash for the pair of values.
template <typename T1, typename T2>
size_t Hash(T1 const & t1, T2 const & t2)
{
  /// @todo Probably, we need better hash for 2 integral types.
  return (hash<T1>()(t1) ^ (hash<T2>()(t2) << 1));
}

}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2015.
// Modifications copyright (c) 2015, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

// Use, modification and distribution is 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_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_POINT_CIRCLE_HPP
#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_POINT_CIRCLE_HPP

#include <cstddef>

#include <boost/range.hpp>

#include <boost/geometry/util/math.hpp>

#include <boost/geometry/strategies/buffer.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace buffer
{

/*!
\brief Create a circular buffer around a point
\ingroup strategies
\details This strategy can be used as PointStrategy for the buffer algorithm.
    It creates a circular buffer around a point. It can be applied
    for points and multi_points, but also for a linestring (if it is degenerate,
    so consisting of only one point) and for polygons (if it is degenerate).
    This strategy is only applicable for Cartesian coordinate systems.

\qbk{
[heading Example]
[buffer_point_circle]
[heading Output]
[$img/strategies/buffer_point_circle.png]
[heading See also]
\* [link geometry.reference.algorithms.buffer.buffer_7_with_strategies buffer (with strategies)]
\* [link geometry.reference.strategies.strategy_buffer_point_square point_square]
}
 */
class point_circle
{
public :
    //! \brief Constructs the strategy
    //! \param count number of points for the created circle (if count
    //! is smaller than 3, count is internally set to 3)
    explicit point_circle(std::size_t count = 90)
        : m_count((count < 3u) ? 3u : count)
    {}

#ifndef DOXYGEN_SHOULD_SKIP_THIS
    //! Fills output_range with a circle around point using distance_strategy
    template
    <
        typename Point,
        typename OutputRange,
        typename DistanceStrategy
    >
    inline void apply(Point const& point,
                DistanceStrategy const& distance_strategy,
                OutputRange& output_range) const
    {
        typedef typename boost::range_value<OutputRange>::type output_point_type;

        typedef typename geometry::select_most_precise
            <
                typename geometry::select_most_precise
                    <
                        typename geometry::coordinate_type<Point>::type,
                        typename geometry::coordinate_type<output_point_type>::type
                    >::type,
                double
            >::type promoted_type;

        promoted_type const buffer_distance = distance_strategy.apply(point, point,
                        strategy::buffer::buffer_side_left);

        promoted_type const two = 2.0;
        promoted_type const two_pi = two * geometry::math::pi<promoted_type>();

        promoted_type const diff = two_pi / promoted_type(m_count);
        promoted_type a = 0;

        for (std::size_t i = 0; i < m_count; i++, a -= diff)
        {
            output_point_type p;
            set<0>(p, get<0>(point) + buffer_distance * cos(a));
            set<1>(p, get<1>(point) + buffer_distance * sin(a));
            output_range.push_back(p);
        }

        // Close it:
        output_range.push_back(output_range.front());
    }
#endif // DOXYGEN_SHOULD_SKIP_THIS

private :
    std::size_t m_count;
};

}} // namespace strategy::buffer

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_POINT_CIRCLE_HPP


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

#include "required_defines.hpp"

#include "performance_measure.hpp"

#include <boost/math/special_functions/beta.hpp>
#include <boost/array.hpp>

#define T double
#include "../test/ibeta_data.ipp"
#include "../test/ibeta_int_data.ipp"
#include "../test/ibeta_large_data.ipp"
#include "../test/ibeta_small_data.ipp"

template <std::size_t N>
double ibeta_evaluate2(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
      result += boost::math::ibeta(data[i][0], data[i][1], data[i][2]);
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_test, "ibeta")
{
   double result = ibeta_evaluate2(ibeta_data);
   result += ibeta_evaluate2(ibeta_int_data);
   result += ibeta_evaluate2(ibeta_large_data);
   result += ibeta_evaluate2(ibeta_small_data);

   consume_result(result);
   set_call_count(
      (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

template <std::size_t N>
double ibeta_inv_evaluate2(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
      result += boost::math::ibeta_inv(data[i][0], data[i][1], data[i][5]);
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_inv_test, "ibeta_inv")
{
   double result = ibeta_inv_evaluate2(ibeta_data);
   result += ibeta_inv_evaluate2(ibeta_int_data);
   result += ibeta_inv_evaluate2(ibeta_large_data);
   result += ibeta_inv_evaluate2(ibeta_small_data);

   consume_result(result);
   set_call_count(
      (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

template <std::size_t N>
double ibeta_invab_evaluate2(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
   {
      //std::cout << "ibeta_inva(" << data[i][1] << "," << data[i][2] << "," << data[i][5] << ");" << std::endl;
      result += boost::math::ibeta_inva(data[i][1], data[i][2], data[i][5]);
      //std::cout << "ibeta_invb(" << data[i][0] << "," << data[i][2] << "," << data[i][5] << ");" << std::endl;
      result += boost::math::ibeta_invb(data[i][0], data[i][2], data[i][5]);
      //std::cout << "ibetac_inva(" << data[i][1] << "," << data[i][2] << "," << data[i][6] << ");" << std::endl;
      result += boost::math::ibetac_inva(data[i][1], data[i][2], data[i][6]);
      //std::cout << "ibetac_invb(" << data[i][0] << "," << data[i][2] << "," << data[i][6] << ");" << std::endl;
      result += boost::math::ibetac_invb(data[i][0], data[i][2], data[i][6]);
   }
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_test, "ibeta_invab")
{
   double result = ibeta_invab_evaluate2(ibeta_data);
   result += ibeta_invab_evaluate2(ibeta_int_data);
   result += ibeta_invab_evaluate2(ibeta_large_data);
   result += ibeta_invab_evaluate2(ibeta_small_data);

   consume_result(result);
   set_call_count(
      4 * (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

#ifdef TEST_CEPHES

extern "C" {

double incbet(double a, double b, double x);
double incbi(double a, double b, double y);

}

template <std::size_t N>
double ibeta_evaluate_cephes(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
      result += incbet(data[i][0], data[i][1], data[i][2]);
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_test, "ibeta-cephes")
{
   double result = ibeta_evaluate_cephes(ibeta_data);
   result += ibeta_evaluate_cephes(ibeta_int_data);
   result += ibeta_evaluate_cephes(ibeta_large_data);
   result += ibeta_evaluate_cephes(ibeta_small_data);

   consume_result(result);
   set_call_count(
      (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

template <std::size_t N>
double ibeta_inv_evaluate_cephes(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
      result += incbi(data[i][0], data[i][1], data[i][5]);
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_inv_test, "ibeta_inv-cephes")
{
   double result = ibeta_inv_evaluate_cephes(ibeta_data);
   result += ibeta_inv_evaluate_cephes(ibeta_int_data);
   result += ibeta_inv_evaluate_cephes(ibeta_large_data);
   result += ibeta_inv_evaluate_cephes(ibeta_small_data);

   consume_result(result);
   set_call_count(
      (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

#endif

#ifdef TEST_GSL
//
// This test segfaults inside GSL....
//

#include <gsl/gsl_sf.h>

template <std::size_t N>
double ibeta_evaluate_gsl(const boost::array<boost::array<T, 7>, N>& data)
{
   double result = 0;
   for(unsigned i = 0; i < N; ++i)
      result += gsl_sf_beta_inc(data[i][0], data[i][1], data[i][2]);
   return result;
}

BOOST_MATH_PERFORMANCE_TEST(ibeta_test, "ibeta-gsl")
{
   double result = ibeta_evaluate_gsl(ibeta_data);
   result += ibeta_evaluate_gsl(ibeta_int_data);
   result += ibeta_evaluate_gsl(ibeta_large_data);
   result += ibeta_evaluate_gsl(ibeta_small_data);

   consume_result(result);
   set_call_count(
      (sizeof(ibeta_data)
      + sizeof(ibeta_int_data)
      + sizeof(ibeta_large_data)
      + sizeof(ibeta_small_data)) / sizeof(ibeta_data[0]));
}

#endif


/**
 * This file contains headers of all the files (and other 3rd party libraries) for the zaamath library.
 */

#ifndef PROMETHEUS_MATH_HPP
#define PROMETHEUS_MATH_HPP

#include <Eigen/Dense> // Matrix/linear algebra library
#include "highfive/H5Easy.hpp" // HDF5 header-only library
#include "stats/stats.hpp" // probability distributions library
#include "hypothesis_testing.hpp" // implementation of various hypothesis tests for Eigen objects
#include "summary_statistics.hpp" // implementation of several useful summary statistics functions for Eigen objects
#include "optimization.hpp" // implementation of stochastic optimization algorithms for Eigen objects
#include "stopwatch.hpp" // implementation of a stopwatch class for benchmarking and analysis purposes

#endif //PROMETHEUS_MATH_HPP


///////////////////////////////////////////////////////////////////////////////
// statistics::survival::model::models::meta_failure_distribution.hpp        //
//                                                                           //
//  Copyright 2009 Erwann Rogard. 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)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_SURVIVAL_MODEL_MODELS_META_FAILURE_DISTRIBUTION_HPP_ER_2009
#define BOOST_STATISTICS_SURVIVAL_MODEL_MODELS_META_FAILURE_DISTRIBUTION_HPP_ER_2009
#include <cmath>
#include <boost/statistics/model/wrap/aggregate/model_covariate_parameter.hpp>
#include <boost/standard_distribution/distributions/exponential.hpp>
#include <boost/statistics/survival/data/meta/failure_distribution.hpp>
#include <boost/statistics/survival/model/models/exponential/model.hpp>

namespace boost{
namespace statistics{

namespace survival{
namespace data{

    template<typename T>
    struct meta_failure_distribution< survival::model::exponential::model<T> >{
        typedef survival::model::exponential::model<T> model_;
        typedef math::exponential_distribution<T> type;

        template<typename X,typename P>
        static type make(
            boost::statistics::model::model_covariate_parameter_<model_,X,P> mcp
        ){
            T lambda = model_::log_rate(
                mcp.covariate(),
                mcp.parameter()
            );
            lambda = exp( lambda );
            return type(
                lambda
            );
        }
    };

}// data
}// survival
}// statistics
}// boost

#endif


/*
 * This file is part of the ProVANT simulator project.
 * Licensed under the terms of the MIT open source license. More details at
 * https://github.com/Guiraffo/ProVANT-Simulator/blob/master/LICENSE.md
 */
/**
 * @file integrator_tests.cpp
 * @brief This file contains the tests of the Integrator class.
 *
 * @author Júnio Eduardo de Morais Aquino
 */

#include "provant_simulator_math_utils/integrator.h"

#include <gtest/gtest.h>

#include <Eigen/Eigen>

#include <memory>

class IntegratorTest : public ::testing::Test
{
protected:
  void SetUp() override
  {
    defaultIntegrator.reset(new Integrator<double>(1.0));

    lineIntegrator.reset(new Integrator<double>(1.0));
    lineIntegrator->update(0.0);
    lineIntegrator->update(1.0);
    lineIntegrator->update(2.0);
    lineIntegrator->update(3.0);
    lineIntegrator->update(4.0);
    lineIntegrator->update(5.0);

    nonZeroInitialValueIntegrator.reset(new Integrator<double>(1.0, 10.0));

    zero << 0,0,0,0;
    vectorIntegrator.reset(new Integrator<Eigen::Vector4d>(1.0, zero, zero));
  }

  void TearDown() override
  {
    defaultIntegrator.release();
    lineIntegrator.release();
    nonZeroInitialValueIntegrator.release();
    vectorIntegrator.release();
  }

  std::unique_ptr<Integrator<double>> defaultIntegrator;
  std::unique_ptr<Integrator<double>> lineIntegrator;
  std::unique_ptr<Integrator<double>> nonZeroInitialValueIntegrator;
  std::unique_ptr<Integrator<Eigen::Vector4d>> vectorIntegrator;
  Eigen::Vector4d zero;
};

TEST_F(IntegratorTest, InitializesStepTime)
{
  ASSERT_DOUBLE_EQ(1.0, defaultIntegrator->stepTime());
  ASSERT_DOUBLE_EQ(1.0, vectorIntegrator->stepTime());
}

TEST_F(IntegratorTest, InitializesInitialValue)
{
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(10.0, nonZeroInitialValueIntegrator->value());

  ASSERT_TRUE(vectorIntegrator->value().isApprox(zero));
}

TEST_F(IntegratorTest, IsInitiallyZero)
{
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->previousValue());

  Eigen::Vector4d zero;
  zero << 0, 0, 0, 0;
  ASSERT_TRUE(vectorIntegrator->value().isApprox(zero));
}

TEST_F(IntegratorTest, UpdatesInitialValue)
{
  defaultIntegrator->setInitialValue(10.0);
  ASSERT_DOUBLE_EQ(10.0, defaultIntegrator->initialValue());
}

TEST_F(IntegratorTest, CanReset)
{
  lineIntegrator->reset();

  ASSERT_DOUBLE_EQ(lineIntegrator->initialValue(), lineIntegrator->value());
  ASSERT_DOUBLE_EQ(0.0, lineIntegrator->previousValue());

  nonZeroInitialValueIntegrator->reset();
  ASSERT_DOUBLE_EQ(10.0, nonZeroInitialValueIntegrator->value());
  ASSERT_DOUBLE_EQ(0.0, nonZeroInitialValueIntegrator->previousValue());

  Eigen::Vector4d sample;
  sample << 1, 1, 1, 1;
  vectorIntegrator->update(sample);
  vectorIntegrator->reset();
  ASSERT_TRUE(vectorIntegrator->value().isApprox(zero));
}

TEST_F(IntegratorTest, UpdatesPreviousValue)
{
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->previousValue());

  defaultIntegrator->update(1.0);
  ASSERT_DOUBLE_EQ(1.0, defaultIntegrator->previousValue());
  defaultIntegrator->update(2.0);
  ASSERT_DOUBLE_EQ(2.0, defaultIntegrator->previousValue());
  defaultIntegrator->update(3.0);
  ASSERT_DOUBLE_EQ(3.0, defaultIntegrator->previousValue());
  defaultIntegrator->update(4.0);
  ASSERT_DOUBLE_EQ(4.0, defaultIntegrator->previousValue());
  defaultIntegrator->update(5.0);
  ASSERT_DOUBLE_EQ(5.0, defaultIntegrator->previousValue());
}

TEST_F(IntegratorTest, VectorUpdatesPreviousValue)
{
  Eigen::Vector4d sample;
  sample << 1, 1, 1, 1;

  vectorIntegrator->update(sample);
  ASSERT_TRUE(vectorIntegrator->previousValue().isApprox(sample));
}

TEST_F(IntegratorTest, ReturnsIntegralValue)
{
  ASSERT_DOUBLE_EQ(0.5, defaultIntegrator->update(1.0));
  ASSERT_DOUBLE_EQ(2.0, defaultIntegrator->update(2.0));
  ASSERT_DOUBLE_EQ(4.5, defaultIntegrator->update(3.0));
  ASSERT_DOUBLE_EQ(8.0, defaultIntegrator->update(4.0));
  ASSERT_DOUBLE_EQ(12.5, defaultIntegrator->update(5.0));
}

TEST_F(IntegratorTest, VectorReturnsIntegralValue)
{
  Eigen::Vector4d sample, res;
  sample << 1, 1, 1, 1;
  res << 0.5, 0.5, 0.5, 0.5;

  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));
}

TEST_F(IntegratorTest, UpdatesIntegralValue)
{
  ASSERT_DOUBLE_EQ(0.5, defaultIntegrator->update(1.0));
  ASSERT_DOUBLE_EQ(0.5, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(2.0, defaultIntegrator->update(2.0));
  ASSERT_DOUBLE_EQ(2.0, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(4.5, defaultIntegrator->update(3.0));
  ASSERT_DOUBLE_EQ(4.5, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(8.0, defaultIntegrator->update(4.0));
  ASSERT_DOUBLE_EQ(8.0, defaultIntegrator->value());
  ASSERT_DOUBLE_EQ(12.5, defaultIntegrator->update(5.0));
  ASSERT_DOUBLE_EQ(12.5, defaultIntegrator->value());
}

TEST_F(IntegratorTest, VectorUpdatesIntegralValue)
{
  Eigen::Vector4d sample, res;
  sample << 1, 1, 1, 1;
  res << 0.5, 0.5, 0.5, 0.5;

  vectorIntegrator->update(sample);
  ASSERT_TRUE(vectorIntegrator->value().isApprox(res));
}

TEST_F(IntegratorTest, DecreasingLineIntegral)
{
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->value());

  ASSERT_DOUBLE_EQ(-0.5, defaultIntegrator->update(-1.0));
  ASSERT_DOUBLE_EQ(-2.0, defaultIntegrator->update(-2.0));
  ASSERT_DOUBLE_EQ(-4.5, defaultIntegrator->update(-3.0));
  ASSERT_DOUBLE_EQ(-8.0, defaultIntegrator->update(-4.0));
  ASSERT_DOUBLE_EQ(-12.5, defaultIntegrator->update(-5.0));
}

TEST_F(IntegratorTest, NonZeroStartValueIntegralTest)
{
  ASSERT_DOUBLE_EQ(10.0, nonZeroInitialValueIntegrator->value());

  ASSERT_DOUBLE_EQ(9.5, nonZeroInitialValueIntegrator->update(-1.0));
  ASSERT_DOUBLE_EQ(8, nonZeroInitialValueIntegrator->update(-2.0));
  ASSERT_DOUBLE_EQ(5.5, nonZeroInitialValueIntegrator->update(-3.0));
  ASSERT_DOUBLE_EQ(2.0, nonZeroInitialValueIntegrator->update(-4.0));
  ASSERT_DOUBLE_EQ(-2.5, nonZeroInitialValueIntegrator->update(-5.0));
}

TEST_F(IntegratorTest, VariyingLineTest)
{
  ASSERT_DOUBLE_EQ(0.5, defaultIntegrator->update(1.0));
  ASSERT_DOUBLE_EQ(2.0, defaultIntegrator->update(2.0));
  ASSERT_DOUBLE_EQ(4.5, defaultIntegrator->update(3.0));
  ASSERT_DOUBLE_EQ(8.0, defaultIntegrator->update(4.0));
  ASSERT_DOUBLE_EQ(12.5, defaultIntegrator->update(5.0));
  ASSERT_DOUBLE_EQ(17.0, defaultIntegrator->update(4.0));
  ASSERT_DOUBLE_EQ(20.5, defaultIntegrator->update(3.0));
  ASSERT_DOUBLE_EQ(23.0, defaultIntegrator->update(2.0));
  ASSERT_DOUBLE_EQ(24.5, defaultIntegrator->update(1.0));
  ASSERT_DOUBLE_EQ(25.0, defaultIntegrator->update(0.0));
  ASSERT_DOUBLE_EQ(24.5, defaultIntegrator->update(-1.0));
  ASSERT_DOUBLE_EQ(23.0, defaultIntegrator->update(-2.0));
  ASSERT_DOUBLE_EQ(20.5, defaultIntegrator->update(-3.0));
  ASSERT_DOUBLE_EQ(17.0, defaultIntegrator->update(-4.0));
  ASSERT_DOUBLE_EQ(12.5, defaultIntegrator->update(-5.0));
  ASSERT_DOUBLE_EQ(7.0, defaultIntegrator->update(-6.0));
  ASSERT_DOUBLE_EQ(0.5, defaultIntegrator->update(-7.0));
  ASSERT_DOUBLE_EQ(-7.0, defaultIntegrator->update(-8.0));
}

TEST_F(IntegratorTest, ConstantZeroTest)
{
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->update(0.0));
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->update(0.0));
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->update(0.0));
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->update(0.0));
  ASSERT_DOUBLE_EQ(0.0, defaultIntegrator->update(0.0));
}

TEST_F(IntegratorTest, VectorIntegral)
{
  Eigen::Vector4d sample, res;
  sample << 1, 1, -1, -1;
  res << 0.5, 0.5, -0.5, -0.5;

  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));

  sample << 2.0, 2.0, -2.0, -2.0;
  res << 2.0, 2.0, -2.0, -2.0;
  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));

  sample << 3.0, 3.0, -3.0, -3.0;
  res << 4.5, 4.5, -4.5, -4.5;
  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));

  sample << 4.0, 4.0, -4.0, -4.0;
  res << 8.0, 8.0, -8.0, -8.0;
  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));

  sample << 5.0, 5.0, -5.0, -5.0;
  res << 12.5, 12.5, -12.5, -12.5;
  ASSERT_TRUE(vectorIntegrator->update(sample).isApprox(res));
}

int main(int argc, char** argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  try
  {
    return RUN_ALL_TESTS();
  }
  catch (const std::exception& e)
  {
    std::cerr << "Unhandled excpetion with message: " << e.what() << std::endl;
    return -1;
  }
}


/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
#include <NTL/ZZ.h>
#include <algorithm>
#include <complex>

#include "norms.h"
#include "EncryptedArray.h"
#include "FHE.h"
#include "debugging.h"

NTL_CLIENT

bool verbose=false;

// Compute the L-infinity distance between two vectors
double calcMaxDiff(const vector<cx_double>& v1,
                   const vector<cx_double>& v2){

  if(lsize(v1)!=lsize(v2))
    NTL::Error("Vector sizes differ.\nFAILED\n");

  double maxDiff = 0.0;
  for (long i=0; i<lsize(v1); i++) {
    double diffAbs = std::abs(v1[i]-v2[i]);
    if (diffAbs > maxDiff)
      maxDiff = diffAbs;
  }

  return maxDiff;
}

inline bool cx_equals(const vector<cx_double>& v1,
                      const vector<cx_double>& v2,
                      double epsilon)
{
  return (calcMaxDiff(v1,v2) < epsilon);
}

void testBasicArith(const FHEPubKey& publicKey,
                    const FHESecKey& secretKey,
                    const EncryptedArrayCx& ea, double epsilon);
void testComplexArith(const FHEPubKey& publicKey,
                      const FHESecKey& secretKey,
                      const EncryptedArrayCx& ea, double epsilon);
void testRotsNShifts(const FHEPubKey& publicKey,
                     const FHESecKey& secretKey,
                     const EncryptedArrayCx& ea, double epsilon);

int main(int argc, char *argv[])
{

  // Commandline setup

  ArgMapping amap;

  long m=16;
  long r=8;
  long L=150;
  double epsilon=0.01; // Accepted accuracy

  amap.arg("m", m, "Cyclotomic index");
  amap.note("e.g., m=1024, m=2047");
  amap.arg("r", r, "Bits of precision");
  amap.arg("L", L, "Number of levels");
  amap.arg("ep", epsilon, "Accepted accuracy");
  amap.arg("verbose", verbose, "more printouts");

  amap.parse(argc, argv);

  try{

    // FHE setup keys, context, SKMs, etc

    FHEcontext context(m, /*p=*/-1, r);
    buildModChain(context, L, /*c=*/2);

    FHESecKey secretKey(context);
    secretKey.GenSecKey(); // A +-1/0 secret key
    addSome1DMatrices(secretKey); // compute key-switching matrices

    const FHEPubKey publicKey = secretKey;
    const EncryptedArrayCx& ea = context.ea->getCx();

    if (verbose) {
      ea.getPAlgebra().printout();
      cout << "r = " << context.alMod.getR() << endl;
      cout << "ctxtPrimes="<<context.ctxtPrimes
           << ", specialPrimes="<<context.specialPrimes<<endl<<endl;
    }

    // Run the tests.
    testBasicArith(publicKey, secretKey, ea, epsilon);
    testComplexArith(publicKey, secretKey, ea, epsilon);
    testRotsNShifts(publicKey, secretKey, ea, epsilon);

  }
  catch (exception& e) {
    cerr << e.what() << endl;
    cerr << "***Major FAIL***" << endl;
  }

  return 0;
}

void testBasicArith(const FHEPubKey& publicKey,
                    const FHESecKey& secretKey,
                    const EncryptedArrayCx& ea, double epsilon)
{
  if (verbose)  cout << "Test Arithmetic ";
  // Test objects

  Ctxt c1(publicKey), c2(publicKey), c3(publicKey);

  vector<cx_double> vd;
  vector<cx_double> vd1, vd2, vd3;
  ea.random(vd1);
  ea.random(vd2);

  // test encoding of shorter vectors
  vd1.resize(vd1.size()-2);
  ea.encrypt(c1, publicKey, vd1);
  vd1.resize(vd1.size()+2, 0.0);

  ea.encrypt(c2, publicKey, vd2);

  // Test - Multiplication
  c1 *= c2;
  for (long i=0; i<lsize(vd1); i++) vd1[i] *= vd2[i];

  ZZX poly;
  ea.random(vd3);
  ea.encode(poly,vd3);
  c1.addConstant(poly); // vd1*vd2 + vd3
  for (long i=0; i<lsize(vd1); i++) vd1[i] += vd3[i];

  // Test encoding, encryption of a single number
  double xx = NTL::RandomLen_long(16)/double(1L<<16); // random in [0,1]
  ea.encryptOneNum(c2, publicKey, xx);
  c1 += c2;
  for (auto& x : vd1) x += xx;

  // Test - Multiply by a mask
  vector<long> mask(lsize(vd1), 1);
  for (long i=0; i*(i+1)<lsize(mask); i++) {
    mask[i*i] = 0;
    mask[i*(i+1)] = -1;
  }

  ea.encode(poly,mask);
  c1.multByConstant(poly); // mask*(vd1*vd2 + vd3)
  for (long i=0; i<lsize(vd1); i++) vd1[i] *= mask[i];

  // Test - Addition
  ea.random(vd3);
  ea.encrypt(c3, publicKey, vd3);
  c1 += c3;
  for (long i=0; i<lsize(vd1); i++) vd1[i] += vd3[i];

  c1.negate();
  c1.addConstant(to_ZZ(1));
  for (long i=0; i<lsize(vd1); i++) vd1[i] = 1.0 - vd1[i];

  // Diff between approxNums HE scheme and plaintext floating
  ea.decrypt(c1, secretKey, vd);
#ifdef DEBUG_PRINTOUT
  printVec(cout<<"res=", vd, 10)<<endl;
  printVec(cout<<"vec=", vd1, 10)<<endl;
#endif
  if (verbose)
    cout << "(max |res-vec|_{infty}="<< calcMaxDiff(vd, vd1) << "): ";

  cx_equals(vd, vd1, epsilon)?
    cout << "GOOD\n":
    cout << "BAD\n";
}

void testComplexArith(const FHEPubKey& publicKey,
                      const FHESecKey& secretKey,
                      const EncryptedArrayCx& ea, double epsilon)
{

  // Test complex conjugate
  Ctxt c1(publicKey), c2(publicKey);

  vector<cx_double> vd;
  vector<cx_double> vd1, vd2;
  ea.random(vd1);
  ea.random(vd2);

  ea.encrypt(c1, publicKey, vd1);
  ea.encrypt(c2, publicKey, vd2);

  if (verbose)
    cout << "Test Conjugate: ";
  for_each(vd1.begin(), vd1.end(), [](cx_double& d){d=std::conj(d);});
  c1.complexConj();
  ea.decrypt(c1, secretKey, vd);
#ifdef DEBUG_PRINTOUT
  printVec(cout<<"vd1=", vd1, 10)<<endl;
  printVec(cout<<"res=", vd, 10)<<endl;
#endif
  cx_equals(vd, vd1, epsilon)?
    cout << "GOOD\n":
    cout << "BAD\n";

  // Test that real and imaginary parts are actually extracted.
  Ctxt realCtxt(c2), imCtxt(c2);
  vector<cx_double> realParts(vd2), real_dec;
  vector<cx_double> imParts(vd2), im_dec;

  if (verbose)
    cout << "Test Real and Im parts: ";
  for_each(realParts.begin(), realParts.end(), [](cx_double& d){d=std::real(d);});
  for_each(imParts.begin(), imParts.end(), [](cx_double& d){d=std::imag(d);});

  ea.extractRealPart(realCtxt);
  ea.decrypt(realCtxt, secretKey, real_dec);

  ea.extractImPart(imCtxt);
  ea.decrypt(imCtxt, secretKey, im_dec);

#ifdef DEBUG_PRINTOUT
  printVec(cout<<"vd2=", vd2, 10)<<endl;
  printVec(cout<<"real=", realParts, 10)<<endl;
  printVec(cout<<"res=", real_dec, 10)<<endl;
  printVec(cout<<"im=", imParts, 10)<<endl;
  printVec(cout<<"res=", im_dec, 10)<<endl;
#endif
  cx_equals(realParts,real_dec,epsilon) && cx_equals(imParts,im_dec,epsilon)?
    cout << "GOOD\n":
    cout << "BAD\n";
}

void testRotsNShifts(const FHEPubKey& publicKey,
                     const FHESecKey& secretKey,
                     const EncryptedArrayCx& ea, double epsilon)
{

  std::srand(std::time(0)); // set seed, current time.
  int nplaces = rand() % static_cast<int>(ea.size()/2.0) + 1;

  if (verbose)
    cout << "Test Rotation of " << nplaces << ": ";

  Ctxt c1(publicKey);
  vector<cx_double> vd1;
  vector<cx_double> vd_dec;
  ea.random(vd1);
  ea.encrypt(c1, publicKey, vd1);

#ifdef DEBUG_PRINTOUT
  printVec(cout<< "vd1=", vd1, 10)<<endl;
#endif
  std::rotate(vd1.begin(), vd1.end()-nplaces, vd1.end());
  ea.rotate(c1, nplaces);
  ea.decrypt(c1, secretKey, vd_dec);
#ifdef DEBUG_PRINTOUT
  printVec(cout<< "vd1(rot)=", vd1, 10)<<endl;
  printVec(cout<<"res: ", vd_dec, 10)<<endl;
#endif

  cx_equals(vd1, vd_dec, epsilon)?
    cout << "GOOD\n":
    cout << "BAD\n";
}