#include <ctime>
#include <string>
#include <algorithm>
#include <iostream>
#include <random>
#include <math.h>
#include <vtkNew.h>
#include <vtkPolyDataReader.h>
#include <vtkPolyDataWriter.h>
#include <vtkCellLocator.h>
#include <vtkDelaunay3D.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkPolyData.h>
#include <vtkDoubleArray.h>
#include <vtkPoints.h>
#include <vtkIdFilter.h>
#include <vtkPointData.h>
#include <vtkCellArray.h>
#include <vtkIdList.h>
#include <vtkLinearSubdivisionFilter.h>
#include <vtkGenericCell.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkDataObject.h>
#include <Eigen/Dense>

extern "C" void shexpandlsq_wrapper_(double* cilm, double* d, double* lat,
	double* lon, int* N, int* lmax, double* chi2);

extern "C" void makegridpoints_wrapper_(double* cilm, int* lmax, int* n,
	double* lat, double* lon, double* points, int* dealloc);

extern "C" void glqgridcoord_wrapper_(double* latglq, double* longlq,
	int* lmax, int* nlat, int* nlong);

extern "C" void shglq_wrapper_(int* lmax, double* zero, double* w,
	double* plx);

extern "C" void shexpandglq_wrapper_(double* cilm, int* lmax, double* gridglq,
	double* w, double* plx);

extern "C" void makegridglq_wrapper_(double* gridglq, double* cilm, int* lmax,
	double* plx);

extern "C" void shexpanddh_wrapper_(double* grid, int* n, double* cilm,
	int* lmax);

extern "C" void makegriddh_wrapper_(double* grid, int* n, double* cilm,
	int* lmax);

extern "C" void shpowerspectrum_wrapper_(double* cilm, int* lmax,
	double* pspectrum);

int main(){

    // Read the polydata
    vtkNew<vtkPolyDataReader> reader;
    reader->SetFileName("T7.vtk");
    reader->Update();
    auto pointCloud = reader->GetOutput();
    int N = pointCloud->GetNumberOfPoints();

    // Copy the point coordinates into a matrix
    Eigen::Map<Eigen::Matrix3Xd> ptsMat((double_t*)pointCloud->GetPoints()
	    ->GetData()->GetVoidPointer(0),3,N);

    // Calculate average radius
    double_t R = ptsMat.colwise().norm().mean();

    // Calculate points on a sphere of average radius and displacements
    vtkNew<vtkPoints> spherePts;
    vtkNew<vtkDoubleArray> displacements;
    displacements->SetName("Displacements");
    displacements->SetNumberOfComponents(1);
    for(auto i = 0; i < N; ++i){
	Eigen::Vector3d p, q;
	pointCloud->GetPoint(i, &p(0));
	q =  R*p.normalized();
	spherePts->InsertNextPoint( &q(0) );
	displacements->InsertNextTuple1(p.norm() - R);
    }

    // Create a mesh on the sphere
    vtkNew<vtkPolyData> sphere;
    sphere->SetPoints( spherePts );
    sphere->GetPointData()->AddArray( displacements );
    vtkNew<vtkIdFilter> idf;
    idf->SetIdsArrayName( "OrigIds" );
    idf->PointIdsOn();
    idf->SetInputData( sphere );
    vtkNew<vtkDelaunay3D> d3D;
    d3D->SetInputConnection( idf->GetOutputPort() );
    vtkNew<vtkDataSetSurfaceFilter> dssf;
    dssf->SetInputConnection(d3D->GetOutputPort());
    dssf->Update();
    auto final = dssf->GetOutput()->GetPolys();
    auto idArray = dssf->GetOutput()->GetPointData()->GetArray( "OrigIds" );
    final->InitTraversal();
    vtkNew<vtkCellArray> triangles;
    vtkNew<vtkIdList> ids;
    while( final->GetNextCell( ids ) ){
	triangles->InsertNextCell(3);
	for( auto i = 0; i < 3; ++i )
	    triangles->InsertCellPoint( (vtkIdType)idArray->GetTuple1(
			ids->GetId(i) ) );
    }
    sphere->SetPolys( triangles );
    vtkNew<vtkPolyDataWriter> writer;
    writer->SetFileName("AverageSphere.vtk");
    writer->SetInputData(sphere);
    writer->Write();

    // Create a locator to identify the cells that contain the Gauss quadrature
    // points
    vtkNew<vtkCellLocator> cellLoc;
    cellLoc->SetDataSet( sphere );
    cellLoc->BuildLocator();

    //**********************************************************************//
    // Create the Gauss-Lengendre grid
    int nlat, nlong, lmax = 7;
    Eigen::VectorXd latglq(lmax + 1);
    Eigen::VectorXd longlq(2*lmax + 1);
    glqgridcoord_wrapper_(latglq.data(), longlq.data(), &lmax, &nlat, &nlong);

    // Find the cell to which each point of the grid belongs and set its value
    // by linear interpolation
    auto coords = [&R]( const double_t T, const double_t P ){
	Eigen::Vector3d X;
	auto SinT = std::sin(T*M_PI/180.0);
	X << R*SinT*std::cos(P*M_PI/180.0), R*SinT*std::sin(P*M_PI/180.0),
	  R*std::cos( T*M_PI/180.0);
	return X;
    };
    /*
       vtkNew<vtkPoints> glqPoints;
       vtkNew<vtkCellArray> glqVerts;
       vtkNew<vtkDoubleArray> glqDisp;
       glqDisp->SetName("GLQDisp");
       glqDisp->SetNumberOfComponents(3);
       */
    Eigen::VectorXd gridglq((lmax + 1)*(2*lmax + 1));
    Eigen::VectorXd plx( (lmax + 1)*(lmax + 1)*(lmax + 2)/2 );
    Eigen::VectorXd w( lmax + 1 ), zero( lmax + 1 );
    Eigen::VectorXd cilm1( 2*(lmax + 1)*(lmax + 1) );
    Eigen::VectorXd pspectrum1( lmax + 1 );
    shglq_wrapper_(&lmax, zero.data(), w.data(), plx.data() );

    clock_t t = clock();
    for(auto z = 0; z < 1000; ++z){
	for( auto j = 0; j < 2*lmax + 1; ++j ){
	    for( auto i = 0; i < lmax + 1; ++i ){
		// Get the spherical coordinate
		auto X = coords( 90.0 - latglq(i), longlq(j) );
		// Locate cell
		Eigen::Vector3d closestPoint, pcoords, weights;
		vtkIdType cellId;
		int subId;
		double_t dist2;
		vtkNew<vtkGenericCell> genCell;
		cellLoc->FindClosestPoint( &X(0), &closestPoint(0), genCell,
			cellId, subId, dist2);
		// Get the parametric coordinates and weights
		genCell->EvaluatePosition( &X(0), &closestPoint(0), subId,
			&pcoords(0), dist2, &weights(0) );
		vtkNew<vtkIdList> pointIds;
		sphere->GetCellPoints( cellId, pointIds );
		// Interpolate to find the displacement at quadrature point
		gridglq(i + (lmax + 1)*j) =
		    weights[0]*displacements->GetTuple1(pointIds->GetId(0)) +
		    weights[1]*displacements->GetTuple1(pointIds->GetId(1)) +
		    weights[2]*displacements->GetTuple1(pointIds->GetId(2));
		//glqPoints->InsertNextPoint( &X(0) );
		//Eigen::Vector3d disp;
		//disp = gridglq(i + (lmax + 1)*j)*X.normalized();
		//glqDisp->InsertNextTuple( &disp(0) );
	    }
	}
	/*
	   vtkNew<vtkPolyData> glqBody;
	   glqBody->SetPoints( glqPoints );
	   glqBody->GetPointData()->SetVectors( glqDisp );
	   writer->SetInputData( glqBody );
	   writer->SetFileName( "GLQBody.vtk" );
	   writer->Write();
	   */

	// Expand using Gauss-Legendre Quadrature and get the power spectrum
	shexpandglq_wrapper_( cilm1.data(), &lmax, gridglq.data(), w.data(),
		plx.data());
    }
    t = clock() - t;
    std::cout<< "Time for 1000 steps GLQ = " << ((float)t)/CLOCKS_PER_SEC
       	<< std::endl;

    shpowerspectrum_wrapper_( cilm1.data(), &lmax, pspectrum1.data() );
    // Cross-check
    Eigen::VectorXd gridglq_out( (lmax + 1)*(2*lmax + 1) );
    makegridglq_wrapper_(gridglq_out.data(), cilm1.data(), &lmax, plx.data());
    std::cout<< "Error norm of GLQ = " << (gridglq - gridglq_out).norm()
	<< std::endl;
    //**********************************************************************//
    // Expand using Driscol-Healy and get the power spectrum
    int ndh = 2*(lmax + 1);
    /*
       vtkNew<vtkPoints> DHPoints;
       vtkNew<vtkCellArray> DHVerts;
       vtkNew<vtkDoubleArray> DHDisp;
       DHDisp->SetName("DHGridDisp");
       DHDisp->SetNumberOfComponents(3);
       */
    Eigen::VectorXd gridDH( ndh*ndh );
    Eigen::VectorXd cilm2( ndh*ndh/2 ), pspectrum2(lmax + 1);

    t = clock();
    for(auto z = 0; z < 1000; ++z){
	for( auto i = 0; i < ndh; ++i ){
	    auto latDH = i*(180.0/ndh);
	    for( auto j = 0; j < ndh; ++j ){
		// Get the spherical coordinate
		auto lonDH = j*360.0/ndh;
		auto X = coords( latDH, lonDH );
		// Locate cell
		Eigen::Vector3d closestPoint, pcoords, weights, dummy;
		vtkIdType cellId;
		int subId;
		double_t dist2;
		vtkNew<vtkGenericCell> genCell;
		cellLoc->FindClosestPoint( &X(0), &closestPoint(0), genCell,
			cellId, subId, dist2);
		// Get the parametric coordinates and weights
		auto out = genCell->EvaluatePosition( &closestPoint(0), &dummy(0),
			subId, &pcoords(0), dist2, &weights(0) );
		vtkNew<vtkIdList> pointIds;
		sphere->GetCellPoints( cellId, pointIds );
		// Interpolate to find the displacement at quadrature point
		gridDH(i + ndh*j) =
		    weights[0]*displacements->GetTuple1(pointIds->GetId(0)) +
		    weights[1]*displacements->GetTuple1(pointIds->GetId(1)) +
		    weights[2]*displacements->GetTuple1(pointIds->GetId(2));
		//DHPoints->InsertNextPoint( &X(0) );
		//Eigen::Vector3d disp;
		//disp = gridDH(j + ndh*i)*X.normalized();
		//DHDisp->InsertNextTuple( &disp(0) );
	    }
	}
	/*
	   vtkNew<vtkPolyData> DHBody;
	   DHBody->SetPoints( DHPoints );
	   DHBody->GetPointData()->SetVectors( DHDisp );
	   writer->SetInputData( DHBody );
	   writer->SetFileName( "DHBody.vtk" );
	   writer->Write();
	   */

	// Finally expand using DH scheme
	shexpanddh_wrapper_(gridDH.data(), &ndh, cilm2.data(), &lmax);
    }
    t = clock() - t;
    std::cout<< "Time for 1000 steps DH = " << ((float)t)/CLOCKS_PER_SEC
       	<< std::endl;

    shpowerspectrum_wrapper_(cilm2.data(), &lmax, pspectrum2.data());
    // Cross-check
    Eigen::VectorXd gridDH_out( ndh*ndh );
    makegriddh_wrapper_(gridDH_out.data(), &ndh, cilm2.data(), &lmax);
    std::cout<< "Error norm of DH = " << (gridDH - gridDH_out).norm()
	<< std::endl;
    //**********************************************************************//
    //Now we will use LSQ
    Eigen::VectorXd latLSQ(N), lonLSQ(N), gridLSQ(N);
    Eigen::VectorXd cilm3(2*(lmax+1)*(lmax+1)), pspectrum3(lmax + 1);
    double_t chi2;
    t = clock();
    for(auto z = 0; z < 1000; ++z){
	for( auto i = 0; i < N; ++i ){
	    double_t X[3], x, y, z;
	    spherePts->GetPoint(i, X);
	    x = X[0]; y = X[1]; z = X[2];
	    // Calculate latitude and longitude for the points
	    latLSQ(i) = std::atan2(z, sqrt(x*x + y*y)) * 180.0/M_PI;
	    lonLSQ(i) = std::atan2(y, x) * 180.0/M_PI;
	    gridLSQ(i) = displacements->GetTuple1(i);
	}
	shexpandlsq_wrapper_(cilm3.data(), gridLSQ.data(), latLSQ.data(),
		lonLSQ.data(), &N, &lmax, &chi2);
    }
    t = clock() - t;
    std::cout<< "Time for 1000 steps LSQ = " << ((float)t)/CLOCKS_PER_SEC
       	<< std::endl;

    shpowerspectrum_wrapper_(cilm3.data(), &lmax, pspectrum3.data());
    // Cross-check
    Eigen::VectorXd gridLSQ_out(N);
    int dealloc = 1;
    makegridpoints_wrapper_(cilm3.data(), &lmax, &N, latLSQ.data(),
	    lonLSQ.data(), gridLSQ_out.data(), &dealloc);
    std::cout<< "Error norm of LSQ = " << (gridLSQ_out - gridLSQ).norm()
	<< std::endl;

    //**********************************************************************//
    // Now let's print the power-spectrums
    std::cout<< " Power spectrum from GLQ = " << std::endl
	<< pspectrum1.transpose() << std::endl;
    std::cout<< " Power spectrum from DH = " << std::endl
	<< pspectrum2.transpose() << std::endl;
    std::cout<< " Power spectrum from LSQ = " << std::endl
	<< pspectrum3.transpose() << std::endl;

    //**********************************************************************//
    // Now we will subdivide the sphere polydata and calculate the
    // displacements at more points using the cilm calculated above. We will
    // print all three vtk surfaces and see which is the best.
    vtkNew<vtkLinearSubdivisionFilter> lsdf;
    lsdf->SetInputData( sphere );
    lsdf->SetNumberOfSubdivisions(2);
    lsdf->Update();
    auto sphere2 = lsdf->GetOutput();
    int N2 = sphere2->GetNumberOfPoints();
    Eigen::VectorXd lat2(N2), lon2(N2), grid2(N2);
    for( auto i = 0; i < N2; ++i ){
	double_t X[3], x, y, z;
	sphere2->GetPoint(i, X);
	x = X[0]; y = X[1]; z = X[2];
	// Calculate latitude and longitude for the points
	lat2(i) = std::atan2(z, sqrt(x*x + y*y)) * 180.0/M_PI;
	lon2(i) = std::atan2(y, x) * 180.0/M_PI;
    }
    auto writeResult = [&](Eigen::VectorXd &C, std::string file){
	// First get the deformation vlaues
	makegridpoints_wrapper_(C.data(), &lmax, &N2, lat2.data(),
		lon2.data(), grid2.data(), &dealloc);
	// Add the deformations to average radius along each point's direction
	vtkNew<vtkPoints> newPoints;
	for( auto i = 0; i < N2; ++i ){
	    Eigen::Vector3d X, Xpd;
	    sphere2->GetPoint( i, &X(0) );
	    Xpd = (R + grid2(i))*X.normalized();
	    newPoints->InsertNextPoint( &Xpd(0) );
	}
	vtkNew<vtkPolyData> temp;
	temp->SetPoints( newPoints );
	vtkNew<vtkVertexGlyphFilter> vgf;
	vgf->SetInputData( temp );
	writer->SetInputConnection( vgf->GetOutputPort() );
	writer->SetFileName( file.c_str() );
	writer->Update();
	writer->Write();
    };

    writeResult( cilm1, "LSQ_Result.vtk" );
    writeResult( cilm2, "GLQ_Result.vtk" );
    writeResult( cilm3, "DH_Result.vtk" );

    return 0;
}


