#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>
#include <map>
#include <unordered_map>
#include <numeric>
#include <Eigen>
#include <time.h>
using namespace std;

// 平面
enum Plane
{
	XOY,
	XOZ,
	YOZ
};

// 空间直方图计算
// 法向量的视点坐标
struct Normal
{
	double x, y, z;
};
struct Point3D
{
	double x, y, z;
	Normal normal;
	double angle;
	// 重点：重载“<”，作为map的键
	bool operator<(const Point3D & p)const {
		return (x < p.x) || (x == p.x && y < p.y);
	}

};

struct Point2D
{
	float x, y;
	bool operator<(const Point2D & p)const {
		return (x < p.x) || (x == p.x && y < p.y);
	}

};

struct Boundary
{
	double xMin, xMax;
	double yMin, yMax;
	double zMin, zMax;
};

ostream & operator<<(ostream & out, Point2D A) {

	out << "[" << A.x << "," << A.y << "]";
	return out;
}

ostream & operator<<(ostream & out, Point3D A) {

	out << "[" << A.x << "," << A.y << "," << A.z << "]";
	return out;
}
ostream & operator<<(ostream & out, Normal A) {

	out << "[" << A.x << "," << A.y << "," << A.z << "]";
	return out;
}
// 读取文件点云数据
vector<Point2D> getData(const char* filename, Plane intestingPlane, vector<float> &boundaryValue) {

	FILE *fp_txt1;

	fp_txt1 = fopen(filename, "r");

	Point3D TxtPoint1;
	Point2D tempData;
	vector<Point2D> pointCloudData;

	vector<float> x_data, y_data;

	if (fp_txt1)
	{
		// 获取要统计的平面的点云
		switch (intestingPlane) {
		case XOY:
			while (fscanf(fp_txt1, "%lf %lf %lf", &TxtPoint1.x, &TxtPoint1.y, &TxtPoint1.z) != EOF)
			{

				tempData.x = TxtPoint1.x;
				x_data.push_back(TxtPoint1.x);
				y_data.push_back(TxtPoint1.y);
				tempData.y = TxtPoint1.y;
				pointCloudData.push_back(tempData);
			}
			break;
		case XOZ:
			while (fscanf(fp_txt1, "%lf %lf %lf", &TxtPoint1.x, &TxtPoint1.y, &TxtPoint1.z) != EOF)
			{

				tempData.x = TxtPoint1.x;
				tempData.y = TxtPoint1.z;
				x_data.push_back(TxtPoint1.x);
				y_data.push_back(TxtPoint1.z);
				pointCloudData.push_back(tempData);
			}
			break;
		case YOZ:
			while (fscanf(fp_txt1, "%lf %lf %lf", &TxtPoint1.x, &TxtPoint1.y, &TxtPoint1.z) != EOF)
			{

				tempData.x = TxtPoint1.y;
				tempData.y = TxtPoint1.z;
				x_data.push_back(TxtPoint1.y);
				y_data.push_back(TxtPoint1.z);
				pointCloudData.push_back(tempData);
			}
			break;
		default:
			break;
		}

	}
	else{

		cout << "txt数据加载失败！" << endl;
	}
	sort(x_data.begin(), x_data.end());
	sort(y_data.begin(), y_data.end());
	boundaryValue.push_back(x_data[0]);
	boundaryValue.push_back(x_data[x_data.size() - 1]);
	boundaryValue.push_back(y_data[0]);
	boundaryValue.push_back(y_data[y_data.size() - 1]);
	return pointCloudData;
}

// 统计各个单位点云数目,
map<Point2D, int> countPointCloud(vector<Point2D> pointCloudDatas, vector<float> boudaryData, int segNum) {
	// 区间数10*10

	float xSegValue = (boudaryData[1] - boudaryData[0]) / segNum;
	float ySegValue = (boudaryData[3] - boudaryData[2]) / segNum;
	//cout << "xSegValue: " << xSegValue << " ySegValue: " << ySegValue << endl;
	map<Point2D, int> mpPointData;
	int tenSum = 0;
	// 判断属于哪个区间，并统计该区间点云的个数
	for (Point2D & pointCloudData : pointCloudDatas) {
		Point2D tempData;

		// 值减去最小值再除以区间间隔值，判断属于哪个区间
		float xTemp = (pointCloudData.x - boudaryData[0]) / xSegValue;
		float yTemp = (pointCloudData.y - boudaryData[2]) / ySegValue;
		tempData.x = floorf(xTemp);
		tempData.y = floorf(yTemp);
		if (tempData.x == segNum || tempData.y == segNum) {
			tenSum += 1;

		}
		else {
			mpPointData[tempData] += 1;
		}

	}
	int sum = 0;
	for (auto it = mpPointData.begin(); it != mpPointData.end(); it++) {
		sum += it->second;
	}
	return mpPointData;
}

// 计算相似度
float cacular2Similarity(map<Point2D,int> data1, map<Point2D,int> data2, int segNum) {
	// 存储100个单元的点云数
	vector<int> unitPointNum1(segNum * segNum, 0);
	vector<int> unitPointNum2(segNum * segNum, 0);
	// 临时存储当前遍历的位置
	Point2D curLoc;
	int k = 0;

	// 提取出100个单元中每个单元的点云数
	for (int i = 0; i < segNum; ++i) {
		for (int j = 0; j < segNum; ++j) {
			curLoc.x = i;
			curLoc.y = j;
			if (data1.find(curLoc) != data1.end()) {
				unitPointNum1[k] = data1[curLoc];
			}
			if (data2.find(curLoc) != data2.end()) {
				unitPointNum2[k] = data2[curLoc];
			}
			k++;
		}
	}

	int sumNum1 = accumulate(unitPointNum1.begin(), unitPointNum1.end(), 0);
	int sumNum2 = accumulate(unitPointNum2.begin(), unitPointNum2.end(), 0);
	double similarity = 0;
	for (int i = 0; i < segNum * segNum; ++i) {
		similarity += sqrt(((double)unitPointNum1[i] / sumNum1) * ((double)unitPointNum2[i] / sumNum2));
	}
	return similarity;
}

//二维平面栅格化后，相似度比较
void similarity2D(const char *billboardFile, const char *geometricFile) {

	vector<Plane> Planes{ XOY, XOZ, YOZ };
	vector<float> similarities(3);
	int segNum = 20;
	for (int i = 0; i < 3; i++)
	{
		vector<float> b_boudaryData;
		vector<float> g_boudaryData;
		vector<Point2D> billboardData = getData(billboardFile, Planes[i], b_boudaryData);
		vector<Point2D> geometricData = getData(geometricFile, Planes[i], g_boudaryData);

		// 2D网格点云数目统计信息
		auto billboardDataCount = countPointCloud(billboardData, b_boudaryData, segNum);
		auto geometricDataCount = countPointCloud(geometricData, g_boudaryData, segNum);

		// 计算相似度
		similarities[i] = cacular2Similarity(billboardDataCount, geometricDataCount, segNum);
		cout << similarities[i] << endl;
	}

	cout.setf(ios::fixed);
	cout.setf(ios::showpoint);
	cout.precision(4);

	float average = (float)accumulate(similarities.begin(), similarities.end(), 0.0f) / 3;
	cout << "========================相似度信息=========================" << endl;
	for (float i : similarities) {
		cout << i << " ";
	}
	cout << average << endl;
}

// 获取三维点云的数据，遍历点云获得边界X、Y、Z的最大，最小值
 vector<Point3D> get3Data(const char* filename, Boundary &boundary) {

	 FILE *fp_txt1;

	 fp_txt1 = fopen(filename, "r");

	 Point3D TxtPoint;
	 vector<Point3D>pointData;
	 vector<double> x_data, y_data, z_data;
	 while (fscanf(fp_txt1, "%lf %lf %lf %lf %lf %lf", &TxtPoint.x, &TxtPoint.y, &TxtPoint.z,
													   &TxtPoint.normal.x, &TxtPoint.normal.y, &TxtPoint.normal.z) != EOF)
	 {
		 x_data.push_back(TxtPoint.x);
		 y_data.push_back(TxtPoint.y);
		 z_data.push_back(TxtPoint.z);
		 pointData.push_back(TxtPoint);
	 }
	 cout << "=========================统计数据======================" << endl;
	 cout << "3D点云数据数目: " << pointData.size() << endl;

	 // 统计边界
	 sort(x_data.begin(), x_data.end());
	 sort(y_data.begin(), y_data.end());
	 sort(z_data.begin(), z_data.end());
	 boundary.xMin = x_data[0]; boundary.xMax = x_data[x_data.size() - 1];
	 boundary.yMin = y_data[0]; boundary.yMax = y_data[y_data.size() - 1];
	 boundary.zMin = z_data[0]; boundary.zMax = z_data[z_data.size() - 1];

	 return pointData;
}

 // 统计各个网格内的数据，网格化
map<Point3D, vector<Point3D>> countMeshData(vector<Point3D> &pointDatas, Boundary boundary, int meshNum) {

	double xMeshValue = (boundary.xMax - boundary.xMin) / meshNum;
	double yMeshValue = (boundary.yMax - boundary.yMin) / meshNum;
	double zMeshValue = (boundary.zMax - boundary.zMin) / meshNum;
	// 键为网格坐标，键值为该网格内的点云
	map<Point3D, vector<Point3D>> meshData;
	Point3D tempData;
	for (auto & pointData : pointDatas) {
		// 值减去最小值再除以区间间隔值，判断属于哪个区间
		double xTemp = (pointData.x - boundary.xMin) / xMeshValue;
		double yTemp = (pointData.y - boundary.yMin) / yMeshValue;
		double zTemp = (pointData.z - boundary.zMin) / zMeshValue;
		tempData.x = floor(xTemp);
		tempData.y = floor(yTemp);
		tempData.z = floor(zTemp);
		if (tempData.x == meshNum || tempData.y == meshNum || tempData.z == meshNum) {
			// 边界处理
			auto xtemp = tempData.x == meshNum ? tempData.x - 1 : tempData.x;
			tempData.x = xtemp;
			auto ytemp = tempData.y == meshNum ? tempData.y - 1 : tempData.y;
			tempData.y = ytemp;
			auto ztemp = tempData.z == meshNum ? tempData.z - 1 : tempData.z;
			tempData.z = ztemp;
		}
		// 数据存储
		meshData[tempData].push_back(pointData);
	}
	int pointSum = 0;
	cout << "网格数量：" << meshData.size() << endl;
	return meshData;
}

int main() {

	const char* billboardFile = "billboard.txt";
	const char* geometricFile = "geometric.txt";
	similarity2D(billboardFile, geometricFile);
	system("pause");
	return 0;
}


//  Boost integer/integer_mask.hpp header file  ------------------------------//

//  (C) Copyright Daryle Walker 2001.  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies.  This software is provided "as is" without express or
//  implied warranty, and with no claim as to its suitability for any purpose.

//  See http://www.boost.org for updates, documentation, and revision history.

#ifndef BOOST_INTEGER_INTEGER_MASK_HPP
#define BOOST_INTEGER_INTEGER_MASK_HPP

#include <boost/integer_fwd.hpp>  // self include

#include <boost/config.hpp>   // for BOOST_STATIC_CONSTANT
#include <boost/integer.hpp>  // for boost::uint_t

#include <climits>  // for UCHAR_MAX, etc.
#include <cstddef>  // for std::size_t

#include <boost/limits.hpp>  // for std::numeric_limits

namespace boost
{

//  Specified single-bit mask class declaration  -----------------------------//
//  (Lowest bit starts counting at 0.)

template < std::size_t Bit >
struct high_bit_mask_t
{
    typedef typename uint_t<(Bit + 1)>::least  least;
    typedef typename uint_t<(Bit + 1)>::fast   fast;

    BOOST_STATIC_CONSTANT( least, high_bit = (least( 1u ) << Bit) );
    BOOST_STATIC_CONSTANT( fast, high_bit_fast = (fast( 1u ) << Bit) );

    BOOST_STATIC_CONSTANT( std::size_t, bit_position = Bit );

};  // boost::high_bit_mask_t

//  Specified bit-block mask class declaration  ------------------------------//
//  Makes masks for the lowest N bits
//  (Specializations are needed when N fills up a type.)

template < std::size_t Bits >
struct low_bits_mask_t
{
    typedef typename uint_t<Bits>::least  least;
    typedef typename uint_t<Bits>::fast   fast;

    BOOST_STATIC_CONSTANT( least, sig_bits = (~( ~(least( 0u )) << Bits )) );
    BOOST_STATIC_CONSTANT( fast, sig_bits_fast = fast(sig_bits) );

