/**
 *  This test ensures that our internal distributed sparse matrix behave as
 *  their Elemental counter parts.
 *  This test builds on the following assumptions:
 *
 *      - Elemental distributions and Gemms are implemented correctly.
 */

#include <string>
#include <vector>

#include <boost/mpi.hpp>
#include <boost/test/minimal.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>

#define SKYLARK_NO_ANY

#include "../../skylark.hpp"

#include "test_utils.hpp"

typedef El::DistMatrix<double, El::VC, El::STAR> dense_vc_star_matrix_t;
typedef skylark::base::sparse_vc_star_matrix_t<double> sparse_vc_star_matrix_t;

typedef El::DistMatrix<double, El::STAR, El::VR> dense_star_vr_matrix_t;
typedef skylark::base::sparse_star_vr_matrix_t<double> sparse_star_vr_matrix_t;

boost::random::mt19937 gen;

template <typename dense_matrix_t, typename sparse_matrix_t>
void create_random_sparse_matrix_pair(
        sparse_matrix_t& sparse, dense_matrix_t& dense,
        bool symmetrize = false) {

    boost::random::uniform_int_distribution<> value_dist(1, 500);
    boost::random::uniform_int_distribution<> nnz_dist(0, 9);

    El::Zero(dense);

    size_t rows = dense.Height();

    for (int col = 0; col < dense.Width(); col++) {
        if (symmetrize) rows = col;
        for (int row = 0; row < rows; row++) {
            if (nnz_dist(gen) != 0)
                continue;

            double val = static_cast<double>(value_dist(gen));
            dense.Update(row, col, val);
            sparse.queue_update(row, col, val);

            if (symmetrize) {
                dense.Update(col, row, val);
                sparse.queue_update(col, row, val);
            }
        }
    }

    sparse.finalize();
}

template <typename dense_matrix_t, typename sparse_matrix_t>
void test_matrix_properties(
        const dense_matrix_t& A, const sparse_matrix_t& A_sparse) {
    // FIXME: test more

    BOOST_REQUIRE(A_sparse.width()    == A.Width());
    BOOST_REQUIRE(A_sparse.height()   == A.Height());
    BOOST_REQUIRE(A_sparse.nonzeros() <= A.Width() * A.Height());

    BOOST_REQUIRE(A_sparse.local_width()  == A.LocalWidth());
    BOOST_REQUIRE(A_sparse.local_height() == A.LocalHeight());
}

template <typename dense_matrix_t, typename sparse_matrix_t>
void check_equal(
        const dense_matrix_t& A, const sparse_matrix_t& A_sparse) {
    const int* indptr    = A_sparse.indptr();
    const int* indices   = A_sparse.indices();
    const double* values = A_sparse.locked_values();

    double threshold = 1e-7;
    for (int col = 0; col < A_sparse.local_width(); col++) {
        for (int idx = indptr[col]; idx < indptr[col + 1]; idx++) {
            int row = indices[idx];
            if (fabs(A.GetLocal(row, col) - values[idx]) > threshold) {
                std::cout << "(" << row << ", " << col << " ) diff = "
                          << A.GetLocal(row, col) - values[idx] << std::endl;
            }
            BOOST_REQUIRE(fabs(A.GetLocal(row, col) - values[idx]) < threshold);
        }
    }
}

// FIXME: merge helper functions
template <typename sparse_matrix_t>
void test_gemm(El::Orientation oA, El::Orientation oB, double alpha,
        const sparse_matrix_t& A_sparse, const El::DistMatrix<double, El::VC, El::STAR>& A,
        double beta, El::Int target_width, boost::mpi::communicator world,
        const El::Grid& grid) {
    // FIXME: cannot handle this case yet
    // assert(std::fabs(beta) < 1e-5);
    beta = 0.0;

    El::Int target_height = A_sparse.height();
    El::Int B_height = A.Width();
    if (oA == El::TRANSPOSE) {
        target_height = A_sparse.width();
        B_height = A.Height();
    }

    El::DistMatrix<double, El::STAR, El::STAR> B(grid);
    El::Uniform(B, B_height, target_width);
    if (oB == El::TRANSPOSE) target_width = B_height;

    sparse_vc_star_matrix_t A_sparse_vc_result(
        target_height, target_width, grid);
    skylark::base::Gemm(El::NORMAL, El::NORMAL, alpha,
        A_sparse, B, beta, A_sparse_vc_result);

    BOOST_REQUIRE(A_sparse_vc_result.is_finalized());

    El::DistMatrix<double> A_mcmr = A;
    El::DistMatrix<double> B_mcmr = B;
    El::DistMatrix<double> C(grid);
    El::Uniform(C, target_height, target_width);
    El::Gemm(oA, oB, alpha, A, B, beta, C);

    dense_vc_star_matrix_t A_vc_result = C;

    BOOST_REQUIRE(A_vc_result.LocalWidth() == A_sparse_vc_result.local_width());
    BOOST_REQUIRE(A_vc_result.LocalHeight() == A_sparse_vc_result.local_height());

    check_equal(A_vc_result, A_sparse_vc_result);
}

// FIXME: merge helper functions
template <typename sparse_matrix_t>
void test_gemm_vc(El::Orientation oA, El::Orientation oB, double alpha,
        const sparse_matrix_t& A_sparse, const El::DistMatrix<double, El::STAR, El::VR>& A,
        double beta, El::Int target_width, boost::mpi::communicator world,
        const El::Grid& grid) {

    El::Int target_height = A_sparse.height();
    El::Int B_height = A.Width();
    if (oA == El::TRANSPOSE) {
        target_height = A_sparse.width();
        B_height = A.Height();
    }

    El::DistMatrix<double, El::STAR, El::STAR> B(grid);
    El::Uniform(B, B_height, target_width);
    if (oB == El::TRANSPOSE) target_width = B_height;

    El::DistMatrix<double, El::VC, El::STAR> A_sparse_vc_result(grid);
    El::Uniform(A_sparse_vc_result, target_height, target_width);
    El::Zero(A_sparse_vc_result);
    skylark::base::Gemm(El::NORMAL, El::NORMAL, alpha,
        A_sparse, B, beta, A_sparse_vc_result);

    El::DistMatrix<double> A_mcmr = A;
    El::DistMatrix<double> B_mcmr = B;
    El::DistMatrix<double> C(grid);
    El::Uniform(C, target_height, target_width);
    // FIXME: should be _mcmr
    El::Gemm(oA, oB, alpha, A, B, beta, C);

    El::DistMatrix<double, El::VC, El::STAR> A_vc_result = C;

    BOOST_REQUIRE(A_vc_result.LocalWidth() == A_sparse_vc_result.LocalWidth());
    BOOST_REQUIRE(A_vc_result.LocalHeight() == A_sparse_vc_result.LocalHeight());

    El::Matrix<double> A_vc_result_gathered = C.Matrix();
    El::Matrix<double> A_sparse_vc_result_gathered = A_sparse_vc_result.Matrix();

    if (!test::util::equal(A_vc_result_gathered, A_sparse_vc_result_gathered))
        BOOST_FAIL("Gemm VC/STAR application not equal");
}

//FIXME: merge helper functions
template <typename sparse_matrix_t>
void test_gemm_vr(El::Orientation oA, El::Orientation oB, double alpha,
        const sparse_matrix_t& A_sparse, const El::DistMatrix<double, El::STAR, El::VR>& A,
        double beta, El::Int target_width, boost::mpi::communicator world) {

    El::Int target_height = A_sparse.height();
    El::Int B_height = A.Width();
    if (oA == El::TRANSPOSE) {
        target_height = A_sparse.width();
        B_height = A.Height();
    }

    El::DistMatrix<double, El::VC, El::STAR> B(A.Grid());
    El::Uniform(B, B_height, target_width);
    if (oB == El::TRANSPOSE) target_width = B_height;

    El::DistMatrix<double, El::STAR, El::STAR> A_sparse_vr_result(A.Grid());
    El::Uniform(A_sparse_vr_result, target_height, target_width);
    El::Zero(A_sparse_vr_result);
    skylark::base::Gemm(El::NORMAL, El::NORMAL, alpha,
        A_sparse, B, beta, A_sparse_vr_result);

    El::DistMatrix<double> A_mcmr = A;
    El::DistMatrix<double> B_mcmr = B;
    El::DistMatrix<double> C(A.Grid());
    El::Uniform(C, target_height, target_width);
    // FIXME: should be _mcmr
    El::Gemm(oA, oB, alpha, A, B, beta, C);

    El::DistMatrix<double, El::STAR, El::STAR> A_vr_result = C;

    BOOST_REQUIRE(A_vr_result.LocalWidth() == A_sparse_vr_result.LocalWidth());
    BOOST_REQUIRE(A_vr_result.LocalHeight() == A_sparse_vr_result.LocalHeight());

    if (!test::util::equal(A_vr_result, A_vr_result))
        BOOST_FAIL("Gemm STAR/VR application not equal");
}

/**
 *  Test symmetric matrix multiply for sparse distributed matrices.
 *  Multiply with a random (uniform) dense matrix.
 *
 *  FIXME: merge helper functions into test utils
 *  FIXME: target_width is a bad name
 */
template <typename sparse_matrix_t>
void test_symm(El::LeftOrRight side, El::UpperOrLower uplo, double alpha,
        const sparse_matrix_t& A_sparse,
        const El::DistMatrix<double, El::STAR, El::VC>& A,
        double beta, El::Int target_width, boost::mpi::communicator world) {

    // determine dimensions of random multiplier
    El::Int target_height = A.Height();
    El::Int B_height = A.Height();
    if (side == El::RIGHT) {
        target_height = target_width;
        B_height = target_width;
        target_width = A.Width();
    }

    // create random multiplier
    El::DistMatrix<double, El::VC, El::STAR> B(A.Grid());
    El::Uniform(B, B_height, target_width);

    // perform sparse Symm
    El::DistMatrix<double, El::VC, El::STAR> A_sparse_vc_result(A.Grid());
    El::Uniform(A_sparse_vc_result, target_height, target_width);
    El::Zero(A_sparse_vc_result);
    skylark::base::Symm(side, uplo, alpha, A_sparse, B, beta, A_sparse_vc_result);

    // controll with expected value from Elemental Symm (MC/MR)
    El::DistMatrix<double> A_mcmr = A;
    El::DistMatrix<double> B_mcmr = B;
    El::DistMatrix<double> C_mcmr(A.Grid());
    El::Uniform(C_mcmr, target_height, target_width);
    El::Symm(side, uplo, alpha, A, B, beta, C_mcmr);

    // and convert matrix back to VC/STAR
    El::DistMatrix<double, El::VC, El::STAR> A_vc_result = C_mcmr;

    // finally we can check for equality (dimensions and values)
    BOOST_REQUIRE(A_vc_result.LocalWidth()  == A_sparse_vc_result.LocalWidth());
    BOOST_REQUIRE(A_vc_result.LocalHeight() == A_sparse_vc_result.LocalHeight());

    El::Matrix<double> A_vc_result_gathered = A_vc_result.Matrix();
    El::Matrix<double> A_sparse_vc_result_gathered = A_sparse_vc_result.Matrix();

    if (!test::util::equal(A_vc_result_gathered, A_sparse_vc_result_gathered))
        BOOST_FAIL("Symm VC/STAR application not equal");

    world.barrier();
}

