#include <CGAL/Simple_cartesian.h>
#include <CGAL/Monge_via_jet_fitting.h>

#include <fstream>
#include <cassert>

#include <CGAL/property_map.h>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

using namespace std;

#include "PolyhedralSurf.h"
#include "PolyhedralSurf_operations.h"
#include "PolyhedralSurf_rings.h"

//Kernel of the PolyhedralSurf
typedef double                DFT;
typedef CGAL::Simple_cartesian<DFT>  Data_Kernel;
typedef Data_Kernel::Point_3  DPoint;
typedef Data_Kernel::Vector_3 DVector;

//HDS
typedef PolyhedralSurf::Vertex_handle Vertex_handle;
typedef PolyhedralSurf::Vertex Vertex;
typedef PolyhedralSurf::Halfedge_handle Halfedge_handle;
typedef PolyhedralSurf::Halfedge Halfedge;
typedef PolyhedralSurf::Vertex_iterator Vertex_iterator;
typedef PolyhedralSurf::Facet_handle Facet_handle;
typedef PolyhedralSurf::Facet Facet;

struct Hedge_cmp{
  bool operator()(Halfedge_handle a,  Halfedge_handle b) const{
    return &*a < &*b;
  }
};

struct Facet_cmp{
  bool operator()(Facet_handle a, Facet_handle b) const{
    return &*a < &*b;
  }
};

//Vertex property map, with std::map
typedef std::map<Vertex*, int> Vertex2int_map_type;
typedef boost::associative_property_map< Vertex2int_map_type > Vertex_PM_type;
typedef T_PolyhedralSurf_rings<PolyhedralSurf, Vertex_PM_type > Poly_rings;

//Hedge property map, with enriched Halfedge with its length
// typedef HEdge_PM<PolyhedralSurf> Hedge_PM_type;
// typedef T_PolyhedralSurf_hedge_ops<PolyhedralSurf, Hedge_PM_type> Poly_hedge_ops;
//Hedge property map, with std::map
typedef std::map<Halfedge_handle, double, Hedge_cmp> Hedge2double_map_type;
typedef boost::associative_property_map<Hedge2double_map_type> Hedge_PM_type;
typedef T_PolyhedralSurf_hedge_ops<PolyhedralSurf, Hedge_PM_type> Poly_hedge_ops;

// //Facet property map with enriched Facet with its normal
// typedef Facet_PM<PolyhedralSurf> Facet_PM_type;
// typedef T_PolyhedralSurf_facet_ops<PolyhedralSurf, Facet_PM_type> Poly_facet_ops;
//Facet property map, with std::map
typedef std::map<Facet_handle, Vector_3, Facet_cmp> Facet2normal_map_type;
typedef boost::associative_property_map<Facet2normal_map_type> Facet_PM_type;
typedef T_PolyhedralSurf_facet_ops<PolyhedralSurf, Facet_PM_type> Poly_facet_ops;

typedef double                   LFT;
typedef CGAL::Simple_cartesian<LFT>     Local_Kernel;
typedef CGAL::Monge_via_jet_fitting<Data_Kernel> My_Monge_via_jet_fitting;
typedef My_Monge_via_jet_fitting::Monge_form My_Monge_form;

// default parameter values and global variables
unsigned int d_fitting = 2;
unsigned int d_monge = 2;
unsigned int nb_rings = 0;//seek min # of rings to get the required #pts
unsigned int nb_points_to_use = 0;//
bool verbose = false;
unsigned int min_nb_points = (d_fitting + 1) * (d_fitting + 2) / 2;

//gather points around the vertex v using rings on the
//polyhedralsurf. the collection of points resorts to 3 alternatives:
// 1. the exact number of points to be used
// 2. the exact number of rings to be used
// 3. nothing is specified
void gather_fitting_points(Vertex* v,
                           std::vector<DPoint> &in_points,
                           Vertex_PM_type& vpm)
{
  //container to collect vertices of v on the PolyhedralSurf
  std::vector<Vertex*> gathered;
  //initialize
  in_points.clear();

  //OPTION -p nb_points_to_use, with nb_points_to_use != 0. Collect
  //enough rings and discard some points of the last collected ring to
  //get the exact "nb_points_to_use"
  if ( nb_points_to_use != 0 ) {
    Poly_rings::collect_enough_rings(v, nb_points_to_use, gathered, vpm);
    if ( gathered.size() > nb_points_to_use ) gathered.resize(nb_points_to_use);
  }
  else { // nb_points_to_use=0, this is the default and the option -p is not considered;
    // then option -a nb_rings is checked. If nb_rings=0, collect
    // enough rings to get the min_nb_points required for the fitting
    // else collect the nb_rings required
    if ( nb_rings == 0 )
      Poly_rings::collect_enough_rings(v, min_nb_points, gathered, vpm);
    else Poly_rings::collect_i_rings(v, nb_rings, gathered, vpm);
  }

  //store the gathered points
  std::vector<Vertex*>::iterator
    itb = gathered.begin(), ite = gathered.end();
  CGAL_For_all(itb,ite) in_points.push_back((*itb)->point());
}

///////////////MAIN///////////////////////////////////////////////////////
#if defined(CGAL_USE_BOOST_PROGRAM_OPTIONS) && ! defined(DONT_USE_BOOST_PROGRAM_OPTIONS)
int main(int argc, char *argv[])
#else
int main()
#endif
{
  string if_name_string;
  string if_name; //input file name
  string w_if_name;  //as above, but / replaced by _
  string res4openGL_fname;
  string verbose_fname;
  std::ofstream out_4ogl, out_verbose;

  try {
#if defined(CGAL_USE_BOOST_PROGRAM_OPTIONS) && ! defined(DONT_USE_BOOST_PROGRAM_OPTIONS)
    po::options_description desc("Allowed options");
    desc.add_options()
      ("help,h", "produce help message.")
      ("input-file,f", po::value<string>(&if_name_string)->default_value(CGAL::data_file_path("meshes/ellipe0.003.off")),
       "name of the input off file")
      ("degree-jet,d", po::value<unsigned int>(&d_fitting)->default_value(2),
       "degree of the jet, 1 <= degre-jet <= 4")
      ("degree-monge,m", po::value<unsigned int>(&d_monge)->default_value(2),
       "degree of the Monge rep, 1 <= degree-monge <= degree-jet")
      ("nb-rings,a", po::value<unsigned int>(&nb_rings)->default_value(0),
       "number of rings to collect neighbors. 0 means collect enough rings to make appro possible a>=1 fixes the nb of rings to be collected")
      ("nb-points,p", po::value<unsigned int>(&nb_points_to_use)->default_value(0),
       "number of neighbors to use.  0 means this option is not considered, this is the default p>=1 fixes the nb of points to be used")
      ("verbose,v", po::value<bool>(&verbose)->default_value(false),
       "verbose output on text file")
      ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
      cout << desc << "\n";
      return 1;
    }
#else
    std::cerr << "Command-line options require Boost.ProgramOptions" << std::endl;
    if_name_string = CGAL::data_file_path("meshes/ellipe0.003.off");
    d_fitting = 2;
    d_monge = 2;
    nb_rings = 0;
    nb_points_to_use = 0;
    verbose = false;
#endif
  }
  catch(exception& e) {
    cerr << "error: " << e.what() << "\n";
    return 1;
  }
  catch(...) {
    cerr << "Exception of unknown type!\n";
  }

  //modify global variables which are fct of options:
  min_nb_points = (d_fitting + 1) * (d_fitting + 2) / 2;
  if (nb_points_to_use < min_nb_points && nb_points_to_use != 0)
    {std::cerr << "the nb of points asked is not enough to perform the fitting" << std::endl; exit(0);}

  //prepare output file names
  //--------------------------
  std::cerr << "if_name_string" << if_name_string  << std::endl;
  if_name = if_name_string;

  w_if_name = if_name;
  for(unsigned int i=0; i<w_if_name.size(); i++)
    if (w_if_name[i] == '/') w_if_name[i]='_';
  cerr << if_name << '\n';
  cerr << w_if_name << '\n';

  res4openGL_fname = w_if_name + ".4ogl.txt";
std::cerr << "res4openGL_fname" << res4openGL_fname  << std::endl;
  out_4ogl.open(res4openGL_fname.c_str(), std::ios::out);
  assert(out_4ogl.good());
  //if verbose only...
  if(verbose){
    verbose_fname  = w_if_name + ".verb.txt";
    out_verbose.open(verbose_fname.c_str(), std::ios::out);
    assert(out_verbose.good());
    CGAL::IO::set_pretty_mode(out_verbose);
  }
  unsigned int nb_vertices_considered = 0;//count vertices for verbose

  //load the model from <mesh.off>
  //------------------------------
  PolyhedralSurf P;
  std::ifstream stream(if_name.c_str());
  stream >> P;
  std::cout << "loadMesh...  "<< "Polysurf with " << P.size_of_vertices()
            << " vertices and " << P.size_of_facets()
            << " facets. " << std::endl;

  if(verbose)
    out_verbose << "Polysurf with " << P.size_of_vertices()
                << " vertices and " << P.size_of_facets()
                << " facets. " << std::endl;
  //exit if not enough points in the model
  if (min_nb_points > P.size_of_vertices())    exit(0);

  //create property maps
  //-----------------------------
  //Vertex, using a std::map
  Vertex2int_map_type vertex2props;
  Vertex_PM_type vpm(vertex2props);

  //Hedge, with enriched hedge
  //HEdgePM_type hepm = get_hepm(boost::edge_weight_t(), P);
  //Hedge, using a std::map
  Hedge2double_map_type hedge2props;
  Hedge_PM_type hepm(hedge2props);

  //Facet PM, with enriched Facet
  //FacetPM_type fpm = get_fpm(boost::vertex_attribute_t(), P);
  //Facet PM, with std::map
  Facet2normal_map_type facet2props;
  Facet_PM_type fpm(facet2props);

  //initialize Polyhedral data : length of edges, normal of facets
  Poly_hedge_ops::compute_edges_length(P, hepm);
  Poly_facet_ops::compute_facets_normals(P, fpm);

  //MAIN LOOP: perform calculation for each vertex
  //----------------------------------------------
  std::vector<DPoint> in_points;  //container for data points
  Vertex_iterator vitb, vite;

  //initialize the tag of all vertices to -1
  vitb = P.vertices_begin(); vite = P.vertices_end();
  CGAL_For_all(vitb,vite) put(vpm, &(*vitb), -1);

  vitb = P.vertices_begin(); vite = P.vertices_end();
  for (; vitb != vite; vitb++) {
    //initialize
    Vertex* v = &(*vitb);
    in_points.clear();
    My_Monge_form monge_form;

    //gather points around the vertex using rings
    gather_fitting_points(v, in_points, vpm);

    //skip if the nb of points is to small
    if ( in_points.size() < min_nb_points )
      {std::cerr << "not enough pts for fitting this vertex" << in_points.size() << std::endl;
        continue;}

    // perform the fitting
    My_Monge_via_jet_fitting monge_fit;
    monge_form = monge_fit(in_points.begin(), in_points.end(),
                           d_fitting, d_monge);
    //switch min-max ppal curv/dir wrt the mesh orientation
    const DVector normal_mesh = Poly_facet_ops::compute_vertex_average_unit_normal(v, fpm);
    monge_form.comply_wrt_given_normal(normal_mesh);

    //OpenGL output. Scaling for ppal dir, may be optimized with a
    //global mean edges length computed only once on all edges of P
    DFT scale_ppal_dir = Poly_hedge_ops::compute_mean_edges_length_around_vertex(v, hepm)/2;

    out_4ogl << v->point()  << " ";
    monge_form.dump_4ogl(out_4ogl, scale_ppal_dir);

    //verbose txt output
    if (verbose) {
      std::vector<DPoint>::iterator itbp = in_points.begin(), itep = in_points.end();
      out_verbose << "in_points list : " << std::endl ;
      for (;itbp!=itep;itbp++) out_verbose << *itbp << std::endl ;

      out_verbose << "--- vertex " <<  ++nb_vertices_considered
                  <<        " : " << v->point() << std::endl
                  << "number of points used : " << in_points.size() << std::endl
        ;// << monge_form;
    }
  } //all vertices processed

  //cleanup filenames
  //------------------
  out_4ogl.close();
  if(verbose) {
    out_verbose.close();
  }
  return 0;
}