    BOOST_STATIC_CONSTANT( std::size_t, bit_count = Bits );

};  // boost::low_bits_mask_t

#define BOOST_LOW_BITS_MASK_SPECIALIZE( Type )                                  \
  template <  >  struct low_bits_mask_t< std::numeric_limits<Type>::digits >  { \
      typedef std::numeric_limits<Type>           limits_type;                  \
      typedef uint_t<limits_type::digits>::least  least;                        \
      typedef uint_t<limits_type::digits>::fast   fast;                         \
      BOOST_STATIC_CONSTANT( least, sig_bits = (~( least(0u) )) );              \
      BOOST_STATIC_CONSTANT( fast, sig_bits_fast = fast(sig_bits) );            \
      BOOST_STATIC_CONSTANT( std::size_t, bit_count = limits_type::digits );    \
  }

BOOST_LOW_BITS_MASK_SPECIALIZE( unsigned char );

#if USHRT_MAX > UCHAR_MAX
BOOST_LOW_BITS_MASK_SPECIALIZE( unsigned short );
#endif

#if UINT_MAX > USHRT_MAX
BOOST_LOW_BITS_MASK_SPECIALIZE( unsigned int );
#endif

#if ULONG_MAX > UINT_MAX
BOOST_LOW_BITS_MASK_SPECIALIZE( unsigned long );
#endif

#undef BOOST_LOW_BITS_MASK_SPECIALIZE

}  // namespace boost

#endif  // BOOST_INTEGER_INTEGER_MASK_HPP


#define BOOST_TEST_MODULE "test_excluded_volume_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/forcefield/global/ExcludedVolumePotential.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>

BOOST_AUTO_TEST_CASE(EXV_double)
{
    using traits_type = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type   = typename traits_type::real_type;
    using molecule_id_type = mjolnir::Topology::molecule_id_type;
    using group_id_type    = mjolnir::Topology::group_id_type;
    constexpr std::size_t N = 10000;
    constexpr real_type   h = 1e-6;

    const real_type sigma   = 3.0;
    const real_type epsilon = 1.0;
    mjolnir::ExcludedVolumePotential<traits_type> exv{
        epsilon, mjolnir::ExcludedVolumePotential<traits_type>::default_cutoff(),
        {{0, sigma}, {1, sigma}}, {},
        mjolnir::IgnoreMolecule<molecule_id_type>("Nothing"),
        mjolnir::IgnoreGroup   <group_id_type   >({})
    };

    const real_type x_min = 0.8 * sigma;
    const real_type x_max = exv.cutoff_ratio() * sigma;
    const real_type dx = (x_max - x_min) / N;

    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x    = x_min + i * dx;
        const real_type pot1 = exv.potential(0, 1, x + h);
        const real_type pot2 = exv.potential(0, 1, x - h);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = exv.derivative(0, 1, x);

        BOOST_TEST(dpot == deri, boost::test_tools::tolerance(h));
    }
}

BOOST_AUTO_TEST_CASE(EXV_float)
{
    using traits_type = mjolnir::SimulatorTraits<float, mjolnir::UnlimitedBoundary>;
    using real_type   = typename traits_type::real_type;
    using molecule_id_type = mjolnir::Topology::molecule_id_type;
    using group_id_type    = mjolnir::Topology::group_id_type;
    constexpr static std::size_t N = 1000;
    constexpr static real_type   h = 0.002;
    constexpr static real_type tol = 0.005;

    const real_type sigma   = 3.0;
    const real_type epsilon = 1.0;

    mjolnir::ExcludedVolumePotential<traits_type> exv{
        epsilon, mjolnir::ExcludedVolumePotential<traits_type>::default_cutoff(),
        {{0, sigma}, {1, sigma}}, {},
        mjolnir::IgnoreMolecule<molecule_id_type>("Nothing"),
        mjolnir::IgnoreGroup   <group_id_type   >({})
    };
    const real_type cutoff = exv.cutoff_ratio();

    const real_type x_min = 0.8f   * sigma;
    const real_type x_max = cutoff * sigma;
    const real_type dx = (x_max - x_min) / N;

    for(std::size_t i=0; i<N; ++i)
    {
        const real_type x    = x_min + i * dx;
        const real_type pot1 = exv.potential(0, 1, x + h);
        const real_type pot2 = exv.potential(0, 1, x - h);
        const real_type dpot = (pot1 - pot2) / (2 * h);
        const real_type deri = exv.derivative(0, 1, x);

        BOOST_TEST(dpot == deri, boost::test_tools::tolerance(tol));
    }
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_TOOLBOX_ELLIPTIC_FUNCTIONS_SCALAR_ELLPE_HPP_INCLUDED
#define NT2_TOOLBOX_ELLIPTIC_FUNCTIONS_SCALAR_ELLPE_HPP_INCLUDED
#include <nt2/toolbox/elliptic/functions/ellpe.hpp>
#include <boost/math/special_functions.hpp>
#include <nt2/include/constants/digits.hpp>
#include <nt2/include/constants/real.hpp>
#include <nt2/toolbox/trigonometric/constants.hpp>
#include <nt2/toolbox/polynomials/functions/scalar/impl/horner.hpp>
#include <nt2/include/functions/scalar/is_ltz.hpp>
#include <nt2/include/functions/scalar/is_eqz.hpp>
#include <nt2/include/functions/scalar/oneminus.hpp>
#include <nt2/include/functions/scalar/sqrt.hpp>
#include <nt2/include/functions/scalar/log.hpp>
#include <nt2/sdk/error/policies.hpp>

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

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

    NT2_FUNCTOR_CALL(1)
    {
      return nt2::ellpe(result_type(a0));
    }
  };
} }

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

    typedef A0 result_type;

    NT2_FUNCTOR_CALL(1)
    {
      typedef result_type type;
      if (a0>One<A0>()||(is_ltz(a0))) return Nan<type>();
      if (is_eqz(a0))  return One<type>();
      if (a0 == One<A0>()) return Pio_2<type>();
      return boost::math::ellint_2(sqrt(oneminus(type(a0))), nt2_policy());
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is float
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::ellpe_, tag::cpu_
                            , (A0)
                            , (scalar_< single_<A0> >)
                            )
  {
    typedef A0 result_type;
    NT2_FUNCTOR_CALL(1)
    {
      if (a0>One<A0>()||(is_ltz(a0))) return Nan<A0>();
      if (is_eqz(a0))  return One<A0>();
      if (a0 == One<A0>()) return Pio_2<A0>();
      A0 tmp1 = horner< NT2_HORNER_COEFF(float, 11,
                              (0x392102f5,
                               0x3b246c1b,
                               0x3c0e578f,
                               0x3c2fe240,
                               0x3bfebca9,
                               0x3bf882cf,
                               0x3c3d8b3f,
                               0x3cb2d89a,
                               0x3d68ac90,
                               0x3ee2e430,
                               0x3f800000) ) > (a0);
      A0 tmp2 = horner< NT2_HORNER_COEFF(float, 10,
                                    (0x38098de4,
                                     0x3a84557e,
                                     0x3bd53114,
                                     0x3c8a54f6,
                                     0x3cd67118,
                                     0x3d0925e1,
                                     0x3d2ef92b,
                                     0x3d6fffe9,
                                     0x3dc00000,
                                     0x3e800000
                                     ) ) > (a0);
      return tmp1-nt2::log(a0)*a0*tmp2;
    }
  };
} }

#endif


#include <iostream>
#include <vector>
#include <set>
#include <iterator>
#include <math.h>
#include <chrono>
#include <exception>
#include <queue>
#include <boost/math/distributions/normal.hpp>
#include <sys/time.h>
#include "ptss_dse.hpp"
#include "ptss_config.hpp"
#include "ptss_pkmin.hpp"

using namespace std;
#define NSAMPLES 100

int main(int argc, char **argv) {
    if (argc < 2) {
        cout << "Enter The deadline, Peak Power Cap" << endl;
        exit(EXIT_FAILURE);
    }
    double deadline   = atof(argv[1]);
    //vector<double> pkp_cap  = {8.5,9.22,9.94,10.66,11.38,12.11,12.83,13.55,14.27,15.0};
    vector<double> pkp_cap  = {9,10,11,12,13,14,15,16,17};

    // struct timeval t1, t2;
    // gettimeofday(&t1,NULL);
    int j = NSAMPLES,i = 0;
    for (i = 0; i < pkp_cap.size();i++) {
        j = NSAMPLES;
        while (j > 0) {
            cout << "("<<i<<","<<j<<") Iteration"<<endl;
            ptss_DSE_hrt obj(deadline,pkp_cap[i]);
            obj.display();
            j--;
        }
    }
    // gettimeofday(&t2,NULL);
    // double elapsed  = t2.tv_sec-t1.tv_sec;
}


#include <algorithm>
#include <boost/optional.hpp>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

#define REP(i, n) for(int i = 0, i##_MACRO = (n); i < i##_MACRO; i++)
#define RANGE(i, a, b) for(int i = (a), i##_MACRO = (b); i < i##_MACRO; i++)
#define EACH(e, a) for(auto&& e : a)
#define ALL(a) (a).begin(), (a).end()
#define AALL(a, n) (a), ((a) + (n))
#define FILL(a, n) memset((a), n, sizeof(a))
#define FILLZ(a) FILL(a, 0)
#define INT(x) (static_cast<int>(x))
#define MODNUM (INT(1e9 + 7))
#define MOD(x) ((x) % MODNUM)

using namespace std;

using ll = long long;
using VI = vector<int>;
using VI2D = vector<vector<int>>;

const int INF = 2e9;
const double EPS = 1e-10;
const double PI = acos(-1.0);

const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, -1, 0, 1};

template <typename T>
int sign(T x) {
	return x < 0 ? -1 : x > 0 ? 1 : 0;
}

