// Filename: CirculationModel_RV_PA.cpp
// Created on 20 Aug 2007 by Boyce Griffith

// Modified 2019, Alexander D. Kaiser

#include "CirculationModel_RV_PA.h"
#include "pnpoly.h"
/////////////////////////////// INCLUDES /////////////////////////////////////

#ifndef included_IBAMR_config
#include <IBAMR_config.h>
#define included_IBAMR_config
#endif

#ifndef included_SAMRAI_config
#include <SAMRAI_config.h>
#define included_SAMRAI_config
#endif

// SAMRAI INCLUDES
#include <CartesianGridGeometry.h>
#include <CartesianPatchGeometry.h>
#include <PatchLevel.h>
#include <SideData.h>
#include <tbox/RestartManager.h>
#include <tbox/SAMRAI_MPI.h>
#include <tbox/Utilities.h>

// C++ STDLIB INCLUDES
#include <cassert>

#include <Eigen/Dense>
using namespace Eigen;

namespace
{
// Name of output file.
static const string DATA_FILE_NAME = "bc_data.m";

}

/////////////////////////////// NAMESPACE ////////////////////////////////////

/////////////////////////////// STATIC ///////////////////////////////////////

/////////////////////////////// PUBLIC ///////////////////////////////////////

CirculationModel_RV_PA::CirculationModel_RV_PA(Pointer<Database> input_db,
                                               const fourier_series_data *fourier_right_ventricle,
                                               const fourier_series_data *fourier_right_pa,
                                               const fourier_series_data *fourier_left_pa,
                                               string right_ventricle_vertices_file_name,
                                               string right_pa_vertices_file_name,
                                               string left_pa_vertices_file_name,
                                               const double  cycle_duration,
                                               const double  t_offset_bcs_unscaled,
                                               const double  initial_time,
                                               double P_initial_pa,
                                               bool rcr_bcs_on,
                                               bool resistance_bcs_on,
                                               bool inductor_bcs_on)
    :
      d_object_name("circ_model_rv_pa"),  // constant name here
      d_registered_for_restart(true),      // always true
      d_fourier_right_ventricle(fourier_right_ventricle),
      d_fourier_right_pa(fourier_right_pa),
      d_fourier_left_pa(fourier_left_pa),
      d_cycle_duration(cycle_duration),
      d_t_offset_bcs_unscaled(t_offset_bcs_unscaled),
      d_current_idx_series(0),
      d_Q_right_ventricle(0.0),
      d_Q_right_pa(0.0),
      d_Q_left_pa(0.0),
      d_Q_right_pa_previous(0.0),
      d_Q_left_pa_previous(0.0),
      d_time(initial_time),
      d_right_pa_P(P_initial_pa),
      d_right_pa_P_Wk(P_initial_pa),
      d_right_pa_P_distal(P_initial_pa),
      d_right_pa_P_distal_previous(P_initial_pa),
      d_left_pa_P(P_initial_pa),
      d_left_pa_P_Wk(P_initial_pa),
      d_left_pa_P_distal(P_initial_pa),
      d_left_pa_P_distal_previous(P_initial_pa),
      d_area_right_ventricle(0.0),
      d_area_right_pa(0.0),
      d_area_left_pa (0.0),
      d_area_initialized(false),
      d_rcr_bcs_on(rcr_bcs_on),
      d_resistance_bcs_on(resistance_bcs_on),
      d_inductor_bcs_on(inductor_bcs_on)
{

    if (d_registered_for_restart)
    {
        RestartManager::getManager()->registerRestartItem(d_object_name, this);
    }

    // Initialize object with data read from the input and restart databases.
    const bool from_restart = RestartManager::getManager()->isFromRestart();
    if (from_restart)
    {
        getFromRestart();
    }

    if (d_rcr_bcs_on){
        if (input_db){
            // left and right equal for now
            d_right_pa_R_proximal = input_db->getDouble("right_pa_R_proximal");
            d_right_pa_R_distal   = input_db->getDouble("right_pa_R_distal");
            d_right_pa_C          = input_db->getDouble("right_pa_C");

            d_left_pa_R_proximal  = input_db->getDouble("left_pa_R_proximal");
            d_left_pa_R_distal    = input_db->getDouble("left_pa_R_distal");
            d_left_pa_C           = input_db->getDouble("left_pa_C");

            std::cout << "input db got values:\n";
            std::cout << "right: R_proximal = " << d_right_pa_R_proximal << "\tR_distal = " << d_right_pa_R_distal << "\tC = " << d_right_pa_C << "\n";
            std::cout << "left : R_proximal = " << d_left_pa_R_proximal << "\tR_distal = " << d_left_pa_R_distal << "\tC = " << d_left_pa_C << "\n";
        }
        else {
            TBOX_ERROR("Must provide valid input_db");
        }
    }

    if (d_resistance_bcs_on){
        d_right_pa_resistance = input_db->getDouble("right_pa_R");
        d_left_pa_resistance  = input_db->getDouble("left_pa_R");
        std::cout << "input db got values:\n";
        std::cout << "right: resistance = " << d_right_pa_resistance << "\n";
        std::cout << "left : R_proximal = " << d_left_pa_resistance << "\n";
    }

    if (d_inductor_bcs_on){
        d_right_pa_inductance = input_db->getDouble("right_pa_L");
        d_left_pa_inductance  = input_db->getDouble("left_pa_L");
        std::cout << "input db got values:\n";
        std::cout << "right: inductance = " << d_right_pa_inductance << "\n";
        std::cout << "left : inductance = " << d_left_pa_inductance << "\n";
    }

    if ((d_rcr_bcs_on && d_resistance_bcs_on) ||
        (d_rcr_bcs_on && d_inductor_bcs_on)   ||
        (d_inductor_bcs_on && d_resistance_bcs_on)) {
        TBOX_ERROR("Cannot us two types of bc simulataneously");
    }

    double x,x_prev,y,y_prev,z,z_prev;
    double tol = 1.0e-2;

    // read vertices from file
    ifstream right_ventricle_file(right_ventricle_vertices_file_name.c_str(), ios::in);

    if(!right_ventricle_file){
        TBOX_ERROR("Aorta file not found\n");
    }

    right_ventricle_file >> d_n_pts_right_ventricle;

    d_right_ventricle_points_idx1 = new double[d_n_pts_right_ventricle];
    d_right_ventricle_points_idx2 = new double[d_n_pts_right_ventricle];

    for (int i=0; i<d_n_pts_right_ventricle; i++){
        right_ventricle_file >> x;
        right_ventricle_file >> d_right_ventricle_points_idx1[i];
        right_ventricle_file >> d_right_ventricle_points_idx2[i];

        if (i>0){
            if (fabs(x_prev - x) > tol){
                TBOX_ERROR("x coordinates must be consistent\n");
            }
        }
        x_prev = x;

    }
    pout << "to right_ventricle file close\n";
    right_ventricle_file.close();
    d_right_ventricle_axis = 0;
    d_right_ventricle_side = 0;

    // read vertices from file
    ifstream right_pa_file(right_pa_vertices_file_name.c_str(), ios::in);

    if(!right_pa_file){
        TBOX_ERROR("Aorta file not found\n");
    }

    right_pa_file >> d_n_pts_right_pa;

    d_right_pa_points_idx1 = new double[d_n_pts_right_pa];
    d_right_pa_points_idx2 = new double[d_n_pts_right_pa];

    for (int i=0; i<d_n_pts_right_pa; i++){
        right_pa_file >> d_right_pa_points_idx1[i];
        right_pa_file >> d_right_pa_points_idx2[i];
        right_pa_file >> z;

        if (i>0){
            if (fabs(z_prev - z) > tol){
                TBOX_ERROR("z coordinates must be consistent\n");
            }
        }
        z_prev = z;

    }
    pout << "to right_pa file close\n";
    right_pa_file.close();
    d_right_pa_axis = 2;
    d_right_pa_side = 0;

    // read vertices from file
    ifstream left_pa_file(left_pa_vertices_file_name.c_str(), ios::in);

    if(!left_pa_file){
        TBOX_ERROR("Left PA file not found\n");
    }

    left_pa_file >> d_n_pts_left_pa;

    d_left_pa_points_idx1 = new double[d_n_pts_left_pa];
    d_left_pa_points_idx2 = new double[d_n_pts_left_pa];

    for (int i=0; i<d_n_pts_left_pa; i++){
        left_pa_file >> d_left_pa_points_idx1[i];
        left_pa_file >> y;
        left_pa_file >> d_left_pa_points_idx2[i];

        if (i>0){
            if (fabs(y_prev - y) > tol){
                TBOX_ERROR("y coordinates must be consistent\n");
            }
        }
        y_prev = y;

    }
    pout << "to left_pa file close\n";
    left_pa_file.close();
    d_left_pa_axis = 1;
    d_left_pa_side = 0;

    pout << "passed contstructor\n";

    return;
} // CirculationModel

CirculationModel_RV_PA::~CirculationModel_RV_PA()
{
    return;
} // ~CirculationModel_RV_PA