#include<iostream>
#include <Eigen/Eigenvalues>
#include"numerics.hpp"
#include"reddm.hpp"
#include"tpoperators.hpp"
#include "files.hpp"
#include <boost/program_options.hpp>

using namespace boost::program_options;
using namespace Many_Body;
int main(int argc, char *argv[])
{
  using Mat= Operators::Mat;
  size_t M{};
  size_t L{};
  double t0{};
  double omega{};
  double gamma{};
  bool PB{};
  try
  {
    options_description desc{"Options"};
    desc.add_options()
      ("help,h", "Help screen")
      ("L", value(&L)->default_value(4), "L")
      ("M", value(&M)->default_value(2), "M")
      ("t", value(&t0)->default_value(1.), "t0")
      ("gam", value(&gamma)->default_value(1.), "gamma")
      ("omg", value(&omega)->default_value(1.), "omega")
    ("pb", value(&PB)->default_value(true), "PB");

    variables_map vm;
    store(parse_command_line(argc, argv, desc), vm);
    notify(vm);

    if (vm.count("help"))
      {std::cout << desc << '\n'; return 0;}
    else{
      if (vm.count("L"))
      {      std::cout << "L: " << vm["L"].as<size_t>() << '\n';

      }
     if (vm.count("M,m"))
      {
	std::cout << "M: " << vm["M"].as<size_t>() << '\n';

      }
      if (vm.count("t"))
      {
	std::cout << "t0: " << vm["t"].as<double>() << '\n';
      }
       if (vm.count("omg"))
      {
	std::cout << "omega: " << vm["omg"].as<double>() << '\n';
      }
       if (vm.count("gam"))
      {
	std::cout << "gamma: " << vm["gam"].as<double>() << '\n';
      }
              if (vm.count("pb"))
      {
	std::cout << "PB: " << vm["pb"].as<bool>() << '\n';
      }
    }
  }
  catch (const error &ex)
  {
    std::cerr << ex.what() << '\n';
    return 0;
  }

using HolsteinBasis= TensorProduct<ElectronBasis, PhononBasis>;
   // std::vector<size_t> ee(L, 0);
   //    ee[L-1]=1;
   //    ElectronState aa(ee);
   // ElectronBasis e(aa);
   ElectronBasis e( L, 1);
   //   std::cout<< e<<std::endl;

  PhononBasis ph(L, M);
  //  std::cout<< ph<<std::endl;
  HolsteinBasis TP(e, ph);
  //  std::cout<< TP<<std::endl;

  std::cout<<"total dim "<< TP.dim << std::endl;
  std::cout<<std::endl;
        Mat E1=Operators::EKinOperatorL(TP, e, t0, PB);
      Mat Ebdag=Operators::NBosonCOperator(TP, ph, gamma, PB);
      Mat Eb=Operators::NBosonDOperator(TP, ph, gamma, PB);
      Mat Eph=Operators::NumberOperator(TP, ph, omega,  PB);

 //      Eigen::VectorXd eigenVals(TP.dim);
      Mat H=E1  +Ebdag + Eb+ Eph;
      //+phMOM;
      //
      //

	//
      //+
      //+ phKNN phK+;

          Eigen::MatrixXcd HH=Eigen::MatrixXcd(H);
	  //	   std::cout<< HH << std::endl;
	  // for(int i=0; i<HH.rows(); i++)
	  //   {
	  //    for(int j=0; j<HH.rows(); j++)
	  //   {
	  //     if(std::abs(HH(i, j))>0.00001){
	  //     	if(std::abs(HH(j, i)-HH(i, j))>0.000001){ std::cout<< "err "<<std::endl;}
	  //     		std::cout<< "went from  "<< i << " to "<< j << "  " <<HH(i, j)<< std::endl;
	  //     }
	  //   }
	  //   }
	  // std::cout<< HH << std::endl;
	     Eigen::VectorXd ev=Eigen::VectorXd(TP.dim);
	     diagMat(HH, ev);
	     std::cout<<"GS "<< std::setprecision(8)<< ev[0]<< std::endl;
	     std::cout<< " nect "<< std::endl;
	     std::cout<< ev<<std::endl;
			      bin_write("holstdata.bin", ev);
	     //	     std::cout<< HH.col(0).adjoint()*Eph*HH.col(0);
   // 	     Eigen::MatrixXd MDF2=HH.adjoint()*(O)*HH;
   // Eigen::VectorXd v=MDF2.diagonal();
   // std::cout<< v << std::endl;
   // std::cout <<"mean "<< v.mean()<< std::endl;
 // 	  bool isDiag=false;
 // 	 //  std::vector<double> Tr={0.0100, 0.1000, 1.000, 2.0000, 5.0000, 10.0000};
 // 	 //  for(auto t: Tr){
 // 	 //      std::string sT=std::string(std::to_string(t)).substr(0,6);
 // auto t=0.5;
 // 	       auto optModes=makeThermalRDMTP(HH,ev,  TP, t, isDiag, 0);
 // 	 //  //	  std::cout << "sum of all eigenvalues "<< optModes.sum()<< std::endl;
 // 	 //      isDiag=true;
 // 	  int n=0;

 // 	  for(auto& l : optModes)
 // 	    {
 // 	      	      std::string sn=std::string(std::to_string(n)).substr(0,1);
 // 		      //std::string filename="OML"+std::to_string(L)+"M"+std::to_string(M)+"t0_"+"1.0"+"gam"+sgam+"omg"+ somg+"T"+ sT+ "esec" +sn  + ".bin";
 // 		 std::cout << n << std::endl;
 // 		 std::cout<< "value "<< " fot T = "<< t <<std::endl;
 // 		 	      std::cout<< l<<std::endl;
 // 			      std::cout << " and sum "<< l.sum()<< std::endl;
  // 			      //bin_write(filename, l);
 // 		 n++;
 // 	    }

	  // }
  //   std::cout<< TP<< std::endl;
  // Eigen::VectorXd v1=Eigen::VectorXd::Zero(TP.dim);
  // v1[1]=1/std::sqrt(2);
  // Eigen::VectorXd v2=Eigen::VectorXd::Zero(TP.dim);
  //   v2[5]=1/std::sqrt(2);
  // 	Eigen::VectorXd V1=v1 +v2;
  // 	Eigen::MatrixXd V2=V1.transpose();
  // 	// std::cout<< V1<<std::endl;
  // 	// std::cout<< V2<<std::endl;
  // 	MatrixXd M=V1*(V2);
  // 	// std::cout<< M<<std::endl;
  // 	// 	std::cout<< g2<<std::endl;
  // 	//	makeRedDM(g2, 0, M);
  // 	makeRedDMTP(TP, ph,  0, M);
  return 0;
}


#ifndef qlex_multibootstrap_hpp
#define qlex_multibootstrap_hpp

#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/bootstraphelper.hpp>
#include <ql/math/optimization/costfunction.hpp>
#include <ql/math/optimization/constraint.hpp>
#include <ql/math/optimization/armijo.hpp>
#include <ql/math/optimization/levenbergmarquardt.hpp>
#include <ql/math/optimization/leastsquare.hpp>
#include <ql/math/optimization/problem.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <ql/termstructures/iterativebootstrap.hpp>
#include <ql/patterns/lazyobject.hpp>
#include <boost/shared_ptr.hpp>

using namespace QuantLib;

namespace QLExtension {
	// penalty function class for multiple curve
	template <class Curve>
	class MultiCurvePenaltyFunction : public CostFunction {
	public:
		typedef typename Curve::traits_type Traits;
		typedef typename Traits::helper helper;
		typedef
			typename std::vector< boost::shared_ptr<helper> >::const_iterator helper_iterator;

		MultiCurvePenaltyFunction(std::vector<Curve*> curves,
			std::vector<Size> initialIndexes,
			std::vector<helper_iterator> rateHelpersStarts,
			std::vector<helper_iterator> rateHelpersEnds)
			: curves_(curves), initialIndexes_(initialIndexes),
			rateHelpersStarts_(rateHelpersStarts), rateHelpersEnds_(rateHelpersEnds) {
		}

		Real value(const Array& x) const;
		Disposable<Array> values(const Array& x) const;

	private:
		std::vector<Curve*> curves_;
		std::vector<Size> initialIndexes_;
		std::vector<helper_iterator> rateHelpersStarts_;
		std::vector<helper_iterator> rateHelpersEnds_;
	};

	template <class Curve>
	class MultiCurveOptimizer : public LazyObject {
	public:
		MultiCurveOptimizer(bool forcePositive = true)
			: accuracy_(1.0e-12), forcePositive_(forcePositive){};
		void addTermStructure(Curve* c) {
			for (Size i = 0; i<c->instruments_.size(); ++i){
				this->registerWith(c->instruments_[i]);
			}
			ts_.push_back(c);
			accuracy_ = accuracy_ > c->accuracy_ ? c->accuracy_ : accuracy_;
		};
		void optimize() const { calculate(); };
		//! \name Observer interface
		//@{
		void update();
		//@}
	private:
		//! \name LazyObject interface
		//@{
		void performCalculations() const;
		//@}
		Real accuracy_;
		bool forcePositive_;
		std::vector<Curve*> ts_;
	};

	template <class Curve>
	void MultiCurveOptimizer<Curve>::update() {
		LazyObject::update();
	}

	template <class Curve>
	void MultiCurveOptimizer<Curve>::performCalculations() const {

		std::vector<Size> initialIndexes;
		std::vector< typename MultiCurvePenaltyFunction<Curve>::helper_iterator > rateHelpersStarts;
		std::vector< typename MultiCurvePenaltyFunction<Curve>::helper_iterator > rateHelpersEnds;

		Size nInsts = 0;
		for (Size i = 0; i < ts_.size(); i++) {
			ts_[i]->bootstrap_.initialize();
			nInsts += ts_[i]->instruments_.size();
			initialIndexes.push_back(1);
			rateHelpersStarts.push_back(ts_[i]->instruments_.begin());
			rateHelpersEnds.push_back(ts_[i]->instruments_.end());
		}

		LevenbergMarquardt solver(accuracy_,
			accuracy_,
			accuracy_);

		EndCriteria endCriteria(20 * nInsts, 10, 0.00, accuracy_, 0.00);
		PositiveConstraint posConstraint;
		NoConstraint noConstraint;
		Constraint& solverConstraint = forcePositive_ ?
			static_cast<Constraint&>(posConstraint) :
			static_cast<Constraint&>(noConstraint);

		Array startArray(nInsts);

		Size pos = 0;
		for (Size i = 0; i < ts_.size(); i++) {
			for (Size j = 0; j < ts_[i]->instruments_.size(); j++) {
				startArray[pos] = ts_[i]->data_[j + initialIndexes[i]];
				pos++;
			}
		}

		MultiCurvePenaltyFunction<Curve> currentCost(
			ts_,
			initialIndexes,
			rateHelpersStarts,
			rateHelpersEnds);

		Problem toSolve(currentCost, solverConstraint, startArray);

		EndCriteria::Type endType = solver.minimize(toSolve, endCriteria);

		// check the end criteria
		QL_REQUIRE(endType == EndCriteria::StationaryFunctionAccuracy ||
			endType == EndCriteria::StationaryFunctionValue,
			"Unable to strip yieldcurve to required accuracy ");

	}

	//! multiple bootstrapper for simultanaous bootstrap of multiple curves.
	/*!
	\TODO: template <class Curve> should be unique for all curves.
	*/
	template <class Curve>
	class MultiBootstrap {
		typedef typename Curve::traits_type Traits;
		typedef typename Curve::interpolator_type Interpolator;
	public:

		MultiBootstrap(boost::shared_ptr<MultiCurveOptimizer<Curve> > optimizer =
			boost::shared_ptr<MultiCurveOptimizer<Curve> >(new MultiCurveOptimizer<Curve>()));
		void setup(Curve* ts);
		void calculate() const;

	private:
		friend MultiCurveOptimizer<Curve>;
		void initialize() const;
		mutable bool validCurve_;
		Curve* ts_;
		boost::shared_ptr<MultiCurveOptimizer<Curve> > multiCurveOptimizer_;
	};

	// template definitions

	template <class Curve>
	MultiBootstrap<Curve>::MultiBootstrap(boost::shared_ptr<MultiCurveOptimizer<Curve> > optimizer)
		: ts_(0), validCurve_(false), multiCurveOptimizer_(optimizer)
	{}

