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

/*
 Copyright (C) 2011 Peter Caspers

 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.
*/

/*! \file markovfunctional.hpp
    \brief Markov Functional 1 Factor Model
*/

#ifndef quantlib_markovfunctional_hpp
#define quantlib_markovfunctional_hpp

#include <iostream>
#include <iomanip>

#include <boost/timer/timer.hpp>

#include <math.h>
#include <boost/math/special_functions/fpclassify.hpp>

#include <ql/models/model.hpp>
#include <ql/models/parameter.hpp>
#include <ql/math/interpolation.hpp>
#include <ql/math/interpolations/loginterpolation.hpp>
#include <ql/math/integrals/gaussianquadratures.hpp>
#include <ql/math/distributions/normaldistribution.hpp>
#include <ql/math/solvers1D/brent.hpp>
#include <ql/math/errorfunction.hpp>
#include <ql/indexes/iborindex.hpp>
#include <ql/indexes/swapindex.hpp>
#include <ql/instruments/vanillaswap.hpp>
#include <ql/time/date.hpp>
#include <ql/time/period.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <ql/termstructures/volatility/swaption/swaptionvolstructure.hpp>
#include <ql/termstructures/volatility/optionlet/optionletvolatilitystructure.hpp>
#include <ql/termstructures/volatility/smilesection.hpp>
#include <ql/stochasticprocess.hpp>
#include <ql/pricingengines/blackformula.hpp>
#include <ql/pricingengines/blackcalculator.hpp>
#include <ql/utilities/null.hpp>

#include <mfstateprocess.hpp>
#include <kahalesmilesection.hpp>

namespace QuantLib {

    //! One factor Markov Functional model class.
    /*! blah blah
		TODO use voltermstructure's timeFromReference() for std dev calculation of market digitals (or blackVariance()), also in swaption and caplet engine
			 however, is this then still consistent with the process x starting at yts's reference date? think about that and make it consistent ...
    */

    class MarkovFunctional : public TermStructureConsistentModel, public CalibratedModel  {

      public:

		struct ModelSettings {

			ModelSettings(const int xGridPoints,const Real xStdDevs,const int gaussHermitePoints,const Real digitalGap,const int arbitrageCheckGridPoints,
				const Real rateCutLeft, const Rate rateCutRight, const Rate upperRateBound, const bool adjustDigitals, const bool forceExactYtsFit, const int digitalInterpolationDegree):

					xGridPoints_(xGridPoints), xStdDevs_(xStdDevs), gaussHermitePoints_(gaussHermitePoints), digitalGap_(digitalGap),
					arbitrageCheckGridPoints_(arbitrageCheckGridPoints), rateCutLeft_(rateCutLeft), rateCutRight_(rateCutRight),
					upperRateBound_(upperRateBound), adjustDigitals_(adjustDigitals), forceExactYtsFit_(forceExactYtsFit), digitalInterpolationDegree_(digitalInterpolationDegree) {}

			void validate() const {

				QL_REQUIRE(xGridPoints_>0,"At least one grid point (" << xGridPoints_ << ") for the state process discretization must be given");
				QL_REQUIRE(xStdDevs_>0.0,"Multiple of standard deviations covered by state process discretization (" << xStdDevs_ << ") must be positive");
				QL_REQUIRE(gaussHermitePoints_>0,"Number of gauss hermite integration points (" << gaussHermitePoints_ << ") must be positive");
				QL_REQUIRE(digitalGap_>0.0 && rateCutLeft_-digitalGap_/2.0 >=0.0 &&
					rateCutLeft_+digitalGap_/2.0<=rateCutRight_ && rateCutRight_+digitalGap_/2.0 <=upperRateBound_,
					"Rate cut left (" << rateCutLeft_ << "), digital gap (" << digitalGap_ << "), rate cut right (" << rateCutRight_ <<
					") and upper rate bound (" << upperRateBound_ << ") are not consistent");
				QL_REQUIRE(arbitrageCheckGridPoints_>0,"Number of grid points for arbitrage check (" << arbitrageCheckGridPoints_ << ") must be positive");
				QL_REQUIRE(digitalInterpolationDegree_>=0 && digitalInterpolationDegree_<=4,"Polynomial degree for digital payoff interpolation (" << digitalInterpolationDegree_ <<") must be between 0 and 4");

			}

			const int xGridPoints_;
			const Real xStdDevs_;
			const int gaussHermitePoints_;
			const Real digitalGap_;
			const int arbitrageCheckGridPoints_;
			const Real rateCutLeft_;
			const Real rateCutRight_;
			const Real upperRateBound_;
			const bool adjustDigitals_;
			const bool forceExactYtsFit_;
			const int digitalInterpolationDegree_;

		};

		struct CalibrationPoint {
			bool isCaplet_;
			//Date expiry_;
			Period tenor_;
			std::vector<Date> paymentDates_;
			std::vector<Real> yearFractions_;
			Real atm_;
			Real annuity_;
			Real afLeftBound_, afRightBound_;
			Real alphaL_,betaL_,alphaR_,betaR_;
			boost::shared_ptr<KahaleSmileSection> smileSection_;
		};

		struct ModelOutputs {
			std::vector<Date> expiries_;
			std::vector<Period> tenors_;
			std::vector<Real> afLeftBounds_;
			std::vector<Real> afRightBounds_;
			std::vector<Real> adjustmentFactors_;
			std::vector<Real> digitalsAdjustmentFactors_;
		};

        MarkovFunctional(const Handle<YieldTermStructure>& termStructure,
						const Real reversion,
						const std::vector<Date>& volstepdates,
						const std::vector<Real>& volatilities,
						const Handle<SwaptionVolatilityStructure>& swaptionVol,
						const std::vector<Date>& swaptionExpiries,
						const std::vector<Period>& swaptionTenors,
						const boost::shared_ptr<SwapIndex>& swapIndexBase,
						const int xGridPoints=512, const Real xStdDevs=7.0, const int gaussHermitePoints=32,
						const Real digitalGap=1E-8, const int arbitrageCheckGridPoints=200, const Real rateCutLeft=0.0001, const Real rateCutRight=1.9000,
						const Real upperRateBound=2.0, const bool adjustDigitals=false, const bool forceExactYtsFit=false, const int digitalInterpolationDegree=2);

		MarkovFunctional(const Handle<YieldTermStructure>& termStructure,
						const Real reversion,
						const std::vector<Date>& volstepdates,
						const std::vector<Real>& volatilities,
						const Handle<OptionletVolatilityStructure>& capletVol,
						const std::vector<Date>& capletExpiries,
						const boost::shared_ptr<IborIndex>& iborIndex,
						const int xGridPoints=512, const Real xStdDevs=7.0, const int gaussHermitePoints=32,
						const Real digitalGap=1E-8, const int arbitrageCheckGridPoints=200, const Real rateCutLeft=0.0001, const Real rateCutRight=1.9000,
						const Real upperRateBound=2.0, const bool adjustDigitals=false, const bool forceExactYtsFit=false, const int digitalInterpolationDegree=2);

		const ModelSettings& modelSettings() const { return modelSettings_; }
		const ModelOutputs& modelOutputs() const { return modelOutputs_; }

		const Date& numeraireDate() const { return numeraireDate_; }
		const Time& numeraireTime() const { return numeraireTime_; }

		const boost::shared_ptr<StochasticProcess1D> stateProcess() const { return stateProcess_; }

		Real numeraire(const Time t, const Real x=0.0) const;
		Real deflatedZerobond(const Time T, const Time t=0.0, const Real x=0.0) const;

