#include<stdio.h>
#include<iostream>
#include<math.h>
#include <unistd.h>
#include <armadillo>
#include <fstream>
#include<map>
#include<string>
#include<iomanip>
#include<limits>
#include<string>
#include<regex>
#include<sstream>
#include<tuple>
#include <chrono>

#include "utils.hpp"
#include "core.hpp"
#include "point.hpp"

#define ARMA_DONT_PRINT_ERRORS
using namespace arma;

typedef std::map<char, float> char_float_map;
typedef std::map<char, double> char_double_map;
typedef std::vector<std::string> vec_str;
typedef std::vector<double> vec_doub;
typedef std::vector<long double> vec_ldoub;

bool debug_mode = true;

void meshfree_solver(char* file_name, int num_iters);
void run_code(CodiPoint* globaldata, CodiConfig configData, codi::RealReverse* res_old, int numPoints, codi::RealReverse tempdq[][2][4], int max_iters);
void test_code(CodiPoint* globaldata, CodiConfig configData, codi::RealReverse* res_old, int numPoints, int max_iters);

int main(int argc, char **argv)
{
	printf("\nMeshfree AD\n");

	/* initialize random seed*/
	srand (time(NULL));
	arma_rng::set_seed_random();

	meshfree_solver(argv[1], std::stoi(argv[2])); //Warning: Casting from char to int loses precision
	//Gotta see if maintaining a global array id efficient or if passing around by reference is efficient
	//For all we know, maintaining a global data structure instead of passing it around might be more efficient

	cout<<"\n Max Iters: "<<std::stoi(argv[2])<<endl;
}

void meshfree_solver(char* file_name, int max_iters)
{

	CodiConfig configData = CodiConfig();
	configData.setConfig();
	if (debug_mode)
		configData.printConfig();

	std::string format = configData.format.type;
	if (debug_mode)
		cout<<"\nFormat: "<<format<<endl;

	cout<<"\nFilename: "<<file_name<<endl;
	//cout<<"hi"<<endl;

	int numPoints = 0;
	std::fstream datafile(file_name, ios::in);

	if(datafile.is_open())
	{
		std::string temp;
		getline(datafile, temp);
		std::stringstream num(temp);
		num >> numPoints;
	}

	std::string temp;
	std::string* new_file = new std::string[numPoints];
	if(datafile.is_open())
	{
		for(int i=0; i<numPoints; i++)
		{
			getline(datafile, new_file[i]);
		}
	}
	datafile.close();

	//cout<<new_file[1][0]<<endl;
	cout<<"\nNo. of points: "<<numPoints<<endl;

	std::regex ws_re("\\s+");
	std::vector<vec_str> result;
	for(int i=0; i<numPoints; i++)  // This might include the first line as well so, you need to store that separately or just throw the 1st line away (the one that contains the file length)
									// There are 48738 lines apart from that
	{
    	std::vector<std::string> temp{std::sregex_token_iterator(new_file[i].begin(), new_file[i].end(), ws_re, -1), {}};
    	result.push_back(temp);
    }
	// Free up the space taken by new_file
	delete[] new_file;

#if 0

    for(int j=0; j<numPoints; j++)
    {
		for (int i=0; i<result[j].size(); i++)
			cout<<"Result: "<<j<<" " <<i<<" "<<result[j][i]<<endl;

	}

#endif

	std::vector<vec_doub> result_doub;
	for(int j=0; j<numPoints; j++)
    {
		vec_doub temp;
		for (int i=0; i<result[j].size(); i++)
			temp.push_back(std::stod(result[j][i]));
		result_doub.push_back(temp);

	}

	std::vector<vec_str>().swap(result); // Free up the space taken up by result

#if 0

	checkFileRead(result_doub, numPoints);

	for(int j=0; j<20; j++)
    {
		for (int i=0; i<result_doub[j].size(); i++)
			cout<<std::fixed<<std::setprecision(20)<<"Result Doub: "<<j<<" " <<i<<" "<<result_doub[j][i]<<endl;

	}
#endif

	CodiPoint* globaldata = new CodiPoint[numPoints];
	codi::RealReverse res_old[1] = {0.0};

	codi::RealReverse defprimal[4];
	getInitialPrimitive(configData, defprimal);
	//printPrimal(defprimal);

	cout<<"\n-----Start Read-----\n"; // readFile function in Julia

	//cout<<result_doub[0][0]<<endl;

/* Read File Legacy */

#if 0
	for(int idx=0; idx<numPoints; idx++)
	{

		int connectivity[20] = {0};
		for(int iter=8; iter<result_doub[idx].size(); iter++)
		{
			connectivity[iter-8] = result_doub[idx][iter];
		}

		// Assign values to each of the elements of global data
		// Make sure the types specifications are adhered to

		globaldata[idx].localID = idx;
		globaldata[idx].x = result_doub[idx][0];
		globaldata[idx].y = result_doub[idx][1];
		globaldata[idx].left = (int)result_doub[idx][2];
		globaldata[idx].right = (int)result_doub[idx][3];
		globaldata[idx].flag_1 = (int)result_doub[idx][4];
		globaldata[idx].flag_2 = (int)result_doub[idx][5];
		globaldata[idx].short_distance = result_doub[idx][6];
		globaldata[idx].nbhs = (int)result_doub[idx][7];

		for(int i=0; i<20; i++)
		{
			globaldata[idx].conn[i] = connectivity[i];
			// (connectivity[i]!=0) cout<<"\n non-zero connectivity \n";
		}

		globaldata[idx].nx = 0.0;
		globaldata[idx].ny = 0.0;

		for(int i=0; i<4; i++)
		{
			globaldata[idx].prim[i] = defprimal[i];
			globaldata[idx].flux_res[i] = 0.0;
			globaldata[idx].q[i] = 0.0;
			globaldata[idx].dq1[i] = 0.0;
			globaldata[idx].dq2[i] = 0.0;
			globaldata[idx].max_q[i] = 0.0;
			globaldata[idx].min_q[i] = 0.0;
			globaldata[idx].prim_old[i] = 0.0;
		}

		globaldata[idx].xpos_nbhs = 0;
		globaldata[idx].xneg_nbhs = 0;
		globaldata[idx].ypos_nbhs = 0;
		globaldata[idx].yneg_nbhs = 0;

		globaldata[idx].entropy = 0.0;
		globaldata[idx].delta = 0.0;

		for(int i=0; i<20; i++)
		{
			globaldata[idx].xpos_conn[i] = 0;
			globaldata[idx].xneg_conn[i] = 0;
			globaldata[idx].ypos_conn[i] = 0;
			globaldata[idx].yneg_conn[i] = 0;
		}

	}

	cout<<"\n-----End Read-----\n";

#endif

/* Read File Quadtree */

	for(int idx=0; idx<numPoints; idx++)
	{

		int connectivity[20] = {0};
		for(int iter=11; iter<result_doub[idx].size(); iter++)
		{
			connectivity[iter-11] = result_doub[idx][iter];
		}

		// Assign values to each of the elements of global data
		// Make sure the types specifications are adhered to

		globaldata[idx].localID = idx;
		globaldata[idx].x = result_doub[idx][0];
		globaldata[idx].y = result_doub[idx][1];
		globaldata[idx].left = (int)result_doub[idx][2];
		globaldata[idx].right = (int)result_doub[idx][3];
		globaldata[idx].flag_1 = (int)result_doub[idx][4];
		globaldata[idx].flag_2 = (int)result_doub[idx][5];
		globaldata[idx].short_distance = result_doub[idx][9];
		globaldata[idx].nbhs = (int)result_doub[idx][10];

		for(int i=0; i<20; i++)
		{
			globaldata[idx].conn[i] = connectivity[i];
			// (connectivity[i]!=0) cout<<"\n non-zero connectivity \n";
		}

		globaldata[idx].nx = result_doub[idx][6];
		globaldata[idx].ny = result_doub[idx][7];

		for(int i=0; i<4; i++)
		{
			globaldata[idx].prim[i] = defprimal[i];
			globaldata[idx].flux_res[i] = 0.0;
			globaldata[idx].q[i] = 0.0;
			globaldata[idx].dq1[i] = 0.0;
			globaldata[idx].dq2[i] = 0.0;
			globaldata[idx].max_q[i] = 0.0;
			globaldata[idx].min_q[i] = 0.0;
			globaldata[idx].prim_old[i] = 0.0;
		}

		globaldata[idx].xpos_nbhs = 0;
		globaldata[idx].xneg_nbhs = 0;
		globaldata[idx].ypos_nbhs = 0;
		globaldata[idx].yneg_nbhs = 0;

		globaldata[idx].entropy = 0.0;
		globaldata[idx].delta = 0.0;

		for(int i=0; i<20; i++)
		{
			globaldata[idx].xpos_conn[i] = 0;
			globaldata[idx].xneg_conn[i] = 0;
			globaldata[idx].ypos_conn[i] = 0;
			globaldata[idx].yneg_conn[i] = 0;
		}

	}

	cout<<"\n-----End Read-----\n";
	if(configData.core.restart != 1) std::vector<vec_doub>().swap(result_doub);

	if(configData.core.restart == 1)
    {
        char* file_name = "/home/hari/Work/Meshfree_cpp/restart.dat";
        std::fstream datafile(file_name, ios::in);
        std::string temp;
        std::regex ws_re("\\s+");

        int numPoints, max_iters;
        double residue;

        if(datafile.is_open())
        {

            getline(datafile, temp);
            //cout<<temp;
            std::vector<std::string> temp_vec{std::sregex_token_iterator(temp.begin(), temp.end(), ws_re, -1), {}};
            //cout<<"\ntemp_vec: "<<temp_vec[0]<<temp_vec[1]<<"and"<<temp_vec[2]<<"and"<<temp_vec[3]<<endl;
            numPoints = (int) std::stod(temp_vec[1]);
        }

        std::string new_file[numPoints];
        if(datafile.is_open())
        {
            for(int i=0; i<numPoints; i++)
            {
                getline(datafile, new_file[i]);
            }
        }
        datafile.close();

        std::vector<vec_str> result;
        for(int i=0; i<numPoints; i++)  // This might include the first line as well so, you need to store that separately or just throw the 1st line away (the one that contains the file length)
                                        // There are 48738 lines apart from that
        {
            std::vector<std::string> temp{std::sregex_token_iterator(new_file[i].begin(), new_file[i].end(), ws_re, -1), {}};
            result.push_back(temp);
        }

        std::vector<vec_doub> result_doub;

        for(int j=0; j<numPoints; j++)
        {
            vec_doub temp;
            for (int i=1; i<result[j].size(); i++)
                temp.push_back(std::stod(result[j][i]));
            result_doub.push_back(temp);
        }

        //checkFileRead(result_doub, numPoints);
        // for(int j=0; j<20; j++)
        // {
        //     for (int i=0; i<result_doub[j].size(); i++)
        //         cout<<std::fixed<<std::setprecision(20)<<"Result Doub: "<<j<<" " <<i<<" "<<result_doub[j][i]<<endl;

        // }

        for(int i=0; i<numPoints; i++)
        {
        	for(int j=0; j<4; j++)
        		globaldata[i].prim[j] = result_doub[i][5+j];
        }

        //cout<<"\n Check prim [0] pt"<<std::fixed<<std::setprecision(17)<<globaldata[0].prim[0]<<endl;
    }

	// Interior, Out and Wall were defined as Int64 in Julia, so am defining them as long long

	long long interior = configData.point_config.interior;
	long long wall = configData.point_config.wall;
	long long outer = configData.point_config.outer;

	cout<<"\n-----Computing Normals-----\n";
	for(int idx=0; idx<numPoints; idx++)
		placeNormals(globaldata, idx, configData, interior, wall, outer);

	cout<<"\n-----Start Connectivity Generation-----\n";
	for(int idx=0; idx<numPoints; idx++)
		calculateConnectivity(globaldata, idx);
	cout<<"\n-----Connectivity Generation Done-----\n";

	cout<<"\n"<<max_iters+1<<endl;

	test_code(globaldata, configData, res_old, numPoints, max_iters);

	// Open the timer and print the timer that benchmarks all of these

	cout<<"\n--------Done--------\n"<<endl;

}

