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

#include <iostream>
#include <boost/math/differentiation/autodiff.hpp>
#include "Instrument.h"
#include "Interpolator.h"
#include "Holiday.h"
#include "Utility.h"
#include "YearFraction.h"

using namespace boost::math::differentiation;
typedef boost::gregorian::date date;

//class FixedCashFlow {
//private:
//    double _flow;
//
//public:
//    double accural_yf;
//    double coupon_rate;
//    double payment_date;
//    double notional;
//
//    FixedCashFlow() {
//        accural_yf = 1.0;
//        coupon_rate = 0.02;
//        notional = 1000000.0;
//
//    }
//
//    double get_flow() {
//        _flow = notional * accural_yf * coupon_rate;
//        return _flow;
//    }
//
//};
//
//double get_dsc() {
//    return 0.9;
//}
//
//template <typename T>
//T plus(T const& x, T const& y) {
//
//    return x + y;
//}
//
//static class FixedFlowCalculator {
//
//private:
//    const static unsigned Order = 2;                  // Highest order derivative to be calculated.
//
//    template <typename T>
//    static T _price(double const& flow, T const& DF) {
//        return flow * DF;
//    }
//
//public:
//    auto static price(FixedCashFlow& cf) {
//        auto const df = make_fvar<double, Order>(get_dsc());
//        auto const pv = _price(cf.get_flow(), df);
//        return pv;
//    }
//};

//int main()
//{
//    deriv2 deriv_obj = deriv2(2.0);
//    deriv_obj.show_value();
//    std::cout << "Hello World!\n";
//
//    FixedCashFlow a_cash = FixedCashFlow();
//    std::cout << "my flow is: " << a_cash.get_flow() << std::endl;
//    auto pv = FixedFlowCalculator::price(a_cash);
//
//    std::cout << "PV = " << pv.derivative(0) << std::endl;
//    std::cout << "dPV/dDF = " << pv.derivative(1) << std::endl;
//
//    auto pv2 = plus(pv,pv);
//    std::cout << "PV = " << pv2.derivative(0) << std::endl;
//    std::cout << "dPV/dDF = " << pv2.derivative(1) << std::endl;
//
//    //using namespace boost::math::differentiation;
//
//    //constexpr unsigned Order = 5;                  // Highest order derivative to be calculated.
//    //auto const x = make_fvar<double, Order>(2.0);  // Find derivatives at x=2.
//    //auto const y = fourth_power(x);
//    //for (unsigned i = 0; i <= Order; ++i)
//    //    std::cout << "y.derivative(" << i << ") = " << y.derivative(i) << std::endl;
//
//}

// Equations and function/variable names are from
// https://en.wikipedia.org/wiki/Greeks_(finance)#Formulas_for_European_option_Greeks
// Standard normal probability density function
template<typename X>
X phi(const X& x)
{
    return boost::math::constants::one_div_root_two_pi<double>() * exp(-0.5 * x * x);
}
// Standard normal cumulative distribution function
template<typename X>
X Phi(const X& x)
{
    return 0.5 * erfc(-boost::math::constants::one_div_root_two<double>() * x);
}
enum CP { call, put };
// Assume zero annual dividend yield (q=0).
using namespace boost::math::differentiation;

template<typename Price, typename Sigma, typename Tau, typename Rate>
promote<Price, Sigma, Tau, Rate>
black_scholes_option_price(CP cp, double K, const Price& S, const Sigma& sigma, const Tau& tau, const Rate& r)
{
    using namespace std;
    const auto d1 = (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
    const auto d2 = (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
    if (cp == call)
        return S * Phi(d1) - exp(-r * tau) * K * Phi(d2);
    else
        return exp(-r * tau) * K * Phi(-d2) - S * Phi(-d1);
}

int main()
{
    const double K = 100.0; // Strike price.
    auto const S = make_fvar<double, 3>(105); // Stock price.
    auto const sigma = make_fvar<double, 0, 3>(5); // Volatility.
    auto const tau = make_fvar<double, 0, 0, 1>(30.0 / 365);  // Time to expiration in years. (30 days).
    auto const r = make_fvar<double, 0, 0, 0, 1>(1.25 / 100); // Interest rate.

    const auto call_price = black_scholes_option_price(call, K, S, sigma, tau, r);
    const auto put_price = black_scholes_option_price(put, K, S, sigma, tau, r);
    // Compare automatically calculated greeks by autodiff with formulas for greeks.
    // https://en.wikipedia.org/wiki/Greeks_(finance)#Formulas_for_European_option_Greeks

    const double d1 = static_cast<double>((log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau)));
    const double d2 = static_cast<double>((log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau)));
    const double formula_call_delta = +Phi(+d1);
    const double formula_put_delta = -Phi(-d1);
    const double formula_vega = static_cast<double>(S * phi(d1) * sqrt(tau));
    const double formula_call_theta = static_cast<double>(-S * phi(d1) * sigma / (2 * sqrt(tau)) - r * K * exp(-r * tau) * Phi(+d2));
    const double formula_put_theta = static_cast<double>(-S * phi(d1) * sigma / (2 * sqrt(tau)) + r * K * exp(-r * tau) * Phi(-d2));
    const double formula_call_rho = static_cast<double>(+K * tau * exp(-r * tau) * Phi(+d2));
    const double formula_put_rho = static_cast<double>(-K * tau * exp(-r * tau) * Phi(-d2));
    const double formula_gamma = static_cast<double>(phi(d1) / (S * sigma * sqrt(tau)));
    const double formula_vanna = static_cast<double>(-phi(d1) * d2 / sigma);
    const double formula_charm = static_cast<double>(phi(d1) * (d2 * sigma * sqrt(tau) - 2 * r * tau) / (2 * tau * sigma * sqrt(tau)));
    const double formula_vomma = static_cast<double>(S * phi(d1) * sqrt(tau) * d1 * d2 / sigma);
    const double formula_veta = static_cast<double>(-S * phi(d1) * sqrt(tau) * (r * d1 / (sigma * sqrt(tau)) - (1 + d1 * d2) / (2 * tau)));
    const double formula_speed = static_cast<double>(-phi(d1) * (d1 / (sigma * sqrt(tau)) + 1) / (S * S * sigma * sqrt(tau)));
    const double formula_zomma = static_cast<double>(phi(d1) * (d1 * d2 - 1) / (S * sigma * sigma * sqrt(tau)));
    const double formula_color =
        static_cast<double>(-phi(d1) / (2 * S * tau * sigma * sqrt(tau)) * (1 + (2 * r * tau - d2 * sigma * sqrt(tau)) * d1 / (sigma * sqrt(tau))));
    const double formula_ultima = -formula_vega * static_cast<double>((d1 * d2 * (1 - d1 * d2) + d1 * d1 + d2 * d2) / (sigma * sigma));
    std::cout << std::setprecision(std::numeric_limits<double>::digits10)
        << "autodiff black-scholes call price = " << call_price.derivative(0, 0, 0, 0) << '\n'
        << "autodiff black-scholes put  price = " << put_price.derivative(0, 0, 0, 0) << '\n'
        << "\n## First-order Greeks\n"
        << "autodiff call delta = " << call_price.derivative(1, 0, 0, 0) << '\n'
        << " formula call delta = " << formula_call_delta << '\n'
        << "autodiff call vega  = " << call_price.derivative(0, 1, 0, 0) << '\n'
        << " formula call vega  = " << formula_vega << '\n'
        << "autodiff call theta = " << -call_price.derivative(0, 0, 1, 0) << '\n' // minus sign due to tau = T-time
        << " formula call theta = " << formula_call_theta << '\n'
        << "autodiff call rho   = " << call_price.derivative(0, 0, 0, 1) << '\n'
        << " formula call rho   = " << formula_call_rho << '\n'
        << '\n'
        << "autodiff put delta = " << put_price.derivative(1, 0, 0, 0) << '\n'
        << " formula put delta = " << formula_put_delta << '\n'
        << "autodiff put vega  = " << put_price.derivative(0, 1, 0, 0) << '\n'
        << " formula put vega  = " << formula_vega << '\n'
        << "autodiff put theta = " << -put_price.derivative(0, 0, 1, 0) << '\n'
        << " formula put theta = " << formula_put_theta << '\n'
        << "autodiff put rho   = " << put_price.derivative(0, 0, 0, 1) << '\n'
        << " formula put rho   = " << formula_put_rho << '\n'
        << "\n## Second-order Greeks\n"
        << "autodiff call gamma = " << call_price.derivative(2, 0, 0, 0) << '\n'
        << "autodiff put  gamma = " << put_price.derivative(2, 0, 0, 0) << '\n'
        << "      formula gamma = " << formula_gamma << '\n'
        << "autodiff call vanna = " << call_price.derivative(1, 1, 0, 0) << '\n'
        << "autodiff put  vanna = " << put_price.derivative(1, 1, 0, 0) << '\n'
        << "      formula vanna = " << formula_vanna << '\n'
        << "autodiff call charm = " << -call_price.derivative(1, 0, 1, 0) << '\n'
        << "autodiff put  charm = " << -put_price.derivative(1, 0, 1, 0) << '\n'
        << "      formula charm = " << formula_charm << '\n'
        << "autodiff call vomma = " << call_price.derivative(0, 2, 0, 0) << '\n'
        << "autodiff put  vomma = " << put_price.derivative(0, 2, 0, 0) << '\n'
        << "      formula vomma = " << formula_vomma << '\n'
        << "autodiff call veta = " << call_price.derivative(0, 1, 1, 0) << '\n'
        << "autodiff put  veta = " << put_price.derivative(0, 1, 1, 0) << '\n'
        << "      formula veta = " << formula_veta << '\n'
        << "\n## Third-order Greeks\n"
        << "autodiff call speed = " << call_price.derivative(3, 0, 0, 0) << '\n'
        << "autodiff put  speed = " << put_price.derivative(3, 0, 0, 0) << '\n'
        << "      formula speed = " << formula_speed << '\n'
        << "autodiff call zomma = " << call_price.derivative(2, 1, 0, 0) << '\n'
        << "autodiff put  zomma = " << put_price.derivative(2, 1, 0, 0) << '\n'
        << "      formula zomma = " << formula_zomma << '\n'
        << "autodiff call color = " << call_price.derivative(2, 0, 1, 0) << '\n'
        << "autodiff put  color = " << put_price.derivative(2, 0, 1, 0) << '\n'
        << "      formula color = " << formula_color << '\n'
        << "autodiff call ultima = " << call_price.derivative(0, 3, 0, 0) << '\n'
        << "autodiff put  ultima = " << put_price.derivative(0, 3, 0, 0) << '\n'
        << "      formula ultima = " << formula_ultima << '\n'
        ;

    auto v = std::vector<double>{ 1.0, 2.0, 5.0, 10.0 };
    auto v2 = std::vector<double>{ 10.0, 20.0, 50.0, 100.0 };
    auto interp_1 = LinearInterpolator<double, double>(v, v2);
    std::cout << "interp function: " << interp_1.interp(2.5) << std::endl;
    std::cout << "interp function: " << interp_1.interp(12.5) << std::endl;
    std::cout << "interp function: " << interp_1.interp(0.5) << std::endl;
    Holiday us_holiday;
    date day1{ 2021, 5, 31 };
    std::cout << "is " << day1 << " a holiday: " << us_holiday.is_holiday(day1) << std::endl;
    date day2 = bus_day_shift(day1, 30, us_holiday);
    std::cout << "So after adj current date is " << day2  << std::endl;

    std::string aa = "1y";
    std::string bb = "6m";

    std::cout << "test function 1y: " << tenor_to_year(aa) << std::endl;
    std::cout << "test function 6m: " << tenor_to_year(bb) << std::endl;

    FixedCashFlow cf(1000000.00, 0.02, day1, day2, DCC::A30360);
    CashFlowSchedule cfs(1000000.0, 0.02, day1, 2, "10Y");
    cfs.show();
    return 0;
}

// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu

// Tips for Getting Started:
//   1. Use the Solution Explorer window to add/manage files
//   2. Use the Team Explorer window to connect to source control
//   3. Use the Output window to see build output and other messages
//   4. Use the Error List window to view errors
//   5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
//   6. In the future, to open this project again, go to File > Open > Project and select the .sln file


#ifndef UTIL_HPP
#define UTIL_HPP

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 550
#define SCALE 30.f
#define PI 3.1459
#define EXP 2.71828182845904523536
#define DELIM ","

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>

namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<float> matrix;
typedef ublas::vector<float> vector;

inline float
random_float (float min, float max)
{
  float random = ((float) rand()) / (float) RAND_MAX;
  float range = max - min;
  return (random*range) + min;
}

