//******************************************************************************
//  project:		Weather-based simulation framework (WBSF)
//	Programmer:     Rémi Saint-Amant
//
//  It under the terms of the GNU General Public License as published by
//     the Free Software Foundation
//  It is provided "as is" without express or implied warranty.
//
// Class:	CStatistic, CStatisticEx, CStatisticXW, CStatisticXY, CStatisticXYEx, CStatisticXYW
//
//****************************************************************************
// 02/12/2015   Rémi Saint-Amant  Add weathed statistic and quartile
// 22/12/2014   Rémi Saint-Amant  Add RANGE as statistic type
// 24/05/2013	Rémi Saint-Amant  Add standart deviation over population (N)
// 20/02/2009	Rémi Saint-Amant  Add classification class
// 04/03/2004	Rémi Saint-Amant  Remove the old GetVaporPressureDeficit
// 20/11/2003	Rémi Saint-Amant  Initiale Version
//****************************************************************************

#include "stdafx.h"
#include <math.h>
#include <float.h>
#include <algorithm>
#include <boost/math/distributions/normal.hpp>

#include "Basic/Statistic.h"
#include "Basic/UtilMath.h"
#include "WeatherBasedSimulationString.h"

using namespace std;

namespace WBSF
{

	//this static variable is share by CStatistic and CStatisticXY
	double STAT_VMISS = -9999999.0;

	const char* CStatistic::NAME[NB_STATXY_TYPE_EX] =
	{
		"Lowest", "Mean", "Sum", "Sum²", "StandardDeviation (N-1)", "StandardDeviation (N)", "StandardError", "CoeficientOfVariation", "Variance", "Highest", "TotalSumOfSquares", "QuadraticMean", "Range", "NbValue",
		"MeanAbsoluteDeviation", "Skewness", "Kurtosis", "Median", "Mode", "Lo", "Q¹", "Q³", "Hi", "IQR",
		"MeanX", "MeanY", "Intercept", "Slope", "Covariance", "Correlation", "Bias", "MeanAbsolutError", "RootMeanSquareError", "ResidualSumOfSquare", "CoeficientOfDetermination", "CoeficientOfCorrelation", "R²",
		"TheilSenIntercept", "TheilSenSlope", "Likelihood"
		//, "LogLikelihood2"
	};

	StringVector CStatistic::TITLE;
	const char* CStatistic::GetTitle(size_t s)
	{
		assert(s < NB_STATXY_TYPE_EX);
		if (TITLE.empty())
			TITLE.LoadString(IDS_STR_STATISTIC, "|;");

		assert(TITLE.size() == NB_STATXY_TYPE_EX);

		return TITLE[s].c_str();
	}

	void CStatistic::ReloadString()
	{
		TITLE.clear();
	}

	CStatistic::CStatistic()
	{
		Reset();
	}

	void CStatistic::Reset()
	{
		m_nbValues = 0;

		m_lowest = DBL_MAX;
		m_sum = 0;
		m_sum² = 0;
		m_hightest = -DBL_MAX;
	}

	CStatistic& CStatistic::operator=(const CStatistic& in)
	{
		if (&in != this)
		{
			m_nbValues = in.m_nbValues;

			m_lowest = in.m_lowest;
			m_sum = in.m_sum;
			m_sum² = in.m_sum²;
			m_hightest = in.m_hightest;
		}

		return *this;
	}

	CStatistic& CStatistic::operator+=(double value)
	{
		_ASSERTE(!_isnan(value));

		m_nbValues++;

		if (value < m_lowest)
			m_lowest = value;

		m_sum += value;
		m_sum² += Square(value);
		if (value > m_hightest)
			m_hightest = value;

		return *this;
	}

	CStatistic& CStatistic::operator+=(const CStatistic& statistic)
	{
		m_nbValues += statistic.m_nbValues;

		if (statistic.m_lowest < m_lowest)
			m_lowest = statistic.m_lowest;

		m_sum += statistic.m_sum;
		m_sum² += statistic.m_sum²;
		if (statistic.m_hightest > m_hightest)
			m_hightest = statistic.m_hightest;

		return *this;
	}

	double CStatistic::operator[](size_t type)const
	{
		_ASSERTE(type >= 0 && type < NB_STAT_TYPE);
		const CStatistic& me = *this;

		double value = STAT_VMISS;

		if (m_nbValues > 0 || type == NB_VALUE)
		{
			size_t nbVal = m_nbValues == 1 ? m_nbValues : m_nbValues - 1;
			switch (type)
			{
			case LOWEST: value = m_lowest; break;
			case SUM:value = m_sum; break;
			case SUM²:value = m_sum²; break;
			case MEAN:value = m_sum / m_nbValues; break;
			case STD_DEV:value = sqrt(std::max(0.0, (m_sum² - Square(m_sum) / m_nbValues)) / (nbVal)); break;
			case STD_DEV_OVER_POP:value = sqrt(std::max(0.0, (m_sum² - Square(m_sum) / m_nbValues)) / (m_nbValues)); break;
			case STD_ERR:value = (me[STD_DEV] / sqrt((double)m_nbValues)); break;
			case TSS: value = std::max(0.0, m_sum² - Square(m_sum) / m_nbValues); break;
			case COEF_VAR:
				if (!(m_sum² > 0 && m_sum == 0))
				{
					value = m_sum != 0 ? me[STD_DEV] / me[MEAN] : 0;//est-ce que doit accepter les valeur négative???
					break;
				}
			case VARIANCE:value = std::max(0.0, (m_sum² - Square(m_sum) / m_nbValues) / (nbVal)); break;
			case HIGHEST:value = m_hightest; break;
			case QUADRATIC_MEAN:  value = sqrt(m_sum² / m_nbValues); break;
			case RANGE: value = m_hightest - m_lowest; break;
			case NB_VALUE:value = (double)m_nbValues; break;
			default: _ASSERTE(false);//not supported by this statistic class
			}
		}

		_ASSERTE(!_isnan(value));
		return value;
	}

	double CStatistic::GetVMiss(){ return STAT_VMISS; }
	void CStatistic::SetVMiss(double vmiss){ STAT_VMISS = vmiss; }

	bool CStatistic::operator==(const CStatistic& in)const
	{
		bool bEqual = true;
		if (m_nbValues != in.m_nbValues)bEqual = false;
		if (fabs(m_lowest - in.m_lowest) > EPSILON_DATA)bEqual = false;
		if (fabs(m_sum - in.m_sum) > EPSILON_DATA)bEqual = false;
		if (fabs(m_sum² - in.m_sum²) > EPSILON_DATA)bEqual = false;
		if (fabs(m_hightest - in.m_hightest) > EPSILON_DATA)bEqual = false;

		return bEqual;
	}
	//*************************************************************
	//CStatisticEx : keep in memory all elements for MAD computation

	CStatisticEx::CStatisticEx()
	{
		Reset();
	}

	void CStatisticEx::Reset()
	{
		CStatistic::Reset();
		m_values.clear();
		m_sorted.clear();
	}

	CStatisticEx& CStatisticEx::operator=(const CStatisticEx& in)
	{
		CStatistic::operator=(in);
		m_values = in.m_values;
		m_sorted.clear();

		return *this;
	}

	CStatisticEx&  CStatisticEx::operator+=(double value)
	{
		CStatistic::operator+=(value);
		m_values.push_back(value);
		m_sorted.clear();

		return *this;
	}

	CStatisticEx&  CStatisticEx::operator+=(const CStatisticEx& statistic)
	{
		CStatistic::operator+=(statistic);
		m_values.insert(m_values.end(), statistic.m_values.begin(), statistic.m_values.end());
		m_sorted.clear();

		return *this;
	}

	void CStatisticEx::sort()const
	{
		if (m_sorted.empty())
		{
			CStatisticEx& me = const_cast<CStatisticEx&>(*this);
			me.m_sorted = m_values;
			std::sort(me.m_sorted.begin(), me.m_sorted.end());
		}

		ASSERT(m_sorted.size() == m_values.size());
	}

	double CStatisticEx::operator[](size_t type)const
	{
		_ASSERTE(m_values.size() == m_nbValues);
		_ASSERTE(type >= 0 && type < NB_STAT_TYPE_EX);
		const CStatisticEx& me = *this;

		double value = STAT_VMISS;

		if (m_nbValues > 0 || type == NB_VALUE)
		{
			if (type == MAD)
			{
				value = 0;
				double mean = CStatistic::operator[](MEAN);
				for (int i = 0; i < (int)m_values.size(); i++)
					value += fabs(m_values[i] - mean);

				value /= m_values.size();
			}
			else if (type == SKEWNESS)
			{
				value = 0;
				double mean = CStatistic::operator[](MEAN);
				for (int i = 0; i < (int)m_values.size(); i++)
					value += pow(m_values[i] - mean, 3);

				double SD = CStatistic::operator[](STD_DEV);
				double tmp = ((m_values.size() - 1)*pow(SD, 3));

				if (tmp != 0)
					value /= tmp;
			}
			else if (type == KURTOSIS)
			{
				value = 0;
				double mean = CStatistic::operator[](MEAN);
				for (int i = 0; i < (int)m_values.size(); i++)
					value += pow(m_values[i] - mean, 4);

				double SD = CStatistic::operator[](STD_DEV);
				double tmp = ((m_values.size() - 1)*pow(SD, 4));

				if (tmp != 0)
					value /= tmp;
			}
			else if (type == MEDIAN || type == Q²)
			{
				sort();

				size_t N1 = (m_sorted.size() + 1) / 2 - 1;
				size_t N2 = m_sorted.size() / 2 + 1 - 1;
				ASSERT(N2 == N1 || N2 == N1 + 1);

				value = (m_sorted[N1] + m_sorted[N2]) / 2.0;
			}
			else if (type == MODE )
			{
				sort();

				int maxOcc = 0;
				int occ = 0;
				double lastVal = -DBL_MAX;
				for (size_t i = 0; i < m_sorted.size(); i++)
				{
					if (fabs(m_sorted[i] - lastVal) < 0.000001)
					{
						occ++;
					}
					else
					{
						occ = 1;
						lastVal = m_sorted[i];
					}

					if (occ > maxOcc)
					{
						maxOcc = occ;
						value = lastVal;
					}
				}
			}
			else if (type == Qᴸ)
			{
				sort();

				double Q1 = me[Q¹];
				double IQR = me[INTER_Q];
				value = max(Q1 - 1.5*IQR, m_sorted.front());
			}
			else if (type == Q¹)
			{
				size_t N1 = (m_sorted.size() + 1) / 4 - 1;
				size_t N2 = m_sorted.size() / 4 + 1 - 1;
				ASSERT(N2 == N1 || N2 == N1 + 1);

				sort();
				value = (m_sorted[N1] + m_sorted[N2]) / 2.0;
			}
			else if (type == Q³)
			{
				size_t N1 = 3 * (m_sorted.size() + 1) / 4 - 1;
				size_t N2 = 3 * m_sorted.size() / 4 + 1 - 1;
				ASSERT(N2 == N1 || N2 == N1 + 1);

				sort();
				value = (m_sorted[N1] + m_sorted[N2]) / 2.0;
			}
			else if (type == Qᴴ)
			{
				sort();

				double Q3 = me[Q³];
				double IQR = me[INTER_Q];
				value = min(Q3 + 1.5*IQR, m_sorted.back());
			}
			else if (type == INTER_Q)
			{
				value = me[Q³] - me[Q¹];
			}
			else
			{
				value = CStatistic::operator[](type);
			}
		}

		_ASSERTE(!_isnan(value));
		return value;
	}