#define BOOST_TEST_MODULE XOR Test
#include <boost/test/included/unit_test.hpp>

#include "../network.h"
#include "../gradient.h"
#include "../evolution.h"
#include <memory>

// Setup the test parameters
struct XORdata {
  std::vector<boost::numeric::ublas::vector<double> > input;
  std::vector<boost::numeric::ublas::vector<double> > expected;

  XORdata() {
    boost::numeric::ublas::vector<double> in1(2);
    in1[0] = 0.0;
    in1[1] = 0.0;
    input.push_back(in1);

    boost::numeric::ublas::vector<double> expv1(1);
    expv1[0] = 0.0;
    expected.push_back(expv1);

    boost::numeric::ublas::vector<double> in2(2);
    in2[0] = 0.0;
    in2[1] = 1.0;
    input.push_back(in2);

    boost::numeric::ublas::vector<double> expv2(1);
    expv2[0] = 1.0;
    expected.push_back(expv2);

    boost::numeric::ublas::vector<double> in3(2);
    in3[0] = 1.0;
    in3[1] = 0.0;
    input.push_back(in3);

    boost::numeric::ublas::vector<double> expv3(1);
    expv3[0] = 1.0;
    expected.push_back(expv3);

    boost::numeric::ublas::vector<double> in4(2);
    in4[0] = 1.0;
    in4[1] = 1.0;
    input.push_back(in4);

    boost::numeric::ublas::vector<double> expv4(1);
    expv4[0] = 0.0;
    expected.push_back(expv4);
  }
};

BOOST_AUTO_TEST_CASE(XOR_test_feed_forward)
{
  /*
  * We test if the feed forward implementation is working by using known
  * weights for an XOR network.
  */

  XORdata test;

  std::vector<int> size;
  size.push_back(2);
  NeuralNetwork network(size, 2, 1, new SigmoidFunction());
  network.initializeRandomWeights();

  auto weights = network.getWeights();

  // Weights which produce an XOR network with the sigmoid activation function
  weights[0](0,0) = -10.0;
  weights[0](0,1) = 20.0;
  weights[0](0,2) = 20.0;

  weights[0](1,0) = 30.0;
  weights[0](1,1) = -20.0;
  weights[0](1,2) = -20.0;

  weights[1](0,0) = -30.0;
  weights[1](0,1) = 20.0;
  weights[1](0,2) = 20.0;

  network.setWeights(weights);

  // We test the inputs against the known outputs
  for (int i = 0; i < test.input.size(); ++i) {
    BOOST_CHECK_SMALL(network.feedForwardVector(test.input[i])[0] - test.expected[i][0], 0.01);
  }
}

BOOST_AUTO_TEST_CASE(XOR_test_train_SGD)
{
  /*
  * We test the back propogation implementation by using stochastic gradient descent
  * to learn the weights for an XOR network.
  *
  * This test may fail if poor initial conditions are randomly selected.
  */

  XORdata test;

  std::vector<int> size;
  size.push_back(2);

  std::unique_ptr<NeuralNetwork> network(new NeuralNetwork(size, 2, 1, new SigmoidFunction()));
  network->initializeRandomWeights();

  StochasticGradientDescent SGD(network.get(), 0.1);
  // We train the network using the above pairs of inputs and expected values.
  std::cout << "Before training J=" << network->cost(test.input, test.expected) << std::endl;
  // We use SGD to train the weights.
  SGD.train(test.input, test.expected, 1e-3, 2);
  std::cout << "After training J=" << network->cost(test.input, test.expected) << std::endl;

  // We test the newly found weights
  for (int i = 0; i < test.input.size(); ++i) {
    std::cout << "Output: " << network->feedForwardVector(test.input[i])[0] << " Expected: " << test.expected[i][0] << std::endl;
    BOOST_CHECK_SMALL(network->feedForwardVector(test.input[i])[0] - test.expected[i][0], 0.01);
  }
}

BOOST_AUTO_TEST_CASE(XOR_test_train_evo)
{
  /*
  * We use the FEP implementation to learn weights for an XOR network.
  */

  XORdata test;

  std::vector<int> size;
  size.push_back(2);

  std::unique_ptr<NeuralNetwork> network(new NeuralNetwork(size, 2, 1, new SigmoidFunction()));
  network->initializeRandomWeights();

  EvolutionaryProgramming FEP(network.get(), -20.0, 20.0, 100);
  // We train the network using the above pairs of inputs and expected values.
  std::cout << "Before training J=" << network->cost(test.input, test.expected) << std::endl;
  std::cout << "Maximum number of fitness evaluations: " << 100000 << std::endl;

  // We use FEP to train the weights
  FEP.train(test.input, test.expected, 100000);
  std::cout << "After training J=" << network->cost(test.input, test.expected) << std::endl;

  // We test the newly found weights
  for (int i = 0; i < test.input.size(); ++i) {
    std::cout << "Output: " << network->feedForwardVector(test.input[i])[0] << " Expected: " << test.expected[i][0] << std::endl;
    BOOST_CHECK_SMALL(network->feedForwardVector(test.input[i])[0] - test.expected[i][0], 0.01);
  }
}


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 *  @file   testGaussianConditional.cpp
 *  @brief  Unit tests for Conditional gaussian
 *  @author Christian Potthast
 **/

#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/TestableAssertions.h>

#include <gtsam/base/Matrix.h>
#include <gtsam/base/VerticalBlockMatrix.h>
#include <gtsam/inference/Key.h>
#include <gtsam/linear/JacobianFactor.h>
#include <gtsam/linear/GaussianConditional.h>
#include <gtsam/linear/GaussianBayesNet.h>

#include <boost/assign/std/list.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/make_shared.hpp>
#include <boost/assign/list_of.hpp>

#include <iostream>
#include <sstream>
#include <vector>

using namespace gtsam;
using namespace std;
using namespace boost::assign;

static const double tol = 1e-5;

static Matrix R = (Matrix(2, 2) <<
    -12.1244,  -5.1962,
          0.,   4.6904).finished();

/* ************************************************************************* */
TEST(GaussianConditional, constructor)
{
  Matrix S1 = (Matrix(2, 2) <<
      -5.2786,  -8.6603,
      5.0254,   5.5432).finished();
  Matrix S2 = (Matrix(2, 2) <<
      -10.5573,  -5.9385,
      5.5737,   3.0153).finished();
  Matrix S3 = (Matrix(2, 2) <<
      -11.3820,  -7.2581,
      -3.0153,  -3.5635).finished();

  Vector d = Vector2(1.0, 2.0);
  SharedDiagonal s = noiseModel::Diagonal::Sigmas(Vector2(3.0, 4.0));

  vector<pair<Key, Matrix> > terms = pair_list_of
      (1, R)
      (3, S1)
      (5, S2)
      (7, S3);

  GaussianConditional actual(terms, 1, d, s);

  GaussianConditional::const_iterator it = actual.beginFrontals();
  EXPECT(assert_equal(Key(1), *it));
  EXPECT(assert_equal(R, actual.get_R()));
  ++ it;
  EXPECT(it == actual.endFrontals());

  it = actual.beginParents();
  EXPECT(assert_equal(Key(3), *it));
  EXPECT(assert_equal(S1, actual.get_S(it)));

  ++ it;
  EXPECT(assert_equal(Key(5), *it));
  EXPECT(assert_equal(S2, actual.get_S(it)));

  ++ it;
  EXPECT(assert_equal(Key(7), *it));
  EXPECT(assert_equal(S3, actual.get_S(it)));

  ++it;
  EXPECT(it == actual.endParents());

  EXPECT(assert_equal(d, actual.get_d()));
  EXPECT(assert_equal(*s, *actual.get_model()));

  // test copy constructor
  GaussianConditional copied(actual);
  EXPECT(assert_equal(d, copied.get_d()));
  EXPECT(assert_equal(*s, *copied.get_model()));
  EXPECT(assert_equal(R, copied.get_R()));
}

/* ************************************************************************* */
TEST( GaussianConditional, equals )
{
  // create a conditional gaussian node
  Matrix A1(2,2);
  A1(0,0) = 1 ; A1(1,0) = 2;
  A1(0,1) = 3 ; A1(1,1) = 4;

  Matrix A2(2,2);
  A2(0,0) = 6 ; A2(1,0) = 0.2;
  A2(0,1) = 8 ; A2(1,1) = 0.4;

  Matrix R(2,2);
  R(0,0) = 0.1 ; R(1,0) = 0.3;
  R(0,1) = 0.0 ; R(1,1) = 0.34;

  SharedDiagonal model = noiseModel::Diagonal::Sigmas(Vector2(1.0, 0.34));

  Vector d = Vector2(0.2, 0.5);

  GaussianConditional
    expected(1, d, R, 2, A1, 10, A2, model),
    actual(1, d, R, 2, A1, 10, A2, model);

  EXPECT( expected.equals(actual) );
}

/* ************************************************************************* */
TEST( GaussianConditional, solve )
{
  //expected solution
  Vector expectedX(2);
  expectedX(0) = 20-3-11 ; expectedX(1) = 40-7-15;

  // create a conditional Gaussian node
  Matrix R = (Matrix(2, 2) <<  1., 0.,
                            0., 1.).finished();

  Matrix A1 = (Matrix(2, 2) << 1., 2.,
                            3., 4.).finished();

  Matrix A2 = (Matrix(2, 2) << 5., 6.,
                            7., 8.).finished();

  Vector d(2); d << 20.0, 40.0;

  GaussianConditional cg(1, d, R, 2, A1, 10, A2);

  Vector sx1(2); sx1 << 1.0, 1.0;
  Vector sl1(2); sl1 << 1.0, 1.0;

  VectorValues expected = map_list_of
    (1, expectedX)
    (2, sx1)
    (10, sl1);

  VectorValues solution = map_list_of
    (2, sx1) // parents
    (10, sl1);
  solution.insert(cg.solve(solution));

  EXPECT(assert_equal(expected, solution, tol));
}

