#include <iostream>
#include <fstream>
#include <cmath>
#include <math.h>
#include <vector>
#include <array>
#include "SpaceAdaptiveSolver.hpp"
#include "TriDiagMatrix.hpp"
#include "MassMatrix.hpp"
#include "StiffnessMatrix.hpp"
#include <string>
#include <functional>
#include <boost/math/quadrature/gauss.hpp>
using namespace std;
using namespace boost::math::quadrature;

void AdaptiveSolver::SetTolerances(const double refineby, const double coarsenby)
{
    tolerance = refineby;
    coarseningtol = coarsenby;
}

void AdaptiveSolver::AdaptiveSolve()
{
mpsmesh.PrintSpaceNodes();
mpcurrenTimeStep = 0;
mpcurrentMeshIndex = 0;
oldmesh.CopySpaceMesh(mpsmesh);

//AnalyticSolutionVec();
//mpPreviousSolution = mpAnalyticSolution;
mppde->InitialCondition(mpsmesh, mpPreviousSolution);
stiff.SetParameters(k_0, k_L, mpa);

int m = mptmesh.NumberOfTimeSteps();
for(int j = 0; j<m; j++)
{
mpcurrenTimeStep = j+1;
mpcurrentMeshIndex = j;
stiff.BuildGeneralStiffnessMatrix ( mpsmesh );
stiff.MultiplyByScalar( mptmesh.ReadTimeMesh(mpcurrentMeshIndex) );
mass.BuildGeneralMassMatrix(mpsmesh);

LHS.AddTwoMatrices( mass, stiff );
g_0 = mppde->FirstBoundary(mptmesh.ReadTimeStep(mpcurrenTimeStep));
g_L =mppde->SecondBoundary(mptmesh.ReadTimeStep(mpcurrenTimeStep));
BuildRHS();
BuiltbrVec();
VectorTimesScalar( br, mptmesh.ReadTimeMesh(mpcurrentMeshIndex) );

AddVectors( br, mpRHS, mpRHS );

LHS.MatrixSolver( mpRHS, mpx );

oldmesh.CopySpaceMesh(mpsmesh);
mpsmesh.PrintSpaceNodes();

mpPreviousSolution = mpx;

    BuildGradientVec(mpx, mpsmesh, FEMGradient);
    GradientRecoveryFunction( mpsmesh, FEMGradient, GradientRecovery );
    BuildErrorEstimate();
    PrintVector(ErrorEstimate);

    SaveRefinementNodes(mpNodeToInsert);
    PrintVector(mpNodeToInsert);

    if(ErrorEstimate.size()>3)
    {
    std::cout<< ErrorEstimate.size()<<"\n";
    SaveIntervalsForCoarsening();
    mpsmesh.CoarsenIntervals(NodesForRemoval);
    }
    else
    {
    NodesForRemoval.clear();
    }

    mpsmesh.InsertArray( mpNodeToInsert );
}
    std::cout<<mpsmesh.meshsize() <<"\n";
    std::cout<<"\n";
}

void AdaptiveSolver::SaveIntervalsForCoarsening(  )
{
    NodesForRemoval.clear();
    for(int i=1; i<ErrorEstimate.size()-1; i++)
    {
        if (sqrt(ErrorEstimate.at(i)+ErrorEstimate.at(i+1))<coarseningtol)
        {
            NodesForRemoval.push_back(i+1);
        }
    }
        for (auto k: NodesForRemoval)
        std::cout << k << ", ";
        std::cout << " \n";
}

void AdaptiveSolver::SaveIntervalsForRefinement()
{
    intervalsForRefinement.clear();
    for(int i=0; i<ErrorEstimate.size(); i++)
    {
        if (sqrt(ErrorEstimate.at(i))>tolerance)
        {
            intervalsForRefinement.push_back(i);
        }
    }
}

void AdaptiveSolver::SaveRefinementNodes( std::vector<double>& Nodes_to_insert )
{
    Nodes_to_insert.clear();
    double midpoint;
    for(int i=0; i<ErrorEstimate.size(); i++)
    {
        if (sqrt(ErrorEstimate.at(i))>tolerance)
        {
           midpoint = 0.5*(mpsmesh.ReadSpaceNode(i)+mpsmesh.ReadSpaceNode(i+1));
           Nodes_to_insert.push_back(midpoint);
        }
    }
}

void AdaptiveSolver::BuildRHS()
{
mpRHS.clear();
std::vector<double> intervals;

refinedsmesh.CommonMesh(mpsmesh, oldmesh);

double integral;
refinedsmesh.Range(mpsmesh.ReadSpaceNode(0), mpsmesh.ReadSpaceNode(1), intervals);
    for(int j = 0; j<intervals.size()-1; j++)
    {
        integral = integral + IntegrateBasisWithU(0, intervals.at(j), intervals.at(j+1));
    }
    mpRHS.push_back(integral);

for (int i = 1; i<mpsmesh.meshsize(); i++)
{
    integral=0;
    refinedsmesh.Range(mpsmesh.ReadSpaceNode(i-1), mpsmesh.ReadSpaceNode(i+1), intervals);

    for(int j = 0; j<intervals.size()-1; j++)
    {
        integral = integral + IntegrateBasisWithU(i, intervals.at(j),
                    intervals.at(j+1));
    }
    mpRHS.push_back(integral);
}
integral=0;
refinedsmesh.Range(mpsmesh.ReadSpaceNode(mpsmesh.meshsize()-1), mpsmesh.ReadSpaceNode(mpsmesh.meshsize()), intervals);
    for(int j = 0; j<intervals.size()-1; j++)
    {
        integral = integral + IntegrateBasisWithU(mpsmesh.meshsize(), intervals.at(j), intervals.at(j+1));
    }
    mpRHS.push_back(integral);
}

void AdaptiveSolver::MeshRefinement()
{

    SaveIntervalsForRefinement();
    SaveIntervalsForCoarsening();
    mpsmesh.BisectIntervals(intervalsForRefinement);
    mpsmesh.CoarsenIntervals(NodesForRemoval);

}

double AdaptiveSolver::IntegrateBasisWithU( int NodeIndex, double lowerlimit,
                              double upperlimit )
{
   //std::cout<<NodeIndex<<", "<<lowerlimit<<", "<< upperlimit<< "\n";
    auto SolutionWithBasis = [&](double x)
        { return mpsmesh.GeneralTestFunctions( NodeIndex, x)*GeneralInterpolant( x, mpPreviousSolution, oldmesh ); };

    return gauss<double, 7>::integrate(SolutionWithBasis, lowerlimit, upperlimit);
}

void AdaptiveSolver::UnitTest()
{
mpsmesh.GenerateUniformMesh(1,4);
oldmesh.GenerateUniformMesh(1, 4);
mpPreviousSolution = {1,2,2,1};

BuildRHS();

MassMatrix test;
test.BuildGeneralMassMatrix(mpsmesh);
test.MatrixVectorMultiplier( mpPreviousSolution, mpRHS );
PrintVector(mpRHS);

mpsmesh.RemoveSpaceNode(1);
mpsmesh.PrintSpaceNodes();

BuildRHS();
}

//void AdaptiveSolver::SaveIntervalsForCoarsening2()
//{
//    NodesForRemoval.clear();
//    for(int i=0; i<ErrorEstimate.size()-1; i++)
//    {
//        if ((sqrt(ErrorEstimate.at(i))<coarseningtol)||(sqrt(ErrorEstimate.at(i+1))<coarseningtol))
//        {
//            NodesForRemoval.push_back(i+1);
//        }
//    }
//}

void AdaptiveSolver::SaveIntervalsForCoarsening2()
{
    NodesForRemoval.clear();
    for(int i=1; i<ErrorEstimate.size()-1; i++)
    {
        if ((sqrt(ErrorEstimate.at(i))<coarseningtol)||(sqrt(ErrorEstimate.at(i+1))<coarseningtol))
        {
            NodesForRemoval.push_back(i+1);
        }
    }
}

Solver class with methods to adapt in space/SpaceAdaptiveSolver.cpp


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

NTL_CLIENT

void test(GF2X& P, GF2EX& f, GF2EX& g, GF2EX& h, GF2EX& hx, GF2EX& s, GF2EX& t)
{
   /* P is the polynomial of the extension
    * f and g the polynomials
    * h the gcd
    * hx the gcd obtained using XGCD
    * s, t are Bezout coefficients hx=f*s+g*t
    */
   GF2EX htest,rf,rg;

   if (h!=hx){
       cout << P << "\n" << f << "\n" << g << "\n";
       Error("different gcd:\n");
   }

   if (max(deg(f), deg(g)) > 0 || min(deg(f), deg(g)) >= 0) {
      if (deg(s) >= deg(g) || deg(t) >= deg(f)) {
	 cout << P << "\n" << f << "\n" << g << "\n";
	 Error("degree bounds at fault:\n");
      }
   }

   mul(s,s,f);
   mul(t,t,g);
   add(htest,t,s);
   if (h!=htest){
      cout << P << "\n" << f << "\n" << g << "\n";
      Error("xgcd at fault:\n");
   }
   if (!IsZero(h)){
      rem(rf,f,h);
      rem(rg,f,h);
      if ((!IsZero(rf))||(!IsZero(rg))){
         cout << P << "\n" << f << "\n" << g << "\n";
         Error("not a common divisor\n");
      }
   }else{
       if (!IsZero(f) && !IsZero(g)){
         cout << "debug:\n";
         cout << P << "\n" << f << "\n" << g << "\n" << h << "\n";
         Error("ooops:\n");
      }
   }
}

int main()
{

   GF2X P;

   BuildIrred(P, 128);

   GF2E::init(P);

   for (long i = 0; i < 400; i++) {
      if (i%10 == 0) cerr << ".";
      GF2EX f,g,h,s,t,hx;

      long deg_h;
      if (RandomBnd(2))
         deg_h = RandomBnd(10)+1;
      else
         deg_h = RandomBnd(500)+1;

      random(h, deg_h);
      SetCoeff(h, deg_h);

      long deg_f;
      if (RandomBnd(2))
         deg_f = RandomBnd(10)+1;
      else
         deg_f = RandomBnd(1000)+1;

      random(f, deg_f);
      f *= h;

      long deg_g;
      if (RandomBnd(2))
         deg_g = RandomBnd(10)+1;
      else
         deg_g = RandomBnd(1000)+1;

      random(g, deg_g);
      g *= h;

      h = 0;

      GCD(h, f, g);
      XGCD(hx, s, t, f, g);
      test(P, f, g, h, hx, s, t);
   }

   cerr << "\n";

}


/*
 * base_types.hpp
 *
 *  Copyright (c) 2014 Kumar Robotics. All rights reserved.
 *
 *  This file is part of kr_math.
 *
 *  Created on: 28/06/2014
 *      Author: gareth
 */

/*
 * @brief Convenient shorthand names for working with vectors/matrices.
 */
#ifndef KR_MATH_BASE_TYPES_H_
#define KR_MATH_BASE_TYPES_H_

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

namespace kr {

template <typename Scalar, int Rows, int Cols>
using Mat = Eigen::Matrix<Scalar, Rows, Cols>;

template <typename Scalar, int Rows>
using Vec = Eigen::Matrix<Scalar, Rows, 1>;

template <typename Scalar> using Vec2 = Mat<Scalar, 2, 1>;

template <typename Scalar> using Vec3 = Mat<Scalar, 3, 1>;

template <typename Scalar> using Vec4 = Mat<Scalar, 4, 1>;

template <typename Scalar> using Mat2 = Mat<Scalar, 2, 2>;

template <typename Scalar> using Mat3 = Mat<Scalar, 3, 3>;

template <typename Scalar> using Mat4 = Mat<Scalar, 4, 4>;

template <typename Scalar> using Quat = Eigen::Quaternion<Scalar>;

typedef Vec2<float> Vec2f;
typedef Vec2<double> Vec2d;

typedef Vec3<float> Vec3f;
typedef Vec3<double> Vec3d;

typedef Vec4<float> Vec4f;
typedef Vec4<double> Vec4d;

typedef Mat2<float> Mat2f;
typedef Mat2<double> Mat2d;

typedef Mat3<float> Mat3f;
typedef Mat3<double> Mat3d;

typedef Mat4<float> Mat4f;
typedef Mat4<double> Mat4d;

typedef Quat<float> Quatf;
typedef Quat<double> Quatd;

} // namespace kr