void run_code(CodiPoint* globaldata, CodiConfig configData, codi::RealReverse* res_old, int numPoints, CodiTempqDers* tempdq, int max_iters)
{
	auto begin = std::chrono::high_resolution_clock::now();
	codi::RealReverse::TapeType& tape = codi::RealReverse::getGlobalTape();
  	tape.setActive();
	  tape.registerInput(globaldata[10].x);
	for (int i=0; i<max_iters; i++)
	{
		//debug_main_store(main_store);
		fpi_solver_codi(i, globaldata, configData, res_old, numPoints, tempdq);
	}
	tape.registerOutput(globaldata[10].prim[2]);
	tape.setPassive();
	globaldata[10].prim[2].setGradient(1.0);
  	tape.evaluate();
	cout<<"\n Required Sensitivity is: "<<globaldata[10].x.getGradient()<<endl;

	auto end = std::chrono::high_resolution_clock::now();
	auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
	printf("\nTime measured: %.5f seconds.\n", elapsed.count() * 1e-9);
}

void test_code(CodiPoint* globaldata, CodiConfig configData, codi::RealReverse* res_old, int numPoints, int max_iters)
{
	cout<<"\nStarting warmup function \n";
	res_old[0] = 0.0;

	cout<<"\nStarting main function \n";
	CodiTempqDers* tempdq = new CodiTempqDers[numPoints];
	//cout<<"\n no seg fault till here \n";

	for (int i=0; i<numPoints; i++)
		tempdq[i].setTempdq();

	run_code(globaldata, configData, res_old, numPoints, tempdq, max_iters);
}


/**
 * @file tests/main_tests/hmm_loglik_test.cpp
 * @author Daivik Nema
 *
 * Test mlpackMain() of hmm_loglik_main.cpp
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#include <string>

#define BINDING_TYPE BINDING_TYPE_TEST
static const std::string testName = "HMMLoglik";

#include <mlpack/core.hpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "test_helper.hpp"
#include <mlpack/methods/hmm/hmm_model.hpp>
#include <mlpack/methods/hmm/hmm.hpp>
#include <mlpack/methods/hmm/hmm_loglik_main.cpp>

#include <boost/test/unit_test.hpp>
#include "../test_tools.hpp"

#include "hmm_test_utils.hpp"

using namespace mlpack;

struct HMMLoglikTestFixture
{
 public:
  HMMLoglikTestFixture()
  {
    // Cache in the options for this program.
    IO::RestoreSettings(testName);
  }

  ~HMMLoglikTestFixture()
  {
    // Clear the settings.
    bindings::tests::CleanMemory();
    IO::ClearSettings();
  }
};

BOOST_FIXTURE_TEST_SUITE(HMMLoglikMainTest, HMMLoglikTestFixture);

BOOST_AUTO_TEST_CASE(HMMLoglikOutputNegativeTest)
{
  // Load data to train a discrete HMM model with.
  arma::mat inp;
  data::Load("obs1.csv", inp);
  std::vector<arma::mat> trainSeq = {inp};

  // Initialize and train an HMM model.
  HMMModel* h = new HMMModel(DiscreteHMM);
  h->PerformAction<InitHMMModel, std::vector<arma::mat>>(&trainSeq);
  h->PerformAction<TrainHMMModel, std::vector<arma::mat>>(&trainSeq);

  // Set the params for the hmm_loglik invocation
  SetInputParam("input_model", h);
  SetInputParam("input", inp);

  mlpackMain();

  double loglik = IO::GetParam<double>("log_likelihood");

  // Since the log of a probability <= 0 ...
  BOOST_REQUIRE(loglik <= 0);
}

BOOST_AUTO_TEST_SUITE_END();


//Templated Spreadsort-based implementation of integer_sort

//          Copyright Steven J. Ross 2001 - 2014.
// 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)

// See http://www.boost.org/libs/sort/ for library home page.

/*
Some improvements suggested by:
Phil Endecott and Frank Gennari

Doxygen comments by Paul A. Bristow Jan 2015

*/

#ifndef BOOST_INTEGER_SORT_HPP
#define BOOST_INTEGER_SORT_HPP
#include <algorithm>
#include <vector>
#include <cstring>
#include <limits>
#include <boost/static_assert.hpp>
#include <boost/sort/spreadsort/detail/constants.hpp>
#include <boost/sort/spreadsort/detail/integer_sort.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>

namespace boost {
namespace sort {
namespace spreadsort {
  //Top-level sorting call for integers.

/*! \brief Integer sort algorithm using random access iterators.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
    windows_integer_sort
   \n
    osx_integer_sort

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.

   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is mutable.
   \pre @c RandomAccessIter @c value_type is LessThanComparable
   \pre @c RandomAccessIter @c value_type supports the @c operator>>,
   which returns an integer-type right-shifted a specified number of bits.
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors, or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).

*/
  template <class RandomAccessIter>
  inline void integer_sort(RandomAccessIter first, RandomAccessIter last)
  {
    // Don't sort if it's too small to optimize.
    if (last - first < detail::min_sort_size)
      boost::sort::pdqsort(first, last);
    else
      detail::integer_sort(first, last, *first >> 0);
  }

/*! \brief Integer sort algorithm using range.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
    windows_integer_sort
   \n
    osx_integer_sort

   \param[in] range Range [first, last) for sorting.

   \pre [@c first, @c last) is a valid range.
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors, or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).

*/
template <class Range>
inline void integer_sort(Range& range)
{
  integer_sort(boost::begin(range), boost::end(range));
}

/*! \brief Integer sort algorithm using random access iterators with both right-shift and user-defined comparison operator.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
    windows_integer_sort
   \n
    osx_integer_sort

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \param[in] shift Functor that returns the result of shifting the value_type right a specified number of bits.
   \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.

   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is mutable.
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors,
   or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
*/
  template <class RandomAccessIter, class Right_shift, class Compare>
  inline void integer_sort(RandomAccessIter first, RandomAccessIter last,
                           Right_shift shift, Compare comp) {
    if (last - first < detail::min_sort_size)
      boost::sort::pdqsort(first, last, comp);
    else
      detail::integer_sort(first, last, shift(*first, 0), shift, comp);
  }

/*! \brief Integer sort algorithm using range with both right-shift and user-defined comparison operator.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
    windows_integer_sort
   \n
    osx_integer_sort

   \param[in] range Range [first, last) for sorting.
   \param[in] shift Functor that returns the result of shifting the value_type right a specified number of bits.
   \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.

   \pre [@c first, @c last) is a valid range.
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors,
   or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).
*/
template <class Range, class Right_shift, class Compare>
inline void integer_sort(Range& range, Right_shift shift, Compare comp)
{
  integer_sort(boost::begin(range), boost::end(range), shift, comp);
}

/*! \brief Integer sort algorithm using random access iterators with just right-shift functor.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n

\par Performance:
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
  *  windows_integer_sort\n
  *  osx_integer_sort

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \param[in] shift A functor that returns the result of shifting the value_type right a specified number of bits.

   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is mutable.
   \pre @c RandomAccessIter @c value_type is LessThanComparable
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors,
   or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).

*/
  template <class RandomAccessIter, class Right_shift>
  inline void integer_sort(RandomAccessIter first, RandomAccessIter last,
                           Right_shift shift) {
    if (last - first < detail::min_sort_size)
      boost::sort::pdqsort(first, last);
    else
      detail::integer_sort(first, last, shift(*first, 0), shift);
  }

/*! \brief Integer sort algorithm using range with just right-shift functor.
  (All variants fall back to @c boost::sort::pdqsort if the data size is too small, < @c detail::min_sort_size).

  \details @c integer_sort is a fast templated in-place hybrid radix/comparison algorithm,
which in testing tends to be roughly 50% to 2X faster than @c std::sort for large tests (>=100kB).\n

\par Performance:
Worst-case performance is   O(N * (lg(range)/s + s)) ,
so @c integer_sort is asymptotically faster
than pure comparison-based algorithms. @c s is @c max_splits, which defaults to 11,
so its worst-case with default settings for 32-bit integers is
 O(N * ((32/11)  slow radix-based iterations fast comparison-based iterations).\n\n
Some performance plots of runtime vs. n and log(range) are provided:\n
  *  windows_integer_sort\n
  *  osx_integer_sort

   \param[in] range Range [first, last) for sorting.
   \param[in] shift A functor that returns the result of shifting the value_type right a specified number of bits.

   \pre [@c first, @c last) is a valid range.
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps (or moves),
   the right shift, subtraction of right-shifted elements, functors,
   or any operations on iterators throw.

   \warning Throwing an exception may cause data loss. This will also throw if a small vector resize throws, in which case there will be no data loss.
   \warning Invalid arguments cause undefined behaviour.
   \note @c spreadsort function provides a wrapper that calls the fastest sorting algorithm available for a data type,
   enabling faster generic-programming.

   \remark The lesser of  O(N*log(N))  comparisons and  O(N*log(K/S + S)) operations worst-case, where:
   \remark  *  N is @c last - @c first,
   \remark  *  K is the log of the range in bits (32 for 32-bit integers using their full range),
   \remark  *  S is a constant called max_splits, defaulting to 11 (except for strings where it is the log of the character size).

*/
template <class Range, class Right_shift>
inline void integer_sort(Range& range, Right_shift shift)
{
  integer_sort(boost::begin(range), boost::end(range), shift);
}
}
}
}