	template <class Curve>
	void MultiBootstrap<Curve>::setup(Curve* ts) {

		ts_ = ts;

		Size n = ts_->instruments_.size();
		QL_REQUIRE(n >= Interpolator::requiredPoints,
			"not enough instruments: " << n << " provided, " <<
			Interpolator::requiredPoints << " required");

		for (Size i = 0; i<n; ++i){
			ts_->registerWith(ts_->instruments_[i]);
		}

		multiCurveOptimizer_->addTermStructure(ts_);

	}

	template <class Curve>
	void MultiBootstrap<Curve>::initialize() const {

		Size nInsts = ts_->instruments_.size();

		// ensure rate helpers are sorted
		std::sort(ts_->instruments_.begin(), ts_->instruments_.end(),
			detail::BootstrapHelperSorter());

		// check that there is no instruments with the same maturity
		for (Size i = 1; i<nInsts; ++i) {
			Date m1 = ts_->instruments_[i - 1]->latestDate(),
				m2 = ts_->instruments_[i]->latestDate();
			QL_REQUIRE(m1 != m2,
				"two instruments have the same maturity (" << m1 << ")");
		}

		// check that there is no instruments with invalid quote
		for (Size i = 0; i<nInsts; ++i)
			QL_REQUIRE(ts_->instruments_[i]->quote()->isValid(),
			io::ordinal(i + 1) << " instrument (maturity: " <<
			ts_->instruments_[i]->latestDate() <<
			") has an invalid quote");

		// setup instruments
		for (Size i = 0; i<nInsts; ++i) {
			// don't try this at home!
			// This call creates instruments, and removes "const".
			// There is a significant interaction with observability.
			ts_->instruments_[i]->setTermStructure(const_cast<Curve*>(ts_));
		}
		// set initial guess only if the current curve cannot be used as guess
		if (validCurve_)
			QL_ENSURE(ts_->data_.size() == nInsts + 1,
			"dimension mismatch: expected " << nInsts + 1 <<
			", actual " << ts_->data_.size());
		else {
			ts_->data_ = std::vector<Rate>(nInsts + 1);
			ts_->data_[0] = Traits::initialValue(ts_);
		}

		// calculate dates and times
		ts_->dates_ = std::vector<Date>(nInsts + 1);
		ts_->times_ = std::vector<Time>(nInsts + 1);
		ts_->dates_[0] = Traits::initialDate(ts_);
		ts_->times_[0] = ts_->timeFromReference(ts_->dates_[0]);
		for (Size i = 0; i<nInsts; ++i) {
			ts_->dates_[i + 1] = ts_->instruments_[i]->latestDate();
			ts_->times_[i + 1] = ts_->timeFromReference(ts_->dates_[i + 1]);
			if (!validCurve_)
				ts_->data_[i + 1] = ts_->data_[i];
		}

		ts_->interpolation_ =
			ts_->interpolator_.interpolate(ts_->times_.begin(),
			ts_->times_.end(),
			ts_->data_.begin());

	}

	template <class Curve>
	void MultiBootstrap<Curve>::calculate() const {
		validCurve_ = false;
		multiCurveOptimizer_->optimize();
		validCurve_ = true;

	}

	template <class Curve>
	Real MultiCurvePenaltyFunction<Curve>::value(const Array& x) const {
		Array::const_iterator guessIt = x.begin();
		for (Size n = 0; n < curves_.size(); n++) {
			Size nInsts = curves_[n]->instruments_.size();
			Size i = initialIndexes_[n];
			for (Size j = 0; j < nInsts; j++) {
				Traits::updateGuess(curves_[n]->data_, *guessIt, j + i);
				++guessIt;
			}
			curves_[n]->interpolation_.update();
		}

		Real penalty = 0.0;
		for (Size n = 0; n < curves_.size(); n++) {
			helper_iterator instIt = rateHelpersStarts_[n];
			while (instIt != rateHelpersEnds_[n]) {
				Real quoteError = (*instIt)->quoteError();
				penalty += std::fabs(quoteError);
				++instIt;
			}
		}
		return penalty;
	}

	template <class Curve>
	Disposable<Array> MultiCurvePenaltyFunction<Curve>::values(const Array& x) const {
		Array::const_iterator guessIt = x.begin();
		for (Size n = 0; n < curves_.size(); n++) {
			Size nInsts = curves_[n]->instruments_.size();
			Size i = initialIndexes_[n];
			for (Size j = 0; j < nInsts; j++) {
				Traits::updateGuess(curves_[n]->data_, *guessIt, j + i);
				++guessIt;
			}
			curves_[n]->interpolation_.update();
		}

		Array penalties(x.size());
		Array::iterator penIt = penalties.begin();
		for (Size n = 0; n < curves_.size(); n++) {
			helper_iterator instIt = rateHelpersStarts_[n];
			while (instIt != rateHelpersEnds_[n]) {
				Real quoteError = (*instIt)->quoteError();
				*penIt = std::fabs(quoteError);
				++instIt;
				++penIt;
			}
		}
		return penalties;
	}
}

#endif


//
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// 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_DISJOINT_SETS_HPP
#define BOOST_DISJOINT_SETS_HPP

#include <vector>
#include <boost/graph/properties.hpp>
#include <boost/pending/detail/disjoint_sets.hpp>

namespace boost {

  struct find_with_path_halving {
    template <class ParentPA, class Vertex>
    Vertex operator()(ParentPA p, Vertex v) {
      return detail::find_representative_with_path_halving(p, v);
    }
  };

  struct find_with_full_path_compression {
    template <class ParentPA, class Vertex>
    Vertex operator()(ParentPA p, Vertex v){
      return detail::find_representative_with_full_compression(p, v);
    }
  };

  // This is a generalized functor to provide disjoint sets operations
  // with "union by rank" and "path compression".  A disjoint-set data
  // structure maintains a collection S={S1, S2, ..., Sk} of disjoint
  // sets. Each set is identified by a representative, which is some
  // member of of the set. Sets are represented by rooted trees. Two
  // heuristics: "union by rank" and "path compression" are used to
  // speed up the operations.

  // Disjoint Set requires two vertex properties for internal use.  A
  // RankPA and a ParentPA. The RankPA must map Vertex to some Integral type
  // (preferably the size_type associated with Vertex). The ParentPA
  // must map Vertex to Vertex.
  template <class RankPA, class ParentPA,
    class FindCompress = find_with_full_path_compression
    >
  class disjoint_sets {
    typedef disjoint_sets self;

    inline disjoint_sets() {}
  public:
    inline disjoint_sets(RankPA r, ParentPA p)
      : rank(r), parent(p) {}

    inline disjoint_sets(const self& c)
      : rank(c.rank), parent(c.parent) {}

    // Make Set -- Create a singleton set containing vertex x
    template <class Element>
    inline void make_set(Element x)
    {
      put(parent, x, x);
      typedef typename property_traits<RankPA>::value_type R;
      put(rank, x, R());
    }

    // Link - union the two sets represented by vertex x and y
    template <class Element>
    inline void link(Element x, Element y)
    {
      detail::link_sets(parent, rank, x, y, rep);
    }

    // Union-Set - union the two sets containing vertex x and y
    template <class Element>
    inline void union_set(Element x, Element y)
    {
      link(find_set(x), find_set(y));
    }

    // Find-Set - returns the Element representative of the set
    // containing Element x and applies path compression.
    template <class Element>
    inline Element find_set(Element x)
    {
      return rep(parent, x);
    }

    template <class ElementIterator>
    inline std::size_t count_sets(ElementIterator first, ElementIterator last)
    {
      std::size_t count = 0;
      for ( ; first != last; ++first)
      if (get(parent, *first) == *first)
        ++count;
      return count;
    }

    template <class ElementIterator>
    inline void normalize_sets(ElementIterator first, ElementIterator last)
    {
      for (; first != last; ++first)
        detail::normalize_node(parent, *first);
    }

    template <class ElementIterator>
    inline void compress_sets(ElementIterator first, ElementIterator last)
    {
      for (; first != last; ++first)
        detail::find_representative_with_full_compression(parent, *first);
    }
  protected:
    RankPA rank;
    ParentPA parent;
    FindCompress rep;
  };

  template <class ID = identity_property_map,
            class InverseID = identity_property_map,
            class FindCompress = find_with_full_path_compression
            >
  class disjoint_sets_with_storage
  {
    typedef typename property_traits<ID>::value_type Index;
    typedef std::vector<Index> ParentContainer;
    typedef std::vector<unsigned char> RankContainer;
  public:
    typedef typename ParentContainer::size_type size_type;

    disjoint_sets_with_storage(size_type n = 0,
                               ID id_ = ID(),
                               InverseID inv = InverseID())
      : id(id_), id_to_vertex(inv), rank(n, 0), parent(n)
    {
      for (Index i = 0; i < n; ++i)
        parent[i] = i;
    }
    // note this is not normally needed
    template <class Element>
    inline void
    make_set(Element x) {
      parent[x] = x;
      rank[x]   = 0;
    }
    template <class Element>
    inline void
    link(Element x, Element y)
    {
      extend_sets(x,y);
      detail::link_sets(&parent[0], &rank[0],
                        get(id,x), get(id,y), rep);
    }
    template <class Element>
    inline void
    union_set(Element x, Element y) {
      Element rx = find_set(x);
      Element ry = find_set(y);
      link(rx, ry);
    }
    template <class Element>
    inline Element find_set(Element x) {
      return id_to_vertex[rep(&parent[0], get(id,x))];
    }

    template <class ElementIterator>
    inline std::size_t count_sets(ElementIterator first, ElementIterator last)
    {
      std::size_t count = 0;
      for ( ; first != last; ++first)
      if (parent[*first] == *first)
        ++count;
      return count;
    }

    template <class ElementIterator>
    inline void normalize_sets(ElementIterator first, ElementIterator last)
    {
      for (; first != last; ++first)
        detail::normalize_node(&parent[0], *first);
    }

    template <class ElementIterator>
    inline void compress_sets(ElementIterator first, ElementIterator last)
    {
      for (; first != last; ++first)
        detail::find_representative_with_full_compression(&parent[0],
                                                          *first);
    }

    const ParentContainer& parents() { return parent; }

  protected:

    template <class Element>
    inline void
    extend_sets(Element x, Element y)
    {
      Index needed = get(id,x) > get(id,y) ? get(id,x) + 1 : get(id,y) + 1;
      if (needed > parent.size()) {
        rank.insert(rank.end(), needed - rank.size(), 0);
        for (Index k = parent.size(); k < needed; ++k)
        parent.push_back(k);
      }
    }

    ID id;
    InverseID id_to_vertex;
    RankContainer rank;
    ParentContainer parent;
    FindCompress rep;
  };

} // namespace boost

#endif // BOOST_DISJOINT_SETS_HPP


/*
 * Copyright (c) 2017, The Regents of the University of California (Regents).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *    3. Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Please contact the author(s) of this library if you have any questions.
 * Authors: David Fridovich-Keil   ( dfk@eecs.berkeley.edu )
 */

///////////////////////////////////////////////////////////////////////////////
//
// Defines the GaussianProcess class.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef GP_PROCESS_GAUSSIAN_PROCESS_H
#define GP_PROCESS_GAUSSIAN_PROCESS_H

#include "../kernels/kernel.hpp"
#include "../utils/types.hpp"

#include <Eigen/Cholesky>
#include <glog/logging.h>
#include <vector>

namespace gp {

  class GaussianProcess {
  public:
    ~GaussianProcess() {}

