/* adcpp_eigen_forward.test.cpp
 *
 *  Created on: 21 Aug 2019
 *      Author: Fabian Meyer
 */

#include <catch2/catch.hpp>
#include <adcpp/adcpp_eigen.hpp>
#include <Eigen/Geometry>
#include <Eigen/Eigenvalues>

using namespace adcpp;

TEST_CASE("Eigen forward algorithmic differentiation")
{
    double eps = 1e-6;

    SECTION("exp")
    {
        fwd::Vector2d x;
        x << fwd::Double(3,1), fwd::Double(2,0);

        Eigen::Vector2d valExp;
        valExp <<
            std::exp(x(0).value()),std::exp(x(1).value());
        Eigen::Matrix2d jacExp;
        jacExp << std::exp(x(0).value()), 0,
            0, std::exp(x(1).value());

        fwd::Vector2d fx = x.array().exp();

        x << fwd::Double(3,0), fwd::Double(2,1);
        fwd::Vector2d fy = x.array().exp();

        REQUIRE(Approx(valExp(0)).margin(eps) == fx(0).value());
        REQUIRE(Approx(valExp(1)).margin(eps) == fx(1).value());
        REQUIRE(Approx(valExp(0)).margin(eps) == fy(0).value());
        REQUIRE(Approx(valExp(1)).margin(eps) == fy(1).value());

        REQUIRE(Approx(jacExp(0, 0)).margin(eps) == fx(0).derivative());
        REQUIRE(Approx(jacExp(1, 0)).margin(eps) == fx(1).derivative());
        REQUIRE(Approx(jacExp(0, 1)).margin(eps) == fy(0).derivative());
        REQUIRE(Approx(jacExp(1, 1)).margin(eps) == fy(1).derivative());
    }

    SECTION("singular value decomposition")
    {
        fwd::Matrix4d A;
        A << fwd::Double(2, 1), 3, 11, 5,
            1, 1, 5, 2,
            2, 1, -3, 2,
            1, 1, -3, 4;
        fwd::Vector4d b;
        b << 2, 1, -3, -3;

        fwd::Vector4d resultAct;
        Eigen::Vector4d valExp;
        Eigen::Vector4d gradExp;
        valExp << -0.5, -0.1875, 0.4375, -0.25;
        gradExp << -0.205283, 0.687338, -0.0722404, -0.117474;

        Eigen::JacobiSVD<fwd::Matrix4d, Eigen::FullPivHouseholderQRPreconditioner>
            solver(A, Eigen::ComputeFullU | Eigen::ComputeFullV);
        resultAct = solver.solve(b);

        REQUIRE(Approx(valExp(0)).margin(eps) == resultAct(0).value());
        REQUIRE(Approx(valExp(1)).margin(eps) == resultAct(1).value());
        REQUIRE(Approx(valExp(2)).margin(eps) == resultAct(2).value());
        REQUIRE(Approx(valExp(3)).margin(eps) == resultAct(3).value());

        REQUIRE(Approx(gradExp(0)).margin(eps) == resultAct(0).derivative());
        REQUIRE(Approx(gradExp(1)).margin(eps) == resultAct(1).derivative());
        REQUIRE(Approx(gradExp(2)).margin(eps) == resultAct(2).derivative());
        REQUIRE(Approx(gradExp(3)).margin(eps) == resultAct(3).derivative());
    }

    // SECTION("eigen value decomposition")
    // {
    //     fwd::Matrix4d A;
    //     A << fwd::Double(2, 1), 3, 11, 5,
    //         1, 1, 5, 2,
    //         2, 1, -3, 2,
    //         1, 1, -3, 4;
    //     fwd::Vector4d b;
    //     b << 2, 1, -3, -3;
    //
    //     Eigen::Vector4d eigvalsExp;
    //     eigvalsExp << 7.27048, -5.64984, -0.291657, 2.67103;
    //     Eigen::Vector4d eiggradExp;
    //     eiggradExp <<  0.536189,  0.463811, 0, 0;
    //
    //     Eigen::EigenSolver<fwd::Matrix4d> solver(A);
    //     fwd::Vector4d eigvals = solver.eigenvalues().real();
    //     fwd::Matrix4d eigvecs = solver.eigenvectors().real();
    //
    //     REQUIRE(Approx(eigvalsExp(0)).margin(eps) == eigvals(0).value());
    //     REQUIRE(Approx(eigvalsExp(1)).margin(eps) == eigvals(1).value());
    //     REQUIRE(Approx(eigvalsExp(2)).margin(eps) == eigvals(2).value());
    //     REQUIRE(Approx(eigvalsExp(3)).margin(eps) == eigvals(3).value());
    //
    //     REQUIRE(Approx(eiggradExp(0)).margin(eps) == eigvals(0).derivative());
    //     REQUIRE(Approx(eiggradExp(1)).margin(eps) == eigvals(1).derivative());
    //     REQUIRE(Approx(eiggradExp(2)).margin(eps) == eigvals(2).derivative());
    //     REQUIRE(Approx(eiggradExp(3)).margin(eps) == eigvals(3).derivative());
    // }

    SECTION("multiple outputs")
    {
        fwd::Vector2d x;
        x << fwd::Double(3,1), fwd::Double(2,0);

        fwd::Matrix2d c;
        c << fwd::Double(2.1), fwd::Double(3.4),
            fwd::Double(1.6), fwd::Double(2.3);

        Eigen::Vector2d valExp;
        valExp <<
            x(0).value() * c(0, 0).value() +  x(1).value() * c(0, 1).value(),
            x(0).value() * c(1, 0).value() +  x(1).value() * c(1, 1).value();
        Eigen::Matrix2d jacExp;
        jacExp << c(0, 0).value(), c(0, 1).value(),
            c(1, 0).value(), c(1, 1).value();

        fwd::Vector2d fx = c * x;

        x << fwd::Double(3,0), fwd::Double(2,1);
        fwd::Vector2d fy = c * x;

        REQUIRE(Approx(valExp(0)).margin(eps) == fx(0).value());
        REQUIRE(Approx(valExp(1)).margin(eps) == fx(1).value());
        REQUIRE(Approx(valExp(0)).margin(eps) == fy(0).value());
        REQUIRE(Approx(valExp(1)).margin(eps) == fy(1).value());

        REQUIRE(Approx(jacExp(0, 0)).margin(eps) == fx(0).derivative());
        REQUIRE(Approx(jacExp(1, 0)).margin(eps) == fx(1).derivative());
        REQUIRE(Approx(jacExp(0, 1)).margin(eps) == fy(0).derivative());
        REQUIRE(Approx(jacExp(1, 1)).margin(eps) == fy(1).derivative());
    }
}


/*
This file implements a simple neural network class and wrapppers to access it fom Python.
We use the mean square error as loss function and a sigmoid as activation function.
(We use that, if f is the sigmoid, f' = (1-f)*f.)
We assume that
	* the number of neurons in the first layer is equal to the number of inputs,
	* there is exactly one neuron in the last layer.
*/

#include <iostream>
#include <fstream>
#include <math.h>
#include <random>
#include <vector>
#include <boost/python.hpp>

using namespace std;
using namespace boost::python;

template <typename T>
vector<T> python_list_to_vector(list& l, T x){ // second argument used for template deduction
	vector<T> res;
	for(int i=0; i < len(l); i++){
		res.push_back(extract<T>(l[i]));
	}
	return res;
}

template <typename T>
list vector_to_python_list(vector<T> v){
	list res;
	for(int i=0; i<v.size(); i++){
		res.append(v[i]);
	}
	return res;
}

template <typename T>
vector<vector<T>> python_list_of_lists_to_vector(list& l, T x){ // second argument used for template deduction
	vector<vector<T>> res;
	for(int i=0; i < len(l); i++){
		vector<T> line;
		for(int j=0; j < len(l[i]); j++){
			line.push_back(extract<T>(l[i][j]));
		}
		res.push_back(line);
	}
	return res;
}

double sigmoid(double x){
	return (1. / (1. + exp(-x)));
}

class NeuralNetwork1{

	private:

		int N_layers;
		vector<int> layers;
		vector<vector<vector<double>>> weights;
		vector<vector<double>> bias;

	public:

		// constructor with random weights and bias

		void build_network(vector<int> layers_){
			N_layers = layers_.size();
			default_random_engine generator(time(0));
			normal_distribution<double> distribution(0.,1.);
			int N_k;
			for(int i=0; i<N_layers; i++){
				layers.push_back(layers_[i]);
				vector<vector<double>> weights_layer;
				vector<double> bias_layer;
				for(int j=0; j<layers[i]; j++){
					vector<double> weights_neuron;
					if(i > 0){
						N_k = layers[i-1];
					}
					else{
						N_k = layers[0]; // assume the first layer as as many neurons as there are inputs
					}
					for(int k=0; k<N_k; k++){
						weights_neuron.push_back(distribution(generator));
					}
					weights_layer.push_back(weights_neuron);
					bias_layer.push_back(distribution(generator));
				}
				weights.push_back(weights_layer);
				bias.push_back(bias_layer);
			}
		}

		NeuralNetwork1(vector<int> layers_){
			build_network(layers);
		}

		// constructor with random weights and bias - python case
		NeuralNetwork1(list& layers_l){
			build_network(python_list_to_vector(layers_l, 0));
		}

		// constructor with given weights and bias
		NeuralNetwork1(vector<int> layers_,
			          vector<vector<vector<double>>> weights_,
		              vector<vector<double>> bias_){
			N_layers = layers_.size();
			layers = layers_;
			weights = weights_;
			bias = bias_;
		}

		// constructor accepting Python lists instead of vectors
		NeuralNetwork1(list& layers_,
			          list& weights_,
		              list& bias_){
			N_layers = len(layers_);
			for(int i=0; i<N_layers; i++){
				layers.push_back(extract<int>(layers_[i]));
				vector<vector<double>> weights_layer;
				vector<double> bias_layer;
				for(int j=0; j<layers[i]; j++){
					vector<double> weights_neuron;
					for(int k=0; k<len(weights_[i][j]); k++){
						weights_neuron.push_back(extract<double>(weights_[i][j][k]));
					}
					weights_layer.push_back(weights_neuron);
					bias_layer.push_back(extract<double>(bias_[i][j]));
				}
				weights.push_back(weights_layer);
				bias.push_back(bias_layer);
			}
		}

		vector<double> feedforward(vector<double> x){
			for(int i=0; i<N_layers; i++){
				vector<double> y;
				for(int j=0; j<layers[i]; j++){
					double z = bias[i][j];
					for(int k=0; k<x.size(); k++){
						z += weights[i][j][k]*x[k];
					}
					y.push_back(sigmoid(z));
				}
				x = y;
			}
			return x;
		}