void CirculationModel_RV_PA::advanceTimeDependentData(const double dt,
                                                        const Pointer<PatchHierarchy<NDIM> > hierarchy,
                                                        const int U_idx,
                                                        const int /*P_idx*/,
                                                        const int /*wgt_cc_idx*/,
                                                        const int wgt_sc_idx)
{
    // Compute the mean flow rates in the vicinity of the inflow and outflow
    // boundaries.

    double Q_right_ventricle_local = 0.0;
    double Q_right_pa_local = 0.0;
    double Q_left_pa_local = 0.0;

    double area_right_ventricle_local = 0.0;
    double area_right_pa_local = 0.0;
    double area_left_pa_local = 0.0;

    if (d_inductor_bcs_on){
        // save old values of Q for taking time derivatives
        d_Q_right_pa_previous = d_Q_right_pa;
        d_Q_left_pa_previous = d_Q_left_pa;
    }

    for (int ln = 0; ln <= hierarchy->getFinestLevelNumber(); ++ln)
    {
        Pointer<PatchLevel<NDIM> > level = hierarchy->getPatchLevel(ln);
        for (PatchLevel<NDIM>::Iterator p(level); p; p++)
        {
            Pointer<Patch<NDIM> > patch = level->getPatch(p());
            Pointer<CartesianPatchGeometry<NDIM> > pgeom = patch->getPatchGeometry();
            if (pgeom->getTouchesRegularBoundary())
            {
                Pointer<SideData<NDIM, double> > U_data = patch->getPatchData(U_idx);
                Pointer<SideData<NDIM, double> > wgt_sc_data = patch->getPatchData(wgt_sc_idx);
                const Box<NDIM>& patch_box = patch->getBox();
                const double* const x_lower = pgeom->getXLower();
                const double* const dx = pgeom->getDx();
                double dV = 1.0;
                for (int d = 0; d < NDIM; ++d)
                {
                    dV *= dx[d];
                }

                for(int axis=0; axis<3; axis++)
                {
                    for(int side=0; side<2; side++)
                    {
                        const bool is_lower = (side == 0);
                        if (pgeom->getTouchesRegularBoundary(axis, side))
                        {

                            Vector n;
                            for (int d = 0; d < NDIM; ++d)
                            {
                                n[d] = axis == d ? (is_lower ? -1.0 : +1.0) : 0.0;
                            }
                            Box<NDIM> side_box = patch_box;
                            if (is_lower)
                            {
                                side_box.lower(axis) = patch_box.lower(axis);
                                side_box.upper(axis) = patch_box.lower(axis);
                            }
                            else
                            {
                                side_box.lower(axis) = patch_box.upper(axis) + 1;
                                side_box.upper(axis) = patch_box.upper(axis) + 1;
                            }
                            for (Box<NDIM>::Iterator b(side_box); b; b++)
                            {
                                const Index<NDIM>& i = b();

                                double X[NDIM];
                                for (int d = 0; d < NDIM; ++d)
                                {
                                    X[d] = x_lower[d] + dx[d] * (double(i(d) - patch_box.lower(d)) + (d == axis ? 0.0 : 0.5));
                                }

                                double X_in_plane_1 = 0.0;
                                double X_in_plane_2 = 0.0;
                                if (axis == 0)
                                {
                                    X_in_plane_1 = X[1];
                                    X_in_plane_2 = X[2];
                                }
                                else if (axis == 1)
                                {
                                    X_in_plane_1 = X[0];
                                    X_in_plane_2 = X[2];
                                }
                                else if (axis == 2)
                                {
                                    X_in_plane_1 = X[0];
                                    X_in_plane_2 = X[1];
                                }
                                else{
                                    TBOX_ERROR("Invalid value of axis\n");
                                }

                                const int in_right_ventricle  = this->point_in_right_ventricle(X_in_plane_1, X_in_plane_2, axis, side);
                                const int in_right_pa         = this->point_in_right_pa       (X_in_plane_1, X_in_plane_2, axis, side);
                                const int in_left_pa          = this->point_in_left_pa        (X_in_plane_1, X_in_plane_2, axis, side);

                                if (in_right_ventricle && in_right_pa){
                                    TBOX_ERROR("Position is within two inlets and outlets, should be impossible\n");
                                }
                                if (in_right_ventricle && in_left_pa){
                                    TBOX_ERROR("Position is within two inlets and outlets, should be impossible\n");
                                }
                                if (in_right_pa && in_left_pa){
                                    TBOX_ERROR("Position is within two inlets and outlets, should be impossible\n");
                                }

                                if (in_right_ventricle)
                                {
                                    const SideIndex<NDIM> i_s(i, axis, SideIndex<NDIM>::Lower);
                                    if ((*wgt_sc_data)(i_s) > std::numeric_limits<double>::epsilon())
                                    {
                                        double dA = dV / dx[axis];
                                        Q_right_ventricle_local += (*U_data)(i_s)* n[axis] * dA;

                                        if (!d_area_initialized){
                                            area_right_ventricle_local += dA;
                                        }

                                    }
                                }

                                if (in_right_pa)
                                {
                                    const SideIndex<NDIM> i_s(i, axis, SideIndex<NDIM>::Lower);
                                    if ((*wgt_sc_data)(i_s) > std::numeric_limits<double>::epsilon())
                                    {
                                        double dA = dV / dx[axis];
                                        Q_right_pa_local += (*U_data)(i_s) * n[axis] * dA;

                                        if (!d_area_initialized){
                                            area_right_pa_local += dA;
                                        }

                                    }
                                }

                                if (in_left_pa)
                                {
                                    const SideIndex<NDIM> i_s(i, axis, SideIndex<NDIM>::Lower);
                                    if ((*wgt_sc_data)(i_s) > std::numeric_limits<double>::epsilon())
                                    {
                                        double dA = dV / dx[axis];
                                        Q_left_pa_local += (*U_data)(i_s) * n[axis] * dA;

                                        if (!d_area_initialized){
                                            area_left_pa_local += dA;
                                        }

                                    }
                                }

                            }
                        }
                    }
                }
            }
        }
    }

    d_Q_right_ventricle = SAMRAI_MPI::sumReduction(Q_right_ventricle_local);
    d_Q_right_pa        = SAMRAI_MPI::sumReduction(Q_right_pa_local);
    d_Q_left_pa         = SAMRAI_MPI::sumReduction(Q_left_pa_local);

    if (!d_area_initialized){
        d_area_right_ventricle = SAMRAI_MPI::sumReduction(area_right_ventricle_local);
        d_area_right_pa        = SAMRAI_MPI::sumReduction(area_right_pa_local);
        d_area_left_pa         = SAMRAI_MPI::sumReduction(area_left_pa_local);
        d_area_initialized = true;
    }

    // print_summary();

    // bool debug_out_areas = false;
    // if (debug_out_areas){
    //     pout << "d_area_right_ventricle = " << d_area_right_ventricle << "\n";
    //     pout << "d_area_right_pa = " << d_area_right_pa << "\n";
    //     pout << "d_area_left_pa = " << d_area_left_pa << "\n";
    // }

    d_time += dt;

    // compute which index in the Fourier series we need here
    // always use a time in current cycle
    double t_reduced = d_time - d_cycle_duration * floor(d_time/d_cycle_duration);

    // fourier series has its own period, scale to that
    double t_scaled = t_reduced * (d_fourier_right_ventricle->L  / d_cycle_duration);

    // start offset some arbitrary time in the cardiac cycle, but this is relative to the series length
    double t_scaled_offset = t_scaled + d_t_offset_bcs_unscaled;

    // Fourier data here
    // index without periodicity
    unsigned int k = (unsigned int) floor(t_scaled_offset / (d_fourier_right_ventricle->dt));

    // // take periodic reduction
    d_current_idx_series = k % (d_fourier_right_ventricle->N_times);

    if (d_rcr_bcs_on){
        // The downstream pressure is determined by a three-element Windkessel model.

        double coeff_left = (d_left_pa_C / dt + 1.0 / d_left_pa_R_distal);
        double coeff_right = (d_right_pa_C / dt + 1.0 / d_right_pa_R_distal);

        // grab the downstream pressures
        d_right_pa_P_distal_previous = d_right_pa_P_distal;
        d_right_pa_P_distal = MMHG_TO_CGS * d_fourier_right_pa->values[d_current_idx_series];

        d_left_pa_P_distal_previous = d_left_pa_P_distal;
        d_left_pa_P_distal = MMHG_TO_CGS * d_fourier_left_pa->values[d_current_idx_series];

        // hooked to ground version
        // d_right_pa_P_Wk = ((d_right_pa_C / dt) * d_right_pa_P_Wk + d_Q_right_pa) / (d_right_pa_C / dt + 1.0 / d_right_pa_R_distal);
        // d_right_pa_P = d_right_pa_P_Wk + d_right_pa_R_proximal * d_Q_right_pa;

        // d_left_pa_P_Wk = ((d_left_pa_C / dt) * d_left_pa_P_Wk + d_Q_left_pa) / (d_left_pa_C / dt + 1.0 / d_left_pa_R_distal);
        // d_left_pa_P = d_left_pa_P_Wk + d_left_pa_R_proximal * d_Q_left_pa;

        d_right_pa_P_Wk = ((d_right_pa_C / dt) * (d_right_pa_P_Wk - d_right_pa_P_distal_previous) + coeff_right*d_right_pa_P_distal + d_Q_right_pa) / coeff_right;
        d_right_pa_P = d_right_pa_P_Wk + d_right_pa_R_proximal * d_Q_right_pa;

        d_left_pa_P_Wk = ((d_left_pa_C / dt) * (d_left_pa_P_Wk - d_left_pa_P_distal_previous) + coeff_right*d_left_pa_P_distal + d_Q_left_pa) / coeff_left;
        d_left_pa_P = d_left_pa_P_Wk + d_left_pa_R_proximal * d_Q_left_pa;

    }
    else if (d_resistance_bcs_on){
        // pressure upstream of resistance determined by series
        d_right_pa_P_Wk = MMHG_TO_CGS * d_fourier_right_pa->values[d_current_idx_series];
        d_left_pa_P_Wk  = MMHG_TO_CGS * d_fourier_left_pa->values[d_current_idx_series];

        // resistance bcs determine outlet pressure
        d_right_pa_P = d_right_pa_P_Wk + d_right_pa_resistance * d_Q_right_pa;
        d_left_pa_P  = d_left_pa_P_Wk  + d_left_pa_resistance  * d_Q_left_pa;
    }

    else if (d_inductor_bcs_on){
        // pressure upstream of resistance determined by series
        d_right_pa_P_Wk = MMHG_TO_CGS * d_fourier_right_pa->values[d_current_idx_series];
        d_left_pa_P_Wk  = MMHG_TO_CGS * d_fourier_left_pa->values[d_current_idx_series];

        // inductance bcs determine update on pressure
        d_right_pa_P = d_right_pa_P_Wk + d_right_pa_inductance * (d_Q_right_pa - d_Q_right_pa_previous)/dt;
        d_left_pa_P  = d_left_pa_P_Wk  + d_left_pa_inductance  * (d_Q_left_pa  - d_Q_left_pa_previous )/dt ;
    }

    else {
        d_right_pa_P = MMHG_TO_CGS * d_fourier_right_pa->values[d_current_idx_series];
        d_left_pa_P  = MMHG_TO_CGS * d_fourier_left_pa->values[d_current_idx_series];
    }

    // bool debug_out = false;
    // if (debug_out){
    //     pout << "circ mode: d_time = " << d_time << ", d_current_idx_series = " << d_current_idx_series << "\n";
    //     pout << "t_reduced = " << t_reduced << " t_scaled = " << t_scaled << " t_scaled_offset = " << t_scaled_offset << "\n";
    //     pout << "k (unreduced idx) = " << k << " d_current_idx_series = " << d_current_idx_series << "\n\n";
    // }

    writeDataFile();

} // advanceTimeDependentData

void CirculationModel_RV_PA::set_Q_valve(double Q_valve){
    d_Q_valve = Q_valve;
}

void
CirculationModel_RV_PA::putToDatabase(Pointer<Database> db)
{

    db->putInteger("d_current_idx_series", d_current_idx_series);
    db->putDouble("d_Q_right_ventricle", d_Q_right_ventricle);
    db->putDouble("d_Q_right_pa", d_Q_right_pa);
    db->putDouble("d_Q_left_pa", d_Q_left_pa);
    db->putDouble("d_Q_right_pa_previous", d_Q_right_pa_previous);
    db->putDouble("d_Q_left_pa_previous", d_Q_left_pa_previous);
    db->putDouble("d_Q_valve", d_Q_valve);
    db->putDouble("d_right_pa_P", d_right_pa_P);
    db->putDouble("d_right_pa_P_Wk", d_right_pa_P_Wk);
    db->putDouble("d_right_pa_P_distal", d_right_pa_P_distal);
    db->putDouble("d_right_pa_P_distal_previous", d_right_pa_P_distal_previous);
    db->putDouble("d_left_pa_P",d_left_pa_P);
    db->putDouble("d_left_pa_P_Wk",d_left_pa_P_Wk);
    db->putDouble("d_left_pa_P_distal", d_left_pa_P_distal);
    db->putDouble("d_left_pa_P_distal_previous", d_left_pa_P_distal_previous);
    db->putDouble("d_time", d_time);
    db->putBool("d_rcr_bcs_on", d_rcr_bcs_on);
    db->putBool("d_resistance_bcs_on", d_resistance_bcs_on);
    db->putBool("d_inductor_bcs_on", d_inductor_bcs_on);
    return;
} // putToDatabase

void CirculationModel_RV_PA::print_summary(){

    double P_right_ventricle = d_fourier_right_ventricle->values[d_current_idx_series];
    double P_right_pa;
    double P_left_pa;

    if (d_rcr_bcs_on){
        P_right_pa        = d_right_pa_P / MMHG_TO_CGS;
        P_left_pa         = d_left_pa_P / MMHG_TO_CGS;
    }
    else{
        P_right_pa        = d_fourier_right_pa->values[d_current_idx_series];
        P_left_pa         = d_fourier_left_pa->values[d_current_idx_series];
    }

    pout << "rcr_bcs_on = " << d_rcr_bcs_on << "\n";
    pout << "% time \t P_right_ventricle (mmHg)\t P_right_pa (mmHg)\t P_left_pa (mmHg)\t Q_right_ventricle (ml/s)\t d_Q_right_pa (ml/s)\t d_Q_right_pa (ml/s)\tQ_valve (ml/s) \t idx" ;
    if (d_rcr_bcs_on || d_resistance_bcs_on){
        pout << "\t right_pa_P_Wk\t left_pa_P_Wk\t ";
    }
    pout << "\n";
    pout << d_time << " " << P_right_ventricle <<  " " << P_right_pa << " " << P_left_pa << " " << d_Q_right_ventricle << " " << d_Q_right_pa << " " << d_Q_left_pa << " " << d_Q_valve << " " << d_current_idx_series;
    if (d_rcr_bcs_on || d_resistance_bcs_on){
        pout  << " " << d_right_pa_P_Wk << " " << d_left_pa_P_Wk;
    }
    pout << "\n";

}