		Real zerobond(const Time T, const Time t=0.0, const Real x=0.0) const;
		Real zerobond(const Date& maturity, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;

		Real zerobondOption(const Option::Type& type, const Date& expiry, const Date& maturity, const Rate strike, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;

		Real forwardRate(const Date& fixing, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;
		Real swapRate(const Date& fixing, const Period& tenor, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;
		Real swapAnnuity(const Date& fixing, const Period& tenor, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;

		Real capletPrice(const Option::Type& type, const Date& expiry, const Rate strike, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;
		Real swaptionPrice(const Option::Type& type, const Date& expiry, const Period& tenor, const Rate strike, const Date& referenceDate = Null<Date>(), const Real x=0.0) const;

      protected:

		void generateArguments();

      private:

		void initialize();
	    void updateNumeraireTabulation();

		void makeSwaptionCalibrationPoint(const Date& expiry, const Period& tenor);
		void makeCapletCalibrationPoint(const Date& expiry);

		void arbitrageFreeStrikeRange(const Date& expiry, CalibrationPoint& p) const;
		Real marketSwapRate(const Date& expiry, const CalibrationPoint& p, const Real digitalPrice) const;
		Real marketDigitalPrice(const Date& expiry, const CalibrationPoint& p, const Option::Type& type, const Real strike) const;
		static Real gaussianPolynomialIntegral(const Real a, const Real b, const Real c, const Real d, const Real e, const Real x0, const Real x1);

		ModelSettings modelSettings_;
		ModelOutputs modelOutputs_;

		const bool capletCalibrated_;

		boost::shared_ptr<GaussianQuadrature> gaussHermite_;
		const CumulativeNormalDistribution cnd_;

		boost::shared_ptr<IborIndex> ytsLinkedIborIndex_;
		boost::shared_ptr<StochasticProcess1D> stateProcess_;

		std::vector<boost::shared_ptr<std::vector<Real>>> numeraireDiscretization_;
		std::vector<boost::shared_ptr<Interpolation>> numeraire_;

		Parameter reversion_;
		Parameter& sigma_;

		std::vector<Date> volstepdates_;
		std::vector<Time> volsteptimes_;
		Array volsteptimesArray_;				// FIXME this is redundant (copy of volsteptimes_)
		std::vector<double> volatilities_;

		Date numeraireDate_;
		Time numeraireTime_;

		Handle<SwaptionVolatilityStructure> swaptionVol_;
		Handle<OptionletVolatilityStructure> capletVol_;

		std::vector<Date> swaptionExpiries_, capletExpiries_;
		std::vector<Period> swaptionTenors_;
		boost::shared_ptr<SwapIndex> swapIndexBase_;
		boost::shared_ptr<IborIndex> iborIndex_;

		//std::set<CalibrationPoint> calibrationPoints_;
		std::map<Date,CalibrationPoint> calibrationPoints_;
		std::vector<double> times_;
		std::vector<double> y_;

		mutable boost::timer::cpu_timer cpuTimer_,cpuTimer2_;

    };

	//bool operator<(const MarkovFunctional::CalibrationPoint& a, const MarkovFunctional::CalibrationPoint& b);

}

#endif


////////////////////////////////////////////////////////////////////////
// exhaustive test in simd mode for functor nt2::sin
////////////////////////////////////////////////////////////////////////
#include <nt2/include/functions/sin.hpp>
#include <boost/simd/sdk/simd/native.hpp>

#include <nt2/include/functions/iround.hpp>
#include <nt2/include/functions/load.hpp>
#include <nt2/include/functions/min.hpp>
#include <nt2/include/functions/splat.hpp>
#include <nt2/include/functions/successor.hpp>

#include <nt2/include/constants/real.hpp>

#include <nt2/sdk/meta/cardinal_of.hpp>
#include <nt2/sdk/meta/as_integer.hpp>

#include <iostream>

typedef BOOST_SIMD_DEFAULT_EXTENSION ext_t;

#include <nt2/modules/trigonometric/exhaustive/simd/sin.float.hpp>
#include <nt2/modules/trigonometric/exhaustive/simd/sin.nt2_int32_t.hpp>
#include <nt2/modules/trigonometric/exhaustive/simd/sin.nt2_uint32_t.hpp>

int main(){
{
  //place your test call for float here
  //place your test call for nt2::int32_t here
  //place your test call for nt2::uint32_t here
  return 0;
}


#include <map>
#include <queue>
#include <unordered_map>
#include <Eigen/Sparse>
#include <Euclid/Geometry/DEC.h>
#include <Euclid/Topology/HomologyGenerator.h>

namespace Euclid
{

namespace _impl
{

template<typename Mesh, typename T>
void build_closedness_matrix(const Mesh& mesh,
                             std::vector<Eigen::Triplet<T>>& triplets)
{
    auto fimap = get(boost::face_index, mesh);
    auto eimap = get(boost::edge_index, mesh);
    auto frange = faces(mesh);
    --frange.second; // the last face is redundant
    for (auto f : frange) {
        auto fidx = get(fimap, f);
        for (auto h : halfedges_around_face(halfedge(f, mesh), mesh)) {
            auto e = edge(h, mesh);
            triplets.emplace_back(
                fidx, get(eimap, e), halfedge_orientation(mesh, h));
        }
    }
}

template<typename Mesh, typename T>
void build_harmonicity_matrix(const Mesh& mesh,
                              std::vector<Eigen::Triplet<T>>& triplets)
{
    auto vimap = get(boost::vertex_index, mesh);
    auto eimap = get(boost::edge_index, mesh);
    auto offset = static_cast<int>(num_faces(mesh)) - 1;
    auto vrange = vertices(mesh);
    --vrange.second; // the last vertex is redundant
    for (auto v : vrange) {
        auto vidx = get(vimap, v);
        for (auto h : halfedges_around_target(v, mesh)) {
            auto e = edge(h, mesh);
            auto w = cotangent_weight(h, mesh);
            triplets.emplace_back(offset + vidx,
                                  get(eimap, e),
                                  halfedge_orientation(mesh, h) * w);
        }
    }
}

template<typename Mesh, typename T>
void build_duality_matrix(const Mesh& mesh,
                          const VertexChains<Mesh>& basis,
                          std::vector<Eigen::Triplet<T>>& triplets)
{
    auto eimap = get(boost::edge_index, mesh);
    auto offset = static_cast<int>(num_faces(mesh) + num_vertices(mesh)) - 2;
    for (size_t i = 0; i < basis.size(); ++i) {
        for (size_t j = 0; j < basis[i].size(); ++j) {
            auto vcurrent = basis[i][j];
            auto vnext = basis[i][(j + 1) % basis[i].size()];
            auto [h, hfound] = halfedge(vcurrent, vnext, mesh);
            auto e = edge(h, mesh);
            triplets.emplace_back(
                offset + i, get(eimap, e), halfedge_orientation(mesh, h));
        }
    }
}

template<typename Mesh,
         typename Derived,
         typename SEM,
         typename SVM,
         typename VertexUVMap,
         typename VertexParameterizedMap>
void integrate_holomorphic_one_forms(
    const Mesh& mesh,
    const Eigen::MatrixBase<Derived>& one_forms,
    CGAL::Seam_mesh<Mesh, SEM, SVM>& seam_mesh,
    VertexUVMap uvmap,
    VertexParameterizedMap vpmap)
{
    using Seam_mesh = CGAL::Seam_mesh<Mesh, SEM, SVM>;
    using Point_2 =
        typename CGAL::Kernel_traits<typename boost::property_traits<
            VertexUVMap>::value_type>::Kernel::Point_2;
    using Vector_2 =
        typename CGAL::Kernel_traits<typename boost::property_traits<
            VertexUVMap>::value_type>::Kernel::Vector_2;
    using vertex_descriptor =
        typename boost::graph_traits<Seam_mesh>::vertex_descriptor;
    auto underlying_eimap = get(boost::edge_index, mesh);

    std::queue<vertex_descriptor> queue;
    auto root = *(vertices(seam_mesh).first);
    queue.push(root);
    put(uvmap, root, Point_2(0, 0));
    put(vpmap, root, true);
    while (!queue.empty()) {
        auto v = queue.front();
        queue.pop();
        for (auto h : halfedges_around_source(v, seam_mesh)) {
            auto vv = target(h, seam_mesh);
            if (!get(vpmap, vv)) {
                auto underlying_e = edge(h.tmhd, mesh);
                auto e_idx = get(underlying_eimap, underlying_e);
                auto uv = get(uvmap, v);
                auto s = halfedge_orientation(mesh, h.tmhd);
                uv += s * Vector_2(one_forms(e_idx, 0), one_forms(e_idx, 1));
                put(uvmap, vv, uv);
                put(vpmap, vv, true);
                queue.push(vv);
            }
        }
    }
}

} // namespace _impl

template<typename Mesh, typename DerivedA, typename DerivedB>
void holomorphic_one_form_basis(const Mesh& mesh,
                                Eigen::MatrixBase<DerivedA>& primal,
                                Eigen::MatrixBase<DerivedB>& conjugate)
{
    auto basis = greedy_homology_generators(mesh);
    holomorphic_one_form_basis(mesh, basis, primal, conjugate);
}

template<typename Mesh, typename DerivedA, typename DerivedB>
void holomorphic_one_form_basis(const Mesh& mesh,
                                const VertexChains<Mesh>& homology_generators,
                                Eigen::MatrixBase<DerivedA>& primal,
                                Eigen::MatrixBase<DerivedB>& conjugate)
{
    using Scalar = typename DerivedA::Scalar;
    using Triplet = Eigen::Triplet<Scalar>;
    using Triplets = std::vector<Triplet>;
    using SpMat = Eigen::SparseMatrix<Scalar>;
    using Vec = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
    using Mat = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
    auto ng = homology_generators.size();
    auto nv = num_vertices(mesh);
    auto ne = num_edges(mesh);
    auto nf = num_faces(mesh);
    primal.derived().resize(ne, ng);

    // Primal one form basis
    Triplets triplets;
    _impl::build_closedness_matrix(mesh, triplets);
    _impl::build_harmonicity_matrix(mesh, triplets);
    _impl::build_duality_matrix(mesh, homology_generators, triplets);
    SpMat lhs(ne, ne);
    lhs.setFromTriplets(triplets.begin(), triplets.end());
    Eigen::SparseLU<SpMat> primal_solver;
    primal_solver.compute(lhs);
    for (size_t i = 0; i < ng; ++i) {
        Vec rhs = Vec::Zero(ne);
        rhs(nv + nf - 2 + i) = 1.0;
        primal.col(i) = primal_solver.solve(rhs);
    }

    // Conjugate one form basis
    Mat W(ng, ng);
    for (size_t i = 0; i < ng; ++i) {
        for (size_t j = 0; j < ng; ++j) {
            Vec wedge;
            wedge1(mesh, primal.col(i), primal.col(j), wedge);
            W(i, j) = wedge.sum();
        }
    }
    Mat B(ng, ng);
    for (size_t i = 0; i < ng; ++i) {
        for (size_t j = 0; j < ng; ++j) {
            Vec wedge;
            star_wedge1(mesh, primal.col(i), primal.col(j), wedge);
            B(i, j) = wedge.sum();
        }
    }
    Eigen::ColPivHouseholderQR<Mat> conjugate_solver;
    conjugate_solver.compute(W);
    Mat L = conjugate_solver.solve(B);
    conjugate = primal * L;
}

template<typename Mesh,
         typename Derived,
         typename SEM,
         typename SVM,
         typename VertexUVMap>
void integrate_holomorphic_one_forms(
    const Mesh& mesh,
    const Eigen::MatrixBase<Derived>& one_forms,
    CGAL::Seam_mesh<Mesh, SEM, SVM>& seam_mesh,
    VertexUVMap uvmap)
{
    using SM = CGAL::Seam_mesh<Mesh, SEM, SVM>;
    using vd = typename boost::graph_traits<SM>::vertex_descriptor;
    using PM = std::map<vd, bool>;
    PM parameterized;
    boost::associative_property_map<PM> vpmap(parameterized);
    _impl::integrate_holomorphic_one_forms(
        mesh, one_forms, seam_mesh, uvmap, vpmap);
}

template<typename Mesh, typename SEM, typename SVM>
Holomorphic_one_forms_parameterizer3<Mesh, SEM, SVM>::
    Holomorphic_one_forms_parameterizer3(const Mesh& mesh)
    : _underlying_mesh(mesh)
{
    holomorphic_one_form_basis(mesh, _primal, _conjugate);
    Eigen::VectorXd coeffs = Eigen::VectorXd::Zero(_primal.cols());
    coeffs(0) = 1.0;
    set_coeffs(coeffs);
}

template<typename Mesh, typename SEM, typename SVM>
Holomorphic_one_forms_parameterizer3<Mesh, SEM, SVM>::
    Holomorphic_one_forms_parameterizer3(
        const Mesh& mesh,
        const VertexChains<Mesh>& homology_generators)
    : _underlying_mesh(mesh)
{
    holomorphic_one_form_basis(mesh, homology_generators, _primal, _conjugate);
    Eigen::VectorXd coeffs = Eigen::VectorXd::Zero(_primal.cols());
    coeffs(0) = 1.0;
    set_coeffs(coeffs);
}

template<typename Mesh, typename SEM, typename SVM>
template<typename Derived>
void Holomorphic_one_forms_parameterizer3<Mesh, SEM, SVM>::set_coeffs(
    const Eigen::MatrixBase<Derived>& coeffs)
{
    _one_forms.resize(_primal.rows(), 2);
    _one_forms.col(0) = _primal * coeffs;
    _one_forms.col(1) = _conjugate * coeffs;
}

template<typename Mesh, typename SEM, typename SVM>
template<typename VertexUVMap,
         typename VertexIndexMap,
         typename VertexParameterizedMap>
CGAL::Surface_mesh_parameterization::Error_code
Holomorphic_one_forms_parameterizer3<Mesh, SEM, SVM>::parameterize(
    TriangleMesh& mesh,
    halfedge_descriptor,
    VertexUVMap uvmap,
    VertexIndexMap,
    VertexParameterizedMap vpmap)
{
    _impl::integrate_holomorphic_one_forms(
        _underlying_mesh, _one_forms, mesh, uvmap, vpmap);
    return CGAL::Surface_mesh_parameterization::OK;
}

} // namespace Euclid


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   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_GALLERY_FUNCTIONS_SOBOL_HPP_INCLUDED
#define NT2_GALLERY_FUNCTIONS_SOBOL_HPP_INCLUDED
#include <nt2/include/functor.hpp>
#include <nt2/sdk/meta/size_as.hpp>
#include <nt2/sdk/meta/value_as.hpp>
#include <nt2/include/constants/nbmantissabits.hpp>
#include <boost/dispatch/dsl/semantic_of.hpp>
#include <nt2/core/container/dsl/value_type.hpp>
#include <nt2/core/container/dsl/size.hpp>

/*!
 * \ingroup algebra
 * \defgroup algebra_sobol sobol
 *   sobol(n) is an integer matrix containing a sobol sequence
 *   allowing to define quasi random numbers  fitted to Monte-Carlo
 *   integration up to dimension n.
 *   Currently n must be less or equal to 100.
 *   the type of the table element used to store the result is important,
 *   uint32_t or uint64_t will generate different sequences suited for respectively
 *   float and double quasi-random number sequence generation.
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/sobol.hpp>
 * \endcode
 *
 *
**/
//==============================================================================
// sobol actual class forward declaration
//==============================================================================

namespace nt2 { namespace tag
  {
    /*!
     * \brief Define the tag sobol_ of functor sobol
     *        in namespace nt2::tag for toolbox algebra
    **/
    struct sobol_ :  ext::unspecified_<sobol_>
    {
      typedef ext::unspecified_<sobol_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_sobol_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
    template<class Site>
    BOOST_FORCEINLINE generic_dispatcher<tag::sobol_, Site> dispatching_sobol_(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
    {
      return generic_dispatcher<tag::sobol_, Site>();
    }
    template<class... Args>
    struct impl_sobol_;
  }
  NT2_FUNCTION_IMPLEMENTATION(tag::sobol_, sobol, 2)

  template < class T>
  BOOST_FORCEINLINE typename meta::call<tag::sobol_(size_t,meta::as_<T>)>::type
  sobol(size_t const & a0)
  {
    return sobol(a0, nt2::meta::as_<T>());
  }

}

namespace nt2 { namespace ext
{
  template<class Domain, class Expr,  int N>
  struct  size_of<tag::sobol_, Domain, N, Expr>
  {
    typedef _2D                               result_type;
    BOOST_FORCEINLINE result_type operator()(Expr& e) const
    {
      result_type sizee;
      sizee[1] = boost::proto::value(boost::proto::child_c<0>(e));
      sizee[0] = 23;
      return sizee;
    }
  };

  template <class Domain, class Expr, int N>
  struct value_type < tag::sobol_, Domain,N,Expr>
  {
    typedef typename  boost::proto::result_of::child_c<Expr&,0>::type  t_t;
    typedef typename  boost::dispatch::meta::semantic_of<t_t>::type type;
  };
} }
#endif


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

#include "sycomore/magnetization.h"

BOOST_AUTO_TEST_CASE(ComplexMagnetizationFromMagnetization, *boost::unit_test::tolerance(1e-9))
{
    sycomore::Magnetization const m_r{1,2,3};
    auto && m_c = sycomore::as_complex_magnetization(m_r);

    BOOST_TEST(m_c.p.real() == std::sqrt(2.)/2.);
    BOOST_TEST(m_c.p.imag() == std::sqrt(2.));

    BOOST_TEST(m_c.z == 3);

    BOOST_TEST(m_c.m.real() == std::sqrt(2.)/2.);
    BOOST_TEST(m_c.m.imag() == -std::sqrt(2.));
}

BOOST_AUTO_TEST_CASE(ComplexMagnetizationToMagnetization, *boost::unit_test::tolerance(1e-9))
{
    sycomore::ComplexMagnetization const m_c{
        {std::sqrt(2.)/2., std::sqrt(2.)},
        3,
        {std::sqrt(2.)/2., -std::sqrt(2.)}};

    auto && m_r = sycomore::as_real_magnetization(m_c);

    BOOST_TEST(m_r[0] == 1);
    BOOST_TEST(m_r[1] == 2);
    BOOST_TEST(m_r[2] == 3);
}


/**======================================================================================================================================================
 * @brief  Approximate inference Marginalization
 *        /Script to find the amalgamation on CIMs.
 *          i.e Marginalization operation takes a CIM Q S|C , a set of variables X from,
 *            and an initial distribution P over the variables of S. It returns a reduced
 *            CIM of the form: Q S'|C = marg PX ( Q S|C ) where S'=S-X.
 *              The resulting approximation is a linear approximation of the marginal.
 *              Q S'|C (s'1 → s'2|c)≈ ∑ Q S|C (s'1⊕y → s'2⊕y|c) P0 (y|s'1,c)
 *
 * @version 1.0
 * @author Intern: Bernardin HOUESSOU for LIP6
 * Created on 6 juillet 2015, 17:37
 * File:   main.cpp
 *=====================================================================================================================================================
*/

//Used and required library(ies)/header(s)/inlcude(s)===============================================================================================================================
#include <cstdlib>                    //
#include <armadillo>                 // Armadillo header::armadillo library
#include <iostream>                 // Console commands ::defines the standard Input/Output stream objects std::cout
#include <string>                  // String header::defines several functions to manipulate C++ strings
#include <fstream>                // File commands::defines the Input/Output stream class to operate on files
#include <vector>                // For vector
#include <cctype>               // For uppercase & lowercase conForersion
#include <cstring>             // For  strcopy() and ....
#include <map>                // Use of Map
#include "TabVar.h"
#include "MatrixCIM.h"       // Calling created header/library
#include <sstream>
#include <functional>   // std::minus
#include <numeric>     // std::accumulate
//Used namespaces====================================================================================================================================================
using namespace std;
using namespace arma;

/********************************************************************************************************************************
 LIST OF USEFUL FUNCTIONS
 **/
/***********************************************************************************************************************************************
Function "countnumberfile()" to count number of times the file "lines1.txt" has been used in the background
-Specially created for the use in the function "speciallogs()
*/
int countnumberfile()
{
	//Needed variables
	ofstream outputFile1;
	int count1=0;

    //checking number of times the file 'lines1.txt' has been used
	ifstream inputFile1("Resources/Temporary/lines1.txt");
	inputFile1 >> count1;
	inputFile1.close();
	count1++ ;

    //Saving count to file 'lines1.txt'
	outputFile1.open ("Resources/Temporary/lines1.txt");
	outputFile1 <<count1 << endl;
	outputFile1.close();

return count1;
}
//End of Function "countsaveddistrotofile()"**************************************************************************************

/****************************************************************************************************************************************************
Function "speciallogs() to save all printed elements by "cout<<" on the screen into a file
*/
void speciallogs()
{
        //redirect output of cout to file "Result/output.txt
	freopen("Resources/Result/resultlogMarg.txt", "app", stdout );
	//Displaying the n_th number before each trial
	std::cout<<"|"<<"N°"<<countnumberfile()<<"|"<<endl;
}
//End of Function "speciallogs()"*********************************************************************************************************************

/********************************************************************************************************************************
Function "createemptymfoismmatrix() " to convert int to string
*/
string int2strconvertor(int n) {
	stringstream ss;
	ss << n;
	return ss.str();
}
//End of function "int2strconvertor() " ******************************************************************************************************

/********************************************************************************************************************************
Function "str2intconvertor() " to convert string to int
*/
int str2intconvertor(std::string str) {

	return atoi( str.c_str() );
}
//End of function "str2intconvertor( ) " ******************************************************************************************************

/********************************************************************************************************************************
Function "convert() " to convert multimaptomap
*/

ms convert(const multimap_tablestring& multimapstring)
{
    ms r;

    for (multimap_tablestring::const_iterator it = multimapstring.begin(); it !=multimapstring.end(); ++it)
    {

        std::vector<std::string>& s(r[it->first]);
        s.push_back(it->second);

    }

    return r;
}
//End of function "convertmultimaptomap() " ******************************************************************************************************

ms2 convert2(const multimap_tablestring& multimapstring2)
{
    ms2 r;

    for (multimap_tablestring::const_iterator it = multimapstring2.begin(); it !=multimapstring2.end(); ++it)
    {

        std::set<std::string>& s(r[it->first]);
        s.insert(it->second);

    }

    return r;
}
//End of function "convertmultimaptomap() " ******************************************************************************************************

/*Vector split function to split matrix values only from text file
 */
StringVector splitvarnbstate(string str, char delimiter) {
  StringVector internal;
  stringstream ss(str); // Turn the string into a stream.
  string tok;
  string tok2=tok;

  while(getline(ss, tok, delimiter))
  {
        tok=tok2;
        char delimiter2=';';
        while(getline(ss, tok, delimiter2)) {
           if(getline(ss, tok, delimiter2)) {
             // std::cout<<tok;
            internal.push_back(tok);
           }
        }
  }
return internal;
}

/*Vector split function to split matrix values only from text file
 */
StringVector splitmatrixvaluesonly11(string str, char delimiter) {
  StringVector internal;
  stringstream ss(str); // Turn the string into a stream.
  string tok;
  string tok2=tok;
  while(getline(ss, tok, delimiter))
  {
        tok=tok2;
        char delimiter2=';';
        if(getline(ss, tok, delimiter2)) {
                    if(getline(ss, tok, delimiter2)) {
           if(getline(ss, tok, delimiter2)) {
             // std::cout<<tok;
            internal.push_back(tok);
           }
        }
        }
  }
return internal;
}

/*Vector split function to split Variables only from text file
 */
StringVector splitdistrovariable(string strv, char delimiterv) {
  StringVector internalv;
  delimiterv='|';//replace old delimiter by delimiterv
  stringstream ssv(strv); // Turn the string into a stream.
  string tokv;
  string tok2v=tokv;
  while(getline(ssv, tokv, delimiterv))
  {
        tokv=tok2v;
        char delimiter2v='+';
        if(getline(ssv, tokv, delimiter2v)) {
           if(getline(ssv ,tokv, delimiter2v)) {
              //std::cout<<tok;
            internalv.push_back(tokv);
             }
        }
  }
return internalv;
}

/*Vector split function to split distro Variables from text file
 */
StringVector splitdistroparents(string str, char delimiter) {

  StringVector internal;
  stringstream ss(str); // Turn the string into a stream.
  string tok;
  string tok2=tok;
  delimiter='+';//replacing old delimiter=* to this one
  while(getline(ss, tok, delimiter))
  {
        tok=tok2;
        char delimiter2='|';
        if(getline(ss, tok, delimiter2)) {
            delimiter2=';';
           if(getline(ss, tok, delimiter2))
              internal.push_back(tok);
        }
  }
return internal;
}

/*Vector split function to split given distribution P0 values from text file
 */
StringVector splitdistrovalues(string str, char delimiter) {

 StringVector internal;
  //char delimiter1=':';
  stringstream ss(str); // Turn the string into a stream.
  string tok;
  string tok2=tok;
  delimiter='+';
    while(getline(ss, tok, delimiter))
    {
         tok=tok2;
         char delimiter2=';';
         if(getline(ss, tok, delimiter2)) {
             if(getline(ss, tok, delimiter2))

            // std::cout<<tok;
            internal.push_back(tok);

         }
    }
return internal;
}

/*Vector split function to split parents variables only from text file
 */
StringVector splitvarparentsonly1(string str, char delimiter) {
  StringVector internal;
  //char delimiter1=':';
  stringstream ss(str); // Turn the string into a stream.
  string tok;
  string tok2=tok;
    while(getline(ss, tok, delimiter))
    {
         tok=tok2;
         char delimiter2=';';
         if(getline(ss, tok, delimiter2)) {
            // std::cout<<tok;
            internal.push_back(tok);

         }
    }
return internal;
}

/*Vector split function to split Variables only from text file
 */
StringVector splitvariable(string strv, char delimiterv) {
  StringVector internalv;
  delimiterv=',';//replace old delimiter by delimiterv
  stringstream ssv(strv); // Turn the string into a stream.
  string tokv;
  string tok2v=tokv;
  while(getline(ssv, tokv, delimiterv))
  {
        tokv=tok2v;
        char delimiter2v=':';
        if(getline(ssv, tokv, delimiter2v)) {
           if(getline(ssv ,tokv, delimiter2v)) {
              //std::cout<<tok;
            internalv.push_back(tokv);
             }
        }
  }
return internalv;
}

/*********************************************************************************************************************************************************************
 * MAIN FUNCTION
 *********************************************************************************************************************************************************************
 */

int main(int argc, char** argv) {

    //Sending result to logfile
    speciallogs();

    //load the text file and put it into a single string:
    std::ifstream in("Resources/statespacevar.txt");
    std::stringstream buffer;
    buffer << in.rdbuf();
    std::string test = buffer.str();
    //Declaring and initializing my delimiter
    char delimiter1='|';
    //Splitting content of file into vector 'varstat' to get state of var
    StringVector varstate=splitvarnbstate(test, delimiter1);
    size_t varstatesize=varstate.size();//size of the vector 'varstat'
    map_table mapdesvarstate;
    map_table::iterator itstate;
    //
    string KEY;

    //Splitting content of file into vector 'sep'
    StringVector sep= splitmatrixvaluesonly11(test, delimiter1);
    size_t aa=sep.size();//size of the vector 'sep'
    //
    StringVector variableinitial=splitvariable(test, delimiter1);
    size_t bb=variableinitial.size();//size of the vector 'sep'
    //
    StringVector parentsvariable=splitvarparentsonly1(test, delimiter1);
    size_t parentsvarsize=parentsvariable.size();//size of the vector 'sep'
    //
    StringVector matrixvalues=splitmatrixvaluesonly11(test, delimiter1);
    size_t matrixvaluessize=matrixvalues.size();//size of the vector 'sep'
    //
    map2string mapvarparstate;
    map2string::iterator mp3st;
    //
    map_tablelistbig my_map;
    map_tablelistbig::iterator iterr;
    int my_mapsize=my_map.size();

    map_tablelistbig my_map2;
    map_tablelistbig::iterator iterr2;
    int my_mapsize2=my_map2.size();
    //
    Biglist biglist;
    //
    std::vector<std::string> VartableIni;
    biglist.Vartable=VartableIni;
    //
    std::vector<std::string> VarstateIni;
    biglist.Varstate=VarstateIni;
    //
    std::vector<std::string> ParentstableIni;
    biglist.Parentstable=ParentstableIni;
    //
    std::vector<std::string> VarMatrixIni;
    biglist.VarMatrix=VarMatrixIni;
    //
    for(size_t i = 0; i < bb; i++){
        biglist.Vartable.push_back (variableinitial[i]);
        biglist.Varstate.push_back (varstate[i+i]);
        biglist.Parentstable.push_back (parentsvariable[i]);
        biglist.VarMatrix.push_back (matrixvalues[i]);
        std::string Keystring;
        Keystring="  "+biglist.Vartable.at(i)+"        "+"  "+biglist.Varstate.at(i)+" "+"       "+biglist.Parentstable.at(i)+"         "+" "+biglist.VarMatrix.at(i);
        my_map.insert( std::pair<int,std::string > (i,Keystring));
    }
            //Display content of map initial
           // showmate(" ", my_map);
          //Delete element at position i and display content of map after deletion
         // deletemapelement(0, my_map);

        //Splitting content of file into vector 'sep'
        StringVector septest= splitvariable(test, delimiter1);
        size_t aatest=septest.size();//size of the vector 'sep'

        map_tablestring mapdesvarstring;
        map_tablestring::iterator itestring;

        mapdesvarstring[septest[0]]=varstate[0];

    for (itestring=mapdesvarstring.begin(); itestring!=mapdesvarstring.end(); ++itestring) {
        for(size_t i = 1; i <aatest; i++){
            KEY=septest[i];
            std::pair<map_tablestring::iterator,bool> retstring;
            retstring = mapdesvarstring.insert ( std::pair<std::string,std::string >(KEY,varstate[i+i]) );
        }
    }
        //Creation of stringvector to handle variable,state and ...
        StringVector varok1;
        StringVector varok1state;
        StringVector savedoldvarok1;

        for (itestring=mapdesvarstring.begin();itestring!=mapdesvarstring.end(); ++itestring){
            varok1.push_back(itestring->first);
            savedoldvarok1.push_back(itestring->first);
            varok1state.push_back(itestring->second);
            std::reverse(varok1state.begin(),varok1state.end());
        }
        //cout << "Map size " << mapdesvarstring.size() << endl;
        multimap_tablestring multimapstring;
        multimap_tablestring::iterator multiitestring;
        StringVector varandapparitioninmap;

          for(size_t j = 0; j <bb; j++)
              multimapstring.insert(std::pair<const char* const,std::string>(variableinitial[j].c_str(), parentsvariable[j]));

            for(size_t j = 0; j <varok1.size(); j++)  {
               varandapparitioninmap.push_back(int2strconvertor(multimapstring.count(varok1[j].c_str())));
            }

        StringVector reversevarok1;
        StringVector reversevarandapparitioninmap;
        //std::cout << "****************************************"<<endl;
        //std::cout << "Number of elements with key: "<<endl;
        //for(size_t j = 0; j <varandapparitioninmap.size(); j++)
        //cout<<""<<varandapparitioninmap[j]<<endl<<endl;
        std::reverse(varok1.begin(),varok1.end());
        std::reverse(varandapparitioninmap.begin(),varandapparitioninmap.end());

        for (std::vector<std::string>::iterator ite=varok1.begin(); ite!=varok1.end(); ++ite)
              reversevarok1.push_back(*ite);
        for (std::vector<std::string>::iterator it=varandapparitioninmap.begin(); it!=varandapparitioninmap.end(); ++it)
             reversevarandapparitioninmap.push_back(*it);
         /*
                 for(size_t j = 0; j <reversevarok1.size(); j++){
                      cout<<""<<reversevarok1[j]<<" : "<<reversevarandapparitioninmap[j]<<endl<<endl;
                }
         */

        for(size_t j = 0; j <varok1.size(); j++)  {

         varandapparitioninmap.push_back(int2strconvertor(multimapstring.count(varok1[j].c_str())));
        }

        //calling function to convert multimap to map
        ms s(convert(multimapstring));

        //declaration of an int vector to get the offset or "pas" value of a special variable for later use
        intVector offsetorpas;

          for(size_t i = 0; i < mapdesvarstring.size(); i++)
         {
            int offsetovar;
            int wght1var=1;
            if(i==0)
            {
              offsetovar=1;
              //cout<<"--Offset or Weight of Variable: "<<i+1<<" -- "<<varok1.at(i)<<" = "<<offsetovar<<endl;
              offsetorpas.push_back(offsetovar);
            }
            //Determination of the different offset basing on the number of state of each variable
            if(i!=0){
                //convert string to integer
                int stateprevious;
                stateprevious=atoi( varok1state.at(i-1).c_str() );
                //cout<<stateprevious<<endl;
                if(i==1){
                    offsetovar=1;
                    offsetovar=offsetovar * stateprevious;
                    offsetorpas.push_back(offsetovar);
                }
                if(i!=1){
                offsetovar= offsetovar * stateprevious;
                offsetorpas.push_back(offsetovar);
                }
             //cout<<"--Offset or Weight of Variable: "<<i+1<<" -- "<<varok1.at(i)<<" = "<<offsetovar<<endl;
            }
            //cout<<varok1state.at(i)<<endl<<endl;
         }

/*****************************************************************************************************************************************************/
                //STATING CREATING CIM MATRIXES AND USING THEIR VALUES FOR THE JIM MATRIX
                int nbcimmatrix=bb;int matrixnbrows=2;int nbvariable=mapdesvarstring.size();

                //intVector valuediagonal;
                doubleVector matvalueeach;
                std::string indexmat;
                std::string matvalue;
                string fmatrixname;
                mat matrixname;
		for (int i=0;i<nbcimmatrix;i++)
                {   //cout<<matrixvalues[i]<<endl;
                    indexmat=int2strconvertor(i);
                    //string fmatrixname="Q"+indexmat;
                    fmatrixname="Q"+biglist.Vartable.at(i)+"|"+biglist.Parentstable.at(i);
                    string fulltrixfilename="Resources/Matrices/CIMs/"+ fmatrixname +".txt";
                    matrixname.resize(matrixnbrows,matrixnbrows); // change the size of matrixname atomatically(data is not preserved)
                    //matrixname.zeros();//set all elements to Zeros
                    matrixname=matrixvalues[i];
                    matrixname.save(fulltrixfilename,csv_ascii);
                    //matrixname.print(fulltrixfilename);
                     //cout << fmatrixname<<".n_rows: " << matrixname.n_rows << endl;  // .n_rows and .n_cols are read only
                    //cout << fmatrixname<< ".n_cols: " << matrixname.n_cols << endl;
                    //get each matrix value and print them
                     //cout<<fmatrixname<<endl;
                     int nbdelements=matrixname.size();
                     for(int i=0;i<=nbdelements-1;i++){
                        indexmat=int2strconvertor(i);
                        matvalue="val "+indexmat;
                        //cout<< matvalue<<": "<<matrixname.at(i)<<endl;
                        matvalueeach.push_back(matrixname.at(i));
                     }
		}
                   // std::cout <<endl; //JUMP LINE
                    int cimmatrow=pow(nbvariable, 2);
                    mat JIM;string varall;string fjimmatname;string fulljimtrixname;
                    fjimmatname="Q_JIM";
                    fulljimtrixname="Resources/Result/"+ fjimmatname +".txt";
                    JIM.resize(cimmatrow,cimmatrow);
                    int matvalueeachsize=matvalueeach.size();

 /*                    cout<<"Vector containing all the elements of the CIM matrixes:"<<endl;
                     for (int n=0;n<matvalueeachsize;n++){
                     cout<<matvalueeach[n]<<"; ";
                     }*/

                    //Convert String vector to int vector
                    intVector convstr2intstate;
                    for (int i=0;i<mapdesvarstring.size();i++){
                    convstr2intstate.push_back(atoi(varok1state[i].c_str()));
                    }
                    //Produit cartésien des états des variables ---  line=Prod*card(Var) orπ*card(Var)
                    //ostream_iterator< int > output( cout, " " );
                    //cout << "Contenu du vecteur: ";
                    //copy( convstr2intstate.begin(), convstr2intstate.end(), output );
                    //cout<<"Produit Cartésien des variables:π*card(Var): "<< accumulate (convstr2intstate.begin(), convstr2intstate.end(),1,multiplies<int>())<< endl;
                    int lineprodcarte=accumulate (convstr2intstate.begin(), convstr2intstate.end(),1,multiplies<int>());
                    //cout <<lineprodcarte <<endl;

                    int offsetoupas;
                    for (int i=0;i<mapdesvarstring.size();i++)
                    {
                       offsetoupas=offsetorpas[i];
                       //cout<<"--Offset or Weight of Variable: "<<varok1.at(i)<<" is:"<<offsetoupas <<" with state: "<<varok1state[i]<<endl;

                    }
                     cout<<"***************************************************"<<endl;
                    int offsetpassize=offsetorpas.size();
                    for (int init=0;init<offsetpassize;init++)
                    {
                        offsetorpas[init];
                    }

                   ///Initializing some values for the JIM
                    //WORKFORALL MATRIX
                    JIM.at(0,1)=matvalueeach[1];//1 ->1 ->0,1
                    JIM.at(1,0)=matvalueeach[2];//1 ->1 ->1,0

                     int newvaluerow;
                     int newvaluecol;
                     int nullvalue=0;
                     for (int l=0;l<cimmatrow;l++){
                     int newrowcolsize=sqrt(cimmatrow);

                     //Starting distribution of CIM value to JIM
                     for (int at=1;at<newrowcolsize;at++){

                         int normalsize=my_map.size();
                         int nonnormalsize=mapdesvarstring.size();
                         int squaredenormal=sqrt(normalsize);
                         if(normalsize%2!=0){
                            newvaluerow=JIM.at(at+1,0)=matvalueeach[matvalueeachsize-6];//10->8 ->2,0
                            newvaluecol=JIM.at(at+1,l)=matvalueeach[matvalueeachsize-11];//5 ->11 ->2,3
                            newvaluecol=JIM.at(at+2,at)=matvalueeach[matvalueeachsize-2];//14->13->3,1
                            newvaluecol=JIM.at(at+2,at+1)=matvalueeach[matvalueeachsize-10];//6 ->14->3,2
                         }
                         else
                         {
                            newvaluerow=JIM.at(at,0)=matvalueeach[matvalueeachsize-14];//2 ->4 ->1,0
                            newvaluerow=JIM.at(at+1,0)=matvalueeach[matvalueeachsize-6];//10->8 ->2,0
                            newvaluecol=JIM.at(at+1,l)=matvalueeach[matvalueeachsize-11];//5 ->11 ->2,3
                            newvaluecol=JIM.at(at+2,at)=matvalueeach[matvalueeachsize-2];//14->13->3,1
                            newvaluecol=JIM.at(at+2,at+1)=matvalueeach[matvalueeachsize-10];//6 ->14->3,2
                        }
                     }
                        //cout<<"**************************************"<<endl;
                        for (int c=0;c<cimmatrow;c++){
                            for (int at=1;at<newrowcolsize;at++){
                                newvaluerow=JIM.at(0,at)=matvalueeach[at];//1 ->1 ->0,1
                                newvaluerow=JIM.at(0,at+1)=matvalueeach[matvalueeachsize-7];//9  ->2 ->0,2
                                newvaluecol=JIM.at(at,c+1)=matvalueeach[matvalueeachsize-3];//13 ->7 ->1,3
                            }
                        }
                    }
                        //getting anti or opposite of the main diagonal of the JIM matrix
                        vec newoppdiagonalval= diagvec(fliplr(JIM));
                        vec newoppdiagonalval1= newoppdiagonalval.zeros();
                        mat newJIM;
                        mat revnewJIM;
                        newJIM=fliplr(JIM);
                        newJIM.diag().zeros();
                        revnewJIM=fliplr(newJIM);
                        //revnewJIM.print("JIM after oppo diagonal values set to 0");
                        //newoppdiagonalval.print("newoppdiagonalval");
                        JIM=revnewJIM;

                        //calculate -end diagonal value
                        // Write the diagonal of the matrix when i = j
                        for (int i=0;i<cimmatrow;i++)
                        {
                            double sumij=0;
                            //display table
                            for (int j=0;j<cimmatrow;j++)
                                if (i != j){
                                    sumij=sumij+ JIM(i,j);

                            }
                            //set all JIM(rows,rows) equal to sumij
                           JIM(i,i)= -sumij;
                        }

                        JIM.print(fulljimtrixname);
			JIM.save(fulljimtrixname,csv_ascii);
                        std::cout <<endl; //JUMP LINE
                        cout<<"***************************************************"<<endl;

 /*****************************************************************************************************************************************************/
/////STARTING MARGINALIZATION OF JIM
    //Splitting distribution contents for marginalization from file into differents vectors
    char delimiterdistro='*';
    StringVector distrovar=splitdistrovariable(test, delimiterdistro);
    StringVector distrovarparents=splitdistroparents(test, delimiterdistro);
    StringVector distroval=splitdistrovalues(test, delimiterdistro);
    //Getting sise of the different vectors
    size_t distrovarsize=distrovar.size();//size of the vector 'distrovar'
    size_t distrovarparsize=distrovarparents.size();//size of the vector 'distrovarparents'
    size_t distrovalsize=distroval.size();//size of the vector 'distroval'

    //Printing their content

/*  for(auto i :distrovar)      cout<<"VALUE OF distrovariables: "<<i <<endl;
    for(auto j :distrovarparents)      cout<<"VALUE OF distrovarparents: "<<j <<endl;
    for(auto k :distroval)      cout<<"VALUE OF distrovalues: "<<k <<endl;
 */

   //STATING CREATING Given Distribution matrices files AND USING THEIR VALUES TO get another MATRIX and marg...
    //Initialization and creation of the required variables
    int nbdistribution=distrovarsize;int distrorows=1;int distrocols=2;
    doubleVector distrovalueeach;
    std::string indexdistro;
    std::string distrovalue;
    string fdistroname;
    rowvec distro;

    //starting of loopfor the creation
    for (int i=0;i<nbdistribution;i++)
    {
        //cout<<matrixvalues[i]<<endl;
    indexdistro=int2strconvertor(i);
    fdistroname=distrovar.at(i)+"|"+distrovarparents.at(i);
    string fulltrixfilename="Resources/Matrices/Distribution/"+ fdistroname +".txt";
    distro.resize(distrorows,distrocols); // change the size of matrixname atomatically(data is not preserved)
    //matrixname.zeros();//set all elements to Zeros
    distro=distroval[i];
    distro.save(fulltrixfilename,csv_ascii);
    distro.print(fulltrixfilename);
    std::cout <<endl; //JUMP LINE
/*  cout << fdistroname<<".n_rows: " << distro.n_rows << endl;  // .n_rows and .n_cols are read only
    cout << fdistroname<< ".n_cols: " << distro.n_cols << endl;

    //get each matrix value and print them
    cout<<fdistroname<<endl;
 */
    //Getting value of each distribution depending of the varaible
    int nbdelements=distro.size();
    //printing and stroring into vector those distribution
    for(int i=0;i<=nbdelements-1;i++) {
        indexdistro=int2strconvertor(i);
//        distrovalue="val "+indexdistro;
//        cout<< distrovalue<<": "<<distro.at(i)<<endl;
        distrovalueeach.push_back(distro.at(i));
        }
    }

///This part need to be modify in order to have an automatic assigment of the values of the different distro
    ///creating row vectors for the different distrovalues
    rowvec p01,p02,p03;
    //
    p01=distroval.at(0);
    p02=distroval.at(1);
    p03=distroval.at(2);

    //Concatening or joining distributions withparents"
    mat distoprodcombi=join_horiz(p02.t(),p03.t());
    distoprodcombi.t();//transpose of...
    distoprodcombi.print("Concatenate distributions with parents\n");
    distoprodcombi.each_row() %= p01;   // p01 distri without parents
    mat P0avt=distoprodcombi;
    P0avt.print("\nMatrix-before colonns elements sum\n");
    mat p0 = sum(distoprodcombi,1);
    mat recupP0avt=P0avt;
    //recupP0zavt.print("P0zavt recup");
    p0.print("\nMatrix-after colonns elements sum\n");

    //dividing each row element by the appropraite row element of p0
    size_t divr=0;//initializing divr
    while( divr<p0.size()){
         (P0avt.rows(divr,divr) ) /=p0[divr];
         divr++;
    }
    mat res=P0avt ;
    //res.print("\n res\n");
    cout<<"\n**************************************"<<endl;
  ///Get necessary values from CIM of JIM for margin calculation
    //Calcul of differents values to put in each row using operator * to perform element multiplication,
    res.at(0,0) *=matvalueeach[5];//3 //
    res.at(0,1) *=matvalueeach[9];//5
    res.at(1,0) *=matvalueeach[6];//15
    res.at(1,1) *=matvalueeach[10];//4
    //addition each elemt of a row an put it in a matrix of sise 2*1 of name res
    res = sum(res,1);
    //Concatening or joining distributions res to obtain expected result
    res=join_horiz(res,res);

    //
    mat marg=res;
    int P0varrowcol=distro.size();
    string combidistromatname;string fullcombidistroname;
    combidistromatname="Marg P0";
    fullcombidistroname="Resources/Result/"+ combidistromatname +".txt";
    marg.resize(P0varrowcol,P0varrowcol);

    // Write the diagonal of the matrix when i = j
    for (int i=0;i<P0varrowcol;i++)
    {
        double sumij=0;
        double sumji=0;
        //display table
        for (int j=0;j<P0varrowcol;j++)
            if (i != j){
                sumij=sumij+ marg(i,j);
                sumji=sumij+ marg(j,i);
        }
        //set all JIM(rows,rows) equal to sumij
       marg(i,i)= -sumij;

    }
    //DISPLAYING MARGINALIZATION VALUE
    //marg.print(fullcombidistroname);
    cout<<"Result of the Marginalization is: "<<endl<<endl<<combidistromatname<<":"<<endl;
    cout<<marg<<endl;
/*    JIM.save(fullcombidistroname,csv_ascii);
    cout << combidistromatname<<".n_rows: " << marg.n_rows << endl;  // .n_rows and .n_cols are read only
    cout << combidistromatname<< ".n_cols: "<< marg.n_cols << endl;
    cout<< combidistromatname<<" size:"<<marg.size()<<endl;
    std::cout <<endl; //JUMP LINE*/
    cout<<"**************************************"<<endl;

    std::cout <<endl; //JUMP LINE
     return 0;
}
//END OF MAIN===================================================================================================================================================


#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Surface_mesh/IO.h>

#include <boost/property_map/property_map.hpp>

#include <iostream>
#include <fstream>
#include <set>
#include <unordered_map>

#include <CGAL/boost/graph/selection.h>
#include <CGAL/boost/graph/helpers.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> SM;
typedef boost::graph_traits<SM>::face_descriptor face_descriptor;

void border_cases()
{
  SM sm_ref;
  std::ifstream input("data/nm_selection_removal.off");
  input >> sm_ref;

  {
    SM sm = sm_ref;
    std::vector<SM::Face_index> faces_to_remove;
    faces_to_remove.push_back(SM::Face_index(5));
    faces_to_remove.push_back(SM::Face_index(6));
    SM::Property_map<SM::Face_index, bool> is_selected = sm.add_property_map<SM::Face_index, bool>("f:is_selected", false).first;
    is_selected[SM::Face_index(5)]=true;
    is_selected[SM::Face_index(6)]=true;
    CGAL::expand_face_selection_for_removal(faces_to_remove,
                                            sm,
                                            is_selected);
    int i=0;
    for(face_descriptor fh : sm.faces())
      if(!is_selected[fh]) ++i;
    assert(i==4);
  }

  {
    SM sm = sm_ref;
    std::vector<SM::Face_index> faces_to_remove;
    faces_to_remove.push_back(SM::Face_index(4));
    faces_to_remove.push_back(SM::Face_index(6));
    SM::Property_map<SM::Face_index, bool> is_selected = sm.add_property_map<SM::Face_index, bool>("f:is_selected", false).first;
    is_selected[SM::Face_index(4)]=true;
    is_selected[SM::Face_index(6)]=true;
    CGAL::expand_face_selection_for_removal(faces_to_remove,
                                            sm,
                                            is_selected);
    int i=0;
    for(face_descriptor fh : sm.faces())
      if(!is_selected[fh]) ++i;

    assert(i==1 || i==4); // depends on the start point
  }

  {
    SM sm = sm_ref;
    std::vector<SM::Face_index> faces_to_remove;
    faces_to_remove.push_back(SM::Face_index(4));
    faces_to_remove.push_back(SM::Face_index(5));
    faces_to_remove.push_back(SM::Face_index(6));
    SM::Property_map<SM::Face_index, bool> is_selected = sm.add_property_map<SM::Face_index, bool>("f:is_selected", false).first;
    is_selected[SM::Face_index(4)]=true;
    is_selected[SM::Face_index(5)]=true;
    is_selected[SM::Face_index(6)]=true;
    CGAL::expand_face_selection_for_removal(faces_to_remove,
                                            sm,
                                            is_selected);
    int i=0;
    for(face_descriptor fh : sm.faces())
      if(!is_selected[fh]) ++i;
    assert(i==4); // depends on the start point
  }

}

int main()
{
  SM sm;
  std::ifstream input(CGAL::data_file_path("meshes/head.off"));
  input >> sm;

// define my selection of faces to remove
  std::unordered_map<face_descriptor, bool> is_selected_map;

  const int selection_indices[30] = {652,18,328,698,322,212,808,353,706,869,646,352,788,696,714,796,937,2892,374,697,227,501,786,794,345,16,21,581,347,723};
  std::set<int> index_set(&selection_indices[0], &selection_indices[0]+30);

  std::vector<face_descriptor> faces_to_remove;
  int index = 0;
  for(face_descriptor fh : faces(sm))
  {
    if(index_set.count(index)==0)
      is_selected_map[fh]=false;
    else
    {
      faces_to_remove.push_back(fh);
      is_selected_map[fh]=true;
    }
    ++index;
  }

  std::size_t nb_input_faces = sm.number_of_faces();

  expand_face_selection_for_removal(faces_to_remove,
                                    sm,
                                    boost::make_assoc_property_map(is_selected_map));

  index=0;
  for(face_descriptor fh : faces(sm))
  {
    if (is_selected_map[fh])
    {
      CGAL::Euler::remove_face(halfedge(fh, sm), sm);
      ++index;
    }
  }

  CGAL_USE(nb_input_faces);
  assert( sm.number_of_faces()+30 < nb_input_faces);
  assert(is_valid_polygon_mesh(sm));

  border_cases();

  return 0;
}


#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <CGAL/Three/Scene_interface.h>
#include <CGAL/Three/Scene_item_rendering_helper.h>
#include <CGAL/Three/Triangle_container.h>
#include <CGAL/Three/Edge_container.h>
#include <CGAL/Three/Three.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QInputDialog>
#include <QMessageBox>
#include <QMap>
#include "Messages_interface.h"
#include "Kernel_type.h"
#include "Scene_surface_mesh_item.h"
#include "Color_ramp.h"
#include "triangulate_primitive.h"
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/distance.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/boost/iterator/counting_iterator.hpp>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Spatial_sort_traits_adapter_3.h>
#include <CGAL/property_map.h>
#include <boost/container/flat_map.hpp>
using namespace CGAL::Three;
namespace PMP = CGAL::Polygon_mesh_processing;
typedef Viewer_interface Vi;
typedef Triangle_container Tc;
typedef Edge_container Ec;

typedef Scene_surface_mesh_item Scene_face_graph_item;
typedef Scene_face_graph_item::Face_graph Face_graph;

#if defined(CGAL_LINKED_WITH_TBB)
#include <tbb/parallel_for.h>
template <class AABB_tree, class Point_3>
struct Distance_computation{
  const AABB_tree& tree;
  const std::vector<Point_3>& sample_points;
  const Point_3 initial_hint;
  std::vector<double>& output;

  Distance_computation(const AABB_tree& tree,
                       const Point_3 p,
                       const std::vector<Point_3>& sample_points,
                       std::vector<double>& out )
    : tree(tree)
    , sample_points(sample_points)
    , initial_hint(p)
    , output(out)
  {
  }
  void
  operator()(const tbb::blocked_range<std::size_t>& range) const
  {
    Point_3 hint = initial_hint;
    for( std::size_t i = range.begin(); i != range.end(); ++i)
    {
      hint = tree.closest_point(sample_points[i], hint);
      Kernel::FT dist = squared_distance(hint,sample_points[i]);
      double d = CGAL::sqrt(dist);
      output[i] = d;
    }
  }
};
#endif

class Scene_distance_polyhedron_item: public Scene_item_rendering_helper
{
  Q_OBJECT
public:
  Scene_distance_polyhedron_item(Face_graph* poly, Face_graph* polyB, QString other_name, int sampling_pts)
    : poly(poly),
      poly_B(polyB),
      other_poly(other_name)
  {
    setTriangleContainer(0, new Tc(Vi::PROGRAM_WITH_LIGHT,
                                   false));
    setEdgeContainer(0, new Ec(Vi::PROGRAM_NO_SELECTION, false));
    nb_pts_per_face = sampling_pts;
    this->setRenderingMode(FlatPlusEdges);
    thermal_ramp.build_thermal();
  }
  bool supportsRenderingMode(RenderingMode m) const {
    return (m == Flat || m == FlatPlusEdges);
  }
  Scene_item* clone() const {return nullptr;}
  QString toolTip() const {return QString("Item %1 with color indicating distance with %2").arg(this->name()).arg(other_poly);}
  void draw(Viewer_interface *viewer) const
  {
    if(!isInit(viewer))
      initGL(viewer);
    if ( getBuffersFilled() &&
         ! getBuffersInit(viewer))
    {
      initializeBuffers(viewer);
      setBuffersInit(viewer, true);
    }
    getTriangleContainer(0)->draw(viewer, false);
  }

  void drawEdges(Viewer_interface* viewer) const
  {
    getEdgeContainer(0)->setColor(QColor(Qt::black));
    getEdgeContainer(0)->draw(viewer, true);
  }

  void compute_bbox() const {
    setBbox(PMP::bbox(*poly));
  }

private:
  Face_graph* poly;
  Face_graph* poly_B;
  QString other_poly;
  mutable std::vector<float> m_vertices;
  mutable std::vector<float> edge_vertices;
  mutable std::vector<float> normals;
  mutable std::vector<float> colors;
  Color_ramp thermal_ramp;
  int nb_pts_per_face;

  mutable std::size_t nb_pos;
  mutable std::size_t nb_edge_pos;
  //fills 'out' and returns the hausdorff distance for calibration of the color_ramp.

  double compute_distances(const Face_graph& m, const std::vector<Kernel::Point_3>& sample_points,
                           std::vector<double>& out)const
  {
    typedef CGAL::AABB_face_graph_triangle_primitive<Face_graph> Primitive;
    typedef CGAL::AABB_traits<Kernel, Primitive> Traits;
    typedef CGAL::AABB_tree< Traits > Tree;

    Tree tree( faces(m).first, faces(m).second, m);
    tree.build();
    boost::graph_traits<Face_graph>::vertex_descriptor vd = *(vertices(m).first);
    Traits::Point_3 hint = get(CGAL::vertex_point,m, vd);

#if !defined(CGAL_LINKED_WITH_TBB)
    double hdist = 0;
    for(std::size_t i = 0; i<sample_points.size(); ++i)
    {
      hint = tree.closest_point(sample_points[i], hint);
      Kernel::FT dist = squared_distance(hint,sample_points[i]);
      double d = CGAL::sqrt(dist);
      out[i]= d;
      if (d>hdist) hdist=d;
    }
      return hdist;
#else
    double distance=0;
    Distance_computation<Tree, Kernel::Point_3> f(tree, hint, sample_points, out);
    tbb::parallel_for(tbb::blocked_range<std::size_t>(0, sample_points.size()), f);
    for(std::size_t i = 0; i< out.size(); ++i){
      if(out[i] > distance)
        distance = out[i];
    }
    return distance;
#endif
  }

  void computeElements()const
  {
    QApplication::setOverrideCursor(Qt::WaitCursor);
    m_vertices.resize(0);
    edge_vertices.resize(0);
    normals.resize(0);
    colors.resize(0);

    typedef Kernel::Vector_3            Vector;
    typedef boost::graph_traits<Face_graph>::face_descriptor   face_descriptor;
    typedef boost::graph_traits<Face_graph>::vertex_descriptor vertex_descriptor;

    typedef boost::property_map<Face_graph,CGAL::vertex_point_t>::type VPmap;
    VPmap vpmap = get(CGAL::vertex_point,*poly);

    //facets
    {
      boost::container::flat_map<face_descriptor, Vector> face_normals_map;
      boost::associative_property_map< boost::container::flat_map<face_descriptor, Vector> >
          nf_pmap(face_normals_map);
      boost::container::flat_map<vertex_descriptor, Vector> vertex_normals_map;
      boost::associative_property_map< boost::container::flat_map<vertex_descriptor, Vector> >
          nv_pmap(vertex_normals_map);

      PMP::compute_normals(*poly, nv_pmap, nf_pmap);
      std::vector<Kernel::Point_3> total_points(0);

      for(boost::graph_traits<Face_graph>::face_descriptor f : faces(*poly)) {
        Vector nf = get(nf_pmap, f);
        typedef FacetTriangulator<Face_graph, Kernel, boost::graph_traits<Face_graph>::vertex_descriptor> FT;

        //compute distance with other polyhedron
        //sample facet
        std::vector<Kernel::Point_3> sampled_points;
        std::size_t nb_points =  (std::max)((int)std::ceil(nb_pts_per_face * PMP::face_area(f,*poly,PMP::parameters::geom_traits(Kernel()))),
                                            1);
        Kernel::Point_3 &p = get(vpmap,target(halfedge(f,*poly),*poly));
        Kernel::Point_3 &q = get(vpmap,target(next(halfedge(f,*poly),*poly),*poly));
        Kernel::Point_3 &r = get(vpmap,target(next(next(halfedge(f,*poly),*poly),*poly),*poly));
        CGAL::Random_points_in_triangle_3<Kernel::Point_3> g(p, q, r);
        std::copy_n(g, nb_points, std::back_inserter(sampled_points));
        sampled_points.push_back(p);
        sampled_points.push_back(q);
        sampled_points.push_back(r);

        //triangle facets with sample points for color display
        FT triangulation(f,sampled_points,nf,poly);

        if(triangulation.cdt->dimension() != 2 )
        {
          qDebug()<<"Error : cdt not right (dimension != 2). Facet not displayed";
          continue;
        }

        //iterates on the internal faces to add the vertices to the positions
        //and the normals to the appropriate vectors

        for(FT::CDT::Finite_faces_iterator
            ffit = triangulation.cdt->finite_faces_begin(),
            end = triangulation.cdt->finite_faces_end();
            ffit != end; ++ffit)
        {
          if(ffit->info().is_external)
            continue;

          for (int i = 0; i<3; ++i)
          {
            total_points.push_back(ffit->vertex(i)->point());
            m_vertices.push_back(ffit->vertex(i)->point().x());
            m_vertices.push_back(ffit->vertex(i)->point().y());
            m_vertices.push_back(ffit->vertex(i)->point().z());

            normals.push_back(nf.x());
            normals.push_back(nf.y());
            normals.push_back(nf.z());
          }
        }
      }
      //compute the distances
      typedef CGAL::Spatial_sort_traits_adapter_3<Kernel,
                CGAL::Pointer_property_map<Kernel::Point_3>::type > Search_traits_3;

      std::vector<double> distances(total_points.size());
      std::vector<std::size_t> indices;
      indices.reserve(total_points.size());
      std::copy(boost::counting_iterator<std::size_t>(0),
                boost::counting_iterator<std::size_t>(total_points.size()),
                std::back_inserter(indices));
      spatial_sort(indices.begin(),
                   indices.end(),
                  Search_traits_3(CGAL::make_property_map(total_points)));
      std::vector<Kernel::Point_3> sorted_points(total_points.size());
      for(std::size_t i = 0; i < sorted_points.size(); ++i)
      {
        sorted_points[i] = total_points[indices[i]];
      }

      double hausdorff = compute_distances(*poly_B,
                                           sorted_points,
                                           distances);
      if(hausdorff == 0)
        hausdorff++;
      //compute the colors
      colors.resize(sorted_points.size()*3);
      for(std::size_t i=0; i<sorted_points.size(); ++i)
      {
        std::size_t k = indices[i];
        double d = distances[i]/hausdorff;
        colors[3*k]=thermal_ramp.r(d);
        colors[3*k+1]=thermal_ramp.g(d);
        colors[3*k+2]=thermal_ramp.b(d);
      }
    }

    //edges
    {
      //Lines
      typedef Kernel::Point_3                Point;
      typedef boost::graph_traits<Face_graph>::edge_descriptor        edge_descriptor;

      for(edge_descriptor he : edges(*poly)){
        const Point& a = get(vpmap,target(he,*poly));
        const Point& b = get(vpmap,source(he,*poly));
        {

          edge_vertices.push_back(a.x());
          edge_vertices.push_back(a.y());
          edge_vertices.push_back(a.z());

          edge_vertices.push_back(b.x());
          edge_vertices.push_back(b.y());
          edge_vertices.push_back(b.z());
        }
      }
    }

    Tc* tc = getTriangleContainer(0);
    Ec* ec = getEdgeContainer(0);

    tc->allocate(
          Tc::Flat_vertices,
          m_vertices.data(),
          static_cast<GLsizei>(m_vertices.size()*sizeof(float)));
    tc->allocate(Tc::Flat_normals,
                 normals.data(),
                 static_cast<GLsizei>(normals.size()*sizeof(float)));
    tc->allocate(Tc::FColors,
                 colors.data(),
                 static_cast<GLsizei>(colors.size()*sizeof(float)));

    ec->allocate(Ec::Vertices,
                 edge_vertices.data(),
                 static_cast<GLsizei>(edge_vertices.size()*sizeof(float)));
    nb_pos = m_vertices.size();
    nb_edge_pos = edge_vertices.size();
    compute_bbox();
    setBuffersFilled(true);
    QApplication::restoreOverrideCursor();
  }

  void initializeBuffers(Viewer_interface *viewer)const
  {
    Tc* tc = getTriangleContainer(0);
    Ec* ec = getEdgeContainer(0);
    tc->initializeBuffers(viewer);
    ec->initializeBuffers(viewer);
    tc->setFlatDataSize(nb_pos);
    ec->setFlatDataSize(nb_edge_pos);
    m_vertices.clear();
    edge_vertices.clear();
    normals.clear();
    colors.clear();
    m_vertices.shrink_to_fit();
    edge_vertices.shrink_to_fit();
    normals.shrink_to_fit();
    colors.shrink_to_fit();
  }
};
class DistancePlugin :
    public QObject,
    public Polyhedron_demo_plugin_interface
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0" FILE "distance_plugin.json")

  typedef Kernel::Point_3 Point_3;
public:
  //decides if the plugin's actions will be displayed or not.
  bool applicable(QAction*) const
  {
    return scene->selectionIndices().size() == 2 &&
        qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().first())) &&
        qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().last()));
  }
  //the list of the actions of the plugin.
  QList<QAction*> actions() const
  {
    return _actions;
  }
  //this acts like a constructor for the plugin. It gets the references to the mainwindow and the scene, and connects the action.
  void init(QMainWindow* mw, Scene_interface* sc, Messages_interface* mi)
  {
    //gets the reference to the message interface, to display text in the console widget
    this->messageInterface = mi;
    //get the references
    this->scene = sc;
    this->mw = mw;
    //creates the action
    QAction *actionComputeDistance= new QAction(QString("Compute Distance Between Polyhedra"), mw);
    //specifies the subMenu
    actionComputeDistance->setProperty("subMenuName", "Polygon Mesh Processing");
    //links the action
    if(actionComputeDistance) {
      connect(actionComputeDistance, SIGNAL(triggered()),
              this, SLOT(createDistanceItems()));
      _actions << actionComputeDistance;
    }
  }
public Q_SLOTS:
  void createDistanceItems()
  {
    bool ok = false;
    nb_pts_per_face = QInputDialog::getInt(mw, tr("Sampling"),
                                               tr("Number of points per face:"),40, 1,2147483647,1, &ok);
    if (!ok)
      return;

    //check the initial conditions
    Scene_face_graph_item* itemA = qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().first()));
    Scene_face_graph_item* itemB = qobject_cast<Scene_face_graph_item*>(scene->item(scene->selectionIndices().last()));
    if(! CGAL::is_triangle_mesh(*itemA->polyhedron()) ||
       !CGAL::is_triangle_mesh(*itemB->polyhedron()) ){
      CGAL::Three::Three::error(QString("Distance not computed. (Both polyhedra must be triangulated)"));
      return;
    }
    QApplication::setOverrideCursor(Qt::WaitCursor);
    Scene_distance_polyhedron_item* new_itemA = new Scene_distance_polyhedron_item(itemA->polyhedron(),itemB->polyhedron(), itemB->name(), nb_pts_per_face);
    Scene_distance_polyhedron_item* new_itemB = new Scene_distance_polyhedron_item(itemB->polyhedron(),itemA->polyhedron(), itemA->name(), nb_pts_per_face);
    itemA->setVisible(false);
    itemB->setVisible(false);
    new_itemA->setName(QString("%1 to %2").arg(itemA->name()).arg(itemB->name()));
    new_itemB->setName(QString("%1 to %2").arg(itemB->name()).arg(itemA->name()));
    scene->addItem(new_itemA);
    scene->addItem(new_itemB);
    QApplication::restoreOverrideCursor();
  }
private:
  int nb_pts_per_face;
  QList<QAction*> _actions;
  Messages_interface* messageInterface;
  //The reference to the scene
  Scene_interface* scene;
  //The reference to the main window
  QMainWindow* mw;
};
#include "Distance_plugin.moc"


// The MIT License (MIT)
//
// Copyright (c) 2015 Jonathan McCluskey and William Harding
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <memory>
#include <gmpxx.h>
#include "Rsa.h"
#include "Utilities.h"

#include <sstream>
#include <omp.h>

namespace
{
    const unsigned int BASE = 10;
}

BOOST_AUTO_TEST_CASE(Rsa_test_simple)
{
    //p = 193
    //q = 101
    Rsa::PrivateKey priv("2743", BASE);
    Rsa::PublicKey  pub((mpz_class("19493", BASE)), (mpz_class("7", BASE)));

    std::stringstream message;
    message << "aaaaaa";
    {
        mpz_class plain_text = Utilities::StringToNumber(message.str());

        mpz_class cipher_text = Rsa::Encipher(plain_text, pub, true);
        mpz_class deciphered_text = Rsa::Decipher(cipher_text, priv, pub, true);

        BOOST_CHECK_EQUAL(Utilities::NumberToString(plain_text), Utilities::NumberToString(deciphered_text));
    }
    {
        message << "a";
        mpz_class plain_text = Utilities::StringToNumber(message.str());

        mpz_class cipher_text = Rsa::Encipher(plain_text, pub, true);
        mpz_class deciphered_text = Rsa::Decipher(cipher_text, priv, pub, true);

        BOOST_CHECK_EQUAL(Utilities::NumberToString(plain_text), Utilities::NumberToString(deciphered_text));
    }
}

BOOST_AUTO_TEST_CASE(Rsa_test_alphabet)
{
    Rsa::PrivateKey priv("2743", BASE);
    Rsa::PublicKey  pub((mpz_class("19493", BASE)), (mpz_class("7", BASE)));

    std::stringstream message;
    message << "abcdefghijklmnopqrstuvwxyz";
    mpz_class plain_text = Utilities::StringToNumber(message.str());

    mpz_class cipher_text = Rsa::Encipher(plain_text, pub, true);
    mpz_class deciphered_text = Rsa::Decipher(cipher_text, priv, pub, true);

    BOOST_CHECK_EQUAL(Utilities::NumberToString(plain_text), Utilities::NumberToString(deciphered_text));
}

BOOST_AUTO_TEST_CASE(Rsa_test_signature)
{
    Rsa::PrivateKey priv("2743", BASE);
    Rsa::PublicKey  pub((mpz_class("19493", BASE)), (mpz_class("7", BASE)));

    std::stringstream message;
    message << "abcdefghijklmnopqrstuvwxyz";
    mpz_class plain_text = Utilities::StringToNumber(message.str());

    mpz_class cipher_text = Rsa::Sign(plain_text, priv, pub, true);
    mpz_class deciphered_text = Rsa::Unsign(cipher_text, pub, true);

    BOOST_CHECK_EQUAL(Utilities::NumberToString(plain_text), Utilities::NumberToString(deciphered_text));
}

BOOST_AUTO_TEST_CASE(Rsa_test_1)
{
    Rsa::PrivateKey priv;
    Rsa::PublicKey  pub;
    std::tie(priv, pub) = Rsa::GenerateKeys(1024);

    std::stringstream message;
    message << "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message";
    mpz_class plain_text = Utilities::StringToNumber(message.str());

    double start = omp_get_wtime();
    mpz_class cipher_text = Rsa::Encipher(plain_text, pub, true);
    double end = omp_get_wtime();
    std::cout << "RSA Encipher (1024 bit) Test 1 Timing " << end - start << "s" << std::endl;

    start = omp_get_wtime();
    mpz_class deciphered_text = Rsa::Decipher(cipher_text, priv, pub, true);
    end = omp_get_wtime();
    std::cout << "RSA Decipher (1024 bit) Test 1 Timing " << end - start << "s" << std::endl;

    BOOST_CHECK_EQUAL(Utilities::NumberToString(plain_text), Utilities::NumberToString(deciphered_text));
}

BOOST_AUTO_TEST_CASE(Rsa_test_2)
{
    Rsa::PrivateKey priv;
    Rsa::PublicKey  pub;
    std::tie(priv, pub) = Rsa::GenerateKeys(1024);

    std::stringstream message;
    message << "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message" <<
               "this is a very secret message";

    mpz_class plain_text = Utilities::StringToNumber(message.str());

    double start = omp_get_wtime();
    mpz_class signed_text = Rsa::Sign(plain_text, priv, pub, true);
    double end = omp_get_wtime();
    std::cout << "RSA Signature (1024 bit) Test 1 Timing " << end - start << "s" << std::endl;

    start = omp_get_wtime();
    mpz_class unsigned_text = Rsa::Unsign(signed_text, pub, true);
    end = omp_get_wtime();
    std::cout << "RSA Verify (1024 bit) Test 1 Timing " << end - start << "s" << std::endl;

    BOOST_CHECK(plain_text == unsigned_text);
}

BOOST_AUTO_TEST_CASE(Rsa_test_3)
{
    Rsa::PrivateKey priv1("2743", BASE);
    Rsa::PublicKey  pub1((mpz_class("19493", BASE)), (mpz_class("7", BASE)));

    //p = 107
    //q = 139
    Rsa::PrivateKey priv2("8359", BASE);
    Rsa::PublicKey  pub2((mpz_class("14873", BASE)), (mpz_class("7", BASE)));

    mpz_class plain_text("654321");

    mpz_class signed_text = Rsa::Sign(plain_text, priv1, pub1, true);
    mpz_class signed_cipher_text = Rsa::Encipher(signed_text, pub2, false);
    mpz_class new_signed_text = Rsa::Decipher(signed_cipher_text, priv2, pub2, false);
    BOOST_CHECK(signed_text == new_signed_text);
    mpz_class unsigned_plain_text = Rsa::Unsign(new_signed_text, pub1, true);

    BOOST_CHECK(plain_text == unsigned_plain_text);
}


/*
 * AlignType.hpp
 *
 *  Created on: 21.02.2019
 *      Author: tomlucas
 */

#ifndef ESTIMATORS_ALIGNTYPE_HPP_
#define ESTIMATORS_ALIGNTYPE_HPP_

#include <Eigen/Core>

namespace zavi::estimator{
	/**
	 * Defines which type to use for the calibration matrix between imu and body frame
	 */
	template<typename T>
	using ALIGNMENT_TYPE=Eigen::Matrix<T,4,4>;
	static constexpr int ALIGNMENT_SIZE=16;
}

#endif /* ESTIMATORS_ALIGNTYPE_HPP_ */


///////////////////////////////////////////////////////////////////
//  Copyright Eduardo Quintana 2021
//  Copyright Janek Kozicki 2021
//  Copyright Christopher Kormanyos 2021
//  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_MATH_FFT_BSLBACKEND_HPP
  #define BOOST_MATH_FFT_BSLBACKEND_HPP

