// ---------------------------------------------------------------------
//
// Copyright (c) 2018 - 2019 by the IBAMR developers
// All rights reserved.
//
// This file is part of IBAMR.
//
// IBAMR is free software and is distributed under the 3-clause BSD
// license. The full text of the license can be found in the file
// COPYRIGHT at the top level directory of IBAMR.
//
// ---------------------------------------------------------------------

// Config files
#include <SAMRAI_config.h>

// Headers for basic PETSc functions
#include <petscsys.h>

// Headers for basic SAMRAI objects
#include <BergerRigoutsos.h>
#include <CartesianGridGeometry.h>
#include <LoadBalancer.h>
#include <StandardTagAndInitialize.h>

// Headers for basic libMesh objects
#include <libmesh/equation_systems.h>
#include <libmesh/exodusII_io.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/mesh_triangle_interface.h>

// Headers for application-specific algorithm/data structure objects
#include <ibamr/IBExplicitHierarchyIntegrator.h>
#include <ibamr/IBFEDirectForcingKinematics.h>
#include <ibamr/IBFEMethod.h>
#include <ibamr/INSStaggeredHierarchyIntegrator.h>

#include <ibtk/AppInitializer.h>
#include <ibtk/IBTKInit.h>
#include <ibtk/IBTK_MPI.h>
#include <ibtk/LEInteractor.h>
#include <ibtk/ibtk_utilities.h>
#include <ibtk/libmesh_utilities.h>
#include <ibtk/muParserCartGridFunction.h>
#include <ibtk/muParserRobinBcCoefs.h>

#include <boost/multi_array.hpp>

// Set up application namespace declarations
#include <ibamr/app_namespaces.h>

// Coordinate mapping function.

static double R, H, L;
void
coordinate_mapping_function(libMesh::Point& X, const libMesh::Point& s, void* /*ctx*/)
{
    X(0) = s(0) + L / 4.0;
    X(1) = s(1) + H / 4.0;
    ;
    return;
} // coordinate_mapping_function

void
cylinder_kinematics(double /*data_time*/, Eigen::Vector3d& U_com, Eigen::Vector3d& W_com, void* /*ctx*/)
{
    U_com.setZero();
    W_com.setZero();

    return;
} // cylinder_kinematics

// Function prototypes
static ofstream U_stream, X_stream;
void postprocess_data(double loop_time, Pointer<IBFEDirectForcingKinematics> df_kinematics_ops);

/*******************************************************************************
 * For each run, the input filename and restart information (if needed) must   *
 * be given on the command line.  For non-restarted case, command line is:     *
 *                                                                             *
 *    executable <input file name>                                             *
 *                                                                             *
 * For restarted run, command line is:                                         *
 *                                                                             *
 *    executable <input file name> <restart directory> <restart number>        *
 *                                                                             *
 *******************************************************************************/

int
main(int argc, char* argv[])
{
    // Initialize IBAMR and libraries. Deinitialization is handled by this object as well.
    IBTKInit ibtk_init(argc, argv, MPI_COMM_WORLD);
    const LibMeshInit& init = ibtk_init.getLibMeshInit();

    { // cleanup dynamically allocated objects prior to shutdown

        // Parse command line options, set some standard options from the input
        // file, initialize the restart database (if this is a restarted run),
        // and enable file logging.
        Pointer<AppInitializer> app_initializer = new AppInitializer(argc, argv, "IB.log");
        Pointer<Database> input_db = app_initializer->getInputDatabase();

        // Get various standard options set in the input file.
        const bool dump_viz_data = app_initializer->dumpVizData();
        const int viz_dump_interval = app_initializer->getVizDumpInterval();
        const bool uses_visit = dump_viz_data && app_initializer->getVisItDataWriter();
#ifdef LIBMESH_HAVE_EXODUS_API
        const bool uses_exodus = dump_viz_data && !app_initializer->getExodusIIFilename().empty();
#else
        const bool uses_exodus = false;
        if (!app_initializer->getExodusIIFilename().empty())
        {
            plog << "WARNING: libMesh was compiled without Exodus support, so no "
                 << "Exodus output will be written in this program.\n";
        }
#endif
        const string exodus_filename = app_initializer->getExodusIIFilename();

        const bool dump_restart_data = app_initializer->dumpRestartData();
        const int restart_dump_interval = app_initializer->getRestartDumpInterval();
        const string restart_dump_dirname = app_initializer->getRestartDumpDirectory();
        const string restart_read_dirname = app_initializer->getRestartReadDirectory();
        const int restart_restore_num = app_initializer->getRestartRestoreNumber();

        const bool dump_postproc_data = app_initializer->dumpPostProcessingData();
        const int postproc_data_dump_interval = app_initializer->getPostProcessingDataDumpInterval();
        const string postproc_data_dump_dirname = app_initializer->getPostProcessingDataDumpDirectory();
        if (dump_postproc_data && (postproc_data_dump_interval > 0) && !postproc_data_dump_dirname.empty())
        {
            Utilities::recursiveMkdir(postproc_data_dump_dirname);
        }

        const bool dump_timer_data = app_initializer->dumpTimerData();
        const int timer_dump_interval = app_initializer->getTimerDumpInterval();

        // Create a simple FE mesh.
        Mesh solid_mesh(init.comm(), NDIM);
        const double dx = input_db->getDouble("DX");
        const double ds = input_db->getDouble("MFAC") * dx;
        string elem_type = input_db->getString("ELEM_TYPE");
        R = input_db->getDouble("R");
        H = input_db->getDouble("H");
        L = input_db->getDouble("L");
        if (NDIM == 2 && (elem_type == "TRI3" || elem_type == "TRI6"))
        {
#ifdef LIBMESH_HAVE_TRIANGLE
            const int num_circum_nodes = ceil(2.0 * M_PI * R / ds);
            for (int k = 0; k < num_circum_nodes; ++k)
            {
                const double theta = 2.0 * M_PI * static_cast<double>(k) / static_cast<double>(num_circum_nodes);
                solid_mesh.add_point(libMesh::Point(R * cos(theta), R * sin(theta)));
            }
            TriangleInterface triangle(solid_mesh);
            triangle.triangulation_type() = TriangleInterface::GENERATE_CONVEX_HULL;
            triangle.elem_type() = Utility::string_to_enum<ElemType>(elem_type);
            triangle.desired_area() = 1.5 * sqrt(3.0) / 4.0 * ds * ds;
            triangle.insert_extra_points() = true;
            triangle.smooth_after_generating() = true;
            triangle.triangulate();
#else
            TBOX_ERROR("ERROR: libMesh appears to have been configured without support for Triangle,\n"
                       << "       but Triangle is required for TRI3 or TRI6 elements.\n");
#endif
        }
        else
        {
            // NOTE: number of segments along boundary is 4*2^r.
            const double num_circum_segments = 2.0 * M_PI * R / ds;
            const int r = log2(0.25 * num_circum_segments);
            MeshTools::Generation::build_sphere(solid_mesh, R, r, Utility::string_to_enum<ElemType>(elem_type));
        }

        // Ensure nodes on the surface are on the analytic boundary.
        MeshBase::element_iterator el_end = solid_mesh.elements_end();
        for (MeshBase::element_iterator el = solid_mesh.elements_begin(); el != el_end; ++el)
        {
            Elem* const elem = *el;
            for (unsigned int side = 0; side < elem->n_sides(); ++side)
            {
                const bool at_mesh_bdry = !elem->neighbor_ptr(side);
                if (!at_mesh_bdry) continue;
                for (unsigned int k = 0; k < elem->n_nodes(); ++k)
                {
                    if (!elem->is_node_on_side(k, side)) continue;
                    Node& n = elem->node_ref(k);
                    n = R * n.unit();
                }
            }
        }
        solid_mesh.prepare_for_use();
        Mesh& mesh = solid_mesh;

        // Create major algorithm and data objects that comprise the
        // application.  These objects are configured from the input database
        // and, if this is a restarted run, from the restart database.
        Pointer<INSHierarchyIntegrator> navier_stokes_integrator = new INSStaggeredHierarchyIntegrator(
            "INSStaggeredHierarchyIntegrator",
            app_initializer->getComponentDatabase("INSStaggeredHierarchyIntegrator"));

        Pointer<IBFEMethod> ib_method_ops =
            new IBFEMethod("IBFEMethod",
                           app_initializer->getComponentDatabase("IBFEMethod"),
                           &mesh,
                           app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),
                           /*register_for_restart*/ true,
                           restart_read_dirname,
                           restart_restore_num);
        Pointer<IBHierarchyIntegrator> time_integrator =
            new IBExplicitHierarchyIntegrator("IBHierarchyIntegrator",
                                              app_initializer->getComponentDatabase("IBHierarchyIntegrator"),
                                              ib_method_ops,
                                              navier_stokes_integrator);
        Pointer<CartesianGridGeometry<NDIM> > grid_geometry = new CartesianGridGeometry<NDIM>(
            "CartesianGeometry", app_initializer->getComponentDatabase("CartesianGeometry"));
        Pointer<PatchHierarchy<NDIM> > patch_hierarchy = new PatchHierarchy<NDIM>("PatchHierarchy", grid_geometry);
        Pointer<StandardTagAndInitialize<NDIM> > error_detector =
            new StandardTagAndInitialize<NDIM>("StandardTagAndInitialize",
                                               time_integrator,
                                               app_initializer->getComponentDatabase("StandardTagAndInitialize"));
        Pointer<BergerRigoutsos<NDIM> > box_generator = new BergerRigoutsos<NDIM>();
        Pointer<LoadBalancer<NDIM> > load_balancer =
            new LoadBalancer<NDIM>("LoadBalancer", app_initializer->getComponentDatabase("LoadBalancer"));
        Pointer<GriddingAlgorithm<NDIM> > gridding_algorithm =
            new GriddingAlgorithm<NDIM>("GriddingAlgorithm",
                                        app_initializer->getComponentDatabase("GriddingAlgorithm"),
                                        error_detector,
                                        box_generator,
                                        load_balancer);

        // Create IBFE direct forcing kinematics object.
        Pointer<IBFEDirectForcingKinematics> df_kinematics_ops = new IBFEDirectForcingKinematics(
            "cylinder_dfk",
            app_initializer->getComponentDatabase("CylinderIBFEDirectForcingKinematics"),
            ib_method_ops,
            /*part*/ 0,
            /*register_for_restart*/ true);
        ib_method_ops->registerDirectForcingKinematics(df_kinematics_ops, /*part*/ 0);

        // Specify structure kinematics
        FreeRigidDOFVector solve_dofs;
        solve_dofs.setZero();
        solve_dofs << 1, 1, 1;
        df_kinematics_ops->setSolveRigidBodyVelocity(solve_dofs);
        df_kinematics_ops->registerKinematicsFunction(&cylinder_kinematics, NULL);

        // Configure the IBFE solver.
        ib_method_ops->initializeFEEquationSystems();
        ib_method_ops->registerInitialCoordinateMappingFunction(coordinate_mapping_function);
        EquationSystems* equation_systems = ib_method_ops->getFEDataManager()->getEquationSystems();

        // Create Eulerian initial condition specification objects.
        if (input_db->keyExists("VelocityInitialConditions"))
        {
            Pointer<CartGridFunction> u_init = new muParserCartGridFunction(
                "u_init", app_initializer->getComponentDatabase("VelocityInitialConditions"), grid_geometry);
            navier_stokes_integrator->registerVelocityInitialConditions(u_init);
        }

        if (input_db->keyExists("PressureInitialConditions"))
        {
            Pointer<CartGridFunction> p_init = new muParserCartGridFunction(
                "p_init", app_initializer->getComponentDatabase("PressureInitialConditions"), grid_geometry);
            navier_stokes_integrator->registerPressureInitialConditions(p_init);
        }

        // Create Eulerian boundary condition specification objects (when necessary).
        const IntVector<NDIM>& periodic_shift = grid_geometry->getPeriodicShift();
        vector<RobinBcCoefStrategy<NDIM>*> u_bc_coefs(NDIM);
        if (periodic_shift.min() > 0)
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                u_bc_coefs[d] = NULL;
            }
        }
        else
        {
            for (unsigned int d = 0; d < NDIM; ++d)
            {
                const std::string bc_coefs_name = "u_bc_coefs_" + std::to_string(d);

                const std::string bc_coefs_db_name = "VelocityBcCoefs_" + std::to_string(d);

                u_bc_coefs[d] = new muParserRobinBcCoefs(
                    bc_coefs_name, app_initializer->getComponentDatabase(bc_coefs_db_name), grid_geometry);
            }
            navier_stokes_integrator->registerPhysicalBoundaryConditions(u_bc_coefs);
        }

        // Create Eulerian body force function specification objects.
        if (input_db->keyExists("ForcingFunction"))
        {
            Pointer<CartGridFunction> f_fcn = new muParserCartGridFunction(
                "f_fcn", app_initializer->getComponentDatabase("ForcingFunction"), grid_geometry);
            time_integrator->registerBodyForceFunction(f_fcn);
        }

        // Set up visualization plot file writers.
        Pointer<VisItDataWriter<NDIM> > visit_data_writer = app_initializer->getVisItDataWriter();
        if (uses_visit)
        {
            time_integrator->registerVisItDataWriter(visit_data_writer);
        }
        std::unique_ptr<ExodusII_IO> exodus_io(uses_exodus ? new ExodusII_IO(mesh) : NULL);

        // Check to see if this is a restarted run to append current exodus files
        if (uses_exodus)
        {
            const bool from_restart = RestartManager::getManager()->isFromRestart();
            exodus_io->append(from_restart);
        }

        // Initialize hierarchy configuration and data on all patches.
        ib_method_ops->initializeFEData();
        time_integrator->initializePatchHierarchy(patch_hierarchy, gridding_algorithm);

        // Deallocate initialization objects.
        app_initializer.setNull();

        // Print the input database contents to the log file.
        plog << "Input database:\n";
        input_db->printClassData(plog);

        // Write out initial visualization data.
        int iteration_num = time_integrator->getIntegratorStep();
        double loop_time = time_integrator->getIntegratorTime();
        if (dump_viz_data)
        {
            pout << "\n\nWriting visualization files...\n\n";
            if (uses_visit)
            {
                time_integrator->setupPlotData();
                visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
            }
            if (uses_exodus)
            {
                exodus_io->write_timestep(
                    exodus_filename, *equation_systems, iteration_num / viz_dump_interval + 1, loop_time);
            }
        }

        // Open streams to save COM position and velocity.
        if (IBTK_MPI::getRank() == 0)
        {
            X_stream.open("X.curve", ios_base::out | ios_base::trunc);
            U_stream.open("U.curve", ios_base::out | ios_base::trunc);

            X_stream.precision(10);
            U_stream.precision(10);
        }

        // Main time step loop.
        double loop_time_end = time_integrator->getEndTime();
        double dt = 0.0;
        while (!MathUtilities<double>::equalEps(loop_time, loop_time_end) && time_integrator->stepsRemaining())
        {
            iteration_num = time_integrator->getIntegratorStep();
            loop_time = time_integrator->getIntegratorTime();

            pout << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "At beginning of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";

            dt = time_integrator->getMaximumTimeStepSize();
            time_integrator->advanceHierarchy(dt);
            loop_time += dt;

            pout << "\n";
            pout << "At end       of timestep # " << iteration_num << "\n";
            pout << "Simulation time is " << loop_time << "\n";
            pout << "+++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            pout << "\n";

            // At specified intervals, write visualization and restart files,
            // print out timer data, and store hierarchy data for post
            // processing.
            iteration_num += 1;
            const bool last_step = !time_integrator->stepsRemaining();
            if (dump_viz_data && (iteration_num % viz_dump_interval == 0 || last_step))
            {
                pout << "\nWriting visualization files...\n\n";
                if (uses_visit)
                {
                    time_integrator->setupPlotData();
                    visit_data_writer->writePlotData(patch_hierarchy, iteration_num, loop_time);
                }
                if (uses_exodus)
                {
                    exodus_io->write_timestep(
                        exodus_filename, *equation_systems, iteration_num / viz_dump_interval + 1, loop_time);
                }
            }
            if (dump_restart_data && (iteration_num % restart_dump_interval == 0 || last_step))
            {
                pout << "\nWriting restart files...\n\n";
                RestartManager::getManager()->writeRestartFile(restart_dump_dirname, iteration_num);
                ib_method_ops->writeFEDataToRestartFile(restart_dump_dirname, iteration_num);
            }
            if (dump_timer_data && (iteration_num % timer_dump_interval == 0 || last_step))
            {
                pout << "\nWriting timer data...\n\n";
                TimerManager::getManager()->print(plog);
            }
            if (dump_postproc_data && (iteration_num % postproc_data_dump_interval == 0 || last_step))
            {
                postprocess_data(loop_time, df_kinematics_ops);
            }
        }

        // Close the logging streams.
        if (IBTK_MPI::getRank() == 0)
        {
            X_stream.close();
            U_stream.close();
        }

        // Cleanup Eulerian boundary condition specification objects (when
        // necessary).
        for (unsigned int d = 0; d < NDIM; ++d) delete u_bc_coefs[d];

    } // cleanup dynamically allocated objects prior to shutdown
} // main