/* ************************************************************************* */
TEST( GaussianConditional, solve_simple )
{
  // 2 variables, frontal has dim=4
  VerticalBlockMatrix blockMatrix(list_of(4)(2)(1), 4);
  blockMatrix.matrix() <<
      1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.1,
      0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.2,
      0.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.3,
      0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 0.4;

  // solve system as a non-multifrontal version first
  GaussianConditional cg(list_of(1)(2), 1, blockMatrix);

  // partial solution
  Vector sx1 = Vector2(9.0, 10.0);

  // elimination order: 1, 2
  VectorValues actual = map_list_of
    (2, sx1); // parent

  VectorValues expected = map_list_of<Key, Vector>
    (2, sx1)
    (1, (Vector(4) << -3.1,-3.4,-11.9,-13.2).finished());

  // verify indices/size
  EXPECT_LONGS_EQUAL(2, (long)cg.size());
  EXPECT_LONGS_EQUAL(4, (long)cg.rows());

  // solve and verify
  actual.insert(cg.solve(actual));
  EXPECT(assert_equal(expected, actual, tol));
}

/* ************************************************************************* */
TEST( GaussianConditional, solve_multifrontal )
{
  // create full system, 3 variables, 2 frontals, all 2 dim
  VerticalBlockMatrix blockMatrix(list_of(2)(2)(2)(1), 4);
  blockMatrix.matrix() <<
      1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.1,
      0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.2,
      0.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.3,
      0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 0.4;

  // 3 variables, all dim=2
  GaussianConditional cg(list_of(1)(2)(10), 2, blockMatrix);

  EXPECT(assert_equal(Vector(blockMatrix.full().rightCols(1)), cg.get_d()));

  // partial solution
  Vector sl1 = Vector2(9.0, 10.0);

  // elimination order; _x_, _x1_, _l1_
  VectorValues actual = map_list_of
    (10, sl1); // parent

  VectorValues expected = map_list_of<Key, Vector>
    (1, Vector2(-3.1,-3.4))
    (2, Vector2(-11.9,-13.2))
    (10, sl1);

  // verify indices/size
  EXPECT_LONGS_EQUAL(3, (long)cg.size());
  EXPECT_LONGS_EQUAL(4, (long)cg.rows());

  // solve and verify
  actual.insert(cg.solve(actual));
  EXPECT(assert_equal(expected, actual, tol));

}

/* ************************************************************************* */
TEST( GaussianConditional, solveTranspose ) {
  /** create small Chordal Bayes Net x <- y
   * x y d
   * 1 1 9
   *   1 5
   */
  Matrix R11 = (Matrix(1, 1) << 1.0).finished(), S12 = (Matrix(1, 1) << 1.0).finished();
  Matrix R22 = (Matrix(1, 1) << 1.0).finished();
  Vector d1(1), d2(1);
  d1(0) = 9;
  d2(0) = 5;

  // define nodes and specify in reverse topological sort (i.e. parents last)
  GaussianBayesNet cbn = list_of
    (GaussianConditional(1, d1, R11, 2, S12))
    (GaussianConditional(1, d2, R22));

  // x=R'*y, y=inv(R')*x
  // 2 = 1    2
  // 5   1 1  3

  VectorValues
    x = map_list_of<Key, Vector>
      (1, (Vector(1) << 2.).finished())
      (2, (Vector(1) << 5.).finished()),
    y = map_list_of<Key, Vector>
      (1, (Vector(1) << 2.).finished())
      (2, (Vector(1) << 3.).finished());

  // test functional version
  VectorValues actual = cbn.backSubstituteTranspose(x);
  CHECK(assert_equal(y, actual));
}

/* ************************************************************************* */
TEST( GaussianConditional, information ) {

  // Create R matrix
  Matrix R(4,4); R <<
      1, 2, 3, 4,
      0, 5, 6, 7,
      0, 0, 8, 9,
      0, 0, 0, 10;

  // Create conditional
  GaussianConditional conditional(0, Vector::Zero(4), R);

  // Expected information matrix (using permuted R)
  Matrix IExpected = R.transpose() * R;

  // Actual information matrix (conditional should permute R)
  Matrix IActual = conditional.information();
  EXPECT(assert_equal(IExpected, IActual));
}