		// feedforward using a Python list as input
		list feedforward_python(list& x){
			vector<double> y = feedforward(python_list_to_vector(x, 0.));
			return vector_to_python_list(y);
		}

		// evaluating the loss function
		double loss(vector<vector<double>> data, vector<double> y_true_all){
			double res = 0.;
			for(int i=0; i<y_true_all.size(); i++){
				res += pow(feedforward(data[i])[0] - y_true_all[i], 2);
			}
			return res / y_true_all.size();
		}

		// evaluating the loss function - Python
		double loss_python(list& data, list& y_true_all){
			return loss(python_list_of_lists_to_vector(data ,0.), python_list_to_vector(y_true_all, 0.));
		}

		// training function
		void train(vector<vector<double>> data, vector<double> y_true_all, double learn_rate, long epochs){
			for(long epoch = 0; epoch < epochs; epoch++){
				for(int index_data = 0; index_data < y_true_all.size(); index_data++){
					vector<double> x = data[index_data];
					double y_true = y_true_all[index_data];

					// feedforward, retaining the state of each neuron
					vector<vector<double>> states;
					states.push_back(x);
					for(int i=0; i<N_layers; i++){
						vector<double> y;
						for(int j=0; j<layers[i]; j++){
							double z = bias[i][j];
							for(int k=0; k<x.size(); k++){
								z += weights[i][j][k]*x[k];
							}
							y.push_back(sigmoid(z));
						}
						states.push_back(y);
						x = y;
					}

					//predicted value: state of the last neuron (the last layer is assumed to contain only one neuron)
					double y_pred = states[N_layers][0];

					// derivative of the loss function with respect to y_pred
					double d_L_d_ypred = 2.*(y_pred - y_true);

					// partial derivatives
					vector<vector<vector<double>>> d_ypred_d_x;
					vector<vector<vector<double>>> d_ypred_d_weights;
					vector<vector<double>> d_ypred_d_bias;

					//partial derivatives - output layer
					double state = states[N_layers][0];
					vector<vector<double>> d_ypred_d_x_layer;
					vector<double> d_ypred_d_x_neuron;
					vector<vector<double>> d_ypred_d_weights_layer;
					vector<double> d_ypred_d_weights_neuron;
					vector<double> d_ypred_d_bias_layer;
					for(int k=0; k<layers[N_layers-2]; k++){
						d_ypred_d_x_neuron.push_back(weights[N_layers-1][0][k]*state*(1.-state));
						d_ypred_d_weights_neuron.push_back(states[N_layers-1][k]*state*(1.-state));
					}
					d_ypred_d_x_layer.push_back(d_ypred_d_x_neuron);
					d_ypred_d_weights_layer.push_back(d_ypred_d_weights_neuron);
					d_ypred_d_bias_layer.push_back(state*(1.-state));

					d_ypred_d_x.insert(d_ypred_d_x.begin(), d_ypred_d_x_layer);
					d_ypred_d_weights.insert(d_ypred_d_weights.begin(), d_ypred_d_weights_layer);
					d_ypred_d_bias.insert(d_ypred_d_bias.begin(), d_ypred_d_bias_layer);

					//partial derivatives - other layers
					for(int i=2; i<N_layers+1; i++){
						vector<vector<double>> d_ypred_d_x_layer;
						vector<vector<double>> d_ypred_d_weights_layer;
						vector<double> d_ypred_d_bias_layer;
						for(int j=0; j<layers[N_layers - i]; j++){
							double d_ypred_d_yint = 0.;
							for(int k=0; k<layers[N_layers-i+1]; k++){
								d_ypred_d_yint += d_ypred_d_x[0][k][j];
							}
							double state = states[N_layers-i+1][j];
							vector<double> d_ypred_d_x_neuron;
							vector<double> d_ypred_d_weights_neuron;
							for(int k=0; k<weights[N_layers-i][j].size(); k++){
								d_ypred_d_x_neuron.push_back(weights[N_layers-i][j][k]*state*(1.-state)*d_ypred_d_yint);
								d_ypred_d_weights_neuron.push_back(states[N_layers-i][k]*state*(1.-state)*d_ypred_d_yint);
							}
							d_ypred_d_x_layer.push_back(d_ypred_d_x_neuron);
							d_ypred_d_weights_layer.push_back(d_ypred_d_weights_neuron);
							d_ypred_d_bias_layer.push_back(state*(1.-state)*d_ypred_d_yint);
						}
						d_ypred_d_x.insert(d_ypred_d_x.begin(), d_ypred_d_x_layer);
						d_ypred_d_weights.insert(d_ypred_d_weights.begin(), d_ypred_d_weights_layer);
						d_ypred_d_bias.insert(d_ypred_d_bias.begin(), d_ypred_d_bias_layer);
					}

					// update weights and bias
					for(int i=0; i<N_layers; i++){
						for(int j=0; j<layers[i]; j++){
							for(int k=0; k<weights[i][j].size(); k++){
								weights[i][j][k] -= learn_rate * d_L_d_ypred * d_ypred_d_weights[i][j][k];
							}
							bias[i][j] -= learn_rate * d_L_d_ypred * d_ypred_d_bias[i][j];
						}
					}
				}
			}
		}

		// training function - Python
		void train_python(list& data, list& y_true_all, double learn_rate, long epochs){
			train(python_list_of_lists_to_vector(data ,0.), python_list_to_vector(y_true_all, 0.), learn_rate, epochs);
		}

		// save the neural network to a file
		void save(char* filename){
			ofstream file;
			file.open(filename);
			file << N_layers << '\n';
			for(int i=0; i<N_layers; i++){
				file << layers[i] << '\t';
			}
			file << '\n';
			for(int i=0; i<N_layers; i++){
				for(int j=0; j<layers[i]; j++){
					file << weights[i][j].size() << '\t';
					for(int k=0; k<weights[i][j].size(); k++){
						file << weights[i][j][k] << '\t';
					}
					file << bias[i][j] << '\t';
				}
			}
			file.close();
		}

		// load the neural network from a file
		void load(char* filename){
			ifstream file;
			file.open(filename);
			file >> N_layers;
			layers.clear();
			long n_neurons;
			long n_weights;
			double weight_;
			double bias_;
			for(int i=0; i<N_layers; i++){
				file >> n_neurons;
				layers.push_back(n_neurons);
			}
			weights.clear();
			bias.clear();
			for(int i=0; i<N_layers; i++){
				vector<vector<double>> weights_layer;
				vector<double> bias_layer;
				for(int j=0; j<layers[i]; j++){
					vector<double> weights_neuron;
					file >> n_weights;
					for(int k=0; k<n_weights; k++){
						file >> weight_;
						weights_neuron.push_back(weight_);
					}
					file >> bias_;
					bias_layer.push_back(bias_);
					weights_layer.push_back(weights_neuron);
				}
				weights.push_back(weights_layer);
				bias.push_back(bias_layer);
			}
			file.close();
		}

		// implement load
};

BOOST_PYTHON_MODULE(NN1)
{
    class_<NeuralNetwork1>("NeuralNetwork1", init<list&>())
		.def(init<list&, list&, list&>())
		.def("feedforward", &NeuralNetwork1::feedforward_python)
		.def("loss", &NeuralNetwork1::loss_python)
		.def("train", &NeuralNetwork1::train_python)
		.def("save", &NeuralNetwork1::save)
		.def("load", &NeuralNetwork1::load)
	;
}


#pragma once
/*!
* \file GrapheneFloquet.hpp
*
* \brief Functions associated to Floquet Hamiltonian calculations
*
* \author Author: D. Gagnon <denisg6@hotmail.com>
*/
// Include some headers
#include <complex>
#include <armadillo>
#include <boost/numeric/odeint.hpp>

using namespace std::complex_literals; // Complex literals

typedef std::complex<double> c_state_type; // Type of container used to hold the state vector
typedef boost::numeric::odeint::runge_kutta_cash_karp54< c_state_type > error_stepper_type; // Error stepper for odeint

/// Real part of gamma factor appearing in the tight-binding Hamiltonian
double Re_Gamma (double kx, double ky)
{
    return 1.0 + 2.0*cos(0.5*sqrt(3.0)*kx)*cos(0.5*3.0*ky);
}

/// Imaginary part of gamma factor appearing in the tight-binding Hamiltonian
double Im_Gamma (double kx, double ky)
{
    return 2.0*cos(0.5*sqrt(3.0)*kx)*sin(0.5*3.0*ky);
}

/*!
* \class gamma_integrand
*
* \brief Base class for the integrand used in gamma factor calculations
*
* \author Author: D. Gagnon <denisg6@hotmail.com>
*/
class gamma_integrand {

    // Parameters of the Hamiltonian
    double m_kx,m_ky,m_omega,m_E0,m_index;

private:

    // Physical constants definitions
    double hbar = 6.5821195140e-16;                             // Planck constant over 2 pi in eV s
    double lat_constant = 2.4e-10/sqrt(3.0);                    // Lattice constant in meters
    double v_Fermi = 0.003646*2.9979245800e+08;                 // Fermi velocity in graphene

public:

    /// Constructor taking as input the parameters of the Hamiltonian
    /// @param kx float, x-component of the momentum
    /// @param ky float, y-component of the momentum
    /// @param omega float, angular frequency of the field in rad/s
    /// @param E0 float, electric field peak value in V/m
    /// @param index int, index of Fourier coefficients
    gamma_integrand(double kx, double ky, double omega, double E0, int index)
        : m_kx(kx)
        , m_ky(ky)
        , m_omega(omega)
        , m_E0(E0)
        , m_index(index) { }

    /// Overload of operator() for ODE integration
    void operator() ( const c_state_type &z, c_state_type &dzdt, const double t)
    {

        // Conversion factor
        double factor = lat_constant*m_E0/(hbar*m_omega);

        // Peierls substitution
        double kx_field = m_kx + factor*std::cos(t);
        double ky_field = m_ky;

        // Compute gamma factors
        double Re_Gamma_t = Re_Gamma(kx_field, ky_field);
        double Im_Gamma_t = Im_Gamma(kx_field, ky_field);

        // Rhs of the ODE system
        dzdt = (Re_Gamma_t + 1i*Im_Gamma_t)*std::exp(-1i*m_index*t);

    }

    /// Function updating the value of the index
    void SetIndex(int m)
    {
        this->m_index = m;
    }

};