#endif


//---------------------------------------------------------------------------//
// Copyright (c) 2018-2019 Nil Foundation AG
// Copyright (c) 2018-2019 Mikhail Komarov <nemo@nilfoundation.org>
//
// 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
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_CHIA_POLICY_HPP
#define CRYPTO3_CHIA_POLICY_HPP

#if defined(CRYPTO3_VDF_FLINT)

#include <flint/fmpz.h>

#elif defined(CRYPTO3_VDF_GMP)

#include <gmpxx.h>

#elif defined(CRYPTO3_VDF_MPIR)

#include <mpirxx.h>

#elif defined(CRYPTO3_VDF_BOOST)

#include <boost/multiprecision/number.hpp>

#endif

#ifdef CRYPTO3_VDF_SIMD

#include <nil/crypto3/vdf/detail/vpmadd/avxmul.hpp>

#endif

namespace nil {
    namespace crypto3 {
        namespace vdf {
#ifdef CRYPTO3_VDF_BOOST
            using namespace boost::multiprecision;
#endif

            /*!
             * @brief Defines y = ax^2 + bxy + y^2
             * @tparam NumberType
             */
            template<typename NumberType>
            struct binary_quadratic_form {
                typedef NumberType number_type;
#if defined(CRYPTO3_VDF_GMP) || defined(CRYPTO3_VDF_MPIR)

                binary_quadratic_form() {
                    mpz_inits(a, b, c, NULL);
                }

#elif defined(CRYPTO3_VDF_FLINT)

#endif

                // y = ax^2 + bxy + y^2
                number_type a;
                number_type b;
                number_type c;
                // number_type d; // discriminant
            };

            namespace detail {
                struct chia_policy {

#define LOG2(X) (63 - __builtin_clzll((X)))

                    constexpr static const int64_t threshold = 1UL << 31;
                    constexpr static const int64_t double_threshold = 1UL << 31;
                    constexpr static const int64_t exp_threshold = 63;
                    constexpr static const int64_t maxv = ((1UL << 63) - 1);

                    template<typename NumberType>
                    struct state_type {
                        typedef NumberType number_type;
                        typedef binary_quadratic_form<number_type> form_type;
#if defined(CRYPTO3_VDF_GMP) || defined(CRYPTO3_VDF_MPIR)

                        state_type() {
                            mpz_inits(D, L, NULL);
                            mpz_inits(r, ra, s, p, NULL);
                            mpz_inits(G, dx, dy, By, Dy, x, y, t1, t2, bx, by, ax, ay, q, t, Q1, NULL);
                            mpz_inits(faa, fab, fac, fba, fbb, fbc, fca, fcb, fcc, NULL);
                        }

#elif defined(CRYPTO3_VDF_FLINT)

                        state_type() {
                            fmpz_init(D);
                            fmpz_init(L);
                            fmpz_init(r);
                            fmpz_init(ra);
                            fmpz_init(s);
                            fmpz_init(p);
                            fmpz_init(G);
                            fmpz_init(dx);
                            fmpz_init(dy);
                            fmpz_init(By);
                            fmpz_init(Dy);
                            fmpz_init(x);
                            fmpz_init(y);
                            fmpz_init(t1);
                            fmpz_init(t2);
                            fmpz_init(bx);
                            fmpz_init(by);
                            fmpz_init(ax);
                            fmpz_init(ay);
                            fmpz_init(q);
                            fmpz_init(t);
                            fmpz_init(Q1);
                            fmpz_init(faa);
                            fmpz_init(fab);
                            fmpz_init(fac);
                            fmpz_init(fba);
                            fmpz_init(fbb);
                            fmpz_init(fbc);
                            fmpz_init(fca);
                            fmpz_init(fcb);
                            fmpz_init(fcc);
                        }
#endif

                        number_type D, L;
                        number_type r, ra, s, p;
                        number_type G, dx, dy, By, Dy, x, y, t1, t2, bx, by, ax, ay, q, t, Q1;

                        number_type faa, fab, fac, fba, fbb, fbc, fca, fcb, fcc;

                        form_type form;
                    };
                };
            }
        }
    }
}

#endif //CRYPTO3_CHIA_POLICY_HPP


//==============================================================================
//         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 NT2_TOOLBOX_BESSEL_FUNCTIONS_SCALAR_JNI_HPP_INCLUDED
#define NT2_TOOLBOX_BESSEL_FUNCTIONS_SCALAR_JNI_HPP_INCLUDED
#include <nt2/toolbox/bessel/functions/jni.hpp>
#include <nt2/include/constants/digits.hpp>
#include <nt2/include/functions/scalar/j0.hpp>
#include <nt2/include/functions/scalar/j1.hpp>
#include <nt2/include/functions/scalar/cospi.hpp>
#include <nt2/sdk/meta/adapted_traits.hpp>
#include <nt2/include/functions/scalar/is_less.hpp>
#include <nt2/include/functions/scalar/is_greater.hpp>
#include <nt2/include/functions/scalar/splat.hpp>
#include <boost/simd/sdk/math.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A1 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::jni_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_< integer_<A0> >)(scalar_< arithmetic_<A1> >)
                            )
  {

    typedef typename boost::dispatch::meta::as_floating<A0,A1>::type result_type;

    NT2_FUNCTOR_CALL(2)
    {
      return nt2::jni(a0, result_type(a1));
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A1 is double
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::jni_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_< integer_<A0> >)(scalar_< double_<A1> >)
                            )
  {

    typedef A1 result_type;

    NT2_FUNCTOR_CALL(2)
    {
      if (is_inf(a1)) return Zero<A1>();
    #if defined(BOOST_SIMD_HAS__JN)
      return ::_jn(a0, a1);
    #elif defined(BOOST_SIMD_HAS_JN)
      return ::jn(a0, a1);
    #else
      #error jn not supported
    #endif
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A1 is float
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::jni_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_< integer_<A0> >)(scalar_< single_<A1> >)
                            )
  {
    typedef A1 result_type;
    NT2_FUNCTOR_CALL(2)
    {
      result_type x = a1;
      const size_t n1 = nt2::abs(a0);
      result_type sign = a0<Zero<A0>()?cospi(n1):One<A0>();
      if( n1 == 0 )
        return( sign * nt2::j0(x) );
      if( n1 == 1 )
        return( sign * nt2::j1(x) );
      if( n1 == 2 )
        return mul(sign, (mul(Two<result_type>(), nt2::j1(x) / x)  -  nt2::j0(x)) );

      /* continued fraction */
      ptrdiff_t k = 24;
      result_type pk = Two<result_type>()*(result_type(n1) + result_type(k));
      result_type ans = pk;
      result_type xk = sqr(x);
      do {
        pk  = pk - Two<result_type>();
        ans = pk - (xk/ans);
      }
      while( --k > 0 );
      /* backward recurrence */

      pk = One<result_type>();
      /*pkm1 = 1.0/ans;*/
      result_type xinv = rec(x);
      result_type pkm1 = ans * xinv;
      k = n1-1;
      result_type r = 2.0f*k;

      do{
        const result_type pkm2 = (pkm1 * r  -  pk * x) * xinv;
        pk = pkm1;
        pkm1 = pkm2;
        r -= Two<result_type>();
      }
      while( --k > 0 );
      return sign*select(gt(abs(pk), nt2::abs(pkm1)),nt2::j1(x)/pk, nt2::j0(x)/pkm1);
      //     return ::jnf(a0, a1);
    }
  };
} }

#endif


// #include <cstdint>
// #include <cstdio>
// #include <cstring>
// #include <iomanip>
// #include <iostream>
// #include <string>
// #include <vector>

#include <bitset>
#include <boost/format/format_fwd.hpp>
#include <boost/format/group.hpp>
#include <exception>
#include <ios>
#include <math.h>
#include <ostream>
#include <stdexcept>
#include <stdio.h>

// local libs
#include "args.hxx"
#include "boost/format.hpp"

const std::string version = "v1.0.1";