int CirculationModel_RV_PA::point_in_right_ventricle(double testx, double testy, int axis, int side){
    // checks whether given point is in right ventricle

    // quick exit for correct side and axis
    if ((axis != d_right_ventricle_axis) || (side != d_right_ventricle_side))
        return 0;

    return pnpoly(d_n_pts_right_ventricle, d_right_ventricle_points_idx1, d_right_ventricle_points_idx2, testx, testy);
}

int CirculationModel_RV_PA::point_in_right_pa(double testx, double testy, int axis, int side){
    // checks whether given point is in right ventricle

    // quick exit for correct side and axis
    if ((axis != d_right_pa_axis) || (side != d_right_pa_side))
        return 0;

    return pnpoly(d_n_pts_right_pa, d_right_pa_points_idx1, d_right_pa_points_idx2, testx, testy);
}

int CirculationModel_RV_PA::point_in_left_pa(double testx, double testy, int axis, int side){
    // checks whether given point is in right ventricle

    // quick exit for correct side and axis
    if ((axis != d_left_pa_axis) || (side != d_left_pa_side))
        return 0;

    return pnpoly(d_n_pts_left_pa, d_left_pa_points_idx1, d_left_pa_points_idx2, testx, testy);
}

void CirculationModel_RV_PA::write_plot_code()
{
    static const int mpi_root = 0;
    if (SAMRAI_MPI::getRank() == mpi_root)
    {
        ofstream fout(DATA_FILE_NAME.c_str(), ios::app);
        fout.setf(ios_base::scientific);
        fout.setf(ios_base::showpos);
        fout.precision(10);

        fout << "];\n";
        fout << "MMHG_TO_CGS = 1333.22368;\n";
        fout << "fig = figure;\n";
        fout << "times   =  bc_vals(:,1);\n";
        fout << "p_rv    =  bc_vals(:,2);\n";
        fout << "p_rpa   =  bc_vals(:,3);\n";
        fout << "p_lpa   =  bc_vals(:,4);\n";
        fout << "q_rv    = -bc_vals(:,5); \n";
        fout << "q_rpa   =  bc_vals(:,6);\n";
        fout << "q_lpa   =  bc_vals(:,7);\n";
        fout << "q_valve =  bc_vals(:,8);\n";
        fout << "p_wk_rpa =  bc_vals(:,9);\n";
        fout << "p_wk_lpa =  bc_vals(:,10);\n";
        fout << "load '../bc_variables_experimental.mat'\n";
        fout << "subplot(2,1,1)\n";
        fout << "plot(times, p_rv, 'k')\n";
        fout << "hold on\n";
        fout << "plot(times, p_rpa, ':k')\n";
        fout << "plot(times, p_lpa, '-.k')\n";
        fout << "plot(times_exp, p_rv_exp)\n";
        fout << "plot(times_exp, p_pa_exp)\n";
        fout << "plot(times, p_wk_rpa)\n";
        fout << "plot(times, p_wk_lpa)\n";
        fout << "% legend('P RV', 'P RPA', 'PWK RPA', 'P LPA', 'PWK LPA', 'P EXP RV', 'P EXP PA', Location','NorthEastOutside');\n";
        fout << "legend('P RV', 'P RPA', 'P LPA', 'P EXP RV', 'P EXP PA', 'WK RPA', 'WK LPA', 'Location','NorthEastOutside');\n";
        fout << "xlabel('t (s)')\n";
        fout << "ylabel('P (mmHg)')\n";
        fout << "subplot(2,1,2)\n";
        fout << "plot(times, q_rv, 'k')\n";
        fout << "hold on\n";
        fout << "plot(times, q_rpa, '--k')\n";
        fout << "plot(times, q_lpa, '-.k')\n";
        fout << "plot(times_two_cycles, q_rv_exp)\n";
        fout << "plot(bc_vals(:,1), zeros(size(q_rv)), ':k')\n";
        fout << "legend('Q RV', 'Q RPA', 'Q LPA', 'Q EXP RV', 'Location', 'NorthEastOutside')\n";
        fout << "xlabel('t (s)')\n";
        fout << "ylabel('Flow (ml/s)')\n";
        fout << "set(fig, 'Position', [100, 100, 1000, 750])\n";
        fout << "set(fig,'PaperPositionMode','auto')\n";
        fout << "printfig(fig, 'bc_model_variables_with_experimental')\n";
        fout << "q_mean = mean(q_rv)\n";
    }
    return;
}

/////////////////////////////// PROTECTED ////////////////////////////////////

/////////////////////////////// PRIVATE //////////////////////////////////////

void
    CirculationModel_RV_PA::writeDataFile() const
{
    static const int mpi_root = 0;
    if (SAMRAI_MPI::getRank() == mpi_root)
    {
        static bool file_initialized = false;
        const bool from_restart = RestartManager::getManager()->isFromRestart();
        if (!from_restart && !file_initialized)
        {
            ofstream fout(DATA_FILE_NAME.c_str(), ios::out);
            fout << "% time \t P_right_ventricle (mmHg)\t P_right_pa (mmHg)\t P_left_pa (mmHg)\t d_Q_right_ventricle (ml/s)\t d_Q_right_pa (ml/s)\td_Q_left_pa (ml/s) \td_Q_valve (ml/s) \t d_right_pa_P_Wk \t d_left_pa_P_Wk";
            if (d_rcr_bcs_on){
                fout << "d_right_pa_P_distal \t d_left_pa_P_distal";
            }
            fout << "\n"
                 << "bc_vals = [";
            file_initialized = true;
        }

        ofstream fout(DATA_FILE_NAME.c_str(), ios::app);

        fout << d_time;
        fout.setf(ios_base::scientific);
        fout.setf(ios_base::showpos);
        fout.precision(10);

        double P_right_ventricle = d_fourier_right_ventricle->values[d_current_idx_series];

        fout << " " << P_right_ventricle <<  " " << d_right_pa_P/MMHG_TO_CGS << " " << d_left_pa_P/MMHG_TO_CGS;
        fout << " " << d_Q_right_ventricle << " " << d_Q_right_pa << " " << d_Q_left_pa << " " << d_Q_valve;
        fout << " " << d_right_pa_P_Wk/MMHG_TO_CGS << " " << d_left_pa_P_Wk/MMHG_TO_CGS;
        if(d_rcr_bcs_on){
            fout << " " << d_right_pa_P_distal/MMHG_TO_CGS << " " << d_left_pa_P_distal/MMHG_TO_CGS;
        }
        fout << "; \n";

    }

    return;
} // writeDataFile

void
CirculationModel_RV_PA::getFromRestart()
{
    Pointer<Database> restart_db = RestartManager::getManager()->getRootDatabase();
    Pointer<Database> db;
    if (restart_db->isDatabase(d_object_name))
    {
        db = restart_db->getDatabase(d_object_name);
    }
    else
    {
        TBOX_ERROR("Restart database corresponding to " << d_object_name << " not found in restart file.");
    }

    d_current_idx_series         = db->getInteger("d_current_idx_series");
    d_Q_right_ventricle          = db->getDouble("d_Q_right_ventricle");
    d_Q_right_pa                 = db->getDouble("d_Q_right_pa");
    d_Q_left_pa                  = db->getDouble("d_Q_left_pa");
    d_Q_right_pa_previous        = db->getDouble("d_Q_right_pa_previous");
    d_Q_left_pa_previous         = db->getDouble("d_Q_left_pa_previous");
    d_Q_valve                    = db->getDouble("d_Q_valve");
    d_right_pa_P                 = db->getDouble("d_right_pa_P");
    d_right_pa_P_Wk              = db->getDouble("d_right_pa_P_Wk");
    d_right_pa_P_distal          = db->getDouble("d_right_pa_P_distal");
    d_right_pa_P_distal_previous = db->getDouble("d_right_pa_P_distal_previous");
    d_left_pa_P                  = db->getDouble("d_left_pa_P");
    d_left_pa_P_Wk               = db->getDouble("d_left_pa_P_Wk");
    d_left_pa_P_distal           = db->getDouble("d_left_pa_P_distal");
    d_left_pa_P_distal_previous  = db->getDouble("d_left_pa_P_distal_previous");
    d_time                       = db->getDouble("d_time");
    d_rcr_bcs_on                 = db->getBool("d_rcr_bcs_on");
    d_resistance_bcs_on          = db->getBool("d_resistance_bcs_on");
    d_inductor_bcs_on            = db->getBool("d_inductor_bcs_on");
    return;
} // getFromRestart

/////////////////////////////// NAMESPACE ////////////////////////////////////

/////////////////////////////// TEMPLATE INSTANTIATION ///////////////////////

//////////////////////////////////////////////////////////////////////////////


#include <mesh_array_io_tetgen.h>

#include <tiny.h>

#include <boost/filesystem.hpp>

#include <cassert>
#include <fstream>

namespace mesh_array
{

  template<typename T>
  void read_tetgen(
                   std::string const & filename
                   , T3Mesh & mesh
                   , VertexAttribute<T,T3Mesh> & X
                   , VertexAttribute<T,T3Mesh> & Y
                   , VertexAttribute<T,T3Mesh> & Z
                   )
  {
    boost::filesystem::path  mypath(filename);

    std::string const ext = boost::filesystem::extension( mypath );

    assert( ext == "" || !"read_tetgen(): illegal file name extension");

    std::string const filename1 = filename + ".node";
    std::string const filename2 = filename + ".face";

    std::ifstream file1(filename1.c_str());
    std::ifstream file2(filename2.c_str());

    assert (file1.is_open() || !"read_tetgen(): unable ot open file");
    assert (file2.is_open() || !"read_tetgen(): unable ot open file");

    size_t cnt_vertices;    // Total number of vertices
    size_t dimension;       // The dimension of the coordinate space
    size_t cnt_attributes;  // The number of attributes for each vertex
    size_t cnt_triangles;   // Total number of triangles
    size_t cnt_points;      // The number of points per triangle (must be 3)
    bool has_boundary;      // Boundary marker (if used)

    file1 >> cnt_vertices  >> dimension  >> cnt_attributes >> has_boundary;
    file2 >> cnt_triangles >> cnt_points >> cnt_attributes;

    assert (cnt_vertices > 0u    || !"read_tetgen(): unsupported format");
    assert (dimension == 3u      || !"read_tetgen(): unsupported format");
    assert (cnt_attributes == 0u || !"read_tetgen(): unsupported format");
    assert (cnt_triangles > 0u   || !"read_tetgen(): unsupported format");
    assert (cnt_points == 3u     || !"read_tetgen(): unsupported format");

    mesh.clear();

    mesh.set_capacity(cnt_vertices, cnt_triangles);

    X.bind(mesh);
    Y.bind(mesh);
    Z.bind(mesh);

    std::vector<Vertex> vertices (cnt_vertices);

    for(size_t idx=0u; idx<cnt_vertices; ++idx)
    {
      size_t external_idx;

      T x;
      T y;
      T z;

      file1 >> external_idx >> x >> y >> z;

      assert( external_idx < cnt_vertices || !"read_tetgen(): input error");

      assert( is_number(x) || !"read_tetgen(): x was nan");
      assert( is_finite(x) || !"read_tetgen(): x was infinity");
      assert( is_number(y) || !"read_tetgen(): y was nan");
      assert( is_finite(y) || !"read_tetgen(): y was infinity");
      assert( is_number(z) || !"read_tetgen(): z was nan");
      assert( is_finite(z) || !"read_tetgen(): z was infinity");

      Vertex v = mesh.push_vertex();

      X(v) = x;
      Y(v) = y;
      Z(v) = z;

      vertices[external_idx] = v;
    }

    for(size_t idx=0u; idx<cnt_triangles; ++idx)
    {
      size_t external_idx;

      size_t i;   // external index of node i
      size_t j;   // external index of node j
      size_t k;   // external index of node k

      file2 >> external_idx >> i >> j >> k;

      assert( external_idx < cnt_triangles || !"read_tetgen(): input error");
      assert( i < cnt_vertices             || !"read_tetgen(): input error");
      assert( j < cnt_vertices             || !"read_tetgen(): input error");
      assert( k < cnt_vertices             || !"read_tetgen(): input error");

      mesh.push_triangle(
                         vertices[i]
                         , vertices[j]
                         , vertices[k]
                         );
    }

    file1.close();
    file2.close();
  }

