//
//  EuropeanOption.cpp
//  VI.3 Option Pricing
//
//  Created by Zhehao Li on 2020/4/27.
//  Copyright © 2020 Zhehao Li. All rights reserved.
//

#include "EuropeanOption.hpp"
#include <boost/math/distributions/normal.hpp>

/* Private Memeber Functions */
// Call price
double EuropeanOption::CallPrice(double S) const{                   // S is the price of underlying assets
    double tmp = sig * sqrt(T);
    double d1 = ( log(S / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;
    double d2 = d1 - tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return ( S * exp((b - r) * T) * cdf(stdNormal, d1) ) - ( K * exp(- r * T) * cdf(stdNormal, d2) );
}

// Put price
double EuropeanOption::PutPrice(double S) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(S / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;
    double d2 = d1 - tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return ( K * exp(- r * T) * cdf(stdNormal, -d2) ) - ( S * exp((b - r) * T) * cdf(stdNormal, -d1) );
}

// Call delta
double EuropeanOption::CallDelta(double S) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(S / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return exp((b - r) * T) * cdf(stdNormal, d1);
}

// Put delta
double EuropeanOption::PutDelta(double S) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(S / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return exp((b - r) * T) * ( cdf(stdNormal, d1) - 1.0 );
}

// Gamma
double EuropeanOption::CallPutGamma(double S) const{
    double tmp = S * sig * sqrt(T);
    double d1 = ( log(S / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);

    return exp((b - r) * T) * pdf(stdNormal, d1) / tmp;
}

/* Public Memeber Functions */
// Default constructor
EuropeanOption::EuropeanOption(): K(110.0), T(0.5), r(0.05), sig(0.2), b(0.05) {
    //
}

// Copy constructor
EuropeanOption::EuropeanOption(const EuropeanOption & option2) : K(option2.K), T(option2.T), r(option2.r), b(option2.b), sig(option2.sig) {
    //
}

// Destructor
EuropeanOption::~EuropeanOption() {}

// Assignment Operators
EuropeanOption & EuropeanOption::operator = (const EuropeanOption & option2) {
    if (this == &option2) {
        return *this;
    }
    else{
        Option::operator=(option2);
        K   = option2.K;
        T   = option2.T;
        r   = option2.r;
        b   = option2.b;
        sig = option2.sig;

        return *this;
    }
}

// Accessing functions
// Get the price of sensitvit of option
double EuropeanOption::getValue(optionOutput output, double S) const{

    switch (output) {
        case value:
            return Value(S);
        case delta:
            return Delta(S);
        case gama:
            return Gamma(S);
        default:
            return 0;
    }
}

// Compute the price of option
double EuropeanOption::Value(double S) const{
    if (optType == "C") {
        return CallPrice(S);
    }
    else{
        return PutPrice(S);
    }
}

// Compute the delta of option
double EuropeanOption::Delta(double S) const{
    if (optType == "C") {
        return CallDelta(S);
    }
    else{
        return PutDelta(S);
    }
}

// Compute the gamma of option
double EuropeanOption::Gamma(double S) const{
    return CallPutGamma(S);
}

// Put-Call Parity
double EuropeanOption::PutCallParity(double S){

    if (optType == "C") {
        double P = CallPrice(S) + K * exp(- r * T) - S;
        return P;
    }
    else{
        double C = PutPrice(S) + S - K * exp(- r * T);
        return C;
    }
}

// Check if Put-Call Party holds
bool EuropeanOption::PutCallParity(double CP, double S){

    double P = PutCallParity(S);

    if (fabs(P - CP) < 1e-4) {
        return true;
    }
    else{
        return false;
    }
}

// Modifier functions
// Change the type of option
void EuropeanOption::toggle(){

    if (optType == "C") {
        optType = "P";
    }
    else{
        optType = "C";
    }
}

void EuropeanOption::setValue(double val, optionInput input){

    switch (input) {
        case strike:
            K = val;
            break;
        case maturity:
            T = val;
            break;
        case rate:
            r = val;
            break;
        case cost:
            b = val;
            break;
        case volatility:
            sig = val;
            break;
        default:
            break;
    }
}


#include "angular_gauss.hpp"

#include <array>
#include <cassert>
#include <cmath>

#include <boost/math/special_functions/erf.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/matrix.hpp>

#include "types/types.hpp"

namespace ublas = boost::numeric::ublas;

void AngularGauss::InvMatrix(const ublas::matrix<double>& input_mat, ublas::matrix<double>& inverse_mat) {
    ublas::matrix<double> A(input_mat);

    ublas::permutation_matrix<std::size_t> pm(A.size1());
    int factorization_status = ublas::lu_factorize(A, pm);
    assert(factorization_status == 0);

    inverse_mat.assign(ublas::identity_matrix<double>(A.size1()));
    lu_substitute(A, pm, inverse_mat);
}

double AngularGauss::Det(const ublas::matrix<double>& m) const {
    assert(m.size1() == 3 && m.size2() == 3);

    const double a = m(0, 0);
    const double b = m(0, 1);
    const double c = m(0, 2);
    const double d = m(1, 0);
    const double e = m(1, 1);
    const double f = m(1, 2);
    const double g = m(2, 0);
    const double h = m(2, 1);
    const double k = m(2, 2);

    const double determinant =
        (a * ((e * k) - (f * h))) - (b * ((k * d) - (f * g))) + (c * ((d * h) - (e * g)));

    return determinant;
}

double AngularGauss::InnerProduct(const Vector& x, const Vector& y) const {
    double result = 0;
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            result += lambda(i, j) * x.at(i) * y.at(j);

    return result;
}

AngularGauss::AngularGauss(const Vector& mean_vec, const ublas::matrix<double>& cov_mat) {
    assert(cov_mat.size1() == 3 && cov_mat.size2() == 3);

    std::copy(mean_vec.begin(), mean_vec.end(), mean.begin());
    lambda = ublas::matrix<double>(3,3);
    InvMatrix(cov_mat, lambda);
    det_lambda = Det(lambda);
    mean_norm  = std::sqrt(InnerProduct(mean, mean));
}

double AngularGauss::Calc(const CoordsOfPoint& u) const {
    double u_norm = std::sqrt(InnerProduct(u, u));
    double z      = InnerProduct(mean, u) / u_norm;

    double coeff = std::exp(-0.5 * mean_norm * mean_norm) * std::sqrt(det_lambda) /
                   (4 * M_PI * u_norm * u_norm * u_norm);

    return coeff * (z * std::sqrt(M_2_PI) +
                    std::exp(0.5 * z * z) * (1 + z * z) * (1 + boost::math::erf(z / M_SQRT2)));
}

const std::array<double, 3> AngularGauss::Mean(void) const {
    return std::array<double, 3>{mean};
}


#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <boost/timer.hpp>
#include <cassert>

// O(NlogN)
int kthMin1(std::vector<int>& data, int k) {
    std::sort(data.begin(), data.end());

    return data[k - 1];
}

// O(NlogN)
int kthMin2(const std::vector<int>& data, int k) {
    std::priority_queue<int> pq;
    for (const auto& num : data) {
        pq.push(num);
        if (pq.size() > k) {
            pq.pop();
        }
    }

    return pq.top();
}

// O(N + klogN)
int kthMin3(std::vector<int>& data, int k) {
    std::make_heap(data.begin(), data.end(), std::greater<>());

    for (int i = k - 1; i > 0; i--) {
        std::pop_heap(data.begin(), data.end(), std::greater<>());
        data.pop_back();
    }

    return data.front();
}

int partition(std::vector<int>& data, int low, int high) {
    int i = low;
    int j = high + 1;
    int pivot = data[low];
    while (true) {
        while (data[++i] < pivot) {
            if (i == high) break;
        }

        while (data[--j] > pivot) {
            if (j == low) break;
        }

        if (i >= j) break;

        std::swap(data[i], data[j]);
    }

    std::swap(data[low], data[j]);

    return j;
}

// O(N) average case
int kthMin4(std::vector<int>& data, int k) {
    k--;
    int low = 0;
    int high = data.size() - 1;
    while (high > low) {
        int middle = partition(data, low, high);
        if (middle == k) return data[k];
        else if (middle > k) high = middle - 1;
        else low = middle + 1;
    }

    return data[low];
}

std::vector<std::vector<int>> createVectors(int row, int col) {
    static std::uniform_int_distribution<int> distribution(
            std::numeric_limits<int>::min(),
            std::numeric_limits<int>::max()
    );
    static std::default_random_engine generator;

    std::vector<int> data(col);
    std::generate(data.begin(), data.end(),
                  []() { return distribution(generator); });

    return {row, data};
}

int main() {
    const int NUMS_LENGTH = 100;
    auto test_cases = createVectors(4, NUMS_LENGTH);

    std::random_device device;
    std::default_random_engine engine(device());
    std::uniform_int_distribution<int> dist(1, 100);

    const int K = dist(engine);

    boost::timer t1;
    int k1 = kthMin1(test_cases[0], K);
    std::cout << t1.elapsed() * 1000 << std::endl;

    boost::timer t2;
    int k2 = kthMin2(test_cases[1], K);
    std::cout << t2.elapsed() * 1000 << std::endl;

    boost::timer t3;
    int k3 = kthMin3(test_cases[2], K);
    std::cout << t3.elapsed() * 1000 << std::endl;

    boost::timer t4;
    int k4 = kthMin4(test_cases[3], K);
    std::cout << t4.elapsed() * 1000 << std::endl;

    assert(k1 == k2 && k2 == k3 && k3 == k4);

    return 0;
}


/***********************************************************************
This file is part of the librjmcmc project source files.

Copyright : Institut Geographique National (2008-2012)
Contributors : Mathieu Brédif, Olivier Tournaire, Didier Boldo
email : librjmcmc@ign.fr

This software is a generic C++ library for stochastic optimization.

This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited liability.

In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

***********************************************************************/

#ifndef __GAMMA_DISTRIBUTION_HPP__
#define __GAMMA_DISTRIBUTION_HPP__

#include <boost/random/gamma_distribution.hpp>
#include <boost/math/distributions/gamma.hpp>

namespace rjmcmc {

    // beta^alpha * x^(alpha-1) * exp(-beta * x) / Gamma(alpha)
    class gamma_distribution {
    public:
        typedef double real_type;
        typedef boost::gamma_distribution<real_type>          rand_distribution_type;
        typedef boost::math::gamma_distribution<real_type>    math_distribution_type;

        gamma_distribution(real_type alpha, real_type beta)
            : m_rand(alpha,beta)
            , m_math(alpha,1./beta)
        {}

        // new/old: (mean^(n1-n0) * n0! / n1!
        real_type pdf_ratio(real_type x0, real_type x1) const
        {
            return pow(x1/x0,m_rand.alpha()-1)*exp(m_rand.beta()*(x0-x1));
        }

        real_type pdf(real_type x) const
        {
            return boost::math::pdf(m_math, x);
        }

        template<typename Engine>
        inline real_type operator()(Engine& e) const { return m_rand(e); }

    private:
        mutable rand_distribution_type m_rand;
        math_distribution_type m_math;
    };

}; // namespace rjmcmc

#endif // __GAMMA_DISTRIBUTION_HPP__


#include <iostream>
#include <array>
#include <cmath>
#include <tuple>
#include <cassert>
#include <NTL/ZZ.h>
#include <NTL/vector.h>
#include <random>
#include <chrono>

using namespace std;

using namespace NTL;

//ex. compile:
//g++ -o <exe> remainder_alg_first_pass.cpp -std=c++11 -march=native -O2 -lntl -lgmp -lgmpxx -pthread

// to compile and run on linux use
// g++ -o <executable file name> remainder_alg_first_pass.cpp
// ./<executable file name>

//note that everywhere it says 'int' we will eventually want to use GMP or whatnot
//ints are too small...

//translates indices in trees to indices in arrays
//if you want left child of (i,j), do (i+1, 2*j)
//right child is (i+1, 2*j+1), and parent is (i-1, j//2)
int flatten_index(int i, int j)
{
	assert (j <= pow(2,i) - 1);
	return pow(2,i) +j-1;
}

//gives tree indices from array index
tuple<int, int> lift_index(int k)
{
	int i = log2(k+1);
	int j = k + 1 - pow(2,i);

	return make_tuple(i,j);
}

void print_tree(Vec<ZZ> & X)
{
	cout << endl;
	for (int i = 0; i < X.length(); i++)
	{
		cout << X[i] << " , ";
	}
	cout << endl;
}

//reflects a tree on the y-axis in place
void reflect_tree(Vec<ZZ> & X) //why doesn't mutability work the way I think it does?
{
	int depth = log2(X.length());

	for (int i = 1; i <= depth; i++)
	{
		int width = pow(2,i);

		for (int j = 0; 2*j < width; j++)
		{
			int a = flatten_index(i,j);
			int b = flatten_index(i, width-j-1);

			//this switches the two locations
			X[b] = X[a]^X[b];
			X[a] = X[a]^X[b];
			X[b] = X[a]^X[b];
		}
	}
}

Vec<ZZ> zero_vector(int N)
{
	Vec<ZZ> ret;
	ret.SetLength(N);
	for (int i = 0; i < N; i++)
	{
		ret[i] = 0;
	}

	return ret;
}

//given an array of size N, returns the product tree of size 2N
//assumes N is a power of 2
//it won't have to be (we can pad it etc.) but this is just a preliminary version
Vec<ZZ> product_tree(Vec<ZZ> &X, Vec<ZZ> &mtree)
{

	//INPUT: a list of integers; OUTPUT: a product tree of double the size
	int depth = log2(X.length()); //round this UP when not power of 2

	Vec<ZZ> ptree;
	ptree = zero_vector(2*X.length()-1);

	//initialize leaves
	for (int j = 0; j < pow(2, depth); j++)
	{
		int leaf = flatten_index(depth,j);

		ptree[leaf] = X[j];

		if (mtree[leaf] != 0)
		{
			ptree[leaf] %= mtree[leaf];
		}
	}

	//build up the product tree recursively
	for (int i = depth-1; i >= 0; i--)
	{
		for (int j = 0; j < pow(2,i); j++)
		{
			int parent = flatten_index(i,j);
			int left = 2*parent + 1;
			int right = left + 1;

			//padding is not necessary if on this step we catch IndexError with 1
			ptree[parent] = ptree[left]*ptree[right];

			if (mtree[parent] != 0)
			{
				ptree[parent] %= mtree[parent];
			}

		}
	}

	return ptree;
}

Vec<ZZ> accumulating_tree(Vec<ZZ> &X, Vec<ZZ> &mtree)
{
	//code cut from body of accumulating_remainder_tree
	//this can be used to store the product tree of A modulo the mi
	//this can be applied to any tree; for our applications we will only apply it to product trees
	//INPUT: a tree; OUTPUT: accumulating tree (of same size)
	assert (X.length() == mtree.length());

	int depth = log2(X.length());

	Vec<ZZ> acctree;
	acctree.SetLength(X.length());
	acctree[0] = 1;

	for (int i = 0; i < depth; i++)
	{
		for (int j = 0; j < pow(2,i); j++)
		{
			int parent = flatten_index(i,j);
			int left = 2*parent + 1;
			int right = left+1;

			acctree[left] = acctree[parent];
			acctree[right] = acctree[parent]*X[left];

			if (mtree[left] != 0)
			{
				acctree[left] %= mtree[left];
			}

			if (mtree[right] != 0)
			{
				acctree[right] %= mtree[right];
			}
		}
	}

	return acctree;

}

//so might make sense to pad beginning of m with a 1, and pad end of A with a 1
//given A0, A1, ... An, and m0, m1, ... mn, this returns
//1, A0 mod m1, A0*A1 mod m2, ... A0*...An-1 mod mn
Vec<ZZ> accumulating_remainder_tree(Vec<ZZ> &A, Vec<ZZ> &m)
{
	assert (A.length() == m.length());

	int depth = log2(A.length());

	Vec<ZZ> ztree = zero_vector(2*m.length()-1);

	Vec<ZZ> m_ptree = product_tree(m, ztree);

	//print_tree(m_ptree);
	reflect_tree(m_ptree);
	Vec<ZZ> m_acctree = accumulating_tree(m_ptree, ztree);
	reflect_tree(m_acctree);
	reflect_tree(m_ptree);

	//Vec<ZZ> m_acctree = reflect_tree(accumulating_tree(reflect_tree(m_ptree), ztree));

	//print_tree(m_acctree);

	//this power tree is reduced modulo the reflected m_ftree
	Vec<ZZ> A_ptree = product_tree(A, m_acctree);
	//delete m_acctree;

	//print_tree(A_ptree);

	Vec<ZZ> A_rtree = accumulating_tree(A_ptree, m_ptree);

	//print_tree(A_rtree);

	//need a nice array slice here...
	Vec<ZZ> C;
	C.SetLength(A.length());

	for (int j = 0; j < pow(2,depth); j++)
	{
		C[j] = A_rtree[flatten_index(depth, j)];
	}

	return C;

}

int main()
{
	long N = pow(2,16);

	Vec<ZZ> A;
	Vec<ZZ> m;

	A.SetLength(N);
	m.SetLength(N);

	random_device rd;
	mt19937 mt(rd());
	uniform_int_distribution<int> dist(1, N);

	for(int i = 0; i < N; i++){
		int bitsize = log2(i+1)+2;

		A[i] = dist(mt);
		m[i] = dist(mt);

	}

	//Vec<ZZ> ztree = zero_vector(2*N);

	//print_tree(A);
	//print_tree(m);

	auto start = chrono::high_resolution_clock::now();
	Vec<ZZ> remainders = accumulating_remainder_tree(A,m);
	auto finish = chrono::high_resolution_clock::now();

	chrono::duration<double> runtime = finish-start;

	//print_tree(remainders);
	cout << runtime.count() << endl;

}


//
//  main.cpp
//  NRM - Newton-Raphson Method (for regression problem)
//
//  Created by Zhalgas Baibatyr on 1/19/16.
//  Copyright © 2016 Zhalgas Baibatyr. All rights reserved.
//

/*

    DEGREE ----- the degree of a polynomial in regression function
    N_SAMPLES -- number of training samples
    N_FEATURES - number of given input features
    Hessian ---- Hessian matrix
    features --- input features, including intercept term (xₒ = 1)
    target ----- target (output) values
    params ----- parameters (weights) with initial values (zeros)
    hypothesis - hypothesis function
    dCost ------ derivative of a cost function

*/

#include <armadillo>

using namespace arma;

int main()
{
    /* Loading initial data: */
    mat initData;
    initData.load("../data/regression1", arma_ascii);

    /* Initializing constants: */
    const uword DEGREE = 5;
    const uword N_SAMPLES  = initData.n_rows;
    const uword N_FEATURES = initData.n_cols - 1;

    /* Transforming input data using polynomial formula: */
    mat features(N_SAMPLES, DEGREE * N_FEATURES + 1);
    features.col(0) = ones<vec>(N_SAMPLES);
    for (int i = 0; i < N_FEATURES; ++i)
    {
        for (int j = 1; j <= DEGREE; ++j)
        {
            features.col(i * DEGREE + j) = pow(initData.col(i), j);
        }
    }

    /* Preparing other data: */
    vec target = initData.col(initData.n_cols - 1);
    vec params = zeros<vec>(features.n_cols);
    vec hypothesis;
    vec dCost;
    mat Hessian;

    wall_clock timer;
    double elapsedTime;
    timer.tic();

    /* Newton-Raphson Method performs here: */
    Hessian = features.t() * features;
    while (true)
    {
        /* Calculating hypothesis values and updating parameters: */
        hypothesis = features * params;
        dCost = features.t() * (hypothesis - target) / N_SAMPLES;
        params -= Hessian.i() * dCost;
     /* params -= solve(Hessian, dCost); */

        /* Checking how close to the minimum: */
        if (norm(dCost) < 1e-5)
        {
            hypothesis = features * params;
            break;
        }
    }

    /* Measuring the performance of the algorithm: */
    elapsedTime = timer.toc();
    printf("Elapsed time: %f sec.\n\n", elapsedTime);

    mat outputData = join_rows(initData.cols(0, initData.n_cols - 2), hypothesis);
    outputData.save("outputData", arma_ascii);
    hypothesis.save("hypothesis", arma_ascii);
    params.save("params", arma_ascii);

    return EXIT_SUCCESS;
}


#include <LinearModel.hpp>

#include <Eigen/Dense>
#include <Utils.hpp>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
#include <random>
#include <vector>

LinearModel::LinearModel(int weights_count, bool is_classification) : BaseModel(weights_count, is_classification) {
    weights = new double[weights_count + 1];
    // Init all weights and biases between -1.0 and 1.0
    for (int i = 0; i < weights_count + 1; i++) {
        weights[i] = ml::rand(-1, 1);
    }
}

void LinearModel::train(const Eigen::MatrixXd& train_inputs, const Eigen::MatrixXd& train_outputs, int epochs, double learning_rate) {
    const size_t sample_count = train_inputs.rows();
    const size_t inputs_size = train_inputs.cols();
    const size_t outputs_size = train_outputs.cols();

    if (is_classification) {
        std::vector<int> trainingSetOrder(sample_count);

        for (int i = 0; i < trainingSetOrder.size(); i++) {
            trainingSetOrder[i] = i;
        }

        Eigen::MatrixXd activation(sample_count, outputs_size);

        // Iterate with epochs
        for (int i = 0; i < epochs; i++) {
            // *** Predict Function ***

            // compute activation fonction
            predict(train_inputs, activation);  // or `predict(train_inputs.row(trainingSetID), activation);` in trainingSetOrder loops to predict by row

            // shuffle the training set
            ml::random_shuffle<int>(trainingSetOrder);

            // for each training set
            for (int j = 0; j < trainingSetOrder.size(); j++) {
                // select a training set ID
                int trainingSetID = trainingSetOrder[j];

                // *** Learn Function ***

                // Backpropagation of error on weights / Adjust the weights
                for (int k = 0; k < outputs_size; k++) {
                    const double target_value = train_outputs(trainingSetID, k);
                    const double actual_value = activation(trainingSetID, k);

                    double error = actual_value - target_value;
                    if (is_classification) {
                        error *= (actual_value * actual_value);
                    }

                    for (int l = 0; l < inputs_size; l++) {
                        const double entry_value = train_inputs(trainingSetID, l);

                        weights[l] -= (learning_rate * error * entry_value);
                    }
                    weights[weights_count] -= (learning_rate * error * 1);  // bias
                }
            }
        }
    } else { // to be verified
        // Add a column of one (at the right), for the bias
        Eigen::MatrixXd tmp(train_inputs.rows(), train_inputs.cols() + 1);
        Eigen::VectorXd vec(train_inputs.rows());
        for (int i = 0; i < train_inputs.rows(); i++) {
            vec(i) = 1;
        }
        tmp << train_inputs, vec;

        // Compute the transpose
        Eigen::MatrixXd inputs_transposed = tmp.transpose();
        Eigen::MatrixXd inv_inputs_transposed = (inputs_transposed * tmp).completeOrthogonalDecomposition().pseudoInverse();

        // Compute weights
        Eigen::MatrixXd w = inv_inputs_transposed * inputs_transposed * train_outputs;

        for (int i = 0; i < inputs_size + 1; i++) {
            weights[i] = w(i, 0);
        }
    }
}

double LinearModel::_activation(double value) const {
    if (is_classification) {
        value = std::tanh(value);
        return (value != 0) ? (value > 0) ? 1 : -1 : 0;
    } else {
        return value;
    }
}

void LinearModel::predict(const Eigen::MatrixXd& inputs, Eigen::MatrixXd& outputs){
    assert(inputs.rows() == outputs.rows());  // or maybe resize outputs

    // for each sample
    for (int i = 0; i < inputs.rows(); i++) {
        // Loop on outputs (here we have only one output)
        for (int j = 0; j < outputs.cols(); j++) {
            double activation = weights[weights_count];

            // Loop on inputs
            for (int k = 0; k < inputs.cols(); k++) {
                activation += inputs(i, k) * weights[k];
            }

            // compute the _sigmoid of the activation
            outputs(i, j) = _activation(activation);
        }
    }
}


#include "VectorMath.h"
#include <Eigen/Dense>
#include <random>
#include <iostream>

using namespace Eigen;

const Matrix3d VectorMath::crossProductMatrix(const Eigen::Vector3d &v)
{
    Matrix3d result;
    result << 0, -v[2], v[1],
            v[2], 0, -v[0],
            -v[1], v[0], 0;
    return result;
}

const Matrix3d VectorMath::rotationMatrix(const Vector3d &axisAngle)
{
    double theta = axisAngle.norm();
    Vector3d thetahat = axisAngle/theta;

    if(theta == 0)
        thetahat.setZero();

    Matrix3d result;
    result.setIdentity();
    result = cos(theta)*result + sin(theta)*crossProductMatrix(thetahat) + (1-cos(theta))*thetahat*thetahat.transpose();
    return result;
}

double VectorMath::randomUnitIntervalReal()
{
    return double(rand())/double(RAND_MAX);
}

const Vector3d VectorMath::axisAngle(const Matrix3d &rotationMatrix)
{
    Matrix3d I;
    I.setIdentity();
    Matrix3d RminusI = rotationMatrix - I;

    JacobiSVD<Matrix3d> svd(RminusI, ComputeFullV);
    //assert(fabs(svd.singularValues()[2]) < 1e-8);
    Vector3d axis = svd.matrixV().col(2);
    Vector3d testAxis = perpToAxis(axis);
    Vector3d resultAxis = rotationMatrix*testAxis;
    double theta = atan2(testAxis.cross(resultAxis).dot(axis), testAxis.dot(resultAxis));
    return theta*axis;
}

const Vector3d VectorMath::perpToAxis(const Vector3d &v)
{
    int mincoord = 0;
    double minval = std::numeric_limits<double>::infinity();
    for(int i=0; i<3; i++)
    {
        if(fabs(v[i]) < minval)
        {
            mincoord = i;
            minval = fabs(v[i]);
        }
    }
    Vector3d other(0,0,0);
    other[mincoord] = 1.0;
    Vector3d result = v.cross(other);
    result.normalize();
    return result;
}

const Matrix3d VectorMath::TMatrix(const Vector3d &v)
{
    double vnormsq = v.dot(v);
    Matrix3d I;
    I.setIdentity();
    if(vnormsq < 1e-8)
        return I;

    Matrix3d R = rotationMatrix(v);
    return (v*v.transpose() + (R.transpose()-I)*crossProductMatrix(v))/vnormsq;
}

const Matrix3d VectorMath::DrotVector(const Vector3d &axisangle, const Vector3d &rotatingVector)
{
    Matrix3d R = rotationMatrix(axisangle);
    Matrix3d result = -R * crossProductMatrix(rotatingVector) * TMatrix(axisangle);
    return result;
}

const Eigen::Vector3d VectorMath::randomPointOnSphere()
{
    std::random_device r;
    std::mt19937 generator(r());
    std::normal_distribution<double> distribution(0.0,1.0);
    Vector3d vec(distribution(generator), distribution(generator), distribution(generator));
    vec /= vec.norm();
    return vec;
}


/** \file  main.cpp
    \brief Test and demonstration program.
           Copyright 2006, 2010, 2013 by Erik Schloegl
     */

#include <iostream>
#include <cstdlib>
#include <boost/bind.hpp>
#include "BlackScholesAsset.hpp"
#include "Payoff.hpp"
#include "Binomial.hpp"

using namespace quantfin;

/** Test and demonstration for finite difference schemes.

    Command-line arguments:
      -# initial stock price.
         The default is 100.
      -# interest rate.
         The default is 5%.
      -# volatility.
         The default is 30%.
      -# maturity.
         The default is 1.5.
      -# moneyness.
         The default is 1 (at the money).
      -# N, time line refinement.
         The default is 10.
  */
int main(int argc,char* argv[])
{
  using std::cout;
  using std::endl;
  using std::flush;

  int i,j;
  try {
    for (i=0;i<argc;i++) cout << argv[i] << ' ';
	cout << endl;
    double S = 100.0;
    if (argc>1) S = atof(argv[1]);
    double r = 0.05;
    if (argc>2) r = atof(argv[2]);
    double sgm = 0.3;
    if (argc>3) sgm = atof(argv[3]);
    double mat = 1.5;
    if (argc>4) mat = atof(argv[4]);
    double K = 1.0;
    if (argc>5) K = atof(argv[5]);
    K *= S;
    int N = 10;
    if (argc>6) N = atoi(argv[6]);
    Array<double,1> T(N+1);
    firstIndex idx;
    double dt = mat/N;
    T = idx*dt;
    ConstVol vol(sgm);
    BlackScholesAsset stock(&vol,S);
    cout << "S: " << S << "\nK: " << K << "\nr: " << r << "\nT: " << mat << "\nsgm: " << sgm << endl;
    double CFcall = stock.option(mat,K,r);
    double CFput  = stock.option(mat,K,r,-1);
    double CFiput = stock.option(T(N/2),K,r,-1);
    cout << "Closed form call: " << CFcall << endl;
    cout << "Closed form put: " << CFput << endl;
    cout << "Closed form intermediate maturity put: " << CFiput << endl;
    cout << "Time line refinement: " << N << endl;
    cout << "Creating BinomialLattice object" << endl;
    BinomialLattice btree(stock,r,mat,N);
    Payoff call(K);
    Payoff put(K,-1);
    boost::function<double (double)> f;
    f = boost::bind(std::mem_fun(&Payoff::operator()),&call,_1);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial call (CRR): " << btree.result() << "\nDifference to closed form: " << CFcall - btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial call (JR): " << btree.result() << "\nDifference to closed form: " << CFcall - btree.result() << endl;
    f = boost::bind(std::mem_fun(&Payoff::operator()),&put,_1);
    btree.set_CoxRossRubinstein();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (CRR): " << btree.result() << "\nDifference to closed form: " << CFput - btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (JR): " << btree.result() << "\nDifference to closed form: " << CFput - btree.result() << endl;
    btree.set_Tian();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (Tian): " << btree.result() << "\nDifference to closed form: " << CFput - btree.result() << endl;
    btree.set_LeisenReimer(K);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (LR): " << btree.result() << "\nDifference to closed form: " << CFput - btree.result() << endl;
    EarlyExercise amput(put);
    boost::function<double (double,double)> g;
    g = boost::bind(boost::mem_fn(&EarlyExercise::operator()),&amput,_1,_2);
    btree.set_CoxRossRubinstein();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (CRR): " << btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (JR): " << btree.result() << endl;
    btree.set_LeisenReimer(K);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (LR): " << btree.result() << endl;

    // with dividends
    stock.dividend_yield(0.03);
    CFcall = stock.option(mat,K,r);
    CFput  = stock.option(mat,K,r,-1);
    cout << "Closed form call: " << CFcall << endl;
    cout << "Closed form put: " << CFput << endl;
    f = boost::bind(std::mem_fun(&Payoff::operator()),&call,_1);
    btree.set_CoxRossRubinstein();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial call (CRR): " << btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial call (JR): " << btree.result() << endl;
    btree.set_LeisenReimer(K);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial call (LR): " << btree.result() << endl;
    f = boost::bind(std::mem_fun(&Payoff::operator()),&put,_1);
    btree.set_CoxRossRubinstein();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (CRR): " << btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (JR): " << btree.result() << endl;
    btree.set_Tian();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (Tian): " << btree.result() << endl;
    btree.set_LeisenReimer(K);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0);
    cout << "Binomial put (LR): " << btree.result() << endl;
    btree.set_CoxRossRubinstein();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (CRR): " << btree.result() << endl;
    btree.set_JarrowRudd();
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (JR): " << btree.result() << endl;
    btree.set_LeisenReimer(K);
    btree.apply_payoff(N-1,f);
    btree.rollback(N-1,0,g);
    cout << "Binomial American put (LR): " << btree.result() << endl;
	} // end of try block

  catch (std::logic_error xcpt) {
    std::cerr << xcpt.what() << endl; }
  catch (std::runtime_error xcpt) {
    std::cerr << xcpt.what() << endl; }
  catch (...) {
    std::cerr << "Other exception caught" << endl; }

  return 0;
}


#include "tools.h"
#include "logger.h"
#include <iostream>

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

using Eigen::VectorXd;
using Eigen::MatrixXd;
using std::vector;

static const Eigen::IOFormat HeavyFormat = Eigen::IOFormat(Eigen::StreamPrecision, 1, ", ", ";\n", "[", "]", "[", "]");

static double Cross2d(const Eigen::Vector2d &vec1, const Eigen::Vector2d &vec2) {
    return vec1[0] * vec2[1] - vec1[1] * vec2[0];
}

Eigen::Vector4d Tools::CalculateRMSE(const vector<Eigen::Vector4d> &estimations,
                                     const vector<Eigen::Vector4d> &ground_truth) {
    BOOST_ASSERT(estimations.size() == ground_truth.size());
    Eigen::Vector4d rmse;

    const size_t data_size = estimations.size();
    if (data_size < 1) {
        BOOST_LOG_TRIVIAL(error) << (boost::format("Got invalid size, estimations.size()=%zu, ground_truth.size()=%zu.")
                                     % estimations.size() % ground_truth.size()).str();
        return rmse;
    }

    Eigen::Map<const Eigen::Array4Xd> estimations_map(reinterpret_cast<const double*>(estimations.data()),
                                                      4, data_size);
    Eigen::Map<const Eigen::Array4Xd> ground_truth_map(reinterpret_cast<const double*>(ground_truth.data()),
                                                       4, data_size);
    rmse = ((estimations_map - ground_truth_map).square().rowwise().sum() / data_size).sqrt();
    BOOST_LOG_TRIVIAL(info) << (boost::format("rmse=%s") % rmse.transpose().format(HeavyFormat)).str();
    return rmse;
}

Eigen::Matrix<double, 3, 4> Tools::CalculateJacobian(const VectorXd& x_state) {
   auto J = Eigen::Matrix<double, 3, 4>();
   const double &px = x_state[0];
   const double &py = x_state[1];
   const double &vx = x_state[2];
   const double &vy = x_state[3];
   const double p_norm = x_state.topRows<2>().norm();
   const double p_norm2 = p_norm * p_norm;

   if (std::abs(p_norm) < 1e-6) {
       return J;
   }

   J <<
       // row1
       px / p_norm, py / p_norm, 0.0, 0.0,
       // row2
       -py / p_norm2, px / p_norm2, 0.0, 0.0,
       // row3
       py * (Cross2d(x_state.bottomRows<2>(), x_state.topRows<2>())) / (p_norm * p_norm2),
       px * (Cross2d(x_state.topRows<2>(), x_state.bottomRows<2>())) / (p_norm * p_norm2),
       px / p_norm, py / p_norm;

    return J;
}


/// @file  eval.hpp
/// @brief Declarations for evaluation methods

#pragma once
#ifndef OGT_EVAL_EVAL_HPP
#define OGT_EVAL_EVAL_HPP

#include <ogt/config.hpp>
#include <ogt/util/chain_merge.hpp>
#include <Eigen/Dense>
#include <functional>
#include <vector>

namespace OGT_NAMESPACE {
namespace eval {

/// Calculate raw stress. This is the sum of the squared error of the distances.
/// It takes its minimum at zero, which indicates a perfect embedding.
/// If requested, the estimated distances can first be scaled by the optimal
/// constant to minimize stress.
/// The scaled versions of these values can be used to compare embeddings of the
/// same dataset, but as they are not normalized they cannot be compared across
/// datasets.
///
/// The unscaled version is referred to as \sigma_r in:
/// [1] I. Borg & P. Groenen (1997): Modern multidimensional scaling: theory
///     and applications. Springer.
double mdsStress(const Eigen::MatrixXd& dhat, const Eigen::MatrixXd& dtrue,
	bool scaled = false);

/// Calculate normed stress. This is a normalized version of mdsStress(), which
/// can thus be compared between different datasets.
/// It takes its minimum at zero, which indicates a perfect embedding.
///
/// The unscaled version is referred to as Stress-1 or \sigma_1 in:
/// [1] I. Borg & P. Groenen (1997): Modern multidimensional scaling: theory
///     and applications. Springer.
double mdsNormedStress(const Eigen::MatrixXd& dhat,
	const Eigen::MatrixXd& dtrue, bool scaled = false);

/// Calculate normed rank stress. That is, the normalized sum-squared amount by
/// which each similarity constraint is violated.
/// This essentially treats dist(i,j) as the target distance for dist(k,l)
/// if dist(i,j) immediately precedes dist(k,l) in the partial order of
/// distances.
double mdsNormedRankStress(const Eigen::MatrixXd& dhat,
	const OGT_NAMESPACE::util::ChainMerge& order);

/// Calculate the root mean squared error of the distances, after finding the
/// scaling which minimizes this error.
/// In other words, we report RMSE for the best linear fit between the
/// distance matrices.
double distRmse(const Eigen::MatrixXd& dhat, const Eigen::MatrixXd& dtrue);

/// Calculate the Kendall's tau-b of two distance vectors to other points.
/// Does so in O(n log n) time, using
/// Knight's Algorithm](http://adereth.github.io/blog/2013/10/30/efficiently-computing-kendalls-tau/).
double kendallTau(const Eigen::VectorXd& d1, const Eigen::VectorXd& d2);

/// Calculate the mean Kendall's tau-b of rankings by each row.
double meanKendallTau(const Eigen::MatrixXd& dhat,
	const Eigen::MatrixXd& dtrue);

/// Calculate the weighted Kendall's tau of two vectors of numbers.
/// The tau value is given for vectors r and s. The function w provides
/// a weight for each rank in the list.
///
/// [1] S. Vigna, A Weighted Correlation Index for Rankings with Ties. WWW, 2015
double weightedTau(const Eigen::VectorXd& r, const Eigen::VectorXd& s,
	std::function<double(size_t /* rank */)> w);

/// Calculate the weighted Kendall's tau of two vectors of numbers.
/// The tau value is given for vectors r and s. The functions w1 and w2 provide
/// weights for each rank in the list.
///
/// [1] S. Vigna, A Weighted Correlation Index for Rankings with Ties. WWW, 2015
double weightedTau(const Eigen::VectorXd& r, const Eigen::VectorXd& s,
	std::function<double(size_t /* rank */)> w1,
	std::function<double(size_t /* rank */)> w2);

/// The hyperbolic tau: weightedTau with rank weight 1/(1 + r).
double hyperbolicTau(const Eigen::VectorXd& r, const Eigen::VectorXd& s);

/// Calculate the mean hyperbolic tau of rankings by each row.
double meanHyperbolicTau(const Eigen::MatrixXd& dhat,
	const Eigen::MatrixXd& dtrue);

} // end namespace eval
} // end namespace OGT_NAMESPACE
#endif /* OGT_EVAL_EVAL_HPP */


// (C) Copyright John Maddock 2006
// 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)

#ifdef _MSC_VER
#  pragma warning(disable: 4512) // assignment operator could not be generated.
#  pragma warning(disable: 4510) // default constructor could not be generated.
#  pragma warning(disable: 4610) // can never be instantiated - user defined constructor required.
#endif

#include <iostream>
#include <iomanip>
#include <boost/math/distributions/binomial.hpp>

void find_max_sample_size(double p, unsigned successes)
{
   //
   // p         = success ratio.
   // successes = Total number of observed successes.
   //
   // Calculate how many trials we can have to ensure the
   // maximum number of successes does not exceed "successes".
   // A typical use would be failure analysis, where you want
   // zero or fewer "successes" with some probability.
   //
   using namespace std;
   using namespace boost::math;

   // Print out general info:
   cout <<
      "________________________\n"
      "Maximum Number of Trials\n"
      "________________________\n\n";
   cout << setprecision(7);
   cout << setw(40) << left << "Success ratio" << "=  " << p << "\n";
   cout << setw(40) << left << "Maximum Number of \"successes\" permitted" << "=  " << successes << "\n";
   //
   // Define a table of confidence intervals:
   //
   double alpha[] = { 0.5, 0.25, 0.1, 0.05, 0.01, 0.001, 0.0001, 0.00001 };
   //
   // Print table header:
   //
   cout << "\n\n"
           "____________________________\n"
           "Confidence        Max Number\n"
           " Value (%)        Of Trials \n"
           "____________________________\n";
   //
   // Now print out the data for the table rows.
   //
   for(unsigned i = 0; i < sizeof(alpha)/sizeof(alpha[0]); ++i)
   {
      // Confidence value:
      cout << fixed << setprecision(3) << setw(10) << right << 100 * (1-alpha[i]);
      // calculate trials:
      double t = binomial_distribution<>::find_maximum_number_of_trials(successes, p, alpha[i]);
      t = floor(t);
      // Print Trials:
      cout << fixed << setprecision(0) << setw(15) << right << t << endl;
   }
   cout << endl;
}

int main()
{
   find_max_sample_size(1.0/1000, 0);
   find_max_sample_size(1.0/10000, 0);
   find_max_sample_size(1.0/100000, 0);
   find_max_sample_size(1.0/1000000, 0);

   return 0;
}


/*****************************************************************************
*
* Rokko: Integrated Interface for libraries of eigenvalue decomposition
*
* Copyright (C) 2012-2015 Rokko Developers https://github.com/t-sakashita/rokko
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
*****************************************************************************/

#ifndef ROKKO_UTILITY_LAPLACIAN_MATRIX_HPP
#define ROKKO_UTILITY_LAPLACIAN_MATRIX_HPP

#include <cmath>
#include <stdexcept>
#include <boost/throw_exception.hpp>
#include <rokko/localized_matrix.hpp>

namespace rokko {

class laplacian_matrix {
public:
  template<typename T>
  static void multiply(int dim, const T* x, T* y) {
    y[0] = x[0] - x[1];
    y[dim-1] = 2 * x[dim-1] - x[dim - 2];
    for (int k = 1; k < (dim-1); ++k) { // from 1 to end-1
      y[k] = - x[k-1] + 2 * x[k] - x[k+1];
    }
  }

  template<typename T>
  static void multiply(int dim, const std::vector<T>& v, std::vector<T>& w) {
    multiply(dim, &v[0], &w[0]);
  }

  template<typename T, typename MATRIX_MAJOR>
  static void generate(rokko::localized_matrix<T, MATRIX_MAJOR>& mat) {
    if (mat.rows() != mat.cols())
      BOOST_THROW_EXCEPTION(std::invalid_argument("laplacian_matrix::generate() : non-square matrix"));
    mat.setZero();
    int n = mat.rows();
    mat(0, 0) = 1; mat(0, 1) = -1;
    mat(n-1, n-2) = -1;  mat(n-1, n-1) = 2;
    for(int i = 1; i < n-1; ++i) {
      mat(i, i-1) = -1;
      mat(i, i) = 2;
      mat(i, i+1) = -1;
    }
  }

  // calculate k-th smallest eigenvalue of dim-dimensional Laplacian matrix (k=0...dim-1)
  static double eigenvalue(int dim, int k) {
    return 2 * (1 - std::cos(M_PI * (2 * k + 1) / (2 * dim + 1)));
  }
};

} // namespace rokko

#endif // ROKKO_UTILITY_LAPLACIAN_MATRIX_HPP


// HardCoded.cpp
//
// C++ code to price an option, essential algorithms.
//
// We take CEV model with a choice of the elaticity parameter
// and the Euler method. We give option price and number of times
// S hits the origin.
//
// (C) Datasim Education BC 2008-2011
//
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include "OptionData.hpp"
#include "UtilitiesDJD/RNG/NormalGenerator.hpp"
#include "UtilitiesDJD/Geometry/Range.cpp"
#include <cmath>
#include <iostream>

template <class T> void print(const std::vector<T>& myList)
{  // A generic print function for vectors

	std::cout << std::endl << "Size of vector is " << l.size() << "\n[";

	// We must use a const iterator here, otherwise we get a compiler error.
	std::vector<T>::const_iterator i;
	for (i = myList.begin(); i != myList.end(); ++i)
	{
		std::cout << *i << ",";

	}

	std::cout << "]\n";
}
template <typename Type>
boost::tuple<Type, Type> SDSE(const std::vector<Type>& price, const Type& r, const Type& T)
{
	Type temp1(0), temp2(0);
	for (int i = 0; i < price.size(); i++)
	{
		temp1 += price[i] * price[i];
		temp2 += price[i];
	}

	int M = price.size();
	Type sd = sqrt(temp1 - 1 / M * temp2 * temp2) * exp(-2 * r * T) / (M - 1);
	Type se = sd / sqrt(M);

	return boost::make_tuple(sd, se);
}
namespace SDEDefinition
{ // Defines drift + diffusion + data

	OptionData* data;				// The data for the option MC

	double drift(double t, double X)
	{ // Drift term

		return (data->r) * X; // r - D
	}

	double diffusion(double t, double X)
	{ // Diffusion term

		double betaCEV = 1.0;
		return data->sig * pow(X, betaCEV);

	}

	double diffusionDerivative(double t, double X)
	{ // Diffusion term, needed for the Milstein method

		double betaCEV = 1.0;
		return 0.5 * (data->sig) * (betaCEV)*pow(X, 2.0 * betaCEV - 1.0);
	}
} // End of namespace

int main()
{
	std::cout << "1 factor MC with explicit Euler" << endl;

	// Store Batch 1 to Batch 2 data in a vector.
	typedef boost::tuple<double, double, double, double, double> TupleFive;
	vector<TupleFive> vecBatch;
	vecBatch.push_back(boost::make_tuple(0.25, 65.0, 0.30, 0.08, 60.0));
	vecBatch.push_back(boost::make_tuple(1.00, 100.0, 0.20, 0.00, 100.0));
	//Batch 4: T = 30.0, K = 100.0, sig = 0.30, r = 0.08, S = 100.0 (C = 92.17570, P = 1.24750)
	//vecBatch.push_back(boost::make_tuple(30.00, 100.0, 0.30, 0.08, 100.0));

	// Vector to store the prices of put and call.
	vector<double> vecCallPrice, vecPutPrice;
	for (int i = 0; i < vecBatch.size(); i++)
	{
		OptionData myOption;
		myOption.T = vecBatch[i].get<0>();
		myOption.K = vecBatch[i].get<1>();
		myOption.sig = vecBatch[i].get<2>();
		myOption.r = vecBatch[i].get<3>();
		myOption.type = 1;
		double S_0 = vecBatch[i].get<4>();
		long N = 100;
		std::cout << "Number of subintervals in time: ";
		std::cin >> N;

		// Create the basic SDE (Context class)
		Range<double> range(0.0, myOption.T);
		double VOld = S_0;
		double VNew;

		std::vector<double> x = range.mesh(N);

		// V2 mediator stuff
		long NSim = 50000;
		std::cout << "Number of simulations: ";
		std::cin >> NSim;

		double k = myOption.T / double(N);
		double sqrk = sqrt(k);

		// Normal random number
		double dW;
		// Call option price.
		double price1 = 0.0;
		// Put option price.
		double price2 = 0.0;

		// NormalGenerator is a base class
		NormalGenerator* myNormal = new BoostNormal();

		using namespace SDEDefinition;
		SDEDefinition::data = &myOption;

		std::vector<double> res;
		int coun = 0; // Number of times S hits origin

		// A.
		for (long i = 1; i <= NSim; ++i)
		{ // Calculate a path at each iteration

			if ((i / 10000) * 10000 == i)
			{// Give status after each 1000th iteration

				std::cout << i << std::endl;
			}

			VOld = S_0;
			for (unsigned long index = 1; index < x.size(); ++index)
			{

				// Create a random number
				dW = myNormal->getNormal();

				// The FDM (in this case explicit Euler)
				VNew = VOld + (k * drift(x[index - 1], VOld))
					+ (sqrk * diffusion(x[index - 1], VOld) * dW);

				VOld = VNew;

				// Spurious values
				if (VNew <= 0.0) coun++;
			}
			double tmp1 = myOption.myPayOffFunction(VNew);
			price1 += (tmp1) / double(NSim);
			vecCallPrice.push_back(tmp1);
			myOption.type = -1;
			double tmp2 = myOption.myPayOffFunction(VNew);
			price2 += (tmp2) / double(NSim);
			vecPutPrice.push_back(tmp2);
			myOption.type = 1;
		}

		// D. Finally, discounting the average price.
		price1 *= exp(-myOption.r * myOption.T);
		price2 *= exp(-myOption.r * myOption.T);

		// Cleanup; V2 use scoped pointer
		delete myNormal;

		std::cout << "Price, after discounting: Call = " << price1 << ", Put = " << price2 << std::endl;
		std::cout << "Number of times origin is hit: " << coun << endl;

		// Print SD and SE.
		boost::tuple<double, double> tupleCall = SDSE<double>(vecCallPrice, myOption.r, myOption.T);
		boost::tuple<double, double> tuplePut = SDSE<double>(vecPutPrice, myOption.r, myOption.T);
		std::cout << "Batch " << i + 1 << ", Call: NT = " << N << ", NSIM = " << NSim
			<< ", SD = " << tupleCall.get<0>() << ", SE = " << tupleCall.get<1>() << endl;
		std::cout << "Batch " << i + 1 << ", Put: NT = " << N << ", NSIM = " << NSim
			<< ", SD = " << tuplePut.get<0>() << ", SE = " << tuplePut.get<1>() << endl;
	}
	return 0;
}


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

#ifndef BOOST_MATH_SPECIAL_SPHERICAL_HARMONIC_HPP
#define BOOST_MATH_SPECIAL_SPHERICAL_HARMONIC_HPP

#ifdef _MSC_VER
#pragma once
#endif

#include <boost/math/special_functions/legendre.hpp>
#include <boost/math/tools/workaround.hpp>
#include <complex>

namespace boost{
namespace math{

namespace detail{

//
// Calculates the prefix term that's common to the real
// and imaginary parts.  Does *not* fix up the sign of the result
// though.
//
template <class T, class Policy>
inline T spherical_harmonic_prefix(unsigned n, unsigned m, T theta, const Policy& pol)
{
   BOOST_MATH_STD_USING

   if(m > n)
      return 0;

   T sin_theta = sin(theta);
   T x = cos(theta);

   T leg = detail::legendre_p_imp(n, m, x, static_cast<T>(pow(fabs(sin_theta), T(m))), pol);

   T prefix = boost::math::tgamma_delta_ratio(static_cast<T>(n - m + 1), static_cast<T>(2 * m), pol);
   prefix *= (2 * n + 1) / (4 * constants::pi<T>());
   prefix = sqrt(prefix);
   return prefix * leg;
}
//
// Real Part:
//
template <class T, class Policy>
T spherical_harmonic_r(unsigned n, int m, T theta, T phi, const Policy& pol)
{
   BOOST_MATH_STD_USING  // ADL of std functions

   bool sign = false;
   if(m < 0)
   {
      // Reflect and adjust sign if m < 0:
      sign = m&1;
      m = abs(m);
   }
   if(m&1)
   {
      // Check phase if theta is outside [0, PI]:
      T mod = boost::math::tools::fmod_workaround(theta, T(2 * constants::pi<T>()));
      if(mod < 0)
         mod += 2 * constants::pi<T>();
      if(mod > constants::pi<T>())
         sign = !sign;
   }
   // Get the value and adjust sign as required:
   T prefix = spherical_harmonic_prefix(n, m, theta, pol);
   prefix *= cos(m * phi);
   return sign ? T(-prefix) : prefix;
}

template <class T, class Policy>
T spherical_harmonic_i(unsigned n, int m, T theta, T phi, const Policy& pol)
{
   BOOST_MATH_STD_USING  // ADL of std functions

   bool sign = false;
   if(m < 0)
   {
      // Reflect and adjust sign if m < 0:
      sign = !(m&1);
      m = abs(m);
   }
   if(m&1)
   {
      // Check phase if theta is outside [0, PI]:
      T mod = boost::math::tools::fmod_workaround(theta, T(2 * constants::pi<T>()));
      if(mod < 0)
         mod += 2 * constants::pi<T>();
      if(mod > constants::pi<T>())
         sign = !sign;
   }
   // Get the value and adjust sign as required:
   T prefix = spherical_harmonic_prefix(n, m, theta, pol);
   prefix *= sin(m * phi);
   return sign ? T(-prefix) : prefix;
}

template <class T, class U, class Policy>
std::complex<T> spherical_harmonic(unsigned n, int m, U theta, U phi, const Policy& pol)
{
   BOOST_MATH_STD_USING
   //
   // Sort out the signs:
   //
   bool r_sign = false;
   bool i_sign = false;
   if(m < 0)
   {
      // Reflect and adjust sign if m < 0:
      r_sign = m&1;
      i_sign = !(m&1);
      m = abs(m);
   }
   if(m&1)
   {
      // Check phase if theta is outside [0, PI]:
      U mod = boost::math::tools::fmod_workaround(theta, 2 * constants::pi<U>());
      if(mod < 0)
         mod += 2 * constants::pi<U>();
      if(mod > constants::pi<U>())
      {
         r_sign = !r_sign;
         i_sign = !i_sign;
      }
   }
   //
   // Calculate the value:
   //
   U prefix = spherical_harmonic_prefix(n, m, theta, pol);
   U r = prefix * cos(m * phi);
   U i = prefix * sin(m * phi);
   //
   // Add in the signs:
   //
   if(r_sign)
      r = -r;
   if(i_sign)
      i = -i;
   static const char* function = "boost::math::spherical_harmonic<%1%>(int, int, %1%, %1%)";
   return std::complex<T>(policies::checked_narrowing_cast<T, Policy>(r, function), policies::checked_narrowing_cast<T, Policy>(i, function));
}

} // namespace detail

template <class T1, class T2, class Policy>
inline std::complex<typename tools::promote_args<T1, T2>::type>
   spherical_harmonic(unsigned n, int m, T1 theta, T2 phi, const Policy& pol)
{
   typedef typename tools::promote_args<T1, T2>::type result_type;
   typedef typename policies::evaluation<result_type, Policy>::type value_type;
   return detail::spherical_harmonic<result_type, value_type>(n, m, static_cast<value_type>(theta), static_cast<value_type>(phi), pol);
}

template <class T1, class T2>
inline std::complex<typename tools::promote_args<T1, T2>::type>
   spherical_harmonic(unsigned n, int m, T1 theta, T2 phi)
{
   return boost::math::spherical_harmonic(n, m, theta, phi, policies::policy<>());
}

template <class T1, class T2, class Policy>
inline typename tools::promote_args<T1, T2>::type
   spherical_harmonic_r(unsigned n, int m, T1 theta, T2 phi, const Policy& pol)
{
   typedef typename tools::promote_args<T1, T2>::type result_type;
   typedef typename policies::evaluation<result_type, Policy>::type value_type;
   return policies::checked_narrowing_cast<result_type, Policy>(detail::spherical_harmonic_r(n, m, static_cast<value_type>(theta), static_cast<value_type>(phi), pol), "bost::math::spherical_harmonic_r<%1%>(unsigned, int, %1%, %1%)");
}

template <class T1, class T2>
inline typename tools::promote_args<T1, T2>::type
   spherical_harmonic_r(unsigned n, int m, T1 theta, T2 phi)
{
   return boost::math::spherical_harmonic_r(n, m, theta, phi, policies::policy<>());
}

template <class T1, class T2, class Policy>
inline typename tools::promote_args<T1, T2>::type
   spherical_harmonic_i(unsigned n, int m, T1 theta, T2 phi, const Policy& pol)
{
   typedef typename tools::promote_args<T1, T2>::type result_type;
   typedef typename policies::evaluation<result_type, Policy>::type value_type;
   return policies::checked_narrowing_cast<result_type, Policy>(detail::spherical_harmonic_i(n, m, static_cast<value_type>(theta), static_cast<value_type>(phi), pol), "boost::math::spherical_harmonic_i<%1%>(unsigned, int, %1%, %1%)");
}

template <class T1, class T2>
inline typename tools::promote_args<T1, T2>::type
   spherical_harmonic_i(unsigned n, int m, T1 theta, T2 phi)
{
   return boost::math::spherical_harmonic_i(n, m, theta, phi, policies::policy<>());
}

} // namespace math
} // namespace boost

#endif // BOOST_MATH_SPECIAL_SPHERICAL_HARMONIC_HPP


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

void eigenValueSolver()
{
    // ComplexEigenSolver<MatrixXcf> ces;
    Eigen::EigenSolver<Eigen::MatrixXd> ces;
    Eigen::MatrixXd A(4, 4);
    A(0, 0) = 18;
    A(0, 1) = -9;
    A(0, 2) = -27;
    A(0, 2) = 24;
    A(1, 0) = -9;
    A(1, 1) = 4.5;
    A(1, 2) = 13.5;
    A(1, 3) = -12;
    A(2, 0) = -27;
    A(2, 1) = 13.5;
    A(2, 2) = 40.5;
    A(2, 3) = -36;
    A(3, 0) = 24;
    A(3, 1) = -12;
    A(3, 2) = -36;
    A(3, 3) = 32;
    ces.compute(A);
    std::cout << "The eigenvalues of A are:" << std::endl
         << ces.eigenvalues() << std::endl;
    std::cout << "The matrix of eigenvectors, V, is:" <<std::endl
         << ces.eigenvectors() << std::endl
         << std::endl;
    //  complex<float> lambda =  ces.eigenvalues()[0];
    //  cout << "Consider the first eigenvalue, lambda = " << lambda << endl;
    //  VectorXcf v = ces.eigenvectors().col(0);
    //  cout << "If v is the corresponding eigenvector, then lambda * v = " <<
    //  endl << lambda * v << endl;
    //  cout << "... and A * v = " << endl << A * v << endl << endl;
    //
    //  cout << "Finally, V * D * V^(-1) = " << endl
    //       << ces.eigenvectors() * ces.eigenvalues().asDiagonal() *
    //       ces.eigenvectors().inverse() << endl;
}

int main()
{

}


#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <random>
#include <string>
#include <vector>

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

using Eigen::MatrixXd;
using Eigen::VectorXd;
using Eigen::seq;

MatrixXd read_tableau(const std::string& filePath) {
  std::ifstream file(filePath);
  if(!file.is_open())
    throw std::ifstream::failure("File could not be opened");
  int m, n;
  file >> m >> n;
  MatrixXd tableau(m+1, n+1);
  for (int i = 0; i < m + 1; i++)
      for (int j = 0; j < n + 1; j++)
        file >> tableau(i, j);
  file.close();
  return tableau;
}

auto initialize(int m, int n) {
  VectorXd x(n), y(m), s(n);
  double mu;
  std::random_device r;
  std::default_random_engine engine(r());
  std::uniform_real_distribution<> u_dist(-1.0, 1.0);
  double positive_lower = std::nextafter(0, std::numeric_limits<double>::max());
  // Positive uniform distribution
  std::uniform_real_distribution<> u_dist_pos(positive_lower, 1.0);
  for(int i = 0; i < n; i++) {
    x(i) = u_dist_pos(engine);
    s(i) = u_dist_pos(engine);
  }
  for (int i = 0; i < m; i++)
    y(i) = u_dist(engine);
  mu = u_dist_pos(engine);
  return std::make_tuple(x, y, s, mu);
}

double get_alpha(const VectorXd& vec, const VectorXd& delta_vec) {
  assert(vec.size() == delta_vec.size());
  int size = vec.size();
  double alpha = std::numeric_limits<double>::max();
  bool all_delta_nonnegative= true;
  double ratio;
  for(int i = 0; i < size; i++)
    if(delta_vec(i) < 0 && (ratio = -(vec(i) / delta_vec(i))) < alpha) {
      alpha = ratio;
      all_delta_nonnegative = false;
    }
  return all_delta_nonnegative ? 1.0 : alpha;
}

auto interior_point(const MatrixXd& A, const VectorXd& b, const VectorXd& c,
                    double eps = 10e-6, double max_norm = 10e6,
                    int k_max = 1000) {
  int m = A.rows(), n = A.cols();
  assert(m == b.size());
  assert(n == c.size());
  double sq_norm, max_sq_norm = std::pow(max_norm, 2);
  constexpr double fraction = 1 - 10e-7;
  double theta = (n > 13) ? (1 - 3.5 / std::sqrt(n)) : 0.5;
  auto [x, y, s, mu] = initialize(m, n);
  int k = 0;
  do {
    MatrixXd S = s.asDiagonal();
    MatrixXd D = (x.array() / s.array()).matrix().asDiagonal();
    VectorXd rho_P = b - A * x;
    VectorXd rho_D = c - A.transpose() * y - s;
    VectorXd v = (mu - (x.array() * s.array())).matrix().transpose();
    VectorXd delta_y = -((A * D * A.transpose()).inverse() *
                         (A * S.inverse() * v - A * D * rho_D - rho_P));
    VectorXd delta_s = -(A.transpose() * delta_y) + rho_D;
    VectorXd delta_x = S.inverse() * v - D * delta_s;
    double alpha = fraction * std::min(get_alpha(x, delta_x),
                                       get_alpha(s, delta_s));
    VectorXd x_new = x + alpha * delta_x;
    VectorXd y_new = y + alpha * delta_y;
    VectorXd s_new = s + alpha * delta_s;

    // Concatenate vectors.
    VectorXd joined(x.size() + y.size() + s.size());
    joined << x, y, s;
    VectorXd joined_new(x_new.size() + y_new.size() + s_new.size());
    joined_new << x_new, y_new, s_new;
    sq_norm = (joined_new - joined).squaredNorm();
    // sq_norm = std::max({
    //     (x_new - x).squaredNorm(),
    //     (y_new - y).squaredNorm(),
    //     (s_new - s).squaredNorm()
    //   });
    x = x_new;
    y = y_new;
    s = s_new;
    mu *= theta;
    k++;
  } while((x.transpose() * s > eps) &&
          (k < k_max) &&
          (sq_norm < max_sq_norm));
  if(x.transpose() * s < eps)
    return std::make_tuple(x, y);
  else if(sq_norm > max_sq_norm)
    throw std::invalid_argument("Algorithm did not converge. Norm exceeded.");
  else
    throw std::invalid_argument("Algorithm did not converge."
                                "Number of maximum iterations exceeded.");
}

int main(int argc, char** argv) {
  // Process command-line arguments
  std::string file_path = "";
  std::vector<std::string> args(argv + 1, argv + argc);
  for(auto arg = args.begin(); arg != args.end(); arg++) {
    if(*arg == "-i")
      file_path = *(++arg);
  }
  if(file_path.empty()) {
    std::cout << "USAGE: pdip -i <file>" << std::endl;
    return EXIT_FAILURE;
  }

  MatrixXd tableau;
  try {
    tableau = read_tableau(file_path);
  }
  catch(std::ifstream::failure& ex) {
    std::cout << ex.what() << std::endl;
    return EXIT_FAILURE;
  }
  auto start = std::chrono::steady_clock::now();

  int m = tableau.rows() - 1, n = tableau.cols() - 1;
  MatrixXd A = tableau(seq(0, m-1), seq(0, n-1));
  VectorXd b = tableau(seq(0, m-1), n);
  VectorXd c = tableau(m, seq(0, n-1));

  VectorXd x_sum = VectorXd::Zero(n);
  VectorXd y_sum = VectorXd::Zero(m);
  int n_runs = 30;
  for (int i = 0; i < n_runs; i++) {
    auto [x, y] = interior_point(A, b, c, 10e-10);
    x_sum += x;
    y_sum += y;
  }

  std::cout << "Average solutions after " << n_runs << " executions:"
            << std::endl;
  VectorXd x_avg = x_sum.transpose().array() / n_runs;
  VectorXd y_avg = y_sum.transpose().array() / n_runs;

  auto end = std::chrono::steady_clock::now();

  std::cout << "x:" << std::endl << x_avg << std::endl;
  std::cout << std::endl;
  std::cout << "y:" << std::endl << y_avg << std::endl;
  std::cout << std::endl;
  std::cout << "optimum:" << std::endl << x_avg.dot(c) << std::endl;
  std::cout << std::endl;
  std::cout << "Running time: "
            << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() / n_runs
            << '\n';
  return EXIT_SUCCESS;
}


#ifndef RKIMPLEMENTER

#define RKIMPLEMENTER

#include <Eigen/Dense>
#include <vector>

/**
 *
 * Implementation of an explicit Runge Kutta Solver. See the documentation for more information.
 *
 */
template <class Step> class ExplicitRungeKuttaIntegrator {

    public:
        /**
         * Constructor for the ExplicitRungeKuttaIntegrator
         *
         * @param A the coefficients matrix A of a butcher scheme
         * @param b the weights vector for a butcher scheme. Note that we are taking a column vector whereas in a butcher tableau this would be a row vector (though doesn't matter if one uses VectorXd in Eigen).
         */
        ExplicitRungeKuttaIntegrator(const Eigen::MatrixXd &A, const Eigen::VectorXd &b):A(A),b(b),size(A.cols()){
        }

        /**
         * The solve methods applies an explicit Runge Kutta method to a given ODE
         *
         * @param f the function we are integrating over
         * @param time the time interval we want to integrate over
         * @param y0 the initial state of the system
         * @param steps the number of integration steps we would like to make (number of steps equals number of runge kutta method evaluations)
         *
         * @return a std::vector of states, one for every integration step performed. The first step will be the supplied y0.
         */
        template<typename Function>
        std::vector<Step> solve(Function &&f, double time, const Step &y0, unsigned int steps){

            std::vector<Step> stepsVector;

            // as advertised the first position will be our initial state
            stepsVector.push_back(y0);

            // knowing the total time and number of steps allows us to calculate the time we integrate over every step
            double h = time / steps;

            // now we call the solver "steps" times to do the actual integration
            for(unsigned int i = 0; i < steps; i++){
                // integrate and directly add to our list
                stepsVector.push_back(iteration(f,stepsVector.back(),h));
            }

            return stepsVector;

        }

    private:
        /**
         * This function computes on single runge kutta step and returns its result.
         *
         * @param f the function we are integrating over
         * @param y0 the starting value (the initial value or the previous step's result)
         * @param h the step size
         *
         * @return the computation of one runge kutta step
         */
        template<typename Function>
        Step iteration(Function &&f, Step &y0, const double h){

            // initialize current step as previous step
            Step y1 = y0;

            // temporary vector to store the increments
            std::vector<Step> increments;

            // calculate an increment per loop iteration
            for(unsigned int i = 0; i < size; i++){

                Step increment = y0;
                // second loop to account for dependency of current increments on previous increments
                for(unsigned int k = 0; k < i; k++){
                    increment += h*A(i,k) * increments[k];
                }

                // store the increment
                increments.push_back(f(increment));
            }

            // now we add the increments with correct weights to y0 which we already copied over to y1
            for(unsigned int i = 0; i < size; i++){
                y1 += h*b(i) *increments[i];
            }

            return y1;

        }

        const Eigen::MatrixXd A;
        const Eigen::VectorXd b;
        unsigned int size;
};

template <typename Step> class ImplicitRungeKuttaIntegrator {

    public:
        ImplicitRungeKuttaIntegrator(const Eigen::MatrixXd &A, const Eigen::VectorXd &b) : A(A),b(b),size(A.cols()){
        }

        /**
         * The solve methods applies an implicit Runge Kutta method to a given ODE
         *
         * @param f the function we are integrating over
         * @param the Jacobian of f, needed for finding 0 (newton method) to solve for stages.
         * @param time the time interval we want to integrate over
         * @param y0 the initial state of the system
         * @param steps the number of integration steps we would like to make (number of steps equals number of runge kutta method evaluations)
         *
         * @return a std::vector of states, one for every integration step performed. The first step will be the supplied y0.
         */
        template<typename Function, typename Jacobian>
        std::vector<Step> solve(Function &&f, Jacobian && J, double time, const Step &y0, unsigned int steps){

            std::vector<Step> stepsVector;

            // as advertised the first position will be our initial state
            stepsVector.push_back(y0);

            // knowing the total time and number of steps allows us to calculate the time we integrate over every step
            double h = time / steps;

            // now we call the solver "steps" times to do the actual integration
            for(unsigned int i = 0; i < steps; i++){
                // integrate and directly add to our list
                stepsVector.push_back(iteration(f,stepsVector.back(),h));
            }

            return stepsVector;

        }

    private:
        /**
         * This function computes on single runge kutta step and returns its result. This method uses
         *
         * @param f the function we are integrating over
         * @param y0 the starting value (the initial value or the previous step's result)
         * @param h the step size
         *
         * @return the computation of one runge kutta step
         */
        template<typename Function, typename Jacobian>
        Step iteration(Function &&f, Jacobian && J, Step &y0, const double h){

            throw "not yet implemented";
            // TODO

        }

    private:
        const Eigen::MatrixXd A;
        const Eigen::VectorXd b;
        unsigned int size;
};

// a collection of optimization methods needed for implicit runge-kutta methods
namespace OptimizationMethods{

    /**
     * Takes a function f, its derivative J as well as a starting point x0 and finds the root x' with f(x') = 0
     * Source: Adapted from 'C++ code 8.4.4.5' of the book https://www.sam.math.ethz.ch/~grsam/NCSE19/NumCSE_Lecture_Document.pdf
     *
     * @param f the function whose root we want to find
     * @param J the jacobian of f
     * @param x0 the starting value for the newton iteration
     * @param reltol if the difference between two iterations is smaller than rtol*x', with x' a likely root, we stop the iteration
     * @param abstol if the difference between two iterations is smaller than abstol we stop the iteration
     *
     * @exception if the function does not converge an error will be thrown
     *
     *
     * @return the root of f (the value x where f(x) = 0)
     *
     */
    template<typename Step, typename Function, typename Jacobian>
    Step dampedNewton(Function &&f, Jacobian &&J, Step x0, double reltol = 1e-7, double abstol=1e-8){

        // first we check the dimensionality of the function
        uint32_t n = x0.size();
        Step correction(n), tentativeCorrection(n);
        Step x(n);
        Step xTemp(n);
        double correctionNorm, tentativeCorrectionNorm;

        // convergence variables
        double lambda = 1.0;
        double lmin = 1E-3;

        do {
            // calculate the difference to the next iterate
            auto jacobianLUFactorized = J(x).lu();
            correction = jacobianLUFactorized.solve(f(x));
            correctionNorm = correction.norm();

            do {
                // reduction of damping factor
                lambda /= 2;
                // check for non convergence
                if(lambda < lmin){
                    throw "No convergence";
                }
                // tentative next iterate
                xTemp = x-lambda*correction;
                tentativeCorrection = jacobianLUFactorized.solve(f(xTemp));
                tentativeCorrectionNorm = tentativeCorrection.norm();
            } while(tentativeCorrectionNorm > (1-lambda/2)*correctionNorm);
            // we accept the new step
            x = xTemp;
            // we somewhat reduce the damping
            lambda = std::min(2*lambda,1.0);
        } while((tentativeCorrectionNorm > reltol*x.norm()) && tentativeCorrectionNorm > abstol);
    }
}

#endif


#include <cmath>
#include <memory>

#include <Eigen/Geometry>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/float64.hpp"
#include "sensor_msgs/msg/imu.hpp"

using namespace std::chrono_literals;
using std::placeholders::_1;

double normaliseAngle(double x)
{
  x = fmod(x + M_PI, 2*M_PI);
  if (x < 0)
    x += 2*M_PI;
  return x - M_PI;
}

Eigen::Vector3d ToEulerAngles(Eigen::Quaterniond q)
{
  Eigen::Vector3d angles;

  // roll (x-axis rotation)
  double sinr_cosp = 2 * (q.w() * q.x() + q.y() * q.z());
  double cosr_cosp = 1 - 2 * (q.x() * q.x() + q.y() * q.y());
  angles(0) = std::atan2(sinr_cosp, cosr_cosp);

  // pitch (y-axis rotation)
  double sinp = 2 * (q.w() * q.y() - q.z() * q.x());
  if (std::abs(sinp) >= 1)
    angles(1) = std::copysign(M_PI / 2, sinp); // use 90 degrees if out of range
  else
    angles(1) = std::asin(sinp);

  // yaw (z-axis rotation)
  double siny_cosp = 2 * (q.w() * q.z() + q.x() * q.y());
  double cosy_cosp = 1 - 2 * (q.y() * q.y() + q.z() * q.z());
  angles(2) = std::atan2(siny_cosp, cosy_cosp);

  return angles;
}

class Imu2rpy : public rclcpp::Node
{
public:
  Imu2rpy()
      : Node("imu_to_rpy")
  {
    imu_sub_ = this->create_subscription<sensor_msgs::msg::Imu>(
        "imu", 10, std::bind(&Imu2rpy::imu_callback, this, _1));

    roll_publisher_ = this->create_publisher<std_msgs::msg::Float64>("roll", 10);
    pitch_publisher_ = this->create_publisher<std_msgs::msg::Float64>("pitch", 10);
    yaw_publisher_ = this->create_publisher<std_msgs::msg::Float64>("yaw", 10);

    roll_rate_publisher_ = this->create_publisher<std_msgs::msg::Float64>("roll_rate", 10);
    pitch_rate_publisher_ = this->create_publisher<std_msgs::msg::Float64>("pitch_rate", 10);
    yaw_rate_publisher_ = this->create_publisher<std_msgs::msg::Float64>("yaw_rate", 10);
  }

private:

  void imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg)
  {
    Eigen::Quaterniond q(msg->orientation.w, msg->orientation.x, msg->orientation.y, msg->orientation.z);
    //auto euler = q.toRotationMatrix().eulerAngles(2, 1, 0);
    auto euler = ToEulerAngles(q);

    double roll = euler(0);
    double pitch = euler(1);
    double yaw = euler(2);

    //std::string message1 = "w=" + std::to_string(q.w()) + ", x= " + std::to_string(q.x()) +
    //  ", y= " + std::to_string(q.y()) + ", z= " + std::to_string(q.z());
    //RCLCPP_INFO(this->get_logger(), "Incoming quaternion '%s'", message1.c_str());

    //std::string message2 = "roll=" + std::to_string(roll) + ", pitch= " + std::to_string(pitch) + ", yaw= " + std::to_string(yaw);
    //RCLCPP_INFO(this->get_logger(), "Converted euler angles '%s'", message2.c_str());

    //Eigen::Quaterniond q2 = Eigen::AngleAxisd(roll, Eigen::Vector3d::UnitX()) *
    //  Eigen::AngleAxisd(pitch, Eigen::Vector3d::UnitY()) *
    //  Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ());

    //std::string message3 = "w=" + std::to_string(q2.w()) + ", x= " + std::to_string(q2.x()) +
    //  ", y= " + std::to_string(q2.y()) + ", z= " + std::to_string(q2.z());
    //RCLCPP_INFO(this->get_logger(), "Converted quaternion '%s'\n", message3.c_str());

    auto roll_msg = std_msgs::msg::Float64();
    roll_msg.data = roll;

    auto pitch_msg = std_msgs::msg::Float64();
    pitch_msg.data = pitch;

    auto yaw_msg = std_msgs::msg::Float64();
    yaw_msg.data = yaw;

    auto roll_rate_msg = std_msgs::msg::Float64();
    roll_rate_msg.data = msg->angular_velocity.x;

    auto pitch_rate_msg = std_msgs::msg::Float64();
    pitch_rate_msg.data = msg->angular_velocity.y;

    auto yaw_rate_msg = std_msgs::msg::Float64();
    yaw_rate_msg.data = msg->angular_velocity.z;

    roll_publisher_->publish(roll_msg);
    pitch_publisher_->publish(pitch_msg);
    yaw_publisher_->publish(yaw_msg);
    roll_rate_publisher_->publish(roll_rate_msg);
    pitch_rate_publisher_->publish(pitch_rate_msg);
    yaw_rate_publisher_->publish(yaw_rate_msg);
  }

  rclcpp::Subscription<sensor_msgs::msg::Imu>::SharedPtr imu_sub_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr roll_publisher_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr pitch_publisher_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr yaw_publisher_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr roll_rate_publisher_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr pitch_rate_publisher_;
  rclcpp::Publisher<std_msgs::msg::Float64>::SharedPtr yaw_rate_publisher_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<Imu2rpy>());
  rclcpp::shutdown();
  return 0;
}