unsigned long long int hex_to_dec(std::string hex);
std::string hex_to_bin(std::string hex);

std::string dec_to_bin(unsigned long long int dec, std::string bin);

unsigned long long int bin_to_dec(std::string bin);
std::string bin_insert_space(std::string bin, int spacing_option);

std::string dec_to_bin_set_spacing(unsigned long long int dec);

int main(int argc, char **argv) {

  args::ArgumentParser parser("Numvert", "Author: David Ryan\n~ ALL TO HIM");

  args::Group inputGroup(parser,
                         "Input types:", args::Group::Validators::AtLeastOne);

  args::Group outputFormatingGroup(
      parser, "Output formating options:", args::Group::Validators::DontCare);

  args::Group otherOptionsGroup(
      parser, "Other options:", args::Group::Validators::DontCare);

  args::HelpFlag help(parser, "help", "Display this help menu", {'?', "help"});

  args::ValueFlagList<std::string> hexadecimalInputFlg(
      inputGroup, "Hexadecimal",
      "Hexadecimal input, case insensitive (do not apply 0x prefix)",
      {'h', "hex"});
  args::ValueFlagList<unsigned long long int> decimalInputFlg(
      inputGroup, "Decimal", "Decimal input", {'d', "dec", 'i', "int"});
  args::ValueFlagList<std::string> binaryInputFlg(
      inputGroup, "Binary", "Binary input (do not apply 0b prefix)",
      {'b', "bin"});

  // args::ValueFlag<std::string> octalDeicmalInput(
  //     parser, "Octal Decimal", "Octal Decimal input", {'o', "oct"});

  /* Output Formating Group */
  args::Flag printFullFlg(outputFormatingGroup, "Print Full",
                          "Prints advanced output", {'f', "full"});

  args::ValueFlag<int> printBinSpaceFlg(
      outputFormatingGroup, "4 or 8",
      "Prints spaces in between every forth binary bit or every byte",
      {'s', "binary-spacing"});

  args::Flag truncateBinFlg(outputFormatingGroup, "Truncate Binary",
                            "Truncates binary output to least significant '1'",
                            {"tb", "truncate-binary"});

  args::Flag printCapHexFlg(outputFormatingGroup, "Print Hex with Cap",
                            "Prints all hexadecimal output with capital "
                            "letters (can not be used as input)",
                            {'H', "cap-hex"});

  /* Other options */

  args::Flag versionFlg(otherOptionsGroup, "Version",
                        "Prints version information", {"version"});

  // args::Flag signedBinInput(otherOptionsGroup, "Signed Binary Input", "Marks
  // all binary input as signed values", {"sb"}); // TODO: signed binary math

  try {
    parser.ParseCLI(argc, argv);
  } catch (const args::Help &e) {
    std::cout << parser;
    return 0;
  } catch (args::ParseError &e) {
    std::cerr << e.what() << std::endl;
    std::cerr << parser;
    return 1;
  } catch (args::ValidationError &e) {
    std::cerr << e.what() << std::endl;
    std::cerr << parser;
    return 1;
  }

  std::cout << boost::format(
                   "+---------------------------------------numvert--+")
            << std::endl;

  /************************
   * HEXADECIMAL INPUT
   *************************/
  if (hexadecimalInputFlg) {
    for (std::string hex : args::get(hexadecimalInputFlg)) {

      // printf("[0x%s]\n\r", hex.c_str());

      if (printCapHexFlg) {

        std::cout << boost::format("[%1%]") %
                         boost::io::group(std::hex, std::showbase,
                                          std::uppercase, hex_to_dec(hex))
                  << std::endl;

        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase,
                                          std::uppercase, hex_to_dec(hex))
                  << std::endl;
        // printf("\tHEX : 0x%llX\n\r", hex_to_dec(hex));
      } else {

        std::cout << boost::format("[%1%]") % boost::io::group(std::hex,
                                                               std::showbase,
                                                               hex_to_dec(hex))
                  << std::endl;

        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase,
                                          hex_to_dec(hex))
                  << std::endl;
        // std::cout << boost::format("\tHEX : %1%") % hex << std::endl;
        // printf("\tHEX : 0x%s\n\r", hex.c_str());
      }

      std::cout << boost::format("\tDEC : %1%") %
                       boost::io::group(std::dec, hex_to_dec(hex))
                << std::endl;
      // printf("\tDEC : %llu\n\r", hex_to_dec(hex));

      if (printBinSpaceFlg) {

        // std::cout << boost::format("\tBIN : 0b%1%") %
        //                  bin_insert_space(hex_to_bin(hex),
        //                                   args::get(printBinSpaceFlg))
        //           << std::endl;
        printf("\tBIN : 0b%s\n\r",
               bin_insert_space(hex_to_bin(hex), args::get(printBinSpaceFlg))
                   .c_str());
      } else {
        // std::cout << boost::format("\tBIN : 0b%1%") % hex_to_bin(hex)
        //           << std::endl;
        printf("\tBIN : 0b%s\n\r", hex_to_bin(hex).c_str());
      }
      std::cout << boost::format(
                       "+------------------------------------------------+")
                << std::endl;
      // printf("+------------------------------------------------+\n\r");
    }
  }

  /************************
   * DECIMAL INPUT
   *************************/
  if (decimalInputFlg) {
    for (unsigned long long int dec : decimalInputFlg) {

      std::cout << boost::format("[%1%]") % dec << std::endl;
      // printf("[%llu]\n\r", dec);
      if (printCapHexFlg) {
        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase,
                                          std::uppercase, dec)
                  << std::endl;
        // printf("\tHEX : 0x%llX\n\r", dec);
      } else {
        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase, dec)
                  << std::endl;
        // printf("\tHEX : 0x%llx\n\r", dec);
      }

      std::cout << boost::format("\tDEC : %1%") % dec << std::endl;
      // printf("\tDEC : %llu\n\r", dec);

      std::string bin;

      if (truncateBinFlg) // truncating bin option
      {
        bin = dec_to_bin(dec, "");
      } else {
        bin = dec_to_bin_set_spacing(dec);
      }

      if (printBinSpaceFlg) // bin spacing option
      {
        std::cout << boost::format("\tBIN : 0b%1%") %
                         bin_insert_space(bin, args::get(printBinSpaceFlg))
                  << std::endl;
        // printf("\tBIN : 0b%s\n\r",
        //        bin_insert_space(bin, args::get(printBinSpaceFlg)).c_str());
      } else {
        std::cout << boost::format("\tBIN : 0b%1%") % bin << std::endl;
        // printf("\tBIN : 0b%s\n\r", bin.c_str());
      }

      std::cout << boost::format(
          "+------------------------------------------------+") << std::endl;
    }
  }

  /************************
   * BINARY INPUT
   *************************/
  if (binaryInputFlg) {
    for (std::string bin : args::get(binaryInputFlg)) {

      std::cout << boost::format("[0b%1%]") % bin << std::endl;
      // printf("[0b%s]\n\r", bin.c_str());
      if (printCapHexFlg) {
        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase,
                                          std::uppercase, bin_to_dec(bin))
                  << std::endl;
        // printf("\tHEX : 0x%llX\n\r", bin_to_dec(bin));
      } else {
        std::cout << boost::format("\tHEX : %1%") %
                         boost::io::group(std::hex, std::showbase,
                                          bin_to_dec(bin))
                  << std::endl;
        // printf("\tHEX : 0x%llx\n\r", bin_to_dec(bin));
      }

      std::cout << boost::format("\tDEC : %1%") % bin_to_dec(bin) << std::endl;
      // printf("\tDEC : %llu\n\r", bin_to_dec(bin));
      std::cout << boost::format("\tBIN : 0b%1%") % bin << std::endl;
      // printf("\tBIN : 0b%s\n\r", bin.c_str());

      std::cout << boost::format(
                       "+------------------------------------------------+")
                << std::endl;
    }
  }

  if (versionFlg) {
    std::cout << boost::format("version: %1%") % version << std::endl;
    // printf("version: %s\n\r", version.c_str());
    std::cout << boost::format(
                     "+------------------------------------------------+")
              << std::endl;
  }

  return 0;
}

std::string hex_to_bin(std::string hex) {
  std::string bin = "";
  int i = 0;
  try{
  while (hex.at(i)) {
    switch (hex.at(i)) {
    case '0':
      bin.append("0000");
      break;
    case '1':
      bin.append("0001");
      break;
    case '2':
      bin.append("0010");
      break;
    case '3':
      bin.append("0011");
      break;
    case '4':
      bin.append("0100");
      break;
    case '5':
      bin.append("0101");
      break;
    case '6':
      bin.append("0110");
      break;
    case '7':
      bin.append("0111");
      break;
    case '8':
      bin.append("1000");
      break;
    case '9':
      bin.append("1001");
      break;
    case 'A':
    case 'a':
      bin.append("1010");
      break;
    case 'B':
    case 'b':
      bin.append("1011");
      break;
    case 'C':
    case 'c':
      bin.append("1100");
      break;
    case 'D':
    case 'd':
      bin.append("1101");
      break;
    case 'E':
    case 'e':
      bin.append("1110");
      break;
    case 'F':
    case 'f':
      bin.append("1111");
      break;
    default:
      std::string returnError =
          "ERROR: unable to parse Hex, invalid character: \'";
      returnError = returnError + hex[i] + '\'';
      return returnError;
    }
    i++;
  }
  } catch (const std::exception &e) {
    return bin;
  }
  return bin;
}

unsigned long long int hex_to_dec(std::string hex) {
  return stoull(hex, nullptr, 16);
}

std::string dec_to_bin(unsigned long long int dec, std::string bin) {
  if (dec > 1) {
    bin = dec_to_bin(dec / 2, bin);
  }
  bin.append(std::to_string(dec % 2));
  return bin;
}

