//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_HYPOT_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_HYPOT_HPP_INCLUDED
#include <boost/simd/function/fast.hpp>
#include <boost/simd/function/fast.hpp>

#ifndef BOOST_SIMD_NO_INVALIDS
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/function/scalar/is_inf.hpp>
#include <boost/simd/function/scalar/is_nan.hpp>
#endif
#include <boost/simd/constant/maxexponentm1.hpp>
#include <boost/simd/constant/minexponent.hpp>
#include <boost/simd/function/scalar/abs.hpp>
#include <boost/simd/function/scalar/exponent.hpp>
#include <boost/simd/function/scalar/ldexp.hpp>
#include <boost/simd/function/scalar/max.hpp>
#include <boost/simd/function/scalar/min.hpp>
#include <boost/simd/function/scalar/sqr.hpp>
#include <boost/simd/function/scalar/sqrt.hpp>
#include <boost/dispatch/function/overload.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/function/std.hpp>
#include <boost/config.hpp>
#include <cmath>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  BOOST_DISPATCH_OVERLOAD ( hypot_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_<bd::floating_<A0> >
                          , bd::scalar_<bd::floating_<A0> >
                          )
  {

    BOOST_FORCEINLINE A0 operator() ( A0 a0, A0 a1) const BOOST_NOEXCEPT
    {
      using i_t = bd::as_integer_t<A0>;
      #ifndef BOOST_SIMD_NO_INVALIDS
      if (is_nan(a0) && is_inf(a1)) return Inf<A0>();
      if (is_inf(a0) && is_nan(a1)) return Inf<A0>();
      #endif
      A0 r =  bs::abs(a0);
      A0 i =  bs::abs(a1);
      i_t e =  exponent(bs::max(i, r));
      e = bs::min(bs::max(e,Minexponent<A0>()),Maxexponentm1<A0>());
      return bs::ldexp(sqrt(sqr(bs::ldexp(r, -e))+sqr(bs::ldexp(i, -e))), e);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( hypot_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_<bd::floating_<A0> >
                          , bd::scalar_<bd::floating_<A0> >
                          , boost::simd::std_tag
                          )
  {

    BOOST_FORCEINLINE A0 operator() ( A0 a0, A0 a1
                                    , std_tag const&) const BOOST_NOEXCEPT
    {
      return std::hypot(a0, a1);
    }
  };
} } }

#endif


#include <iostream>
#include <string>
#include <windows.h>
#include <time.h>
#include <Eigen/Dense>
#include "utlt.h"
#include "OneD_system_solve.h"
#include "OneD_exciton_TDDFT_Linear_resp.h"

using namespace std;
using Eigen::VectorXd;

int main()
{
	clock_t t_begin,t_end;
	t_begin=clock();
	int band_considered(3), gridnum(150), kpointnum(151);
	double amplitude1(3.5645),amplitude2(1.0);
	VectorXd w1(2), h1(w1.rows());
	// w1<<2.4, 0.6; h1<<0.0, 8.0;
	w1<<0.5, 0.5; h1<<0.0, 20.0;
	VectorXd w2(4), h2(w2.rows());
	w2<<2.4, 0.6, 2.8, 0.2;
	h2<<0.0, 8.0, 0.0, 10.0;
	// --------------------------------------------------------------------------------
	OneD_system KP_model(w1, h1, "My_kp_model", gridnum, kpointnum, band_considered);
	cout<<KP_model.name<<" is running..."<<endl;
	KP_model.Solve_wf_dens();
	KP_model.Write_Energy();
	KP_model.Write_system_spec ();
	exciton_TDDFT_Tamm_Dancoff_Soft_Coulumb KP_exciton( KP_model,1,2 );
	KP_exciton.Solve_f_kq (0.01);
	KP_exciton.Write_excitation_shift ();
	KP_exciton.Write_excitation_mix ();
	KP_exciton.Write_matrix_fkq ();
	KP_exciton.Write_eff_fkq ();
	KP_exciton.Write_sum_fkq();
	//--------------------------------------------------------------------------------
	// OneD_system Double_model(w2, h2, "dmodel", gridnum, kpointnum, band_considered);
	// cout<<Double_model.name<<" is running..."<<endl;
	// Double_model.Write_system_spec ();
	// Double_model.Solve_wf_dens ();
	// exciton_TDDFT_Tamm_Dancoff_delta Dw_exciton( Double_model,1,2 );
	// Dw_exciton.Solve_f_kq (amplitude1);
	// Dw_exciton.Write_excitation_shift ();
	// Dw_exciton.Write_excitation_mix ();
	// Dw_exciton.Write_matrix_fkq ();
	// Dw_exciton.Write_eff_fkq ();
	// Dw_exciton.Write_sum_fkq();
	//--------------------------------------------------------------------------------
	// OneD_system scmodel(w1, h1, "scmodel", gridnum, kpointnum, band_considered);
	// cout<<scmodel.name<<" is running..."<<endl;
	// scmodel.Write_system_spec ();
	// scmodel.Solve_wf_dens ();
	// exciton_TDDFT_Tamm_Dancoff_Soft_Coulumb sc_exciton( scmodel,1,2 );
	// sc_exciton.Solve_f_kq (amplitude2);
	// sc_exciton.Write_excitation_shift ();
	// sc_exciton.Write_excitation_mix ();
	// sc_exciton.Write_matrix_fkq ();
	// sc_exciton.Write_eff_fkq ();
	// sc_exciton.Write_sum_fkq();
	//---------------------------------------------------------------------------------
	// OneD_system anymodel(w1, h1, "anymodel", gridnum, kpointnum, band_considered);
	// cout<<anymodel.name<<" is running..."<<endl;
	// anymodel.Write_system_spec ();
	// anymodel.Solve_wf_dens ();
	// exciton_TDDFT_Tamm_Dancoff_any_kernel any_exciton( anymodel,1,2 );
	// any_exciton.Solve_f_kq (amplitude1);
	// any_exciton.Write_excitation_shift ();
	// any_exciton.Write_excitation_mix ();
	// any_exciton.Write_matrix_fkq ();
	// any_exciton.Write_eff_fkq ();
	// any_exciton.Write_sum_fkq();
	//---------------------------------------------------------------------------------
	Eigen::Matrix2cd a;
	a<<dcmplx(1,2),dcmplx(2,3),dcmplx(1,4),dcmplx(2,4);
	cout<<a<<endl;
	cout<<a.cwiseAbs()<<endl;
	t_end=clock();
	cout<<"program running time "<<(double(t_end)-double(t_begin))/CLOCKS_PER_SEC<<endl;
	MessageBox(NULL, L"Work finished!", L"Notice", MB_OK);
	return 0;
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_ULPDIST_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_ULPDIST_HPP_INCLUDED

#include <boost/simd/ieee/functions/ulpdist.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/divides.hpp>
#include <boost/simd/include/functions/simd/is_equal.hpp>
#include <boost/simd/include/functions/simd/is_nan.hpp>
#include <boost/simd/include/functions/simd/max.hpp>
#include <boost/simd/include/functions/simd/abs.hpp>
#include <boost/simd/include/functions/simd/frexp.hpp>
#include <boost/simd/include/functions/simd/ldexp.hpp>
#include <boost/simd/include/functions/simd/if_zero_else.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/functions/simd/logical_and.hpp>
#include <boost/simd/include/functions/simd/logical_or.hpp>
#include <boost/simd/include/constants/eps.hpp>
#include <boost/dispatch/meta/as_integer.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( ulpdist_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<floating_<A0>,X>))
                                      ((simd_<floating_<A0>,X>))
                                    )
  {
    typedef A0 result_type;

    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      typedef typename dispatch::meta::as_integer<A0>::type itype;
      itype e1, e2;

      A0 m1 = boost::simd::frexp(a0, e1);
      A0 m2 = boost::simd::frexp(a1, e2);

      itype expo = -boost::simd::max(e1, e2);

      A0 e = select ( boost::simd::is_equal(e1, e2)
                    , boost::simd::abs(m1-m2)
                    , boost::simd::abs( boost::simd::ldexp(a0, expo)
                                      - boost::simd::ldexp(a1, expo)
                                      )
                    );

      return if_zero_else ( logical_or( logical_and(is_nan(a0), is_nan(a1))
                                      , boost::simd::is_equal(a0, a1)
                                      )
                          , e/Eps<A0>()
                          );
    }
  };
} } }