  template
  void read_tetgen<float>(
                          std::string const & filename
                          , T3Mesh & mesh
                          , VertexAttribute<float,T3Mesh> & X
                          , VertexAttribute<float,T3Mesh> & Y
                          , VertexAttribute<float,T3Mesh> & Z
                          );

  template
  void read_tetgen<double>(
                           std::string const & filename
                           , T3Mesh & mesh
                           , VertexAttribute<double,T3Mesh> & X
                           , VertexAttribute<double,T3Mesh> & Y
                           , VertexAttribute<double,T3Mesh> & Z
                           );

  template<typename T>
  void read_tetgen(
                   std::string const & filename
                   , T4Mesh & mesh
                   , VertexAttribute<T,T4Mesh> & X
                   , VertexAttribute<T,T4Mesh> & Y
                   , VertexAttribute<T,T4Mesh> & Z
                   )
  {
    boost::filesystem::path  mypath(filename);

    std::string const ext = boost::filesystem::extension( mypath );

    assert( ext == "" || !"read_tetgen(): illegal file name extension");

    std::string const filename1 = filename + ".node";
    std::string const filename2 = filename + ".ele";

    std::ifstream file1(filename1.c_str());
    std::ifstream file2(filename2.c_str());

    assert (file1.is_open() || !"read_tetgen(): unable to open file");
    assert (file2.is_open() || !"read_tetgen(): unable to open file");

    size_t cnt_vertices;     // Total number of vertices
    size_t dimension;        // The number of coordinates per vertex
    size_t cnt_attributes;   // The number of attributes per vertex
    size_t cnt_tetrahedra;   // Total number of tetrahedra
    size_t cnt_points;       // The number of vertices per tetrahedra (always 4)
    bool has_boundary;       // Boundary marker

    file1 >> cnt_vertices   >> dimension  >> cnt_attributes >> has_boundary;
    file2 >> cnt_tetrahedra >> cnt_points >> cnt_attributes;

    assert (cnt_vertices > 0u    || !"read_tetgen(): unsupported format");
    assert (dimension == 3u      || !"read_tetgen(): unsupported format");
    assert (cnt_attributes == 0u || !"read_tetgen(): unsupported format");
    assert (cnt_tetrahedra > 0u  || !"read_tetgen(): unsupported format");
    assert (cnt_points == 4u     || !"read_tetgen(): unsupported format");

    mesh.clear();

    mesh.set_capacity(cnt_vertices, cnt_tetrahedra);

    X.bind(mesh);
    Y.bind(mesh);
    Z.bind(mesh);

    std::vector<Vertex> vertices (cnt_vertices);

    for(size_t idx=0u; idx<cnt_vertices; ++idx)
    {
      size_t external_idx;

      T x;
      T y;
      T z;

      file1 >> external_idx >> x >> y >> z;

      assert( external_idx < cnt_vertices || !"read_tetgen(): input error");

      assert( is_number(x) || !"read_tetgen(): x was nan");
      assert( is_finite(x) || !"read_tetgen(): x was infinity");
      assert( is_number(y) || !"read_tetgen(): y was nan");
      assert( is_finite(y) || !"read_tetgen(): y was infinity");
      assert( is_number(z) || !"read_tetgen(): z was nan");
      assert( is_finite(z) || !"read_tetgen(): z was infinity");

      Vertex v = mesh.push_vertex();

      X(v) = x;
      Y(v) = y;
      Z(v) = z;

      vertices[external_idx] = v;
    }

    for(size_t idx=0u; idx<cnt_tetrahedra; ++idx)
    {
      size_t external_idx;

      size_t i;  // external index of node i
      size_t j;  // external index of node j
      size_t k;  // external index of node k
      size_t m;  // external index of node m

      file2 >> external_idx >> i >> j >> k >> m;

      assert( external_idx < cnt_tetrahedra || !"read_tetgen(): input error");
      assert( i < cnt_vertices              || !"read_tetgen(): input error");
      assert( j < cnt_vertices              || !"read_tetgen(): input error");
      assert( k < cnt_vertices              || !"read_tetgen(): input error");
      assert( m < cnt_vertices              || !"read_tetgen(): input error");

      mesh.push_tetrahedron(
                            vertices[i]
                            , vertices[j]
                            , vertices[k]
                            , vertices[m]
                            );
    }

    file1.close();
    file2.close();
  }

  template
  void read_tetgen<float>(
                          std::string const & filename
                          , T4Mesh & mesh
                          , VertexAttribute<float,T4Mesh> & X
                          , VertexAttribute<float,T4Mesh> & Y
                          , VertexAttribute<float,T4Mesh> & Z
                          );

  template
  void read_tetgen<double>(
                           std::string const & filename
                           , T4Mesh & mesh
                           , VertexAttribute<double,T4Mesh> & X
                           , VertexAttribute<double,T4Mesh> & Y
                           , VertexAttribute<double,T4Mesh> & Z
                           );

  template<typename T>
  void write_tetgen(
                    std::string const & filename
                    , T3Mesh const & mesh
                    , VertexAttribute<T,T3Mesh> const & X
                    , VertexAttribute<T,T3Mesh> const & Y
                    , VertexAttribute<T,T3Mesh> const & Z
                    )
  {
    boost::filesystem::path  mypath(filename);

    std::string const ext = boost::filesystem::extension( mypath );

    assert( ext == "" || !"write_tetgen(): illegal file name extension");

    std::string const filename1 = filename + ".node";
    std::string const filename2 = filename + ".face";

    std::ofstream file1(filename1.c_str());
    std::ofstream file2(filename2.c_str());

    assert( file1.is_open() || !"write_tetgen(): could not open file");
    assert( file2.is_open() || !"write_tetgen(): could not open file");

    file1.precision(30);
    file2.precision(30);

    size_t const cnt_nodes     = mesh.vertex_size();
    size_t const cnt_triangles = mesh.triangle_size();

    file1 << cnt_nodes
    << " "
    << 3u
    << " "
    << 0u
    << " "
    << 0u
    << std::endl;

    file2 << cnt_triangles
    << " "
    << 3u
    << " "
    << 0u
    << std::endl;

    for(size_t idx=0u; idx<cnt_nodes; ++idx)
    {
      Vertex v = mesh.vertex(idx);

      file1 << "  "
      << v.idx()
      << "  "
      << X(v)
      << " "
      << Y(v)
      << " "
      << Z(v)
      << std::endl;
    }

    for(size_t idx=0u; idx<cnt_triangles; ++idx)
    {
      Triangle t = mesh.triangle(idx);

      file2 << t.idx()
      << "  "
      << t.i()
      << " "
      << t.j()
      << " "
      << t.k()
      << std::endl;
    }

    file1.flush();
    file2.flush();
    file1.close();
    file2.close();
  }

  template
  void write_tetgen<float>(
                           std::string const & filename
                           , T3Mesh const & mesh
                           , VertexAttribute<float,T3Mesh> const & X
                           , VertexAttribute<float,T3Mesh> const & Y
                           , VertexAttribute<float,T3Mesh> const & Z
                           );

  template
  void write_tetgen<double>(
                            std::string const & filename
                            , T3Mesh const & mesh
                            , VertexAttribute<double,T3Mesh> const & X
                            , VertexAttribute<double,T3Mesh> const & Y
                            , VertexAttribute<double,T3Mesh> const & Z
                            );

  template<typename T>
  void write_tetgen(
                    std::string const & filename
                    , T4Mesh const & mesh
                    , VertexAttribute<T,T4Mesh> const & X
                    , VertexAttribute<T,T4Mesh> const & Y
                    , VertexAttribute<T,T4Mesh> const & Z
                    )
  {
    boost::filesystem::path  mypath(filename);

    std::string const ext = boost::filesystem::extension( mypath );

    assert( ext == "" || !"write_tetgen(): illegal file name extension");

    std::string const filename1 = filename + ".node";
    std::string const filename2 = filename + ".ele";

    std::ofstream file1(filename1.c_str());
    std::ofstream file2(filename2.c_str());

    assert(file1.is_open() || !"write_tetgen(): could not open file");
    assert(file2.is_open() || !"write_tetgen(): could not open file");

    file1.precision(30);
    file2.precision(30);

    size_t const cnt_nodes       = mesh.vertex_size();
    size_t const cnt_tetrahedron = mesh.tetrahedron_size();

    file1 << cnt_nodes
    << " "
    << 3u
    << " "
    << 0u
    << " "
    << 0u
    << std::endl;

    file2 << cnt_tetrahedron
    << " "
    << 4u
    << " "
    << 0u
    << std::endl;

    for(size_t idx=0u; idx<cnt_nodes; ++idx)
    {
      Vertex v = mesh.vertex(idx);

      file1 << "  "
      << v.idx()
      << "  "
      << X(v)
      << " "
      << Y(v)
      << " "
      << Z(v)
      << std::endl;
    }

    for(size_t idx=0u; idx<cnt_tetrahedron; ++idx)
    {
      Tetrahedron t = mesh.tetrahedron(idx);

      file2 << t.idx()
      << "  "
      << t.i()
      << " "
      << t.j()
      << " "
      << t.k()
      << " "
      << t.m()
      << std::endl;
    }

    file1.flush();
    file2.flush();
    file1.close();
    file2.close();
  }

  template
  void write_tetgen<float>(
                           std::string const & filename
                           , T4Mesh const & mesh
                           , VertexAttribute<float,T4Mesh> const & X
                           , VertexAttribute<float,T4Mesh> const & Y
                           , VertexAttribute<float,T4Mesh> const & Z
                           );

  template
  void write_tetgen<double>(
                            std::string const & filename
                            , T4Mesh const & mesh
                            , VertexAttribute<double,T4Mesh> const & X
                            , VertexAttribute<double,T4Mesh> const & Y
                            , VertexAttribute<double,T4Mesh> const & Z
                            );

} // end namespace mesh_array


#include "hamming.h"
#include <stdexcept>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(no_difference_between_identical_strands)
{
    BOOST_REQUIRE_EQUAL(0, hamming::compute("A", "A"));
}

#if defined(EXERCISM_RUN_ALL_TESTS)
BOOST_AUTO_TEST_CASE(complete_hamming_distance_for_single_nucleotide_strand)
{
    BOOST_REQUIRE_EQUAL(1, hamming::compute("A", "G"));
}

BOOST_AUTO_TEST_CASE(complete_hamming_distance_for_small_strand)
{
    BOOST_REQUIRE_EQUAL(2, hamming::compute("AG", "CT"));
}

BOOST_AUTO_TEST_CASE(small_hamming_distance)
{
    BOOST_REQUIRE_EQUAL(1, hamming::compute("AT", "CT"));
}

BOOST_AUTO_TEST_CASE(small_hamming_distance_in_longer_strand)
{
    BOOST_REQUIRE_EQUAL(1, hamming::compute("GGACG", "GGTCG"));
}

BOOST_AUTO_TEST_CASE(domain_error_when_first_string_is_longer)
{
    BOOST_REQUIRE_THROW(hamming::compute("AAAG", "AAA"), std::domain_error);
}

BOOST_AUTO_TEST_CASE(domain_error_when_second_string_is_longer)
{
    BOOST_REQUIRE_THROW(hamming::compute("AAA", "AAAG"), std::domain_error);
}

BOOST_AUTO_TEST_CASE(large_hamming_distance)
{
    BOOST_REQUIRE_EQUAL(4, hamming::compute("GATACA", "GCATAA"));
}

BOOST_AUTO_TEST_CASE(hamming_distance_in_very_long_strand)
{
    BOOST_REQUIRE_EQUAL(9, hamming::compute("GGACGGATTCTG", "AGGACGGATTCT"));
}
#endif


// Copyright (C) 2012-2013 Vicente Botet
//
//  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)

#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_EXECUTORS
#define BOOST_THREAD_USES_LOG_THREAD_ID
#define BOOST_THREAD_QUEUE_DEPRECATE_OLD
//#ifndef BOOST_NO_CXX11_DECLTYPE_N3276
//#define BOOST_THREAD_NO_CXX11_DECLTYPE_N3276
//#endif