std::string bin_insert_space(std::string bin, int spacing_option) {
  int spacer = 0;
  for (int i = bin.length(); i > 0; i--) {
    if (spacing_option == 4 && (spacer % 4) == 0) {
      bin.insert(i, " ");
    } else if (spacing_option == 8 && (spacer % 8) == 0) {
      bin.insert(i, " ");
    }
    spacer++;
  }
  return bin;
}

unsigned long long int bin_to_dec(std::string bin) {
  return stoull(bin, nullptr, 2);
}

std::string dec_to_bin_set_spacing(unsigned long long int dec) {
  if (dec <= 16) {
    std::bitset<4> bin(dec);
    return bin.to_string();
  } else if (dec <= 256) {
    std::bitset<8> bin(dec);
    return bin.to_string();
  } else if (dec <= 4096) {
    std::bitset<12> bin(dec);
    return bin.to_string();
  } else if (dec <= 65536) {
    std::bitset<16> bin(dec);
    return bin.to_string();
  } else if (dec <= 1048576) {
    std::bitset<20> bin(dec);
    return bin.to_string();
  } else if (dec <= 16777216) {
    std::bitset<24> bin(dec);
    return bin.to_string();
  } else if (dec <= 268435456) {
    std::bitset<28> bin(dec);
    return bin.to_string();
  } else if (dec <= 4294967296) {
    std::bitset<432> bin(dec);
    return bin.to_string();
  }

  std::bitset<64> bin(dec);
  return bin.to_string();
}

// TODO: dec and hex to binary with set length


/**
 * @file search.cpp
 *
 * @brief search function of parameter generator of 64 bit Mersenne Twister.
 *
 * The functions in this file are simple. They parse the command line
 * options and call all_in_one function which does almost all things.
 * Users can change this file so that it fits to their applications
 * and OS.
 *
 * @author Mutsuo Saito
 * @author Makoto Matsumoto (Hiroshima University)
 * @author Takuji Nishimura (Yamagata University)
 *
 * Copyright (C) 2019 Mutsuo Saito, Makoto Matsumoto,
 * Takuji Nishimura and Hiroshima University.
 * All rights reserved.
 *
 * The MIT License is applied to this software, see
 * LICENSE
 */
#include <stdint.h>
#include <inttypes.h>
#include <iostream>
#include <iomanip>
#include <time.h>
#include <string>
//#include <sstream>
//#include <MTToolBox/AlgorithmRecursionAndTempering.hpp>
#include <MTToolBox/AlgorithmBestBits.hpp>
#include <MTToolBox/AlgorithmRecursionSearch.hpp>
#include <MTToolBox/AlgorithmEquidistribution.hpp>
#include <MTToolBox/AlgorithmPartialBitPattern.hpp>
//#include <MTToolBox/MersenneTwister.hpp>
#include "MixedSequence.hpp"
//#include <NTL/GF2X.h>
//#include "options.hpp"
#include "mt64Search.hpp"
#include "search.h"

using namespace std;
using namespace MTToolBox;
using namespace NTL;

namespace {
    int search_main(options& opt, ostream& os, ostream& log, int count);
}

/**
 * search parameters using all_in_one function in the file search_all.hpp
 * @param opt command line options
 * @param count number of parameters user requested
 * @return 0 if this ends normally
 */
int search(options& opt, ostream& os, ostream& log, int count) {
    try {
        return search_main(opt, os, log, count);
    } catch (underflow_error &e) {
        log << "# search end: sequence has wasted out." << endl;
        return 0;
    }
}

namespace {
    int search_main(options& opt, ostream& os, ostream& log, int count) {
        typedef AlgorithmPartialBitPattern<uint64_t, 64, 1, 47, 5> stsl1;
        typedef AlgorithmPartialBitPattern<uint64_t, 64, 1, 27, 5> stsl2;

        stsl1 apbp1;
        stsl2 apbp2;
        uint32_t seq = 0;
        seq = ~seq;
        if (opt.seq > 0) {
            seq = opt.seq;
        }
        MixedSequence mx(seq, opt.seed, 0);
        mt64 g(opt.mexp, opt.id);
        //static const int shifts[] = {17, 37};
        // limit_v 何ビットテンパリングするか とりあえず 15のまま
        //AlgorithmBestBits<uint32_t> tmp(64, shifts, 2, 15);
        if (opt.verbose) {
            time_t t = time(NULL);
            log << "#search start id = " << opt.id << " at " << ctime(&t) << endl;
            log << "#seed = " << dec << opt.seed
                << ", seq = " << seq << endl;
        }
        if (opt.fixedPOS > 0) {
            g.setFixedPOS(opt.fixedPOS);
        }

        AlgorithmRecursionSearch<uint64_t> ars(g, mx);
        long cnt = 0;
        os << "# " << g.getHeaderString() << ", delta"
           << endl;
        while (cnt < count) {
            if (ars.start(opt.logcount)) {
                log << "# search found: " << dec << g.getID()
                    << ", " << g.getSEQ()
                    << "; tempering search start..." << endl;
                g.setTmpIdx(0);
                apbp1(g, false);
                g.setTmpIdx(1);
                apbp2(g, false);
                AlgorithmEquidistribution<uint64_t> equi(g, 64, opt.mexp);
                int veq[64];
                int delta = equi.get_all_equidist(veq);
                if (delta > opt.max_defect) {
                    log << "# search skipped: " << dec << g.getID()
                        << ", " << g.getSEQ()
                        << "; dd = " << delta << endl;
                    continue;
                }
                os << g.getParamString();
                os << "," << dec << delta << endl;
#if defined(DEBUG)
                for (int j = 0; j < 64; j++) {
                    cout << "k(" << dec << (j + 1) << ") = " << dec << veq[j];
                    cout << "\td(" << dec << (j + 1) << ") = " << dec
                         << (opt.mexp / (j + 1) - veq[j]) << endl;
                }
#endif
                cnt++;
            } else {
                log << "# search not found: " << dec << g.getID()
                    << ", " << g.getSEQ() << endl;
            }
        }
        if (opt.verbose) {
            time_t t = time(NULL);
            log << "search end at " << ctime(&t) << endl;
        }
        return 0;
    }
}


#include "Cannon\HybridCannon.h"
#include "ShootingSimple\BisectionComponent.h"
#include "Problems\TroeschProblem.h"
#include "MultipleShooting\HybridMultipleShootingComponent.h"
#include "Utils\AuxUtils.h"
#include <time.h>
#include <boost\timer.hpp>
#include <omp.h>

using namespace std;
using namespace auxutils;

//typedef float_50_noet numType;
typedef double numType;

// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
const std::string currentDateTime() {
    time_t     now = time(0);
    struct tm  tstruct;
    char       buf[80];
    localtime_s(&tstruct, &now);
    // Visit http://en.cppreference.com/w/cpp/chrono/c/strftime
    // for more information about date/time format
    strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);

    return buf;
}

int main(){
	char answer = 'y';
	cout.precision(std::numeric_limits<numType>::digits10);
	while (answer == 'y')
	{
		cout << "lambda =";
		numType l;
		cin >> l;
		numType h;
		cout << "h = ";
		cin >> h;
		h = max((numType)0.000001, min((numType)0.1, h));
		cout <<"h= " << h<< endl;

		TroeschProblem<numType> tp(l);

		PointSimple<numType> ptLeft;
		ptLeft.Argument  = 0;
		ptLeft.Value  = 0;

		PointSimple<numType> ptRight;
		ptRight.Argument  = 1;
		ptRight.Value  = 1;

		HybridMultipleShootingComponent<numType> HMSComp(tp);

		do
		{
			boost::timer t;
			bool succeeded;
			std::vector<InitCondition<numType>> solution = HMSComp.Run(ptLeft, ptRight, h, succeeded);
			//cout << currentDateTime() << endl;
			cout << t.elapsed() << endl;
			cout << "Number of knots = " << solution.size() << endl;
			cout << "Init. slope = " << solution[0].Derivative << endl;
			//std::string fileName = "f:\\TroeschProblemSolution.txt";
			///auxutils::SaveToFile(solution, fileName.c_str());
			//cout << "Result is saved to " << fileName << endl;
			cout << "Continue? y/n";
			cin >> answer;
		} while (answer == 'r');
	}
}


#pragma once

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

#define TSINFINITY       1e20
#define TSEPSILON        1e-6
#define TSPIVOTLIMIT     0.00

using namespace std;
namespace ublas = boost::numeric::ublas;

namespace t_simplex {

	/* DECLARATION OF DATA TYPES */
	enum TsError { TsErrBadInput };

	//TsSignature is used for inputting the source and sink signatures

	class TsSignature
	{
	public:
		unsigned int n;									// Number of features in the signature
		int *features;							// Pointer to the features vector
		double *weights;						// Pointer to the weights of the features
		TsSignature(unsigned int nin, int *fin, double * win) :n(nin), features(fin), weights(win) {};
	};

	//TsFlow is used for outputting the final flow table
	typedef struct TsFlow
	{
		int from;								// Feature number in signature 1
		int to;									// Feature number in signature 2
		double amount;							// Amount of flow from signature1.features[from] to signature2.features[to]
	} TsFlow;

	// TsBasic is used for 2D lists, allowing for easy navigation of the basic variables
	typedef struct TsBasic
	{
		int i, j;
		double val;
		TsBasic *nextCus, *prevCus;				//next/previous node in the column
		TsBasic *nextFac, *prevFac;				//next/previous node in the row
	} TsBasic;

	// TsStone is used for _BFS
	typedef struct TsStone
	{
		struct TsStone *prev;
		struct TsBasic *node;
	} TsStone;

	// TsVogPen is used for 1D lists in _initVogel
	typedef struct TsVogPen
	{
		int i;
		struct TsVogPen *next, *prev;
		int one, two;
		double oneCost, twoCost;
	} TsVogPen;