	double CStatisticEx::percentil(double p)const
	{
		ASSERT(p >= 0 && p <= 100);
		sort();//create sorted array

		double value = STAT_VMISS;

		if (!m_sorted.empty())
		{
			size_t n = max(size_t(1), size_t(ceil(p / 100 * m_sorted.size()))) - size_t(1);
			value = m_sorted[n];
		}

		return value;
	}

	//*************************************************************
	//Weighted CStatistic
	CStatisticXW::CStatisticXW()
	{
		m_wx = 0;
		m_wx² = 0;
	}

	void CStatisticXW::Reset()
	{
		m_x.clear();
		m_w.clear();
		m_wx = 0;
		m_wx² = 0;
	}

	CStatisticXW& CStatisticXW::operator=(const CStatisticXW& in)
	{
		m_x = in.m_x;
		m_w = in.m_w;
		m_wx = in.m_wx;
		m_wx² = in.m_wx²;

		return *this;
	}

	CStatisticXW& CStatisticXW::Add(double x, double w)
	{
		m_x.insert(x);
		m_w.insert(w);
		m_wx += w*x;
		m_wx² += w*x*x;

		return *this;
	}

	CStatisticXW& CStatisticXW::operator+=(const CStatisticXW& statistic)
	{
		m_x += statistic.m_x;
		m_w += statistic.m_w;
		m_wx += statistic.m_wx;
		m_wx² += statistic.m_wx²;

		return *this;
	}

	double CStatisticXW::operator[](size_t type)const
	{
		ASSERT(m_x[NB_VALUE] == m_w[NB_VALUE]);
		ASSERT(type >= 0 && type < NB_STAT_TYPE);

		double value = STAT_VMISS;

		const CStatisticXW& me = *this;
		double nbValues = m_x[NB_VALUE];

		if (nbValues > 0 || type == NB_VALUE)
		{
			switch (type)
			{
			case LOWEST:	value = m_x[LOWEST]; break;
			case SUM:		value = m_wx; break;
			case SUM²:		value = m_wx²; break;
			case MEAN:		value = m_wx / m_w[SUM]; break;
				//from https://stat.ethz.ch/pipermail/r-help/2008-July/168762.html
			case STD_DEV:
			case STD_DEV_OVER_POP:
			{
				if ((Square(m_w[SUM]) - m_w[SUM²]) != 0)
				{
					double mean = me[MEAN];
					double sum = 0;
					for (size_t i = 0; i < m_x().size(); i++)
						sum += m_w(i) * Square(m_x(i) - mean);

					value = m_w[SUM] / (Square(m_w[SUM]) - m_w[SUM²]) *sum;
				}
				break;
			}
			case STD_ERR:value = (me[STD_DEV] / sqrt(m_w[SUM])); break;
			case TSS: value = std::max(0.0, m_wx² * m_w[SUM] - Square(m_wx)); break;
			case COEF_VAR:
				if (!(m_wx²>0 && m_wx == 0))
				{
					value = m_wx > 0 ? me[STD_DEV] / me[MEAN] : 0;
					break;
				}

			case VARIANCE: value = ((Square(m_w[SUM]) - m_w[SUM²]) != 0) ? (m_wx² * m_w[SUM] - Square(m_wx)) / (Square(m_w[SUM]) - m_w[SUM²]) : VMISS; break;
			case HIGHEST: value = m_x[HIGHEST]; break;
			case QUADRATIC_MEAN:  value = sqrt(m_wx² / m_w[SUM]); break;
			case RANGE: value = m_x[RANGE]; break;
			case NB_VALUE: value = nbValues; break;
			default: ASSERT(false);//not supported by this class
			}
		}

		_ASSERTE(!_isnan(value));

		return value;
	}

	//*************************************************************
	//CStatisticXY: statistic of 2 variables

	CStatisticXY::CStatisticXY()
	{
		Reset();
	}

	CStatisticXY& CStatisticXY::operator=(const CStatisticXY& in)
	{
		if (&in != this)
		{
			m_x = in.m_x;
			m_y = in.m_y;
			m_lowest = in.m_lowest;
			m_xy = in.m_xy;
			m_e = in.m_e;
			m_rs = in.m_rs;
			m_ae = in.m_ae;
			m_hightest = in.m_hightest;
		}

		return *this;
	}

	void CStatisticXY::Reset()
	{
		m_x.clear();
		m_y.clear();

		m_lowest = DBL_MAX;
		m_xy = 0;
		m_e = 0;
		m_rs = 0;
		m_ae = 0;
		m_hightest = -DBL_MAX;
	}

	//x = pred, y = obs
	CStatisticXY& CStatisticXY::Add(double x, double y)
	{
		m_x += x;
		m_y += y;

		double e = (x - y);
		if (e < m_lowest)
			m_lowest = e;

		m_xy += x*y;
		m_e += (x - y);
		m_rs += Square(x - y);
		m_ae += abs(x - y);

		if (e > m_hightest)
			m_hightest = e;

		return *this;
	}

	CStatisticXY& CStatisticXY::operator+=(const CStatisticXY& in)
	{
		m_x += in.m_x;
		m_y += in.m_y;

		if (in.m_lowest < m_lowest)
			m_lowest = in.m_lowest;

		m_xy += in.m_xy;
		m_e += in.m_e;
		m_rs += in.m_rs;
		m_ae += in.m_ae;

		if (in.m_hightest > m_hightest)
			m_hightest = in.m_hightest;

		return *this;
	}

	double CStatisticXY::operator[](size_t type)const
	{
		_ASSERTE(m_x[NB_VALUE] == m_y[NB_VALUE]);
		_ASSERTE(type >= 0 && type < NB_STATXY_TYPE);

		double value = STAT_VMISS;

		const CStatisticXY& me = *this;
		double nbValues = m_x[NB_VALUE];

		if (nbValues > 0 || type == NB_VALUE)
		{
			double nbVal = (nbValues == 1) ? nbValues : nbValues - 1;
			switch (type)
			{
			case LOWEST:	value = m_lowest; break;
			case MEAN:		value = m_e / nbValues; break;//mean of error (bias) (m_y[MEAN] + m_x[MEAN]) / 2; break;
			case MEAN_X:	value = m_x[MEAN]; break;
			case MEAN_Y:	value = m_y[MEAN]; break;
			case TSS:		value = m_y[TSS]; break;
			case INTERCEPT: value = m_y[MEAN] - m_x[MEAN] * me[SLOPE]; break;
			case SLOPE:
				if (m_x[TSS] != 0)
					value = (m_xy - (m_x[SUM] * m_y[SUM] / nbValues)) / m_x[TSS];
				break;
			case COVARIANCE: value = ((m_xy - m_x[SUM] * m_y[MEAN] - m_y[SUM] * m_x[MEAN] + nbValues*m_x[MEAN] * m_y[MEAN])) / nbVal; break;
			case CORRELATION:
			{
				double stdDev = (m_x[STD_DEV] * m_y[STD_DEV]);
				if (stdDev != 0)
					value = me[COVARIANCE] / stdDev;
				break;
			}
			case BIAS:		value = m_e / nbValues; break;
			case MAE:		value = m_ae / nbValues; break;
			case RMSE:		value = sqrt(m_rs / nbValues); break;
			case RSS:		value = m_rs; break;
			case COEF_D:	value = me[TSS] > 0 ? (1 - me[RSS] / me[TSS]) : STAT_VMISS; break;
			case COEF_C:
			{
				double d = sqrt(nbValues*m_x[SUM²] - Square(m_x[SUM]))*sqrt(nbValues*m_y[SUM²] - Square(m_y[SUM]));
				if (d != 0)
					value = (nbValues*m_xy - m_x[SUM] * m_y[SUM]) / d;
				break;
			}
			case STAT_R²:
			{
				double c = me[COEF_C];
				if (c > STAT_VMISS)
					value = Square(c);
				break;
			}
			case HIGHEST:	value = m_hightest; break;
			case RANGE:		value = m_hightest - m_lowest; break;
			case NB_VALUE:	value = nbValues; break;
			default: _ASSERTE(false);
			}
		}

		_ASSERTE(!_isnan(value));

		return value;
	}

	double CStatisticXY::GetVMiss(){ return STAT_VMISS; }
	void CStatisticXY::SetVMiss(double vmiss){ STAT_VMISS = vmiss; }

	//*****************************************************************
	//CStatisticXYEx: statistic of 2 variables, keeps all values in memory
	CStatisticXYEx::CStatisticXYEx()
	{
		Reset();
	}

	void CStatisticXYEx::Reset()
	{
		CStatisticXY::Reset();
		m_xValues.clear();
		m_yValues.clear();
	}

	CStatisticXYEx& CStatisticXYEx::operator=(const CStatisticXYEx& in)
	{
		if (&in != this)
		{
			CStatisticXY::operator=(in);
			m_xValues = m_xValues;
			m_yValues = m_yValues;
		}

		return *this;
	}