#endif


#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <cassert>

#include <boost/program_options.hpp>

#include <amgcl/util.hpp>
#include <amgcl/io/mm.hpp>
#include <amgcl/io/binary.hpp>

extern "C" {
#include <metis.h>
}

using amgcl::precondition;

//---------------------------------------------------------------------------
void pointwise_graph(
        int n, int block_size,
        const std::vector<int> &ptr,
        const std::vector<int> &col,
        std::vector<int> &pptr,
        std::vector<int> &pcol
        )
{
    int np = n / block_size;

    assert(np * block_size == n);

    // Create pointwise matrix
    std::vector<int> ptr1(np + 1, 0);
    std::vector<int> marker(np, -1);
    for(int ip = 0, i = 0; ip < np; ++ip) {
        for(int k = 0; k < block_size; ++k, ++i) {
            for(int j = ptr[i]; j < ptr[i+1]; ++j) {
                int cp = col[j] / block_size;
                if (marker[cp] != ip) {
                    marker[cp] = ip;
                    ++ptr1[ip+1];
                }
            }
        }
    }

    std::partial_sum(ptr1.begin(), ptr1.end(), ptr1.begin());
    std::fill(marker.begin(), marker.end(), -1);

    std::vector<int> col1(ptr1.back());

    for(int ip = 0, i = 0; ip < np; ++ip) {
        int row_beg = ptr1[ip];
        int row_end = row_beg;

        for(int k = 0; k < block_size; ++k, ++i) {
            for(int j = ptr[i]; j < ptr[i+1]; ++j) {
                int cp = col[j] / block_size;

                if (marker[cp] < row_beg) {
                    marker[cp] = row_end;
                    col1[row_end++] = cp;
                }
            }
        }
    }

    // Transpose pointwise matrix
    int nnz = ptr1.back();

    std::vector<int> ptr2(np + 1, 0);
    std::vector<int> col2(nnz);

    for(int i = 0; i < nnz; ++i)
        ++( ptr2[ col1[i] + 1 ] );

    std::partial_sum(ptr2.begin(), ptr2.end(), ptr2.begin());

    for(int i = 0; i < np; ++i)
        for(int j = ptr1[i]; j < ptr1[i+1]; ++j)
            col2[ptr2[col1[j]]++] = i;

    std::rotate(ptr2.begin(), ptr2.end() - 1, ptr2.end());
    ptr2.front() = 0;

    // Merge both matrices.
    std::fill(marker.begin(), marker.end(), -1);
    pptr.resize(np + 1, 0);

    for(int i = 0; i < np; ++i) {
        for(int j = ptr1[i]; j < ptr1[i+1]; ++j) {
            int c = col1[j];
            if (marker[c] != i) {
                marker[c] = i;
                ++pptr[i + 1];
            }
        }

        for(int j = ptr2[i]; j < ptr2[i+1]; ++j) {
            int c = col2[j];
            if (marker[c] != i) {
                marker[c] = i;
                ++pptr[i + 1];
            }
        }
    }

    std::partial_sum(pptr.begin(), pptr.end(), pptr.begin());
    std::fill(marker.begin(), marker.end(), -1);

    pcol.resize(pptr.back());

    for(int i = 0; i < np; ++i) {
        int row_beg = pptr[i];
        int row_end = row_beg;

        for(int j = ptr1[i]; j < ptr1[i+1]; ++j) {
            int c = col1[j];

            if (marker[c] < row_beg) {
                marker[c] = row_end;
                pcol[row_end++] = c;
            }
        }

        for(int j = ptr2[i]; j < ptr2[i+1]; ++j) {
            int c = col2[j];

            if (marker[c] < row_beg) {
                marker[c] = row_end;
                pcol[row_end++] = c;
            }
        }
    }
}

//---------------------------------------------------------------------------
std::vector<int> pointwise_partition(
        int npart,
        const std::vector<int> &ptr,
        const std::vector<int> &col
        )
{
    int nrows = ptr.size() - 1;

    std::vector<int> part(nrows);

    if (npart == 1) {
        std::fill(part.begin(), part.end(), 0);
    } else {
        int edgecut;

#if defined(METIS_VER_MAJOR) && (METIS_VER_MAJOR >= 5)
        int nconstraints = 1;
        METIS_PartGraphKway(
                &nrows, //nvtxs
                &nconstraints, //ncon -- new
                const_cast<int*>(ptr.data()), //xadj
                const_cast<int*>(col.data()), //adjncy
                NULL, //vwgt
                NULL, //vsize -- new
                NULL, //adjwgt
                &npart,
                NULL,//real t *tpwgts,
                NULL,// real t ubvec
                NULL,
                &edgecut,
                part.data()
                );
#else
        int wgtflag = 0;
        int numflag = 0;
        int options = 0;

        METIS_PartGraphKway(
                &nrows,
                const_cast<int*>(ptr.data()),
                const_cast<int*>(col.data()),
                NULL,
                NULL,
                &wgtflag,
                &numflag,
                &npart,
                &options,
                &edgecut,
                part.data()
                );
#endif
    }

    return part;
}