int test_main(int argc, char *argv[]) {
    El::Initialize(argc, argv);
    boost::mpi::environment env(argc, argv);
    boost::mpi::communicator world;
    MPI_Comm mpi_world(world);
    El::Grid grid(mpi_world);

    //////////////////////////////////////////////////////////////////////////
    //[> Setup <]

    const unsigned int num_repetitions = 10;

    gen.seed(static_cast<unsigned int>(std::time(0)));
    boost::random::uniform_int_distribution<> dim_dist(250, 1000);
    boost::random::uniform_int_distribution<> target_dist(15, 200);

    //////////////////////////////////////////////////////////////////////////
    //[> Test properties <]

    {
    const int height = dim_dist(gen);
    const int width  = dim_dist(gen);

    dense_vc_star_matrix_t A_vc(grid);
    El::Uniform(A_vc, height, width);
    sparse_vc_star_matrix_t A_sparse_vc(height, width, grid);
    create_random_sparse_matrix_pair(A_sparse_vc, A_vc);

    test_matrix_properties(A_vc, A_sparse_vc);
    check_equal(A_vc, A_sparse_vc);

    dense_star_vr_matrix_t A_vr(grid);
    El::Uniform(A_vr, height, width);
    sparse_star_vr_matrix_t A_sparse_vr(height, width, grid);
    create_random_sparse_matrix_pair(A_sparse_vr, A_vr);

    test_matrix_properties(A_vr, A_sparse_vr);
    check_equal(A_vr, A_sparse_vr);
    }

    //////////////////////////////////////////////////////////////////////////
    //[> Test Symm <]
    //
    // FIXME: currently only for sparse_vc_star_matrix_t

    {
    for (size_t rep = 0; rep < num_repetitions; rep++) {
        const int symm_dim    = dim_dist(gen);
        const int symm_target = target_dist(gen);

        // create a symmetric test input matrix
        dense_vc_star_matrix_t A_vc_symm(grid);
        El::Uniform(A_vc_symm, symm_dim, symm_dim);
        sparse_vc_star_matrix_t A_sparse_vc_symm(symm_dim, symm_dim, grid);
        create_random_sparse_matrix_pair(A_sparse_vc_symm, A_vc_symm, true);

        if (world.rank() == 0) std::cout << "Testing SYMMs: ("
            << symm_dim << " x " << symm_dim << ") -> "
            << symm_target << std::endl;

        if (world.rank() == 0)
            std::cout << "\tsparse_vc_star -> vc_star (LEFT, UPPER):";
        test_symm(El::LEFT, El::UPPER, 1.0,
                A_sparse_vc_symm, A_vc_symm, 0.0, symm_target, world);
        world.barrier();
        if (world.rank() == 0) std::cout << " ok" << std::endl;

        if (world.rank() == 0)
            std::cout << "\tsparse_vc_star -> vc_star (RIGHT, UPPER):";
        test_symm(El::RIGHT, El::UPPER, 1.0,
                A_sparse_vc_symm, A_vc_symm, 0.0, symm_target, world);
        world.barrier();
        if (world.rank() == 0) std::cout << " ok" << std::endl;

        world.barrier();
    }

    if (world.rank() == 0) std::cout << "Done." << std::endl;
    }

    //////////////////////////////////////////////////////////////////////////
    //[> Test Gemm <]

    {
    for (size_t rep = 0; rep < num_repetitions; rep++) {
        const int gemm_height = dim_dist(gen);
        const int gemm_width  = dim_dist(gen);
        const int gemm_target = target_dist(gen);

        dense_vc_star_matrix_t A_vc(grid);
        El::Uniform(A_vc, gemm_height, gemm_width);
        sparse_vc_star_matrix_t A_sparse_vc(gemm_height, gemm_width, grid);
        create_random_sparse_matrix_pair(A_sparse_vc, A_vc);

        if (world.rank() == 0) std::cout << "Testing GEMMs: ("
            << A_vc.Height() << ", " << A_vc.Width() << ") -> "
            << gemm_target << std::endl;

        if (world.rank() == 0)
            std::cout << "\tsparse(VC/STAR) * dense(STAR/STAR) "
                      << "-> sparse(VC/STAR) (NORMAL, NORMAL):";
        test_gemm(El::NORMAL, El::NORMAL, 1.0,
                A_sparse_vc, A_vc, 0.0, gemm_target, world, grid);
        world.barrier();
        if (world.rank() == 0) std::cout << " ok" << std::endl;

#if 0
        if (world.rank() == 0) std::cout
            << "\tsparse(VC/STAR) x dense(STAR/STAR) "
            << "-> dense(VC/STAR) (NORMAL, NORMAL):";
        test_gemm_vc(El::NORMAL, El::NORMAL, 1.0,
                A_sparse_vc, A_vc, 0.0, target, world, grid);
        world.barrier();
        if (world.rank() == 0) std::cout << " ok" << std::endl;

        if (world.rank() == 0)
            std::cout << "\tsparse(STAR/VR) x dense(VC/STAR) "
                      << "-> dense(STAR/STAR) (NORMAL, NORMAL):";
        test_gemm_vr(El::NORMAL, El::NORMAL, 1.0,
                A_sparse_vr, A_vr, 0.0, target, world);
        world.barrier();
        if (world.rank() == 0) std::cout << " ok" << std::endl;
#endif

        world.barrier();
    }

    if (world.rank() == 0) std::cout << "Done." << std::endl;
    }

    //////////////////////////////////////////////////////////////////////////
    //[> Test I/O <]

    // only run test if we got a file name
    if (argc < 2) {
        El::Finalize();
        return 0;
    }

    sparse_vc_star_matrix_t X(0, 0, grid);
    El::DistMatrix<double, El::VC, El::STAR> Y;

    std::string fname(argv[1]);
    try {
        skylark::utility::io::ReadLIBSVM(fname, X, Y, skylark::base::COLUMNS);
    } catch (skylark::base::skylark_exception ex) {
        SKYLARK_PRINT_EXCEPTION_DETAILS(ex);
        SKYLARK_PRINT_EXCEPTION_TRACE(ex);
        errno = *(boost::get_error_info<skylark::base::error_code>(ex));
        std::cout << "Caught exception, exiting with error " << errno << ": ";
        std::cout << skylark_strerror(errno) << std::endl;
        BOOST_FAIL("Exception when reading libSVM file.");
    }

    El::DistMatrix<double, El::VC, El::STAR> X_ref;
    El::DistMatrix<double, El::VC, El::STAR> Y_ref;
    try {
        skylark::utility::io::ReadLIBSVM(
            fname, X_ref, Y_ref, skylark::base::COLUMNS);
    } catch (skylark::base::skylark_exception ex) {
        SKYLARK_PRINT_EXCEPTION_DETAILS(ex);
        SKYLARK_PRINT_EXCEPTION_TRACE(ex);
        errno = *(boost::get_error_info<skylark::base::error_code>(ex));
        std::cout << "Caught exception, exiting with error " << errno << ": ";
        std::cout << skylark_strerror(errno) << std::endl;
        BOOST_FAIL("Exception when reading libSVM file.");
    }

    check_equal(X_ref, X);

    El::Finalize();
    return 0;
}


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// 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)

/**
\file

\brief conversion_factor.cpp

\details An example of using conversion_factor.

Output:
@verbatim

//[conversion_factor_output
1e-005
100
1e-005
100
0.01
//]

@endverbatim
**/

#include <iostream>

#include <boost/units/cmath.hpp>
#include <boost/units/io.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/cgs/acceleration.hpp>
#include <boost/units/systems/si/acceleration.hpp>
#include <boost/units/systems/si/force.hpp>
#include <boost/units/systems/cgs/force.hpp>
#include <boost/units/systems/si/mass.hpp>
#include <boost/units/systems/cgs/mass.hpp>
#include <boost/units/systems/si/momentum.hpp>
#include <boost/units/systems/cgs/momentum.hpp>

int main()
{
    using namespace boost;
    using namespace boost::units;

    //[conversion_factor_snippet_1

    double dyne_to_newton =
        conversion_factor(cgs::dyne,si::newton);
    std::cout << dyne_to_newton << std::endl;

    double force_over_mass_conversion =
        conversion_factor(si::newton/si::kilogram,cgs::dyne/cgs::gram);
    std::cout << force_over_mass_conversion << std::endl;

    double momentum_conversion =
        conversion_factor(cgs::momentum(),si::momentum());
    std::cout << momentum_conversion << std::endl;

    double momentum_over_mass_conversion =
        conversion_factor(si::momentum()/si::mass(),cgs::momentum()/cgs::gram);
    std::cout << momentum_over_mass_conversion << std::endl;

    double acceleration_conversion =
        conversion_factor(cgs::gal,si::meter_per_second_squared);
    std::cout << acceleration_conversion << std::endl;

    //]

    return 0;
}


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

  @copyright 2016 NumScale SAS
  @copyright 2016 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_FUNCTION_INTERLEAVE_SECOND_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_INTERLEAVE_SECOND_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-swar
    Function object implementing interleave_second capabilities

    Computes a vector from a combination of the two inputs.

    @par Semantic:

    For every parameters of type T0:

    @code
    T0 r = interleave_second(a,b);
    @endcode

    is equivalent to :

    @code
    r = [ a[n/2-1] b[n/2-1] a[n/2] b[n/2] ... a[n-1] b[n-1] ]
    @endcode

    with <tt> n = cardinal_of<T>::value </tt>

  **/
  const boost::dispatch::functor<tag::interleave_second_> interleave_second = {};
} }
#endif

#include <boost/simd/function/scalar/interleave_second.hpp>
#include <boost/simd/function/simd/interleave_second.hpp>

#endif


/*
The main evaluation system for SDS persistence.
Moritz Laass, Martin Werner

*/
#define OVERWRITE_PROGRESS 1
// Default Headers for C++
#include <vector>
#include <cmath>
#include <set>
#include <numeric>
#include <limits>
#include <functional>
#include <sstream>
#include <chrono>

#include <fstream>
#include <iostream>
//#include <boost/range/irange.hpp>

// Boost Geometry
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>

namespace bg = boost::geometry;
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::linestring<point> linestring;
const auto as_str = [](const linestring &ls) {std::stringstream ss; ss << bg::wkt(ls); return ss.str(); };

#include "trajcomp-persistence.hpp"
#include <frechet/frechetrange.hpp>
#include "frechet_boostgeom.hpp"

// Diverse Trajectory Algorithm Implementations
#include <trajcomp/trajcomp.hpp>

// Configuration and aggregate result communication based on JSON
#include <jsonconf.hpp>

// Full result collection as CSV using emit lambda
typedef std::function<void(std::vector<std::string>)> emit_function_type;