/* ************************************************************************* */
TEST( GaussianConditional, isGaussianFactor ) {

  // Create R matrix
  Matrix R(4,4); R <<
      1, 2, 3, 4,
      0, 5, 6, 7,
      0, 0, 8, 9,
      0, 0, 0, 10;

  // Create a conditional
  GaussianConditional conditional(0, Vector::Zero(4), R);

  // Expected information matrix computed by conditional
  Matrix IExpected = conditional.information();

  // Expected information matrix computed by a factor
  JacobianFactor jf = conditional;
  Matrix IActual = jf.information();

  EXPECT(assert_equal(IExpected, IActual));
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */


#ifndef KOMUNA_VEKTORO_HPP
#define KOMUNA_VEKTORO_HPP

#include "../torent/Torentu.hpp"
#include "../trajtoj/numero.hpp"
#include <cmath>
#include <boost/serialization/access.hpp>
#ifdef ĈU_UZAS_QT
#include <QPoint>
#include <QPointF>
#endif

template<class T>
class Vektor2D
{
public:
    struct Ordiganto
    {
        Buleo operator()(const Vektor2D<T>& a, const Vektor2D<T>& b) const
        {
            eligu a.x < b.x aŭ (a.x == b.x kaj a.y < b.y);
        }
    };

    Vektor2D() : x(0), y(0) {}
    Vektor2D(T x, T y) : x(x), y(y) {}//-«
    Nenio operator+=(const Vektor2D<T> alia) { x += alia.x; y += alia.y; }
    Nenio operator-=(const Vektor2D<T> alia) { x -= alia.x; y -= alia.y; }  //»-
    Buleo operator==(const Vektor2D<T> alia) const { eligu x == alia.x && y == alia.y; }
    Buleo operator!=(const Vektor2D<T> alia) const { eligu x != alia.x || y != alia.y; }
    Vektor2D<T> operator+(const Vektor2D<T> alia) const { eligu Vektor2D<T>(x + alia.x, y + alia.y); }
    Vektor2D<T> operator-(const Vektor2D<T> alia) const { eligu Vektor2D<T>(x - alia.x, y - alia.y); }
    Vektor2D<T> operator*(const Vektor2D<T> alia) const { eligu Vektor2D<T>(x * alia.x, y * alia.y); }
    Vektor2D<T> operator/(const Vektor2D<T> alia) const { eligu Vektor2D<T>(x / alia.x, y / alia.y); }
    Vektor2D<T> operator*(const T s) const { eligu Vektor2D<T>(x * s, y * s); }
    Vektor2D<T> operator/(const T s) const { eligu Vektor2D<T>(x / s, y / s); }
    Vektor2D<T> operator%(const T s) const { eligu Vektor2D<T>(x % s, y % s); }
    T skalaranProduton(const Vektor2D<T> alia) const { eligu x * alia.x + y * alia.y; }; //-«
    template<class K> Vektor2D<K> al() const { eligu Vektor2D<K>(ŝanĝu_al<K>(x), ŝanĝu_al<K>(y)); } //»-
    ℚ normon() const { eligu sqrt((ℚ) (x * x + y * y)); }
    Vektor2D<T> dikunNormo(T celnormo) const;

#ifdef ĈU_UZAS_QT
    UZU_SE_(T, ĉu_estas_ℤ<P1>)
    Vektor2D(const QPoint& p) : x(p.x()), y(p.y()) {};
    UZU_SE_(T, ĉu_estas_ℚ_krom_ℤ<P1>)
    Vektor2D(const QPointF& p) : x(p.x()), y(p.y()) {};
    UZU_SE_(T, ĉu_estas_ℤ<P1>)
    operator QPoint() { eligu QPoint(x, y); }
    UZU_SE_(T, ĉu_estas_ℚ_krom_ℤ<P1>)
    operator QPointF() { eligu QPointF(x, y); }
#endif

    T x, y;
    TORENTU { torento & x & y; }
};

template<class T>
class Vektor
{
public:
    struct Ordiganto
    {
        Buleo operator()(const Vektor<T>& a, const Vektor<T>& b) const
        {
            eligu a.x < b.x aŭ (a.x == b.x kaj (a.y < b.y aŭ (a.y == b.y kaj a.z < b.z)));
        }
    };

    template<class K>
    static Vektor<T> prenuEl(const K& k) { eligu Vektor<T>(k.x, k.y, k.z); };
    Vektor() : x(0), y(0), z(0) {}
    Vektor(T x, T y, T z) : x(x), y(y), z(z) {}
    explicit Vektor(Vektor2D<T> xy, T z = 0) : x(xy.x), y(xy.y), z(z) {} //-«
    Buleo operator==(const Vektor<T> alia) const { eligu x == alia.x && y == alia.y && z == alia.z; }
    Buleo operator!=(const Vektor<T> alia) const { eligu x != alia.x || y != alia.y || z != alia.z; }
    Nenio operator+=(const Vektor<T> alia) { x += alia.x; y += alia.y; z += alia.z; }
    Nenio operator-=(const Vektor<T> alia) { x -= alia.x; y -= alia.y; z -= alia.z; } //»-
    Vektor<T> operator+(const Vektor<T> alia) const { eligu Vektor<T>(x + alia.x, y + alia.y, z + alia.z); }
    Vektor<T> operator-(const Vektor<T> alia) const { eligu Vektor<T>(x - alia.x, y - alia.y, z - alia.z); }
    Vektor<T> operator*(const T s) const { eligu Vektor<T>(x * s, y * s, z * s); }
    Vektor<T> operator/(const T s) const { eligu Vektor<T>(x / s, y / s, z / s); }
    T skalaranProduton(const Vektor<T> alia) const { eligu x * alia.x + y * alia.y + z * alia.z; }; // -«
    Vektor<T> ortanProduton(const Vektor<T> a) const { eligu {y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x};}
    template<class K> Vektor<K> al() const { eligu Vektor<K>(ŝanĝu_al<K>(x), ŝanĝu_al<K>(y), ŝanĝu_al<K>(z)); }
    Vektor2D<T> xy() const { eligu Vektor2D<T>(x, y); }
    ℚ normon() const { eligu sqrt((ℚ)(x * x + y * y + z * z)); }
    Vektor<T> dikunNormo(T celnormo) const { premisu (normon() != 0); eligu (*this * celnormo) / normon(); } //»-

    T x, y, z;
    TORENTU { torento & x & y & z; }
};

//-«
template <class K> const Vektor2D<K> nulvektor2D = Vektor2D<K>(0, 0);
template <class K> const Vektor<K> nulvektor = Vektor<K>(0, 0, 0);
template <class K> const Vektor2D<K> nenie2D = Vektor2D<K>(maximumDe<K>, maximumDe<K>);
template <class K> const Vektor<K> nenie = Vektor<K>(maximumDe<K>, maximumDe<K>, maximumDe<K>);

template <class K> Vektor<K> operator*(const K s, Vektor<K> v) { eligu v * s; }
template <class K> Vektor2D<K> operator*(const K s, Vektor2D<K> v) { eligu v * s; }//»-

typedef Vektor<ℕ2> BazaKoordinato;
typedef Vektor2D<ℕ2> BazaKoordinato2D;
//typedef Vektor<FiksitaKomo<ℕ4, pow(2, bitojDen<ℕ4> - bitojDen<ℕ2>)>> BazaSubkoordinato;

template<class K>
Vektor2D<K> Vektor2D<K>::dikunNormo(K celnormo) const
{
    premisu (*this != nulvektor2D<K>);
    eligu (*this * celnormo) / (K) normon();
}

#endif //KOMUNA_VEKTORO_HPP


///////////////////////////////////////////////////////////////
//  Copyright 2011 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt

//[mpfi_eg
#include <boost/multiprecision/mpfi.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <iostream>

int main()
{
   using namespace boost::multiprecision;

   // Operations at variable precision and no numeric_limits support:
   mpfi_float a = 2;
   mpfi_float::default_precision(1000);
   std::cout << mpfi_float::default_precision() << std::endl;
   std::cout << sqrt(a) << std::endl; // print root-2

   // Operations at fixed precision and full numeric_limits support:
   mpfi_float_100 b = 2;
   std::cout << std::numeric_limits<mpfi_float_100>::digits << std::endl;
   // We can use any C++ std lib function:
   std::cout << log(b) << std::endl; // print log(2)

   // Access the underlying data:
   mpfi_t r;
   mpfi_init(r);
   mpfi_set(r, b.backend().data());

   // Construct some explicit intervals and perform set operations:
   mpfi_float_50 i1(1, 2), i2(1.5, 2.5);
   std::cout << intersect(i1, i2) << std::endl;
   std::cout << hull(i1, i2) << std::endl;
   std::cout << overlap(i1, i2) << std::endl;
   std::cout << subset(i1, i2) << std::endl;
   mpfi_clear(r);
   return 0;
}
//]


/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
 */

#include <dai/daialg.h>
#include <dai/alldai.h>
#include <strstream>

using namespace dai;

const double tol = 1e-8;

#define BOOST_TEST_MODULE DAIAlgTest

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE( calcMarginalTest ) {
    Var v0( 0, 2 );
    Var v1( 1, 2 );
    Var v2( 2, 2 );
    Var v3( 3, 2 );
    VarSet v01( v0, v1 );
    VarSet v02( v0, v2 );
    VarSet v03( v0, v3 );
    VarSet v12( v1, v2 );
    VarSet v13( v1, v3 );
    VarSet v23( v2, v3 );
    std::vector<Factor> facs;
    facs.push_back( createFactorIsing( v0, v1, 1.0 ) );
    facs.push_back( createFactorIsing( v1, v2, 1.0 ) );
    facs.push_back( createFactorIsing( v2, v3, 1.0 ) );
    facs.push_back( createFactorIsing( v3, v0, 1.0 ) );
    facs.push_back( createFactorIsing( v0, -1.0 ) );
    facs.push_back( createFactorIsing( v1, -1.0 ) );
    facs.push_back( createFactorIsing( v2, -1.0 ) );
    facs.push_back( createFactorIsing( v3, 1.0 ) );
    Factor joint = facs[0] * facs[1] * facs[2] * facs[3] * facs[4] * facs[5] * facs[6] * facs[7];
    FactorGraph fg( facs );
    ExactInf ei( fg, PropertySet()("verbose",(size_t)0) );
    ei.init();
    ei.run();
    VarSet vs;

    vs = v0;        BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v1;        BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v2;        BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v3;        BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v01;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v02;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v03;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v12;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v13;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v23;       BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v01 | v2;  BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v01 | v3;  BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v02 | v3;  BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v12 | v3;  BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
    vs = v01 | v23; BOOST_CHECK( dist( calcMarginal( ei, vs, false ), joint.marginal( vs ), DISTTV ) < tol );
}

BOOST_AUTO_TEST_CASE( calcPairBeliefsTest ) {
    Var v0( 0, 2 );
    Var v1( 1, 2 );
    Var v2( 2, 2 );
    Var v3( 3, 2 );
    VarSet v01( v0, v1 );
    VarSet v02( v0, v2 );
    VarSet v03( v0, v3 );
    VarSet v12( v1, v2 );
    VarSet v13( v1, v3 );
    VarSet v23( v2, v3 );
    std::vector<Factor> facs;
    facs.push_back( createFactorIsing( v0, v1, 1.0 ) );
    facs.push_back( createFactorIsing( v1, v2, 1.0 ) );
    facs.push_back( createFactorIsing( v2, v3, 1.0 ) );
    facs.push_back( createFactorIsing( v3, v0, 1.0 ) );
    facs.push_back( createFactorIsing( v0, -1.0 ) );
    facs.push_back( createFactorIsing( v1, -1.0 ) );
    facs.push_back( createFactorIsing( v2, -1.0 ) );
    facs.push_back( createFactorIsing( v3, 1.0 ) );
    Factor joint = facs[0] * facs[1] * facs[2] * facs[3] * facs[4] * facs[5] * facs[6] * facs[7];
    FactorGraph fg( facs );
    ExactInf ei( fg, PropertySet()("verbose",(size_t)0) );
    ei.init();
    ei.run();
    VarSet vs;

    std::vector<Factor> pb = calcPairBeliefs( ei, v01 | v23, false, false );
    BOOST_CHECK( dist( pb[0], joint.marginal( v01 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[1], joint.marginal( v02 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[2], joint.marginal( v03 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[3], joint.marginal( v12 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[4], joint.marginal( v13 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[5], joint.marginal( v23 ), DISTTV ) < tol );

    pb = calcPairBeliefs( ei, v01 | v23, false, true );
    BOOST_CHECK( dist( pb[0], joint.marginal( v01 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[1], joint.marginal( v02 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[2], joint.marginal( v03 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[3], joint.marginal( v12 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[4], joint.marginal( v13 ), DISTTV ) < tol );
    BOOST_CHECK( dist( pb[5], joint.marginal( v23 ), DISTTV ) < tol );
}


/**
 * @file   main_grid_transfer_test.cpp
 * @author  <simon@thinkpadX1>
 * @date   Tue Mar 31 15:27:16 2015
 *
 * @brief  Just for debugging purposes!
 *         GridTransfer (dh, dh) => should yield identity matrix
 *
 *
 */

// deal.II includes ----------------------------------------------
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/grid/grid_in.h>
#include <deal.II/lac/vector.h>

// system includes -----------------------------------------------
#include <hdf5.h>
#include <yaml-cpp/yaml.h>
#include <Eigen/Dense>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
// from eigen unsupported
#include <Eigen/KroneckerProduct>

// own includes --------------------------------------------------
#include "aux/eigen2hdf.hpp"
#include "aux/message.hpp"
#include "aux/timer.hpp"
#include "grid_transfer.hpp"
#include "init/import/load_coefficients.hpp"

#include "spectral/basis/indexer.hpp"

#include "l2errors.hpp"
// class SimpleGridHandler, Solution
#include "grid/grid_tools.hpp"
#include "solution_handler.hpp"
#include "spectral_transfer_matrix.hpp"

const int dim = 2;

namespace bf = boost::filesystem;
namespace po = boost::program_options;

using namespace boltzmann;
using namespace std;

const std::string spectral_basis_fname = "spectral_basis.desc";

typedef dealii::DoFHandler<dim> dh_t;
typedef dealii::Vector<double> vector_t;

int main(int argc, char* argv[])
{
  boltzmann::Timer<> timer;

  po::options_description options("options");
  options.add_options()
      ("config,c", po::value<string>()->required(), "config file")
      ("help,h", "help");
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, options), vm);
  po::notify(vm);

  string config_name = vm["config"].as<string>();

  if (!boost::filesystem::is_regular_file(config_name)) {
    cout << "config file not found\n";
    return 1;
  }

  YAML::Node config = YAML::LoadFile(config_name);

  string str_input_grid = config["input"]["grid"].as<string>();
  string str_input_path = config["input"]["path"].as<string>();
  string str_input_solution = config["input"]["solution"].as<string>();

  string str_ref_grid = config["reference"]["grid"].as<string>();
  string str_ref_path = config["reference"]["path"].as<string>();
  string str_ref_solution = config["reference"]["solution"].as<string>();

  dealii::FE_Q<dim> fe(1);
  shared_ptr<SimpleGridHandler> ref_grid_ptr =
      make_shared<SimpleGridHandler>(str_ref_path, str_ref_grid);
  shared_ptr<SimpleGridHandler> grid_ptr =
      make_shared<SimpleGridHandler>(str_input_path, str_input_grid);

  const auto& ref_dh = ref_grid_ptr->get_dofhandler();
  const auto& input_dh = grid_ptr->get_dofhandler();

  GridTransfer<dim> grid_transfer;
  grid_transfer.init(ref_dh, ref_dh);
  const auto& Tx = grid_transfer.get_transfer_matrix();

  // output to hdf5
  hid_t file;
  file = H5Fcreate("transfer_matrix.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
  eigen2hdf::save_sparse(file, "Tx", Tx);

  // // load solution data
  // typedef Solution<SimpleGridHandler> solution_t;
  // solution_t input_solution(grid_ptr, str_input_path, str_input_solution);
  // solution_t ref_solution(ref_grid_ptr, str_ref_path, str_ref_solution);

  // auto& mu_input = input_solution.get_solution();

  // spectral transfer matrix
  auto Tv =
      spectral_transfer_matrix(ref_grid_ptr->get_spectral_basis(), grid_ptr->get_spectral_basis());
  eigen2hdf::save_sparse(file, "Tv", Tv);

  // timer.start();
  // Eigen::SparseMatrix<double> T = Eigen::kroneckerProduct(Tx, Tv);
  // print_timer(timer.stop(), "make T");

  // timer.start();
  // eigen2hdf::save_sparse(file, "T", T);
  // print_timer(timer.stop(), "save T");

  // timer.start();
  // Eigen::VectorXd out = T*mu_input;
  // print_timer(timer.stop(), "interpolation to fine grid");

  // eigen2hdf::save(file, "coeffs", out);

  // auto vertex2dofidx = vertex_to_dof_index(ref_dh);
  // std::ofstream fout("v2d.dat");
  // for (auto it = vertex2dofidx.begin(); it != vertex2dofidx.end();
  //      ++it) {
  //   fout << it->first << "\t" << it->second << endl;
  // }
  // fout.close();

  // ------------------------------------------------------------------------------------------
  // TODO load coefficients

  // ------------------------------------------------------------------------------------------
  // compute errors, global, cell-wise

  // load basis file
  H5Fclose(file);

  return 0;
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_SORT_HPP_INCLUDED
#define BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_SORT_HPP_INCLUDED

#ifdef BOOST_SIMD_HAS_SSE2_SUPPORT
#include <boost/simd/swar/functions/sort.hpp>
#include <boost/simd/include/functions/simd/min.hpp>
#include <boost/simd/include/functions/simd/max.hpp>
#include <boost/simd/include/functions/simd/minimum.hpp>
#include <boost/simd/include/functions/simd/maximum.hpp>
#include <boost/simd/include/functions/simd/make.hpp>
#include <boost/simd/swar/functions/shuffle.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( sort_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < type32_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE result_type operator()(A0 const& a0) const
    {
      // half-permute
      A0 p0 = shuffle<2,3,0,1>(a0);
      A0 mn = min(a0,p0);
      A0 mx = max(a0,p0);

      // cross vector concatenation and reversal
      A0 minmax = shuffle<0,1,6,7>(mn,mx);
      A0 maxmin = shuffle<1,0,7,6>(mn,mx);

      mn = min(minmax,maxmin);
      mx = max(minmax,maxmin);

      // rearrange partial max/min while keeping min and max in place
         p0 = shuffle<0,2,5,7>(mn,mx);
      A0 p1 = shuffle<0,2,1,3>(p0);

      // Bring sorted min/max in the proper place
      return shuffle<0,1,6,7>(min(p1,p0),max(p1,p0));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( sort_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < type64_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE result_type operator()(A0 const& a0) const
    {
      // Better latency
      A0 p0 = shuffle<1,0>(a0);
      return shuffle<0,2>(min(a0,p0),max(a0,p0));
    }
  };
} } }

#endif
#endif


#include <NTL/GF2EX.h>
#include <NTL/GF2XFactoring.h>

namespace NTL {

void BuildPlain(GF2EXModulus& F, const GF2EX& f, bool plain);

}

NTL_CLIENT

#define TIME_IT(t, action) \
do { \
   double _t0, _t1; \
   long _iter = 1; \
   long _cnt = 0; \
   do { \
      _t0 = GetTime(); \
      for (long _i = 0; _i < _iter; _i++) { action; _cnt++; } \
      _t1 = GetTime(); \
   } while ( _t1 - _t0 < 2 && (_iter *= 2)); \
   t = (_t1 - _t0)/_iter; \
} while(0)

long test(long k)
{
   GF2X P;

   BuildIrred(P, k);
   GF2EPush push(P);

   for (long n = 5; ; n+=5) {
      cerr << ",";
      GF2EX a, r, f;
      random(a, 2*n-1);
      random(f, n);
      SetCoeff(f, n);
      GF2EXModulus F1, F2;
      BuildPlain(F1, f, false);
      BuildPlain(F2, f, true);
      double t1, t2;
      TIME_IT(t1, rem(r, a, F1));
      TIME_IT(t2, rem(r, a, F2));
      double t = t1/t2;
      if (t <= 0.95) return n;
   }
}

int main()
{
   cerr << "0.5 " << test(32) << "\n";
   for (long i = 1; i <= 40 ; i++) {
      cerr << i << " " << test(64*i) << "\n";
   }
}


// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.

#include "precomp.hpp"

#include <Eigen/Sparse>

using namespace Eigen;

namespace cv { namespace alphamat {

static
void solve(SparseMatrix<double> Wcm, SparseMatrix<double> Wuu, SparseMatrix<double> Wl, SparseMatrix<double> Dcm,
        SparseMatrix<double> Duu, SparseMatrix<double> Dl, SparseMatrix<double> T,
        Mat& wf, Mat& alpha)
{
    float suu = 0.01, sl = 0.1, lamd = 100;

    SparseMatrix<double> Lifm = ((Dcm - Wcm).transpose()) * (Dcm - Wcm) + sl * (Dl - Wl) + suu * (Duu - Wuu);

    SparseMatrix<double> A;
    int n = wf.rows;
    VectorXd b(n), x(n);

    Eigen::VectorXd wf_;
    cv2eigen(wf, wf_);

    A = Lifm + lamd * T;
    b = (lamd * T) * (wf_);

    ConjugateGradient<SparseMatrix<double>, Lower | Upper> cg;

    cg.setMaxIterations(500);
    cg.compute(A);
    x = cg.solve(b);
    CV_LOG_INFO(NULL, "ALPHAMAT: #iterations:     " << cg.iterations());
    CV_LOG_INFO(NULL, "ALPHAMAT: estimated error: " << cg.error());

    int nRows = alpha.rows;
    int nCols = alpha.cols;
    float pix_alpha;
    for (int j = 0; j < nCols; ++j)
    {
        for (int i = 0; i < nRows; ++i)
        {
            pix_alpha = x(i + j * nRows);
            if (pix_alpha < 0)
                pix_alpha = 0;
            if (pix_alpha > 1)
                pix_alpha = 1;
            alpha.at<uchar>(i, j) = uchar(pix_alpha * 255);
        }
    }
}

void infoFlow(InputArray image_ia, InputArray tmap_ia, OutputArray result)
{
    Mat image = image_ia.getMat();
    Mat tmap = tmap_ia.getMat();

    int64 begin = cv::getTickCount();

    int nRows = image.rows;
    int nCols = image.cols;
    int N = nRows * nCols;

    SparseMatrix<double> T(N, N);
    typedef Triplet<double> Tr;
    std::vector<Tr> triplets;

    //Pre-process trimap
    for (int i = 0; i < nRows; ++i)
    {
        for (int j = 0; j < nCols; ++j)
        {
            uchar& pix = tmap.at<uchar>(i, j);
            if (pix <= 0.2f * 255)
                pix = 0;
            else if (pix >= 0.8f * 255)
                pix = 255;
            else
                pix = 128;
        }
    }

    Mat wf = Mat::zeros(nRows * nCols, 1, CV_8U);

    // Column Major Interpretation for working with SparseMatrix
    for (int i = 0; i < nRows; ++i)
    {
        for (int j = 0; j < nCols; ++j)
        {
            uchar pix = tmap.at<uchar>(i, j);

            // collection of known pixels samples
            triplets.push_back(Tr(i + j * nRows, i + j * nRows, (pix != 128) ? 1 : 0));

            // foreground pixel
            wf.at<uchar>(i + j * nRows, 0) = (pix > 200) ? 1 : 0;
        }
    }

    SparseMatrix<double> Wl(N, N), Dl(N, N);
    local_info(image, tmap, Wl, Dl);

    SparseMatrix<double> Wcm(N, N), Dcm(N, N);
    cm(image, tmap, Wcm, Dcm);

    Mat new_tmap = tmap.clone();

    SparseMatrix<double> Wuu(N, N), Duu(N, N);
    Mat image_t = image.t();
    Mat tmap_t = tmap.t();
    UU(image, tmap, Wuu, Duu);

    double elapsed_secs = ((double)(getTickCount() - begin)) / getTickFrequency();

    T.setFromTriplets(triplets.begin(), triplets.end());

    Mat alpha = Mat::zeros(nRows, nCols, CV_8UC1);
    solve(Wcm, Wuu, Wl, Dcm, Duu, Dl, T, wf, alpha);

    alpha.copyTo(result);

    elapsed_secs = ((double)(getTickCount() - begin)) / getTickFrequency();
    CV_LOG_INFO(NULL, "ALPHAMAT: total time: " << elapsed_secs);
}

}}  // namespace cv::alphamat


// Copyright (C) 2013  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include "opaque_types.h"
#include <dlib/python.h>
#include <dlib/matrix.h>
#include <dlib/data_io.h>
#include <dlib/sparse_vector.h>
#include <dlib/optimization.h>
#include <dlib/statistics/running_gradient.h>

using namespace dlib;
using namespace std;
namespace py = pybind11;

typedef std::vector<std::pair<unsigned long,double> > sparse_vect;

void _make_sparse_vector (
    sparse_vect& v
)
{
    make_sparse_vector_inplace(v);
}

void _make_sparse_vector2 (
    std::vector<sparse_vect>& v
)
{
    for (unsigned long i = 0; i < v.size(); ++i)
        make_sparse_vector_inplace(v[i]);
}

py::tuple _load_libsvm_formatted_data(
    const std::string& file_name
)
{
    std::vector<sparse_vect> samples;
    std::vector<double> labels;
    load_libsvm_formatted_data(file_name, samples, labels);
    return py::make_tuple(samples, labels);
}

void _save_libsvm_formatted_data (
    const std::string& file_name,
    const std::vector<sparse_vect>& samples,
    const std::vector<double>& labels
)
{
    pyassert(samples.size() == labels.size(), "Invalid inputs");
    save_libsvm_formatted_data(file_name, samples, labels);
}

// ----------------------------------------------------------------------------------------

py::list _max_cost_assignment (
    const matrix<double>& cost
)
{
    if (cost.nr() != cost.nc())
        throw dlib::error("The input matrix must be square.");

    // max_cost_assignment() only works with integer matrices, so convert from
    // double to integer.
    const double scale = (std::numeric_limits<dlib::int64>::max()/1000)/max(abs(cost));
    matrix<dlib::int64> int_cost = matrix_cast<dlib::int64>(round(cost*scale));
    return vector_to_python_list(max_cost_assignment(int_cost));
}

double _assignment_cost (
    const matrix<double>& cost,
    const py::list& assignment
)
{
    return assignment_cost(cost, python_list_to_vector<long>(assignment));
}

// ----------------------------------------------------------------------------------------

size_t py_count_steps_without_decrease (
    py::object arr,
    double probability_of_decrease
)
{
    DLIB_CASSERT(0.5 < probability_of_decrease && probability_of_decrease < 1);
    return count_steps_without_decrease(python_list_to_vector<double>(arr), probability_of_decrease);
}

// ----------------------------------------------------------------------------------------

size_t py_count_steps_without_decrease_robust (
    py::object arr,
    double probability_of_decrease,
    double quantile_discard
)
{
    DLIB_CASSERT(0.5 < probability_of_decrease && probability_of_decrease < 1);
    DLIB_CASSERT(0 <= quantile_discard && quantile_discard <= 1);
    return count_steps_without_decrease_robust(python_list_to_vector<double>(arr), probability_of_decrease, quantile_discard);
}

// ----------------------------------------------------------------------------------------

double probability_that_sequence_is_increasing (
    py::object arr
)
{
    DLIB_CASSERT(len(arr) > 2);
    return probability_gradient_greater_than(python_list_to_vector<double>(arr), 0);
}

// ----------------------------------------------------------------------------------------

void hit_enter_to_continue()
{
    std::cout << "Hit enter to continue";
    std::cin.get();
}

// ----------------------------------------------------------------------------------------

void bind_other(py::module &m)
{
    m.def("max_cost_assignment", _max_cost_assignment, py::arg("cost"),
"requires    \n\
    - cost.nr() == cost.nc()    \n\
      (i.e. the input must be a square matrix)    \n\
ensures    \n\
    - Finds and returns the solution to the following optimization problem:    \n\
    \n\
        Maximize: f(A) == assignment_cost(cost, A)    \n\
        Subject to the following constraints:    \n\
            - The elements of A are unique. That is, there aren't any     \n\
              elements of A which are equal.      \n\
            - len(A) == cost.nr()    \n\
    \n\
    - Note that this function converts the input cost matrix into a 64bit fixed    \n\
      point representation.  Therefore, you should make sure that the values in    \n\
      your cost matrix can be accurately represented by 64bit fixed point values.    \n\
      If this is not the case then the solution my become inaccurate due to    \n\
      rounding error.  In general, this function will work properly when the ratio    \n\
      of the largest to the smallest value in cost is no more than about 1e16.   "
        );

    m.def("assignment_cost", _assignment_cost, py::arg("cost"),py::arg("assignment"),
"requires    \n\
    - cost.nr() == cost.nc()    \n\
      (i.e. the input must be a square matrix)    \n\
    - for all valid i:    \n\
        - 0 <= assignment[i] < cost.nr()    \n\
ensures    \n\
    - Interprets cost as a cost assignment matrix. That is, cost[i][j]     \n\
      represents the cost of assigning i to j.      \n\
    - Interprets assignment as a particular set of assignments. That is,    \n\
      i is assigned to assignment[i].    \n\
    - returns the cost of the given assignment. That is, returns    \n\
      a number which is:    \n\
        sum over i: cost[i][assignment[i]]   "
        );

    m.def("make_sparse_vector", _make_sparse_vector ,
"This function modifies its argument so that it is a properly sorted sparse vector.    \n\
This means that the elements of the sparse vector will be ordered so that pairs    \n\
with smaller indices come first.  Additionally, there won't be any pairs with    \n\
identical indices.  If such pairs were present in the input sparse vector then    \n\
their values will be added together and only one pair with their index will be    \n\
present in the output.   "
        );
    m.def("make_sparse_vector", _make_sparse_vector2 ,
        "This function modifies a sparse_vectors object so that all elements it contains are properly sorted sparse vectors.");

    m.def("load_libsvm_formatted_data",_load_libsvm_formatted_data, py::arg("file_name"),
"ensures    \n\
    - Attempts to read a file of the given name that should contain libsvm    \n\
      formatted data.  The data is returned as a tuple where the first tuple    \n\
      element is an array of sparse vectors and the second element is an array of    \n\
      labels.    "
    );

    m.def("save_libsvm_formatted_data",_save_libsvm_formatted_data, py::arg("file_name"), py::arg("samples"), py::arg("labels"),
"requires    \n\
    - len(samples) == len(labels)    \n\
ensures    \n\
    - saves the data to the given file in libsvm format   "
    );

    m.def("hit_enter_to_continue", hit_enter_to_continue,
        "Asks the user to hit enter to continue and pauses until they do so.");

    m.def("count_steps_without_decrease",py_count_steps_without_decrease, py::arg("time_series"), py::arg("probability_of_decrease")=0.51,
"requires \n\
    - time_series must be a one dimensional array of real numbers.  \n\
    - 0.5 < probability_of_decrease < 1 \n\
ensures \n\
    - If you think of the contents of time_series as a potentially noisy time \n\
      series, then this function returns a count of how long the time series has \n\
      gone without noticeably decreasing in value.  It does this by scanning along \n\
      the elements, starting from the end (i.e. time_series[-1]) to the beginning, \n\
      and checking how many elements you need to examine before you are confident \n\
      that the series has been decreasing in value.  Here, \"confident of decrease\" \n\
      means the probability of decrease is >= probability_of_decrease.   \n\
    - Setting probability_of_decrease to 0.51 means we count until we see even a \n\
      small hint of decrease, whereas a larger value of 0.99 would return a larger \n\
      count since it keeps going until it is nearly certain the time series is \n\
      decreasing. \n\
    - The max possible output from this function is len(time_series). \n\
    - The implementation of this function is done using the dlib::running_gradient \n\
      object, which is a tool that finds the least squares fit of a line to the \n\
      time series and the confidence interval around the slope of that line.  That \n\
      can then be used in a simple statistical test to determine if the slope is \n\
      positive or negative."
    /*!
        requires
            - time_series must be a one dimensional array of real numbers.
            - 0.5 < probability_of_decrease < 1
        ensures
            - If you think of the contents of time_series as a potentially noisy time
              series, then this function returns a count of how long the time series has
              gone without noticeably decreasing in value.  It does this by scanning along
              the elements, starting from the end (i.e. time_series[-1]) to the beginning,
              and checking how many elements you need to examine before you are confident
              that the series has been decreasing in value.  Here, "confident of decrease"
              means the probability of decrease is >= probability_of_decrease.
            - Setting probability_of_decrease to 0.51 means we count until we see even a
              small hint of decrease, whereas a larger value of 0.99 would return a larger
              count since it keeps going until it is nearly certain the time series is
              decreasing.
            - The max possible output from this function is len(time_series).
            - The implementation of this function is done using the dlib::running_gradient
              object, which is a tool that finds the least squares fit of a line to the
              time series and the confidence interval around the slope of that line.  That
              can then be used in a simple statistical test to determine if the slope is
              positive or negative.
    !*/
    );

    m.def("count_steps_without_decrease_robust",py_count_steps_without_decrease_robust, py::arg("time_series"), py::arg("probability_of_decrease")=0.51, py::arg("quantile_discard")=0.1,
"requires \n\
    - time_series must be a one dimensional array of real numbers.  \n\
    - 0.5 < probability_of_decrease < 1 \n\
    - 0 <= quantile_discard <= 1 \n\
ensures \n\
    - This function behaves just like \n\
      count_steps_without_decrease(time_series,probability_of_decrease) except that \n\
      it ignores values in the time series that are in the upper quantile_discard \n\
      quantile.  So for example, if the quantile discard is 0.1 then the 10% \n\
      largest values in the time series are ignored."
    /*!
        requires
            - time_series must be a one dimensional array of real numbers.
            - 0.5 < probability_of_decrease < 1
            - 0 <= quantile_discard <= 1
        ensures
            - This function behaves just like
              count_steps_without_decrease(time_series,probability_of_decrease) except that
              it ignores values in the time series that are in the upper quantile_discard
              quantile.  So for example, if the quantile discard is 0.1 then the 10%
              largest values in the time series are ignored.
    !*/
    );

    m.def("probability_that_sequence_is_increasing",probability_that_sequence_is_increasing, py::arg("time_series"),
        "returns the probability that the given sequence of real numbers is increasing in value over time.");
}


/**
 * @author Andre Anjos <andre.anjos@idiap.ch>
 * @date Sun 27 Oct 09:02:32 2013
 *
 * @brief Uniform distributions (with integers or floating point numbers)
 */

#define BOB_CORE_RANDOM_MODULE
#include <bob.core/random_api.h>
#include <bob.blitz/cppapi.h>
#include <bob.blitz/cleanup.h>
#include <bob.extension/documentation.h>

#include <boost/make_shared.hpp>

#include <boost/random.hpp>

static auto uniform_doc = bob::extension::ClassDoc(
  BOB_EXT_MODULE_PREFIX ".uniform",
  "Models a random uniform distribution",
  "On each invocation, it returns a random value uniformly distributed in the set of numbers [min, max] (integer) and [min, max[ (real-valued)"
)
.add_constructor(bob::extension::FunctionDoc(
  "uniform",
  "Constructs a new uniform distribution object",
  "If the values ``min`` and ``max`` are not given, they are assumed to be ``min=0`` and ``max=9``, for integral distributions and ``min=0.0`` and ``max=1.0`` for real-valued distributions."
)
.add_prototype("dtype, [min], [max]", "")
.add_parameter("dtype", ":py:class:`numpy.dtype` or anything that converts to a dtype", "The data type to get the distribution for")
.add_parameter("min", "dtype", "[Default: 0] The minimum value to draw")
.add_parameter("max", "dtype", "[Default: 1. (for real-valued ``dtype``) or 9 (for integral ``dtype``)] The maximum value to be drawn")
);

/* How to create a new PyBoostUniformObject */
static PyObject* PyBoostUniform_New(PyTypeObject* type, PyObject*, PyObject*) {

  /* Allocates the python object itself */
  PyBoostUniformObject* self = (PyBoostUniformObject*)type->tp_alloc(type, 0);
  self->type_num = NPY_NOTYPE;
  self->distro.reset();

  return Py_BuildValue("N", self);
}

/* How to delete a PyBoostUniformObject */
static void PyBoostUniform_Delete (PyBoostUniformObject* o) {
  o->distro.reset();
  Py_TYPE(o)->tp_free((PyObject*)o);
}

static boost::shared_ptr<void> make_uniform_bool() {
  return boost::make_shared<boost::uniform_smallint<uint8_t>>(0, 1);
}

template <typename T>
boost::shared_ptr<void> make_uniform_int(PyObject* min, PyObject* max) {
  T cmin = 0;
  if (min) cmin = PyBlitzArrayCxx_AsCScalar<T>(min);
  T cmax = 9;
  if (max) cmax = PyBlitzArrayCxx_AsCScalar<T>(max);
  return boost::make_shared<boost::uniform_int<T>>(cmin, cmax);
}

template <typename T>
boost::shared_ptr<void> make_uniform_real(PyObject* min, PyObject* max) {
  T cmin = 0;
  if (min) cmin = PyBlitzArrayCxx_AsCScalar<T>(min);
  T cmax = 1;
  if (max) cmax = PyBlitzArrayCxx_AsCScalar<T>(max);
  return boost::make_shared<boost::uniform_real<T>>(cmin, cmax);
}

PyObject* PyBoostUniform_SimpleNew (int type_num, PyObject* min, PyObject* max) {
BOB_TRY
  if (type_num == NPY_BOOL && (min || max)) {
    PyErr_Format(PyExc_ValueError, "uniform distributions of boolean scalars cannot have a maximum or minimum");
    return 0;
  }

  PyBoostUniformObject* retval = (PyBoostUniformObject*)PyBoostUniform_New(&PyBoostUniform_Type, 0, 0);
  if (!retval) return 0;
  auto retval_ = make_safe(retval);

  retval->type_num = type_num;

  switch(type_num) {
    case NPY_BOOL:
      retval->distro = make_uniform_bool();
      break;
    case NPY_UINT8:
      retval->distro = make_uniform_int<uint8_t>(min, max);
      break;
    case NPY_UINT16:
      retval->distro = make_uniform_int<uint16_t>(min, max);
      break;
    case NPY_UINT32:
      retval->distro = make_uniform_int<uint32_t>(min, max);
      break;
    case NPY_UINT64:
      retval->distro = make_uniform_int<uint64_t>(min, max);
      break;
    case NPY_INT8:
      retval->distro = make_uniform_int<int8_t>(min, max);
      break;
    case NPY_INT16:
      retval->distro = make_uniform_int<int16_t>(min, max);
      break;
    case NPY_INT32:
      retval->distro = make_uniform_int<int32_t>(min, max);
      break;
    case NPY_INT64:
      retval->distro = make_uniform_int<int64_t>(min, max);
      break;
    case NPY_FLOAT32:
      retval->distro = make_uniform_real<float>(min, max);
      break;
    case NPY_FLOAT64:
      retval->distro = make_uniform_real<double>(min, max);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot create %s(T) with T having an unsupported numpy type number of %d", Py_TYPE(retval)->tp_name, retval->type_num);
      return 0;
  }

  if (!retval->distro) { // a problem occurred
    return 0;
  }

  return Py_BuildValue("O", retval);
BOB_CATCH_FUNCTION("SimpleNew", 0)
}

/* Implements the __init__(self) function */
static int PyBoostUniform_Init(PyBoostUniformObject* self, PyObject *args, PyObject* kwds) {
BOB_TRY
  char** kwlist = uniform_doc.kwlist();

  PyObject* min = 0;
  PyObject* max = 0;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|OO", kwlist, &PyBlitzArray_TypenumConverter, &self->type_num, &min, &max)) return -1; ///< FAILURE

  if (self->type_num == NPY_BOOL && (min || max)) {
    PyErr_Format(PyExc_ValueError, "uniform distributions of boolean scalars cannot have a maximum or minimum");
    return -1;
  }

  switch(self->type_num) {
    case NPY_BOOL:
      self->distro = make_uniform_bool();
      break;
    case NPY_UINT8:
      self->distro = make_uniform_int<uint8_t>(min, max);
      break;
    case NPY_UINT16:
      self->distro = make_uniform_int<uint16_t>(min, max);
      break;
    case NPY_UINT32:
      self->distro = make_uniform_int<uint32_t>(min, max);
      break;
    case NPY_UINT64:
      self->distro = make_uniform_int<uint64_t>(min, max);
      break;
    case NPY_INT8:
      self->distro = make_uniform_int<int8_t>(min, max);
      break;
    case NPY_INT16:
      self->distro = make_uniform_int<int16_t>(min, max);
      break;
    case NPY_INT32:
      self->distro = make_uniform_int<int32_t>(min, max);
      break;
    case NPY_INT64:
      self->distro = make_uniform_int<int64_t>(min, max);
      break;
    case NPY_FLOAT32:
      self->distro = make_uniform_real<float>(min, max);
      break;
    case NPY_FLOAT64:
      self->distro = make_uniform_real<double>(min, max);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot create %s(T) with T having an unsupported numpy type number of %d", Py_TYPE(self)->tp_name, self->type_num);
      return -1;
  }

  if (!self->distro) { // a problem occurred
    return -1;
  }

  return 0; ///< SUCCESS
BOB_CATCH_MEMBER("constructor", -1)
}

int PyBoostUniform_Check(PyObject* o) {
  if (!o) return 0;
  return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBoostUniform_Type));
}

int PyBoostUniform_Converter(PyObject* o, PyBoostUniformObject** a) {
  if (!PyBoostUniform_Check(o)) return 0;
  Py_INCREF(o);
  (*a) = reinterpret_cast<PyBoostUniformObject*>(o);
  return 1;
}

static auto min_doc = bob::extension::VariableDoc(
  "min",
  "dtype",
  "The smallest value that the distribution can produce"
);
template <typename T> PyObject* get_minimum_int(PyBoostUniformObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_int<T>>(self->distro)->min());
}

template <typename T> PyObject* get_minimum_real(PyBoostUniformObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_real<T>>(self->distro)->min());
}

/**
 * Accesses the min value
 */
static PyObject* PyBoostUniform_GetMin(PyBoostUniformObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_BOOL:
      Py_RETURN_FALSE;
    case NPY_UINT8:
      return get_minimum_int<uint8_t>(self);
    case NPY_UINT16:
      return get_minimum_int<uint16_t>(self);
    case NPY_UINT32:
      return get_minimum_int<uint32_t>(self);
    case NPY_UINT64:
      return get_minimum_int<uint64_t>(self);
    case NPY_INT8:
      return get_minimum_int<int8_t>(self);
    case NPY_INT16:
      return get_minimum_int<int16_t>(self);
    case NPY_INT32:
      return get_minimum_int<int32_t>(self);
    case NPY_INT64:
      return get_minimum_int<int64_t>(self);
    case NPY_FLOAT32:
      return get_minimum_real<float>(self);
    case NPY_FLOAT64:
      return get_minimum_real<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot get minimum of %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("min", 0)
}

static auto max_doc = bob::extension::VariableDoc(
  "max",
  "dtype",
  "The largest value that the distributioncan produce",
  "Integer uniform distributions are bound at [min, max], while real-valued distributions are bound at [min, max[."
);
template <typename T> PyObject* get_maximum_int(PyBoostUniformObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_int<T>>(self->distro)->max());
}

template <typename T> PyObject* get_maximum_real(PyBoostUniformObject* self) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_real<T>>(self->distro)->max());
}

/**
 * Accesses the max value
 */
static PyObject* PyBoostUniform_GetMax(PyBoostUniformObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_BOOL:
      Py_RETURN_TRUE;
    case NPY_UINT8:
      return get_maximum_int<uint8_t>(self);
    case NPY_UINT16:
      return get_maximum_int<uint16_t>(self);
    case NPY_UINT32:
      return get_maximum_int<uint32_t>(self);
    case NPY_UINT64:
      return get_maximum_int<uint64_t>(self);
    case NPY_INT8:
      return get_maximum_int<int8_t>(self);
    case NPY_INT16:
      return get_maximum_int<int16_t>(self);
    case NPY_INT32:
      return get_maximum_int<int32_t>(self);
    case NPY_INT64:
      return get_maximum_int<int64_t>(self);
    case NPY_FLOAT32:
      return get_maximum_real<float>(self);
    case NPY_FLOAT64:
      return get_maximum_real<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot get maximum of %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("max", 0)
}

static auto dtype_doc = bob::extension::VariableDoc(
  "dtype",
  ":py:class:`numpy.dtype`",
  "The type of scalars produced by this uniform distribution"
);
static PyObject* PyBoostUniform_GetDtype(PyBoostUniformObject* self) {
BOB_TRY
  return Py_BuildValue("N", PyArray_DescrFromType(self->type_num));
BOB_CATCH_MEMBER("dtype", 0)
}

static PyGetSetDef PyBoostUniform_getseters[] = {
    {
      dtype_doc.name(),
      (getter)PyBoostUniform_GetDtype,
      0,
      dtype_doc.doc(),
      0,
    },
    {
      min_doc.name(),
      (getter)PyBoostUniform_GetMin,
      0,
      min_doc.doc(),
      0,
    },
    {
      max_doc.name(),
      (getter)PyBoostUniform_GetMax,
      0,
      max_doc.doc(),
      0,
    },
    {0}  /* Sentinel */
};

static auto reset_doc = bob::extension::FunctionDoc(
  "reset",
  "Resets this distribution",
  "After calling this method, subsequent uses of the distribution do not depend on values produced by any random number generator prior to invoking reset",
  true
)
.add_prototype("")
;
template <typename T> PyObject* reset_smallint(PyBoostUniformObject* self) {
  boost::static_pointer_cast<boost::uniform_smallint<T>>(self->distro)->reset();
  Py_RETURN_NONE;
}

template <typename T> PyObject* reset_int(PyBoostUniformObject* self) {
  boost::static_pointer_cast<boost::uniform_int<T>>(self->distro)->reset();
  Py_RETURN_NONE;
}

template <typename T> PyObject* reset_real(PyBoostUniformObject* self) {
  boost::static_pointer_cast<boost::uniform_real<T>>(self->distro)->reset();
  Py_RETURN_NONE;
}

static PyObject* PyBoostUniform_Reset(PyBoostUniformObject* self) {
BOB_TRY
  switch (self->type_num) {
    case NPY_BOOL:
      return reset_smallint<uint8_t>(self);
    case NPY_UINT8:
      return reset_int<uint8_t>(self);
    case NPY_UINT16:
      return reset_int<uint16_t>(self);
    case NPY_UINT32:
      return reset_int<uint32_t>(self);
    case NPY_UINT64:
      return reset_int<uint64_t>(self);
    case NPY_INT8:
      return reset_int<int8_t>(self);
    case NPY_INT16:
      return reset_int<int16_t>(self);
    case NPY_INT32:
      return reset_int<int32_t>(self);
    case NPY_INT64:
      return reset_int<int64_t>(self);
    case NPY_FLOAT32:
      return reset_real<float>(self);
    case NPY_FLOAT64:
      return reset_real<double>(self);
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot reset %s(T) with T having an unsupported numpy type number of %d (DEBUG ME)", Py_TYPE(self)->tp_name, self->type_num);
      return 0;
  }
BOB_CATCH_MEMBER("reset", 0)
}

static auto call_doc = bob::extension::FunctionDoc(
  "draw",
  "Draws one random number from this distribution using the given ``rng``",
  ".. note:: The :py:meth:`__call__` function is a synonym for this ``draw``.",
  true
)
.add_prototype("rng", "value")
.add_parameter("rng", ":py:class:`mt19937`", "The random number generator to use")
.add_return("value", "dtype", "A random value that follows the uniform distribution")
;
static PyObject* call_bool(PyBoostUniformObject* self, PyBoostMt19937Object* rng) {
  if (boost::static_pointer_cast<boost::uniform_smallint<uint8_t>>(self->distro)->operator()(*rng->rng)) Py_RETURN_TRUE;
  Py_RETURN_FALSE;
}

template <typename T> PyObject* call_int(PyBoostUniformObject* self, PyBoostMt19937Object* rng) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_int<T>>(self->distro)->operator()(*rng->rng));
}