inline vector
sigmoid(vector z)
{
  for (unsigned long i=0; i < z.size(); ++i)
  {
    z(i) = (1.f / (1.f + std::pow(EXP, -z(i))));
  }
  return z;
}

inline std::vector<matrix> split_theta(vector theta, int inputs, int hidden)
{
  std::vector<matrix> split_T;
  matrix Theta1(inputs,hidden);
  matrix Theta2(hidden+1,1);

  int w_index = 0;
  for (int i=0; i < hidden; ++i)
  {
    for(int k=0; k < inputs; ++k)
    {
      Theta1(k,i) = theta(w_index);
      ++w_index;
    }
  }

  for(int j=0; j < hidden; ++j)
  {
    Theta2(j,0) = theta(w_index);
    ++w_index;
  }

  split_T.push_back(Theta1);
  split_T.push_back(Theta2);

  return(split_T);
}

#endif


#include "ann.h"
#include <boost/range/algorithm/for_each.hpp>
#include <algorithm>

namespace ann {

float calculateLoss(const math::MatrixF &currentOutput, const math::MatrixF &expectedOutput)
{
    auto m = static_cast<float>(expectedOutput.getRowCount());
    auto hatLog = currentOutput.apply(std::logf);
    auto yml = expectedOutput.hadamardProduct(hatLog);
    auto LSum = math::sum(yml);
    auto L = -(1.0f / m) * LSum;
    return L;
}

float sigmoid(float value)
{
    return 1 / (1 + std::expf(value));
}

float sigmoidDerived(float value)
{
    const auto s = sigmoid(value);
    return s * (1.0f - s);
}

float leakyLRU(float value)
{
    return std::max<float>(0.01f * value, value);
}

float leakyLRUDerived(float value)
{
    return (value < 0.0f) ? 0.01f : 1.0f;
}

void ANN::configureNetwork(size_t inputSize)
{
    Layer::layerFx f = [](const math::MatrixF &m) {
        return m.apply(leakyLRU);
    };

    Layer::layerFx fd = [](const math::MatrixF &m) {
        return m.apply(leakyLRUDerived);
    };

    const size_t layerSize1 = 128;
    const size_t layerSize2 = 64;

    mLayers.emplace_back(inputSize, layerSize1, f, fd);
    mLayers.emplace_back(layerSize1, layerSize2, f, fd);
    mLayers.emplace_back(layerSize2, 10, math::columnWiseSoftMax<float>, fd);
}

float ANN::train(const math::MatrixF &input, const math::MatrixF &expectedOutput)
{
    auto inputData = input;
    boost::for_each(mLayers, [&inputData](Layer &layer){
        inputData = layer.feedForward(inputData);
    });

    auto loss = calculateLoss(mLayers.back().getResult(), expectedOutput);

    auto d = mLayers.back().beginBackPropagation(expectedOutput);
    std::for_each(mLayers.rbegin() + 1, mLayers.rend(), [&d](Layer &layer){
        d = layer.calculateGradients(d);
    });

    boost::for_each(mLayers, [](Layer &layer){
        layer.applyGradients(0.5f);
    });

    return loss;
}

const math::MatrixF& ANN::test(const math::MatrixF &input)
{
    auto inputData = input;
    boost::for_each(mLayers, [&inputData](Layer &layer) {
        inputData = layer.feedForward(inputData);
    });
    return mLayers.back().getResult();
}

IANN::trainData ANN::collectTrainData()
{
    trainData result;

    auto activationResult = mLayers.back().getResult();
    const uint32_t numberOfSamples = static_cast<uint32_t>(activationResult.getColumnCount());
    result.push_back(numberOfSamples);
    for (size_t i = 0; i < numberOfSamples; ++i)
    {
        const uint32_t predictionLabel = static_cast<uint32_t>(math::getMaxElementIndex(activationResult.columnBegin(i), activationResult.columnEnd(i)));
        result.push_back(predictionLabel);
    }
    return result;
}

}


#include "problemes.h"
#include "arithmetique.h"
#include "graphe.h"

#include <fstream>
#include <boost/algorithm/string.hpp>

typedef unsigned long long nombre;
typedef std::vector<nombre> vecteur;
typedef std::vector<vecteur> matrice;

typedef std::pair<nombre, nombre> paire;
typedef std::vector<paire> vecteur_paire;

ENREGISTRER_PROBLEME(83, "Path sum: four ways") {
    // In the 5 by 5 matrix below, the minimal path sum from the top left to the bottom right,
    // by moving left, right, up, and down, is indicated in bold red and is equal to 2297.
    //
    // Find the minimal path sum, in matrix.txt (right click and "Save Link/Target As..."), a 31K
    // text file containing a 80 by 80 matrix, from the top left to the bottom right by moving left,
    // right, up, and down.
    matrice m;
    std::ifstream ifs("data/p083_matrix.txt");
    std::string ligne;
    while (ifs >> ligne) {
        std::vector<std::string> v;
        boost::split(v, ligne, boost::is_any_of(","));
        vecteur l;
        for (const auto &s: v) {
            l.push_back(std::stoull(s));
        }
        m.push_back(std::move(l));
    }

    const nombre taille = m.size();
    graphe::Dijkstra::graphe graphe;
    for (nombre i = 0; i < taille; ++i)
        for (nombre j = 0; j < taille; ++j) {
            vecteur_paire v;
            const nombre poids = m[i][j];
            if (i > 0)
                v.emplace_back((i - 1) * taille + j, poids);
            if (j > 0)
                v.emplace_back(i * taille + j - 1, poids);
            if (i < taille - 1)
                v.emplace_back((i + 1) * taille + j, poids);
            if (j < taille - 1)
                v.emplace_back(i * taille + j + 1, poids);

            graphe[i * taille + j] = v;
        }

    graphe::Dijkstra dijkstra(graphe, 0, (taille - 1) * (taille + 1));
    nombre resultat = dijkstra.algorithme() + m[taille - 1][taille - 1];
    return std::to_string(resultat);
}


#include "RefinedDynamicalSystem.h"
#include <boost/timer.hpp>
#include <set>
#include <Eigen/QR>
#include <Eigen/Eigenvalues>