/*
Section 1: Simplification Functions

This section implements relevant simplification functions for trajectories

- douglas_peucker(ls,eps):
       Douglas-Peucker with distance threshold eps
- persistence_beta(ls, beta):
       Persistence with Beta-Pruning, beta is the length of the bars to prune
- persistence_mrs(ls, beta, eps, rounds)
       Persistence simplificaiton with beta-pruning followed by <rounds> MRS rounds using
       a point distance threshold of eps. This threshold grows exponentially with rounds.
- persistence_sds(ls, beta, eps, rounds)
       Persistence simplificaiton with beta-pruning followed by <rounds> SDS rounds using
       a segment distance threshold of eps. This threshold grows exponentially with rounds.

*/

linestring douglas_peucker(const linestring &ls, double eps)
{
	auto traj = extract(ls);
	auto simplified = trajcomp::douglas_peucker(traj, eps);
	linestring ret;
	for (const auto &c : simplified)
		ret.push_back(point(c[0], c[1]));
	return ret;
}

linestring persistence_beta(const linestring &ls, double beta)
{
	return extract_linestring(persistence::persistence(persistence::traj_to_curve(extract(ls)), beta));
}

linestring persistence_mrs(const linestring &ls, double beta, double eps, int levels)
{
	auto traj = extract(ls);
	auto curve = persistence::traj_to_curve(traj);
	for (size_t i = 0; i < levels; i++)
	{
		auto p_result = persistence::persistence(curve, beta, -1);
		curve = prune_curve_dist(p_result.pruned, eps * pow(2, i));
	}
	linestring rls;
	for (auto c : curve)
		rls.push_back(ls[c.index]);
	return rls;
}

linestring persistence_sds(const linestring &ls, double beta, double eps, int levels)
{
	auto traj = extract(ls);
	auto curve = persistence::traj_to_curve(traj);
	for (size_t i = 0; i < levels; i++)
	{
		auto p_result = persistence::persistence(curve, beta, -1);
		//curve = prune_curve_dist(, pow(2,i));
		curve = prune_sds(p_result.pruned, eps);
		// use curve.index
		eps *= 2; // exponential growth as well - otherwise unfair
	}
	linestring rls;
	for (auto c : curve)
		rls.push_back(ls[c.index]);
	return rls;
}

JSONConfig cfg;

/*
Section 2: Algorithms for processing WKT files.

Algorithm 2.1: Scan a WKT file

scan (input, output, func):
   scan over input line by line, parse as WKT ignoring errors and call func for
   each line. Func signature is
       void func(ls, emit, first),
   where ls is the linestring, emit is a lambda writing vectors of strings to output and
   first is set in case of the first line is to be written (e.g., CSV headers)

*/

template <typename callback>
void scan(std::string input, std::string output, callback func)
{
	std::ifstream ifs(input);
	std::ofstream ofs(output);
	std::string line;
	if (ifs.fail())
		std::cout << "failed to find: " << input << std::endl;

	if (ofs.fail())
		std::cout << "failed to find: " << input << std::endl;

	ofs << std::setprecision(10);
	auto emit = [&ofs](const std::vector<std::string> &columns) {
		std::stringstream ss;
		std::copy(columns.begin(), columns.end(), std::ostream_iterator<std::string>(ss, ";"));
		std::string line = ss.str();
		line.pop_back();
		ofs << line << std::endl;
	};
	bool first = true;
	while (std::getline(ifs, line))
	{
		linestring ls;
		try
		{
			bg::read_wkt(line, ls);
		}
		catch (...)
		{
			//std::cout << "Not a geom: " << line << std::endl;
			continue;
		}
		// tune DP
		func(ls, emit, first);
		first = false;
	}
}

/*
Algorithm 2.2: Calibrate a simplification algorithm

calibrate (filename, N, s, interval, [tag])
   Determines good parameters for a single-parameter simplifier s reaching an average
   trajectory size of N on data taken from filename. The single parameter is initialized from
   an <interval>. As all our parameters are monotonous, we apply exponential growth to
   the second bound followed by nested interval search. Progress messages are decorated
   with the given tag to know which algorithm is running and how far the interval converged

   Data is written to "garbage.tmp" as scan does not yet allow to scan without output.

   Reacts to #define OVERWRITE_PROGRESS by either using carriage return and flush for
   interactive use  or simple newlines for log-like files.
*/

template <typename simplifier>
std::pair<double, double> calibrate(std::string filename, size_t N, simplifier s, std::pair<double, double> interval, std::string tag = "Current")
{

	int points = 0;
	int lines = 0;
	double eps = 1, avg_points = -1;
	// first, exponentiall grow the second interval
	std::cout << "Calibration started for " << tag << std::endl;
	while (true)
	{
		std::vector<size_t> npts{};

		double m = interval.second;
		scan(filename, "garbage.tmp",
				 [&npts, m, &s](linestring ls, emit_function_type emit, bool first) {
					 npts.push_back(s(ls, m).size());
				 });
		assert(npts.size() != 0);
		avg_points = static_cast<double>(std::accumulate(npts.begin(), npts.end(), 0)) / npts.size();
		//	     std::cout << "Calibrate to " << avg_points << std::endl;
		if (avg_points > N)
		{
			interval.second *= 2;
		}
		else
		{
			break;
		}
		std::cout << std::setprecision(5) << tag << ":" << avg_points << "Grow it between [" << interval.first << ";" << interval.second << "]";
	}
	double m;
	const double convergence_threshold = 1e-10;
	while (fabs(interval.second - interval.first) > convergence_threshold)
	{
		std::vector<size_t> npts;
		npts.clear();
		m = (interval.first + interval.second) / 2;
		scan(filename, "garbage.tmp",
				 [&npts, m, &s](linestring ls, emit_function_type emit, bool first) {
					 npts.push_back(s(ls, m).size());
				 });
		assert(npts.size() != 0);
		avg_points = static_cast<double>(std::accumulate(npts.begin(), npts.end(), 0)) / npts.size();
		//	     std::cout << "Calibrate to " << avg_points << std::endl;
		if (avg_points < N)
		{
			interval.second = m;
		}
		else
		{
			interval.first = m;
		}
#if OVERWRITE_PROGRESS
		std::cout << "\33[2K\r";
#endif
		std::cout << std::setprecision(5) << tag << ":\t" << avg_points << "\t Search Interval: [" << interval.first << ";" << interval.second << "]";

#if OVERWRITE_PROGRESS
		std::cout.flush();
#else
		std::cout << std::endl;
#endif
	}
#if OVERWRITE_PROGRESS
	std::cout << std::endl;
#endif

	return std::make_pair(m, avg_points);
}

/*
Algorithm 2.3: Run Calibration

runcalibration ()
   Reads paramters from JSON and tunes and evaluates all relevant algorithms writing
   aggregate results to a log file and to a JSON output. Therefore, parameters are
   estimated for all algorithms using parameters supplied from JSON. Then all data
   is transformed with found parameters and statistics on the Fr�chet distance as well as
   on the number of points are collected.
*/

void runcalibration()
{
	std::string filename = cfg.get<std::string>("input");
	int N = cfg.get<int>("N");
	std::cout << std::setprecision(32);

	// Calibrate Douglas-Peucker [dp.first ~ epsilon, dp.second ~ avg. number of points]
	auto dp = calibrate(
			filename, N, [](linestring in, double eps) {
				return douglas_peucker(in, eps);
			},
			{-1, 1}, "Douglas Peucker");
	// Calibrate Persistence with Beta Pruning
	auto beta = calibrate(
			filename, N,
			[](linestring in, double eps) {
				return persistence_beta(in, eps);
			},
			{0, 10000}, "Persistence Beta");
	// Calibrate MRS in two steps (beta for kappa*N, eps for N using this beta)
	double kappa = cfg.get<double>("kappa");
	auto mrs_beta = calibrate(
			filename, static_cast<int>(kappa * N),
			[](linestring in, double beta) {
				return persistence_beta(in, beta);
			},
			{0, 10000}, "MRS Kappa's Beta");

	auto mrs_eps = calibrate(
			filename, N,
			[&mrs_beta](linestring in, double eps) {
				return persistence_mrs(in, mrs_beta.first, eps, cfg.get<int>("mrs_rounds"));
			},
			{0, 1}, "Kappas MRS Epsilon");

	// Calibrate SDS in two steps (beta for kappa*N, eps for N using this beta)

	auto sds_beta = mrs_beta; // re-use result
	auto sds_eps = calibrate(
			filename, N,
			[&sds_beta](linestring in, double eps) {
				return persistence_sds(in, sds_beta.first, eps, cfg.get<int>("sds_rounds"));
			},
			{0, 1}, "Kappas Epsilon");

	// Phase 3:  Transform with complete statistics gathering and result writing
	std::map<std::string, std::vector<double>> errors;
	std::map<std::string, std::vector<double>> lengths;
	scan(filename, cfg.get<std::string>("output"),
			 [&](linestring ls, emit_function_type emit, bool first) {
				 if (first)
					 emit({"ls_orig", "ls_pers", "ls_dp", "ls_sds", "ls_mrs", "e_pers", "e_dp", "e_sds", "e_mrs",
								 "n_orig", "n_pers", "n_dp", "n_sds", "n_mrs"});
				 auto ls_pers = persistence_beta(ls, beta.first);
				 auto ls_dp = douglas_peucker(ls, dp.first);
				 auto ls_sds = persistence_sds(ls, sds_beta.first, sds_eps.first, cfg.get<int>("sds_rounds"));
				 auto ls_mrs = persistence_mrs(ls, mrs_beta.first, mrs_eps.first, cfg.get<int>("mrs_rounds"));
				 // errors

				 errors["pers"].push_back(frechet_distance(ls, ls_pers));
				 errors["dp"].push_back(frechet_distance(ls, ls_dp));
				 errors["sds"].push_back(frechet_distance(ls, ls_sds));
				 errors["mrs"].push_back(frechet_distance(ls, ls_mrs));

				 lengths["pers"].push_back(ls_pers.size());
				 lengths["dp"].push_back(ls_dp.size());
				 lengths["sds"].push_back(ls_sds.size());
				 lengths["mrs"].push_back(ls_mrs.size());

				 emit({
						 as_str(ls),
						 as_str(ls_pers),
						 as_str(ls_dp),
						 as_str(ls_sds),
						 as_str(ls_mrs),

						 std::to_string(errors["pers"].back()),
						 std::to_string(errors["dp"].back()),
						 std::to_string(errors["sds"].back()),
						 std::to_string(errors["mrs"].back()),
						 std::to_string(ls.size()),
						 std::to_string(ls_pers.size()),
						 std::to_string(ls_dp.size()),
						 std::to_string(ls_sds.size()),
						 std::to_string(ls_mrs.size()),

				 });
			 });

	// Compute average error statistics
	for (const auto &e : errors)
	{
		const std::string &key = e.first;
		const std::vector<double> &val = e.second;
		const std::vector<double> &_n = lengths[key];
		auto mean = std::accumulate(val.begin(), val.end(), 0.0) / val.size();
		auto meanLen = std::accumulate(_n.begin(), _n.end(), 0.0) / _n.size();
		std::cout << key << "\t:" << mean << "\t (" << meanLen << ")" << std::endl;
		cfg.set("e_" + key, mean);
		cfg.set("n_" + key, meanLen);
	}
}