template <typename T> PyObject* call_real(PyBoostUniformObject* self, PyBoostMt19937Object* rng) {
  return PyBlitzArrayCxx_FromCScalar(boost::static_pointer_cast<boost::uniform_real<T>>(self->distro)->operator()(*rng->rng));
}

/**
 * Calling a PyBoostUniformObject to generate a random number
 */
static PyObject* PyBoostUniform_Call(PyBoostUniformObject* self, PyObject *args, PyObject* kwds) {
BOB_TRY
  char** kwlist = call_doc.kwlist();

  PyBoostMt19937Object* rng;

  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PyBoostMt19937_Type, &rng)) return 0; ///< FAILURE

  switch(self->type_num) {
    case NPY_BOOL:
      return call_bool(self, rng);
      break;
    case NPY_UINT8:
      return call_int<uint8_t>(self, rng);
      break;
    case NPY_UINT16:
      return call_int<uint16_t>(self, rng);
      break;
    case NPY_UINT32:
      return call_int<uint32_t>(self, rng);
      break;
    case NPY_UINT64:
      return call_int<uint64_t>(self, rng);
      break;
    case NPY_INT8:
      return call_int<int8_t>(self, rng);
      break;
    case NPY_INT16:
      return call_int<int16_t>(self, rng);
      break;
    case NPY_INT32:
      return call_int<int32_t>(self, rng);
      break;
    case NPY_INT64:
      return call_int<int64_t>(self, rng);
      break;
    case NPY_FLOAT32:
      return call_real<float>(self, rng);
      break;
    case NPY_FLOAT64:
      return call_real<double>(self, rng);
      break;
    default:
      PyErr_Format(PyExc_NotImplementedError, "cannot call %s(T) with T having an unsupported numpy type number of %d", Py_TYPE(self)->tp_name, self->type_num);
  }

  return 0; ///< FAILURE