    // Constructors. By default picks 10% of the maximum number of points
    // randomly within the unit box [-1, 1]^d.
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             size_t dimension, size_t max_points = 100);
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             const PointSet& points,
                             size_t max_points = 100);
    explicit GaussianProcess(const Kernel::Ptr& kernel, double noise,
                             const PointSet& points,
                             const VectorXd& targets,
                             size_t max_points = 100);

    // Evaluate mean and variance at a point.
    void Evaluate(const VectorXd& x, double& mean, double& variance) const;
    void EvaluateTrainingPoint(size_t ii, double& mean, double& variance) const;

    // Add new point(s). Returns whether or not points were added (points will
    // only be added until 'max_points' is reached).
    bool Add(const VectorXd& x, double target);
    bool Add(const std::vector<VectorXd>& points, const VectorXd& targets);

    // Update the training targets in the direction of the gradient of the
    // mean squared error at the given points. Returns the mean squared error.
    // If 'finalize' is set, computes regressed targets - only set to false if
    // you are doing repeated updates, and be sure to set true on final update.
    double UpdateTargets(const std::vector<VectorXd>& points,
                         const std::vector<double>& targets,
                         double step_size, bool finalize = true);

    // Learn kernel hyperparameters by maximizing log-likelihood of the
    // training data.
    bool LearnHyperparams();

    // Immutable accessors.
    const MatrixXd& ImmutableCovariance() const { return covariance_; }
    const VectorXd& ImmutableRegressedTargets() const { return regressed_; }
    const VectorXd& ImmutableTargets() const { return targets_; }
    const ConstPointSet ImmutablePoints() const { return points_; }
    const Eigen::LLT<MatrixXd>& ImmutableCholesky() const { return llt_; }
    size_t Dimension() const { return dimension_; }

  private:
    // Compute the covariance and cross covariance against the training points.
    void Covariance();
    void CrossCovariance(const VectorXd& x, VectorXd& cross) const;

    // Kernel.
    const Kernel::Ptr kernel_;

    // Noise variance.
    const double noise_;

    // Training points, targets, and regressed targets (inv(cov) * targets).
    const PointSet points_;
    size_t dimension_;
    VectorXd targets_;
    VectorXd regressed_;

    // Maximum number of points.
    const size_t max_points_;

    // Covariance matrix, with Cholesky decomposition.
    MatrixXd covariance_;
    Eigen::LLT<MatrixXd> llt_;
  }; //\class GaussianProcess

}  //\namespace gp

#endif


#include <bg/bondgeek.hpp>

#include <iostream>
#include <algorithm>

#include <boost/timer.hpp>

using namespace std;
using namespace QuantLib;
using namespace bondgeek;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {
    Integer sessionId() { return 0; }
}
#endif

/* TODO: Instrument interface
 - swaps: euro swaps, usd
 - bonds:  UST, corp, tax-exempt munis
 */
int main ()
{
    cout << "QuantLib Version #: " << QL_VERSION << endl ;

    Calendar calendar = TARGET();
//    Date todaysDate = TARGET().adjust( Date(20, September, 2004) );
    Date todaysDate = TARGET().adjust( Date(28, May, 2012) );

    Settings::instance().evaluationDate() = todaysDate;
    cout << "settings: " << &Settings::instance() << endl;
    cout << "\n\nToday: " << todaysDate << endl;

    string futtenors[] = {"ED1", "ED2", "ED3", "ED4", "ED5", "ED6", "ED7", "ED8"};
    double futspots[] = {96.2875, 96.7875, 96.9875, 96.6875, 96.4875, 96.3875, 96.2875, 96.0875};

    string depotenors[] = {"1W", "1M", "3M", "6M", "9M", "1y"};
    double depospots[] = {.0382, 0.0372, 0.0363, 0.0353, 0.0348, 0.0345};
    string swaptenors[] = {"2y", "3y", "5y", "10Y", "15Y"};
    double swapspots[] = {0.037125, 0.0398, 0.0443, 0.05165, 0.055175};

    cout << "test sc1" << endl;
    RateHelperCurve acurve = RateHelperCurve(EURiborCurve("6M"));
    acurve.update(depotenors, depospots, 6,
                  swaptenors, swapspots, 5,
                  todaysDate);

    cout << "quote:  " << acurve.tenorquote("10Y") << endl;

    cout << "test tenors\n" << Period(6, Months) << " | " << Tenor("6M") << endl;
    cout << "fixing calendar: " << acurve.calendar() << endl;

    /*********************
     * SWAPS TO BE PRICED *
     **********************/

    RelinkableHandle<YieldTermStructure>forecastingTermStructure = acurve.forecastingTermStructure();

    // constant nominal 1,000,000 Euro
    Real nominal = 1000000.0;
    // fixed leg
    Frequency fixedLegFrequency = Annual;
    BusinessDayConvention fixedLegConvention = Unadjusted;
    BusinessDayConvention floatingLegConvention = ModifiedFollowing;
    DayCounter fixedLegDayCounter = Thirty360(Thirty360::European);
    Rate fixedRate = 0.04;
    DayCounter floatingLegDayCounter = Actual360();

    // floating leg
    Frequency floatingLegFrequency = Semiannual;

    boost::shared_ptr<IborIndex> euriborIndex(new Euribor6M(forecastingTermStructure));

    Spread spread = 0.0;

    Integer lenghtInYears = 5;
    VanillaSwap::Type swapType = VanillaSwap::Payer;

    Date settlementDate = acurve.referenceDate();
    Date maturity = acurve.referenceDate() + lenghtInYears*Years;
    Schedule fixedSchedule(settlementDate, maturity,
                           Period(fixedLegFrequency),
                           calendar, fixedLegConvention,
                           fixedLegConvention,
                           DateGeneration::Forward, false);
    Schedule floatSchedule(settlementDate, maturity,
                           Period(floatingLegFrequency),
                           calendar, floatingLegConvention,
                           floatingLegConvention,
                           DateGeneration::Forward, false);
    VanillaSwap spot5YearSwap(swapType, nominal,
                              fixedSchedule, fixedRate, fixedLegDayCounter,
                              floatSchedule, euriborIndex, spread,
                              floatingLegDayCounter);

    Date fwdStart = calendar.advance(settlementDate, 1, Years);
    Date fwdMaturity = fwdStart + lenghtInYears*Years;
    Schedule fwdFixedSchedule(fwdStart, fwdMaturity,
                              Period(fixedLegFrequency),
                              calendar, fixedLegConvention,
                              fixedLegConvention,
                              DateGeneration::Forward, false);
    Schedule fwdFloatSchedule(fwdStart, fwdMaturity,
                              Period(floatingLegFrequency),
                              calendar, floatingLegConvention,
                              floatingLegConvention,
                              DateGeneration::Forward, false);
    VanillaSwap oneYearForward5YearSwap(swapType, nominal,
                                        fwdFixedSchedule, fixedRate, fixedLegDayCounter,
                                        fwdFloatSchedule, euriborIndex, spread,
                                        floatingLegDayCounter);

    cout << "swap." << endl;

    boost::shared_ptr<PricingEngine> swapEngine = createPriceEngine<DiscountingSwapEngine>(
                                                                                acurve.discountingTermStructure()
                                                                                );

    spot5YearSwap.setPricingEngine(swapEngine);
    oneYearForward5YearSwap.setPricingEngine(swapEngine);

    Real NPV;
    Rate fairRate;
    Spread fairSpread;

    cout << "spot " << endl;
    NPV = spot5YearSwap.NPV();
    fairSpread = spot5YearSwap.fairSpread();
    fairRate = spot5YearSwap.fairRate();

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << NPV <<
    "   | Fair Spread: " << io::rate(fairSpread) <<
    "   | Fair Rate: " << io::rate(fairRate) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << spot5YearSwap.fixedLegNPV() <<
    "   | fx NPV : " << spot5YearSwap.floatingLegNPV() << endl;

    cout << "forward " << endl;
    NPV = oneYearForward5YearSwap.NPV();
    fairSpread = oneYearForward5YearSwap.fairSpread();
    fairRate = oneYearForward5YearSwap.fairRate();

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << NPV <<
    "   | Fair Spread: " << io::rate(fairSpread) <<
    "   | Fair Rate: " << io::rate(fairRate) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << oneYearForward5YearSwap.fixedLegNPV() <<
    "   | fx NPV : " << oneYearForward5YearSwap.floatingLegNPV() << endl;

    cout << "test libor clone" << endl;
    RelinkableHandle<YieldTermStructure> indexTermStructure;
    boost::shared_ptr<IborIndex> libor3m(new USDLibor(Period(3, Months),
                                                      indexTermStructure));

    Handle<YieldTermStructure>testTS = acurve.forecastingTermStructure();
    boost::shared_ptr<IborIndex> newlib = libor3m->clone(testTS);

    // If one wanted a USD Libor index....
    USDLiborBase testIndex;

    cout << "\n\nSwap to compare to first swap " << endl;
    cout << "Qswap" << endl;
    cout << "mty: " << maturity << " | cpn: " << io::rate(fixedRate) << endl;
    EuriborBase euribor(6, Months);

    FixedFloatSwap qswp(settlementDate,
                        maturity,
                        fixedRate,
                        euribor(acurve.yieldTermStructurePtr()),
                        FixedPayer,
                        0.0,
                        1000000.0,
                        Annual,
                        Thirty360(Thirty360::European),
                        Unadjusted,
                        Semiannual,
                        Actual360(),
                        ModifiedFollowing,
                        TARGET()
                        );

    qswp.setPricingEngine(swapEngine);

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp.NPV() <<
    "   | Fair Spread: " << io::rate(qswp.fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp.fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp.fixedLegNPV() <<
    "   | fl NPV : " << qswp.floatingLegNPV() << endl;

    cout << "Inspect Legs" << endl << endl;
    Leg fixedLeg = qswp.fixedLeg();
    Leg floatingLeg = qswp.floatingLeg();

    cout << "Fixed: " << endl;
    Leg::iterator fxIt;
    int cfCount =0;
    Date cfDate;
    double cfAmt;
    double cfDF;
    double cfNPV = 0.0;
    for (fxIt=fixedLeg.begin(); fxIt < fixedLeg.end(); fxIt++) {
        cfDate = (*fxIt)->date();
        cfAmt = (*fxIt)->amount();
        cfDF = acurve.discount((*fxIt)->date());
        cfNPV += cfAmt*cfDF;

        cout << cfCount++ << ") "
        << std::setw(24) << cfDate << " | "  << std::setw(12)
        << cfAmt << " | "
        << std::setprecision(6) << cfDF << " | "
        << std::setprecision(2) << cfNPV
        << endl;
    }

    cout << "Floating: " << endl;
    Leg::iterator flIt;
    cfCount =0;
    cfNPV = 0.0;
    for (flIt=floatingLeg.begin(); flIt < floatingLeg.end(); flIt++) {
        cfDate = (*flIt)->date();
        cfAmt = (*flIt)->amount();
        cfDF = acurve.discount((*flIt)->date());
        cfNPV += cfAmt*cfDF;

        cout << cfCount++ << ") "
        << std::setw(24) << cfDate << " | "  << std::setw(12)
        << cfAmt << " | "
        << std::setprecision(6) << cfDF << " | "
        << std::setprecision(2) << cfNPV
        << endl;
    }

    cout << "swp2 " << endl;

    SwapType<Euribor> euriborswaps(Annual,
                                   Thirty360(Thirty360::European),
                                   Unadjusted,
                                   Semiannual,
                                   Actual360(),
                                   ModifiedFollowing,
                                   TARGET()
                                   );

    cout << "create" << endl;
    boost::shared_ptr<FixedFloatSwap> qswp2 = euriborswaps.create(settlementDate,
                                                                  maturity,
                                                                  fixedRate);

    cout << "pricing" << endl;
    qswp2->setEngine(acurve);

    cout << "link" << endl;
    euriborswaps.linkIndex(acurve);

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp2->NPV() <<
    "   | Fair Spread: " << io::rate(qswp2->fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp2->fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp2->fixedLegNPV() <<
    "   | fx NPV : " << qswp2->floatingLegNPV() << endl;

    cout << "\nSwap 3: forward" << endl;
    boost::shared_ptr<FixedFloatSwap> qswp3 = euriborswaps.create(fwdStart,
                                                                  fwdMaturity,
                                                                  fixedRate);
    cout << "fixed rate: " << qswp3->fixedRate() << endl;
    qswp3->setPricingEngine(swapEngine);
    euriborswaps.linkIndexTo(acurve.yieldTermStructurePtr());

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "NPV : " << qswp3->NPV() <<
    "   | Fair Spread: " << io::rate(qswp3->fairSpread()) <<
    "   | Fair Rate: " << io::rate(qswp3->fairRate()) << endl;

    cout << std::setprecision(2) << std::setw(12) << std::fixed <<
    "fx NPV : " << qswp3->fixedLegNPV() <<
    "   | fx NPV : " << qswp3->floatingLegNPV() << endl;

    cout << "\n\nBonds" << endl;

    boost::shared_ptr<BulletBond> bond1(new BulletBond(.045,
                                                       Date(15, May, 2017),
                                                       Date(15, May, 2003))
                                        );

    cout << "mty: " << bond1->maturityDate() << endl;
    cout << "stl: " << bond1->settlementDate() << endl;

    cout << "test" << endl;
    bond1->setEngine(acurve);

    cout << "bondprice: " ;
    double prc = bond1->cleanPrice();
    cout << std::setprecision(3) << prc << endl;

    cout << "Yield: " ;
    double yld = bond1->yield(prc, bond1->dayCounter(), Compounded, bond1->frequency());
    cout << io::rate(yld) << endl;

    cout << "\n\nIMM Stuff\n";
    cout << "settle: " << settlementDate << endl;
    Date  imm = IMM::nextDate(settlementDate);
    string immcode = IMM::code(imm);

    Date imm2 = imm_nextDate(imm);
    string immcode2 = imm_nextCode(immcode);

    cout << "date: " << imm << " | code: " << immcode << endl;
    cout << "date: " << imm2 << " | code: " << immcode2 << endl;

    cout << "ED3: " << FuturesTenor("ED3") << endl;

    CurveMap depocurve;
    CurveMap futscurve;
    CurveMap swapcurve;
    RateHelperCurve rhcurve( EURiborCurve("6M", Annual) );

    if (futscurve.empty()) {
        cout << "futscurve empty " << endl;
    } else {
        cout << "futscurve not empty: " << endl;
    }

    for (int i=0; i<2; i++)
        depocurve[depotenors[i]] = depospots[i];

    for (int i=0; i<6; i++)
        futscurve[futtenors[i]] = futspots[i];

    for (int i=0; i<5; i++)
        swapcurve[swaptenors[i]] = swapspots[i];

    rhcurve.update(depocurve, futscurve, swapcurve);

    cout << "\n10Y: " << io::rate(rhcurve.tenorquote("10Y")) << endl;
    cout << "discount: " << rhcurve.discount(10.0) << endl;
    return 0;
}


#include <iostream>
#include <NTL/ZZ.h>
#include <gmp.h>
#include <gmpxx.h>
#include "../bernmm-1.1/bern_modp_util.h"
#include "../bernmm-1.1/bern_modp.h"
#include "../bernmm-1.1/bern_rat.h"
#include "berrb.h"

Berrb::Berrb()
{

}

Berrb::~Berrb()
{

}

std::string Berrb::ber(long k, long thread)
{
	mpq_t r;
	mpq_init(r);
	bernmm::bern_rat(r, k, thread);

    mpq_class q(r);

	mpq_clear(r);

    return q.get_str();
}

long Berrb::ber_modp(long p, long k)
{
    long r = bernmm::bern_modp(p, k);

    return r;
}


#include "sparse.h"

#define USE_EIGEN
//#define USE_SUITESPARSE

#ifdef USE_EIGEN
#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET
//#define EIGEN_CHOLMOD_SUPPORT
//#define EIGEN_UMFPACK_SUPPORT
#include <Eigen/Cholesky>
#include <Eigen/LU>
#include <Eigen/Sparse>
#include <Eigen/SparseExtra>
using namespace Eigen;
#endif  // USE_EIGEN

#ifdef USE_SUITESPARSE
#include <suitesparse/SuiteSparseQR.hpp>
#endif // USE_SUITESPARSE

namespace LinAlg
{

//----( sparse solvers )------------------------------------------------------

#ifdef USE_EIGEN

// conversions between k nearest neighbor & Eigen sparse matrix formats

void knn_to_eigen (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_A,
    SparseMatrix<double> & eigen_A)
{
  DynamicSparseMatrix<double> A(dim,dim);

  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      A.coeffRef(i,j) = knn_A[knn_pos];
    }
  }

  eigen_A = A;
}

void symmetric_knn_to_eigen (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_A,
    SparseMatrix<double> & eigen_A)
{
  DynamicSparseMatrix<double> A(dim,dim);

  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      A.coeffRef(i,j) = A.coeffRef(j,i) = knn_A[knn_pos];
    }
  }

  eigen_A = A;
}