std::string toString(std::vector<double> vec)
{
	std::stringstream ss;
	ss << "[";
	for (auto i = 0; i < vec.size(); i++)
	{
		if (i != 0)
			ss << ",";
		ss << vec[i];
	}
	ss << "]";
	return ss.str();
}
void profile(std::string filename, double pers_beta, double dp_eps, double sds_beta, double sds_eps, int sds_rounds, double mrs_beta, double mrs_eps, int mrs_rounds)
{
	int multiplier = 1;
	int iterations = 0;
	std::vector<persistence::Trajectory> trajectories;
	// extract all linestrings > 100 < 200
	scan(filename, cfg.get<std::string>("output"),
			 [&](linestring ls, emit_function_type emit, bool first) {
				 auto traj = extract(ls);
				 //printf("%d\n", traj.size());
				 trajectories.push_back(traj);
			 });

	std::sort(trajectories.begin(), trajectories.end(), [](const persistence::Trajectory &a, const persistence::Trajectory &b) { return a.size() < b.size(); });
	printf("size: %d min: %d max: %d\n", (int)trajectories.size(), (int)trajectories[0].size(), (int)trajectories.back().size());

	auto begin = std::chrono::high_resolution_clock::now();
	auto end = std::chrono::high_resolution_clock::now();
	double time;

	cfg.set("pers_time", (time / 1e+9) / (double)multiplier);

	std::vector<double> pers_times;
	for (auto j = 0; j < trajectories.size(); j++)
	{
		begin = std::chrono::high_resolution_clock::now();
		for (auto i = 0; i < multiplier; i++)
			persistence_beta(extract_linestring(trajectories[i]), pers_beta);

		end = std::chrono::high_resolution_clock::now();
		time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
		pers_times.push_back((time / 1e+9) / multiplier);
	}
	cfg.set("pers_times", toString(pers_times));
	printf("persistence_beta profiled!");
	std::vector<double> dp_times;
	for (auto j = 0; j < trajectories.size(); j++)
	{
		begin = std::chrono::high_resolution_clock::now();
		for (auto i = 0; i < multiplier; i++)
			douglas_peucker(extract_linestring(trajectories[i]), dp_eps);

		end = std::chrono::high_resolution_clock::now();
		time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
		dp_times.push_back((time / 1e+9) / multiplier);
	}

	cfg.set("dp_times", toString(dp_times));
	printf("douglas_peucker beta profiled!");

	std::vector<double> sds_times;
	for (auto j = 0; j < trajectories.size(); j++)
	{
		begin = std::chrono::high_resolution_clock::now();
		for (auto i = 0; i < multiplier; i++)
			persistence_sds(extract_linestring(trajectories[i]), sds_beta, sds_eps, sds_rounds);

		end = std::chrono::high_resolution_clock::now();
		time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
		sds_times.push_back((time / 1e+9) / multiplier);
	}

	cfg.set("sds_times", toString(sds_times));
	printf("persistence_sds profiled!");

	std::vector<double> mrs_times;
	for (auto j = 0; j < trajectories.size(); j++)
	{
		begin = std::chrono::high_resolution_clock::now();
		for (auto i = 0; i < multiplier; i++)
			persistence_mrs(extract_linestring(trajectories[i]), mrs_beta, mrs_eps, mrs_rounds);

		end = std::chrono::high_resolution_clock::now();
		time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
		mrs_times.push_back((time / 1e+9) / multiplier);
	}

	cfg.set("mrs_times", toString(mrs_times));
	printf("persistence_mrs profiled!");
}
void runprofile()
{
	std::string filename = cfg.get<std::string>("input");

	double pers_beta = cfg.get<double>("pers_beta");
	double dp_eps = cfg.get<double>("dp_eps");
	double sds_beta = cfg.get<double>("sds_beta");
	double sds_eps = cfg.get<double>("sds_eps");
	int sds_rounds = (int)cfg.get<double>("sds_rounds");
	double mrs_beta = cfg.get<double>("mrs_beta");
	double mrs_eps = cfg.get<double>("mrs_eps");
	int mrs_rounds = (int)cfg.get<double>("mrs_rounds");

	profile(filename, pers_beta, dp_eps, sds_beta, sds_eps, sds_rounds, mrs_beta, mrs_eps, mrs_rounds);
}

int main(int argc, char **argv)
{
	bool profile = false;
	// TODO retrieve profiling run from argc,argv
	if (argc < 3)
		throw(std::runtime_error("Call: " + std::string(argv[0]) + " <input> <output>, where input is a JSON and output as well. They are allowed to be in-place"));
	if (argc > 3)
		profile = std::string(argv[3]).compare("--profile") == 0;
	printf("%s", argv[3]);
	cfg.load(argv[1]);
	std::cout << cfg.dumps() << std::endl;
	if (!profile)
	{
		printf("run calibration: \n");
		runcalibration();
	}
	else
	{
		printf("run profile: \n");
		cfg.set("output", std::string(argv[2]));
		runprofile();
	}

	cfg.save(argv[2]);
	return 0;
}


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2015 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <gudhi/Points_off_io.h>

#include <iostream>
#include <string>
#include <vector>

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

using Point_d = std::vector<double>;