	// Helper function for _initVogel
	inline void addPenalty(
		TsVogPen * pitr,
		double &cost,
		int &i)
	{
		if (pitr != NULL)
		{
			if (cost < pitr->oneCost)
			{
				pitr->twoCost = pitr->oneCost;
				pitr->two = pitr->one;
				pitr->oneCost = cost;
				pitr->one = i;
			}
			else if (cost < pitr->twoCost)
			{
				pitr->twoCost = cost;
				pitr->two = i;
			}
		}
	}

	/* DECLARATIONS */
	double transportSimplex(
		const unsigned &update_xij_mode,
		ublas::matrix<double> &custom_cij,
		TsFlow *flowTable,
		unsigned int *flowSize,
		TsSignature *facility,
		TsSignature *customer,
		const double &sum_dj,
		const double &sum_bi,
		const vector <double > &dj);

	double _pivot(
		TsBasic * basics,
		TsBasic ** facBasics,
		TsBasic ** cusBasics,
		bool ** isBasic,
		int &m,
		int &n,
		const vector <double > &dj);

	TsStone * _BFS(
		TsStone * stoneTree,
		TsBasic ** facBasics,
		TsBasic ** cusBasics,
		bool complete = false);

	void _initVogel(
		double *S,
		double *D,
		TsBasic * basicsEnd,
		TsBasic ** facBasics,
		TsBasic ** cusBasics,
		bool ** isBasic,
		int m,
		int n);

	void _initNW(
		double *bi,
		double *dj,
		TsBasic * basicsEnd,
		TsBasic ** facBasics,
		TsBasic ** cusBasics,
		bool ** isBasic,
		int m,
		int n);

	void _initLCM(
		double *bi,
		double *dj,
		TsBasic * basicsEnd,
		TsBasic ** facBasics,
		TsBasic ** cusBasics,
		bool ** isBasic,
		int m,
		int n,
		ublas::matrix<unsigned int> &cij_column_sorted,
		int *facilities);
}


/*
 * Copyright 2018 James Dyer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * This is being developed for the TANGO Project: http://tango-project.eu
 */

#include "PolyACDEncrypter.h"

#include <NTL/ZZ.h>
#include <NTL/ZZX.h>
#include <json/json.h>
#include <cryptopp/osrng.h>
#include <cryptopp/rng.h>
#include <chrono>
#include <string>
#include <sstream>

PolyACDEncrypter::PolyACDEncrypter(int lambda, int mu, int d) {
	this->mu = mu;
	RandomLen(k1,lambda);
	k2.SetLength(d+1);
	for(int i =0; i < d; i++){
		NTL::ZZ tmp;
		RandomBits(tmp,mu);
		SetCoeff(k2,i,tmp);
	}
	SetCoeff(k2,d,1);
}

PolyACDEncrypter::PolyACDEncrypter(int mu, std::string& secrets) {
	this->mu = mu;
	Json::Value root;   // will contains the root value after parsing.
	Json::Reader reader;
	bool parsingSuccessful = reader.parse(secrets,root);
	if (parsingSuccessful){
		std::istringstream k1buf(root["k1"].asString());
		k1buf >> k1;
		std::istringstream k2buf(root["k2"].asString());
		k2buf >> k2;
	}
}

PolyACDEncrypter::~PolyACDEncrypter() {
}

std::string PolyACDEncrypter::writeSecretsToJSON(){
	Json::Value root;
	std::ostringstream k1buf;
	k1buf << k1;
	root["k1"]= k1buf.str();
	std::ostringstream k2buf;
	k2buf << k2;
	root["k2"]= k2buf.str();
	Json::FastWriter writer;
	return writer.write(root);
}

NTL::ZZX PolyACDEncrypter::encrypt(NTL::ZZX& plaintext){
	NTL::ZZX p = k2*plaintext;
	long degree = deg(p);
	NTL::ZZ m_n = LeadCoeff(plaintext);
	NTL::ZZX p2;
	p2.SetLength(degree-1);
	for (long i = 0; i < degree-1; i++){
		SetCoeff(p2,i,coeff(p,i));
	}
	SetCoeff(p2,degree-1,p[degree-1]+k1*m_n);
	NTL::ZZX r;
	long d = deg(k2);
	r.SetLength(d-1);
	for(long i =0; i < d; i++){
		NTL::ZZ tmp;
		RandomBits(tmp,mu);
		SetCoeff(r,i,tmp);
	}
	return p2+r;
}

NTL::ZZX PolyACDEncrypter::encrypt(NTL::vec_ZZ& plaintext){
	NTL::ZZX ptext = to_ZZX(plaintext);
	return encrypt(ptext);
}


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/1-50/8/problem8.hpp"

BOOST_AUTO_TEST_SUITE( Problem8 )

    BOOST_AUTO_TEST_CASE( Example ) {
        auto res = problems::problem8::solve(4);
        BOOST_CHECK_EQUAL(res, 5832);
    }

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem8::solve();
        BOOST_CHECK_EQUAL(res, 23514624000);
    }

BOOST_AUTO_TEST_SUITE_END()


// Copyright Daniel Wallin, Eric Niebler 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_TIME_SERIES_NUMERIC_COARSE_GRAIN_060615_HPP
#define BOOST_TIME_SERIES_NUMERIC_COARSE_GRAIN_060615_HPP

#include <boost/concept/requires.hpp>
#include <boost/integer_traits.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range_run_storage/algorithm/copy.hpp>
#include <boost/time_series/concepts.hpp>
#include <boost/time_series/sparse_series.hpp>
#include <boost/time_series/ordered_inserter.hpp>
#include <boost/time_series/numeric/detail/find_period.hpp>

namespace boost { namespace time_series
{

    namespace detail
    {
        namespace rrs = range_run_storage;

        template<typename Out, typename Offset>
        struct coarse_grain_inserter
        {
            coarse_grain_inserter(Out out, Offset factor)
              : out_(out)
              , offset_(inf)
              , factor_(factor)
            {}

            template<typename Run, typename Value>
            void set_at(Run &run, Value &value)
            {
                this->offset_ = std::min<Offset>(this->offset_, rrs::offset(run));
                typedef typename result_of<rrs::op::subrun(Run &)>::type subrun_type;
                typedef std::pair<Offset, Offset> period_type;
                typedef rrs::op::overlap overlap_fun(period_type, subrun_type);
                typedef typename result_of<overlap_fun>::type overlap_type;

                subrun_type subrun(rrs::subrun(run));

                do
                {
                    period_type period(detail::find_period(subrun, this->offset_, this->factor_));
                    overlap_type overlap(rrs::overlap(period, subrun));

                    if(0 == rrs::offset(overlap) % this->factor_)
                    {
                        this->out_(value, period.first / this->factor_);
                    }

                    rrs::advance_to(subrun, rrs::end_offset(overlap));
                }
                while(rrs::has_leftover(subrun));
            }

            Out const &out()
            {
                return this->out_;
            }
        private:
            Out out_;
            Offset offset_;
            Offset factor_;
        };

    } // namespace detail

    /// \brief Generate a series of a coarser discretization by picking the values
    /// at the coarse-grained points.
    ///
    /// Generate a series of a coarser discretization by picking the values
    /// at the coarse-grained points.
    ///
    /// \param series The input series.
    /// \param discretization The coarser discretization.
    /// \param out An \c OrderedInserter into which to write the coarser series.
    /// \return Either a <tt>sparse_series\<\></tt> containing the coarser series, or a
    ///         copy of the \c OrderedInserter passed in.
    /// \attention If using the version that takes an \c OrderedInserter, you must call
    ///         <tt>.commit()</tt> on the returned \c OrderedInserter when you are done with it.
    /// \pre The coarser discretization is a multiple of the finer discretization.
    template <class Series, class Discretization, class Out>
    BOOST_CONCEPT_REQUIRES(
        ((concepts::TimeSeries<Series const>)),
    (ordered_inserter<Out>))
    coarse_grain(Series const &series, Discretization discretization, ordered_inserter<Out> out)
    {
        typedef typename concepts::TimeSeries<Series const>::offset_type offset_type;

        BOOST_ASSERT(discretization > series.discretization());
        BOOST_ASSERT(discretization % series.discretization() == 0);

        offset_type factor = discretization / series.discretization();

        detail::coarse_grain_inserter<ordered_inserter<Out>, offset_type> o(out, factor);
        return range_run_storage::copy(series, o).out();
    }

    /// \overload
    ///
    template <class Series, class Discretization>
    BOOST_CONCEPT_REQUIRES(
        ((concepts::TimeSeries<Series const>)),
    (sparse_series<
        typename concepts::TimeSeries<Series const>::value_type
      , Discretization
    >))
    coarse_grain(Series const &series, Discretization discretization)
    {
        typedef typename concepts::TimeSeries<Series const>::value_type value_type;

        sparse_series<value_type, Discretization> result(
            time_series::discretization = discretization
        );

        time_series::coarse_grain(
            series
          , discretization
          , time_series::make_ordered_inserter(result)
        ).commit();

        return result;
    }

}} // namespace boost::time_series

#endif // BOOST_TIME_SERIES_NUMERIC_COARSE_GRAIN_060615_HPP


// Author(s): Jeroen van der Wulp
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// 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)
//
/// \file representative_generator_test.cpp
/// \brief Add your file description here.

#define BOOST_TEST_MODULE representative_generator_test

#include <boost/test/included/unit_test.hpp>

#include "mcrl2/data/list.h"
#include "mcrl2/data/parse.h"
#include "mcrl2/data/representative_generator.h"
#include "mcrl2/data/set.h"

using namespace mcrl2;
using namespace mcrl2::data;