void eigen_to_knn (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const SparseMatrix<double> & eigen_A,
    Vector<float> & knn_A)
{
  for (size_t i = 0; i < dim; ++i) {
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      size_t j = nbhd[knn_pos];

      knn_A[knn_pos] = eigen_A.coeff(i,j);
    }
  }
}

void sparse_symmetric_solve (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & knn_Pxx,
    const Vector<float> & knn_Pxy,
    Vector<float> & knn_Fxy,
    double tol,
    bool debug)
{
  ASSERT_SIZE(nbhd, dim * degree);
  ASSERT_SIZE(knn_Pxx, dim * degree);
  ASSERT_SIZE(knn_Pxy, dim * degree);
  ASSERT_SIZE(knn_Fxy, dim * degree);

  LOG("solving sparse " << dim << " x " << dim << " matrix problem");
  SparseMatrix<double> Pxx;
  symmetric_knn_to_eigen(dim, degree, nbhd, knn_Pxx, Pxx);

  //typedef SparseLLT<SparseMatrix<double>, Cholmod> Solver;
  typedef SparseLLT<SparseMatrix<double> > Solver;
  Solver solver(Pxx);

  VectorXd Fxy_i(dim);
  VectorXd Pxy_i(dim);
  float residual = 0;

  for (size_t i = 0; i < dim; ++i) {

    Pxy_i.setZero();
    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      Pxy_i(nbhd[knn_pos]) = knn_Pxy[knn_pos];
    }

    Fxy_i = Pxy_i;
    solver.solveInPlace(Fxy_i);
    // eigen does not set the succeeded flag, otherwise we could check this
    //ASSERT(solver.succeeded(), "sparse linear solver failed");

    for (size_t n = 0; n < degree; ++n) {
      size_t knn_pos = degree * i + n;
      knn_Fxy[knn_pos] = Fxy_i(nbhd[knn_pos]);
    }

    if (debug) {
      Pxy_i -= Pxx * Fxy_i;
      residual += Pxy_i.norm();
    }
  }

  if (debug) {

    LOG(" 2-norm of dense residual = " << residual);

    SparseMatrix<double> Pxy, Fxy;
    knn_to_eigen(dim, degree, nbhd, knn_Pxy, Pxy);
    knn_to_eigen(dim, degree, nbhd, knn_Fxy, Fxy);

    Pxy -= Pxx * Fxy;
    LOG(" 2-norm of sparse residual = " << Pxy.norm());
  }
}

#endif // USE_EIGEN

//----------------------------------------------------------------------------

#ifdef USE_SUITESPARSE

// This uses the GLP's SuiteSparseQR function
// http://www.cise.ufl.edu/research/sparse/SPQR/SPQR/Doc/spqr_user_guide.pdf

// conversions between k nearest neighbor & cholmod formats for sparse matrices