/// Function returning the Fourier coefficients to be used in Floquet Hamiltonian
std::vector< c_state_type > GammaValues(double kx, double ky, double omega, double E0, int max_index)
{
    std::vector< c_state_type > Gamma(2*max_index + 1, 0.0);

    for (int vec_index = 0; vec_index < 2*max_index + 1; vec_index ++)
    {

        // State variable to solve result
        c_state_type z(0.0,0.0);

        // Define integrand
        gamma_integrand integrand(kx,ky,omega,E0, vec_index - max_index);

        // Adaptive integration
        boost::numeric::odeint::integrate_adaptive(
            boost::numeric::odeint::make_controlled< error_stepper_type >( 1.0e-10, 1.0e-6 ),
            integrand,
            z, 0.0, 2.0*M_PI, 2.0*M_PI/10000.0 );

        // Assign value to "Gamma" vector
        Gamma[vec_index] = (0.5/M_PI)*z;

    }

    return Gamma;
}

/// Compute Floquet eigen-energies and probabilities
arma::vec QuasiEnergies(double kx, double ky, double omega, double E0,
                        int blocks, double &prob, double &prob_sigma)
{

    // Physical constants definitions
    double hbar = 6.5821195140e-16;                             // Planck constant over 2 pi in eV s
    double lat_constant = 2.4e-10/sqrt(3.0);                    // Lattice constant in meters
    double v_Fermi = 0.003646*2.9979245800e+08;                 // Fermi velocity in graphene

    double freq = hbar*omega;                        // Frequency in eV
    double tb = - 2*(hbar*v_Fermi)/(3.0*lat_constant); // Tight-binding energy of graphene

    // Total number of blocks including
    // zeroth block
    int totalblocks = 2*blocks + 1;

    // Pre-calculate Fourier coefficients of Hamiltonian
    auto Gamma = GammaValues(kx,ky,omega,E0,2*blocks);

    // Initialize Tmatrix which is to be filled by [totalblocks] 2 x 2 matrices
    arma::Mat<double> zeromat(2*totalblocks,2*totalblocks, arma::fill::zeros);
    arma::cx_mat Ham(zeromat,zeromat); // Hamiltonian

    // FILL MAIN DIAGONAL OF HAMILTONIAN
    // Initialize index m
    int m = -blocks;

    for (int j_= 0; j_ < 2*totalblocks; j_ = j_ + 2)
    {
        Ham.diag(0)[j_] = m*freq;
        Ham.diag(0)[j_ + 1] = m*freq;

        // Increment m
        m++;
    }

    // FILL UPPER AND LOWER DIAGONAL OF HAMILTONIAN

    int nm = 0; // Initialize index (n-m)
    int index_shift = 2*blocks; // Index shift (for gamma vector)

    for (int i_= 1; i_ < 2*totalblocks; i_ = i_ + 2) // Loop on diagonals
    {
        int index0 = nm;
        int index1 = nm + 1;

        //std::cout << i_ << std::endl;

        for (int j_ = 0; j_ < 2*totalblocks - i_; j_ ++) // Loop on elements of diagonals
        {

            //std::cout << j_ << std::endl;

            if (j_ % 2 == 0) // If j_ is even do
            {
                Ham.diag( i_)[j_] = tb*std::conj(Gamma[-index0 + index_shift]);
                Ham.diag(-i_)[j_] = tb*Gamma[index0 + index_shift];

                //std::cout << "Even j" << std::endl;
                //std::cout << Ham.diag( i_)[j_] << std::endl;
                //std::cout << Ham.diag( -i_)[j_] << std::endl;

            }
            else // If j_ is odd do
            {
                Ham.diag( i_)[j_] = tb*Gamma[-index1 + index_shift];
                Ham.diag(-i_)[j_] = tb*std::conj(Gamma[index1 + index_shift]);

                //std::cout << "Odd j" << std::endl;
                //std::cout << Ham.diag( i_)[j_] << std::endl;
                //std::cout << Ham.diag( -i_)[j_] << std::endl;
            }
        }

        nm++; // Increment (n-m) as we shift to the next non-zero diagonal

    }

    // std::cout << Ham << std::endl;

    // arma::mat realpart = arma::real(Ham);
    // arma::mat imagpart = arma::imag(Ham);

    // realpart.save("Hamreal.dat",arma::raw_ascii);
    // imagpart.save("Hamimag.dat",arma::raw_ascii);

    // Initialize eigenvalue vector and eigenvector matrix and
    arma::vec eigval;
    arma::cx_mat eigvec;

    // Compute eigenvalues
    bool status = arma::eig_sym(eigval, eigvec, Ham);
    if (status == false)
    {
        std::cout << "Eigenvalue decomposition failed" << std::endl;
    }

    // std::cout << eigvec << std::endl;

    // COMPUTE TRANSITION PROBABILITY

    // Variables to be used in loop
    prob = 0.0;
    prob_sigma = 0.0; // Passed by reference, will change
    std::complex<double> alphazero, betazero; // Ground state amplitude
    std::complex<double> alphan, betan; // Excited state amplitude
    std::complex<double> pos; // Positive energy state amplitude
    std::complex<double> neg; // Negative energy state amplitude
    arma::cx_vec the_eigenvec;

    // Compute "no-field" phase factor
    double angle = std::atan2(Im_Gamma(kx,ky), Re_Gamma(kx,ky) );
    std::complex<double> phase_factor = std::exp(1i*angle);

    // Loop on every eigenvector
    for (size_t i_= 0; i_ < eigvec.n_cols; i_ ++)
    {
        the_eigenvec = eigvec.col(i_); // Slice eigenvectors

        alphazero = the_eigenvec[the_eigenvec.n_elem/2 - 1];
        betazero = the_eigenvec[the_eigenvec.n_elem/2];

        // Negative energy state
        neg = 0.5*std::sqrt(2.0)*(alphazero - phase_factor*betazero);

        // Loop on elements
        for (size_t j_= 0; j_ < the_eigenvec.n_elem; j_ ++)
        {

            if (j_ % 2 != 0) // If j is odd do, else do nothing
            {
                alphan = the_eigenvec[j_ - 1];
                betan  = the_eigenvec[j_];

                // Positive energy state
                pos = 0.5*std::sqrt(2.0)*(alphan + phase_factor*betan);

                // Fermi's rule
                prob += std::norm(std::conj(pos)*neg); // Transition between eigenstates
                prob_sigma += std::norm(std::conj(betan)*alphazero); // Transition between sigma_z eigenstates
            }
        }
    }

    return eigval;

}


#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include "../simple_lib/include/simple_activation.h"

using namespace Eigen;

double cross_entropy_error(MatrixXd &, MatrixXd &); // プロトタイプ宣言

class simpleNet
{
private:
    MatrixXd W = MatrixXd::Random(2, 3); // 乱数で初期化(本来はXavierの初期値とかにしたい？)

public:
    simpleNet(); // コンストラクタ
    MatrixXd predict(MatrixXd &);
    double loss(MatrixXd &, MatrixXd &);
    MatrixXd gradient(MatrixXd&, MatrixXd&);

    void print_W(void);
};

int main(){
    using std::cout;
    using std::endl;

    simpleNet net;
    net.print_W();

    MatrixXd X = MatrixXd::Zero(2, 1);
    MatrixXd P;

    X << 0.6, 0.9;

    P = net.predict(X);
    cout << "--- predict result ---" << endl;
    cout << P << endl;
    cout << "--- softmax ---" << endl;
    cout << MyDL::softmax(P) << endl;

    MatrixXd t = MatrixXd::Zero(1, 3);
    t << 0, 0, 1; // この書き方をするには、上記のようにメモリ確保をしておく必要がある(でないとセグフォになる)

    double loss;
    loss = net.loss(X, t);

    cout << "--- loss ---" << endl;
    cout << loss << endl;

    MatrixXd dW = MatrixXd::Zero(2, 3);

    dW = net.gradient(X, t);

    cout << "--- dW ---" << endl;
    cout << dW << endl; // 0.2, 0.2, -0.4; 0.3, 0.3, -0.6 (Wをすべて1で初期化した場合)が正解

    return 0;
}

simpleNet::simpleNet(){
    // W << 0.47355232, 0.9977393, 0.84668094, 0.85557411, 0.03563661, 0.69422093;
    W << 1,1,1,1,1,1;
}

MatrixXd simpleNet::predict(MatrixXd& X){
    return X.transpose() * W;
}

double simpleNet::loss(MatrixXd& X, MatrixXd& t){
    MatrixXd Y, Z;
    Z = simpleNet::predict(X);
    Y = MyDL::softmax(Z);

    double loss;
    loss = cross_entropy_error(Y, t);

    return loss;
}

void simpleNet::print_W(void){
    using std::cout;
    using std::endl;

    cout << " --- simpleNet parameter W --- " << endl;
    cout << "W = " << W << endl;
}

MatrixXd simpleNet::gradient(MatrixXd& x, MatrixXd& t)
{
    double h = 1e-4;
    MatrixXd grad = MatrixXd::Zero(2, 3);

    for (int i = 0; i < 6; i++)
    {
        double tmp_val = W(i);
        double f_xh1;
        double f_xh2;
        // f(x+h) の計算
        W(i) = tmp_val + h; // 変数xのi番目の要素だけ増分を取った形に変更
        f_xh1 = simpleNet::loss(x, t);
        // f(x-h)の計算
        W(i) = tmp_val - h;
        f_xh2 = simpleNet::loss(x, t);

        grad(i) = (f_xh1 - f_xh2) / (2 * h);
        W(i) = tmp_val; // 元の値に戻す
    }

    return grad;
}

// one-hot labelバージョンの のミニバッチ実装
double cross_entropy_error(MatrixXd &y, MatrixXd &t)
{
    int batch_size = y.rows();
    double ret = (t.array() * y.array().log()).sum() / batch_size;
    return -ret;
}


#include <cmath>
#include <unordered_map>
#include <vector>
#include <string>
#include <armadillo>
#include <algorithm>

using std::vector;
using arma::dmat;

// input peakDays check: they cant be on day 0 or _dOI by definition (debugging)
bool PeakCheck(const int peak, const int dOI) {
	if ((peak<=0)||(peak>=dOI)) return 1;
	else return 0;
}

// Finds the closest boundary between end and beginning of illnes and
// peak day
int MinDist(const int dOI, const int peakDay) { return std::min(peakDay, dOI-peakDay); }

// Finds the gaussian integral between -inf and x
double CumGauss(const double& x) {
    double PAYLOAD;
    PAYLOAD = 0.5*(erf(10) + erf(x));
    return PAYLOAD;
}

// Cretes a square matrix of zeros setting the first row equal to the
// vector<double> given as argument (broadcast).
// NB: it converts std::vectors tu arma::dmat-s
dmat CastVecMat(const vector<double>& vec) {
	// we need to make vec a row vector first
	arma::drowvec temp(vec);

	// building the payload
	int size = temp.n_elem;
	dmat PAYLOAD(temp);
	PAYLOAD.resize(size, size);

	// initializing setting to zero the zero elements
	PAYLOAD(1,0,arma::size(size-1,size)) = arma::zeros(size-1,size);
	return PAYLOAD;
}

