Skip to content
Snippets Groups Projects
Select Git revision
  • 2c4b0cf7b69acb54c5cfbf60c1e4ccde42f7c719
  • main default protected
2 results

MathUtils.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    MathUtils.cpp 28.11 KiB
    /*
     * MathUtils.cpp
     *
     * Author:
     *       Oleg Kalashev
     *
     * Copyright (c) 2020 Institute for Nuclear Research, RAS
     *
     * 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.
     */
    
    #ifdef USE_BOOST
    
    #include <boost/numeric/odeint/config.hpp>
    
    #include <boost/numeric/odeint.hpp>
    #include <boost/numeric/odeint/stepper/bulirsch_stoer.hpp>
    #include <boost/numeric/odeint/stepper/bulirsch_stoer_dense_out.hpp>
    
    #endif
    
    #include "Utils.h"
    #include "MathUtils.h"
    #include <gsl/gsl_errno.h>
    #include "gsl/gsl_sf_erf.h"
    #include <gsl/gsl_math.h>
    #include "TableFunction.h"
    #include "nr/nr3.h"
    //namespace nr {
    #include "nr/stepper.h"
    #include "nr/odeint.h"
    #include "nr/stepperdopr5.h"
    //}
    
    
    
    namespace Utils {
    #ifdef USE_BOOST
    	using namespace boost::numeric::odeint;
    
    	template< class Obj , class Mem >
    	class ode_wrapper
    	{
    		Obj* m_pObj;
    		Mem m_mem;
    
    	public:
    
    		ode_wrapper( Obj* obj , Mem mem ) : m_pObj(obj ) , m_mem(mem ) { }
    
    		template< class State , class Deriv , class Time >
    		void operator()( const State &x , Deriv &dxdt , Time t )
    		{
    			((*m_pObj).*m_mem)(x , dxdt , t );
    		}
    	};
    
    	template< class Obj , class Mem >
    	ode_wrapper< Obj , Mem > make_ode_wrapper( Obj* obj , Mem mem )
    	{
    		return ode_wrapper< Obj , Mem >( obj , mem );
    	}
    
    
    	template< class Obj , class Mem >
    	class observer_wrapper
    	{
    		Obj* m_pObj;
    		Mem m_mem;
    
    	public:
    
    		observer_wrapper( Obj* obj , Mem mem ) : m_pObj(obj ) , m_mem(mem ) { }
    
    		template< class State , class Time >
    		void operator()( const State &x , Time t )
    		{
    			((*m_pObj).*m_mem)(x , t );
    		}
    	};
    
    	template< class Obj , class Mem >
    	observer_wrapper< Obj , Mem > make_observer_wrapper( Obj* obj , Mem mem )
    	{
    		return observer_wrapper< Obj , Mem >( obj , mem );
    	}
    
    	template< class X=double >
    	class Sampler4 : public ISampler<X> {
    		//typedef runge_kutta_dopri5<X> dopri5_type;
    		//typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type;
    		//typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type;
    		//typedef bulirsch_stoer_dense_out<X> dopri5_type;
    	public:
    		Sampler4(){
    			fIntermediateVals=new std::vector<X>;
    			fIntermediateTs=new std::vector<X>;
    			fIntermediateTs->reserve(256);
    			fIntermediateVals->reserve(256);
    		}
    
    		~Sampler4(){
    			delete fIntermediateVals;
    			delete fIntermediateTs;
    		}
    
    		void System(const X &x , X &dxdt , X t ){
    			dxdt=fFunc->f(t);
    		}
    
    		void Output(const X &x , X t ){
    			if(fMaxX>0 && x>=fMaxX){
    				X lastT = *(fIntermediateTs->end()-1);
    				X lastX = *(fIntermediateVals->end()-1);
    				if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){
    					X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX);
    					throw result;
    				}
    				X totRate = x-lastX;
    				throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr);
    			}
    			else {
    				fIntermediateVals->push_back(x);
    				fIntermediateTs->push_back(t);
    			}
    		}
    
    		/// If total rate is known it should be passed via aTotRate argument
    		/// Otherwize aTotRate should be set to 0 (it will be calculated by function)
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			fMaxX = aTotRate*aRand;
    			fRelErr = aRelErr;
    			fAbsErr = aAbsErr;
    
    			size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1;
    			X step= (aTmax-aTmin)/nPoints;
    
    			fIntermediateTs->resize(0);//this will not decrease capacity of vectors
    			fIntermediateVals->resize(0);
    
    			X curT=aTmin;
    			fFunc = &f;
    			aTotRate = 0.;
    
    			X dt = (aTmax - aTmin) * aRelErr;
    
    			try {
    				//dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() );
    				bulirsch_stoer_dense_out< X > stepper( aAbsErr , aRelErr);
    				integrate_adaptive( stepper , make_ode_wrapper(this,&Sampler4::System) , aTotRate , aTmin , aTmax , dt , make_observer_wrapper(this, &Sampler4::Output) );
    				//integrate_const
    				//integrate_adaptive(dopri5, make_ode_wrapper(this,&Sampler3::System), aTotRate, aTmin, aTmax, dt,
    								   //make_observer_wrapper(this, &Sampler3::Output));
    			}catch (X aValue){
    				return aValue;
    			}
    			if(aTotRate<=0){
    				return aTmax;
    			}
    			std::vector<X>& intermediateVals = *fIntermediateVals;
    			std::vector<X>& intermediateTs = *fIntermediateTs;
    			X searchVal = aTotRate * aRand;
    			size_t i2= intermediateVals.size();
    			size_t i1=0;
    			for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2)
    			{
    				if(intermediateVals[i] < searchVal)
    					i1 = i;
    				else
    					i2 = i;
    			}
    			X t1 = intermediateTs[i1];
    			X t2 = intermediateTs[i2];
    			X x1 = intermediateVals[i1];
    			X x2 = intermediateVals[i2];
    			if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){
    				return t1 + (t2-t1)/(x2-x1)*(searchVal-x1);
    			}
    			X totRate=x2-x1;
    			return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr);
    		}
    	private:
    		const FunctionX<X>* fFunc;
    		std::vector<X>* fIntermediateVals;
    		std::vector<X>* fIntermediateTs;
    		X      fMaxX;
    		X      fRelErr;
    		X      fAbsErr;
    	};
    
    	template< class X=double >
    	class Sampler3 : public ISampler<X> {
    		typedef runge_kutta_dopri5<X> dopri5_type;
    		typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type;
    		typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type;
    	public:
    		Sampler3(){
    			fIntermediateVals=new std::vector<X>;
    			fIntermediateTs=new std::vector<X>;
    			fIntermediateTs->reserve(256);
    			fIntermediateVals->reserve(256);
    		}
    
    		~Sampler3(){
    			delete fIntermediateVals;
    			delete fIntermediateTs;
    		}
    
    		void System(const X &x , X &dxdt , X t ){
    			dxdt=fFunc->f(t);
    		}
    
    		void Output(const X &x , X t ){
    			if(fMaxX>0 && x>=fMaxX){
    				X lastT = *(fIntermediateTs->end()-1);
    				X lastX = *(fIntermediateVals->end()-1);
    				if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){
    					X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX);
    					throw result;
    				}
    				X totRate = x-lastX;
    				throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr);
    			}
    			else {
    				fIntermediateVals->push_back(x);
    				fIntermediateTs->push_back(t);
    			}
    		}
    
    		/// If total rate is known it should be passed via aTotRate argument
    		/// Otherwize aTotRate should be set to 0 (it will be calculated by function)
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			fMaxX = aTotRate*aRand;
    			fRelErr = aRelErr;
    			fAbsErr = aAbsErr;
    
    			size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1;
    			X step= (aTmax-aTmin)/nPoints;
    
    			fIntermediateTs->resize(0);//this will not decrease capacity of vectors
    			fIntermediateVals->resize(0);
    
    			X curT=aTmin;
    			fFunc = &f;
    			aTotRate = 0.;
    			dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() );
    			X dt = (aTmax - aTmin) * aRelErr;
    
    			try {
    				integrate_adaptive(dopri5, make_ode_wrapper(this,&Sampler3::System), aTotRate, aTmin, aTmax, dt,
    								   make_observer_wrapper(this, &Sampler3::Output));
    			}catch (X aValue){
    				return aValue;
    			}
    			if(aTotRate<=0){
    				return aTmax;
    			}
    			std::vector<X>& intermediateVals = *fIntermediateVals;
    			std::vector<X>& intermediateTs = *fIntermediateTs;
    			X searchVal = aTotRate * aRand;
    			size_t i2= intermediateVals.size();
    			size_t i1=0;
    			for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2)
    			{
    				if(intermediateVals[i] < searchVal)
    					i1 = i;
    				else
    					i2 = i;
    			}
    			X t1 = intermediateTs[i1];
    			X t2 = intermediateTs[i2];
    			X x1 = intermediateVals[i1];
    			X x2 = intermediateVals[i2];
    			if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){
    				return t1 + (t2-t1)/(x2-x1)*(searchVal-x1);
    			}
    			X totRate=x2-x1;
    			return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr);
    		}
    	private:
    		const FunctionX<X>* fFunc;
    		std::vector<X>* fIntermediateVals;
    		std::vector<X>* fIntermediateTs;
    		X      fMaxX;
    		X      fRelErr;
    		X      fAbsErr;
    	};
    
    	template<typename X = double >
    	class Sampler : public ISampler<X> {
    		typedef runge_kutta_dopri5<X> dopri5_type;
    		typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type;
    		typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type;
    	public:
    		Sampler(){
    			fIntermediateVals=0;
    			fIntermediateTs=0;
    		}
    		void operator()(const X &x , X &dxdt , const X t ){
    			dxdt=fFunc->f(t);
    		}
    		void operator()(const X &x , const X t ){
    			(*fIntermediateVals)[fLastStep++]=x;
    			double a=(*fIntermediateVals)[fLastStep-1];
    			a=a+1;
    		}
    
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			// create a vector with observation time points
    			size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr/2+0.5);
    			X step= (aTmax-aTmin)/nPoints;
    			std::vector<X> intermediateVals(nPoints + 1);
    			std::vector<X> intermediateTs(nPoints + 1);
    			fIntermediateVals=&intermediateVals;
    			fIntermediateTs=&intermediateTs;
    			X curT=aTmin;
    			for( size_t i=0 ; i<=nPoints ; ++i, curT+=step )
    				intermediateTs[i] = curT;
    
    			fFunc = &f;
    			aTotRate = 0.;
    			dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() );
    			X dt = (aTmax - aTmin) * aRelErr;
    
    			fLastStep=0;
    			integrate_times(dopri5 , (*this) , aTotRate , intermediateTs, dt , (*this) );
    			if(aTotRate==0.){
    				//aTotRate=0.;
    				return aTmax;
    			}
    
    			X searchVal = aTotRate * aRand;
    			size_t i2= intermediateVals.size();
    			size_t i1=0;
    			for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2)
    			{
    				if(intermediateVals[i] < searchVal)
    					i1 = i;
    				else
    					i2 = i;
    			}
    			X t1 = intermediateTs[i1];
    			X t2 = intermediateTs[i2];
    			X x1 = intermediateVals[i1];
    			X x2 = intermediateVals[i2];
    			return t1 + (t2 - t1) / (x2 - x1) * (searchVal - x1);
    		}
    
    		const FunctionX<X>* fFunc;
    		std::vector<X>* fIntermediateVals;
    		std::vector<X>* fIntermediateTs;
    
    		size_t      fLastStep;
    	};
    
    	template<typename X = double >
    	class LogSampler : public ISampler<X> {
    		typedef runge_kutta_dopri5<X> dopri5_type;
    		typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type;
    		typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type;
    	public:
    		LogSampler(){
    			fIntermediateVals=0;
    			fIntermediateTs=0;
    		}
    		void operator()(const X &x , X &dxdt , const X t ){
    			dxdt=fFunc->f(t);
    		}
    		void operator()(const X &x , const X t ){
    			(*fIntermediateVals)[fLastStep++]=x;
    			double a=(*fIntermediateVals)[fLastStep-1];
    			a=a+1;
    		}
    
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			// create a vector with observation time points
    			size_t nPoints = (size_t)(log(aTmax/aTmin)/log(1.0+aRelErr)/2+0.5);
    			X step= pow(aTmax/aTmin, 1./nPoints);
    			std::vector<X> intermediateVals(nPoints + 1);
    			std::vector<X> intermediateTs(nPoints + 1);
    			fIntermediateVals=&intermediateVals;
    			fIntermediateTs=&intermediateTs;
    			X curT=aTmin;
    			for( size_t i=0 ; i<=nPoints ; ++i, curT*=step )
    				intermediateTs[i] = curT;
    
    			fFunc = &f;
    			aTotRate = 0.;
    			dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() );
    			X dt = (aTmax - aTmin) * aRelErr;
    
    			fLastStep=0;
    			integrate_times(dopri5 , (*this) , aTotRate , intermediateTs, dt , (*this) );
    			if(aTotRate==0.){
    				//aTotRate=0.;
    				return aTmax;
    			}
    
    			X searchVal = aTotRate * aRand;
    			size_t i2= intermediateVals.size();
    			size_t i1=0;
    			for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2)
    			{
    				if(intermediateVals[i] < searchVal)
    					i1 = i;
    				else
    					i2 = i;
    			}
    			X t1 = intermediateTs[i1];
    			X t2 = intermediateTs[i2];
    			X x1 = intermediateVals[i1];
    			X x2 = intermediateVals[i2];
    			return t1 + (t2 - t1) / (x2 - x1) * (searchVal - x1);
    		}
    
    		const FunctionX<X>* fFunc;
    		std::vector<X>* fIntermediateVals;
    		std::vector<X>* fIntermediateTs;
    
    		size_t      fLastStep;
    	};
    
    	template<typename X = double >
    	class Sampler2 : public ISampler<X> {
    		typedef runge_kutta_dopri5<X> dopri5_type;
    		typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type;
    		typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type;
    	public:
    		Sampler2(){
    			fIntermediateVals=0;
    			fIntermediateTs=0;
    		}
    		~Sampler2()
    		{
    			fIntermediateVals=0;
    			fIntermediateTs=0;
    		}
    		void operator()(const X &x , X &dxdt , const double t ){
    			dxdt=fFunc->f(t);
    		}
    		void operator()(const X &x , const X t ){
    			if(fMaxX>0 && x>=fMaxX){
    				X lastT = *(fIntermediateTs->end()-1);
    				X lastX = *(fIntermediateVals->end()-1);
    				if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){
    					X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX);
    					throw result;
    				}
    				X totRate = x-lastX;
    				throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr);
    			}
    			else {
    				fIntermediateVals->push_back(x);
    				fIntermediateTs->push_back(t);
    			}
    		}
    
    		/// If total rate is known it should be passed via aTotRate argument
    		/// Otherwize aTotRate should be set to 0 (it will be calculated by function)
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			fMaxX = aTotRate*aRand;
    			//fTmin = aTmin;
    			//fTmax = aTmax;
    			//fTotRate = aTotRate;
    			fRelErr = aRelErr;
    			fAbsErr = aAbsErr;
    
    			size_t nPoints = 128;//(size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1;
    			//X step= (aTmax-aTmin)/nPoints;
    			std::vector<X> intermediateVals;
    			std::vector<X> intermediateTs;
    			intermediateTs.reserve(nPoints);
    			intermediateVals.reserve(nPoints);
    			fIntermediateVals=&intermediateVals;
    			fIntermediateTs=&intermediateTs;
    			X curT=aTmin;
    			fFunc = &f;
    			aTotRate = 0.;
    
    			///TODO: try different steppers
    
    			dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() );
    			X dt = (aTmax - aTmin) * aRelErr;
    
    			try {
    				integrate_adaptive(dopri5, (*this), aTotRate, aTmin, aTmax, dt,
    								   (*this));//this will create two copies of (*this)
    				/* //this one is a bit slower than dense_output_dopri5_type
    				typedef runge_kutta_cash_karp54< X > error_stepper_type;
    				integrate_adaptive( make_controlled< error_stepper_type >( aAbsErr , aRelErr ) ,
    									(*this) , aTotRate , aTmin, aTmax, dt,
    									(*this));*/
    
    			}catch (X aValue){
    				return aValue;
    			}
    			if(aTotRate<=0){
    				return aTmax;
    			}
    
    			X searchVal = aTotRate * aRand;
    			size_t i2= intermediateVals.size();
    			size_t i1=0;
    			for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2)
    			{
    				if(intermediateVals[i] < searchVal)
    					i1 = i;
    				else
    					i2 = i;
    			}
    			X t1 = intermediateTs[i1];
    			X t2 = intermediateTs[i2];
    			X x1 = intermediateVals[i1];
    			X x2 = intermediateVals[i2];
    			if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){
    				return t1 + (t2-t1)/(x2-x1)*(searchVal-x1);
    			}
    			X totRate=x2-x1;
    			return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr);
    		}
    
    		const FunctionX<X>* fFunc;
    		std::vector<X>* fIntermediateVals;
    		std::vector<X>* fIntermediateTs;
    		X      fMaxX;
    		X      fRelErr;
    		X      fAbsErr;
    	};
    
    #endif //#ifdef USE_BOOST
    
    	template<typename X = double >
    	class NRSampler : public ISampler<X> {
    		X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand,
    				 X & aTotRate, X aRelErr, X aAbsErr = 1e300){
    			X x;
    			MathUtils::SampleLogDistributionNR(f,aRand,x,aTotRate,aTmin,aTmax,aRelErr);
    		}
    	};
    
    	double MathUtils::SolveEquation(
    			gsl_function F,	double x_lo, double x_hi,
    			const double relError, const int max_iter,
    			const gsl_root_fsolver_type *T)
    {
        double r = 0;
    	int status;
    	int iter = 0;
    
    	gsl_root_fsolver *solver = gsl_root_fsolver_alloc (T);
    	gsl_root_fsolver_set (solver, &F, x_lo, x_hi);
    	do
    	{
    	 	iter++;
    	    status = gsl_root_fsolver_iterate (solver);
    	    r = gsl_root_fsolver_root (solver);
    	    x_lo = gsl_root_fsolver_x_lower (solver);
    	    x_hi = gsl_root_fsolver_x_upper (solver);
    	    status = gsl_root_test_interval (x_lo, x_hi, 0, relError);
    	}
    	while (status == GSL_CONTINUE && iter < max_iter);
    	ASSERT(status == GSL_SUCCESS);
    	gsl_root_fsolver_free (solver);
    	if (status != GSL_SUCCESS)
    	{
    		if(iter >= max_iter)
    			Exception::Throw("Failed to solve equation: maximal number of iterations achieved");
    	  	else
    	  		Exception::Throw("Failed to solve equation: GSL status " + ToString(status));
    	}
    	return r;
    }
    
    double MathUtils::SolveEquation(
    		double (*aEquation) (double, void*),
    		double x_lo, double x_hi, void* aEquationPars,
    		const double relError, const int max_iter,
    		const gsl_root_fsolver_type *T)
    {
    	gsl_function F;
    	F.function = aEquation;
    	F.params = aEquationPars;
    	return SolveEquation(F,	x_lo, x_hi, relError, max_iter, T);
    }
    
    MathUtils::MathUtils():
    gslQAGintegrator(0)
    {
    
    }
    
    MathUtils::~MathUtils()
    {
    	if(gslQAGintegrator)
    		gsl_integration_workspace_free (gslQAGintegrator);
    }
    
    	template<class X> class GaussDisr : public FunctionX<X>{
    	public:
    		X mean;
    		X sigma;
    		GaussDisr(X aMean, X aSigma):mean(aMean),sigma(aSigma){}
    		virtual X f(X _x) const{
    			X diff = (_x-mean)/sigma;
    			return exp(-diff*diff*0.5)/sigma*0.398942280401433;
    		}
    	};
    
    	template<class X> void ISampler<X>::UnitTest()
    	{
    
    		X mean = 1e10;
    		X sigma = 1e9;
    		X minX=1e9;
    		X maxX=1e11;
    		X minI = 0.5*(1+gsl_sf_erf((minX-mean)/sigma/sqrt(2.0)));
    		X maxI = 0.5*(1+gsl_sf_erf((maxX-mean)/sigma/sqrt(2.0)));
    		X totI=maxI-minI;
    
    		GaussDisr<X> gd(mean,sigma);
    		X relError = 1e-6;
    		int nSteps = 10000;
    //gnuplot command: 0.5*(1+erf((x-mean)/sigma/sqrt(2.0))) w l
    
    		for(int i=1; i<nSteps; i++){
    			X rand = 1./nSteps*i;
    			X totRate = 0;
    			X curX = sample(gd, minX, maxX, rand, totRate, relError);
    			X exactFrac = (0.5*(1+gsl_sf_erf((curX-mean)/sigma/sqrt(2.0)))-minI)/totI;
    			std::cout << curX << "\t" << rand << "\t"  << exactFrac << std::endl;
    		}
    	}
    
    	template void ISampler<double>::UnitTest();
    	template void ISampler<long double>::UnitTest();
    
    	template<typename X> bool MathUtils::SampleLogscaleDistribution(const Function& aDistrib, double aRand, X& aOutputX, X& aOutputIntegral, int nStepsS, X xMin, X xMax, double aRelError)
    {
    	std::vector<X> sArray,ratesArray;
    	double distrXmin = aDistrib.Xmin();
    	if(xMin < distrXmin)
    		xMin = distrXmin;
    	double distrXmax = aDistrib.Xmax();
    	if(xMax > distrXmax)
    		xMax = distrXmax;
    	ASSERT(xMin>0. && xMin<xMax);
    	double stepS = pow(xMax/xMin,1./nStepsS);
    
    	sArray.push_back(0);
    	ratesArray.push_back(0);
    
    	X taleAccLimit;
    	RelAccuracy<X>(taleAccLimit);
    	taleAccLimit*=10;
    	int maxIntervals = (int)(0.1/aRelError + 10.5);
    	aOutputIntegral=0.;
    	double deltaLogS = log(stepS);
    	double s=xMin;
    	for(int iS=1; iS<=nStepsS; iS++)
    	{
    		double s2=s*stepS;
    		X rate = Integration_qag(aDistrib,s,s2,1e-300,aRelError,maxIntervals);
    		ASSERT_VALID_NO(rate);
    		if(rate==0. && aOutputIntegral==0.)
    		{//move Smax
    			sArray[0] = deltaLogS*iS;
    		}
    		else
    		{
    			if(rate==0 || (aOutputIntegral>0 && rate/aOutputIntegral < taleAccLimit))
    				rate = aOutputIntegral*taleAccLimit;//avoiding zero derivative (inverse function must be defined everywhere)
    			aOutputIntegral += rate;
    			sArray.push_back(deltaLogS*iS);
    			ratesArray.push_back(aOutputIntegral);
    		}
    		s=s2;
    	}
    	if(aOutputIntegral>0)
    	{//sampling s
    		ASSERT(sArray.size()>1);
    		double randRate = aRand*aOutputIntegral;
    		for(int i = ratesArray.size()-1;i>=0; i--)
    			ratesArray[i]-=randRate;
    		GSLTableFunc func(sArray, ratesArray, 0, 0, gsl_interp_cspline);
    		func.SetAutoLimits();
    		double logError = aRelError/(nStepsS*deltaLogS);
    		if(logError>aRelError)
    			logError = aRelError;
    		aOutputX = SolveEquation(func, 0, nStepsS*deltaLogS, logError);
    		aOutputX = xMin*exp(aOutputX);
    		ASSERT(aOutputX>=xMin && aOutputX<=xMax);
    		return true;
    	}
    	return false;
    }
    
    class MathUtilsODE
    {
    public:
    	MathUtilsODE(const Function& aDistrib):fDistrib(aDistrib)
    	{}
    	void operator() (const Doub x, VecDoub_I &y, VecDoub_O &dydx) {
    		double val = fDistrib(x);
    		if(val!=0)
    			dydx[0]= val;
    		else
    			dydx[0]= 0.;
    		ASSERT(val > -1.6e308 && val < 1.6e308);
    	}
    private:
    	const Function& fDistrib;
    };
    
    IFunctionCallHandlerX<double>* MathUtils::fLogger = 0;
    
    bool MathUtils::SampleDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError)
    {
    	const Function* distrib = &aDistrib;
    	SafePtr<DebugFunctionX<double> > func;
    	if(fLogger)
    	{
    		func = new DebugFunctionX<double>(aDistrib, *fLogger);
    		distrib = func;
    	}
    
    	double distrXmin = distrib->Xmin();
    	if(xMin < distrXmin)
    		xMin = distrXmin;
    	double distrXmax = distrib->Xmax();
    	if(xMax > distrXmax)
    		xMax = distrXmax;
    	ASSERT(xMax>xMin);
    
    	double initialStep = aRelError*(xMax-xMin);
    
    	MathUtilsODE d(*distrib);
    
    	const Doub atol=0.;//1.0e-3;
    	const Doub hmin=0.0;//minimal step (can be zero)
    	VecDoub ystart(1);
    	ystart[0]=0.;
    	Output out(-1); //output is saved at every integration step
    	Odeint<StepperDopr5<MathUtilsODE> > ode(ystart,xMin,xMax,atol,aRelError,initialStep,hmin,out,d);
    	ode.integrate();
    	aOutputIntegral = ystart[0];
    	if(aOutputIntegral<=0)
    		return false;
    	aRand *= aOutputIntegral;
    	int i1=0;
    	int i2=out.count-1;
    	double* ysave = out.ysave[0];
    	for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2)
    	{
    		double y = ysave[i];
    		if(y<aRand)
    			i1 = i;
    		else
    			i2 = i;
    	}
    	double y1=ysave[i1];
    	double y2=ysave[i2];
    	double x1=out.xsave[i1];
    	double x2=out.xsave[i2];
    	aRand -= y1;
    	aOutputX = x1 + (x2-x1)/(y2-y1)*aRand;//make a linear estimate of X
    	if(fabs((x2-x1)/aOutputX)<=aRelError)
    		return true;
    	ystart[0]=0.;
    	initialStep = aRelError*(aOutputX>0 ? aOutputX : (x2-x1));
    	Output out2(2+(int)fabs((x2-x1)/initialStep));
    
    	Odeint<StepperDopr5<MathUtilsODE> > ode2(ystart,x1,x2,atol,aRelError,initialStep,hmin,out2,d);
    	ode2.integrate();
    	i1=0;
    	i2=out2.count-1;
    	ysave = out2.ysave[0];
    	for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2)
    	{
    		double y = ysave[i];
    		if(y<aRand)
    			i1 = i;
    		else
    			i2 = i;
    	}
    	y1=ysave[i1];
    	y2=ysave[i2];
    	x1=out2.xsave[i1];
    	x2=out2.xsave[i2];
    	aOutputX = x1 + (x2-x1)/(y2-y1)*(aRand-y1);//make a linear estimate of X
    	return true;
    }
    
    class MathUtilsLogODE
    {
    public:
    	MathUtilsLogODE(const Function& aDistrib):fDistrib(aDistrib)
    	{}
    	void operator() (const Doub x, VecDoub_I &y, VecDoub_O &dydx) {
    		double xx = exp(x);
    		double val = xx*fDistrib(xx);
    		if(val!=0)
    			dydx[0]= val;
    		else
    			dydx[0]= 0.;
    		ASSERT(val > -1.6e308 && val < 1.6e308);
    	}
    private:
    	const Function& fDistrib;
    };
    
    bool MathUtils::SampleLogDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError)
    {
    #ifdef USE_BOOST
        return SampleLogDistributionBoost(aDistrib, aRand, aOutputX, aOutputIntegral, xMin, xMax, aRelError);
    #else
    	return SampleLogDistributionNR(aDistrib, aRand, aOutputX, aOutputIntegral, xMin, xMax, aRelError);
    #endif
    }
    
    bool MathUtils::SampleLogDistributionNR(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError)
    {
    	ASSERT(aRelError>0 && aRelError<=0.1);
    
    	const Function* distrib = &aDistrib;
    	SafePtr<DebugFunctionX<double> > func;
    	if(fLogger)
    	{
    		func = new DebugFunctionX<double>(aDistrib, *fLogger);
    		distrib = func;
    	}
    
    	double distrXmin = distrib->Xmin();
    	if(xMin < distrXmin)
    		xMin = distrXmin;
    	double distrXmax = distrib->Xmax();
    	if(xMax > distrXmax)
    		xMax = distrXmax;
    	ASSERT(xMax>xMin && xMin>0);
    	xMin=log(xMin);
    	xMax=log(xMax);
    
    	double initialStep = 0.5*(xMax-xMin);
    //	if(aRelError<initialStep)
    //		initialStep=aRelError;
    
    	MathUtilsLogODE d(*distrib);
    
    	const Doub atol=0;
    	const Doub hmin=0.0;//minimal step (can be zero)
    	VecDoub ystart(1);
    	ystart[0]=0.;
    	Output out(-1); //output is saved at every integration step
    	Odeint<StepperDopr5<MathUtilsLogODE> > ode(ystart,xMin,xMax,atol,aRelError,initialStep,hmin,out,d);
    	ode.integrate();
    	aOutputIntegral = ystart[0];
    	if(aOutputIntegral<=0)
    		return false;
    	double yRand = aRand*aOutputIntegral;
    	int i1=0;
    	int i2=out.count-1;
    	double* ysave = out.ysave[0];
    	for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2)
    	{
    		double y = ysave[i];
    		if(y<yRand)
    			i1 = i;
    		else
    			i2 = i;
    	}
    	double y1=ysave[i1];
    	double y2=ysave[i2];
    	double x1=out.xsave[i1];
    	double x2=out.xsave[i2];
    	double yFrac = (yRand-y1)/(y2-y1);
    	if((x2-x1)<aRelError)
    	{
    		aOutputX = x1 + (x2-x1)*yFrac;//make a linear estimate of X in log scale
    		ASSERT(aOutputX>=xMin && aOutputX<=xMax);
    	}
    	else
    	{
    		ystart[0]=0.;
    		initialStep = 0.5*(x2-x1);//aRelError;
    		Output out2(2+(int)fabs((x2-x1)/initialStep));
    		Odeint<StepperDopr5<MathUtilsLogODE> > ode2(ystart,x1,x2,atol,aRelError,initialStep,hmin,out2,d);
    		ode2.integrate();
    		yRand = ystart[0]*yFrac;
    		i1=0;
    		i2=out2.count-1;
    		ysave = out2.ysave[0];
    		for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2)
    		{
    			double y = ysave[i];
    			if(y<yRand)
    				i1 = i;
    			else
    				i2 = i;
    		}
    		y1=ysave[i1];
    		y2=ysave[i2];
    		x1=out2.xsave[i1];
    		x2=out2.xsave[i2];
    		aOutputX = x1 + (x2-x1)/(y2-y1)*(yRand-y1);//make a linear estimate of X in log scale
    		ASSERT(aOutputX>=xMin && aOutputX<=xMax);
    	}
    	aOutputX = exp(aOutputX);
    	ASSERT_VALID_NO(aOutputX);
    	return true;
    }
    
    
    bool MathUtils::SampleLogDistributionBoost(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError)
    	{
    #ifdef USE_BOOST
    		ASSERT(aRelError>0 && aRelError<=0.1);
    		ASSERT(xMin>0 && xMax>xMin);
    		LogSampler<double> dil;//slower 25 sec
    		//Sampler2<double> dil;// 20 sec (todo: fix memory leaks)
    		//Sampler3<double> dil;// 20 sec (todo: fix memory leaks)
    		aOutputIntegral=0.;
    
    		aOutputX=dil.sample(aDistrib, xMin, xMax, aRand, aOutputIntegral, aRelError);
    
    		ASSERT_VALID_NO(aOutputX);
    		return true;
    #else
    	Exception::Throw("MathUtils::SampleLogDistributionBoost boostlib support is disabled");
    	return false;//avoid compiler warning
    #endif
    	}
    
    template<typename X> void MathUtils::RelAccuracy(X& aOutput)
    {
    	NOT_IMPLEMENTED
    }
    
    template<> void MathUtils::RelAccuracy<double>(double& aOutput)
    {
    	aOutput = 1e-15;
    }
    
    template<> void MathUtils::RelAccuracy<long double>(long double& aOutput)
    {
    	aOutput = 1e-18L;
    }
    
    template bool MathUtils::SampleLogscaleDistribution<double>(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, int nStepsS, double xMin, double xMax, double aRelError);
    //template bool MathUtils::SampleLogscaleDistribution<long double>(const Function& aDistrib, double aRand, long double& aOutput, int nStepsS, long double xMin, long double xMax, double aRelError);
    
    	int MathUtils::UnitTest(){
    		//LogSampler<double> dil;//slower 25 sec
    		//Sampler2<double> dil;// 20 sec (todo: fix memory leaks)
    		//Sampler3<double> dil;// 20 sec (todo: fix memory leaks)
    		//Sampler4<double> dil;
    		NRSampler<double> dil;
    		dil.UnitTest();
    	}
    
    double MathUtils::Integration_qag (
    		gsl_function aFunction,
    		double aXmin,
    		double aXmax,
    		double epsabs,
    		double epsrel,
    		size_t limit,
    		int key)
    {
    	if(gslQAGintegrator==0)
    		gslQAGintegrator = gsl_integration_workspace_alloc (limit);
    	else if(gslQAGintegrator->limit < limit)
    	{
    		gsl_integration_workspace_free (gslQAGintegrator);
    		gslQAGintegrator = gsl_integration_workspace_alloc (limit);
    	}
    	double result, abserr;
    	try{
    		if(epsabs==0)
    			epsabs = std::numeric_limits<double>::min();
    		int failed = gsl_integration_qag (&aFunction, aXmin, aXmax, epsabs, epsrel, limit, key, gslQAGintegrator, &result, &abserr);
    		if(failed)
    		{
    			ASSERT(0);
    			Exception::Throw("Integration failed with code " + ToString(failed));
    		}
    	}catch(Exception* ex)
    	{
    #ifdef _DEBUG
    		GslProxyFunction f(aFunction,aXmin,aXmax);
    		std::cerr << "\n\n#Integration_qag debug output:" << std::endl;
    		bool logscale = aXmin>0 && aXmax/aXmin > 100;
    		f.Print(std::cerr, 100, logscale, aXmin, aXmax);
    		std::cerr << "\n\n#end of Integration_qag debug output" << std::endl;
    #endif
    		throw ex;
    	}
    	return result;
    }
    
    } /* namespace Utils */