  #include <algorithm>
  #include <cmath>
  #include <type_traits>
  #include <boost/math/fft/multiprecision_complex.hpp>

  #include <boost/math/fft/algorithms.hpp>
  #include <boost/math/fft/real_algorithms.hpp>
  #include <boost/math/fft/dft_api.hpp>

  namespace boost { namespace math {  namespace fft {

  namespace detail {

  /*
    Boost DFT backend:
    It handles RingTypes and it calls the appropriate specialized functions if
    the type is complex.

    A type is considered "complex" if is_boost_complex::value == true. A user-defined
    type can become complex by specializing that trait.

    We have specialized algorithms for complex numbers and general purpose DFT
    that need the specification of a root of unity.
    The general purpose DFT work with complex and non-complex types, but its
    performance and precision could be lower than the specialized complex
    versions.

    This interface selects general purpose DFT for non-complex types,
    and for complex types the default behaviour is to use the specialized
    complex algorithms, unless the user provides a root of unity 'W', in which case
    the interface will execute general purpose DFT using W.
  */
  template<class RingType, class allocator_t = std::allocator<RingType> >
  class bsl_backend
  {
  public:
    using value_type     = RingType;
    using allocator_type = allocator_t;

  private:
    enum plan_type { forward_plan , backward_plan};