BOB_CATCH_MEMBER("call", 0)
}

static PyMethodDef PyBoostUniform_methods[] = {
    {
      call_doc.name(),
      (PyCFunction)PyBoostUniform_Call,
      METH_VARARGS|METH_KEYWORDS,
      call_doc.doc(),
    },
    {
      reset_doc.name(),
      (PyCFunction)PyBoostUniform_Reset,
      METH_NOARGS,
      reset_doc.doc(),
    },
    {0}  /* Sentinel */
};

extern PyObject* scalar_to_bytes(PyObject* s);

/**
 * String representation and print out
 */
static PyObject* PyBoostUniform_Repr(PyBoostUniformObject* self) {
BOB_TRY
  PyObject* smin = scalar_to_bytes(PyBoostUniform_GetMin(self));
  if (!smin) return 0;
  auto smin_ = make_safe(smin);
  PyObject* smax = scalar_to_bytes(PyBoostUniform_GetMax(self));
  if (!smax) return 0;
  auto smax_ = make_safe(smax);

  return
    PyString_FromFormat
      (
       "%s(dtype='%s', min=%s, max=%s)",
       Py_TYPE(self)->tp_name, PyBlitzArray_TypenumAsString(self->type_num),
       PyString_AS_STRING(smin), PyString_AS_STRING(smax)
      );
BOB_CATCH_MEMBER("repr", 0)
}