	double CStatisticXYEx::operator[](size_t type)const
	{
		_ASSERTE(m_xValues.size() == m_yValues.size());
		_ASSERTE(m_xValues.size() == GetX()[NB_VALUE]);
		_ASSERTE(m_yValues.size() == GetY()[NB_VALUE]);
		_ASSERTE(type >= 0 && type < NB_STATXY_TYPE_EX);

		const CStatisticXYEx& me = *this;
		double value = STAT_VMISS;

		if (m_xValues.size() > 0)
		{
			if (type == INTERCEPT_THEIL_SEN)
			{
				value = m_yValues[MEDIAN] - m_xValues[MEDIAN] * me[SLOPE_THEIL_SEN];
			}
			else if (type == SLOPE_THEIL_SEN)
			{
				CStatisticEx stat;
				ASSERT(m_xValues.size() == m_yValues.size());
				for (size_t i = 0; i < m_xValues.size(); i++)
				{
					for (size_t j = i + 1; j < m_xValues.size(); j++)
					{
						double deltaX = (m_xValues[i] - m_xValues[j]);
						double deltaY = (m_yValues[i] - m_yValues[j]);
						if (deltaX != 0)
							stat += deltaY / deltaX;
					}
				}

				value = stat[MEDIAN];
			}
			//else if (type == LOG_LIKELIHOOD1)
			//{
			//	//double xSum = m_x[SUM];
			//	double ySum = m_y[SUM];
			//	if (ySum > 0)
			//	{
			//		double LL = 0;
			//		//likelihood with classical sigma hat
			//		double sigma = sqrt(me[RSS] / (m_xValues.size() - 1))*(m_xValues.size()) / (m_xValues.size() - 1);
			//		for (size_t i = 0; i < m_xValues.size(); i++)
			//		{
			//			double m = m_xValues[i];
			//			boost::math::normal_distribution<> N(m, sigma);

			//			double x = m_yValues[i];
			//			double p = boost::math::pdf(N, x);
			//			ASSERT(p > 0);
			//			LL += log(p);
			//		}

			//		value = LL;
			//	}
			//}
			else if (type == LIKELIHOOD)
			{
				double xSum = m_x[SUM];
				double ySum = m_y[SUM];
				if (ySum > 0)
				{
					double LL = LogFactorial(xSum);
					for (size_t i = 0; i < m_xValues.size(); i++)
					{
						double p = pow(m_yValues[i] / ySum, m_xValues[i]);
						LL += log(p);
						LL -= LogFactorial(m_xValues[i]);
					}

					value = LL;
				}
			}
			else
			{
				value = CStatisticXY::operator[](type);
			}
		}

		_ASSERTE(!_isnan(value) && _finite(value));
		return value;
	}

	//x = pred, y = obs
	CStatisticXYEx& CStatisticXYEx::Add(double x, double y)
	{
		m_CS.Enter();
		CStatisticXY::Add(x, y);

		m_xValues.push_back(x);
		m_yValues.push_back(y);
		m_CS.Leave();

		return *this;
	}

	CStatisticXYEx& CStatisticXYEx::operator+=(const CStatisticXYEx& statistic)
	{
		CStatisticXY::operator+=(statistic);
		m_xValues.insert(m_xValues.end(), statistic.m_xValues.begin(), statistic.m_xValues.end());
		m_yValues.insert(m_yValues.end(), statistic.m_yValues.begin(), statistic.m_yValues.end());

		return *this;
	}

	//cook distance
	std::vector<double> CStatisticXYEx::GetCookDistance()const
	{
		ASSERT(m_xValues.size() == m_yValues.size());

		std::vector<double> cookD(x().size());

		vector<array<double, 2>> X(x().size());
		for (size_t i = 0; i < x().size(); i++)
		{
			X[i][0] = 1;
			X[i][1] = x(i);
		}

		double d = (m_x[NB_VALUE] * m_x[SUM²]) - Square(m_x[SUM]);
		array<array<double, 2>, 2> s =
		{
			{{m_x[SUM²] / d,-m_x[SUM] / d},
			{-m_x[SUM] / d, m_x[NB_VALUE] / d}}
		};

		vector<vector<double>> P(X.size());
		for (size_t i = 0; i < X.size(); i++)
		{
			P[i].resize(X.size());
			for (size_t j = 0; j < X.size(); j++)
			{
				P[i][j] = (s[0][0] * X[i][0] + s[1][0] * X[i][1])*X[j][0] + (s[0][1] * X[i][0] + s[1][1] * X[i][1])*X[j][1];
			}
		}

		array<double, 2> b = { 0 };
		for (size_t i = 0; i < X.size(); i++)
		{
			b[0] += (s[0][0] * X[i][0] + s[1][0] * X[i][1])*y(i);
			b[1] += (s[0][1] * X[i][0] + s[1][1] * X[i][1])*y(i);
		}

		double RSS=0;
		for (size_t i = 0; i < X.size(); i++)
			RSS += Square(y(i) - (b[0] * X[i][0] + b[1] * X[i][1]));

		size_t k = 2;
		double s2 = RSS / (X.size() - k);                  //three predictors(including intercept(100 - k = 98))

		for (size_t i = 0; i < X.size(); i++)
		{
			double res² = Square(y(i) - (b[0] * X[i][0] + b[1] * X[i][1]));
			cookD[i] = (res² / (k*s2))*(P[i][i] / Square(1.0 - P[i][i]));
		}

		return cookD;

	}

	//*************************************************************
	//Weighted CStatisticXY
	CStatisticXYW::CStatisticXYW()
	{
		m_wxy = 0;
		m_we = 0;
		m_wrs = 0;
		m_wae = 0;
	}

	void CStatisticXYW::Reset()
	{
		m_xw.clear();
		m_yw.clear();
		m_wxy = 0;
		m_we = 0;
		m_wrs = 0;
		m_wae = 0;
	}

	CStatisticXYW& CStatisticXYW::operator=(const CStatisticXYW& in)
	{
		m_xw = in.m_xw;
		m_yw = in.m_yw;
		m_wxy = in.m_wxy;
		m_we = in.m_we;
		m_wrs = in.m_wrs;
		m_wae = in.m_wae;

		return *this;
	}

	CStatisticXYW& CStatisticXYW::Add(double x, double y, double w)
	{
		m_xw.insert(x, w);
		m_yw.insert(y, w);
		double e = x - y;
		m_wxy += w*x*y;
		m_we += w*e;
		m_wrs += w*e*e;
		m_wae += w*fabs(e);

		return *this;
	}

	CStatisticXYW& CStatisticXYW::operator+=(const CStatisticXYW& in)
	{
		m_xw += in.m_xw;
		m_yw += in.m_yw;
		m_wxy += in.m_wxy;
		m_we += in.m_we;
		m_wrs += in.m_wrs;
		m_wae += in.m_wae;

		return *this;
	}

	double CStatisticXYW::operator[](size_t type)const
	{
		ASSERT(m_xw[NB_VALUE] == m_yw[NB_VALUE]);
		ASSERT(type >= 0 && type < NB_STATXY_TYPE);

		double value = STAT_VMISS;

		const CStatisticXYW& me = *this;
		double nbValues = m_xw[NB_VALUE];

		if (nbValues > 0 || type == NB_VALUE)
		{
			switch (type)
			{
			case LOWEST: /*value = m_e[LOWEST];*/ break;
			case MEAN: value = m_we / m_xw.W(SUM); break;
			case MEAN_X: value = m_xw[MEAN]; break;
			case MEAN_Y:value = m_yw[MEAN]; break;
			case TSS: value = m_yw[TSS]; break;
			case INTERCEPT: value = m_yw[MEAN] - m_xw[MEAN] * me[SLOPE]; break;
			case SLOPE:
				if (m_xw[TSS] != 0)
					value = (m_wxy - (m_xw[SUM] * m_yw[SUM] / m_xw.W(SUM))) / m_xw[TSS];
				break;
			case COVARIANCE: value = ((m_wxy - m_xw[SUM] * m_yw[MEAN] - m_yw[SUM] * m_xw[MEAN] + m_xw.W(SUM)*m_xw[MEAN] * m_yw[MEAN])) / m_xw.W(SUM); break;
			case CORRELATION:
			{
				double stdDev = (m_xw[STD_DEV] * m_yw[STD_DEV]);
				if (stdDev != 0)
					value = me[COVARIANCE] / stdDev;
				break;
			}

			case BIAS: value = m_we / m_xw.W(SUM); break;
			case MAE: value = m_wae / m_xw.W(SUM); break;
			case RMSE: value = sqrt(m_wrs / m_xw.W(SUM)); break;//ici il ya une erreur dans le calcul... a faire
			case RSS: value = m_wrs / m_xw.W(SUM); break;
			case COEF_D: value = me[TSS] > 0 ? (1 - me[RSS] / me[TSS]) : STAT_VMISS; break;
			case COEF_C:
			{
				double d = sqrt(max(0.0, m_xw.W(SUM)*m_xw[SUM²] - Square(m_xw[SUM])))*sqrt(max(0.0, m_xw.W(SUM) * m_yw[SUM²] - Square(m_yw[SUM])));
				if (d != 0)
					value = (m_xw.W(SUM)*m_wxy - m_xw[SUM] * m_yw[SUM]) / d;
				break;
			}
			case STAT_R²:
			{
				double c = me[COEF_C];
				if (c > STAT_VMISS)
					value = Square(c);
				break;
			}
			case HIGHEST: value = STAT_VMISS; break;
			case RANGE: value = STAT_VMISS; break;
			case NB_VALUE: value = nbValues; break;
			default: _ASSERTE(false);
			}
		}

		_ASSERTE(!_isnan(value));

		return value;
	}

	//************************************************************

	CClassify::CClassify()
	{
		Reset();
	}

	void CClassify::Reset()
	{
		m_stat.clear();
		m_values.clear();
	}

	void CClassify::Add(double x, double y)
	{
		m_values.push_back(CXY<double, double>(x, y));
	}

	void CClassify::ClassifyManual(vector<double> classLimit)
	{
		_ASSERTE(classLimit.size() >= 2);
		m_stat.clear();
		m_stat.resize(classLimit.size() - 1);

		if (m_values.size() > 0)
		{
			std::sort(m_values.begin(), m_values.end());

			double first = m_values.begin()->m_x;
			double last = m_values.rbegin()->m_x;
			if (classLimit[0] == -1)
				classLimit[0] = first;

			if (classLimit[classLimit.size() - 1] == -1)
				classLimit[classLimit.size() - 1] = last;

			int j = 0;
			for (int i = 0; i<int(classLimit.size() - 1); i++)
			{
				while (j < (int)m_values.size() &&
					m_values[j].m_x >= classLimit[i] &&
					m_values[j].m_x < classLimit[i + 1])
				{
					m_stat[i].m_x += m_values[j].m_x;
					m_stat[i].m_y += m_values[j].m_y;
					j++;
				}
			}
		}
	}

	void CClassify::ClassifyEqualInterval(short nbClass)
	{
		m_stat.clear();
		m_stat.resize(nbClass);

		if (m_values.size() > 0)
		{
			std::sort(m_values.begin(), m_values.end());

			double first = m_values.begin()->m_x;
			double last = m_values.rbegin()->m_x;
			double classSize = (double)(last - first) / nbClass;
			int j = 0;
			for (int i = 0; i < nbClass; i++)
			{
				double limit = first + (i + 1)*classSize;

				while (j < (int)m_values.size() &&
					m_values[j].m_x <= limit)
				{
					m_stat[i].m_x += m_values[j].m_x;
					m_stat[i].m_y += m_values[j].m_y;
					j++;
				}
			}
		}
	}