    void execute(plan_type plan, const RingType * in, RingType* out)const
    {
      const long N = static_cast<long>(size());
      const int sign = (plan == forward_plan ? 1 : -1);

      // select the implementation according to the DFT size
      switch(N)
      {
        case 0:
          return;
        case 1:
          out[0]=in[0];
          return;
        case 2:
          detail::complex_dft_2(in,out,sign);
          return;
      }

      if( detail::is_power2(N) )
      {
        detail::complex_dft_power2(in,in+N,out,sign);
      }
      else if(detail::is_prime(N))
      {
        // detail::complex_dft_prime_bruteForce(in,in+N,out,sign);
        detail::complex_dft_prime_rader(in,in+N,out,sign,alloc);
      }
      else
      {
        detail::complex_dft_composite(in,in+N,out,sign,alloc);
      }
    }

  public:

    // the provided root of unity is used instead of exp(-i 2 pi/n)
    constexpr bsl_backend(std::size_t n, const allocator_type& in_alloc = allocator_type{}):
        alloc{in_alloc},
        my_size{n}
    {
    }

    ~bsl_backend()
    {
    }

    void resize(std::size_t new_size)
    {
      my_size = new_size;
    }
    RingType inverse_root(RingType root) const
    {
      return detail::power(root,size()-1);
    }