#endif // KR_MATH_BASE_TYPES_H_


#include <I3Test.h>

#include "common.h"
#include "MuonGun/Cylinder.h"

#include <boost/make_shared.hpp>

TEST_GROUP(Integration);

using namespace I3MuonGun;

namespace {

double one(double depth, double cos_theta) {
	return 1.;
}

}

TEST(Constant)
{
	Cylinder surface(1000, 500);

	ENSURE_DISTANCE(surface.IntegrateFlux(one, 0., 1.),
	    surface.GetAcceptance(0., 1.), surface.GetAcceptance(0., 1.)/1e4,
	    "Numerical integration of a constant is accurate to 1e-4");
}


#include "benchmark.h"

#include <Eigen/Core>
#include <Eigen/Cholesky>
#include <Eigen/LU>
#include <fstream>
#include <chrono>
#include <iostream>
#include <cstdlib>

namespace
{
	class Gaussian
	{
		static const double log2pi;

		size_t n_;
		Eigen::VectorXd mean_;
		Eigen::MatrixXd covariance_;
		Eigen::MatrixXd invCovariance_;
		double logdet_;
		Eigen::LLT<Eigen::MatrixXd> cholesky_;

		Eigen::VectorXd tmp_;

		EIGEN_MAKE_ALIGNED_OPERATOR_NEW

	public:
		Gaussian(const Eigen::VectorXd& mean, const Eigen::MatrixXd& cov)
			: n_(mean.size()), mean_(mean), covariance_(cov), logdet_(0)
		{
			invCovariance_.resizeLike(covariance_);
			computeDetInv();
		}
		void computeDetInv()
		{
			bool invertible = true;
			double det;
			if (n_ == 1) {
				covariance_.block<1, 1>(0, 0).computeInverseAndDetWithCheck(invCovariance_, det, invertible);
				logdet_ = std::log(det);
			}
			else if (n_ == 2) {
				covariance_.block<2, 2>(0, 0).computeInverseAndDetWithCheck(invCovariance_, det, invertible);
				logdet_ = std::log(det);
			}
			else if (n_ == 3) {
				covariance_.block<3, 3>(0, 0).computeInverseAndDetWithCheck(invCovariance_, det, invertible);
				logdet_ = std::log(det);
			}
			else if (n_ == 4) {
				covariance_.block<4, 4>(0, 0).computeInverseAndDetWithCheck(invCovariance_, det, invertible);
				logdet_ = std::log(det);
			}
			else {
				cholesky_.compute(covariance_);
				invertible = cholesky_.info() == Eigen::Success;
				//https://gist.github.com/redpony/fc8a0db6b20f7b1a3f23#gistcomment-2277286
				logdet_ = cholesky_.matrixL().toDenseMatrix().diagonal().array().log().sum();
			}
			if (!invertible)
			{
				std::cerr << "Covariance is singular, re-initialize with the identity matrix" << std::endl;
				covariance_.setIdentity(n_, n_);
				invCovariance_.setIdentity(n_, n_);
				logdet_ = 0; // log(det(In))=log(1)=0
			}
		}
		//Returns the log-probability of x in this gaussian
		double logP(const Eigen::VectorXd& x)
		{
			tmp_ = x - mean_;
			double alpha;
			if (n_ <= 4)
				alpha = tmp_.dot(invCovariance_*tmp_);
			else
				alpha = tmp_.dot(cholesky_.solve(tmp_));
			return -0.5 * (alpha + n_*log2pi + logdet_);
		}
		//getter+setter
		const Eigen::VectorXd& mean() const { return mean_; }
		Eigen::VectorXd& mean() { return mean_; }
		const Eigen::MatrixXd& cov() const { return covariance_; }
		Eigen::MatrixXd& cov() { return covariance_; }

		friend std::ostream& operator<<(std::ostream& o, const Gaussian& g)
		{
			o << "Mean: " << g.mean().transpose() << "\n"
				<< "Cov:\n" << g.cov();
			return o;
		}
	};
	const double Gaussian::log2pi = 1.8378770664093454835606594728112f;

	template<typename Derived, typename Scalar = typename Derived::Scalar>
	Scalar LSE(const Eigen::MatrixBase<Derived>& vec)
	{
		Scalar m = vec.maxCoeff();
		return m + std::log((vec.array() - m).exp().sum());
	}

}

void benchmark_Eigen(
	const std::string& pointsFile,
	const std::string& settingsFile,
	int numIterations,
    Json::Object& returnValues)
{
    //load settings and points
	int dimension, components, numPoints;
	Eigen::MatrixXd points;
	std::vector<double> logWeights;
	std::vector<Gaussian> gaussians;
	{ //points
		std::ifstream in(pointsFile);
		in >> dimension >> components >> numPoints;
		points.resize(dimension, numPoints);
		float dummy; //skip ground truth
		for (int i = 0; i < components * (1 + dimension + dimension * dimension); ++i) in >> dummy;
		for (int i = 0; i < numPoints; ++i) { //read points
			Eigen::VectorXd p(dimension);
			for (int d = 0; d < dimension; ++d) in >> p[d];
			points.col(i) = p;
		}
	}
	{ //initial settings
		std::ifstream in(settingsFile);
		int dummy;
		in >> dimension >> components >> dummy;
		logWeights.resize(components);
		gaussians.reserve(components);
		Eigen::VectorXd mean(dimension);
		Eigen::MatrixXd cov(dimension, dimension);
		for (int i=0; i<components; ++i)
		{
			double weight;
			in >> weight;
			logWeights[i] = std::log(weight);
			for (int x = 0; x < dimension; ++x) in >> mean[x];
			for (int x = 0; x < dimension; ++x)
				for (int y = 0; y < dimension; ++y)
					in >> cov(x, y);
			gaussians.emplace_back(mean, cov);
		}
	}

#ifndef NDEBUG
	for (int k = 0; k < components; ++k)
	{
		std::cout << "\nComponent " << k
			<< "\nWeight: " << std::exp(logWeights[k])
			<< "\n" << gaussians[k] << std::endl;
	}
#endif

	//temporary memory
	Eigen::MatrixXd logW(numPoints, components);

	//run EM (fixed number of iterations)
	std::cout << "Run EM algorithm" << std::endl;
	std::chrono::time_point<std::chrono::steady_clock> start;
	double logLikeliehoodAccum;
	for (int iter=0; iter < numIterations; ++iter)
	{
		//half of the iterations for warm up
		if (iter==numIterations/2) start = std::chrono::steady_clock::now();

#ifndef NDEBUG
		std::cout << "    Iteration " << iter << std::endl;
#endif

		//Precomputation
#pragma omp parallel for
		for (int i = 0; i < components; ++i)
			gaussians[i].computeDetInv();

		//E-Step
		logLikeliehoodAccum = 0;
		for (int i=0; i<numPoints; ++i)
		{
			//compute membership weight w_ik
			for (int k=0; k<components; ++k)
			{
				double lw = gaussians[k].logP(points.col(i)) + logWeights[k];
				logW(i, k) = lw;
			}
			double lse = LSE(logW.row(i));
			logW.row(i) -= Eigen::RowVectorXd::Constant(components, lse);
			logLikeliehoodAccum += lse;

#ifndef NDEBUG
			std::cout << "Point " << i << ":";
			for (int k = 0; k < components; ++k)
				std::cout << " " << std::exp(logW(i, k));
			std::cout << std::endl;
#endif
		}

		//M-Step
		for (int k=0; k<components; ++k)
		{
#ifndef NDEBUG
			std::cout << "\nComponent " << k << " pre-update:"
				<< "\nWeight: " << std::exp(logWeights[k])
				<< "\n" << gaussians[k] << std::endl;
#endif

			double logNk = LSE(logW.col(k));
			double divNk = 1.0f / std::exp(logNk);
			logWeights[k] = logNk - std::log(numPoints);
			//Eigen does not support batches, so I have to write it explicitly as a loop here
			//(and broadcasting is very ugly)
			gaussians[k].mean().setZero();
			gaussians[k].cov().setZero();
			for (int i = 0; i < numPoints; ++i)
				gaussians[k].mean() += std::exp(logW(i, k)) * points.col(i);
			gaussians[k].mean() *= divNk;
			for (int i = 0; i < numPoints; ++i)
				gaussians[k].cov() += std::exp(logW(i, k)) *
					(points.col(i) - gaussians[k].mean()) * (points.col(i) - gaussians[k].mean()).transpose();
			gaussians[k].cov() = gaussians[k].cov()*divNk
			+ Eigen::MatrixXd::Identity(dimension, dimension) * 1e-1;

#ifndef NDEBUG
			std::cout << "Component " << k << " post-update:"
				<< "\nWeight: " << std::exp(logWeights[k])
				<< "\n" << gaussians[k] << std::endl;
#endif
		}

#ifndef NDEBUG
		std::cout << " -> log-likelihood: " << logLikeliehoodAccum << "\n";
#endif
	}
#ifdef NDEBUG
	std::cout << " -> log-likelihood: " << logLikeliehoodAccum << "\n";
#endif

	auto finish = std::chrono::steady_clock::now();
	double elapsed = std::chrono::duration_cast<
		std::chrono::duration<double>>(finish - start).count() * 1000 * 2; //*2 for warm up
	std::cout << "    Done in " << elapsed << "ms" << std::endl;

	//save results
	returnValues.Insert(std::make_pair("Time", elapsed));
	returnValues.Insert(std::make_pair("LogLikelihood", logLikeliehoodAccum));
	Json::Array comData;
	for (int k=0; k<components; ++k)
	{
		Json::Object com;
		com.Insert(std::make_pair("Weight", std::exp(logWeights[k])));
		Json::Array m, c;
		for (int i = 0; i < dimension; ++i) m.PushBack(gaussians[k].mean()[i]);
		for (int i = 0; i < dimension; ++i)
			for (int j = 0; j < dimension; ++j)
				c.PushBack(gaussians[k].cov()(i, j));
		com.Insert(std::make_pair("Mean", m));
		com.Insert(std::make_pair("Cov", c));
		comData.PushBack(com);
	}
	returnValues.Insert(std::make_pair("Components", comData));
}


