#include "GeometryOperationsBoost.h"
#include "GeometryBoost.h"
#include "../GeometryQt.h"

#include <boost/geometry.hpp>

#include <QDebug>

namespace LineArcOffsetDemo {

LineArcGeometry::MultiShape GeometryOperationsBoost::identity(const LineArcGeometry::MultiShape &multiShape)
{
    // qDebug() << "GeometryOperationsBoost::identity()";
    const BoostGeometry::Polygons polygons = MultiShapeToPolygons(multiShape);
    return PolygonsToMultiShape(polygons);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::join(const LineArcGeometry::MultiShape &multiShape)
{
    // qDebug() << "GeometryOperationsBoost::join()";
    const BoostGeometry::Polygons polygons = MultiShapeToPolygons(multiShape);
    BoostGeometry::Polygons joined;
    for (BoostGeometry::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
    {
        BoostGeometry::Polygons result;
        boost::geometry::union_(joined, *it, result);
        joined = result;
    }
    return PolygonsToMultiShape(joined);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::join(const LineArcGeometry::MultiShape &a, const LineArcGeometry::MultiShape &b)
{
    // qDebug() << "GeometryOperationsBoost::join()";
    const BoostGeometry::Polygons aa = MultiShapeToPolygons(a);
    const BoostGeometry::Polygons bb = MultiShapeToPolygons(b);
    BoostGeometry::Polygons result;
    boost::geometry::union_(aa, bb, result);
    return PolygonsToMultiShape(result);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::intersection(const LineArcGeometry::MultiShape &a, const LineArcGeometry::MultiShape &b)
{
    // qDebug() << "GeometryOperationsBoost::intersection()";
    const BoostGeometry::Polygons aa = MultiShapeToPolygons(a);
    const BoostGeometry::Polygons bb = MultiShapeToPolygons(b);
    BoostGeometry::Polygons result;
    boost::geometry::intersection(aa, bb, result);
    return PolygonsToMultiShape(result);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::difference(const LineArcGeometry::MultiShape &a, const LineArcGeometry::MultiShape &b)
{
    // qDebug() << "GeometryOperationsBoost::difference()";
    const BoostGeometry::Polygons aa = MultiShapeToPolygons(a);
    const BoostGeometry::Polygons bb = MultiShapeToPolygons(b);
    BoostGeometry::Polygons result;
    boost::geometry::difference(aa, bb, result);
    return PolygonsToMultiShape(result);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::symmetricDifference(const LineArcGeometry::MultiShape &multiShape)
{
    // qDebug() << "GeometryOperationsBoost::symmetricDifference()";
    const BoostGeometry::Polygons polygons = MultiShapeToPolygons(multiShape);
    BoostGeometry::Polygons xorResult;
    for (BoostGeometry::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
    {
        BoostGeometry::Polygons result;
        boost::geometry::sym_difference(xorResult, *it, result);
        xorResult = result;
    }
    return PolygonsToMultiShape(xorResult);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::symmetricDifference(const LineArcGeometry::MultiShape &a, const LineArcGeometry::MultiShape &b)
{
    // qDebug() << "GeometryOperationsBoost::symmetricDifference()";
    const BoostGeometry::Polygons aa = MultiShapeToPolygons(a);
    const BoostGeometry::Polygons bb = MultiShapeToPolygons(b);
    BoostGeometry::Polygons result;
    boost::geometry::sym_difference(aa, bb, result);
    return PolygonsToMultiShape(result);
}

LineArcGeometry::MultiShape GeometryOperationsBoost::offset(const LineArcGeometry::MultiShape &multiShape, double radius)
{
    // qDebug() << "GeometryOperationsBoost::offset()";
    const BoostGeometry::Polygons polygons = MultiShapeToPolygons(multiShape);
    BoostGeometry::Polygons result;
    boost::geometry::strategy::buffer::distance_symmetric<BoostGeometry::NumberType> distance_strategy(ToBoostNumber(radius));
    const int points_per_circle = 24;  // 360 / 15
    boost::geometry::strategy::buffer::join_round join_strategy(points_per_circle);
    boost::geometry::strategy::buffer::end_round end_strategy(points_per_circle);
    boost::geometry::strategy::buffer::point_circle circle_strategy(points_per_circle);
    boost::geometry::strategy::buffer::side_straight side_strategy;
    boost::geometry::buffer(polygons, result, distance_strategy, side_strategy, join_strategy, end_strategy, circle_strategy);
    return PolygonsToMultiShape(result);
}

} // namespace LineArcOffsetDemo


// Copyright (C) 2001 Vladimir Prus <ghost@cs.msu.su>
// Copyright (C) 2001 Jeremy Siek <jsiek@cs.indiana.edu>
// Permission to copy, use, modify, sell and distribute this software is
// granted, provided this copyright notice appears in all copies and
// modified version are clearly marked as such. This software is provided
// "as is" without express or implied warranty, and with no claim as to its
// suitability for any purpose.

#include <boost/graph/vector_as_graph.hpp>
#include <boost/graph/transitive_closure.hpp>

#include <iostream>

#include <boost/graph/vector_as_graph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>

#include <cstdlib>
#include <ctime>
#include <boost/progress.hpp>
using namespace std;
using namespace boost;

void generate_graph(int n, double p, vector< vector<int> >& r1)
{
  static class {
  public:
    double operator()() {
      return double(rand())/RAND_MAX;
    }
  } gen;
  r1.clear();
  r1.resize(n);
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
      if (gen() < p)
        r1[i].push_back(j);
}

// (i,j) is in E' if j is reachable from i
// Hmm, is_reachable does not detect when there is a non-trivial path
// from i to i. It always returns true for is_reachable(i,i).
// This needs to be fixed/worked around.
template <typename Graph, typename GraphTC>
bool check_transitive_closure(Graph& g, GraphTC& tc)
{
  std::vector<default_color_type> color_map_vec(num_vertices(g));
  typename graph_traits<GraphTC>::vertex_iterator i, i_end;
  for (tie(i, i_end) = vertices(tc); i != i_end; ++i) {
    typename graph_traits<GraphTC>::out_edge_iterator j, j_end;
    for (tie(j, j_end) = out_edges(*i, tc); j != j_end; ++j)
      if (!is_reachable(source(*j, g), target(*j, g), g, &color_map_vec[0]))
        return false;
  }
  return true;
}

bool test(int n, double p)
{
  vector< vector<int> > g1, g1_tc;
  generate_graph(n, p, g1);
  cout << "Created graph with " << n << " vertices.\n";

  vector< vector<int> > g1_c(g1);

  {
    progress_timer t;
    cout << "transitive_closure" << endl;
    transitive_closure(g1, g1_tc, vertex_index_map(identity_property_map()));
  }

  if(check_transitive_closure(g1, g1_tc))
    return true;
  else {
    //cout << "Original graph was " << multiline << g1_c << endl;
    //cout << "Result is " << multiline << g1 << endl;
    return false;
  }
}

int main()
{
  srand(time(0));
  static class {
  public:
    double operator()() {
      return double(rand())/RAND_MAX;
    }
  } gen;

  for (size_t i = 0; i < 100; ++i) {
    int n = 0 + int(20*gen());
    double p = gen();
    if (!test(n, p)) {
      cout << "Failed." << endl;
      return 1;
    }
  }
  cout << "Passed." << endl;
}


//
// Copyright 2019-2020 Mateusz Loskot <mateusz at loskot dot net>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
#include <boost/gil.hpp>
#include <boost/gil/extension/numeric/affine.hpp>
#include <boost/gil/extension/numeric/resample.hpp>
#include <boost/gil/extension/numeric/sampler.hpp>

#include <boost/core/lightweight_test.hpp>

namespace gil = boost::gil;

// FIXME: Remove when https://github.com/boostorg/core/issues/38 happens
#define BOOST_GIL_TEST_IS_CLOSE(a, b, epsilon) BOOST_TEST(std::abs((a) - (b)) < (epsilon))

namespace {
constexpr double HALF_PI = 1.57079632679489661923;
}

void test_matrix3x2_default_constructor()
{
    gil::matrix3x2<int> m1;
    BOOST_TEST(m1.a == 1);
    BOOST_TEST(m1.b == 0);
    BOOST_TEST(m1.c == 0);
    BOOST_TEST(m1.d == 1);
    BOOST_TEST(m1.e == 0);
    BOOST_TEST(m1.f == 0);
}

void test_matrix3x2_parameterized_constructor()
{
    gil::matrix3x2<int> m1(1, 2, 3, 4, 5, 6);
    BOOST_TEST(m1.a == 1);
    BOOST_TEST(m1.b == 2);
    BOOST_TEST(m1.c == 3);
    BOOST_TEST(m1.d == 4);
    BOOST_TEST(m1.e == 5);
    BOOST_TEST(m1.f == 6);
}

void test_matrix3x2_copy_constructor()
{
    gil::matrix3x2<int> m1(1, 2, 3, 4, 5, 6);
    gil::matrix3x2<int> m2(m1);
    BOOST_TEST(m2.a == 1);
    BOOST_TEST(m2.b == 2);
    BOOST_TEST(m2.c == 3);
    BOOST_TEST(m2.d == 4);
    BOOST_TEST(m2.e == 5);
    BOOST_TEST(m2.f == 6);
}

void test_matrix3x2_assignment_operator()
{
    gil::matrix3x2<int> m1(1, 2, 3, 4, 5, 6);
    gil::matrix3x2<int> m2;
    m2 = m1;
    BOOST_TEST(m2.a == 1);
    BOOST_TEST(m2.b == 2);
    BOOST_TEST(m2.c == 3);
    BOOST_TEST(m2.d == 4);
    BOOST_TEST(m2.e == 5);
    BOOST_TEST(m2.f == 6);
}

void test_matrix3x2_multiplication_assignment()
{
    gil::matrix3x2<int> m1;
    gil::matrix3x2<int> m2;
    m2 *= m1;
    BOOST_TEST(m2.a == 1);
    BOOST_TEST(m2.b == 0);
    BOOST_TEST(m2.c == 0);
    BOOST_TEST(m2.d == 1);
    BOOST_TEST(m2.e == 0);
    BOOST_TEST(m2.f == 0);

    gil::matrix3x2<int> m3(0, 0, 0, 0, 0, 0);
    m2 *= m3;
    BOOST_TEST(m2.a == 0);
    BOOST_TEST(m2.b == 0);
    BOOST_TEST(m2.c == 0);
    BOOST_TEST(m2.d == 0);
    BOOST_TEST(m2.e == 0);
    BOOST_TEST(m2.f == 0);
}

void test_matrix3x2_matrix3x2_multiplication()
{
    gil::matrix3x2<int> m1;
    gil::matrix3x2<int> m2(0, 0, 0, 0, 0, 0);
    gil::matrix3x2<int> m3;
    m3 = m1 * m2;
    BOOST_TEST(m3.a == 0);
    BOOST_TEST(m3.b == 0);
    BOOST_TEST(m3.c == 0);
    BOOST_TEST(m3.d == 0);
    BOOST_TEST(m3.e == 0);
    BOOST_TEST(m3.f == 0);
}

void test_matrix3x2_vector_multiplication()
{
    gil::matrix3x2<int> m1;
    gil::point<int> v1{2, 4};

    gil::point<int> v2 = v1 * m1;
    BOOST_TEST(v2.x == 2);
    BOOST_TEST(v2.y == 4);

    gil::point<int> v3 = gil::transform(m1, v1);
    BOOST_TEST(v3.x == 2);
    BOOST_TEST(v3.y == 4);
}

void test_matrix3x2_get_rotate()
{
    auto m1 = gil::matrix3x2<double>::get_rotate(HALF_PI);
    BOOST_GIL_TEST_IS_CLOSE(m1.a, std::cos(HALF_PI), 0.03);
    BOOST_TEST(m1.b == 1);
    BOOST_TEST(m1.c == -1);
    BOOST_GIL_TEST_IS_CLOSE(m1.d, std::cos(HALF_PI), 0.03);
    BOOST_TEST(m1.e == 0);
    BOOST_TEST(m1.f == 0);
}

void test_matrix3x2_get_scale()
{
    gil::matrix3x2<int> m1;
    m1 = gil::matrix3x2<int>::get_scale(2);
    BOOST_TEST(m1.a == 2);
    BOOST_TEST(m1.b == 0);
    BOOST_TEST(m1.c == 0);
    BOOST_TEST(m1.d == 2);
    BOOST_TEST(m1.e == 0);
    BOOST_TEST(m1.f == 0);
    m1 = gil::matrix3x2<int>::get_scale(2, 4);
    BOOST_TEST(m1.a == 2);
    BOOST_TEST(m1.d == 4);
    m1 = gil::matrix3x2<int>::get_scale(gil::point<int>{4, 8});
    BOOST_TEST(m1.a == 4);
    BOOST_TEST(m1.d == 8);
}

void test_matrix3x2_get_translate()
{
    gil::matrix3x2<int> m1;
    m1 = gil::matrix3x2<int>::get_translate(2, 4);
    BOOST_TEST(m1.a == 1);
    BOOST_TEST(m1.b == 0);
    BOOST_TEST(m1.c == 0);
    BOOST_TEST(m1.d == 1);
    BOOST_TEST(m1.e == 2);
    BOOST_TEST(m1.f == 4);
    m1 = gil::matrix3x2<int>::get_translate(gil::point<int>{4, 8});
    BOOST_TEST(m1.e == 4);
    BOOST_TEST(m1.f == 8);
}

void test_matrix3x2_transform()
{
    gil::matrix3x2<int> m1;
    gil::point<int> v1{2, 4};
    gil::point<int> v2 = gil::transform(m1, v1);
    BOOST_TEST(v2.x == 2);
    BOOST_TEST(v2.y == 4);
}

int main()
{
    test_matrix3x2_default_constructor();
    test_matrix3x2_parameterized_constructor();
    test_matrix3x2_copy_constructor();
    test_matrix3x2_assignment_operator();
    test_matrix3x2_multiplication_assignment();
    test_matrix3x2_matrix3x2_multiplication();
    test_matrix3x2_vector_multiplication();
    test_matrix3x2_get_rotate();
    test_matrix3x2_get_scale();
    test_matrix3x2_get_translate();
    test_matrix3x2_transform();

    return ::boost::report_errors();
}


//
// Copyright (c) 2020 INRIA
//

#include "pinocchio/bindings/python/algorithm/algorithms.hpp"
#include "pinocchio/algorithm/frames-derivatives.hpp"

#include <boost/python/tuple.hpp>

namespace pinocchio
{
  namespace python
  {
    namespace bp = boost::python;

    bp::tuple getFrameVelocityDerivatives_proxy(const Model & model,
                                                Data & data,
                                                const Model::FrameIndex frame_id,
                                                ReferenceFrame rf)
    {
      typedef Data::Matrix6x Matrix6x;

      Matrix6x partial_dq(Matrix6x::Zero(6,model.nv));
      Matrix6x partial_dv(Matrix6x::Zero(6,model.nv));

      getFrameVelocityDerivatives(model,data,frame_id,rf,
                                  partial_dq,partial_dv);

      return bp::make_tuple(partial_dq,partial_dv);
    }

    bp::tuple getFrameAccelerationDerivatives_proxy(const Model & model,
                                                    Data & data,
                                                    const Model::FrameIndex frame_id,
                                                    ReferenceFrame rf)
    {
      typedef Data::Matrix6x Matrix6x;

      Matrix6x v_partial_dq(Matrix6x::Zero(6,model.nv));
      Matrix6x a_partial_dq(Matrix6x::Zero(6,model.nv));
      Matrix6x a_partial_dv(Matrix6x::Zero(6,model.nv));
      Matrix6x a_partial_da(Matrix6x::Zero(6,model.nv));

      getFrameAccelerationDerivatives(model,data,frame_id,rf,
                                      v_partial_dq,a_partial_dq,
                                      a_partial_dv,a_partial_da);

      return bp::make_tuple(v_partial_dq,a_partial_dq,a_partial_dv,a_partial_da);
    }

    void exposeFramesDerivatives()
    {
      using namespace Eigen;

      bp::def("getFrameVelocityDerivatives",
              getFrameVelocityDerivatives_proxy,
              bp::args("model","data","frame_id","reference_frame"),
              "Computes the partial derivatives of the spatial velocity of a given frame with respect to\n"
              "the joint configuration and velocity and returns them as a tuple.\n"
              "The Jacobians can be either expressed in the LOCAL frame of the joint, in the LOCAL_WORLD_ALIGNED frame or in the WORLD coordinate frame depending on the value of reference_frame.\n"
              "You must first call computeForwardKinematicsDerivatives before calling this function.\n\n"
              "Parameters:\n"
              "\tmodel: model of the kinematic tree\n"
              "\tdata: data related to the model\n"
              "\tframe_id: index of the frame\n"
              "\treference_frame: reference frame in which the resulting derivatives are expressed\n");

      bp::def("getFrameAccelerationDerivatives",
              getFrameAccelerationDerivatives_proxy,
              bp::args("model","data","frame_id","reference_frame"),
              "Computes the partial derivatives of the spatial acceleration of a given frame with respect to\n"
              "the joint configuration, velocity and acceleration and returns them as a tuple.\n"
              "The Jacobians can be either expressed in the LOCAL frame of the joint, in the LOCAL_WORLD_ALIGNED frame or in the WORLD coordinate frame depending on the value of reference_frame.\n"
              "You must first call computeForwardKinematicsDerivatives before calling this function.\n\n"
              "Parameters:\n"
              "\tmodel: model of the kinematic tree\n"
              "\tdata: data related to the model\n"
              "\tframe_id: index of the frame\n"
              "\treference_frame: reference frame in which the resulting derivatives are expressed\n");

    }

  } // namespace python
} // namespace pinocchio


// Copyright © 2017-2021 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#include "Fee.h"

#include "Data.h"
#include "HexCoding.h"
#include "uint256.h"
// always includes nvp.hpp before cpp_bin_float.hpp
#include <boost/serialization/nvp.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>

#include <cmath>
#include <cstddef>
#include <vector>

using json = nlohmann::json;
using float128_t = boost::multiprecision::cpp_bin_float_quad;
using namespace std;

namespace TW::Ethereum::Fee {

static const uint256_t defaultTip = 2000000000;

auto samplingCurve(double sumWeight) -> double {
    static const double sampleMin = 0.1;
    static const double sampleMax = 0.3;

    if (sumWeight <= sampleMin) {
        return 0.0;
    }

    if (sumWeight >= sampleMax) {
        return 1.0;
    }

    return (1 - cos((sumWeight - sampleMin) * 2 * M_PI / (sampleMax - sampleMin))) / 2;
}

auto suggestTip(const json& feeHistory) -> uint256_t {

    if (!feeHistory.contains("reward")) {
        return defaultTip;
    }

    vector<uint256_t> rewards;

    for (auto& item : feeHistory["reward"]) {
        const auto hex = parse_hex(item[0], true);
        const auto reward = load(hex);
        if (reward > 0) {
            rewards.push_back(reward);
        }
    }

    if (rewards.empty()) {
        return defaultTip;
    }

    // return the median of the rewards
    sort(rewards.begin(), rewards.end());
    return rewards[rewards.size() / 2];
}

auto suggestBaseFee(vector<uint256_t> baseFees, vector<size_t> order, double timeFactor)
    -> uint256_t {
    if (timeFactor < 1e-6) {
        return baseFees.back();
    }

    double pendingWeight = (1 - exp(-1 / timeFactor)) / (1 - exp(-1 * double(baseFees.size()) / timeFactor));
    double sumWeight = 0.0;
    double samplingCurveLast = 0.0;
    float128_t result = 0;
    for (size_t i = 0; i < order.size(); i++) {
        sumWeight += pendingWeight * exp((double(order[i]) - baseFees.size() + 1) / timeFactor);
        double samplingCurveValue = samplingCurve(sumWeight);
        result += (samplingCurveValue - samplingCurveLast) * float128_t(baseFees[order[i]]);
        if (samplingCurveValue >= 1) {
            return uint256_t(boost::multiprecision::ceil(result));
        }
        samplingCurveLast = samplingCurveValue;
    }
    return uint256_t(boost::multiprecision::ceil(result));
}

auto suggestFee(const json& feeHistory) -> json {
    // tailored from:
    // https://github.com/zsfelfoldi/ethereum-docs/blob/master/eip1559/feeHistory_example.js
    vector<uint256_t> baseFees;
    vector<size_t> order;

    const auto& array = feeHistory["baseFeePerGas"];
    for (size_t i = 0; i < array.size(); i++) {
        const auto hex = parse_hex(array[i], true);
        baseFees.push_back(load(hex));
        order.push_back(i);
    }

    // The last (pending) block is also assumed to end up being full
    baseFees.back() = baseFees.back() * 9 / 8;

    const auto& gasRatio = feeHistory["gasUsedRatio"];
    for (size_t i = gasRatio.size() - 1; i > 0; i--) {
        if (i < gasRatio.size() && gasRatio[i] > 0.9) {
            baseFees[i] = baseFees[i + 1];
        }
    }

    std::sort(order.begin(), order.end(), [&baseFees](const size_t lhs, const size_t rhs) {
        return baseFees[lhs] > baseFees[rhs];
    });

    const auto baseFee = suggestBaseFee(baseFees, order, 15);
    const auto tip = suggestTip(feeHistory);

    return json{
        {"baseFee", toString(baseFee)},
        {"maxPriorityFee", toString(tip)},
    };
}

} // namespace TW::Ethereum::Fee


#define BOOST_AUTO_TEST_MAIN

#include <boost/test/included/unit_test.hpp>
#include <set>
#include "PrimeNumbers/PrimeNumbers.h"

BOOST_AUTO_TEST_SUITE(PrimeNumbersTests)

BOOST_AUTO_TEST_CASE(EmptySetFor0)
{
	std::set<std::size_t> primes = GeneratePrimeNumbersSet(0);
	BOOST_CHECK(primes.empty());
}

BOOST_AUTO_TEST_CASE(SetWithOneElement)
{
	std::set<std::size_t> primes = GeneratePrimeNumbersSet(2);
	BOOST_CHECK(primes.size() == 1);
	BOOST_CHECK(primes.find(2) != primes.end());
}

BOOST_AUTO_TEST_CASE(PrimesTo10)
{
	std::size_t expectedPrimesArray[] =
	{
		2, 3, 5, 7
	};
	const size_t NUMBERS_COUNT = sizeof(expectedPrimesArray) / sizeof(*expectedPrimesArray);
	std::set<std::size_t> expectedPrimes;
	expectedPrimes.insert(&expectedPrimesArray[0], &expectedPrimesArray[NUMBERS_COUNT]);

	std::set<std::size_t> primes = GeneratePrimeNumbersSet(10);
	BOOST_CHECK(expectedPrimes == primes);
}

BOOST_AUTO_TEST_SUITE_END()


//////////////////////////////////////////////////////////////////////////////////////
// distribution::toolkit::distributions::exponential::description.hpp                //
//                                                                                //
//  (C) Copyright 2009 Erwann Rogard                                                //
//  Use, modification and distribution are subject to the                           //
//  Boost Software License, Version 1.0. (See accompanying file                     //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)                //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_EXPONENTIAL_DESCRIPTION_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_EXPONENTIAL_DESCRIPTION_HPP_ER_2009
#include <string>
#include <boost/format.hpp>
#include <boost/math/distributions/exponential.hpp>

namespace boost{
namespace math{

    template<typename T,typename P>
    std::string
    description(
        const boost::math::exponential_distribution<T,P>& dist
    ){
        static const char* msg = "exponential(%1%)";
        boost::format f(msg); f%dist.lambda();
        return f.str();
    }

}// math
}// boost

#endif


#ifndef ITL_PC_BLOCK_DIAGONAL_INCLUDE
#define ITL_PC_BLOCK_DIAGONAL_INCLUDE

#include <boost/numeric/itl/pc/diagonal.hpp>

#include "solver/BlockMTLMatrix.h"

namespace itl
{
  namespace pc
  {

    /// Diagonal Preconditioner
    template <>
    class diagonal<AMDiS::BlockMTLMatrix, mtl::Collection<AMDiS::BlockMTLMatrix>::value_type>
    {
    public:
      typedef mtl::Collection<AMDiS::BlockMTLMatrix>::value_type   Value;
      typedef Value                                                value_type;
      typedef mtl::Collection<AMDiS::BlockMTLMatrix>::size_type    size_type;
      typedef diagonal                                             self;

      /// Constructor takes matrix reference
      explicit diagonal(const AMDiS::BlockMTLMatrix& A) : inv_diag(num_rows(A))
      {
        MTL_THROW_IF(num_rows(A) != num_cols(A), mtl::matrix_not_square());
        using math::reciprocal;

        for (size_t i = 0; i < A.n_rows; i++)
          inv_diag[A.getRowRange(i)] = mtl::matrix::diagonal(A.getSubMatrix(i,i));

        for (size_type i = 0; i < num_rows(A); ++i)
          inv_diag[i]= reciprocal(inv_diag[i]);
      }

      /// Member function solve, better use free function solve
      template <typename Vector>
      Vector solve(const Vector& x) const
      {
        Vector y(resource(x));
        solve(x, y);
        return y;
      }

      template <typename VectorIn, typename VectorOut>
      void solve(const VectorIn& x, VectorOut& y) const
      {
        y.checked_change_resource(x);
        MTL_THROW_IF(size(x) != size(inv_diag), mtl::incompatible_size());
        for (size_type i= 0; i < size(inv_diag); ++i)
          y[i]= inv_diag[i] * x[i];
      }

      /// Member function for solving adjoint problem, better use free function adjoint_solve
      template <typename Vector>
      Vector adjoint_solve(const Vector& x) const
      {
        Vector y(resource(x));
        adjoint_solve(x, y);
        return y;
      }

      template <typename VectorIn, typename VectorOut>
      void adjoint_solve(const VectorIn& x, VectorOut& y) const
      {
        using mtl::conj;
        y.checked_change_resource(x);
        MTL_THROW_IF(size(x) != size(inv_diag), mtl::incompatible_size());
        for (size_type i= 0; i < size(inv_diag); ++i)
          y[i]= conj(inv_diag[i]) * x[i];
      }

    protected:
      mtl::vector::dense_vector<value_type>    inv_diag;
    };

  }
} // namespace itl::pc

#endif // ITL_PC_DIAGONAL_INCLUDE


/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2008, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the Willow Garage nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

/* Author: Zachary Kingston */

#define BOOST_TEST_MODULE "ConstrainedPlanning"
#include <boost/test/unit_test.hpp>

#include <fstream>

#include <ompl/base/Constraint.h>
#include <ompl/base/ConstrainedSpaceInformation.h>
#include <ompl/base/spaces/constraint/ConstrainedStateSpace.h>
#include <ompl/base/spaces/constraint/AtlasStateSpace.h>
#include <ompl/base/spaces/constraint/TangentBundleStateSpace.h>
#include <ompl/base/spaces/constraint/ProjectedStateSpace.h>

#include "ompl/geometric/planners/kpiece/BKPIECE1.h"
#include "ompl/geometric/planners/kpiece/KPIECE1.h"
#include "ompl/geometric/planners/rrt/RRT.h"
#include "ompl/geometric/planners/rrt/RRTConnect.h"
#include "ompl/geometric/planners/est/EST.h"
#include "ompl/geometric/planners/est/BiEST.h"
#include "ompl/geometric/planners/prm/PRM.h"

#include "ompl/base/Planner.h"
#include "ompl/geometric/PathSimplifier.h"
#include "ompl/base/objectives/PathLengthOptimizationObjective.h"

using namespace ompl;
namespace ob = ompl::base;
namespace og = ompl::geometric;

static const double SOLUTION_TIME = 5.0;
static const bool VERBOSE = true;
static const unsigned int TRIALS = 10;

class Sphere : public ob::Constraint
{
public:
    Sphere() : ob::Constraint(3, 1)
    {
    }

    void function(const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::VectorXd> out) const override
    {
        out[0] = x.norm() - 1;
    }

    void jacobian(const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::MatrixXd> out) const override
    {
        out = x.transpose().normalized();
    }
};

class SphereProjection : public ob::ProjectionEvaluator
{
public:
    SphereProjection(const ob::StateSpacePtr &space) : ob::ProjectionEvaluator(space)
    {
    }

    unsigned int getDimension() const override
    {
        return 2;
    }

    void defaultCellSizes() override
    {
        cellSizes_.resize(2);
        cellSizes_[0] = 0.1;
        cellSizes_[1] = 0.1;
    }

    void project(const ob::State *state, Eigen::Ref<Eigen::VectorXd> projection) const override
    {
        auto &&x = *state->as<ob::ConstrainedStateSpace::StateType>();
        projection(0) = atan2(x[1], x[0]);
        projection(1) = acos(x[2]);
    }
};

bool isValid(const ob::State *state)
{
    auto &&x = *state->as<ob::ConstrainedStateSpace::StateType>();

    if (-0.80 < x[2] && x[2] < -0.6)
    {
        if (-0.05 < x[1] && x[1] < 0.05)
            return x[0] > 0;
        return false;
    }
    else if (-0.1 < x[2] && x[2] < 0.1)
    {
        if (-0.05 < x[0] && x[0] < 0.05)
            return x[1] < 0;
        return false;
    }
    else if (0.6 < x[2] && x[2] < 0.80)
    {
        if (-0.05 < x[1] && x[1] < 0.05)
            return x[0] < 0;
        return false;
    }

    return true;
}

enum SPACE_TYPE
{
    PJ,
    AT,
    TB
};

class TestPlanner
{
public:
    TestPlanner()
    {
        msg::setLogLevel(msg::LOG_ERROR);
    }

    virtual ~TestPlanner() = default;
    virtual ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) = 0;

    void setupSpace()
    {
        switch (type)
        {
            case PJ:
                OMPL_INFORM("Using Projection-Based State Space!");
                css = std::make_shared<ob::ProjectedStateSpace>(space, constraint);
                csi = std::make_shared<ob::ConstrainedSpaceInformation>(css);
                break;
            case AT:
                OMPL_INFORM("Using Atlas-Based State Space!");
                css = std::make_shared<ob::AtlasStateSpace>(space, constraint);
                csi = std::make_shared<ob::ConstrainedSpaceInformation>(css);
                break;
            case TB:
                OMPL_INFORM("Using Tangent Bundle-Based State Space!");
                css = std::make_shared<ob::TangentBundleStateSpace>(space, constraint);
                csi = std::make_shared<ob::TangentBundleSpaceInformation>(css);
                break;
        }

        css->setup();
    }

    void setStartAndGoalStates(ob::ProblemDefinitionPtr &pdef)
    {
        Eigen::VectorXd start(3), goal(3);
        start << 0, 0, -1;
        goal << 0, 0, 1;

        // Create start and goal states (poles of the sphere)
        ob::ScopedState<> sstart(css);
        ob::ScopedState<> sgoal(css);

        sstart->as<ob::ConstrainedStateSpace::StateType>()->copy(start);
        sgoal->as<ob::ConstrainedStateSpace::StateType>()->copy(goal);

        switch (type)
        {
            case AT:
            case TB:
                css->as<ob::AtlasStateSpace>()->anchorChart(sstart.get());
                css->as<ob::AtlasStateSpace>()->anchorChart(sgoal.get());
                break;
            default:
                break;
        }

        // Setup problem
        pdef->setStartAndGoalStates(sstart, sgoal, 1e-3);
    }

    bool testSphereEnv(enum SPACE_TYPE sp, double *time = nullptr, double *pathLength = nullptr)
    {
        type = sp;

        space = std::make_shared<ob::RealVectorStateSpace>(3);
        // Set bounds on the space.
        ob::RealVectorBounds bounds(3);
        bounds.setLow(-2);
        bounds.setHigh(2);
        space->as<ob::RealVectorStateSpace>()->setBounds(bounds);

        constraint = std::make_shared<Sphere>();

        setupSpace();

        csi->setStateValidityChecker(isValid);

        /* instantiate problem definition */
        auto pdef(std::make_shared<ob::ProblemDefinition>(csi));
        auto opt(std::make_shared<ob::PathLengthOptimizationObjective>(csi));
        /* make optimizing planners stop when any solution is found */
        opt->setCostThreshold(opt->infiniteCost());
        pdef->setOptimizationObjective(opt);

        /* instantiate motion planner */
        ob::PlannerPtr planner = newPlanner(csi);
        planner->setProblemDefinition(pdef);
        planner->setup();

        setStartAndGoalStates(pdef);

        planner->clear();
        pdef->clearSolutionPaths();

        /* start counting time */
        ompl::time::point startTime = ompl::time::now();

        if (planner->solve(SOLUTION_TIME))
        {
            ompl::time::duration elapsed = ompl::time::now() - startTime;
            if (time != nullptr)
                *time += ompl::time::seconds(elapsed);

            og::PathGeometric *path = static_cast<og::PathGeometric *>(pdef->getSolutionPath().get());

            /* make the solution more smooth */
            auto sm(std::make_shared<og::PathSimplifier>(csi));

            startTime = ompl::time::now();
            sm->simplify(*path, SOLUTION_TIME);
            elapsed = ompl::time::now() - startTime;
            if (pathLength != nullptr)
                *pathLength += path->length();
            if (time != nullptr)
                *time += ompl::time::seconds(elapsed);

            return true;
        }
        return false;
    }

    ob::StateSpacePtr space;
    ob::ConstraintPtr constraint;

    ob::ConstrainedStateSpacePtr css;
    ob::ConstrainedSpaceInformationPtr csi;
    enum SPACE_TYPE type;
};

class PlanTest
{
public:
    void runSphereTest(TestPlanner *p, enum SPACE_TYPE type, double *success, double *avgruntime, double *avglength)
    {
        double time = 0.0;
        double length = 0.0;
        int good = 0;
        int N = 10;

        for (int i = 0; i < N; ++i)
            if (p->testSphereEnv(type, &time, &length))
                good++;

        *success = 100.0 * (double)good / (double)N;
        *avgruntime = time / (double)N;
        *avglength = length / (double)N;

        if (verbose_)
        {
            printf("    Success rate: %f%%\n", *success);
            printf("    Average runtime: %f\n", *avgruntime);
            printf("    Average path length: %f\n", *avglength);
        }
    }

    template <typename T, SPACE_TYPE N>
    void runAllTests(double min_success, double max_avgtime)
    {
        TestPlanner *p = new T();

        double success = 0.0;
        double avgruntime = 0.0;
        double avglength = 0.0;

        if (verbose_)
            printf("\n========= Running sphere test\n\n");

        runSphereTest(p, N, &success, &avgruntime, &avglength);
        BOOST_CHECK(success >= min_success);
        BOOST_CHECK(avgruntime < max_avgtime);
        BOOST_CHECK(avglength < 8.0);

        delete p;
    }

protected:
    PlanTest()
    {
        verbose_ = VERBOSE;
    }

    bool verbose_;
};

class RRTTest : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto rrt(std::make_shared<og::RRT>(si));
        return rrt;
    }
};

class RRTConnectTest : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto rrt(std::make_shared<og::RRTConnect>(si));
        return rrt;
    }
};

class KPIECE1Test : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto kpiece(std::make_shared<og::KPIECE1>(si));

        kpiece->setProjectionEvaluator(std::make_shared<SphereProjection>(si->getStateSpace()));

        return kpiece;
    }
};

class BKPIECE1Test : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto kpiece(std::make_shared<og::BKPIECE1>(si));