template <>
int sign(double x) {
	return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

template <typename T>
void chmax(T& m, T x) {
	m = max(m, x);
}

template <typename T>
void chmin(T& m, T x) {
	m = min(m, x);
}

template <typename T>
T square(T x) {
	return x * x;
}

inline int toInt(string s) {
	int v;
	istringstream sin(s);
	sin >> v;
	return v;
}

inline long long mod_pow(long long a, long long n, long long mod) {
	long long ret = 1;
	while(n != 0) {
		if(n % 2) {
			ret = (ret * a) % mod;
		}
		a = (a * a) % mod, n /= 2;
	}
	return ret;
}

// modの逆元
inline long long mod_inv(long long n, long long mod) {
	return mod_pow(n, mod - 2, mod);
}

// エラトステネスの篩
template <typename T>
inline std::vector<T> sieve_of_eratosthenes(T n) {
	std::vector<T> sieve(n, 0);
	for(int i = 2; i < n; i++) {
		sieve[i] = i;
	}
	T i = 2;
	while(i * i < n) {
		if(sieve[i]) {
			for(T j = i * i; j < n; j += i) {
				sieve[j] = 0;
			}
		}
		i++;
	}
	return sieve;
}

// 素数リスト
template <typename T>
inline std::vector<T> prime_list(T n) {
	std::vector<T> primes = sieve_of_eratosthenes(n);
	primes.erase(std::remove(primes.begin(), primes.end(), 0), primes.end());
	return primes;
}

// 素因数分解(素数表を用いる)
inline std::unordered_map<long long, int>
factor(long long n, std::vector<long long>& primes) {
	std::unordered_map<long long, int> factors;
	for(int i = 0; primes[i] * primes[i] <= n; i++) {
		int j = 0;
		while(n % primes[i] == 0) {
			n /= primes[i];
			j++;
		}
		if(j != 0) {
			factors[primes[i]] = j;
		}
	}
	if(n != 1) {
		factors[n] = 1;
	}
	return factors;
}

// vectorの要素すべての最小公倍数をmodで割った余りを返す
template <typename T>
inline long long
mod_lcm(std::vector<T>& v, std::vector<long long>& primes, long long mod) {
	int n = v.size();
	std::unordered_map<long long, int> lcm_factors;
	for(int i = 0; i < n; i++) {
		std::unordered_map<long long, int> factors = factor(v[i], primes);
		for(const std::pair<long long, int>& factor : factors) {
			if(lcm_factors[factor.first] < factor.second) {
				lcm_factors[factor.first] = factor.second;
			}
		}
	}
	long long retval = 1;
	for(const std::pair<long long, int>& factor : lcm_factors) {
		retval = (retval * mod_pow(factor.first, factor.second, mod)) % mod;
	}
	return retval;
}

int main() {
	int n;
	scanf("%d", &n);
	VI a(n);
	REP(i, n) { scanf("%d", &a[i]); }
	vector<ll> primes = prime_list<ll>(1e6 + 100);
	ll lcmall = mod_lcm(a, primes, MODNUM);
	ll result = 0;
	REP(i, n) { result = MOD(result + MOD(lcmall * mod_inv(a[i], MODNUM))); }
	printf("%lld\n", result);
	return 0;
}


#include <iostream>
#include <vector>

#include <boost/scope_exit.hpp>

#include <amgcl/backend/builtin.hpp>
#include <amgcl/value_type/static_matrix.hpp>
#include <amgcl/adapter/crs_tuple.hpp>
#include <amgcl/mpi/distributed_matrix.hpp>
#include <amgcl/io/mm.hpp>

#include <amgcl/profiler.hpp>

namespace amgcl {
    profiler<> prof;
}

namespace math = amgcl::math;

template <class Val>
void assemble(
        int n, int beg, int end,
        std::vector<int> &ptr,
        std::vector<int> &col,
        std::vector<Val> &val
        )
{
    int chunk = end - beg;

    ptr.clear(); ptr.reserve(chunk + 1); ptr.push_back(0);
    col.clear(); col.reserve(chunk * 4);
    val.clear(); val.reserve(chunk * 4);

    for(int j = beg, i = 0; j < end; ++j, ++i) {
        if (j > 0) {
            col.push_back(j - 1);
            val.push_back(-math::identity<Val>());
        }

        col.push_back(j);
        val.push_back(2 * math::identity<Val>());

        if (j+1 < n) {
            col.push_back(j+1);
            val.push_back(-math::identity<Val>());
        }

        if (j+5 < n) {
            col.push_back(j+5);
            val.push_back(-0.1 * math::identity<Val>());
        }

        ptr.push_back(col.size());
    }
}

template <class Val>
void test() {
    typedef typename math::rhs_of<Val>::type Rhs;

    amgcl::mpi::communicator comm(MPI_COMM_WORLD);

    int n = 16;
    int chunk_len = (n + comm.size - 1) / comm.size;
    int chunk_beg = std::min(n, chunk_len * comm.rank);
    int chunk_end = std::min(n, chunk_len * (comm.rank + 1));
    int chunk = chunk_end - chunk_beg;

    std::vector<int> chunks(comm.size);
    MPI_Allgather(&chunk, 1, MPI_INT, &chunks[0], 1, MPI_INT, comm);
    std::vector<int> displ(comm.size, 0);
    for(int i = 1; i < comm.size; ++i)
        displ[i] = displ[i-1] + chunks[i-1];

    std::vector<int>    ptr;
    std::vector<int>    col;
    std::vector<Val> val;
    std::vector<Rhs> x(chunk);
    std::vector<Rhs> y(chunk);

    assemble(n, chunk_beg, chunk_end, ptr, col, val);

    for(int i = 0; i < chunk; ++i) x[i] = math::constant<Rhs>(drand48());

    typedef amgcl::backend::builtin<Val> Backend;
    typedef amgcl::mpi::distributed_matrix<Backend> Matrix;

    Matrix A(comm, std::tie(chunk, ptr, col, val), chunk);

    auto B = amgcl::mpi::product(A, A);
    B->move_to_backend();

    amgcl::backend::spmv(1, *B, x, 0, y);

    std::vector<Rhs> X(n), R(n);
    MPI_Gatherv(&x[0], chunk, amgcl::mpi::datatype<Rhs>(), &X[0], &chunks[0], &displ[0], amgcl::mpi::datatype<Rhs>(), 0, comm);
    MPI_Gatherv(&y[0], chunk, amgcl::mpi::datatype<Rhs>(), &R[0], &chunks[0], &displ[0], amgcl::mpi::datatype<Rhs>(), 0, comm);

    if (comm.rank == 0) {
        std::vector<Rhs> Y(n);
        assemble(n, 0, n, ptr, col, val);

        amgcl::backend::crs<Val> A( std::tie(n, ptr, col, val) );
        amgcl::backend::spmv(1, *amgcl::backend::product(A, A), X, 0, Y);

        double s = 0;
        for(int i = 0; i < n; ++i) {
            double d = math::norm(R[i] - Y[i]);
            s += d * d;
        }
        std::cout << "Error: " << s << std::endl;
    }
}

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);
    BOOST_SCOPE_EXIT(void) {
        MPI_Finalize();
    } BOOST_SCOPE_EXIT_END

    test< double >();
    test< amgcl::static_matrix<double,2,2> >();
}


/**
 * @file clipper.cpp
 * @brief Core CLIPPER algorithm: find dense clusters w.r.t constraints
 * @author Parker Lusk <plusk@mit.edu>
 * @date 3 October 2020
 */

#include <chrono>
#include <iostream>
#include <functional>
#include <limits>
#include <queue>
#include <random>
#include <utility>
#include <vector>

#include <Eigen/Sparse>

#include "clipper/find_dense_cluster.h"
#include "clipper/utils.h"

namespace clipper {

inline void homotopy(Eigen::MatrixXd& Md, const Eigen::MatrixXd& M, const Eigen::MatrixXd& Cb, double d)
{
  // if (M.cols() < 2600) {
    Md = M - d*Cb;
  //   return;
  // }

// #pragma omp parallel for default(none) shared(Md, M, d, Cb)
//   for (size_t c=0; c<M.cols(); ++c) {
//     Md.col(c) = M.col(c) - d*Cb.col(c);
//   }
}

template <typename T, IsEigenBase<T>>
Solution findDenseCluster(const T& _M, const T& C,
                          const Eigen::VectorXd& u0, const Params& params)
{
  const auto t1 = std::chrono::high_resolution_clock::now();
  //
  // Initialization
  //

  const size_t n = _M.cols();

  // Zero out any entry corresponding to an active constraint
  const Eigen::MatrixXd M = _M.cwiseProduct(C);

  // Binary complement of constraint matrix
  const Eigen::MatrixXd Cb = Eigen::MatrixXd::Ones(n,n) - C;

  // one step of power method to have a good scaling of u
  Eigen::VectorXd u = M * u0;
  u /= u.norm();

  // initial value of d
  double d = 0; // zero if there are no active constraints
  Eigen::MatrixXd Cbu = Cb * u;
  const auto idxD = ((Cbu.array()>params.eps) && (u.array()>params.eps));
  if (idxD.sum() > 0) {
    Eigen::MatrixXd Mu = M * u;
    const Eigen::VectorXd num = idxD.select(Mu, std::numeric_limits<double>::infinity());
    const Eigen::VectorXd den = idxD.select(Cbu, 1);
    d = (num.array() / den.array()).minCoeff();
  }

  Eigen::MatrixXd Md = Eigen::MatrixXd(M.rows(), M.cols());
  homotopy(Md, M, Cb, d);

  // initialize memory
  Eigen::VectorXd gradF = Eigen::VectorXd(n);
  Eigen::VectorXd unew = Eigen::VectorXd(n);
  Eigen::VectorXd Mu = Eigen::VectorXd(n);
  Eigen::VectorXd num = Eigen::VectorXd(n);
  Eigen::VectorXd den = Eigen::VectorXd(n);

  //
  // Orthogonal projected gradient ascent with homotopy
  //

  double F = 0; // objective value

  size_t i, j, k; // iteration counters
  for (i=0; i<params.maxoliters; ++i) {
    F = u.transpose() * Md * u; // current objective value

    //
    // Orthogonal projected gradient ascent
    //

    for (j=0; j<params.maxiniters; ++j) {
      gradF = Md * u;

      // if (params.orthogonal) {
      //   // orthogonal projection of gradient onto tangent plane to S^n at u
      //   gradF = gradF - (gradF.transpose() * u) * u;

      //   if (gradF.norm() < params.tol_Fop) break;
      // }

      // double alpha = params.alpha;
      // if (alpha <= 0) {
      //   const auto idxA = ((gradF.array()<-params.eps) && (u.array()>params.eps));
      //   if (idxA.sum()) {
      //     const Eigen::VectorXd num = idxA.select(u, std::numeric_limits<double>::infinity());
      //     const Eigen::VectorXd den = idxA.select(gradF, 1);
      //     alpha = (num.array() / den.array()).abs().minCoeff();
      //   } else {
      //     alpha = std::pow(1.0/params.beta, 3) / gradF.norm();
      //   }
      // }

      double alpha = 1;

      //
      // Backtracking line search on gradient ascent
      //

      double Fnew = 0, deltaF = 0;
      for (k=0; k<params.maxlsiters; ++k) {
        unew = u + alpha * gradF;                     // gradient step
        unew = unew.cwiseMax(0);                      // project onto positive orthant
        unew.normalize();                             // project onto S^n
        Fnew = unew.transpose() * Md * unew;          // new objective value after step
        deltaF = Fnew - F;                            // change in objective value

        if (deltaF < -params.eps) {
          // objective value decreased---we need to backtrack, so reduce step size
          alpha = alpha * params.beta;
        } else {
          break; // obj value increased, stop line search
        }
      }
      const double deltau = (unew - u).norm();

      // update values
      F = Fnew;
      u = unew;

      // check if desired accuracy has been reached by gradient ascent
      if (deltau < params.tol_u || std::abs(deltaF) < params.tol_F) break;
    }

    //
    // Increase d
    //

    Cbu = Cb * u;
    const auto idxD = ((Cbu.array() > params.eps) && (u.array() > params.eps));
    if (idxD.sum() > 0) {
      Mu = M * u;
      num = idxD.select(Mu, std::numeric_limits<double>::infinity());
      den = idxD.select(Cbu, 1);
      const double deltad = (num.array() / den.array()).abs().minCoeff();

      d += deltad;
      homotopy(Md, M, Cb, d);

    } else {
      break;
    }
  }

  //
  // Generate output
  //

  // estimate cluster size using largest eigenvalue
  const int omega = std::round(F);

  // extract indices of nodes in identified dense cluster
  std::vector<int> I = utils::findIndicesOfkLargest(u, omega);

  const auto t2 = std::chrono::high_resolution_clock::now();
  const auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1);
  const double elapsed = static_cast<double>(duration.count()) / 1e9;

  Solution soln;
  soln.t = elapsed;
  soln.ifinal = i;
  std::swap(soln.nodes, I);
  soln.u.swap(u);
  soln.score = F;

  return soln;
}

template <typename T, IsEigenBase<T>>
Solution findDenseCluster(const T& M, const T& C,
                          const Params& params)
{
  return findDenseCluster(M, C, utils::randvec(M.cols()), params);
}

// template specializations for dense and sparse Eigen matrices
template Solution findDenseCluster<Eigen::MatrixXd>(const Eigen::MatrixXd&,
            const Eigen::MatrixXd&, const Eigen::VectorXd&, const Params&);
template Solution findDenseCluster<SpMat>(const SpMat&,
            const SpMat&, const Eigen::VectorXd&, const Params&);
template Solution findDenseCluster<Eigen::MatrixXd>(const Eigen::MatrixXd&,
            const Eigen::MatrixXd&, const Params&);
template Solution findDenseCluster<SpMat>(const SpMat&,
            const SpMat&, const Params&);

// methods for sparse matrices from scoreSparseConsistency