namespace abstract{

/// Constructs an empty buffer
template<class scalar>
CegarSystem<scalar>::CegarSystem(int dimension,int idimension) :
  DynamicalSystem<scalar>(dimension,idimension)
{}

/// Solves the Sylvester equation AX+XB=C for X
template<class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::solveSylvester(const MatrixS &A,const MatrixS &B,const MatrixS &C,bool BisDiagonal)
{
  MatrixS U,V;
  SolverMatrixType refA,refB;
  interToRef(refA,A);
  interToRef(refB,B);
  Eigen::RealSchur<SolverMatrixType> realSchur(refA.transpose());
  refToInter(U,realSchur.matrixU());//We want transposed so keep it this way
  refA=realSchur.matrixT().transpose();//Lower triangular
  if (BisDiagonal) {
    V=MatrixS::Identity(C.rows(),refB.rows());
  }
  else {
    interToRef(refB,B);
    realSchur.compute(refB);
    refToInter(V,realSchur.matrixU());
    refB=realSchur.matrixT();
  }
  MatrixS result=U*C*V;
  for (int row=0;row<result.rows();row++) {
    for (int col=0;col<result.cols();col++) {
      for (int i=0;i<row;i++) {
        result.coeffRef(row,col)-=refA.coeff(row,i)*result.coeff(i,col);
      }
      for (int i=0;i<col;i++) {
        result.coeffRef(row,col)-=result.coeff(row,i)*refB.coeff(i,col);
      }
      result.coeffRef(row,col)/=refA.coeff(row,row)+refB.coeff(col,col);
    }
  }
  return result;
}

/// Retrieves the characteristic polynomial coefficients of the dynamics
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getDynamicPolynomialCoefficients()
{
  MatrixC result=MatrixC::Zero(2,m_dimension+1);
  result.coeffRef(0,0)=ms_one;
  result.coeffRef(0,1)=-m_eigenValues.coeff(0,0);
  for (int i=1;i<m_dimension;i++) {
    result.row(1)=-result.row(0)*m_eigenValues.coeff(i,i);
    result.block(0,1,1,i+1)+=result.block(1,0,1,i+1);
  }
  return result.real();
}

/// Retrieves the reachability matrix [B AB A^2B ...A^{n-1}B]
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getReachabilityMatrix()
{
  MatrixS result(m_sensitivity.rows(),m_dimension*m_sensitivity.cols());
  result.block(0,0,m_sensitivity.rows(),m_sensitivity.cols())=m_sensitivity;
  MatrixS multiplier=m_dynamics;
  for (int i=1;i<m_dimension;i++)
  {
    result.block(0,i*m_sensitivity.cols(),m_sensitivity.rows(),m_sensitivity.cols())=multiplier*m_sensitivity;
    multiplier*=m_dynamics;
  }
  if (ms_trace_dynamics) ms_logger.logData(result,"Reachability Matrix");
  return result;
}

/// Retrieves the reachability matrix [1 a1 a2...a_{n-1};0 1 a1 ... a_{n-2}...]
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getCanonicalReachabilityMatrix()
{
  //TODO: not sure how this works on MIMO
  MatrixS result=MatrixS::Identity(m_sensitivity.rows(),m_dimension*m_sensitivity.cols());
  MatrixS coefficients=getDynamicPolynomialCoefficients();
  if (ms_trace_dynamics>=eTraceDynamics) {
    MatrixS matrix=coefficients.row(0);
    ms_logger.logData(matrix,"Coefficients");
  }
  for (int i=1;i<m_dimension;i++) result.block(i-1,i,1,m_dimension-i)=coefficients.block(0,1,1,m_dimension-i);
  if (ms_trace_dynamics>=eTraceDynamics) ms_logger.logData(result,"Canonical Reachability Matrix");
  return result;
}

/// Retrieves the transform matrix T : z=T^{-1}x turns A,B,C into controllable canonical form
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getReachableCanonicalTransformMatrix()
{
  MatrixS result=getReachabilityMatrix()*getCanonicalReachabilityMatrix();
  return result.inverse();
}

/// Retrieves the observability matrix [C CA CA^2 ...CA^{n-1}]^T
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getObservabilityMatrix()
{
  MatrixS result(m_dimension*m_outputSensitivity.rows(),m_outputSensitivity.cols());
  result.block(0,0,m_outputSensitivity.rows(),m_outputSensitivity.cols())=m_outputSensitivity;
  MatrixS multiplier=m_dynamics;
  for (int i=1;i<m_dimension;i++)
  {
    result.block(i*m_outputSensitivity.rows(),0,m_outputSensitivity.rows(),m_outputSensitivity.cols())=m_outputSensitivity*multiplier;
    multiplier*=m_dynamics;
  }
  return result;
}

/// Retrieves the observability matrix [1 0 ...;a1 1 0 ...;...;a_{n-1}...a1 1]^-1
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getInverseCanonicalObservabilityMatrix()
{
  //TODO: not sure how this works on MIMO
  MatrixS result=MatrixS::Identity(m_dimension*m_outputSensitivity.rows(),m_outputSensitivity.cols());
  MatrixS coefficients=getDynamicPolynomialCoefficients();
  for (int i=0;i<m_dimension;i++) {
    for (int j=0;j<i;j++) {
      result.coeffRef(i,j)=coefficients.coeff(0,m_dimension-i-j-1);
    }
  }
  return result;
}

/// Retrieves the transform matrix T : z=T^{-1}x turns A,B,C into observable canonical form
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getObservableCanonicalTransformMatrix()
{
  return getObservabilityMatrix().inverse()*getInverseCanonicalObservabilityMatrix();
}

/// Retrieves the reference gain
template <class scalar>
typename CegarSystem<scalar>::MatrixS CegarSystem<scalar>::getReferenceGain()
{
  return m_outputSensitivity*((m_dynamics-(m_sensitivity*m_feedback)).inverse()*m_sensitivity);
}

/// Retrieves the reach tube at the given iteration
template <class scalar>
/// Retrieves refined dynamics given a safety specification
AbstractPolyhedra<scalar>& CegarSystem<scalar>::getRefinedDynamics(int refinements,powerS iteration,int directions,inputType_t inputType)
{
  AbstractPolyhedra<scalar>&reachTube=getAbstractReachTube(iteration,2,directions,inputType);
  if (ms_trace_dynamics>=eTraceDynamics) {
    reachTube.logTableau("PreCegar:",true);
    reachTube.logVertices(true);
  }
  //if (m_safeReachTube.getPolyhedra().contains(reachTube)) return getAbstractDynamics(inputType);
  if (!m_safeReachTube.isEmpty()) {
    AbstractPolyhedra<scalar> bounds=synthesiseDynamicBounds(m_inputType,m_safeReachTube.getPolyhedra(eEigenSpace));
    if (ms_trace_dynamics>=eTraceDynamics) {
      bounds.logTableau("Projected Bounds: ",true);
    }
    for(int i=0;(i<refinements) && refineAbstractDynamics(bounds);i++);
    if (ms_trace_dynamics>=eTraceDynamics) {
      AbstractPolyhedra<scalar>& result=getRefinedAbstractReachTube(eNormalSpace);
      result.logTableau("PosCegar:",true);
      result.logVertices(true);
    }
  }
  return getAbstractDynamics(inputType);
}

/// Synthesises a bound on the dynamics given a known guard and eigenvectors.
template<class scalar>
AbstractPolyhedra<scalar> CegarSystem<scalar>::synthesiseDynamicBounds(inputType_t inputType,AbstractPolyhedra<scalar> &end)
{
  m_inputType=inputType;
  MatrixS vectors;
  int numVertices;
  getAbstractVertices(end.getDirections(),vectors,numVertices);
  vectors.transposeInPlace();
  MatrixS supports(vectors.rows(),1);
  MatrixS endSupports=end.getSupports();
  int perTemplate=supports.rows()/endSupports.rows();
  for (int i=0;i<endSupports.rows();i++)
  {
    for (int j=0;j<perTemplate;j++) {
      supports.coeffRef(i*perTemplate+j,0)=endSupports.coeff(i,0);
    }
  }
  if (m_inputType>eNoInputs) {
    MatrixS inSupports=m_accelVertices*end.getDirections();
    demergeAccelInSupports(supports,inSupports,endSupports.rows());
  }
  AbstractPolyhedra<scalar> bounds;
  bounds.load(vectors,supports);
  if (ms_trace_dynamics>=eTraceAll) {
    bounds.logTableau("Full Bounds",true);
  }
  bounds.removeRedundancies();
  return bounds;
}

/// Creates a model for the quantization noise as an input specification
template<class scalar>
AbstractPolyhedra<scalar> CegarSystem<scalar>::generateNoiseInput()
{
  int odimension=(m_odimension>0) ? m_odimension : m_dimension;
  int ndimension=1;
  AbstractPolyhedra<scalar> result(ndimension);
  int fbits=m_paramValues.coeff(eNumBits,1)-m_paramValues.coeff(eNumBits,2);
  if (fbits<=0) fbits=m_paramValues.coeff(eNumBits,0);
  if (fbits<=0) fbits=func::getDefaultPrec();
  scalar lsb=func::pow(this->ms_two,-fbits);
  MatrixS faces(2*ndimension,ndimension);
  MatrixS supports(2*ndimension,1);
  faces.block(0,0,ndimension,ndimension)=MatrixS::Identity(ndimension,ndimension);
  faces.block(ndimension,0,ndimension,ndimension)=-MatrixS::Identity(ndimension,ndimension);
  scalar fb_noise=m_feedback.cwiseAbs().sum()/*lpNorm<1>()*/+this->ms_one;//|Noise|<(|K|_1+1)lsb assuming q1,q2=1lsb,(q3:=q1->K) = (|K|_1q1+q2)
  supports.coeffRef(0,0)=lsb*(fb_noise);
  supports.coeffRef(1,0)=supports.coeff(0,0);
  result.load(faces,supports);
  return result;
}

/// Creates a model for the input of the closed loop
template<class scalar>
AbstractPolyhedra<scalar> CegarSystem<scalar>::generateFeedbackInput(int fdimension,bool makeNoise,MatrixS &sensitivity)
{
  AbstractPolyhedra<scalar> inputs(0);
  if (m_reference.getDimension()>0) {
    inputs.copy(m_reference);
    fdimension=m_reference.getDimension();
  }
  if (m_idimension>fdimension) {
    MatrixS inputFaces=m_inputs.getFaceDirections().rightCols(m_idimension-fdimension);
    MatrixS inSupports=m_inputs.getSupports();
    inputs.concatenate(inputFaces,inSupports);
  }
  if (makeNoise) {
    //m_closedLoop.setInputType(eVariableInputs);
    AbstractPolyhedra<scalar> noiseInputs=generateNoiseInput();
    inputs.concatenate(noiseInputs);
    sensitivity.conservativeResize(m_dimension,inputs.getDimension());
    sensitivity.block(0,inputs.getDimension()-noiseInputs.getDimension(),m_dimension,noiseInputs.getDimension())=MatrixS::Ones(m_dimension,noiseInputs.getDimension());
  }
  return inputs;
}

/// Retrieves a list of iterations whose reach set fails the specification
template<class scalar>
bool CegarSystem<scalar>::findCounterExampleIterations(powerList &iterations,AbstractPolyhedra<scalar> &bounds)
{
  AbstractPolyhedra<scalar>& dynamics=getAbstractDynamics(m_inputType);
  MatrixS &vertices=dynamics.getVertices(true);
  bool found=false;
  for (int i=0;i<vertices.rows();i++) {
    MatrixS point=vertices.row(i);
    if (!bounds.isInside(point)) {
      findIterations(point,iterations);
      found=true;
    }
  }
  return found;
}

/// Refines the abstraction in order to meet the safety specification
template<class scalar>
bool CegarSystem<scalar>::refineAbstractDynamics(AbstractPolyhedra<scalar> &bounds,powerList &iterations)
{
  AbstractPolyhedra<scalar>& dynamics=getAbstractDynamics(m_inputType);
  if (findCounterExampleIterations(iterations,bounds)) {
    typename powerList::iterator it;
    for (it=iterations.begin();it!=iterations.end();it++) {
      addSupportsAtIteration(dynamics,it->first,m_maxIterations);
    }
    return true;
  }
  return false;
}

/// Corrects the support set by the input offset
template <class scalar>
void CegarSystem<scalar>::demergeAccelInSupports(MatrixS &supports,MatrixS &inSupports,int numTemplates)
{
  if (!m_hasOnes || (m_inputType==eVariableInputs))  {
    for (int row=0;row<numTemplates;row++) {
      int pos=row*m_numVertices;
      supports.coeffRef(pos,0)-=inSupports.coeff(0,row);
      for (int point=1;point<m_numVertices;point++) {
        supports.coeffRef(pos+point,0)-=inSupports.coeff(point%inSupports.rows(),row);
      }
    }
  }
}

/// Retrieves the support set for the inputs
template <class scalar>
typename JordanMatrix<scalar>::MatrixS& CegarSystem<scalar>::getRefinedAccelInSupports()
{
  if (m_hasOnes && (m_inputType==eVariableInputs)) {
    AbstractPolyhedra<scalar>& inputDynamics=getAbstractDynamics(eParametricInputs);
    MatrixS supports;
    inputDynamics.maximiseAll(m_abstractInputVertices,supports);
    m_accelInSupports=supports.transpose();
    if (ms_trace_dynamics>=eTraceAbstraction) ms_logger.logData(m_accelInSupports,"Input Supports",true);
  }
  return m_accelInSupports;
}

/// Retrieves the reach tube at the given iteration
template <class scalar>
AbstractPolyhedra<scalar>& CegarSystem<scalar>::getRefinedAbstractReachTube(space_t space,bool guarded)
{
  boost::timer timer;
  AbstractPolyhedra<scalar>& init=m_initialState.getPolyhedra(eEigenSpace);
  AbstractPolyhedra<scalar>& dynamics=getAbstractDynamics(m_inputType);

  MatrixS& templates=getTemplates(eEigenSpace);
  if (ms_trace_time) ms_logger.logData(timer.elapsed()*1000,"Abstract Vertices: ",true);
  MatrixS supports;
  if (!dynamics.maximiseAll(m_abstractVertices,supports)) processError(dynamics.getName());

  if (m_inputType>eNoInputs) getRefinedAccelInSupports();
  if (ms_trace_dynamics>=eTraceAll) {
    traceSupports(templates,supports,dynamics,m_abstractVertices);
  }
  if (m_inputType>eNoInputs) {
    mergeAccelInSupports(supports,templates.cols());
    if (ms_trace_dynamics>=eTraceAll) {
      ms_logger.logData(m_abstractVertices,supports,"Combined",true);
    }
  }
  mergeAbstractSupports(templates,supports);
  MatrixS faces=templates.transpose();
  m_pAbstractReachTube->mergeLoad(init,faces,supports,eEigenSpace);
  AbstractPolyhedra<scalar>& result=m_pAbstractReachTube->getPolyhedra(space);
  if (guarded) getGuardedReachTube(result,space);
  if (ms_trace_dynamics>=eTraceAbstraction) result.logTableau();
  m_reachTime=timer.elapsed()*1000;
  result.setCalculationTime(m_reachTime);
  if (ms_trace_time) ms_logger.logData(m_reachTime,"Abstract Reach Time: ",true);
  return result;
}

#ifdef USE_LDOUBLE
  #ifdef USE_SINGLES
    template class CegarSystem<long double>;
  #endif
  #ifdef USE_INTERVALS
    template class CegarSystem<ldinterval>;
  #endif
#endif
#ifdef USE_MPREAL
  #ifdef USE_SINGLES
    template class CegarSystem<mpfr::mpreal>;
  #endif
  #ifdef USE_INTERVALS
    template class CegarSystem<mpinterval>;
  #endif
#endif

}


//
// Copyright 2005-2007 Adobe Systems Incorporated
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//

#include <boost/gil/image.hpp>
#include <boost/gil/typedefs.hpp>
#include <boost/gil/extension/io/jpeg.hpp>

// Creates a synthetic image defining the Mandelbrot set.
// The example relies on a virtual_2d_locator to iterate over the pixels in the destination view.
// The pixels (of type rgb8_pixel_t) are generated programmatically, and the code shows how to access
// the colour channels and set them to arbitrary values.

using namespace boost::gil;

// Models a Unary Function
template <typename P>   // Models PixelValueConcept
struct mandelbrot_fn
{
    using point_t = boost::gil::point_t;
    using const_t = mandelbrot_fn;
    using value_type = P;
    using reference = value_type;
    using const_reference = value_type;
    using argument_type = point_t;
    using result_type = reference;
    static constexpr bool is_mutable =false;

    value_type                    _in_color,_out_color;
    point_t                       _img_size;
    static const int MAX_ITER=100;        // max number of iterations

    mandelbrot_fn() {}
    mandelbrot_fn(const point_t& sz, const value_type& in_color, const value_type& out_color) : _in_color(in_color), _out_color(out_color), _img_size(sz) {}

    result_type operator()(const point_t& p) const {
        // normalize the coords to (-2..1, -1.5..1.5)
        // (actually make y -1.0..2 so it is asymmetric, so we can verify some view factory methods)
        double t=get_num_iter(point<double>(p.x/(double)_img_size.x*3-2, p.y/(double)_img_size.y*3-1.0f));//1.5f));
        t=pow(t,0.2);

        value_type ret;
        for (std::size_t k=0; k<num_channels<P>::value; ++k)
            ret[k]=(typename channel_type<P>::type)(_in_color[k]*t + _out_color[k]*(1-t));
        return ret;
    }

private:
    double get_num_iter(const point<double>& p) const {
        point<double> Z(0,0);
        for (int i=0; i<MAX_ITER; ++i) {
            Z = point<double>(Z.x*Z.x - Z.y*Z.y + p.x, 2*Z.x*Z.y + p.y);
            if (Z.x*Z.x + Z.y*Z.y > 4)
                return i/(double)MAX_ITER;
        }
        return 0;
    }
};

int main()
{
    using deref_t = mandelbrot_fn<rgb8_pixel_t>;
    using point_t = deref_t::point_t;
    using locator_t = virtual_2d_locator<deref_t,false>;
    using my_virt_view_t = image_view<locator_t>;

    boost::function_requires<PixelLocatorConcept<locator_t>>();
    gil_function_requires<StepIteratorConcept<locator_t::x_iterator>>();

    point_t dims(200,200);
    my_virt_view_t mandel(dims, locator_t(point_t(0,0), point_t(1,1), deref_t(dims, rgb8_pixel_t(255,0,255), rgb8_pixel_t(0,255,0))));
    write_view("out-mandelbrot.jpg",mandel, jpeg_tag{});

    return 0;
}