//---------------------------------------------------------------------------
std::vector<int> partition(
        int n, int nparts, int block_size,
        const std::vector<int> &ptr, const std::vector<int> &col
        )
{
    // Pointwise graph
    std::vector<int> pptr, pcol;
    pointwise_graph(n, block_size, ptr, col, pptr, pcol);

    // Pointwise partition
    std::vector<int> ppart = pointwise_partition(nparts, pptr, pcol);

    std::vector<int> part(n);
    for(int i = 0; i < n; ++i)
        part[i] = ppart[i / block_size];

    return part;
}

//---------------------------------------------------------------------------
int main(int argc, char *argv[]) {
    namespace po = boost::program_options;

    try {
        std::string ifile;
        std::string ofile = "partition.mtx";

        int nparts, block_size;

        po::options_description desc("Options");

        desc.add_options()
            ("help,h", "show help")
            ("input,i",      po::value<std::string>(&ifile)->required(), "Input matrix")
            ("output,o",     po::value<std::string>(&ofile)->default_value(ofile), "Output file")
            (
             "binary,B",
             po::bool_switch()->default_value(false),
             "When specified, treat input files as binary instead of as MatrixMarket. "
            )
            ("nparts,n",     po::value<int>(&nparts)->required(), "Number of parts")
            ("block_size,b", po::value<int>(&block_size)->default_value(1), "Block size")
            ;

        po::positional_options_description pd;
        pd.add("input", 1);

        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).positional(pd).run(), vm);

        if (vm.count("help")) {
            std::cout << desc << std::endl;
            return 0;
        }

        po::notify(vm);

        size_t rows;
        std::vector<int> ptr, col;
        std::vector<double> val;

        bool binary = vm["binary"].as<bool>();

        if (binary) {
            amgcl::io::read_crs(ifile, rows, ptr, col, val);
        } else {
            size_t cols;
            std::tie(rows, cols) = amgcl::io::mm_reader(ifile)(ptr, col, val);
            precondition(rows == cols, "Non-square system matrix");
        }

        std::vector<int> part = partition(rows, nparts, block_size, ptr, col);

        if (binary) {
            std::ofstream p(ofile.c_str(), std::ios::binary);

            amgcl::io::write(p, rows);
            amgcl::io::write(p, size_t(1));
            amgcl::io::write(p, part);
        } else {
            amgcl::io::mm_write(ofile, &part[0], part.size());
        }
    } catch (const std::exception &e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}


//
// Created by Amir Masoud Abdol on 2019-05-22.
//

#define BOOST_TEST_DYN_LINK

#define BOOST_TEST_MODULE ReviewStrategy Tests

#include <boost/test/unit_test.hpp>
namespace tt = boost::test_tools;

#include <armadillo>
#include <iostream>
#include <vector>
#include <algorithm>

#include "sam.h"
#include "test_fixtures.h"

#include "Researcher.h"
#include "HackingStrategy.h"

using namespace arma;
using namespace sam;
using namespace std;

BOOST_AUTO_TEST_SUITE( journal );

BOOST_AUTO_TEST_CASE( constructor ) {

}

BOOST_AUTO_TEST_CASE( general_setter_getter_methods ) {

}

BOOST_AUTO_TEST_SUITE_END();

BOOST_FIXTURE_TEST_SUITE( selection_strategies, SampleResearch );

    BOOST_AUTO_TEST_CASE( significant_selection_pub_bias_rate )
    {

        // Setting up the Research

//        nobs = 20;
        mean = 1;
        var = 1;
        nobs = single_sample_find_df(0, mean, var, 0.05, TestStrategy::TestSide::TwoSided);
//        nobs /= 2;

        nsims = 10000;

        float n_pubs = j_conf["max_pubs"].get<int>() * nsims;

    	initResearch();

        // Starting the Test

        vec pub_sigs(n_pubs);

    	BOOST_TEST (setup.ng() == ng);

        for (auto &p : {0., 0.05, 0.5, 0.95} ) {

            s_s_conf["pub_bias_rate"] = p;
            researcher.journal->review_strategy = ReviewStrategy::build(s_s_conf);

            for (int i = 0; i < nsims; i++) {

                while (researcher.journal->isStillAccepting()) {

                    researcher.prepareResearch();

                    researcher.performResearch();

                    researcher.publishResearch();

                }

                for (int k = 0; k < j_conf["max_pubs"].get<int>(); ++k)
                {
                    pub_sigs[i * j_conf["max_pubs"].get<int>() + k] = researcher.journal->publications_list[k].sig;
                }

                researcher.journal->clear();
            }

            std::cout << arma::mean(pub_sigs) << std::endl;

            BOOST_TEST( arma::mean(pub_sigs) > s_s_conf["pub_bias_rate"] );

        }

    }

    BOOST_AUTO_TEST_CASE( random_selection )
    {

        // Setting up the Research

        mean = 0.25;
        var = 1;
        nobs = single_sample_find_df(0, mean, var, 0.05, TestStrategy::TestSide::TwoSided);
        nobs /= 3;

        nsims = 1000;

        float n_pubs = j_conf["max_pubs"].get<int>() * nsims;

        s_s_conf["name"] = "RandomSelection";

        initResearch();

        // Starting the Test

        vec pub_sigs;

        BOOST_TEST (setup.ng() == ng);

        pub_sigs.set_size(n_pubs);

        for (int i = 0; i < nsims; i++) {

            while (researcher.journal->isStillAccepting()) {

                researcher.prepareResearch();

                researcher.performResearch();

                researcher.publishResearch();

            }

            for (int k = 0; k < j_conf["max_pubs"].get<int>(); ++k)
            {
                pub_sigs[i * j_conf["max_pubs"].get<int>() + k] = researcher.journal->publications_list[k].sig;
            }

            researcher.journal->clear();
        }

        BOOST_CHECK_SMALL( arma::mean(pub_sigs) - 0.5 , 0.1);

    }

BOOST_AUTO_TEST_SUITE_END();