// Very specific function, build the propagation matrix of a status change
// given its discrete cumulative function.
dmat BuildDistribMat(vector<double> vec) {

	// calculate the distribution from a cumulative
	for (int i= vec.size()-1; i!=0; i--) vec[i] -= vec[i-1];

	// insert a zero at the beginning (as the model requires it)
	vec.insert(vec.begin(),0);

	// now add the zeroes underneath to make it a matrix
	dmat PAYLOAD(CastVecMat(vec));

	return PAYLOAD;
}

// Computes the discrete cumulative probability function of a status change given
// its parameters
vector<double> CumProbFunc(const int dOI, const int peakDay, const double finalProb,const std::string type) {

	// Dictionary {1 = Gaussian, 2 = Uniform}
	std::unordered_map<std::string, int> Map;
		Map["Gaussian"] = 1;
		Map["Uniform"] = 2;

	vector<double> PAYLOAD(dOI);

    // switch depending on what type the distribution is
	switch(Map[type]) {
                case 1: {
						// we assume that the distance between end/beginning of illness and
						// the peak day of a status change is 5 sigma
                        double sigma = static_cast<double>(MinDist(dOI, peakDay))/5;
                        // filling the gaussian cumulative
                        for (int i=0; i<dOI; i++)
                            PAYLOAD[i] = finalProb*CumGauss((i-peakDay+1)/sqrt(2)/sigma);
                        break;
                }
                case 2: {
						// slope
                        double distrValue = 1./static_cast<double>(dOI)*finalProb;
                        // filling the uniform cumulative
						for (int i=0; i<dOI; i++)
							PAYLOAD[i] = distrValue*static_cast<double>(i+1);
                        break;
                }
	};

	return PAYLOAD;
}


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

using namespace std;
using namespace Eigen;

int main()
{
   MatrixXd A = MatrixXd::Random(100,100);
   MatrixXd b = MatrixXd::Random(100,50);
   MatrixXd x = A.fullPivLu().solve(b);
   double relative_error = (A*x - b).norm() / b.norm(); // norm() is L2 norm
   cout << "The relative error is:\n" << relative_error << endl;
}


// smooth: Lie Theory for Robotics
// https://github.com/pettni/smooth
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
//
// Copyright (c) 2021 Petter Nilsson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#ifndef SMOOTH__SE3_HPP_
#define SMOOTH__SE3_HPP_

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

#include <complex>

#include "internal/lie_group_base.hpp"
#include "internal/macro.hpp"
#include "internal/se3.hpp"
#include "lie_group.hpp"
#include "map.hpp"
#include "so3.hpp"

namespace smooth {

template<typename Scalar>
class SE2;

/**
 * @brief Base class for SE3 Lie group types.
 *
 * Internally represented as \f$\mathbb{S}^3 \times \mathbb{R}^3\f$.
 *
 * Memory layout
 * -------------
 *
 * - Group:    \f$ \mathbf{x} = [x, y, z, q_x, q_y, q_z, q_w] \f$
 * - Tangent:  \f$ \mathbf{a} = [v_x, v_y, v_z, \omega_x, \omega_y, \omega_z] \f$
 *
 * Constraints
 * -----------
 *
 * - Group:   \f$q_x^2 + q_y^2 + q_z^2 + q_w^2 = 1 \f$
 * - Tangent: \f$ -\pi < \omega_x, \omega_y, \omega_z \leq \pi \f$
 *
 * Lie group matrix form
 * ---------------------
 *
 * \f[
 * \mathbf{X} =
 * \begin{bmatrix}
 *   R & T \\
 *   0 & 1
 * \end{bmatrix} \in \mathbb{R}^{4 \times 4}
 * \f]
 *
 * where \f$R\f$ is a 3x3 rotation matrix and \f$ T = [x, y, z]^T \f$.
 *
 *
 * Lie algebra matrix form
 * -----------------------
 *
 * \f[
 * \mathbf{a}^\wedge =
 * \begin{bmatrix}
 *   0        & -\omega_z & \omega_y  & v_x \\
 *  \omega_z  & 0         & -\omega_x & v_y \\
 *  -\omega_y & \omega_x  & 0         & v_y \\
 *  0         & 0         & 0         & 0
 * \end{bmatrix} \in \mathbb{R}^{4 \times 4}
 * \f]
 */
template<typename _Derived>
class SE3Base : public LieGroupBase<_Derived>
{
  using Base = LieGroupBase<_Derived>;

protected:
  SE3Base() = default;

public:
  SMOOTH_INHERIT_TYPEDEFS;

  /**
   * @brief Access SO(3) part.
   */
  Map<SO3<Scalar>> so3() requires is_mutable
  {
    return Map<SO3<Scalar>>(static_cast<_Derived &>(*this).data() + 3);
  }

  /**
   * @brief Const access SO(3) part.
   */
  Map<const SO3<Scalar>> so3() const
  {
    return Map<const SO3<Scalar>>(static_cast<const _Derived &>(*this).data() + 3);
  }

  /**
   * @brief Access R3 part.
   */
  Eigen::Map<Eigen::Vector3<Scalar>> r3() requires is_mutable
  {
    return Eigen::Map<Eigen::Vector3<Scalar>>(static_cast<_Derived &>(*this).data());
  }

  /**
   * @brief Const access R3 part.
   */
  Eigen::Map<const Eigen::Vector3<Scalar>> r3() const
  {
    return Eigen::Map<const Eigen::Vector3<Scalar>>(static_cast<const _Derived &>(*this).data());
  }

  /**
   * @brief Return as 3D Eigen transform.
   */
  Eigen::Transform<Scalar, 3, Eigen::Isometry> isometry() const
  {
    return Eigen::Translation<Scalar, 3>(r3()) * so3().quat();
  }

  /**
   * @brief Tranformation action on 3D vector.
   */
  template<typename EigenDerived>
  Eigen::Vector3<Scalar> operator*(const Eigen::MatrixBase<EigenDerived> & v) const
  {
    return so3() * v + r3();
  }

  /**
   * @brief Project to SE2.
   *
   * @note SE2 header must be included.
   */
  SE2<Scalar> project_se2() const
  {
    return SE2<Scalar>(so3().project_so2(), r3().template head<2>());
  }
};

// \cond
template<typename _Scalar>
class SE3;
// \endcond

// \cond
template<typename _Scalar>
struct liebase_info<SE3<_Scalar>>
{
  static constexpr bool is_mutable = true;

  using Impl   = SE3Impl<_Scalar>;
  using Scalar = _Scalar;

  template<typename NewScalar>
  using PlainObject = SE3<NewScalar>;
};
// \endcond

/**
 * @brief Storage implementation of SE3 Lie group.
 *
 * @see SE3Base for memory layout.
 */
template<typename _Scalar>
class SE3 : public SE3Base<SE3<_Scalar>>
{
  using Base = SE3Base<SE3<_Scalar>>;

  SMOOTH_GROUP_API(SE3);

public:
  /**
   * @brief Construct from SO3 and translation.
   *
   * @param so3 orientation component.
   * @param r3 translation component.
   */
  template<typename SO3Derived, typename T3Derived>
  SE3(const SO3Base<SO3Derived> & so3, const Eigen::MatrixBase<T3Derived> & r3)
  {
    Base::so3() = static_cast<const SO3Derived &>(so3);
    Base::r3()  = static_cast<const T3Derived &>(r3);
  }

  /**
   * @brief Construct from Eigen transform.
   */
  SE3(const Eigen::Transform<Scalar, 3, Eigen::Isometry> & t)
  {
    Base::so3() = smooth::SO3<Scalar>(Eigen::Quaternion<Scalar>(t.rotation()));
    Base::r3()  = t.translation();
  }
};

// \cond
template<typename _Scalar>
struct liebase_info<Map<SE3<_Scalar>>> : public liebase_info<SE3<_Scalar>>
{};
// \endcond

/**
 * @brief Memory mapping of SE3 Lie group.
 *
 * @see SE3Base for memory layout.
 */
template<typename _Scalar>
class Map<SE3<_Scalar>> : public SE3Base<Map<SE3<_Scalar>>>
{
  using Base = SE3Base<Map<SE3<_Scalar>>>;

  SMOOTH_MAP_API();
};

// \cond
template<typename _Scalar>
struct liebase_info<Map<const SE3<_Scalar>>> : public liebase_info<SE3<_Scalar>>
{
  static constexpr bool is_mutable = false;
};
// \endcond

/**
 * @brief Const memory mapping of SE3 Lie group.
 *
 * @see SE3Base for memory layout.
 */
template<typename _Scalar>
class Map<const SE3<_Scalar>> : public SE3Base<Map<const SE3<_Scalar>>>
{
  using Base = SE3Base<Map<const SE3<_Scalar>>>;

  SMOOTH_CONST_MAP_API();
};

using SE3f = SE3<float>;   ///< SE3 with float
using SE3d = SE3<double>;  ///< SE3 with double

}  // namespace smooth

#endif  // SMOOTH__SE3_HPP_


/*
Author: Rohan Chetan Thanki
Date created: 16-Oct-2021
*/

// This file contains the 'main' function. Program execution begins and ends there.

#include "Hedging_Portfolio.hpp"
#include "Utilities.cpp"
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include<boost/tokenizer.hpp>
#include <boost/date_time.hpp>
#include<boost/tokenizer.hpp>
#include <boost/date_time.hpp>

/*********************************************** PART 1 ******************************************************/
inline void run_part_1(void)
{
    cout << "\nStart of part 1\n";
    // creating variables
    unsigned int numPaths = 1000;
    unsigned int N = 100;
    double T = 0.4;
    double u = 0.05;
    double sigma = 0.24;
    double r = 0.025;
    double S0 = 100;
    double K = 105;
    char optionFlag = 'c';

    vector<vector<double>> stockPrices = simulateStockPrices(numPaths, N, S0, T, u, sigma);
    vector<vector<Hedging_Portfolio>> hedgedPortfolioAllStates = computeHedgingErrors(stockPrices, K, r, T, sigma, 'c');

    vector<vector<double>> callOptionPrices = getHedgedPortfolioParam(hedgedPortfolioAllStates, "V");
    vector<vector<double>> deltas = getHedgedPortfolioParam(hedgedPortfolioAllStates, "DELTA");
    vector<vector<double>> B = getHedgedPortfolioParam(hedgedPortfolioAllStates, "B");
    vector<vector<double>> hedgingErrors = getHedgedPortfolioParam(hedgedPortfolioAllStates, "HE");

    write2DVectorToCSV(stockPrices, "Output_Files/Part1/Stock_Prices.csv");
    write2DVectorToCSV(callOptionPrices, "Output_Files/Part1/Call_Option_Prices.csv");
    write2DVectorToCSV(deltas, "Output_Files/Part1/Call_Option_Deltas.csv");
    write2DVectorToCSV(B, "Output_Files/Part1/B.csv");
    write2DVectorToCSV(hedgingErrors, "Output_Files/Part1/Hedging_Errors.csv");

    cout << "End of part 1\n\n";
}