/*! \file mabinogi_roulette_mc.cpp
    \brief マビノギのルーレットビンゴをモンテカルロ・シミュレーションする

    Copyright © 2015-2017 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "../checkpoint/checkpoint.h"
#include "goexit/goexit.h"
#ifdef HAVE_SSE2
	#include "myrandom/myrandsfmt.h"
#else
	#include "myrandom/myrand.h"
#endif
#include <algorithm>                            // for std::shuffle
#include <cstdint>                              // for std::int32_t
#include <cmath>                                // for std::sqrt
#ifdef _MSC_VER
	#include <format>                           // for std::format
#endif
#include <fstream>                              // for std::ofstream
#include <iostream>                             // for std::cout
#include <iterator>                             // for std::begin, std::ostream_iterator
#include <map>                                  // for std::map
#include <random>                               // for std::mt19937
#include <unordered_map>                        // for std::unordered_map
#include <utility>                              // for std::make_pair, std::move
#include <vector>                               // for std::vector
#include <valarray>                             // for std::valarray
#include <boost/algorithm/cxx11/iota.hpp>       // for boost::algorithm::iota
#ifndef _MSC_VER
	#include <boost/format.hpp>                 // for boost::format
#endif
#include <boost/range/algorithm.hpp>            // for boost::find, boost::max_element, boost::transform
#include <tbb/concurrent_vector.h>              // for tbb::concurrent_vector
#include <tbb/parallel_for.h>                   // for tbb::parallel_for

namespace {
    //! A global variable (constant expression).
    /*!
        列のサイズ
    */
    static auto constexpr COLUMN = 5ULL;

    //! A global variable (constant expression).
    /*!
        行のサイズ
    */
    static auto constexpr ROW = 5ULL;

    //! A global variable (constant expression).
    /*!
        ビンゴボードのマス数
    */
    static auto constexpr BOARDSIZE = ROW * COLUMN;

    //! A global variable (constant expression).
    /*!
        モンテカルロシミュレーションの試行回数
    */
    static auto constexpr MCMAX = 1000000U;

    //! A global variable (constant expression).
    /*!
        行・列の総数
    */
    static auto constexpr ROWCOLUMN = ROW + COLUMN;

    //! A typedef.
    /*!
        そのマスに書かれてある番号と、そのマスが当たったかどうかを示すフラグのstd::pair
    */
    using mypair = std::pair<std::int32_t, bool>;

    //! A typedef.
    /*!
        数字と数字のstd::pair
    */
    using mypair2 = std::pair<std::int32_t, std::int32_t>;

    //! A typedef.
    /*!
        (n + 1)個目の行・列が埋まったときの分布を格納するためのmapの型
    */
    using mymap = std::map<std::int32_t, std::int32_t>;

	//! A function.
	/*!
		(n + 1)個目の行・列またはマスが埋まったときの平均試行回数、埋まっているマスまたは行・列の平均個数を求める
		\param mcresult モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
		\param size 行・列またはマスの総数
		\return (n + 1)個目の行・列が埋まったときの平均試行回数、埋まっているマスの平均個数が格納された可変長配列のstd::pair
	*/
	std::pair< std::valarray<double>, std::valarray<double> > eval_average(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::size_t size);

	//! A function.
	/*!
		(n + 1)個目の行・列が埋まったときの中央値を求める
		\param (n + 1)個目の数値n
		\param mcresult モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
		\return (n + 1)個目の行・列が埋まったときの中央値
	*/
	std::int32_t eval_median(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n);

	//! A function.
	/*!
		(n + 1)個目の行・列が埋まったときの最頻値と分布を求める
		\param (n + 1)個目の数値n
		\param mcresult モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
		\return (n + 1)個目の行・列が埋まったときの最頻値と分布のstd::pair
	*/
	std::pair<std::int32_t, std::map<std::int32_t, std::int32_t> > eval_mode(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n);

	//! A function.
	/*!
		(n + 1)個目の行・列が埋まったときの標準偏差を求める
		\param avgten (n + 1)個目の行・列が埋まったときの平均試行回数
		\param (n + 1)個目の数値n
		\param mcresult モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
		\return (n + 1)個目の行・列が埋まったときの標準偏差
	*/
	double eval_std_deviation(double avg, tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n);

    //! A function.
    /*!
        ビンゴボードを生成する
        \return ビンゴボードが格納された可変長配列
    */
    auto makeboard();

#ifdef _CHECK_PARALELL_PERFORM
    //! A function.
    /*!
        モンテカルロ・シミュレーションを行う
        \return モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
    */
	std::pair<std::vector< std::vector<mypair2> >, std::vector< std::vector<mypair2> > > montecarlo();
#endif

    //! A function.
    /*!
        モンテカルロ・シミュレーションの実装
        \param mr 自作乱数クラスのオブジェクト
        \return モンテカルロ法の結果が格納された可変長配列
    */
	template <typename MyRandom>
	std::pair<std::vector<mypair2>, std::vector<mypair2> > montecarloImpl(MyRandom & mr);

    //! A function.
    /*!
        モンテカルロ・シミュレーションをTBBで並列化して行う
        \return モンテカルロ・シミュレーションの結果が格納された二次元可変長配列
    */
	std::pair<tbb::concurrent_vector< std::vector<mypair2> >, tbb::concurrent_vector< std::vector<mypair2> > > montecarloTBB();

    //! A function.
    /*!
        (n + 1)個目の行・列が埋まったときの分布をcsvファイルに出力する
		\param distmap (n + 1)個目の行・列が埋まったときの分布
		\param filename ファイル名
    */
    void outputcsv(mymap const & distmap, std::string const & filename);
}

int main()
{
    checkpoint::CheckPoint cp;

    cp.checkpoint("処理開始", __LINE__);

#ifdef _CHECK_PARALELL_PERFORM
    // モンテカルロ・シミュレーションの結果を代入
    auto const mcresult(montecarlo());

    cp.checkpoint("並列化無効", __LINE__);
#endif

    // TBBで並列化したモンテカルロ・シミュレーションの結果を代入
    auto const mcresult2(montecarloTBB());

    cp.checkpoint("並列化有効", __LINE__);

    auto const [trialavg, fillavg] = eval_average(mcresult2.first, ROWCOLUMN);

    for (auto n = 0U; n < ROWCOLUMN; n++) {
		auto const [mode, distmap] = eval_mode(mcresult2.first, n);
#ifdef _MSC_VER
		outputcsv(distmap, std::format("result/distribution_{:d}個目.csv", n + 1));

        std::cout
			<< std::format("ビンゴ{:d}個目に必要な平均試行回数：{:.1f}回, 効率：{:.1f}(回/個), ", n + 1, trialavg[n], trialavg[n] / static_cast<double>(n + 1))
			<< std::format("中央値：{:d}回, 最頻値：{:d}回, 標準偏差：{:.1f}, ", eval_median(mcresult2.first, n), mode, eval_std_deviation(trialavg[n], mcresult2.first, n))
			<< std::format("埋まっているマスの平均個数：{:.1f}個\n", fillavg[n]);
#else
        outputcsv(distmap, (boost::format("result/distribution_%d個目.csv") % (n + 1)).str());

        std::cout
            << boost::format("ビンゴ%d個目に必要な平均試行回数：%.1f回, 効率：%.1f(回/個), ")
            % (n + 1)
            % trialavg[n]
            % (trialavg[n] / static_cast<double>(n + 1))
            << boost::format("中央値：%d回, 最頻値：%d回, 標準偏差：%.1f, ")
            % eval_median(mcresult2.first, n)
            % mode
            % eval_std_deviation(trialavg[n], mcresult2.first, n)
            << boost::format("埋まっているマスの平均個数：%.1f個\n")
            % fillavg[n];
#endif
    }

	auto const [trialavg2, fillavg2] = eval_average(mcresult2.second, BOARDSIZE);

	for (auto n = 0U; n < BOARDSIZE; n++) {
		auto const [mode, distmap] = eval_mode(mcresult2.second, n);
#ifdef _MSC_VER
        outputcsv(distmap, std::format("result/distribution2_{:d}個目.csv", n + 1));

        std::cout
            << std::format("{:d}個目のマスに必要な平均試行回数：{:.1f}回, 効率：{:.1f}(回/個), ", n + 1, trialavg2[n], trialavg2[n] / static_cast<double>(n + 1))
            << std::format("中央値：{:d}回, 最頻値：{:d}回, 標準偏差：{:.1f}, ", eval_median(mcresult2.second, n), mode, eval_std_deviation(trialavg2[n], mcresult2.second, n))
            << std::format("埋まっている行・列の平均個数：{:.1f}個\n", fillavg2[n]);
#else
		outputcsv(distmap, (boost::format("result/distribution2_%d個目.csv") % (n + 1)).str());

		std::cout
			<< boost::format("%d個目のマスに必要な平均試行回数：%.1f回, 効率：%.1f(回/個), ")
			% (n + 1)
			% trialavg2[n]
			% (trialavg2[n] / static_cast<double>(n + 1))
			<< boost::format("中央値：%d回, 最頻値：%d回, 標準偏差：%.1f, ")
			% eval_median(mcresult2.second, n)
			% mode
			% eval_std_deviation(trialavg2[n], mcresult2.second, n)
			<< boost::format("埋まっている行・列の平均個数：%.1f個\n")
			% fillavg2[n];
#endif
	}

    cp.checkpoint("それ以外の処理", __LINE__);

    cp.checkpoint_print();

	goexit::goexit();

    return 0;
}

namespace {
    std::pair< std::valarray<double>, std::valarray<double> > eval_average(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::size_t size)
    {
        // モンテカルロ・シミュレーションの平均試行回数の結果を格納した可変長配列
        std::valarray<double> trialavg(size);

        // モンテカルロ・シミュレーションのn回目の試行で、埋まっているマスの数を格納した可変長配列
        std::valarray<double> fillavg(size);

        // 行・列の総数分繰り返す
        for (auto n = 0U; n < size; n++) {
            // 総和を0で初期化
            auto trialsum = 0;
            auto fillsum = 0;

            // 試行回数分繰り返す
            for (auto j = 0U; j < MCMAX; j++) {
                // j回目の結果を加える
                trialsum += mcresult[j][n].first;
                fillsum += mcresult[j][n].second;
            }

            // 平均を算出してn行・列目のtrialavg、fillavgに代入
            trialavg[n] = static_cast<double>(trialsum) / static_cast<double>(MCMAX);
            fillavg[n] = static_cast<double>(fillsum) / static_cast<double>(MCMAX);
        }

        return std::make_pair(std::move(trialavg), std::move(fillavg));
    }

	std::int32_t eval_median(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n)
	{
		// 中央値を求めるために必要な可変長配列
		std::vector<std::int32_t> medtmp(MCMAX);

		// 中央値を求めるために必要な可変長配列を、モンテカルロ法の結果から生成
		boost::transform(
			mcresult,
			medtmp.begin(),
			[n](auto const & res) { return res[n].first; });

		// 中央値を求めるためにソートする
		boost::sort(medtmp);

		// 中央値を求める
		if constexpr (MCMAX % 2) {
			// 要素が奇数個なら中央の要素を返す
			return medtmp[(MCMAX - 1) / 2];
		}
		else {
			// 要素が偶数個なら中央二つの平均を返す
			return (medtmp[(MCMAX / 2) - 1] + medtmp[MCMAX / 2]) / 2;
		}
	}

	std::pair<std::int32_t, mymap> eval_mode(tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n)
	{
		// (n + 1)個目の行・列が埋まったときの分布
		std::unordered_map<std::int32_t, std::int32_t> distmap;

		// distmapを埋める
		for (auto const & res : mcresult) {
			// (n + 1)個目の行・列が埋まったときの回数をkeyとする
			auto const key = res[n].first;

			// keyが存在するかどうか
			auto itr = distmap.find(key);
			if (itr == distmap.end()) {
				// keyが存在しなかったので、そのキーでハッシュを拡張（値1）
				distmap.emplace(key, 1);
			}
			else {
				// keyが指す値を更新
				itr->second++;
			}
		}

		// 最頻値を探索
		auto const mode = boost::max_element(
			distmap,
			[](auto const & p1, auto const & p2) { return p1.second < p2.second; })->first;

		// 最頻値と(n + 1)個目の行・列が埋まったときの分布をpairにして返す
		return std::make_pair(mode, mymap(distmap.begin(), distmap.end()));
	}

	double eval_std_deviation(double avg, tbb::concurrent_vector< std::vector<mypair2> > const & mcresult, std::int32_t n)
	{
		// 標準偏差を求めるために必要な可変長配列
		std::valarray<double> devtmp(MCMAX);

		// 標準偏差の計算
		boost::transform(
			mcresult,
			std::begin(devtmp),
			[avg, n](auto const & res) {
			auto const val = static_cast<double>(res[n].first);
			return (val - avg) * (val - avg);
		});

		// 標準偏差を求める
		return std::sqrt(devtmp.sum() / static_cast<double>(MCMAX));
	}

    auto makeboard()
    {
        // 仮のビンゴボードを生成
        std::vector<std::int32_t> boardtmp(BOARDSIZE);

        // 仮のビンゴボードに1～25の数字を代入
        boost::algorithm::iota(boardtmp, 1);

        // 仮のビンゴボードの数字をシャッフル
        std::shuffle(boardtmp.begin(), boardtmp.end(), std::mt19937());

        // ビンゴボードを生成
        std::vector<mypair> board(BOARDSIZE);

        // 仮のビンゴボードからビンゴボードを生成する
        boost::transform(
            boardtmp,
            board.begin(),
            [](auto n) { return std::make_pair(n, false); });

        // ビンゴボードを返す
        return board;
    }

#ifdef _CHECK_PARALELL_PERFORM
	std::pair<std::vector< std::vector<mypair2> >, std::vector< std::vector<mypair2> > > montecarlo()
    {
        // モンテカルロ・シミュレーションの結果を格納するための二次元可変長配列
		std::pair<std::vector< std::vector<mypair2> >, std::vector< std::vector<mypair2> > > mcresult;

		// MCMAX個の容量を確保
		mcresult.first.reserve(MCMAX);
		mcresult.second.reserve(MCMAX);

#ifdef HAVE_SSE2
		// 自作乱数クラスを初期化
		myrandom::MyRandSfmt mr(1, BOARDSIZE);
#else
		// 自作乱数クラスを初期化
		myrandom::MyRand mr(1, BOARDSIZE);
#endif
        // 試行回数分繰り返す
        for (auto n = 0U; n < MCMAX; n++) {
			// モンテカルロ・シミュレーションの結果を代入
			auto const [resf, ress] = montecarloImpl(mr);
			mcresult.first.emplace_back(resf);
			mcresult.second.emplace_back(ress);
        }

        // モンテカルロ・シミュレーションの結果を返す
        return mcresult;
    }
#endif