    constexpr std::size_t size() const { return my_size; }

    void forward(const RingType* in, RingType* out) const
    {
      execute(forward_plan,in,out);
    }

    void backward(const RingType* in, RingType* out) const
    {
      execute(backward_plan,in,out);
    }
    void dft(const RingType* in, RingType* out, RingType w) const
    {
      const long N = static_cast<long>(size());
      // select the implementation according to the DFT size
      if( detail::is_power2(N))
      {
        detail::dft_power2(in,in+N,out,w);
      }
      else
      {
        detail::dft_composite(in,in+N,out,w,alloc);
      }
    }

  private:
    allocator_type alloc;
    std::size_t my_size{};
  };

  template<class T, class allocator_t = std::allocator<T> >
  class bsl_rfft_backend
  {
  public:
    using value_type     = T;
    using allocator_type = allocator_t;

    // the provided root of unity is used instead of exp(-i 2 pi/n)
    constexpr bsl_rfft_backend(std::size_t n, const allocator_type& in_alloc = allocator_type{}):
        alloc{in_alloc},
        my_size{n}
    {
    }

    ~bsl_rfft_backend()
    {
    }

    void resize(std::size_t new_size)
    {
      my_size = new_size;
    }

    constexpr std::size_t size() const { return my_size; }
    constexpr std::size_t unique_complex_size() const { return my_size/2 + 1;}