/*********************************************** PART 2 ******************************************************/
inline void run_part_2(void)
{
    // reading interest rate data
    vector<vector<string>> interestRateData = readCSV("Data/interest.csv", 1, 2);
    //vector<boost::gregorian::date> interestRateDateVec = stringtoDateVect(interestRateData[0]);
    vector<string> interestRateDateVec = interestRateData[0];
    vector<double> interestRateVec = stringToDoubleVect(interestRateData[1]);

    // reading stock data
    vector<vector<string>> stockData = readCSV("Data/sec_GOOG.csv", 1, 2);
    //vector<boost::gregorian::date> stockDateVec = stringtoDateVect(stockData[0]);
    vector<string> stockDateVec = stockData[0];
    vector<double> stockPriceVec = stringToDoubleVect(stockData[1]);

    // reading option data
    //vector<vector<string>> optionData = readCSV("Data/op_GOOG.csv", 1, 6);
    string optionFilePath = "Data/op_GOOG.csv";
    ifstream infile(optionFilePath);
    string line;
    vector<vector<string>> csvData;

    // get user input
    string startDateUser, endDateUser, expDateUser;
    char optionFlagUser;
    double strikePriceUser;

    /*startDateUser = "2011-07-05";
    endDateUser = "2011-07-29";
    expDateUser = "2011-09-17";
    optionFlagUser = 'c';
    strikePriceUser = 500;*/

    std::cout << "Enter start date in YYYY-MM-DD format: "; cin >> startDateUser;
    std::cout << "Enter end date in YYYY-MM-DD format: "; cin >> endDateUser;
    std::cout << "Enter maturity date in YYYY-MM-DD format: "; cin >> expDateUser;
    std::cout << "Enter option Flag: "; cin >> optionFlagUser;
    std::cout << "Enter strike price of the option: "; cin >> strikePriceUser;

    // Read options data line by line
    getline(infile, line);                              // skipping the first line
    vector<Hedging_Portfolio> allHedgingPortfolios;     // creating a vector to store the hedging portfolio at each date
    while (getline(infile, line))
    {
        // tokenising the row data
        vector<string> rowData = string_splitter(line, ',');

        // reading data in each row
        string date = rowData[0];
        string expDate = rowData[1];
        char flag = toupper(rowData[2][0]);
        double strikePrice = stod(rowData[3]);
        double bidPrice = stod(rowData[4]);
        double askPrice = stod(rowData[5]);
        double marketPrice = (bidPrice + askPrice) / 2;

        // skipping the loop if the details donot match user input
        if (!(strikePrice == strikePriceUser && expDate == expDateUser && flag == toupper(optionFlagUser) && date >= startDateUser && date <= endDateUser))
            continue;

        // creating a hedging portfolio object
        Hedging_Portfolio rowobj;

        //setting values in the object
        rowobj.setDate(date);                               // setting date
        rowobj.setExpDate(rowData[1]);                      // setting expiration date
        rowobj.setFlag(rowData[2][0]);                      // setting flag of the option
        rowobj.setStrikePrice(stod(rowData[3]));            // setting strike price
        rowobj.setOptionPrice(marketPrice);                 // setting market price

        // setting the interest rate
        try
        {
            double rate = findVal(interestRateDateVec, interestRateVec, rowobj.getDate()) / 100;
            rowobj.setRiskFreeRate(rate);
        }
        catch (...)
        {
            std::cout << "No interest rate found for date " << rowobj.getDate() << endl;
        }

        // setting the stock price
        try
        {
            double spotPrice = findVal(stockDateVec, stockPriceVec, rowobj.getDate());
            rowobj.setSpotPrice(spotPrice);
        }
        catch (...)
        {
            std::cout << "No interest rate found for date " << rowobj.getDate() << endl;
        }

        // setting time to maturity
        rowobj.setTimeToMaturity(countWeekDays(rowobj.getDate(), rowobj.getDate()) * 1.0 / 252);

        // setting implied volatility
        double maxVol = 100;
        double impliedVol = rowobj.computeImpliedVol(maxVol);
        rowobj.setVolatility(impliedVol);
        //cout << date << "\t" << expDate << "\t" << flag << "\t" << strikePrice << "\t" << rowobj.getRiskFreeRate() << "\t" << rowobj.getSpotPrice() << "\t" << impliedVol << endl;

        // setting delta
        double delta = rowobj.computeDelta();
        rowobj.setDelta(delta);

        // add the object to the vector
        allHedgingPortfolios.push_back(rowobj);
    }
    infile.close();

    // creating a filestream object to write to a csv file
    string outputFilePath = "Output_Files/Part2/Real_Market_Data_Hedging.csv";
    vector<string> outputFileHeaders{ "Date", "Stock Price", "Option Price", "Implied Volatility", "Option Delta", "Hedging Error", "PNL Naked Short Call", "PNL Hedged Portfolio" };
    std::ofstream outfile(outputFilePath);
    for (int i = 0; i <= outputFileHeaders.size() - 1; i++)
        outfile << outputFileHeaders[i] << ",";
    outfile << "\n";

    // iterating through the vector to compute hedging errors
    for (int j = 0; j <= allHedgingPortfolios.size() - 1; j++)
    {
        // creating variables
        double S = allHedgingPortfolios[j].getSpotPrice();
        double TMat = allHedgingPortfolios[j].getTimeToMaturity();
        double delta = allHedgingPortfolios[j].getDelta();
        double V = allHedgingPortfolios[j].getOptionPrice();
        double dt = 1.0 / 252;
        double deltaPrev, BPrev, rPrev, B, HE;

        // setting B
        if (j == 0)
            B = V - delta * S;
        else
        {
            rPrev = allHedgingPortfolios[j - 1].getRiskFreeRate();
            deltaPrev = allHedgingPortfolios[j - 1].getDelta();
            BPrev = allHedgingPortfolios[j - 1].getB();
            B = ((deltaPrev - delta) * S) + BPrev * exp(rPrev * dt);
        }
        allHedgingPortfolios[j].setB(B);

        // setting Hedging Error
        if (j == 0)
            HE = 0;
        else
            HE = deltaPrev * S + BPrev * exp(rPrev * dt) - V;

        allHedgingPortfolios[j].setHedgingError(HE);

        // Setting PNL for naked short call
        double pnlNaked =  allHedgingPortfolios[0].getOptionPrice() - allHedgingPortfolios[j].getOptionPrice();
        allHedgingPortfolios[j].setpnlNaked(pnlNaked);

        // Setting PNL for hedged portfolio
        double pnlHedged = HE;
        allHedgingPortfolios[j].setpnlHedged(pnlHedged);

        // writing to output file
        outfile << allHedgingPortfolios[j].getDate() << ",";
        outfile << allHedgingPortfolios[j].getSpotPrice() << ",";
        outfile << allHedgingPortfolios[j].getOptionPrice() << ",";
        outfile << allHedgingPortfolios[j].getVolatility() << ",";
        outfile << allHedgingPortfolios[j].getDelta() << ",";
        outfile << allHedgingPortfolios[j].getHedgingError() << ",";
        outfile << allHedgingPortfolios[j].getpnlNaked() << ",";
        outfile << allHedgingPortfolios[j].getpnlHedged() << endl;
    }

    outfile.close();
    cout << "\nEnd of part 2\n";
}


//
// OpenTissue, A toolbox for physical based simulation and animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <OpenTissue/configuration.h>

#include <OpenTissue/core/math/math_random.h>
#include <OpenTissue/core/math/big/big_types.h>
#include <OpenTissue/core/math/big/big_moore_penrose_pseudoinverse.h>
#include <OpenTissue/core/math/big/big_generate_random.h>

#define BOOST_AUTO_TEST_MAIN
#include <OpenTissue/utility/utility_push_boost_filter.h>
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>
#include <OpenTissue/utility/utility_pop_boost_filter.h>

template<typename matrix_type,typename vector_type>
void test(matrix_type const & A, vector_type  & x, vector_type const & b, vector_type const & y)
{
  using std::min;
  using std::max;

  typedef typename matrix_type::value_type real_type;
  typedef typename matrix_type::size_type  size_type;

  real_type const tol = boost::numeric_cast<real_type>(0.01);

  matrix_type invA;
  OpenTissue::math::big::svd_moore_penrose_pseudoinverse(A,invA);
  {
    ublas::noalias(x) = ublas::prod( invA, b);
    vector_type tst;
    tst.resize( b.size(), false);
    ublas::noalias(tst) = ublas::prod( A, x);
    for(size_type i = 0; i < b.size(); ++i)
      BOOST_CHECK_CLOSE( real_type( tst(i) ), real_type( b(i) ), tol );
  }

  OpenTissue::math::big::lu_moore_penrose_pseudoinverse(A,invA);
  {
    ublas::noalias(x) = ublas::prod( invA, b);
    vector_type tst;
    tst.resize( b.size(), false);
    ublas::noalias(tst) = ublas::prod( A, x);
    for(size_type i = 0; i < b.size(); ++i)
      BOOST_CHECK_CLOSE( real_type( tst(i) ), real_type( b(i) ), tol );
  }
}

BOOST_AUTO_TEST_SUITE(opentissue_math_big_svd);

BOOST_AUTO_TEST_CASE(logic_and_valid_arguments_testing)
{
  typedef ublas::compressed_matrix<double> matrix_type;
  typedef ublas::vector<double>            vector_type;
  typedef vector_type::size_type           size_type;

  {
    matrix_type A;
    matrix_type invA;
    BOOST_CHECK_THROW( OpenTissue::math::big::lu_moore_penrose_pseudoinverse(A, invA), std::invalid_argument );
    BOOST_CHECK_THROW( OpenTissue::math::big::svd_moore_penrose_pseudoinverse(A, invA), std::invalid_argument );
  }
}

BOOST_AUTO_TEST_CASE(random_test_case)
{

  typedef ublas::compressed_matrix<double> matrix_type;
  typedef ublas::vector<double>            vector_type;
  typedef vector_type::size_type           size_type;

  for(size_type tst=0;tst<10;++tst)
  {

    size_t M = 10;
    size_t N = 5;

    matrix_type A;
    vector_type x;
    vector_type b;
    vector_type y;

    // square system
    OpenTissue::math::big::generate_random( M, M, A); // A may not be invertibel
    OpenTissue::math::big::generate_random( M, x);
    b.resize(M,false);
    y.resize(M,false);
    ublas::noalias(b) = ublas::prod(A,x);
    y.assign(x);
    x.clear();
    test(A,x,b,y);

    // more rows than columns
    OpenTissue::math::big::generate_random( M, N, A);  // A must have full column rank
    OpenTissue::math::big::generate_random( N, x);
    b.resize(M,false);
    y.resize(N,false);
    ublas::noalias(b) = ublas::prod(A,x);
    y.assign(x);
    x.clear();
    test(A,x,b,y);

    // more columns than rows
    OpenTissue::math::big::generate_random( N, M, A);  // A must have full row rank
    OpenTissue::math::big::generate_random( M, x);
    b.resize(N,false);
    y.resize(M,false);
    ublas::noalias(b) = ublas::prod(A,x);
    y.assign(x);
    x.clear();
    test(A,x,b,y);
  }

}