	void CClassify::ClassifyNaturalBreak(short nbClass)
	{
		m_stat.clear();
		m_stat.resize(nbClass);

		std::sort(m_values.begin(), m_values.end());

		double classSize = (double)m_values.size() / nbClass;
		for (int i = 0; i < nbClass; i++)
		{
			int f = int(i*classSize);
			int l = int((i + 1)*classSize);
			_ASSERTE(f >= 0 && f <= (int)m_values.size());
			_ASSERTE(l >= 0 && l <= (int)m_values.size());

			for (int j = f; j < l; j++)
			{
				m_stat[i].m_x += m_values[j].m_x;
				m_stat[i].m_y += m_values[j].m_y;
			}
		}
	}

}//namespace WBSF


// Implementing the class that is defined in the header file: ChooserOption.hpp
//
// (c) Sudhansh Dua

#include "ChooserOption.hpp"
#include <string>
#include <boost/math/distributions.hpp>
#include <cmath>

using namespace std;
using namespace boost::math;

//	Gaussian functions using boost libraries
double ChooserOption::N(double x) const
{
	normal_distribution<> Standard_normal(0.0, 1.0);
	return cdf(Standard_normal, x);
}

double ChooserOption::n(double x) const
{
	normal_distribution<> Standard_normal(0.0, 1.0);
	return pdf(Standard_normal, x);
}

double ChooserOption::ChooserPrice() const
{
	return ::ChooserPrice(S, K, T, t, r, sig, b);
}

// Initialising all the default values
void ChooserOption::init()
{
	//	Default values
	r = 0.03;
	sig = 0.2;
	K = 105;
	S = 100;			//	Default stock price
	b = r;				//	Black - Scholes(1973) stock option model : b = r
	T = 1;
	t = 0.5;			//	time that has passed since t = 0

}

void ChooserOption::copy(const ChooserOption& option)
{
	r = option.r;
	t = option.t;
	T = option.T;
	sig = option.sig;
	K = option.K;
	b = option.b;
	S = option.S;
}

//	Constructors and destructor
//	Default Constructor
ChooserOption::ChooserOption() : Option()
{
	init();
}

//	Copy constructor
ChooserOption::ChooserOption(const ChooserOption& option) : Option(option)
{
	copy(option);
}

//	Constructor that accepts values
ChooserOption::ChooserOption(const double& S1, const double& K1, const double& T1, const double& t1, const double& r1,
	const double& sig1, const double& b1) : Option(), S(S1), K(K1), T(T1), t(t1), r(r1), sig(sig1), b(b1) {}

//	Destructor
ChooserOption::~ChooserOption() {}

//	Assignment Operator
ChooserOption& ChooserOption::operator = (const ChooserOption& option)
{
	if (this == &option)
	{
		return *this;		//	Self-assignment check!
	}
	Option::operator = (option);
	copy(option);
	return *this;
}

// Member function that calculate the option price
double ChooserOption::Price() const
{
	return ChooserPrice();
}

// Global functions
double ChooserPrice(const double S, const double K, const double T, const double t, const double r, const double sig, const double b)
{

	double y1 = (log(S / K) + (b * T) + (sig * sig * 0.5 * t)) / (sig * sqrt(t));
	double y2 = y1 - (sig * sqrt(t));

	double d1 = (log(S / K) + (b + (sig * sig) * 0.5) * T) / (sig * sqrt(T));
	double d2 = d1 - (sig * sqrt(T));

	normal_distribution<> standard_normal(0.0, 1.0);

	double w = (S * exp((b - r) * T) * cdf(standard_normal, d1)) - (K * exp(-r * T) * cdf(standard_normal, d2)) - (S * exp((b - r) * T) * cdf(standard_normal, -y1)) + (K * exp(-r * T) * cdf(standard_normal, -y2));
	return w;

}


/*
 * Copyright 2021 MusicScience37 (Kenta Kabashima)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*!
 * \file
 * \brief Example of 1-dimensional RBF interpolation.
 */
#include <Eigen/Core>
#include <pybind11/eigen.h>
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include "num_collect/interp/kernel/euclidean_distance.h"
#include "num_collect/interp/kernel/gaussian_rbf.h"
#include "num_collect/interp/kernel/kernel_interpolator.h"
#include "num_collect/interp/kernel/rbf_kernel.h"

auto main() -> int {
    using num_collect::interp::kernel::euclidean_distance;
    using num_collect::interp::kernel::gaussian_rbf;
    using num_collect::interp::kernel::kernel_interpolator;
    using num_collect::interp::kernel::rbf_kernel;

    using kernel_type =
        rbf_kernel<euclidean_distance<double>, gaussian_rbf<double>>;

    const auto vars = std::vector<double>{0.0, 0.1, 0.5, 0.4, 1.2, 1.0};
    const auto data = Eigen::VectorXd{{0.0, 0.2, 0.5, 0.7, 1.0, 2.0}};

    auto interpolator = kernel_interpolator<kernel_type>();
    interpolator.compute(vars, data);

    constexpr num_collect::index_type num_samples = 201;
    const Eigen::VectorXd sample_vars =
        Eigen::VectorXd::LinSpaced(num_samples, -0.1, 1.3);
    Eigen::VectorXd sample_data = Eigen::VectorXd::Zero(num_samples);
    Eigen::VectorXd sample_upper = Eigen::VectorXd::Zero(num_samples);
    Eigen::VectorXd sample_lower = Eigen::VectorXd::Zero(num_samples);
    for (num_collect::index_type i = 0; i < num_samples; ++i) {
        const auto [mean, var] =
            interpolator.evaluate_mean_and_variance_on(sample_vars(i));
        const auto err = 3.0 * std::sqrt(var);
        sample_data(i) = mean;
        sample_upper(i) = mean + err;
        sample_lower(i) = mean - err;
    }

    pybind11::scoped_interpreter interpreter;
    auto go = pybind11::module::import("plotly.graph_objects");
    auto fig = go.attr("Figure")();

    // upper and lower limits
    fig.attr("add_trace")(go.attr("Scatter")(pybind11::arg("x") = sample_vars,
        pybind11::arg("y") = sample_lower, pybind11::arg("mode") = "lines",
        pybind11::arg("name") = "Lower bound (3 sigma)"));
    fig.attr("add_trace")(go.attr("Scatter")(pybind11::arg("x") = sample_vars,
        pybind11::arg("y") = sample_upper, pybind11::arg("mode") = "lines",
        pybind11::arg("name") = "Uppper bound (3 sigma)",
        pybind11::arg("fill") = "tonexty"));

    // interpolated line
    fig.attr("add_trace")(go.attr("Scatter")(pybind11::arg("x") = sample_vars,
        pybind11::arg("y") = sample_data, pybind11::arg("mode") = "lines",
        pybind11::arg("name") = "Interpolation"));

    // inputs
    fig.attr("add_trace")(go.attr("Scatter")(pybind11::arg("x") = vars,
        pybind11::arg("y") = data, pybind11::arg("mode") = "markers",
        pybind11::arg("name") = "Input data"));

    fig.attr("update_layout")(pybind11::arg("title") = "RBF Interpolation",
        pybind11::arg("xaxis_title") = "x", pybind11::arg("yaxis_title") = "y");

    fig.attr("write_html")("interp_rbf_1dim.html");
    fig.attr("write_image")("interp_rbf_1dim.png");
}


#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>
#include <random>
#include <limits>

#include "GRANSAC.hpp"
#include "least_squares_model.hpp"

#include <vector>
#include <Eigen/QR>

using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 1 && argc != 3)
    {
        std::cout << "[ USAGE ]: " << argv[0] << " [<Image Size> = 1000] [<nPoints> = 500]" << std::endl;
        return -1;
    }

    int side = 400;
    int n_points = 50;
    if (argc == 3)
    {
        side = std::atoi(argv[1]);
        n_points = std::atoi(argv[2]);
    }

    cv::Mat img_canvas(side, side, CV_8UC3);
    img_canvas.setTo(255);

    // Randomly generate points in a 2D plane roughly aligned in a line for testing
    std::random_device seed_device;
    std::mt19937 RNG = std::mt19937(seed_device());

    std::uniform_int_distribution<int> uni_dist(0, side - 1); // [Incl, Incl]
    int perturb = 40;
    std::normal_distribution<float> perturb_dist(0, perturb);

    std::vector<std::shared_ptr<GRANSAC::AbstractParameter>> cand_points;
    while (n_points > 0){
        int diag = uni_dist(RNG);
        float x = diag + perturb_dist(RNG);
        // y = 2100-20x+0.05*x*x
        float y;
        if (n_points > 5 )
            y = 2100 - 20 * x + 0.05 * x * x + perturb_dist(RNG);
        else
            y = uni_dist(RNG);

        if (x<=side && x > 0 && y<=side && y > 0){
            cv::Point pt(floor(x), floor(y));
            cv::circle(img_canvas, pt, floor(side / 100) + 2, cv::Scalar(0, 0, 0), 2, cv::LINE_AA);

            std::shared_ptr<GRANSAC::AbstractParameter> cand_pt = std::make_shared<GRANSAC::Point2D>(pt.x, pt.y);
            cand_points.push_back(cand_pt);

            n_points -= 1;
        }
    }

    //set grid
    std::map<string, float> additional_params = {{"img_width", float(side)}, {"img_height", float(side)}, {"grid_num_x", float(side/10)}, {"grid_num_y", float(side/10)}};

    //draw grid
    cv::Mat img_overlay;
    float alpha = 0.3;
    img_canvas.copyTo(img_overlay);
    for (int i=0; i < side/10; i++){
        cv::Point pt_1(i*10, 0);
        cv::Point pt_2(i*10, side);
        cv::line(img_overlay, pt_1, pt_2, cv::Scalar(0, 100, 0), 1, cv::LINE_AA, 0);
        cv::Point pt_3(0, i*10);
        cv::Point pt_4(side, i*10);
        cv::line(img_overlay, pt_3, pt_4, cv::Scalar(0, 100, 0), 1, cv::LINE_AA, 0);
    }
    cv::addWeighted(img_overlay, alpha, img_canvas, 1 - alpha, 0, img_canvas);

    // estimate parameters with RANSAC
    int param_num = 4; // 3rd order
    additional_params["sample_number"]=10; // sample number for over-deterministic least squares
    GRANSAC::RANSAC<GRANSAC::LinearLeastSquaresModel<4>, 4> estimator;
    estimator.initialize(1, 100, additional_params); // Threshold, iterations, the threshold is p-1 distance on grid, 1 means 1 grid cell distance

    float time_average = 0;
    for (int i=0; i<100; i++){
        int start = cv::getTickCount();
        estimator.estimate(cand_points);
        int end = cv::getTickCount();
        float time_of_trial = float(end - start) / float(cv::getTickFrequency()) * 1000.0; // [ms]
        time_average += time_of_trial;
    }
    std::cout << "RANSAC took, average time in 100 trials: " << time_average/100 << " ms." << std::endl;

    // draw definition points from best model
    auto best_inliers = estimator.getBestInliers();
    if (best_inliers.size() > 0)
    {
        for (auto &inlier : best_inliers)
        {
            auto RPt = std::dynamic_pointer_cast<GRANSAC::Point2D>(inlier);
            cv::Point pt(floor(RPt->m_point2D[0]), floor(RPt->m_point2D[1]));
            cv::circle(img_canvas, pt, floor(side / 100), cv::Scalar(0, 255, 0), -1, cv::LINE_AA);
        }
    }

    // draw curve from best model
    auto best_line = estimator.getBestModel();
    if (best_line)
    {
        cout << "coefficients are : " << endl;
        for (auto each : best_line->getModelCoefficients()){
            cout << each << ", ";
        }
        cout << endl;

        std::vector<float> x_values, y_values, coeff;
        for (int i=0; i<best_line->getModelDefParams().size(); i++){
            auto best_line_pt = std::dynamic_pointer_cast<GRANSAC::Point2D>(best_line->getModelDefParams()[i]);
            x_values.push_back(best_line_pt->m_point2D[0]); //x
            y_values.push_back(best_line_pt->m_point2D[1]); //x
            cv::Point pt(floor(best_line_pt->m_point2D[0]), floor(best_line_pt->m_point2D[1]));
            cv::circle(img_canvas, pt, floor(side / 100), cv::Scalar(0, 0, 255), -1, cv::LINE_AA);
        }

        // draw polynomial line
        float start_point_x = 0;
        float end_point_x = side;
        vector<cv::Point2f> curve_points;
        //Define the curve through equation. In this example, a simple parabola
        for (float x = start_point_x; x <= end_point_x; x+=1){
            float y = 0;
            for (int j = 0; j < best_line->getModelCoefficients().size(); j++){
                y += best_line->getModelCoefficients()[j]*std::pow(x, float(j));
            }

            cv::Point2f new_point = cv::Point2f(x, y);                  //resized to better visualize
            curve_points.push_back(new_point);                       //add point to vector/list
        }
        for (int i = 0; i < curve_points.size() - 1; i++){
            cv::line(img_canvas, curve_points[i], curve_points[i + 1], cv::Scalar(0,255,0), 2, CV_AA);
        }
    }

    while (true)
    {
        cv::imshow("RANSAC Example", img_canvas);
        char Key = cv::waitKey(1);
        if (Key == 27)
            return 0;
    }

    return 0;
}


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