	template <typename MyRandom>
	std::pair<std::vector<mypair2>, std::vector<mypair2> > montecarloImpl(MyRandom & mr)
    {
        // ビンゴボードを生成
        auto board(makeboard());

        // その行・列が既に埋まっているかどうかを格納する可変長配列
        // ROWCOLUMN個の要素をfalseで初期化
        std::vector<bool> rcfill(ROWCOLUMN, false);

        // 行・列が埋まるまでに要した回数と、その時点で埋まったマスを格納した
        // 可変長配列
        std::vector<mypair2> fillnum;

		// (n + 1)個目のマスが埋まったときの回数と、その時点で埋まった行・列を格納した
        // 可変長配列
		std::vector<mypair2> fillnum2;

        // ROWCOLUMN個の容量を確保
        fillnum.reserve(ROWCOLUMN);

        // その時点で埋まっているマスを計算するためのラムダ式
        auto const sum = [](auto const & vec) {
            auto cnt = 0;
            for (auto & e : vec) {
                if (e.second) {
                    cnt++;
                }
            }

            return cnt;
        };

        // 無限ループ
        for (auto n = 1; ; n++) {
            // 乱数で得た数字で、かつまだ当たってないマスを検索
            auto itr = boost::find(board, std::make_pair(mr.myrand(), false));

            // そのようなマスがあった
            if (itr != board.end()) {
                // そのマスは当たったとし、フラグをtrueにする
                itr->second = true;
            }
            // そのようなマスがなかった
            else {
                //ループ続行
                continue;
            }

            // 各行・列が埋まったかどうかをチェック
            for (auto j = 0U; j < ROW; j++) {
                // 各行が埋まったかどうかのフラグ
                auto rowflag = true;

                // 各行が埋まったかどうかをチェック
                for (auto k = 0U; k < COLUMN; k++) {
                    rowflag &= board[COLUMN * j + k].second;
                }

                // 行の処理
                if (rowflag &&
                    // その行は既に埋まっているかどうか
                    !rcfill[j]) {
                    // その行は埋まったとして、フラグをtrueにする
                    rcfill[j] = true;

                    // 要した試行回数と、その時点で埋まったマスの数を格納
                    fillnum.emplace_back(n, sum(board));
                }

                // 各列が埋まったかどうかのフラグ
                auto columnflag = true;

                // 各列が埋まったかどうかをチェック
                for (auto k = 0U; k < ROW; k++) {
                    columnflag &= board[j + COLUMN * k].second;
                }

                // 列の処理
                if (columnflag &&
                    // その列は既に埋まっているかどうか
                    !rcfill[j + ROW]) {

                    // その列は埋まったとして、フラグをtrueにする
                    rcfill[j + ROW] = true;

                    // 要した試行回数と、その時点で埋まったマスの数を格納
                    fillnum.emplace_back(n, sum(board));
                }
            }

			// 要した試行回数と、その時点で埋まっている行・列の数を格納
			fillnum2.emplace_back(n, static_cast<std::int32_t>(fillnum.size()));

            // 全ての行・列が埋まったかどうか
            if (fillnum.size() == ROWCOLUMN) {
                // 埋まったのでループ脱出
                break;
            }
        }

        // 要した試行関数の可変長配列を返す
        return std::make_pair(std::move(fillnum), std::move(fillnum2));
    }

    std::pair<tbb::concurrent_vector< std::vector<mypair2> >, tbb::concurrent_vector< std::vector<mypair2> > > montecarloTBB()
    {
        // モンテカルロ・シミュレーションの結果を格納するための二次元可変長配列
        // 複数のスレッドが同時にアクセスする可能性があるためtbb::concurrent_vectorを使う
        std::pair<tbb::concurrent_vector< std::vector<mypair2> >, tbb::concurrent_vector< std::vector<mypair2> > > mcresult;

        // MCMAX個の容量を確保
        mcresult.first.reserve(MCMAX);
		mcresult.second.reserve(MCMAX);

        // MCMAX回のループを並列化して実行
        tbb::parallel_for(
            0U,
            MCMAX,
            1U,
            [&mcresult](auto) {

#ifdef HAVE_SSE2
			// 自作乱数クラスを初期化
			myrandom::MyRandSfmt mr(1, BOARDSIZE);
#else
			// 自作乱数クラスを初期化
			myrandom::MyRand mr(1, BOARDSIZE);
#endif

            // モンテカルロ・シミュレーションの結果を代入
			auto const [resf, ress] = montecarloImpl(mr);
            mcresult.first.emplace_back(resf);
			mcresult.second.emplace_back(ress);
        });

        // モンテカルロ・シミュレーションの結果を返す
        return mcresult;
    }

    void outputcsv(mymap const & distmap, std::string const & filename)
    {
        std::ofstream ofs(filename);

        boost::transform(
            distmap,
            std::ostream_iterator<std::string>(ofs, "\n"),
#ifdef _MSC_VER
            [](auto const& p) { return std::format("{:d},{:d}", p.first, p.second); });
#else
            [](auto const & p) { return (boost::format("%d,%d") % p.first % p.second).str(); });
#endif
    }
}


////////////////////////////////////////////////////////////////////////////////
// MIT License
//
// Copyright (c) 2017 Jonas Spenger
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
//
// This is a modified (see below) version of the SAX algorithm proposed in:
//  Lin, Jessica, et al. "A symbolic representation of time series, with
//  implications for streaming algorithms." Proceedings of the 8th ACM SIGMOD
//  workshop on Research issues in data mining and knowledge discovery.  ACM,
//  2003.
//
// This implementation (MSAX) differs from the original SAX in two aspects.
//  (1) The time series is normalized by normalizing each point to the
//      neighbouring #windowsize points. That is, for each point we subtract
//      the moving average and divide by the moving standard deviation (with
//      a moving window of size windowsize). In the original SAX algorithm,
//      each subsequence is normalized.
//  (2) Because the entire time series is normalized, this implementation
//      outputs just one symbolic sequence, that represents the entire time
//      series. In comparison to the original SAX, which outputs a symbolic
//      sequence for every subsequence of the time series (of size windowsize),
//      resulting in more than one symbolic sequences.
//
//  To use this include file and run msax::run() with following parameters:
//    timeSeries: the time series (a container / vector)
//    alphabetSize: the size of the alphabet
//    frameSize: the size of a frame that gets transformed to one symbol, the
//               dimensionality reduction so to speak
//    windowSize: the size of the moving window used for the normalization
//
////////////////////////////////////////////////////////////////////////////////

#pragma once

#include <boost/math/distributions/normal.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
#include <boost/accumulators/statistics/rolling_variance.hpp>
#include <vector>

namespace msax {

template <typename T>
std::vector<int> run(const T& timeSeries,
                     size_t alphabetSize,
                     size_t frameSize,
                     size_t windowSize) {

  //////////////////////////////////////////////////////////////////////////////
  // Normalize the time series
  //////////////////////////////////////////////////////////////////////////////

  T normalizedTimeSeries(timeSeries.size());

  // accumulator for calculating moving variance and moving average
  boost::accumulators::accumulator_set
    <double,
    boost::accumulators::stats<boost::accumulators::tag::rolling_mean,
    boost::accumulators::tag::rolling_variance> >
    accumulator(boost::accumulators::tag::rolling_window::window_size = windowSize);

  // calculate the mean and variance for first windowsize elements
  for (size_t i = 0; i < windowSize; ++i) {
      accumulator(timeSeries[i]);
  }
  // calculate the normalized values for the first windowsize / 2 elements
  for (size_t i = 0; i < windowSize / 2; ++i) {
    normalizedTimeSeries[i] = (timeSeries[i] - boost::accumulators::rolling_mean(accumulator))
      / std::sqrt(boost::accumulators::rolling_variance(accumulator));
  }
  // calculate normalized values for windowsize/2 to timeSeries.size - (windowSize + 1) / 2 values
  for (size_t i = windowSize; i < timeSeries.size(); ++i) {
    accumulator(timeSeries[i]);
    size_t j = i - (windowSize + 1) / 2;
    normalizedTimeSeries[j] = (timeSeries[j] - boost::accumulators::rolling_mean(accumulator))
      / std::sqrt(boost::accumulators::rolling_variance(accumulator));
  }
  // calculate normalized values for the last (windowSize + 1) / 2 values
  for (size_t i = timeSeries.size() - (windowSize + 1) / 2; i < timeSeries.size(); ++i) {
    normalizedTimeSeries[i] = (timeSeries[i] - boost::accumulators::rolling_mean(accumulator))
      / std::sqrt(boost::accumulators::rolling_variance(accumulator));
  }

  //////////////////////////////////////////////////////////////////////////////
  // Perform PAA (Piecewise Aggregate Approximation)
  //////////////////////////////////////////////////////////////////////////////

  T paaTimeSeries((normalizedTimeSeries.size() - 1) / frameSize + 1);

  for (size_t i = 0; i < paaTimeSeries.size(); ++i) {
    double mean = 0.0;

    if (i == paaTimeSeries.size() - 1) {
      for (size_t j = i * frameSize; j < normalizedTimeSeries.size(); ++j) {
        mean += normalizedTimeSeries[j];
      }
      mean = mean / ((double)(normalizedTimeSeries.size() - i * frameSize));
    } else {
      for (size_t j = i * frameSize; j < (i+1) * frameSize; ++j) {
        mean += normalizedTimeSeries[j];
      }
      mean = mean / ((double)frameSize);
    }
    paaTimeSeries[i] = mean;
  }

  //////////////////////////////////////////////////////////////////////////////
  // Calculate breakpoints
  //////////////////////////////////////////////////////////////////////////////

  std::vector<double> breakpoints(alphabetSize - 1);
  boost::math::normal stdNormDist(0.0, 1.0); // standard normal distribution
  for (size_t i = 1; i < alphabetSize; ++i) {
    double q = quantile(stdNormDist, ((double) i) / ((double) alphabetSize));
    breakpoints[i-1] = q;
  }

  //////////////////////////////////////////////////////////////////////////////
  // Discretize
  //////////////////////////////////////////////////////////////////////////////

  std::vector<int> output(paaTimeSeries.size());
  // for each element of paaTimeSeries, map it to the corresponding symbol
  // as defined by the breakpoints, if breakpoints[i] < value < breakpoints[i+1],
  // then map it to symbol nom i+1
  for (size_t i = 0; i < output.size(); ++i) {
    for (size_t j = 0; j < breakpoints.size(); ++j) {
      output[i] = breakpoints.size();
      if (paaTimeSeries[i] < breakpoints[j]) {
        output[i] = j;
        break;
      }
    }
  }

  return output;
} // run()

} // namespace msax


/*
 * statistics_ext.cc
 *
 *  Copyright (C) 2013 Diamond Light Source
 *
 *  Author: James Parkhurst
 *
 *  This code is distributed under the BSD license, a copy of which is
 *  included in the root directory of this package.
 */
#include <boost/python.hpp>
#include <boost/python/def.hpp>
#include <boost/math/distributions/normal.hpp>
#include <dials/array_family/scitbx_shared_and_versa.h>
#include <dials/algorithms/statistics/kolmogorov_smirnov_one_sided_distribution.h>
#include <dials/algorithms/statistics/kolmogorov_smirnov_two_sided_distribution.h>
#include <dials/algorithms/statistics/kolmogorov_smirnov_test.h>
#include <dials/algorithms/statistics/poisson_test.h>
#include <dials/algorithms/statistics/correlation.h>
#include <dials/algorithms/statistics/binned_gmm.h>