void
postprocess_data(double loop_time, Pointer<IBFEDirectForcingKinematics> df_kinematics_ops)
{
    const Eigen::Vector3d& X = df_kinematics_ops->getStructureCOM();
    const Eigen::Vector3d& U = df_kinematics_ops->getCOMTransVelocity();
    const Eigen::Vector3d& W = df_kinematics_ops->getCOMRotVelocity();
    if (IBTK_MPI::getRank() == 0)
    {
        X_stream << loop_time << "\t" << X[0] << "\t" << X[1] << endl;
        U_stream << loop_time << "\t" << U[0] << "\t" << U[1] << "\t" << W[2] << endl;
        ;
    }
    return;
} // postprocess_data


#include <boost/bind.hpp>
#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Empty.h>

turtlesim::PoseConstPtr g_pose;
turtlesim::Pose g_goal;

enum State
{
  FORWARD,
  STOP_FORWARD,
  TURN,
  STOP_TURN,
};

State g_state = FORWARD;
State g_last_state = FORWARD;
bool g_first_goal_set = false;

#define PI 3.141592

void poseCallback(const turtlesim::PoseConstPtr& pose)
{
  g_pose = pose;
}

bool hasReachedGoal()
{
  return fabsf(g_pose->x - g_goal.x) < 0.1 && fabsf(g_pose->y - g_goal.y) < 0.1 && fabsf(g_pose->theta - g_goal.theta) < 0.01;
}

bool hasStopped()
{
  return g_pose->angular_velocity < 0.0001 && g_pose->linear_velocity < 0.0001;
}

void printGoal()
{
  ROS_INFO("New goal [%f %f, %f]", g_goal.x, g_goal.y, g_goal.theta);
}

void commandTurtle(ros::Publisher twist_pub, float linear, float angular)
{
  geometry_msgs::Twist twist;
  twist.linear.x = linear;
  twist.angular.z = angular;
  twist_pub.publish(twist);
}

void stopForward(ros::Publisher twist_pub)
{
  if (hasStopped())
  {
    ROS_INFO("Reached goal");
    g_state = TURN;
    g_goal.x = g_pose->x;
    g_goal.y = g_pose->y;
    g_goal.theta = fmod(g_pose->theta + PI/2.0, 2*PI);
    // wrap g_goal.theta to [-pi, pi)
    if (g_goal.theta >= PI) g_goal.theta -= 2 * PI;
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}

void stopTurn(ros::Publisher twist_pub)
{
  if (hasStopped())
  {
    ROS_INFO("Reached goal");
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}

void forward(ros::Publisher twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_FORWARD;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 1.0, 0.0);
  }
}

void turn(ros::Publisher twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_TURN;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 0.0, 0.4);
  }
}

void timerCallback(const ros::TimerEvent&, ros::Publisher twist_pub)
{
  if (!g_pose)
  {
    return;
  }

  if (!g_first_goal_set)
  {
    g_first_goal_set = true;
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }

  if (g_state == FORWARD)
  {
    forward(twist_pub);
  }
  else if (g_state == STOP_FORWARD)
  {
    stopForward(twist_pub);
  }
  else if (g_state == TURN)
  {
    turn(twist_pub);
  }
  else if (g_state == STOP_TURN)
  {
    stopTurn(twist_pub);
  }
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "draw_square");
  ros::NodeHandle nh;

  // turtlesim中更新时 turtle1发布pose话题 从而触发回调poseCallback 保存位姿
  ros::Subscriber pose_sub = nh.subscribe("turtle1/pose", 1, poseCallback);

  ros::Publisher twist_pub = nh.advertise<geometry_msgs::Twist>("turtle1/cmd_vel", 1);
  ros::ServiceClient reset = nh.serviceClient<std_srvs::Empty>("reset");

  // 定时器超时，触发twist_pub 发布turtle1/cmd_vel话题，turtlesim中turtle1回调velocityCallback函数进行位姿更新
  // turtlesim 窗口更新 触发pose发布，回调poseCallback 更新位姿
  // 如此往复
  ros::Timer timer = nh.createTimer(ros::Duration(0.016), boost::bind(timerCallback, _1, twist_pub));

  std_srvs::Empty empty;
  reset.call(empty);

  ros::spin();
}


char const* NOTES =
  "Process (PLF format) lattice: sharpen distribution, nbest, graphviz, push weights\n"
  ;

#include <iostream>
#include <fstream>
#include <vector>

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

#include "filelib.h"
#include "tdict.h"
#include "prob.h"
#include "hg.h"
#include "hg_io.h"
#include "viterbi.h"
#include "kbest.h"

namespace po = boost::program_options;
using namespace std;

void InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("input,i", po::value<string>(), "REQ. Lattice input file (PLF), - for STDIN")
        ("prior_scale,p", po::value<double>()->default_value(1.0), "Scale path probabilities by this amount < 1 flattens, > 1 sharpens")
        ("weight,w", po::value<vector<double> >(), "Weight(s) for arc features")
	("output,o", po::value<string>()->default_value("plf"), "Output format (text, plf)")
	("command,c", po::value<string>()->default_value("push"), "Operation to perform: push, graphviz, 1best, 2best ...")
        ("help,h", "Print this help message and exit");
  po::options_description clo("Command line options");
  po::options_description dcmdline_options;
  dcmdline_options.add(opts);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  po::notify(*conf);

  if (conf->count("help") || conf->count("input") == 0) {
    cerr << dcmdline_options << endl << NOTES << endl;
    exit(1);
  }
}

int main(int argc, char **argv) {
  po::variables_map conf;
  InitCommandLine(argc, argv, &conf);
  string infile = conf["input"].as<string>();
  ReadFile rf(infile);
  istream* in = rf.stream();
  assert(*in);
  SparseVector<double> wts;
  vector<double> wv;
  if (conf.count("weight") > 0) wv = conf["weight"].as<vector<double> >();
  if (wv.empty()) wv.push_back(1.0);
  for (int i = 0; i < wv.size(); ++i) {
    const string fname = "Feature_" + boost::lexical_cast<string>(i);
    cerr << "[INFO] Arc weight " << (i+1) << " = " << wv[i] << endl;
    wts.set_value(FD::Convert(fname), wv[i]);
  }
  const string cmd = conf["command"].as<string>();
  const bool push_weights = cmd == "push";
  const bool output_plf = cmd == "plf";
  const bool graphviz = cmd == "graphviz";
  const bool kbest = cmd.rfind("best") == (cmd.size() - 4) && cmd.size() > 4;
  int k = 1;
  if (kbest) {
    k = boost::lexical_cast<int>(cmd.substr(0, cmd.size() - 4));
    cerr << "KBEST = " << k << endl;
  }
  const double scale = conf["prior_scale"].as<double>();
  int lc = 0;
  while(*in) {
    ++lc;
    string plf;
    getline(*in, plf);
    if (plf.empty()) continue;
    Hypergraph hg;
    HypergraphIO::ReadFromPLF(plf, &hg);
    hg.Reweight(wts);
    if (graphviz) hg.PrintGraphviz();
    if (push_weights) hg.PushWeightsToSource(scale);
    if (output_plf) {
      cout << HypergraphIO::AsPLF(hg) << endl;
    } else {
      KBest::KBestDerivations<vector<WordID>, ESentenceTraversal> kbest(hg, k);
      for (int i = 0; i < k; ++i) {
        const KBest::KBestDerivations<vector<WordID>, ESentenceTraversal>::Derivation* d =
          kbest.LazyKthBest(hg.nodes_.size() - 1, i);
        if (!d) break;
        cout << lc << " ||| " << TD::GetString(d->yield) << " ||| " << d->score << endl;
      }
    }
  }
  return 0;
}


//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Alejandro Cabrera 2011.
// Distributed under the Boost
// Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or
// copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/bloom_filter for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#include "detail/pow.hpp"
#include "detail/generator.hpp"
#include "detail/benchmark.hpp"

#include <iostream>

#include <boost/progress.hpp>
#include <boost/unordered_set.hpp>
using namespace std;
using boost::detail::Pow;
using boost::detail::benchmark;
using boost::detail::generator;

int main()
{
  static const size_t REPEAT = Pow<10, 3>::val; // 1000
  static const size_t OPS = Pow<10, 6>::val; // 1,000,000 inserts

  boost::progress_display progress(REPEAT);
  double total_time = 0.0;

  benchmark<int, OPS,
	    generator<int>,
	    boost::unordered_set<int> > bench;

  for (size_t i = 0; i < REPEAT; ++i, ++progress) {
    bench.run();
    total_time += bench.time();
    bench.reset();
  }

  cout << REPEAT << " trials of " << OPS << " insertions took "
       << total_time << " seconds" << endl;

  return 0;
}