//  Copyright (c) 2011-2013 Thomas Heller
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "jacobi.hpp"
#include <boost/program_options.hpp>
#include <iostream>
#include <chrono>

using boost::program_options::variables_map;
using boost::program_options::options_description;
using boost::program_options::value;
using boost::program_options::store;
using boost::program_options::parse_command_line;

using boost::shared_ptr;
using std::min;
using std::vector;

void jacobi_kernel_wrap(size_t y_begin, size_t y_end, size_t n, vector<double> & dst, vector<double> const & src) {
    for(size_t y = y_begin; y < y_end; ++y) {
        size_t offset = y * n;
        for(size_t x = offset + 1; x < offset + n-1; ++x) {
            dst[x] = (src[x-n] + src[x+n] + src[x] + src[x-1] + src[x+1]) * 0.2;
        }
    }
}

void jacobi( size_t n , size_t iterations, size_t block_size, std::string output_filename) {
    shared_ptr< vector<double> > grid_new(new vector<double>(n * n, 1));
    shared_ptr< vector<double> > grid_old(new vector<double>(n * n, 1));

    size_t n_block = static_cast<size_t>(std::ceil(double(n)/block_size));

#pragma omp parallel
{
#pragma omp single
{
    auto start = std::chrono::high_resolution_clock::now();

    for(size_t i = 0; i < iterations; ++i) {
        for(size_t y = 1, j = 0; y < n - 1; y += block_size, ++j) {
            size_t y_end = min(y + block_size, n - 1);

            double *dest = (*grid_new).data();
            double *src = (*grid_old).data();

#pragma omp task firstprivate(y, y_end, grid_new, grid_old) depend(inout: dest[y*n], src[y*n])
        {
            jacobi_kernel_wrap(y, y_end, n, boost::ref(*grid_new), boost::cref(*grid_old));
        }
            std::swap(grid_new, grid_old);
        }
    }
#pragma omp taskwait
    auto end = std::chrono::high_resolution_clock::now();
    double elapsed = std::chrono::duration_cast< std::chrono::duration<double> >(end-start).count();
    jacobi_smp::report_timing(n, iterations, elapsed);
    jacobi_smp::output_grid(output_filename, *grid_old, n);
}}

}

int main(int argc, char **argv)
{
    options_description
        desc_cmd("usage: jacobi_omp [options]");

    desc_cmd.add_options()
        (
         "n", value<std::size_t>()->default_value(16)
         , "Will run on grid with dimensions (n x n)"
        )
        (
         "iterations", value<std::size_t>()->default_value(1000)
         , "Number of iterations"
        )
        (
         "block-size", value<std::size_t>()->default_value(256)
         , "Block size of the different chunks to calculate in parallel"
        )
        (
         "output-filename", value<std::string>()
         , "Filename of the result (if empty no result is written)"
        );

    variables_map vm;
    store(parse_command_line( argc, argv, desc_cmd), vm);
    boost::program_options::notify(vm);

    std::size_t n           = vm["n"].as<std::size_t>();
    std::size_t iterations  = vm["iterations"].as<std::size_t>();
    std::size_t block_size  = vm["block-size"].as<std::size_t>();

    std::string output_filename;
    if(vm.count("output-filename"))
    {
        output_filename = vm["output-filename"].as<std::string>();
    }

    jacobi(n, iterations, block_size, output_filename);

    return 0;
}


#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 <boost/test/unit_test.hpp>

#include "Werk/Utility/Counter.hpp"

BOOST_AUTO_TEST_SUITE(CounterTest)

BOOST_AUTO_TEST_CASE(TestAdd)
{
	Werk::Counter<> c;
	BOOST_REQUIRE_EQUAL(c.value(), 0);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 1);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 2);

	c.reset();
	BOOST_REQUIRE_EQUAL(c.value(), 0);
}

BOOST_AUTO_TEST_CASE(TestMultiply)
{
	Werk::Counter<uint64_t, 7, 3, Werk::CounterMode::MULTIPLY> c;
	BOOST_REQUIRE_EQUAL(c.value(), 7);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 21);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 63);

	c.reset();
	BOOST_REQUIRE_EQUAL(c.value(), 7);
}

BOOST_AUTO_TEST_CASE(TestShift)
{
	Werk::Counter<uint64_t, 1, 1, Werk::CounterMode::LSHIFT> c;
	BOOST_REQUIRE_EQUAL(c.value(), 1);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 2);

	c.increment();
	BOOST_REQUIRE_EQUAL(c.value(), 4);

	c.reset();
	BOOST_REQUIRE_EQUAL(c.value(), 1);
}

BOOST_AUTO_TEST_SUITE_END()


/* boost random/seed_seq.hpp header file
 *
 * Copyright Steven Watanabe 2010
 * 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)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id: seed_seq.hpp 80996 2012-10-16 03:11:52Z marshall $
 *
 */

#ifndef BOOST_RANDOM_SEED_SEQ_HPP
#define BOOST_RANDOM_SEED_SEQ_HPP

#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <cstddef>
#include <vector>
#include <algorithm>
#include <iterator>

#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
#include <initializer_list>
#endif

namespace boost {
namespace random {

/**
 * The class @c seed_seq stores a sequence of 32-bit words
 * for seeding a \pseudo_random_number_generator.  These
 * words will be combined to fill the entire state of the
 * generator.
 */
class seed_seq {
public:
    typedef boost::uint_least32_t result_type;

    /** Initializes a seed_seq to hold an empty sequence. */
    seed_seq() {}
#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
    /** Initializes the sequence from an initializer_list. */
    template<class T>
    seed_seq(const std::initializer_list<T>& il) : v(il.begin(), il.end()) {}
#endif
    /** Initializes the sequence from an iterator range. */
    template<class Iter>
    seed_seq(Iter first, Iter last) : v(first, last) {}
    /** Initializes the sequence from Boost.Range range. */
    template<class Range>
    explicit seed_seq(const Range& range)
      : v(boost::begin(range), boost::end(range)) {}