#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/future.hpp>
#if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)

#include <numeric>
#include <algorithm>
#include <functional>
#include <iostream>
#include <list>

template<typename T>
struct sorter
{
    boost::basic_thread_pool pool;
    typedef std::list<T> return_type;

    std::list<T> do_sort(std::list<T> chunk_data)
    {
        if(chunk_data.empty())
        {
            return chunk_data;
        }

        std::list<T> result;
        result.splice(result.begin(),chunk_data, chunk_data.begin());
        T const& partition_val=*result.begin();

        typename std::list<T>::iterator divide_point=
            std::partition(chunk_data.begin(), chunk_data.end(), [&](T const& val){return val<partition_val;});

        std::list<T> new_lower_chunk;
        new_lower_chunk.splice(new_lower_chunk.end(), chunk_data, chunk_data.begin(), divide_point);

        boost::future<std::list<T> > new_lower = boost::async(pool, &sorter::do_sort, this, std::move(new_lower_chunk));
        //boost::future<std::list<T> > new_lower = boost::async<return_type>(pool, &sorter::do_sort, this, std::move(new_lower_chunk));

        std::list<T> new_higher(do_sort(chunk_data));

        result.splice(result.end(),new_higher);
        while(!new_lower.is_ready())
        {
            pool.schedule_one_or_yield();
        }

        result.splice(result.begin(),new_lower.get());
        return result;
    }
};

template<typename T>
std::list<T> parallel_quick_sort(std::list<T>& input)
{
    if(input.empty())
    {
        return input;
    }
    sorter<T> s;

    return s.do_sort(input);
}

int main()
{
  try
  {
    const int s = 101;
    std::list<int> lst;
    for (int i=0; i<s;++i)
      lst.push_back(100-i);
    std::list<int> r = parallel_quick_sort(lst);
    for (std::list<int>::const_iterator it=r.begin(); it != r.end(); ++it)
      std::cout << *it << std::endl;

  }
  catch (std::exception& ex)
  {
    std::cout << "ERROR= " << ex.what() << "" << std::endl;
    return 1;
  }
  catch (...)
  {
    std::cout << " ERROR= exception thrown" << std::endl;
    return 2;
  }
  return 0;
}
#else
//#warning "This compiler doesn't supports variadics and move semantics"
int main()
{
  return 0;
}
#endif


#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <libsocket/inetclientstream.hpp>
#include <unistd.h>

using namespace std;

void read_file(string filename, stringstream &buffer){
  std::ifstream file(filename.c_str());
  if ( file )
  {
    buffer << file.rdbuf();
    file.close();
  }
}

void notify(const string& msg) {
  try {
    libsocket::inet_stream sock("localhost", "9001", LIBSOCKET_IPv4);
    sock << msg;
  } catch (...) {
    // standalone usage
  }
}

int main()
{
  stringstream text;
  read_file("/tmp/1.json", text);

  stringstream ostr;
  ostr << "C++ Boost\t" << getpid();
  notify(ostr.str());

  boost::property_tree::ptree jobj;
  boost::property_tree::read_json(text, jobj);
  double x = 0, y = 0, z = 0;
  int len = 0;

  BOOST_FOREACH(boost::property_tree::ptree::value_type &coord, jobj.get_child("coordinates"))
  {
    len += 1;
    x += coord.second.get<double>("x");
    y += coord.second.get<double>("y");
    z += coord.second.get<double>("z");
  }

  printf("%.8f\n", x / len);
  printf("%.8f\n", y / len);
  printf("%.8f\n", z / len);

  notify("stop");
  return 0;
}


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

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

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

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

#include <boost/assign.hpp>
// clang-format off
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
// clang-format on
#include <boost/variant.hpp>

#include <test/capfloormarketdata.hpp>
#include <test/toplevelfixture.hpp>

#include <ql/math/interpolations/backwardflatinterpolation.hpp>
#include <ql/quotes/simplequote.hpp>
#include <qle/math/flatextrapolation.hpp>
#include <qle/termstructures/capfloortermvolcurve.hpp>

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

namespace bdata = boost::unit_test::data;
using boost::assign::list_of;
using boost::assign::map_list_of;
using std::boolalpha;
using std::fixed;
using std::map;
using std::ostream;
using std::pair;
using std::setprecision;
using std::string;
using std::vector;

namespace {

// Variables to be used in the test
struct CommonVars : public qle::test::TopLevelFixture {

    // Constructor
    CommonVars()
        : referenceDate(5, Feb, 2016), settlementDays(0), calendar(TARGET()), bdc(Following),
          dayCounter(Actual365Fixed()), tolerance(1.0e-12) {

        // Reference date
        Settings::instance().evaluationDate() = referenceDate;

        // Test cap floor data
        CapFloorVolatilityEUR testData;
        tenors = testData.atmTenors;
        volQuotes.resize(tenors.size());
        volHandles.resize(tenors.size());
        for (Size i = 0; i < tenors.size(); ++i) {
            volQuotes[i] = boost::make_shared<SimpleQuote>(testData.nAtmVols[i]);
            volHandles[i] = Handle<Quote>(volQuotes[i]);
        }
    }

    // Valuation date for the test
    Date referenceDate;

    // Variables used in the optionlet volatility structure creation
    Natural settlementDays;
    Calendar calendar;
    BusinessDayConvention bdc;
    DayCounter dayCounter;

    // Test tolerance for comparing the NPVs
    Real tolerance;

    // Cap floor test data
    vector<Period> tenors;
    vector<boost::shared_ptr<SimpleQuote> > volQuotes;
    vector<Handle<Quote> > volHandles;
};

// Interpolation types for the data driven test case
typedef boost::variant<Linear, BackwardFlat, QuantExt::LinearFlat, Cubic, QuantExt::CubicFlat> InterpolationType;

vector<InterpolationType> interpolationTypes = list_of(InterpolationType(Linear()))(InterpolationType(BackwardFlat()))(
    InterpolationType(QuantExt::LinearFlat()))(InterpolationType(Cubic()))(InterpolationType(QuantExt::CubicFlat()));

// If the cap floor term volatility curve in the test has a floating or fixed reference date
vector<bool> isMovingValues = list_of(true)(false);

// If the cap floor term volatility curve has a flat first period or not
vector<bool> flatFirstPeriodValues = list_of(true)(false);

// So that I can reuse below
string to_string(const InterpolationType& interpolationType) {
    string result;
    switch (interpolationType.which()) {
    case 0:
        result = "Linear";
        break;
    case 1:
        result = "BackwardFlat";
        break;
    case 2:
        result = "LinearFlat";
        break;
    case 3:
        result = "Cubic";
        break;
    case 4:
        result = "CubicFlat";
        break;
    default:
        BOOST_FAIL("Unexpected interpolation type");
    }
    return result;
}

} // namespace

// Needed for BOOST_DATA_TEST_CASE below
// https://stackoverflow.com/a/33965517/1771882
namespace boost {
namespace test_tools {
namespace tt_detail {
template <> struct print_log_value<InterpolationType> {
    void operator()(ostream& os, const InterpolationType& interpolationType) { os << to_string(interpolationType); }
};
} // namespace tt_detail
} // namespace test_tools
} // namespace boost

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

BOOST_AUTO_TEST_SUITE(CapFloorTermVolCurveTests)