namespace dials { namespace algorithms { namespace boost_python {

  using namespace boost::python;

  template <typename RealType>
  RealType kolmogorov_smirnov_one_sided_cdf(std::size_t n, RealType x) {
    return cdf(kolmogorov_smirnov_one_sided_distribution<RealType>(n), x);
  }

  template <typename RealType>
  RealType kolmogorov_smirnov_two_sided_cdf(std::size_t n, RealType x) {
    return cdf(kolmogorov_smirnov_two_sided_distribution<RealType>(),
               x * std::sqrt((double)n));
  }

  // template <typename RealType>
  // RealType kolmogorov_smirnov_one_sided_pdf(std::size_t n, RealType x) {
  // return pdf(kolmogorov_smirnov_one_sided_distribution<RealType>(n), x);
  //}

  template <typename RealType>
  boost::python::tuple kolmogorov_smirnov_test_standard_normal(
    const af::const_ref<RealType> &data,
    std::string type) {
    // Get the enumeration
    KSType etype = TwoSided;
    if (type.compare("less") == 0) {
      etype = Less;
    } else if (type.compare("greater") == 0) {
      etype = Greater;
    } else {
      DIALS_ASSERT(type.compare("two_sided") == 0);
    }

    // Perform the test
    std::pair<RealType, RealType> result =
      kolmogorov_smirnov_test(boost::math::normal_distribution<RealType>(0, 1),
                              data.begin(),
                              data.end(),
                              etype);
    return boost::python::make_tuple(result.first, result.second);
  }

  BOOST_PYTHON_MODULE(dials_algorithms_statistics_ext) {
    def("kolmogorov_smirnov_one_sided_cdf", &kolmogorov_smirnov_one_sided_cdf<double>);
    def("kolmogorov_smirnov_two_sided_cdf", &kolmogorov_smirnov_two_sided_cdf<double>);
    // def("kolmogorov_smirnov_one_sided_pdf",
    //    &kolmogorov_smirnov_one_sided_pdf<double>);
    def("kolmogorov_smirnov_test_standard_normal",
        &kolmogorov_smirnov_test_standard_normal<double>,
        (arg("data"), arg("type") = "two_sided"));

    def("poisson_expected_max_counts", &poisson_expected_max_counts);

    def("spearman_correlation_coefficient", &spearman_correlation_coefficient<double>);
    def("pearson_correlation_coefficient", &pearson_correlation_coefficient<double>);

    class_<BinnedGMMSingle1DFixedMean>("BinnedGMMSingle1DFixedMean", no_init)
      .def(init<const af::const_ref<double> &,
                const af::const_ref<double> &,
                const af::const_ref<double> &,
                double,
                double,
                double,
                std::size_t>())
      .def("max_iter", &BinnedGMMSingle1DFixedMean::max_iter)
      .def("num_iter", &BinnedGMMSingle1DFixedMean::num_iter)
      .def("epsilon", &BinnedGMMSingle1DFixedMean::epsilon)
      .def("mu", &BinnedGMMSingle1DFixedMean::mu)
      .def("sigma", &BinnedGMMSingle1DFixedMean::sigma);

    class_<BinnedGMMSingle1D>("BinnedGMMSingle1D", no_init)
      .def(init<const af::const_ref<double> &,
                const af::const_ref<double> &,
                const af::const_ref<double> &,
                double,
                double,
                double,
                std::size_t>())
      .def("max_iter", &BinnedGMMSingle1D::max_iter)
      .def("num_iter", &BinnedGMMSingle1D::num_iter)
      .def("epsilon", &BinnedGMMSingle1D::epsilon)
      .def("mu", &BinnedGMMSingle1D::mu)
      .def("sigma", &BinnedGMMSingle1D::sigma);
  }

}}}  // namespace dials::algorithms::boost_python


#include <boost/numeric/odeint.hpp>
#include <catch/catch.hpp>
#include <range/v3/all.hpp>

#include "../include/Plant.hpp"
#include "../include/control-frp.hpp"
#include "../include/pid.hpp"
#include "../include/util/util-sim.hpp"
#include "../include/util/util.hpp"

#include "calculations/analytical_solutions.cpp"

#ifdef PLOT
#include "../include/plotting/gnuplot-iostream.h"
#include "../include/plotting/plot-helpers.hpp"
#endif  // PLOT

using namespace ranges;
namespace ode = boost::numeric::odeint;

using CState = PIDState<>;                       // AKA U
using PState = SignalPt<std::array<double, 2>>;  // AKA X
// NB:
// PState = SignalPt<std::array<double, 2>>
//          ┌                ┐
//          │        ┌   ┐   │
//        = │  · ,   │ · │   │
//          │        │ · │   │
//          │        └   ┘   │
//          └                ┘
//            ^ Time   ^ [Position, Speed]
//
// On the other hand, sim::PState is just for Boost.odeint. It doesn't need
// time, but must be augmented with the control variable:
//
// sim::Pstate = std::array<double, 3>
//                ┌   ┐
//                │ · │  // Position
//             =  │ · │  // Speed
//                │ · │  // control variable for Boost.odeint.
//                └   ┘

constexpr double dt = 0.001;  // seconds.
constexpr auto dts = util::double_to_duration(dt);
const auto now = chrono::steady_clock::now();

constexpr double mass = 1.;
constexpr double damp = 10. / mass;
constexpr double spring = 20. / mass;
constexpr double staticForce = 1. / mass;
constexpr double simTime = 2;  // seconds
const sim::Plant plant(staticForce, damp, spring);
ode::runge_kutta4<sim::PState> stepper;

const CState u0 = {now, 0., 0., 0.};
const PState x0 = {now, {0., 0.}};

// controlled_step : (X, U) → X  = (PState, CState) → PState
const auto controlled_step = [](const PState& x, const CState& u) -> PState {
  // Remember: sim::PState is the timeless, augmented PState, not X. (See NB.)
  sim::PState xOld = {x.value[0], x.value[1], u.ctrlVal};
  sim::PState xNew = util::do_step_with(plant, stepper, dt, xOld);

  return {x.time + dts, {xNew[0], xNew[1]}};
};

// step_response_coalg : (X, U) → optional<((X, U), (X, U))>
const auto make_step_response_coalg = [](auto controller) {
  return
      [controller](const std::pair<PState, CState>& xu)
          -> std::optional<
              std::pair<std::pair<PState, CState>, std::pair<PState, CState>>> {
        const auto [x, u] = xu;
        constexpr double positionSetpoint = 1.;

        if (x.time > now + 2s) return {};

        const SignalPt<double> error = {x.time, x.value[0] - positionSetpoint};
        CState uNew = controller(u, error);
        PState xNew = controlled_step(x, uNew);

        return {{{x, u}, {xNew, uNew}}};
      };
};

TEST_CASE(
    "Given system and controller parameters, simulation should reproduce "
    "analytically computed step responses to within a margin of error. See "
    "src/calculations for details. Simulations performed using anamorphism.") {
  SECTION("Test A (Proportional Control) anamorphism.") {
    constexpr double Kp = 300.;
    constexpr double Ki = 0.;
    constexpr double Kd = 0.;

    auto step_response_coalg =
        make_step_response_coalg(pid_algebra(Kp, Ki, Kd));

    // result : std::vector<std::pair<PState, CState>>
    auto result = util::unfold(step_response_coalg, std::pair{x0, u0});

    {
      constexpr double margin = 0.03;
      auto simulatedPositions =
          util::fmap([](auto xu) { return xu.first.value[0]; }, result);
      auto theoreticalPositions = util::fmap(
          [](auto xu) {
            return analyt::test_A(util::unchrono_sec(xu.first.time - now));
          },
          result);

#ifdef PLOT

      const auto testData = util::fmap(
          [](const auto& xu) {
            return std::make_pair(util::unchrono_sec(xu.first.time - now),
                                  xu.first.value[0]);
          },
          result);

      plot_with_tube("Test A, (Kp, Ki, Kd) = (300, 0, 0).", testData,
                     &analyt::test_A, margin);

#endif  // PLOT

      REQUIRE(util::compareVectors(simulatedPositions, theoreticalPositions,
                                   margin));
    }
  }

  SECTION("Test B (Proportional-Derivative Control) anamorphism.") {
    constexpr double Kp = 300.;
    constexpr double Ki = 0.;
    constexpr double Kd = 10.;

    auto step_response_coalg =
        make_step_response_coalg(pid_algebra(Kp, Ki, Kd));

    // result : std::vector<std::pair<PState, CState>>
    auto result = util::unfold(step_response_coalg, std::pair{x0, u0});

    {
      constexpr double margin = 0.03;
      auto simulatedPositions =
          util::fmap([](auto xu) { return xu.first.value[0]; }, result);
      auto theoreticalPositions = util::fmap(
          [](auto xu) {
            return analyt::test_B(util::unchrono_sec(xu.first.time - now));
          },
          result);

#ifdef PLOT

      const auto testData = util::fmap(
          [](const auto& xu) {
            return std::make_pair(util::unchrono_sec(xu.first.time - now),
                                  xu.first.value[0]);
          },
          result);

      plot_with_tube("Test B, (Kp, Ki, Kd) = (300, 0, 10).", testData,
                     &analyt::test_B, margin);

#endif  // PLOT

      REQUIRE(util::compareVectors(simulatedPositions, theoreticalPositions,
                                   margin));
    }
  }

  SECTION("Test C (Proportional-Integral Control) anamorphism.") {
    constexpr double Kp = 30.;
    constexpr double Ki = 70.;
    constexpr double Kd = 0.;

    auto step_response_coalg =
        make_step_response_coalg(pid_algebra(Kp, Ki, Kd));

    // result : std::vector<std::pair<PState, CState>>
    auto result = util::unfold(step_response_coalg, std::pair{x0, u0});

    {
      constexpr double margin = 0.03;
      auto simulatedPositions =
          util::fmap([](auto xu) { return xu.first.value[0]; }, result);
      auto theoreticalPositions = util::fmap(
          [](auto xu) {
            return analyt::test_C(util::unchrono_sec(xu.first.time - now));
          },
          result);

#ifdef PLOT

      const auto testData = util::fmap(
          [](const auto& xu) {
            return std::make_pair(util::unchrono_sec(xu.first.time - now),
                                  xu.first.value[0]);
          },
          result);

      plot_with_tube("Test C, (Kp, Ki, Kd) = (30, 70, 0).", testData,
                     &analyt::test_C, margin);

#endif  // PLOT

      REQUIRE(util::compareVectors(simulatedPositions, theoreticalPositions,
                                   margin));
    }
  }

  SECTION("Test D (Proportional-Integral-Derivative Control) anamorphism.") {
    constexpr double Kp = 350.;
    constexpr double Ki = 300.;
    constexpr double Kd = 50.;

    auto step_response_coalg =
        make_step_response_coalg(pid_algebra(Kp, Ki, Kd));

    // result : std::vector<std::pair<PState, CState>>
    auto result = util::unfold(step_response_coalg, std::pair{x0, u0});

    {
      constexpr double margin = 0.07;
      auto simulatedPositions =
          util::fmap([](auto xu) { return xu.first.value[0]; }, result);
      auto theoreticalPositions = util::fmap(
          [](auto xu) {
            return analyt::test_D(util::unchrono_sec(xu.first.time - now));
          },
          result);

#ifdef PLOT

      const auto testData = util::fmap(
          [](const auto& xu) {
            return std::make_pair(util::unchrono_sec(xu.first.time - now),
                                  xu.first.value[0]);
          },
          result);

      plot_with_tube("Test D, (Kp, Ki, Kd) = (350, 300, 50).", testData,
                     &analyt::test_D, margin);

#endif  // PLOT

      REQUIRE(util::compareVectors(simulatedPositions, theoreticalPositions,
                                   margin));
    }
  }
}


#include <iostream>
#include <armadillo>

using namespace std;
using namespace arma;