    /**
     * Fills a range with 32-bit values based on the stored sequence.
     *
     * Requires: Iter must be a Random Access Iterator whose value type
     * is an unsigned integral type at least 32 bits wide.
     */
    template<class Iter>
    void generate(Iter first, Iter last) const
    {
        typedef typename std::iterator_traits<Iter>::value_type value_type;
        std::fill(first, last, static_cast<value_type>(0x8b8b8b8bu));
        std::size_t s = v.size();
        std::size_t n = last - first;
        std::size_t t =
            (n >= 623) ? 11 :
            (n >=  68) ?  7 :
            (n >=  39) ?  5 :
            (n >=   7) ?  3 :
            (n - 1)/2;
        std::size_t p = (n - t) / 2;
        std::size_t q = p + t;
        std::size_t m = (std::max)(s+1, n);
        value_type mask = 0xffffffffu;
        for(std::size_t k = 0; k < m; ++k) {
            value_type r1 =
                *(first + k%n) ^ *(first + (k+p)%n) ^ *(first + (k+n-1)%n);
            r1 = r1 ^ (r1 >> 27);
            r1 = (r1 * 1664525u) & mask;
            value_type r2 = r1 +
                ((k == 0) ? s :
                 (k <= s) ? k % n + v[k - 1] :
                 (k % n));
            *(first + (k+p)%n) = (*(first + (k+p)%n) + r1) & mask;
            *(first + (k+q)%n) = (*(first + (k+q)%n) + r2) & mask;
            *(first + k%n) = r2;
        }
        for(std::size_t k = m; k < m + n; ++k) {
            value_type r3 =
                (*(first + k%n) + *(first + (k+p)%n) + *(first + (k+n-1)%n))
                & mask;
            r3 = r3 ^ (r3 >> 27);
            r3 = (r3 * 1566083941u) & mask;
            value_type r4 = r3 - k%m;
            *(first + (k+p)%n) ^= r3;
            *(first + (k+q)%n) ^= r4;
            *(first + k%n) = r4;
        }
    }
    /** Returns the size of the sequence. */
    std::size_t size() const { return v.size(); }
    /** Writes the stored sequence to iter. */
    template<class Iter>
    void param(Iter out) { std::copy(v.begin(), v.end(), out); }
private:
    std::vector<result_type> v;
};

}
}

#endif


#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;
}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_NEARBYINT_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_NEARBYINT_HPP_INCLUDED

#include <boost/simd/constant/twotonmb.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/detail/enforce_precision.hpp>
#include <boost/simd/function/abs.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/config.hpp>
#include <boost/simd/function/std.hpp>
#include <boost/simd/function/is_negative.hpp>
#include <cmath>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  BOOST_DISPATCH_OVERLOAD ( nearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::floating_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      detail::enforce_precision<A0> enforcer;
      const A0 v = bs::abs(a0);
      const A0 t2n = Twotonmb<A0>();
      A0 d0 = (v+t2n);
      A0 d = (d0-t2n);
      d = (v < t2n)?d:v;
      return is_negative(a0) ? -d : d;
    }
  };

  BOOST_DISPATCH_OVERLOAD ( nearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::integer_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 a0) const BOOST_NOEXCEPT
    {
      return a0;
    }
  };

  BOOST_DISPATCH_OVERLOAD ( nearbyint_
                          , (typename A0)
                          , bd::cpu_
                          , bs::std_tag
                          , bd::scalar_< bd::floating_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const std_tag&, A0 a0) const BOOST_NOEXCEPT
    {
      return std::nearbyint(a0);
    }
  };

} } }

#endif


#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


///////////////////////////////////////////////////////////////////////////////
// importance_sampling::grid.hpp                                             //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_GRID_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_GRID_HPP_ER_2009
#include <boost/numeric/conversion/converter.hpp>
#include <boost/limits.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace importance_sampling{

// Equally spaced univariate grid.
template<typename T>
class grid{
    public:
    typedef std::size_t size_type;
    private:
    typedef boost::numeric::converter<T,size_type> conv_t;
    public:
    typedef T result_type;
    grid(T min, T max, size_type n);
    T operator()();

    private:
    size_type   n_;
    size_type   i_;
    T           d_;
    T           x_;
};

template<typename T>
grid<T>::grid(T min, T max, size_type n)
: n_(n), i_(0), d_((max-min)/conv_t::convert(n-1)),x_(min) {}

template<typename T>
T grid<T>::operator()(){
    BOOST_ASSERT(i_<n_);
    T result = x_;
    x_ += d_;
    ++i_;
    return result;
}

}// importance_sampling
}// detail
}// statistics
}// boost

#endif


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 *  @file   testGaussianConditional.cpp
 *  @brief  Unit tests for Conditional gaussian
 *  @author Christian Potthast
 **/

#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/TestableAssertions.h>

#include <gtsam/base/Matrix.h>
#include <gtsam/linear/JacobianFactor.h>
#include <gtsam/linear/GaussianConditional.h>
#include <gtsam/linear/GaussianBayesNet.h>

#include <iostream>
#include <sstream>
#include <vector>
#include <boost/assign/std/list.hpp>
#include <boost/assign/std/vector.hpp>

using namespace gtsam;
using namespace std;
using namespace boost::assign;

static const double tol = 1e-5;
static const Index _x_=0, _x1_=1, _l1_=2;

Matrix R = Matrix_(2,2,
		-12.1244,  -5.1962,
					0.,   4.6904);

/* ************************************************************************* */
TEST(GaussianConditional, constructor)
{
  Matrix S1 = Matrix_(2,2,
      -5.2786,  -8.6603,
      5.0254,   5.5432);
  Matrix S2 = Matrix_(2,2,
      -10.5573,  -5.9385,
      5.5737,   3.0153);
  Matrix S3 = Matrix_(2,2,
      -11.3820,  -7.2581,
      -3.0153,  -3.5635);

  Vector d = Vector_(2, 1.0, 2.0);
  Vector s = Vector_(2, 3.0, 4.0);

  list<pair<Index, Matrix> > terms;
  terms +=
      make_pair(3, S1),
      make_pair(5, S2),
      make_pair(7, S3);

  GaussianConditional actual(1, d, R, terms, s);

  GaussianConditional::const_iterator it = actual.beginFrontals();
  EXPECT(assert_equal(Index(1), *it));
  EXPECT(assert_equal(R, actual.get_R()));
  ++ it;
  EXPECT(it == actual.endFrontals());

  it = actual.beginParents();
  EXPECT(assert_equal(Index(3), *it));
  EXPECT(assert_equal(S1, actual.get_S(it)));

  ++ it;
  EXPECT(assert_equal(Index(5), *it));
  EXPECT(assert_equal(S2, actual.get_S(it)));

  ++ it;
  EXPECT(assert_equal(Index(7), *it));
  EXPECT(assert_equal(S3, actual.get_S(it)));

  ++it;
  EXPECT(it == actual.endParents());

  EXPECT(assert_equal(d, actual.get_d()));
  EXPECT(assert_equal(s, actual.get_sigmas()));

  // test copy constructor
  GaussianConditional copied(actual);
  EXPECT(assert_equal(d, copied.get_d()));
  EXPECT(assert_equal(s, copied.get_sigmas()));
  EXPECT(assert_equal(R, copied.get_R()));
}

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