/**
 * @file categorical.hpp
 * @author Vahid Bastani
 *
 * implementation of categorical (multinomial) distribution
 */
#ifndef SSMPACK_DISTRIBUTION_CATEGORICAL_HPP
#define SSMPACK_DISTRIBUTION_CATEGORICAL_HPP

#include "ssmkit/random/generator.hpp"

#include <armadillo>

namespace ssmkit {
namespace distribution {

/** Categorical (multinomial) distribution
 *
 * \f{equation}{p(x|\mathbf{p}) = \mathcal{Cat}(\mathbf{p})\f}
 * where \f$\mathbf{p} = [p_0, \cdots, p_N]^T\f$ and \f$p(x=i|\mathbf{p}) = p_i\f$
 */
class Categorical {
  //! Type of the parameter vector \f$\mathbf{p}\f$.
  using TParameterVar = arma::vec;
  //! Type of the random variable \f$x\f$.
  using TValueType = unsigned int;

 private:
 //! The parameter vector \f$\mathbf{p}\f$.
 TParameterVar param_;
 //! Cumulative distribution function.
 TParameterVar cdf_;
 //! Core random number distribution.
 std::uniform_real_distribution<double> uniform_;
 //! Length of the parameter vector \f$N+1\f$.
 TValueType max_;