static cholmod_sparse * knn_to_cholmod (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

static cholmod_sparse * symmetric_knn_to_cholmod (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

static void cholmod_to_knn (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const cholmod_sparse * a,
    Vector<float> & A,
    cholmod_common * cc)
{
  TODO("implement conversion");
}

void sparse_symmetric_solve (
    const size_t dim,
    const size_t degree,
    const Vector<uint16_t> & nbhd,
    const Vector<float> & Pxx,
    const Vector<float> & Pxy,
    Vector<float> & Fxy,
    double tol,
    bool debug)
{
  ASSERT_SIZE(Pxx, dim * degree);
  ASSERT_SIZE(Pxy, dim * degree);
  ASSERT_SIZE(Fxy, dim * degree);

  TODO("implement sparse symmetric linear solver");

  LOG("solving sparse " << dim << " x " << dim << " matrix problem");

  // start CHOLMOD
  cholmod_common Common, *cc = &Common;
  cholmod_l_start(cc);

  cholmod_sparse * A = symmetric_knn_to_cholmod(dim, degree, nbhd, Pxx, cc);
  cholmod_sparse * B = knn_to_cholmod(dim, degree, nbhd, Pxy, cc);

  // matlab equivalent: X = A\B
  int ordering = CHOLMOD_NATURAL;
  cholmod_sparse * X = SuiteSparseQR<double>(ordering, tol, A, B, cc);

  cholmod_to_knn(dim, degree, nbhd, X, Fxy, cc);

  if (debug) {
    cholmod_sparse * B2 = cholmod_l_ssmult(A, B, 0, true, true, cc);

    Vector<float> Pxy2(Pxy.size);
    cholmod_to_knn(dim, degree, nbhd, B2, Pxy2, cc);

    cholmod_l_free_sparse(&B2, cc);

    LOG(" 2-norm of residual = " << dist_squared(Pxy, Pxy2));
  }

  // free everything and finish CHOLMOD
  cholmod_l_free_sparse(&A, cc);
  cholmod_l_free_sparse(&B, cc);
  cholmod_l_free_sparse(&X, cc);
  cholmod_l_finish(cc);
}

#endif // USE_SUITESPARSE

} // namespace LinAlg


#include <deal.II/base/utilities.h>
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/timer.h>
#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/index_set.h>
#include <deal.II/base/parameter_handler.h>

#include <deal.II/lac/generic_linear_algebra.h>
namespace LA
{
    using namespace dealii::LinearAlgebraPETSc;
#  define USE_PETSC_LA
}

#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
//#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/constraint_matrix.h>
#include <deal.II/lac/sparsity_tools.h>

//#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/error_estimator.h>
//#include <deal.II/numerics/solution_transfer.h>
//#include <deal.II/numerics/matrix_tools.h>

#include <deal.II/lac/petsc_parallel_sparse_matrix.h>
#include <deal.II/lac/petsc_parallel_vector.h>
#include <deal.II/lac/petsc_solver.h>
#include <deal.II/lac/petsc_precondition.h>

#include <deal.II/distributed/tria.h>
#include <deal.II/distributed/grid_refinement.h>
#include <deal.II/grid/filtered_iterator.h>

#include <fstream>
#include <iostream>

#include "my_utility_functions.h"
#include "parameters.h"

namespace CPPLS
{
	using namespace dealii;

	template <int dim>
	class LayerMovementProblem
	{
	public:
		LayerMovementProblem (const CPPLS::Parameters &parameters);
		~LayerMovementProblem ();
		void run();

	private:
	//Member Data
		//runtime parameters
		const CPPLS::Parameters parameters;
		//mpi communication
  		MPI_Comm mpi_communicator;
		const unsigned int n_mpi_processes;
		const unsigned int this_mpi_process;
		//mesh
		parallel::distributed::Triangulation<dim> triangulation;
		//FE basis spaces
		//pressure
		int                  degree_P;
	  	DoFHandler<dim>      dof_handler_P;
	  	FE_Q<dim>            fe_P;
	  	IndexSet             locally_owned_dofs_P;
	  	IndexSet             locally_relevant_dofs_P;
	  	//temperature
	  	int                  degree_T;
	  	DoFHandler<dim>      dof_handler_T;
	  	FE_Q<dim>            fe_T;
	  	IndexSet             locally_owned_dofs_T;
	  	IndexSet             locally_relevant_dofs_T;
	  	// level set (can multiple level sets use the same of below? probably not IndexSets)std::vector<IndexSets>
	  	int                  degree_LS;
	  	DoFHandler<dim>      dof_handler_LS;
	  	FE_Q<dim>            fe_LS;
	  	IndexSet             locally_owned_dofs_LS;
	  	IndexSet             locally_relevant_dofs_LS;

	  	//output stream where only mpi rank 0 output gets to stdout
	  	ConditionalOStream                pcout;

		const double time_step;
  		double current_time;

  		//FE Field Solution Vectors

		  LA::MPI::Vector locally_relevant_solution_u; //ls
		  LA::MPI::Vector locally_relevant_solution_p;
		  LA::MPI::Vector locally_relevant_solution_t;
		  LA::MPI::Vector locally_relevant_solution_f; //speed function
		  LA::MPI::Vector completely_distributed_solution_u;
		  LA::MPI::Vector completely_distributed_solution_p;
		  LA::MPI::Vector completely_distributed_solution_t;
		  LA::MPI::Vector completely_distributed_solution_f;

		  LA::MPI::Vector overburden;
		  LA::MPI::Vector bulkdensity;
		  LA::MPI::Vector porosity;

  		// for boundary conditions
	  	std::vector<unsigned int> boundary_values_id_u;
	  	std::vector<unsigned int> boundary_values_id_p;
	  	std::vector<unsigned int> boundary_values_id_t;
	  	std::vector<double> boundary_values_u;
	  	std::vector<double> boundary_values_p;
	  	std::vector<double> boundary_values_t;

  		//Physical Vectors

  	//Member Functions

	  //create mesh
	  void setup_geometry();

	  void setup_material_configuration(LA::MPI::Vector/*std::vector<LA::MPI::Vector>*/);
	  // initialize vectors
	  void setup_dofs_P();
	  void setup_dofs_T();

	  void assemble_system_P();
	  void assemble_system_T();

	  void compute_bulkdensity();
	  void compute_overburden();
	  void compute_porosity();
	  void compute_speed_function();

	  void output_vectors();

	};

//Constructor

	template<int dim>
 	LayerMovementProblem<dim>::LayerMovementProblem (const CPPLS::Parameters &parameters)
 	:
	parameters(parameters),
	mpi_communicator (MPI_COMM_WORLD),
 	n_mpi_processes {Utilities::MPI::n_mpi_processes(mpi_communicator)},
 	this_mpi_process {Utilities::MPI::this_mpi_process(mpi_communicator)},
 	{};

//
template<int dim>
void
	LayerMovementProblem<dim>::setup_geometry()
	{
		  //GridGenerator::hyper_cube(triangulation, 0, parameters.box_size);
		  //GridGenerator::subdivided_hyper_rectangle(triangulation, 0, parameters.box_size);
   		  triangulation.refine_global(parameters.initial_refinement_level);

	}

template <int dim>
	void
	LayerMovementProblem<dim>::compute_bulkdensity()
	{
		for (auto cell : filter_iterators(triangulation.active_cell_iterators(),
													IteratorFilters::LocallyOwnedCell()))
		  {
		    fe_values.reinit (cell);
		    //query the local porosity

		  }
	}

template <int dim>
	void
	LayerMovementProblem<dim>::compute_overburden()
	{

	    const QGauss<dim>  quadrature_formula(3);

	    FEValues<dim> fe_values (fe, quadrature_formula,
	                             update_values    |  update_gradients |
	                             update_quadrature_points |
	                             update_JxW_values);

	    const unsigned int   dofs_per_cell = fe.dofs_per_cell;
	    const unsigned int   n_q_points    = quadrature_formula.size();

	    FullMatrix<double>   cell_matrix (dofs_per_cell, dofs_per_cell);
	    Vector<double>       cell_rhs (dofs_per_cell);

	    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
		for (auto cell : filter_iterators(triangulation.active_cell_iterators(),
													IteratorFilters::LocallyOwnedCell())
		  {
		    fe_values.reinit (cell);

		  }
	}

template <int dim>
	void LayerMovementProblem<dim>::assemble_system_P()
	{

    TimerOutput::Scope t(computing_timer, "assembly_P");
     const QGauss<dim>  quadrature_formula(3);

     // RightHandSide<dim> right_hand_side;
     // //set time too
     // right_hand_side.set_time(time);
     // DiffusionCoefficient<dim> diffusion_coeff;

    FEValues<dim> fe_values (fe_P, quadrature_formula,
                             update_values    |  update_gradients |
                             update_quadrature_points |
                             update_JxW_values);

    const unsigned int   dofs_per_cell = fe.dofs_per_cell;
    const unsigned int   n_q_points    = quadrature_formula.size();

    FullMatrix<double>   cell_laplace_matrix (dofs_per_cell, dofs_per_cell);
    FullMatrix<double>   cell_mass_matrix (dofs_per_cell, dofs_per_cell);
    Vector<double>       cell_rhs (dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    std::vector<double> u_at_quad(n_q_points);

    // typename DoFHandler<dim>::active_cell_iterator
    // cell = dof_handler.begin_active(),
    // endc = dof_handler.end();
    // for (; cell!=endc; ++cell)
    //   if (cell->is_locally_owned())
    //     {

      for (auto cell : filter_iterators(dof_handler.active_cell_iterators(),
										IteratorFilters::LocallyOwnedCell())
  		{
          cell_laplace_matrix = 0;
          cell_mass_matrix = 0;
          cell_rhs = 0;

          fe_values.reinit (cell);
          fe_values.get_function_values(interface_phi,phi_at_quad);
          fe_values.get_function_values(locally_relevant_solution_p,pressure_at_quad);

          for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
            {
              poro = get_poro(phi_at_quad[q_point]);
	          press = get_pressure(pressure_at_quad[q_point]);
              poro = exp(-k*press);

            }

          cell->get_dof_indices (local_dof_indices);
          constraints.distribute_local_to_global (cell_laplace_matrix,
                                                  cell_rhs,
                                                  local_dof_indices,
                                                  laplace_matrix,
                                                  forcing);
          constraints.distribute_local_to_global (cell_mass_matrix,
                                                  local_dof_indices,
                                                  mass_matrix);

        }

    // Notice that the assembling above is just a local operation. So, to
    // form the "global" linear system, a synchronization between all
    // processors is needed. This could be done by invoking the function
    // compress(). See @ref GlossCompress  "Compressing distributed objects"
    // for more information on what is compress() designed to do.
    laplace_matrix.compress (VectorOperation::add);
    mass_matrix.compress (VectorOperation::add);
    forcing.compress (VectorOperation::add);

  }

	}

template <int dim>
void
LayerMovementProblem<dim>::output_vectors()
{
  DataOut<dim> data_out;
  data_out.attach_dof_handler (dof_handler_LS);
  data_out.add_data_vector (locally_relevant_solution_u, "u");
  data_out.build_patches ();

  const std::string filename = ("sol_vectors-" +
				Utilities::int_to_string (output_number, 3) +
				"." +
				Utilities::int_to_string
				(triangulation.locally_owned_subdomain(), 4));
  std::ofstream output ((filename + ".vtu").c_str());
  data_out.write_vtu (output);

  if (Utilities::MPI::this_mpi_process(mpi_communicator) == 0)
    {
      std::vector<std::string> filenames;
      for (unsigned int i=0;
	   i<Utilities::MPI::n_mpi_processes(mpi_communicator);
	   ++i)
	filenames.push_back ("sol_vectors-" +
			     Utilities::int_to_string (output_number, 3) +
			     "." +
			     Utilities::int_to_string (i, 4) +
			     ".vtu");

      std::ofstream master_output ((filename + ".pvtu").c_str());
      data_out.write_pvtu_record (master_output, filenames);
    }
}

template<int dim>
	LayerMovementProblem<dim>::run()
	{
		//common mesh
		setup_geometry();
		//
		//initialize level set solver
		LevelSetSolver<dim> level_set_solver;

		//initialize pressure solver
		PressureEquation<dim> pressure_solver;
		//initialize temperature solver
		//TemperatureEquation<dim> temperature_solver;

  // TIME STEPPING
  for (timestep_number=1, time=time_step; time<=final_time;
       time+=time_step,++timestep_number)
    {
      pcout << "Time step " << timestep_number
	    << " at t=" << time
	    << std::endl;
      // GET NAVIER STOKES VELOCITY
      navier_stokes.set_phi(locally_relevant_solution_phi);
      navier_stokes.nth_time_step();
      navier_stokes.get_velocity(locally_relevant_solution_u,locally_relevant_solution_v);
      transport_solver.set_velocity(locally_relevant_solution_u,locally_relevant_solution_v);
      // GET LEVEL SET SOLUTION
      transport_solver.nth_time_step();
      transport_solver.get_unp1(locally_relevant_solution_phi);
      if (get_output && time-(output_number)*output_time>0)
	output_results();
    }

	}

} //end namespace CPPLS

constexpr int dim {3};

int main(int argc, char *argv[])
{
  // One of the new features in C++11 is the chrono component of
  // the standard library. This gives us an easy way to time the output.
  try
    {
	    using namespace dealii;
	    using namespace CPPLS;

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

	  	Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

	  	CPPLS::Parameters parameters;
	  	parameters.read_parameter_file("parameters.prm");

	  	LayerMovementProblem<dim> run_layers(parameters);
	  	run_layers.run();

	  	auto t1 = std::chrono::high_resolution_clock::now();
	  	if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
	    {
	      std::cout << "time elapsed: "
	                << std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count()
	                << " milliseconds."
	                << std::endl;
	    }

    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl << exc.what()
                << std::endl << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl << "Aborting!"
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;

}


#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/placeholders.hpp>

#include <cassert>

namespace mpl = boost::mpl;
using namespace mpl::placeholders;

typedef mpl::vector_c<int, 1, 0, 0, 0, 0, 0, 0> mass;
typedef mpl::vector_c<int, 0, 1, 0, 0, 0, 0, 0> length;
typedef mpl::vector_c<int, 0, 0, 1, 0, 0, 0, 0> time;
typedef mpl::vector_c<int, 0, 0, 0, 1, 0, 0, 0> charge;
typedef mpl::vector_c<int, 0, 0, 0, 0, 1, 0, 0> temperature;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 1, 0> intensity;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 0, 1> substance;

/**
 * Wrapper for int
 */
template <int n>
struct Int {
    const static int value = n;
};

template <class T, class Dimensions>
struct quantity {
    explicit quantity(T x): value_(x) {}

    template <class Other>
        quantity(const quantity<T, Other>& rhs): value_(rhs.value()) {
            static_assert(mpl::equal<Other, Dimensions>::value, "They must has same dimensions");
        }

    T value() const { return value_; }
    private:
        T value_;
};

template <class T, class D>
quantity<T, D> operator + (const quantity<T,D>& lhs, const quantity<T, D>& rhs) {
    return quantity<T, D>(lhs.value() + rhs.value());
}

template <class T, class D>
quantity<T, D> operator - (const quantity<T, D>& lhs, const quantity<T, D>& rhs) {
    return quantity<T, D>(lhs.value() - rhs.value());
}

/**
 * wrapper for plus
 */
struct plus_f {
    template <class T1, class T2>
        struct apply: mpl::plus<T1, T2> {};
};

/**
 * wrapper for minus
 */
struct minus_f {
    template <class T1, class T2>
        struct apply: mpl::minus<T1, T2> {};
};

/**
 * operator * overloading, using placeholder to compute type
 */
template <class T, class D1, class D2>
quantity<T, typename mpl::transform<D1, D2, mpl::plus<_1, _2>>::type>
    operator * (const quantity<T, D1>& lhs, const quantity<T, D2>& rhs) {
        typedef typename mpl::transform<D1, D2, mpl::plus<_1, _2>>::type dim;
        return quantity<T, dim>(lhs.value() * rhs.value());
    }

template <class T, class D1, class D2>
quantity<T, typename mpl::transform<D1, D2, mpl::minus<_1, _2>>::type>
    operator / (const quantity<T, D1>& lhs, const quantity<T, D2>& rhs) {
        typedef typename mpl::transform<D1, D2, mpl::minus<_1, _2>>::type dim;
        return quantity<T, dim>(lhs.value() / rhs.value());
    }

int main()
{
    quantity<float, length> la(1.0);
    quantity<float, length> lb(2.0);
    quantity<float, mass> m(1.0);

    quantity<float, length> ls = la + lb;
    assert(ls.value() == 3.0 && "la + lb");

    auto res = la * m;

    return 0;
}


#include "../../include/waveO1/observer.hpp"
#include "../../include/waveO1/utilities.hpp"
#include "../../include/mymfem/utilities.hpp"

#include <iostream>
#include <Eigen/Core>

// Computes DG error on time-like face at a specified time
double WaveO1Observer
:: eval_dgFtimeError (std::shared_ptr<GridFunction>& p,
                      std::shared_ptr<GridFunction>& v,
                      double t) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto faceElGeomType = p->FESpace()->GetMesh()
                ->GetFaceGeometryType(0);

    // L2 error in pressure
    // set integration rules
    IntegrationRules rule1{};
    const IntegrationRule *ir1;
    int order1 = 2*p->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(faceElGeomType, order1);
    m_pE_coeff->SetTime(t);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Ftime(p.get(),
                                    m_pE_coeff.get());

    // L2 error in velocity
    // set integration rules
    IntegrationRules rule2{};
    const IntegrationRule *ir2;
    int order2 = 2*v->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(faceElGeomType, order2);
    m_vE_coeff->SetTime(t);
    m_assembleDgError->SetIntegrationRule(ir2);
    errv = m_assembleDgError->Ftime(v.get(),
                                    m_vE_coeff.get());

    return (errp + errv);
}

//! Computes DG error for a space-like face
//! at a specified time
double WaveO1Observer
:: eval_dgFspaceError
(std::shared_ptr<GridFunction>& p,
 std::shared_ptr<GridFunction>& v,
 double t,
 std::shared_ptr<WaveO1InvSqMediumCoeff>&
 invSqMed) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto elGeomType = p->FESpace()
                ->GetFE(0)->GetGeomType();

    IntegrationRules rule1{}, rule2{};
    const IntegrationRule *ir1, *ir2;

    m_pE_coeff->SetTime(t);
    m_vE_coeff->SetTime(t);

    // pressure
    int order1 = 2*p->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(elGeomType, order1);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Fspace(p.get(),
                                     m_pE_coeff.get(),
                                     invSqMed.get());

    // velocity
    int order2 = 2*v->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(elGeomType, order2);
    m_assembleDgError->SetIntegrationRule(ir2);
    m_vE->ProjectCoefficient(*m_vE_coeff);
    errv = m_assembleDgError->Fspace(v.get(),
                                     m_vE_coeff.get());

    return (errp + errv);
}

double WaveO1Observer
:: eval_dgFspaceError
(std::shared_ptr<GridFunction>& p1,
 std::shared_ptr<GridFunction>& p2,
 std::shared_ptr<GridFunction>& v1,
 std::shared_ptr<GridFunction>& v2,
 double t,
 std::shared_ptr<WaveO1InvSqMediumCoeff>&
 invSqMed) const
{
    double errp=0;
    double errv=0;

    // assumes that all elements have the same geometry
    auto elGeomType = p1->FESpace()
                ->GetFE(0)->GetGeomType();

    IntegrationRules rule1{}, rule2{};
    const IntegrationRule *ir1, *ir2;

    m_pE_coeff->SetTime(t);
    m_vE_coeff->SetTime(t);

    // pressure
    int order1 = 2*p1->FESpace()->GetFE(0)->GetOrder()+2;
    ir1 = &rule1.Get(elGeomType, order1);
    m_assembleDgError->SetIntegrationRule(ir1);
    errp = m_assembleDgError->Fspace(p1.get(), p2.get(),
                                     m_pE_coeff.get(),
                                     invSqMed.get());

    // velocity
    int order2 = 2*v1->FESpace()->GetFE(0)->GetOrder()+2;
    ir2 = &rule2.Get(elGeomType, order2);
    m_assembleDgError->SetIntegrationRule(ir2);
    errv = m_assembleDgError->Fspace(v1.get(), v2.get(),
                                     m_vE_coeff.get());

    return (errp + errv);
}

// Computes DG error
std::tuple <double, double> WaveO1Observer
:: eval_xtDgError (BlockVector& W) const
{
    double errDgFtime=0;
    double errDgFspace=0;

    int Nt = m_tWspace->GetNE();
    int xdimW1 = m_xW1space->GetTrueVSize();
    int xdimW2 = m_xW2space->GetTrueVSize();

    Vector& p = W.GetBlock(0);
    Vector& v = W.GetBlock(1);

    Vector pSol1(xdimW1), pSol2(xdimW1);
    std::shared_ptr<GridFunction> pGSol1
            = std::make_shared<GridFunction>
            (m_xW1space, pSol1);
    std::shared_ptr<GridFunction> pGSol2
            = std::make_shared<GridFunction>
            (m_xW1space, pSol2);

    Vector vSol1(xdimW2), vSol2(xdimW2);
    std::shared_ptr<GridFunction> vGSol1
            = std::make_shared<GridFunction>
            (m_xW2space, vSol1);
    std::shared_ptr<GridFunction> vGSol2
            = std::make_shared<GridFunction>
            (m_xW2space, vSol2);

    // medium
    auto invSqMed = std::make_shared<WaveO1InvSqMediumCoeff>
            (m_testCase);

    ElementTransformation *tTrans = nullptr;
    const FiniteElement *tFe = nullptr;
    Vector tShape;
    Array<int> tVdofs;

    // DG error for time-like faces
    Eigen::VectorXd bufErrFtime(Nt);
    bufErrFtime.setZero();

    double errFtime;
    for (int n=0; n<Nt; n++)
    {
        m_tWspace->GetElementVDofs(n, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(n);

        tFe = m_tWspace->GetFE(n);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        int order = 2*tFe->GetOrder()+2;
        const IntegrationRule *ir
                = &IntRules.Get(tFe->GetGeomType(), order);

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            tTrans->SetIntPoint(&ip);
            tFe->CalcShape(ip, tShape);

            // build solution at time t
            Vector t;
            tTrans->Transform(ip, t);
            build_xSol_FG(p, tShape, tVdofs, pSol1);
            build_xSol_FG(v, tShape, tVdofs, vSol1);

            errFtime = eval_dgFtimeError
                    (pGSol1, vGSol1, t(0));

            double w = ip.weight*tTrans->Weight();
            bufErrFtime(n) += w*errFtime;
        }
    }
    errDgFtime = std::sqrt(bufErrFtime.sum());

    // DG error for space-like faces
    Eigen::VectorXd bufErrFspace(Nt+1);
    bufErrFspace.setZero();

    Vector t;
    IntegrationPoint ip0, ip1;
    ip0.Set1w(0, 1);
    ip1.Set1w(1, 1);

    // for t=0
    {
        int n=0;
        int np = n;

        // build solution at tn^{+}
        m_tWspace->GetElementVDofs(np, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(np);
        tFe = m_tWspace->GetFE(np);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip0);
        tFe->CalcShape(ip0, tShape);
        tTrans->Transform(ip0, t);
        build_xSol_FG(p, tShape, tVdofs, pSol2);
        build_xSol_FG(v, tShape, tVdofs, vSol2);

        // compute error
        bufErrFspace(0) = eval_dgFspaceError
                (pGSol2, vGSol2, t(0), invSqMed);
    }

    // for 0 < t < T
    for (int n=1; n<Nt; n++)
    {
        int nm = n-1;
        int np = n;

        // build solution at tn^{-}
        m_tWspace->GetElementVDofs(nm, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(nm);
        tFe = m_tWspace->GetFE(nm);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip1);
        tFe->CalcShape(ip1, tShape);
        tTrans->Transform(ip1, t);
        build_xSol_FG(p, tShape, tVdofs, pSol1);
        build_xSol_FG(v, tShape, tVdofs, vSol1);

        // build solution at tn^{+}
        m_tWspace->GetElementVDofs(np, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(np);
        tFe = m_tWspace->GetFE(np);
        tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip0);
        tFe->CalcShape(ip0, tShape);
        tTrans->Transform(ip0, t);
        build_xSol_FG(p, tShape, tVdofs, pSol2);
        build_xSol_FG(v, tShape, tVdofs, vSol2);

        // compute error
        bufErrFspace(n) = eval_dgFspaceError
                (pGSol1, pGSol2, vGSol1, vGSol2,
                 t(0), invSqMed);
    }

    // for t=T
    {
        int n = Nt;
        int nm = n-1;

        // build solution at tn^{-}
        m_tWspace->GetElementVDofs(nm, tVdofs);
        tTrans = m_tWspace->GetElementTransformation(nm);
        tFe = m_tWspace->GetFE(nm);
        int tNdofs = tFe->GetDof();
        tShape.SetSize(tNdofs);

        tTrans->SetIntPoint(&ip1);
        tFe->CalcShape(ip1, tShape);
        tTrans->Transform(ip1, t);
        build_xSol_FG(p, tShape, tVdofs, pSol1);
        build_xSol_FG(v, tShape, tVdofs, vSol1);

        // compute error
        bufErrFspace(n) = eval_dgFspaceError
                (pGSol1, vGSol1, t(0), invSqMed);
    }
    errDgFspace = std::sqrt(0.5*bufErrFspace.sum());

    return {errDgFtime, errDgFspace};
}

// End of file


#include "davidson_util.h"

#include <Eigen/Dense>
#include <algorithm>
#include <cmath>

std::pair<double, std::vector<double>> DavidsonUtil::diagonalize(
    const std::vector<double>& initial_vector,
    const std::vector<double>& diagonal,
    const std::function<std::vector<double>(
        const std::vector<double>&, const bool)>& apply_hamiltonian,
    const int max_iterations_in,
    const bool verbose) {
  const double TOLERANCE = 1.0e-10;
  const double EPSILON = 1.0e-10;
  const int n_dets = initial_vector.size();
  double lowest_eigenvalue = 0.0;
  std::vector<double> lowest_eigenvector;

  if (n_dets == 1) {
    lowest_eigenvalue = diagonal[0];
    lowest_eigenvector.resize(1);
    lowest_eigenvector[0] = 1.0;
    return std::make_pair(lowest_eigenvalue, std::move(lowest_eigenvector));
  }

  const int max_iterations = std::min(n_dets, max_iterations_in);
  double lowest_eigenvalue_prev = 0.0;
  double residual_norm = 0.0;

  Eigen::MatrixXd v = Eigen::MatrixXd::Zero(n_dets, max_iterations);
  for (int i = 0; i < n_dets; i++) v(i, 0) = initial_vector[i];
  v.col(0).normalize();

  Eigen::MatrixXd Hv = Eigen::MatrixXd::Zero(n_dets, max_iterations);
  Eigen::VectorXd w = Eigen::VectorXd::Zero(n_dets);
  Eigen::VectorXd Hw = Eigen::VectorXd::Zero(n_dets);
  Eigen::MatrixXd h_krylov =
      Eigen::MatrixXd::Zero(max_iterations, max_iterations);
  Eigen::MatrixXd h_overwrite;
  Eigen::VectorXd eigenvalues = Eigen::VectorXd::Zero(max_iterations);
  int len_work = 3 * max_iterations - 1;
  Eigen::VectorXd work(len_work);
  bool converged = false;
  std::vector<double> tmp_v(n_dets);

  // Get diagonal elements.
  Eigen::VectorXd diag_elems(n_dets);
  for (int i = 0; i < n_dets; i++) diag_elems[i] = diagonal[i];

  // First iteration.
  for (int i = 0; i < n_dets; i++) tmp_v[i] = v(i, 0);
  const auto& tmp_Hv = apply_hamiltonian(tmp_v, true);
  for (int i = 0; i < n_dets; i++) Hv(i, 0) = tmp_Hv[i];
  lowest_eigenvalue = v.col(0).dot(Hv.col(0));
  h_krylov(0, 0) = lowest_eigenvalue;
  w = v.col(0);
  Hw = Hv.col(0);
  if (verbose) print_intermediate_result(0, lowest_eigenvalue);

  for (int it = 1; it < max_iterations; it++) {
    // Compute residual.
    for (int j = 0; j < n_dets; j++) {
      const double diff_to_diag = lowest_eigenvalue - diag_elems[j];
      if (std::abs(diff_to_diag) < EPSILON) {
        v(j, it) = -1.0;
      } else {
        v(j, it) = (Hw(j, 0) - lowest_eigenvalue * w(j, 0)) / diff_to_diag;
      }
    }

    // If residual is small, converge.
    residual_norm = v.col(it).norm();
    if (residual_norm < TOLERANCE) converged = true;

    // Orthogonalize and normalize.
    for (int i = 0; i < it; i++) {
      double norm = v.col(it).dot(v.col(i));
      v.col(it) -= norm * v.col(i);
    }
    v.col(it).normalize();

    // Apply H once.
    for (int i = 0; i < n_dets; i++) tmp_v[i] = v(i, it);
    const auto& tmp_Hv2 = apply_hamiltonian(tmp_v, false);
    for (int i = 0; i < n_dets; i++) Hv(i, it) = tmp_Hv2[i];

    // Construct subspace matrix.
    for (int i = 0; i <= it; i++) {
      h_krylov(i, it) = v.col(i).dot(Hv.col(it));
      h_krylov(it, i) = h_krylov(i, it);
    }

    // Diagonalize subspace matrix.
    len_work = 3 * it + 2;
    h_overwrite = h_krylov.leftCols(it + 1).topRows(it + 1);
    Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigenSolver(
        h_krylov.leftCols(it + 1).topRows(it + 1));
    const auto& eigenvalues = eigenSolver.eigenvalues();
    const auto& eigenvectors = eigenSolver.eigenvectors();
    lowest_eigenvalue = eigenvalues[0];
    int lowest_id = 0;
    for (int i = 1; i <= it; i++) {
      if (eigenvalues[i] < lowest_eigenvalue) {
        lowest_eigenvalue = eigenvalues[i];
        lowest_id = i;
      }
    }
    w = v.leftCols(it) * eigenvectors.col(lowest_id).topRows(it);
    Hw = Hv.leftCols(it) * eigenvectors.col(lowest_id).topRows(it);

    if (verbose) print_intermediate_result(it, lowest_eigenvalue);
    if (std::abs(lowest_eigenvalue - lowest_eigenvalue_prev) < TOLERANCE) {
      converged = true;
      break;
    } else {
      lowest_eigenvalue_prev = lowest_eigenvalue;
    }

    if (converged) break;
  }

  lowest_eigenvector.resize(n_dets);
  for (int i = 0; i < n_dets; i++) lowest_eigenvector[i] = w(i);

  return std::make_pair(lowest_eigenvalue, std::move(lowest_eigenvector));
}

void DavidsonUtil::print_intermediate_result(
    const int iteration, const double lowest_eigenvalue) {
  printf(
      "Davidson Iteration #%d. Eigenvalue: %#.12f\n",
      iteration,
      lowest_eigenvalue);
}


// Distributed under the MIT License.
// See LICENSE.txt for details.

#include "Domain/CoordinateMaps/SpecialMobius.hpp"

#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <cmath>
#include <pup.h>

#include "DataStructures/Tensor/Tensor.hpp"
#include "ErrorHandling/Assert.hpp"
#include "Utilities/ConstantExpressions.hpp"
#include "Utilities/DereferenceWrapper.hpp"
#include "Utilities/EqualWithinRoundoff.hpp"
#include "Utilities/GenerateInstantiations.hpp"
#include "Utilities/MakeWithValue.hpp"
#include "Utilities/StdArrayHelpers.hpp"

namespace domain {
namespace CoordinateMaps {

SpecialMobius::SpecialMobius(const double mu) noexcept
    : mu_(mu), is_identity_(mu_ == 0.0) {
  // Note: Empirically we have found that the map is accurate
  // to 12 decimal places for mu = 0.96.
  ASSERT(abs(mu) < 0.96, "The magnitude of mu must be less than 0.96.");
}

template <typename T>
std::array<tt::remove_cvref_wrap_t<T>, 3> SpecialMobius::mobius_distortion(
    const std::array<T, 3>& coords, const double mu) const noexcept {
  using ReturnType = tt::remove_cvref_wrap_t<T>;
  const ReturnType& x = coords[0];
  const ReturnType& y = coords[1];
  const ReturnType& z = coords[2];
  const double mu_squared = square(mu);
  const ReturnType r_squared = square(x) + square(y) + square(z);
  const ReturnType lambda = 1.0 / (1.0 - 2.0 * mu * x + mu_squared * r_squared);
  return std::array<ReturnType, 3>{
      {lambda * ((1.0 + mu_squared) * x - mu * (1.0 + r_squared)),
       (1.0 - mu_squared) * lambda * y, (1.0 - mu_squared) * lambda * z}};
}

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>
SpecialMobius::mobius_distortion_jacobian(const std::array<T, 3>& coords,
                                          const double mu) const noexcept {
  using ReturnType = tt::remove_cvref_wrap_t<T>;
  const ReturnType& x = coords[0];
  const ReturnType& y = coords[1];
  const ReturnType& z = coords[2];
  const double mu_squared = square(mu);
  const ReturnType r_squared = square(x) + square(y) + square(z);
  const ReturnType common_factor =
      (mu_squared - 1.0) / square(1.0 - 2.0 * mu * x + mu_squared * r_squared);
  auto jacobian_matrix =
      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(
          dereference_wrapper(coords[0]), 0.0);

  get<0, 0>(jacobian_matrix) =
      -(mu_squared * (2.0 * square(x) - r_squared) - 2.0 * mu * x + 1.0) *
      common_factor;
  get<1, 0>(jacobian_matrix) = (2.0 * mu * y * (mu * x - 1.0)) * common_factor;
  get<2, 0>(jacobian_matrix) = (2.0 * mu * z * (mu * x - 1.0)) * common_factor;

  get<0, 1>(jacobian_matrix) = -1.0 * get<1, 0>(jacobian_matrix);
  get<1, 1>(jacobian_matrix) =
      -(mu_squared * (r_squared - 2.0 * square(y)) - 2.0 * mu * x + 1.0) *
      common_factor;
  get<2, 1>(jacobian_matrix) = 2.0 * mu_squared * y * z * common_factor;

  get<0, 2>(jacobian_matrix) = -1.0 * get<2, 0>(jacobian_matrix);
  get<1, 2>(jacobian_matrix) = get<2, 1>(jacobian_matrix);
  get<2, 2>(jacobian_matrix) =
      -(mu_squared * (r_squared - 2.0 * square(z)) - 2.0 * mu * x + 1.0) *
      common_factor;
  return jacobian_matrix;
}

template <typename T>
std::array<tt::remove_cvref_wrap_t<T>, 3> SpecialMobius::operator()(
    const std::array<T, 3>& source_coords) const noexcept {
  return mobius_distortion(source_coords, mu_);
}

boost::optional<std::array<double, 3>> SpecialMobius::inverse(
    const std::array<double, 3>& target_coords) const noexcept {
  // Invert only points inside or on the unit sphere.
  const auto r_squared = magnitude(target_coords);
  if (r_squared <= 1.0 or equal_within_roundoff(r_squared,1.0)) {
    return mobius_distortion(target_coords, -mu_);
  }
  return boost::none;
}

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> SpecialMobius::jacobian(
    const std::array<T, 3>& source_coords) const noexcept {
  return mobius_distortion_jacobian(source_coords, mu_);
}

template <typename T>
tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>
SpecialMobius::inv_jacobian(const std::array<T, 3>& source_coords) const
    noexcept {
  return mobius_distortion_jacobian((*this)(source_coords), -mu_);
}

void SpecialMobius::pup(PUP::er& p) noexcept {
  p | mu_;
  p | is_identity_;
}

bool operator==(const SpecialMobius& lhs, const SpecialMobius& rhs) noexcept {
  return lhs.mu_ == rhs.mu_ and lhs.is_identity_ == rhs.is_identity_;
}

bool operator!=(const SpecialMobius& lhs, const SpecialMobius& rhs) noexcept {
  return not(lhs == rhs);
}

// Explicit instantiations
/// \cond
#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)

#define INSTANTIATE(_, data)                                                   \
  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3> SpecialMobius:: \
  operator()(const std::array<DTYPE(data), 3>& source_coords) const noexcept;  \
  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \
  SpecialMobius::jacobian(const std::array<DTYPE(data), 3>& source_coords)     \
      const noexcept;                                                          \
  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \
  SpecialMobius::inv_jacobian(const std::array<DTYPE(data), 3>& source_coords) \
      const noexcept;

GENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,
                                      std::reference_wrapper<const double>,
                                      std::reference_wrapper<const DataVector>))

#undef DTYPE
#undef INSTANTIATE
/// \endcond
}  // namespace CoordinateMaps
}  // namespace domain


