//// Copyright (c) 2018 Silvio Mayolo
//// See LICENSE.txt for licensing details

#include "catch2/catch.hpp"
#include "Number.hpp"
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <string>
#include <complex>
#include <limits>

TEST_CASE( "Number construction", "[number]" ) {
  REQUIRE( Number((Number::smallint)0).hierarchyLevel() == Number::SMALLINT );
  REQUIRE( Number((Number::bigint)10).hierarchyLevel() == Number::BIGINT );
  REQUIRE( Number( Number::ratio(1, 2) ).hierarchyLevel() == Number::RATIO );
  REQUIRE( Number(3.0).hierarchyLevel() == Number::FLOATING );
  REQUIRE( Number(1+1i).hierarchyLevel() == Number::COMPLEX );

  Number a { (Number::smallint)10 };
  Number b = a;

  REQUIRE( a == b );

}

TEST_CASE( "Number equality and comparison", "[number]" ) {

  Number smallZero { (Number::smallint)0 };
  Number   bigZero { (Number::bigint)  0 };

  Number   intOne { (Number::bigint) 1  };
  Number ratioOne { Number::ratio(1, 1) };

  Number floatingOne { 1.0 };

  REQUIRE( smallZero == smallZero );
  REQUIRE(   bigZero ==   bigZero );
  REQUIRE( smallZero ==   bigZero );
  REQUIRE( smallZero !=    intOne );
  REQUIRE(  ratioOne ==    intOne );
  REQUIRE(  ratioOne ==  ratioOne );

  REQUIRE(   smallZero <      intOne );
  REQUIRE(     bigZero <      intOne );
  REQUIRE(   smallZero <    ratioOne );
  REQUIRE(      intOne >   smallZero );
  REQUIRE(     bigZero < floatingOne );
  REQUIRE(         0.0 < floatingOne );
  REQUIRE( floatingOne <         2.0 );

}

TEST_CASE( "Number addition and subtraction", "[number]" ) {

  Number    small { (Number::smallint)1 };
  Number      big { (Number::bigint)  1 };
  Number    ratio { Number::ratio(1, 1) };
  Number floating {                1.0  };
  Number  complex {          1.0 + 0.0i };

  SECTION( "Addition and subtraction behave correctly" ) {
    REQUIRE( small + small == 2l );
    REQUIRE( small +   big == 2l );
    REQUIRE( small + ratio == 2l );
    REQUIRE(   big +   big == 2l );
    REQUIRE(   big + ratio == 2l );
    REQUIRE( ratio + ratio == 2l );
    REQUIRE( small - small == 0l );
    REQUIRE( small -   big == 0l );
    REQUIRE( small - ratio == 0l );
    REQUIRE(   big -   big == 0l );
    REQUIRE(   big - ratio == 0l );
    REQUIRE( ratio - ratio == 0l );
  }

  SECTION( "Negation behaves correctly" ) {
    REQUIRE( -small == -1l );
    REQUIRE( -  big == -1l );
    REQUIRE( -ratio == -1l );
  }

  SECTION( "Addition and subtraction coerce to the larger type" ) {
    REQUIRE( (   small +    small).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (   small +      big).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small +    ratio).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small + floating).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small +  complex).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( (   small -    small).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (   small -      big).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small -    ratio).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small - floating).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small -  complex).hierarchyLevel() == Number::COMPLEX  );
  }

  SECTION( "Overflows are detected" ) {
    Number maximum { std::numeric_limits<Number::smallint>::max() };
    REQUIRE( (maximum - small).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (maximum + small).hierarchyLevel() == Number::BIGINT   );
  }

}

TEST_CASE( "Number multiplication", "[number]" ) {

  Number    small { (Number::smallint)2 };
  Number      big { (Number::bigint)  2 };
  Number    ratio { Number::ratio(2, 1) };
  Number floating {                2.0  };
  Number  complex {          2.0 + 0.0i };

  SECTION( "Multiplication behaves correctly" ) {
    REQUIRE( small * small == 4l );
    REQUIRE( small *   big == 4l );
    REQUIRE( small * ratio == 4l );
    REQUIRE(   big *   big == 4l );
    REQUIRE(   big * ratio == 4l );
    REQUIRE( ratio * ratio == 4l );
  }

  SECTION( "Multiplication coerces to the larger type" ) {
    REQUIRE( (   small *    small).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (   small *      big).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small *    ratio).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small * floating).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small *  complex).hierarchyLevel() == Number::COMPLEX  );
  }

  SECTION( "Overflows are detected" ) {
    Number maximum { std::numeric_limits<Number::smallint>::max() };
    Number one { (Number::smallint)1 };
    REQUIRE( (maximum *   one).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (maximum * small).hierarchyLevel() == Number::BIGINT   );
  }

}

TEST_CASE( "Numerical division and reciprocation", "[number]" ) {

  Number    smallOne { (Number::smallint)1 };
  Number      bigOne { (Number::bigint)  1 };
  Number    ratioOne { Number::ratio(1, 1) };
  Number floatingOne {                1.0  };
  Number  complexOne {          1.0 + 0.0i };

  Number    smallTwo { (Number::smallint)2 };
  Number      bigTwo { (Number::bigint)  2 };
  Number    ratioTwo { Number::ratio(2, 1) };
  Number floatingTwo {                2.0  };
  Number  complexTwo {          2.0 + 0.0i };

  SECTION( "Division and reciprocation behave correctly" ) {
    REQUIRE( (smallOne / smallTwo) == Number::ratio(1, 2) );
    REQUIRE( (  bigOne / smallTwo) == Number::ratio(1, 2) );
    REQUIRE( (  bigOne /   bigTwo) == Number::ratio(1, 2) );
    REQUIRE( (smallOne /   bigTwo) == Number::ratio(1, 2) );
    REQUIRE( (  bigTwo / smallOne) == Number::ratio(2, 1) );
    REQUIRE( (ratioTwo / ratioOne) == Number::ratio(2, 1) );
  }

  SECTION( "Reciprocation widens appropriately" ) {
    REQUIRE(    smallOne.recip().hierarchyLevel() == Number::RATIO    );
    REQUIRE(      bigOne.recip().hierarchyLevel() == Number::RATIO    );
    REQUIRE(    ratioOne.recip().hierarchyLevel() == Number::RATIO    );
    REQUIRE( floatingOne.recip().hierarchyLevel() == Number::FLOATING );
    REQUIRE(  complexOne.recip().hierarchyLevel() == Number::COMPLEX  );
  }

  SECTION( "Division widens appropriately" ) {
    REQUIRE( (   smallOne /    smallOne).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   smallOne /      bigOne).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   smallOne /    ratioOne).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   smallOne / floatingOne).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   smallOne /  complexOne).hierarchyLevel() == Number::COMPLEX  );
  }

}

TEST_CASE( "Numerical modulo", "[number]" ) {

  Number    small { (Number::smallint)3 };
  Number      big { (Number::bigint)  3 };
  Number    ratio { Number::ratio(3, 1) };
  Number floating {                3.0  };
  Number  complex {          3.0 + 0.0i };

  SECTION( "Modulo behaves correctly" ) {
    REQUIRE( (small % 2l) == 1l );
    REQUIRE( (  big % 2l) == 1l );
    REQUIRE( (ratio % 2l) == 1l );
  }

  SECTION( "Modulo widens appropriately" ) {
    REQUIRE( (   small %    small).hierarchyLevel() == Number::SMALLINT );
    REQUIRE( (   small %      big).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small %    ratio).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small % floating).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small %  complex).hierarchyLevel() == Number::COMPLEX  );
  }

  SECTION( "Modulo behaves correctly given negative numbers" ) {
    REQUIRE( ( small %  2l) ==  1l );
    REQUIRE( ( small % -2l) == -1l );
    REQUIRE( (-small %  2l) ==  1l );
    REQUIRE( (-small % -2l) == -1l );
    REQUIRE( (   big %  2l) ==  1l );
    REQUIRE( (   big % -2l) == -1l );
    REQUIRE( (-  big %  2l) ==  1l );
    REQUIRE( (-  big % -2l) == -1l );
    REQUIRE( ( ratio %  2l) ==  1l );
    REQUIRE( ( ratio % -2l) == -1l );
    REQUIRE( (-ratio %  2l) ==  1l );
    REQUIRE( (-ratio % -2l) == -1l );
  }

  SECTION( "Modulo handles rational results" ) {
    Number a { Number::ratio(3, 2) };
    Number b { 2l };
    REQUIRE( b % a == Number::ratio(1, 2) );
  }

}