using namespace boost::numeric::odeint;

const double b = 0.1;
const double g = 0.05;

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

void sir( const state_type &x , state_type &dxdt , double t )
{
    dxdt[0] = -b * x[0] * x[1];
    dxdt[1] = b * x[0] * x[1] - g * x[1];
    dxdt[2] = g * x[1];
}

void write_sir(const state_type &x , const double t )
{
    std::cout << t << ' ' << x[0] << ' ' << x[1] << ' ' << x[2] << std::endl;
}

int main(int argc, char **argv)
{
    state_type x = { 0.99 , 0.01 , 0.0 }; // initial conditions
    integrate( sir , x , 0.0 , 200.0 , 0.1 , write_sir );
}


// Spline fit with unknown parameter positions.

#include <iostream>

#include "main.h"
#include <unsupported/Eigen/LevenbergMarquardt>
#include <unsupported/Eigen/SparseExtra>

#include <Eigen/Eigen>
#include "unsupported/Eigen/src/SparseExtra/BlockSparseQR.h"
#include "unsupported/Eigen/src/SparseExtra/BlockDiagonalSparseQR.h"

// This disables some useless Warnings on MSVC.
// It is intended to be done for this test only.
#include <Eigen/src/Core/util/DisableStupidWarnings.h>

#define NUM_SAMPLE_POINTS 100

struct SplineParameter {
  int segment;
  double t;
};

template <typename _Scalar>
struct SplineFitting : LevenbergMarquardtFunctor<_Scalar>
{
  typedef LevenbergMarquardtFunctor<Scalar> Base;

  typedef int Index;
  typedef Matrix<Scalar, Dynamic, 1> InputType;
  typedef Matrix<Scalar, Dynamic, 1> ValueType;
  typedef Matrix<Scalar, Dynamic, 1> StepType;

  typedef SparseMatrix<Scalar, ColMajor, Index> JacobianType;

  typedef SparseQR<SparseMatrix<Scalar>, COLAMDOrdering<int> > BlockSolver;
  typedef ColPivHouseholderQR<Matrix<Scalar, Dynamic, Dynamic> > DenseSolver;
  typedef BlockDiagonalSparseQR<JacobianType, DenseSolver> LeftSuperBlockSolver;
  typedef BlockSparseQR<JacobianType, LeftSuperBlockSolver, DenseSolver> QRSolver;

  const Eigen::Matrix<double, 3, Eigen::Dynamic> SplinePoints;

  static const int nParamsModel = 5;

  SplineFitting(Eigen::Matrix<double, 3, Eigen::Dynamic>& points) :
    Base(nParamsModel + points.cols(), points.cols() * 2),
    SplinePoints(points)
  {
  }

  // Functor functions
  int operator()(const InputType& uv, ValueType& fvec) {
    int npoints = SplinePoints.cols();
    auto params = uv.tail(nParamsModel);
    double a = params[0];
    double b = params[1];
    double x0 = params[2];
    double y0 = params[3];
    double r = params[4];
    for (int i = 0; i < npoints; i++) {
      double t = uv[i];
      double x = a*cos(t)*cos(r) - b*sin(t)*sin(r) + x0;
      double y = a*cos(t)*sin(r) + b*sin(t)*cos(r) + y0;
      fvec(2 * i + 0) = SplinePoints(0, i) - x;
      fvec(2 * i + 1) = SplinePoints(1, i) - y;
    }

    return 0;
  }

  int df(const InputType& uv, JacobianType& fjac) {
    // X_i - (a*cos(t_i) + x0)
    // Y_i - (b*sin(t_i) + y0)
    int npoints = SplinePoints.cols();
    auto params = uv.tail(nParamsModel);
    double a = params[0];
    double b = params[1];
    double r = params[4];
    for (int i = 0; i<npoints; i++) {
      double t = uv(i);
      fjac.coeffRef(2 * i, npoints + 0) = -cos(t)*cos(r);
      fjac.coeffRef(2 * i, npoints + 1) = +sin(t)*sin(r);
      fjac.coeffRef(2 * i, npoints + 2) = -1;
      fjac.coeffRef(2 * i, npoints + 4) = +a*cos(t)*sin(r) + b*sin(t)*cos(r);
      fjac.coeffRef(2 * i, i) = +a*cos(r)*sin(t) + b*sin(r)*cos(t);

      fjac.coeffRef(2 * i + 1, npoints + 0) = -cos(t)*sin(r);
      fjac.coeffRef(2 * i + 1, npoints + 1) = -sin(t)*cos(r);
      fjac.coeffRef(2 * i + 1, npoints + 3) = -1;
      fjac.coeffRef(2 * i + 1, npoints + 4) = -a*cos(t)*cos(r) + b*sin(t)*sin(r);
      fjac.coeffRef(2 * i + 1, i) = +a*sin(r)*sin(t) - b*cos(r)*cos(t);
    }

    fjac.makeCompressed();
    return 0;
  }

  void initQRSolver(QRSolver &qr) {
    // set block size
    qr.getLeftSolver().setSparseBlockParams(2, 1);
    qr.setBlockParams(SplinePoints.cols());
  }

  void increment_in_place(InputType* x, StepType const& p)
  {
    *x += p;
  }

  double estimateNorm(InputType const& x, StepType const& diag)
  {
    return x.cwiseProduct(diag).stableNorm();
  }
};