/* ------------------------------------------------------------------*\
                        ╦═╗╔═╗╔╦╗╔═╗╔═╗╔═╗
                        ╠╦╝║ ║║║║╚═╗║ ║║
                        ╩╚═╚═╝╩ ╩╚═╝╚═╝╚═╝
 Reduced Order Modelling, Simulation, Optimization of Coupled Systems
                            2017-2021

 Authors :
 Ashwin Nayak, Andres Prieto, Daniel Fernandez Comesana

 Disclaimer :
 In downloading this SOFTWARE you are deemed to have read and agreed to
 the following terms: This SOFTWARE has been designed with an exclusive
 focus on civil applications. It is not to be used for any illegal,
 deceptive, misleading or unethical purpose or in any military
 applications. This includes ANY APPLICATION WHERE THE USE OF THE
 SOFTWARE MAY RESULT IN DEATH, PERSONAL INJURY OR SEVERE PHYSICAL
 OR ENVIRONMENTAL DAMAGE. Any redistribution of the software must
 retain this disclaimer. BY INSTALLING, COPYING, OR OTHERWISE USING
 THE SOFTWARE, YOU AGREE TO THE TERMS ABOVE. IF YOU DO NOT AGREE TO
 THESE TERMS, DO NOT INSTALL OR USE THE SOFTWARE

 Acknowledgements:
 The ROMSOC project has received funding from the European Union’s
 Horizon 2020 research and innovation programme under the Marie
 Skłodowska-Curie Grant Agreement No. 765374.
\*-------------------------------------------------------------------*/

// Include necessary PYBIND files
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
namespace py = pybind11;

// Include necessary DOLFIN classes
#include <dolfin/function/Expression.h>
#include <dolfin/function/Constant.h>

// Include necessary BOOST classes
#include <boost/math/special_functions/legendre.hpp>
#include <boost/math/special_functions/bessel.hpp>
#include <boost/math/special_functions/hankel.hpp>

#include <complex>
#include <iostream>
class ScatteringExact_Re : public dolfin::Expression {
    public:
    double p0, k, a;
    ScatteringExact_Re() : dolfin::Expression(), p0(1.0), k(400.),  a(0.05){}

    void eval(Eigen::Ref<Eigen::VectorXd> values, Eigen::Ref<const Eigen::VectorXd> x) const {

        using namespace boost::math;

        std::complex<double> one_i(0.0,1.0);

        double r = x.norm();

        // Compute Scattered field
        double tol = 1e-10;
        int l=0;
        int N=50;
        std::complex<double> p_sc = 0., p_sc_add = 0.;

        auto J = [&] () -> std::complex<double> {
                        return ((double)l* sph_bessel(l,k*a) / (k*a) ) - sph_bessel(l+1,k*a);
                    };

        auto H = [&] () -> std::complex<double> {
                        return ((double)l* sph_hankel_1(l,k*a) / (k*a) ) - sph_hankel_1(l+1,k*a);
                    };

        p_sc_add = - p0 * J()* sph_hankel_1(l,k*r) * legendre_p(l,x[0]/r)/ H();
        p_sc += p_sc_add;

        while ((l<N) && (abs(p_sc_add/p_sc) > tol)) {
            ++l;
            std::complex<double>  A_l = (2*l+1.0)*std::pow(one_i,l)*J()/H();
            p_sc_add = -p0 * A_l * sph_hankel_1(l,k*r) * legendre_p(l,x[0]/r);
            p_sc += p_sc_add;
        }

        values[0] = cos(k*x[0]) + p_sc.real();
    }

};

PYBIND11_MODULE(SIGNATURE, m) {
    py::class_<ScatteringExact_Re, std::shared_ptr<ScatteringExact_Re>, dolfin::Expression>
    (m, "ScatteringExact_Re")
    .def(py::init<>())
    .def_readwrite("p0", &ScatteringExact_Re::p0)
    .def_readwrite("k", &ScatteringExact_Re::k)
    .def_readwrite("a", &ScatteringExact_Re::a)
    ;
}


#include "potentials.hpp"
#include "metropolis.hpp"
#include "distance.hpp"
#include <cmath>
#include <cassert>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include "yaml-cpp/yaml.h"

namespace pauth {

using namespace arma;

double abstract_LJ_full_potential::_U(const metropolis &sim) const {

  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &edge_lengths = sim.edge_lengths();
  const auto N = sim.N();
  double potential = 0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N - 1; ++i) {
    for (auto j = i + 1; j < N; ++j) {

      const double rij2 = sim.m(positions.col(i), positions.col(j),
                                edge_lengths);
      const double rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
      const double rat2 = (rzero * rzero) / rij2;
      const double rat6 = rat2 * rat2 * rat2;

      potential += 4.0 * get_well_depth(molecular_ids[i], molecular_ids[j]) *
                   (rat6 * rat6 - rat6);
    }
  }

  return potential;
}

double abstract_LJ_full_potential::_delta_U(const metropolis &sim, const size_t j,
                                       arma::vec &rn_j) const {

  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &ro_j = positions.col(j);
  const auto &edge_lengths = sim.edge_lengths();
  const auto N = sim.N();
  double dU = 0;

  #pragma omp parallel for reduction(+:dU)
  for (auto i = size_t{0}; i < N; ++i) {
    if (i == j) continue;

    const auto well_depth = get_well_depth(molecular_ids[i], molecular_ids[j]);
    double rij2 = sim.m(positions.col(i), ro_j, edge_lengths);
    double rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
    double rat2 = (rzero * rzero) / rij2;
    double rat6 = rat2 * rat2 * rat2;

    const double Uo = 4.0 * well_depth * (rat6 * rat6 - rat6);

    rij2 = sim.m(positions.col(i), rn_j, edge_lengths);
    rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
    rat2 = (rzero * rzero) / rij2;
    rat6 = rat2 * rat2 * rat2;

    const double Un = 4.0 * well_depth * (rat6 * rat6 - rat6);

    dU += Un - Uo;
  }

  return dU;
}

arma::vec abstract_LJ_full_potential::_forceij(const metropolis &sim, const size_t i,
                                          const size_t j) const {
  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &edge_lengths = sim.edge_lengths();
  const auto rij = sim.rij(positions.col(i), positions.col(j), edge_lengths);
  const double _rij2 = dot(rij, rij);
  const double rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
  const double rat2 = (rzero * rzero) / _rij2;
  const double rat6 = rat2 * rat2 * rat2;
  const double rat8 = rat6 * rat2;
  const double well_depth = get_well_depth(molecular_ids[i], molecular_ids[j]);

  return rij / rzero * well_depth * (48.0 * rat8 * rat6 - 24.0 * rat8);
}

double abstract_LJ_cutoff_potential::_U(const metropolis &sim) const {

  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &edge_lengths = sim.edge_lengths();
  const auto N = sim.N();
  double potential = 0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N - 1; ++i) {
    for (auto j = i + 1; j < N; ++j) {

      const double rij2 = sim.m(positions.col(i), positions.col(j),
                                edge_lengths);

      if (rij2 <= _rc2) {

        const double rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
        const double rz2 = rzero * rzero;
        const double rz4 = rz2 * rz2;
        const double rz8 = rz4 * rz4;

        const double dudr_rc = 6.0 * (rz4 * rz2 * rzero) / (_rc7)-12.0 *
                               (rz8 * rz4 * rzero) / (_rc13);
        const double u_rc = ((rz8 * rz4) / (_rc12) - (rz4 * rz2) / (_rc6));

        const double rat2 = rz2 / rij2;
        const double rat6 = rat2 * rat2 * rat2;

        potential += 4.0 * get_well_depth(molecular_ids[i], molecular_ids[j]) *
                     ((rat6 * rat6 - rat6) - u_rc -
                      (std::sqrt(rij2) - _cutoff) * dudr_rc);
      }
    }
  }

  return potential;
}

double abstract_LJ_cutoff_potential::_delta_U(const metropolis &sim,
                                              const size_t j,
                                              arma::vec &rn_j) const {

  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &ro_j = positions.col(j);
  const auto &edge_lengths = sim.edge_lengths();
  const auto N = sim.N();
  double dU = 0;

  #pragma omp parallel for reduction(+:dU)
  for (auto i = size_t{0}; i < N; ++i) {
    if (i == j) continue;

    const auto rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
    const auto well_depth = get_well_depth(molecular_ids[i], molecular_ids[j]);
    const double rz2 = rzero * rzero;
    const double rz4 = rz2 * rz2;
    const double rz8 = rz4 * rz4;

    const double dudr_rc = 6.0 * (rz4 * rz2 * rzero) / (_rc7)-12.0 *
                           (rz8 * rz4 * rzero) / (_rc13);
    const double u_rc = ((rz8 * rz4) / (_rc12) - (rz4 * rz2) / (_rc6));

    double Uo = 0.0;
    double rij2 = sim.m(positions.col(i), ro_j, edge_lengths);

    if (rij2 <= _rc2) {
      const double rat2 = rz2 / rij2;
      const double rat6 = rat2 * rat2 * rat2;

      Uo = 4.0 * well_depth *
           ((rat6 * rat6 - rat6) - u_rc -
            (std::sqrt(rij2) - _cutoff) * dudr_rc);
    }

    double Un = 0.0;
    rij2 = sim.m(positions.col(i), rn_j, edge_lengths);

    if (rij2 <= _rc2) {
      const double rat2 = rz2 / rij2;
      const double rat6 = rat2 * rat2 * rat2;

      Un = 4.0 * well_depth *
           ((rat6 * rat6 - rat6) - u_rc -
            (std::sqrt(rij2) - _cutoff) * dudr_rc);
    }

    dU += Un - Uo;
  }

  return dU;
}

arma::vec abstract_LJ_cutoff_potential::_forceij(const metropolis &sim,
                                                 const size_t i,
                                                 const size_t j) const {

  const auto &molecular_ids = sim.molecular_ids();
  const auto &positions = sim.positions();
  const auto &edge_lengths = sim.edge_lengths();
  const auto _rij = sim.rij(positions.col(i), positions.col(j), edge_lengths);
  const double _rij2 = dot(_rij, _rij);

  if (_rij2 <= _rc2) {

    const double rzero = get_rzero(molecular_ids[i], molecular_ids[j]);
    const double rz2 = rzero * rzero;
    const double rz4 = rz2 * rz2;
    const double rz8 = rz4 * rz4;

    const double dudr_rc = 24.0 * (rz4 * rz2 * rzero) / (_rc7)-48.0 *
                           (rz8 * rz4 * rzero) / (_rc13);

    const double rat2 = rz2 / _rij2;
    const double rat6 = rat2 * rat2 * rat2;
    const double rat8 = rat6 * rat2;
    const double well_depth =
        get_well_depth(molecular_ids[i], molecular_ids[j]);

    return _rij / rzero * well_depth *
           (48.0 * rat8 * rat6 - 24.0 * rat8 + dudr_rc / std::sqrt(_rij2));
  }

  return arma::zeros(sim.D());
}

double abstract_spring_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto &molecular_ids = sim.molecular_ids();
  const auto& positions = sim.positions();
  double potential = 0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N; ++i) {
    const double r2 = dot(positions.col(i), positions.col(i));
    potential += 0.5 * get_k(molecular_ids[i]) * r2;
  }

  return potential;
}

double abstract_spring_potential::_delta_U(const metropolis &sim,
                                           const size_t j,
                                           arma::vec &rn_j) const {
  const arma::vec &ro_j = sim.positions().col(j);
  return 0.5 * get_k(sim.molecular_ids()[j]) *
         (dot(rn_j, rn_j) - dot(ro_j, ro_j));
}

const_poly_spring_potential::const_poly_spring_potential(
    const std::initializer_list<double> &coeffs)
    : pcoeffs(coeffs) {

  for (size_t i = 1; i < pcoeffs.size(); ++i)
    fcoeffs.push_back(pcoeffs[i] * 2 * i);
}

double const_poly_spring_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto& positions = sim.positions();
  double potential = 0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N; ++i) {
    double Ui = 0.0;
    const double r2 = dot(positions.col(i), positions.col(i));
    for (auto p = size_t{0}; p < pcoeffs.size(); ++p) {
      double x = 1;
      for (auto k = size_t{0}; k < p; ++k)
        x *= r2;
      Ui += pcoeffs[p] * x;
    }
    potential += Ui;
  }

  return potential;
}

double const_poly_spring_potential::_delta_U(const metropolis &sim,
                                             const size_t j,
                                             arma::vec &rn_j) const {
  const arma::vec &ro_j = sim.positions().col(j);

  double Uo = 0.0;
  double r2 = dot(ro_j, ro_j);
  for (auto p = size_t{0}; p < pcoeffs.size(); ++p) {
    double x = 1;
    for (auto k = size_t{0}; k < p; ++k)
      x *= r2;
    Uo += pcoeffs[p] * x;
  }

  double Un = 0.0;
  r2 = dot(rn_j, rn_j);
  for (auto p = size_t{0}; p < pcoeffs.size(); ++p) {
    double x = 1;
    for (auto k = size_t{0}; k < p; ++k)
      x *= r2;
    Un += pcoeffs[p] * x;
  }

  return Un - Uo;
}