 public:
  //! Default constructor \f$\mathbf{p}=[1.0]\f$.
  Categorical() : Categorical(arma::ones<arma::vec>(1)) {}
  /** Constructor
   * @param parameter The parameter vector \f$\mathbf{p}\f$.
   * @pre The sum of the elements of \p parameter should be 1.0.
   */
  Categorical(TParameterVar parameters)
      : param_(std::move(parameters)) {calcCDF(); calcMax();}
  //! Return a random variable from the distribution.
  TValueType random() {
    double rv = uniform_(random::Generator::get().getGenerator());
    for (TValueType i = 0; i < max_; ++i)
      if (rv < cdf_(i))
        return i;

    return 0; // this line never get reached
  }
  //! Return likelihood of the given random variable
  double likelihood(const TValueType &rv) {
    return param_(rv);
  }

  /** Change parameters of the distribution
   * @param parameter The parameter vector \f$\mathbf{p}\f$.
   * @pre The sum of the elements of \p parameter should be 1.0.
   */
  Categorical &parameterize(const TParameterVar & param){
    param_ = param;
    calcCDF();
    calcMax();
    return *this;
  }

  private:
   void calcCDF() { cdf_ = arma::cumsum(param_); }
   void calcMax() { max_ = param_.n_rows; }
};

} // namespace ssmkit
} // namespace distribution