BOOST_AUTO_TEST_SUITE_END();


/*
 * Copyright (c) 1990 Michael E. Hohmeyer,
 *       hohmeyer@icemcfd.com
 * Permission is granted to modify and re-distribute this code in any manner
 * as long as this notice is preserved.  All standard disclaimers apply.
 *
 * R. Seidel's algorithm for solving LPs (linear programs.)
 */

/*
 * Copyright (c) 2021 Zhepei Wang,
 *       wangzhepei@live.com
 * 1. Bug fix in "move_to_front" function that "prev[m]" is illegally accessed
 *    while "prev" originally has only m ints. It is fixed by allocating a
 *    "prev" with m + 1 ints.
 * 2. Add Eigen interface.
 * 3. Resursive template.
 * Permission is granted to modify and re-distribute this code in any manner
 * as long as this notice is preserved.  All standard disclaimers apply.
 *
 * Ref: Seidel, R. (1991), "Small-dimensional linear programming and convex
 *      hulls made easy", Discrete & Computational Geometry 6 (1): 423–434,
 *      doi:10.1007/BF02574699
 */

#ifndef SDLP_HPP
#define SDLP_HPP

#include <Eigen/Eigen>
#include <cmath>
#include <random>

namespace sdlp
{
    constexpr double eps = 1.0e-12;

    enum
    {
        /* minimum attained */
        MINIMUM = 0,
        /* no feasible region */
        INFEASIBLE,
        /* unbounded solution */
        UNBOUNDED,
        /* only a vertex in the solution set */
        AMBIGUOUS,
    };

    inline double dot2(const double a[2],
                       const double b[2])
    {
        return a[0] * b[0] + a[1] * b[1];
    }

    inline double cross2(const double a[2],
                         const double b[2])
    {
        return a[0] * b[1] - a[1] * b[0];
    }

    inline bool unit2(const double a[2],
                      double b[2])
    {
        const double mag = std::sqrt(a[0] * a[0] +
                                     a[1] * a[1]);
        if (mag < 2.0 * eps)
        {
            return true;
        }
        b[0] = a[0] / mag;
        b[1] = a[1] / mag;
        return false;
    }

    /* unitize a d + 1 dimensional point */
    template <int d>
    inline bool unit(double *a)
    {
        double mag = 0.0;
        for (int i = 0; i <= d; i++)
        {
            mag += a[i] * a[i];
        }
        if (mag < (d + 1) * eps * eps)
        {
            return true;
        }
        mag = 1.0 / std::sqrt(mag);
        for (int i = 0; i <= d; i++)
        {
            a[i] *= mag;
        }
        return false;
    }

    /* optimize the unconstrained objective */
    template <int d>
    inline int lp_no_con(const double *n_vec,
                         const double *d_vec,
                         double *opt)
    {
        double n_dot_d = 0.0;
        double d_dot_d = 0.0;
        for (int i = 0; i <= d; i++)
        {
            n_dot_d += n_vec[i] * d_vec[i];
            d_dot_d += d_vec[i] * d_vec[i];
        }
        if (d_dot_d < eps * eps)
        {
            n_dot_d = 0.0;
            d_dot_d = 1.0;
        }
        for (int i = 0; i <= d; i++)
        {
            opt[i] = -n_vec[i] +
                     d_vec[i] * n_dot_d / d_dot_d;
        }
        /* normalize the optimal point */
        if (unit<d>(opt))
        {
            opt[d] = 1.0;
            return AMBIGUOUS;
        }
        else
        {
            return MINIMUM;
        }
    }

    /* returns the plane index that is in i's place */
    inline int move_to_front(const int i,
                             int *next,
                             int *prev)
    {
        if (i == 0 || i == next[0])
        {
            return i;
        }
        const int previ = prev[i];
        /* remove i from it's current position */
        next[prev[i]] = next[i];
        prev[next[i]] = prev[i];
        /* put i at the front */
        next[i] = next[0];
        prev[i] = 0;
        prev[next[i]] = i;
        next[0] = i;
        return previ;
    }

    inline void lp_min_lin_rat(const bool degen,
                               const double cw_vec[2],
                               const double ccw_vec[2],
                               const double n_vec[2],
                               const double d_vec[2],
                               double opt[2])
    {
        /* linear rational function case */
        const double d_cw = dot2(cw_vec, d_vec);
        const double d_ccw = dot2(ccw_vec, d_vec);
        const double n_cw = dot2(cw_vec, n_vec);
        const double n_ccw = dot2(ccw_vec, n_vec);
        if (degen)
        {
            /* if degenerate simply compare values */
            if (n_cw / d_cw < n_ccw / d_ccw)
            {
                opt[0] = cw_vec[0];
                opt[1] = cw_vec[1];
            }
            else
            {
                opt[0] = ccw_vec[0];
                opt[1] = ccw_vec[1];
            }
            /* check CW/CCW bounds are not near a poles */
        }
        else if (std::fabs(d_cw) > 2.0 * eps &&
                 std::fabs(d_ccw) > 2.0 * eps)
        {
            /* the valid region does not contain a poles */
            if (d_cw * d_ccw > 0.0)
            {
                /* find which end has the minimum value */
                if (n_cw / d_cw < n_ccw / d_ccw)
                {
                    opt[0] = cw_vec[0];
                    opt[1] = cw_vec[1];
                }
                else
                {
                    opt[0] = ccw_vec[0];
                    opt[1] = ccw_vec[1];
                }
            }
            else
            {
                /* the valid region does contain a poles */
                if (d_cw > 0.0)
                {
                    opt[0] = -d_vec[1];
                    opt[1] = d_vec[0];
                }
                else
                {
                    opt[0] = d_vec[1];
                    opt[1] = -d_vec[0];
                }
            }
        }
        else if (std::fabs(d_cw) > 2.0 * eps)
        {
            /* CCW bound is near a pole */
            if (n_ccw * d_cw > 0.0)
            {
                /* CCW bound is a positive pole */
                opt[0] = cw_vec[0];
                opt[1] = cw_vec[1];
            }
            else
            {
                /* CCW bound is a negative pole */
                opt[0] = ccw_vec[0];
                opt[1] = ccw_vec[1];
            }
        }
        else if (std::fabs(d_ccw) > 2.0 * eps)
        {
            /* CW bound is near a pole */
            if (n_cw * d_ccw > 2.0 * eps)
            {
                /* CW bound is at a positive pole */
                opt[0] = ccw_vec[0];
                opt[1] = ccw_vec[1];
            }
            else
            {
                /* CW bound is at a negative pole */
                opt[0] = cw_vec[0];
                opt[1] = cw_vec[1];
            }
        }
        else
        {
            /* both bounds are near poles */
            if (cross2(d_vec, n_vec) > 0.0)
            {
                opt[0] = cw_vec[0];
                opt[1] = cw_vec[1];
            }
            else
            {
                opt[0] = ccw_vec[0];
                opt[1] = ccw_vec[1];
            }
        }
    }

    inline int wedge(const double (*halves)[2],
                     const int m,
                     int *next,
                     int *prev,
                     double cw_vec[2],
                     double ccw_vec[2],
                     bool *degen)
    {
        int i;
        double d_cw, d_ccw;
        bool offensive;

        *degen = false;
        for (i = 0; i != m; i = next[i])
        {
            if (!unit2(halves[i], ccw_vec))
            {
                /* CW */
                cw_vec[0] = ccw_vec[1];
                cw_vec[1] = -ccw_vec[0];
                /* CCW */
                ccw_vec[0] = -cw_vec[0];
                ccw_vec[1] = -cw_vec[1];
                break;
            }
        }
        if (i == m)
        {
            return UNBOUNDED;
        }
        i = 0;
        while (i != m)
        {
            offensive = false;
            d_cw = dot2(cw_vec, halves[i]);
            d_ccw = dot2(ccw_vec, halves[i]);
            if (d_ccw >= 2.0 * eps)
            {
                if (d_cw <= -2.0 * eps)
                {
                    cw_vec[0] = halves[i][1];
                    cw_vec[1] = -halves[i][0];
                    unit2(cw_vec, cw_vec);
                    offensive = true;
                }
            }
            else if (d_cw >= 2.0 * eps)
            {
                if (d_ccw <= -2.0 * eps)
                {
                    ccw_vec[0] = -halves[i][1];
                    ccw_vec[1] = halves[i][0];
                    unit2(ccw_vec, ccw_vec);
                    offensive = true;
                }
            }
            else if (d_ccw <= -2.0 * eps &&
                     d_cw <= -2.0 * eps)
            {
                return INFEASIBLE;
            }
            else if (d_cw <= -2.0 * eps ||
                     d_ccw <= -2.0 * eps ||
                     cross2(cw_vec, halves[i]) < 0.0)
            {
                /* degenerate */
                if (d_cw <= -2.0 * eps)
                {
                    unit2(ccw_vec, cw_vec);
                }
                else if (d_ccw <= -2.0 * eps)
                {
                    unit2(cw_vec, ccw_vec);
                }
                *degen = true;
                offensive = true;
            }
            /* place this offensive plane in second place */
            if (offensive)
            {
                i = move_to_front(i, next, prev);
            }
            i = next[i];
            if (*degen)
            {
                break;
            }
        }
        if (*degen)
        {
            while (i != m)
            {
                d_cw = dot2(cw_vec, halves[i]);
                d_ccw = dot2(ccw_vec, halves[i]);
                if (d_cw < -2.0 * eps)
                {
                    if (d_ccw < -2.0 * eps)
                    {
                        return INFEASIBLE;
                    }
                    else
                    {
                        cw_vec[0] = ccw_vec[0];
                        cw_vec[1] = ccw_vec[1];
                    }
                }
                else if (d_ccw < -2.0 * eps)
                {
                    ccw_vec[0] = cw_vec[0];
                    ccw_vec[1] = cw_vec[1];
                }
                i = next[i];
            }
        }
        return MINIMUM;
    }