int main()
{
  arma::mat output;
  arma::mat input;

  input = arma::zeros(16, 1);
  input(0) = 1.5;
  input(1) = 2.0;
  input(2) = 2.3;
  input(3) = 2.2;
  input(4) = 1.7;
  input(5) = 2.1;
  input(6) = 1.9;
  input(7) = 2.1;
  input(8) = 1.4;
  input(9) = 1.8;
  input(10) = 1.5;
  input(11) = 1.6;
  input(12) = 1.3;
  input(13) = 1.6;
  input(14) = 1.4;
  input(15) = 1.7;

  //input = arma::mat("1.5 2.0 2.3 2.2 1.7 2.1 1.9 2.1 1.4 1.8 1.5 1.6 1.3 1.6 1.4 1.7").t();

  cout << "-------------------------------------" << endl;
  cout << "INPUT : " << endl;
  cout << "Input shape : "<< input.n_rows << " " << input.n_cols << endl;
  cout << "Input (Transposed view) : " << input.t() << endl;
  cout << "-------------------------------------" << endl;

  const size_t kernelWidth = 2;
  const size_t kernelHeight = 2;
  const size_t strideWidth = 2;
  const size_t strideHeight = 2;
  const bool floor = true;
  size_t inSize = 0;
  size_t outSize = 0;
  bool reset = false;
  size_t inputWidth = 0;
  size_t inputHeight = 0;
  size_t outputWidth = 0;
  size_t outputHeight = 0;
  bool deterministic = false;
  size_t offset = 0;
  size_t batchSize = 0;

  inputHeight = 4;
  inputWidth = 4;
  batchSize = input.n_cols;
  inSize = input.n_elem / (inputWidth * inputHeight * batchSize);

  // cube(ptr_aux_mem, n_rows, n_cols, n_slices, copy_aux_mem = true, strict = false)
  arma::cube inputTemp = arma::cube(const_cast<arma::mat &>(input).memptr(),
      inputWidth, inputHeight, batchSize * inSize, false, false);

  cout << "inputTemp BEFORE POOLING: " << endl;
  cout << "inputTemp shape : "<< inputTemp.n_rows << " " << inputTemp.n_cols << endl;
  cout << "Num slices   : " << inputTemp.n_slices << endl;
  cout << inputTemp << endl;
  cout << "-------------------------------------" << endl;

  outputWidth = std::floor(((inputWidth - (double) kernelWidth) / (double) strideWidth) + 1);
  outputHeight = std::floor(((inputHeight - (double) kernelHeight) / (double) strideHeight) + 1);
  offset = 0;

  arma::cube outputTemp = arma::zeros<arma::cube>(outputWidth, outputHeight, batchSize * inSize);

  cout << "outputTemp BEFORE POOLING: " << endl;
  cout << "outputTemp shape : "<< outputTemp.n_rows << " " << outputTemp.n_cols << endl;
  cout << outputTemp << endl;
  cout << "-------------------------------------" << endl;

  std::vector<arma::cube> poolingIndices;
  poolingIndices.push_back(outputTemp);

  arma::Mat<size_t> indices;
  arma::Col<size_t> indicesCol;

  size_t elements = inputWidth * inputHeight;
  indicesCol = arma::linspace<arma::Col<size_t> >(0, (elements - 1), elements);
  indices = arma::Mat<size_t>(indicesCol.memptr(), inputWidth, inputHeight);
  reset = true;

  cout << "All parameters of layer : " << endl;
  cout << "kernelWidth   : " << kernelWidth << endl;
  cout << "kernelHeight  : " << kernelHeight << endl;
  cout << "strideWidth   : " << strideWidth << endl;
  cout << "strideHeight  : " << strideHeight << endl;
  cout << "inputWidth    : " << inputWidth << endl;
  cout << "inputHeight   : " << inputHeight << endl;
  cout << "outputWidth   : " << outputWidth << endl;
  cout << "outputHeight  : " << outputHeight << endl;
  cout << "batchSize     : " << batchSize << endl;
  cout << "inSize        : " << inSize << endl;
  cout << "outSize       : " << batchSize * inSize << endl;
  cout << "offset        : " << offset << endl;
  cout << "-------------------------------------" << endl;

  cout << "Calculations done inside (reset) conditional statement : " << endl;
  cout << "indicesCol : " << endl;
  cout << "indicesCol shape : "<< indicesCol.n_rows << " " << indicesCol.n_cols << endl;
  cout << "indicesCol (Transposed view) : " << indicesCol.t() << endl;
  cout << "indices : " << endl;
  cout << "indices shape : "<< indices.n_rows << " " << indices.n_cols << endl;
  cout << indices << endl;
  cout << "-------------------------------------" << endl;

  for (size_t s = 0; s < inputTemp.n_slices; s++)
  {
    // PoolingOperation(inputTemp.slice(s), outputTemp.slice(s),poolingIndices.back().slice(s));

    const arma::mat& in = inputTemp.slice(s);
    arma::mat& out = outputTemp.slice(s);
    arma::mat& poolIndexes = poolingIndices.back().slice(s);

    cout << "poolingIndices FOR SLICE " << s << " BEFORE POOLING: " << endl;
    cout << poolingIndices.back().slice(s)<< endl;
    cout << "-------------------------------------" << endl;

    cout << "POOLING OPERATIONS START NOW." << endl;
    cout << "-------------------------------------" << endl;

    size_t x = 0;
    for (size_t j = 0, colidx = 0; j < out.n_cols; ++j, colidx += strideHeight)
    {
      size_t y = 0;
      for (size_t i = 0, rowidx = 0; i < out.n_rows; ++i, rowidx += strideWidth)
      {

        arma::mat subInput = in(
            arma::span(rowidx, rowidx + kernelWidth - 1 - offset),
            arma::span(colidx, colidx + kernelHeight - 1 - offset)
        );

        // const size_t idx = pooling.Pooling(subInput);
        const size_t idx = arma::as_scalar(arma::find(subInput.max() == subInput, 1));
        out(i, j) = subInput(idx);

        arma::Mat<size_t> subIndices = indices(
            arma::span(rowidx, rowidx + kernelWidth - 1 - offset),
            arma::span(colidx, colidx + kernelHeight - 1 - offset)
        );

        poolIndexes(i, j) = subIndices(idx);

        cout << "COUNTER (" << x << ", " << y << ") CALCULATIONS : " << endl;
        cout << "subInput : " << endl;
        cout << subInput << endl;
        cout << "subIndices : " << endl;
        cout << subIndices << endl;
        cout << "idx : " << idx << endl;
        cout << "subInput(idx) : " << endl;
        cout << subInput(idx) << endl;
        cout << "subIndices(idx) : " << endl;
        cout << subIndices(idx) << endl;
        cout << "-------------------------------------" << endl;
        y++;
      }
      x++;
    }

  cout << "POOLING OPERATIONS END NOW." << endl;
  cout << "-------------------------------------" << endl;

  cout << "poolingIndices FOR SLICE " << s << " POST POOLING: " << endl;
  cout << poolingIndices.back().slice(s)<< endl;
  cout << "-------------------------------------" << endl;

  }

  cout << "inputTemp AFTER POOLING: " << endl;
  cout << "inputTemp shape : "<< inputTemp.n_rows << " " << inputTemp.n_cols << endl;
  cout << "Num slices   : " << inputTemp.n_slices << endl;
  cout << inputTemp << endl;
  cout << "-------------------------------------" << endl;

  cout << "outputTemp AFTER POOLING: " << endl;
  cout << "outputTemp shape : "<< outputTemp.n_rows << " " << outputTemp.n_cols << endl;
  cout << outputTemp << endl;
  cout << "-------------------------------------" << endl;

  output = arma::mat(outputTemp.memptr(), outputTemp.n_elem / batchSize, batchSize);
  outputWidth = outputTemp.n_rows;
  outputHeight = outputTemp.n_cols;
  outSize = batchSize * inSize;

  cout << "OUTPUT : " << endl;
  cout << "Output shape : "<< output.n_rows << " " << output.n_cols << endl;
  cout << "Output (Transposed view) : " << output.t() << endl;
  cout << "-------------------------------------" << endl;

  cout << "-------------------------------------" << endl;
  cout << "PSEUDO BACKWARD : " << endl;
  cout << "-------------------------------------" << endl;

  arma::cube gTemp;
  gTemp = arma::zeros<arma::cube>(inputTemp.n_rows, inputTemp.n_cols, inputTemp.n_slices);

  arma::cube mappedError;
  mappedError = arma::ones<arma::cube>(outputWidth, outputHeight, outSize);

  for (size_t s = 0; s < mappedError.n_slices; s++)
  {
    //Unpooling(mappedError.slice(s), gTemp.slice(s), poolingIndices.back().slice(s));

    const arma::mat& error = mappedError.slice(s);
    arma::mat& out = gTemp.slice(s);
    arma::mat& poIdx = poolingIndices.back().slice(s);

    for (size_t i = 0; i < poIdx.n_elem; ++i)
    {
      out(poIdx(i)) += error(i);
    }

    cout << "OUTPUT : " << endl;
    cout << "Output shape : "<< out.n_rows << " " << out.n_cols << endl;
    cout << "Output (Transposed view) : " << endl << out << endl;
    cout << "-------------------------------------" << endl;
  }

  cout << "-------------------------------------" << endl;
  cout << "UNPOOLING : " << endl;
  cout << "-------------------------------------" << endl;

  // Output of MaxPool is the input for UnPooling.
  input = arma::zeros(4, 1);
  input(0) = 2.1;
  input(1) = 2.3;
  input(2) = 1.8;
  input(3) = 1.7;

  cout << "-------------------------------------" << endl;
  cout << "INPUT FOR UNPOOLING: " << endl;
  cout << "Input shape : "<< input.n_rows << " " << input.n_cols << endl;
  cout << "Input (Transposed view) : " << input.t() << endl;
  cout << "-------------------------------------" << endl;

  inSize = 0;
  outSize = 0;
  reset = false;
  inputWidth = 0;
  inputHeight = 0;
  outputWidth = 0;
  outputHeight = 0;
  batchSize = 0;

  inputHeight = 2;
  inputWidth = 2;
  batchSize = input.n_cols;
  inSize = input.n_elem / (inputWidth * inputHeight * batchSize);

  // cube(ptr_aux_mem, n_rows, n_cols, n_slices, copy_aux_mem = true, strict = false)
  inputTemp = arma::cube(const_cast<arma::mat &>(input).memptr(),
      inputWidth, inputHeight, batchSize * inSize, false, false);

  cout << "inputTemp BEFORE UNPOOLING: " << endl;
  cout << "inputTemp shape : "<< inputTemp.n_rows << " " << inputTemp.n_cols << endl;
  cout << "Num slices   : " << inputTemp.n_slices << endl;
  cout << inputTemp << endl;
  cout << "-------------------------------------" << endl;

  outputWidth = (inputWidth - 1) * strideWidth + kernelWidth;
  outputHeight = (inputHeight - 1) * strideHeight + kernelHeight;
  outSize = batchSize * inSize;
  outputTemp = arma::zeros<arma::cube>(outputWidth, outputHeight,
      outSize);

  cout << "outputTemp BEFORE UNPOOLING: " << endl;
  cout << "outputTemp shape : "<< outputTemp.n_rows << " " << outputTemp.n_cols << endl;
  cout << outputTemp << endl;
  cout << "-------------------------------------" << endl;

  for (size_t s = 0; s < inputTemp.n_slices; s++)
  {
    // Unpooling(inputTemp.slice(s), outputTemp.slice(s), poolingIndices.back().slice(s));
    const arma::mat& IN = inputTemp.slice(s);
    arma::mat& OUT = outputTemp.slice(s);
    arma::mat& INDICES = poolingIndices.back().slice(s);

    for (size_t i = 0; i < INDICES.n_elem; ++i)
    {
      OUT(INDICES(i)) += IN(i);
    }

    cout << "OUTPUT : " << endl;
    cout << "Output shape : "<< OUT.n_rows << " " << OUT.n_cols << endl;
    cout << "Output : " << endl << OUT << endl;
    cout << "-------------------------------------" << endl;
  }

  cout << "inputTemp AFTER UNPOOLING: " << endl;
  cout << "inputTemp shape : "<< inputTemp.n_rows << " " << inputTemp.n_cols << endl;
  cout << "Num slices   : " << inputTemp.n_slices << endl;
  cout << inputTemp << endl;
  cout << "-------------------------------------" << endl;

  cout << "outputTemp AFTER UNPOOLING: " << endl;
  cout << "outputTemp shape : "<< outputTemp.n_rows << " " << outputTemp.n_cols << endl;
  cout << outputTemp << endl;
  cout << "-------------------------------------" << endl;

  cout << "-------------------------------------" << endl;
  cout << "UNPOOLING BACKWARD (PROOF OF CONCEPT): " << endl;
  cout << "-------------------------------------" << endl;

  arma::mat L;
  L = arma::zeros(16, 1);
  L(0) = 1;
  L(1) = 2;
  L(2) = 3;
  L(3) = 4;
  L(4) = 5;
  L(5) = 6;
  L(6) = 7;
  L(7) = 8;
  L(8) = 9;
  L(9) = 10;
  L(10) = 11;
  L(11) = 12;
  L(12) = 13;
  L(13) = 14;
  L(14) = 15;
  L(15) = 16;

  mappedError = arma::cube(const_cast<arma::mat &>(L).memptr(), outputWidth, outputHeight, outSize, false, false);
  gTemp = arma::zeros<arma::cube>(inputWidth, inputHeight, outSize);

  for (size_t s = 0; s < gTemp.n_slices; s++)
  {
    arma::mat& gySlice = mappedError.slice(s);
    arma::mat& gSlice = gTemp.slice(s);
    arma::mat& idxs = poolingIndices.back().slice(s);

    cout << "gySlice: " << endl;
    cout << "gy shape : "<< gySlice.n_rows << " " << gySlice.n_cols << endl;
    cout << gySlice << endl;
    cout << "-------------------------------------" << endl;

    cout << "gTempSlice BEFORE: " << endl;
    cout << "g shape : "<< gSlice.n_rows << " " << gSlice.n_cols << endl;
    cout << gSlice << endl;
    cout << "-------------------------------------" << endl;

    for (size_t i = 0, j = 0; i < idxs.n_elem; ++i, ++j)
    {
      gSlice(j) = gySlice(idxs(i));
    }

    cout << "gTempSlice AFTER: " << endl;
    cout << "g shape : "<< gSlice.n_rows << " " << gSlice.n_cols << endl;
    cout << gSlice << endl;
    cout << "-------------------------------------" << endl;
  }

  arma::mat g = arma::mat(gTemp.memptr(), gTemp.n_elem / batchSize, batchSize);
  cout << "g: " << endl;
  cout << "g shape : "<< g.n_rows << " " << g.n_cols << endl;
  cout << g << endl;
  cout << "-------------------------------------" << endl;

  return 0;
}