#endif //SSMPACK_DISTRIBUTION_CATEGORICAL_HPP


#pragma once

#include "numeric/dense_matrix.hpp"
#include <Eigen/Geometry>

/// \file jacobian_determinant.hpp

namespace neon
{
namespace detail
{
template <typename matrix_type>
[[nodiscard]] auto jacobian_determinant(matrix_type const& jacobian)
{
    return jacobian.determinant();
}

[[nodiscard]] inline auto jacobian_determinant(matrix32 const& jacobian)
{
    return jacobian.col(0).cross(jacobian.col(1)).norm();
}

[[nodiscard]] inline auto jacobian_determinant(matrix31 const& jacobian) { return jacobian.norm(); }
}

/**
 * Compute the Jacobian determinant for a volume or surface mapping.  In three
 * dimensions it performs the following operation:
 * \f{align*}{
 *     j &= \det \begin{bmatrix}
 *             \frac{\partial x}{\partial \xi} & \frac{\partial x}{\partial \eta} & \frac{\partial
 * x}{\partial \zeta} \\
 *             \frac{\partial y}{\partial \xi} & \frac{\partial y}{\partial \eta} & \frac{\partial
 * y}{\partial \zeta} \\ \frac{\partial z}{\partial \xi} & \frac{\partial z}{\partial \eta} &
 * \frac{\partial z}{\partial \zeta} \end{bmatrix} \f}
 *
 * However the determinant for non-square Jacobian is not defined.  When there
 * is a mapping from \f$ \mathbb{R}^3 \f$ to \f$ \mathbb{R}^2 \f$ the Jacobian
 * `determinant' can be computed by
 * \f{align*}{
 *     j &= || \mathbf{x}_{,\mathbf{\xi}} \times \mathbf{x}_{,\mathbf{\eta}} ||
 * \f}
 * where the Jacobian is given by the non-square matrix
 * \f{align*}{
 *    & \begin{bmatrix}
 *          \frac{\partial x}{\partial \xi} & \frac{\partial x}{\partial \eta} \\
 *          \frac{\partial y}{\partial \xi} & \frac{\partial y}{\partial \eta} \\
 *          \frac{\partial z}{\partial \xi} & \frac{\partial z}{\partial \eta}
 *      \end{bmatrix}
 * \f}
 * This is useful when the surface is described by a two dimensional
 * element but there are only three dimensional coordinates \cite Anton1998.
 */
template <typename matrix_expression>
[[nodiscard]] inline auto jacobian_determinant(matrix_expression const& jacobian)
{
    return detail::jacobian_determinant(jacobian.eval());
}
}