void testSplineFitting() {

  // Spline PARAMETERS
  double a, b, x0, y0, r;
  a = 7.5;
  b = 2;
  x0 = 17.;
  y0 = 23.;
  r = 0.23;

  std::cout << "GROUND TRUTH   " << " ";
  std::cout << "a=" << a << "\t";
  std::cout << "b=" << b << "\t";
  std::cout << "x0=" << x0 << "\t";
  std::cout << "y0=" << y0 << "\t";
  std::cout << "r=" << r*180. / EIGEN_PI << "\t";
  std::cout << std::endl;

  // CREATE DATA SAMPLES

  int nDataPoints = NUM_SAMPLE_POINTS;
  Eigen::Matrix<double, 3, Eigen::Dynamic> SplinePoints;
  SplinePoints.resize(3, nDataPoints);
  double incr = 1.3*EIGEN_PI / double(nDataPoints);
  for (int i = 0; i<nDataPoints; i++) {
    double t = double(i)*incr;
    SplinePoints(0, i) = x0 + a*cos(t)*cos(r) - b*sin(t)*sin(r);
    SplinePoints(1, i) = y0 + a*cos(t)*sin(r) + b*sin(t)*cos(r);
    SplinePoints(2, i) = 1;
  }

  // INITIAL PARAMS
  SplineFitting<double>::InputType lm_params;
  auto& params = lm_params;
  params.resize(SplineFitting<double>::nParamsModel + nDataPoints);
  double minX, minY, maxX, maxY;
  minX = maxX = SplinePoints(0, 0);
  minY = maxY = SplinePoints(1, 0);
  for (int i = 0; i<SplinePoints.cols(); i++) {
    minX = (std::min)(minX, SplinePoints(0, i));
    maxX = (std::max)(maxX, SplinePoints(0, i));
    minY = (std::min)(minY, SplinePoints(1, i));
    maxY = (std::max)(maxY, SplinePoints(1, i));
  }
  params(SplinePoints.cols()) = 0.5*(maxX - minX);
  params(SplinePoints.cols() + 1) = 0.5*(maxY - minY);
  params(SplinePoints.cols() + 2) = 0.5*(maxX + minX);
  params(SplinePoints.cols() + 3) = 0.5*(maxY + minY);
  params(SplinePoints.cols() + 4) = 0;
  for (int i = 0; i<SplinePoints.cols(); i++) {
    params(i) = double(i)*incr;
  }

  std::cout << "INITIALIZATION" << " ";
  std::cout << "a=" << params(SplinePoints.cols()) << "\t";
  std::cout << "b=" << params(SplinePoints.cols() + 1) << "\t";
  std::cout << "x0=" << params(SplinePoints.cols() + 2) << "\t";
  std::cout << "y0=" << params(SplinePoints.cols() + 3) << "\t";
  std::cout << "r=" << params(SplinePoints.cols() + 4)*180. / EIGEN_PI << "\t";
  std::cout << std::endl << std::endl;

  SplineFitting<double> functor(SplinePoints);
  Eigen::LevenbergMarquardt< SplineFitting<double> > lm(functor);
  Eigen::LevenbergMarquardtSpace::Status info;

  info = lm.minimizeInit(lm_params);
  if (info == Eigen::LevenbergMarquardtSpace::ImproperInputParameters) {
    std::cerr << "Improper Input Parameters" << std::endl;
    return;
  }

  do {
    info = lm.minimizeOneStep(lm_params);
  } while (info == Eigen::LevenbergMarquardtSpace::Running);

  std::cout << "END" << " ";
  std::cout << "a=" << params(SplinePoints.cols()) << "\t";
  std::cout << "b=" << params(SplinePoints.cols() + 1) << "\t";
  std::cout << "x0=" << params(SplinePoints.cols() + 2) << "\t";
  std::cout << "y0=" << params(SplinePoints.cols() + 3) << "\t";
  std::cout << "r=" << params(SplinePoints.cols() + 4)*180. / EIGEN_PI << "\t";
  std::cout << std::endl << std::endl;

  // check parameters ambiguity before test result
  // a should be bigger than b
  if (fabs(params(SplinePoints.cols() + 1)) > fabs(params(SplinePoints.cols()))) {
    std::swap(params(SplinePoints.cols()), params(SplinePoints.cols() + 1));
    params(SplinePoints.cols() + 4) -= 0.5*EIGEN_PI;
  }
  // a and b should be positive
  if (params(SplinePoints.cols())<0) {
    params(SplinePoints.cols()) *= -1.;
    params(SplinePoints.cols() + 1) *= -1.;
    params(SplinePoints.cols() + 4) += EIGEN_PI;
  }
  // fix rotation angle range
  while (params(SplinePoints.cols() + 4) < 0) params(SplinePoints.cols() + 4) += 2.*EIGEN_PI;
  while (params(SplinePoints.cols() + 4) > EIGEN_PI) params(SplinePoints.cols() + 4) -= EIGEN_PI;

  eigen_assert(fabs(a - params(SplinePoints.cols())) < 0.00001);
  eigen_assert(fabs(b - params(SplinePoints.cols() + 1)) < 0.00001);
  eigen_assert(fabs(x0 - params(SplinePoints.cols() + 2)) < 0.00001);
  eigen_assert(fabs(y0 - params(SplinePoints.cols() + 3)) < 0.00001);
  eigen_assert(fabs(r - params(SplinePoints.cols() + 4)) < 0.00001);

}

void test_spline_fitting()
{
  CALL_SUBTEST(testSplineFitting());
}


/**
 * @file cd_tools.cc
 * @brief Utility Functions for the Convection-Diffusion Problem
 * @author Philippe Peter
 * @date July 2021
 * @copyright Developed at SAM, ETH Zurich
 */

#include "cd_tools.h"

#include <lf/base/base.h>
#include <lf/geometry/geometry.h>
#include <lf/mesh/mesh.h>

#include <Eigen/Core>
#include <algorithm>
#include <memory>

namespace ConvectionDiffusion {

double Diameter(const lf::mesh::Entity& entity) {
  const lf::geometry::Geometry* geo_p = entity.Geometry();
  Eigen::MatrixXd corners = lf::geometry::Corners(*geo_p);

  switch (entity.RefEl()) {
    case lf::base::RefEl::kTria(): {
      // Diameter of a triangle corresponds to the longest edge
      Eigen::Vector2d e0 = corners.col(1) - corners.col(0);
      Eigen::Vector2d e1 = corners.col(2) - corners.col(1);
      Eigen::Vector2d e2 = corners.col(0) - corners.col(1);
      return std::max(e0.norm(), std::max(e1.norm(), e2.norm()));
    }
    case lf::base::RefEl::kQuad(): {
      // Diameter of a (convex) quadrilateral corresponds to the longer diagonal
      Eigen::Vector2d d0 = corners.col(2) - corners.col(0);
      Eigen::Vector2d d1 = corners.col(3) - corners.col(1);
      return std::max(d0.norm(), d1.norm());
    }
    default: {
      LF_ASSERT_MSG(false,
                    "Diameter not available for " << entity.RefEl().ToString());
    }
  }
}  // namespace ConvectionDiffusion

double MeshWidth(std::shared_ptr<const lf::mesh::Mesh> mesh_p) {
  double h = 0.0;
  for (const lf::mesh::Entity* entity_p : mesh_p->Entities(0)) {
    h = std::max(h, Diameter(*entity_p));
  }
  return h;
}

}  // namespace ConvectionDiffusion


#ifndef CANNON_ML_RLS_H
#define CANNON_ML_RLS_H

/*!
 * \file cannon/ml/rls.hpp
 * \brief File containing RLSFilter class definition.
 */

#include <utility>

#include <Eigen/Dense>

using namespace Eigen;

namespace cannon {
  namespace ml {

    /*!
     * \brief Class representing a Recursive Least Squares Filter supporting
     * intercept estimation and forgetting. See
     * http://cannontwo.com/assets/rls_notes.pdf.
     */
    class RLSFilter {
      public:

        RLSFilter() = delete;

        /*!
         * \brief Constructor taking input dimension, output dimension, inverse
         * covariance matrix initialization alpha, and forgetting factor.
         */
        RLSFilter(unsigned int in_dim, unsigned int out_dim,
                  double alpha = 1.0e6, double forgetting = 1.0)
            : in_dim_(in_dim), out_dim_(out_dim), param_dim_(in_dim),
              alpha_(alpha), forgetting_(forgetting),
              intercept_(VectorXd::Zero(out_dim_)),
              theta_(MatrixXd::Zero(param_dim_, out_dim_)),
              corrected_theta_(MatrixXd::Zero(param_dim_, out_dim_)),
              feat_mean_(RowVectorXd::Zero(param_dim_)),
              output_mean_(RowVectorXd::Zero(out_dim_)),
              covar_(MatrixXd::Identity(param_dim_, param_dim_) * alpha_),
              pred_error_covar_(MatrixXd::Zero(out_dim_, out_dim_)) {}

        /*!
         * \brief Get number of data points contributing to this filter.
         *
         * \returns Number of data points used to fit this filter.
         */
        unsigned int get_num_data() const;

        /*!
         * \brief Process a single datum and update this filter.
         *
         * \param in_vec Input features of the datum to process.
         * \param output Output features of the datum to process.
         */
        void process_datum(const VectorXd& in_vec, const VectorXd& output);

        /*!
         * \brief Get the identified linear parameters and offset vector fit by
         * this filter.
         *
         * \returns A pair containing the linear parameters and offset vector.
         */
        std::pair<MatrixXd, VectorXd> get_identified_mats() const;

        /*!
         * \brief Get prediction error covariance matrix for this filter.
         *
         * \returns Prediction error covariance matrix.
         */
        MatrixXd get_pred_error_covar() const;

        /*!
         * \brief Predict the value of the linear approximation fit by this
         * filter for the input point.
         *
         * \param in_vec Input features of point to predict for.
         *
         * \returns Prediction fit by this filter.
         */
        VectorXd predict(const VectorXd& in_vec) const;

        /*!
         * \brief Set the parameters of this RLS filter. Input and output mean
         * are recovered from the intercept.
         *
         * \param theta Parameter matrix to set.
         * \param intercept Intercept to set.
         * \param in_mean Input mean.
         */
        void set_params(const Ref<const MatrixXd> &theta,
                        const Ref<const VectorXd> &intercept,
                        const Ref<const VectorXd> &in_mean);

        /*!
         * \brief Reset this filter.
         */
        void reset();

      private:

        /*!
         * \brief Make internal feature vector for the input features.
         *
         * \param in_vec Input features to transform into internal feature space.
         *
         * \returns Internal features.
         */
        RowVectorXd make_feature_vec_(const VectorXd& in_vec) const;

        /*!
         * \brief Make U matrix for inverse covariance matrix update.
         *
         * \param feat Internal feature vector for update.
         *
         * \returns U matrix.
         */
        MatrixX2d make_u_(const RowVectorXd& feat) const;

        /*!
         * \brief Make V matrix for inverse covariance matrix update.
         *
         * \param feat Internal feature vector for update.
         *
         * \returns V matrix.
         */
        Matrix2Xd make_v_(const RowVectorXd& feat) const;

        /*!
         * \brief Make C matrix for inverse covariance matrix update.
         *
         * \returns C matrix.
         */
        Matrix2d make_c_() const;

        /*!
         * \brief Update inverse covariance matrix for this filter.
         *
         * \param u U matrix
         * \param c C matrix
         * \param v V matrix
         */
        void update_covar_(const MatrixX2d& u, const Matrix2d& c,
            const Matrix2Xd& v);

        /*!
         * \brief Update linear approximation stored by this filter.
         *
         * \param c_t C matrix transpose.
         * \param feat Internal feature vector for new datum.
         * \param output Output for new datum.
         */
        void update_theta_(const MatrixXd& c_t, const RowVectorXd& feat,
            const VectorXd& output);

        /*!
         * \brief Update output mean for this filter.
         *
         * \param output Observed output.
         */
        void update_output_mean_(const VectorXd& output);

        /*!
         * \brief Update internal feature mean for this filter.
         *
         * \param feat Observed internal features.
         */
        void update_feat_mean_(const RowVectorXd& feat);

        /*!
         * \brief Update prediction error covariance matrix in light of an
         * observed datum.
         *
         * \param in_vec Input feature vector.
         * \param output Observed output.
         */
        void update_pred_error_covar_(const VectorXd& in_vec, const VectorXd& output);

        // Parameters
        unsigned int in_dim_; //!< Dimension of input to this filter.
        unsigned int out_dim_; //!< Dimension of output for this filter.
        unsigned int param_dim_; //!< Number of linear parameters of this filter.
        double alpha_; //!< Inverse covariance matrix initialization parameter.
        double forgetting_; //!< Forgetting factor.
        double t_ = 0.0; //!< Number of observed data points.

        // Matrices
        VectorXd intercept_; //!< Intercept vector of filter
        MatrixXd theta_; //!< Linear parameters of filter
        MatrixXd corrected_theta_; //!< Linear parameters corrected for intercept
        RowVectorXd feat_mean_; //!< Mean of internal features for this filter
        RowVectorXd output_mean_; //!< Mean of observed outputs for this filter
        MatrixXd covar_; //!< Internal inverse covariance matrix.
        MatrixXd pred_error_covar_; //!< Prediction error covariance matrix for this filter.

    };

  } // namespace ml
} // namespace cannon