/*
 Copyright (C) 2016 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 <qle/methods/multipathgeneratorbase.hpp>

#include <boost/make_shared.hpp>

using namespace QuantLib;

namespace QuantExt {

MultiPathGeneratorMersenneTwister::MultiPathGeneratorMersenneTwister(
    const boost::shared_ptr<StochasticProcess>& process, const TimeGrid& grid, BigNatural seed, bool antitheticSampling)
    : process_(process), grid_(grid), seed_(seed), antitheticSampling_(antitheticSampling), antitheticVariate_(true) {
    reset();
}

void MultiPathGeneratorMersenneTwister::reset() {
    PseudoRandom::rsg_type rsg = PseudoRandom::make_sequence_generator(process_->size() * (grid_.size() - 1), seed_);
    pg_ = boost::make_shared<MultiPathGenerator<PseudoRandom::rsg_type> >(process_, grid_, rsg, false);
    antitheticVariate_ = true;
}

MultiPathGeneratorSobol::MultiPathGeneratorSobol(const boost::shared_ptr<StochasticProcess>& process,
                                                 const TimeGrid& grid, BigNatural seed,
                                                 SobolRsg::DirectionIntegers directionIntegers)
    : process_(process), grid_(grid), seed_(seed), directionIntegers_(directionIntegers) {
    reset();
}

void MultiPathGeneratorSobol::reset() {
    pg_ = boost::make_shared<MultiPathGenerator<InverseCumulativeRsg<SobolRsg, InverseCumulativeNormal> > >(
        process_, grid_,
        InverseCumulativeRsg<SobolRsg, InverseCumulativeNormal>(
            SobolRsg(process_->size() * (grid_.size() - 1), seed_, directionIntegers_)));
}

MultiPathGeneratorSobolBrownianBridge::MultiPathGeneratorSobolBrownianBridge(
    const boost::shared_ptr<StochasticProcess>& process, const TimeGrid& grid,
    SobolBrownianGenerator::Ordering ordering, BigNatural seed, SobolRsg::DirectionIntegers directionIntegers)
    : process_(process), grid_(grid), ordering_(ordering), seed_(seed), directionIntegers_(directionIntegers),
      next_(MultiPath(process->size(), grid), 1.0) {
    reset();
}

void MultiPathGeneratorSobolBrownianBridge::reset() {
    gen_ = boost::make_shared<SobolBrownianGenerator>(process_->size(), grid_.size() - 1, ordering_, seed_,
                                                      directionIntegers_);
}

const Sample<MultiPath>& MultiPathGeneratorSobolBrownianBridge::next() const {
    Array asset = process_->initialValues();
    MultiPath& path = next_.value;
    for (Size j = 0; j < asset.size(); ++j) {
        path[j].front() = asset[j];
    }
    next_.weight = gen_->nextPath();
    std::vector<Real> output(asset.size());
    for (Size i = 1; i < grid_.size(); ++i) {
        Real t = grid_[i - 1];
        Real dt = grid_.dt(i - 1);
        gen_->nextStep(output);
        Array tmp(output.begin(), output.end());
        asset = process_->evolve(t, asset, dt, tmp);
        for (Size j = 0; j < asset.size(); ++j) {
            path[j][i] = asset[j];
        }
    }
    return next_;
}

boost::shared_ptr<MultiPathGeneratorBase> makeMultiPathGenerator(const SequenceType s,
                                                                 const boost::shared_ptr<StochasticProcess>& process,
                                                                 const TimeGrid& timeGrid, const BigNatural seed,
                                                                 const SobolBrownianGenerator::Ordering ordering,
                                                                 const SobolRsg::DirectionIntegers directionIntegers) {
    switch (s) {
    case MersenneTwister:
        return boost::make_shared<QuantExt::MultiPathGeneratorMersenneTwister>(process, timeGrid, seed, false);
    case MersenneTwisterAntithetic:
        return boost::make_shared<QuantExt::MultiPathGeneratorMersenneTwister>(process, timeGrid, seed, true);
    case Sobol:
        return boost::make_shared<QuantExt::MultiPathGeneratorSobol>(process, timeGrid, seed, directionIntegers);
    case SobolBrownianBridge:
        return boost::make_shared<QuantExt::MultiPathGeneratorSobolBrownianBridge>(process, timeGrid, ordering, seed,
                                                                                directionIntegers);
    default:
        QL_FAIL("Unknown sequence type");
    }
}

std::ostream& operator<<(std::ostream& out, const SequenceType s) {
    switch (s) {
    case MersenneTwister:
        return out << "MersenneTwister";
    case MersenneTwisterAntithetic:
        return out << "MersenneTwisterAntithetic";
    case Sobol:
        return out << "Sobol";
    case SobolBrownianBridge:
        return out << "SobolBrownianBridge";
    default:
        return out << "Unknown sequence type";
    }
}

} // namespace QuantExt


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS_HOMOGENEOUS_SYSTEM_HPP_INCLUDED
#define BOOST_UNITS_HOMOGENEOUS_SYSTEM_HPP_INCLUDED

#include <boost/mpl/bool.hpp>

#include <boost/units/config.hpp>
#include <boost/units/static_rational.hpp>

#ifdef BOOST_UNITS_CHECK_HOMOGENEOUS_UNITS

#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/not.hpp>

#include <boost/units/detail/linear_algebra.hpp>

#endif

namespace boost {

namespace units {

/// A system that can uniquely represent any unit
/// which can be composed from a linearly independent set
/// of base units.  It is safe to rebind a unit with
/// such a system to different dimensions.
///
/// Do not construct this template directly.  Use
/// make_system instead.
template<class L>
struct homogeneous_system {
    /// INTERNAL ONLY
    typedef L type;
};

template<class T, class E>
struct static_power;

template<class T, class R>
struct static_root;

/// INTERNAL ONLY
template<class L, long N, long D>
struct static_power<homogeneous_system<L>, static_rational<N,D> >
{
    typedef homogeneous_system<L> type;
};

/// INTERNAL ONLY
template<class L, long N, long D>
struct static_root<homogeneous_system<L>, static_rational<N,D> >
{
    typedef homogeneous_system<L> type;
};

namespace detail {

template<class System, class Dimensions>
struct check_system;

#ifdef BOOST_UNITS_CHECK_HOMOGENEOUS_UNITS

template<class L, class Dimensions>
struct check_system<homogeneous_system<L>, Dimensions> :
    boost::mpl::not_<
        boost::is_same<
            typename calculate_base_unit_exponents<
                L,
                Dimensions
            >::type,
            inconsistent
        >
    > {};

#else

template<class L, class Dimensions>
struct check_system<homogeneous_system<L>, Dimensions> : mpl::true_ {};

#endif

} // namespace detail

} // namespace units

} // namespace boost

#if BOOST_UNITS_HAS_BOOST_TYPEOF

#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()

BOOST_TYPEOF_REGISTER_TEMPLATE(boost::units::homogeneous_system, (class))

#endif

#endif


#pragma once
#include "svm_model.h"
#include "params_io.h"
#include <boost/filesystem.hpp>
#include <svm.h>
#include <algorithm>
#include <vector>
//#include <iostream>

namespace mlmodels
{
namespace svm
{

namespace
{

using node_data = std::vector<svm_node>;

const auto UNKOWN_FEATURES_COUNT = 9999999u;

template<typename It>
value_type best_prob(It from, It to)
{
    std::sort(from, to, [](auto left, auto right) {
                    return left > right;
                }
    );
    return *from;
}

template<typename T>
T* allocate(std::size_t nodes)
{
    return static_cast<T*>(malloc(sizeof(T) *nodes));
}

template<typename T>
void deallocate(T* node)
{
    free(node);
}

template<typename It>
void fill_node(svm_node* row_node, It row_begin, It row_end)
{
    unsigned int index = 1;
    std::for_each(row_begin, row_end, [&row_node, &index](auto val) {
            row_node->index = index++;
            row_node->value = val;
            ++row_node;
        }
    );
    row_node->index = -1;
}

template<typename It>
svm_node* allocate_node(It row_begin, It row_end, std::size_t cols)
{
    auto row_node = allocate<svm_node>(cols+1);
    fill_node(row_node, row_begin, row_end);
    return row_node;
}

template<typename It>
value_type run_prediction(It row_begin, It row_end, std::size_t features,
        double* prob_est, svm_node* pred_arg, const svm_model* model)
{
    fill_node(pred_arg, row_begin, row_end);
    svm_predict_probability(model, pred_arg, prob_est);
    return best_prob(prob_est, prob_est + features);
}

bool do_validate(const model_type& model, std::size_t columns);

inline bool validate(const model_type& model, const class_data& cd) {
    return do_validate(model, cd.size());
}

inline bool validate(const model_type& model, const training_data& td) {
    return do_validate(model, columns(td));
}

using prediction_function_t = value_type(*)(const svm_model*,
                                double* , const node_data&, unsigned int);
value_type predict_no_probs(const svm_model*,
                    double* , const node_data&, unsigned int);
value_type predict_by_probs(const svm_model*,
                    double* , const node_data&, unsigned int);

prediction_function_t assign_prob_func(const svm_parameter& params)
{
    switch (params.svm_type) {
        case ::NU_SVR:
        case ::EPSILON_SVR:
        case ::ONE_CLASS:
            return predict_no_probs;
        default:
            return params.probability ? predict_by_probs : predict_no_probs;
    }
}

value_type predict_no_probs(const svm_model* the_model,
                double* , const node_data& pred_arg, unsigned int)
{
    return static_cast<value_type>(svm_predict(the_model, pred_arg.data()));
}

value_type predict_by_probs(const svm_model* the_model,
                double* prob_est, const node_data& pred_arg, unsigned int classes)
{
    svm_predict_probability(the_model, pred_arg.data(), prob_est);
    return best_prob(prob_est, prob_est + classes);
}

}   // end of local namespace

void release_model(svm_model* m)
{
    svm_free_and_destroy_model(&m);
}

struct model_args
{
    svm_problem data;

    model_args()
    {
        data.l = -1;
        data.y = nullptr;
        data.x = nullptr;
    }

    model_args(const training_data& from, const class_data& labels) : model_args{}
    {
        using range = training_data::index_range;
        if (rows(from) != labels.size()) {
            throw std::runtime_error{"invalid arguments for svm model training - number of rows for data " +
               std::to_string(rows(from)) + " != " + std::to_string(labels.size())};
        }

        data.l = rows(from);    // number of rows is the length
        data.y = allocate<double>(rows(from));
        data.x = allocate<svm_node*>(rows(from));
        auto cols = columns(from);
        for (auto r = 0; r < data.l; ++r) {
            auto row = from[boost::indices[r][range()]];
            data.x[r] = allocate_node(row.begin(), row.end(), cols);
            data.y[r] = labels[r];
        }
    }

    ~model_args()
    {
        if (data.l > 0 && data.x && data.y) {
            for (auto i = 0; i < data.l; i++) {
                   deallocate<svm_node>(data.x[i]);
            }
            deallocate<svm_node*>(data.x);
            deallocate<double>(data.y);
        }
    }

};

const svm_problem* recast(const model_args& from) {
    return &from.data;
}

svm_model* run_train(const model_args& arg, const ::svm_parameter& params)
{
 //   std::cout<<"start running model train, with args:\n"<<params<<std::endl;
    return ::svm_train(recast(arg), &params);
 //   std::cout<<"finish model training"<<std::endl;
}

struct model_impl
{
    using training_args = model_args;
    using model_type = svm_model*;

    training_args args4train;
    model_type the_model = nullptr;
    unsigned int features_count = 0;

    model_impl()
    {
    }

    model_impl(const svm_parameter& params,
            const training_data& from, const class_data& labels) :
                    args4train{from, labels},
                    the_model{nullptr},
                    features_count{0u},
                    predict_op{assign_prob_func(params)}
    {
  //      std::cout<<"run training on "<<columns(from)<<" X "<<rows(from)<< " of data and "<<labels.size()<<" rows of labels"<<std::endl;
        the_model = run_train(args4train, params);
        if (the_model) {
            features_count = columns(from);
        }
    }

    ~model_impl()
    {
        if (the_model) {
            svm_free_and_destroy_model(&the_model);
        }
    }

    explicit model_impl(const char* from) :
            args4train{},
            the_model{svm_load_model(from)}
    {
        if (the_model) {
            features_count = UNKOWN_FEATURES_COUNT;
        }
    }

    unsigned int features() const
    {
        return features_count;
    }

    bool good() const {
        return features() != 0u;
    }

    bool correct(const testing_data& data) const {
        return correct(columns(data));
    }

    bool correct(const class_data& data) const {
        return correct(data.size());
    }

    bool correct(std::size_t size) const {
        return the_model && size != 0 && (features() == UNKOWN_FEATURES_COUNT || features() == size);
    }

    class_data test(const testing_data& data) const
    {
        using range = training_data::index_range;

        if (correct(data)) {
            auto classes = svm_get_nr_class(the_model);
            std::vector<double> prob_est(classes, -17.f);
            auto r = rows(data);
            auto cols = columns(data);
            class_data ans(r, 0);
            node_data pred_arg{cols+1};
            for (auto i = 0u; i < r; ++i) {
                auto row = data[boost::indices[i][range()]];
                fill_node(pred_arg.data(), row.begin(), row.end());
                //svm_predict_probability(the_model, pred_arg.data(), prob_est.data());
                ans[i] = predict_op(the_model, prob_est.data(), pred_arg, classes); //best_prob(std::begin(prob_est), std::end(prob_est));
            }
            return ans;
        }
        return class_data{};
    }

    value_type predict(const class_data& samples) const
    {
        if (correct(samples)) {
            node_data pred_arg{samples.size()+1};
            fill_node(pred_arg.data(), std::begin(samples), std::end(samples));
            return predict_no_probs(the_model, nullptr, pred_arg, 0u);
        }
        return std::numeric_limits<value_type>::max();
    }

    testing_result predict(const testing_data& data) const
    {
        using range = training_data::index_range;

        if (correct(data)) {
            auto r = rows(data);
            auto cols = columns(data);
            testing_result output(r, testing_result::value_type{});
            node_data pred_arg{cols+1};
            for (auto i = 0u; i < r; ++i) {
                auto row = data[boost::indices[i][range()]];
                fill_node(pred_arg.data(), row.begin(), row.end());
                output[i] = static_cast<value_type>(svm_predict(the_model, pred_arg.data()));
            }
            return output;

        }
        return testing_result{};
    }

    bool save(const char* to) const
    {
        if (the_model) {
            return svm_save_model(to, the_model) > -1;
        } else {
            return false;
        }
    }
    predictions_list detailed_testing(const testing_data& data) const
    {
        using range = training_data::index_range;

        if (correct(data)) {
            auto classes = svm_get_nr_class(the_model);
            std::vector<double> prob_est(classes, -17.f);
            auto r = rows(data);
            auto cols = columns(data);
            predictions_list ans;

            node_data pred_arg{cols+1};
            for (auto i = 0u; i < r; ++i) {
                auto row = data[boost::indices[i][range()]];
                fill_node(pred_arg.data(), row.begin(), row.end());
                svm_predict_probability(the_model, pred_arg.data(), prob_est.data());
                ans.push_back(prob_est);
            }
            return ans;
        }
        return predictions_list{};
    }

private:
    prediction_function_t predict_op = nullptr;
};

bool save(model_type model, const char* to)
{
    if (!model) {
        return false;
    }
    boost::filesystem::path p{to};
    if (boost::filesystem::exists(p.parent_path())) {
        return model->save(to);
    } else {
        return false;
    }
}

model_type load(const char* from)
{
    boost::filesystem::path p{from};
    if (boost::filesystem::exists(p)) {
        return std::make_shared<model_impl>(from);
    }
    return model_type{};

}

model_type create(const svm_parameter& params,
                    const training_data& data, const class_data& targets)
{
    if (empty(data) || targets.empty() || rows(data) != targets.size()) {
        return model_type{};
    } else {
        return std::make_shared<model_impl>(params, data, targets);
    }
}

class_data test(model_type model, const testing_data& data)
{
    if (validate(model, data)) {
        return model->test(data);
    }
    return class_data{};
}

value_type predict(model_type model, const class_data& samples)
{
    if (validate(model, samples)) {
        return model->predict(samples);
    }
    return std::numeric_limits<value_type>::max();
}

testing_result predict(model_type model, const testing_data& data)
{
    if (validate(model, data)) {
        return model->predict(data);
    }
    return testing_result{};
}

predictions_list test_detailed(model_type model, const testing_data& data)
{
    if (model) {
        return model->detailed_testing(data);
    }
    return predictions_list{};
}

namespace {

bool do_validate(const model_type& model, std::size_t columns) {
    return model && model->correct(columns);
}

}   // end of local namespace
}   // end of namesapce svm
}   // end of mlmodels


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

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

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/function/saturated.hpp>
#include <boost/simd/function/group.hpp>
#include <boost/simd/function/multiplies.hpp>
#include <boost/simd/function/split_multiplies.hpp>
#include <boost/simd/detail/dispatch/meta/upgrade.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;

  BOOST_DISPATCH_OVERLOAD ( multiplies_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::saturated_tag
                          , bs::pack_<bd::floating_<A0>, X>
                          , bs::pack_<bd::floating_<A0>, X>
                          )
  {
    BOOST_FORCEINLINE
    A0 operator()(const saturated_tag&, const A0& a0, const A0& a1) const BOOST_NOEXCEPT
    {
      return a0*a1;
    }
  };

  BOOST_DISPATCH_OVERLOAD_IF( multiplies_
                            , (typename A0, typename X)
                            , (bd::is_upgradable<A0>)
                            , bd::cpu_
                            , bs::saturated_tag
                            , bs::pack_<bd::integer_<A0>, X>
                            , bs::pack_<bd::integer_<A0>, X>
                            )
  {
    BOOST_FORCEINLINE
    A0 operator()(const saturated_tag &, const A0& a0, const A0& a1) const BOOST_NOEXCEPT
    {
      auto sm = split_multiplies(a0, a1);
      return saturated_(group)(sm.first, sm.second);
    }
  };
} } }

#endif


#define BOOST_TEST_MODULE DataTestSuite
#define BOOST_TEST_DYN_LINK

#include <boost/test/included/unit_test.hpp>
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
#include <memory>
#include <hops/hops.hpp>

BOOST_AUTO_TEST_SUITE(DataTestSuite)

    BOOST_AUTO_TEST_CASE(ChainDataTracking) {
        Eigen::MatrixXd A(4, 2);
        Eigen::VectorXd b(4);
        Eigen::VectorXd x(2);

        A << 1, 0,
                0, 1,
                -1, 0,
                0, -1;
        b << 1, 1, 0, 0;
        x << 0.5, 0.5;

        hops::ChainData chainData;
        long numberOfSamples = 1000;

        if (true) {
            hops::RandomNumberGenerator randomNumberGenerator;
            std::unique_ptr<hops::MarkovChain> mc = hops::MarkovChainFactory::createMarkovChain(
                    hops::MarkovChainType::HitAndRun, A, b, x);
            mc->installDataObject(chainData);

            mc->draw(randomNumberGenerator, numberOfSamples);
        }

        BOOST_CHECK_EQUAL(chainData.getAcceptanceRates().size(), numberOfSamples);
        BOOST_CHECK_EQUAL(chainData.getStates().size(), numberOfSamples);
        BOOST_CHECK_EQUAL(chainData.getTimestamps().size(), numberOfSamples);
    }

    BOOST_AUTO_TEST_CASE(SetProblemAndRun) {
        unsigned dimension = 2;
        Eigen::MatrixXd A(4, dimension);
        Eigen::VectorXd b(4);
        Eigen::VectorXd x(dimension);

        A << 1, 0,
                0, 1,
                -1, 0,
                0, -1;
        b << 1, 1, 0, 0;
        x << 0.5, 0.5;

        unsigned long numberOfSamples = 1000;

        hops::MultivariateGaussianModel<Eigen::MatrixXd, Eigen::VectorXd>
                model(Eigen::VectorXd::Zero(dimension), Eigen::MatrixXd::Identity(dimension, dimension));

        hops::Problem problem(A, b, model);
        problem.setStartingPoint(x);
        hops::Run<decltype(model)> run(problem, hops::MarkovChainType::HitAndRun, numberOfSamples);

        run.sample();

        BOOST_CHECK_EQUAL(run.getData().getStates()[0]->size(), numberOfSamples);
    }

    BOOST_AUTO_TEST_CASE(CopyRun) {
        unsigned dimension = 2;
        Eigen::MatrixXd A(4, dimension);
        Eigen::VectorXd b(4);
        Eigen::VectorXd x(dimension);

        A << 1, 0,
                0, 1,
                -1, 0,
                0, -1;
        b << 1, 1, 0, 0;
        x << 0.5, 0.5;

        hops::MultivariateGaussianModel<Eigen::MatrixXd, Eigen::VectorXd>
                model(Eigen::VectorXd::Zero(dimension), Eigen::MatrixXd::Identity(dimension, dimension));

        BOOST_CHECK_NO_THROW(
                hops::Problem problem(A, b, model);
                problem.setStartingPoint(x);
                hops::Run<decltype(model)> run(problem);

                hops::Run<decltype(model)> run2(run);
        );
    }

    BOOST_AUTO_TEST_CASE(ComputeStatisticsAfterRun) {
        unsigned dimension = 2;
        Eigen::MatrixXd A(4, dimension);
        Eigen::VectorXd b(4);
        Eigen::VectorXd x(dimension);

        A << 1, 0,
                0, 1,
                -1, 0,
                0, -1;
        b << 1, 1, 0, 0;
        x << 0.5, 0.5;

        unsigned long numberOfSamples = 10;

        hops::MultivariateGaussianModel<Eigen::MatrixXd, Eigen::VectorXd>
                model(Eigen::VectorXd::Zero(dimension), Eigen::MatrixXd::Identity(dimension, dimension));

        hops::Problem problem(A, b, model);
        problem.setStartingPoint(x);
        hops::Run<decltype(model)> run(problem, hops::MarkovChainType::HitAndRun, numberOfSamples);

        run.sample();

        BOOST_CHECK_EQUAL(run.getData().getStates()[0]->size(), numberOfSamples);

        run.getData().computeExpectedSquaredJumpDistance();

        double expectedResult = 3.20334;
        double actualResult = hops::computeExpectedSquaredJumpDistance(run.getData())(0);

        BOOST_CHECK_CLOSE(expectedResult, actualResult, 0.01);
    }
}


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

#include "component_algorithms.hpp"

#include "tile_definition.hpp"
#include "tile_library.hpp"

#include "core/transform.hpp"

#include <boost/optional.hpp>

namespace components
{
    core::IntRect tile_group_bounding_box(const TileGroupDefinition& tile_group, const TileLibrary& tile_lib)
    {
        boost::optional<core::IntRect> result;
        for (const auto& sub_tile : tile_group.sub_tiles())
        {
            if (auto tile = tile_lib.tile(sub_tile.id))
            {
                core::IntRect pat_rect = tile->pattern_rect;

                core::Rect<double> sub_rect(0.0, 0.0,
                    static_cast<double>(pat_rect.width), static_cast<double>(pat_rect.height));

                core::Rect<double> transformed_rect = core::transform_rect(sub_rect, sub_tile.rotation);

                core::IntRect int_rect(
                    static_cast<std::int32_t>(sub_tile.position.x - transformed_rect.width * 0.5),
                    static_cast<std::int32_t>(sub_tile.position.y - transformed_rect.height * 0.5),
                    static_cast<std::int32_t>(transformed_rect.width),
                    static_cast<std::int32_t>(transformed_rect.height)
                );

                if (!result) result = int_rect;
                else result = combine(*result, int_rect);
            }
        }

        if (result) return *result;

        return {};
    }
}


// Copyright 2019-2022 Cambridge Quantum Computing
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <boost/graph/adjacency_list.hpp>
#include <catch2/catch.hpp>
#include <iostream>
#include <vector>

#include "Architecture/Architecture.hpp"
#include "Graphs/ArticulationPoints.hpp"

namespace tket {
namespace graphs {
namespace tests {
namespace test_ArticulationPoints {

using Edge = std::pair<unsigned, unsigned>;
using Vertex = unsigned;

static UndirectedConnGraph<Node> get_graph(std::vector<Edge> edges) {
  unsigned n_vertices = 0;
  for (auto [e1, e2] : edges) {
    if (e1 >= n_vertices) n_vertices = e1 + 1;
    if (e2 >= n_vertices) n_vertices = e2 + 1;
  }
  UndirectedConnGraph<Node> graph(n_vertices);
  for (unsigned i = 0; i < n_vertices; ++i) {
    graph[boost::vertex(i, graph)] = Node(i);
  }
  for (auto [e1, e2] : edges) {
    boost::add_edge(e1, e2, graph);
  }
  return graph;
}

SCENARIO("Build a BicomponentGraph") {
  UndirectedConnGraph<Node> graph;
  std::vector<std::vector<Vertex>> bicomps;
  std::vector<std::pair<unsigned, unsigned>> select_which;
  GIVEN("A simple graph") {
    graph = get_graph({{0, 1}, {1, 2}, {2, 3}, {1, 3}, {4, 0}});
    bicomps = {{0, 1}, {0, 4}, {1, 2, 3}};
    select_which = {{1, 2}, {4, 3}, {2, 3}};
  }
  GIVEN("A graph with 4 biconnected components") {
    bicomps = {{0, 1, 2}, {3, 4, 5, 6}, {7, 8, 9}, {10, 11}};
    std::vector<Vertex> links{3, 7, 4, 5};

    std::vector<Edge> edges;
    for (unsigned i = 0; i < links.size(); ++i) {
      auto &bicomp = bicomps[i];
      edges.push_back({bicomp[0], links[i]});
      for (unsigned j = 0; j < bicomp.size(); ++j) {
        for (unsigned k = j + 1; k < bicomp.size(); ++k) {
          edges.push_back({bicomp[j], bicomp[k]});
        }
      }
    }

    graph = get_graph(edges);
    select_which = {{0, 2}, {7, 4}, {10, 6}};
  }

  auto bg = detail::BicomponentGraph<Node>(graph);
  auto tester = detail::BicomponentGraphTester(&bg);
  auto &g = tester.get_graph();

  REQUIRE(boost::num_vertices(g) == tester.n_components());

  // Nodes in each comp must share a component vertex
  for (const std::vector<Vertex> &comp : bicomps) {
    std::set<unsigned> common_comps = tester.get_comps(Node(comp[0]));
    for (Vertex v : comp) {
      Node n(v);
      std::set<unsigned> new_set;
      std::set_intersection(
          common_comps.begin(), common_comps.end(), tester.get_comps(n).begin(),
          tester.get_comps(n).end(), std::inserter(new_set, new_set.begin()));
      common_comps = new_set;
    }
    REQUIRE(common_comps.size() == 1);
  }

  // select components and make sure selection is propagated
  for (auto [selecting, nb_selected] : select_which) {
    bg.select_comps(std::vector<Node>{Node(selecting)});
    bg.propagate_selected_comps();
    unsigned actual_nb_selected = 0;
    for (bool p : tester.get_selected_comps()) {
      actual_nb_selected += p;
    }
    REQUIRE(actual_nb_selected == nb_selected);
  }
}

SCENARIO("Run get_subgraph_aps") {
  UndirectedConnGraph<Node> graph = get_graph(
      {{0, 1},
       {1, 2},
       {0, 2},
       {2, 3},
       {3, 4},
       {2, 4},
       {4, 5},
       {5, 6},
       {6, 7},
       {7, 5},
       {4, 7},
       {7, 8},
       {8, 9},
       {7, 9}});
  UndirectedConnGraph<Node> subgraph =
      get_graph({{1, 2}, {0, 2}, {2, 3}, {6, 7}, {7, 5}});
  auto aps = get_subgraph_aps<Node>(graph, subgraph);
  auto expected_aps = std::set<Node>{Node(2), Node(4), Node(7)};

  REQUIRE(aps.size() == expected_aps.size());
  for (Node n : expected_aps) {
    REQUIRE(aps.count(n));
  }
}

SCENARIO("Find APs of disconnected nodes") {
  UndirectedConnGraph<Node> graph(2);
  for (unsigned i = 0; i < 2; ++i) {
    graph[boost::vertex(i, graph)] = Node(i);
  }

  UndirectedConnGraph<Node> subgraph(1);
  subgraph[boost::vertex(0, graph)] = Node(0);

  REQUIRE(get_subgraph_aps<Node>(graph, subgraph).size() == 0);
}

SCENARIO("Test APs in Architecture", "[architectures],[routing]") {
  Architecture arc({{0, 1}, {1, 2}, {2, 3}});
  node_set_t ap = arc.get_articulation_points();

  GIVEN("Removing node that preserves connected graph") {
    REQUIRE(ap.find(Node(0)) == ap.end());
    REQUIRE(ap.find(Node(3)) == ap.end());
  }
  GIVEN("Removing node doesn't preserve connected graph") {
    REQUIRE(ap.find(Node(1)) != ap.end());
    REQUIRE(ap.find(Node(2)) != ap.end());
  }

  Architecture arc2({{0, 1}, {0, 2}, {0, 3}, {2, 3}});
  ap = arc2.get_articulation_points();
  REQUIRE(ap.find(Node(0)) != ap.end());

  arc2.remove_node(Node(1));
  ap = arc2.get_articulation_points();
  REQUIRE(ap.find(Node(0)) == ap.end());
}

}  // namespace test_ArticulationPoints
}  // namespace tests
}  // namespace graphs
}  // namespace tket


/**
 * \ file GainColoredCompressorFilter.cpp
 */