// sparse method similar to the above with the difference that M,C have zero
// elements along the diagonal
Solution findDenseClusterOfSparseGraph(const SpMat &M, const SpMat &C,
                                       const Eigen::VectorXd &u0,
                                       const Params &params) {
  // std::cout << "number of threads : " << Eigen::nbThreads() << std::endl;
  const auto t1 = std::chrono::high_resolution_clock::now();
  //
  // Initialization
  //

  const size_t n = M.cols();

  // un-necessary compute
  // Zero out any entry corresponding to an active constraint
  // const Eigen::MatrixXd M = _M.cwiseProduct(C);

  // this needs to be replaced
  // Binary complement of constraint matrix
  // const Eigen::MatrixXd Cb = Eigen::MatrixXd::Ones(n, n) - C;
  const Eigen::VectorXd ones = Eigen::VectorXd::Ones(n);

  // one step of power method to have a good scaling of u
  Eigen::VectorXd u = M * u0 + u0; // since M here is not diagonal
  // Eigen::VectorXd u = u0;
  u /= u.norm();

  // initial value of d
  double d = 0; // zero if there are no active constraints
  // Eigen::MatrixXd Cbu = Cb * u;
  Eigen::MatrixXd Cbu = ones * u.sum() - C * u - u;
  const auto idxD = ((Cbu.array() > params.eps) && (u.array() > params.eps));
  if (idxD.sum() > 0) {
    Eigen::MatrixXd Mu = M * u + u;
    const Eigen::VectorXd num =
        idxD.select(Mu, std::numeric_limits<double>::infinity());
    const Eigen::VectorXd den = idxD.select(Cbu, 1);
    d = (num.array() / den.array()).minCoeff();
  }

  // this should be replaced with an efficient representation
  // Md = M - d * Cb;
  // Eigen::MatrixXd Md = Eigen::MatrixXd(M.rows(), M.cols());
  // homotopy(Md, M, Cb, d);

  // initialize memory
  Eigen::VectorXd gradF = Eigen::VectorXd(n);
  Eigen::VectorXd gradFnew = Eigen::VectorXd(n);
  Eigen::VectorXd unew = Eigen::VectorXd(n);
  Eigen::VectorXd Mu = Eigen::VectorXd(n);
  Eigen::VectorXd num = Eigen::VectorXd(n);
  Eigen::VectorXd den = Eigen::VectorXd(n);

  //
  // Orthogonal projected gradient ascent with homotopy
  //

  double F = 0; // objective value

  size_t i, j, k; // iteration counters
  for (i = 0; i < params.maxoliters; ++i) {
    // F = u.transpose() * Md * u; // current objective value
    // gradF = (1 + d) * (M * u + u) - d * ones * u.sum();
    gradF = (1 + d) * u - d * ones * u.sum() + M * u + d * C * u;
    F = u.dot(gradF);
    // std::cout << std::endl;
    // std::cout << "obj F: " << F << " - dval: " << d << std::endl;
    //
    // Orthogonal projected gradient ascent
    //

    for (j = 0; j < params.maxiniters; ++j) {
      // gradF = Md * u;

      // if (params.orthogonal) {
      //   // orthogonal projection of gradient onto tangent plane to S^n at u
      //   gradF = gradF - (gradF.transpose() * u) * u;

      //   if (gradF.norm() < params.tol_Fop) break;
      // }

      // double alpha = params.alpha;
      // if (alpha <= 0) {
      //   const auto idxA = ((gradF.array()<-params.eps) &&
      //   (u.array()>params.eps)); if (idxA.sum()) {
      //     const Eigen::VectorXd num = idxA.select(u,
      //     std::numeric_limits<double>::infinity()); const Eigen::VectorXd den
      //     = idxA.select(gradF, 1); alpha = (num.array() /
      //     den.array()).abs().minCoeff();
      //   } else {
      //     alpha = std::pow(1.0/params.beta, 3) / gradF.norm();
      //   }
      // }

      double alpha = 1;

      //
      // Backtracking line search on gradient ascent
      //

      double Fnew = 0, deltaF = 0;
      for (k = 0; k < params.maxlsiters; ++k) {
        unew = u + alpha * gradF; // gradient step
        unew = unew.cwiseMax(0);  // project onto positive orthant
        unew.normalize();         // project onto S^n
        // Fnew = unew.transpose() * Md * unew; // new objective value after
        // step
        // gradFnew = (1 + d) * (M * unew + unew) - d * ones * unew.sum();
        gradFnew =
            (1 + d) * unew - d * ones * unew.sum() + M * unew + d * C * unew;
        Fnew = unew.dot(gradFnew);

        deltaF = Fnew - F; // change in objective value

        if (deltaF < -params.eps) {
          // objective value decreased---we need to backtrack, so reduce step
          // size
          alpha = alpha * params.beta;
        } else {
          // std::cout << "breaking at " << k << " out of ls " <<
          // params.maxlsiters
          //           << std::endl;
          break; // obj value increased, stop line search
        }
      }
      const double deltau = (unew - u).norm();

      // std::cout << "Fnew: " << Fnew << " Unew: " << unew.sum() << std::endl;
      // update values
      F = Fnew;
      u = unew;
      // gradF = gradFnew;

      // check if desired accuracy has been reached by gradient ascent
      if (deltau < params.tol_u || std::abs(deltaF) < params.tol_F) {
        // std::cout << "breaking at " << j << " out of in " <<
        // params.maxiniters
        //           << std::endl;
        break;
      }
    }

    //
    // Increase d
    //

    // Cbu = Cb * u;
    Cbu = ones * u.sum() - C * u - u;
    const auto idxD = ((Cbu.array() > params.eps) && (u.array() > params.eps));
    if (idxD.sum() > 0) {
      Mu = M * u + u;
      num = idxD.select(Mu, std::numeric_limits<double>::infinity());
      den = idxD.select(Cbu, 1);
      const double deltad = (num.array() / den.array()).abs().minCoeff();

      // std::cout << "delta d:" << deltad << std::endl;
      d += deltad;
      // homotopy(Md, M, Cb, d);

    } else {
      // std::cout << "breaking at " << i << " out of oo " << params.maxoliters
      //           << std::endl;
      break;
    }
  }
  //
  // Generate output
  //

  // estimate cluster size using largest eigenvalue
  const int omega = std::round(F);

  // extract indices of nodes in identified dense cluster
  std::vector<int> I = utils::findIndicesOfkLargest(u, omega);

  const auto t2 = std::chrono::high_resolution_clock::now();
  const auto duration =
      std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1);
  const double elapsed = static_cast<double>(duration.count()) / 1e9;

  Solution soln;
  soln.t = elapsed;
  soln.ifinal = i;
  std::swap(soln.nodes, I);
  soln.u.swap(u);
  soln.score = F;

  return soln;
}

Solution findDenseClusterOfSparseGraph(const SpMat &M, const SpMat &C,
                                       const Params &params) {
  return findDenseClusterOfSparseGraph(M, C,
                                       C * Eigen::VectorXd::Ones(C.cols()) +
                                           Eigen::VectorXd::Ones(C.cols()),
                                       params);
}

} // ns clipper


#include <Eigen/Dense>
#include <iostream>
#include <math.h>
#define PI  3.141592
//LEG LINK LENGTHS
#define L1 0.12
#define L2 0.303
#define L3 0.303

#define FL 0
#define FR 1
#define BL 2
#define BR 3
#define MASS 11.168
/*inertia  = 0.178402 -0.0458692 0.0180358
-0.0458692 0.470056 -8.95236e-08
0.0180358 -8.95236e-08 0.446392
*/
//LEG-FL

int bound_end_eff_pos(double &x, double &y, double &z)
{
    if(x<= 0.4 && x>= - 0.4)
    {
        if(y <= 0.22 && y>= 0)
        {
            if(z<= -0.15 && z>= -0.4)
            {
                return 1;
            }
        }
    }
    return 0;
}
void _forward_kinematics_FL(Eigen::Vector3d &end_effector_pos, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    end_effector_pos[0] = (l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]));
    end_effector_pos[1] = l1*cos(thetas[0])+(l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0]);
    end_effector_pos[2] = l1*sin(thetas[0])-((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]));
}

void _inverse_kinematics_FL(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, float l1 = L1, float l2 = L2, float l3 = L3)
{
    double x = end_effector_pos[0];
    double y = end_effector_pos[1];
    double z = end_effector_pos[2];
    int isValid = bound_end_eff_pos(x,y,z);
    if(!isValid)
    {
       // std::cout<<"invalid point: "<<x<<","<<y<<","<<z<<std::endl;
    }
    double r, th23, th1, th2, th3,t;
    r = sqrt(y*y + z*z -l1*l1);
    th1 = atan2(y*r + z*l1, y*l1 - z*r);
    t = (2*l2*x + sqrt(4*pow(l2,2)*pow(r,2) - pow(r,4) + 4*pow(l2,2)*pow(x,2) - 2*pow(r,2)*pow(x,2) - pow(x,4)))/(2*l2*r + pow(r,2) + pow(x,2));
    th23 = atan2(2*t, 1-t*t);
    th2 = atan2(x - l2*sin(th23), r - l2*cos(th23));
    th3 = th23 - th2;
    thetas[0]=th1;
    thetas[1]=th2;
    thetas[2]=th3;
}

void _jacobian_FL(Eigen::Matrix<double, 3, 3> &jacob, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    jacob << 0,l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]),l3*cos(thetas[1] + thetas[2]),
           (l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]) - l1*sin(thetas[0]),(-l2*sin(thetas[1]) - l3*sin(thetas[1] + thetas[2]))*sin(thetas[0]),-l3*sin(thetas[1] + thetas[2])*sin(thetas[0]),
           ((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0])) +l1*cos(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*cos(thetas[0]),l3*sin(thetas[1] + thetas[2])*cos(thetas[0]);

}

void _inverse_dynamics_FL(Eigen::Vector3d &torques, Eigen::Vector3d &force, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Finds Joint torques for desired force at end effector
    torques =  [abduction, hip, knee]
    force = [x,y,z]
    thetas = [abduction, hip, knee] angles in radians
    l1, l2, l3 = link lengths
    */
    Eigen::Matrix<double,3,3> jacob;
    _jacobian_FL(jacob, thetas);
    torques = jacob.transpose()*force;
}

//LEG-FR
void _forward_kinematics_FR(Eigen::Vector3d &end_effector_pos, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    end_effector_pos[0] = (l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]));
    end_effector_pos[1] = -1*(l1*cos(thetas[0])+(l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0]));
    end_effector_pos[2] = l1*sin(thetas[0])-((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]));
}

void _inverse_kinematics_FR(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, float l1 = L1, float l2 = L2, float l3 = L3)
{
    double x = end_effector_pos[0];
    double y = -1*end_effector_pos[1];
    double z = end_effector_pos[2];
    int isValid = bound_end_eff_pos(x,y,z);
    if(!isValid)
    {
       // std::cout<<"invalid point: "<<x<<","<<y<<","<<z<<std::endl;
    }
    double r, th23, th1, th2, th3,t;
    r = sqrt(y*y + z*z -l1*l1);
    th1 = atan2(y*r + z*l1, y*l1 - z*r);
    t = (2*l2*x + sqrt(4*pow(l2,2)*pow(r,2) - pow(r,4) + 4*pow(l2,2)*pow(x,2) - 2*pow(r,2)*pow(x,2) - pow(x,4)))/(2*l2*r + pow(r,2) + pow(x,2));
    th23 = atan2(2*t, 1-t*t);
    th2 = atan2(x - l2*sin(th23), r - l2*cos(th23));
    th3 = th23 - th2;
    thetas[0]=th1;
    thetas[1]=th2;
    thetas[2]=th3;
}

void _jacobian_FR(Eigen::Matrix<double, 3, 3> &jacob, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
   jacob << 0,l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]),l3*cos(thetas[1] + thetas[2]),
           (l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]) - l1*sin(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*sin(thetas[0]),l3*sin(thetas[1] + thetas[2])*sin(thetas[0]),
           ((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0])) +l1*cos(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*cos(thetas[0]),l3*sin(thetas[1] + thetas[2])*cos(thetas[0]);
}

void _inverse_dynamics_FR(Eigen::Vector3d &torques, Eigen::Vector3d &force, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Finds Joint torques for desired force at end effector
    torques =  [abduction, hip, knee]
    force = [x,y,z]
    thetas = [abduction, hip, knee] angles in radians
    l1, l2, l3 = link lengths
    */
    Eigen::Matrix<double,3,3> jacob;
    _jacobian_FR(jacob, thetas);
    torques = jacob.transpose()*force;
    torques[0]=-1*torques[0];
}

//LEG-BL
void _forward_kinematics_BL(Eigen::Vector3d &end_effector_pos, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    end_effector_pos[0] = (l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]));
    end_effector_pos[1] = l1*cos(thetas[0])+(l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0]);
    end_effector_pos[2] = l1*sin(thetas[0])-((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]));
}

void _inverse_kinematics_BL(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, float l1 = L1, float l2 = L2, float l3 = L3)
{
    double x = end_effector_pos[0];
    double y = end_effector_pos[1];
    double z = end_effector_pos[2];
    int isValid = bound_end_eff_pos(x,y,z);
    if(!isValid)
    {
        //std::cout<<"invalid point: "<<x<<","<<y<<","<<z<<std::endl;
    }
    double r, th23, th1, th2, th3,t;
    r = sqrt(y*y + z*z -l1*l1);
    th1 = atan2(y*r + z*l1, y*l1 - z*r);
    t = (2*l2*x + sqrt(4*pow(l2,2)*pow(r,2) - pow(r,4) + 4*pow(l2,2)*pow(x,2) - 2*pow(r,2)*pow(x,2) - pow(x,4)))/(2*l2*r + pow(r,2) + pow(x,2));
    th23 = atan2(2*t, 1-t*t);
    th2 = atan2(x - l2*sin(th23), r - l2*cos(th23));
    th3 = th23 - th2;
    thetas[0]=th1;
    thetas[1]=th2;
    thetas[2]=th3;
}