#pragma once

#include <armadillo>
#include <iostream>
#include <unordered_map>
#include <cmath>
#include <Eigen/Dense>
// using Eigen::ArrayXi;
using namespace std;
// using namespace arma;
using ArrayXL = Eigen::Array<int64_t, Eigen::Dynamic, 1>;

struct shannonEntropy {

    typedef unordered_map<uint64_t, size_t> histoMap;
//    typedef unordered_map<int, double> probMap;

    static shannonEntropy::histoMap calcDistribution(const ArrayXL &seq) {
        shannonEntropy::histoMap histo;
        for (const uint64_t &v: seq) {
            shannonEntropy::histoMap::iterator it = histo.find(v);
            if (it == histo.end()) {
                histo.insert(std::make_pair(v, 1));
            }else{
                it->second = it->second + 1;
            }
        }
        return histo;
    }

    static double calcProbability(const shannonEntropy::histoMap &histo, const ArrayXL &seq) {
        double scale = 1.0 / seq.size();
        double H=0;
        for(auto v: histo) {
            double prob = v.second * scale;
            H = H - (prob * log2(prob));
        }
        return H;
    }

    static double calc(const ArrayXL &seq) {
        shannonEntropy::histoMap histo = shannonEntropy::calcDistribution(seq);
//        for(auto v: histo) {
//            cout << v.first << ": " << v.second << endl;
//        }
        double prob = shannonEntropy::calcProbability(histo, seq);
        return prob;
    }
};