void test_representative_generator()
{
  data_specification specification=parse_data_specification
                                   ("map f__:Nat; \n"
                                    "    g__:List(Bool);\n"
                                    "    h__:Set(Real);\n"
                                   );

  std::vector< data::structured_sort_constructor_argument > arguments;
  arguments.push_back(structured_sort_constructor_argument("s", basic_sort("E")));
  arguments.push_back(structured_sort_constructor_argument("n", sort_nat::nat()));

  std::vector< structured_sort_constructor > constructors;
  constructors.push_back(structured_sort_constructor("d", structured_sort_constructor_argument_list(arguments.begin(), arguments.begin() + 1)));
  constructors.push_back(structured_sort_constructor("e", structured_sort_constructor_argument_list(arguments.begin() + 1, arguments.begin() + 2)));

  specification.add_alias(alias(basic_sort("D"), structured_sort(structured_sort_constructor_list(constructors.begin(), constructors.begin() + 1))));
  specification.add_alias(alias(basic_sort("E"), structured_sort(structured_sort_constructor_list(constructors.begin() + 1, constructors.begin() + 2))));

  representative_generator default_expression_generator(specification);

  // Should be true or false, since constants are preferred to other constructors or mappings
  BOOST_CHECK(default_expression_generator(sort_bool::bool_()) == sort_bool::true_() ||
              default_expression_generator(sort_bool::bool_()) == sort_bool::false_());

  // Should be c0, since constants are preferred to other constructors or mappings
  BOOST_CHECK(default_expression_generator(sort_nat::nat()) == sort_nat::c0());

  // Should be empty, since constants are preferred to other constructors or mappings
  BOOST_CHECK(default_expression_generator(sort_list::list(sort_bool::bool_())) == sort_list::empty(sort_bool::bool_()));

  // Should be e(0), since constants are preferred to other constructors or mappings
  BOOST_CHECK(default_expression_generator(basic_sort("E")) ==
              constructors[1].constructor_function(basic_sort("E"))(default_expression_generator(sort_nat::nat())));

  // Should be d(e(0)), since constants are preferred to other constructors or mappings
  BOOST_CHECK(default_expression_generator(basic_sort("D")) ==
              constructors[0].constructor_function(basic_sort("D"))(default_expression_generator(basic_sort("E"))));

  // Check whether the representative of the set of reals is the empty set of reals, or the set of all reals.
  // The answer depends on the relative ordering of the false_function and true_function in sets of function symbols.
  // This in turns depends on the place where these functions end up in the memory.
  const data_expression t=default_expression_generator(container_sort(set_container(),data::sort_real::real_()));
  BOOST_CHECK(t == data::sort_set::constructor(data::sort_real::real_(),
                                               data::sort_set::false_function(data::sort_real::real_()),
                                               data::sort_fset::empty(data::sort_real::real_()))  ||
              t == data::sort_set::constructor(data::sort_real::real_(),
                                               data::sort_set::true_function(data::sort_real::real_()),
                                               data::sort_fset::empty(data::sort_real::real_())));

   // Check whether a proper representative term is generated for a function sort.
   const sort_expression s1=function_sort({ basic_sort("Nat")}, basic_sort("Nat"));
   BOOST_CHECK(default_expression_generator(s1).sort() == s1);
   const sort_expression s2=function_sort({ basic_sort("Nat"), basic_sort("Real")}, basic_sort("Nat"));
   BOOST_CHECK(default_expression_generator(s2).sort() == s2);
}

BOOST_AUTO_TEST_CASE(test_main)
{
  test_representative_generator();
}


// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

#include "stdafx.h"

#include <boost/test/unit_test.hpp>
#include "Common/boost-taef.h"
#include "../Common/LargeInteger.Test.Constants.h"

namespace FederationUnitTests
{
    using namespace std;
    using namespace Common;
    using namespace Federation;

    //   UnitTests for the NodeId class. The operators, walks and distances (except min)
    //   are not tested as they are simple wrappers around LargeInteger operators which have been tested ow.
    //
    class NodeIdTests
    {
    };

    BOOST_FIXTURE_TEST_SUITE(NodeIdTestsSuite,NodeIdTests)

    BOOST_AUTO_TEST_CASE(MinDistanceIsCorrect)
    {
        LargeIntegerTestConstants consts;
        NodeId id1(consts.MaxMax);
        NodeId id2(consts.MaxZero);
        NodeId id3(consts.ZeroMax);
        NodeId id4(consts.ZeroZero);
        NodeId id5(consts.Test1);
        NodeId id6(consts.Test2);
        NodeId id7(consts.Test3);

        VERIFY_IS_TRUE(id1.MinDist(id2) == consts.ZeroMax);
        VERIFY_IS_TRUE(id1.MinDist(id3) == consts.ZeroMax + LargeInteger::One);
        VERIFY_IS_TRUE(id1.MinDist(id4) == consts.ZeroOne);
        VERIFY_IS_TRUE(id1.MinDist(id5) == consts.Test1 - consts.MaxMax);
        VERIFY_IS_TRUE(id1.MinDist(id6) == consts.Test2MaxMaxMinDist);
        VERIFY_IS_TRUE(id1.MinDist(id7) == consts.Test3MaxMaxMinDist);

        VERIFY_IS_TRUE(id2.MinDist(id3) == consts.ZeroMax - consts.MaxZero);
        VERIFY_IS_TRUE(id2.MinDist(id4) == consts.ZeroMax + LargeInteger::One);
        VERIFY_IS_TRUE(id2.MinDist(id5) == consts.Test1 - consts.MaxZero);
        VERIFY_IS_TRUE(id2.MinDist(id6) == consts.Test2MaxZeroMinDist);
        VERIFY_IS_TRUE(id2.MinDist(id7) == consts.Test3MaxZeroMinDist);

        VERIFY_IS_TRUE(id3.MinDist(id4) == consts.ZeroMax);
        VERIFY_IS_TRUE(id3.MinDist(id5) == consts.ZeroMax - consts.Test1);
        VERIFY_IS_TRUE(id3.MinDist(id6) == consts.Test2ZeroMaxMinDist);
        VERIFY_IS_TRUE(id3.MinDist(id7) == consts.Test3ZeroMaxMinDist);

        VERIFY_IS_TRUE(id4.MinDist(id5) == consts.Test1 - consts.ZeroZero);
        VERIFY_IS_TRUE(id4.MinDist(id6) == consts.Test2ZeroZeroMinDist);
        VERIFY_IS_TRUE(id4.MinDist(id7) == consts.Test3ZeroZeroMinDist);

        VERIFY_IS_TRUE(id5.MinDist(id6) == consts.Test1Test2MinDist);
        VERIFY_IS_TRUE(id5.MinDist(id7) == consts.Test1Test3MinDist);
        VERIFY_IS_TRUE(id6.MinDist(id7) == consts.Test2Test3MinDist);
    }

    // Tests the correctness of the Precedes function.
    BOOST_AUTO_TEST_CASE(PrecedesIsCorrect)
    {
        LargeIntegerTestConstants consts;
        NodeId id1(consts.MaxMax);
        NodeId id2(consts.MaxZero);
        NodeId id3(consts.ZeroMax);
        NodeId id4(consts.ZeroZero);

        VERIFY_IS_TRUE(!id1.Precedes(id2));
        VERIFY_IS_TRUE(id1.Precedes(id3));
        VERIFY_IS_TRUE(id1.Precedes(id4));
        VERIFY_IS_TRUE(id2.Precedes(id3));
        VERIFY_IS_TRUE(id2.Precedes(id4));
        VERIFY_IS_TRUE(!id3.Precedes(id4));
    }

    BOOST_AUTO_TEST_SUITE_END()
}


//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Alejandro Cabrera 2011.
// 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)
//
// See http://www.boost.org/libs/bloom_filter for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_BLOOM_FILTER_DYNAMIC_BLOOM_FILTER_HPP
#define BOOST_BLOOM_FILTER_DYNAMIC_BLOOM_FILTER_HPP 1

#include <cmath>

#include <boost/config.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/size.hpp>
#include <boost/dynamic_bitset.hpp>

#include <boost/bloom_filter/detail/apply_hash.hpp>
#include <boost/bloom_filter/detail/exceptions.hpp>
#include <boost/bloom_filter/hash/default.hpp>

namespace boost {
  namespace bloom_filters {
    template <typename T,
	      class HashFunctions = mpl::vector<boost_hash<T> >,
	      class Block = size_t,
	      class Allocator = std::allocator<Block> >
    class dynamic_bloom_filter {
    public:
      typedef T value_type;
      typedef T key_type;
      typedef HashFunctions hash_function_type;
      typedef Block block_type;
      typedef Allocator allocator_type;
      typedef dynamic_bitset<block_type, allocator_type> bitset_type;
      typedef dynamic_bloom_filter<T, HashFunctions,
				   Block, Allocator> this_type;

    private:
      typedef detail::apply_hash<mpl::size<HashFunctions>::value - 1,
				 this_type> apply_hash_type;

    public:

      //* constructors
      dynamic_bloom_filter() {}

      explicit dynamic_bloom_filter(const size_t bit_capacity) :
	bits(bit_capacity) {}

      template <typename InputIterator>
      dynamic_bloom_filter(const InputIterator start,
			   const InputIterator end)
	: bits(std::distance(start, end) * 4)
      {
	for (InputIterator i = start; i != end; ++i)
	  this->insert(*i);
      }

      //* query functions
      static BOOST_CONSTEXPR size_t num_hash_functions() {
        return mpl::size<HashFunctions>::value;
      }

      double false_positive_rate() const {
        const double n = static_cast<double>(this->bits.count());
        static const double k = static_cast<double>(num_hash_functions());
        static const double m = static_cast<double>(this->bits.size());
        static const double e =
	  2.718281828459045235360287471352662497757247093699959574966;
        return std::pow(1 - std::pow(e, -k * n / m), k);
      }

      size_t count() const {
        return this->bits.count();
      }

      size_t bit_capacity() const {
	return this->bits.size();
      }

      bool empty() const {
	return this->count() == 0;
      }

      const bitset_type&
      data() const
      {
	return this->bits;
      }