void _jacobian_BL(Eigen::Matrix<double, 3, 3> &jacob, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
   jacob << 0,l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]),l3*cos(thetas[1] + thetas[2]),
           (l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]) - l1*sin(thetas[0]),(-l2*sin(thetas[1]) - l3*sin(thetas[1] + thetas[2]))*sin(thetas[0]),-l3*sin(thetas[1] + thetas[2])*sin(thetas[0]),
           ((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0])) +l1*cos(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*cos(thetas[0]),l3*sin(thetas[1] + thetas[2])*cos(thetas[0]);

}

void _inverse_dynamics_BL(Eigen::Vector3d &torques, Eigen::Vector3d &force, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Finds Joint torques for desired force at end effector
    torques =  [abduction, hip, knee]
    force = [x,y,z]
    thetas = [abduction, hip, knee] angles in radians
    l1, l2, l3 = link lengths
    */
    Eigen::Matrix<double,3,3> jacob;
    _jacobian_BL(jacob, thetas);
    torques = jacob.transpose()*force;
}

//Leg - BR
void _forward_kinematics_BR(Eigen::Vector3d &end_effector_pos, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    end_effector_pos[0] = (l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]));
    end_effector_pos[1] = -1*(l1*cos(thetas[0])+(l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0]));
    end_effector_pos[2] = l1*sin(thetas[0])-((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]));
}

void _inverse_kinematics_BR(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, float l1 = L1, float l2 = L2, float l3 = L3)
{
    double x = end_effector_pos[0];
    double y = -1*end_effector_pos[1];
    double z = end_effector_pos[2];
    int isValid = bound_end_eff_pos(x,y,z);
    if(!isValid)
    {
       // std::cout<<"invalid point: "<<x<<","<<y<<","<<z<<std::endl;
    }
    double r, th23, th1, th2, th3,t;
    r = sqrt(y*y + z*z -l1*l1);
    th1 = atan2(y*r + z*l1, y*l1 - z*r);
    t = (2*l2*x + sqrt(4*pow(l2,2)*pow(r,2) - pow(r,4) + 4*pow(l2,2)*pow(x,2) - 2*pow(r,2)*pow(x,2) - pow(x,4)))/(2*l2*r + pow(r,2) + pow(x,2));
    th23 = atan2(2*t, 1-t*t);
    th2 = atan2(x - l2*sin(th23), r - l2*cos(th23));
    th3 = th23 - th2;
    thetas[0]=th1;
    thetas[1]=th2;
    thetas[2]=th3;
}

void _jacobian_BR(Eigen::Matrix<double, 3, 3> &jacob, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
   jacob << 0,l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]),l3*cos(thetas[1] + thetas[2]),
           (l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*cos(thetas[0]) - l1*sin(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*sin(thetas[0]),l3*sin(thetas[1] + thetas[2])*sin(thetas[0]),
           ((l2*cos(thetas[1]) + l3*cos(thetas[1] + thetas[2]))*sin(thetas[0])) +l1*cos(thetas[0]),(l2*sin(thetas[1]) + l3*sin(thetas[1] + thetas[2]))*cos(thetas[0]),l3*sin(thetas[1] + thetas[2])*cos(thetas[0]);

}

void _inverse_dynamics_BR(Eigen::Vector3d &torques, Eigen::Vector3d &force, Eigen::Vector3d &thetas, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Finds Joint torques for desired force at end effector
    torques =  [abduction, hip, knee]
    force = [x,y,z]
    thetas = [abduction, hip, knee] angles in radians
    l1, l2, l3 = link lengths
    */
    Eigen::Matrix<double,3,3> jacob;
    _jacobian_BR(jacob, thetas);
    torques = jacob.transpose()*force;
    torques[0]=-1*torques[0];
}

void forward_kinematics(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, int leg, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Performs forward kinematics for a 3R spatial manipulator
    thetas = [abduction, hip, knee] angles in radians
    end_effector_pos = [x,y,z] in base frame
    l1, l2, l3 = link lengths
    FL=0
    FR=1
    BL=2
    BR=3
    */
   if(leg == FL)
   {
       _forward_kinematics_FL(thetas, end_effector_pos);
   }
   else if(leg == FR)
   {
       _forward_kinematics_FR(thetas, end_effector_pos);
   }
   else if(leg == BL)
   {
       _forward_kinematics_BL(thetas, end_effector_pos);
   }
   else if(leg == BR)
   {
       _forward_kinematics_BR(thetas, end_effector_pos);
   }
   else
   {
       std::cout<<"Invalid leg number, forward kinematics"<<std::endl;
   }

}

void inverse_kinematics(Eigen::Vector3d &thetas, Eigen::Vector3d &end_effector_pos, int leg, float l1 = L1, float l2 = L2, float l3 = L3)
{
    /*
    Performs forward kinematics for a 3R spatial manipulator
    thetas = [abduction, hip, knee] angles in radians
    end_effector_pos = [x,y,z] in base frame
    l1, l2, l3 = link lengths
    FL=0
    FR=1
    BL=2
    BR=3
    */
   if(leg == FL)
   {
       _inverse_kinematics_FL(thetas, end_effector_pos);
   }
   else if(leg == FR)
   {
       _inverse_kinematics_FR(thetas, end_effector_pos);
   }
   else if(leg == BL)
   {
       _inverse_kinematics_BL(thetas, end_effector_pos);
   }
   else if(leg == BR)
   {
       _inverse_kinematics_BR(thetas, end_effector_pos);
   }
   else
   {
       std::cout<<"Invalid leg number, inverse kinematics"<<std::endl;
   }

}

// int main()
// {

//     // Eigen::Vector3d end_eff(0.15, 0.15, 0.2);
//     // Eigen::Vector3d fwd_kin(0.0, 0.0, 0.0);
//     // Eigen::Vector3d jpos(0,0,0);
//     // Eigen::Matrix<double,3,3> jaco;
//     // // _inverse_kinematics_BR(jpos, end_eff);
//     // _forward_kinematics_FL(fwd_kin, jpos);
//     // _jacobian_FL(jaco, jpos);
//     // std::cout<<jaco<<std::endl;
//     Eigen::Vector3d thetas(1.2, 0.3, 0.66);
//     Eigen::Vector3d fwd_kin(0.0, 0.0, 0.0);
//     Eigen::Vector3d inv_kin(0,0,0);
//     Eigen::Vector3d fwd_kin2(0,0,0);
//     _forward_kinematics_FL(fwd_kin, thetas);
//     _inverse_kinematics_FL(inv_kin, fwd_kin);
//     _forward_kinematics_FL(fwd_kin2, inv_kin);
//     std::cout<<fwd_kin[0]<<","<<fwd_kin[1]<<","<<fwd_kin[2]<<std::endl;
//     std::cout<<fwd_kin2[0]<<","<<fwd_kin2[1]<<","<<fwd_kin2[2]<<std::endl;

//     return 0;
// }


#include "cnn/nodes.h"
#include "cnn/cnn.h"
#include "cnn/training.h"
#include "cnn/gpu-ops.h"
#include "cnn/expr.h"
#include "cnn/grad-check.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <iostream>
#include <fstream>

using namespace std;
using namespace cnn;
using namespace cnn::expr;

int main(int argc, char** argv) {
  cnn::Initialize(argc, argv);

  // parameters
  const unsigned int HIDDEN_SIZE = 8;
  const unsigned int ITERATIONS = 30;
  Model m;
  SimpleSGDTrainer sgd(&m);
  //MomentumSGDTrainer sgd(&m);

  ComputationGraph cg;

  Expression W = parameter(cg, m.add_parameters({HIDDEN_SIZE, 2}, 1.0, "W"));
  Expression b = parameter(cg, m.add_parameters({HIDDEN_SIZE}, 0.0, "b"));
  Expression V = parameter(cg, m.add_parameters({1, HIDDEN_SIZE}, 1.0, "V"));
  Expression a = parameter(cg, m.add_parameters({1}, 0.0, "a"));

  vector<cnn::real> x_values(2);  // set x_values to change the inputs to the network
  Expression x = input(cg, {2}, &x_values);
  cnn::real y_value;  // set y_value to change the target output
  Expression y = input(cg, &y_value);

  Expression h = tanh(W*x + b);
  //Expression h = softsign(W*x + b);
  Expression y_pred = V*h + a;
  Expression loss = squared_distance(y_pred, y);

  cg.PrintGraphviz();
  if (argc == 2) {
    ifstream in(argv[1]);
    boost::archive::text_iarchive ia(in);
    ia >> m;
  }

  // train the parameters
  for (unsigned iter = 0; iter < ITERATIONS; ++iter) {
    cnn::real loss = 0;
    for (unsigned mi = 0; mi < 4; ++mi) {
      bool x1 = mi % 2;
      bool x2 = (mi / 2) % 2;
      x_values[0] = x1 ? 1 : -1;
      x_values[1] = x2 ? 1 : -1;
      y_value = (x1 != x2) ? 1 : -1;
      loss += as_scalar(cg.forward());

//      CheckGrad(m, cg);

      cg.backward();
      sgd.update(1.0);
    }
    sgd.update_epoch();
    loss /= 4;
    cerr << "E = " << loss << endl;
  }
  boost::archive::text_oarchive oa(cout);
  oa << m;
}


#include <iostream>
#include <fstream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <Eigen/Eigen>

#include <boost/format.hpp>

#include <octomap/octomap.h>
#include <octomap/ColorOcTree.h>

#define OCTOMAP_WITH_COLOR      1

int main(int argc, char** argv)
{
    std::vector<cv::Mat> color_images, depth_images;
    std::vector<Eigen::Isometry3d, Eigen::aligned_allocator<Eigen::Isometry3d>> poses;

    std::ifstream fin("../../data/pose.txt");
    if (!fin) {
        std::cerr << "please run the program with the directory -'../../data/' and file 'pose.txt'.";
        exit(EXIT_FAILURE);
    }

    for (int i=0; i < 5; i ++) {
        boost::format fmt("../../data/%s/%d.%s");
        color_images.push_back(cv::imread((fmt%"color"%(i+1)%"png").str()));
        depth_images.push_back(cv::imread((fmt%"depth"%(i+1)%"pgm").str(), -1));

        double data[7] = {0};
        for (int i = 0; i < 7; i ++) {
            fin >> data[i];
        }
        Eigen::Quaterniond q(data[6], data[3], data[4], data[5]);
        Eigen::Isometry3d T(q);
        T.pretranslate(Eigen::Vector3d(data[0], data[1], data[2]));
        poses.push_back(T);
    }

    double cx = 325.5;
    double cy = 253.5;
    double fx = 518.0;
    double fy = 519.0;
    double depthScale = 1000.0;

    std::cout << "The image is being converted to a OctoMap ..." << std::endl;

    // octomap tree
#if OCTOMAP_WITH_COLOR
    struct RGB_Color {
        uchar r;
        uchar g;
        uchar b;
    };

    octomap::ColorOcTree tree(0.05);
#else
    octomap::OcTree tree(0.05);     // Resolution
#endif

    for (int i = 0; i < 5; i++) {
        std::cout << "Image "<< i+1 << " is being converted ... " << std::endl;

        cv::Mat color = color_images[i];
        cv::Mat depth = depth_images[i];
        Eigen::Isometry3d T = poses[i];

        octomap::Pointcloud cloud;  // the point cloud in octomap
#if OCTOMAP_WITH_COLOR
        std::vector<RGB_Color> rgb_colors;
#endif

        for (int v = 0; v < color.rows; v++) {
            for (int u = 0; u < color.cols; u++) {
                unsigned int d = depth.ptr<unsigned short>(v)[u];   // depth value
                if (d == 0) continue;       /* last measure, value is 0 */
                if (d >= 7000) continue;    /* the depth value too large */

                Eigen::Vector3d point;
                point[2] = double(d) / depthScale;
                point[0] = (u-cx)*point[2]/fx;
                point[1] = (v-cy)*point[2]/fy;
                Eigen::Vector3d point_world = T*point;

                cloud.push_back(point_world[0], point_world[1], point_world[2]);
#if OCTOMAP_WITH_COLOR
                RGB_Color rgb_color;
                rgb_color.b = color.ptr<uchar>(v)[3*u];
                rgb_color.g = color.ptr<uchar>(v)[3*u + 1];
                rgb_color.r = color.ptr<uchar>(v)[3*u + 2];
                rgb_colors.push_back(rgb_color);
#endif
            }
        }

        tree.insertPointCloud(cloud, octomap::point3d(T(0,3), T(1,3), T(2,3)));

#if OCTOMAP_WITH_COLOR
        int j = 0;
//         for (octomap::point3d_collection::iterator p = cloud.begin(); p < cloud.end(); p ++) {
        for (auto p = cloud.begin(); p < cloud.end(); p++) {
            auto rgb = rgb_colors.data()[j];
            tree.integrateNodeColor((*p).x(), (*p).y(), (*p).z(), rgb.r, rgb.g, rgb.b);
            j ++;
        }
#endif
    }

    tree.updateInnerOccupancy();
    std::cout << "saving octomap..." << std::endl;

#if OCTOMAP_WITH_COLOR
    tree.write("octomap.ot");
#else
    tree.writeBinary("octomap.bt");
#endif

    return 0;
}


// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/ext/std/ratio.hpp>
#include <boost/hana/not_equal.hpp>

#include <ratio>
namespace hana = boost::hana;

BOOST_HANA_CONSTANT_CHECK(hana::equal(std::ratio<3, 4>{}, std::ratio<15, 20>{}));
BOOST_HANA_CONSTANT_CHECK(hana::not_equal(std::ratio<3, 4>{}, std::ratio<3, 5>{}));

int main() { }


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2008, 2009 StatPro Italia srl

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include "creditdefaultswap.hpp"
#include "utilities.hpp"
#include <ql/cashflows/iborcoupon.hpp>
#include <ql/instruments/creditdefaultswap.hpp>
#include <ql/instruments/makecds.hpp>
#include <ql/pricingengines/credit/midpointcdsengine.hpp>
#include <ql/pricingengines/credit/integralcdsengine.hpp>
#include <ql/pricingengines/credit/isdacdsengine.hpp>
#include <ql/termstructures/credit/flathazardrate.hpp>
#include <ql/termstructures/credit/interpolatedhazardratecurve.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/termstructures/yield/discountcurve.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/ratehelpers.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/math/interpolations/backwardflatinterpolation.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/calendars/weekendsonly.hpp>
#include <ql/currencies/america.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <map>

#include <iomanip>
#include <iostream>

using namespace QuantLib;
using namespace boost::unit_test_framework;
using boost::assign::map_list_of;
using std::map;

void CreditDefaultSwapTest::testCachedValue() {

    BOOST_TEST_MESSAGE("Testing credit-default swap against cached values...");

    SavedSettings backup;

    // Initialize curves
    Settings::instance().evaluationDate() = Date(9,June,2006);
    Date today = Settings::instance().evaluationDate();
    Calendar calendar = TARGET();

    Handle<Quote> hazardRate = Handle<Quote>(
                ext::shared_ptr<Quote>(new SimpleQuote(0.01234)));
    RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve;
    probabilityCurve.linkTo(
        ext::shared_ptr<DefaultProbabilityTermStructure>(
                   new FlatHazardRate(0, calendar, hazardRate, Actual360())));

    RelinkableHandle<YieldTermStructure> discountCurve;

    discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>(
                            new FlatForward(today,0.06,Actual360())));

    // Build the schedule
    Date issueDate = calendar.advance(today, -1, Years);
    Date maturity = calendar.advance(issueDate, 10, Years);
    Frequency frequency = Semiannual;
    BusinessDayConvention convention = ModifiedFollowing;

    Schedule schedule(issueDate, maturity, Period(frequency), calendar,
                      convention, convention, DateGeneration::Forward, false);

    // Build the CDS
    Rate fixedRate = 0.0120;
    DayCounter dayCount = Actual360();
    Real notional = 10000.0;
    Real recoveryRate = 0.4;

    CreditDefaultSwap cds(Protection::Seller, notional, fixedRate,
                          schedule, convention, dayCount, true, true);
    cds.setPricingEngine(ext::shared_ptr<PricingEngine>(
         new MidPointCdsEngine(probabilityCurve,recoveryRate,discountCurve)));

    Real npv = 295.0153398;
    Rate fairRate = 0.007517539081;

    Real calculatedNpv = cds.NPV();
    Rate calculatedFairRate = cds.fairSpread();
    Real tolerance = 1.0e-7;

    if (std::fabs(calculatedNpv - npv) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce NPV with mid-point engine\n"
            << std::setprecision(10)
            << "    calculated NPV: " << calculatedNpv << "\n"
            << "    expected NPV:   " << npv);

    if (std::fabs(calculatedFairRate - fairRate) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce fair rate with mid-point engine\n"
            << std::setprecision(10)
            << "    calculated fair rate: " << calculatedFairRate << "\n"
            << "    expected fair rate:   " << fairRate);

    cds.setPricingEngine(ext::shared_ptr<PricingEngine>(
                          new IntegralCdsEngine(1*Days,probabilityCurve,
                                                recoveryRate,discountCurve)));

    calculatedNpv = cds.NPV();
    calculatedFairRate = cds.fairSpread();
    tolerance = 1.0e-5;

    if (std::fabs(calculatedNpv - npv) > notional*tolerance*10)
        BOOST_ERROR(
            "Failed to reproduce NPV with integral engine "
            "(step = 1 day)\n"
            << std::setprecision(10)
            << "    calculated NPV: " << calculatedNpv << "\n"
            << "    expected NPV:   " << npv);

    if (std::fabs(calculatedFairRate - fairRate) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce fair rate with integral engine "
            "(step = 1 day)\n"
            << std::setprecision(10)
            << "    calculated fair rate: " << calculatedFairRate << "\n"
            << "    expected fair rate:   " << fairRate);

    cds.setPricingEngine(ext::shared_ptr<PricingEngine>(
                          new IntegralCdsEngine(1*Weeks,probabilityCurve,
                                                recoveryRate,discountCurve)));

    calculatedNpv = cds.NPV();
    calculatedFairRate = cds.fairSpread();
    tolerance = 1.0e-5;

    if (std::fabs(calculatedNpv - npv) > notional*tolerance*10)
        BOOST_ERROR(
            "Failed to reproduce NPV with integral engine "
            "(step = 1 week)\n"
            << std::setprecision(10)
            << "    calculated NPV: " << calculatedNpv << "\n"
            << "    expected NPV:   " << npv);

    if (std::fabs(calculatedFairRate - fairRate) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce fair rate with integral engine "
            "(step = 1 week)\n"
            << std::setprecision(10)
            << "    calculated fair rate: " << calculatedFairRate << "\n"
            << "    expected fair rate:   " << fairRate);
}