arma::vec const_poly_spring_potential::_forceij(const metropolis &sim, const size_t,
                                                const size_t) const {
  return arma::zeros(sim.D());
}

double const_quad_spring_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto &positions = sim.positions();
  double potential = 0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N; ++i) {
    const double r2 = dot(positions.col(i), positions.col(i));
    potential += a * r2 * r2 + b * r2 + c;
  }

  return potential;
}

double const_quad_spring_potential::_delta_U(const metropolis &sim,
                                             const size_t j,
                                             arma::vec &rn_j) const {
  const arma::vec &ro_j = sim.positions().col(j);
  const double r2o = dot(ro_j, ro_j);
  const double r2n = dot(rn_j, rn_j);

  return a * (r2n*r2n - r2o*r2o) + b * (r2n - r2o);
}

arma::vec abstract_spring_potential::_forceij(const metropolis &sim,
                                                      const size_t,
                                                      const size_t) const {
  return arma::zeros(sim.D());
}

arma::vec const_quad_spring_potential::_forceij(const metropolis &sim, const size_t,
                                                const size_t) const {
  return arma::zeros(sim.D());
}

double twostate_int_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto &positions = sim.positions();

  double potential = 0.0;

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N; ++i) {
    assert(_check_x(positions.col(i)));
    potential += _Ui(positions.col(i));
  }

  #pragma omp parallel for reduction(+:potential)
  for (auto i = size_t{0}; i < N; ++i) {
    for (auto j = size_t{i+1}; j < N; ++j) {
      potential += _Ui(positions.col(i)) * _Ui(positions.col(j));
    }
  }

  return potential;
}

double twostate_int_potential::_delta_U(const metropolis &sim, const size_t j,
                                        arma::vec &rn_j) const {

  const auto N = sim.N();
  const auto &positions = sim.positions();

  double dUi = _Ui(rn_j) - _Ui(positions.col(j));

  double U2 = 0.0;
  #pragma omp parallel for reduction(+:U2)
  for (auto i = size_t{0}; i < N; ++i) {
    if (i == j) continue;
    U2 += _Ui(positions.col(i));
  }

  return dUi + dUi * U2;

}

arma::vec twostate_int_potential::_forceij(const metropolis &sim, const size_t,
                                           const size_t) const {
  return arma::zeros(sim.D());
}

abstract_LJ_lookup_potential::abstract_LJ_lookup_potential(const char *fname) {
  vector<string> molecular_strs;
  const auto well_params = YAML::LoadFile(fname);

  for (const auto &well_param : well_params) {
    auto molecular_pair_str = well_param.first.as<string>();
    boost::split(molecular_strs, molecular_pair_str, boost::is_any_of(","));
    for (auto &molecular_str : molecular_strs) boost::trim(molecular_str);
    if (molecular_names_to_ids.count(molecular_strs[0]) &&
        molecular_names_to_ids.count(molecular_strs[1])) {
      unordered_pair<molecular_id> key {
        molecular_names_to_ids.at(molecular_strs[0]),
        molecular_names_to_ids.at(molecular_strs[1])
      };
      _well_depth_map[key] = well_param.second["epsilon"].as<double>();
      _rzero_map[key] = well_param.second["sigma"].as<double>();
    }
    else throw runtime_error(string("Molecular pair ") +
                             well_param.first.as<string>() +
                             string(" not found."));
  }
}

double abstract_LJ_lookup_potential::_get_well_depth(const molecular_id id1,
                                                     const molecular_id id2) const {
  unordered_pair<molecular_id> key { id1, id2 };
  return _well_depth_map.at(key);
}

double abstract_LJ_lookup_potential::_get_rzero(const molecular_id id1,
                                                const molecular_id id2) const {
  unordered_pair<molecular_id> key { id1, id2 };
  return _rzero_map.at(key);
}

double dipole_electric_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto &positions = sim.positions();
  double sum = 0.0;

  #pragma omp parallel for reduction(+:sum)
  for (auto i = size_t{0}; i < N; ++i) {
    auto& positionsi = positions.col(i);
    for (auto& kv : _efield)
      sum += -(positionsi(kv.first) * kv.second(positionsi));
  }

  return sum;

}

double dipole_electric_potential::_delta_U(const metropolis &sim,
  const size_t j, arma::vec &dx) const {

  auto& positionsj = sim.positions().col(j);

  double sum = 0.0;
  for (auto& kv : _efield)
    sum += -(dx(kv.first) * kv.second(positionsj));

  return sum;

}

arma::vec dipole_electric_potential::_forceij(const metropolis &sim,
    const size_t, const size_t) const { return arma::zeros(sim.D()); }

double abstract_dipole_strain_potential::_U(const metropolis &sim) const {

  const auto N = sim.N();
  const auto &positions = sim.positions();
  const auto &ids = sim.molecular_ids();
  double sum = 0.0;

  #pragma omp parallel for reduction(+:sum)
  for (auto i = size_t{0}; i < N; ++i) {

    auto& xsi = positions.col(i);
    auto pi = p(xsi);

    // Ui = 1/2 * p . X p
    sum += arma::dot(pi, inv_chi(xsi, ids[i]) * pi);

  }

  return 0.5 * sum;

}

double abstract_dipole_strain_potential::_delta_U(const metropolis &sim,
    const size_t j, arma::vec &dx) const {

  auto& xs = sim.positions().col(j);
  const auto id = sim.molecular_ids()[j];
  auto xs_new = xs + dx;

  auto pi = p(xs);
  auto pnew = p(xs_new);

  return 0.5 * (arma::dot(pi, inv_chi(xs, id) * pi) -
                arma::dot(pnew, inv_chi(xs_new, id) * pnew));

}

arma::vec abstract_dipole_strain_potential::_forceij
  (const metropolis &sim, const size_t, const size_t) const {
  return arma::zeros(sim.D());
}

// definitions for pure virtual destructors
abstract_potential::~abstract_potential() {}
abstract_LJ_potential::~abstract_LJ_potential() {}
abstract_LJ_full_potential::~abstract_LJ_full_potential() {}
abstract_LJ_lookup_potential::~abstract_LJ_lookup_potential() {}
abstract_LJ_cutoff_potential::~abstract_LJ_cutoff_potential() {}
abstract_spring_potential::~abstract_spring_potential() {}
abstract_dipole_strain_potential::~abstract_dipole_strain_potential() {}
abstract_dipole_strain_2d_potential::~abstract_dipole_strain_2d_potential() {}
abstract_dipole_strain_3d_potential::~abstract_dipole_strain_3d_potential() {}
abstract_dipole_strain_linear_potential::~abstract_dipole_strain_linear_potential() {}

} // namespace pauth


/*
 * lorenz_gmpxx.cpp
 *
 * This example demonstrates how odeint can be used with arbitrary precision types.
 *
 * Copyright 2011-2012 Karsten Ahnert
 * Copyright 2011-2012 Mario Mulansky
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or
 * copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#include <iostream>
#include <boost/array.hpp>

#include <gmpxx.h>

#include <boost/numeric/odeint.hpp>

using namespace std;
using namespace boost::numeric::odeint;

//[ gmpxx_lorenz
typedef mpf_class value_type;
typedef boost::array< value_type , 3 > state_type;

struct lorenz
{
    void operator()( const state_type &x , state_type &dxdt , value_type t ) const
    {
        const value_type sigma( 10.0 );
        const value_type R( 28.0 );
        const value_type b( value_type( 8.0 ) / value_type( 3.0 ) );

        dxdt[0] = sigma * ( x[1] - x[0] );
        dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
        dxdt[2] = -b * x[2] + x[0] * x[1];
    }
};
//]

struct streaming_observer
{
    std::ostream& m_out;

    streaming_observer( std::ostream &out ) : m_out( out ) { }

    template< class State , class Time >
    void operator()( const State &x , Time t ) const
    {
        m_out << t;
        for( size_t i=0 ; i<x.size() ; ++i ) m_out << "\t" << x[i] ;
        m_out << "\n";
    }
};

int main( int argc , char **argv )
{
    //[ gmpxx_integration
    const int precision = 1024;
    mpf_set_default_prec( precision );

    state_type x = {{ value_type( 10.0 ) , value_type( 10.0 ) , value_type( 10.0 ) }};

    cout.precision( 1000 );
    integrate_const( runge_kutta4< state_type , value_type >() ,
            lorenz() , x , value_type( 0.0 ) , value_type( 10.0 ) , value_type( value_type( 1.0 ) / value_type( 10.0 ) ) ,
            streaming_observer( cout ) );
    //]

    return 0;
}


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

#include <iostream>
#include <boost/math/tools/ulps_plot.hpp>
#include <boost/core/demangle.hpp>
#include <boost/math/distributions/kolmogorov_smirnov.hpp>

using boost::math::tools::ulps_plot;

int main() {
    using PreciseReal = long double;
    using CoarseReal = float;

    boost::math::kolmogorov_smirnov_distribution<CoarseReal> dist_coarse(10);
    auto pdf_coarse = [&, dist_coarse](CoarseReal x) {
        return boost::math::pdf(dist_coarse, x);
    };
    boost::math::kolmogorov_smirnov_distribution<PreciseReal> dist_precise(10);
    auto pdf_precise = [&, dist_precise](PreciseReal x) {
        return boost::math::pdf(dist_precise, x);
    };

    int samples = 2500;
    int width = 800;
    PreciseReal clip = 100;

    std::string filename1 = "kolmogorov_smirnov_pdf_" + boost::core::demangle(typeid(CoarseReal).name()) + ".svg";
    auto plot1 = ulps_plot<decltype(pdf_precise), PreciseReal, CoarseReal>(pdf_precise, 0.0, 1.0, samples);
    plot1.clip(clip).width(width);
    std::string title1 = "Kolmogorov-Smirnov PDF (N=10) ULP plot at " + boost::core::demangle(typeid(CoarseReal).name()) + " precision";
    plot1.title(title1);
    plot1.vertical_lines(10);
    plot1.add_fn(pdf_coarse);
    plot1.write(filename1);
}


#include <iomanip>
#include <iostream>

#include <mxpfit/approx_sph_bessel.hpp>

// #include <mxpfit/math/sph_bessel.hpp>

#include <boost/math/special_functions/bessel.hpp>

//==============================================================================
// Main
//==============================================================================

using Index              = Eigen::Index;
using Real               = double;
using Complex            = std::complex<Real>;
using RealArray          = Eigen::Array<Real, Eigen::Dynamic, 1>;
using ComplexArray       = Eigen::Array<Complex, Eigen::Dynamic, 1>;
using ExponentialSumType = mxpfit::ExponentialSum<Complex, Complex>;

void sph_bessel_kernel_error(int l, const RealArray& x,
                             const ExponentialSumType& ret,
                             bool verbose_print = false)
{
    RealArray exact(x.size());
    RealArray approx(x.size());

    for (Index i = 0; i < x.size(); ++i)
    {
        // exact(i)  = math::sph_bessel_j(l, x(i));
        exact(i)  = boost::math::sph_bessel(l, x(i));
        approx(i) = std::real(ret(x(i)));
    }

    RealArray abserr(Eigen::abs(exact - approx));

    if (verbose_print)
    {
        for (Index i = 0; i < x.size(); ++i)
        {
            std::cout << std::setw(24) << x(i) << ' '      // point
                      << std::setw(24) << exact(i) << ' '  // exact value
                      << std::setw(24) << approx(i) << ' ' // approximation
                      << std::setw(24) << abserr(i) << '\n';
        }
    }

    Index imax;
    abserr.maxCoeff(&imax);

    std::cout << "\n  abs. error in interval [" << x(0) << ","
              << x(x.size() - 1) << "]\n"
              << "    maximum : " << abserr(imax) << '\n'
              << "    averaged: " << abserr.sum() / x.size() << std::endl;
}

int main()
{
    std::cout.precision(15);
    std::cout.setf(std::ios::scientific);

    const Real threshold = 1.0e-12;
    const Real eps       = Eigen::NumTraits<Real>::epsilon();
    const Index lmax     = 20;
    const Index N        = 1000000; // # of sampling points

    std::cout
        << "# Approximation of spherical Bessel function by exponential sum\n";

    RealArray x = Eigen::pow(10.0, RealArray::LinSpaced(N, -5.0, 7.0));
    ExponentialSumType ret;
    mxpfit::ApproxSphBesselFunction<Real> sph_bessel_approx;
    for (Index l = 0; l <= lmax; ++l)
    {
        std::cout << "\n# --- order " << l;
        ret = sph_bessel_approx.compute(l, threshold);
        const auto thresh_weight =
            std::max(eps, threshold) / std::sqrt(Real(ret.size()));
        ret = mxpfit::removeIf(
            ret, [=](const Complex& /*exponent*/, const Complex& wi) {
                return std::abs(std::real(wi)) < thresh_weight &&
                       std::abs(std::imag(wi)) < thresh_weight;
            });

        const bool verbose = false;
        sph_bessel_kernel_error(l, x, ret, verbose);
        std::cout << " (" << ret.size() << " terms approximation)\n";
        std::cout << "# real(exponent), imag(exponent), real(weight), "
                     "imag(weight)\n";
        for (Index i = 0; i < ret.size(); ++i)
        {
            std::cout << std::setw(24) << std::real(ret.exponent(i)) << '\t'
                      << std::setw(24) << std::imag(ret.exponent(i)) << '\t'
                      << std::setw(24) << std::real(ret.weight(i)) << '\t'
                      << std::setw(24) << std::imag(ret.weight(i)) << '\n';
        }
        std::cout << '\n' << std::endl;

        // std::cout << "# no. of terms and (exponents, weights)\n" << ret <<
        // '\n'; std::cout << "# sum of weights: " << ret.weights().sum() <<
        // '\n';
    }

    return 0;
}