#endif /* ifndef CANNON_ML_RLS_H */


/* Basic example showing the usage of QuantLibAdjoint
*/

#include <ql/indexes/ibor/euribor.hpp>
#include <ql/instruments/makevanillaswap.hpp>
#include <ql/termstructures/yield/flatforward.hpp>

#include <boost/make_shared.hpp>

#include <iostream>

using namespace QuantLib;
using std::vector;
using std::cout;
using std::endl;
using std::ios;

int main(int, char* []) {

	Date referenceDate(3, Aug, 2016);
	Settings::instance().evaluationDate() = referenceDate;
	Actual365Fixed dayCounter;

	// Example 1

	// These will be the X (independent) and Y (dependent) vectors
	vector<Rate> zeroRate(1, 0.02);
	vector<Rate> swapNpv(1, 0.0);

	// Start taping with zeroRate as independent variable and set up flat zero curve
	cl::Independent(zeroRate);
	RelinkableHandle<YieldTermStructure> flatCurve(boost::make_shared<FlatForward>(referenceDate, zeroRate[0], dayCounter));
	flatCurve->enableExtrapolation();

	// Create and price swap
	Period swapTenor(5, Years);
	boost::shared_ptr<IborIndex> iborIndex = boost::make_shared<Euribor6M>(flatCurve);
	Rate fixedRate = 0.03;
	Period forwardStart(0, Days);
	boost::shared_ptr<VanillaSwap> swap = MakeVanillaSwap(swapTenor, iborIndex, fixedRate, forwardStart) .withNominal(100);
	swapNpv[0] = swap->NPV();

	// Stop taping and transfer operation sequence to function f (ultimately an AD function object)
	cl::tape_function<double> f(zeroRate, swapNpv);

	// Calculate d(swapNpv) / d(zero) with forward and reverse mode
	vector<double> dZ(1, 1.0);
	double forwardDeriv = f.Forward(1, dZ)[0];
	double reverseDeriv = f.Reverse(1, dZ)[0];

	// Calculate analytically the derivative
	Real derivative = 0.0;
	const Leg& fixedLeg = swap->fixedLeg();
	for (const auto& cf : fixedLeg) {
		Real amount = cf->amount();
		Time time = dayCounter.yearFraction(referenceDate, cf->date());
		DiscountFactor discount = flatCurve->discount(time);
		derivative += amount * time * discount;
	}
	Time timeToStart = dayCounter.yearFraction(referenceDate, swap->startDate());
	Time timeToEnd = dayCounter.yearFraction(referenceDate, swap->maturityDate());
	derivative += 100*(timeToEnd * flatCurve->discount(timeToEnd) - timeToStart * flatCurve->discount(timeToStart));

	//Compare bumped value to Taylor approximation
	Real basis_point = 0.01;
	Real approx_sensitivity = derivative * basis_point;
	//new bumped yield curve
	//when yield curve changes, swap recalculates
	flatCurve.linkTo(boost::make_shared<FlatForward>(referenceDate, zeroRate[0] + basis_point, dayCounter));
	Real bumped_npv = swap->NPV();

	// Output the results
	cout.precision(9);
	cout.setf(ios::fixed, ios::floatfield);
	cout << "Forward derivative:  " << forwardDeriv << endl;
	cout << "Reverse derivative:  " << reverseDeriv << endl;
	cout << "Analytic derivative: " << derivative << endl;
	//approx should be close to bumped
	cout << "Approx_Sensitivity: " << approx_sensitivity << endl;
	cout << "Bumped Sensitivity: " << bumped_npv - swapNpv[0] << endl;

	return 0;
}


/**
 * @ file pointEvaluation.cc
 * @ brief NPDE homework PointEvaluationRhs code
 * @ author Christian Mitsch, Liaowang Huang (refactoring)
 * @ date 22/03/2019, 06/01/2020 (refactoring)
 * @ copyright Developed at ETH Zurich
 */

#include "pointevaluationrhs.h"

#include <lf/assemble/assemble.h>
#include <lf/base/base.h>
#include <lf/geometry/geometry.h>
#include <lf/mesh/mesh.h>
#include <lf/mesh/utils/utils.h>
#include <lf/uscalfe/uscalfe.h>

#include <Eigen/Core>
#include <Eigen/LU>
#include <Eigen/SparseLU>
#include <cmath>
#include <utility>

#include "pointevaluationrhs_norms.h"

namespace PointEvaluationRhs {

/* SAM_LISTING_BEGIN_5 */
Eigen::Vector2d GlobalInverseTria(Eigen::Matrix<double, 2, 3> mycorners,
                                  Eigen::Vector2d x) {
  Eigen::Vector2d x_hat;

#if SOLUTION
  // The unique affine mapping of the unit triangle to a general triangle
  // is given by the formula $\cob{\Bx = \VA_K*\wh{\Bx} + \Bs}$,
  // Use the vertex coordinates to calculate matrix A and translation vector s
  Eigen::Matrix2d A(2, 2);
  A.col(0) = mycorners.col(1) - mycorners.col(0);
  A.col(1) = mycorners.col(2) - mycorners.col(0);
  // The inverse mapping: $\cob{\wh{\Bx} = \VA_K^{-1} * (\Bx - \Ba_0)}$
  x_hat = A.partialPivLu().solve(x - mycorners.col(0));
#else
  //====================
  // Your code goes here
  //====================
#endif
  return x_hat;
}
/* SAM_LISTING_END_5 */

/** @brief Numerically stable solution of a quadratic equation in R
 * @param a,b,c coefficients of quadratic polynomias ax^2+bx+c
 * @return both zeros, NaN if complex
 */
/* SAM_LISTING_BEGIN_4 */
std::pair<double, double> solveQuadraticEquation(double a, double b, double c) {
  // Implement the cases which are solvable and return their solutions
#if SOLUTION
  if (a != 0.) {
    b /= a;
    c /= a;
    const double D = b * b - 4 * c;  // discriminant
    if (D >= 0) {                    // real solutions
      const double rtD = std::sqrt(D);
      // Real solutions, cancellation-free formulas !
      if (b < 0) {
        const double root = 0.5 * (-b + rtD);
        return {root, c / root};
      } else {  // b >= 0
        const double root = 0.5 * (-b - rtD);
        return {root, c / root};
      }
    }
  } else {
    if (b != 0.0) {
      return {-c / b, -c / b};
    }
  }
#else
  //====================
  // Your code goes here
  //====================
#endif
  // Return NAN if there are no (real) roots
  return {NAN, NAN};
}

/* SAM_LISTING_END_4 */

/** @brief Computes the area of a triangle
 * @param a,b,c vertex coordinate vectors
 */
inline double triaArea(const Eigen::Vector2d a, const Eigen::Vector2d b,
                       const Eigen::Vector2d c) {
  double result = 0;
#if SOLUTION
  result = 0.5 * std::abs((b[0] - a[0]) * (c[1] - a[1]) -
                          (b[1] - a[1]) * (c[0] - a[0]));
#else
  //====================
  // Your code goes here
  //====================
#endif
  return result;
}

Eigen::Vector2d GlobalInverseQuad(Eigen::Matrix<double, 2, 4> vert,
                                  Eigen::Vector2d x) {
  constexpr double kEPS = 1.0E-8;
  Eigen::Vector2d x_hat;

  // Implement and use the functions triaArea and solveQuadraticEquation
#if SOLUTION
  /* SAM_LISTING_BEGIN_1 */
  // I: Find sub-triangle with largest area and use its
  // middle vertex as new "vertex 0".
  unsigned int vt_zero_idx{0};
  double max_area{0.0};
  for (int l = 0; l < 4; ++l) {
    const double sub_tria_area =
        triaArea(vert.col(l), vert.col((l + 1) % 4), vert.col((l + 2) % 4));
    if (sub_tria_area > max_area) {
      vt_zero_idx = (l + 1) % 4;
      max_area = sub_tria_area;
    }
  }
  // vt_zero_idx stores the index of the vertex that will now
  // be regarded as "pivotal vertex 0"

  /* SAM_LISTING_END_1 */

  const Eigen::Vector2d corner0 = vert.col(vt_zero_idx);
  const Eigen::Vector2d corner1 = vert.col((vt_zero_idx + 1) % 4);
  const Eigen::Vector2d corner2 = vert.col((vt_zero_idx + 2) % 4);
  const Eigen::Vector2d corner3 = vert.col((vt_zero_idx + 3) % 4);

  /* SAM_LISTING_BEGIN_2 */
  // Use corners to calculate matrix A, the translation t
  // and the coefficient vector d
  Eigen::Matrix2d A(2, 2);
  A.col(0) = corner1 - corner0;
  A.col(1) = corner3 - corner0;
  Eigen::Vector2d t = corner0;
  Eigen::Vector2d d = corner2 + corner0 - corner1 - corner3;

  // Calculate the coefficients in the non-linear system of equations
  Eigen::Vector2d y = A.partialPivLu().solve(x - t);
  Eigen::Vector2d q = A.partialPivLu().solve(d);
  Eigen::Vector2d q_orth(q(1), -q(0));

  // Treat exceptional cases
  // If q vanishes
  const double ref_size = y.norm();
  if (q.norm() < kEPS * ref_size) {
    x_hat = y;
  } else if (std::abs(q(0)) < kEPS * ref_size) {
    x_hat(0) = y(0);
    x_hat(1) = y(1) / (1 + q(1) * x_hat(0));
  } else if (std::abs(q(1)) < kEPS * ref_size) {
    x_hat(1) = y(1);
    x_hat(0) = y(0) / (1 + q(0) * x_hat(1));
  } else {
    // Generic case; solve quadratic equation
    double a = q(0);
    double b = 1 + y.dot(q_orth);
    double c = -y(1);

    auto [x_op1, x_op2] = solveQuadraticEquation(a, b, c);
    // No solution
    if (std::isnan(x_op1) || std::isnan(x_op2)) {
      x_hat(0) = NAN;
      x_hat(1) = NAN;
    } else {
      // There is a solution
      // Find root in the unit interval -> x_op1
      if ((x_op1 < 0.) || (x_op1 > 1.)) {
        if ((x_op2 >= 0.) && (x_op2 <= 1.)) {
          x_op1 = x_op2;
        }
      }
      x_hat(1) = x_op1;
      x_hat(0) = (y.dot(q_orth) + q(0) * x_hat(1)) / q(1);
    }
  }
  /* SAM_LISTING_END_2 */
  /* SAM_LISTING_BEGIN_3 */
  // Reverse initial permutation
  switch (vt_zero_idx) {
    case 1: {
      x_hat = ((Eigen::Matrix2d() << 0., -1., 1., 0.)).finished() * x_hat +
              Eigen::Vector2d(1.0, 0.0);
      break;
    }
    case 2: {
      x_hat = -x_hat + Eigen::Vector2d(1.0, 1.0);
      break;
    }
    case 3: {
      x_hat = ((Eigen::Matrix2d() << 0., 1., -1., 0.)).finished() * x_hat +
              Eigen::Vector2d(0.0, 1.0);
      break;
    }
  }
    /* SAM_LISTING_END_3 */
#else
  //====================
  // Your code goes here
  //====================
#endif
  return x_hat;
}

std::pair<double, double> normsSolutionPointLoadDirichletBVP(
    const lf::assemble::DofHandler &dofh, Eigen::Vector2d source_point,
    Eigen::VectorXd &sol_vec) {
  std::pair<double, double> result(0, 0);
  const unsigned int N_dofs = dofh.NumDofs();
  sol_vec.resize(N_dofs);
  sol_vec.setZero();
#if SOLUTION
  /* SAM_LISTING_BEGIN_7 */
  // Assemble matrix A
  lf::assemble::COOMatrix<double> A(N_dofs, N_dofs);
  lf::uscalfe::LinearFELaplaceElementMatrix loc_mat_laplace{};
  lf::assemble::AssembleMatrixLocally(0, dofh, dofh, loc_mat_laplace, A);
  // Build rhs vector using dedicated ENTITY_VECTOR_PROVIDER
  Eigen::VectorXd rhs(N_dofs);
  rhs.setZero();
  PointEvaluationRhs::DeltaLocalVectorAssembler myvec_pro(source_point);
  lf::assemble::AssembleVectorLocally(0, dofh, myvec_pro, rhs);

  // Enforce Dirichlet boundary conditions
  const double boundary_val = 0;  // zero Dirichlet boundary conditions
  auto bd_flags{lf::mesh::utils::flagEntitiesOnBoundary(dofh.Mesh(), 2)};
  auto my_selector = [&dofh, &bd_flags, &boundary_val](unsigned int dof_idx) {
    if (bd_flags(dofh.Entity(dof_idx))) {
      return (std::pair<bool, double>(true, boundary_val));
    } else {
      // interior node: the value we return here does not matter
      return (std::pair<bool, double>(false, 42.0));
    }
  };
  lf::assemble::FixFlaggedSolutionComponents<double>(my_selector, A, rhs);

  // Solve linear system of equations A*x = rhs
  const Eigen::SparseMatrix<double> A_crs(A.makeSparse());
  Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
  solver.compute(A_crs);
  if (solver.info() == Eigen::Success) {
    sol_vec = solver.solve(rhs);
  } else {
    LF_ASSERT_MSG(false, "Eigen Factorization failed");
  }
  /* SAM_LISTING_END_7 */
  // return the norms of the solution vector
  result = std::pair<double, double>(
      PointEvaluationRhs::computeL2normLinearFE(dofh, sol_vec),
      PointEvaluationRhs::computeH1seminormLinearFE(dofh, sol_vec));
#else
  //====================
  // Your code goes here
  //====================
#endif
  return result;
}

/* SAM_LISTING_BEGIN_6 */
Eigen::VectorXd DeltaLocalVectorAssembler::Eval(const lf::mesh::Entity &cell) {
  Eigen::VectorXd result;
  // get the coordinates of the corners of this cell
  const lf::geometry::Geometry *geo_ptr = cell.Geometry();
  auto vertices = lf::geometry::Corners(*geo_ptr);
#if SOLUTION
  Eigen::Vector2d x_hat;
  // the margin we allow when we determine wether a point is inside an
  // element
  const double margin = 1e-10;
  if (lf::base::RefEl::kTria() == cell.RefEl()) {
    x_hat = PointEvaluationRhs::GlobalInverseTria(vertices, x_0);
    result.resize(3);
    result.setZero();
    if (x_hat(0) <= 1 + margin && x_hat(0) >= 0 - margin &&
        x_hat(1) <= 1 + margin && x_hat(1) >= 0 - margin &&
        x_hat(1) + x_hat(0) <= 1 + margin) {
      already_found = true;
      // Barycentric coordinates on reference triangle
      result[0] = 1.0 - x_hat(0) - x_hat(1);
      result[1] = x_hat(0);
      result[2] = x_hat(1);
    }
  } else if (lf::base::RefEl::kQuad() == cell.RefEl()) {
    x_hat = PointEvaluationRhs::GlobalInverseQuad(vertices, x_0);
    result.resize(4);
    result.setZero();
    if (x_hat(0) <= 1 + margin && x_hat(0) >= 0 - margin &&
        x_hat(1) <= 1 + margin && x_hat(1) >= 0 - margin) {
      already_found = true;
      // Local shape functions on unit square
      result[0] = (1.0 - x_hat(0)) * (1.0 - x_hat(1));
      result[1] = x_hat(0) * (1.0 - x_hat(1));
      result[2] = x_hat(0) * x_hat(1);
      result[3] = (1.0 - x_hat(0)) * x_hat(1);
    }
  } else {
    LF_ASSERT_MSG(
        false, "Function only defined for triangular or quadrilateral cells");
  }
#else
  //====================
  // Your code goes here
  //====================
#endif
  return result;
}
/* SAM_LISTING_END_6 */

}  // namespace PointEvaluationRhs