    /* return the minimum on the projective line */
    inline int lp_base_case(const double (*halves)[2], /* halves --- half lines */
                            const int m,               /* m      --- terminal marker */
                            const double n_vec[2],     /* n_vec  --- numerator funciton */
                            const double d_vec[2],     /* d_vec  --- denominator function */
                            double opt[2],             /* opt    --- optimum  */
                            int *next,                 /* next, prev  --- double linked list of indices */
                            int *prev)
    {
        double cw_vec[2], ccw_vec[2];
        bool degen;
        int status;

        /* find the feasible region of the line */
        status = wedge(halves, m, next, prev, cw_vec, ccw_vec, &degen);

        if (status == INFEASIBLE)
        {
            return status;
        }
        /* no non-trivial constraints one the plane: return the unconstrained optimum */
        if (status == UNBOUNDED)
        {
            return lp_no_con<1>(n_vec, d_vec, opt);
        }

        if (std::fabs(cross2(n_vec, d_vec)) < 2.0 * eps * eps)
        {
            if (dot2(n_vec, n_vec) < 2.0 * eps * eps ||
                dot2(d_vec, d_vec) > 2.0 * eps * eps)
            {
                /* numerator is zero or numerator and denominator are linearly dependent */
                opt[0] = cw_vec[0];
                opt[1] = cw_vec[1];
                status = AMBIGUOUS;
            }
            else
            {
                /* numerator is non-zero and denominator is zero minimize linear functional on circle */
                if (!degen &&
                    cross2(cw_vec, n_vec) <= 0.0 &&
                    cross2(n_vec, ccw_vec) <= 0.0)
                {
                    /* optimum is in interior of feasible region */
                    opt[0] = -n_vec[0];
                    opt[1] = -n_vec[1];
                }
                else if (dot2(n_vec, cw_vec) > dot2(n_vec, ccw_vec))
                {
                    /* optimum is at CCW boundary */
                    opt[0] = ccw_vec[0];
                    opt[1] = ccw_vec[1];
                }
                else
                {
                    /* optimum is at CW boundary */
                    opt[0] = cw_vec[0];
                    opt[1] = cw_vec[1];
                }
                status = MINIMUM;
            }
        }
        else
        {
            /* niether numerator nor denominator is zero */
            lp_min_lin_rat(degen, cw_vec, ccw_vec, n_vec, d_vec, opt);
            status = MINIMUM;
        }
        return status;
    }

    /* find the largest coefficient in a plane */
    template <int d>
    inline void findimax(const double *pln,
                         int *imax)
    {
        *imax = 0;
        double rmax = std::fabs(pln[0]);
        for (int i = 1; i <= d; i++)
        {
            const double ab = std::fabs(pln[i]);
            if (ab > rmax)
            {
                *imax = i;
                rmax = ab;
            }
        }
    }

    template <int d>
    inline void vector_up(const double *equation,
                          const int ivar,
                          const double *low_vector,
                          double *vector)
    {
        vector[ivar] = 0.0;
        for (int i = 0; i <= d; i++)
        {
            if (i != ivar)
            {
                const int j = i < ivar ? i : i - 1;
                vector[i] = low_vector[j];
                vector[ivar] -= equation[i] * low_vector[j];
            }
        }
        vector[ivar] /= equation[ivar];
    }

    template <int d>
    inline void vector_down(const double *elim_eqn,
                            const int ivar,
                            const double *old_vec,
                            double *new_vec)
    {
        double ve = 0.0;
        double ee = 0.0;
        for (int i = 0; i <= d; i++)
        {
            ve += old_vec[i] * elim_eqn[i];
            ee += elim_eqn[i] * elim_eqn[i];
        }
        const double fac = ve / ee;
        for (int i = 0; i <= d; i++)
        {
            if (i != ivar)
            {
                new_vec[i < ivar ? i : i - 1] =
                    old_vec[i] - elim_eqn[i] * fac;
            }
        }
    }

    template <int d>
    inline void plane_down(const double *elim_eqn,
                           const int ivar,
                           const double *old_plane,
                           double *new_plane)
    {
        const double crit = old_plane[ivar] / elim_eqn[ivar];
        for (int i = 0; i <= d; i++)
        {
            if (i != ivar)
            {
                new_plane[i < ivar ? i : i - 1] =
                    old_plane[i] - elim_eqn[i] * crit;
            }
        }
    }

    template <int d>
    inline int linfracprog(const double *halves, /* halves  --- half spaces */
                           const int max_size,   /* max_size --- size of halves array */
                           const int m,          /* m       --- terminal marker */
                           const double *n_vec,  /* n_vec   --- numerator vector */
                           const double *d_vec,  /* d_vec   --- denominator vector */
                           double *opt,          /* opt     --- optimum */
                           double *work,         /* work    --- work space (see below) */
                           int *next,            /* next    --- array of indices into halves */
                           int *prev)            /* prev    --- array of indices into halves */
    /*
    **
    ** half-spaces are in the form
    ** halves[i][0]*x[0] + halves[i][1]*x[1] +
    ** ... + halves[i][d-1]*x[d-1] + halves[i][d]*x[d] >= 0
    **
    ** coefficients should be normalized
    ** half-spaces should be in random order
    ** the order of the half spaces is 0, next[0] next[next[0]] ...
    ** and prev[next[i]] = i
    **
    ** halves: (max_size)x(d+1)
    **
    ** the optimum has been computed for the half spaces
    ** 0 , next[0], next[next[0]] , ... , prev[0]
    ** the next plane that needs to be tested is 0
    **
    ** m is the index of the first plane that is NOT on the list
    ** i.e. m is the terminal marker for the linked list.
    **
    ** the objective function is dot(x,nvec)/dot(x,dvec)
    ** if you want the program to solve standard d dimensional linear programming
    ** problems then n_vec = ( x0, x1, x2, ..., xd-1, 0)
    ** and           d_vec = (  0,  0,  0, ...,    0, 1)
    ** and halves[0] = (0, 0, ... , 1)
    **
    ** work points to (max_size+3)*(d+2)*(d-1)/2 double space
    */
    {
        int status, imax;
        double *new_opt, *new_n_vec, *new_d_vec, *new_halves, *new_work;
        const double *plane_i;

        double val = 0.0;
        for (int j = 0; j <= d; j++)
        {
            val += d_vec[j] * d_vec[j];
        }
        const bool d_vec_zero = (val < (d + 1) * eps * eps);

        /* find the unconstrained minimum */
        status = lp_no_con<d>(n_vec, d_vec, opt);
        if (m <= 0)
        {
            return status;
        }

        /* allocate memory for next level of recursion */
        new_opt = work;
        new_n_vec = new_opt + d;
        new_d_vec = new_n_vec + d;
        new_halves = new_d_vec + d;
        new_work = new_halves + max_size * d;
        for (int i = 0; i != m; i = next[i])
        {
            /* if the optimum is not in half space i then project the problem onto that plane */
            plane_i = halves + i * (d + 1);
            /* determine if the optimum is on the correct side of plane_i */
            val = 0.0;
            for (int j = 0; j <= d; j++)
            {
                val += opt[j] * plane_i[j];
            }
            if (val < -(d + 1) * eps)
            {
                /* find the largest of the coefficients to eliminate */
                findimax<d>(plane_i, &imax);
                /* eliminate that variable */
                if (i != 0)
                {
                    const double fac = 1.0 / plane_i[imax];
                    for (int j = 0; j != i; j = next[j])
                    {
                        const double *old_plane = halves + j * (d + 1);
                        const double crit = old_plane[imax] * fac;
                        double *new_plane = new_halves + j * d;
                        for (int k = 0; k <= d; k++)
                        {
                            const int l = k < imax ? k : k - 1;
                            new_plane[l] = k != imax ? old_plane[k] - plane_i[k] * crit : new_plane[l];
                        }
                    }
                }
                /* project the objective function to lower dimension */
                if (d_vec_zero)
                {
                    vector_down<d>(plane_i, imax, n_vec, new_n_vec);
                    for (int j = 0; j < d; j++)
                    {
                        new_d_vec[j] = 0.0;
                    }
                }
                else
                {
                    plane_down<d>(plane_i, imax, n_vec, new_n_vec);
                    plane_down<d>(plane_i, imax, d_vec, new_d_vec);
                }
                /* solve sub problem */
                status = linfracprog<d - 1>(new_halves, max_size, i, new_n_vec,
                                            new_d_vec, new_opt, new_work, next, prev);
                /* back substitution */
                if (status != INFEASIBLE)
                {
                    vector_up<d>(plane_i, imax, new_opt, opt);

                    /* inline code for unit */
                    double mag = 0.0;
                    for (int j = 0; j <= d; j++)
                    {
                        mag += opt[j] * opt[j];
                    }
                    mag = 1.0 / sqrt(mag);
                    for (int j = 0; j <= d; j++)
                    {
                        opt[j] *= mag;
                    }
                }
                else
                {
                    return status;
                }
                /* place this offensive plane in second place */
                i = move_to_front(i, next, prev);
            }
        }
        return status;
    }

    template <>
    inline int linfracprog<1>(const double *halves,
                              const int max_size,
                              const int m,
                              const double *n_vec,
                              const double *d_vec,
                              double *opt,
                              double *work,
                              int *next,
                              int *prev)
    {
        if (m > 0)
        {
            return lp_base_case((const double(*)[2])halves, m,
                                n_vec, d_vec, opt, next, prev);
        }
        else
        {
            return lp_no_con<1>(n_vec, d_vec, opt);
        }
    }

    inline void rand_permutation(const int n,
                                 int *p)
    {
        typedef std::uniform_int_distribution<int> rand_int;
        typedef rand_int::param_type rand_range;
        static std::mt19937_64 gen;
        static rand_int rdi(0, 1);
        int j, k;
        for (int i = 0; i < n; i++)
        {
            p[i] = i;
        }
        for (int i = 0; i < n; i++)
        {
            rdi.param(rand_range(0, n - i - 1));
            j = rdi(gen) + i;
            k = p[j];
            p[j] = p[i];
            p[i] = k;
        }
    }