<%
cfg['compiler_args'] = ['-std=c++11','-DUSE_DOUBLE']
cfg['include_dirs'] = ['eigen', 'include']
cfg['sources'] = ['./HODLR_Matrix.cpp',
    './HODLR_Node.cpp',
    './HODLR_Tree.cpp',
    './HODLR_Tree_NonSPD.cpp',
    './HODLR_Tree_SPD.cpp',
    './KDTree.cpp',
    './sample_funs.cpp',
    './predict.cpp']
setup_pybind11(cfg)
%>

#include <squaredeMat.hpp> //squared exponential kernel
#include <squaredeP1Mat.hpp>
#include <random>
#include <chrono>
#include <iostream>
#include <fstream>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <Eigen/Dense>
#include "HODLR_Tree.hpp"
#include "sample_funs.h"

using std::gamma_distribution;
using std::uniform_real_distribution;
using std::normal_distribution;

namespace py = pybind11;

void predict_module(py::module &);

Eigen::VectorXd samplefstar_HODLR(Eigen::MatrixXd X, Eigen::MatrixXd Y, Eigen::MatrixXd Xtest, double sig, double rho, double tau, HODLR_Tree* T, double multiplier, std::function<float()> &r_std_normal, int s) {

    /*
       Sample a draw of the GP function f*|f, sig, rho, tau, x*.
       Assume a squared exponential Gaussian Process based on
       observed function f at new test points x*.
    */

    int Ntest =  Xtest.rows();
    int N =  X.rows();
    int D =  X.cols();

    double sigsq = pow(sig, 2.0);
    double tmpSSR;

    Eigen::VectorXd fstarsamp(Ntest);
    Eigen::VectorXd KobsNew(N);

    for (int i = 0; i < Ntest; i++) {
      Eigen::RowVectorXd Xtest_i = Xtest.row(i);

      // Covariance between X and Xtest
      for (int j = 0; j < N; j++) {
        Eigen::RowVectorXd tmp = X.row(j) - Xtest_i;
        tmpSSR = 0.0;
        for (int d = 0; d < D; d++) {
          tmpSSR = tmpSSR + pow(tmp(d), 2.0);
        }
        KobsNew(j) = sigsq * exp(- tmpSSR * rho);
      }

      // Variance at Xtest
      double kNewNew = sigsq + 1e-8;

      // Posterior mean and variance of f* at xtest(i)
      double sdstar = pow(kNewNew - (multiplier * KobsNew.transpose() * T->solve(tau * KobsNew))(0, 0), 0.5);
      double mustar = (multiplier * KobsNew.transpose() * T->solve(tau * Y))(0, 0);

      auto normal_samp = r_std_normal();

      fstarsamp(i) =  sdstar * normal_samp + mustar;
    }

    return fstarsamp;
}

Eigen::VectorXd get_transition_probs_byind(Eigen::VectorXi rhovec_inds, double krnl, int ind_current) {
  /*
    Creates transition probability matrix for Metropolis Hastings update of lengthscale
  */
  int nrhos = rhovec_inds.rows();
  Eigen::VectorXd prop_prob(nrhos);
  for (int i = 0; i < nrhos; i++) {
    prop_prob(i) = exp(-krnl * pow((double)rhovec_inds(i) - (double)ind_current, 2.0));
  }
  prop_prob(ind_current) = 0.0;
  double sumInv = 1.0 / prop_prob.sum();
  prop_prob = sumInv * prop_prob;
  return prop_prob;
}

py::list sampleGP_HODLR(Eigen::MatrixXd X, Eigen::VectorXd Y,
                          double sig, Eigen::VectorXd rho_choices, double tau,
                          bool regression=true,
                          bool Gibbs_ls=false, bool default_MHkernel=true,
                          double numeric_MHkernel=-1.0,
                          double a_f=1, double b_f=1,
                          double a_tau=1, double b_tau=1,
                          double tol=1e-12, int M=20,
                          bool save_fsamps=true, unsigned int seed=169,
                          int burnin=1000, int nsamps=100, int thin=10, bool verbose=false) {
  /*
    C++ sample hyperparameter and function draws
    from a squared exponential Gaussian Process
    based on observed data y having precision tau.
    X   -matrix of locations
    Y   -matrix of observed data
    tau -data precision
    sig -function std deviation
    rho -length-scale
    a_f and b_f - prior sig^(-2) ~ Ga(a_f/2, b_f/2)
    a_tau and b_tau - prior tau ~ Ga(a_tau/2, b_tau/2)
    tol -specified tolerance for accuracy of calculations
    Gibbs_ls: bool, whether to use Gibbs or MH to sample length scale
    M   -Max submatrix size
  */

  // For sampling random numbers
  std::mt19937 generator(seed);

  // For sampling standard uniforms (for MH acceptance evaluation)
  uniform_real_distribution<float> unif(0, 1);
  std::function<float()> runif = bind(unif, generator);

  // For sampling standard normals (e.g. for sampling f)
  normal_distribution<float> normal(0, 1);
  std::function<float()> r_std_normal = bind(normal, generator);

  // Data sizes and initializing fsamp and params / transformed params
  int N = X.rows();

  Eigen::VectorXd KobsNew(N);
  Eigen::VectorXd fsamp(N);

  double sqrt_tau;
  if (!regression) { // If you observe non-noisy data
    fsamp = Y;
  }
  else {
    sqrt_tau = pow(tau, 0.5);
  }

  double sigsq = pow(sig, 2.0);
  double prec_f = 1 / sigsq;

  // Set up HODLR details
  int n_levels = log(N / M) / log(2);
  bool is_sym = true;
  bool is_pd  = true;

  // Create rho and matrices for each value option
  int nrhos = rho_choices.rows();
  Eigen::MatrixXd transition_probs_mat(nrhos,nrhos);
  bool fix_ls;
  int rho_ind;

  Eigen::VectorXd logdetK_all(nrhos);
  SQRExponential_Kernel* K_tmp;
  HODLR_Tree* S_tmp;
  std::vector<HODLR_Tree*> Svec;
  Svec.reserve(nrhos);

  if (nrhos != 1) {
    fix_ls = false;
    rho_ind = round(nrhos / 2);
    if (!Gibbs_ls) {
      // Make transition probability matrix from each "view" of the data
      // (i.e., from each index at which you could make a proposal)
      bool cond1 = (numeric_MHkernel > 0);
      double krnl;
      if (cond1 | default_MHkernel) {
        if (cond1) {
          krnl = numeric_MHkernel;
        }

        if (default_MHkernel) {
          krnl = 1.0 / pow((double)nrhos, 1.0);
        }

        Eigen::VectorXi rhovec_inds(nrhos);
        for (int i = 0; i < nrhos; i++) {
          rhovec_inds(i) = i;
        }
        for (int i = 0; i < nrhos; i++) { // Each col is "view" from that index.
          transition_probs_mat.col(i) = get_transition_probs_byind(rhovec_inds, krnl, i);
        }
      } else{
        printf("default_MHkernel must be TRUE or numeric_MHkernel needs to be set > 0(see function description for details).");
      }
    }
  }
  else {
    fix_ls = true;
    rho_ind = 0;
    if (verbose) {
      printf("Length scale fixed to provided input: %.2f.\n", rho_choices(rho_ind));
      printf("To sample length scale from discrete options provide vector rho_choices.\n");
    }
  }

  // Svec here is K in the paper
  for (int i = 0; i < nrhos; i++) {
    // Set up squared exponential kernel K with sigf fixed to 1.
    K_tmp  = new SQRExponential_Kernel(X, N, 1, rho_choices(i));
    S_tmp  = new HODLR_Tree(n_levels, tol, K_tmp); // Without noise (i.e., Sigma)
    Svec.push_back(S_tmp);
    Svec[i]->assembleTree(is_sym, is_pd);
    Svec[i]->factorize();
    logdetK_all(i) = Svec[i]->logDeterminant();
  }

  // Initialize first sample of rho
  double rho = rho_choices(rho_ind);

  // Set up storage
  int samp_count = 0;
  int total_draws = burnin + nsamps * thin;
  Eigen::VectorXd tau_save(nsamps);
  Eigen::VectorXd sigf_save(nsamps);
  Eigen::VectorXd rho_save(nsamps);
  int nsamps_f = nsamps;
  if (!save_fsamps) {
    nsamps_f = 0;
  }
  Eigen::MatrixXd f_save(N, nsamps_f);

  for (int s = 0; s < total_draws; s++) {
    bool save_samps = (s >= burnin) & (((s + 1) % thin) == 0);

    // Non-noisy data
    if (regression) {

      // Assemble the kernel with noise term tau
      SQRExponentialP1_Kernel* L  = new SQRExponentialP1_Kernel(X, N, sig, rho, tau);
      HODLR_Tree* T = new HODLR_Tree(n_levels, tol, L); // With noise (i.e. Sigma + I/tau)
      T->assembleTree(is_sym, is_pd);
      T->factorize();

      // Sample f
      fsamp = samplef_HODLR(X, Y, sig, tau, T, Svec[rho_ind], r_std_normal);

      delete T;
      delete L;
    }

    // Sample function variance
    prec_f = sample_prec_f(Svec[rho_ind], fsamp, a_f, b_f, generator);
    sig = pow(prec_f, -0.5);
    sigsq = pow(sig, 2.0);

    // Sample length scale
    if (!fix_ls) {
      if (Gibbs_ls) {
        rho_ind = sample_rho_gibbs(prec_f, fsamp, Svec, logdetK_all, generator);
      }
      else {
        rho_ind = sample_rho_mh(rho_ind, transition_probs_mat, prec_f, fsamp, Svec, logdetK_all, generator, runif);
      }
      rho = rho_choices(rho_ind);
    }

    // Sample noise precision
    if (regression) {
      tau = sample_tau(Y, fsamp, a_tau, b_tau, generator);
      sqrt_tau = pow(tau, 0.5);
    }

    if ((s == burnin) and verbose) {
      printf("Finished with burnin, beginning sampling. \n");
    }
    if ((10 * (s + 1) % total_draws == 0) and verbose) {
      printf("%d%% done \n", 100 * (s + 1) / total_draws);
    }

    // Save samples if you're past burnin
    if (save_samps) {
      if (regression) {
        tau_save(samp_count) = tau;
      }
      sigf_save(samp_count) = sig;
      rho_save(samp_count) = rho;

      if (save_fsamps) {
        f_save.col(samp_count) = fsamp;
      }

      samp_count = samp_count + 1;
    }

  } // END for (int s=0; s < total_draws; s++)

  delete S_tmp;
  delete K_tmp;
  std::vector<HODLR_Tree*>().swap(Svec);

  py::list out;
  out.append(tau_save);
  out.append(sigf_save);
  out.append(rho_save);
  out.append(f_save);
  return out;
}

PYBIND11_MODULE(sample, m) {
    m.doc() = "C++ Implementation of FIFA-GP.";
    m.def("samplegp", &sampleGP_HODLR, "1-d GP.");
    predict_module(m);
}


#include "CGAL_point_transform_function.hpp"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Regular_triangulation_3.h>
#include <CGAL/Regular_triangulation_euclidean_traits_3.h>
#include <CGAL/Fixed_alpha_shape_3.h>
#include <CGAL/Triangulation_vertex_base_with_info_3.h>
#include <CGAL/Fixed_alpha_shape_vertex_base_3.h>
#include <CGAL/Fixed_alpha_shape_cell_base_3.h>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <utility>
#include <iterator>
#include <stdexcept>