// HardCoded.cpp
//
// C++ code to price an option, essential algorithms.
//
// We take CEV model with a choice of the elaticity parameter
// and the Euler method. We give option price and number of times
// S hits the origin.
//
// (C) Datasim Education BC 2008-2011
//

#ifndef _SCL_SECURE_NO_WARNINGS
#define _SCL_SECURE_NO_WARNINGS
#endif

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "OptionData.hpp"
#include "StandardDeviation_StandardError.hpp"
#include "UtilitiesDJD/RNG/NormalGenerator.hpp"
#include "UtilitiesDJD/Geometry/Range.cpp"
#include <boost/tuple/tuple.hpp> // include tuple class
#include <boost/tuple/tuple_io.hpp> // include I/O for tuple
#include <vector>
#include <cmath>
#include <iostream>

template <class T> void print(const std::vector<T>& myList)
{  // A generic print function for vectors

	std::cout << std::endl << "Size of vector is " << l.size() << "\n[";

	// We must use a const iterator here, otherwise we get a compiler error.
	std::vector<T>::const_iterator i;
	for (i = myList.begin(); i != myList.end(); ++i)
	{
			std::cout << *i << ",";

	}

	std::cout << "]\n";
}

namespace SDEDefinition
{ // Defines drift + diffusion + data

	OptionData* data;				// The data for the option MC

	double drift(double t, double X)
	{ // Drift term

		return (data->r)*X; // r - D
	}

	double diffusion(double t, double X)
	{ // Diffusion term

		double betaCEV = 1.0;
		return data->sig * pow(X, betaCEV);

	}

	double diffusionDerivative(double t, double X)
	{ // Diffusion term, needed for the Milstein method

		double betaCEV = 1.0;
		return 0.5 * (data->sig) * (betaCEV) * pow(X, 2.0 * betaCEV - 1.0);
	}
} // End of namespace

int main()
{
	std::cout <<  "1 factor MC with explicit Euler\n";

	/*==========================MODIFIED================================*/
	vector<double> price_vec; // create a vector to store prices for each simulation
	/*================================================================*/

	// Batch 1 data
	OptionData myOption;
	myOption.K = 65.0;
	myOption.T = 0.25;
	myOption.r = 0.08;
	myOption.sig = 0.3;
	myOption.type = -1;	// Put -1, Call +1
	double S_0 = 60.0;

	//// Batch 2 data
	//OptionData myOption;
	//myOption.K = 100.0;
	//myOption.T = 1.0;
	//myOption.r = 0.0;
	//myOption.sig = 0.2;
	//myOption.type = -1;	// Put -1, Call +1
	//double S_0 = 100.0;

	//// Batch 4 data
	//OptionData myOption;
	//myOption.K = 100.0;
	//myOption.T = 30.0;
	//myOption.r = 0.08;
	//myOption.sig = 0.30;
	//myOption.type = 1;	// Put -1, Call +1
	//double S_0 = 100.0;

	long N = 100;
	std::cout << "Number of subintervals in time: ";
	std::cin >> N;

	// Create the basic SDE (Context class)
	Range<double> range (0.0, myOption.T);
	double VOld = S_0;
	double VNew;

	std::vector<double> x = range.mesh(N);

	// V2 mediator stuff
	long NSim = 50000;
	std::cout << "Number of simulations: ";
	std::cin >> NSim;

	double k = myOption.T / double (N);
	double sqrk = sqrt(k);

	// Normal random number
	double dW;
	double price = 0.0;	// Option price

	// NormalGenerator is a base class
	NormalGenerator* myNormal = new BoostNormal();

	using namespace SDEDefinition;
	SDEDefinition::data = &myOption;

	std::vector<double> res;
	int coun = 0; // Number of times S hits origin

	// A.
	for (long i = 1; i <= NSim; ++i)
	{ // Calculate a path at each iteration

		if ((i/10000) * 10000 == i)
		{// Give status after each 10000th iteration

				std::cout << i << std::endl;
		}

		VOld = S_0;
		for (unsigned long index = 1; index < x.size(); ++index)
		{

			// Create a random number
			dW = myNormal->getNormal();

			// The FDM (in this case explicit Euler)
			VNew = VOld  + (k * drift(x[index-1], VOld))
						+ (sqrk * diffusion(x[index-1], VOld) * dW);

			VOld = VNew;

			// Spurious values
			if (VNew <= 0.0) coun++;
		}

		double tmp = myOption.myPayOffFunction(VNew);
		/*==========================MODIFIED================================*/
		price_vec.push_back(tmp); // add each simulation price to the price vector
	    /*================================================================*/
		price += (tmp)/double(NSim);
	}

	// D. Finally, discounting the average price
	price *= exp(-myOption.r * myOption.T);

	// Cleanup; V2 use scoped pointer
	delete myNormal;

	std::cout << "Price, after discounting: " << price << ", " << std::endl;
	std::cout << "Number of times origin is hit: " << coun << endl;

	/*==========================MODIFIED================================*/
	// show the standard deviation and standard error
	boost::tuple<double, double> SD_SE = get_SD_SE(price_vec, myOption.r, myOption.T);

	cout << "SD = " << get<0>(SD_SE) << ", SE = " << get<1>(SD_SE) << endl;
	/*================================================================*/
	system("Pause");

	return 0;
}

Part D Monte Carlo Pricing Methods/Exericse D Monte Carlo Pricing Methods/TestMC.cpp