void CreditDefaultSwapTest::testCachedMarketValue() {

    BOOST_TEST_MESSAGE(
        "Testing credit-default swap against cached market values...");

    SavedSettings backup;

    Settings::instance().evaluationDate() = Date(9,June,2006);
    Date evalDate = Settings::instance().evaluationDate();
    Calendar calendar = UnitedStates();

    std::vector<Date> discountDates;
    discountDates.push_back(evalDate);
    discountDates.push_back(calendar.advance(evalDate, 1, Weeks,  ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 1, Months, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 2, Months, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 3, Months, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 6, Months, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate,12, Months, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 2, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 3, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 4, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 5, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 6, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 7, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 8, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate, 9, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate,10, Years, ModifiedFollowing));
    discountDates.push_back(calendar.advance(evalDate,15, Years, ModifiedFollowing));

    std::vector<DiscountFactor> dfs;
    dfs.push_back(1.0);
    dfs.push_back(0.9990151375768731);
    dfs.push_back(0.99570502636871183);
    dfs.push_back(0.99118260474528685);
    dfs.push_back(0.98661167950906203);
    dfs.push_back(0.9732592953359388 );
    dfs.push_back(0.94724424481038083);
    dfs.push_back(0.89844996737120875  );
    dfs.push_back(0.85216647839921411  );
    dfs.push_back(0.80775477692556874  );
    dfs.push_back(0.76517289234200347  );
    dfs.push_back(0.72401019553182933  );
    dfs.push_back(0.68503909569219212  );
    dfs.push_back(0.64797499814013748  );
    dfs.push_back(0.61263171936255534  );
    dfs.push_back(0.5791942350748791   );
    dfs.push_back(0.43518868769953606  );

    const DayCounter& curveDayCounter=Actual360();

    RelinkableHandle<YieldTermStructure> discountCurve;
    discountCurve.linkTo(
        ext::shared_ptr<YieldTermStructure>(
            new DiscountCurve(discountDates, dfs, curveDayCounter)));

    DayCounter dayCounter = Thirty360();
    std::vector<Date> dates;
    dates.push_back(evalDate);
    dates.push_back(calendar.advance(evalDate, 6, Months, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 1, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 2, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 3, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 4, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 5, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate, 7, Years, ModifiedFollowing));
    dates.push_back(calendar.advance(evalDate,10, Years, ModifiedFollowing));

    std::vector<Probability> defaultProbabilities;
    defaultProbabilities.push_back(0.0000);
    defaultProbabilities.push_back(0.0047);
    defaultProbabilities.push_back(0.0093);
    defaultProbabilities.push_back(0.0286);
    defaultProbabilities.push_back(0.0619);
    defaultProbabilities.push_back(0.0953);
    defaultProbabilities.push_back(0.1508);
    defaultProbabilities.push_back(0.2288);
    defaultProbabilities.push_back(0.3666);

    std::vector<Real> hazardRates;
    hazardRates.push_back(0.0);
    for (Size i=1; i<dates.size(); ++i) {
        Time t1 = dayCounter.yearFraction(dates[0], dates[i-1]);
        Time t2 = dayCounter.yearFraction(dates[0], dates[i]);
        Probability S1 = 1.0 - defaultProbabilities[i-1];
        Probability S2 = 1.0 - defaultProbabilities[i];
        hazardRates.push_back(std::log(S1/S2)/(t2-t1));
    }

    RelinkableHandle<DefaultProbabilityTermStructure> piecewiseFlatHazardRate;
    piecewiseFlatHazardRate.linkTo(
        ext::shared_ptr<DefaultProbabilityTermStructure>(
               new InterpolatedHazardRateCurve<BackwardFlat>(dates,
                                                             hazardRates,
                                                             Thirty360())));

    // Testing credit default swap

    // Build the schedule
    Date issueDate(20, March, 2006);
    Date maturity(20, June, 2013);
    Frequency cdsFrequency = Semiannual;
    BusinessDayConvention cdsConvention = ModifiedFollowing;

    Schedule schedule(issueDate, maturity, Period(cdsFrequency), calendar,
                      cdsConvention, cdsConvention,
                      DateGeneration::Forward, false);

    // Build the CDS
    Real recoveryRate = 0.25;
    Rate fixedRate=0.0224;
    DayCounter dayCount=Actual360();
    Real cdsNotional=100.0;

    CreditDefaultSwap cds(Protection::Seller, cdsNotional, fixedRate,
                          schedule, cdsConvention, dayCount, true, true);
    cds.setPricingEngine(ext::shared_ptr<PricingEngine>(
                          new MidPointCdsEngine(piecewiseFlatHazardRate,
                                                recoveryRate,discountCurve)));

    Real calculatedNpv = cds.NPV();
    Real calculatedFairRate = cds.fairSpread();

    double npv = -1.364048777;        // from Bloomberg we have 98.15598868 - 100.00;
    double fairRate =  0.0248429452; // from Bloomberg we have 0.0258378;

    Real tolerance = 1e-9;

    if (std::fabs(npv - calculatedNpv) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce the npv for the given credit-default swap\n"
            << std::setprecision(10)
            << "    computed NPV:  " << calculatedNpv << "\n"
            << "    Given NPV:     " << npv);

    if (std::fabs(fairRate - calculatedFairRate) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce the fair rate for the given credit-default swap\n"
            << std::setprecision(10)
            << "    computed fair rate:  " << calculatedFairRate << "\n"
            << "    Given fair rate:     " << fairRate);
}