    template <int d>
    inline double linprog(const Eigen::Matrix<double, d, 1> &c,
                          const Eigen::Matrix<double, -1, d> &A,
                          const Eigen::Matrix<double, -1, 1> &b,
                          Eigen::Matrix<double, d, 1> &x)
    /*
    **  min cTx, s.t. Ax<=b
    **  dim(x) << dim(b)
    */
    {
        int m = b.size() + 1;
        x.setZero();
        if (m <= 1)
        {
            return c.cwiseAbs().maxCoeff() > 0.0 ? -INFINITY : 0.0;
        }

        Eigen::VectorXi perm(m - 1);
        Eigen::VectorXi next(m);
        /* original allocated size is m, here changed to m + 1 for legal tail accessing */
        Eigen::VectorXi prev(m + 1);
        Eigen::Matrix<double, d + 1, 1> n_vec;
        Eigen::Matrix<double, d + 1, 1> d_vec;
        Eigen::Matrix<double, d + 1, 1> opt;
        Eigen::Matrix<double, d + 1, -1, Eigen::ColMajor> halves(d + 1, m);
        Eigen::VectorXd work((m + 3) * (d + 2) * (d - 1) / 2);

        halves.col(0).setZero();
        halves(d, 0) = 1.0;
        halves.topRightCorner(d, m - 1) = -A.transpose();
        halves.bottomRightCorner(1, m - 1) = b.transpose();
        /* normalize all halves as required in linfracprog */
        halves.colwise().normalize();
        n_vec.head(d) = c;
        n_vec(d) = 0.0;
        d_vec.setZero();
        d_vec(d) = 1.0;

        /* randomize the input planes */
        rand_permutation(m - 1, perm.data());
        /* previous to 0 is actually never used */
        prev(0) = 0;
        /* link the zero position in at the beginning */
        next(0) = perm(0) + 1;
        prev(perm(0) + 1) = 0;
        /* link the other planes */
        for (int i = 0; i < m - 2; i++)
        {
            next(perm(i) + 1) = perm(i + 1) + 1;
            prev(perm(i + 1) + 1) = perm(i) + 1;
        }
        /* flag the last plane */
        next(perm(m - 2) + 1) = m;

        int status = sdlp::linfracprog<d>(halves.data(), m, m,
                                          n_vec.data(), d_vec.data(),
                                          opt.data(), work.data(),
                                          next.data(), prev.data());

        /* handle states for linprog whose definitions differ from linfracprog */
        double minimum = INFINITY;
        if (status != sdlp::INFEASIBLE)
        {
            if (opt(d) != 0.0 && status != sdlp::UNBOUNDED)
            {
                x = opt.head(d) / opt(d);
                minimum = c.dot(x);
            }

            if (opt(d) == 0.0 || status == sdlp::UNBOUNDED)
            {
                x = opt.head(d);
                minimum = -INFINITY;
            }
        }

        return minimum;
    }

} // namespace sdlp

#endif


#include <Eigen/Dense>
#include "gtest/gtest.h"

#include "math/matrix/gauss_jordan.h"

namespace GraphSfM {
using RowMajorMatrixXd =
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;

TEST(GaussJordan, FullDiagonalizationOnSquaredRowMajorMatrix) {
  const int kNumRows = 32;
  RowMajorMatrixXd mat = RowMajorMatrixXd::Random(kNumRows, kNumRows);
  GaussJordan(&mat);
  // Trace of matrix must be equals to the number of rows.
  EXPECT_NEAR(mat.trace(), static_cast<double>(kNumRows), 1e-6);
  // Verify that the lower triangular part sums to the trace.
  EXPECT_NEAR(mat.sum(), mat.trace(), 1e-6);
}

TEST(GaussJordan, FullDiagonalizationOnSquaredColumnMajorMatrix) {
  const int kNumRows = 32;
  Eigen::MatrixXd mat = Eigen::MatrixXd::Random(kNumRows, kNumRows);
  GaussJordan(&mat);
  // Trace of matrix must be equals to the number of rows.
  EXPECT_NEAR(mat.trace(), static_cast<double>(kNumRows), 1e-6);
  // Verify that the lower triangular part sums to the trace.
  EXPECT_NEAR(mat.sum(), mat.trace(), 1e-6);
}

TEST(GaussJordan, EliminationOnFatMatrix) {
  const int kNumRows = 32;
  const int kNumCols = kNumRows + 4;
  RowMajorMatrixXd mat = RowMajorMatrixXd::Random(kNumRows, kNumCols);
  GaussJordan(&mat);
  // Verify that the left-block (rows, rows) is diagonalized.
  EXPECT_NEAR(mat.block(0, 0, kNumRows, kNumRows).sum(),
              mat.block(0, 0, kNumRows, kNumRows).trace(), 1e-6);
}

TEST(GaussJordan, PartialEliminationOnFatMatrix) {
  const int kNumRows = 32;
  const int kNumCols = kNumRows + 4;
  const int kNumRowsToProcess = kNumRows - 4;
  const int kLastRowToProcess = kNumRowsToProcess - 1;
  RowMajorMatrixXd mat = RowMajorMatrixXd::Random(kNumRows, kNumCols);
  GaussJordan(kLastRowToProcess, &mat);
  // Verify that the left-block (rows, rows) is diagonalized.
  EXPECT_NEAR(mat.block(0, 0, kNumRowsToProcess, kNumRowsToProcess).trace(),
              static_cast<double>(kNumRowsToProcess),
              1e-6);
}

TEST(GaussJordan, PartialDiagonalizationOnFatMatrix) {
  const int kNumRows = 32;
  const int kNumCols = kNumRows + 4;
  const int kLastRowToProcess = 2;
  RowMajorMatrixXd mat = RowMajorMatrixXd::Random(kNumRows, kNumCols);
  GaussJordan(kNumRows - 1, kLastRowToProcess, &mat);
  // Verify that the left-block (rows, rows) is partially diagonalized.
  EXPECT_NEAR(mat.block(kLastRowToProcess, kLastRowToProcess,
                        kNumRows - kLastRowToProcess,
                        kNumRows - kLastRowToProcess).sum(),
              kNumRows - kLastRowToProcess, 1e-6);
  EXPECT_NEAR(mat.block(kLastRowToProcess, kLastRowToProcess,
                        kNumRows - kLastRowToProcess,
                        kNumRows - kLastRowToProcess).sum(),
              mat.block(kLastRowToProcess, kLastRowToProcess,
                        kNumRows - kLastRowToProcess,
                        kNumRows - kLastRowToProcess).trace(),
              1e-6);
  EXPECT_NE(mat.block(0, 0, kNumRows, kNumRows).sum(),
            mat.block(kLastRowToProcess, kLastRowToProcess,
                      kNumRows - kLastRowToProcess,
                      kNumRows - kLastRowToProcess).sum());
}

TEST(GaussJordan, FullDiagonalizationOnLargeSquaredMatrix) {
  const int kNumRows = 400;
  const int kNumCols = kNumRows;
  RowMajorMatrixXd mat = RowMajorMatrixXd::Random(kNumRows, kNumCols);
  GaussJordan(&mat);
  EXPECT_NEAR(mat.sum(), mat.trace(), 1e-6);
}

}  // namespace GraphSfM


#include <Eigen/Dense>
#include "simple_layer.h"
#include "simple_activation.h"
#include "simple_loss.h"

namespace MyDL{

    using namespace Eigen;

    // -------------------------------------------------
    //          MulLayer
    // -------------------------------------------------
    MatrixXd MulLayer::forward(MatrixXd& x, MatrixXd& y){
        _x = x;
        _y = y;

        return x.array() * y.array();
    }

    void MulLayer::backward(MatrixXd& dout, MatrixXd& dx, MatrixXd& dy){
        dx = dout.array() * _y.array();
        dy = dout.array() * _x.array();
    }

    // -------------------------------------------------
    //          AddLayer
    // -------------------------------------------------
    MatrixXd AddLayer::forward(MatrixXd& x, MatrixXd& y){
        return x + y;
    }

    void AddLayer::backward(MatrixXd& dout, MatrixXd& dx, MatrixXd& dy){
        dx = dout;
        dy = dout;
    }

    // -------------------------------------------------
    //          ReLU
    // -------------------------------------------------
    MatrixXd ReLU::forward(MatrixXd& x){
        mask = x.unaryExpr([](double p){return p >= 0;}).cast<double>();
        return x.array() * mask.array();
    }

    MatrixXd ReLU::backward(MatrixXd& dout){
        return dout.array() * mask.array();
    }

    // -------------------------------------------------
    //          Sigmoid
    // -------------------------------------------------
    MatrixXd Sigmoid::forward(MatrixXd& x){
        _y = x.unaryExpr([](double p){return 1/(1 + exp(-p));});
        return _y; // 内部変数に格納するの忘れがち
    }

    MatrixXd Sigmoid::backward(MatrixXd& dout){
        return dout.array() * (MatrixXd::Ones(dout.rows(), dout.cols()).array() - _y.array()) * _y.array();
    }

    // -------------------------------------------------
    //          Affine
    // -------------------------------------------------
    Affine::Affine(MatrixXd& W, VectorXd& b){
        _W = W;
        _b = b;
    }

    MatrixXd Affine::forward(MatrixXd& X){
        MatrixXd Y;
        _X = X; // 内部変数に格納するの忘れがち
        Y = (X * _W).rowwise() + _b.transpose();
        return Y;
    }

    MatrixXd Affine::backward(MatrixXd& dout){
        MatrixXd dX;
        dX = dout * _W.transpose();
        dW = _X.transpose() * dout;
        db = dout.colwise().sum();
        return dX;
    }

    // -------------------------------------------------
    //          SoftmaxWithLoss
    // -------------------------------------------------
    double SoftmaxWithLoss::forward(MatrixXd& X, MatrixXd& t){
        _t = t;
        _Y = softmax(X);
        _loss = cross_entropy_error(_Y, t);
        return _loss;
    }

    MatrixXd SoftmaxWithLoss::backward(double dout){
        double batch_size = _t.rows();
        MatrixXd dx;
        dx = (_Y - _t) / batch_size;

        return dx;
    }

}


// weighted_die.cpp
//
// Copyright (c) 2009
// Steven Watanabe
//
// 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)

//[weighted_die
/*`
    For the source of this example see
    [@boost://libs/random/example/weighted_die.cpp weighted_die.cpp].
*/
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <vector>
#include <algorithm>
#include <numeric>

boost::mt19937 gen;

/*`
   This time, instead of a fair die, the probability of
   rolling a 1 is 50% (!).  The other five faces are all
   equally likely.
*/
static const double probabilities[] = {
    0.5, 0.1, 0.1, 0.1, 0.1, 0.1
};

/*`
  Now define a function that simulates rolling this die.
  Note that the C++0x library contains a `discrete_distribution`
  class which would be a better way to do this.
*/
int roll_weighted_die() {
    std::vector<double> cumulative;
    std::partial_sum(&probabilities[0], &probabilities[0] + 6,
                     std::back_inserter(cumulative));
    boost::uniform_real<> dist(0, cumulative.back());
    boost::variate_generator<boost::mt19937&, boost::uniform_real<> > die(gen, dist);
    /*<< Find the position within the sequence and add 1
         (to make sure that the result is in the range [1,6]
         instead of [0,5])
    >>*/
    return (std::lower_bound(cumulative.begin(), cumulative.end(), die()) - cumulative.begin()) + 1;
}

//]

#include <iostream>

int main() {
    for(int i = 0; i < 10; ++i) {
        std::cout << roll_weighted_die() << std::endl;
    }
}