GaussianConditional construct() {
	Vector d = Vector_(2, 1.0, 2.0);
	Vector s = Vector_(2, 3.0, 4.0);
	GaussianConditional::shared_ptr shared(new GaussianConditional(1, d, R, s));
	return *shared;
}

TEST(GaussianConditional, return_value)
{
	Vector d = Vector_(2, 1.0, 2.0);
	Vector s = Vector_(2, 3.0, 4.0);
  GaussianConditional copied = construct();
  EXPECT(assert_equal(d, copied.get_d()));
  EXPECT(assert_equal(s, copied.get_sigmas()));
  EXPECT(assert_equal(R, copied.get_R()));
}

/* ************************************************************************* */
TEST( GaussianConditional, equals )
{
  // create a conditional gaussian node
  Matrix A1(2,2);
  A1(0,0) = 1 ; A1(1,0) = 2;
  A1(0,1) = 3 ; A1(1,1) = 4;

  Matrix A2(2,2);
  A2(0,0) = 6 ; A2(1,0) = 0.2;
  A2(0,1) = 8 ; A2(1,1) = 0.4;

  Matrix R(2,2);
  R(0,0) = 0.1 ; R(1,0) = 0.3;
  R(0,1) = 0.0 ; R(1,1) = 0.34;

  Vector tau(2);
  tau(0) = 1.0;
  tau(1) = 0.34;

  Vector d(2);
  d(0) = 0.2; d(1) = 0.5;

  GaussianConditional
    expected(_x_,d, R, _x1_, A1, _l1_, A2, tau),
    actual(_x_,d, R, _x1_, A1, _l1_, A2, tau);

  EXPECT( expected.equals(actual) );
}

/* ************************************************************************* */
TEST( GaussianConditional, solve )
{
  //expected solution
  Vector expectedX(2);
  expectedX(0) = 20-3-11 ; expectedX(1) = 40-7-15;

  // create a conditional Gaussian node
  Matrix R = Matrix_(2,2,   1., 0.,
                            0., 1.);

  Matrix A1 = Matrix_(2,2,  1., 2.,
                            3., 4.);

  Matrix A2 = Matrix_(2,2,  5., 6.,
                            7., 8.);

  Vector d(2);
  d(0) = 20.0; d(1) = 40.0;

  Vector tau = ones(2);

  GaussianConditional cg(_x_, d, R, _x1_, A1, _l1_, A2, tau);

  Vector sx1(2);
  sx1(0) = 1.0; sx1(1) = 1.0;

  Vector sl1(2);
  sl1(0) = 1.0; sl1(1) = 1.0;

  VectorValues solution(vector<size_t>(3, 2));
  solution[_x_]  = d;
  solution[_x1_] = sx1; // parents
  solution[_l1_] = sl1;

  VectorValues expected(vector<size_t>(3, 2));
  expected[_x_] = expectedX;
  expected[_x1_] = sx1;
  expected[_l1_] = sl1;
  cg.solveInPlace(solution);

  EXPECT(assert_equal(expected, solution, 0.0001));
}

/* ************************************************************************* */
TEST( GaussianConditional, solve_simple )
{
	Matrix full_matrix = Matrix_(4, 7,
			1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.1,
			0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.2,
			0.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.3,
			0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 0.4);

	// solve system as a non-multifrontal version first
	// 2 variables, frontal has dim=4
	vector<size_t> dims; dims += 4, 2, 1;
	GaussianConditional::rsd_type matrices(full_matrix, dims.begin(), dims.end());
	Vector sigmas = ones(4);
	vector<size_t> cgdims; cgdims += _x_, _x1_;
	GaussianConditional cg(cgdims.begin(), cgdims.end(), 1, matrices, sigmas);

	// partial solution
	Vector sx1 = Vector_(2, 9.0, 10.0);

	// elimination order; _x_, _x1_
	vector<size_t> vdim; vdim += 4, 2;
	VectorValues actual(vdim);
	actual[_x1_] = sx1; // parent

	VectorValues expected(vdim);
	expected[_x1_] = sx1;
	expected[_x_] = Vector_(4, -3.1,-3.4,-11.9,-13.2);

	// verify indices/size
	EXPECT_LONGS_EQUAL(2, cg.size());
	EXPECT_LONGS_EQUAL(4, cg.dim());

	// solve and verify
	cg.solveInPlace(actual);
	EXPECT(assert_equal(expected, actual, tol));
}

/* ************************************************************************* */
TEST( GaussianConditional, solve_multifrontal )
{
	// create full system, 3 variables, 2 frontals, all 2 dim
	Matrix full_matrix = Matrix_(4, 7,
			1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.1,
			0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.2,
			0.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.3,
			0.0, 0.0, 0.0, 3.0, 0.0, 4.0, 0.4);

	// 3 variables, all dim=2
	vector<size_t> dims; dims += 2, 2, 2, 1;
	GaussianConditional::rsd_type matrices(full_matrix, dims.begin(), dims.end());
	Vector sigmas = ones(4);
	vector<size_t> cgdims; cgdims += _x_, _x1_, _l1_;
	GaussianConditional cg(cgdims.begin(), cgdims.end(), 2, matrices, sigmas);

	EXPECT(assert_equal(Vector_(4, 0.1, 0.2, 0.3, 0.4), cg.get_d()));

	// partial solution
	Vector sl1 = Vector_(2, 9.0, 10.0);

	// elimination order; _x_, _x1_, _l1_
	VectorValues actual(vector<size_t>(3, 2));
	actual[_l1_] = sl1; // parent

	VectorValues expected(vector<size_t>(3, 2));
	expected[_x_] = Vector_(2, -3.1,-3.4);
	expected[_x1_] = Vector_(2, -11.9,-13.2);
	expected[_l1_] = sl1;

	// verify indices/size
	EXPECT_LONGS_EQUAL(3, cg.size());
	EXPECT_LONGS_EQUAL(4, cg.dim());

	// solve and verify
	cg.solveInPlace(actual);
	EXPECT(assert_equal(expected, actual, tol));

}