void CreditDefaultSwapTest::testImpliedHazardRate() {

    BOOST_TEST_MESSAGE("Testing implied hazard-rate for credit-default swaps...");

    SavedSettings backup;

    // Initialize curves
    Calendar calendar = TARGET();
    Date today = calendar.adjust(Date::todaysDate());
    Settings::instance().evaluationDate() = today;

    Rate h1 = 0.30, h2 = 0.40;
    DayCounter dayCounter = Actual365Fixed();

    std::vector<Date> dates(3);
    std::vector<Real> hazardRates(3);
    dates[0] = today;
    hazardRates[0] = h1;

    dates[1] = today + 5*Years;
    hazardRates[1] = h1;

    dates[2] = today + 10*Years;
    hazardRates[2] = h2;

    RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve;
    probabilityCurve.linkTo(ext::shared_ptr<DefaultProbabilityTermStructure>(
                    new InterpolatedHazardRateCurve<BackwardFlat>(dates,
                                                                  hazardRates,
                                                                  dayCounter)));

    RelinkableHandle<YieldTermStructure> discountCurve;
    discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>(
                            new FlatForward(today,0.03,Actual360())));

    Frequency frequency = Semiannual;
    BusinessDayConvention convention = ModifiedFollowing;

    Date issueDate = calendar.advance(today, -6, Months);
    Rate fixedRate = 0.0120;
    DayCounter cdsDayCount = Actual360();
    Real notional = 10000.0;
    Real recoveryRate = 0.4;

    Rate latestRate = Null<Rate>();
    for (Integer n=6; n<=10; ++n) {

        Date maturity = calendar.advance(issueDate, n, Years);
        Schedule schedule(issueDate, maturity, Period(frequency), calendar,
                          convention, convention,
                          DateGeneration::Forward, false);

        CreditDefaultSwap cds(Protection::Seller, notional, fixedRate,
                              schedule, convention, cdsDayCount,
                              true, true);
        cds.setPricingEngine(ext::shared_ptr<PricingEngine>(
                         new MidPointCdsEngine(probabilityCurve,
                                               recoveryRate, discountCurve)));

        Real NPV = cds.NPV();
        Rate flatRate = cds.impliedHazardRate(NPV, discountCurve,
                                              dayCounter,
                                              recoveryRate);

        if (flatRate < h1 || flatRate > h2) {
            BOOST_ERROR("implied hazard rate outside expected range\n"
                        << "    maturity: " << n << " years\n"
                        << "    expected minimum: " << h1 << "\n"
                        << "    expected maximum: " << h2 << "\n"
                        << "    implied rate:     " << flatRate);
        }

        if (n > 6 && flatRate < latestRate) {
            BOOST_ERROR("implied hazard rate decreasing with swap maturity\n"
                        << "    maturity: " << n << " years\n"
                        << "    previous rate: " << latestRate << "\n"
                        << "    implied rate:  " << flatRate);
        }

        latestRate = flatRate;

        RelinkableHandle<DefaultProbabilityTermStructure> probability;
        probability.linkTo(ext::shared_ptr<DefaultProbabilityTermStructure>(
         new FlatHazardRate(
           today,
           Handle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(flatRate))),
           dayCounter)));

        CreditDefaultSwap cds2(Protection::Seller, notional, fixedRate,
                               schedule, convention, cdsDayCount,
                               true, true);
        cds2.setPricingEngine(ext::shared_ptr<PricingEngine>(
                               new MidPointCdsEngine(probability,recoveryRate,
                                                     discountCurve)));

        Real NPV2 = cds2.NPV();
        Real tolerance = 1.0;
        if (std::fabs(NPV-NPV2) > tolerance) {
            BOOST_ERROR("failed to reproduce NPV with implied rate\n"
                        << "    expected:   " << NPV << "\n"
                        << "    calculated: " << NPV2);
        }
    }
}

void CreditDefaultSwapTest::testFairSpread() {

    BOOST_TEST_MESSAGE(
        "Testing fair-spread calculation for credit-default swaps...");

    SavedSettings backup;

    // Initialize curves
    Calendar calendar = TARGET();
    Date today = calendar.adjust(Date::todaysDate());
    Settings::instance().evaluationDate() = today;

    Handle<Quote> hazardRate = Handle<Quote>(
                ext::shared_ptr<Quote>(new SimpleQuote(0.01234)));
    RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve;
    probabilityCurve.linkTo(
        ext::shared_ptr<DefaultProbabilityTermStructure>(
                   new FlatHazardRate(0, calendar, hazardRate, Actual360())));

    RelinkableHandle<YieldTermStructure> discountCurve;
    discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>(
                            new FlatForward(today,0.06,Actual360())));

    // Build the schedule
    Date issueDate = calendar.advance(today, -1, Years);
    Date maturity = calendar.advance(issueDate, 10, Years);
    BusinessDayConvention convention = Following;

    Schedule schedule =
        MakeSchedule().from(issueDate)
                      .to(maturity)
                      .withFrequency(Quarterly)
                      .withCalendar(calendar)
                      .withTerminationDateConvention(convention)
                      .withRule(DateGeneration::TwentiethIMM);

    // Build the CDS
    Rate fixedRate = 0.001;
    DayCounter dayCount = Actual360();
    Real notional = 10000.0;
    Real recoveryRate = 0.4;

    ext::shared_ptr<PricingEngine> engine(
          new MidPointCdsEngine(probabilityCurve,recoveryRate,discountCurve));

    CreditDefaultSwap cds(Protection::Seller, notional, fixedRate,
                          schedule, convention, dayCount, true, true);
    cds.setPricingEngine(engine);

    Rate fairRate = cds.fairSpread();

    CreditDefaultSwap fairCds(Protection::Seller, notional, fairRate,
                              schedule, convention, dayCount, true, true);
    fairCds.setPricingEngine(engine);

    Real fairNPV = fairCds.NPV();
    Real tolerance = 1e-9;

    if (std::fabs(fairNPV) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce null NPV with calculated fair spread\n"
            << "    calculated spread: " << io::rate(fairRate) << "\n"
            << "    calculated NPV:    " << fairNPV);
}

void CreditDefaultSwapTest::testFairUpfront() {

    BOOST_TEST_MESSAGE(
        "Testing fair-upfront calculation for credit-default swaps...");

    SavedSettings backup;

    // Initialize curves
    Calendar calendar = TARGET();
    Date today = calendar.adjust(Date::todaysDate());
    Settings::instance().evaluationDate() = today;

    Handle<Quote> hazardRate = Handle<Quote>(
                ext::shared_ptr<Quote>(new SimpleQuote(0.01234)));
    RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve;
    probabilityCurve.linkTo(
        ext::shared_ptr<DefaultProbabilityTermStructure>(
                   new FlatHazardRate(0, calendar, hazardRate, Actual360())));

    RelinkableHandle<YieldTermStructure> discountCurve;
    discountCurve.linkTo(ext::shared_ptr<YieldTermStructure>(
                            new FlatForward(today,0.06,Actual360())));

    // Build the schedule
    Date issueDate = today;
    Date maturity = calendar.advance(issueDate, 10, Years);
    BusinessDayConvention convention = Following;

    Schedule schedule =
        MakeSchedule().from(issueDate)
                      .to(maturity)
                      .withFrequency(Quarterly)
                      .withCalendar(calendar)
                      .withTerminationDateConvention(convention)
                      .withRule(DateGeneration::TwentiethIMM);

    // Build the CDS
    Rate fixedRate = 0.05;
    Rate upfront = 0.001;
    DayCounter dayCount = Actual360();
    Real notional = 10000.0;
    Real recoveryRate = 0.4;

    ext::shared_ptr<PricingEngine> engine(
          new MidPointCdsEngine(probabilityCurve, recoveryRate,
                                discountCurve, true));

    CreditDefaultSwap cds(Protection::Seller, notional, upfront, fixedRate,
                          schedule, convention, dayCount, true, true);
    cds.setPricingEngine(engine);

    Rate fairUpfront = cds.fairUpfront();

    CreditDefaultSwap fairCds(Protection::Seller, notional,
                              fairUpfront, fixedRate,
                              schedule, convention, dayCount, true, true);
    fairCds.setPricingEngine(engine);

    Real fairNPV = fairCds.NPV();
    Real tolerance = 1e-9;

    if (std::fabs(fairNPV) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce null NPV with calculated fair upfront\n"
            << "    calculated upfront: " << io::rate(fairUpfront) << "\n"
            << "    calculated NPV:     " << fairNPV);

    // same with null upfront to begin with
    upfront = 0.0;
    CreditDefaultSwap cds2(Protection::Seller, notional, upfront, fixedRate,
                           schedule, convention, dayCount, true, true);
    cds2.setPricingEngine(engine);

    fairUpfront = cds2.fairUpfront();

    CreditDefaultSwap fairCds2(Protection::Seller, notional,
                               fairUpfront, fixedRate,
                               schedule, convention, dayCount, true, true);
    fairCds2.setPricingEngine(engine);

    fairNPV = fairCds2.NPV();

    if (std::fabs(fairNPV) > tolerance)
        BOOST_ERROR(
            "Failed to reproduce null NPV with calculated fair upfront\n"
            << "    calculated upfront: " << io::rate(fairUpfront) << "\n"
            << "    calculated NPV:     " << fairNPV);
}

void CreditDefaultSwapTest::testIsdaEngine() {

    BOOST_TEST_MESSAGE(
        "Testing ISDA engine calculations for credit-default swaps...");

    SavedSettings backup;

    Date tradeDate(21, May, 2009);
    Settings::instance().evaluationDate() = tradeDate;

    //build an ISDA compliant yield curve
    //data comes from Markit published rates
    std::vector<ext::shared_ptr<RateHelper> > isdaRateHelpers;
    int dep_tenors[] = {1, 2, 3, 6, 9, 12};
    double dep_quotes[] = {0.003081,
                           0.005525,
                           0.007163,
                           0.012413,
                           0.014,
                           0.015488};

    for(size_t i = 0; i < sizeof(dep_tenors) / sizeof(int); i++) {
        isdaRateHelpers.push_back(ext::make_shared<DepositRateHelper>(
                                     dep_quotes[i], dep_tenors[i] * Months, 2,
                                     WeekendsOnly(), ModifiedFollowing,
                                     false, Actual360()
                                     )
            );
    }
    int swap_tenors[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 30};
    double swap_quotes[] = {0.011907,
                            0.01699,
                            0.021198,
                            0.02444,
                            0.026937,
                            0.028967,
                            0.030504,
                            0.031719,
                            0.03279,
                            0.034535,
                            0.036217,
                            0.036981,
                            0.037246,
                            0.037605};

    ext::shared_ptr<IborIndex> isda_ibor = ext::make_shared<IborIndex>(
        "IsdaIbor", 3 * Months, 2, USDCurrency(), WeekendsOnly(),
        ModifiedFollowing, false, Actual360());
    for(size_t i = 0; i < sizeof(swap_tenors) / sizeof(int); i++) {
        isdaRateHelpers.push_back(ext::make_shared<SwapRateHelper>(
                                      swap_quotes[i], swap_tenors[i] * Years,
                                      WeekendsOnly(),
                                      Semiannual,
                                      ModifiedFollowing, Thirty360(), isda_ibor
                                      )
            );
    }

    RelinkableHandle<YieldTermStructure> discountCurve;
    discountCurve.linkTo(
            ext::make_shared<PiecewiseYieldCurve<Discount, LogLinear> >(
                0, WeekendsOnly(), isdaRateHelpers, Actual365Fixed())
        );

    RelinkableHandle<DefaultProbabilityTermStructure> probabilityCurve;
    Date termDates[] = {Date(20, June, 2010),
                        Date(20, June, 2011),
                        Date(20, June, 2012),
                        Date(20, June, 2016),
                        Date(20, June, 2019)};
    Rate spreads[] = {0.001, 0.1};
    Rate recoveries[] = {0.2, 0.4};

    double markitValues[] = {97798.29358, //0.001
                             97776.11889, //0.001
                             -914971.5977, //0.1
                             -894985.6298, //0.1
                             186921.3594, //0.001
                             186839.8148, //0.001
                             -1646623.672, //0.1
                             -1579803.626, //0.1
                             274298.9203,
                             274122.4725,
                             -2279730.93,
                             -2147972.527,
                             592420.2297,
                             591571.2294,
                             -3993550.206,
                             -3545843.418,
                             797501.1422,
                             795915.9787,
                             -4702034.688,
                             -4042340.999};
    Real tolerance;
    if (IborCoupon::usingAtParCoupons()) {
        tolerance = 1.0e-6;
    } else {
        /* The risk-free curve is a bit off. We might skip the tests
           altogether and rely on running them with indexed coupons
           disabled, but leaving them can be useful anyway. */
        tolerance = 1.0e-3;
    }

    size_t l = 0;

    for(size_t i = 0; i < sizeof(termDates) / sizeof(Date); i++) {
        for(size_t j = 0; j < 2; j++) {
            for(size_t k = 0; k < 2; k++) {

            ext::shared_ptr<CreditDefaultSwap> quotedTrade =
                MakeCreditDefaultSwap(termDates[i], spreads[j])
                .withNominal(10000000.);

            Rate h = quotedTrade->impliedHazardRate(0.,
                                                    discountCurve,
                                                    Actual365Fixed(),
                                                    recoveries[k],
                                                    1e-10,
                                                    CreditDefaultSwap::ISDA);

            probabilityCurve.linkTo(
                ext::make_shared<FlatHazardRate>(
                    0, WeekendsOnly(), h, Actual365Fixed())
                );

            ext::shared_ptr<IsdaCdsEngine> engine = ext::make_shared<IsdaCdsEngine>(
                probabilityCurve, recoveries[k], discountCurve,
                boost::none, IsdaCdsEngine::Taylor, IsdaCdsEngine::HalfDayBias,
                IsdaCdsEngine::Piecewise);

            ext::shared_ptr<CreditDefaultSwap> conventionalTrade =
                MakeCreditDefaultSwap(termDates[i], 0.01)
                .withNominal(10000000.)
                .withPricingEngine(engine);

            BOOST_CHECK_CLOSE(conventionalTrade->notional() * conventionalTrade->fairUpfront(),
                              markitValues[l],
                              tolerance);

            l++;

            }
        }
    }

}