        kpiece->setProjectionEvaluator(std::make_shared<SphereProjection>(si->getStateSpace()));

        return kpiece;
    }
};

class ESTTest : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto est(std::make_shared<og::EST>(si));
        return est;
    }
};

class BiESTTest : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto est(std::make_shared<og::BiEST>(si));
        return est;
    }
};

class PRMTest : public TestPlanner
{
protected:
    ob::PlannerPtr newPlanner(const ob::SpaceInformationPtr &si) override
    {
        auto prm(std::make_shared<og::PRM>(si));
        return prm;
    }
};

BOOST_FIXTURE_TEST_SUITE(MyPlanTestFixture, PlanTest)

#ifndef MACHINE_SPEED_FACTOR
#define MACHINE_SPEED_FACTOR 3.0
#endif

// define boost tests for a planner assuming the naming convention is followed
#define OMPL_PLANNER_TEST(Name, Type, MinSuccess, MaxAvgTime)                                                          \
    BOOST_AUTO_TEST_CASE(geometric_##Name##Type)                                                                       \
    {                                                                                \
        if (VERBOSE)                                                                                \
            printf("\n\n\n*****************************\nTesting %s %s ...\n", #Type, #Name);                          \
        runAllTests<Name##Test, Type>(MinSuccess, MaxAvgTime * MACHINE_SPEED_FACTOR);                                  \
        if (VERBOSE)                                                                                \
            printf("Done with %s.\n", #Name);                                                                          \
    }

OMPL_PLANNER_TEST(RRT, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(RRT, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(RRT, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(RRTConnect, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(RRTConnect, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(RRTConnect, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(KPIECE1, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(KPIECE1, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(KPIECE1, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(BKPIECE1, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(BKPIECE1, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(BKPIECE1, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(EST, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(EST, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(EST, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(BiEST, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(BiEST, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(BiEST, TB, 95.0, 1.0)

OMPL_PLANNER_TEST(PRM, PJ, 95.0, 1.0)
OMPL_PLANNER_TEST(PRM, AT, 95.0, 1.0)
OMPL_PLANNER_TEST(PRM, TB, 95.0, 1.0)

BOOST_AUTO_TEST_SUITE_END()


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

/* Test_General.cpp - A general test program that uses a mix of operations over four ciphertexts.
 */
#include <NTL/ZZ.h>
#include <NTL/BasicThreadPool.h>
#include <helib/helib.h>
#include <NTL/lzz_pXFactoring.h>

#include <cassert>
#include <cstdio>
#include <helib/ArgMap.h>
#include <helib/fhe_stats.h>
#include <helib/log.h>

NTL_CLIENT
using namespace helib;

//#define HELIB_DEBUG

#if 1
#include <helib/debugging.h>
#define debugCompare(ea,sk,p,c) {\
  PtxtArray pp(ea);\
  pp.decrypt(c, sk);\
  if (pp != p) { \
    std::cout << "oops:\n"; std::cout << p << "\n"; \
    std::cout << pp << "\n"; \
    exit(0); \
  }}
#else
#define debugCompare(ea,sk,p,c)
#endif

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

1. c1.multiplyBy(c0)
2. c0 += random constant
3. c2 *= random constant
4. tmp = c1
5. ea.shift(tmp, random amount in [-nSlots/2, nSlots/2])
6. c2 += tmp
7. ea.rotate(c2, random amount in [1-nSlots, nSlots-1])
8. c1.negate()
9. c3.multiplyBy(c2)
10. c0 -= c3

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

static bool noPrint = true;
static long special_bits;

void  TestIt(long R, long p, long r, long d, long c, long k, long w,
               long L, long m, const Vec<long>& gens, const Vec<long>& ords)
{
  char buffer[32];
  if (!noPrint) {
    std::cout << "\n\n******** TestIt" << (isDryRun()? "(dry run):" : ":");
    std::cout << " R=" << R
	      << ", p=" << p
	      << ", r=" << r
	      << ", d=" << d
	      << ", c=" << c
	      << ", k=" << k
	      << ", w=" << w
	      << ", L=" << L
	      << ", m=" << m
	      << ", gens=" << gens
	      << ", ords=" << ords
	      << endl;
  }
  vector<long> gens1, ords1;
  convert(gens1, gens);
  convert(ords1, ords);

  if (!noPrint) fhe_stats = true;

  Context context { ContextBuilder<BGV>()

      .m(m).p(p).r(r)
      .gens(gens1).ords(ords1)
      .bits(L).c(c).bitsInSpecialPrimes(special_bits)

  };

#if 0
  // This works in C++17, but not earlier, as it then
  // requires a copy or move constructor
  Context context =
    ContextBuilder<BGV>()
      .m(m).p(p).r(r)
      .gens(gens1).ords(ords1)
      .bits(L).c(c).bitsInSpecialPrimes(special_bits);
#endif

#if 0
  Context context(m, p, r, gens1, ords1);
  buildModChain(context, L, c,
    /*willBeBootstrappable=*/false,
    /*t=*/0,
    /*resolution=*/3,
    /*bitsInSpecialPrimes=*/special_bits);
#endif

  ZZX G;
  if (d == 0)
    G = context.alMod.getFactorsOverZZ()[0];
  else
    G = makeIrredPoly(p, d);

  if (!noPrint) {
    context.zMStar.printout();
    std::cout << endl;

    std::cout << "security=" << context.securityLevel()<<endl;
    std::cout << "# small primes = " << context.smallPrimes.card() << "\n";
    std::cout << "# ctxt primes = " << context.ctxtPrimes.card() << "\n";
    std::cout << "# bits in ctxt primes = "
	 << long(context.logOfProduct(context.ctxtPrimes)/log(2.0) + 0.5) << "\n";
    std::cout << "# special primes = " << context.specialPrimes.card() << "\n";
    std::cout << "# bits in special primes = "
	 << long(context.logOfProduct(context.specialPrimes)/log(2.0) + 0.5) << "\n";
    std::cout << "G = " << G << "\n";

  }

  SecKey secretKey(context);
  const PubKey& publicKey = secretKey;
  secretKey.GenSecKey(); // A +-1/0 secret key
  addSome1DMatrices(secretKey); // compute key-switching matrices that we need

  std::shared_ptr<EncryptedArray> ea_ptr(std::make_shared<EncryptedArray>(context, G));
  // Alias to avoid issues with previous code
  EncryptedArray& ea = *ea_ptr;
  long nslots = ea.size();
#ifdef HELIB_DEBUG
  dbgKey = &secretKey;
  dbgEa  = ea_ptr;
#endif

  PtxtArray p0(ea), p1(ea), p2(ea), p3(ea);

  p0.random();
  p1.random();
  p2.random();
  p3.random();

  Ctxt c0(publicKey), c1(publicKey), c2(publicKey), c3(publicKey);
  p0.encrypt(c0);
  p1.encrypt(c1);
  p2.encrypt(c2);
  p3.encrypt(c3);

  resetAllTimers();

  HELIB_NTIMER_START(Circuit);

  for (long i = 0; i < R; i++) {

    if (!noPrint) std::cout << "*** round " << i << "..."<<endl;

     long shamt = RandomBnd(2*(nslots/2) + 1) - (nslots/2);
                  // random number in [-nslots/2..nslots/2]
     long rotamt = RandomBnd(2*nslots - 1) - (nslots - 1);
                  // random number in [-(nslots-1)..nslots-1]

     // two random constants
     PtxtArray const1(ea), const2(ea);
     const1.random();
     const2.random();

     p1 *= p0;     // c1.multiplyBy(c0)
     c1.multiplyBy(c0);
     if (!noPrint) CheckCtxt(c1, "c1*=c0");
     debugCompare(ea,secretKey,p1,c1);

     p0 += const1; // c0 += random constant
     c0.addConstant(const1);
     if (!noPrint) CheckCtxt(c0, "c0+=k1");
     debugCompare(ea,secretKey,p0,c0);

     p2 *= const2; // c2 *= random constant
     c2.multByConstant(const2);
     if (!noPrint) CheckCtxt(c2, "c2*=k2");
     debugCompare(ea,secretKey,p2,c2);

#if 0
     p2 *= 3L;
     c2 *= 3L;
     if (!noPrint) CheckCtxt(c2, "c2*=3");
     debugCompare(ea,secretKey,p2,c2);

     p2 += 3L;
     c2 += 3L;
     if (!noPrint) CheckCtxt(c2, "c2+=3");
     debugCompare(ea,secretKey,p2,c2);

#endif

     PtxtArray tmp_p(p1); // tmp = c1
     Ctxt tmp(c1);
     sprintf(buffer, "tmp=c1>>=%d", (int)shamt);
     shift(tmp_p, shamt); // ea.shift(tmp, random amount in [-nSlots/2,nSlots/2])
     ea.shift(tmp, shamt);
     if (!noPrint) CheckCtxt(tmp, buffer);
     debugCompare(ea,secretKey,tmp_p,tmp);

     p2 += tmp_p;  // c2 += tmp
     c2 += tmp;
     if (!noPrint) CheckCtxt(c2, "c2+=tmp");
     debugCompare(ea,secretKey,p2,c2);

     sprintf(buffer, "c2>>>=%d", (int)rotamt);
     rotate(p2, rotamt); // ea.rotate(c2, random amount in [1-nSlots, nSlots-1])
     ea.rotate(c2, rotamt);
     if (!noPrint) CheckCtxt(c2, buffer);
     debugCompare(ea,secretKey,p2,c2);

     p1.negate(); // c1.negate()
     c1.negate();
     if (!noPrint) CheckCtxt(c1, "c1=-c1");
     debugCompare(ea,secretKey,p1,c1);

     p3 *= p2; // c3.multiplyBy(c2)
     c3.multiplyBy(c2);
     if (!noPrint) CheckCtxt(c3, "c3*=c2");
     debugCompare(ea,secretKey,p3,c3);

     p0 -= p3; // c0 -= c3
     c0 -= c3;
     if (!noPrint) CheckCtxt(c0, "c0=-c3");
     debugCompare(ea,secretKey,p0,c0);

  }

  c0.cleanUp();
  c1.cleanUp();
  c2.cleanUp();
  c3.cleanUp();

  HELIB_NTIMER_STOP(Circuit);

  if (!noPrint) {
    std::cout << endl;
    printAllTimers();
    std::cout << endl;
  }
  resetAllTimers();
  HELIB_NTIMER_START(Check);

  PtxtArray pp0(ea);
  PtxtArray pp1(ea);
  PtxtArray pp2(ea);
  PtxtArray pp3(ea);

  pp0.decrypt(c0, secretKey);
  pp1.decrypt(c1, secretKey);
  pp2.decrypt(c2, secretKey);
  pp3.decrypt(c3, secretKey);

  if (pp0 == p0 && pp1 == p1
      && pp2 == p2 && pp3 == p3)
       std::cout << "GOOD\n";
  else std::cout << "BAD\n";

  HELIB_NTIMER_STOP(Check);

  if (!noPrint) {
    printAllTimers();
    print_stats(cout);
    std::cout << endl;
  }
}

/* A general test program that uses a mix of operations over four ciphertexts.
 * Usage: Test_General_x [ name=value ]...
 *   R       number of rounds  [ default=1 ]
 *   p       plaintext base  [ default=2 ]
 *   r       lifting  [ default=1 ]
 *   d       degree of the field extension  [ default=1 ]
 *              d == 0 => factors[0] defines extension
 *   c       number of columns in the key-switching matrices  [ default=2 ]
 *   k       security parameter  [ default=80 ]
 *   L       # of bits in the modulus chain  [ default=heuristic ]
 *   s       minimum number of slots  [ default=0 ]
 *   repeat  number of times to repeat the test  [ default=1 ]
 *   m       use specified value as modulus
 *   mvec    use product of the integers as  modulus
 *              e.g., mvec='[5 3 187]' (this overwrite the m argument)
 *   gens    use specified vector of generators
 *              e.g., gens='[562 1871 751]'
 *   ords    use specified vector of orders
 *              e.g., ords='[4 2 -4]', negative means 'bad'
 */
int main(int argc, char **argv)
{
  helog.setLogToStderr();

  setTimersOn();

  ArgMap amap;

  bool dry=false;
  amap.arg("dry", dry, "dry=1 for a dry-run");

  long R=1;
  amap.arg("R", R, "number of rounds");

  long p=2;
  amap.arg("p", p, "plaintext base");

  long r=1;
  amap.arg("r", r,  "lifting");

  long d=1;
  amap.arg("d", d, "degree of the field extension");
  amap.note("d == 0 => factors[0] defines extension");

  long c=2;
  amap.arg("c", c, "number of columns in the key-switching matrices");

  long k=80;
  amap.arg("k", k, "security parameter");

  long L=500;
  amap.arg("L", L, "# of bits in the modulus chain");

  long s=0;
  amap.arg("s", s, "minimum number of slots");

  long repeat=1;
  amap.arg("repeat", repeat,  "number of times to repeat the test");

  long chosen_m=0;
  amap.arg("m", chosen_m, "use specified value as modulus", nullptr);

  Vec<long> mvec;
  amap.arg("mvec", mvec, "use product of the integers as  modulus", nullptr);
  amap.note("e.g., mvec='[5 3 187]' (this overwrite the m argument)");

  Vec<long> gens;
  amap.arg("gens", gens, "use specified vector of generators", nullptr);
  amap.note("e.g., gens='[562 1871 751]'");

  Vec<long> ords;
  amap.arg("ords", ords, "use specified vector of orders", nullptr);
  amap.note("e.g., ords='[4 2 -4]', negative means 'bad'");

  long seed=0;
  amap.arg("seed", seed, "PRG seed");

  long nt=1;
  amap.arg("nt", nt, "num threads");

  amap.arg("noPrint", noPrint, "suppress printouts");

  amap.arg("special_bits", special_bits, "# of bits in special primes");

  amap.parse(argc, argv);

  SetSeed(ZZ(seed));
  SetNumThreads(nt);

  long w = 64; // Hamming weight of secret key

  if (mvec.length()>0)
    chosen_m = computeProd(mvec);
  long m = FindM(k, L, c, p, d, s, chosen_m, !noPrint);

  setDryRun(dry);
  for (long repeat_cnt = 0; repeat_cnt < repeat; repeat_cnt++) {
    TestIt(R, p, r, d, c, k, w, L, m, gens, ords);
  }
}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
// Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France.

// This file was modified by Oracle on 2015.
// Modifications copyright (c) 2015, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_SEGMENT_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_SEGMENT_HPP

#include <boost/mpl/assert.hpp>
#include <boost/type_traits/is_same.hpp>

#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/tags.hpp>

#include <boost/geometry/algorithms/detail/envelope/box.hpp>
#include <boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp>
#include <boost/geometry/algorithms/detail/envelope/segment.hpp>

#include <boost/geometry/algorithms/detail/expand/indexed.hpp>

#include <boost/geometry/algorithms/dispatch/expand.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace expand
{

struct segment_on_sphere
{
    template <typename Box, typename Segment>
    static inline void apply(Box& box, Segment const& segment)
    {
        Box mbrs[2];

        // compute the envelope of the segment
        detail::envelope::envelope_segment_on_sphere
            <
                dimension<Segment>::value
            >::apply(segment, mbrs[0]);

        // normalize the box
        detail::envelope::envelope_box_on_spheroid::apply(box, mbrs[1]);

        // compute the envelope of the two boxes
        detail::envelope::envelope_range_of_boxes::apply(mbrs, box);
    }
};

}} // namespace detail::expand
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename Box, typename Segment,
    typename StrategyLess, typename StrategyGreater,
    typename CSTagOut, typename CSTag
>
struct expand
    <
        Box, Segment,
        StrategyLess, StrategyGreater,
        box_tag, segment_tag,
        CSTagOut, CSTag
    > : detail::expand::expand_indexed
        <
            0, dimension<Segment>::value, StrategyLess, StrategyGreater
        >
{
    BOOST_MPL_ASSERT_MSG((boost::is_same<CSTagOut, CSTag>::value),
                         COORDINATE_SYSTEMS_MUST_BE_THE_SAME,
                         (types<CSTagOut, CSTag>()));
};

template
<
    typename Box, typename Segment,
    typename StrategyLess, typename StrategyGreater
>
struct expand
    <
        Box, Segment,
        StrategyLess, StrategyGreater,
        box_tag, segment_tag,
        spherical_equatorial_tag, spherical_equatorial_tag
    > : detail::expand::segment_on_sphere
{};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_SEGMENT_HPP


#include <fstream>
#include <memory>

#include <Eigen/Core>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <maplab-common/test/testing-entrypoint.h>
#include <maplab-common/test/testing-predicates.h>
#include <simulation/generic-path-generator.h>

#include "imu-integrator/imu-integrator.h"

using namespace imu_integrator;  // NOLINT

double getGravityMagnitude() {
  return 9.81;
}

class PosegraphErrorTerms : public ::testing::Test {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

 protected:
  virtual void SetUp() {
    settings_.imu_sigmas.acc_bias_random_walk_noise_density = 0;
    settings_.imu_sigmas.acc_noise_density = 0;
    settings_.gravity_meter_by_second2 = getGravityMagnitude();
    settings_.imu_sigmas.gyro_bias_random_walk_noise_density = 0;
    settings_.imu_sigmas.gyro_noise_density = 0;
    settings_.imu_noise_bias_seed = 10;
    settings_.mode = test_trajectory_gen::Path::kCircular;
    settings_.num_of_path_constraints = 8;
    settings_.sampling_time_second = 0.001;
    settings_.gravity_meter_by_second2 = getGravityMagnitude();
    delta_time_seconds_ = 0.0;
  }

  void constructIntegrator() {
    integrator_ = std::shared_ptr<ImuIntegratorRK4>(
        new ImuIntegratorRK4(
            settings_.imu_sigmas.gyro_noise_density,
            settings_.imu_sigmas.gyro_bias_random_walk_noise_density,
            settings_.imu_sigmas.acc_noise_density,
            settings_.imu_sigmas.acc_bias_random_walk_noise_density,
            settings_.gravity_meter_by_second2));
  }

  void integrate() {
    integrator_->integrate(
        current_state_, debiased_imu_readings_, delta_time_seconds_,
        &next_state_, &next_phi_, &next_cov_);
  }

  std::shared_ptr<ImuIntegratorRK4> integrator_;

  // Struct contains settings for imu integrator and path generator.
  test_trajectory_gen::PathAndLandmarkSettings settings_;

  // Imu integrator integrate() arguments.
  Eigen::Matrix<double, kStateSize, 1> current_state_;
  Eigen::Matrix<double, 2 * kImuReadingSize, 1>
      debiased_imu_readings_;
  Eigen::Matrix<double, kStateSize, 1> next_state_;
  Eigen::Matrix<double, kErrorStateSize, kErrorStateSize>
      next_phi_;
  Eigen::Matrix<double, kErrorStateSize, kErrorStateSize>
      next_cov_;
  double delta_time_seconds_;
};

TEST_F(PosegraphErrorTerms, ImuIntegratorTrajectory4d) {
  test_trajectory_gen::GenericPathGenerator path_generator(settings_);
  path_generator.generatePath();

  const Eigen::Matrix4Xd& true_G_q_B = path_generator.getTrueRotations();
  const Eigen::Matrix3Xd& true_positions = path_generator.getTruePositions();
  const Eigen::Matrix<double, 6, Eigen::Dynamic>& imu_data =
      path_generator.getImuData();

  constructIntegrator();

  current_state_.setZero();
  // Please note that state contains B_q_G quaternion (it simplifies Jacobians).
  current_state_.block<kStateOrientationBlockSize, 1>(
      kStateOrientationOffset, 0)
      << 0,
      0, 0, 1;
  current_state_.block<kPositionBlockSize, 1>(
      kStatePositionOffset, 0)
      << 10,
      0, 0;

  delta_time_seconds_ = settings_.sampling_time_second;

  std::ofstream traj_file;
  traj_file.open("traj.csv");

  for (int i = 0; i < (imu_data.cols() - 1); ++i) {
    debiased_imu_readings_.block<kImuReadingSize, 1>(0, 0) =
        imu_data.col(i);
    debiased_imu_readings_.block<kImuReadingSize, 1>(
        kImuReadingSize, 0) = imu_data.col(i + 1);

    integrate();

    current_state_ = next_state_;

    Eigen::Vector3d position = current_state_.block<3, 1>(13, 0);
    Eigen::Quaterniond B_q_G(current_state_.block<4, 1>(0, 0).data());
    Eigen::Quaterniond G_q_B(true_G_q_B.col(i + 1).data());
    EXPECT_NEAR_EIGEN(position, true_positions.col(i + 1), 1e-5);
    EXPECT_NEAR_KINDR_QUATERNION(
        pose::Quaternion(B_q_G), pose::Quaternion(G_q_B.inverse()), 1e-6);

    traj_file << current_state_(13, 0) << ", " << current_state_(14, 0) << ", "
              << current_state_(15, 0) << ", " << true_positions(0, i + 1)
              << ", " << true_positions(1, i + 1) << ", "
              << true_positions(2, i + 1) << std::endl;
  }
  traj_file.close();
}

MAPLAB_UNITTEST_ENTRYPOINT


#include <Eigen/Dense>
#include <vector>
#include <fstream>
#include <iostream>
#include <cmath>

#include "../include/KF.h"
#include "../include/dsho.h"
#include "../include/matern32.h"
#include "../include/ndsho.h"

using namespace Eigen;
using namespace std;

#define TWOPI 6.283185307179586

// function to read a space separated file

int main() {

  std::random_device rd;
  std::mt19937 rng(rd());
  std::normal_distribution<double> gaussian(0.0,0.05);

  int vecsize = 10000;
	// map data to VectorXds
	VectorXd times(vecsize), yi(vecsize);

  double omega0 = TWOPI;

  for (int j=0; j<times.size(); j++){
      times(j) = 0.001*double(j);
      yi(j) = sin(omega0 * times(j)) + gaussian(rng);
  }

	// this is the observational error vector
	VectorXd yerr = VectorXd::Ones(yi.size());
	yerr.array() *= 0.05;

	VectorXd y_sim(vecsize);

	std::ofstream file("GPtest_sin.txt");
	assert(file.is_open());
	//file << "# Sin simulated light curve" << endl;
	//file << "# input parameters: " << endl;
	//file << "# omega: " << omega0 << endl;
	//file << "# time y_sim" << endl;
	for(int i=0; i<times.rows(); i++)
		file <<  times(i) <<" "<< yi(i) << endl;

  for (int j=0; j<times.size(); j++){
    yi(j) += sin((omega0 + 1) * times(j));
  }
	std::ofstream file2("GPtest_dsin.txt");
	assert(file2.is_open());
	for(int i=0; i<times.rows(); i++)
		file2 <<  times(i) <<" "<< yi(i) << endl;

}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file portfolio/builders/equityoption.hpp
    \brief
    \ingroup builders
*/

#pragma once

#include <boost/make_shared.hpp>
#include <ored/portfolio/builders/cachingenginebuilder.hpp>
#include <ored/portfolio/enginefactory.hpp>
#include <ored/utilities/log.hpp>
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
#include <ql/processes/blackscholesprocess.hpp>
#include <ql/version.hpp>

namespace ore {
namespace data {

//! Engine Builder for European Equity Options
/*! Pricing engines are cached by equity/currency

    \ingroup builders
 */
class EquityOptionEngineBuilder : public CachingPricingEngineBuilder<string, const string&, const Currency&> {
public:
    EquityOptionEngineBuilder()
        : CachingEngineBuilder("BlackScholesMerton", "AnalyticEuropeanEngine", {"EquityOption"}) {}

protected:
    virtual string keyImpl(const string& equityName, const Currency& ccy) override {
        return equityName + "/" + ccy.code();
    }

    virtual boost::shared_ptr<PricingEngine> engineImpl(const string& equityName, const Currency& ccy) override {
        string key = keyImpl(equityName, ccy);
        boost::shared_ptr<GeneralizedBlackScholesProcess> gbsp = boost::make_shared<GeneralizedBlackScholesProcess>(
            market_->equitySpot(equityName, configuration(MarketContext::pricing)),
            market_->equityDividendCurve(equityName,
                                         configuration(MarketContext::pricing)), // dividend yield ~ foreign yield
            market_->equityForecastCurve(equityName, configuration(MarketContext::pricing)),
            market_->equityVol(equityName, configuration(MarketContext::pricing)));
        // separate IR curves required for "discounting" and "forward price estimation"
        Handle<YieldTermStructure> discountCurve =
            market_->discountCurve(ccy.code(), configuration(MarketContext::pricing));

        return boost::make_shared<QuantLib::AnalyticEuropeanEngine>(gbsp, discountCurve);
    }
};

} // namespace data
} // namespace ore


//////////////////////////////////////////////////////////////////////////////////
// statistics::survival::data::data::mngr::default_covariates_prior_model.hpp   //
//                                                                              //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                   //
//  Software License, Version 1.0. (See accompanying file                       //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)            //
//////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_SURVIVAL_DATA_DATA_MNGR_DEFAULT_COVARIATES_PRIOR_MODEL_HPP_ER_2009
#define BOOST_STATISTICS_SURVIVAL_DATA_DATA_MNGR_DEFAULT_COVARIATES_PRIOR_MODEL_HPP_ER_2009
#include <boost/statistics/model/wrap/aggregate/prior_model.hpp>
#include <boost/statistics/survival/data/data/mngr/default_covariates_model.hpp>

namespace boost{
namespace statistics{
namespace survival{
namespace data{

    template<typename X,typename D,typename M>
    class default_covariates_prior_model_mngr
        : public default_covariates_model_mngr<X,M>
    {
        typedef default_covariates_model_mngr<X,M> super_;
        public:
        typedef statistics::model::prior_model_<D,M> prior_model_wrapper_type;

        // [ Construction ]
        default_covariates_prior_model_mngr();
        template<typename It>
        default_covariates_prior_model_mngr(
            It b_x,It e_x,      // x values
            prior_model_wrapper_type
        );

        // [ Access ]
        prior_model_wrapper_type
        prior_model_wrapper()const;

        protected:
        D p_;
    };

    template<typename X,typename D,typename M>
    default_covariates_prior_model_mngr<X,D,M>
        ::default_covariates_prior_model_mngr(){}

    template<typename X,typename D,typename M>
    template<typename It>
    default_covariates_prior_model_mngr<X,D,M>
            ::default_covariates_prior_model_mngr
    (
        It b_x,It e_x,      // x values
        prior_model_wrapper_type pm
    ):super_(b_x,e_x,pm),p_(pm.prior()){}

    template<typename X,typename D,typename M>
    typename
        default_covariates_prior_model_mngr<X,D,M>::prior_model_wrapper_type
    default_covariates_prior_model_mngr<X,D,M>::prior_model_wrapper()const
    {
        typedef statistics::model::prior_wrapper<D> prior_;
        return prior_model_wrapper_type(
            prior_(this->p_),
            this->model_wrapper()
        );
    }

}// data
}// survival
}// statistics
}// boost

#endif


/*
 Copyright (C) 2018 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include "toplevelfixture.hpp"
#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <ql/currencies/all.hpp>
#include <ql/indexes/ibor/gbplibor.hpp>
#include <ql/indexes/ibor/usdlibor.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/termstructures/yield/discountcurve.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/time/calendars/all.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/types.hpp>
#include <qle/pricingengines/crossccyswapengine.hpp>
#include <qle/termstructures/crossccybasismtmresetswaphelper.hpp>

using namespace std;
using namespace boost::unit_test_framework;
using namespace QuantLib;
using namespace QuantExt;

namespace {

struct CommonVars {

    Date asof;
    Natural settlementDays;
    Calendar domesticCalendar, foreignCalendar, payCalendar;
    BusinessDayConvention payConvention;
    Natural payLag;
    Period tenor;
    Currency foreignCurrency, domesticCurrency;
    DayCounter dayCount;
    Real foreignNominal;
    boost::shared_ptr<SimpleQuote> spotFxQuote;
    boost::shared_ptr<SimpleQuote> spreadQuote;
    Handle<YieldTermStructure> domesticProjCurve;
    Handle<YieldTermStructure> domesticDiscCurve;
    Handle<YieldTermStructure> foreignProjCurve;
    boost::shared_ptr<IborIndex> domesticIndex;
    boost::shared_ptr<IborIndex> foreignIndex;
    boost::shared_ptr<CrossCcyBasisMtMResetSwapHelper> helper;

    CommonVars() {
        asof = Date(11, Sep, 2018);
        settlementDays = 2;
        domesticCalendar = UnitedStates();
        foreignCalendar = UnitedKingdom();
        payCalendar = JointCalendar(domesticCalendar, foreignCalendar);
        payConvention = Following;
        payLag = 0;
        tenor = 5 * Years;
        domesticCurrency = USDCurrency();
        foreignCurrency = GBPCurrency();
        dayCount = Actual360();
        foreignNominal = 10000000.0;
        spotFxQuote = boost::make_shared<SimpleQuote>(1.2);
        spreadQuote = boost::make_shared<SimpleQuote>(-0.0015);

        // curves
        domesticProjCurve =
            Handle<YieldTermStructure>(boost::make_shared<FlatForward>(0, domesticCalendar, 0.02, Actual365Fixed()));
        foreignProjCurve =
            Handle<YieldTermStructure>(boost::make_shared<FlatForward>(0, foreignCalendar, 0.03, Actual365Fixed()));
        domesticDiscCurve =
            Handle<YieldTermStructure>(boost::make_shared<FlatForward>(0, domesticCalendar, 0.01, Actual365Fixed()));

        // indices
        foreignIndex = boost::make_shared<GBPLibor>(3 * Months, foreignProjCurve);
        domesticIndex = boost::make_shared<USDLibor>(3 * Months, domesticProjCurve);
    }
};

boost::shared_ptr<CrossCcyBasisMtMResetSwap> makeTestSwap(const CommonVars& vars,
                                                          const Handle<YieldTermStructure>& foreignDiscCurve) {
    // Swap schedule
    Date referenceDate = Settings::instance().evaluationDate();
    referenceDate = vars.payCalendar.adjust(referenceDate);
    Date start = vars.payCalendar.advance(referenceDate, vars.settlementDays * Days);
    Date end = start + vars.tenor;
    Schedule schedule(start, end, 3 * Months, vars.payCalendar, vars.payConvention, vars.payConvention,
                      DateGeneration::Backward, false);
    // Create swap
    boost::shared_ptr<FxIndex> fxIndex = boost::make_shared<FxIndex>(
        "dummy", vars.settlementDays, vars.foreignCurrency, vars.domesticCurrency, vars.payCalendar,
        Handle<Quote>(vars.spotFxQuote), foreignDiscCurve, vars.domesticDiscCurve);
    boost::shared_ptr<CrossCcyBasisMtMResetSwap> swap(new CrossCcyBasisMtMResetSwap(
        vars.foreignNominal, vars.foreignCurrency, schedule, vars.foreignIndex, vars.spreadQuote->value(),
        vars.domesticCurrency, schedule, vars.domesticIndex, 0.0, fxIndex, false));
    // Attach pricing engine
    boost::shared_ptr<PricingEngine> engine =
        boost::make_shared<CrossCcySwapEngine>(vars.domesticCurrency, vars.domesticDiscCurve, vars.foreignCurrency,
                                               foreignDiscCurve, Handle<Quote>(vars.spotFxQuote));
    swap->setPricingEngine(engine);
    return swap;
}

// Use helper to build bootstrapped curve
Handle<YieldTermStructure> bootstrappedCurve(CommonVars& vars) {

    // Create a helper
    vector<boost::shared_ptr<RateHelper> > helpers(1);
    vars.helper.reset(new CrossCcyBasisMtMResetSwapHelper(
        Handle<Quote>(vars.spreadQuote), Handle<Quote>(vars.spotFxQuote), vars.settlementDays, vars.payCalendar,
        vars.tenor, vars.payConvention, vars.foreignIndex, vars.domesticIndex, Handle<YieldTermStructure>(),
        vars.domesticDiscCurve));
    helpers[0] = vars.helper;

    // Build yield curve referencing the helper
    return Handle<YieldTermStructure>(
        boost::make_shared<PiecewiseYieldCurve<Discount, LogLinear> >(0, NullCalendar(), helpers, Actual365Fixed()));
}
} // namespace

BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(CrossCcyBasisMtMResetSwapHelperTest)

BOOST_AUTO_TEST_CASE(testBootstrap) {

    BOOST_TEST_MESSAGE("Test simple bootstrap against cross currency MtM resetting swap");

    SavedSettings backup;
    CommonVars vars;
    Settings::instance().evaluationDate() = vars.asof;

    // Create bootstrapped curve
    Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);

    // Create the helper swap manually and price it using curve bootstrapped from helper
    boost::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);

    // Swap should have NPV = 0.0.
    Real tol = 1e-5;
    BOOST_CHECK_SMALL(swap->NPV(), tol);

    // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
    Real relTol = 1e-10;
    BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);

    // Check the 5Y discount factor
    DiscountFactor expDisc = 0.91155524911218166;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);
}

BOOST_AUTO_TEST_CASE(testSpotFxChange) {

    BOOST_TEST_MESSAGE("Test rebootstrap under spot FX change");

    SavedSettings backup;

    CommonVars vars;

    Settings::instance().evaluationDate() = vars.asof;

    // Create a helper and bootstrapped curve
    Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);

    // Create the helper swap manually and price it using curve bootstrapped from helper
    boost::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);

    // Check NPV = 0.0
    Real absTol = 1e-5;
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
    Real relTol = 1e-10;
    BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);

    // Check the 5Y discount factor
    DiscountFactor expDisc = 0.91155524911218166;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Check the nominal of the helper swap
    BOOST_CHECK_CLOSE(vars.spotFxQuote->value(), std::fabs(vars.helper->swap()->leg(2).front()->amount()), relTol);

    // Bump the spot rate by 10%
    vars.spotFxQuote->setValue(vars.spotFxQuote->value() * 1.1);

    // Build a new swap using the updated spot FX rate
    swap = makeTestSwap(vars, discCurve);

    // Check that the new swap's NPV is 0.0
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check the 5Y discount factor again. It should be the same.
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Check the nominal of the helper swap. Should now be the bumped amount
    BOOST_CHECK_CLOSE(vars.spotFxQuote->value(), std::fabs(vars.helper->swap()->leg(2).front()->amount()), relTol);
}

BOOST_AUTO_TEST_CASE(testSpreadChange) {

    BOOST_TEST_MESSAGE("Test rebootstrap under helper spread change");

    SavedSettings backup;

    CommonVars vars;

    Settings::instance().evaluationDate() = vars.asof;

    // Create a helper and bootstrapped curve
    Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);

    // Create the helper swap manually and price it using curve bootstrapped from helper
    boost::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);

    // Check NPV = 0.0
    Real absTol = 1e-5;
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
    Real relTol = 1e-10;
    BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);

    // Check the 5Y discount factor
    DiscountFactor expDisc = 0.91155524911218166;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Add a 10bps spread
    vars.spreadQuote->setValue(0.0015);

    // Build a new swap using the updated spread of 10bps
    swap = makeTestSwap(vars, discCurve);

    // Check that the new swap's NPV is 0.0
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check the 5Y discount factor again. More spread on foreign leg => more significant discount factor.
    expDisc = 0.89807807922008731;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Check the spread of the helper swap. Should now be 15bps.
    BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);
}

BOOST_AUTO_TEST_CASE(testMovingEvaluationDate) {

    BOOST_TEST_MESSAGE("Test rebootstrap after moving evaluation date");

    SavedSettings backup;

    CommonVars vars;

    Settings::instance().evaluationDate() = vars.asof;

    // Create a helper and bootstrapped curve
    Handle<YieldTermStructure> discCurve = bootstrappedCurve(vars);

    // Create the helper swap manually and price it using curve bootstrapped from helper
    boost::shared_ptr<CrossCcyBasisMtMResetSwap> swap = makeTestSwap(vars, discCurve);

    // Check NPV = 0.0
    Real absTol = 1e-5;
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check fair spreads match. Bootstrap uses 1e-12 accuracy.
    Real relTol = 1e-10;
    BOOST_CHECK_CLOSE(vars.spreadQuote->value(), swap->fairForeignSpread(), relTol);

    // Check the 5Y discount factor
    DiscountFactor expDisc = 0.91155524911218166;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Check the start date of the helper swap
    BOOST_CHECK_EQUAL(swap->startDate(), vars.helper->swap()->startDate());

    // Move evaluation date forward
    vars.asof = vars.asof + 1 * Days;
    Settings::instance().evaluationDate() = vars.asof;

    // Build a new swap using new evaluation date
    swap = makeTestSwap(vars, discCurve);

    // Check that the new swap's NPV is 0.0
    BOOST_CHECK_SMALL(swap->NPV(), absTol);

    // Check the 5Y discount factor again. Changes slightly due to helper holidays/weekends.
    expDisc = 0.91155524848230363;
    BOOST_CHECK_CLOSE(expDisc, discCurve->discount(vars.asof + 5 * Years), relTol);

    // Check the start date of the helper swap. Should be 1 day greater.
    BOOST_CHECK_EQUAL(swap->startDate(), vars.helper->swap()->startDate());
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()