#include <ATK/Dynamic/GainColoredCompressorFilter.h>

#include <ATK/Core/InPointerFilter.h>
#include <ATK/Core/OutPointerFilter.h>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp>

#include <boost/math/constants/constants.hpp>
#include <boost/scoped_array.hpp>

#define PROCESSSIZE (64)

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_const_1_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = 1;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_threshold(10);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_CLOSE(1, outdata[i], 0.1);
  }
}

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_const_0_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = 0;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_CLOSE(1, outdata[i], 0.1); // if input is zero, we still need a gain of 1 to have a progression of 1 for values < threshold
  }
}

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_const_1_threshold_05_ratio_2_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = 1;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_threshold(0.5);
  filter.set_ratio(2);
  filter.set_softness(1);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_CLOSE(0.836990654, outdata[i], 0.1);
  }
}

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_const_1_threshold_05_ratio_4_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = 1;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_threshold(0.5);
  filter.set_ratio(4);
  filter.set_softness(1);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_CLOSE(0.765739262, outdata[i], 0.1);
  }
}

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_always_more_1_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = i/1024.;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_threshold(1);
  filter.set_quality(.1);
  filter.set_color(.1);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_GE(outdata[i], 1);
  }
}

BOOST_AUTO_TEST_CASE( GainColoredCompressorFilter_always_less_1_test )
{
  boost::scoped_array<float> data(new float[PROCESSSIZE]);
  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = i/1024.;
  }

  ATK::InPointerFilter<float> generator(data.get(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  boost::scoped_array<float> outdata(new float[PROCESSSIZE]);

  ATK::GainColoredCompressorFilter<float> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);
  filter.set_threshold(1);
  filter.set_quality(.1);
  filter.set_color(-.1);

  ATK::OutPointerFilter<float> output(outdata.get(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(int64_t i = 0; i < PROCESSSIZE; ++i)
  {
    BOOST_REQUIRE_LE(outdata[i], 1);
  }
}


/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE gw_test

// Standard includes
#include <fstream>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/xtp/gw.h"

// VOTCA includes
#include <votca/tools/eigenio_matrixmarket.h>

using namespace votca::xtp;
using namespace std;

BOOST_AUTO_TEST_SUITE(gw_test)

BOOST_AUTO_TEST_CASE(gw_full) {

  Eigen::VectorXd mo_eigenvalues = Eigen::VectorXd::Zero(17);
  mo_eigenvalues << -10.6784, -0.746424, -0.394948, -0.394948, -0.394948,
      0.165212, 0.227713, 0.227713, 0.227713, 0.763971, 0.763971, 0.763971,
      1.05054, 1.13372, 1.13372, 1.13372, 1.72964;
  Eigen::MatrixXd mo_eigenvectors =
      votca::tools::EigenIO_MatrixMarket::ReadMatrix(
          std::string(XTP_TEST_DATA_FOLDER) + "/gw/mo_eigenvectors.mm");

  Eigen::MatrixXd vxc = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/gw/vxc.mm");

  Orbitals orbitals;
  orbitals.QMAtoms().LoadFromFile(std::string(XTP_TEST_DATA_FOLDER) +
                                  "/gw/molecule.xyz");
  BasisSet basis;
  basis.Load(std::string(XTP_TEST_DATA_FOLDER) + "/gw/3-21G.xml");
  orbitals.setDFTbasisName(std::string(XTP_TEST_DATA_FOLDER) + "/gw/3-21G.xml");
  AOBasis aobasis;
  aobasis.Fill(basis, orbitals.QMAtoms());
  orbitals.setBasisSetSize(17);
  orbitals.setNumberOfOccupiedLevels(4);
  orbitals.MOs().eigenvectors() = mo_eigenvectors;
  Logger log;
  TCMatrix_gwbse Mmn{log};
  Mmn.Initialize(aobasis.AOBasisSize(), 0, 16, 0, 16);
  Mmn.Fill(aobasis, aobasis, mo_eigenvectors);

  GW::options opt;
  opt.ScaHFX = 0;
  opt.homo = 4;
  opt.qpmax = 16;
  opt.qpmin = 0;
  opt.rpamax = 16;
  opt.rpamin = 0;
  opt.gw_sc_max_iterations = 1;
  opt.eta = 1e-3;
  opt.sigma_integration = "ppm";
  opt.reset_3c = 5;
  opt.qp_solver = "grid";
  opt.qp_grid_steps = 601;
  opt.qp_grid_spacing = 0.005;
  opt.gw_mixing_order = 0;
  opt.gw_mixing_alpha = 0.7;
  opt.g_sc_limit = 1e-5;
  opt.g_sc_max_iterations = 50;
  opt.gw_sc_limit = 1e-5;

  GW gw(log, Mmn, vxc, mo_eigenvalues);
  gw.configure(opt);

  Eigen::MatrixXd ref = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/gw/ref.mm");

  gw.CalculateGWPerturbation();
  Eigen::VectorXd diag = gw.getGWAResults();
  bool check_diag = ref.diagonal().isApprox(diag, 1e-4);
  if (!check_diag) {
    cout << "GW energies" << endl;
    cout << diag << endl;
    cout << "GW energies ref" << endl;
    cout << ref.diagonal() << endl;
  }
  BOOST_CHECK_EQUAL(check_diag, true);

  gw.CalculateHQP();
  Eigen::MatrixXd offdiag = gw.getHQP();

  bool check_offdiag = ref.isApprox(offdiag, 1e-4);
  if (!check_offdiag) {
    cout << "GW energies" << endl;
    cout << offdiag << endl;
    cout << "GW energies ref" << endl;
    cout << ref << endl;
  }
  BOOST_CHECK_EQUAL(check_offdiag, true);
}

BOOST_AUTO_TEST_CASE(gw_full_QP_grid) {

  Eigen::VectorXd mo_eigenvalues = Eigen::VectorXd::Zero(17);
  mo_eigenvalues << -10.6784, -0.746424, -0.394948, -0.394948, -0.394948,
      0.165212, 0.227713, 0.227713, 0.227713, 0.763971, 0.763971, 0.763971,
      1.05054, 1.13372, 1.13372, 1.13372, 1.72964;
  Eigen::MatrixXd mo_eigenvectors =
      votca::tools::EigenIO_MatrixMarket::ReadMatrix(
          std::string(XTP_TEST_DATA_FOLDER) + "/gw/mo_eigenvectors2.mm");

  Eigen::MatrixXd vxc = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/gw/vxc2.mm");

  Orbitals orbitals;
  orbitals.QMAtoms().LoadFromFile(std::string(XTP_TEST_DATA_FOLDER) +
                                  "/gw/molecule.xyz");
  BasisSet basis;
  basis.Load(std::string(XTP_TEST_DATA_FOLDER) + "/gw/3-21G.xml");
  orbitals.setDFTbasisName(std::string(XTP_TEST_DATA_FOLDER) + "/gw/3-21G.xml");
  AOBasis aobasis;
  aobasis.Fill(basis, orbitals.QMAtoms());
  orbitals.setBasisSetSize(17);
  orbitals.setNumberOfOccupiedLevels(4);
  orbitals.MOs().eigenvectors() = mo_eigenvectors;
  Logger log;
  TCMatrix_gwbse Mmn{log};
  Mmn.Initialize(aobasis.AOBasisSize(), 0, 16, 0, 16);
  Mmn.Fill(aobasis, aobasis, mo_eigenvectors);

  GW::options opt;
  opt.ScaHFX = 0;
  opt.homo = 4;
  opt.qpmax = 16;
  opt.qpmin = 0;
  opt.rpamax = 16;
  opt.rpamin = 0;
  opt.gw_sc_max_iterations = 1;
  opt.qp_solver = "grid";
  opt.eta = 1e-3;
  opt.sigma_integration = "ppm";
  opt.reset_3c = 5;
  opt.qp_grid_steps = 601;
  opt.qp_grid_spacing = 0.005;
  opt.gw_mixing_order = 0;
  opt.gw_mixing_alpha = 0.7;
  opt.g_sc_limit = 1e-5;
  opt.g_sc_max_iterations = 50;
  opt.gw_sc_limit = 1e-5;

  GW gw(log, Mmn, vxc, mo_eigenvalues);
  gw.configure(opt);

  gw.CalculateGWPerturbation();
  Eigen::VectorXd diag = gw.getGWAResults();

  Eigen::MatrixXd ref = votca::tools::EigenIO_MatrixMarket::ReadMatrix(
      std::string(XTP_TEST_DATA_FOLDER) + "/gw/ref2.mm");

  bool check_diag = ref.diagonal().isApprox(diag, 1e-4);
  if (!check_diag) {
    cout << "GW energies" << endl;
    cout << diag << endl;
    cout << "GW energies ref" << endl;
    cout << ref.diagonal() << endl;
  }
  BOOST_CHECK_EQUAL(check_diag, true);

  gw.CalculateHQP();
  Eigen::MatrixXd offdiag = gw.getHQP();

  bool check_offdiag = ref.isApprox(offdiag, 1e-4);
  if (!check_offdiag) {
    cout << "GW energies" << endl;
    cout << offdiag << endl;
    cout << "GW energies ref" << endl;
    cout << ref << endl;
  }
  BOOST_CHECK_EQUAL(check_offdiag, true);
}

BOOST_AUTO_TEST_SUITE_END()


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2009-2012 Barend Gehrels, Amsterdam, the Netherlands.

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

#include <iostream>
#include <string>

#define BOOST_GEOMETRY_NO_BOOST_TEST

#include <test_overlay_p_q.hpp>

#include <boost/program_options.hpp>
#include <boost/timer.hpp>

template <typename Polygon>
inline void make_star(Polygon& polygon,
    int count, double factor1, double factor2, long double offset = 0)
{
    typedef typename bg::point_type<Polygon>::type p;
    typedef typename bg::select_most_precise
        <
            typename bg::coordinate_type<Polygon>::type,
            long double
        >::type coordinate_type;

    // Create star
    coordinate_type cx = 25.0;
    coordinate_type cy = 25.0;

    coordinate_type dx = 50.0;
    coordinate_type dy = 50.0;

    coordinate_type half = 0.5;
    coordinate_type two = 2.0;

    coordinate_type a1 = coordinate_type(factor1) * half * dx;
    coordinate_type b1 = coordinate_type(factor1) * half * dy;
    coordinate_type a2 = coordinate_type(factor2) * half * dx;
    coordinate_type b2 = coordinate_type(factor2) * half * dy;

    coordinate_type pi = boost::math::constants::pi<long double>();
    coordinate_type delta = pi * two / coordinate_type(count - 1);
    coordinate_type angle = coordinate_type(offset) * delta;
    for (int i = 0; i < count - 1; i++, angle += delta)
    {
        bool even = i % 2 == 0;
        coordinate_type s = sin(angle);
        coordinate_type c = cos(angle);
        coordinate_type x = cx + (even ? a1 : a2) * s;
        coordinate_type y = cy + (even ? b1 : b2) * c;
        bg::exterior_ring(polygon).push_back(bg::make<p>(x, y));

    }
    bg::exterior_ring(polygon).push_back(bg::exterior_ring(polygon).front());
}

template <typename T, typename CalculationType>
void test_star(int count, int min_points, int max_points, T rotation, p_q_settings const& settings)
{
    boost::timer t;
    typedef bg::model::d2::point_xy<T> point_type;
    typedef bg::model::polygon<point_type> polygon;

    int n = 0;
    for (int c = 0; c < count; c++)
    {
        for (int i = min_points; i <= max_points; i++)
        {
            std::ostringstream out;
            out << "_" << string_from_type<T>::name() << "_"
                << string_from_type<CalculationType>::name() << "_"
                << i << "_int";

            polygon p;
            make_star(p, i * 2 + 1, 0.5, 1.0);
            polygon q;
            make_star(q, i * 2 + 1, 0.5, 1.0, rotation);

            if (! test_overlay_p_q
                <
                    polygon,
                    CalculationType
                >(out.str(), p, q, settings))
            {
                return;
            }
            n++;
        }
    }
    std::cout
        << "polygons: " << n
        << " type: " << string_from_type<T>::name()
        << " time: " << t.elapsed()  << std::endl;
}

template <typename T, typename CalculationType>
void test_type(int count, int min_points, int max_points, T rotation, p_q_settings const& settings)
{
    test_star<T, CalculationType>(count, min_points, max_points, rotation, settings);
}

template <typename T>
void test_all(std::string const& type, int count, int min_points, int max_points, T rotation, p_q_settings settings)
{
    if (type == "float")
    {
        settings.tolerance = 1.0e-3;
        test_type<float, float>(count, min_points, max_points, rotation, settings);
    }
    else if (type == "double")
    {
        test_type<double, double>(count, min_points, max_points, rotation, settings);
    }
#if defined(HAVE_TTMATH)
    else if (type == "ttmath")
    {
        test_type<ttmath_big, ttmath_big>(count, min_points, max_points, rotation, settings);
    }
#endif
}

int main(int argc, char** argv)
{
    try
    {
        namespace po = boost::program_options;
        po::options_description description("=== recursive_polygons ===\nAllowed options");

        int count = 1;
        //int seed = static_cast<unsigned int>(std::time(0));
        std::string type = "float";
        int min_points = 9;
        int max_points = 9;
        bool ccw = false;
        bool open = false;
        double rotation = 1.0e-13;
        p_q_settings settings;

        description.add_options()
            ("help", "Help message")
            //("seed", po::value<int>(&seed), "Initialization seed for random generator")
            ("count", po::value<int>(&count)->default_value(1), "Number of tests")
            ("diff", po::value<bool>(&settings.also_difference)->default_value(false), "Include testing on difference")
            ("min_points", po::value<int>(&min_points)->default_value(9), "Minimum number of points")
            ("max_points", po::value<int>(&max_points)->default_value(9), "Maximum number of points")
            ("rotation", po::value<double>(&rotation)->default_value(1.0e-13), "Rotation angle")
            ("ccw", po::value<bool>(&ccw)->default_value(false), "Counter clockwise polygons")
            ("open", po::value<bool>(&open)->default_value(false), "Open polygons")
            ("type", po::value<std::string>(&type)->default_value("float"), "Type (float,double)")
            ("wkt", po::value<bool>(&settings.wkt)->default_value(false), "Create a WKT of the inputs, for all tests")
            ("svg", po::value<bool>(&settings.svg)->default_value(false), "Create a SVG for all tests")
        ;

        po::variables_map varmap;
        po::store(po::parse_command_line(argc, argv, description), varmap);
        po::notify(varmap);

        if (varmap.count("help"))
        {
            std::cout << description << std::endl;
            return 1;
        }

        test_all(type, count, min_points, max_points, rotation, settings);
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception " << e.what() << std::endl;
    }
    catch(...)
    {
        std::cout << "Other exception" << std::endl;
    }
    return 0;
}


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

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

 * See LICENSE for the license information

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

/**
 * @file    NonlinearFactorGraph.cpp
 * @brief   Factor Graph Consisting of non-linear factors
 * @author  Frank Dellaert
 * @author  Carlos Nieto
 * @author  Christian Potthast
 */

#include <cmath>
#include <limits>
#include <boost/foreach.hpp>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/geometry/Pose3.h>
#include <gtsam/inference/Ordering.h>
#include <gtsam/inference/FactorGraph-inst.h>
#include <gtsam/symbolic/SymbolicFactorGraph.h>
#include <gtsam/linear/GaussianFactorGraph.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>

#ifdef GTSAM_USE_TBB
#  include <tbb/parallel_for.h>
#endif

using namespace std;

namespace gtsam {

// Instantiate base classes
template class FactorGraph<NonlinearFactor>;

/* ************************************************************************* */
double NonlinearFactorGraph::probPrime(const Values& c) const {
  return exp(-0.5 * error(c));
}

/* ************************************************************************* */
void NonlinearFactorGraph::print(const std::string& str, const KeyFormatter& keyFormatter) const {
  cout << str << "size: " << size() << endl;
  for (size_t i = 0; i < factors_.size(); i++) {
    stringstream ss;
    ss << "factor " << i << ": ";
    if (factors_[i] != NULL) factors_[i]->print(ss.str(), keyFormatter);
  }
}

/* ************************************************************************* */
bool NonlinearFactorGraph::equals(const NonlinearFactorGraph& other, double tol) const {
  return Base::equals(other, tol);
}

/* ************************************************************************* */
void NonlinearFactorGraph::saveGraph(std::ostream &stm, const Values& values,
    const GraphvizFormatting& graphvizFormatting,
    const KeyFormatter& keyFormatter) const
{
  stm << "graph {\n";
  stm << "  size=\"" << graphvizFormatting.figureWidthInches << "," <<
    graphvizFormatting.figureHeightInches << "\";\n\n";

  FastSet<Key> keys = this->keys();

  // Local utility function to extract x and y coordinates
  struct { boost::optional<Point2> operator()(
    const Value& value, const GraphvizFormatting& graphvizFormatting)
  {
    if(const Pose2* p = dynamic_cast<const Pose2*>(&value)) {
      double x, y;
      switch (graphvizFormatting.paperHorizontalAxis) {
      case GraphvizFormatting::X: x = p->x(); break;
      case GraphvizFormatting::Y: x = p->y(); break;
      case GraphvizFormatting::Z: x = 0.0; break;
      case GraphvizFormatting::NEGX: x = -p->x(); break;
      case GraphvizFormatting::NEGY: x = -p->y(); break;
      case GraphvizFormatting::NEGZ: x = 0.0; break;
      default: throw std::runtime_error("Invalid enum value");
      }
      switch (graphvizFormatting.paperVerticalAxis) {
      case GraphvizFormatting::X: y = p->x(); break;
      case GraphvizFormatting::Y: y = p->y(); break;
      case GraphvizFormatting::Z: y = 0.0; break;
      case GraphvizFormatting::NEGX: y = -p->x(); break;
      case GraphvizFormatting::NEGY: y = -p->y(); break;
      case GraphvizFormatting::NEGZ: y = 0.0; break;
      default: throw std::runtime_error("Invalid enum value");
      }
      return Point2(x,y);
    } else if(const Pose3* p = dynamic_cast<const Pose3*>(&value)) {
      double x, y;
      switch (graphvizFormatting.paperHorizontalAxis) {
      case GraphvizFormatting::X: x = p->x(); break;
      case GraphvizFormatting::Y: x = p->y(); break;
      case GraphvizFormatting::Z: x = p->z(); break;
      case GraphvizFormatting::NEGX: x = -p->x(); break;
      case GraphvizFormatting::NEGY: x = -p->y(); break;
      case GraphvizFormatting::NEGZ: x = -p->z(); break;
      default: throw std::runtime_error("Invalid enum value");
      }
      switch (graphvizFormatting.paperVerticalAxis) {
      case GraphvizFormatting::X: y = p->x(); break;
      case GraphvizFormatting::Y: y = p->y(); break;
      case GraphvizFormatting::Z: y = p->z(); break;
      case GraphvizFormatting::NEGX: y = -p->x(); break;
      case GraphvizFormatting::NEGY: y = -p->y(); break;
      case GraphvizFormatting::NEGZ: y = -p->z(); break;
      default: throw std::runtime_error("Invalid enum value");
      }
     return Point2(x,y);
    } else if(const Point3* p = dynamic_cast<const Point3*>(&value)) {
      double x, y;
      switch (graphvizFormatting.paperHorizontalAxis) {
      case GraphvizFormatting::X: x = p->x(); break;
      case GraphvizFormatting::Y: x = p->y(); break;
      case GraphvizFormatting::Z: x = p->z(); break;
      case GraphvizFormatting::NEGX: x = -p->x(); break;
      case GraphvizFormatting::NEGY: x = -p->y(); break;
      case GraphvizFormatting::NEGZ: x = -p->z(); break;
      default: throw std::runtime_error("Invalid enum value");
      }
      switch (graphvizFormatting.paperVerticalAxis) {
      case GraphvizFormatting::X: y = p->x(); break;
      case GraphvizFormatting::Y: y = p->y(); break;
      case GraphvizFormatting::Z: y = p->z(); break;
      case GraphvizFormatting::NEGX: y = -p->x(); break;
      case GraphvizFormatting::NEGY: y = -p->y(); break;
      case GraphvizFormatting::NEGZ: y = -p->z(); break;
      default: throw std::runtime_error("Invalid enum value");
      }
      return Point2(x,y);
    } else {
      return boost::none;
    }
  }} getXY;

  // Find bounds
  double minX = numeric_limits<double>::infinity(), maxX = -numeric_limits<double>::infinity();
  double minY = numeric_limits<double>::infinity(), maxY = -numeric_limits<double>::infinity();
  BOOST_FOREACH(Key key, keys) {
    if(values.exists(key)) {
      boost::optional<Point2> xy = getXY(values.at(key), graphvizFormatting);
      if(xy) {
        if(xy->x() < minX)
          minX = xy->x();
        if(xy->x() > maxX)
          maxX = xy->x();
        if(xy->y() < minY)
          minY = xy->y();
        if(xy->y() > maxY)
          maxY = xy->y();
      }
    }
  }

  // Create nodes for each variable in the graph
  bool firstTimePoses = true;
  Key lastKey;
  BOOST_FOREACH(Key key, keys) {
    // Label the node with the label from the KeyFormatter
    stm << "  var" << key << "[label=\"" << keyFormatter(key) << "\"";
    if(values.exists(key)) {
      boost::optional<Point2> xy = getXY(values.at(key), graphvizFormatting);
      if(xy)
        stm << ", pos=\"" << graphvizFormatting.scale*(xy->x() - minX) << "," << graphvizFormatting.scale*(xy->y() - minY) << "!\"";
    }
    stm << "];\n";

    if (firstTimePoses) {
      lastKey = key;
      firstTimePoses = false;
    } else {
      stm << "  var" << key << "--" << "var" << lastKey << ";\n";
      lastKey = key;
    }
  }
  stm << "\n";

  if(graphvizFormatting.mergeSimilarFactors) {
    // Remove duplicate factors
    FastSet<vector<Key> > structure;
    BOOST_FOREACH(const sharedFactor& factor, *this) {
      if(factor) {
        vector<Key> factorKeys = factor->keys();
        std::sort(factorKeys.begin(), factorKeys.end());
        structure.insert(factorKeys);
      }
    }

    // Create factors and variable connections
    size_t i = 0;
    BOOST_FOREACH(const vector<Key>& factorKeys, structure) {
      // Make each factor a dot
      stm << "  factor" << i << "[label=\"\", shape=point";
      {
        map<size_t, Point2>::const_iterator pos = graphvizFormatting.factorPositions.find(i);
        if(pos != graphvizFormatting.factorPositions.end())
          stm << ", pos=\"" << graphvizFormatting.scale*(pos->second.x() - minX) << "," << graphvizFormatting.scale*(pos->second.y() - minY) << "!\"";
      }
      stm << "];\n";

      // Make factor-variable connections
      BOOST_FOREACH(Key key, factorKeys) {
        stm << "  var" << key << "--" << "factor" << i << ";\n"; }

      ++ i;
    }
  } else {
    // Create factors and variable connections
    for(size_t i = 0; i < this->size(); ++i) {
      if(graphvizFormatting.plotFactorPoints){
    // Make each factor a dot
    stm << "  factor" << i << "[label=\"\", shape=point";
    {
    map<size_t, Point2>::const_iterator pos = graphvizFormatting.factorPositions.find(i);
    if(pos != graphvizFormatting.factorPositions.end())
      stm << ", pos=\"" << graphvizFormatting.scale*(pos->second.x() - minX) << "," << graphvizFormatting.scale*(pos->second.y() - minY) << "!\"";
    }
    stm << "];\n";

    // Make factor-variable connections
    if(graphvizFormatting.connectKeysToFactor && this->at(i)) {
      BOOST_FOREACH(Key key, *this->at(i)) {
      stm << "  var" << key << "--" << "factor" << i << ";\n";
      }
    }

    }
      else {
      if(this->at(i)) {
        Key k;
        bool firstTime = true;
        BOOST_FOREACH(Key key, *this->at(i)) {
        if(firstTime){
          k = key;
          firstTime = false;
          continue;
        }
        stm << "  var" << key << "--" << "var" << k << ";\n";
        k = key;
        }
      }
      }
    }
  }

  stm << "}\n";
}

/* ************************************************************************* */
double NonlinearFactorGraph::error(const Values& c) const {
  gttic(NonlinearFactorGraph_error);
  double total_error = 0.;
  // iterate over all the factors_ to accumulate the log probabilities
  BOOST_FOREACH(const sharedFactor& factor, this->factors_) {
    if(factor)
      total_error += factor->error(c);
  }
  return total_error;
}

/* ************************************************************************* */
FastSet<Key> NonlinearFactorGraph::keys() const {
  FastSet<Key> keys;
  BOOST_FOREACH(const sharedFactor& factor, this->factors_) {
    if(factor)
      keys.insert(factor->begin(), factor->end());
  }
  return keys;
}

/* ************************************************************************* */
Ordering NonlinearFactorGraph::orderingCOLAMD() const
{
  return Ordering::COLAMD(*this);
}

/* ************************************************************************* */
Ordering NonlinearFactorGraph::orderingCOLAMDConstrained(const FastMap<Key, int>& constraints) const
{
  return Ordering::COLAMDConstrained(*this, constraints);
}

/* ************************************************************************* */
SymbolicFactorGraph::shared_ptr NonlinearFactorGraph::symbolic() const
{
  // Generate the symbolic factor graph
  SymbolicFactorGraph::shared_ptr symbolic = boost::make_shared<SymbolicFactorGraph>();
  symbolic->reserve(this->size());

  BOOST_FOREACH(const sharedFactor& factor, *this) {
    if(factor)
      *symbolic += SymbolicFactor(*factor);
    else
      *symbolic += SymbolicFactorGraph::sharedFactor();
  }

  return symbolic;
}

/* ************************************************************************* */
namespace {

#ifdef GTSAM_USE_TBB
  struct _LinearizeOneFactor {
    const NonlinearFactorGraph& graph;
    const Values& linearizationPoint;
    GaussianFactorGraph& result;
    _LinearizeOneFactor(const NonlinearFactorGraph& graph, const Values& linearizationPoint, GaussianFactorGraph& result) :
      graph(graph), linearizationPoint(linearizationPoint), result(result) {}
    void operator()(const tbb::blocked_range<size_t>& r) const
    {
      for(size_t i = r.begin(); i != r.end(); ++i)
      {
        if(graph[i])
          result[i] = graph[i]->linearize(linearizationPoint);
        else
          result[i] = GaussianFactor::shared_ptr();
      }
    }
  };
#endif

}

/* ************************************************************************* */
GaussianFactorGraph::shared_ptr NonlinearFactorGraph::linearize(const Values& linearizationPoint) const
{
  gttic(NonlinearFactorGraph_linearize);

  // create an empty linear FG
  GaussianFactorGraph::shared_ptr linearFG = boost::make_shared<GaussianFactorGraph>();

#ifdef GTSAM_USE_TBB

  linearFG->resize(this->size());
  TbbOpenMPMixedScope threadLimiter; // Limits OpenMP threads since we're mixing TBB and OpenMP
  tbb::parallel_for(tbb::blocked_range<size_t>(0, this->size()),
    _LinearizeOneFactor(*this, linearizationPoint, *linearFG));

#else

  linearFG->reserve(this->size());

  // linearize all factors
  BOOST_FOREACH(const sharedFactor& factor, this->factors_) {
    if(factor) {
      (*linearFG) += factor->linearize(linearizationPoint);
    } else
      (*linearFG) += GaussianFactor::shared_ptr();
  }

#endif

  return linearFG;
}

/* ************************************************************************* */
NonlinearFactorGraph NonlinearFactorGraph::clone() const {
  NonlinearFactorGraph result;
  BOOST_FOREACH(const sharedFactor& f, *this) {
    if (f)
      result.push_back(f->clone());
    else
      result.push_back(sharedFactor()); // Passes on null factors so indices remain valid
  }
  return result;
}

/* ************************************************************************* */
NonlinearFactorGraph NonlinearFactorGraph::rekey(const std::map<Key,Key>& rekey_mapping) const {
  NonlinearFactorGraph result;
  BOOST_FOREACH(const sharedFactor& f, *this) {
    if (f)
      result.push_back(f->rekey(rekey_mapping));
    else
      result.push_back(sharedFactor());
  }
  return result;
}

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

} // namespace gtsam


// Copyright 2018 The Simons Foundation, Inc. - All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef NETKET_MATRIXREPLACEMENT_HPP
#define NETKET_MATRIXREPLACEMENT_HPP

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/IterativeLinearSolvers>
#include <complex>
#include <iostream>
#include <unsupported/Eigen/IterativeSolvers>
#include "Utils/parallel_utils.hpp"

using Eigen::MatrixXcd;
using Eigen::MatrixXd;

namespace netket {
// Forward declaration
class MatrixReplacement;
}  // namespace netket

namespace Eigen {
namespace internal {
// MatrixReplacement looks-like a SparseMatrix, so let's inherits its traits:
template <>
struct traits<netket::MatrixReplacement>
    : public Eigen::internal::traits<
          Eigen::SparseMatrix<std::complex<double>>> {};
}  // namespace internal
}  // namespace Eigen

// Example of a matrix-free wrapper from a user type to Eigen's compatible type
// For the sake of simplicity, this example simply wrap a Eigen::SparseMatrix.
namespace netket {
class MatrixReplacement : public Eigen::EigenBase<netket::MatrixReplacement> {
 public:
  // Required typedefs, constants, and method:
  typedef std::complex<double> Scalar;
  typedef double RealScalar;
  typedef int StorageIndex;
  enum {
    ColsAtCompileTime = Eigen::Dynamic,
    MaxColsAtCompileTime = Eigen::Dynamic,
    IsRowMajor = false
  };
  Index rows() const { return mp_mat_.cols(); }
  Index cols() const { return mp_mat_.cols(); }
  template <typename Rhs>
  Eigen::Product<netket::MatrixReplacement, Rhs, Eigen::AliasFreeProduct>
  operator*(const Eigen::MatrixBase<Rhs> &x) const {
    return Eigen::Product<netket::MatrixReplacement, Rhs,
                          Eigen::AliasFreeProduct>(*this, x.derived());
  }
  // Custom API:
  MatrixReplacement() : shift_(0), scale_(1) {}
  void attachMatrix(const Eigen::MatrixXcd &mat) { mp_mat_ = mat; }
  void attachMatrix(const Eigen::MatrixXd &mat) { mp_mat_ = mat; }
  void setShift(double shift) { shift_ = shift; }
  Eigen::MatrixXcd const &my_matrix() const { return mp_mat_; }
  double shift() const { return shift_; }
  void setScale(double scale) { scale_ = scale; }
  double getScale() const { return scale_; }

 private:
  Eigen::MatrixXcd mp_mat_;
  double shift_;
  double scale_;
};
}  // namespace netket

// Implementation of MatrixReplacement * Eigen::DenseVector though a
// specialization of internal::generic_product_impl:
namespace Eigen {
namespace internal {
template <typename Rhs>
struct generic_product_impl<netket::MatrixReplacement, Rhs, SparseShape,
                            DenseShape,
                            GemvProduct>  // GEMV stands for matrix-vector
    : generic_product_impl_base<
          netket::MatrixReplacement, Rhs,
          generic_product_impl<netket::MatrixReplacement, Rhs>> {
  typedef typename Product<netket::MatrixReplacement, Rhs>::Scalar Scalar;
  template <typename Dest>
  static void scaleAndAddTo(Dest &dst, const netket::MatrixReplacement &lhs,
                            const Rhs &rhs, const Scalar &alpha) {
    // This method should implement "dst += alpha * lhs * rhs" inplace,

    auto vtilde = lhs.my_matrix() * rhs;
    Eigen::VectorXcd res = lhs.my_matrix().adjoint() * vtilde;
    netket::SumOnNodes(res);

    double nor = lhs.getScale();

    dst += alpha * (rhs * lhs.shift() + res * nor);
  }
};
}  // namespace internal
}  // namespace Eigen

#endif