#pragma once
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <vector>

#include "stiffness_matrix.hpp"

//! Sparse Matrix type. Makes using this type easier.
typedef Eigen::SparseMatrix<double> SparseMatrix;

//! Used for filling the sparse matrix.
typedef Eigen::Triplet<double> Triplet;

//----------------AssembleMatrixBegin----------------
//! Assemble the stiffness matrix
//! for the linear system
//!
//! @param[out] A will at the end contain the Galerkin matrix
//! @param[in] vertices a list of triangle vertices
//! @param[in] triangles a list of triangles
template<class Matrix>
void assembleStiffnessMatrix(Matrix& A, const Eigen::MatrixXd& vertices,
                            const Eigen::MatrixXi& triangles)
{

    const int numberOfElements = triangles.rows();
    A.resize(vertices.rows(), vertices.rows());

    std::vector<Triplet> triplets;

    triplets.reserve(numberOfElements * 3 * 3);
    //// ANCSE_START_TEMPLATE
    for (int i = 0; i < numberOfElements; ++i) {
        auto& indexSet = triangles.row(i);

        const auto& a = vertices.row(indexSet(0));
        const auto& b = vertices.row(indexSet(1));
        const auto& c = vertices.row(indexSet(2));

        Eigen::Matrix3d stiffnessMatrix;
        computeStiffnessMatrix(stiffnessMatrix, a, b, c);

        for (int n = 0; n < 3; ++n) {
            for (int m = 0; m < 3; ++m) {
                auto triplet = Triplet(indexSet(n), indexSet(m), stiffnessMatrix(n, m));
                triplets.push_back(triplet);
            }
        }
    }
    //// ANCSE_END_TEMPLATE
    A.setFromTriplets(triplets.begin(), triplets.end());
}
//----------------AssembleMatrixEnd----------------