TEST_CASE( "Exponents", "[number]" ) {

  Number    small { (Number::smallint)3 };
  Number      big { (Number::bigint)  3 };
  Number    ratio { Number::ratio(3, 1) };
  Number floating {                3.0  };
  Number  complex {          3.0 + 0.0i };

  SECTION( "Exponents behave correctly" ) {
    REQUIRE( (small.pow(small)) == 27l );
    REQUIRE( (  big.pow(  big)) == 27l );
    REQUIRE( (ratio.pow(small)) == 27l );
  }

  SECTION( "Exponentiation respects the convoluted casting rules" ) {
    REQUIRE( (   small.pow(     small)).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small.pow(       big)).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (   small.pow(     ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small.pow(  floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( (     big.pow(     small)).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (     big.pow(       big)).hierarchyLevel() == Number::BIGINT   );
    REQUIRE( (     big.pow(     ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (     big.pow(  floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (     big.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( (   ratio.pow(     small)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   ratio.pow(       big)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   ratio.pow(     ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   ratio.pow(  floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   ratio.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( (floating.pow(     small)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(       big)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(     ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(  floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(     small)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(       big)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(     ratio)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(  floating)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( (   small.pow(-    small)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small.pow(-      big)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   small.pow(-    ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   small.pow(- floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (     big.pow(-    small)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (     big.pow(-      big)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (     big.pow(-    ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (     big.pow(- floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   ratio.pow(-    small)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   ratio.pow(-      big)).hierarchyLevel() == Number::RATIO    );
    REQUIRE( (   ratio.pow(-    ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (   ratio.pow(- floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(-    small)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(-      big)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(-    ratio)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( (floating.pow(- floating)).hierarchyLevel() == Number::FLOATING );
    REQUIRE( ( complex.pow(-    small)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(-      big)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(-    ratio)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(- floating)).hierarchyLevel() == Number::COMPLEX  );
    REQUIRE( ( complex.pow(   complex)).hierarchyLevel() == Number::COMPLEX  );
  }

}

// We could test the bitwise operators and trig functions here (also,
// log). But what is there really to test? They literally just
// delegate down to the C++ system calls.

TEST_CASE( "Floor function", "[number]" ) {

  Number    small { (Number::smallint)3 };
  Number      big { (Number::bigint)  3 };
  Number    ratio { Number::ratio(7, 2) };
  Number floating {                3.5  };

  SECTION( "Floor behaves correctly" ) {
    REQUIRE(    small.floor() == 3l );
    REQUIRE(      big.floor() == 3l );
    REQUIRE(    ratio.floor() == 3l );
    REQUIRE( floating.floor() == 3l );
  }

  SECTION( "Floor can narrow the type" ) {
    REQUIRE(   small.floor().hierarchyLevel() == Number::SMALLINT );
    REQUIRE(     big.floor().hierarchyLevel() == Number::BIGINT   );
    REQUIRE(   ratio.floor().hierarchyLevel() == Number::BIGINT   );
    REQUIRE(floating.floor().hierarchyLevel() == Number::BIGINT   );
  }

}

TEST_CASE( "Parts of complex numbers", "[number]" ) {

  Number complex0 { 1 + 2i };
  Number complex1 = complexNumber(1l, 2l);

  REQUIRE( complex0 == complex1 );
  REQUIRE( complex0.realPart() == 1l );
  REQUIRE( complex0.imagPart() == 2l );
  REQUIRE( complex1.realPart() == 1l );
  REQUIRE( complex1.imagPart() == 2l );

  REQUIRE( complex0.realPart().hierarchyLevel() == Number::FLOATING );
  REQUIRE( complex0.imagPart().hierarchyLevel() == Number::FLOATING );
  REQUIRE( complex1.realPart().hierarchyLevel() == Number::FLOATING );
  REQUIRE( complex1.imagPart().hierarchyLevel() == Number::FLOATING );

}

// Constants like epsilon, infinity, NaN could be tested for certain
// properties?

TEST_CASE( "Parsing integers", "[number]" ) {

  REQUIRE( parseInteger("D300") == Number(300l) );
  REQUIRE( parseInteger("X12C") == Number(300l) );
  REQUIRE( parseInteger("B100101100") == Number(300l) );
  REQUIRE( parseInteger("O454") == Number(300l) );
  REQUIRE( parseInteger("B112") == boost::none );
  REQUIRE( parseInteger("D+300") == Number(300l) );
  REQUIRE( parseInteger("X+12C") == Number(300l) );
  REQUIRE( parseInteger("B+100101100") == Number(300l) );
  REQUIRE( parseInteger("O+454") == Number(300l) );
  REQUIRE( parseInteger("B+112") == boost::none );
  REQUIRE( parseInteger("D-300") == Number(-300l) );
  REQUIRE( parseInteger("X-12C") == Number(-300l) );
  REQUIRE( parseInteger("B-100101100") == Number(-300l) );
  REQUIRE( parseInteger("O-454") == Number(-300l) );
  REQUIRE( parseInteger("B-112") == boost::none );

}


/*
 * Copyright Nick Thompson, 2020
 * 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 <iostream>
#include <boost/core/demangle.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/ext/std/integer_sequence.hpp>

#include <boost/multiprecision/float128.hpp>
#include <boost/math/special_functions/daubechies_wavelet.hpp>
#include <quicksvg/graph_fn.hpp>
#include <quicksvg/ulp_plot.hpp>

using boost::multiprecision::float128;
constexpr const int GRAPH_WIDTH = 700;

template<typename Real, int p>
void plot_psi(int grid_refinements = -1)
{
    auto psi = boost::math::daubechies_wavelet<Real, p>();
    if (grid_refinements >= 0)
    {
        psi = boost::math::daubechies_wavelet<Real, p>(grid_refinements);
    }
    auto [a, b] = psi.support();
    std::string title = "Daubechies " + std::to_string(p) + " wavelet";
    title = "";
    std::string filename = "daubechies_" + std::to_string(p) + "_wavelet.svg";
    int samples = 1024;
    quicksvg::graph_fn daub(a, b, title, filename, samples, GRAPH_WIDTH);
    daub.set_gridlines(8, 2*p-1);
    daub.set_stroke_width(1);
    daub.add_fn(psi);
    daub.write_all();
}

template<typename Real, int p>
void plot_dpsi(int grid_refinements = -1)
{
    auto psi = boost::math::daubechies_wavelet<Real, p>();
    if (grid_refinements >= 0)
    {
        psi = boost::math::daubechies_wavelet<Real, p>(grid_refinements);
    }
    auto [a, b] = psi.support();
    std::string title = "Daubechies " + std::to_string(p) + " wavelet derivative";
    title = "";
    std::string filename = "daubechies_" + std::to_string(p) + "_wavelet_prime.svg";
    int samples = 1024;
    quicksvg::graph_fn daub(a, b, title, filename, samples, GRAPH_WIDTH);
    daub.set_stroke_width(1);
    daub.set_gridlines(8, 2*p-1);
    auto dpsi = [psi](Real x)->Real { return psi.prime(x); };
    daub.add_fn(dpsi);
    daub.write_all();
}

template<typename Real, int p>
void plot_convergence()
{
    auto psi1 = boost::math::daubechies_wavelet<Real, p>(1);
    auto [a, b] = psi1.support();
    std::string title = "Daubechies " + std::to_string(p) + " wavelet at  1 (orange), 2 (red), and 21 (blue) grid refinements";
    title = "";
    std::string filename = "daubechies_" + std::to_string(p) + "_wavelet_convergence.svg";

    quicksvg::graph_fn daub(a, b, title, filename, 1024, GRAPH_WIDTH);
    daub.set_stroke_width(1);
    daub.set_gridlines(8, 2*p-1);

    daub.add_fn(psi1, "orange");
    auto psi2 = boost::math::daubechies_wavelet<Real, p>(2);
    daub.add_fn(psi2, "red");

    auto psi21 = boost::math::daubechies_wavelet<Real, p>(21);
    daub.add_fn(psi21);

    daub.write_all();
}

template<typename Real, int p>
void plot_condition_number()
{
    using std::abs;
    using std::log;
    static_assert(p >= 3, "p = 2 is not differentiable, so condition numbers cannot be effectively evaluated.");
    auto phi = boost::math::daubechies_wavelet<Real, p>();
    Real a = phi.support().first + 1000*std::sqrt(std::numeric_limits<Real>::epsilon());
    Real b = phi.support().second - 1000*std::sqrt(std::numeric_limits<Real>::epsilon());
    std::string title = "log10 of condition number of function evaluation for Daubechies " + std::to_string(p) + " wavelet function.";
    title = "";
    std::string filename = "daubechies_" + std::to_string(p) + "_wavelet_condition_number.svg";

    quicksvg::graph_fn daub(a, b, title, filename, 2048, GRAPH_WIDTH);
    daub.set_stroke_width(1);
    daub.set_gridlines(8, 2*p-1);

    auto cond = [&phi](Real x)
    {
        Real y = phi(x);
        Real dydx = phi.prime(x);
        Real z = abs(x*dydx/y);
        using std::isnan;
        if (z==0)
        {
            return Real(-1);
        }
        if (isnan(z))
        {
            // Graphing libraries don't like nan's:
            return Real(1);
        }
        return log10(z);
    };
    daub.add_fn(cond);
    daub.write_all();
}

template<typename CoarseReal, typename PreciseReal, int p, class PsiPrecise>
void do_ulp(int coarse_refinements, PsiPrecise psi_precise)
{
    auto psi_coarse = boost::math::daubechies_wavelet<CoarseReal, p>(coarse_refinements);

    std::string title = std::to_string(p) + " vanishing moment ULP plot at " + std::to_string(coarse_refinements) + " refinements and " + boost::core::demangle(typeid(CoarseReal).name()) + " precision";
    title = "";

    std::string filename = "daubechies_" + std::to_string(p) + "_wavelet_" + boost::core::demangle(typeid(CoarseReal).name()) + "_" + std::to_string(coarse_refinements) + "_refinements.svg";
    int samples = 20000;
    int clip = 20;
    int horizontal_lines = 8;
    int vertical_lines = 2*p - 1;
    quicksvg::ulp_plot<decltype(psi_coarse), CoarseReal, decltype(psi_precise), PreciseReal>(psi_coarse, psi_precise, CoarseReal(psi_coarse.support().first), psi_coarse.support().second, title, filename, samples, GRAPH_WIDTH, clip, horizontal_lines, vertical_lines);
}

int main()
{
    boost::hana::for_each(std::make_index_sequence<18>(), [&](auto i){ plot_psi<double, i+2>(); });
    boost::hana::for_each(std::make_index_sequence<17>(), [&](auto i){ plot_dpsi<double, i+3>(); });
    boost::hana::for_each(std::make_index_sequence<17>(), [&](auto i){ plot_condition_number<double, i+3>(); });
    boost::hana::for_each(std::make_index_sequence<18>(), [&](auto i){ plot_convergence<double, i+2>(); });

    using PreciseReal = float128;
    using CoarseReal = double;
    int precise_refinements = 22;
    constexpr const int p = 9;
    std::cout << "Computing precise wavelet function in " << boost::core::demangle(typeid(PreciseReal).name()) << " precision.\n";
    auto phi_precise = boost::math::daubechies_wavelet<PreciseReal, p>(precise_refinements);
    std::cout << "Beginning comparison with functions computed in " << boost::core::demangle(typeid(CoarseReal).name()) << " precision.\n";
    for (int i = 7; i <= precise_refinements-1; ++i)
    {
        std::cout << "\tCoarse refinement " << i << "\n";
        do_ulp<CoarseReal, PreciseReal, p>(i, phi_precise);
    }
}


/** Test cases for Segment class.
 *
 */

#include "utils/random.h"

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

#include <iostream>
#include <random>
#include <map>
#include <string>
#include <iomanip>

using namespace utils::random;
using namespace std;

bool print_histogram = true;

void printBannerTestCase(string testName)
{
	int N = 50;
	cout << "\n";
	for (int i = 1; i<=N; i++){
		cout << "*" ;
	}
	cout << "\n" ;
	int dashNo = ( N - testName.length() - 2 )  /  2;
	for (int i = 1; i<=dashNo; i++){
		cout << "-" ;
	}
	cout << " " << testName ;

	for (int i = 1; i <= N-2*dashNo-testName.length()-1; i++){
			cout << " " ;
	}
	for (int i = 1; i<=dashNo; i++){
			cout << "-" ;
	}
	cout << "\n";
	for (int i = 1; i<=N; i++){
			cout << "*" ;
	}
	cout << "\n\n";
}

BOOST_AUTO_TEST_CASE(Generate_Normal_Distribution)
{
	printBannerTestCase( "Testing Normal Distribution" );
    int N = 1000000;
    double expmean = 0;
    double sigma = 1;
    vector<double> vs(N);
    genNormalDist(vs, sigma, expmean);

    double foundMean = 0;
    for (auto v : vs) {
    	foundMean += v;
    }
    foundMean = foundMean/N;
    BOOST_CHECK(abs(foundMean - expmean ) < 0.1*abs(foundMean-sigma));
    cout << "Expected Mean: " << expmean << endl;
    cout << "Generated Mean: " << foundMean << endl;
}

BOOST_AUTO_TEST_CASE(Gaussian_Random_Distribution)
{
	printBannerTestCase( "Testing Gaussian Random Distribution" );
	int N = 1000000;
	std::map<int, int> hist;
	double sigma, expmean, min, max, foundMean;
	sigma = 2;
	max = 12;
	min = -2;
	expmean = 5;
	foundMean = 0;
	bool reset = false;

	for( int n=0; n<N; n++ ){
		double number = getGaussianRand(sigma, expmean, min, max, reset );
		foundMean += number;
		++hist[std::round( number)];
	}
	foundMean = foundMean/double(N);
	//checking mean
	if ( expmean!=0 ){
		BOOST_CHECK_CLOSE( foundMean,  expmean, 1);
	}
	BOOST_CHECK(abs( expmean - abs(foundMean) ) < 0.1*sigma);
	//checking minimum
	BOOST_CHECK(hist.begin()->first >= min);
	//checking maximum
	BOOST_CHECK(hist.rbegin()->first <= max );
	if( print_histogram ){
		cout << "Histogram : " << endl;
		for(auto p : hist) {
				std::cout << std::fixed << std::setprecision(1) << std::setw(2)
						  << p.first << ' ' << std::string(p.second/4000, '*') << '\n';
		}
		cout << "\n";
	}
	cout << "Expected Mean: " << expmean << endl;
	cout << "Generated Mean: " << foundMean << endl;
}

BOOST_AUTO_TEST_CASE(Uniform_Random_Distribution)
{
	printBannerTestCase( "Testing Uniform Random Distribution" );
	int N = 1000000;
	std::map<int, int> hist;
	double expmean, min, max, foundMean;
	min = -2;
	max = 12;
	expmean = (min + max)/2;
	foundMean = 0;
	bool reset = false;

	for( int n=0; n<N; n++ ){
		double number = getUniformRand(min, max, reset );
		foundMean += number;
		++hist[std::round( number)];
	}
	foundMean = foundMean/double(N);
	//checking mean
	if ( expmean!=0 ){
			BOOST_CHECK_CLOSE( foundMean,  expmean, 1);
	}
	BOOST_CHECK(abs( expmean - abs(foundMean) ) < 0.1*abs(abs(min)-abs(expmean)));
	//checking minimum
	BOOST_CHECK(hist.begin()->first >= min);
	//checking maximum
	BOOST_CHECK(hist.rbegin()->first <= max );
	if( print_histogram ){
		cout << "Histogram : " << endl;
		for(auto p : hist) {
				std::cout << std::fixed << std::setprecision(1) << std::setw(2)
						  << p.first << ' ' << std::string(p.second/4000, '*') << '\n';
		}
		cout << "\n";
	}
	cout << "Expected Mean: " << expmean << endl;
	cout << "Generated Mean: " << foundMean << endl;
}


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

#include <pch.hpp>

#ifndef BOOST_NO_CXX11_HDR_TUPLE

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/math/tools/roots.hpp>
#include <boost/test/results_collector.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <iostream>
#include <iomanip>
#include <tuple>

// No derivatives - using TOMS748 internally.
struct cbrt_functor_noderiv
{ //  cube root of x using only function - no derivatives.
   cbrt_functor_noderiv(double to_find_root_of) : a(to_find_root_of)
   { // Constructor just stores value a to find root of.
   }
   double operator()(double x)
   {
      double fx = x*x*x - a; // Difference (estimate x^3 - a).
      return fx;
   }
private:
   double a; // to be 'cube_rooted'.
}; // template <class T> struct cbrt_functor_noderiv

// Using 1st derivative only Newton-Raphson
struct cbrt_functor_deriv
{ // Functor also returning 1st derviative.
   cbrt_functor_deriv(double const& to_find_root_of) : a(to_find_root_of)
   { // Constructor stores value a to find root of,
      // for example: calling cbrt_functor_deriv<double>(x) to use to get cube root of x.
   }
   std::pair<double, double> operator()(double const& x)
   { // Return both f(x) and f'(x).
      double fx = x*x*x - a; // Difference (estimate x^3 - value).
      double dx = 3 * x*x; // 1st derivative = 3x^2.
      return std::make_pair(fx, dx); // 'return' both fx and dx.
   }
private:
   double a; // to be 'cube_rooted'.
};
// Using 1st and 2nd derivatives with Halley algorithm.
struct cbrt_functor_2deriv
{ // Functor returning both 1st and 2nd derivatives.
   cbrt_functor_2deriv(double const& to_find_root_of) : a(to_find_root_of)
   { // Constructor stores value a to find root of, for example:
      // calling cbrt_functor_2deriv<double>(x) to get cube root of x,
   }
   std::tuple<double, double, double> operator()(double const& x)
   { // Return both f(x) and f'(x) and f''(x).
      double fx = x*x*x - a; // Difference (estimate x^3 - value).
      double dx = 3 * x*x; // 1st derivative = 3x^2.
      double d2x = 6 * x; // 2nd derivative = 6x.
      return std::make_tuple(fx, dx, d2x); // 'return' fx, dx and d2x.
   }
private:
   double a; // to be 'cube_rooted'.
};

BOOST_AUTO_TEST_CASE( test_main )
{
   int newton_limits = static_cast<int>(std::numeric_limits<double>::digits * 0.6);

   double arg = 1e-50;
   while(arg < 1e50)
   {
      double result = boost::math::cbrt(arg);
      //
      // Start with a really bad guess 5 times below the result:
      //
      double guess = result / 5;
      boost::uintmax_t iters = 1000;
      // TOMS algo first:
      std::pair<double, double> r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
      BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
      BOOST_CHECK_LE(iters, 14);
      // Newton next:
      iters = 1000;
      double dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, guess / 2, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 12);
      // Halley next:
      iters = 1000;
      dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 7);
      // Schroder next:
      iters = 1000;
      dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 11);
      //
      // Over again with a bad guess 5 times larger than the result:
      //
      iters = 1000;
      guess = result * 5;
      r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
      BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
      BOOST_CHECK_LE(iters, 14);
      // Newton next:
      iters = 1000;
      dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 12);
      // Halley next:
      iters = 1000;
      dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 7);
      // Schroder next:
      iters = 1000;
      dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 11);
      //
      // A much better guess, 1% below result:
      //
      iters = 1000;
      guess = result * 0.9;
      r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
      BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
      BOOST_CHECK_LE(iters, 12);
      // Newton next:
      iters = 1000;
      dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 5);
      // Halley next:
      iters = 1000;
      dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 3);
      // Schroder next:
      iters = 1000;
      dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 4);
      //
      // A much better guess, 1% above result:
      //
      iters = 1000;
      guess = result * 1.1;
      r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
      BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
      BOOST_CHECK_LE(iters, 12);
      // Newton next:
      iters = 1000;
      dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 5);
      // Halley next:
      iters = 1000;
      dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 3);
      // Schroder next:
      iters = 1000;
      dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
      BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
      BOOST_CHECK_LE(iters, 4);

      arg *= 3.5;
   }
}

#else

int main() { return 0; }

#endif


/**
 * @brief Functions to compute gradients using finite difference.
 *
 * Based on the functions in https://github.com/PatWie/CppNumericalSolvers
 * and rewritten to use Eigen
 */
#pragma once

#include <Eigen/Core>

namespace fd {

/**
 * @brief Enumeration of available orders of accuracy for finite differences.
 *
 * The corresponding integer values are used internally and should be ignored.
 */
enum AccuracyOrder {
    SECOND, ///< @brief Second order accuracy.
    FOURTH, ///< @brief Fourth order accuracy.
    SIXTH,  ///< @brief Sixth order accuracy.
    EIGHTH  ///< @brief Eighth order accuracy.
};

/**
 * @brief Compute the gradient of a function using finite differences.
 *
 * @param[in]  x         Point at which to compute the gradient.
 * @param[in]  f         Compute the gradient of this function.
 * @param[out] grad      Computed gradient.
 * @param[in]  accuracy  Accuracy of the finite differences.
 * @param[in]  eps       Value of the finite difference step.
 */
void finite_gradient(
    const Eigen::VectorXd& x,
    const std::function<double(const Eigen::VectorXd&)>& f,
    Eigen::VectorXd& grad,
    const AccuracyOrder accuracy = SECOND,
    const double eps = 1.0e-8);

/**
 * @brief Compute the jacobian of a function using finite differences.
 *
 * @param[in]  x         Point at which to compute the jacobian.
 * @param[in]  f         Compute the jacobian of this function.
 * @param[out] jac       Computed jacobian.
 * @param[in]  accuracy  Accuracy of the finite differences.
 * @param[in]  eps       Value of the finite difference step.
 */
void finite_jacobian(
    const Eigen::VectorXd& x,
    const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& f,
    Eigen::MatrixXd& jac,
    const AccuracyOrder accuracy = SECOND,
    const double eps = 1.0e-8);

/**
 * @brief Compute the hessian of a function using finite differences.
 *
 * @param[in]  x         Point at which to compute the hessian.
 * @param[in]  f         Compute the hessian of this function.
 * @param[out] hess      Computed hessian.
 * @param[in]  accuracy  Accuracy of the finite differences.
 * @param[in]  eps       Value of the finite difference step.
 */
void finite_hessian(
    const Eigen::VectorXd& x,
    const std::function<double(const Eigen::VectorXd&)>& f,
    Eigen::MatrixXd& hess,
    const AccuracyOrder accuracy = SECOND,
    const double eps = 1.0e-5);

/**
 * @brief Compare if two gradients are close enough.
 *
 * @param[in] x         The first gradient to compare.
 * @param[in] y         The second gradient to compare against.
 * @param[in] test_eps  Tolerance of equality.
 * @param[in] msg       Debug message header.
 *
 * @return A boolean for if x and y are close to the same value.
 */
bool compare_gradient(
    const Eigen::VectorXd& x,
    const Eigen::VectorXd& y,
    const double test_eps = 1e-4,
    const std::string& msg = "compare_gradient ");

/**
 * @brief Compare if two jacobians are close enough.
 *
 * @param[in] x         The first jacobian to compare.
 * @param[in] y         The second jacobian to compare against.
 * @param[in] test_eps  Tolerance of equality.
 * @param[in] msg       Debug message header.
 *
 * @return A boolean for if x and y are close to the same value.
 */
bool compare_jacobian(
    const Eigen::MatrixXd& x,
    const Eigen::MatrixXd& y,
    const double test_eps = 1e-4,
    const std::string& msg = "compare_jacobian ");

/**
 * @brief Compare if two hessians are close enough.
 *
 * @param[in] x         The first hessian to compare.
 * @param[in] y         The second hessian to compare against.
 * @param[in] test_eps  Tolerance of equality.
 * @param[in] msg       Debug message header.
 *
 * @return A boolean for if x and y are close to the same value.
 */
bool compare_hessian(
    const Eigen::MatrixXd& x,
    const Eigen::MatrixXd& y,
    const double test_eps = 1e-4,
    const std::string& msg = "compare_hessian ");

} // namespace fd


// root_finding_example.cpp

// Copyright Paul A. Bristow 2010.

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

// Example of finding roots using Newton-Raphson, Halley

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

//#ifdef _MSC_VER
//#  pragma warning(disable: 4180) // qualifier has no effect (in Fusion).
//#endif

//#define BOOST_MATH_INSTRUMENT

//[root_finding_example1
/*`
This example demonstrates how to use the various tools for root finding
taking the simple cube root function (cbrt) as an example.
It shows how use of derivatives can improve the speed.
(But is only a demonstration and does not try to make the ultimate improvements of 'real-life'
implementation of boost::math::cbrt; mainly by using a better computed initial 'guess'
at `<boost/math/special_functions/cbrt.hpp>` ).

First some includes that will be needed.
Using statements are provided to list what functions are being used in this example:
you can of course qualify the names in other ways.
*/

#include <boost/math/tools/roots.hpp>
using boost::math::policies::policy;
using boost::math::tools::newton_raphson_iterate;
using boost::math::tools::halley_iterate;
using boost::math::tools::eps_tolerance; // Binary functor for specified number of bits.
using boost::math::tools::bracket_and_solve_root;
using boost::math::tools::toms748_solve;

#include <boost/math/tools/tuple.hpp>
// using boost::math::tuple;
// using boost::math::make_tuple;
// using boost::math::tie;
// which provide convenient aliases for various implementations,
// including std::tr1, depending on what is available.

//] [/root_finding_example1]

#include <iostream>
using std::cout; using std::endl;
#include <iomanip>
using std::setw; using std::setprecision;
#include <limits>
using std::numeric_limits;

//[root_finding_example2
/*`

Let's suppose we want to find the cube root of a number.

The equation we want to solve is:

__spaces ['f](x) = x[cubed]

We will first solve this without using any information
about the slope or curvature of the cbrt function.

We then show how adding what we can know, for this function, about the slope,
the 1st derivation /f'(x)/, will speed homing in on the solution,
and then finally how adding the curvature /f''(x)/ as well will improve even more.

The 1st and 2nd derivatives of x[cubed] are:

__spaces ['f]\'(x) = 2x[sup2]

__spaces ['f]\'\'(x) = 6x
*/

template <class T>
struct cbrt_functor_1
{ //  cube root of x using only function - no derivatives.
  cbrt_functor_1(T const& to_find_root_of) : value(to_find_root_of)
  { // Constructor stores value to find root of.
    // For example: calling cbrt_functor_<T>(x) to get cube root of x.
  }
  T operator()(T const& x)
  { // Return both f(x)only.
    T fx = x*x*x - value; // Difference (estimate x^3 - value).
    return fx;
  }
private:
  T value; // to be 'cube_rooted'.
};

/*`Implementing the cube root function itself is fairly trivial now:
the hardest part is finding a good approximation to begin with.
In this case we'll just divide the exponent by three.
(There are better but more complex guess algorithms used in 'real-life'.)

Cube root function is 'Really Well Behaved' in that it is monotonic
and has only one root (we leave negative values 'as an exercise for the student').
*/

template <class T>
T cbrt_1(T x)
{ // return cube root of x using bracket_and_solve (no derivatives).
  using namespace std;  // Help ADL of std functions.
  using namespace boost::math;
  int exponent;
  frexp(x, &exponent); // Get exponent of z (ignore mantissa).
  T guess = ldexp(1., exponent/3); // Rough guess is to divide the exponent by three.
  T factor = 2; // To multiply
  int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  // digits used to control how accurate to try to make the result.
  int get_digits = (digits * 3) /4; // Near maximum (3/4) possible accuracy.
  //cout  << ", std::numeric_limits<" << typeid(T).name()  << ">::digits = " << digits
  //   << ", accuracy " << get_digits << " bits."<< endl;

  //boost::uintmax_t maxit = (std::numeric_limits<boost::uintmax_t>::max)();
  // (std::numeric_limits<boost::uintmax_t>::max)() = 18446744073709551615
  // which is more than we might wish to wait for!!!
  // so we can choose some reasonable estimate of how many iterations may be needed.
  const boost::uintmax_t maxit = 10;
  boost::uintmax_t it = maxit; // Initally our chosen max iterations, but updated with actual.
  // We could also have used a maximum iterations provided by any policy:
  // boost::uintmax_t max_it = policies::get_max_root_iterations<Policy>();
  bool is_rising = true; // So if result if guess^3 is too low, try increasing guess.
  eps_tolerance<double> tol(get_digits);
  std::pair<T, T> r =
    bracket_and_solve_root(cbrt_functor_1<T>(x), guess, factor, is_rising, tol, it);

  // Can show how many iterations (this information is lost outside cbrt_1).
  cout << "Iterations " << maxit << endl;
  if(it >= maxit)
  { //
    cout << "Unable to locate solution in chosen iterations:"
      " Current best guess is between " << r.first << " and " << r.second << endl;
  }
  return r.first + (r.second - r.first)/2;  // Midway between brackets.
} // T cbrt_1(T x)

//[root_finding_example2
/*`
We now solve the same problem, but using more information about the function,
to show how this can speed up finding the best estimate of the root.

For this function, the 1st differential (the slope of the tangent to a curve at any point) is known.

[@http://en.wikipedia.org/wiki/Derivative#Derivatives_of_elementary_functions derivatives]
gives some reminders.

Using the rule that the derivative of x^n for positive n (actually all nonzero n) is nx^n-1,
allows use to get the 1st differential as 3x^2.

To see how this extra information is used to find the root, view this demo:
[@http://en.wikipedia.org/wiki/Newton%27s_methodNewton Newton-Raphson iterations].

We need to define a different functor that returns
both the evaluation of the function to solve, along with its first derivative:

To \'return\' two values, we use a pair of floating-point values:
*/
template <class T>
struct cbrt_functor_2
{ // Functor also returning 1st derviative.
  cbrt_functor_2(T const& to_find_root_of) : value(to_find_root_of)
  { // Constructor stores value to find root of,
    // for example: calling cbrt_functor_2<T>(x) to use to get cube root of x.
  }
  std::pair<T, T> operator()(T const& x)
  { // Return both f(x) and f'(x).
    T fx = x*x*x - value; // Difference (estimate x^3 - value).
    T dx =  3 * x*x; // 1st derivative = 3x^2.
    return std::make_pair(fx, dx); // 'return' both fx and dx.
  }
private:
  T value; // to be 'cube_rooted'.
}; // cbrt_functor_2

/*`Our cube root function is now:*/

template <class T>
T cbrt_2(T x)
{ // return cube root of x using 1st derivative and Newton_Raphson.
  int exponent;
  frexp(x, &exponent); // Get exponent of z (ignore mantissa).
  T guess = ldexp(1., exponent/3); // Rough guess is to divide the exponent by three.
  T min = ldexp(0.5, exponent/3); // Minimum possible value is half our guess.
  T max = ldexp(2., exponent/3);// Maximum possible value is twice our guess.
  int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  // digits used to control how accurate to try to make the result.
  int get_digits = (digits * 3) /4; // Near maximum (3/4) possible accuracy.

  //boost::uintmax_t maxit = (std::numeric_limits<boost::uintmax_t>::max)();
  // the default (std::numeric_limits<boost::uintmax_t>::max)() = 18446744073709551615
  // which is more than we might wish to wait for!!!  so we can reduce it
  boost::uintmax_t maxit = 10;
  //cout << "Max Iterations " << maxit << endl; //
  T result = newton_raphson_iterate(cbrt_functor_2<T>(x), guess, min, max, get_digits, maxit);
  // Can show how many iterations (updated by newton_raphson_iterate) but lost on exit.
  // cout << "Iterations " << maxit << endl;
  return result;
}

/*`
Finally need to define yet another functor that returns
both the evaluation of the function to solve,
along with its first and second derivatives:

f''(x) = 3 * 3x

To \'return\' three values, we use a tuple of three floating-point values:
*/

template <class T>
struct cbrt_functor_3
{ // Functor returning both 1st and 2nd derivatives.
  cbrt_functor_3(T const& to_find_root_of) : value(to_find_root_of)
  { // Constructor stores value to find root of, for example:
    //  calling cbrt_functor_3<T>(x) to get cube root of x,
  }

  // using boost::math::tuple; // to return three values.
  boost::math::tuple<T, T, T> operator()(T const& x)
  { // Return both f(x) and f'(x) and f''(x).
    using boost::math::make_tuple;
    T fx = x*x*x - value; // Difference (estimate x^3 - value).
    T dx = 3 * x*x; // 1st derivative = 3x^2.
    T d2x = 6 * x; // 2nd derivative = 6x.
    return make_tuple(fx, dx, d2x); // 'return' fx, dx and d2x.
  }
private:
  T value; // to be 'cube_rooted'.
}; // struct cbrt_functor_3

/*`Our cube function is now:*/

template <class T>
T cbrt_3(T x)
{ // return cube root of x using 1st and 2nd derivatives and Halley.
  //using namespace std;  // Help ADL of std functions.
  using namespace boost::math;
  int exponent;
  frexp(x, &exponent); // Get exponent of z (ignore mantissa).
  T guess = ldexp(1., exponent/3); // Rough guess is to divide the exponent by three.
  T min = ldexp(0.5, exponent/3); // Minimum possible value is half our guess.
  T max = ldexp(2., exponent/3);// Maximum possible value is twice our guess.
  int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  // digits used to control how accurate to try to make the result.
  int get_digits = (digits * 3) /4; // Near maximum (3/4) possible accuracy.
  //cout << "Value " << x << ", guess " << guess
  //  << ", min " << min << ", max " << max
  //  << ", std::numeric_limits<" << typeid(T).name()  << ">::digits = " << digits
  //   << ", accuracy " << get_digits << " bits."<< endl;

  //boost::uintmax_t maxit = (std::numeric_limits<boost::uintmax_t>::max)();
  // the default (std::numeric_limits<boost::uintmax_t>::max)() = 18446744073709551615
  // which is more than we might wish to wait for!!!  so we can reduce it
  boost::uintmax_t maxit = 10;
  //cout << "Max Iterations " << maxit << endl; //
  T result = halley_iterate(cbrt_functor_3<T>(x), guess, min, max, digits, maxit);
  // Can show how many iterations (updated by newton_raphson_iterate).
  cout << "Iterations " << maxit << endl;
  return result;
} // cbrt_3(x)

int main()
{
  cout << "Cube Root finding (cbrt) Example." << endl;
  cout.precision(std::numeric_limits<double>::max_digits10);
  // Show all possibly significant decimal digits.
  try
  {

    double v27 = 27; // that has an exact integer cube root.
    double v28 = 28; // whose cube root is not exactly representable.

    // Using bracketing:
    double r = cbrt_1(v27);
    cout << "cbrt_1(" << v27 << ") = " << r << endl;
    r = cbrt_1(v28);
    cout << "cbrt_1(" << v28 << ") = " << r << endl;

    // Using 1st differential Newton-Raphson:
    r = cbrt_2(v27);
    cout << "cbrt_1(" << v27 << ") = " << r << endl;
    r = cbrt_2(v28);
    cout << "cbrt_2(" << v28 << ") = " << r << endl;

    // Using Halley with 1st and 2nd differentials.
    r = cbrt_3(v27);
    cout << "cbrt_3(" << v27 << ") = " << r << endl;
    r = cbrt_3(v28);
    cout << "cbrt_3(" << v28 << ") = " << r << endl;

    //] [/root_finding_example2]
  }
  catch(const std::exception& e)
  { // Always useful to include try & catch blocks because default policies
    // are to throw exceptions on arguments that cause errors like underflow, overflow.
    // Lacking try & catch blocks, the program will abort without a message below,
    // which may give some helpful clues as to the cause of the exception.
    std::cout <<
      "\n""Message from thrown exception was:\n   " << e.what() << std::endl;
  }
  return 0;
}  // int main()

//[root_finding_example_output
/*`
Normal output is:

[pre
  root_finding_example.cpp
  Generating code
  Finished generating code
  root_finding_example.vcxproj -> J:\Cpp\MathToolkit\test\Math_test\Release\root_finding_example.exe
  Cube Root finding (cbrt) Example.
  Iterations 10
  cbrt_1(27) = 3
  Iterations 10
  Unable to locate solution in chosen iterations: Current best guess is between 3.0365889718756613 and 3.0365889718756627
  cbrt_1(28) = 3.0365889718756618
  cbrt_1(27) = 3
  cbrt_2(28) = 3.0365889718756627
  Iterations 4
  cbrt_3(27) = 3
  Iterations 5
  cbrt_3(28) = 3.0365889718756627

] [/pre]

to get some (much!) diagnostic output we can add

#define BOOST_MATH_INSTRUMENT

[pre

]
*/
//] [/root_finding_example_output]


/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Union’s Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/

#pragma once

#include "../util/AlgorithmUtils.hpp"
#include "../util/FluidEigenMappings.hpp"
#include "../../data/TensorTypes.hpp"
#include <Eigen/Core>
#include <cassert>
#include <cmath>

namespace fluid {
namespace algorithm {

class Normalization
{
public:
  using ArrayXd = Eigen::ArrayXd;
  using ArrayXXd = Eigen::ArrayXXd;

  void init(double min, double max, RealMatrixView in)
  {
    using namespace Eigen;
    using namespace _impl;
    mMin = min;
    mMax = max;
    ArrayXXd input = asEigen<Array>(in);
    mDataMin = input.colwise().minCoeff();
    mDataMax = input.colwise().maxCoeff();
    mDataRange = mDataMax - mDataMin;
    mInitialized = true;
  }

  void init(double min, double max, RealVectorView dataMin,
            RealVectorView dataMax)
  {
    using namespace Eigen;
    using namespace _impl;
    mMin = min;
    mMax = max;
    mDataMin = asEigen<Array>(dataMin);
    mDataMax = asEigen<Array>(dataMax);
    mDataRange = mDataMax - mDataMin;
    mDataRange = mDataRange.max(epsilon);
    mInitialized = true;
  }

  void processFrame(const RealVectorView in, RealVectorView out,
                    bool inverse = false) const
  {
    using namespace Eigen;
    using namespace _impl;
    ArrayXd input = asEigen<Array>(in);
    ArrayXd result;
    if (!inverse)
    {
      result = (input - mDataMin) / mDataRange.max(epsilon);
      result = mMin + (result * (mMax - mMin));
    }
    else
    {
      result = (input - mMin) / std::max((mMax - mMin), epsilon);
      result = mDataMin + (result * mDataRange);
    }
    out = asFluid(result);
  }

  void process(const RealMatrixView in, RealMatrixView out,
               bool inverse = false) const
  {
    using namespace Eigen;
    using namespace _impl;
    ArrayXXd input = asEigen<Array>(in);
    ArrayXXd result;
    if (!inverse)
    {
      result = (input.rowwise() - mDataMin.transpose());
      result = result.rowwise() / mDataRange.transpose().max(epsilon);
      result = mMin + (result * (mMax - mMin));
    }
    else
    {
      result = input - mMin;
      result = result / std::max((mMax - mMin), epsilon);
      result = (result.rowwise() * mDataRange.transpose());
      result = (result.rowwise() + mDataMin.transpose());
    }
    out = asFluid(result);
  }

  void setMin(double min) { mMin = min; }
  void setMax(double max) { mMax = max; }
  bool initialized() const { return mInitialized; }

  double getMin() const { return mMin; }
  double getMax() const { return mMax; }

  void getDataMin(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mDataMin);
  }

  void getDataMax(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mDataMax);
  }

  index dims() const { return mDataMin.size(); }
  index size() const { return 1; }

  void clear()
  {
    mMin = 0;
    mMax = 1.0;
    mDataMin.setZero();
    mDataMax.setZero();
    mDataRange.setZero();
    mInitialized = false;
  }

  double  mMin{0.0};
  double  mMax{1.0};
  ArrayXd mDataMin;
  ArrayXd mDataMax;
  ArrayXd mDataRange;
  bool    mInitialized{false};
};
}; // namespace algorithm
}; // namespace fluid


#ifndef _NAIVE_BAYES_
#define _NAIVE_BAYES_ 1

#include <string>
#include <vector>
#include <Eigen/Dense>
#include <cmath>
#include <exception>
#include <algorithm>

#define PI 3.141592653589793238462643383279502884L

#define _l_ std::cout<<__LINE__<<std::endl;

typedef struct {
  unsigned label;
  Eigen::VectorXd features;
} ClassificationObject;

typedef struct {
  unsigned label;
  double prior;
  Eigen::VectorXd mean;
  Eigen::MatrixXd covariance;
  double covarianceDeterminant;
  Eigen::MatrixXd covarianceInverse;
} ClassInfo;

ClassInfo
ComputeClassInfo(
    const std::vector<ClassificationObject>& data,
    const unsigned classLabel,
    const unsigned featureDim) {

  ClassInfo classInfo;
  classInfo.label = classLabel;
  classInfo.mean.setZero(featureDim);
  classInfo.covariance.setZero(featureDim, featureDim);

  // compute mean and prior probability at once
  std::vector<ClassificationObject> subset;
  for (const ClassificationObject& classificationObject : data) {
    if (classificationObject.label == classLabel) {
      classInfo.mean += classificationObject.features;
      subset.push_back(classificationObject);
    }
  }
  classInfo.mean /= subset.size();
  classInfo.prior = (double) subset.size() / (double) data.size();

  // covariance matrix
  // Note: I use a nifty trick that is simpler in code (not sure if
  //       simpler computationally). Let $P_i = x_i - mu$, where $i$
  //       corresponds to a particular observation vector. Then
  //       $A$ is the concatenation of all the $P_i$ as column
  //       vectors ($A = [P_1 ... P_m]$). Then
  //       $\frac{1}{m} * A * A^t$ results in the same computations
  //       that create the covariance matrix as more traditional
  //       formulae.
  Eigen::MatrixXd A(featureDim, subset.size());
  for (unsigned j = 0; j < subset.size(); ++j) {
    A.col(j) = subset[j].features - classInfo.mean;
  }

  classInfo.covariance = A * A.transpose();
  classInfo.covariance /= (double) subset.size();

  classInfo.covarianceDeterminant = classInfo.covariance.determinant();
  classInfo.covarianceInverse = classInfo.covariance.inverse();

  return classInfo;
}

double
GaussianPdf(
  const Eigen::VectorXd& testFeatureVector,
  const ClassInfo& classSummary) {

  const Eigen::VectorXd& x = testFeatureVector;
  const Eigen::VectorXd& mu = classSummary.mean;
  const double& sigmaDet = classSummary.covarianceDeterminant;
  const Eigen::MatrixXd& sigmaInv = classSummary.covarianceInverse;

  double scalingFactor = 1.0 / sqrt(pow(2.0 * PI, mu.size()) * sigmaDet);
  double exponent = -0.5 * (((x - mu).transpose() * sigmaInv).dot((x - mu)));

  return scalingFactor * exp(exponent);
}

unsigned
ClassifyObject(const ClassificationObject& object, const std::vector<ClassInfo>& classSummaries) {
  unsigned mostLikelyClass = 0;
  double highestProbability = classSummaries.front().label;

  for (const ClassInfo& classSummary : classSummaries) {
    double probability = GaussianPdf(object.features, classSummary) * classSummary.prior;
    if (probability > highestProbability) {
      mostLikelyClass = classSummary.label;
      highestProbability = probability;
    }
  }

  return mostLikelyClass;
}

#endif //_NAIVE_BAYES_


#include <iostream>
#include <cmath>
using namespace std;
#include <Eigen/Core>
#include <Eigen/Geometry>

// 李群李代数 库
#include "sophus/so3.hpp"
#include "sophus/se3.hpp"

#include<stdio.h>
#include"mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){
    // nlhs represent the number of parameters of the output
    // plhs is a array of the mxarray pointers, each pointing to the output
    // nrhs represents the number of parameters of the input
    // prhs is a array of the mxarray pointers, each pointing to the input

    // prhs[0], 6x1 matrix
    // prhs[1], Mx1 cell, each cell with NX3 points
    // prhs[2], Mx1 cell, each cell with PXQ single matrix
    // prhs[3], 1x2, or 1x3, or 1x4, or 1x5 matrix
    // prhs[4], 3x3 matrix
    // prhs[5], 1x2 matrix

    if(nrhs < 1){
        mexErrMsgIdAndTxt( "euler2se3Mex:invalidNumInputs", "at least 1 input arguments required");
        return;
    }

    // get the euler transformation
    const size_t *dimArrayOfEulerTransformation = mxGetDimensions(prhs[0]);
    size_t sizeRowsEulerTransformation = *(dimArrayOfEulerTransformation + 0);
    size_t sizeColsEulerTransformation = *(dimArrayOfEulerTransformation + 1);
    if(sizeRowsEulerTransformation != 6 || sizeColsEulerTransformation != 1){
        mexErrMsgIdAndTxt( "EdgeSE3ProjectDirectWithDirstortJacobian:invalidInputs", "the 1st param should be 6x1");
        return;
    }
    double *ptrEulerTransformation = (double *)(mxGetPr(prhs[0]));
    Eigen::Matrix<double, 6, 1> eulerTransformation;
    for(int i = 0; i < 6; i++){
        eulerTransformation(i, 0) = *(ptrEulerTransformation + i);
    }

    // cout<<"eulerTransformation = "<<endl<<eulerTransformation<<endl;

    Eigen::Matrix<double,6,1> se3;

    Eigen::Matrix3d R = Eigen::Matrix3d::Identity();
    Eigen::AngleAxisd rotation_Z_vector ( eulerTransformation(0,0), Eigen::Vector3d ( 0,0,1 ) );     //沿 Z 轴旋转 yaw
    Eigen::AngleAxisd rotation_Y_vector ( eulerTransformation(1,0), Eigen::Vector3d ( 0,1,0 ) );      //沿 Y 轴旋转 pitch
    Eigen::AngleAxisd rotation_X_vector ( eulerTransformation(2,0), Eigen::Vector3d ( 1,0,0 ) );      //沿 X 轴旋转 roll
    R = rotation_Z_vector.toRotationMatrix() * rotation_Y_vector.toRotationMatrix() * rotation_X_vector.toRotationMatrix();

    Eigen::Vector3d t(eulerTransformation(3,0), eulerTransformation(4,0), eulerTransformation(5,0));
    Sophus::SE3<double> SE3_Rt(R, t);           // 从R,t构造SE(3)

    // cout<<"SE3 = "<<endl<<SE3_Rt.matrix()<<endl;

    se3 = SE3_Rt.log();

    // cout<<"se3 = "<<endl<<se3.matrix()<<endl;

    // the se3 will be 6x1
    size_t dimArrayOfSe3[2] = { 6, 1 };
    plhs[0] = mxCreateNumericArray(2, dimArrayOfSe3, mxDOUBLE_CLASS, mxREAL);
    double *ptrSe3 = (double *)mxGetData(plhs[0]);
    for(int i = 0; i < 6; i++){
        ptrSe3[i] = se3(i, 0);
    }
}


#include <vector>

#include <NTL/ZZ.h>

#include "../src/elements/element.hpp"
#include "../src/algorithms/utils.hpp"

/* Example: using the remainder tree algorithm to search for Wolstenholme primes.
 * Wolstenholme's theorem states that choose(2*p - 1, p - 1) = 1 (mod p^3) for prime p.
 * A Wolstenholme prime satisfies the same identity modulo p^4.
 *
 * Suppose we want to use the remainder tree algorithm to aid our search for Wolstenholme primes.
 * Let w_p = choose(2*p - 1, p - 1). We wish to find w_p (mod p^4) for primes p. Define R_1 = 1
 *
 * R_n = 4*n - 2
 *
 * Observe that w_p = ((4*p - 2)/p)*w_{p-1}. Therefore,
 * (R_1 * R_2 ... * R_p)/p! will be w_p
 * Remember the goal is to find w_p (R_p)!/p! (mod p^4). Unfortunately,
 * p! contains a single factor of p which has no inverse modulo p^4.
 *
 * In general to find (n/d) (mod m) where d and m are not coprime, use the following.
 * Suppose q_0*m + r_0 = n/d. For some q_0 and 0 <= r_0 < m.
 * Then multiplying gives q_0*m*d + r_0*d = n
 * If we compute n modulo (m*d), we get q_1*m*d + r_1 = n. Since 0 <= r_0 < m implies 0 <= r_0*d < m*d,
 * then r_1 = r_0*d. The goal is to find r_0, and we do so by computing r_1 and dividing by d.
 *
 * In our case, we should find (R_p)! (mod p^5) and then divide the output by p.
 * Then, reduce modulo p^4 and multiply by the modular inverse of (p-1)!.
 *
 * We will need to do remainder tree twice. The first time, set the multiplicands to be
 * A_0 = 1, and A_n = R_n. Set the moduli to be m_n = n^5 if n is prime, and m_n is 1 otherwise.
 * Then, for each index n in the output, divide the remainder by n, and then reduce modulo (n^4).
 *
 * The second remainder tree, set the multiplicands to be A_0 = A_1 = 1, and A_n = n - 1 and set the
 * moduli to be m_n = n^4 if n is prime, and m_n is 1 otherwise. Then, for each index in the output,
 * set each remainder to be its modular inverse, then multiply by the corresponding index from the first
 * remainder tree. The final vector will contain w_p (mod p^4) for primes p, as desired.
 */

using std::vector;

// Like in the search for Wilson primes, we only need to be able to use integers. See
// wilson.hpp for more explanation on how Elt works and how to wrap types.
using NTL::ZZ;

//The convention here is to generate from lower bound---inclusive to upper---exclusive
vector<Elt<ZZ>> gen_wolstenholme_numerator(long lower, long upper) {
    vector<Elt<ZZ>> output(upper-lower);

    for(long i = lower; i < upper; ++i) {
        if(i <= 1){
            output[i] = Elt<ZZ>(1);
        }
        else {
            //If you worry that the initializer (4*n - 2) in this case will already overflow,
            //Just initialize on i and do arithmetic there.
            output[i-lower] = Elt<ZZ> (4*i - 2);
        }
    }
    return output;
}

vector<Elt<ZZ>> gen_wolstenholme_denominator(long lower, long upper){
    vector<Elt<ZZ>> output(upper-lower);

    for(long i = lower; i < upper; ++i) {
        if(i <= 1){
            output[i] = Elt<ZZ>(1);
        }
        else {
            output[i-lower] = Elt<ZZ> (i - 1);
        }
    }
    return output;
}

vector<Elt<ZZ>> gen_fourth_prime_power(long lower, long upper) {
    vector<Elt<ZZ>> output(upper-lower);

    for(long i = lower; i < upper; i++){
        ZZ n(i);
        if(ProbPrime(n)) { //Technically a sieve is faster & more correct, but shouldn't make a big difference.
                            //This is just an example anyway.
            NTL::power(n, n, 4);
            output[i-lower] = Elt<ZZ>(n);
        }
        else{
            output[i-lower] = Elt<ZZ>(1);
        }
    }
    return output;
}

vector<Elt<ZZ>> gen_fifth_prime_power(long lower, long upper) {
    vector<Elt<ZZ>> output(upper-lower);

    for(long i = lower; i < upper; i++){
        ZZ n(i);
        if(ProbPrime(n)) {
            power(n, n, 5);
            output[i-lower] = Elt<ZZ>(n);
        }
        else {
            output[i-lower] = Elt<ZZ>(1);
        }
    }
    return output;
}

//TODO: combine the above and actually write a search function that zips the outputs and finds XGCD etc.
//TODO: explain how to modify calculate_factorial and compute V


#include "SDL2/SDL.h"
#include <Eigen/Dense>
#include "stdio.h"
#include <iostream>
#include <vector>
#include <cmath>
// SDL_Window *window;

// lldb (print pow correctly): expr -l objective-c -- @import Darwin

class GlobalSettings
{
  public:
  const static int ScreenResolutionX = 640;
  const static int ScreenResolutionY = 480;
};

struct Ray
{
  Eigen::Vector3d origin;
  Eigen::Vector3d direction;
};

class Object;

struct RayHitResult
{
  Eigen::Vector3d hitPosition;
  bool hit;
  Object* hitObject;
};

class Object
{
  public:
  Eigen::Vector3d color;

  virtual RayHitResult raytrace(Ray ray) = 0;
  virtual const Eigen::Vector3d normalAt(const Eigen::Vector3d pos) = 0;
};

class Sphere : public Object
{
  float radii;
  Eigen::Vector3d position;

  public:
  Sphere (double pRadii, Eigen::Vector3d pPosition, Eigen::Vector3d pColor)
  {
    radii = pRadii; // in meters
    position = pPosition;
    color = pColor;
  }

  virtual const Eigen::Vector3d normalAt(const Eigen::Vector3d pPos)
  {
    return pPos - position;
  }

  virtual RayHitResult raytrace(Ray ray)
  {
    // print the closest colision point
    // see equation at https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
    // - (l * ( o - c) ) +- sqrt( (l * ( o - c))^2 - (o-c)^2 + r^2 )
    double l_o_c =
      ray.direction.x() * ( ray.origin.x() - position.x() ) +
      ray.direction.y() * ( ray.origin.y() - position.y() ) +
      ray.direction.z() * ( ray.origin.z() - position.z() );

    double sqrtValue =
      pow( l_o_c, 2)
      - ( pow(ray.origin.x() - position.x(), 2) +
          pow(ray.origin.y() - position.y(), 2) +
          pow(ray.origin.z() - position.z(), 2)
        )
      + pow(radii, 2);

    RayHitResult hitResult;

    if (sqrtValue < 0)
    {
      hitResult.hit = false;

      return hitResult; //  missed the sphere
    }
    else
    {
      double t = - l_o_c - sqrtValue;

      if (t < 0)
      {
        hitResult.hit = false;
        return hitResult;
      }

      // ray equation
      // R(t) = StartPos + Direction * t
      Eigen::Vector3d hitPosition = ray.origin + ray.direction * t;

      hitResult.hit = true;
      hitResult.hitPosition = hitPosition;
      return hitResult;
    }
  }
};

struct Light
{
  Eigen::Vector3d pos;
  Eigen::Vector3d color;
  double intensity = 70;
};

class World
{
  public:
  std::vector<Object*> sceneObjects;
  Light light;

  void spawnObject()
  {
    light.pos = Eigen::Vector3d(0, -2, 0);
    light.color = Eigen::Vector3d(255, 255, 255);

    sceneObjects.push_back(new Sphere(0.5, Eigen::Vector3d(0.5, 0.8, -8), Eigen::Vector3d(100, 100, 0)));
    sceneObjects.push_back(new Sphere(0.5, Eigen::Vector3d(1.9, 0.3, -9.8), Eigen::Vector3d(0, 100, 0)));
    sceneObjects.push_back(new Sphere(0.5, Eigen::Vector3d(0.9, 0.8, -7.5), Eigen::Vector3d(0, 100, 55)));
  }
};

class Camera
{
  public:
  Eigen::Vector3d pos;
  Eigen::Vector3d dir;
  double viewPlaceDist = -0.5;
  double viewPlaneXsize;  // the size of the rendering place, in meters
  double viewPlaneYsize;  // the size of the rendering place, in meters

  Camera(double pScreenWidth, double pScreenHeight)
  {
    viewPlaneYsize = 0.1;
    viewPlaneXsize = (pScreenWidth/pScreenHeight) * viewPlaneYsize;
  }

  Ray RayAtScreenSpace(double x, double y)
  {
    Ray ray;

    ray.origin = Eigen::Vector3d(0, 0, 0.5); // TODO change this to use the camera position
    ray.direction = Eigen::Vector3d(x * viewPlaneXsize, y * viewPlaneYsize, viewPlaceDist);
    ray.direction.normalize();

    return ray;
  }
};

class Renderer
{
  public:

  SDL_Window *window;
  int windowIndex;
  SDL_Renderer* sdl_renderer;

  World* world;

  Renderer(SDL_Window *window, World *pWorld)
  {
    this->window = window;
    world = pWorld;

    windowIndex = -1; // the index of the rendering driver to initialize, or -1 to initialize the first one supporting the requested flags
    int flags = 0;
    sdl_renderer = SDL_CreateRenderer(window, windowIndex, flags);
  }

  void render()
  {
    SDL_SetRenderDrawColor(sdl_renderer, 255, 0, 0, 1); // If something is FULL red on the screen, it means that pixel was not rendered
    SDL_RenderClear(sdl_renderer);
    SDL_RenderPresent( sdl_renderer );

    Camera camera(GlobalSettings::ScreenResolutionX, GlobalSettings::ScreenResolutionY);

    double screenSpaceXRatio = 1.0 / GlobalSettings::ScreenResolutionX;
    double screenSpaceYRatio = 1.0 / GlobalSettings::ScreenResolutionY;

    for (int y = 0; y < GlobalSettings::ScreenResolutionY; ++y)
    {
      for (int x = 0; x < GlobalSettings::ScreenResolutionX; ++x)
      {
        double screenSpaceX = screenSpaceXRatio * x;
        double screenSpaceY = screenSpaceYRatio * y;
        Ray ray = camera.RayAtScreenSpace(screenSpaceX, screenSpaceY);

        RayHitResult hitResult = findClosestHit(world->sceneObjects, ray);

        if (hitResult.hit)
        {
          // calculating pixel color (SHADER!)
          Eigen::Vector3d normal = hitResult.hitObject->normalAt(hitResult.hitPosition);

          Eigen::Vector3d lightNormalToHitPos = world->light.pos - hitResult.hitPosition;
          double distanceFromLight = lightNormalToHitPos.norm();
          double lightAttenuation = (1/ (1 + 0.1 * distanceFromLight + 0.1 * distanceFromLight * distanceFromLight ));

          lightNormalToHitPos.normalize();
          double lightAngle = lightNormalToHitPos.dot(normal);

          if (lightAngle > 0)
          {
            int r = hitResult.hitObject->color.x() * lightAngle * lightAttenuation * world->light.intensity;
            int g = hitResult.hitObject->color.y() * lightAngle * lightAttenuation * world->light.intensity;
            int b = hitResult.hitObject->color.z() * lightAngle * lightAttenuation * world->light.intensity;
            SDL_SetRenderDrawColor(sdl_renderer, r, g, b, 1);
          }
          else
          {
            SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 1);
          }
        }
        else
        {
          SDL_SetRenderDrawColor(sdl_renderer, 50, 50, 50, 1);
        }

        SDL_RenderDrawPoint(sdl_renderer, x, y);
      }
    }

    SDL_RenderPresent( sdl_renderer );
    std::cout << "done" << std::endl;
  }

  RayHitResult findClosestHit(std::vector<Object*> worldObjects, Ray ray)
  {
    RayHitResult closestHitResult;
    closestHitResult.hitPosition = Eigen::Vector3d(9999, 9999, 9999);
    closestHitResult.hit = false;
    closestHitResult.hitObject = nullptr;

    for (int i = 0; i < worldObjects.size(); ++i)
    {
      Object* sceneObject = world->sceneObjects[i];

      RayHitResult hitResult;
      hitResult = sceneObject->raytrace(ray);

      if (hitResult.hit && hitResult.hitPosition.norm() < closestHitResult.hitPosition.norm())
      {
        closestHitResult = hitResult;
        closestHitResult.hitObject = sceneObject;
      }
    }

    return closestHitResult;
  }
};

void waitUntilQuit()
{
  // A basic main loop to prevent blocking
  bool is_running = true;
  SDL_Event event;
  while (is_running) {
      while (SDL_PollEvent(&event)) {
          if (event.type == SDL_QUIT) {
              is_running = false;
          }
      }
      SDL_Delay(16);
  }
}

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

  SDL_Window* window = SDL_CreateWindow(
      "Raytracer",
      SDL_WINDOWPOS_CENTERED,
      SDL_WINDOWPOS_CENTERED,
      GlobalSettings::ScreenResolutionX,
      GlobalSettings::ScreenResolutionY,
      SDL_WINDOW_MAXIMIZED | SDL_WINDOW_SHOWN
  );

  if (window == NULL)
  {
      printf("Could not create window: %s\n", SDL_GetError());
      return 1;
  }

  SDL_Delay(1);

  World world;
  world.spawnObject();

  Renderer render(window, &world);
  render.render();

  waitUntilQuit();

  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}


#include <iostream>
#include <iomanip>
#include <time.h>

#include <boost/multiprecision/cpp_dec_float.hpp>

#include "../include/calcPI.h"

#include <omp.h>

using namespace boost::multiprecision;
//namespace mp = boost::multiprecision; // mp::pow

const long long ACCUR = 100000; // Кол-во знаков после запятой
const size_t DIGNUM = ACCUR+10;

typedef number< cpp_dec_float<DIGNUM> > cpp_dec_float_DIGNUM;

/*
Неэффективная реализация этой формулы:
https://i.imgur.com/dE8klLQ.png
*/
void calcPI()
{
    const cpp_dec_float_DIGNUM ONE = 1;
    const cpp_dec_float_DIGNUM TWO = 2;
    cpp_dec_float_DIGNUM pi;
    cpp_dec_float_DIGNUM buff;
    cpp_dec_float_DIGNUM a;
    cpp_dec_float_DIGNUM b;
    cpp_dec_float_DIGNUM diff;
    cpp_dec_float_DIGNUM arith;
    cpp_dec_float_DIGNUM geom;
    cpp_dec_float_DIGNUM series;

    buff = 10;
    buff = pow(buff, -ACCUR);
    const cpp_dec_float_DIGNUM epsilon = buff;

    a = 1;

    buff = sqrt(TWO);
    b = ONE / buff;

    diff = a - b;
    series = 0;

    size_t n = 0;
    while(diff > epsilon)
    {
        ++n;
        arith = (a + b) / TWO;
        geom = sqrt(a*b);

        a = arith;
        b = geom;

        buff = pow(TWO, n+1);
        series += buff * (a*a - b*b);

        diff = a - b;
    }

    buff = 4;
    pi = (buff*a*a) / (ONE - series);

    //std::cout << "n = " << n << std::endl;
    //std::cout << std::setprecision(std::numeric_limits<number< cpp_dec_float<ACCUR> >>::max_digits10) << "pi = " << pi << std::endl;
}

/*
Неточное вычисление числа pi, с помощью этой формулы:
https://i.imgur.com/gAMAToc.png
*/
void calcPI_mul(const unsigned threadNum)
{
    const size_t N = 100000000;
    const size_t blocksize = 50000;
    const unsigned REP_NUM = 50;

    for(unsigned gi = 0; gi < REP_NUM; ++gi)
    {

        long double sum = 0;

        #pragma omp parallel reduction (+: sum) num_threads(threadNum)
        {
            #pragma omp for schedule(dynamic, blocksize) nowait
            for (size_t i = 0; i < N; ++i)
            {
                long double xi;
                xi = (i + 0.5); xi /= N;
                sum += (long double)4 / (1 + xi*xi);
            }
        }
        sum /= N;
        //std::cout << std::setprecision(80) << sum << std::endl;
    }
}


#include <iostream>
#include <boost/multiprecision/float128.hpp>

using namespace std;
using namespace boost::multiprecision;

class Fibonacci{
    float128 phi;
    float128 a;
    // Source : http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
    // Based on the source, we reduce the formula to on phi as 'V' contribute neglagible and its distance from zero keeps on increasing with sequence.

    float128 even_sum(int num){

        // Calculate the Gemotric Sum i.e. Sum = a * ((1+r**n)/(1-r))
        // n = (1 + r **n )
        float128 n = 1 - pow(phi,3*num);
        // d = (1 - r)
        float128 d = 1 - pow(phi,3);
        // Gm of even number = a * (n/d)
        float128 sum = a * (n/d);

        return sum;
    }

public:

    Fibonacci(){
        phi = (1+sqrt(5))/2;
        a = pow(phi,3)/sqrt(5);
    }

    unsigned long long int get_even_sum(int limit){
        int totalSum;
        if(limit <= 2){
            return 0;
        }
        totalSum = limit / 3;

        return static_cast<unsigned long long int>(even_sum(totalSum)+1);
    }
};

int main()
{

    Fibonacci obj;
    int m_input;
    cout<<"Enter the Fibonacci Range : ";
    cin>>m_input;
    cout<<obj.get_even_sum(m_input);
    return 0;
}


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

// To disable assert*() calls, uncomment this line:
// #define NDEBUG

// This represents a triplet of indices as a column vector of nonnegative
// integers:
// [ i ]
// [ j ]
// [ k ]
typedef Eigen::Matrix<std::size_t, 3, 1> GridIndices;
//
// Returns the indices of the grid cell containing the point |p| for a grid with
// lower corner |lc| and grid cell width (spacing) |dx|.
//
// If |dx| <= 0 or |p|'s location relative to |lc| would result in negative
// indices being returned and assertions are on, then assertion failures will
// crash this program.
inline GridIndices floor(const Eigen::Vector3d& p, const Eigen::Vector3d& lc,
                         double dx) {
  // Ensure grid spacings are positive.
  assert(dx > 0.0);

  // Compute |p|'s location relative to |lc|.
  // Dividing by |dx| yields a 3D vector indicating the number of grid
  // cells (including fractions of grid cells, as the vector elements are
  // floating-point values) away from |lc| that |p| is located.
  Eigen::Vector3d p_lc_over_dx = (p - lc) / dx;

  // Ensure we won't end up with negative indices.
  assert(p_lc_over_dx[0] >= 0.0);
  assert(p_lc_over_dx[1] >= 0.0);
  assert(p_lc_over_dx[2] >= 0.0);

  // Indices are valid. Construct and return them.
  // This casts the elements of the vector above as nonnegative integers.
  return p_lc_over_dx.cast<std::size_t>();
}

void print(const GridIndices& indices) {
  std::cout << "Indices: " << std::endl;
  std::cout << indices << std::endl;
}

inline Eigen::Vector3d weights(const Eigen::Vector3d& p,
                               const Eigen::Vector3d& lc, double dx,
                               const GridIndices& indices) {
  return (p - lc) / dx - indices.cast<double>();
}

void PrintPointIndicesAndWeights(const Eigen::Vector3d& p,
                                 const Eigen::Vector3d& lc, double dx) {
  GridIndices p_indices = floor(p, lc, dx);
  print(p_indices);
  Eigen::Vector3d w = weights(p, lc, dx, p_indices);
  std::cout << "Weights = " << std::endl;
  std::cout << w << std::endl << std::endl;
}

int main(int argc, char** argv) {
  // Let's have our grid's lower corner be at (3, 4, 5) with a grid spacing of
  // 2. So, each grid cell will be a 2 x 2 x 2 cube.
  Eigen::Vector3d lc(3, 4, 5);
  double dx = 2.0;

  // Expect (i, j, k) == (0, 3, 4), (w0, w1, w2) == (0.5, 0, 0.5)
  Eigen::Vector3d p1(4, 10, 14);
  PrintPointIndicesAndWeights(p1, lc, dx);

  // Expect (i, j, k) == (3, 4, 16), (w0, w1, w2) == (0.5, 0.5, 0)
  Eigen::Vector3d p2(10, 13, 37);
  PrintPointIndicesAndWeights(p2, lc, dx);

  // Expect (i, j, k) == (0, 0, 0), (w0, w1, w2) == (0, 0, 0)
  Eigen::Vector3d p3(3, 4, 5);
  PrintPointIndicesAndWeights(p3, lc, dx);
  return 0;
}


//
//  GivensQR.hpp
//  IPC
//
//  Created by Minchen Li on 10/19/19.
//

#include "Types.hpp"

#include <Eigen/Eigen>

namespace IPC {

template <int dim>
class GivensQR {
public:
    static void compute(const Eigen::Matrix<double, dim, dim>& A,
        Eigen::Matrix<double, dim, dim>& Q,
        Eigen::Matrix<double, dim, dim>& R)
    {
        R = A;
        Q.setIdentity();
        for (int j = 0; j < dim; ++j) {
            for (int i = dim - 1; i > j; --i) {
                double a = R(i - 1, j), b = R(i, j);
                double d = a * a + b * b;
                double c = 1, s = 0;
                double sqrtd = std::sqrt(d);
                if (sqrtd) {
                    double t = 1.0 / sqrtd;
                    c = a * t;
                    s = -b * t;
                }

                for (int k = 0; k < dim; ++k) {
                    double tau1 = R(i - 1, k);
                    double tau2 = R(i, k);
                    R(i - 1, k) = c * tau1 - s * tau2;
                    R(i, k) = s * tau1 + c * tau2;
                }

                for (int k = 0; k < dim; ++k) {
                    double tau1 = Q(i - 1, k);
                    double tau2 = Q(i, k);
                    Q(i - 1, k) = c * tau1 - s * tau2;
                    Q(i, k) = s * tau1 + c * tau2;
                }
            }
        }
        Q.transposeInPlace();
    }
};

} // namespace IPC


/**
 * \copyright
 * Copyright (c) 2012-2019, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

#include <gtest/gtest.h>

#include <vector>
#include <numeric>

#include <Eigen/Sparse>

/**
 * This test case checks if the internal Eigen::SparseMatrix compressed storage format
 * is a conventional CSR matrix. Currently this is the case, but it is not guaranteed
 * for all time.
 *
 * Cf. section "Sparse matrix format" on page
 * http://eigen.tuxfamily.org/dox/group__TutorialSparse.html
 */
TEST(MathLibEigen, Eigen2CSR)
{
    const int nrows = 16;
    const int ncols = nrows;

    Eigen::SparseMatrix<double, Eigen::RowMajor> mat(nrows, ncols);

    // set up sparsity pattern
    std::vector<int> pat(nrows);
    for (std::size_t i=0; i<nrows; ++i)
    {
        if (i==0 || i==nrows-1) {
            pat[i] = 2;
        } else {
            pat[i] = 3;
        }
    }

    // CSR representation of the matrix
    std::vector<double> values;
    std::vector<int> ia(nrows+1); // row offsets
    std::vector<int> ja;          // column indices

    std::partial_sum(pat.begin(), pat.end(), ia.begin()+1);

    const int nnz = ia.back();
    values.reserve(nnz);
    ja.reserve(nnz);

    mat.reserve(pat);

    // init matrix, build CSR matrix in parallel
    for (int row=0; row<nrows; ++row) {
        for (int col = -1; col<=1; ++col) {
            int cidx = row + col;
            if (cidx < 0 || cidx >= ncols) continue;

            const double val = (col == 0) ? 2.0 : -1.0;
            values.push_back(val);
            mat.coeffRef(row, cidx) = val;
            ja.push_back(cidx);
        }
    }

    // change matrix
    for (int row=0; row<nrows; ++row) {
        for (int col = -1; col<=1; ++col) {
            int cidx = row + col;
            if (cidx < 0 || cidx >= ncols) continue;

            mat.coeffRef(row, cidx) = 4.0 * mat.coeff(row, cidx);
        }
    }
    // adapt entries of CSR matrix
    for (auto& v : values) v *= 4.0;

    mat.makeCompressed();

    ASSERT_EQ(nnz, mat.nonZeros());
    ASSERT_EQ(nnz, values.size());
    ASSERT_EQ(nnz, ja.size());

    int* ptr = mat.outerIndexPtr();
    int* col = mat.innerIndexPtr();
    double* data = mat.valuePtr();

    for (int r=0; r<(int) nrows; ++r) {
        EXPECT_EQ(ia[r], ptr[r]);
    }

    for (int i=0; i<nnz; ++i) {
        EXPECT_EQ(values[i], data[i]);
        EXPECT_EQ(ja[i], col[i]);
    }
}


#include <catch.hpp>
#include <random>
#include <vector>
#include <Eigen/Core>
#include <torch/torch.h>

#include <renderer_utils.cuh>
#include <losses.h>
#include "check_adjoint.h"
#include "test_utils.h"

//namespace std {
//	template < class T >
//	inline std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
//	{
//		os << "[";
//		for (typename std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii)
//		{
//			os << " " << *ii;
//		}
//		os << " ]";
//		return os;
//	}
//}

TEST_CASE("LogSumExp-double", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int N=1; N<=10; ++N)
	{
		DYNAMIC_SECTION("N=" << N)
		{
			std::vector<double> values(N);
			for (int i = 0; i < N; ++i) values[i] = distr(rnd);

			const auto valuesLambda = [values](int i) {return values[i]; };
			double resultActual = kernel::logSumExp<double>(N, valuesLambda);

			//naive
			double resultExpected = 0;
			for (int i = 0; i < N; ++i) resultExpected += rexp(values[i]);
			resultExpected = rlog(resultExpected);

			REQUIRE(resultActual == Approx(resultExpected));
		}
	}
}
TEST_CASE("LogSumExp-double4", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int N = 1; N <= 10; ++N)
	{
		DYNAMIC_SECTION("N=" << N)
		{
			std::vector<double4> values(N);
			for (int i = 0; i < N; ++i) {
				values[i] = make_double4(
					distr(rnd), distr(rnd),
					distr(rnd), distr(rnd));
			}

			const auto valuesLambda = [values](int i) {return values[i]; };
			double4 resultActual = kernel::logSumExp<double4>(N, valuesLambda);

			//naive
			double4 resultExpected = make_double4(0);
			for (int i = 0; i < N; ++i) resultExpected += rexp(values[i]);
			resultExpected = rlog(resultExpected);

			REQUIRE(resultActual.x == Approx(resultExpected.x));
			REQUIRE(resultActual.y == Approx(resultExpected.y));
			REQUIRE(resultActual.z == Approx(resultExpected.z));
			REQUIRE(resultActual.w == Approx(resultExpected.w));
		}
	}
}

TEST_CASE("LogSumExp-scaling-double", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int N = 1; N <= 10; ++N)
	{
		DYNAMIC_SECTION("N=" << N)
		{
			std::vector<double> values(N);
			std::vector<double> scaling(N);
			for (int i = 0; i < N; ++i) {
				values[i] = distr(rnd);
				scaling[i] = fabs(distr(rnd));
			}
			INFO("values: " << values);
			INFO("scaling: " << scaling);

			const auto valuesLambda = [values](int i) {return values[i]; };
			const auto scalingLambda = [scaling](int i) {return scaling[i]; };
			double resultActual = kernel::logSumExpWithScaling<double>(
				N, valuesLambda, scalingLambda);

			//naive
			double resultExpected = 0;
			for (int i = 0; i < N; ++i)
				resultExpected += scaling[i] * rexp(values[i]);
			resultExpected = rlog(resultExpected);

			REQUIRE(resultActual == Approx(resultExpected));
		}
	}
}
TEST_CASE("LogSumExp-scaling-double4", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int N = 1; N <= 10; ++N)
	{
		DYNAMIC_SECTION("N=" << N)
		{
			std::vector<double4> values(N);
			std::vector<double> scaling(N);
			for (int i = 0; i < N; ++i) {
				values[i] = make_double4(
					distr(rnd), distr(rnd),
					distr(rnd), distr(rnd));
				scaling[i] = fabs(distr(rnd));
			}

			const auto valuesLambda = [values](int i) {return values[i]; };
			const auto scalingLambda = [scaling](int i) {return scaling[i]; };
			double4 resultActual = kernel::logSumExpWithScaling<double4>(
				N, valuesLambda, scalingLambda);

			//naive
			double4 resultExpected = make_double4(0);
			for (int i = 0; i < N; ++i)
				resultExpected += scaling[i] * rexp(values[i]);
			resultExpected = rlog(resultExpected);

			REQUIRE(resultActual.x == Approx(resultExpected.x));
			REQUIRE(resultActual.y == Approx(resultExpected.y));
			REQUIRE(resultActual.z == Approx(resultExpected.z));
			REQUIRE(resultActual.w == Approx(resultExpected.w));
		}
	}
}

TEST_CASE("LogMSE-double", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int i = 1; i <= 50; ++i)
	{
		INFO("i: " << i);
		double logX = distr(rnd);
		double logY = distr(rnd);
		double x = exp(logX), y = exp(logY);
		INFO("x=" << x << ", y=" << y);

		double mseExpected = (x - y) * (x - y);

		double mseActualLog = kernel::logMSE(logX, logY);
		double mseActual = exp(mseActualLog);

		REQUIRE(mseActual == Approx(mseExpected));
	}
}

TEST_CASE("LogL1-double", "[math]")
{
	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);

	for (int i = 1; i <= 50; ++i)
	{
		INFO("i: " << i);
		double logX = distr(rnd);
		double logY = distr(rnd);
		double x = exp(logX), y = exp(logY);
		INFO("x=" << x << ", y=" << y);

		double l1Expected = fabs(x - y);

		double l1ActualLog = kernel::logL1(logX, logY);
		double l1Actual = exp(l1ActualLog);

		REQUIRE(l1Actual == Approx(l1Expected));
	}
}

TEST_CASE("Adjoint-LogMSE", "[adjoint]")
{
	typedef empty TmpStorage_t;
	typedef Eigen::VectorXd Vector_t;

	auto forward = [](const Vector_t& x, TmpStorage_t* tmp) -> Vector_t
	{
		double logX = x[0], logY = x[1];
		double res = kernel::logMSE(logX, logY);
		Vector_t rese(1);
		rese[0] = res;
		return rese;
	};
	auto adjoint = [](const Vector_t& x, const Vector_t& e, const Vector_t& g,
		Vector_t& z, const TmpStorage_t& tmp)
	{
		double logX = x[0], logY = x[1];
		double adjOut = g[0];
		double adjLogX, adjLogY;
		kernel::adjLogMSE(logX, logY, adjOut, adjLogX, adjLogY);
		z[0] = adjLogX;
		z[1] = adjLogY;
	};

	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);
	int N = 20;
	for (int i = 0; i < N; ++i)
	{
		INFO("N=" << i);
		Vector_t x(2);
		for (int j = 0; j < 2; ++j) x[j] = distr(rnd);

		checkAdjoint<Vector_t, TmpStorage_t>(x, forward, adjoint,
			1e-5, 1e-5, 1e-6);
	}
}

TEST_CASE("Adjoint-Log1", "[adjoint]")
{
	typedef empty TmpStorage_t;
	typedef Eigen::VectorXd Vector_t;

	auto forward = [](const Vector_t& x, TmpStorage_t* tmp) -> Vector_t
	{
		double logX = x[0], logY = x[1];
		double res = kernel::logL1(logX, logY);
		Vector_t rese(1);
		rese[0] = res;
		return rese;
	};
	auto adjoint = [](const Vector_t& x, const Vector_t& e, const Vector_t& g,
		Vector_t& z, const TmpStorage_t& tmp)
	{
		double logX = x[0], logY = x[1];
		double adjOut = g[0];
		double adjLogX, adjLogY;
		kernel::adjLogL1(logX, logY, adjOut, adjLogX, adjLogY);
		z[0] = adjLogX;
		z[1] = adjLogY;
	};

	std::default_random_engine rnd(42);
	std::uniform_real_distribution<double> distr(-5, +5);
	int N = 20;
	for (int i = 0; i < N; ++i)
	{
		INFO("N=" << i);
		Vector_t x(2);
		for (int j = 0; j < 2; ++j) x[j] = distr(rnd);

		checkAdjoint<Vector_t, TmpStorage_t>(x, forward, adjoint,
			1e-5, 1e-5, 1e-6);
	}
}

TEST_CASE("Adjoint-LogMSE-Full", "[adjoint]")
{
	torch::Tensor logX = torch::randn({ 4,5 },
		at::TensorOptions().dtype(c10::kDouble).device(c10::kCUDA))
		.requires_grad_(true);
	torch::Tensor logY = torch::randn({ 4,5 },
		at::TensorOptions().dtype(c10::kDouble).device(c10::kCUDA))
		.requires_grad_(true);

	torch::Tensor out = renderer::logMSE(logX, logY);
	torch::Tensor grad_out = torch::rand_like(out);
	auto grad_inputs = torch::autograd::grad(
		{ out }, { logX, logY }, { grad_out });
	torch::Tensor grad_logX = grad_inputs[0];
	torch::Tensor grad_logY = grad_inputs[1];

	std::cout << "grad_logX:\n" << grad_logX << std::endl;
}


/***************************************************************************
 * tiny.cpp    Blitz++ TinyVector<T,N> ray reflection example
 *
 * This example illustrates the TinyVector<T,N> class.  TinyVectors can be
 * used for small vectors whose sizes are known at compile time.  Most
 * operations on TinyVectors have their loops unravelled inline using template
 * metaprograms.
 *
 * The routine reflect(..) calculates the reflection of a monochrome ray
 * of light bouncing off a perfectly reflective, smooth surface.
 ****************************************************************************/

#include <blitz/array.h>
#include <blitz/tinyvec2.h>

BZ_USING_NAMESPACE(blitz)

void reflect(TinyVector<double,3>& reflection, const TinyVector<double,3>& ray,
    const TinyVector<double,3>& surfaceNormal)
{
    // The surface normal must be unit length to use this equation.

    reflection = ray - 2 * dot(ray,surfaceNormal) * surfaceNormal;
}

int main()
{
    TinyVector<double,3> x, y, z;

    // y will be the incident ray
    y[0] = 1;
    y[1] = 0;
    y[2] = -1;

    // z is the surface normal
    z[0] = 0;
    z[1] = 0;
    z[2] = 1;

    reflect(x, y, z);

    cout << "Reflected ray is: [ " << x[0] << " " << x[1] << " " << x[2]
         << " ]" << endl;
}

// Here's the assembly generated for reflect() using KCC +K3 -O2 on an
// IBM RS/6000:

// .reflect__(mangled-name)
//        lfd     fp0,16(r4)
//        lfd     fp1,16(r5)
//        lfd     fp3,8(r4)
//        fm      fp1,fp0,fp1
//        lfd     fp0,8(r5)
//        lfd     fp2,0(r4)
//        fma     fp0,fp3,fp0,fp1
//        lfd     fp1,0(r5)
//        fma     fp0,fp2,fp1,fp0
//        fa      fp0,fp0,fp0
//        fnms    fp1,fp1,fp0,fp2
//        stfd    fp1,0(r3)
//        lfd     fp1,8(r4)
//        lfd     fp2,8(r5)
//        fnms    fp1,fp0,fp2,fp1
//        stfd    fp1,8(r3)
//        lfd     fp1,16(r4)
//        lfd     fp2,16(r5)
//        fnms    fp0,fp0,fp2,fp1
//        stfd    fp0,16(r3)
//        bcr     BO_ALWAYS,CR0_LT


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

#include <boost/math/tools/fraction.hpp>
#include <iostream>
#include <complex>

//[golden_ratio_1
template <class T>
struct golden_ratio_fraction
{
   typedef T result_type;

   result_type operator()()
   {
      return 1;
   }
};
//]

//[cf_tan_fraction
template <class T>
struct tan_fraction
{
private:
   T a, b;
public:
   tan_fraction(T v)
      : a(-v * v), b(-1)
   {}

   typedef std::pair<T, T> result_type;

   std::pair<T, T> operator()()
   {
      b += 2;
      return std::make_pair(a, b);
   }
};
//]
//[cf_tan
template <class T>
T tan(T a)
{
   tan_fraction<T> fract(a);
   return a / continued_fraction_b(fract, std::numeric_limits<T>::epsilon());
}
//]
//[cf_expint_fraction
template <class T>
struct expint_fraction
{
   typedef std::pair<T, T> result_type;
   expint_fraction(unsigned n_, T z_) : b(z_ + T(n_)), i(-1), n(n_) {}
   std::pair<T, T> operator()()
   {
      std::pair<T, T> result = std::make_pair(-static_cast<T>((i + 1) * (n + i)), b);
      b += 2;
      ++i;
      return result;
   }
private:
   T b;
   int i;
   unsigned n;
};
//]
//[cf_expint
template <class T>
inline std::complex<T> expint_as_fraction(unsigned n, std::complex<T> const& z)
{
   boost::uintmax_t max_iter = 1000;
   expint_fraction<std::complex<T> > f(n, z);
   std::complex<T> result = boost::math::tools::continued_fraction_b(
      f,
      std::complex<T>(std::numeric_limits<T>::epsilon()),
      max_iter);
   result = exp(-z) / result;
   return result;
}
//]
//[cf_upper_gamma_fraction
template <class T>
struct upper_incomplete_gamma_fract
{
private:
   typedef typename T::value_type scalar_type;
   T z, a;
   int k;
public:
   typedef std::pair<T, T> result_type;

   upper_incomplete_gamma_fract(T a1, T z1)
      : z(z1 - a1 + scalar_type(1)), a(a1), k(0)
   {
   }

   result_type operator()()
   {
      ++k;
      z += scalar_type(2);
      return result_type(scalar_type(k) * (a - scalar_type(k)), z);
   }
};
//]
//[cf_gamma_Q
template <class T>
inline std::complex<T> gamma_Q_as_fraction(const std::complex<T>& a, const std::complex<T>& z)
{
   upper_incomplete_gamma_fract<std::complex<T> > f(a, z);
   std::complex<T> eps(std::numeric_limits<T>::epsilon());
   return pow(z, a) / (exp(z) *(z - a + T(1) + boost::math::tools::continued_fraction_a(f, eps)));
}
//]

int main()
{
   using namespace boost::math::tools;

   //[cf_gr
   golden_ratio_fraction<double> func;
   double gr = continued_fraction_a(
      func,
      std::numeric_limits<double>::epsilon());
   std::cout << "The golden ratio is: " << gr << std::endl;
   //]

   std::cout << tan(0.5) << std::endl;

   std::complex<double> arg(3, 2);
   std::cout << expint_as_fraction(5, arg) << std::endl;

   std::complex<double> a(3, 3), z(3, 2);
   std::cout << gamma_Q_as_fraction(a, z) << std::endl;

   return 0;
}


/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Union’s Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/

#pragma once

#include "AlgorithmUtils.hpp"
#include <Eigen/Core>
#include <cassert>
#include <cmath>
#include <map>

namespace fluid {
namespace algorithm {

class DistanceFuncs
{

public:
  enum class Distance {
    kManhattan,
    kEuclidean,
    kSqEuclidean,
    kMax,
    kMin,
    kKL,
    kCosine
  };

  using ArrayXcd = Eigen::ArrayXcd;
  using ArrayXd = Eigen::ArrayXd;
  using MatrixXd = Eigen::MatrixXd;
  using DistanceFuncsMap =
      std::map<Distance, std::function<double(ArrayXd, ArrayXd)>>;

  static DistanceFuncsMap& map()
  {
    static DistanceFuncsMap _funcs = {
        {Distance::kManhattan,
         [](ArrayXd x, ArrayXd y) { return (x - y).abs().sum(); }},
        {Distance::kEuclidean,
         [](ArrayXd x, ArrayXd y) {
           return std::sqrt((x - y).square().sum());
         }},
        {Distance::kSqEuclidean,
         [](ArrayXd x, ArrayXd y) { return (x - y).square().sum(); }},
        {Distance::kMax,
         [](ArrayXd x, ArrayXd y) { return (x - y).abs().maxCoeff(); }},
        {Distance::kMin,
         [](ArrayXd x, ArrayXd y) { return (x - y).abs().minCoeff(); }},
        {Distance::kKL,
         [](ArrayXd x, ArrayXd y) {
           auto   logX = x.max(epsilon).log(), logY = y.max(epsilon).log();
           double d1 = (x * (logX - logY)).sum();
           double d2 = (y * (logY - logX)).sum();
           return d1 + d2;
         }},
        {Distance::kCosine, [](ArrayXd x, ArrayXd y) {
           double norm = x.matrix().norm() * y.matrix().norm();
           double dot = x.matrix().dot(y.matrix());
           return dot / norm;
         }}};
    return _funcs;
  }
};

Eigen::MatrixXd DistanceMatrix(Eigen::Ref<Eigen::MatrixXd> X, index distance)
{
  auto            dist = static_cast<DistanceFuncs::Distance>(distance);
  Eigen::MatrixXd D = Eigen::MatrixXd::Zero(X.rows(), X.rows());
  for (index i = 0; i < X.rows(); i++)
  {
    for (index j = 0; j < X.rows(); j++)
    {
      D(i, j) = DistanceFuncs::map()[dist](X.row(i).array(), X.row(j).array());
    }
  }
  return D;
}

template <typename Derived>
Eigen::MatrixXd DistanceMatrix(const Eigen::PlainObjectBase<Derived>& X,
                               const Eigen::PlainObjectBase<Derived>& Y,
                               index                                  distance)
{
  auto            dist = static_cast<DistanceFuncs::Distance>(distance);
  Eigen::MatrixXd D = Eigen::MatrixXd::Zero(X.rows(), Y.rows());
  for (index i = 0; i < X.rows(); i++)
  {
    for (index j = 0; j < Y.rows(); j++)
    {
      D(i, j) = DistanceFuncs::map()[dist](X.row(i).array(), Y.row(j).array());
    }
  }
  return D;
}

} // namespace algorithm
} // namespace fluid