/* ************************************************************************* */
TEST( GaussianConditional, solveTranspose ) {
	static const Index _y_=1;
	/** create small Chordal Bayes Net x <- y
	 * x y d
	 * 1 1 9
	 *   1 5
	 */
	Matrix R11 = Matrix_(1, 1, 1.0), S12 = Matrix_(1, 1, 1.0);
	Matrix R22 = Matrix_(1, 1, 1.0);
	Vector d1(1), d2(1);
	d1(0) = 9;
	d2(0) = 5;
	Vector tau(1);
	tau(0) = 1.0;

	// define nodes and specify in reverse topological sort (i.e. parents last)
	GaussianConditional::shared_ptr Px_y(new GaussianConditional(_x_, d1, R11, _y_, S12, tau));
	GaussianConditional::shared_ptr Py(new GaussianConditional(_y_, d2, R22, tau));
	GaussianBayesNet cbn;
	cbn.push_back(Px_y);
	cbn.push_back(Py);

	// x=R'*y, y=inv(R')*x
	// 2 = 1    2
	// 5   1 1  3

	VectorValues y(vector<size_t>(2,1)), x(vector<size_t>(2,1));
	x[_x_] = Vector_(1,2.);
	x[_y_] = Vector_(1,5.);
	y[_x_] = Vector_(1,2.);
	y[_y_] = Vector_(1,3.);

	// test functional version
	VectorValues actual = backSubstituteTranspose(cbn,x);
	CHECK(assert_equal(y,actual));
}

/* ************************************************************************* */
TEST( GaussianConditional, computeInformation ) {

  // Create R matrix
  Matrix R = (Matrix(4,4) <<
      1, 2, 3, 4,
      0, 5, 6, 7,
      0, 0, 8, 9,
      0, 0, 0, 10).finished();

  // Create conditional
  GaussianConditional conditional(0, Vector::Zero(4), R, Vector::Constant(4, 1.0));

  // Expected information matrix (using permuted R)
  Matrix IExpected = R.transpose() * R;

  // Actual information matrix (conditional should permute R)
  Matrix IActual = conditional.computeInformation();
  EXPECT(assert_equal(IExpected, IActual));
}