#include <iostream>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Regular_triangulation_euclidean_traits_3<K> Gt;
typedef CGAL::Triangulation_vertex_base_with_info_3<unsigned int, Gt> Tb;
typedef CGAL::Fixed_alpha_shape_vertex_base_3<Gt, Tb> Vb;
typedef CGAL::Fixed_alpha_shape_cell_base_3<Gt> Fb;
typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds;
typedef CGAL::Regular_triangulation_3<Gt,Tds> Triangulation_3;
typedef CGAL::Fixed_alpha_shape_3<Triangulation_3> Fixed_alpha_shape_3;
typedef Fixed_alpha_shape_3::Cell_handle Cell_handle;
typedef Fixed_alpha_shape_3::Vertex_handle Vertex_handle;
typedef Fixed_alpha_shape_3::Facet Facet;
typedef Fixed_alpha_shape_3::Edge Edge;
typedef Gt::Weighted_point Weighted_point;
typedef Gt::Bare_point Bare_point;

template <typename T>
T* init_mem(unsigned int size, const T& init_val){
	T* mem = (T*)malloc(sizeof(T)*size);
	if(!mem){
		throw std::runtime_error("Insufficient memory");
	}
	for(unsigned int i = 0; i < size; ++i){
		mem[i] = init_val;
	}
	return mem;
}

void alpha_shapes(
	unsigned int num_points, double alpha,
	const double* points, const double* weights,
	unsigned int* num_edges, unsigned int** edges,
	unsigned int* num_triangles, unsigned int** triangles,
	unsigned int* num_tetrahedra, unsigned int** tetrahedra
){
	busv::CGALPointTransformFunction<Gt> tfunc(points, weights);
	unsigned int i;

	std::cout << "alpha " << alpha << std::endl;

	//build one alpha_shape with alpha=0
	Fixed_alpha_shape_3 as(
		boost::make_transform_iterator(boost::counting_iterator<unsigned int>(0), tfunc),
		boost::make_transform_iterator(boost::counting_iterator<unsigned int>(num_points), tfunc),
		alpha
	);

	i = 0;
	*num_edges = std::distance(as.finite_edges_begin(), as.finite_edges_end());
	*edges = init_mem<unsigned int>((*num_edges)*2, 0);
	for(Fixed_alpha_shape_3::Finite_edges_iterator it = as.finite_edges_begin(), eit = as.finite_edges_end(); it!= eit; ++it){
		Fixed_alpha_shape_3::Edge e = *it;
		//if(as.classify(e) == Fixed_alpha_shape_3::SINGULAR){
		if(as.classify(e) != Fixed_alpha_shape_3::EXTERIOR){
			(*edges)[i] = e.first->vertex(e.second)->info();
			(*edges)[i+1] = e.first->vertex(e.third)->info();
			i += 2;
		}
//		(*edges)[i] = it->vertex(0)->info();
//		(*edges)[i+1] = it->vertex(1)->info();
	}
	*num_edges = i/2;

	i = 0;
	*num_triangles = std::distance(as.finite_facets_begin(), as.finite_facets_end());
	*triangles = init_mem<unsigned int>((*num_triangles)*3, 0);
	for(Fixed_alpha_shape_3::Finite_facets_iterator it = as.finite_facets_begin(), eit = as.finite_facets_end(); it!= eit; ++it){
		Fixed_alpha_shape_3::Facet f = *it;
		//if(as.classify(f) == Fixed_alpha_shape_3::SINGULAR){
		if(as.classify(f) != Fixed_alpha_shape_3::EXTERIOR){
			(*triangles)[i] = f.first->vertex((f.second+1)%4)->info();
			(*triangles)[i+1] = f.first->vertex((f.second+2)%4)->info();
			(*triangles)[i+2] = f.first->vertex((f.second+3)%4)->info();
			i += 3;
		}
	}
	*num_triangles = i/3;

	i = 0;
	*num_tetrahedra = std::distance(as.finite_cells_begin(), as.finite_cells_end());
	*tetrahedra = init_mem<unsigned int>((*num_tetrahedra)*4, 0);
	for(Fixed_alpha_shape_3::Finite_cells_iterator it = as.finite_cells_begin(), eit = as.finite_cells_end(); it!= eit; ++it){
		Fixed_alpha_shape_3::Cell c = *it;
		//if(as.classify(it) == Fixed_alpha_shape_3::INTERIOR){
//		if(as.classify(it) != Fixed_alpha_shape_3::EXTERIOR){
			(*tetrahedra)[i] = c.vertex(0)->info();
			(*tetrahedra)[i+1] = c.vertex(1)->info();
			(*tetrahedra)[i+2] = c.vertex(2)->info();
			(*tetrahedra)[i+3] = c.vertex(3)->info();
			i += 4;
//		}
	}
	*num_tetrahedra = i/4;
}


#include <iostream>
#include <g2o/core/base_vertex.h>
#include <g2o/core/base_unary_edge.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/optimization_algorithm_levenberg.h>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/core/optimization_algorithm_dogleg.h>
#include <g2o/solvers/dense/linear_solver_dense.h>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <cmath>
#include <chrono>

using namespace std;

/* Curve fitting for function "y = e^(a*x*x + b*x + c)"
Only need a single vertex with three parameter a,b,c in it.
All edges are unary edges connected to the only vertex
*/

// vertices as optimization target
// inherent from g2o::BaseVertex, predefined Vertex
class CurveFittingVertex: public g2o::BaseVertex<3, Eigen::Vector3d>
{
    public:
        EIGEN_MAKE_ALIGNED_OPERATOR_NEW
        virtual void setToOriginImpl(){
            _estimate << 0, 0, 0;
        }

        virtual void oplusImpl( const double* update ) // 更新
        {
            _estimate += Eigen::Vector3d(update);
        }

        // read and write leave empty
        virtual bool read( istream& in ) {}
        virtual bool write( ostream& out ) const {}
};

// edges to compute error
class CurveFittingEdge: public g2o::BaseUnaryEdge<1, double, CurveFittingVertex>
{
    public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    CurveFittingEdge( double x ): BaseUnaryEdge(), _x(x) {}

    void computeError(){
        const CurveFittingVertex* v = static_cast<const CurveFittingVertex*>(_vertices[0]);
        const Eigen::Vector3d abc = v->estimate();
        _error(0, 0) = _measurement - std::exp(abc(0, 0)*_x*_x + abc(1, 0)*_x + abc(2, 0));
    }
    virtual bool read( istream& in ) {}
    virtual bool write( ostream& out ) const {}
    public:
        double _x;
};

int main(){
    cv::RNG rng;
    double a=1.0, b=2.0, c=1.0;   // Real parameters
    int N = 200;
    double w_sigma=1.0;
<<<<<<< HEAD
=======

>>>>>>> d3aa7eab7a66633ee6b6716534884a999f4bfb7a
    vector<double> x_data, y_data;      // generated data

    cout<<"generating data: "<<endl;
    for ( int i=0; i<N; i++ )
    {
        double x = i/100.0;
        x_data.push_back ( x );
        y_data.push_back (
            exp ( a*x*x + b*x + c ) + rng.gaussian ( w_sigma )
        );
        cout<<x_data[i]<<" "<<y_data[i]<<endl;
    }

    double parameters[3] = {0,0,0};  // Parameters need to be solved

    typedef g2o::BlockSolver<g2o::BlockSolverTraits<3, 1>> Block;   // input 3 dims, output 1 dims
    Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>();

    Block *solver_ptr = new Block(linearSolver);

    g2o::OptimizationAlgorithmLevenberg *solver = new g2o::OptimizationAlgorithmLevenberg(solver_ptr);

    g2o::SparseOptimizer optimizer;
    optimizer.setAlgorithm (solver);
    optimizer.setVerbose(true);

    // add Vertex as optimization target

    CurveFittingVertex *v = new CurveFittingVertex();
    v->setEstimate(Eigen::Vector3d(0, 0, 0));
    v->setId(0);
    optimizer.addVertex(v);

    for(int i=0;i<N;i++){
        CurveFittingEdge *edge = new CurveFittingEdge(x_data[i]);
        edge->setId(i);
        edge->setVertex(0, v);
        edge->setMeasurement(y_data[i]);
        edge->setInformation(Eigen::Matrix<double, 1, 1>::Identity()*1/(w_sigma*w_sigma));
        optimizer.addEdge(edge);
    }

    // Execute optimization.
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
    optimizer.initializeOptimization();
    optimizer.optimize(100);
    chrono::steady_clock::time_point t2 = chrono::steady_clock::now();

    chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>( t2-t1 );
    cout<<"solve time cost = "<<time_used.count()<<" seconds. "<<endl;

    Eigen::Vector3d parameter_estimate = v->estimate();
    cout << "Estimate model" << parameter_estimate.transpose() << endl;

}


#pragma once
#include <fmt/core.h>
#include <Eigen/Dense>
#include <random>

template<typename T>
using PCoord = Eigen::Matrix<T, 1, 3>;

enum JastrowType {
    SIMPLE_JASTROW,
};

enum AtomicWfnType {
    VB,
    MO,
};

template<typename T>
class WaveFn {
public:
    virtual ~WaveFn(){};
    // return the value of wave function
    virtual T value(const PCoord<T>&)=0;

    // return three components of grad wave function
    virtual PCoord<T> grad(const PCoord<T>&)=0;

    // return laplacian of the wave function
    virtual T laplace(const PCoord<T>&)=0;
};

// Simple Wave function $$\phi(r) = (1+cr)e^{-\alpha r}$$
template <typename T>
class AtomicWaveFn: public WaveFn<T> {
public:
    AtomicWaveFn(T c, T alpha): c(c), alpha(alpha) {};
    ~AtomicWaveFn(){};

    T value(const PCoord<T>& coord) {
        auto r = coord.norm();
        auto ar = alpha*r;
        return (1+c*r)*std::exp(-ar);
    }

    PCoord<T> grad(const PCoord<T>& coord) {
        auto r = coord.norm();
        auto ar = alpha*r;
        auto coeff = (c-alpha*(c*r+1))*std::exp(-ar);
        return coeff*coord.normalized();
    }

    T laplace(const PCoord<T>& coord) {
        auto r = coord.norm();
        auto ar = alpha*r;
        return (c*(ar*(ar-4)+2) + alpha*(ar-2))*std::exp(-ar)/r;
    }

private:
    T c, alpha;
};

// Composed Wave functions: VB
template<typename T>
class VBWaveFn: public WaveFn<T> {
public:
    VBWaveFn(T c, T alpha, const PCoord<T>& R1, const PCoord<T>& R2):
        c(c), alpha(alpha), R1(R1), R2(R2) {
        phi1 = new AtomicWaveFn<T>(c, alpha);
        phi2 = new AtomicWaveFn<T>(c, alpha);
    }

    ~VBWaveFn() {
        delete phi1, phi2;
    }

    T value(const PCoord<T>& r) {
        return phi1->value(r-R1)*phi2->value(r-R2);
    }

    PCoord<T> grad(const PCoord<T>& r) {
        return phi2->value(r-R2)*phi1->grad(r-R1) +
               phi1->value(r-R1)*phi2->grad(r-R2);
    }

    T laplace(const PCoord<T>& r) {
        return phi2->value(r-R2)*phi1->laplace(r-R1) +
               phi1->value(r-R1)*phi2->laplace(r-R2) +
               2*(phi1->grad(r-R1)).dot(phi2->grad(r-R2));
    }

private:
    T c, alpha;
    PCoord<T> R1, R2;
    AtomicWaveFn<T>* phi1;
    AtomicWaveFn<T>* phi2;
};

// Composed Wave functions: MO
template <typename T>
class MOWaveFn: public WaveFn<T> {
public:
    MOWaveFn(T c, T alpha, const PCoord<T>& R1, const PCoord<T>& R2):
        c(c), alpha(alpha), R1(R1), R2(R2) {
        phi1 = new AtomicWaveFn<T>(c, alpha);
        phi2 = new AtomicWaveFn<T>(c, alpha);
    }

    T value(const PCoord<T>& r) {
        return phi1->value(r-R1)+phi2->value(r-R2);
    }

    PCoord<T> grad(const PCoord<T>& r) {
        return phi1->grad(r-R1) + phi2->grad(r-R2);
    }

    T laplace(const PCoord<T>& r) {
        return phi1->laplace(r-R1) + phi2->laplace(r-R2);
    }

private:
    T c, alpha;
    PCoord<T> R1, R2;
    AtomicWaveFn<T>* phi1;
    AtomicWaveFn<T>* phi2;
};