#include <boost/multiprecision/cpp_int.hpp>
#include <cmath>
#include <iostream>
#include <string>

using namespace std;
using boost::multiprecision::cpp_int;

/*
We need to find the n-digit integers that are also an n'th power. i.e. a 3 digit number that is also a third power.
So the integers we need to find are.. 10 ^ (n - 1) <= x ^ n < 10 ^ n;

Or in other words if the function L gets length of an integer then we need:
L(x ^ n) = n.

If our base is x then the maximum it can be is 9 and lowest it can be is 1.
Then we just need to get maximum value of n, which will be given by taking the log and solving for n >.
For x = 9, largest possible n = 22, so we have our upperlimits.
*/

int digitCount(cpp_int n) {
	int digits = 0;
	while(n) {
		n /= 10;
		digits++;
	}
	return digits;
}

int main(int argc, char *argv[]) {
	int result = 0;
	for(int x = 1; x < 10; x++) {
		for(int n = 1; n < 22; n++) {
			if(digitCount(boost::multiprecision::pow((cpp_int)x, n)) == n) {
				result++;
			}
		}
	}
	cout << result << endl;
	return 0;
}


//
//  EuropeanOption.cpp
//  VI.3 Option Pricing
//
//  Created by Zhehao Li on 2020/4/27.
//  Copyright © 2020 Zhehao Li. All rights reserved.
//

#include "EuropeanOption.hpp"
#include <boost/math/distributions/normal.hpp>
#include <iostream>
#include <cmath>