/* ************************************************************************* */
TEST( GaussianConditional, isGaussianFactor ) {

  // Create R matrix
  Matrix R = (Matrix(4,4) <<
      1, 2, 3, 4,
      0, 5, 6, 7,
      0, 0, 8, 9,
      0, 0, 0, 10).finished();

  // Create a conditional
  GaussianConditional conditional(0, Vector::Zero(4), R, Vector::Constant(4, 1.0));

  // Expected information matrix computed by conditional
  Matrix IExpected = conditional.computeInformation();

  // Expected information matrix computed by a factor
  JacobianFactor jf = *conditional.toFactor();
  Matrix IActual = jf.getA(jf.begin()).transpose() * jf.getA(jf.begin());

  EXPECT(assert_equal(IExpected, IActual));
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */


//==================================================================================================
/*!
  @file

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/pack.hpp>
#include <boost/simd/function/tenpower.hpp>
#include <boost/simd/function/bits.hpp>
#include <boost/simd/meta/cardinal_of.hpp>
#include <simd_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/two.hpp>
#include <boost/simd/constant/mtwo.hpp>
#include <boost/simd/constant/constant.hpp>
#include <boost/simd/constant/ratio.hpp>

template <typename T, int N, typename Env>
void test(Env& $)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using p_t = bs::pack<T, N>;
  using fT =  bd::as_floating_t<T>;
  using f_t = bs::pack<fT, N>;

  T a1[N];
  fT b[N];
  for(int i = 0; i < N; ++i)
  {
    a1[i] = (i%2) ? T(i) : T(-i);
    b[i] = bs::tenpower(a1[i]) ;
  }
  p_t aa1(&a1[0], &a1[0]+N);
  f_t bb (&b[0], &b[0]+N);
  STF_ULP_EQUAL(bs::tenpower(aa1), bb, 0.5);
}

STF_CASE_TPL("Check tenpower on pack" , (int32_t)(int64_t)(uint32_t)(uint64_t))//STF_NUMERIC_TYPES)
{
  namespace bs = boost::simd;
  using p_t = bs::pack<T>;
  static const std::size_t N = bs::cardinal_of<p_t>::value;
  test<T, N>($);
  test<T, N/2>($);
  test<T, N*2>($);
}

STF_CASE_TPL (" tenpower unsigned_int", STF_UNSIGNED_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::tenpower;
  using p_t = bs::pack<T>;
  using r_t = decltype(tenpower(p_t()));

  STF_TYPE_IS(r_t, bd::as_floating_t<p_t>);

  // specific values tests
  STF_ULP_EQUAL(tenpower(bs::One<p_t>()), r_t(10), 0.5);
  STF_ULP_EQUAL(tenpower(bs::Two<p_t>()), r_t(100), 0.5);
  STF_ULP_EQUAL(tenpower(bs::Zero<p_t>()), bs::One<r_t>(), 0.5);
} // end of test for unsigned_int_

STF_CASE_TPL (" tenpower signed_int",  STF_SIGNED_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::tenpower;
  using p_t = bs::pack<T>;
  using r_t = decltype(tenpower(p_t()));

  STF_TYPE_IS(r_t, bd::as_floating_t<p_t>);

  // specific values tests
  STF_ULP_EQUAL(tenpower(bs::Mone<p_t>()), r_t(1.0/10), 0.5);
  STF_ULP_EQUAL(tenpower(bs::One<p_t>()), bs::Ten<r_t>(), 0.5);
  STF_ULP_EQUAL(tenpower(bs::Two<p_t>()), r_t(100), 0.5);
  STF_ULP_EQUAL(tenpower(bs::Mtwo<p_t>()), r_t(1.0/100), 0.5);
  STF_ULP_EQUAL(tenpower(bs::Zero<p_t>()), bs::One<r_t>(), 0.5);
} // end of test for signed_int_


#pragma once

#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/numeric/ublas/banded.hpp>

#include <skynet/core/algorithm.hpp>

namespace skynet{

	namespace ublas = boost::numeric::ublas;

	using ublas::prod;
	using ublas::inner_prod;
	using ublas::trans;
	using ublas::row;
	using ublas::column;

	typedef ublas::vector<double>   vectord;
	typedef ublas::matrix<double>   matrixd;

	//namespace boost{namespace numeric{namespace ublas{

	//

	////	template <typename T>
	////	matrix<T> inverse(const matrix<T>& A) {
	////		typedef permutation_matrix<size_t> pmatrix;

	////		pmatrix pm(A.size1());
	////		// perform LU-factorization

	////		matrix<T> A_LU(A.size1(), A.size2());
	////		A_LU.assign(A);
	////		int res = lu_factorize(A_LU,pm);
	////		if( res != 0 ){
	////			THROW_EXCEPTION(std::runtime_error("The matrix is singular."));
	////		}

	////		// create identity matrix of "inverse"
	////		matrix<T> mat_inv(A.size1(), A.size2());
	////		mat_inv.assign(ublas::identity_matrix<T>(A.size1()));

	////		// backsubstitute to get the inverse
	////		lu_substitute(A_LU, pm, mat_inv);

	////		return mat_inv;
	////	}

	//}}}

	template <typename E>
	std::ostream& operator<<(std::ostream &os, const ublas::vector_expression<E> &ve){
		os << "[ ";
		for (size_t i = 0; i < ve().size(); ++i){
			os << ve()[i] << " ";
		}
		os <<"]";
		return os;
	}

}

namespace boost{namespace numeric{namespace ublas{

	template <typename V>
	typename enable_if<is_floating_point<typename V::value_type>, vector<typename V::value_type>>::type
		normalize(const vector_expression<V> &v_e)
	{
		typedef typename V::value_type			value_type;
		auto norm = norm_2(v_e());
		if(norm == 0){
			norm += numeric_limits<value_type>::epsilon();
		}

		vector<value_type> temp(v_e().size());
		skynet::transform(v_e(), temp, [norm](value_type e){
			return e/norm;
		});

		return temp;
	}

}}}


//
//  Created by Robert Binna
//

#include <boost/test/unit_test.hpp>
#include <hot/commons/Algorithms.hpp>

namespace hot { namespace commons {

BOOST_AUTO_TEST_SUITE(AlgorithmsTest)

BOOST_AUTO_TEST_CASE(testGetLeastSignificantBitIndexInByte) {
	for(int i=0; i < 8; ++i) {
		BOOST_REQUIRE_EQUAL(hot::commons::getLeastSignificantBitIndexInByte(0b00000001 << (7-i)), i);
		BOOST_REQUIRE_EQUAL(hot::commons::getLeastSignificantBitIndexInByte(0b11111111 << (7-i)), i);
		BOOST_REQUIRE_EQUAL(hot::commons::getLeastSignificantBitIndexInByte(0b11111101 << (7-i)), i);
	}
}

BOOST_AUTO_TEST_CASE(testGetMostSignificantBitIndexInByte) {
	for(int i=0; i < 8; ++i) {
		BOOST_REQUIRE_EQUAL(hot::commons::getMostSignificantBitIndexInByte(0b10000000 >> i), i);
		BOOST_REQUIRE_EQUAL(hot::commons::getMostSignificantBitIndexInByte(0b11111111 >> i), i);
		BOOST_REQUIRE_EQUAL(hot::commons::getMostSignificantBitIndexInByte(0b10111111 >> i), i);
	}
}

BOOST_AUTO_TEST_SUITE_END()

} }


// Copyright 2015-2018 Hans Dembinski
//
// 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)

// clang-format off

//[ getting_started_listing_01

#include <algorithm>           // std::for_each
#include <boost/format.hpp>    // only needed for printing
#include <boost/histogram.hpp> // make_histogram, regular, weight, indexed
#include <cassert>             // assert (used to test this example for correctness)
#include <functional>          // std::ref
#include <iostream>            // std::cout, std::flush
#include <sstream>             // std::ostringstream

int main() {
  using namespace boost::histogram; // strip the boost::histogram prefix

  /*
    Create a 1d-histogram with a regular axis that has 6 equidistant bins on
    the real line from -1.0 to 2.0, and label it as "x". A family of overloaded
    factory functions called `make_histogram` makes creating histograms easy.

    A regular axis is a sequence of semi-open bins. Extra under- and overflow
    bins extend the axis by default (this can be turned off).

    index    :      -1  |  0  |  1  |  2  |  3  |  4  |  5  |  6
    bin edges:  -inf  -1.0  -0.5   0.0   0.5   1.0   1.5   2.0   inf
  */
  auto h = make_histogram(axis::regular<>(6, -1.0, 2.0, "x"));

  /*
    Let's fill a histogram with data, typically this happens in a loop.

    STL algorithms are supported. std::for_each is very convenient to fill a
    histogram from an iterator range. Use std::ref in the call, if you don't
    want std::for_each to make a copy of your histogram.
  */
  auto data = {-0.5, 1.1, 0.3, 1.7};
  std::for_each(data.begin(), data.end(), std::ref(h));
  // let's fill some more values manually
  h(-1.5); // is placed in underflow bin -1
  h(-1.0); // is placed in bin 0, bin interval is semi-open
  h(2.0);  // is placed in overflow bin 6, bin interval is semi-open
  h(20.0); // is placed in overflow bin 6

  /*
    This does a weighted fill using the `weight` function as an additional
    argument. It may appear at the beginning or end of the argument list. C++
    doesn't have keyword arguments like Python, this is the next-best thing.
  */
  h(0.1, weight(1.0));

  /*
    Iterate over bins with the `indexed` range generator, which provides a
    special accessor object, that can be used to obtain the current bin index,
    and the current bin value by dereferncing (it acts like a pointer to the
    value). Using `indexed` is convenient and gives you better performance than
    looping over the histogram cells with hand-written for loops. By default,
    under- and overflow bins are skipped. Passing `coverage::all` as the
    optional second argument iterates over all bins.

    - Access the value with the dereference operator.
    - Access the current index with `index(d)` method of the accessor.
    - Access the corresponding bin interval view with `bin(d)`.

    The return type of `bin(d)` depends on the axis type (see the axis reference
    for details). It usually is a class that represents a semi-open interval.
    Edges can be accessed with methods `lower()` and `upper()`.
  */

  std::ostringstream os;
  for (auto&& x : indexed(h, coverage::all)) {
    os << boost::format("bin %2i [%4.1f, %4.1f): %i\n")
          % x.index() % x.bin().lower() % x.bin().upper() % *x;
  }

  std::cout << os.str() << std::flush;

  assert(os.str() == "bin -1 [-inf, -1.0): 1\n"
                     "bin  0 [-1.0, -0.5): 1\n"
                     "bin  1 [-0.5, -0.0): 1\n"
                     "bin  2 [-0.0,  0.5): 2\n"
                     "bin  3 [ 0.5,  1.0): 0\n"
                     "bin  4 [ 1.0,  1.5): 1\n"
                     "bin  5 [ 1.5,  2.0): 1\n"
                     "bin  6 [ 2.0,  inf): 2\n");
}

//]