BOOST_DATA_TEST_CASE_F(CommonVars, testCapFloorTermVolCurveInterpolation,
                       bdata::make(interpolationTypes) * bdata::make(isMovingValues) *
                           bdata::make(flatFirstPeriodValues),
                       interpolationType, isMoving, flatFirstPeriod) {

    BOOST_TEST_MESSAGE("Testing cap floor term volatility curve with different interpolation methods");

    BOOST_TEST_MESSAGE("Test inputs are:");
    BOOST_TEST_MESSAGE("  Interpolation type: " << to_string(interpolationType));
    BOOST_TEST_MESSAGE("  Floating reference date: " << boolalpha << isMoving);
    BOOST_TEST_MESSAGE("  Flat first period: " << boolalpha << flatFirstPeriod);

    // Create the CapFloorTermVolatilityStructure using the appropriate interpolation and refernce date/settlement days
    boost::shared_ptr<CapFloorTermVolatilityStructure> cftvs;
    switch (interpolationType.which()) {
    case 0:
        if (isMoving) {
            BOOST_TEST_MESSAGE("Using Linear interpolation with a moving reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
                                       settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        } else {
            BOOST_TEST_MESSAGE("Using Linear interpolation with a fixed reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<Linear> >(
                                       referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        }
        break;
    case 1:
        if (isMoving) {
            BOOST_TEST_MESSAGE("Using BackwardFlat interpolation with a moving reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<BackwardFlat> >(
                                       settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        } else {
            BOOST_TEST_MESSAGE("Using BackwardFlat interpolation with a fixed reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<BackwardFlat> >(
                                       referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        }
        break;
    case 2:
        if (isMoving) {
            BOOST_TEST_MESSAGE("Using LinearFlat interpolation with a moving reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<LinearFlat> >(
                                       settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        } else {
            BOOST_TEST_MESSAGE("Using LinearFlat interpolation with a fixed reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<LinearFlat> >(
                                       referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        }
        break;
    case 3:
        if (isMoving) {
            BOOST_TEST_MESSAGE("Using Cubic interpolation with a moving reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
                                       settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        } else {
            BOOST_TEST_MESSAGE("Using Cubic interpolation with a fixed reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<Cubic> >(
                                       referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        }
        break;
    case 4:
        if (isMoving) {
            BOOST_TEST_MESSAGE("Using CubicFlat interpolation with a moving reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<CubicFlat> >(
                                       settlementDays, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        } else {
            BOOST_TEST_MESSAGE("Using CubicFlat interpolation with a fixed reference date");
            BOOST_REQUIRE_NO_THROW(cftvs = boost::make_shared<InterpolatedCapFloorTermVolCurve<CubicFlat> >(
                                       referenceDate, calendar, bdc, tenors, volHandles, dayCounter, flatFirstPeriod));
        }
        break;
    default:
        BOOST_FAIL("Unexpected interpolation type");
    }

    BOOST_TEST_MESSAGE("Test the initial curve dates");
    for (Size i = 0; i < tenors.size(); ++i) {
        Date curveDate = cftvs->optionDateFromTenor(tenors[i]);
        Date manualDate = calendar.advance(referenceDate, tenors[i], bdc);
        BOOST_CHECK_EQUAL(curveDate, manualDate);
    }

    BOOST_TEST_MESSAGE("Test that curve returns input values on pillars");
    for (Size i = 0; i < tenors.size(); ++i) {
        Volatility vol = cftvs->volatility(tenors[i], 0.01);
        BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
    }

    // Bump the 5Y ATM volatility quote (3rd element in quote vector)
    Size idx = 2;
    Volatility bump = 0.0005;
    Volatility baseValue = volQuotes[idx]->value();
    volQuotes[idx]->setValue(baseValue + bump);

    BOOST_TEST_MESSAGE("Test that curve returns input values on pillars after bump");
    for (Size i = 0; i < tenors.size(); ++i) {
        Volatility vol = cftvs->volatility(tenors[i], 0.01);
        BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
        // Also check the point was bumped
        if (i == idx) {
            BOOST_CHECK_SMALL(vol - baseValue - bump, tolerance);
        }
    }

    BOOST_TEST_MESSAGE("Test the curve dates after moving the evaluation date");
    Date manualDate;
    Date newDate = calendar.advance(referenceDate, 1 * Months);
    Settings::instance().evaluationDate() = newDate;
    for (Size i = 0; i < tenors.size(); ++i) {
        Date curveDate = cftvs->optionDateFromTenor(tenors[i]);
        manualDate =
            isMoving ? calendar.advance(newDate, tenors[i], bdc) : calendar.advance(referenceDate, tenors[i], bdc);
        BOOST_CHECK_EQUAL(curveDate, manualDate);
    }

    BOOST_TEST_MESSAGE("Test that curve returns input values after moving the evaluation date");
    for (Size i = 0; i < tenors.size(); ++i) {
        Volatility vol = cftvs->volatility(tenors[i], 0.01);
        BOOST_CHECK_SMALL(volQuotes[i]->value() - vol, tolerance);
    }

    // Reset the evaluation date
    Settings::instance().evaluationDate() = referenceDate;

    BOOST_TEST_MESSAGE("Test extrapolation settings with out of range date");
    Date oorDate = cftvs->maxDate() + 1 * Months;
    BOOST_CHECK_NO_THROW(cftvs->volatility(oorDate, 0.01, true));
    BOOST_CHECK_THROW(cftvs->volatility(oorDate, 0.01), Error);
    cftvs->enableExtrapolation();
    BOOST_CHECK_NO_THROW(cftvs->volatility(oorDate, 0.01));
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


/*
 * This file is part of the RingDecomposerLib, licensed
 * under BSD New license (see LICENSE in the root directory).
 * Copyright (c) 2016
 * University of Hamburg, ZBH - Center for Bioinformatics
 * Niek Andresen, Florian Flachsenberg, Matthias Rarey
 *
 * Please cite:
 *
 * Kolodzik, A.; Urbaczek, S.; Rarey, M.
 * Unique Ring Families: A Chemically Meaningful Description
 * of Molecular Ring Topologies.
 * J. Chem. Inf. Model., 2012, 52 (8), pp 2013-2021
 *
 * Flachsenberg, F.; Andresen, N.; Rarey, M.
 * RingDecomposerLib: An Open-Source Implementation of
 * Unique Ring Families and Other Cycle Bases.
 * J. Chem. Inf. Model., 2017, 57 (2), pp 122-126
 */

/**
 * @file
 *
 * @brief Runtime bechmark tool for the UniqueRingFamily library and RDKit
 *
 * Start the tool with
 *
 *     RDKitURF -i <filename> -r <rumber of repeats> [--rc]
 *
 * The input file must be in SMILES, SDF or DIMCAS format.
 * Number of repeats specifies the number of times the benchmark
 * is executed. The last parameter is an optional flag.
 * It enables benchmarking of RC calculation (as long as the
 * number is less than 4e6.
 *
 * It outputs some molecule characteristics (number of
 * bonds, atoms, relevant cycles and URFs) as well
 * as the mean runtime for a number of experiments.
 */

#include <algorithm>
#include <chrono>
#include <fstream>
#include <functional>
#include <iostream>

#include <boost/program_options.hpp>

#include <GraphMol/FileParsers/FileParsers.h>
#include <GraphMol/FileParsers/MolSupplier.h>
#include <GraphMol/BondIterators.h>
#include <GraphMol/RingInfo.h>
#include <GraphMol/BondIterators.h>

namespace RingDecomposerLib {
#include "RingDecomposerLib.h"
#include "RDLdimacs.h"
}

namespace options = boost::program_options;

options::variables_map parseCommandLine(int argc, char **argv) {
  options::options_description desc("Allowed options");
  desc.add_options()
      ("input,i", options::value<std::string>(),
          "Input file with molecules.")
      ("repeats,r", options::value<unsigned>(),
          "Number of benchmark repeats")
      ("rc", "calculate relevant cycles")
      ("debug", "Debug output")
      ("help", "show this help");

  options::variables_map parsedMap;
  options::store(options::command_line_parser(argc, argv).
                 options(desc).run(), parsedMap);
  options::notify(parsedMap);

  if (parsedMap.count("help")) {
    std::cout << desc << "\n";
    exit(0);
  }

  if (!parsedMap.count("input")) {
    std::cout << desc << "\n";
    exit(1);
  }

  if (!parsedMap.count("repeats")) {
    std::cout << desc << "\n";
    exit(1);
  }

  return parsedMap;
}

template <typename T>
struct graph_trait {};

// trait for converting RDKit molecule
template<>
struct graph_trait<const RDKit::ROMol> {
    static std::string get_name(const RDKit::ROMol& mol)
    {
      std::string name = "NO_NAME";

      if (mol.hasProp("_Name")) {
        mol.getProp("_Name", name);
      }

      return name;
    }

    static size_t get_nof_atoms(const RDKit::ROMol& mol)
    {
      return mol.getNumAtoms();
    }

    static size_t get_nof_bonds(const RDKit::ROMol& mol)
    {
      return mol.getNumBonds();
    }

    static RDKit::ConstBondIterator_ get_bonds_begin(const RDKit::ROMol& mol)
    {
      return mol.beginBonds();
    }

    static RDKit::ConstBondIterator_ get_bonds_end(const RDKit::ROMol& mol)
    {
      return mol.endBonds();
    }

    static unsigned get_first_id(const RDKit::Bond* b) {
      return b->getBeginAtomIdx();
    }

    static unsigned get_second_id(const RDKit::Bond* b) {
      return b->getEndAtomIdx();
    }
};

// trait for converting dimacs graph
template<>
struct graph_trait<const RingDecomposerLib::RDL_DimacsGraph> {
    static std::string get_name(const RingDecomposerLib::RDL_DimacsGraph& mol)
    {
      std::string name = "NO_NAME";

      if (mol.name) {
        name = mol.name;
      }

      return name;
    }

    static size_t get_nof_atoms(const RingDecomposerLib::RDL_DimacsGraph& mol)
    {
      return mol.nof_nodes;
    }

    static size_t get_nof_bonds(const RingDecomposerLib::RDL_DimacsGraph& mol)
    {
      return mol.nof_edges;
    }

    static RingDecomposerLib::RDL_edge* get_bonds_begin(const RingDecomposerLib::RDL_DimacsGraph& mol)
    {
      return mol.edges;
    }

    static RingDecomposerLib::RDL_edge* get_bonds_end(const RingDecomposerLib::RDL_DimacsGraph& mol)
    {
      return mol.edges + mol.nof_edges;
    }

    static unsigned get_first_id(const RingDecomposerLib::RDL_edge& b) {
      return b[0] - 1;
    }

    static unsigned get_second_id(const RingDecomposerLib::RDL_edge& b) {
      return b[1] - 1;
    }
};

// header for datapoint table
const std::string datapoint_header =
    "datapoint_header;"
    "name;"
    "calculation_time;"
    "nodes_edges_time;"
    "nof_rc_time;"
    "rcp_time;"
    "basis_time;"
    "rc_time";

// header for molecule characterization table
const std::string characterization_header =
    "characterization_header;"
    "name;"
    "nofAtoms;"
    "nofBonds;"
    "nofAtomsLargestRS;"
    "nofBondsLargestRS;"
    "nofUrf;"
    "nofRc";

// separator for results
const std::string separator = "####";

template <typename T>
bool analyze_molecule(
    T& mol,
    unsigned repeats,
    bool rc)
{
  std::string name = graph_trait<T>::get_name(mol);

  std::chrono::time_point<std::chrono::high_resolution_clock> start, end;

  unsigned nof_urf = 0;
  double nof_rc = 0.0;

  std::cout << datapoint_header << std::endl;

  start = std::chrono::high_resolution_clock::now();
  // start timing construction & calculation
  for (unsigned r = 0; r < repeats; ++r) {
    RingDecomposerLib::RDL_graph* graph =
        RingDecomposerLib::RDL_initNewGraph(graph_trait<T>::get_nof_atoms(mol));

    for (auto it = graph_trait<T>::get_bonds_begin(mol);
          it != graph_trait<T>::get_bonds_end(mol); ++it) {
      RingDecomposerLib::RDL_addUEdge(graph, graph_trait<T>::get_first_id(*it),
          graph_trait<T>::get_second_id(*it));
    }
    RingDecomposerLib::RDL_data* data = RingDecomposerLib::RDL_calculate(graph);

    RingDecomposerLib::RDL_deleteData(data);
  }
  // end timing construction & calcuation
  end = std::chrono::high_resolution_clock::now();

  unsigned long long calculation_time =
      std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  double calculation_time_mean = static_cast<double>(calculation_time) / repeats;

  // construct new graph & calculate (for later calculations)
  RingDecomposerLib::RDL_graph* graph =
      RingDecomposerLib::RDL_initNewGraph(graph_trait<T>::get_nof_atoms(mol));

  for (auto it = graph_trait<T>::get_bonds_begin(mol);
        it != graph_trait<T>::get_bonds_end(mol); ++it) {
    unsigned add_result =
        RingDecomposerLib::RDL_addUEdge(graph, graph_trait<T>::get_first_id(*it),
        graph_trait<T>::get_second_id(*it));
    if (add_result == RingDecomposerLib::RDL_DUPLICATE_EDGE) {
      std::cerr << graph_trait<T>::get_first_id(*it) << " "
          << graph_trait<T>::get_second_id(*it) << std::endl;
      std::cerr << "WARNING: duplicate edge for " << name << std::endl;
    }
  }
  RingDecomposerLib::RDL_data* data = RingDecomposerLib::RDL_calculate(graph);
  if (!data) {
    std::cerr << "FATAL: calculation failed for " << name << std::endl;
    return false;
  }

  if (RingDecomposerLib::RDL_getNofURF(data) == RingDecomposerLib::RDL_INVALID_RESULT) {
    std::cerr << "FATAL: invalid result for " << name << std::endl;
    return false;
  }

  // start timing node and edge query
  start = std::chrono::high_resolution_clock::now();
  for (unsigned r = 0; r < repeats; ++r) {
    RingDecomposerLib::RDL_edge* edges;
    RingDecomposerLib::RDL_node* nodes;
    nof_urf = RingDecomposerLib::RDL_getNofURF(data);

    for (unsigned urfidx = 0; urfidx < nof_urf; ++urfidx) {
      RingDecomposerLib::RDL_getEdgesForURF(data, urfidx, &edges);
      RingDecomposerLib::RDL_getNodesForURF(data, urfidx, &nodes);
      free(edges);
      free(nodes);
    }
  }
  // end timing node and edge query
  end = std::chrono::high_resolution_clock::now();

  unsigned long long nodes_edges_time =
      std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  double nodes_edges_time_mean = static_cast<double>(nodes_edges_time) / repeats;

  if (RingDecomposerLib::RDL_getNofRC(data) == RingDecomposerLib::RDL_INVALID_RESULT) {
    std::cerr << "FATAL: invalid result for " << name << std::endl;
    return false;
  }

  // start timing calculate nofRC
  start = std::chrono::high_resolution_clock::now();
  for (unsigned r = 0; r < repeats; ++r) {
    nof_rc = RingDecomposerLib::RDL_getNofRC(data);
  }
  // end timing calculate nofRC
  end = std::chrono::high_resolution_clock::now();

  unsigned long long nof_rc_time =
      std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  double nof_rc_time_mean = static_cast<double>(nof_rc_time) / repeats;

  RingDecomposerLib::RDL_cycle** rcp_cycles;
  unsigned nof_rcp = RingDecomposerLib::RDL_getRCPrototypes(data, &rcp_cycles);
  if (nof_rcp == RingDecomposerLib::RDL_INVALID_RESULT) {
    std::cerr << "FATAL: invalid result for " << name << std::endl;
    return false;
  }
  RingDecomposerLib::RDL_deleteCycles(rcp_cycles, nof_rcp);

  // start timing get RCPs
  start = std::chrono::high_resolution_clock::now();
  for (unsigned r = 0; r < repeats; ++r) {
    RingDecomposerLib::RDL_cycle** cycles;
    unsigned nof_rcp = RingDecomposerLib::RDL_getRCPrototypes(data, &cycles);
    RingDecomposerLib::RDL_deleteCycles(cycles, nof_rcp);
  }
  // end timing get RCPs
  end = std::chrono::high_resolution_clock::now();

  unsigned long long rcp_time =
      std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  double rcp_time_mean = static_cast<double>(rcp_time) / repeats;

  RingDecomposerLib::RDL_cycle** basis_cycles;
  unsigned nof_basis = RingDecomposerLib::RDL_getSSSR(data, &basis_cycles);
  if (nof_basis == RingDecomposerLib::RDL_INVALID_RESULT) {
    std::cerr << "FATAL: invalid result for " << name << std::endl;
    return false;
  }
  RingDecomposerLib::RDL_deleteCycles(basis_cycles, nof_basis);

  // start timing get sssr
  start = std::chrono::high_resolution_clock::now();
  for (unsigned r = 0; r < repeats; ++r) {
    RingDecomposerLib::RDL_cycle** cycles;
    unsigned nof_basis = RingDecomposerLib::RDL_getSSSR(data, &cycles);
    RingDecomposerLib::RDL_deleteCycles(cycles, nof_basis);
  }
  // end timing get sssr
  end = std::chrono::high_resolution_clock::now();

  unsigned long long basis_time =
      std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  double basis_time_mean = static_cast<double>(basis_time) / repeats;

  unsigned long long rc_time = 0ul;
  double rc_time_mean;

  if (rc) {
    unsigned long long rc_count = 0ul;
    RingDecomposerLib::RDL_cycleIterator *it = RingDecomposerLib::RDL_getRCyclesIterator(data);

    if (it == NULL) {
      std::cerr << "FATAL: invalid result for " << name << std::endl;
      return false;
    }

    while (!RingDecomposerLib::RDL_cycleIteratorAtEnd(it)) {
      RingDecomposerLib::RDL_cycle* cycle = RingDecomposerLib::RDL_cycleIteratorGetCycle(it);
      RingDecomposerLib::RDL_deleteCycle(cycle);
      RingDecomposerLib::RDL_cycleIteratorNext(it);
      ++rc_count;
    }
    RingDecomposerLib::RDL_deleteCycleIterator(it);

    if (rc_count != nof_rc) {
      std::cerr << "FATAL: invalid result for " << name << std::endl;
      return false;
    }

    // start timing get RCs
    start = std::chrono::high_resolution_clock::now();
    for (unsigned r = 0; r < repeats; ++r) {
      RingDecomposerLib::RDL_cycleIterator *it = RingDecomposerLib::RDL_getRCyclesIterator(data);
      while (!RingDecomposerLib::RDL_cycleIteratorAtEnd(it)) {
        RingDecomposerLib::RDL_cycle* cycle = RingDecomposerLib::RDL_cycleIteratorGetCycle(it);
        RingDecomposerLib::RDL_deleteCycle(cycle);
        RingDecomposerLib::RDL_cycleIteratorNext(it);
      }
      RingDecomposerLib::RDL_deleteCycleIterator(it);
    }
    // end timing get RCs
    end = std::chrono::high_resolution_clock::now();

    rc_time =
        std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
  }
  rc_time_mean = static_cast<double>(rc_time) / repeats;

  std::cout << "datapoint;"
      << name << ";"
      << calculation_time_mean << ";"
      << nodes_edges_time_mean << ";"
      << nof_rc_time_mean << ";"
      << rcp_time_mean << ";"
      << basis_time_mean << ";"
      << rc_time_mean << std::endl;

  // find the BCC with largest z
  unsigned nof_atoms_largest_rs_by_z = 0;
  unsigned nof_bonds_largest_rs_by_z = 0;
  unsigned largest_z = 0;

  if (RingDecomposerLib::RDL_getNofRingsystems(data)
      == RingDecomposerLib::RDL_INVALID_RESULT) {
    std::cerr << "FATAL: invalid result for " << name << std::endl;
    return false;
  }

  for (unsigned i = 0; i < RingDecomposerLib::RDL_getNofRingsystems(data); ++i) {
    unsigned nof_atoms_rs =
        RingDecomposerLib::RDL_getNofNodesForRingsystem(data, i);
    unsigned nof_bonds_rs =
        RingDecomposerLib::RDL_getNofEdgesForRingsystem(data, i);
    unsigned z = nof_bonds_rs - nof_atoms_rs + 1;
    if (z > largest_z) {
      largest_z = z;
      nof_atoms_largest_rs_by_z = nof_atoms_rs;
      nof_bonds_largest_rs_by_z = nof_bonds_rs;
    }
    else if (z == largest_z && nof_bonds_rs > nof_bonds_largest_rs_by_z) {
      nof_atoms_largest_rs_by_z = nof_atoms_rs;
      nof_bonds_largest_rs_by_z = nof_bonds_rs;
    }
  }

  RingDecomposerLib::RDL_deleteData(data);
  std::cout << separator << std::endl;

  std::cout << characterization_header << std::endl;

  std::cout << "characterization;"
      << name << ";"
      << graph_trait<T>::get_nof_atoms(mol) << ";"
      << graph_trait<T>::get_nof_bonds(mol) << ";"
      << nof_atoms_largest_rs_by_z << ";"
      << nof_bonds_largest_rs_by_z << ";"
      << nof_urf << ";"
      << std::setprecision(0) << std::fixed << nof_rc << std::endl;
  std::cout << separator << std::endl;

  return true;
}

unsigned read_and_analyze_dimacsfile(
    const std::string& input,
    unsigned repeats,
    bool rc)
{
  // try to read dimacs graph
  RingDecomposerLib::RDL_DimacsGraph* graph =
      RingDecomposerLib::RDL_dimacsGraph_read(input.c_str());
  if (!graph || !analyze_molecule(
      *const_cast<const RingDecomposerLib::RDL_DimacsGraph*>(graph), repeats, rc)) {
    std::cout << datapoint_header << std::endl;
    std::cout << "datapoint;$ERROR" << std::endl;
    std::cout << separator << std::endl;

    std::cout << characterization_header << std::endl;
    std::cout << "characterization;$ERROR" << std::endl;
    std::cout << separator << std::endl;

    return EXIT_FAILURE;
  }

  RingDecomposerLib::RDL_dimacsGraph_delete(graph);

  return EXIT_SUCCESS;
}

unsigned read_and_analyze_molfile(
    const std::string& input,
    const std::string& extension,
    unsigned repeats,
    bool rc)
{
  // try reading RDKit molecule depending of file suffix
  RDKit::MolSupplier* supplier;
  if (extension == ".smi") {
    supplier = new RDKit::SmilesMolSupplier (input, " \t", 0, 1, false, true);
  }
  else {
    supplier = new RDKit::SDMolSupplier (input, true, true, true);
  }

  while (!supplier->atEnd()) {
    const RDKit::ROMol* mol = supplier->next();

    if (!mol || !analyze_molecule(*mol, repeats, rc)) {
      std::cout << datapoint_header << std::endl;
      std::cout << "datapoint;$ERROR" << std::endl;
      std::cout << separator << std::endl;

      std::cout << characterization_header << std::endl;
      std::cout << "characterization;$ERROR" << std::endl;
      std::cout << separator << std::endl;

      continue;
    }

    delete mol;
  }

  delete supplier;

  return EXIT_SUCCESS;
}

unsigned analyze_molecules(
    const std::string& input,
    unsigned repeats,
    bool rc)
{
  size_t point_pos = input.find_last_of(".");

  if (point_pos == std::string::npos) {
    std::cout << "could not determine file extension of: " << input << std::endl;
    return EXIT_FAILURE;
  }

  std::string extension = input.substr(point_pos);
  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);

  if (extension == ".smi" || extension == ".sdf") {
    return read_and_analyze_molfile(input, extension, repeats, rc);
  }
  else if (extension == ".dimacs") {
    return read_and_analyze_dimacsfile(input, repeats, rc);
  }
  else {
    std::cout << "invalid file extension detected: " << extension << std::endl;
    return EXIT_FAILURE;
  }
}

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

  RingDecomposerLib::RDL_setOutputFunction(RingDecomposerLib::RDL_writeToStderr);

  options::variables_map parsedMap = parseCommandLine(argc, argv);
  std::string input(parsedMap["input"].as<std::string>());
  unsigned repeats = parsedMap["repeats"].as<unsigned>();
  bool calc_rc = parsedMap.count("rc");
  return analyze_molecules(input, repeats, calc_rc);
}


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

#include <boost/simd/swar/functions/deinterleave_first.hpp>
#include <boost/simd/swar/functions/details/shuffle.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT         ( deinterleave_first_
                                   , boost::simd::tag::sse2_
                                   , (A0)
                                   , ((simd_<single_<A0>,boost::simd::tag::sse_>))
                                     ((simd_<single_<A0>,boost::simd::tag::sse_>))
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0, A0 const& a1) const
    {
      return details::shuffle<0, 2, 0, 2>(a0,a1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( deinterleave_first_
                                   , boost::simd::tag::sse2_
                                   , (A0)
                                   , ((simd_<double_<A0>,boost::simd::tag::sse_>))
                                     ((simd_<double_<A0>,boost::simd::tag::sse_>))
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0, A0 const& a1) const
    {
      return _mm_unpacklo_pd(a0,a1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( deinterleave_first_
                                   , boost::simd::tag::sse2_
                                   , (A0)
                                   , ((simd_<ints16_<A0>,boost::simd::tag::sse_>))
                                     ((simd_<ints16_<A0>,boost::simd::tag::sse_>))
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0, A0 const& a1) const
    {
      result_type x = _mm_unpacklo_epi16(a0,a1);
      result_type y = _mm_unpackhi_epi16(a0,a1);

      return _mm_unpacklo_epi16 ( _mm_unpacklo_epi16(x,y)
                                , _mm_unpackhi_epi16(x,y)
                                );
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( deinterleave_first_
                                   , boost::simd::tag::sse2_
                                   , (A0)
                                   , ((simd_<ints32_<A0>,boost::simd::tag::sse_>))
                                     ((simd_<ints32_<A0>,boost::simd::tag::sse_>))
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0, A0 const& a1) const
    {
      return details::shuffle<0, 2, 0, 2>(a0,a1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( deinterleave_first_
                                   , boost::simd::tag::sse2_
                                   , (A0)
                                   , ((simd_<ints64_<A0>,boost::simd::tag::sse_>))
                                     ((simd_<ints64_<A0>,boost::simd::tag::sse_>))
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0, A0 const& a1) const
    {
      return  _mm_unpacklo_epi64( a0, a1 );
    }
  };

} } }

#endif
#endif


// ADMM
#include <ADMM.h>
#include <ADMMUtils.h>

// GTSAM
#include <gtsam/slam/dataset.h>
#include <gtsam/slam/PriorFactor.h>

// Boost
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>

#include <fstream>
#include <sstream>

using namespace std;
using namespace gtsam;

// To be used when you already have the partitioning (bunch of g2o files + separator file)
int main(int argc, char* argv[])
{

  int num_subgraphs;
  string output_dir; // output_dir
  string input_dir; // input dir

  double rho = 0.1; // penalty parameter in augmented lagrangian
  double mu = 10.0; // parameter used to decide whether to increase or not the rho (adaptive penalty)
  double tau = 2.0; // if we have to increase rho, we do rho <- rho * tau, otherwise rho <- rho / tau
  int maxIter = 1000; // max number of ADMM iterations
  bool isParallel = false; // if true ADMM runs in parallel mode, this is usually less stable
  bool adaptivePenalty = true;
  bool useFlaggedInitialization = false;
  bool computeSubgraphGradient = true;
  int solver = 0; // GN
  double min_p_res = 0.1f;
  double min_d_res = 0.1f;

  // Verbosity
  int verbosity = 0;

  try{
    // Parse program options
    namespace po = boost::program_options;
    po::options_description desc("Options");
    desc.add_options()
        ("help", "Print help messages")
        ("input_dir,i",po::value<string>(&input_dir)->required(), "Input director (required)")
        ("num_subgraphs,n", po::value<int>(&num_subgraphs)->required(), "Num Subgraphs (required)")
        ("result_dir,r", po::value<string>(&output_dir)->required(), "Results Directory (required)")
        ("rho_val", po::value<double>(&rho), "rho value (default: 0.1)")
        ("mu_val,mu", po::value<double>(&mu), "mu value (default: 10.0)")
        ("tau_val,tau", po::value<double>(&tau), "tau value (default: 2.0)")
        ("max_iter", po::value<int>(&maxIter), "Maximum number of iterations (default: 1000)")
        ("adaptive_penalty,adpt_pen", po::value<bool>(&adaptivePenalty), "To use adaptive penalty or not (default: true)")
        ("use_flagged_init", po::value<bool>(&useFlaggedInitialization), "To use Flagged Initialization or not (default: true)")
        ("is_parallel", po::value<bool>(&isParallel), "To use parallel multiblock ADMM or not (default: false)")
        ("compute_subgraph_gradient", po::value<bool>(&computeSubgraphGradient), "To compute subgraph gradient  or not (default: true)")
        ("min_p_res", po::value<double>(&min_p_res), "min_p_res value (default: 0.01f)")
        ("min_d_res", po::value<double>(&min_d_res), "min_d_res value (default: 0.01f)")
        ("solver", po::value<int>(&solver), "Specify solver type (default: GN)")
        ("verbosity", po::value<int>(&verbosity), "Specify verbosity level (default: SILENT)");

    po::variables_map vm;
    try{
      po::store(po::parse_command_line(argc, argv, desc), vm); // can throw
      if ( vm.count("help")  ){ // --help option
        cout << "Partition and Run ADMM" << endl << desc << endl;
        return 0;
      }
      po::notify(vm); // throws on error, so do after help in case
    }
    catch(po::error& e){
      cerr << "ERROR: " << e.what() << endl << endl;
      cerr << desc << endl;
      return 1;
    }
  }
  catch(exception& e){
    cerr << "Unhandled Exception reached the top of main: "
              << e.what() << ", application will now exit" << endl;
    return 2;
  }

  // Create the output directory if it does not exist
  if( !(boost::filesystem::exists(output_dir))){
    boost::filesystem::create_directory(output_dir);
  }

  // Subgraph and Subinitials
  vector<NonlinearFactorGraph> sub_graphs;
  vector<Values> sub_initials;
  PriorFactor<Pose2> posePrior;

  // Read subgraphs
  for(int sub_id = 0; sub_id < num_subgraphs; sub_id++)
  {
    string filepath = input_dir + boost::lexical_cast<string>(sub_id) + string(".g2o");
    GraphAndValues readGraph = readG2o(filepath);
    NonlinearFactorGraph graph = *(readGraph.first);
    Values initial = *(readGraph.second);
    if (sub_id == 0){

      posePrior = PriorFactor<Pose2>(0, initial.at<Pose2>(0),noiseModel::Diagonal::Sigmas(Vector3(0.05, 0.05,  5*M_PI/180)));
      graph.add(posePrior);
    }

    sub_graphs.push_back(graph);
    sub_initials.push_back(initial);
  }

  // Read separators
  string separator_file = input_dir + "separators.txt";
  vector < vector <string> > separators_str;
  ifstream infile;
  infile.open(separator_file.c_str(), ifstream::in);

  while (infile){
    string s;
    if (!getline( infile, s )) break;

    istringstream ss(s);
    vector <string> record;

    while (ss){
      string s;
      if (!getline( ss, s, ',' )) break;
      record.push_back(s);
    }

    separators_str.push_back( record );
  }
  if (!infile.eof()){
    cerr << "File still not read completely!\n";
  }

  // Load the separators
  vector <int>  separators;
  for(size_t i=0; i < separators_str.size(); i++){
    for(size_t j=0; j< separators_str[i].size(); j++){
      separators.push_back(boost::lexical_cast<int>(separators_str[i][j]));
    }
  }

  // Read centralized graph from g2o file
  string filepath = input_dir + "/fullGraph.g2o";
  GraphAndValues readGraph = readG2o(filepath);
  NonlinearFactorGraph graph = *(readGraph.first);
  Values initial = *(readGraph.second);

  // Write down the G2o files
  for(size_t sub_id =0; sub_id < sub_initials.size(); sub_id++){
    string filepath = output_dir + "/" + boost::lexical_cast<string>(sub_id) + "_initial.g2o";
    writeG2o(sub_graphs[sub_id], sub_initials[sub_id], filepath);
  }

  filepath = output_dir + "/separators.txt";
  fstream separatorFile(filepath.c_str(), fstream::out);
  for(size_t i = 0; i < separators.size(); i += 3){
    separatorFile << separators[i] <<  "," << separators[i+1] <<  "," << separators[i+2] << endl;
  }
  separatorFile.close();

  // Run ADMM
  // Construct ADMM
  ADMM admm(rho,
            mu,
            tau,
            maxIter,
            isParallel,
            adaptivePenalty,
            useFlaggedInitialization,
            computeSubgraphGradient,
            min_p_res,
            min_d_res,
            output_dir);

  admm.setSolver(ADMM::Solver(solver));
  admm.setVerbosity(ADMM::Verbosity(verbosity));

  // Load structures
  admm.load(sub_graphs, sub_initials, separators, graph);

  cout << "Running on " << input_dir << endl;
  // Run ADMM
  pair<vector<Values>, vector<double> > primalDualResult = admm.optimize();
  vector<Values> sub_results = primalDualResult.first;

  // Pack ADMM results into a single Value structure, to check centralized error
  logResults(sub_graphs, sub_results, graph, initial, posePrior, output_dir);

  return 0;
}


#include <boost/numeric/ublas/vector.hpp>
#include "crosslink.hpp"
//#include "simulation.hpp"

using namespace std;
namespace ublas = boost::numeric::ublas;

class CrosslinkPair2 {
public:
	CrosslinkPair2(Crosslink &cl1, Crosslink &cl2, double &RRij, int &WBXij, int &WBYij, int &WBZij, int filId1, int filId2);

	void updateEpotPair(double epotPair);
	//void updateContourLength(double contourLength);
	double getContourLength();
	Crosslink* getCrosslink(int i);
	//void updateWB(ublas::vector<int> WB);
	int getWBX();
	int getWBY();
	int getWBZ();

	int getFilId(int i);

	double k2;
	double Leq;
	double r12Abs;
	double f1;

private:
	Crosslink *cl1_;
	Crosslink *cl2_;
	std::vector<int> filIds_; //filId1_= 1 or 2
	double *contourLength_; // length of polymer between crosslinks
	double epotPair_;
	//ublas::vector<int*> WB_;
	int *WBX_, *WBY_, *WBZ_; // tells you in which box cl2 is located (periodic boundary conditions), WBX=WBY=WBZ=0 -> cl2 in real simulation box
						  // WBX/WBY/WBZ=+-1 cl2 shifted one (virtual) box length in positive/negative x/y/z-direction

};


//---------------------------------------------------------------------------//
// Copyright (c) 2020 Alexander Sokolov <asokolov@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_SHA3_FINALIZER_HPP
#define CRYPTO3_SHA3_FINALIZER_HPP

#include <boost/crypto3/hash/detail/sha3/sha3_policy.hpp>

#include <boost/endian/conversion.hpp>

namespace boost {
    namespace crypto3 {
        namespace hashes {
            namespace detail {
                template<typename PolicyType>
                class sha3_finalizer {
                    typedef PolicyType policy_type;

                    constexpr static const std::size_t word_bits = policy_type::word_bits;
                    typedef typename policy_type::word_type word_type;

                    constexpr static const std::size_t state_bits = policy_type::state_bits;
                    constexpr static const std::size_t state_words = policy_type::state_words;
                    typedef typename policy_type::state_type state_type;

                    constexpr static const std::size_t block_bits = policy_type::block_bits;
                    constexpr static const std::size_t block_words = policy_type::block_words;
                    typedef typename policy_type::block_type block_type;

                    constexpr static const std::size_t digest_bits = policy_type::digest_bits;
                    constexpr static const std::size_t digest_blocks = digest_bits / block_bits;
                    constexpr static const std::size_t last_digest_bits = digest_bits % block_bits;
                    constexpr static const std::size_t last_digest_words =
                        last_digest_bits / word_bits + ((last_digest_bits % word_bits) ? 1 : 0);

                    typedef typename policy_type::digest_type digest_type;
                    typedef sha3_functions<digest_bits> policy_func_type;

                public:
                    void operator()(state_type &state) {
                        state_type temp_state;
                        std::fill(temp_state.begin(), temp_state.end(), 0);

                        for (std::size_t i = 0; i != digest_blocks; ++i) {
                            for (std::size_t j = 0; j != block_words; ++j)
                                temp_state[i * block_words + j] = state[j];

                            for (std::size_t i = 0; i != state_words; ++i)
                                boost::endian::endian_reverse_inplace(state[i]);

                            policy_func_type::permute(state);

                            for (std::size_t i = 0; i != state_words; ++i)
                                boost::endian::endian_reverse_inplace(state[i]);
                        }

                        if (last_digest_bits) {
                            for (std::size_t j = 0; j != last_digest_words; ++j)
                                temp_state[digest_blocks * block_words + j] = state[j];
                        }

                        state = temp_state;
                    }
                };
            }    // namespace detail
        }        // namespace hashes
    }            // namespace crypto3
}    // namespace boost

#endif    // CRYPTO3_SHA3_FINALIZER_HPP


#include "generator/regions/region.hpp"

#include "generator/regions/city.hpp"
#include "generator/regions/collector_region_info.hpp"

#include "base/assert.hpp"

#include <algorithm>
#include <numeric>

#include <boost/geometry.hpp>

namespace generator
{
namespace regions
{
namespace
{
template <typename BoostGeometry, typename FbGeometry>
void FillBoostGeometry(BoostGeometry & geometry, FbGeometry const & fbGeometry)
{
  geometry.reserve(fbGeometry.size());
  for (auto const & p : fbGeometry)
    boost::geometry::append(geometry, BoostPoint{p.x, p.y});
}
}  // namespace

Region::Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd)
  : RegionWithName(fb.GetParams().name),
    RegionWithData(rd),
    m_polygon(std::make_shared<BoostPolygon>())
{
  FillPolygon(fb);
  auto rect = fb.GetLimitRect();
  m_rect = BoostRect({{rect.minX(), rect.minY()}, {rect.maxX(), rect.maxY()}});
  m_area = boost::geometry::area(*m_polygon);
}

void Region::DeletePolygon()
{
  m_polygon = nullptr;
}

void Region::FillPolygon(FeatureBuilder1 const & fb)
{
  CHECK(m_polygon, ());

  auto const & fbGeometry = fb.GetGeometry();
  CHECK(!fbGeometry.empty(), ());
  auto it = std::begin(fbGeometry);
  FillBoostGeometry(m_polygon->outer(), *it);
  m_polygon->inners().resize(fbGeometry.size() - 1);
  int i = 0;
  ++it;
  for (; it != std::end(fbGeometry); ++it)
    FillBoostGeometry(m_polygon->inners()[i++], *it);

  boost::geometry::correct(*m_polygon);
}

bool Region::IsCountry() const
{
  static auto const kAdminLevelCountry = AdminLevel::Two;
  return !HasPlaceType() && GetAdminLevel() == kAdminLevelCountry;
}

bool Region::Contains(Region const & smaller) const
{
  CHECK(m_polygon, ());
  CHECK(smaller.m_polygon, ());

  return boost::geometry::covered_by(*smaller.m_polygon, *m_polygon);
}

double Region::CalculateOverlapPercentage(Region const & other) const
{
  CHECK(m_polygon, ());
  CHECK(other.m_polygon, ());

  std::vector<BoostPolygon> coll;
  boost::geometry::intersection(*other.m_polygon, *m_polygon, coll);
  auto const min = std::min(boost::geometry::area(*other.m_polygon),
                            boost::geometry::area(*m_polygon));
  auto const binOp = [](double x, BoostPolygon const & y) { return x + boost::geometry::area(y); };
  auto const sum = std::accumulate(std::begin(coll), std::end(coll), 0., binOp);
  return (sum / min) * 100;
}

bool Region::ContainsRect(Region const & smaller) const
{
  return boost::geometry::covered_by(smaller.m_rect, m_rect);
}

BoostPoint Region::GetCenter() const
{
  BoostPoint p;
  boost::geometry::centroid(m_rect, p);
  return p;
}

bool Region::Contains(City const & cityPoint) const
{
  CHECK(m_polygon, ());

  return boost::geometry::covered_by(cityPoint.GetCenter(), *m_polygon);
}

void Region::SetInfo(City const & cityPoint)
{
  SetStringUtf8MultilangName(cityPoint.GetStringUtf8MultilangName());
  SetAdminLevel(cityPoint.GetAdminLevel());
  SetPlaceType(cityPoint.GetPlaceType());
}
}  // namespace regions
}  // namespace generator