PyTypeObject PyBoostUniform_Type = {
  PyVarObject_HEAD_INIT(0,0)
  0
};

bool init_BoostUniform(PyObject* module)
{
  // initialize the type struct
  PyBoostUniform_Type.tp_name = uniform_doc.name();
  PyBoostUniform_Type.tp_basicsize = sizeof(PyBoostUniformObject);
  PyBoostUniform_Type.tp_flags = Py_TPFLAGS_DEFAULT;
  PyBoostUniform_Type.tp_doc = uniform_doc.doc();
  PyBoostUniform_Type.tp_str = reinterpret_cast<reprfunc>(PyBoostUniform_Repr);
  PyBoostUniform_Type.tp_repr = reinterpret_cast<reprfunc>(PyBoostUniform_Repr);

  // set the functions
  PyBoostUniform_Type.tp_new = PyBoostUniform_New;
  PyBoostUniform_Type.tp_init = reinterpret_cast<initproc>(PyBoostUniform_Init);
  PyBoostUniform_Type.tp_dealloc = reinterpret_cast<destructor>(PyBoostUniform_Delete);
  PyBoostUniform_Type.tp_methods = PyBoostUniform_methods;
  PyBoostUniform_Type.tp_getset = PyBoostUniform_getseters;
  PyBoostUniform_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBoostUniform_Call);

  // check that everything is fine
  if (PyType_Ready(&PyBoostUniform_Type) < 0) return false;

  // add the type to the module
  return PyModule_AddObject(module, "uniform", Py_BuildValue("O", &PyBoostUniform_Type)) >= 0;
}


#include "walker.h"
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include "ParseSpaceDelimited.h"

void cWalker::read(
        const std::string &fname)
    {
        std::ifstream inf(fname);
        if (!inf.is_open())
        {
            std::cout << "cannot open " << fname << "\n";
            exit(1);
        }
        myGrid.clear();
        myRowCount = 0;
        myColCount = -1;
        std::string line;
        while (std::getline(inf, line))
        {
            std::cout << line << "\n";
            auto token = ParseSpaceDelimited(line);
            if (!token.size())
                continue;
            switch (token[0][0])
            {
            case 'h':
            {
                if (myColCount == -1)
                    myColCount = token.size() - 1;
                else if (token.size() - 1 != myColCount)
                    throw std::runtime_error("Bad column count");
                std::vector<float> row;
                for (int k = 1; k < token.size(); k++)
                    row.push_back(atof(token[k].c_str()));
                myGrid.push_back(row);
            }
            break;
            case 's':
                if (token.size() != 3)
                    throw std::runtime_error("Bad start");
                myStartCol = atoi(token[1].c_str()) - 1;
                myStartRow = atoi(token[2].c_str()) - 1;
                break;
            case 'e':
                if (token.size() != 3)
                    throw std::runtime_error("Bad end");
                myEndCol = atoi(token[1].c_str()) - 1;
                myEndRow = atoi(token[2].c_str()) - 1;
                break;
            }
        }
        myRowCount = myGrid.size();
    }

    void cWalker::ConstructBoostGraph()
    {
        //std::cout << "ConstructBoostGraph " << myColCount << " " << myRowCount << "\n";
        for (int row = 0; row < myRowCount; row++)
        {
            for (int col = 0; col < myColCount; col++)
            {
                int n = findoradd(name(col, row));
                myGraph[n].myHeight = myGrid[row][col];
            }
        }

        for (int row = 0; row < myRowCount; row++)
            for (int col = 0; col < myColCount; col++)
            {
                int n = find(name(col, row));
                if (col > 0)
                {
                    int left = find(name(col - 1, row));
                    float delta = myGrid[row][col] - myGrid[col - 1][row];
                    AddLink(n, left, 1 + delta * delta);
                }
                if (col < myColCount - 1)
                {
                    int right = find(name(col + 1, row));
                    float delta = myGrid[row][col] - myGrid[col + 1][row];
                    AddLink(n, right, 1 + delta * delta);
                }
                if (row > 0)
                {
                    int up = find(name(col, row - 1));
                    float delta = myGrid[row][col] - myGrid[col][row - 1];
                    AddLink(n, up, 1 + delta * delta);
                }
                if (row < myRowCount - 1)
                {
                    int up = find(name(col, row + 1));
                    float delta = myGrid[row][col] - myGrid[col][row + 1];
                    AddLink(n, up, 1 + delta * delta);
                }
            }
    }

    void cWalker::Path()
    {
    // run dijkstra algorithm
    int startNode = find(name(myStartCol, myStartRow));
    std::vector<int> p(num_vertices(myGraph));
    std::vector<int> vDist(num_vertices(myGraph));
    boost::dijkstra_shortest_paths(
        myGraph,
        startNode,
        weight_map(get(&cEdge::myCost, myGraph))
            .predecessor_map(boost::make_iterator_property_map(
                p.begin(), get(boost::vertex_index, myGraph)))
            .distance_map(boost::make_iterator_property_map(
                vDist.begin(), get(boost::vertex_index, myGraph))));

        // pick out path, starting at goal and finishing at start
    int goalnode = find(name(myEndCol,myEndRow));
    myPath.push_back(goalnode);
    int prev = goalnode;
    while (1)
    {
        //std::cout << prev << " " << p[prev] << ", ";
        int next = p[prev];
        myPath.push_back(next);
        if (next == startNode)
            break;
        prev = next;
    }

    // reverse so path goes from start to goal
    std::reverse(myPath.begin(), myPath.end());
    }

        std::string cWalker::linksText()
    {
        std::stringstream ss;
        graph_traits<graph_t>::edge_iterator ei, ei_end;
        for (tie(ei, ei_end) = edges(myGraph); ei != ei_end; ++ei)
        {
            ss << "("
               << myGraph[source(*ei, myGraph)].myName << ","
               << myGraph[target(*ei, myGraph)].myName << ","
               << myGraph[*ei].myCost
               << ") ";
        }
        ss << "\n";
        return ss.str();
    }

        std::string cWalker::pathText()
{
    std::stringstream ss;
    for (auto n : myPath)
        ss << myGraph[n].myName << " -> ";
    ss << "\n";
    return ss.str();
}

    int cWalker::findoradd(const std::string &name)
    {
        int n = find(name);
        if (n < 0)
            n = add_vertex(name, myGraph);
        return n;
    }

    int cWalker::find(const std::string &name)
    {
        for (int n = 0; n < num_vertices(myGraph); n++)
        {
            if (myGraph[n].myName == name)
            {
                return n;
            }
        }
        return -1;
    }
    void cWalker::AddLink(int n, int m, float cost)
    {
        myGraph[add_edge(n, m, myGraph).first].myCost = cost;
        myGraph[add_edge(m, n, myGraph).first].myCost = cost;
    }
    std::string cWalker::name(int col, int row)
    {
        return std::to_string(col+1) + "_" + std::to_string(row+1);
    }


#ifndef __AST_BITVEC_HPP_DEFINED__
#define __AST_BITVEC_HPP_DEFINED__

#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/python.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/lexical_cast.hpp>

#include <z3++.h>
#include <assert.h>
#include <type.hpp>

#include <ast.hpp>