      //* core operations
      void insert(const T& t) {
	apply_hash_type::insert(t, bits);
      }

      template <typename InputIterator>
      void insert(const InputIterator start, const InputIterator end) {
	for (InputIterator i = start; i != end; ++i) {
	  this->insert(*i);
	}
      }

      bool probably_contains(const T& t) const {
	return apply_hash_type::contains(t, bits);
      }

      //* auxilliary operations
      void clear() {
        this->bits.reset();
      }

      void swap(dynamic_bloom_filter& other) {
	dynamic_bloom_filter tmp = other;
	other = *this;
	*this = tmp;
      }

      void resize(const size_t new_capacity) {
	bits.clear();
	bits.resize(new_capacity);
      }

      template <typename _T, typename _HashFunctions,
		typename _Block, typename _Allocator>
      friend bool operator==(const dynamic_bloom_filter<_T, _HashFunctions,
							_Block, _Allocator>&,
			     const dynamic_bloom_filter<_T, _HashFunctions,
							_Block, _Allocator>&);

      template <typename _T, typename _HashFunctions,
		typename _Block, typename _Allocator>
      friend bool operator!=(const dynamic_bloom_filter<_T,
							_HashFunctions,
							_Block,
							_Allocator>&,
			     const dynamic_bloom_filter<_T,
							_HashFunctions,
							_Block,
							_Allocator>&);

      dynamic_bloom_filter& operator|=(const dynamic_bloom_filter& rhs) {
	if(this->bit_capacity() != rhs.bit_capacity()) {
	  throw detail::incompatible_size_exception();
	}

        this->bits |= rhs.bits;
        return *this;
      }

      dynamic_bloom_filter& operator&=(const dynamic_bloom_filter& rhs) {
	if(this->bit_capacity() != rhs.bit_capacity()) {
	  throw detail::incompatible_size_exception();
	}

        this->bits &= rhs.bits;
        return *this;
      }

    private:
      bitset_type bits;
    };

    template<class T, class HashFunctions,
	     class Block, class Allocator>
    dynamic_bloom_filter<T, HashFunctions, Block, Allocator>
    operator|(const dynamic_bloom_filter<T,
					 HashFunctions,
					 Block, Allocator>& lhs,
	      const dynamic_bloom_filter<T,
					 HashFunctions,
					 Block, Allocator>& rhs)
    {
      if(lhs.bit_capacity() != rhs.bit_capacity()) {
	throw detail::incompatible_size_exception();
      }

      dynamic_bloom_filter<T, HashFunctions, Block, Allocator> ret(lhs);
      ret |= rhs;
      return ret;
    }

    template<class T, class HashFunctions,
	     class Block, class Allocator>
    dynamic_bloom_filter<T, HashFunctions, Block, Allocator>
    operator&(const dynamic_bloom_filter<T,
					 HashFunctions,
					 Block, Allocator>& lhs,
	      const dynamic_bloom_filter<T,
					 HashFunctions,
					 Block, Allocator>& rhs)
    {
      if(lhs.bit_capacity() != rhs.bit_capacity()) {
	throw detail::incompatible_size_exception();
      }

      dynamic_bloom_filter<T, HashFunctions, Block, Allocator> ret(lhs);
      ret &= rhs;
      return ret;
    }

    template<class T, class HashFunctions,
	     class Block, class Allocator>
    bool
    operator==(const dynamic_bloom_filter<T,
					  HashFunctions,
					  Block, Allocator>& lhs,
	       const dynamic_bloom_filter<T,
					  HashFunctions,
					  Block, Allocator>& rhs)
    {
      if(lhs.bit_capacity() != rhs.bit_capacity()) {
	throw detail::incompatible_size_exception();
      }

      return lhs.bits == rhs.bits;
    }

    template<class T, class HashFunctions,
	     class Block, class Allocator>
    bool
    operator!=(const dynamic_bloom_filter<T,
					  HashFunctions,
					  Block, Allocator>& lhs,
	       const dynamic_bloom_filter<T,
					  HashFunctions,
					  Block, Allocator>& rhs)
    {
      return !(lhs == rhs);
    }

    template<class T, class HashFunctions,
	     class Block, class Allocator>
    void
    swap(dynamic_bloom_filter<T,
			      HashFunctions,
			      Block, Allocator>& lhs,
	 dynamic_bloom_filter<T,
			      HashFunctions,
			      Block, Allocator>& rhs)
    {
      lhs.swap(rhs);
    }
  } // namespace bloom_filter
} // namespace boost
#endif


/*
 *            Copyright 2009-2020 The VOTCA Development Team
 *                       (http://www.votca.org)
 *
 *      Licensed under the Apache License, Version 2.0 (the "License")
 *
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *              http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Third party includes
#include <boost/format.hpp>

// Local VOTCA includes
#include "votca/xtp/atom.h"
#include "votca/xtp/bfgs_trm.h"
#include "votca/xtp/trustregion.h"

namespace votca {
namespace xtp {

void BFGSTRM::Optimize(const Eigen::VectorXd& initialparameters) {
  parameters_ = initialparameters;
  cost_ = costfunction_.EvaluateCost(parameters_);
  double lastcost = cost_;
  Eigen::VectorXd gradient = costfunction_.EvaluateGradient(parameters_);
  for (auto& func : callbacks_) {
    func();
  }

  Eigen::VectorXd delta_p_trial = Eigen::VectorXd::Zero(parameters_.size());
  Eigen::VectorXd last_gradient = Eigen::VectorXd::Zero(parameters_.size());
  double delta_cost = 0;
  for (iteration_ = 1; iteration_ <= max_iteration_; iteration_++) {
    for (Index i = 0; i < 100; i++) {
      TrustRegion subproblem;
      delta_p_trial =
          subproblem.CalculateStep(gradient, hessian_, trust_radius_);
      double trialcost =
          costfunction_.EvaluateCost(parameters_ + delta_p_trial);
      delta_cost = trialcost - lastcost;
      bool step_accepted =
          AcceptRejectStep(delta_p_trial, gradient, delta_cost);
      if (step_accepted) {
        cost_ = trialcost;
        parameters_ += delta_p_trial;
        break;
      }
    }
    gradient = costfunction_.EvaluateGradient(parameters_);
    if (iteration_ > 1) {
      UpdateHessian(delta_p_trial, gradient - last_gradient);
    }
    lastcost = cost_;
    last_gradient = gradient;
    for (auto& func : callbacks_) {
      func();
    }
    if (costfunction_.Converged(delta_p_trial, delta_cost, gradient)) {
      break;
    } else if (iteration_ == max_iteration_) {
      success_ = false;
      XTP_LOG(Log::warning, *pLog_)
          << (boost::format("BFGS-TRM @iteration %1$d: not converged after "
                            "%2$d iterations ") %
              iteration_ % max_iteration_)
                 .str()
          << std::flush;
    }
  }
  return;
}

/* Accept/reject the new geometry and adjust trust radius, if required */
bool BFGSTRM::AcceptRejectStep(const Eigen::VectorXd& delta_p,
                               const Eigen::VectorXd& gradient,
                               double cost_delta) {
  bool step_accepted = false;
  if (cost_delta > 0.0) {
    // total energy has unexpectedly increased, half the trust radius
    trust_radius_ = 0.25 * trust_radius_;
    XTP_LOG(Log::warning, *pLog_)
        << (boost::format("BFGS-TRM @iteration %1$d: DeltaCost %2$2.4e step "
                          "rejected ") %
            iteration_ % cost_delta)
               .str()
        << std::flush;
    XTP_LOG(Log::warning, *pLog_)
        << (boost::format(
                "BFGS-TRM @iteration %1$d: new trust radius %2$2.4e") %
            iteration_ % trust_radius_)
               .str()
        << std::flush;

  } else {
    // total energy has decreased, we accept the step but might update the trust
    // radius
    step_accepted = true;
    // adjust trust radius, if required
    double tr_check = cost_delta / QuadraticEnergy(gradient, delta_p);
    double norm_delta_p = delta_p.squaredNorm();
    if (tr_check > 0.75 &&
        1.25 * norm_delta_p > trust_radius_ * trust_radius_) {
      trust_radius_ = 2.0 * trust_radius_;
    } else if (tr_check < 0.25) {
      trust_radius_ = 0.25 * trust_radius_;
    }
    XTP_LOG(Log::warning, *pLog_)
        << (boost::format(
                "BFGS-TRM @iteration %1$d: DeltaCost/QuadraticApprox %2$2.4f "
                "step accepted ") %
            iteration_ % tr_check)
               .str()
        << std::flush;
    XTP_LOG(Log::warning, *pLog_)
        << (boost::format(
                "BFGS-TRM @iteration %1$d: new trust radius %2$2.4e") %
            iteration_ % trust_radius_)
               .str()
        << std::flush;
  }
  return step_accepted;
}

void BFGSTRM::UpdateHessian(const Eigen::VectorXd& delta_pos,
                            const Eigen::VectorXd& delta_gradient) {
  // second term in BFGS update (needs current Hessian)
  hessian_ -= hessian_ * delta_pos * delta_pos.transpose() *
              hessian_.transpose() /
              (delta_pos.transpose() * hessian_ * delta_pos).value();
  // first term in BFGS update
  hessian_ += (delta_gradient * delta_gradient.transpose()) /
              (delta_gradient.transpose() * delta_pos);
  // symmetrize Hessian (since d2E/dxidxj should be symmetric)
  hessian_ = 0.5 * (hessian_ + hessian_.transpose());
  return;
}

/* Estimate energy change based on quadratic approximation */
double BFGSTRM::QuadraticEnergy(const Eigen::VectorXd& gradient,
                                const Eigen::VectorXd& delta_pos) const {
  return (gradient.transpose() * delta_pos).value() +
         0.5 * (delta_pos.transpose() * hessian_ * delta_pos).value();
}

}  // namespace xtp
}  // namespace votca