    void real_to_halfcomplex(const value_type* in, value_type* out) const
    {
      const long N = static_cast<long>(size());
      // select the implementation according to the DFT size
      switch(N)
      {
        case 0:
          return;
        case 1:
          out[0]=in[0];
          return;
        case 2:
          detail::real_dft_2(in,out,1);
          return;
      }
      if( detail::is_power2(N))
      {
        detail::real_dft_power2(in,in+N,out,1);
      }else
      {
        detail::real_dft_composite(in,in+N,out,1,alloc);
      }
      //if(detail::is_prime(N))
      //{
      //  detail::real_dft_prime_rader(in,in+N,out,sign,alloc);
      //}
    }
    void halfcomplex_to_real(const value_type* in, value_type* out) const
    {
      const long N = static_cast<long>(size());
      // select the implementation according to the DFT size
      switch(N)
      {
        case 0:
          return;
        case 1:
          out[0]=in[0];
          return;
        case 2:
          detail::real_dft_2(in,out,1);
          return;
      }
      if( detail::is_power2(N))
      {
        detail::real_inverse_dft_power2(in,in+N,out,1);
      }else
      {
        detail::real_inverse_dft_composite(in,in+N,out,1,alloc);
      }
      //if(detail::is_prime(N))
      //{
      //  detail::real_inverse_dft_prime_rader(in,in+N,out,sign,alloc);
      //}
    }