namespace ila
{
    class Abstraction;
    class FuncReduction;

    // ---------------------------------------------------------------------- //
    // Bitvector expressions are derived from this class.
    class BitvectorExpr : public Node {
    public:
        // constructor.
        BitvectorExpr(int width);
        // constructor for ChoiceExpr.
        BitvectorExpr(NodeType t);
        // destructor.
        virtual ~BitvectorExpr();
    };

    // ---------------------------------------------------------------------- //
    // Bitvector variables.
    class BitvectorVar : public BitvectorExpr {
    public:
        // constructor.
        BitvectorVar(const std::string& n, int width) ;
        // destructor.
        virtual ~BitvectorVar();
        // clone.
        virtual Node* clone() const;
        // equality method.
        virtual bool equal(const Node* that) const;
        // stream output.
        virtual std::ostream& write(std::ostream& out) const;
    };

    // ---------------------------------------------------------------------- //
    // Bitvector constants.
    class BitvectorConst : public BitvectorExpr {
    protected:
        mp_int_t value;
    public:
        // constructor with longs.
        BitvectorConst(const mp_int_t& v, int width);
        // constructor with ints.
        BitvectorConst(unsigned int v, int width);
        // copy constructor.
        BitvectorConst(const BitvectorConst& other);
        // destructor.
        virtual ~BitvectorConst();
        // clone
        virtual Node* clone() const;
        // equality method.
        virtual bool equal(const Node* that) const;
        // get value.
        const mp_int_t& val() const { return value; }
        // return value.
        virtual py::object getValue() const;
        // stream output.
        virtual std::ostream& write(std::ostream& out) const;
        // get the value as a string.
        std::string vstr() const {
            return boost::lexical_cast<std::string>(value);
        }
    };

    // ---------------------------------------------------------------------- //
    // Bitvector operators.
    class BitvectorOp : public BitvectorExpr {
    public:
        // Number of operands.
        enum Arity { UNARY, BINARY, TERNARY, NARY } arity;

        // What is the operation?
        enum Op {
            // invalid
            INVALID,
            // unary
            NEGATE, COMPLEMENT,
            LROTATE, RROTATE, Z_EXT, S_EXT, EXTRACT,
            // binary.
            ADD, SUB, AND, OR, XOR, XNOR, NAND, NOR,
            SDIV, UDIV, SREM, UREM, SMOD, SHL, LSHR, ASHR,
            MUL, CONCAT, GET_BIT, READMEM, READMEMBLOCK,
            // ternary
            IF, APPLY_FUNC
        } op;

        static const std::string operatorNames[];

    private:
        // the operands themselves.
        nptr_vec_t args;
        std::vector< int > params;

        // Don't forget to update these helper functions below.
        static bool isUnary(Op op) {
            return op >= NEGATE && op <= EXTRACT;
        }
        static bool isBinary(Op op) {
            return op >= ADD && op <= READMEMBLOCK;
        }
        static bool isTernary(Op op) {
            return op >= IF && op <= IF;
        }
        static bool isNary(Op op) {
            return op >= APPLY_FUNC && op <= APPLY_FUNC;
        }
        // helper functions to determine result and argument types.
        static int getUnaryResultWidth(
            Op op,
            const nptr_t& n);
        static int getBinaryResultWidth(
            Op op,
            const nptr_t& n1,
            const nptr_t& n2);
        static int getBinaryResultWidth(
            Op op,
            const nptr_t& n1,
            const nptr_t& n2,
            int param);
        static int getBinaryResultWidth(
            Op op,
            const nptr_t& n1,
            int param);
        static int getNaryResultWidth(
            Op op, nptr_vec_t& args);
        static int getNaryResultWidth(
            Op op, nptr_vec_t& args,
            std::vector< int >& params);

        static bool checkUnaryOpWidth(
            Op op, const nptr_t& n, int width);
        static int checkBinaryOpWidth(
            Op op,
            const nptr_t& n1,
            const nptr_t& n2,
            int width);
        static int checkBinaryOpWidth(
            Op op,
            const nptr_t& n1,
            const nptr_t& n2,
            int param,
            int width);
        static int checkBinaryOpWidth(
            Op op,
            const nptr_t& n1,
            int param,
            int width);
        static int checkNaryOpWidth(
            Op op, nptr_vec_t& args,
            int width);
        static int checkNaryOpWidth(
            Op op,
            nptr_vec_t& args,
            std::vector< int >& params,
            int width);

    public:
        // constructors.
        // Unary op
        BitvectorOp(Op op,
                    const nptr_t& n1);
        BitvectorOp(Op op,
                    const nptr_t& n1,
                    int param);
        BitvectorOp(Op op,
                    const nptr_t& n1,
                    int p1, int p2);
        // Binary op
        BitvectorOp(Op op,
                    const nptr_t& n1,
                    const nptr_t& n2);
        // Binary op with params (read-block)
        BitvectorOp(Op op,
                    const nptr_t& n1,
                    const nptr_t& n2,
                    int blocks, endianness_t e);
        // Ternary op
        BitvectorOp(Op op,
                    nptr_vec_t& args_);
        // copy-constructor with a fresh set of args.
        BitvectorOp(const BitvectorOp* other,
                nptr_vec_t& args_);
        // destructors.
        virtual ~BitvectorOp();

        // clone.
        virtual Node* clone() const;

        // equality method.
        virtual bool equal(const Node* that) const;

        // stream output.
        virtual std::ostream& write(std::ostream& out) const;

        // number of operands.
        virtual unsigned nArgs() const;

        // operand i.
        virtual nptr_t arg(unsigned i) const;

        Op getOp() const { return op; }

        // number of params
        unsigned nParams() const;

        // the ith param.
        int param(unsigned i) const;

        friend class FuncReduction;
    };
}
#endif


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_EXPONENT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_EXPONENT_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-ieee
    Function object implementing exponent capabilities

    Returns the exponent of the floating input.

    @par Semantic:

    @code
    as_integer_t<T> r = exponent(x);
    @endcode

    is similar to:

    @code
    as_integer_t<T> r = ilogb(x);
    @endcode

    @par Note:

     The sign \f$ \pm \f$ , exponent e and mantissa m of a floating point entry x are related by
    x = \f$\pm m\times 2^e\f$, with m between one (included) and two (excluded).

    For integral type inputs exponent is always 0 and mantissa reduces to identity.

    @see mantissa,  frexp, ldexp

  **/
  const boost::dispatch::functor<tag::exponent_> exponent = {};
} }
#endif

#include <boost/simd/function/scalar/exponent.hpp>
#include <boost/simd/function/simd/exponent.hpp>

#endif


/*
 * Copyright 2011 Mario Mulansky
 * Copyright 2012 Karsten Ahnert
 *
 * 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/array.hpp>

//#include <boost/numeric/odeint/stepper/explicit_generic_rk.hpp>
#include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
#include <boost/numeric/odeint/algebra/array_algebra.hpp>

#include "rk_performance_test_case.hpp"

#include "lorenz.hpp"

using namespace boost::numeric::odeint;

typedef boost::array< double , 3 > state_type;

/*
typedef explicit_generic_rk< 4 , 4 , state_type , double , state_type , double , array_algebra > rk4_type;

typedef rk4_type::coef_a_type coef_a_type;
typedef rk4_type::coef_b_type coef_b_type;
typedef rk4_type::coef_c_type coef_c_type;

const boost::array< double , 1 > a1 = {{ 0.5 }};
const boost::array< double , 2 > a2 = {{ 0.0 , 0.5 }};
const boost::array< double , 3 > a3 = {{ 0.0 , 0.0 , 1.0 }};

const coef_a_type a = fusion::make_vector( a1 , a2 , a3 );
const coef_b_type b = {{ 1.0/6 , 1.0/3 , 1.0/3 , 1.0/6 }};
const coef_c_type c = {{ 0.0 , 0.5 , 0.5 , 1.0 }};
*/

typedef runge_kutta4< state_type , double , state_type , double , array_algebra > rk4_type;

class rk4_wrapper
{

public:

    rk4_wrapper()
    //        : m_stepper( a , b , c )
    {}

    void reset_init_cond()
    {
        m_x[0] = 10.0 * rand() / RAND_MAX;
        m_x[1] = 10.0 * rand() / RAND_MAX;
        m_x[2] = 10.0 * rand() / RAND_MAX;
        m_t = 0.0;
    }

    inline void do_step( const double dt )
    {
        m_stepper.do_step( lorenz(), m_x , m_t , dt );
    }

    double state( const size_t i ) const
    { return m_x[i]; }

private:
    state_type m_x;
    double m_t;
    rk4_type m_stepper;
};

int main()
{
    srand( 12312354 );

    rk4_wrapper stepper;

    run( stepper );
}


// Copyright (c) 2022 hs293go
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#include <pybind11/attr.h>
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>

#include <Eigen/Dense>
#include <cmath>

#define IMPORT_FROM(m, fn) auto fn = m.attr(#fn);

namespace py = pybind11;
using namespace pybind11::literals;
int main() {
  const Eigen::VectorXd xv_data = Eigen::VectorXd::LinSpaced(10000, -4, 4);
  const Eigen::VectorXd yv_data =
      xv_data.unaryExpr([](double it) { return tgamma(it); });

  py::scoped_interpreter interp{};
  auto sns = py::module::import("seaborn");
  IMPORT_FROM(sns, set_style);
  set_style("darkgrid");

  auto plt = py::module::import("matplotlib.pyplot");
  IMPORT_FROM(plt, plot);
  IMPORT_FROM(plt, show);
  IMPORT_FROM(plt, ylim);
  IMPORT_FROM(plt, legend);
  IMPORT_FROM(plt, xlabel);
  IMPORT_FROM(plt, ylabel);
  IMPORT_FROM(plt, title);
  IMPORT_FROM(plt, savefig);

  py::array xv(xv_data.size(), xv_data.data());
  py::array yv(yv_data.size(), yv_data.data());

  plot(xv, yv, "--", "linewidth"_a = 3,
       "color"_a = py::make_tuple(0, 0.4, 0.8, 0.6),
       "label"_a = "Function Value");
  ylim(py::make_tuple(-10, 10));
  xlabel("x");
  ylabel(R"($f(x) = \gamma(x)$)");
  title(R"(Gamma Function: $\gamma(z) = \int_0^\infty x^{z-1} e^{-x} dx$)",
        "fontsize"_a = 18);
  savefig("res/detailed.png");
  show();
}


#ifndef NNQS_OPTIMIZERS_RMSPROP_HPP
#define NNQS_OPTIMIZERS_RMSPROP_HPP
#include <Eigen/Dense>
#include <type_traits>

#include <complex>
#include "Utilities/type_traits.hpp"
#include "Optimizers/Optimizer.hpp"

namespace yannq
{
template <typename T>
class RMSProp
	: public OptimizerGeometry<T>
{
public:
	using typename OptimizerGeometry<T>::RealT;
	using typename OptimizerGeometry<T>::Vector;
	using typename OptimizerGeometry<T>::RealVector;

private:
	const double alpha_;
	const double beta_;
	const double eps_;

	int t_;
	RealVector v_;

public:
	static constexpr double DEFAULT_PARAMS[] = {0.05, 0.9, 1e-8};

	static nlohmann::json defaultParams()
	{
		return nlohmann::json
		{
			{"name", "RMSProp"},
			{"alhpa", DEFAULT_PARAMS[0]},
			{"beta", DEFAULT_PARAMS[1]},
			{"eps", DEFAULT_PARAMS[2]},
		};
	}

	nlohmann::json desc() const override
	{
		return nlohmann::json
		{
			{"name", "RMSProp"},
			{"alhpa", alpha_},
			{"beta", beta_},
			{"eps", eps_},
		};
	}

	RMSProp(const nlohmann::json& params)
		: alpha_(params.value("alpha", DEFAULT_PARAMS[0])),
			beta_(params.value("beta", DEFAULT_PARAMS[1])),
			eps_(params.value("eps", DEFAULT_PARAMS[2])),
			 t_{}
	{
	}

	//use oloc to estimate local geometry
	Vector getUpdate(const Vector& grad, const Vector& oloc) override
	{
		auto sqr = [](T x) -> RealT {
			return std::norm(x);
		};

		if(t_ == 0)
		{
			v_ = RealVector::Zero(grad.rows());
		}
		++t_;

		RealVector g = oloc.unaryExpr(sqr);
		v_ *= beta_;
		v_ += (1-beta_)*g;

		RealVector denom = v_.unaryExpr([eps = this->eps_](RealT x){ return sqrt(x)+eps; });
		return -alpha_*grad.cwiseQuotient(denom);
	}
};

template<typename T>
constexpr double RMSProp<T>::DEFAULT_PARAMS[];

} //namespace yannq
#endif//NNQS_OPTIMIZERS_RMSPROP_HPP