BOOST_AUTO_TEST_CASE( points_doc_test )
{
  // Read the OFF file (input file name given as parameter) and triangulates points
  Gudhi::Points_off_reader<Point_d> off_reader("alphacomplexdoc.off");
  // Check the read operation was correct
  BOOST_CHECK(off_reader.is_valid());

  // Retrieve the triangulation
  std::vector<Point_d> point_cloud = off_reader.get_point_cloud();
  BOOST_CHECK(point_cloud.size() == 7);

  std::vector<Point_d> expected_points;
  std::vector<double> point = {1.0, 1.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {7.0, 0.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {4.0, 6.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {9.0, 6.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {0.0, 14.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {2.0, 19.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));
  point = {9.0, 17.0, 0.0};
  expected_points.push_back(Point_d(point.begin(), point.end()));

  BOOST_CHECK(point_cloud == expected_points);
}

BOOST_AUTO_TEST_CASE( Delaunay_triangulation_unexisting_file_read_test )
{
  Gudhi::Points_off_reader<Point_d> off_reader("some_impossible_weird_file_name.off");
  // Check the read operation was correct
  BOOST_CHECK(!off_reader.is_valid());

  std::vector<Point_d> point_cloud = off_reader.get_point_cloud();
  BOOST_CHECK(point_cloud.size() == 0);
}


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

  Modified Christiansen algorithm for tiling two contours.

  Copyright (c) 2013
  Alexander Rukletsov <rukletsov@gmail.com>
  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.

  THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.

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

#ifndef CHRISTIANSEN_TILING_HPP_D80E68A2_DF31_11E2_BAA7_4057033B4CBB
#define CHRISTIANSEN_TILING_HPP_D80E68A2_DF31_11E2_BAA7_4057033B4CBB

#include <vector>
#include <limits>
#include <stdexcept>
#include <utility>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include "bo/distances/distances_3d.hpp"
#include "bo/surfaces/detail/container_traversers.hpp"
#include "bo/surfaces/detail/indexed_tstrip.hpp"

namespace bo {
namespace surfaces {

namespace detail {

// A struct incorporating contour data. Instances can be safely swapped.
template <typename PointsCont>
struct ContourDescriptor
{
    typedef boost::shared_ptr<PointsCont> PointsContPtr;

    ContourDescriptor(std::size_t id, PointsContPtr points_ptr, bool is_closed)
        : ID(id), PointsPtr(points_ptr), IsClosed(is_closed), Size(points_ptr->size())
    { }

    void swap(ContourDescriptor& other)
    {
        // Clone data before swapping in order not to spoil original contours.
        PointsContPtr new_this(new PointsCont(*PointsPtr));
        PointsContPtr new_other(new PointsCont(*other.PointsPtr));
        PointsPtr = new_other;
        other.PointsPtr = new_this;

        std::swap(ID, other.ID);
        std::swap(IsClosed, other.IsClosed);
        std::swap(Size, other.Size);
    }

    std::size_t ID;
    PointsContPtr PointsPtr;
    bool IsClosed;
    std::size_t Size;
};

} // namespace detail

// A class performing modified Christiansen tiling for two slices. Accepts both closed
// contours and contours with exactly one hole.
template <typename RealType>
class ChristiansenTiling: public boost::noncopyable
{
public:
    typedef ChristiansenTiling<RealType> this_type;

    typedef Vector<RealType, 3> Point3D;
    typedef boost::function<RealType (Point3D, Point3D)> Metric;
    typedef std::vector<Point3D> Contour;
    typedef boost::shared_ptr<Contour> ContourPtr;
    typedef detail::ContourDescriptor<Contour> ContourDescriptor;

    typedef detail::ContainerConstTraverser<Contour> ContourTraverser;
    typedef detail::TraverseRuleFactory<Contour> TraverseFactory;

public:
    ChristiansenTiling(std::size_t id1, ContourPtr contour1, bool closed1,
                  std::size_t id2, ContourPtr contour2, bool closed2)
        : contour_descr1_(id1, contour1, closed1), contour_descr2_(id2, contour2, closed2),
          metric_(&bo::distances::euclidean_distance<RealType, 3>)
    { }

    // Implementation for Christiansen algorithm for closed and opened contours.
    //
    // If a contour is closed, it is traversed cyclical starting and ending in the
    // same vertex.
    //
    // If a contour has exactly one hole (i.e. opened), it is traversed once
    // from the start to the end (from one hole edge to the other).
    detail::IndexedTStrip run()
    {
        // Check contours' length.
        if ((contour_descr1_.Size < 2) && (contour_descr2_.Size < 2))
            throw std::logic_error("Cannot run Christiansen triangulation for contours "
                                   "consisting of less than 2 vertices.");

        // If the first contour is closed but the second is opened, traverse direction
        // for the second contour may be determined incorrectly.
        if (contour_descr1_.IsClosed && !contour_descr2_.IsClosed)
            contour_descr1_.swap(contour_descr2_);

        // Create traversers for contours.
        ContourTraverser current1 = create_traverser1_();
        ContourTraverser candidate1 = current1 + 1;

        ContourTraverser current2 = create_traverser2_(current1);
        ContourTraverser candidate2 = current2 + 1;

        // Initialize output TStrip structure.
        detail::IndexedTStrip tstrip(contour_descr1_.ID, current1.index(),
                             contour_descr2_.ID, current2.index(),
                             contour_descr1_.Size + contour_descr2_.Size);

        // Iterate until both contours are exhausted.
        while (candidate1.is_valid() || candidate2.is_valid())
        {
            // Calculate span norms for candidate vertices.
            RealType span1_norm = span_norm(candidate1, current2);
            RealType span2_norm = span_norm(candidate2, current1);

            // Choose and add candidate vertex and corresponding face.
            if (span1_norm > span2_norm)
            {
                tstrip.add2(candidate2.index());
                current2 = candidate2;
                ++candidate2;
            }
            else
            {
                tstrip.add1(candidate1.index());
                current1 = candidate1;
                ++candidate1;
            }
        }

        return tstrip;
    }

private:
    // Creates an appropriate traverser for the first contour depending whether
    // it is closed or not.
    ContourTraverser create_traverser1_() const
    {
        ContourTraverser retvalue = contour_descr1_.IsClosed ?
            ContourTraverser(TraverseFactory::Create(contour_descr1_.PointsPtr, 0, true)) :
            ContourTraverser(TraverseFactory::Create(contour_descr1_.PointsPtr, true));
        return retvalue;
    }

    // Creates an appropriate traverser for the second contour depending whether
    // the contour is closed and how the first contour is oriented.
    ContourTraverser create_traverser2_(const ContourTraverser& traverser1) const
    {
        ContourTraverser retvalue;

        if (contour_descr2_.IsClosed)
        {
            // Find closest vertex on the second contour and determine direction.
            // No need of using kd-tree here, since the operation is done once.
            std::size_t c2min_idx = 0;
            RealType c2min_dist = std::numeric_limits<RealType>::max();

            for (std::size_t c2_idx = 0; c2_idx < contour_descr2_.Size; ++c2_idx)
            {
                RealType cur_dist = metric_(contour_descr2_.PointsPtr->at(c2_idx), *traverser1);
                if (cur_dist < c2min_dist)
                {
                    c2min_idx = c2_idx;
                    c2min_dist = cur_dist;
                }
            }

            // Create a default directed traverser. This is need to calculate the
            // direction if c2min_idx is the last element in the collection.
            retvalue = ContourTraverser(TraverseFactory::Create(contour_descr2_.PointsPtr,
                                                                c2min_idx, true));

            // Contour2 traverse direction should be swapped in order to correspond
            // with the contour1 direction.
            Point3D direction1 = *(traverser1 + 1) - *traverser1;
            Point3D direction2 = *(retvalue + 1) - *retvalue;
            if (direction1 * direction2 < 0)
                retvalue = ContourTraverser(TraverseFactory::Create(
                        contour_descr2_.PointsPtr, c2min_idx, false));
        }
        else
        {
            // Check if contour2_ traverse direction should be swapped in order to
            // correspond with the contour1_'s direction.
            RealType dist_to_first = metric_(contour_descr2_.PointsPtr->front(), *traverser1);
            RealType dist_to_last = metric_(contour_descr2_.PointsPtr->back(), *traverser1);
            bool is_forward2 = (dist_to_first < dist_to_last) ? true : false;

            retvalue = ContourTraverser(TraverseFactory::Create(contour_descr2_.PointsPtr,
                                                                is_forward2));
        }

        return retvalue;
    }

    // Calculates the euclidean norm of the span between candidate vertex on one
    // contour and current vertex on another. If candidate vertex is invalid (this
    // indicates that the corresponding contour has been exhausted), returns infinity.
    // This guarantees that vertices will be sampled solely from the other contour.
    RealType span_norm(const ContourTraverser& candidate,
                       const ContourTraverser& other_current) const
    {
        RealType norm = candidate.is_valid() ?
                    metric_(*candidate, *other_current):
                    std::numeric_limits<RealType>::max();
        return norm;
    }

private:
    ContourDescriptor contour_descr1_;
    ContourDescriptor contour_descr2_;
    const Metric metric_;
};

} // namespace surfaces
} // namespace bo

#endif // CHRISTIANSEN_TILING_HPP_D80E68A2_DF31_11E2_BAA7_4057033B4CBB


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#ifndef INTERFACESIMPL_HPP
#define INTERFACESIMPL_HPP

#include <cmath>
#include <fstream>
#include <vector>

#include "Config.hpp"

#include <Eigen/Core>
#include <boost/foreach.hpp>

// Boost.Odeint includes
#include <boost/numeric/odeint.hpp>

#include "utils/MathUtils.hpp"

namespace interfaces {
/*! \typedef StateType
 *  \brief state vector for the differential equation integrator
 */
typedef std::vector<double> StateType;

/*! \typedef ProfileEvaluator
 *  \brief sort of a function pointer to the dielectric profile evaluation function
 */
typedef pcm::function< pcm::tuple<double, double>(const double) > ProfileEvaluator;

/*! \struct IntegratorParameters
 *  \brief holds parameters for the integrator
 */
struct IntegratorParameters
{
    /*! Absolute tolerance level */
    double eps_abs_     ;
    /*! Relative tolerance level */
    double eps_rel_     ;
    /*! Weight of the state      */
    double factor_x_    ;
    /*! Weight of the state derivative */
    double factor_dxdt_ ;
    /*! Lower bound of the integration interval */
    double r_0_         ;
    /*! Upper bound of the integration interval */
    double r_infinity_  ;
    /*! Time step between observer calls */
    double observer_step_;
    IntegratorParameters(double e_abs, double e_rel, double f_x, double f_dxdt, double r0, double rinf, double step)
        : eps_abs_(e_abs), eps_rel_(e_rel), factor_x_(f_x),
        factor_dxdt_(f_dxdt), r_0_(r0), r_infinity_(rinf), observer_step_(step) {}
};

/*! \class LnTransformedRadial
 *  \brief system of ln-transformed first-order radial differential equations
 *  \author Roberto Di Remigio
 *  \date 2015
 *
 *  Provides a handle to the system of differential equations for the integrator.
 *  The dielectric profile comes in as a boost::function object.
 */
class LnTransformedRadial
{
    private:
        /*! Dielectric profile function and derivative evaluation */
        ProfileEvaluator eval_;
        /*! Angular momentum */
        int l_;
    public:
        /*! Constructor from profile evaluator and angular momentum */
        LnTransformedRadial(const ProfileEvaluator & e, int lval) : eval_(e), l_(lval) {}
        /*! Provides a functor for the evaluation of the system
         *  of first-order ODEs needed by Boost.Odeint
         *  The second-order ODE and the system of first-order ODEs
         *  are reported in the manuscript.
         *  \param[in] rho state vector holding the function and its first derivative
         *  \param[out] drhodr state vector holding the first and second derivative
         *  \param[in] r position on the integration grid
         */
        void operator()(const StateType & rho, StateType & drhodr, const double r)
        {
            // Evaluate the dielectric profile
            double eps = 0.0, epsPrime = 0.0;
            pcm::tie(eps, epsPrime) = eval_(r);
            if (numericalZero(eps)) throw std::domain_error("Division by zero!");
            double gamma_epsilon = epsPrime / eps;
            // System of equations is defined here
            drhodr[0] = rho[1];
            drhodr[1] = -rho[1] * (rho[1] + 2.0/r + gamma_epsilon) + l_ * (l_ + 1) / std::pow(r, 2);
        }
};
} // namespace interfaces

using interfaces::ProfileEvaluator;
using interfaces::IntegratorParameters;

/*! \file InterfacesImpl.hpp
 *  \class RadialFunction
 *  \brief represents solutions to the radial 2nd order ODE
 *  \author Roberto Di Remigio
 *  \date 2015
 *  \tparam StateVariable type of the state variable used in the ODE solver
 *  \tparam ODESystem system of 1st order ODEs replacing the 2nd order ODE
 *  \tparam IndependentSolution encodes which type of radial solution
 */
template <typename StateVariable,
          typename ODESystem,
          template <typename, typename> class IndependentSolution>
class RadialFunction __final
{
    public:
        RadialFunction() : solution_(IndependentSolution<StateVariable, ODESystem>()) {}
        RadialFunction(int l, double r0, double rinf, const ProfileEvaluator & eval, const IntegratorParameters & parms)
            : solution_(IndependentSolution<StateVariable, ODESystem>(l, r0, rinf, eval, parms)) {}
        ~RadialFunction() {}
        /*! \brief Returns value of function and its first derivative at given point
         *  \param[in] point evaluation point
         */
        pcm::tuple<double, double> operator()(double point) const {
            return solution_(point);
        }
        friend std::ostream & operator<<(std::ostream &os, RadialFunction & obj) {
            os << obj.solution_;
            return os;
        }
    private:
        /// Independent solution to the radial equation
        IndependentSolution<StateVariable, ODESystem> solution_;
};

/*! \file InterfacesImpl.hpp
 *  \class Zeta
 *  \brief 1st solution to the radial second order ODE, with r^l behaviour in the origin
 *  \author Roberto Di Remigio
 *  \date 2015
 *  \tparam StateVariable type of the state variable used in the ODE solver
 *  \tparam ODESystem system of 1st order ODEs replacing the 2nd order ODE
 */
template <typename StateVariable,
          typename ODESystem>
class Zeta __final
{
    public:
        Zeta() : L_(0), r_0_(0.0), r_infinity_(0.0) {}
        Zeta(int l, double r0, double rinf, const ProfileEvaluator & eval, const IntegratorParameters & parms)
            : L_(l), r_0_(r0), r_infinity_(rinf) { compute(eval, parms); }
        ~Zeta() {}
        pcm::tuple<double, double> operator()(double point) const {
            return pcm::make_tuple(function_impl(point), derivative_impl(point));
        }
        friend std::ostream & operator<<(std::ostream &os, Zeta & obj) {
            for (size_t i = 0; i < obj.function_[0].size(); ++i) {
                os << obj.function_[0][i] << "    "
                   << obj.function_[1][i] << "    "
                   << obj.function_[2][i] << std::endl;
            }
            return os;
        }
    private:
        typedef pcm::array<StateVariable, 3> RadialSolution;
        /// Angular momentum of the solution
        int L_;
        /// Lower bound of the integration interval
        double r_0_;
        /// Upper bound of the integration interval
        double r_infinity_;
        /// The actual data: grid, function value and first derivative values
        RadialSolution function_;
        /*! Reports progress of differential equation integrator */
        void push_back(const StateVariable & x, double r) {
            function_[0].push_back(r);
            function_[1].push_back(x[0]);
            function_[2].push_back(x[1]);
        }
        /*! \brief Calculates 1st radial solution, i.e. the one with r^l behavior
         *  \param[in] eval   dielectric profile evaluator function object
         *  \param[in] parms parameters for the integrator
         */
        void compute(const ProfileEvaluator & eval, const IntegratorParameters & parms) {
            namespace odeint = boost::numeric::odeint;
            odeint::bulirsch_stoer_dense_out<StateVariable> stepper(parms.eps_abs_, parms.eps_rel_, parms.factor_x_, parms.factor_dxdt_);
            ODESystem system(eval, L_);
            // Holds the initial conditions
            StateVariable init_zeta(2);
            // Set initial conditions
            init_zeta[0] = L_ * std::log(r_0_);
            init_zeta[1] = L_ / r_0_;
            odeint::integrate_adaptive(stepper, system, init_zeta,
                    r_0_, r_infinity_, parms.observer_step_,
                    pcm::bind(&Zeta<StateVariable, ODESystem>::push_back, this, pcm::_1, pcm::_2));
        }
        /*! \brief Returns value of function at given point
         *  \param[in] point evaluation point
         *
         *  We first check if point is below r_0_, if yes we use
         *  the asymptotic form L*log(r) in point.
         */
        double function_impl(double point) const {
            double zeta = 0.0;
            if (point <= r_0_) {
                zeta = L_ * std::log(point);
            } else {
                zeta = splineInterpolation(point, function_[0], function_[1]);
            }
            return zeta;
        }
        /*! \brief Returns value of 1st derivative of function at given point
         *  \param[in] point evaluation point
         *
         *  We first check if point is below r_0_, if yes we use
         *  the asymptotic form L / r in point.
         */
        double derivative_impl(double point) const {
            double zeta = 0.0;
            if (point <= r_0_) {
                zeta = L_ / point;
            } else {
                zeta = splineInterpolation(point, function_[0], function_[2]);
            }
            return zeta;
        }
};

/*! \file InterfacesImpl.hpp
 *  \class Omega
 *  \brief 2nd solution to the radial second order ODE, with r^(-l-1) behaviour at infinity
 *  \author Roberto Di Remigio
 *  \date 2015
 *  \tparam StateVariable type of the state variable used in the ODE solver
 *  \tparam ODESystem system of 1st order ODEs replacing the 2nd order ODE
 */
template <typename StateVariable,
          typename ODESystem>
class Omega __final
{
    public:
        Omega() : L_(0), r_0_(0.0), r_infinity_(0.0) {}
        Omega(int l, double r0, double rinf, const ProfileEvaluator & eval, const IntegratorParameters & parms)
            : L_(l), r_0_(r0), r_infinity_(rinf) { compute(eval, parms); }
        ~Omega() {}
        pcm::tuple<double, double> operator()(double point) const {
            return pcm::make_tuple(function_impl(point), derivative_impl(point));
        }
        friend std::ostream & operator<<(std::ostream &os, Omega & obj) {
            for (size_t i = 0; i < obj.function_[0].size(); ++i) {
                os << obj.function_[0][i] << "    "
                   << obj.function_[1][i] << "    "
                   << obj.function_[2][i] << std::endl;
            }
            return os;
        }
    private:
        typedef pcm::array<StateVariable, 3> RadialSolution;
        /// Angular momentum of the solution
        int L_;
        /// Lower bound of the integration interval
        double r_0_;
        /// Upper bound of the integration interval
        double r_infinity_;
        /// The actual data: grid, function value and first derivative values
        RadialSolution function_;
        /*! Reports progress of differential equation integrator */
        void push_back(const StateVariable & x, double r) {
            function_[0].push_back(r);
            function_[1].push_back(x[0]);
            function_[2].push_back(x[1]);
        }
        /*! \brief calculates 2nd radial solution, i.e. the one with r^(-l-1) behavior
         *  \param[in] eval   dielectric profile evaluator function object
         *  \param[in] parms parameters for the integrator
         */
        void compute(const ProfileEvaluator & eval, const IntegratorParameters & parms) {
            namespace odeint = boost::numeric::odeint;
            odeint::bulirsch_stoer_dense_out<StateVariable> stepper(parms.eps_abs_, parms.eps_rel_, parms.factor_x_, parms.factor_dxdt_);
            ODESystem system(eval, L_);
            // Holds the initial conditions
            StateVariable init_omega(2);
            // Set initial conditions
            init_omega[0] = -(L_ + 1) * std::log(r_infinity_);
            init_omega[1] = -(L_ + 1) / r_infinity_;
            // Notice that we integrate BACKWARDS, so we pass -step to integrate_adaptive
            boost::numeric::odeint::integrate_adaptive(stepper, system, init_omega,
                    r_infinity_, r_0_, -parms.observer_step_,
                    pcm::bind(&Omega<StateVariable, ODESystem>::push_back, this, pcm::_1, pcm::_2));
            // Reverse order of StateVariable-s in RadialSolution
            // this ensures that they are in ascending order, as later expected by function_impl and derivative_impl
            BOOST_FOREACH(StateVariable & comp, function_) {
                std::reverse(comp.begin(), comp.end());
            }
        }
        /*! \brief Returns value of function at given point
         *  \param[in] point evaluation point
         *
         * We first check if point is above r_infinity_, if yes we use
         * the asymptotic form -(L+1)*log(r) in point.
         */
        double function_impl(double point) const {
            double omega = 0.0;
            if (point >= r_infinity_) {
                omega = -(L_ + 1) * std::log(point);
            } else {
                omega = splineInterpolation(point, function_[0], function_[1]);
            }
            return omega;
        }
        /*! \brief Returns value of 1st derivative of function at given point
         *  \param[in] point evaluation point
         *
         * We first check if point is above r_infinity_, if yes we use
         * the asymptotic form -(L+1)/r in point.
         */
        double derivative_impl(double point) const {
            double omega = 0.0;
            if (point >= r_infinity_) {
                omega = -(L_ + 1) / point;
            } else {
                omega = splineInterpolation(point, function_[0], function_[2]);
            }
            return omega;
        }
};

/*! \brief Write contents of a RadialFunction to file
 *  \param[in] f RadialSolution whose contents have to be printed
 *  \param[in] fname name of the file
 *  \author Roberto Di Remigio
 *  \date 2015
 *  \tparam StateVariable type of the state variable used in the ODE solver
 *  \tparam ODESystem system of 1st order ODEs replacing the 2nd order ODE
 *  \tparam IndependentSolution encodes which type of radial solution
 */
template <typename StateVariable,
          typename ODESystem,
          template <typename, typename> class IndependentSolution>
void writeToFile(RadialFunction<StateVariable, ODESystem, IndependentSolution> & f, const std::string & fname) {
    std::ofstream fout;
    fout.open(fname.c_str());
    fout << f << std::endl;
    fout.close();
}

#endif // INTERFACESIMPL_HPP


//=======================================================================
// Copyright (c)
//
// 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)
//=======================================================================
/**
 * @file facility_location_solution_test.cpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-06-20
 */

#include "test_utils/sample_graph.hpp"

#include "paal/data_structures/facility_location/facility_location_solution.hpp"
#include "paal/data_structures/voronoi/voronoi.hpp"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(facility_location_solutionTest) {
    typedef sample_graphs_metrics SGM;
    auto gm = SGM::get_graph_metric_small();
    std::vector<int> fcosts{ 7, 8 };
    auto cost = [&](int i) { return fcosts[i]; };

    typedef paal::data_structures::voronoi<decltype(gm)> VorType;
    typedef paal::data_structures::facility_location_solution<decltype(cost),
                                                              VorType> Sol;
    typedef VorType::GeneratorsSet FSet;
    VorType voronoi(FSet{}, FSet{ SGM::A, SGM::B, SGM::C, SGM::D, SGM::E }, gm);

    Sol sol(voronoi, FSet{ SGM::A, SGM::B }, cost);

    sol.add_facility(SGM::A);
    auto ab_min_a = sol.add_facility(SGM::B);
    auto b_min_ab = sol.rem_facility(SGM::A);
    BOOST_CHECK_EQUAL(sol.add_facility(SGM::A), -b_min_ab);
    BOOST_CHECK_EQUAL(sol.rem_facility(SGM::B), -ab_min_a);
}


// Boost.Geometry - gis-projections (based on PROJ4)

// Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2017, 2018.
// Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Boost.Geometry by Barend Gehrels

// Last updated version of proj: 5.0.0

// Original copyright notice:

// Copyright (c) 2008   Gerald I. Evenden

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

/* The code in this file is largly based upon procedures:
 *
 * Written by: Knud Poder and Karsten Engsager
 *
 * Based on math from: R.Koenig and K.H. Weise, "Mathematische
 * Grundlagen der hoeheren Geodaesie und Kartographie,
 * Springer-Verlag, Berlin/Goettingen" Heidelberg, 1951.
 *
 * Modified and used here by permission of Reference Networks
 * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark
*/

#ifndef BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP
#define BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP

#include <boost/geometry/srs/projections/impl/base_static.hpp>
#include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
#include <boost/geometry/srs/projections/impl/factory_entry.hpp>
#include <boost/geometry/srs/projections/impl/function_overloads.hpp>
#include <boost/geometry/srs/projections/impl/pj_param.hpp>
#include <boost/geometry/srs/projections/impl/projects.hpp>

#include <boost/math/special_functions/hypot.hpp>

namespace boost { namespace geometry
{

namespace projections
{
    #ifndef DOXYGEN_NO_DETAIL
    namespace detail { namespace etmerc
    {

            static const int PROJ_ETMERC_ORDER = 6;

            template <typename T>
            struct par_etmerc
            {
                T    Qn;    /* Merid. quad., scaled to the projection */
                T    Zb;    /* Radius vector in polar coord. systems  */
                T    cgb[6]; /* Constants for Gauss -> Geo lat */
                T    cbg[6]; /* Constants for Geo lat -> Gauss */
                T    utg[6]; /* Constants for transv. merc. -> geo */
                T    gtu[6]; /* Constants for geo -> transv. merc. */
            };

            template <typename T>
            inline T log1py(T const& x) {              /* Compute log(1+x) accurately */
                volatile T
                  y = 1 + x,
                  z = y - 1;
                /* Here's the explanation for this magic: y = 1 + z, exactly, and z
                 * approx x, thus log(y)/z (which is nearly constant near z = 0) returns
                 * a good approximation to the true log(1 + x)/x.  The multiplication x *
                 * (log(y)/z) introduces little additional error. */
                return z == 0 ? x : x * log(y) / z;
            }

            template <typename T>
            inline T asinhy(T const& x) {              /* Compute asinh(x) accurately */
                T y = fabs(x);         /* Enforce odd parity */
                y = log1py(y * (1 + y/(boost::math::hypot(1.0, y) + 1)));
                return x < 0 ? -y : y;
            }

            template <typename T>
            inline T gatg(const T *p1, int len_p1, T const& B) {
                const T *p;
                T h = 0, h1, h2 = 0, cos_2B;

                cos_2B = 2*cos(2*B);
                for (p = p1 + len_p1, h1 = *--p; p - p1; h2 = h1, h1 = h)
                    h = -h2 + cos_2B*h1 + *--p;
                return (B + h*sin(2*B));
            }

            /* Complex Clenshaw summation */
            template <typename T>
            inline T clenS(const T *a, int size, T const& arg_r, T const& arg_i, T *R, T *I) {
                T      r, i, hr, hr1, hr2, hi, hi1, hi2;
                T      sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i;

                /* arguments */
                const T* p = a + size;
                sin_arg_r  = sin(arg_r);
                cos_arg_r  = cos(arg_r);
                sinh_arg_i = sinh(arg_i);
                cosh_arg_i = cosh(arg_i);
                r          =  2*cos_arg_r*cosh_arg_i;
                i          = -2*sin_arg_r*sinh_arg_i;
                /* summation loop */
                for (hi1 = hr1 = hi = 0, hr = *--p; a - p;) {
                    hr2 = hr1;
                    hi2 = hi1;
                    hr1 = hr;
                    hi1 = hi;
                    hr  = -hr2 + r*hr1 - i*hi1 + *--p;
                    hi  = -hi2 + i*hr1 + r*hi1;
                }
                r   = sin_arg_r*cosh_arg_i;
                i   = cos_arg_r*sinh_arg_i;
                *R  = r*hr - i*hi;
                *I  = r*hi + i*hr;
                return(*R);
            }

            /* Real Clenshaw summation */
            template <typename T>
            inline T clens(const T *a, int size, T const& arg_r) {
                T      r, hr, hr1, hr2, cos_arg_r;

                const T* p = a + size;
                cos_arg_r  = cos(arg_r);
                r          =  2*cos_arg_r;

                /* summation loop */
                for (hr1 = 0, hr = *--p; a - p;) {
                    hr2 = hr1;
                    hr1 = hr;
                    hr  = -hr2 + r*hr1 + *--p;
                }
                return(sin(arg_r)*hr);
            }

            // template class, using CRTP to implement forward/inverse
            template <typename T, typename Parameters>
            struct base_etmerc_ellipsoid
                : public base_t_fi<base_etmerc_ellipsoid<T, Parameters>, T, Parameters>
            {
                par_etmerc<T> m_proj_parm;

                inline base_etmerc_ellipsoid(const Parameters& par)
                    : base_t_fi<base_etmerc_ellipsoid<T, Parameters>, T, Parameters>(*this, par)
                {}

                // FORWARD(e_forward)  ellipsoid
                // Project coordinates from geographic (lon, lat) to cartesian (x, y)
                inline void fwd(T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
                {
                    T sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
                    T Cn = lp_lat, Ce = lp_lon;

                    /* ell. LAT, LNG -> Gaussian LAT, LNG */
                    Cn  = gatg(this->m_proj_parm.cbg, PROJ_ETMERC_ORDER, Cn);
                    /* Gaussian LAT, LNG -> compl. sph. LAT */
                    sin_Cn = sin(Cn);
                    cos_Cn = cos(Cn);
                    sin_Ce = sin(Ce);
                    cos_Ce = cos(Ce);

                    Cn     = atan2(sin_Cn, cos_Ce*cos_Cn);
                    Ce     = atan2(sin_Ce*cos_Cn, boost::math::hypot(sin_Cn, cos_Cn*cos_Ce));

                    /* compl. sph. N, E -> ell. norm. N, E */
                    Ce  = asinhy(tan(Ce));     /* Replaces: Ce  = log(tan(fourth_pi + Ce*0.5)); */
                    Cn += clenS(this->m_proj_parm.gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
                    Ce += dCe;
                    if (fabs(Ce) <= 2.623395162778) {
                        xy_y  = this->m_proj_parm.Qn * Cn + this->m_proj_parm.Zb;  /* Northing */
                        xy_x  = this->m_proj_parm.Qn * Ce;  /* Easting  */
                    } else
                        xy_x = xy_y = HUGE_VAL;
                }

                // INVERSE(e_inverse)  ellipsoid
                // Project coordinates from cartesian (x, y) to geographic (lon, lat)
                inline void inv(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
                {
                    T sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
                    T Cn = xy_y, Ce = xy_x;

                    /* normalize N, E */
                    Cn = (Cn - this->m_proj_parm.Zb)/this->m_proj_parm.Qn;
                    Ce = Ce/this->m_proj_parm.Qn;

                    if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */
                        /* norm. N, E -> compl. sph. LAT, LNG */
                        Cn += clenS(this->m_proj_parm.utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
                        Ce += dCe;
                        Ce = atan(sinh(Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - fourth_pi); */
                        /* compl. sph. LAT -> Gaussian LAT, LNG */
                        sin_Cn = sin(Cn);
                        cos_Cn = cos(Cn);
                        sin_Ce = sin(Ce);
                        cos_Ce = cos(Ce);
                        Ce     = atan2(sin_Ce, cos_Ce*cos_Cn);
                        Cn     = atan2(sin_Cn*cos_Ce, boost::math::hypot(sin_Ce, cos_Ce*cos_Cn));
                        /* Gaussian LAT, LNG -> ell. LAT, LNG */
                        lp_lat = gatg(this->m_proj_parm.cgb,  PROJ_ETMERC_ORDER, Cn);
                        lp_lon = Ce;
                    }
                    else
                        lp_lat = lp_lon = HUGE_VAL;
                }

                static inline std::string get_name()
                {
                    return "etmerc_ellipsoid";
                }

            };

            template <typename Parameters, typename T>
            inline void setup(Parameters& par, par_etmerc<T>& proj_parm)
            {
                T f, n, np, Z;

                if (par.es <= 0) {
                    BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
                }

                f = par.es / (1 + sqrt(1 -  par.es)); /* Replaces: f = 1 - sqrt(1-par.es); */

                /* third flattening */
                np = n = f/(2 - f);

                /* COEF. OF TRIG SERIES GEO <-> GAUSS */
                /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */
                /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */
                /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */

                proj_parm.cgb[0] = n*( 2 + n*(-2/3.0  + n*(-2      + n*(116/45.0 + n*(26/45.0 +
                            n*(-2854/675.0 ))))));
                proj_parm.cbg[0] = n*(-2 + n*( 2/3.0  + n*( 4/3.0  + n*(-82/45.0 + n*(32/45.0 +
                            n*( 4642/4725.0))))));
                np     *= n;
                proj_parm.cgb[1] = np*(7/3.0 + n*( -8/5.0  + n*(-227/45.0 + n*(2704/315.0 +
                            n*( 2323/945.0)))));
                proj_parm.cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0  + n*( 904/315.0 +
                            n*(-1522/945.0)))));
                np     *= n;
                /* n^5 coeff corrected from 1262/105 -> -1262/105 */
                proj_parm.cgb[2] = np*( 56/15.0  + n*(-136/35.0 + n*(-1262/105.0 +
                            n*( 73814/2835.0))));
                proj_parm.cbg[2] = np*(-26/15.0  + n*(  34/21.0 + n*(    8/5.0   +
                            n*(-12686/2835.0))));
                np     *= n;
                /* n^5 coeff corrected from 322/35 -> 332/35 */
                proj_parm.cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0)));
                proj_parm.cbg[3] = np*(1237/630.0 + n*( -12/5.0  + n*( -24832/14175.0)));
                np     *= n;
                proj_parm.cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 ));
                proj_parm.cbg[4] = np*(-734/315.0 + n*( 109598/31185.0));
                np     *= n;
                proj_parm.cgb[5] = np*(601676/22275.0 );
                proj_parm.cbg[5] = np*(444337/155925.0);

                /* Constants of the projections */
                /* Transverse Mercator (UTM, ITM, etc) */
                np = n*n;
                /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */
                proj_parm.Qn = par.k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0)));
                /* coef of trig series */
                /* utg := ell. N, E -> sph. N, E,  KW p194 (65) */
                /* gtu := sph. N, E -> ell. N, E,  KW p196 (69) */
                proj_parm.utg[0] = n*(-0.5  + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 +
                            n*(  81/512.0 + n*(-96199/604800.0))))));
                proj_parm.gtu[0] = n*( 0.5  + n*(-2/3.0 + n*(  5/16.0 + n*(41/180.0 +
                            n*(-127/288.0 + n*(  7891/37800.0 ))))));
                proj_parm.utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 +
                            n*( 1118711/3870720.0)))));
                proj_parm.gtu[1] = np*(13/48.0 + n*(-3/5.0  + n*(557/1440.0 + n*(281/630.0 +
                            n*(-1983433/1935360.0)))));
                np      *= n;
                proj_parm.utg[2] = np*(-17/480.0 + n*(  37/840.0 + n*(  209/4480.0  +
                            n*( -5569/90720.0 ))));
                proj_parm.gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 +
                            n*(167603/181440.0))));
                np      *= n;
                proj_parm.utg[3] = np*(-4397/161280.0 + n*(  11/504.0 + n*( 830251/7257600.0)));
                proj_parm.gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0)));
                np     *= n;
                proj_parm.utg[4] = np*(-4583/161280.0 + n*(  108847/3991680.0));
                proj_parm.gtu[4] = np*(34729/80640.0  + n*(-3418889/1995840.0));
                np     *= n;
                proj_parm.utg[5] = np*(-20648693/638668800.0);
                proj_parm.gtu[5] = np*(212378941/319334400.0);

                /* Gaussian latitude value of the origin latitude */
                Z = gatg(proj_parm.cbg, PROJ_ETMERC_ORDER, par.phi0);

                /* Origin northing minus true northing at the origin latitude */
                /* i.e. true northing = N - proj_parm.Zb                         */
                proj_parm.Zb  = - proj_parm.Qn*(Z + clens(proj_parm.gtu, PROJ_ETMERC_ORDER, 2*Z));
            }

            // Extended Transverse Mercator
            template <typename Parameters, typename T>
            inline void setup_etmerc(Parameters& par, par_etmerc<T>& proj_parm)
            {
                setup(par, proj_parm);
            }

            // Universal Transverse Mercator (UTM)
            template <typename Params, typename Parameters, typename T>
            inline void setup_utm(Params const& params, Parameters& par, par_etmerc<T>& proj_parm)
            {
                static const T pi = detail::pi<T>();

                int zone;

                if (par.es == 0.0) {
                    BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
                }

                par.y0 = pj_get_param_b<srs::spar::south>(params, "south", srs::dpar::south) ? 10000000. : 0.;
                par.x0 = 500000.;
                if (pj_param_i<srs::spar::zone>(params, "zone", srs::dpar::zone, zone)) /* zone input ? */
                {
                    if (zone > 0 && zone <= 60)
                        --zone;
                    else {
                        BOOST_THROW_EXCEPTION( projection_exception(error_invalid_utm_zone) );
                    }
                }
                else /* nearest central meridian input */
                {
                    zone = int_floor((adjlon(par.lam0) + pi) * 30. / pi);
                    if (zone < 0)
                        zone = 0;
                    else if (zone >= 60)
                        zone = 59;
                }
                par.lam0 = (zone + .5) * pi / 30. - pi;
                par.k0 = 0.9996;
                par.phi0 = 0.;

                setup(par, proj_parm);
            }

    }} // namespace detail::etmerc
    #endif // doxygen

    /*!
        \brief Extended Transverse Mercator projection
        \ingroup projections
        \tparam Geographic latlong point type
        \tparam Cartesian xy point type
        \tparam Parameters parameter type
        \par Projection characteristics
         - Cylindrical
         - Spheroid
        \par Projection parameters
         - lat_ts: Latitude of true scale
         - lat_0: Latitude of origin
        \par Example
        \image html ex_etmerc.gif
    */
    template <typename T, typename Parameters>
    struct etmerc_ellipsoid : public detail::etmerc::base_etmerc_ellipsoid<T, Parameters>
    {
        template <typename Params>
        inline etmerc_ellipsoid(Params const& , Parameters const& par)
            : detail::etmerc::base_etmerc_ellipsoid<T, Parameters>(par)
        {
            detail::etmerc::setup_etmerc(this->m_par, this->m_proj_parm);
        }
    };

    /*!
        \brief Universal Transverse Mercator (UTM) projection
        \ingroup projections
        \tparam Geographic latlong point type
        \tparam Cartesian xy point type
        \tparam Parameters parameter type
        \par Projection characteristics
         - Cylindrical
         - Spheroid
        \par Projection parameters
         - zone: UTM Zone (integer)
         - south: Denotes southern hemisphere UTM zone (boolean)
        \par Example
        \image html ex_utm.gif
    */
    template <typename T, typename Parameters>
    struct utm_ellipsoid : public detail::etmerc::base_etmerc_ellipsoid<T, Parameters>
    {
        template <typename Params>
        inline utm_ellipsoid(Params const& params, Parameters const& par)
            : detail::etmerc::base_etmerc_ellipsoid<T, Parameters>(par)
        {
            detail::etmerc::setup_utm(params, this->m_par, this->m_proj_parm);
        }
    };

    #ifndef DOXYGEN_NO_DETAIL
    namespace detail
    {

        // Static projection
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::spar::proj_etmerc, etmerc_ellipsoid, etmerc_ellipsoid)
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::spar::proj_utm, utm_ellipsoid, utm_ellipsoid)

        // Factory entry(s)
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_FI(etmerc_entry, etmerc_ellipsoid)
        BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_FI(utm_entry, utm_ellipsoid)

        BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(etmerc_init)
        {
            BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(etmerc, etmerc_entry);
            BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(utm, utm_entry);
        }

    } // namespace detail
    #endif // doxygen

} // namespace projections

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP


#ifndef CRYPT_GMM_SMP_SERVER_HPP
#define CRYPT_GMM_SMP_SERVER_HPP
#include <HElib/EncryptedArray.h>
#include <HElib/Ctxt.h>

#include "SMP/Matrix.hpp"
#include "SMP/DoublePacking.hpp"

#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <vector>
#include <atomic>
#include <thread>
#include <list>
class FHEcontext;
using boost::asio::ip::tcp;
class SMPServer {
public:
	SMPServer();

	~SMPServer();

	void run(tcp::iostream &conn,
			 const long n1,
			 const long n2,
			 const long n3);
	static void print_statistics();

protected:
	//static std::vector<double> setup_times;
	//static std::vector<double> process_columns_times;
	//static std::vector<double> receive_ctx_times;
	//static std::vector<double> evaluate_times;
	//static std::vector<double> response_ctx_times;

	void setup(tcp::iostream &conn);

	void process_columns();

	void receive_ctx(tcp::iostream &conn);

	void evaluate();

	void evaluate_mat_vec();

	void response_ctx(tcp::iostream &conn);

private:
	Matrix A, B;
	Matrix ground_truth;

	FHEcontext *context = nullptr;
	FHEPubKey *ek = nullptr;
	std::vector<std::vector<Ctxt>> enc_A_blk;
	NTL::Mat<internal::PackedRows> plain_B_blk;
	NTL::Mat<NTL::ZZX> encoded_plain_B_blk;
	std::list<Ctxt> results;
};
#endif // CRYPT_GMM_SMP_SERVER_HPP