void CreditDefaultSwapTest::testAccrualRebateAmounts() {

    BOOST_TEST_MESSAGE("Testing accrual rebate amounts on credit default swaps...");

    SavedSettings backup;

    // The accrual values are taken from various test results on the ISDA CDS model website
    // https://www.cdsmodel.com/cdsmodel/documentation.html.

    // Inputs
    Real notional = 10000000;
    Real spread = 0.0100;
    Date maturity(20, Jun, 2014);

    // key is trade date and value is expected accrual
    typedef map<Date, Real> InputData;
    InputData inputs = map_list_of
        (Date(18, Mar, 2009), 24166.67)
        (Date(19, Mar, 2009), 0.00)
        (Date(20, Mar, 2009), 277.78)
        (Date(23, Mar, 2009), 1111.11)
        (Date(19, Jun, 2009), 25555.56)
        (Date(20, Jun, 2009), 25833.33)
        (Date(21, Jun, 2009), 0.00)
        (Date(22, Jun, 2009), 277.78)
        (Date(18, Jun, 2014), 25277.78)
        (Date(19, Jun, 2014), 25555.56);

    BOOST_FOREACH(const InputData::value_type& input, inputs) {
        Settings::instance().evaluationDate() = input.first;
        CreditDefaultSwap cds = MakeCreditDefaultSwap(maturity, spread)
            .withNominal(notional);
        BOOST_CHECK_SMALL(input.second - cds.accrualRebate()->amount(), 0.01);
    }
}

test_suite* CreditDefaultSwapTest::suite() {
    test_suite* suite = BOOST_TEST_SUITE("Credit-default swap tests");
    suite->add(QUANTLIB_TEST_CASE(&CreditDefaultSwapTest::testCachedValue));
    suite->add(QUANTLIB_TEST_CASE(
                              &CreditDefaultSwapTest::testCachedMarketValue));
    suite->add(QUANTLIB_TEST_CASE(
                              &CreditDefaultSwapTest::testImpliedHazardRate));
    suite->add(QUANTLIB_TEST_CASE(&CreditDefaultSwapTest::testFairSpread));
    suite->add(QUANTLIB_TEST_CASE(&CreditDefaultSwapTest::testFairUpfront));
    suite->add(QUANTLIB_TEST_CASE(&CreditDefaultSwapTest::testIsdaEngine));
    suite->add(QUANTLIB_TEST_CASE(&CreditDefaultSwapTest::testAccrualRebateAmounts));
    return suite;
}


#include <Eigen/Dense>
#include <Eigen/Core>

#include "BingoCpp/implicit_regression.h"

namespace bingo {

ImplicitTrainingData *ImplicitTrainingData::GetItem(int item) {
  return new ImplicitTrainingData(x.row(item), dx_dt.row(item));
}

ImplicitTrainingData *ImplicitTrainingData::GetItem(
    const std::vector<int> &items) {
  Eigen::ArrayXXd temp_in(items.size(), x.cols());
  Eigen::ArrayXXd temp_out(items.size(), dx_dt.cols());

  for (std::size_t row = 0; row < items.size(); row ++) {
    temp_in.row(row) = x.row(items[row]);
    temp_out.row(row) = dx_dt.row(items[row]);
  }
  return new ImplicitTrainingData(temp_in, temp_out);
}

void normalize_by_row(Eigen::ArrayXXd *data_array);
Eigen::ArrayXXd dfdx_dot_dfdt(bool normalize_dot,
                              const Eigen::ArrayXXd &dx_dt,
                              const Eigen::ArrayXXd &grad);
bool not_enough_parameters_used(int required_params,
                                const Eigen::ArrayXXd &dot_product);

Eigen::ArrayXXd ImplicitRegression::EvaluateFitnessVector(
    const Equation &individual) const {
  EvalAndDerivative eval_and_grad
      = individual.EvaluateEquationWithXGradientAt(
      ((ImplicitTrainingData*)training_data_)->x);
  Eigen::ArrayXXd dot_product = dfdx_dot_dfdt(
      normalize_dot_,
      ((ImplicitTrainingData*)training_data_)->dx_dt,
      eval_and_grad.second);

  if (required_params_ != kNoneRequired
      && not_enough_parameters_used(required_params_, dot_product)) {
    return Eigen::ArrayXd::Constant(
        ((ImplicitTrainingData*)training_data_)->x.rows(),
         std::numeric_limits<double>::infinity());
  }
  // NOTE tylertownsend: may need to verify eigen NaN conditions
  Eigen::ArrayXXd denominator = dot_product.abs().rowwise().sum();
  Eigen::ArrayXXd normalized_fitness =
      dot_product.rowwise().sum() / denominator;
  return normalized_fitness.unaryExpr([](double v) {
    return std::isfinite(v) ? v : std::numeric_limits<double>::infinity();
  });
}

void normalize_by_row(Eigen::ArrayXXd *data_array) {
  Eigen::ArrayXXd norm_array = data_array->rowwise().norm();
  for (int i = 0; i < norm_array.rows(); i ++) {
    data_array->row(i) /= norm_array.row(i)[0];
  }
}

Eigen::ArrayXXd dfdx_dot_dfdt(bool normalize_dot,
                              const Eigen::ArrayXXd &dx_dt,
                              const Eigen::ArrayXXd &grad) {
  Eigen::ArrayXXd left_dot = grad;
  Eigen::ArrayXXd right_dot = dx_dt;
  if (normalize_dot) {
    normalize_by_row(&left_dot);
    normalize_by_row(&right_dot);
  }

  return left_dot * right_dot;
}

bool not_enough_parameters_used(int required_params,
                                const Eigen::ArrayXXd &dot_product) {
  auto num_params_used = (dot_product.abs() > 1e-16).rowwise().count();
  return !(num_params_used >= required_params).any();
}
} // namespace bingo


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "biharmonic_coordinates.h"
#include "cotmatrix.h"
#include "massmatrix.h"
#include "min_quad_with_fixed.h"
#include "normal_derivative.h"
#include "on_boundary.h"
#include <Eigen/Sparse>

template <
  typename DerivedV,
  typename DerivedT,
  typename SType,
  typename DerivedW>
IGL_INLINE bool igl::biharmonic_coordinates(
  const Eigen::PlainObjectBase<DerivedV> & V,
  const Eigen::PlainObjectBase<DerivedT> & T,
  const std::vector<std::vector<SType> > & S,
  Eigen::PlainObjectBase<DerivedW> & W)
{
  return biharmonic_coordinates(V,T,S,2,W);
}

template <
  typename DerivedV,
  typename DerivedT,
  typename SType,
  typename DerivedW>
IGL_INLINE bool igl::biharmonic_coordinates(
  const Eigen::PlainObjectBase<DerivedV> & V,
  const Eigen::PlainObjectBase<DerivedT> & T,
  const std::vector<std::vector<SType> > & S,
  const int k,
  Eigen::PlainObjectBase<DerivedW> & W)
{
  using namespace Eigen;
  using namespace std;
  // This is not the most efficient way to build A, but follows "Linear
  // Subspace Design for Real-Time Shape Deformation" [Wang et al. 2015].
  SparseMatrix<double> A;
  {
    SparseMatrix<double> N,Z,L,K,M;
    normal_derivative(V,T,N);
    Array<bool,Dynamic,1> I;
    Array<bool,Dynamic,Dynamic> C;
    on_boundary(T,I,C);
    {
      std::vector<Triplet<double> >ZIJV;
      for(int t =0;t<T.rows();t++)
      {
        for(int f =0;f<T.cols();f++)
        {
          if(C(t,f))
          {
            const int i = t+f*T.rows();
            for(int c = 1;c<T.cols();c++)
            {
              ZIJV.emplace_back(T(t,(f+c)%T.cols()),i,1);
            }
          }
        }
      }
      Z.resize(V.rows(),N.rows());
      Z.setFromTriplets(ZIJV.begin(),ZIJV.end());
      N = (Z*N).eval();
    }
    cotmatrix(V,T,L);
    K = N+L;
    massmatrix(V,T,MASSMATRIX_TYPE_DEFAULT,M);
    // normalize
    M /= ((VectorXd)M.diagonal()).array().abs().maxCoeff();
    DiagonalMatrix<double,Dynamic> Minv =
      ((VectorXd)M.diagonal().array().inverse()).asDiagonal();
    switch(k)
    {
      default:
        assert(false && "unsupported");
      case 2:
        // For C1 smoothness in 2D, one should use bi-harmonic
        A = K.transpose() * (Minv * K);
        break;
      case 3:
        // For C1 smoothness in 3D, one should use tri-harmonic
        A = K.transpose() * (Minv * (-L * (Minv * K)));
        break;
    }
  }
  // Vertices in point handles
  const size_t mp =
    count_if(S.begin(),S.end(),[](const vector<int> & h){return h.size()==1;});
  // number of region handles
  const size_t r = S.size()-mp;
  // Vertices in region handles
  size_t mr = 0;
  for(const auto & h : S)
  {
    if(h.size() > 1)
    {
      mr += h.size();
    }
  }
  const size_t dim = T.cols()-1;
  // Might as well be dense... I think...
  MatrixXd J = MatrixXd::Zero(mp+mr,mp+r*(dim+1));
  VectorXi b(mp+mr);
  MatrixXd H(mp+r*(dim+1),dim);
  {
    int v = 0;
    int c = 0;
    for(int h = 0;h<S.size();h++)
    {
      if(S[h].size()==1)
      {
        H.row(c) = V.block(S[h][0],0,1,dim);
        J(v,c++) = 1;
        b(v) = S[h][0];
        v++;
      }else
      {
        assert(S[h].size() >= dim+1);
        for(int p = 0;p<S[h].size();p++)
        {
          for(int d = 0;d<dim;d++)
          {
            J(v,c+d) = V(S[h][p],d);
          }
          J(v,c+dim) = 1;
          b(v) = S[h][p];
          v++;
        }
        H.block(c,0,dim+1,dim).setIdentity();
        c+=dim+1;
      }
    }
  }
  // minimize    ½ W' A W'
  // subject to  W(b,:) = J
  return min_quad_with_fixed(
    A,VectorXd::Zero(A.rows()).eval(),b,J,SparseMatrix<double>(),VectorXd(),true,W);
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
template bool igl::biharmonic_coordinates<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, int, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif


#include <iostream>

#include <../gsoc/boost/boost/convex_hull.hpp>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)

namespace bg = boost::geometry;
int main()
{
	typedef boost::tuple<double, double> point;
    typedef bg::model::multi_point<point> mpoints;

    mpoints mps;
    bg::read_wkt("MULTIPOINT((2.0 1.3)"
    	", (2.4 1.7), (2.8 1.8), (3.4 1.2), (3.7 1.6),(3.4 2.0), (4.1 3.0)"
        ", (5.3 2.6), (5.4 1.2), (4.9 0.8), (2.9 0.7),(2.0 1.3))", mps);

    mpoints hull;

    convex_hull(mps, hull);

    std::cout << bg::num_points(hull) << std::endl;

    typedef typename boost::range_const_iterator<mpoints>::type iterator;
    for ( iterator it = boost::begin(hull) ; it != boost::end(hull) ; ++it ) {
        std::cout << bg::get<0>(*it) << " " << bg::get<1>(*it) << std::endl;
    }
    return 0;
}