#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "vnl/vnl_matrix_ref.h"
#include "vnl/vnl_matrix.h"
#include "itkNumericTraits.h"

#include "tkdCmdParser.h"

#include <iostream>
#include <utility>
#include <fstream>

#include <boost/utility.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/betweenness_centrality.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>

/**
 * (Relative) Betweenness centrality.
 */
template< class DirectedProperty = boost::undirectedS >
class BC
{
public:

	typedef double PixelType;

	typedef boost::property< boost::vertex_index_t, unsigned int, boost::property< boost::vertex_centrality_t, PixelType > > VertexPropertyType;
	typedef boost::property< boost::edge_weight_t, PixelType, boost::property< boost::edge_centrality_t, PixelType > > EdgePropertyType;
	typedef boost::adjacency_matrix< DirectedProperty, VertexPropertyType, EdgePropertyType > GraphMatrixType;

	typedef itk::Image< PixelType, 2 > ImageType;
	typedef vnl_matrix_ref< PixelType > DataMatrixType;
	typedef vnl_vector< PixelType > VectorType;

	/**
	 * Run.
	 */
	void Run( const std::string& filename, PixelType lowerThreshold, PixelType upperThreshold, const std::string& outputFileName,
			bool relative, bool dominance )
	{
		typedef itk::ImageFileReader< ImageType > ReaderType;
		typename ReaderType::Pointer reader = ReaderType::New();
		reader->SetFileName( filename.c_str() );
		reader->Update();

		typename ImageType::Pointer image = reader->GetOutput();
		reader = 0;

		PixelType* buffer = image->GetPixelContainer()->GetBufferPointer();
		typename ImageType::RegionType region = image->GetLargestPossibleRegion();
		typename ImageType::SizeType size = region.GetSize();
		int rows = size[0];
		int cols = size[1];

		DataMatrixType data( rows, cols, buffer );

		typename ImageType::Pointer output = ImageType::New();
		output->CopyInformation( image );
		output->SetRegions( image->GetLargestPossibleRegion() );
		output->Allocate();
		output->FillBuffer( 0 );

		DataMatrixType distance( rows, cols, output->GetPixelContainer()->GetBufferPointer() );

		GraphMatrixType g( rows );

		/* TEST List -> weighted version works, and indeed lower values are shorter paths,
		GraphMatrixType g( 5 );
		boost::add_edge( 0, 1, EdgePropertyType( 1.0 ), g );
		boost::add_edge( 1, 4, EdgePropertyType( 0.2 ), g );
		boost::add_edge( 4, 3, EdgePropertyType( 0.3 ), g );
		boost::add_edge( 0, 2, EdgePropertyType( 1.0 ), g );
		boost::add_edge( 2, 3, EdgePropertyType( 1.0 ), g );
		*/

		const bool isUndirected = typeid(DirectedProperty) == typeid(boost::undirectedS);
		for ( int i = 0; i < rows; ++i )
		{
			int start = isUndirected ? i + 1 : 0;
			for ( int j = start; j < cols; ++j )
			{
				if ( i == j )
				{
					continue;
				}

				PixelType weight = data( i, j );

				if ( weight <= upperThreshold && weight > 0 && weight > lowerThreshold )
				{
					// bc makes use of fwsp, so invert weight (correlation coefficients ...)
					boost::add_edge( i, j, EdgePropertyType( 1.0 / weight ), g );
				}
			}
		}

		VectorType centrality( boost::num_vertices( g ) );
		typename boost::graph_traits< GraphMatrixType >::vertex_iterator vi, vi_end;

		boost::brandes_betweenness_centrality( g,
				centrality_map( boost::get( boost::vertex_centrality, g ) ).
				edge_centrality_map( boost::get( boost::edge_centrality, g ) ).
				weight_map( boost::get(
						boost::edge_weight, g ) ) );

		if ( !relative )
		{
			typename boost::property_map< GraphMatrixType, boost::vertex_centrality_t >::type b = boost::get( boost::vertex_centrality, g );

			for ( tie( vi, vi_end ) = vertices( g ); vi != vi_end; ++vi )
				centrality[*vi] = b[*vi];
		}
		else
		{
			boost::relative_betweenness_centrality( g, boost::get( boost::vertex_centrality, g ) );
			typename boost::property_map< GraphMatrixType, boost::vertex_centrality_t >::type r = boost::get( boost::vertex_centrality, g );

			for ( boost::tie( vi, vi_end ) = vertices( g ); vi != vi_end; ++vi )
				centrality[*vi] = r[*vi];

			// Central point dominance is correct if and only if betweenness centrality is relative!
			if ( dominance )
			{
				double dominance = boost::central_point_dominance( g, boost::get( boost::vertex_centrality, g ) );
				std::cout << "Dominance," << dominance << std::endl;
			}
		}

		WriteVectorToFile( outputFileName, centrality );
	}

	/**
	 * Write vector to filename.
	 */
	void WriteVectorToFile( const std::string& outputFileName, const VectorType& V )
	{
		std::ofstream out( outputFileName.c_str() );

		if ( out.fail() )
		{
			std::cerr << "*** ERROR ***: Not able to write to: " << outputFileName << "!" << std::endl;
			exit( EXIT_FAILURE );
		}

		for ( unsigned int i = 0; i < V.size(); i++ )
			out << V[i] << std::endl;

		out.close();
	}
};

/**
 * Betweenness centrality.
 */
int main( int argc, char ** argv )
{
	tkd::CmdParser p( "betweenness centrality", "Calculate betweenness centrality measure for all vertices" );

	std::string inputFileName;
	std::string outputFileName;

	float lowerThreshold = 0;
	float upperThreshold = 1.0;
	bool isUndirected = true;
	bool relative = true;
	bool dominance = false;

	p.AddArgument( inputFileName, "input" ) ->AddAlias( "i" ) ->SetDescription( "Input image: 2D adjacency matrix" ) ->SetRequired( true );

	p.AddArgument( upperThreshold, "threshold" ) ->AddAlias( "thu" ) ->AddAlias( "t" ) ->SetDescription(
			"Threshold; only include paths with weight in (0, threshold] (default: 1.0)" );

	p.AddArgument( lowerThreshold, "threshold-lower" ) ->AddAlias( "thl" ) ->SetDescription(
			"Lower threshold; only include paths with weight > threshold (default: 0)" );

	p.AddArgument( outputFileName, "output" ) ->AddAlias( "o" ) ->SetDescription(
			"Output image: List with (relative) betweenness centralities per vertex" ) ->SetRequired( true );

	p.AddArgument( isUndirected, "undirected" ) ->AddAlias( "u" ) ->SetDescription(
			"Input is a symmetric matrix representing an undirected graph [default: true]" );

	p.AddArgument( relative, "relative" ) ->AddAlias( "r" ) ->SetDescription( "Relative betweenness centrality algorithm [default: true]" );

	p.AddArgument( dominance, "dominance" ) ->AddAlias( "d" ) ->SetDescription( "Print central point dominance [relative only, default: false]" );

	if ( !p.Parse( argc, argv ) )
	{
		p.PrintUsage( std::cout );
		return -1;
	}

	if ( isUndirected )
	{
		BC< boost::undirectedS > bc;
		bc.Run( inputFileName, lowerThreshold, upperThreshold, outputFileName, relative, dominance );
	} else
	{
		BC< boost::directedS > bc;
		bc.Run( inputFileName, lowerThreshold, upperThreshold, outputFileName, relative, dominance );
	}

	return EXIT_SUCCESS;
}


/*! \file cmsFundTrust.hpp
    \brief cms funding trust pricer - wrapper for excel export (Francis Duffy)
	Peter Caspers
*/

#include <ql/quantlib.hpp>

#include <qle/models/hullwhite1.hpp>
#include <boost/math/distributions/normal.hpp>
#include <math.h>
#include <qle/instruments/capfloorcmsswap.hpp>
#include <qle/pricingengines/treecapfloorcmsswapengine.hpp>
#include <qle/instruments/capfloorcmsswaption.hpp>
#include <qle/pricingengines/treecapfloorcmsswaptionengine.hpp>

#ifndef quantlib_cmsFundTrust_hpp
#define quantlib_cmsFundTrust_hpp

using namespace boost;
using namespace std;

namespace QuantLib {

	/*! pricing for cms funding trust positions as developed by Francis Duffy */

	class CmsFundTrust {

		public:

			/*! constructor */
			CmsFundTrust(const boost::shared_ptr<YieldTermStructure> yts, const boost::shared_ptr<SwaptionVolatilityStructure> swVol, const double meanRev, const double cmsMeanRev);

			/*! get results */
			vector<double> result(string tag);

		private:

			void calculate();

			boost::shared_ptr<YieldTermStructure> yts_;
			boost::shared_ptr<SwaptionVolatilityStructure> swVol_;
			double meanRev_, cmsMeanRev_;

			map<string,vector<double>> results_;

	};

	namespace cmsFundTrustHelper {

		Real cmsSwapletIntegrand(boost::shared_ptr<QuantExt::HullWhite1> aHw1, boost::shared_ptr<CmsCoupon> aCmsCoupon,
				Real cmsCap, Real cmsFloor, Handle<YieldTermStructure> aTermStructure, Real r);

		Real cmsIntegralValue(boost::shared_ptr<QuantExt::HullWhite1> aHw1, boost::shared_ptr<CmsCoupon> aCmsCoupon,
				Real cmsCap, Real cmsFloor, Handle<YieldTermStructure> aTermStructure, vector<Date> & volDates, vector<Real> & sigmas);