// Define Jastrow wavefunction
template<typename T>
// class JastrowWfn: public WaveFn<T> {
class JastrowWfn {
public:
    JastrowWfn(T factor): factor(factor) {}

    ~JastrowWfn() {}

    inline T value(const PCoord<T>& r1, const PCoord<T>& r2) {
        return std::exp(-ufunc(r1, r2));
    }

    // The gradient is symmetric w.r.t r1 and r2
    std::pair<PCoord<T>, PCoord<T>>
    grad(const PCoord<T>& r1, const PCoord<T>& r2) {
        auto val = value(r1, r2);
        auto r12 = r1 - r2;
        auto r = r12.norm();
        auto dv = -r12/(2*r*std::pow(1+r/factor, 2));
        return {-dv*val, dv*val};
    }

    std::pair<T, T>
    laplace(const PCoord<T>& r1, const PCoord<T>& r2) {
        auto val = value(r1, r2);
        auto r = (r1 - r2).norm();
        auto dv = -1/(2*std::pow(1+r/factor, 2));
        auto d2v = -1/(r*std::pow(1+r/factor, 3));
        auto ret = (dv*dv - d2v)*val;
        return {ret, ret};
    }
private:
    T factor;
    inline T ufunc(const PCoord<T>& r1, const PCoord<T> r2) {
        auto r = (r1 - r2).norm();
        return factor/(2 + 2*r/factor);
    }
};

template<typename T, JastrowType Jastrow, AtomicWfnType AtomicWfn>
class H2Mol {

public:
    const JastrowType jastrow_type = Jastrow;
    const AtomicWfnType atomicwfn_type = AtomicWfn;

    H2Mol(T factor, T c, T alpha, const PCoord<T>& R1, const PCoord<T>& R2):
        R1(R1), R2(R2) {
        switch (Jastrow) {
            case JastrowType::SIMPLE_JASTROW:
                jastrow = new JastrowWfn<T>(factor);
                break;
            default:
                throw std::runtime_error("Invalid jastrow function.");
                break;
        }

        switch(AtomicWfn) {
            case AtomicWfnType::MO:
                atomicwfn = new MOWaveFn<T>(c, alpha, R1, R2);
                break;
            case AtomicWfnType::VB:
                atomicwfn = new VBWaveFn<T>(c, alpha, R1, R2);
                break;
            default:
                throw std::runtime_error("Invalid atomic wave function");
                break;
        }
    }

    ~H2Mol() {
        delete jastrow;
        delete atomicwfn;
    }

    // Notice this calculate \psi^* \psi
    T density(const PCoord<T>& r1, const PCoord<T>& r2) {
        return std::pow(jastrow->value(r1, r2)* \
               atomicwfn->value(r1)*atomicwfn->value(r2), 2);
    }

    // Calculate the energy with current density
    T energy(const PCoord<T>& r1, const PCoord<T>& r2) {
        T ret = 0.0;
        PCoord<T> dJdr1, dJdr2;
        T d2Jdr1, d2Jdr2;
        auto jval = jastrow->value(r1, r2);
        std::tie(d2Jdr1, d2Jdr2) = jastrow->laplace(r1, r2);
        std::tie(dJdr1, dJdr2) = jastrow->grad(r1, r2);
        ret += (d2Jdr1+d2Jdr2)/jval;
        auto val1 = atomicwfn->value(r1);
        auto val2 = atomicwfn->value(r2);
        ret += atomicwfn->laplace(r1)/val1;
        ret += atomicwfn->laplace(r2)/val2;

        ret += 2*dJdr1.dot(atomicwfn->grad(r1))/(jval*val1);
        ret += 2*dJdr2.dot(atomicwfn->grad(r2))/(jval*val2);

        ret = -0.5*ret; // kintetic energy

        ret += -1.0/(r1-R1).norm();
        ret += -1.0/(r1-R2).norm();
        ret += -1.0/(r2-R1).norm();
        ret += -1.0/(r2-R2).norm();
        ret += 1.0/(r1-r2).norm();
        ret += 1.0/(R1-R2).norm();

        return ret;
    }

private:
    JastrowWfn<T>* jastrow = nullptr;
    WaveFn<T>* atomicwfn = nullptr;
    PCoord<T> R1, R2;
};

template<typename T> //, typename wfn>
class H2MolQMC {
public:
    H2MolQMC(T factor, T c, T alpha, const PCoord<T>& R1, const PCoord<T>& R2, T dr): dr(dr){
        mol = new H2Mol<T, JastrowType::SIMPLE_JASTROW, AtomicWfnType::MO>(factor, c, alpha, R1, R2);
    }

    ~H2MolQMC() {
        delete mol;
    }

    std::pair<T, T> sample(int maxstep=10000) {

        // Thsi method return the number between [-1, 1]
        PCoord<T> r1 = PCoord<T>::Random();
        PCoord<T> r2 = PCoord<T>::Random();

        PCoord<T> r1_new, r2_new;
        T energy = mol->energy(r1, r2);
        T density = mol->density(r1, r2);
        std::uniform_real_distribution<T> rnum(0, 1);

        //energy_old = mol->energy();
        T energy_tot = 0.0;
        T energy_sq_tot = 0.0;
        int accept = 0;
        for(int i=0; i<maxstep; i++) {

            dr = std::max(dr, 0.1);
            dr = std::min(dr, 10.0);

            r1_new = r1 + dr*PCoord<T>::Random();
            r2_new = r2 + dr*PCoord<T>::Random();

            auto dtmp = mol->density(r1_new, r2_new);
            auto ratio = dtmp/density;

            if(ratio > 1 || ratio > rnum(rgen)) {
                r1 = r1_new;
                r2 = r2_new;
                density = dtmp;
                energy = mol->energy(r1, r2);
                accept += 1;
            }

            if(static_cast<T>(accept)/(i+1) > 0.5) dr*=scale;
            else dr/=scale;

            energy_tot += energy;
            energy_sq_tot += energy*energy;
        }
        auto energy_avg = energy_tot/maxstep;
        auto energy_std = std::sqrt(energy_sq_tot/maxstep - energy_avg*energy_avg);
        fmt::print("Accept ratio: {}\n", (double) accept/maxstep);
        return {energy_avg, energy_std};
    }

private:
    H2Mol<T, JastrowType::SIMPLE_JASTROW, AtomicWfnType::MO>* mol;
    PCoord<T> R1, R2;
    T dr;
    std::random_device rd;
    std::mt19937 rgen{rd()};
    const T scale = 1.01;
    // T c, alpha;
};


#pragma once
#include <cstddef>
#include <random>
#include <functional>
#include <vector>
#include <limits>
#include <iostream>
#include <boost/multiprecision/number.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/math/special_functions/prime.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/multiprecision/miller_rabin.hpp>

namespace mp = boost::multiprecision;
namespace math = boost::math;
namespace rnd = boost::random;

namespace rsa {

    struct random {

        boost::mt19937_64 generator;

        void init_generator(const uint64_t seed) {
            generator = boost::mt19937_64(seed);
        }

        static auto& default_random() {
            static random rand;
            return rand;
        }

    };

    template<unsigned int N>
    struct num_utils {

        using number = mp::number<mp::backends::cpp_int_backend<N, N, mp::unsigned_magnitude, mp::unchecked, void>>;
        using signed_number = mp::number<mp::backends::cpp_int_backend<2 * N, 2 * N, mp::signed_magnitude, mp::unchecked, void>>;

        static auto get_int_random(const number min = std::numeric_limits<number>::min(),
                                   const number max = std::numeric_limits<number>::max()) {
            rnd::uniform_int_distribution<number> dist(min, max);
            return std::bind(dist, std::ref(random::default_random().generator));

        }

        static auto generate_random_prime(const unsigned int k = 100) {
            const auto rand = get_int_random();
            while(true) {
                const auto n = rand();
                for (unsigned int i = 0; i < math::max_prime; ++i)
                    if (n % math::prime(i) == 0)
                        goto next;
                if (mp::miller_rabin_test(n, k))
                    return n;
                next:
                continue;
            }
        }

        static auto bezout_identity(const signed_number& a, const signed_number& b) {
            if (a == 0)
                return std::make_pair(signed_number(0), signed_number(1));
            const auto buff = bezout_identity(b % a, a);
            return std::make_pair(buff.second - (b / a) * buff.first, buff.first);
        }

        template<typename It>
        static auto bytes_to_number(It begin, const It& end) {
            number result;
            mp::import_bits(result, begin, end, 8);
            return result;
        }

        static auto number_to_bytes(const number& number) {
            std::array<char, N / 8> result;
            result.fill(0);
            mp::export_bits(number, result.rbegin(), 8, false);
            return result;
        }

    };

}// namespace rsa


//
//  Neural.cpp
//
//
//  Created by Zac Schulwolf on 12/27/16.
//
// Based on http://neuralnetworksanddeeplearning.com/chap1.html
// Compile by g++ -I "$(brew --prefix eigen)/include/eigen3" Neural.cpp -o Neural

#include <iostream>
#include <Eigen/Dense>
#include <ctime> //for random stuff
#include <list> //for list of Matrices for biases and weights
using namespace Eigen;
using namespace std;

void Biases(list<MatrixXd> &biases, VectorXi sizes){ //NEEDS TO BE OPTIMIZED inline?
    int r;
    for(int i=0; i <sizes.rows()-1;i++){
        r=sizes[i+1];
        biases.push_back(MatrixXd::Random(r,1));
    }

    cout << endl << "Biases:" << endl; //print biases
    for (list<MatrixXd>::iterator itb = biases.begin(); itb != biases.end(); itb++) //print biases
        cout << *itb << endl <<endl; //print biases
}

void Weights(list<MatrixXd> &weights, VectorXi sizes){ //NEEDS TO BE OPTIMIZED inline?
    int x,y;
    for(int i=0; i <sizes.rows()-1;i++){

        y=sizes[i+1];
        x=sizes[i];
        weights.push_back(MatrixXd::Random(y,x));
    }

    cout << endl << "Weights:" << endl; //print weights
    for (list<MatrixXd>::iterator itw = weights.begin(); itw != weights.end(); itw++) //print weights
        cout << *itw << endl <<endl; //print weights
}

class Network{
    int num_layers;
    VectorXi sizes;
    list<MatrixXd> biases;
    list<MatrixXd> weights;

    VectorXd sigmoid(ArrayXd z){
        return (1/(1+exp(-z))).vector();
    }

public:
    Network(VectorXi Sizes){ //constructor
        struct timespec tm; //time for random seed
        clock_gettime(CLOCK_REALTIME,&tm); //time for random seed
        srand(tm.tv_nsec); //This makes the Eigen random different each time        NEEDS TO BE OPTIMIZED

        num_layers = Sizes.rows();
        sizes = Sizes;
        Biases(biases,sizes);
        Weights(weights,sizes);

    }

    VectorXd feedforward(VectorXd a){ //probably needs to be private
		MatrixXd b;
        MatrixXd w;
        list<MatrixXd>::iterator itb = biases.begin();
        list<MatrixXd>::iterator itw = weights.begin();
        cout << "sizes.rows() = " << sizes.rows()<<endl;

        /*for (list<MatrixXd>::iterator itb = biases.begin(); itb != biases.end(); itb++){ //print biases
            b = *itb;
        }*/

        for (int i=0; i<sizes.rows()-1;i++,itb++,itw++){
            ///cout << "asdjkasldjsakldjaklsdjlkas\n";
            //b.resize((*itb).rows(),(*itb).cols());
            //w.resize((*itw).rows(),(*itw).cols());

            cout << (*itb).rows() << endl << (*itb).cols() << endl << endl;
            cout << (*itw).rows() << endl << (*itw).cols() << endl << endl;
            b= *itb;
            w= *itw;
            cout << "*itb:\n"<< *itb << endl <<endl; //print biases
            cout << "*itw:\n"<< *itw << endl <<endl; //print weights

            //cout << "itb at " << i << " is:\n" << *itb << endl;
            //cout << "itw at " << i << " is:\n" << *itw << endl;
            cout << "a:\n" << a << endl << "w:\n" << w << endl <<"a.matrix()\n" << a.matrix() << endl << "b.array():\n" << b.array() << endl;

            a = sigmoid((a * w).array() + b.array()); //w.row(0) is there because weights is always a vector but its easier to store it as a matrix
            cout << endl << endl << a << endl << endl;

        }
        return a;
    }

};

int main(){
    VectorXi s(3);
    s << 1,2,3;
    Network a(s);
    ArrayXd arr(2);
    arr << 1,2;
    a.feedforward(arr);

    return 0;
}