/* Private Memeber Functions */
double EuropeanOption::CallPrice(double U) const{                   // U is the price of underlying assets
    double tmp = sig * sqrt(T);
    double d1 = ( log(U / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;
    double d2 = d1 - tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return ( U * exp((b - r) * T) * cdf(stdNormal, d1) ) - ( K * exp(- r * T) * cdf(stdNormal, d2) );
}

double EuropeanOption::PutPrice(double U) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(U / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;
    double d2 = d1 - tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return ( K * exp(- r * T) * cdf(stdNormal, -d2) ) - ( U * exp((b - r) * T) * cdf(stdNormal, -d1) );
}

double EuropeanOption::CallDelta(double U) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(U / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return exp((b - r) * T) * cdf(stdNormal, d1);
}

double EuropeanOption::PutDelta(double U) const{
    double tmp = sig * sqrt(T);
    double d1 = ( log(U / K) + (b + (sig * sig) * 0.5 ) * T ) / tmp;

    boost::math::normal_distribution<double> stdNormal(0.0, 1.0);   // Create a normal dstn object

    return exp((b - r) * T) * ( cdf(stdNormal, d1) - 1.0 );
}

/* Public Memeber Functions */
// Default constructor
EuropeanOption::EuropeanOption() : r(0.05), sig(0.2), K(110.0), T(0.5), b(0.05), optType("C") {}

// Copy constructor
EuropeanOption::EuropeanOption(const EuropeanOption & option2) : r(option2.r), sig(option2.sig), K(option2.K), T(option2.T), b(option2.b), optType(option2.optType) {}

// Constructor with values
EuropeanOption::EuropeanOption(const std::string & optionType) : optType(optionType) {
    if (optType == "c"){
        optType = "C";
    }
}

// Destructor
EuropeanOption::~EuropeanOption() {}

// Assignment Operators
EuropeanOption & EuropeanOption::operator=(const EuropeanOption & option2) {
    if (this == &option2) {
        return *this;
    }
    else{
        r   = option2.r;
        sig = option2.sig;
        K   = option2.K;
        T   = option2.T;
        b   = option2.b;
        optType = option2.optType;

        return *this;
    }
}

// Accessing functions
double EuropeanOption::Price(double U) const{
    if (optType == "C") {
        std::cout << "Call Price..";
        return CallPrice(U);
    }
    else{
        std::cout << "Put Price..";
        return PutPrice(U);
    }
}

double EuropeanOption::Delta(double U) const{
    if (optType == "C") {
        std::cout << "Call Delta..";
        return CallDelta(U);
    }
    else{
        std::cout << "Put Delta..";
        return PutDelta(U);
    }
}

// Modifier functions
void EuropeanOption::toggle(){
    if (optType == "C") {
        optType = "P";
    }
    else{
        optType = "C";
    }
}

Level_9/VI.3 Option Pricing/VI.3 Option Pricing/EuropeanOption.cpp


// Boost.Geometry
// QuickBook Example
// Copyright (c) 2018, Oracle and/or its affiliates
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//[discrete_frechet_distance
//` Calculate Similarity between two geometries as the discrete frechet distance between them.

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>

int main()
{
    typedef boost::geometry::model::d2::point_xy<double> point_type;
    typedef boost::geometry::model::linestring<point_type> linestring_type;

    linestring_type ls1, ls2;
    boost::geometry::read_wkt("LINESTRING(0 0,1 1,1 2,2 1,2 2)", ls1);
    boost::geometry::read_wkt("LINESTRING(1 0,0 1,1 1,2 1,3 1)", ls2);

    double res = boost::geometry::discrete_frechet_distance(ls1, ls2);

    std::cout << "Discrete Frechet Distance: " << res << std::endl;

    return 0;
}

//]

//[discrete_frechet_distance_output
/*`
Output:
[pre
Discrete Frechet Distance:  1.41421
]
*/
//]


#include "AKS.hpp"

#include "AKSCoefficient.hpp"
#include "AKSPolynomial.hpp"
#include "GMPWrappers.hpp"

#include <mpfr.h>
#include <NTL/ZZ.h>
#include <NTL/RR.h>
#include <NTL/ZZ_pE.h>
#include <NTL/ZZ_pEX.h>

#include <cassert>
#include <cmath>

namespace tfg::aks
{
namespace steps
{
namespace
{

auto isOrderBiggerThan(mpz_class const& n, std::size_t r, std::size_t threshold) -> bool
{
    auto temp = 1_mpz;

    for (auto i = std::size_t{1}; i <= threshold; ++i)
    {
        temp *= n;
        temp %= r;

        if (temp == 1)
        {
            return false;
        }
    }

    return true;
}

auto phi(std::size_t n) -> std::size_t
{
    auto const top = static_cast<std::size_t>(std::sqrt(n));
    auto result = n;

    for (auto p = std::size_t{2}; p <= top; ++p)
    {
        if (n % p == 0)
        {
            while (n % p == 0)
            {
                n /= p;
            }

            result -= result / p;
        }
    }

    if (n > 1)
    {
        result -= result / n; // NOLINT
    }

    return result;
}

auto calculateUpperBound(mpz_class const& n, std::size_t r) -> std::size_t
{
    // log_2(n)
    mpfr_t result;
    mpfr_init_set_z(result, n.get_mpz_t(), MPFR_RNDU); // NOLINT
    mpfr_log2(result, result, MPFR_RNDU);              // NOLINT

    // sqrt(phi(r))
    mpfr_t sqrtPhiR;
    mpfr_init_set_ui(sqrtPhiR, phi(r), MPFR_RNDU); // NOLINT
    mpfr_sqrt(sqrtPhiR, sqrtPhiR, MPFR_RNDU);      // NOLINT

    // log_2(n) * sqrt(phi(r))
    mpfr_mul(result, result, sqrtPhiR, MPFR_RNDU); // NOLINT

    // Return floor(log_2(n) * sqrt(phi(r)))
    return mpfr_get_ui(result, MPFR_RNDD); // NOLINT
}

} // namespace unnamed

auto step1(mpz_class const& n) -> bool
{
    return gmp::isPerfectPower(n);
}

auto step2(mpz_class const& n) -> std::size_t
{
    auto const threshold = [&n]
    {
        // Use MPFR to calculate a more accurate threshold using floating point
        // computations. We round towards +infinity for a more conservative
        // threshold.
        mpfr_t thresholdMPFR;
        mpfr_init_set_z(thresholdMPFR, n.get_mpz_t(), MPFR_RNDU); // NOLINT
        mpfr_log2(thresholdMPFR, thresholdMPFR, MPFR_RNDU);       // NOLINT
        mpfr_sqr(thresholdMPFR, thresholdMPFR, MPFR_RNDU);        // NOLINT

        // Return the result from MPFR as an unsigned long (this threshold is
        // unlikely to be bigger that 32-bits long, let alone 64-bits).
        // Round towards -infinity to get the floored value
        return mpfr_get_ui(thresholdMPFR, MPFR_RNDD); // NOLINT
    }();

    // r = log^2(n) + 2 is the first r such that ord_r(n) can be higher than
    // log^2(n).
    //
    // If r <= log^2(n) + 1, then ord(r) <= phi(r) < r - 1 <= log^2(n)
    for (auto r = threshold + 2;; ++r)
    {
        if (isOrderBiggerThan(n, r, threshold))
        {
            return r;
        }
    }
}

auto step3(mpz_class const& n, std::size_t r) -> bool
{
    for (auto a = std::size_t{2}; a <= r; ++a)
    {
        auto const result = gmp::gcd(a, n);

        if (1 < result && result < n)
        {
            return true;
        }
    }

    return false;
}

auto step4(mpz_class const& n, std::size_t r) -> bool
{
    return n <= r;
}

namespace impl
{

auto step5Direct(mpz_class const& n, std::size_t r) -> bool
{
    // First we calculate the upper bound of the loop
    auto const top = calculateUpperBound(n, r);

    // Prepare the environment to work mod(X^r - 1, n)
    detail::AKSCoefficient::setModule(n);
    detail::AKSPolynomial::setModuleDegree(r);

    // We create both polynomials outside the loop to avoid reallocations.
    auto lhs = detail::AKSPolynomial{};
    auto rhs = detail::AKSPolynomial{};

    // We are also going to create a temporary polynomial to avoid even more
    // reallocations.
    auto temp = detail::AKSPolynomial{0_mpz, 1_mpz};

    // X^n mod(X^r - 1, n) instead of X^n + a mod(X^r - 1, n) so it doesn't
    // depend on a, therefore calculating it only once.
    temp.pow(detail::AKSCoefficient::getModule(), rhs);

    for (auto a = std::size_t{1}; a <= top; ++a)
    {
        // (X + a)^n - a mod(X^r - 1, n)
        temp.setCoefficient(0, mpz_class{a});
        temp.pow(detail::AKSCoefficient::getModule(), lhs);
        lhs -= mpz_class{a};

        if (lhs != rhs)
        {
            return true;
        }
    }

    return false;
}

auto step5NTL(mpz_class const& n, std::size_t r) -> bool
{
    // First we calculate the upper bound of the loop
    auto const top = calculateUpperBound(n, r);
    // Convert GMP's integer to NTL's integer and set ring to Z_n
    auto const nNTL = NTL::conv<NTL::ZZ>(n.get_str().c_str());
    NTL::ZZ_p::init(nNTL);

    // Define the polynomial module X^r - 1
    auto const module = NTL::ZZ_pXModulus{NTL::ZZ_pX{static_cast<long>(r), 1} - 1};
    // Define X^n mod X^r - 1
    auto const rhs = [&nNTL, &module]
    {
        auto result = NTL::ZZ_pX{1, 1};
        NTL::PowerMod(result, result, nNTL, module);

        return result;
    }();

    for (auto a = std::size_t{1}; a <= top; ++a)
    {
        // (X + a)^n - a mod(X^r - 1, n)
        auto lhs = NTL::ZZ_pX{1, 1};
        lhs += static_cast<long>(a);
        NTL::PowerMod(lhs, lhs, nNTL, module);
        lhs -= static_cast<long>(a);

        if ((lhs != rhs) != 0)
        {
            return true;
        }
    }

    return false;
}

} // namespace impl

auto step5(mpz_class const& n, std::size_t r) -> bool
{
    return impl::step5NTL(n, r);
}

auto step6() -> bool
{
    return true;
}

} // namespace steps

auto isPrime(mpz_class const &n) -> bool
{
    // Step 1: Check if n is a perfect power
    if (steps::step1(n))
    {
        return false;
    }

    // Step 2: Find smallest r such that ord_r(n) > log^2(n)
    auto const r = steps::step2(n);

    // Step 3: Check if 1 < (a, n) < n for some a <= r
    if (steps::step3(n, r))
    {
        return false;
    }

    // Step 4: Check if n <= r
    if (steps::step4(n, r))
    {
        return true;
    }

    // Step 5: Check if the polynomial identities are not satisfied
    if (steps::step5(n, r))
    {
        return false;
    }

    // Step 6: If we reach this point, the number is prime.
    return steps::step6();
}

} // namespace tfg::aks


#include "KolmogorovSmirnov.hxx"
#include <boost/format.hpp>
#include <cmath>
#include <iostream>

std::vector<float> KolmogorovSmirnov::cdf(const std::vector<int> & S, int & N)
{
  std::vector<float> ret;

  N = 0;
  for(int i = 0; i < S.size(); i++) {
    N += S[i];
  }

  float fscale = 1.0 / ((float) N);

  float cdf = 0.0;
  for(int i = 0; i < S.size(); i++) {
    cdf += ((float) S[i]) * fscale;
    ret.push_back(cdf);
  }

  return ret;
}

bool KolmogorovSmirnov::test(float alpha,
			     const std::vector<int> & S,
			     const std::vector<int> & X)
{
  int N, M;
  std::vector<float> Scdf = cdf(S, N);
  std::vector<float> Xcdf = cdf(X, M);

  float max_sep = 0.0;
  for(int i = 0; i < S.size(); i++) {
    float sep = fabs(Scdf[i] - Xcdf[i]);
    max_sep = (max_sep > sep) ? max_sep : sep;
  }

  // now is the difference significant?
  float n = (float) N;
  float m = (float) M;

  float calpha = sqrt(-0.5 * log(alpha / 2.0));

  float Dmin = calpha * sqrt((n + m) / (n*m));

  std::cerr << boost::format("Dnm = %f n = %f m = %f calpha = %f Dmin = %f\n")
    % max_sep % n % m % calpha % Dmin;

  return max_sep  > Dmin;
}