		Real solveVolFunc(boost::shared_ptr<QuantExt::HullWhite1> aHw1, boost::shared_ptr<CmsCoupon> aCmsCoupon,
				Real cmsCap, Real cmsFloor, Handle<YieldTermStructure> aTermStructure, Real aCmsRate, vector<Date> & volDates,
				vector<Real> sigmas, Real aVol);

	}

}

#endif


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

#ifdef _MSC_VER
#  define _SCL_SECURE_NO_WARNINGS
#endif

#include <boost/detail/lightweight_test.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/modf.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/random/mersenne_twister.hpp>
#include "test.hpp"

#if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_MPZ) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPQ)
#  define TEST_MPF_50
#  define TEST_MPFR_50
#  define TEST_BACKEND
#  define TEST_CPP_DEC_FLOAT

#ifdef _MSC_VER
#pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
#endif
#ifdef __GNUC__
#pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
#endif

#endif

#if defined(TEST_MPF_50)
#include <boost/multiprecision/gmp.hpp>
#endif
#ifdef TEST_MPFR_50
#include <boost/multiprecision/mpfr.hpp>
#endif
#ifdef TEST_BACKEND
#include <boost/multiprecision/concepts/mp_number_archetypes.hpp>
#endif
#ifdef TEST_CPP_DEC_FLOAT
#include <boost/multiprecision/cpp_dec_float.hpp>
#endif

#ifdef BOOST_MSVC
#pragma warning(disable:4127)
#endif

boost::mt19937 rng;

template <class T>
T get_random()
{
   //
   // Fill all the bits in T with random values,
   // likewise set the exponent to a random value
   // that will still fit inside a T, and always
   // have a remainder as well as an integer part.
   //
   int bits = boost::math::tools::digits<T>();
   int shift = 0;
   int exponent = rng() % (bits - 4);
   T result = 0;
   while(bits > 0)
   {
      result += ldexp(static_cast<T>(rng()), shift);
      shift += std::numeric_limits<int>::digits;
      bits -= std::numeric_limits<int>::digits;
   }
   return rng() & 1u ? T(-ldexp(frexp(result, &bits), exponent)) : T(ldexp(frexp(result, &bits), exponent));
}

template <class T, class U>
void check_within_half(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) > 0.5f)
   {
      BOOST_ERROR("Rounded result differed by more than 0.5 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if((fabs(a - u) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
   {
      BOOST_ERROR("Rounded result was towards zero with boost::round");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

//
// We may not have an abs overload for long long so provide a fall back:
//
template <class T>
inline T safe_abs(T const& v ...)
{
   return v < 0 ? -v : v;
}

template <class T, class U>
void check_trunc_result(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(abs(a) < safe_abs(u))
   {
      BOOST_ERROR("Truncated result had larger absolute value than the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(fabs(static_cast<T>(u)) > fabs(a))
   {
      BOOST_ERROR("Rounded result was away from zero with boost::trunc");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

template <class T, class U>
void check_modf_result(T a, T fract, U ipart)
{
   BOOST_MATH_STD_USING
   if(fract + ipart != a)
   {
      BOOST_ERROR("Fractional and integer results do not add up to the original value");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(fract)) && boost::math::sign(fract))
   {
      BOOST_ERROR("Original and fractional parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(ipart)) && boost::math::sign(ipart))
   {
      BOOST_ERROR("Original and integer parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << ipart << std::endl;
   }
   if(fabs(a-ipart) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << ipart << std::endl;
   }
}

template <class T>
void test()
{
   BOOST_MATH_STD_USING

   for(int i = 0; i < 1000; ++i)
   {
      T arg = get_random<T>();
      T r = round(arg);
      check_within_half(arg, r);
      BOOST_TEST(r == round(arg + 0));
      r = trunc(arg);
      check_trunc_result(arg, r);
      BOOST_TEST(r == trunc(arg + 0));
      T frac = modf(arg, &r);
      check_modf_result(arg, frac, r);

      if(abs(r) < (std::numeric_limits<int>::max)())
      {
         int i = iround(arg);
         check_within_half(arg, i);
         BOOST_TEST(i == iround(arg + 0));
         i = itrunc(arg);
         check_trunc_result(arg, i);
         BOOST_TEST(i == itrunc(arg + 0));
         r = modf(arg, &i);
         check_modf_result(arg, r, i);
      }
      if(abs(r) < (std::numeric_limits<long>::max)())
      {
         long l = lround(arg);
         check_within_half(arg, l);
         BOOST_TEST(l == lround(arg + 0));
         l = ltrunc(arg);
         check_trunc_result(arg, l);
         BOOST_TEST(l == ltrunc(arg + 0));
         r = modf(arg, &l);
         check_modf_result(arg, r, l);
      }

#ifdef BOOST_HAS_LONG_LONG
      if(abs(r) < (std::numeric_limits<boost::long_long_type>::max)())
      {
         boost::long_long_type ll = llround(arg);
         check_within_half(arg, ll);
         BOOST_TEST(ll == llround(arg + 0));
         ll = lltrunc(arg);
         check_trunc_result(arg, ll);
         BOOST_TEST(ll == lltrunc(arg + 0));
         r = modf(arg, &ll);
         check_modf_result(arg, r, ll);
      }
#endif
   }
   //
   // Test boundary cases:
   //
   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      int si = iround(static_cast<T>((std::numeric_limits<int>::max)()));
      check_within_half(static_cast<T>((std::numeric_limits<int>::max)()), si);
      BOOST_TEST(si == iround(static_cast<T>((std::numeric_limits<int>::max)()) + 0));
      si = iround(static_cast<T>((std::numeric_limits<int>::min)()));
      check_within_half(static_cast<T>((std::numeric_limits<int>::min)()), si);
      BOOST_TEST(si == iround(static_cast<T>((std::numeric_limits<int>::min)()) + 0));
      si = itrunc(static_cast<T>((std::numeric_limits<int>::max)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<int>::max)()), si);
      BOOST_TEST(si == itrunc(static_cast<T>((std::numeric_limits<int>::max)()) + 0));
      si = itrunc(static_cast<T>((std::numeric_limits<int>::min)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<int>::min)()), si);
      BOOST_TEST(si == itrunc(static_cast<T>((std::numeric_limits<int>::min)()) + 0));

      si = iround(static_cast<T>((std::numeric_limits<int>::max)() - 1));
      check_within_half(static_cast<T>((std::numeric_limits<int>::max)() - 1), si);
      si = iround(static_cast<T>((std::numeric_limits<int>::min)() + 1));
      check_within_half(static_cast<T>((std::numeric_limits<int>::min)() + 1), si);
      si = itrunc(static_cast<T>((std::numeric_limits<int>::max)() - 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<int>::max)() - 1), si);
      si = itrunc(static_cast<T>((std::numeric_limits<int>::min)() + 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<int>::min)() + 1), si);
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      long k = lround(static_cast<T>((std::numeric_limits<long>::max)()));
      check_within_half(static_cast<T>((std::numeric_limits<long>::max)()), k);
      BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
      k = lround(static_cast<T>((std::numeric_limits<long>::min)()));
      check_within_half(static_cast<T>((std::numeric_limits<long>::min)()), k);
      BOOST_TEST(k == lround(static_cast<T>((std::numeric_limits<long>::min)()) + 0));
      k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)()), k);
      BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 0));
      k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)()), k);
      BOOST_TEST(k == ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) + 0));

      k = lround(static_cast<T>((std::numeric_limits<long>::max)() - 1));
      check_within_half(static_cast<T>((std::numeric_limits<long>::max)() - 1), k);
      k = lround(static_cast<T>((std::numeric_limits<long>::min)() + 1));
      check_within_half(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
      k = ltrunc(static_cast<T>((std::numeric_limits<long>::max)() - 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<long>::max)() - 1), k);
      k = ltrunc(static_cast<T>((std::numeric_limits<long>::min)() + 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<long>::min)() + 1), k);
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
   {
      boost::long_long_type j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
      check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
      BOOST_TEST(j == llround(static_cast<T>((std::numeric_limits<long long>::max)()) + 0));
      j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
      check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
      BOOST_TEST(j == llround(static_cast<T>((std::numeric_limits<long long>::min)()) + 0));
      j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()), j);
      BOOST_TEST(j == lltrunc(static_cast<T>((std::numeric_limits<long long>::max)()) + 0));
      j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()));
      check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()), j);
      BOOST_TEST(j == lltrunc(static_cast<T>((std::numeric_limits<long long>::min)()) + 0));

      j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1));
      check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1), j);
      j = llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1));
      check_within_half(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1), j);
      j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)() - 1), j);
      j = lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1));
      check_trunc_result(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)() + 1), j);
   }
#endif
   //
   // Finish off by testing the error handlers:
   //
   BOOST_CHECK_THROW(static_cast<T>(iround(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(iround(static_cast<T>(-1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(lround(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(lround(static_cast<T>(-1e20))), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(static_cast<T>(llround(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(llround(static_cast<T>(-1e20))), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(static_cast<T>(round(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(iround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(iround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(static_cast<T>(llround(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(llround(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(static_cast<T>(round(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(iround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(static_cast<T>(llround(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
   #endif
   }
   BOOST_CHECK_THROW(static_cast<T>(itrunc(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(itrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(ltrunc(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(ltrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(static_cast<T>(lltrunc(static_cast<T>(1e20))), boost::math::rounding_error);
   BOOST_CHECK_THROW(static_cast<T>(lltrunc(static_cast<T>(-1e20))), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(static_cast<T>(trunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(itrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(itrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(ltrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(ltrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(static_cast<T>(lltrunc(std::numeric_limits<T>::infinity())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lltrunc(-std::numeric_limits<T>::infinity())), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(static_cast<T>(trunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(itrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(ltrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(static_cast<T>(lltrunc(std::numeric_limits<T>::quiet_NaN())), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(itrunc(static_cast<T>((std::numeric_limits<int>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(itrunc(static_cast<T>((std::numeric_limits<int>::min)()) - 1)), boost::math::rounding_error);
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(ltrunc(static_cast<T>((std::numeric_limits<long>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(ltrunc(static_cast<T>((std::numeric_limits<long>::min)()) - 1)), boost::math::rounding_error);
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lltrunc(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1)), boost::math::rounding_error);
   }
#endif
   if(std::numeric_limits<T>::digits >= std::numeric_limits<int>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(iround(static_cast<T>((std::numeric_limits<int>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(iround(static_cast<T>((std::numeric_limits<int>::min)()) - 1)), boost::math::rounding_error);
   }
   if(std::numeric_limits<T>::digits >= std::numeric_limits<long>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(lround(static_cast<T>((std::numeric_limits<long>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(lround(static_cast<T>((std::numeric_limits<long>::min)()) - 1)), boost::math::rounding_error);
   }
#ifndef BOOST_NO_LONG_LONG
   if(std::numeric_limits<T>::digits >= std::numeric_limits<boost::long_long_type>::digits)
   {
      BOOST_CHECK_THROW(static_cast<T>(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::max)()) + 1)), boost::math::rounding_error);
      BOOST_CHECK_THROW(static_cast<T>(llround(static_cast<T>((std::numeric_limits<boost::long_long_type>::min)()) - 1)), boost::math::rounding_error);
   }
#endif
}

int main()
{
#ifdef TEST_MPF_50
   test<boost::multiprecision::mpf_float_50>();
   test<boost::multiprecision::mpf_float_100>();
#endif
#ifdef TEST_MPFR_50
   test<boost::multiprecision::mpfr_float_50>();
   test<boost::multiprecision::mpfr_float_100>();
#endif
#ifdef TEST_CPP_DEC_FLOAT
   test<boost::multiprecision::cpp_dec_float_50>();
   test<boost::multiprecision::cpp_dec_float_100>();
#ifndef SLOW_COMPLER
   // Some "peculiar" digit counts which stress our code:
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<65> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<64> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<63> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<62> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<61, long long> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<60, long long> > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<59, long long, std::allocator<void> > > >();
   test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<58, long long, std::allocator<void> > > >();
#endif
#endif
#ifdef TEST_BACKEND
   test<boost::multiprecision::number<boost::multiprecision::concepts::number_backend_float_architype> >();
#endif
   return boost::report_errors();
}