///////////////////////////////////////////////////////////////////////////////
//  Copyright 2018 John Maddock
//  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_BESSEL_ITERATORS_HPP
#define BOOST_MATH_BESSEL_ITERATORS_HPP

#include <boost/math/tools/recurrence.hpp>

namespace boost {
   namespace math {
      namespace detail {

         template <class T>
         struct bessel_jy_recurrence
         {
            bessel_jy_recurrence(T v, T z) : v(v), z(z) {}
            boost::math::tuple<T, T, T> operator()(int k)
            {
               return boost::math::tuple<T, T, T>(1, -2 * (v + k) / z, 1);
            }

            T v, z;
         };
         template <class T>
         struct bessel_ik_recurrence
         {
            bessel_ik_recurrence(T v, T z) : v(v), z(z) {}
            boost::math::tuple<T, T, T> operator()(int k)
            {
               return boost::math::tuple<T, T, T>(1, -2 * (v + k) / z, -1);
            }

            T v, z;
         };
      } // namespace detail

      template <class T>
      struct bessel_j_backwards_iterator
      {
         typedef std::ptrdiff_t difference_type;
         typedef T value_type;
         typedef T* pointer;
         typedef T& reference;
         typedef std::input_iterator_tag iterator_category;

         bessel_j_backwards_iterator(const T& v, const T& x)
            : it(detail::bessel_jy_recurrence<T>(v, x), boost::math::cyl_bessel_j(v, x))
         {
            if(v < 0)
               boost::math::policies::raise_domain_error("bessel_j_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }

         bessel_j_backwards_iterator(const T& v, const T& x, const T& J_v)
            : it(detail::bessel_jy_recurrence<T>(v, x), J_v)
         {
            if(v < 0)
               boost::math::policies::raise_domain_error("bessel_j_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }
         bessel_j_backwards_iterator(const T& v, const T& x, const T& J_v_plus_1, const T& J_v)
            : it(detail::bessel_jy_recurrence<T>(v, x), J_v_plus_1, J_v)
         {
            if (v < -1)
               boost::math::policies::raise_domain_error("bessel_j_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }

         bessel_j_backwards_iterator& operator++()
         {
            ++it;
            return *this;
         }

         bessel_j_backwards_iterator operator++(int)
         {
            bessel_j_backwards_iterator t(*this);
            ++(*this);
            return t;
         }

         T operator*() { return *it; }

      private:
         boost::math::tools::backward_recurrence_iterator< detail::bessel_jy_recurrence<T> > it;
      };

      template <class T>
      struct bessel_i_backwards_iterator
      {
         typedef std::ptrdiff_t difference_type;
         typedef T value_type;
         typedef T* pointer;
         typedef T& reference;
         typedef std::input_iterator_tag iterator_category;

         bessel_i_backwards_iterator(const T& v, const T& x)
            : it(detail::bessel_ik_recurrence<T>(v, x), boost::math::cyl_bessel_i(v, x))
         {
            if(v < -1)
               boost::math::policies::raise_domain_error("bessel_i_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }
         bessel_i_backwards_iterator(const T& v, const T& x, const T& I_v)
            : it(detail::bessel_ik_recurrence<T>(v, x), I_v)
         {
            if(v < -1)
               boost::math::policies::raise_domain_error("bessel_i_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }
         bessel_i_backwards_iterator(const T& v, const T& x, const T& I_v_plus_1, const T& I_v)
            : it(detail::bessel_ik_recurrence<T>(v, x), I_v_plus_1, I_v)
         {
            if(v < -1)
               boost::math::policies::raise_domain_error("bessel_i_backwards_iterator<%1%>", "Order must be > 0 stable backwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }

         bessel_i_backwards_iterator& operator++()
         {
            ++it;
            return *this;
         }

         bessel_i_backwards_iterator operator++(int)
         {
            bessel_i_backwards_iterator t(*this);
            ++(*this);
            return t;
         }

         T operator*() { return *it; }

      private:
         boost::math::tools::backward_recurrence_iterator< detail::bessel_ik_recurrence<T> > it;
      };

      template <class T>
      struct bessel_i_forwards_iterator
      {
         typedef std::ptrdiff_t difference_type;
         typedef T value_type;
         typedef T* pointer;
         typedef T& reference;
         typedef std::input_iterator_tag iterator_category;

         bessel_i_forwards_iterator(const T& v, const T& x)
            : it(detail::bessel_ik_recurrence<T>(v, x), boost::math::cyl_bessel_i(v, x))
         {
            if(v > 1)
               boost::math::policies::raise_domain_error("bessel_i_forwards_iterator<%1%>", "Order must be < 0 stable forwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }
         bessel_i_forwards_iterator(const T& v, const T& x, const T& I_v)
            : it(detail::bessel_ik_recurrence<T>(v, x), I_v)
         {
            if (v > 1)
               boost::math::policies::raise_domain_error("bessel_i_forwards_iterator<%1%>", "Order must be < 0 stable forwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }
         bessel_i_forwards_iterator(const T& v, const T& x, const T& I_v_plus_1, const T& I_v)
            : it(detail::bessel_ik_recurrence<T>(v, x), I_v_plus_1, I_v)
         {
            if (v > 1)
               boost::math::policies::raise_domain_error("bessel_i_forwards_iterator<%1%>", "Order must be < 0 stable forwards recurrence but got %1%", v, boost::math::policies::policy<>());
         }

         bessel_i_forwards_iterator& operator++()
         {
            ++it;
            return *this;
         }

         bessel_i_forwards_iterator operator++(int)
         {
            bessel_i_forwards_iterator t(*this);
            ++(*this);
            return t;
         }

         T operator*() { return *it; }

      private:
         boost::math::tools::forward_recurrence_iterator< detail::bessel_ik_recurrence<T> > it;
      };

   }
} // namespaces

#endif // BOOST_MATH_BESSEL_ITERATORS_HPP