  private:
    allocator_type alloc;
    std::size_t my_size{};
  };

  } // namespace detail

  template<class RingType = std::complex<double>, class Allocator_t = std::allocator<RingType> >
  using bsl_dft = detail::complex_dft<detail::bsl_backend,RingType,Allocator_t>;

  template<class T = double, class Allocator_t = std::allocator<T> >
  using bsl_rdft = detail::real_dft<detail::bsl_rfft_backend,T,Allocator_t>;

  template<class RingType = std::complex<double>, class Allocator_t = std::allocator<RingType> >
  using bsl_algebraic_dft = detail::algebraic_dft<detail::bsl_backend,RingType,Allocator_t>;

  using bsl_transform = transform< bsl_dft<> >;
  using bsl_algebraic_transform = transform< bsl_algebraic_dft<> >;
  using bsl_real_transform = transform< bsl_rdft<> >;

  } } } // namespace boost::math::fft

#endif // BOOST_MATH_FFT_BSLBACKEND_HPP


#include <wav_utils.hxx>
#include <fftw3.h>
#include <sndfile.h>

#include <iostream>
#include <iomanip>
#include <chrono>

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/foreach.hpp>

// read a wav file and put the file's properties into wav samples
bool
read_wav(wav_samples& ws)
{
  // http://www.mega-nerd.com/libsndfile/api.html#open
  // When opening a file for read, the format field should be set to zero before calling sf_open().
  SF_INFO sfinfo;
  sfinfo.format = 0x0;

  std::cout << "reading wav " << ws.filename << std::endl;
  SNDFILE* sfile = sf_open(ws.filename.c_str(), SFM_READ, &sfinfo);
  if (sfile == NULL) {
    std::cout << "unable to open file " << ws.filename << std::endl;
    std::cout << "error " << sf_strerror(sfile) << std::endl;
    exit(-1);
  }

  std::cout << "num channels    " << sfinfo.channels;
  std::cout << " frames         " << sfinfo.frames;
  std::cout << " sample rate    " << sfinfo.samplerate << std::endl;

  double *samples = (double*) malloc(sizeof(double)*sfinfo.frames*sfinfo.channels);
  sf_readf_double(sfile, samples, sfinfo.frames);

  ws.channels = sfinfo.channels;
  ws.frames = sfinfo.frames;
  ws.samplerate = sfinfo.samplerate;
  ws.samples = samples;

  return true;
}

// now that we know how long of a fft we want, do it on one set of data.
void
add_fft(wav_samples& ws, long int fftsize)
{

  auto begin = std::chrono::high_resolution_clock::now();

  // http://www.fftw.org/fftw3_doc/Complex-One_002dDimensional-DFTs.html#Complex-One_002dDimensional-DFTs
  std::complex<double> *in  = (std::complex<double>*) fftw_malloc(sizeof(std::complex<double>) * fftsize);
  std::complex<double> *out = (std::complex<double>*) fftw_malloc(sizeof(std::complex<double>) * fftsize);
  fftw_plan p = fftw_plan_dft_1d(fftsize,
                                 reinterpret_cast<fftw_complex*>(in),
                                 reinterpret_cast<fftw_complex*>(out),
                                           FFTW_FORWARD, FFTW_ESTIMATE);

  auto end = std::chrono::high_resolution_clock::now();
  std::cout << "create plan " << std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()/1000000000.0 << " sec ";
  begin = std::chrono::high_resolution_clock::now();

  for(int i=0; i<ws.frames; i++) {
    in[i] = ws.samples[i*ws.channels];
  }
  for(int i=ws.frames; i<fftsize; i++) {
    in[i] = 0;
  }

  fftw_execute(p);

  end = std::chrono::high_resolution_clock::now();
  std::cout << "execute " << std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()/1000000000.0 << "sec" << std::endl;

  ws.fft = out;

  fftw_destroy_plan(p);
  fftw_free(in);
  // don't free out. it's returned as part of ws.
}

void write_wav(std::vector<double>& samples,
               long int length,
               int samplerate,
               int channels,
               const std::string &filename)
{
  SF_INFO sfinfo;
  // http://www.mega-nerd.com/libsndfile/api.html#open
  sfinfo.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16;
  sfinfo.samplerate = samplerate;
  sfinfo.channels = channels;

  std::cout << "writing wav " << filename << std::endl;
  SNDFILE* sfile = sf_open(filename.c_str(), SFM_WRITE, &sfinfo);
  if (sfile == NULL) {
    std::cout << "unable to open file " << filename << std::endl;
    std::cout << "error " << sf_strerror(sfile) << std::endl;
    exit(-1);
  }
  sf_writef_double(sfile, samples.data(), length);
  sf_close(sfile);
}

void write_wav_for_fft(std::complex<double> *fft,
                       long int fftsize,
                       int samplerate,
                       const char* filename)
{

  // use malloc instead of stack array (ie samples[numsamples*channel]) as this will likely
  // exceed stack size limit.
  const int channels = 2;
  std::vector<double> samples(fftsize*channels,0);

  // first need to find the right scale factor.
  double maxval=0;
  for(int i=0; i<fftsize; i++) {
    maxval = std::max(maxval, std::abs(fft[i]));
  }
  for(int i=0; i<fftsize; i++) {
    samples[2*i]   = fft[i].real()/maxval;
    samples[2*i+1] = fft[i].imag()/maxval;
  }

  write_wav(samples,
            fftsize,
            samplerate,
            channels,
            filename);
}

correlation_data
correlate_wavs(wav_samples& ws1,
               wav_samples& ws2,
               long int     fftsize,
               int          samplerate,
               bool         savecrosscorrelation)
{
  std::complex<double> *in  = (std::complex<double>*) fftw_malloc(sizeof(std::complex<double>) * fftsize);
  std::complex<double> *out = (std::complex<double>*) fftw_malloc(sizeof(std::complex<double>) * fftsize);

  fftw_plan p_backward = fftw_plan_dft_1d(fftsize,
                                          reinterpret_cast<fftw_complex*>(in),
                                          reinterpret_cast<fftw_complex*>(out),
                                          FFTW_BACKWARD, FFTW_ESTIMATE);

  // piecewise multiple the FFT
  for (int i=0; i<fftsize; i++) {
    in[i] = std::conj(ws1.fft[i]) * ws2.fft[i];
  }
  fftw_execute(p_backward);

  boost::accumulators::accumulator_set< double,
                                        boost::accumulators::features<
                                          boost::accumulators::stats<boost::accumulators::tag::variance,
                                                                     boost::accumulators::tag::max> > > acc;

  //accumulator_set<int, stats<tag::variance> > acc2;
  int maxid = 0;
  for (int i=0; i<fftsize; i++) {
    double mag = std::abs(out[i]);
    acc(mag);
    if (mag == boost::accumulators::max(acc)) { maxid = i; }
  }

  double num_std = (boost::accumulators::max(acc)-
                    boost::accumulators::mean(acc))/sqrt(boost::accumulators::variance(acc));

  std::cout << std::fixed << std::setprecision(2)
            << ws1.filename << " " << ws2.filename
            << " mean "      << std::setw(14) << std::right << boost::accumulators::mean(acc)
            << "\tvariance " << std::setw(14) << std::right << sqrt(boost::accumulators::variance(acc))
            << "\tmax "      << std::setw(14) << std::right << boost::accumulators::max(acc)
            << "\tnum std "  << std::setw(14) << std::right << num_std << std::endl;

#if 0
  write_wav_for_fft(out,
                    fftsize,
                    samplerate,
                    "correlate.wav");
#endif

  // the max correlation id will be positive if the first input ahead of the second.
  // this doesn't appear to really be true. rather, it's the reverse.
  // ie if in1(t) ~= in2(t+maxcorrel_id)
  correlation_data retval;

  if (maxid>fftsize/2) {
    // the order to correlation data matters.
    retval = correlation_data(&ws1, &ws2, fftsize-maxid, num_std);
    if (savecrosscorrelation) {
      // since I'm swapping the order of ws1 and ws2, the output fft needs to be reversed.
      std::reverse(out, out+fftsize);
    }
  } else {
    retval = correlation_data(&ws2, &ws1, maxid,         num_std);
  }

  if (savecrosscorrelation) {
    retval.cross_correlation = out;
  } else {
    fftw_free(out);
  }

  return retval;
}

std::ostream&
operator<< (std::ostream& stream,
            Timeline& tl)
{
  stream << "timeline \n";
  BOOST_FOREACH(auto cl, tl.getClips()) {
    stream << "  " << cl.wss->filename << ":" << cl.offset/cl.wss->samplerate << std::endl;
  }

  return stream;
}

void
mergeTimelines(std::shared_ptr<Timeline> tl1,
               std::shared_ptr<Timeline> tl2,
               long int offset) {

  std::cout << "merging " << *tl1 << " with " << *tl2 << "    offset " << offset/double(tl1->getClips().begin()->wss->samplerate) << std::endl;

  BOOST_FOREACH(Timeline::clip c, tl2->getClips()) {
    tl1->addClip(c.wss, c.offset+offset);
    c.wss->timeline = tl1;
  }

  tl2->getClips().clear();
}

void
correlation_data::write_wav(const std::string &filename)
{
  int length = std::max(wss1->frames, wss2->frames+offset);

  const int channels = 2;
  std::vector<double> samples(length*channels, 0);

  for(int i=0; i<wss1->frames; i++) {
    samples[channels*i] = wss1->samples[i*wss1->channels];
  }
  for(int i=0; i<wss2->frames; i++) {
    samples[channels*(i+offset)+1] = wss2->samples[i*wss2->channels];
  }

  ::write_wav(samples, length, wss1->samplerate, channels, filename);
}