#include <boost/math/special_functions/powm1.hpp>


#pragma once

#include <armadillo>

#include "heattransfer/heattransferbase.hpp"
#include "heattransfer/burialmedium.hpp"
#include "heattransfer/ambientfluid.hpp"

class Pipeline;
class PipeWall;

/*!
 * \brief Base class for heat transfer calculation with 1d radial models.
 *
 * This contains the description of the burial medium, burial depth, ambient
 * fluid etc., as well as the 1d radial discretization of the surroundings of
 * the pipeline (in m_width, m_conductivity, etc.).
 */
class RadialHeatTransfer : public HeatTransferBase
{
public:
    /*!
     * \brief Constructor that sets up the discretization of the pipe surroundings.
     * \param diameter Pipe inner diameter [m]
     * \param pipeWall PipeWall instance
     * \param burialDepth Distance from top of pipe to top of burial medium [m]
     * \param burialMedium BurialMedium instance
     * \param ambientFluid AmbientFluid instance
     */
    RadialHeatTransfer(
            const double diameter,
            const PipeWall& pipeWall,
            const double burialDepth,
            const BurialMedium& burialMedium,
            const AmbientFluid& ambientFluid);

    /*!
     * \brief Calculate the outer film coefficient. Basically a wrapper around
     * utils::calcOuterWallFilmCoefficient(const double, const AmbientFluid&)
     * using the proper arguments.
     *
     * \return Outer film coefficient [W/(m2 K)]
     */
    double calculateOuterFilmCoefficient() const;

    //! The number of discretization elements.
    virtual arma::uword size() const { return m_width.n_elem; }

    /*!
     * \brief Make instance of HeatTransferState from heat flux. Override.
     *
     * Makes HeatTransferState instance with the correct temperature property
     * for 1d radial heat transfer models.
     *
     * \param heatFlux Heat flux [W/m2]
     * \return HeatTransferState instance with temperature.
     */
    virtual HeatTransferState makeState(const double heatFlux) const override
    {
        return HeatTransferState(heatFlux, arma::zeros<arma::vec>(size()));
    }

    /*!
     * \brief Make instance of HeatTransferState from heat flux. Override.
     *
     * Makes HeatTransferState instance with the correct temperature property
     * for 1d radial heat transfer models. Performs a linear interpolation
     * between gas temperature and ambient temperature. This could probably be
     * improved upon, since there is likely an analytical solution.
     *
     * \param heatFlux Heat flux [W/m2]
     * \param ambientTemperature Ambient temperature [K]
     * \param gasTemperature Gas temperature [K]
     * \return HeatTransferState instance with temperature.
     */
    virtual HeatTransferState makeState(
            const double heatFlux,
            const double gasTemperature,
            const double ambientTemperature) const override
    {
        arma::vec shellTemperature = arma::linspace(
                    gasTemperature, ambientTemperature, size());

        return HeatTransferState(heatFlux, shellTemperature);
    }

protected:
    double m_diameter; //!< Pipe inner diameter [m]

    double m_burialDepth; //!< Distance from top of pipe to top of burial medium [m]

    //! Description of the medium the pipeline is buried in (or on top
    //! of/suspended above depending on burial depth).
    BurialMedium m_burialMedium;
    //! Description of the fluid surrounding the pipeline.
    AmbientFluid m_ambientFluid;

    arma::vec m_width;        //!< Width of each discretization shell [m]
    arma::vec m_conductivity; //!< Thermal conductivity of each discretization shell [W/(m K)]
    arma::vec m_density;      //!< Density of each discretization shell [kg/m3]
    arma::vec m_heatCapacity; //!< Heat capacity of each discretization shell (\f$c_p\f$) [J/(kg K)]

    //! Bool that determines whether the discretization layers are due to burial
    //! in the burial medium or not.
    arma::uvec m_isBurialLayer;

    arma::vec m_crossSection; //!< Area/cross-section of each shell [m2]
    arma::vec m_ri;           //!< Inner radius [m]
    arma::vec m_ro;           //!< Outer radius [m]
};


// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2010-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Experiment using Proto to construct geometries

#include <vector>
#include <string>
#include <iostream>

#include <boost/proto/core.hpp>
#include <boost/proto/transform.hpp>
#include <boost/type_traits/add_reference.hpp>

#include <boost/mpl/assert.hpp>

#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>

namespace bg = boost::geometry;
namespace proto = boost::proto;
using proto::_;

struct proto_geometry_tag {};

// Add a coordinate to a ring or linestring
struct append_point : proto::callable
{
    template<typename Sig>
    struct result;

    template<typename This, typename Geometry, typename T1, typename T2>
    struct result<This(Geometry, T1, T2)>
        : boost::add_reference<Geometry>
    {};

    template<typename Geometry, typename T1, typename T2>
    Geometry& operator()(Geometry& geometry, T1 const& x, T2 const& y) const
    {
        typedef typename boost::geometry::point_type<Geometry>::type point;
        point p;
        boost::geometry::set<0>(p, x);
        boost::geometry::set<1>(p, y);
        geometry.push_back(p);
        return geometry;
    }
};

// The grammar for valid geometry expressions,
// and a transform that populates the geometry
struct geometry_grammar
  : proto::or_
    <
        proto::when
            <
                proto::function
                <
                    proto::terminal<proto_geometry_tag>
                  , proto::terminal<proto::convertible_to< double > >
                  , proto::terminal<proto::convertible_to< double > >
                >
              , append_point
                (
                    proto::_data
                  , proto::_value(proto::_child1)
                  , proto::_value(proto::_child2)
                )
            >
      , proto::when
            <
                proto::function
                <
                    geometry_grammar
                    , proto::terminal<proto::convertible_to< double > >
                    , proto::terminal<proto::convertible_to< double > >
                >
                , append_point
                    (
                        geometry_grammar(proto::_child0)
                      , proto::_value(proto::_child1)
                      , proto::_value(proto::_child2)
                    )
            >
    >
{};

template<typename Expr>
struct proto_geometry_expression;

struct proto_geometry_domain
  : proto::domain<proto::pod_generator<proto_geometry_expression>, geometry_grammar>
{};

// An expression wrapper that provides a conversion to a
// ring, that uses the geometry_grammar
template<typename Expr>
struct proto_geometry_expression
{
    BOOST_PROTO_BASIC_EXTENDS(Expr, proto_geometry_expression, proto_geometry_domain)
    BOOST_PROTO_EXTENDS_FUNCTION()

    template <typename T>
    operator T () const
    {
        BOOST_MPL_ASSERT((proto::matches<Expr, geometry_grammar>));
        T geometry;
        return geometry_grammar()(*this, 0, geometry);
    }

    // NOT finished, need more knowledge about Proto
    template <typename T>
    inline T inner() const
    {
        BOOST_MPL_ASSERT((proto::matches<Expr, geometry_grammar>));
        T geometry;
        return geometry_grammar()(*this, 0, geometry);
    }
    inline void inner()
    {
    }
};

int main()
{
    proto_geometry_expression<proto::terminal<proto_geometry_tag>::type> const
            geometry_of = {{{}}};

    // Initialize a ring:
    typedef bg::model::ring<bg::model::d2::point_xy<double> > ring_type;
    ring_type ring = geometry_of(16, 1)(15,2)(14, 3)(13,4)(12, 3.14)(1,6);
    std::cout << bg::wkt(ring) << std::endl;

    // Initialize a line
    typedef bg::model::linestring<bg::model::d2::point_xy<double> > line_type;
    line_type line = geometry_of(1, 1)(2, 2)(3, 3);
    std::cout << bg::wkt(line) << std::endl;

    // For Polygon (not finished) it should be something like:
    typedef bg::model::polygon<bg::model::d2::point_xy<double> > polygon_type;
    //polygon_type polygon = geometry_of(0, 0)(0, 10)(10, 10)(10, 0)(0, 0).inner(2, 2)(2, 5)(5, 5)(5, 2)(2, 2);
    //std::cout << bg::wkt(polygon) << std::endl;

    return 0;
}