#include <boost/container_hash/hash.hpp>

#include <array>
#include <iomanip>
#include <iostream>
#include <limits>
#include <queue>
#include <unordered_set>

#define BOUNDARY 5

enum {
    NOTHING = 0,
    TORCH = 1,
    CLIMBING = 2,
};

enum {
    ROCKY = 0,
    WET = 1,
    NARROW = 2,
};

struct Cell;

struct Point {
    long x = 0, y = 0;
    int holds = TORCH;
    unsigned int duration = 0;

    Point left() const noexcept {
        return {x - 1, y, holds, duration};
    }

    Point right() const noexcept {
        return {x + 1, y, holds, duration};
    }

    Point up() const noexcept {
        return {x, y - 1, holds, duration};
    }

    Point down() const noexcept {
        return {x, y + 1, holds, duration};
    }

    bool operator==(Point const& o) const noexcept { return x == o.x && y == o.y; }

    std::string to_string() const {
        std::string s;
        s.append("x=")
            .append(std::to_string(x))
            .append(", y=")
            .append(std::to_string(y))
            .append(", d=")
            .append(std::to_string(duration))
            .append(", holds=");
        switch(holds) {
        case NOTHING:
            s.append("NOTHING"); break;
        case CLIMBING:
            s.append("CLIMBING"); break;
        case TORCH:
            s.append("TORCH"); break;
        };
        return s;
    }
};

Point operator+(Point lhs, Point const& rhs) noexcept {
    lhs.x += rhs.x;
    lhs.y += rhs.y;
    return lhs;
}

Point operator-(Point lhs, Point const& rhs) noexcept {
    lhs.x -= rhs.x;
    lhs.y -= rhs.y;
    return lhs;
}

namespace std {

template <>
struct hash<Point>
{
    size_t operator()(Point const& p) const noexcept
    {
        size_t seed = 0;
        boost::hash_combine(seed, p.x);
        boost::hash_combine(seed, p.y);
        boost::hash_combine(seed, p.holds);
        return seed;
    }
};

}  // namespace std

struct Cell {
    long erosion_level = 0;
    int type = 0;

    unsigned int duration = 0;

    std::string to_string() const {
        std::string s;
        switch(type) {
        case ROCKY:
            s.append("ROCKY"); break;
        case WET:
            s.append("WET"); break;
        case NARROW:
            s.append("NARROW"); break;
        };
        s.append(", d=").append(std::to_string(duration));
        return s;
    }
};

template<unsigned int MAX_X, unsigned int MAX_Y>
struct World
{
    World(long const depth, Point _target) : target(std::move(_target))
    {
        for (long y = 0; y < MAX_Y; ++y) {
            for (long x = 0; x < MAX_X; ++x) {
                Cell& c = get({x, y});
                uint64_t geo_index = 0;
                if (y == 0 && x == 0) {
                    // leave at 0
                    continue;
                } else if (y == 0) {
                    geo_index = x * 16807;
                } else if (x == 0) {
                    geo_index = y * 48271;
                } else {
                    //X-1,Y and X,Y-1
                    geo_index = at({x - 1, y}).erosion_level * at({x, y - 1}).erosion_level;
                }
                c.erosion_level = (geo_index + depth) % 20183;
                c.type = c.erosion_level % 3;
            }
        }
        at(target).type = ROCKY;
    }

    long risk_level() const {
        long l = 0;
        for (long y = 0; y <= target.y; ++y) {
            for (long x = 0; x <= target.x; ++x) {
                l += at({x, y}).type;
            }
        }
        return l;
    }

    bool can_move2(Point const& p) const {
        Cell const& c0 = at(p);
        if (c0.type == ROCKY && p.holds == NOTHING) {
            // std::cout << "Can't move there\n";
            return false;
        }
        if (c0.type == WET && p.holds == TORCH) {
            // std::cout << "Can't move there\n";
            return false;
        }
        if (c0.type == NARROW && p.holds == CLIMBING) {
            // std::cout << "Can't move there\n";
            return false;
        }
        return true;
    }

    unsigned long rescue2() {
        auto shortest = [this](Point const& a, Point const& b) {
            return a.duration > b.duration;
        };
        std::priority_queue<Point, std::vector<Point>, decltype(shortest)> qq(shortest);

        qq.push(Point{0, 0, TORCH});

        std::unordered_set<Point> visited;
        // visited.insert({0, 0, NOTHING});
        // visited.insert({0, 0, CLIMBING});

        while(!qq.empty()) {
            // {
            //     auto q2 = qq;
            //     std::cout << "qq: ";
            //     while(!q2.empty()) {
            //         std::cout << q2.top().to_string() << ",";
            //         q2.pop();
            //     }
            //     std::cout << "\n";
            // }
            Point p0{qq.top()};
            qq.pop();

            if (!visited.insert(p0).second) {
                // std::cout << " seen " << p0.to_string() << "\n";
                continue;
            }

            Cell& c0 = at(p0);
            if (c0.duration && c0.duration < p0.duration) {
                // noop
            } else {
                c0.duration = p0.duration;
            }

            if (p0 == target) {
                target.duration = p0.duration;
                if (p0.holds != TORCH) {
                   target.duration += 7;
                }
                break;
            }

            // std::cout << "@ " << p0.to_string() << ", cell=" << c0.to_string() << "\n";
            // dump_dur();

            // try all combinations
            int other_tool = -1;
            if (c0.type == ROCKY) {
                assert(p0.holds == TORCH || p0.holds == CLIMBING);
                other_tool = (p0.holds == TORCH ? CLIMBING : TORCH);

            } else if (c0.type == WET) {
                assert(p0.holds == CLIMBING || p0.holds == NOTHING);
                other_tool = (p0.holds == NOTHING ? CLIMBING : NOTHING);

            } else if (c0.type == NARROW) {
                assert(p0.holds == TORCH || p0.holds == NOTHING);
                other_tool = (p0.holds == NOTHING ? TORCH : NOTHING);
            }
            assert(other_tool == NOTHING || other_tool == TORCH || other_tool == CLIMBING);

            for (Point p1 : {p0.right(), p0.down(), p0.left(), p0.up()}) {
                if (p1.x < 0 || p1.x >= MAX_X || p1.y < 0 || p1.y >= MAX_Y) {
                    continue;
                }
                p1.duration += 1;
                if (can_move2(p1)) {
                    // std::cout << "? " << p1.to_string() << "\n";
                    qq.push(std::move(p1));
                }
            }
            Point p1{p0.x, p0.y, other_tool, p0.duration + 7};
            assert(can_move2(p1));
            // std::cout << "? " << p1.to_string() << "\n";
            qq.push(std::move(p1));
        }

        return target.duration;
    }

    Cell& get(Point const& p) { return mapa[p.y][p.x]; }

    Cell const& at(Point const& p) const { return mapa[p.y][p.x]; }
    Cell& at(Point const& p) { return mapa[p.y][p.x]; }

    void dump() const {
        for (long y = 0; y < MAX_Y; ++y) {
            std::string line;
            line.reserve(MAX_X + 1);
            for (long x = 0; x < MAX_X; ++x) {
                if (x == 0 && y == 0) {
                    line.append("X");
                    continue;
                }
                if (target == Point{x, y}) {
                    line.append("T");
                    continue;
                }
                switch(at({x, y}).type) {
                case 0:
                    line.append("."); break;
                case 1:
                    line.append("="); break;
                case 2:
                    line.append("|"); break;
                default:
                    line.append("?");
                }
            }
            std::cout << line << "\n";
        }
    }

    void dump_ero() const {
        for (long y = 0; y < MAX_Y; ++y) {
            for (long x = 0; x < MAX_X; ++x) {
                std::cout << std::setw(10) << at({x, y}).erosion_level;
            }
            std::cout << "\n";
        }
    }

    void dump_dur() const {
        for (long y = 0; y < MAX_Y; ++y) {
            for (long x = 0; x < MAX_X; ++x) {
                std::cout << std::setw(5) << at({x, y}).duration;
            }
            std::cout << "\n";
        }
    }

protected:
    std::array<std::array<Cell, MAX_X>, MAX_Y> mapa;
    Point target;
    unsigned int shortest;
};

int main()
{
    // depth: 510
    // target: 10,10
    // World<15,15> mapa(510, {10, 10});
    // mapa.dump_ero();

    // depth: 5355
    // target: 14,796
    World<28, 801> mapa(5355, {14, 796});

    // mapa.dump();

    std::cout << "1 Result=" << mapa.risk_level() << "\n";

    auto shortest = mapa.rescue2();
    std::cout << "2 Result=" << shortest << "\n";

    return 0;
}


#include <iostream>
#include <fstream>
#include <Eigen/Eigen>
//include the bie header files
#include "material.hh"
#include "precomputed_kernel.hh"
#include "bimat_interface.hh"
#include "infinite_boundary.hh"
//include the fem header files
#include "mesh_Generated.hpp"
#include "bcdof.hpp"
#include "cal_ke.hpp"
#include "cal_fe_global_const_ke.hpp"
#include "mapglobal.hpp"
#include "Slip_Weakening.hpp"
#include "Rate_and_State_Aging.hpp"
#include "cal_slip_sliprate.hpp"
#include "update_disp_velocity.hpp"
#include "time_advance.hpp"
#include "BIE_correct.hpp"

using namespace Eigen;
using namespace std;

int main() {
    // Domain Size
    double x_min = -10e3;
    double x_max = 10e3;
    double y_min = -5.5e3;
    double y_max = 5.5e3;
    int dim = 2.0;
    double dx = 100;
    double dy = 100;
    int nx = (x_max-x_min)/dx;
    int ny = (y_max-y_min)/dy;
    MatrixXd Node = MatrixXd::Zero((nx+1)*(ny+1),2);
    MatrixXd Element = MatrixXd::Zero(nx*ny,4);
    VectorXd fault_surf_nodes = VectorXd::Zero((nx+1),1);
    VectorXd fault_surf_nodes_new = VectorXd::Zero((nx+1),1);
    VectorXd BIE_top_surf_nodes = VectorXd::Zero((nx+1),1);
    VectorXd BIE_bot_surf_nodes = VectorXd::Zero((nx+1),1);
    // Mesh
    mesh_Generated(x_min,x_max,y_min,y_max,dx,dy,nx,ny,Node, Element, fault_surf_nodes, fault_surf_nodes_new, BIE_top_surf_nodes, BIE_bot_surf_nodes);
    int n_nodes = Node.rows();
    int n_el = Element.rows();
    int Ndofn = 2;
    int Nnel = Element.cols();
    nx = fault_surf_nodes.size()-1;
    // Material
    double density = 2670.0;
    double v_s =3.464e3;
    double v_p = 6.0e3;
    double G= pow(v_s,2)*density;
    double Lambda = pow(v_p,2)*density-2.0*G;
    double E  = G*(3.0*Lambda+2.0*G)/(Lambda+G);
    double nu = Lambda/(2.0*(Lambda+G));
    // Time
    double alpha = 0.2;
    double dt = alpha*dx/v_p;
    // Reyleigh Damping
    double beta =0.1;
    double q = beta*dt;
    double time_run = 6.0;
    int numt = time_run/dt;
    //numt = 3;
    VectorXd time = dt*VectorXd::LinSpaced(numt,1,numt);
    // Rate and state friction parameters
    double a = 0.008;
    double b = 0.012;
    double V_0 = 1e-6;
    double f_0 = 0.6;
    double L = 0.02;
    double tau_ini = 75e6;
    double sigma_ini = 120e6;
    double V_ini  = 1e-12;
    double theta_ini = L/V_0*exp((a*log(2*sinh(tau_ini/(a*sigma_ini)))-f_0-a*log(V_ini/V_0))/b);
    // Intialization
    // disp velocity current and next time step (new)
    VectorXd u_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd u_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd a_n = VectorXd::Zero(n_nodes*Ndofn,1);
    // slip and slip-rate
    VectorXd delt_u_n = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd delt_v_n = VectorXd::Zero(Ndofn*(nx+1),1);
    // Stress on the fault T_0 = intial stress , T = sticking force, T_c= stress critical goes into F_global
    VectorXd T_0 = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd T = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd T_c = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd tau_s = VectorXd::Zero(nx+1,1);
    VectorXd F_ext_global = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd theta_n =theta_ini*VectorXd::Ones(nx+1, 1);
    VectorXd theta_dot_n = VectorXd::Zero(nx+1, 1);
    // Setting intial stress on the fault
    for (int i=0; i<T_0.size()/2; i++)
    {
        T_0(2*i) = tau_ini;
        T_0(2*i+1) = -sigma_ini;
    }
    VectorXd x = VectorXd::LinSpaced(nx+1,x_min,x_max);
    double d_tau_0 = 25e6;
    double R = 3e3;
    VectorXd F_r = VectorXd::Zero(nx+1,1);
    for (int i=0; i<nx+1; i++)
    {
        if (fabs(x(i))<R)
        {
            F_r(i) = exp(pow(x(i),2)/(pow(x(i),2)-pow(R,2)));
        }
        else
        {
            F_r(i) = 0.0;
        }
    }
    VectorXd G_t = VectorXd::Zero(numt,1);
    double T_ramp = 1.0;
    for (int i=0; i<numt;i++)
    {
        if(time(i)<T_ramp)
        {
            G_t(i) = exp(pow((time(i)-T_ramp),2)/(time(i)*(time(i)-2*T_ramp)));
        }
        else
        {
            G_t(i) = 1.0;
        }
    }
    cout<<G_t<<endl;
    MatrixXd d_tau = MatrixXd::Zero(nx+1,numt);
    for (int i=0;i<numt;i++)
    {
        d_tau.col(i)=75e6*VectorXd::Ones(nx+1,1)+d_tau_0*F_r*(G_t(i));
    }
    // Get the index degree of freedome for each element
    VectorXd index_el = VectorXd::Zero(Ndofn*Nnel,1);
    MatrixXd index_store = MatrixXd::Zero(Nnel*Ndofn,n_el);
    for (int i=0;i<n_el;i++)
    {
        bcdof(Element.row(i),dim,index_el);
        index_store.col(i) = index_el;
    }
    VectorXd top_surf_index = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd bot_surf_index = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd BIE_top_surf_index = VectorXd::Zero(Ndofn*(nx+1),1);
    VectorXd BIE_bot_surf_index = VectorXd::Zero(Ndofn*(nx+1),1);
    bcdof(fault_surf_nodes,dim,top_surf_index);
    bcdof(fault_surf_nodes_new,dim,bot_surf_index);
    bcdof(BIE_top_surf_nodes,dim,BIE_top_surf_index);
    bcdof(BIE_bot_surf_nodes,dim,BIE_bot_surf_index);
    // Rate and state initlaized v_n delt_v_n
    for (int i=0;i<nx+1;i++)
    {
        delt_v_n(2*i)= V_ini;
        v_n(top_surf_index(2*i))= V_ini/2.0;
        v_n(bot_surf_index(2*i))= -V_ini/2.0;
    }

    // Calculating the Global Mass Vector (lumped mass)
    // Element mass
    double M=density*dx*dy*1.0;
    VectorXd M_el_vec = M/4*VectorXd::Ones(Nnel*Ndofn,1);
    VectorXd M_global_vec=VectorXd::Zero(n_nodes*Ndofn,1);
    for (int i=0 ; i<n_el;i++)
    {
        index_el = index_store.col(i);
        mapglobal(index_el,M_global_vec,M_el_vec);
    }
    // Element matrix
    MatrixXd ke = MatrixXd::Zero(8,8);
    MatrixXd coord = MatrixXd::Zero(4,2);
    VectorXd Element_0= Element.row(0);
    coord.row(0) = Node.row(Element_0(0));
    coord.row(1) = Node.row(Element_0(1));
    coord.row(2) = Node.row(Element_0(2));
    coord.row(3) = Node.row(Element_0(3));
    cal_ke (coord,E,nu,ke);
    // BIE part initiation
    // Setting up the material property for the BIE code
    Material BIE_top_mat = Material(E,nu,density);
    Material BIE_bot_mat = Material(E,nu,density);
    double length = x_max-x_min;
    // infinte bc BIE call infinite_boundary.cc
    PrecomputedKernel h11("kernels/nu_.25_h11.dat");
    PrecomputedKernel h12("kernels/nu_.25_k12.dat");
    PrecomputedKernel h22("kernels/nu_.25_h22.dat");
    InfiniteBoundary BIE_inf_top(length,nx+1,1.0,&BIE_top_mat,&h11,&h12,&h22);
    InfiniteBoundary BIE_inf_bot(length,nx+1,-1.0,&BIE_bot_mat,&h11,&h12,&h22);
    // BIE setting time step
    BIE_inf_top.setTimeStep(dt);
    BIE_inf_bot.setTimeStep(dt);
    // BIE initialization
    BIE_inf_top.init();
    BIE_inf_bot.init();

    //BIE_initiation(E, nu, density, x_max, x_min, nx, dt);
    printf("ready to start\n");
    // Output
    ofstream slip("slip.bin",ios::binary);
    slip.close();
    ofstream slip_rate("slip_rate.bin",ios::binary);
    slip_rate.close();
    ofstream shear("shear.bin",ios::binary);
    shear.close();
    std::ofstream fe("results/fe.txt");
    std::ofstream T_0_out("T_0.txt");

    // Main time loop
    for (int j=0;j<numt;j++)
    {
        // Compute the global internal force
        VectorXd fe_global= VectorXd::Zero(n_nodes*Ndofn,1);
        cal_fe_global_const_ke(n_nodes, n_el, index_store, q, u_n, v_n, Ndofn, ke, fe_global);
        // Friction subroutine
        VectorXd F_fault = VectorXd::Zero(Ndofn*(nx+1),1);
      //  Slip_Weakening(M_global_vec, top_surf_index, bot_surf_index, fe_global, dt, dx, dy, nx, delt_v_n, delt_u_n, T_0, tau_s, mu_s, mu_d, Dc, Ndofn, M, F_fault, T_c);
        for (int i=0;i<nx+1;i++)
        {
            T_0(2*i) = d_tau(i,j);
        }
        Rate_and_State_Aging(M_global_vec, top_surf_index, bot_surf_index, fe_global, dt, dx, dy, nx, delt_u_n, delt_v_n, T_0, tau_s, a, b, L, V_0, f_0, Ndofn, M, F_fault, T_c, theta_n, theta_dot_n);

        // Calculate the global force vector
        // Adding contribution of the fault force to the global force vector F_total
        VectorXd F_total = F_ext_global-fe_global;
        mapglobal(top_surf_index,F_total,-F_fault);
        mapglobal(bot_surf_index,F_total,F_fault);
        // Central Difference Time integration
        time_advance(u_n, v_n, F_total, M_global_vec, dt);
        // Get the slip and slip rate
        //cal_slip_slip_rate(u_n, v_n, top_surf_index, bot_surf_index, Ndofn, nx, delt_u_n, delt_v_n);
        update_disp_velocity(u_n, v_n, top_surf_index, bot_surf_index, Ndofn, nx, delt_u_n, delt_v_n);

        // Correct the BIE surf nodes solutions from FEM with the BIE solution
        BIE_correct(BIE_top_surf_index, BIE_bot_surf_index, fe_global, Ndofn, nx, dx, BIE_inf_top, BIE_inf_bot, u_n, v_n);
        //ofstream file;
        T_0_out<<T_0<<"\n";

        slip.open("slip.bin",ios::binary | ios::app);
        slip.write((char*)(delt_u_n.data()),delt_u_n.size()*sizeof(double));
        slip.close();
        slip_rate.open("slip_rate.bin",ios::binary | ios::app);
        slip_rate.write((char*)(delt_v_n.data()),delt_v_n.size()*sizeof(double));
        slip_rate.close();
        shear.open("shear.bin",ios::binary | ios::app);
        shear.write((char*)(T_c.data()),T_c.size()*sizeof(double));
        shear.close();
        printf("Simulation time = %f\n",time(j));
    }
    return 0;
}


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

/*! \file qle/cashflows/commodityindexedcashflow.hpp
    \brief Cash flow dependent on a single commodity spot price or future's settlement price
 */

#ifndef quantext_commodity_indexed_cash_flow_hpp
#define quantext_commodity_indexed_cash_flow_hpp

#include <boost/optional.hpp>
#include <ql/cashflow.hpp>
#include <ql/patterns/visitor.hpp>
#include <ql/time/schedule.hpp>
#include <qle/indexes/commodityindex.hpp>
#include <qle/time/futureexpirycalculator.hpp>

namespace QuantExt {

//! Cash flow dependent on a single commodity spot price or futures settlement price on a given pricing date
class CommodityIndexedCashFlow : public CashFlow, public Observer {

public:
    //! Constructor taking an explicit \p pricingDate and \p paymentDate
    CommodityIndexedCashFlow(QuantLib::Real quantity, const QuantLib::Date& pricingDate,
                             const QuantLib::Date& paymentDate, const ext::shared_ptr<CommodityIndex>& index,
                             QuantLib::Real spread = 0.0, QuantLib::Real gearing = 1.0, bool useFuturePrice = false,
                             const Date& contractDate = Date(),
                             const ext::shared_ptr<FutureExpiryCalculator>& calc = nullptr);

    /*! Constructor taking a period \p startDate, \p endDate and some conventions. The pricing date and payment date
        are derived from the start date and end date using the conventions.
    */
    CommodityIndexedCashFlow(QuantLib::Real quantity, const QuantLib::Date& startDate, const QuantLib::Date& endDate,
                             const ext::shared_ptr<CommodityIndex>& index, QuantLib::Natural paymentLag,
                             const QuantLib::Calendar& paymentCalendar,
                             QuantLib::BusinessDayConvention paymentConvention, QuantLib::Natural pricingLag,
                             const QuantLib::Calendar& pricingLagCalendar, QuantLib::Real spread = 0.0,
                             QuantLib::Real gearing = 1.0, bool payInAdvance = false, bool isInArrears = true,
                             bool useFuturePrice = false, bool useFutureExpiryDate = true,
                             QuantLib::Natural futureMonthOffset = 0,
                             const ext::shared_ptr<FutureExpiryCalculator>& calc = nullptr,
                             const QuantLib::Date& paymentDateOverride = Date(),
                             const QuantLib::Date& pricingDateOverride = Date());

    //! \name Inspectors
    //@{
    QuantLib::Real quantity() const { return quantity_; }
    const QuantLib::Date& pricingDate() const { return pricingDate_; }
    ext::shared_ptr<CommodityIndex> index() const { return index_; }
    QuantLib::Real spread() const { return spread_; }
    QuantLib::Real gearing() const { return gearing_; }
    bool useFuturePrice() const { return useFuturePrice_; }
    bool useFutureExpiryDate() const { return useFutureExpiryDate_; }
    QuantLib::Natural futureMonthOffset() const { return futureMonthOffset_; }
    QuantLib::Real periodQuantity() const { return periodQuantity_; }
    //@}

    //! \name Event interface
    //@{
    QuantLib::Date date() const { return paymentDate_; }
    //@}

    //! \name CashFlow interface
    //@{
    QuantLib::Real amount() const;
    //@}

    //! \name Visitability
    //@{
    void accept(QuantLib::AcyclicVisitor& v);
    //@}

    //! \name Observer interface
    //@{
    void update();
    //@}

    //! Allow the full calculation period quantity to be updated.
    void setPeriodQuantity(QuantLib::Real periodQuantity);

private:
    QuantLib::Real quantity_;
    QuantLib::Date pricingDate_;
    QuantLib::Date paymentDate_;
    ext::shared_ptr<CommodityIndex> index_;
    QuantLib::Real spread_;
    QuantLib::Real gearing_;
    bool useFuturePrice_;
    bool useFutureExpiryDate_;
    QuantLib::Natural futureMonthOffset_;
    QuantLib::Real periodQuantity_;

    //! Shared initialisation
    void init(const ext::shared_ptr<FutureExpiryCalculator>& calc,
        const QuantLib::Date& contractDate = QuantLib::Date());
};

//! Helper class building a sequence of commodity indexed cashflows
class CommodityIndexedLeg {

public:
    CommodityIndexedLeg(const QuantLib::Schedule& schedule, const ext::shared_ptr<CommodityIndex>& index);
    CommodityIndexedLeg& withQuantities(QuantLib::Real quantity);
    CommodityIndexedLeg& withQuantities(const std::vector<QuantLib::Real>& quantities);
    CommodityIndexedLeg& withPaymentLag(QuantLib::Natural paymentLag);
    CommodityIndexedLeg& withPaymentCalendar(const QuantLib::Calendar& paymentCalendar);
    CommodityIndexedLeg& withPaymentConvention(QuantLib::BusinessDayConvention paymentConvention);
    CommodityIndexedLeg& withPricingLag(QuantLib::Natural pricingLag);
    CommodityIndexedLeg& withPricingLagCalendar(const QuantLib::Calendar& pricingLagCalendar);
    CommodityIndexedLeg& withSpreads(QuantLib::Real spread);
    CommodityIndexedLeg& withSpreads(const std::vector<QuantLib::Real>& spreads);
    CommodityIndexedLeg& withGearings(QuantLib::Real gearing);
    CommodityIndexedLeg& withGearings(const std::vector<QuantLib::Real>& gearings);
    CommodityIndexedLeg& payInAdvance(bool flag = false);
    CommodityIndexedLeg& inArrears(bool flag = true);
    CommodityIndexedLeg& useFuturePrice(bool flag = false);
    CommodityIndexedLeg& useFutureExpiryDate(bool flag = true);
    CommodityIndexedLeg& withFutureMonthOffset(QuantLib::Natural futureMonthOffset);
    CommodityIndexedLeg& withFutureExpiryCalculator(const ext::shared_ptr<FutureExpiryCalculator>& calc = nullptr);
    CommodityIndexedLeg& payAtMaturity(bool flag = false);
    CommodityIndexedLeg& withPricingDates(const std::vector<QuantLib::Date>& pricingDates);
    CommodityIndexedLeg& withPaymentDates(const std::vector<QuantLib::Date>& paymentDates);

    operator Leg() const;

private:
    Schedule schedule_;
    ext::shared_ptr<CommodityIndex> index_;
    std::vector<QuantLib::Real> quantities_;
    QuantLib::Natural paymentLag_;
    QuantLib::Calendar paymentCalendar_;
    QuantLib::BusinessDayConvention paymentConvention_;
    QuantLib::Natural pricingLag_;
    QuantLib::Calendar pricingLagCalendar_;
    std::vector<QuantLib::Real> spreads_;
    std::vector<QuantLib::Real> gearings_;
    bool payInAdvance_;
    bool inArrears_;
    bool useFuturePrice_;
    bool useFutureExpiryDate_;
    QuantLib::Natural futureMonthOffset_;
    ext::shared_ptr<FutureExpiryCalculator> calc_;
    bool payAtMaturity_;
    std::vector<QuantLib::Date> pricingDates_;
    std::vector<QuantLib::Date> paymentDates_;
};

} // namespace QuantExt

#endif


/*
Copyright 2009-2021 Nicolas Colombe

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 <core/random.hpp>

#include <core/string.hpp>
#include <boost/random.hpp>
#include <sstream>

namespace eXl
{
  class BoostRNG : public Random
  {
  public:
    BoostRNG(unsigned int iSeed) : m_Gen(iSeed) {}

    unsigned int Generate() override
    {
      return m_Gen();
    }

    String Save() override
    {
      std::stringstream outStr;
      outStr << m_Gen;

      return StringUtil::FromASCII(outStr.str().c_str());
    }

    void Restore(String const& iStr)
    {
      std::stringstream inStr(StringUtil::ToASCII(iStr).c_str());
      inStr >> m_Gen;
    }

  protected:
    boost::random::mt19937 m_Gen;
  };

  Random* Random::CreateDefaultRNG(unsigned int iSeed)
  {
    return new BoostRNG(iSeed);
  }
}


#include <scitbx/array_family/boost_python/flex_fwd.h>

#include <scitbx/error.h>
#include <scitbx/fftpack/gridding.h>
#include <scitbx/fftpack/complex_to_complex_2d.h>
#include <scitbx/fftpack/complex_to_complex_3d.h>
#include <scitbx/fftpack/real_to_complex_3d.h>
#include <scitbx/array_family/flex_types.h>
#include <scitbx/array_family/accessors/c_grid.h>
#include <scitbx/array_family/boost_python/utils.h>
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/args.hpp>

namespace scitbx { namespace fftpack { namespace {

  void raise_size_error()
  {
    PyErr_SetString(PyExc_RuntimeError, "Array is too small.");
    boost::python::throw_error_already_set();
  }

  template <typename FlexType>
  void assert_0_based_1d_size(
    FlexType const& a,
    std::size_t sz)
  {
    if (!a.check_shared_size()) af::boost_python::raise_shared_size_mismatch();
    af::boost_python::assert_0_based_1d(a.accessor());
    if (a.size() < sz) raise_size_error();
  }

  template <typename FlexType>
  void assert_0_based_2d_size(
    FlexType const& a,
    af::int2 const& fft_n)
  {
    if (!a.check_shared_size()) af::boost_python::raise_shared_size_mismatch();
    af::flex_grid<> const& grid = a.accessor();
    af::boost_python::assert_0_based_2d(grid);
    for(std::size_t i=0;i<2;i++) {
      if (grid.all()[i] != fft_n[i]) raise_size_error();
    }
  }

  template <typename FlexType>
  void assert_0_based_3d_size(
    FlexType const& a,
    af::int3 const& fft_n)
  {
    if (!a.check_shared_size()) af::boost_python::raise_shared_size_mismatch();
    af::flex_grid<> const& grid = a.accessor();
    af::boost_python::assert_0_based_3d(grid);
    for(std::size_t i=0;i<3;i++) {
      if (grid.all()[i] != fft_n[i]) raise_size_error();
    }
  }

  int adjust_gridding_2(int min_grid,
                        int max_prime)
  {
    return adjust_gridding(min_grid, max_prime);
  }

  int adjust_gridding_3(int min_grid,
                        int max_prime,
                        int mandatory_factor)
  {
    return adjust_gridding(min_grid, max_prime, mandatory_factor);
  }

  af::flex_grid_default_index_type
  adjust_gridding_triple_2(
    af::flex_grid_default_index_type const& min_grid,
    int max_prime)
  {
    return adjust_gridding_array_flex(min_grid, max_prime);
  }

  af::flex_grid_default_index_type
  adjust_gridding_triple_3(
    af::flex_grid_default_index_type const& min_grid,
    int max_prime,
    af::flex_grid_default_index_type const& mandatory_factors)
  {
    return adjust_gridding_array_flex(min_grid, max_prime,
                                      mandatory_factors);
  }

  struct factorization_wrappers
  {
    typedef factorization w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("factorization")
        .def(init<std::size_t, bool>())
        .def("n", &w_t::n)
        .def("factors", &w_t::factors)
      ;
    }
  };

  typedef af::flex_double flex_real_array;
  typedef af::ref<double, af::c_grid<2> > ref_2d_real_array;
  typedef af::ref<double, af::c_grid<3> > ref_3d_real_array;
  typedef af::flex_complex_double flex_complex_array;
  typedef af::ref<std::complex<double>, af::c_grid<2> > ref_2d_complex_array;
  typedef af::ref<std::complex<double>, af::c_grid<3> > ref_3d_complex_array;

  struct complex_to_complex_wrappers
  {
    typedef complex_to_complex<double> w_t;

    static flex_complex_array
    forward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_1d_size(a, fft.n());
      fft.forward(a.begin());
      return flex_complex_array(a, af::flex_grid<>(fft.n())
        .set_focus(fft.n()));
    }

    static flex_complex_array
    forward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_1d_size(a, 2 * fft.n());
      fft.forward(a.begin());
      return flex_complex_array(a.handle(), af::flex_grid<>(fft.n())
        .set_focus(fft.n()));
    }

    static flex_complex_array
    backward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_1d_size(a, fft.n());
      fft.backward(a.begin());
      return flex_complex_array(a, af::flex_grid<>(fft.n())
        .set_focus(fft.n()));
    }

    static flex_complex_array
    backward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_1d_size(a, 2 * fft.n());
      fft.backward(a.begin());
      return flex_complex_array(a.handle(), af::flex_grid<>(fft.n())
        .set_focus(fft.n()));
    }

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t, bases<factorization> >("complex_to_complex")
        .def(init<std::size_t>())
        .def("wa", &w_t::wa)
        .def("forward", forward_complex)
        .def("forward", forward_real)
        .def("backward", backward_complex)
        .def("backward", backward_real)
      ;
    }
  };

  struct real_to_complex_wrappers
  {
    typedef real_to_complex<double> w_t;

    static flex_complex_array
    forward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_1d_size(a, fft.n_complex());
      fft.forward(a.begin());
      return flex_complex_array(a, af::flex_grid<>((fft.n_complex()))
        .set_focus(fft.n_complex()));
    }

    static flex_complex_array
    forward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_1d_size(a, fft.m_real());
      fft.forward(a.begin());
      return flex_complex_array(a.handle(), af::flex_grid<>((fft.n_complex()))
        .set_focus(fft.n_complex()));
    }

    static flex_real_array
    backward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_1d_size(a, fft.n_complex());
      fft.backward(a.begin());
      return flex_real_array(a.handle(), af::flex_grid<>((fft.m_real()))
        .set_focus(fft.n_real()));
    }

    static flex_real_array
    backward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_1d_size(a, fft.m_real());
      fft.backward(a.begin());
      return flex_real_array(a, af::flex_grid<>((fft.m_real()))
        .set_focus(fft.n_real()));
    }

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t, bases<factorization> >("real_to_complex")
        .def(init<std::size_t>())
        .def("n_real", &w_t::n_real)
        .def("m_real", &w_t::m_real)
        .def("n_complex", &w_t::n_complex)
        .def("wa", &w_t::wa)
        .def("forward", forward_complex)
        .def("forward", forward_real)
        .def("backward", backward_complex)
        .def("backward", backward_real)
      ;
    }
  };

  struct complex_to_complex_3d_wrappers
  {
    typedef complex_to_complex_3d<double> w_t;

    static flex_complex_array
    forward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_3d_size(a, fft.n());
      ref_3d_complex_array map(a.begin(), af::c_grid<3>(fft.n()));
      fft.forward(map);
      return flex_complex_array(a, af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    forward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_3d_size(a, n_real_from_n_complex(fft.n()));
      ref_3d_real_array map(
        a.begin(), af::c_grid<3>(n_real_from_n_complex(fft.n())));
      fft.forward(map);
      return flex_complex_array(a.handle(), af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    backward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_3d_size(a, fft.n());
      ref_3d_complex_array map(a.begin(), af::c_grid<3>(fft.n()));
      fft.backward(map);
      return flex_complex_array(a, af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    backward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_3d_size(a, n_real_from_n_complex(fft.n()));
      ref_3d_real_array map(
        a.begin(), af::c_grid<3>(n_real_from_n_complex(fft.n())));
      fft.backward(map);
      return flex_complex_array(a.handle(), af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("complex_to_complex_3d")
        .def(init<std::size_t, std::size_t, std::size_t>())
        .def(init<af::int3>())
        .def("n", &w_t::n)
        .def("forward", forward_complex)
        .def("forward", forward_real)
        .def("backward", backward_complex)
        .def("backward", backward_real)
      ;
    }
  };

// 2d c2c fft

  struct complex_to_complex_2d_wrappers
  {
    typedef complex_to_complex_2d<double> w_t;

    static flex_complex_array
    forward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_2d_size(a, fft.n());
      ref_2d_complex_array map(a.begin(), af::c_grid<2>(fft.n()));
      fft.forward(map);
      return flex_complex_array(a, af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    forward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_2d_size(a, n_real_from_n_complex(fft.n()));
      ref_2d_real_array map(
        a.begin(), af::c_grid<2>(n_real_from_n_complex(fft.n())));
      fft.forward(map);
      return flex_complex_array(a.handle(), af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    backward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_2d_size(a, fft.n());
      ref_2d_complex_array map(a.begin(), af::c_grid<2>(fft.n()));
      fft.backward(map);
      return flex_complex_array(a, af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static flex_complex_array
    backward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_2d_size(a, n_real_from_n_complex(fft.n()));
      ref_2d_real_array map(
        a.begin(), af::c_grid<2>(n_real_from_n_complex(fft.n())));
      fft.backward(map);
      return flex_complex_array(a.handle(), af::flex_grid<>(af::adapt(fft.n()))
        .set_focus(af::adapt(fft.n())));
    }

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("complex_to_complex_2d")
        .def(init<std::size_t, std::size_t>())
        .def(init<af::int2>())
        .def("n", &w_t::n)
        .def("forward", forward_complex)
        .def("forward", forward_real)
        .def("backward", backward_complex)
        .def("backward", backward_real)
      ;
    }
  };

  struct real_to_complex_3d_wrappers
  {
    typedef real_to_complex_3d<double> w_t;

    static flex_complex_array
    forward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_3d_size(a, fft.n_complex());
      ref_3d_complex_array map(a.begin(), af::c_grid<3>(fft.n_complex()));
      fft.forward(map);
      return flex_complex_array(a,
        af::flex_grid<>(af::adapt((fft.n_complex())))
        .set_focus(af::adapt(fft.n_complex())));
    }

    static flex_complex_array
    forward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_3d_size(a, fft.m_real());
      ref_3d_real_array map(a.begin(), af::c_grid<3>(fft.m_real()));
      fft.forward(map);
      return flex_complex_array(a.handle(),
        af::flex_grid<>(af::adapt((fft.n_complex())))
        .set_focus(af::adapt(fft.n_complex())));
    }

    static flex_real_array
    backward_complex(w_t& fft, flex_complex_array a)
    {
      assert_0_based_3d_size(a, fft.n_complex());
      ref_3d_complex_array map(a.begin(), af::c_grid<3>(fft.n_complex()));
      fft.backward(map);
      return flex_real_array(a.handle(),
        af::flex_grid<>(af::adapt((fft.m_real())))
        .set_focus(af::adapt(fft.n_real())));
    }

    static flex_real_array
    backward_real(w_t& fft, flex_real_array a)
    {
      assert_0_based_3d_size(a, fft.m_real());
      ref_3d_real_array map(a.begin(), af::c_grid<3>(fft.m_real()));
      fft.backward(map);
      return flex_real_array(a, af::flex_grid<>(af::adapt((fft.m_real())))
        .set_focus(af::adapt(fft.n_real())));
    }

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("real_to_complex_3d")
        .def(init<std::size_t, std::size_t, std::size_t>())
        .def(init<af::int3>())
        .def("n_real", &w_t::n_real)
        .def("m_real", &w_t::m_real)
        .def("n_complex", &w_t::n_complex)
        .def("forward", forward_complex)
        .def("forward", forward_real)
        .def("backward", backward_complex)
        .def("backward", backward_real)
      ;
    }
  };

  af::versa<double, af::flex_grid<> >
  zeros_parallel_double(
    af::flex_grid<> const& flex_grid)
  {
    af::versa<double, af::flex_grid<> > result(
      flex_grid,
      af::init_functor_null<double>());
    double* a = result.begin();
    long n = static_cast<long>(result.size());
    #pragma omp parallel for schedule(static)
    for(long i=0;i<n;i++) a[i] = 0.;
    return result;
  }

  void init_module()
  {
    using namespace boost::python;

    def("adjust_gridding", adjust_gridding_2, (
      arg("min_grid"), arg("max_prime")));
    def("adjust_gridding", adjust_gridding_3, (
      arg("min_grid"), arg("max_prime"), arg("mandatory_factor")));
    def("adjust_gridding_triple", adjust_gridding_triple_2, (
      arg("min_grid"), arg("max_prime")));
    def("adjust_gridding_triple", adjust_gridding_triple_3, (
      arg("min_grid"), arg("max_prime"), arg("mandatory_factors")));

    factorization_wrappers::wrap();
    complex_to_complex_wrappers::wrap();
    real_to_complex_wrappers::wrap();
    complex_to_complex_3d_wrappers::wrap();
    complex_to_complex_2d_wrappers::wrap();
    real_to_complex_3d_wrappers::wrap();

    def("zeros_parallel_double", zeros_parallel_double, (arg("flex_grid")));
  }

}}} // namespace scitbx::fftpack::<anonymous>

BOOST_PYTHON_MODULE(scitbx_fftpack_ext)
{
  scitbx::fftpack::init_module();
}


#include <Eigen/Dense>
#include <aslam/backend/TransformationExpressionNode.hpp>
#include <sm/kinematics/transformations.hpp>

namespace aslam {
namespace backend {

////////////////////////////////////////////
// TransformationExpressionNode: The Super Class
////////////////////////////////////////////
TransformationExpressionNode::TransformationExpressionNode() {}

TransformationExpressionNode::~TransformationExpressionNode() {}

Eigen::Matrix4d TransformationExpressionNode::toTransformationMatrix() {
    return toTransformationMatrixImplementation();
}

void TransformationExpressionNode::evaluateJacobians(JacobianContainer& outJacobians) const {
    evaluateJacobiansImplementation(outJacobians);
}

void TransformationExpressionNode::evaluateJacobians(JacobianContainer& outJacobians,
                                                     const Eigen::MatrixXd& applyChainRule) const {
    SM_ASSERT_EQ_DBG(Exception, applyChainRule.cols(), 6, "The chain rule matrix must have 6 columns");
    evaluateJacobiansImplementation(outJacobians, applyChainRule);
}

void TransformationExpressionNode::getDesignVariables(DesignVariable::set_t& designVariables) const {
    getDesignVariablesImplementation(designVariables);
}

/////////////////////////////////////////////////
// TransformationExpressionNodeMultiply: A container for C1 * C2
/////////////////////////////////////////////////

TransformationExpressionNodeMultiply::TransformationExpressionNodeMultiply(
    boost::shared_ptr<TransformationExpressionNode> lhs, boost::shared_ptr<TransformationExpressionNode> rhs)
    : _lhs(lhs), _rhs(rhs) {
    _T_lhs = _lhs->toTransformationMatrix();
    _T_rhs = _rhs->toTransformationMatrix();
}

TransformationExpressionNodeMultiply::~TransformationExpressionNodeMultiply() {}

void TransformationExpressionNodeMultiply::getDesignVariablesImplementation(
    DesignVariable::set_t& designVariables) const {
    _lhs->getDesignVariables(designVariables);
    _rhs->getDesignVariables(designVariables);
}

Eigen::Matrix4d TransformationExpressionNodeMultiply::toTransformationMatrixImplementation() {
    _T_lhs = _lhs->toTransformationMatrix();
    _T_rhs = _rhs->toTransformationMatrix();
    return _T_lhs * _T_rhs;
}

void TransformationExpressionNodeMultiply::evaluateJacobiansImplementation(JacobianContainer& outJacobians) const {
    _rhs->evaluateJacobians(outJacobians, sm::kinematics::boxTimes(_T_lhs));
    _lhs->evaluateJacobians(outJacobians);
}

void TransformationExpressionNodeMultiply::evaluateJacobiansImplementation(
    JacobianContainer& outJacobians, const Eigen::MatrixXd& applyChainRule) const {
    _rhs->evaluateJacobians(outJacobians, (applyChainRule * sm::kinematics::boxTimes(_T_lhs)));
    _lhs->evaluateJacobians(outJacobians, applyChainRule);
}

////////////////////////////////////////////////////
/// TransformationExpressionNodeInverse: A container for T^-1
////////////////////////////////////////////////////

TransformationExpressionNodeInverse::TransformationExpressionNodeInverse(
    boost::shared_ptr<TransformationExpressionNode> dvTransformation)
    : _dvTransformation(dvTransformation) {
    _T = _dvTransformation->toTransformationMatrix().inverse();
}

TransformationExpressionNodeInverse::~TransformationExpressionNodeInverse() {}

Eigen::Matrix4d TransformationExpressionNodeInverse::toTransformationMatrixImplementation() {
    _T = _dvTransformation->toTransformationMatrix().inverse();
    return _T;
}

void TransformationExpressionNodeInverse::evaluateJacobiansImplementation(JacobianContainer& outJacobians) const {
    _dvTransformation->evaluateJacobians(outJacobians, -sm::kinematics::boxTimes(_T));
}

void TransformationExpressionNodeInverse::evaluateJacobiansImplementation(JacobianContainer& outJacobians,
                                                                          const Eigen::MatrixXd& applyChainRule) const {
    _dvTransformation->evaluateJacobians(outJacobians, (applyChainRule * -sm::kinematics::boxTimes(_T)));
}

void TransformationExpressionNodeInverse::getDesignVariablesImplementation(
    DesignVariable::set_t& designVariables) const {
    _dvTransformation->getDesignVariables(designVariables);
}

TransformationExpressionNodeConstant::TransformationExpressionNodeConstant(const Eigen::Matrix4d& T) : _T(T) {}
TransformationExpressionNodeConstant::~TransformationExpressionNodeConstant() {}

Eigen::Matrix4d TransformationExpressionNodeConstant::toTransformationMatrixImplementation() { return _T; }
void TransformationExpressionNodeConstant::evaluateJacobiansImplementation(
    JacobianContainer& /* outJacobians */) const {}
void TransformationExpressionNodeConstant::evaluateJacobiansImplementation(
    JacobianContainer& /* outJacobians */, const Eigen::MatrixXd& /* applyChainRule */) const {}
void TransformationExpressionNodeConstant::getDesignVariablesImplementation(
    DesignVariable::set_t& /* designVariables */) const {}

}  // namespace backend
}  // namespace aslam


#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/calib3d/calib3d.hpp"

#include <iostream>
#include <ctype.h>
#include <algorithm>
#include <iterator>
#include <vector>
#include <ctime>
#include <sstream>
#include <fstream>
#include <string>
#include <Eigen/Dense>
#include <unsupported/Eigen/NonLinearOptimization>
#include <unsupported/Eigen/NumericalDiff>
#include <opencv2/core/eigen.hpp>

#include "feature.h"
#include "utils.h"
#include "evaluate_odometry.h"
#include "visualOdometry.h"
#include "Frame.h"
#include "MapPoint.h"

using namespace std;

typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloud;

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

    // -----------------------------------------
    // Load images and calibration parameters
    // -----------------------------------------
    bool display_ground_truth = false;
    std::vector<Matrix> pose_matrix_gt;
    if(argc == 4)
    {   display_ground_truth = true;
        cerr << "Display ground truth trajectory" << endl;
        // load ground truth pose
        string filename_pose = string(argv[3]);
        pose_matrix_gt = loadPoses(filename_pose);

    }
    if(argc < 3)
    {
        cerr << "Usage: ./run path_to_sequence path_to_calibration [optional]path_to_ground_truth_pose" << endl;
        return 1;
    }

    // Sequence
    string filepath = string(argv[1]);
    cout << "Filepath: " << filepath << endl;

    // Camera calibration
    string strSettingPath = string(argv[2]);
    cout << "Calibration Filepath: " << strSettingPath << endl;

    cv::FileStorage fSettings(strSettingPath, cv::FileStorage::READ);

    float fx = fSettings["Camera.fx"];
    float fy = fSettings["Camera.fy"];
    float cx = fSettings["Camera.cx"];
    float cy = fSettings["Camera.cy"];
    float bf = fSettings["Camera.bf"];

    cv::Mat projMatrl = (cv::Mat_<float>(3, 4) << fx, 0., cx, 0., 0., fy, cy, 0., 0,  0., 1., 0.);
    cv::Mat projMatrr = (cv::Mat_<float>(3, 4) << fx, 0., cx, bf, 0., fy, cy, 0., 0,  0., 1., 0.);
    cout << "P_left: " << endl << projMatrl << endl;
    cout << "P_right: " << endl << projMatrr << endl;

    // -----------------------------------------
    // Initialize variables
    // -----------------------------------------
    cv::Mat rotation = cv::Mat::eye(3, 3, CV_64F);
    cv::Mat translation_stereo = cv::Mat::zeros(3, 1, CV_64F);

    cv::Mat pose = cv::Mat::zeros(3, 1, CV_64F);
    cv::Mat Rpose = cv::Mat::eye(3, 3, CV_64F);

    cv::Mat frame_pose = cv::Mat::eye(4, 4, CV_64F);
    cv::Mat frame_pose32 = cv::Mat::eye(4, 4, CV_32F);
    // cv::hconcat(cv::Mat::eye(4, 4, CV_64F), cv::Mat::zeros(3, 1, CV_64F), frame_pose);
    // cv::vconcat(frame_pose, cv::Mat::zeros(1, 4, CV_64F), frame_pose);

    std::cout << "frame_pose " << frame_pose << std::endl;

    cv::Mat trajectory = cv::Mat::zeros(600, 1200, CV_8UC3);
    pcl::visualization::PCLVisualizer *visualizer;

    FeatureSet currentVOFeatures;

    PointCloud::Ptr features_cloud_ptr (new PointCloud);

    cv::Mat points4D, points3D;

    int init_frame_id = 0;

    // ------------------------
    // Load first images
    // ------------------------
    cv::Mat imageLeft_t0_color,  imageLeft_t0;

    loadImageLeft(imageLeft_t0_color,  imageLeft_t0, init_frame_id, filepath);

    cv::Mat imageRight_t0_color, imageRight_t0;
    loadImageRight(imageRight_t0_color, imageRight_t0, init_frame_id, filepath);

    float fps;

    // -----------------------------------------
    // Run visual odometry
    // -----------------------------------------

    clock_t tic = clock();

    std::vector<MapPoint> mapPoints;

    std::vector<FeaturePoint> oldFeaturePointsLeft;
    std::vector<FeaturePoint> currentFeaturePointsLeft;

    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));

    for (int frame_id = init_frame_id+1; frame_id < 9000; frame_id++)
    {

        std::cout << std::endl << "frame_id " << frame_id << std::endl;
        // ------------
        // Load images
        // ------------
        cv::Mat imageLeft_t1_color,  imageLeft_t1;
        loadImageLeft(imageLeft_t1_color,  imageLeft_t1, frame_id, filepath);
        cv::Mat imageRight_t1_color, imageRight_t1;
        loadImageRight(imageRight_t1_color, imageRight_t1, frame_id, filepath);

        std::vector<cv::Point2f> oldPointsLeft_t0 = currentVOFeatures.points;

        std::vector<cv::Point2f> pointsLeft_t0, pointsRight_t0, pointsLeft_t1, pointsRight_t1;

        matchingFeatures( imageLeft_t0, imageRight_t0,
                          imageLeft_t1, imageRight_t1,
                          currentVOFeatures,
                          mapPoints,
                          pointsLeft_t0,
                          pointsRight_t0,
                          pointsLeft_t1,
                          pointsRight_t1);

        imageLeft_t0 = imageLeft_t1;
        imageRight_t0 = imageRight_t1;

        std::vector<cv::Point2f>& currentPointsLeft_t0 = pointsLeft_t0;
        std::vector<cv::Point2f>& currentPointsLeft_t1 = pointsLeft_t1;

        std::cout << "oldPointsLeft_t0 size : " << oldPointsLeft_t0.size() << std::endl;
        std::cout << "currentFramePointsLeft size : " << currentPointsLeft_t0.size() << std::endl;

        std::vector<cv::Point2f> newPoints;
        std::vector<bool> valid; // valid new points are ture

        // ---------------------
        // Triangulate 3D Points
        // ---------------------
        cv::Mat points3D_t0, points4D_t0;
        cv::triangulatePoints( projMatrl,  projMatrr,  pointsLeft_t0,  pointsRight_t0,  points4D_t0);
        cv::convertPointsFromHomogeneous(points4D_t0.t(), points3D_t0);
        // std::cout << "points4D_t0 size : " << points4D_t0.size() << std::endl;

        cv::Mat points3D_t1, points4D_t1;
        // std::cout << "pointsLeft_t1 size : " << pointsLeft_t1.size() << std::endl;
        // std::cout << "pointsRight_t1 size : " << pointsRight_t1.size() << std::endl;

        cv::triangulatePoints( projMatrl,  projMatrr,  pointsLeft_t1,  pointsRight_t1,  points4D_t1);
        cv::convertPointsFromHomogeneous(points4D_t1.t(), points3D_t1);
        // std::cout << "points4D_t1 size : " << points4D_t1.size() << std::endl;
        // ---------------------
        // Tracking transfomation
        // ---------------------
        trackingFrame2Frame(projMatrl, projMatrr, pointsLeft_t0, pointsLeft_t1, points3D_t0, rotation, translation_stereo);
        displayTracking(imageLeft_t1, pointsLeft_t0, pointsLeft_t1);

        points4D = points4D_t0;
        frame_pose.convertTo(frame_pose32, CV_32F);
        points4D = frame_pose32 * points4D;
        cv::convertPointsFromHomogeneous(points4D.t(), points3D);

        // -------------------------------
        // Append new points to mapPoints
        // -------------------------------

        // removeExistPoints(newPoints, valid, currentPointsLeft_t0, oldPointsLeft_t0);
        distinguishNewPoints(newPoints, valid, mapPoints, frame_id-1,
                             points3D_t0, points3D_t1, points3D,
                             currentPointsLeft_t0, currentPointsLeft_t1, currentFeaturePointsLeft, oldFeaturePointsLeft);
        oldFeaturePointsLeft = currentFeaturePointsLeft;
        std::cout << "mapPoints size : " << mapPoints.size() << std::endl;

        // ------------------------------------------------
        // Append feature points to Point clouds
        // ------------------------------------------------
        // featureSetToPointCloudsValid(points3D, features_cloud_ptr, valid);
        mapPointsToPointCloudsAppend(mapPoints, features_cloud_ptr);
        std::cout << std::endl << "featureSetToPointClouds size: " << features_cloud_ptr->size() << std::endl;
        simpleVis(features_cloud_ptr, viewer);

        // break;

        // ------------------------------------------------
        // Intergrating and display
        // ------------------------------------------------

        cv::Vec3f rotation_euler = rotationMatrixToEulerAngles(rotation);
        std::cout << "rotation: " << rotation_euler << std::endl;
        std::cout << "translation: " << translation_stereo.t() << std::endl;

        cv::Mat rigid_body_transformation;

        if(abs(rotation_euler[1])<0.1 && abs(rotation_euler[0])<0.1 && abs(rotation_euler[2])<0.1)
        {
            integrateOdometryStereo(frame_id, rigid_body_transformation, frame_pose, rotation, translation_stereo);

        } else {

            std::cout << "Too large rotation"  << std::endl;
        }

        std::cout << "rigid_body_transformation" << rigid_body_transformation << std::endl;

        std::cout << "frame_pose" << frame_pose << std::endl;

        Rpose =  frame_pose(cv::Range(0, 3), cv::Range(0, 3));
        cv::Vec3f Rpose_euler = rotationMatrixToEulerAngles(Rpose);
        std::cout << "Rpose_euler" << Rpose_euler << std::endl;

        cv::Mat pose = frame_pose.col(3).clone();

        clock_t toc = clock();
        fps = float(frame_id-init_frame_id)/(toc-tic)*CLOCKS_PER_SEC;

        // pose = -pose;
        std::cout << "Pose" << pose.t() << std::endl;
        std::cout << "FPS: " << fps << std::endl;

        display(frame_id, trajectory, pose, pose_matrix_gt, fps, display_ground_truth);

        // break;

    }

    return 0;
}


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

#include <boost/range/irange.hpp>

auto solution(int total_presents) {

  const auto total_houses = (total_presents / 10);

  /*
   * A std::array of size 3+ million elements is too much
   * for the stack, so std::vector is used here instead
   */
  auto houses = std::vector<decltype(total_presents)>(total_houses);

  auto min_house = total_houses;

  for(const auto elf_id : boost::irange(2, (total_houses))) {

    if(houses[elf_id] + elf_id >= total_houses){
      min_house = elf_id;
      break;
    }

    for(const auto house_id : boost::irange(1, total_houses)) {

      auto presents = elf_id * house_id;

      if(presents >= total_houses) {
        break;
      }

      houses[presents] += elf_id;
    }
  }

  return min_house;
}

int main() {

	const auto total_presents = 33100000;

	std::cout << solution(total_presents) << std::endl;

}


#pragma once

#include <Eigen/Core>
#include <ipc/utils/eigen_ext.hpp>

namespace ipc {

/// The default tolerance used with Tight-Inclusion CCD.
static constexpr double DEFAULT_CCD_TOLERANCE = 1e-6;
/// The default maximum number of iterations used with Tight-Inclusion CCD.
static constexpr long DEFAULT_CCD_MAX_ITERATIONS = 1e7;
/// The default conservative rescaling value used to avoid taking steps exactly
/// to impact.
static constexpr double DEFAULT_CCD_CONSERVATIVE_RESCALING = 0.8;

// 2D

bool point_edge_ccd_2D(
    const Eigen::Vector2d& p_t0,
    const Eigen::Vector2d& e0_t0,
    const Eigen::Vector2d& e1_t0,
    const Eigen::Vector2d& p_t1,
    const Eigen::Vector2d& e0_t1,
    const Eigen::Vector2d& e1_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

// 3D

bool point_point_ccd(
    const Eigen::Vector3d& p0_t0,
    const Eigen::Vector3d& p1_t0,
    const Eigen::Vector3d& p0_t1,
    const Eigen::Vector3d& p1_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

bool point_edge_ccd_3D(
    const Eigen::Vector3d& p_t0,
    const Eigen::Vector3d& e0_t0,
    const Eigen::Vector3d& e1_t0,
    const Eigen::Vector3d& p_t1,
    const Eigen::Vector3d& e0_t1,
    const Eigen::Vector3d& e1_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

bool point_triangle_ccd(
    const Eigen::Vector3d& p_t0,
    const Eigen::Vector3d& t0_t0,
    const Eigen::Vector3d& t1_t0,
    const Eigen::Vector3d& t2_t0,
    const Eigen::Vector3d& p_t1,
    const Eigen::Vector3d& t0_t1,
    const Eigen::Vector3d& t1_t1,
    const Eigen::Vector3d& t2_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

bool edge_edge_ccd(
    const Eigen::Vector3d& ea0_t0,
    const Eigen::Vector3d& ea1_t0,
    const Eigen::Vector3d& eb0_t0,
    const Eigen::Vector3d& eb1_t0,
    const Eigen::Vector3d& ea0_t1,
    const Eigen::Vector3d& ea1_t1,
    const Eigen::Vector3d& eb0_t1,
    const Eigen::Vector3d& eb1_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

// 2D or 3D

bool point_edge_ccd(
    const VectorMax3d& p_t0,
    const VectorMax3d& e0_t0,
    const VectorMax3d& e1_t0,
    const VectorMax3d& p_t1,
    const VectorMax3d& e0_t1,
    const VectorMax3d& e1_t1,
    double& toi,
    const double tmax = 1.0,
    const double tolerance = DEFAULT_CCD_TOLERANCE,
    const long max_iterations = DEFAULT_CCD_MAX_ITERATIONS,
    const double conservative_rescaling = DEFAULT_CCD_CONSERVATIVE_RESCALING);

} // namespace ipc


/*
 * Copyright 2017-2020 CNRS-AIST JRL and CNRS-UM LIRMM
 */

#include <Eigen/Eigenvalues>

#include <tvm/function/abstract/Function.h>
#include <tvm/task_dynamics/ProportionalDerivative.h>

namespace tvm
{

namespace task_dynamics
{

ProportionalDerivative::ProportionalDerivative(double kp, double kv) : kp_(kp), kv_(kv) {}

ProportionalDerivative::ProportionalDerivative(const Eigen::VectorXd & kp, const Eigen::VectorXd & kv)
: kp_(kp), kv_(kv)
{}

ProportionalDerivative::ProportionalDerivative(const Eigen::MatrixXd & kp, const Eigen::MatrixXd & kv)
: kp_(kp), kv_(kv)
{}

ProportionalDerivative::ProportionalDerivative(double kp, const Eigen::VectorXd & kv) : kp_(kp), kv_(kv) {}

ProportionalDerivative::ProportionalDerivative(double kp, const Eigen::MatrixXd & kv) : kp_(kp), kv_(kv) {}

ProportionalDerivative::ProportionalDerivative(const Eigen::VectorXd & kp, double kv) : kp_(kp), kv_(kv) {}

ProportionalDerivative::ProportionalDerivative(const Eigen::VectorXd & kp, const Eigen::MatrixXd & kv)
: kp_(kp), kv_(kv)
{}

ProportionalDerivative::ProportionalDerivative(const Eigen::MatrixXd & kp, double kv) : kp_(kp), kv_(kv) {}

ProportionalDerivative::ProportionalDerivative(const Eigen::MatrixXd & kp, const Eigen::VectorXd & kv)
: kp_(kp), kv_(kv)
{}

ProportionalDerivative::ProportionalDerivative(double kp) : ProportionalDerivative(kp, 2 * std::sqrt(kp)) {}

ProportionalDerivative::ProportionalDerivative(const Eigen::VectorXd & kp)
: kp_(kp), kv_(mpark::in_place_index<1>, 2 * kp.cwiseSqrt())
{}

ProportionalDerivative::ProportionalDerivative(const Eigen::MatrixXd & kp)
: kp_(kp), kv_(Eigen::MatrixXd(kp.rows(), kp.cols()))
{
  Eigen::RealSchur<Eigen::MatrixXd> dec(kp);
  assert(dec.matrixT().isDiagonal(1e-8) && "kp is not symmetric.");
  assert((dec.matrixT().diagonal().array() >= 0).all() && "kp is indefinite.");
  kv_.emplace<2>(2 * dec.matrixU() * dec.matrixT().diagonal().asDiagonal() * dec.matrixU().transpose());
}

std::unique_ptr<abstract::TaskDynamicsImpl> ProportionalDerivative::impl_(FunctionPtr f,
                                                                          constraint::Type t,
                                                                          const Eigen::VectorXd & rhs) const
{
  return std::make_unique<Impl>(f, t, rhs, kp_, kv_);
}

Order ProportionalDerivative::order_() const { return Order::Two; }

ProportionalDerivative::Impl::Impl(FunctionPtr f,
                                   constraint::Type t,
                                   const Eigen::VectorXd & rhs,
                                   const Gain & kp,
                                   const Gain & kv)
: TaskDynamicsImpl(Order::Two, f, t, rhs), kp_(kp), kv_(kv)
{
  assert((kp.index() == 0                                                             // Scalar gain
          || (kp.index() == 1 && mpark::get<Eigen::VectorXd>(kp).size() == f->size()) // Diagonal gain
          || (kp.index() == 2 && mpark::get<Eigen::MatrixXd>(kp).cols() == f->size()  // Matrix gain
              && mpark::get<Eigen::MatrixXd>(kp).rows() == f->size()))
         && "Gain kp and function have incompatible sizes");

  assert((kv.index() == 0                                                             // Scalar gain
          || (kv.index() == 1 && mpark::get<Eigen::VectorXd>(kv).size() == f->size()) // Diagonal gain
          || (kv.index() == 2 && mpark::get<Eigen::MatrixXd>(kv).cols() == f->size()  // Matrix gain
              && mpark::get<Eigen::MatrixXd>(kv).rows() == f->size()))
         && "Gain kv and function have incompatible sizes");
}

void ProportionalDerivative::Impl::updateValue()
{
  switch(kv_.index())
  {
    case 0:
      value_ = -mpark::get<double>(kv_) * function().velocity();
      break;
    case 1:
      value_.noalias() = -(mpark::get<Eigen::VectorXd>(kv_).asDiagonal() * function().velocity());
      break;
    case 2:
      value_.noalias() = -mpark::get<Eigen::MatrixXd>(kv_) * function().velocity();
      break;
    default:
      assert(false);
  }
  switch(kp_.index())
  {
    case 0:
      value_ -= mpark::get<double>(kp_) * (function().value() - rhs());
      break;
    case 1:
      value_.noalias() -= mpark::get<Eigen::VectorXd>(kp_).asDiagonal() * (function().value() - rhs());
      break;
    case 2:
      value_.noalias() -= mpark::get<Eigen::MatrixXd>(kp_) * (function().value() - rhs());
      break;
    default:
      assert(false);
  }
}

std::pair<const PD::Gain &, const PD::Gain &> ProportionalDerivative::Impl::gains() const { return {kp_, kv_}; }

void ProportionalDerivative::Impl::gains(double kp, double kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::VectorXd & kp, const Eigen::VectorXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::MatrixXd & kp, const Eigen::MatrixXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(double kp, const Eigen::VectorXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(double kp, const Eigen::MatrixXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::VectorXd & kp, double kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::VectorXd & kp, const Eigen::MatrixXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::MatrixXd & kp, double kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(const Eigen::MatrixXd & kp, const Eigen::VectorXd & kv) { gains_(kp, kv); }

void ProportionalDerivative::Impl::gains(double kp)
{
  kp_ = kp;
  kv_ = 2 * std::sqrt(kp);
}

void ProportionalDerivative::Impl::gains(const Eigen::VectorXd & kp)
{
  checkGainSize(kp);
  kp_ = kp;
  kv_.emplace<1>(2 * kp.cwiseSqrt());
}

void ProportionalDerivative::Impl::gains(const Eigen::MatrixXd & kp)
{
  checkGainSize(kp);
  Eigen::RealSchur<Eigen::MatrixXd> dec(kp);
  assert(dec.matrixT().isDiagonal(1e-8) && "kp is not symmetric.");
  assert((dec.matrixT().diagonal().array() >= 0).all() && "kp is indefinite.");
  kv_.emplace<2>(2 * dec.matrixU() * dec.matrixT().diagonal().asDiagonal() * dec.matrixU().transpose());
}

void ProportionalDerivative::Impl::checkGainSize(double) const
{
  // do nothing
}

void ProportionalDerivative::Impl::checkGainSize(const Eigen::VectorXd & k) const
{
  if(k.size() != function().size())
  {
    throw std::runtime_error(
        "[task_dynamics::ProportionalDerivative::Impl::gains] Gain and function have incompatible sizes.");
  }
}

void ProportionalDerivative::Impl::checkGainSize(const Eigen::MatrixXd & k) const
{
  if(k.rows() != function().size() || k.cols() != function().size())
  {
    throw std::runtime_error(
        "[task_dynamics::ProportionalDerivative::Impl::gains] Gain and function have incompatible sizes.");
  }
}

} // namespace task_dynamics

} // namespace tvm


/// @author    Johannes de Fine Licht (definelicht@inf.ethz.ch)
/// @copyright This software is copyrighted under the BSD 3-Clause License.

// #include <fstream>
// #include <boost/filesystem.hpp>

#include <cmath>
#include <iostream>
#include <random>
#include "NBody.h"
#include "Utility.h"

void NewAlgorithmReference(PosMass_t positionMass[], Vec_t velocity[],
                           unsigned timesteps);

int main(int argc, char const **argv) {
  if (argc > 2) {
    std::cerr << "Usage: ./RunNBody.exe [<number of timesteps>]" << std::endl;
    return 1;
  }

  const int timesteps = (argc == 2) ? std::stoi(argv[1]) : kSteps;
  std::vector<Vec_t> velocity(kNBodies);
  std::vector<PosMass_t> position(2 * kNBodies);  // Double buffering

  // maybe make those configurable in the future :), if we use them
  float clusterScale = 1.54f;
  float velocityScale = 8.0f;

  std::random_device rd;
  std::default_random_engine rng(rd());
  std::uniform_real_distribution<double> distMass(1e10, 1e12);
  std::uniform_real_distribution<double> distVelocity(-1e3, 1e3);
  std::uniform_real_distribution<double> distPosition(-1e6, 1e6);

  float scale = clusterScale * std::max<Data_t>(1.0, kNBodies / (1024.0));
  float vscale = velocityScale * scale;

  int i = 0;

  while (i < kNBodies) {
    Vec_t point;
    float lenSqr = 0.0;
    for (int j = 0; j < kDims; j++) {
      point[j] = rand() / (float)RAND_MAX * 2 - 1;
      lenSqr += point[j] * point[j];
    }
    if (lenSqr > 1) continue;

    Vec_t vel;
    lenSqr = 0.0;
    for (int j = 0; j < kDims; j++) {
      vel[j] = rand() / (float)RAND_MAX * 2 - 1;
      lenSqr += vel[j] * vel[j];
    }
    if (lenSqr > 1) continue;

    for (int j = 0; j < kDims; j++) {
      position[i][j] = point[j] * scale;
      velocity[i][j] = vel[j] * vscale;
    }

    position[i][kDims] = 1.0f;  // mass

    i++;
  }

  // NBody(reinterpret_cast<MemoryPack_t const *>(&mass[0]),
  //       reinterpret_cast<MemoryPack_t const *>(&position[0]),
  //       reinterpret_cast<MemoryPack_t *>(&position[0]),
  //       reinterpret_cast<MemoryPack_t const *>(&velocity[0]),
  //       reinterpret_cast<MemoryPack_t *>(&velocity[0]));

  std::vector<PosMass_t> positionRef(position);
  std::vector<Vec_t> velocityRef(velocity);
  std::vector<PosMass_t> positionHardware(position);
  std::vector<MemoryPack_t> velocityHardware((3 * kNBodies * sizeof(Data_t)) /
                                             sizeof(MemoryPack_t));
  hlslib::Stream<MemoryPack_t> velocityReadMemory("velocityReadMemory");
  hlslib::Stream<MemoryPack_t> velocityWriteMemory("velocityWriteMemory");
  hlslib::Stream<Vec_t> velocityReadKernel("velocityReadKernel");
  hlslib::Stream<Vec_t> velocityWriteKernel("velocityWriteKernel");
  hlslib::Stream<MemoryPack_t> positionMassReadMemory("positionMassReadMemory");
  hlslib::Stream<MemoryPack_t> positionMassWriteMemory(
      "positionMassWriteMemory");
  hlslib::Stream<PosMass_t> positionMassReadKernel("positionMassReadKernel");
  hlslib::Stream<PosMass_t> positionMassWriteKernel("positionMassWriteKernel");

  for (int i = 0; i < 3 * kNBodies; i++) {
    velocityHardware[i / (sizeof(MemoryPack_t) / sizeof(Data_t))]
                    [i % (sizeof(MemoryPack_t) / sizeof(Data_t))] =
                        velocity[i / 3][i % 3];
  }

  std::cout << "Running CUDA reference implementation..." << std::flush;
  ReferenceLikeCUDA(positionRef.data(), velocityRef.data(), timesteps);
  std::cout << " Done.\n";

  std::cout << "Running reference implementation of new algorithm..."
            << std::flush;
  NewAlgorithmReference(position.data(), velocity.data(), timesteps);
  std::cout << " Done.\n";

  std::cout << "Running emulation of hardware implementation..." << std::flush;
  NBody(timesteps, reinterpret_cast<MemoryPack_t const *>(&positionHardware[0]),
        reinterpret_cast<MemoryPack_t *>(&positionHardware[0]),
        &velocityHardware[0], &velocityHardware[0], velocityReadMemory,
        velocityReadKernel, positionMassReadMemory, positionMassReadKernel,
        velocityWriteKernel, velocityWriteMemory, positionMassWriteKernel,
        positionMassWriteMemory);
  std::cout << " Done.\n";

  std::cout << "Verifying results..." << std::flush;
  constexpr int kPrintBodies = 0;
  const unsigned offset = (timesteps % 2 == 0) ? 0 : kNBodies;
  for (int i = 0; i < kNBodies; ++i) {
    if (i < kPrintBodies) {
      std::cout << position[i] << " / " << positionRef[i] << ", " << velocity[i]
                << " / " << velocityRef[i] << "\n";
    }
    for (int d = 0; d < kDims; ++d) {
      {
        const auto diff = std::abs(position[i][d] - positionRef[i][d]);
        if (diff >= 1e-4) {
          std::cerr << "Mismatch in reference implementation at index " << i
                    << ": " << position[i] << " (should be " << positionRef[i]
                    << ")." << std::endl;
          return 1;
        }
      }
      {
        const auto diff =
            std::abs(position[i][d] - positionHardware[i + offset][d]);
        if (diff >= 1e-4) {
          std::cerr << "Mismatch in hardware implementation at index " << i
                    << ": " << positionHardware[i + offset] << " (should be "
                    << position[i] << ")." << std::endl;
          return 1;
        }
      }
    }
  }
  std::cout << " Done." << std::endl;

  return 0;
}

// Reference implementation for new hardware design
void NewAlgorithmReference(PosMass_t positionMass[], Vec_t velocity[],
                           unsigned timesteps) {
  for (int t = 0; t < timesteps; t++) {
    PosMass_t positionMassNew[kNBodies];
    Vec_t velocityNew[kNBodies];
    // is a datapack initialised to zero by default?
    PosMass_t posWeight[2][kUnrollDepth][kPipelineFactor];
    Vec_t acc[kUnrollDepth][kPipelineFactor];
    int next = 0;

    for (int i = 0; i < kUnrollDepth; i++) {
      for (int j = 0; j < kPipelineFactor; j++) {
        for (int k = 0; k < kDims + 1; k++) {
          posWeight[0][i][j][k] = positionMass[i * kPipelineFactor + j][k];
          if (k != kDims) acc[i][j][k] = 0.0;
        }
      }
    }
    for (int i = 0; i < kNBodies / (kPipelineFactor * kUnrollDepth); i++) {
      next = 1 - next;
      for (int j = 0; j < kNBodies; j++) {
        PosMass_t currentPos;
        for (int k = 0; k < kDims + 1; k++) {
          currentPos[k] = positionMass[j][k];

          // Here I populate the next buffer with appropriate elments.
          if (j >= (i + 1) * kPipelineFactor * kUnrollDepth &&
              j < (i + 2) * kPipelineFactor * kUnrollDepth &&
              i != kNBodies / (kPipelineFactor * kUnrollDepth) - 1) {
            int a = j - (i + 1) * kPipelineFactor * kUnrollDepth;
            posWeight[next][a / kPipelineFactor][a % kPipelineFactor][k] =
                currentPos[k];
          }
        }

        // Now comes the second unroll, this time per processing element
        for (int l = 0; l < kPipelineFactor; l++) {
          // The loop that is replicated in Hardware
          for (int k = 0; k < kUnrollDepth; k++) {
            PosMass_t s0 = posWeight[1 - next][k][l];
            Vec_t tmpacc = ComputeAcceleration<true>(s0, currentPos);
            // Write to buffer
            if (j != kNBodies - 1) {
              acc[k][l] = acc[k][l] + tmpacc;
            } else {
              for (int s = 0; s < kDims; s++) {
                // Writeout
                Data_t v = (acc[k][l][s] + tmpacc[s]) * kTimestep;
                // test
                acc[k][l][s] = acc[k][l][s] + tmpacc[s];
                float vel = velocity[i * (kPipelineFactor * kUnrollDepth) +
                                     kPipelineFactor * k + l][s] +
                            v;
                velocityNew[i * (kPipelineFactor * kUnrollDepth) +
                            kPipelineFactor * k + l][s] = vel;
                positionMassNew[i * (kPipelineFactor * kUnrollDepth) +
                                kPipelineFactor * k + l][s] =
                    positionMass[i * (kPipelineFactor * kUnrollDepth) +
                                 kPipelineFactor * k + l][s] +
                    vel * kTimestep;
              }
              Vec_t a(static_cast<Data_t>(0));
              acc[k][l] = a;
              positionMassNew[i * (kPipelineFactor * kUnrollDepth) +
                              kPipelineFactor * k + l][kDims] =
                  positionMass[i * (kPipelineFactor * kUnrollDepth) +
                               kPipelineFactor * k + l][kDims];
            }
          }
        }
      }
    }
    // use swap buffers?
    for (int i = 0; i < kNBodies; i++) {
      velocity[i] = velocityNew[i];
      positionMass[i] = positionMassNew[i];
    }
  }
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// 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://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestValarray
#include <boost/test/unit_test.hpp>

#include <boost/compute/system.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/container/valarray.hpp>

#include "check_macros.hpp"
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(size)
{
    boost::compute::valarray<float> array;
    BOOST_CHECK_EQUAL(array.size(), size_t(0));

    array.resize(10);
    BOOST_CHECK_EQUAL(array.size(), size_t(10));
}

BOOST_AUTO_TEST_CASE(at)
{
    int data[] = { 1, 2, 3, 4, 5 };
    boost::compute::valarray<int> array(data, 5);
    BOOST_CHECK_EQUAL(array.size(), size_t(5));

    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(int(array[0]), int(1));
    BOOST_CHECK_EQUAL(int(array[1]), int(2));
    BOOST_CHECK_EQUAL(int(array[2]), int(3));
    BOOST_CHECK_EQUAL(int(array[3]), int(4));
    BOOST_CHECK_EQUAL(int(array[4]), int(5));
}

BOOST_AUTO_TEST_CASE(min_and_max)
{
    int data[] = { 5, 2, 3, 7, 1, 9, 6, 5 };
    boost::compute::valarray<int> array(data, 8);
    BOOST_CHECK_EQUAL(array.size(), size_t(8));

    BOOST_CHECK_EQUAL((array.min)(), int(1));
    BOOST_CHECK_EQUAL((array.max)(), int(9));
}

BOOST_AUTO_TEST_CASE(sum)
{
    int data[] = { 1, 2, 3, 4 };
    boost::compute::valarray<int> array(data, 4);
    boost::compute::system::finish();

    BOOST_CHECK_EQUAL(array.size(), size_t(4));
    BOOST_CHECK_EQUAL(array.sum(), int(10));
}

BOOST_AUTO_TEST_CASE(apply)
{
    int data[] = { -1, 2, -3, 4 };
    boost::compute::valarray<int> array(data, 4);

    boost::compute::abs<int> abs;
    boost::compute::valarray<int> result = array.apply(abs);
    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(int(result[0]), int(1));
    BOOST_CHECK_EQUAL(int(result[1]), int(2));
    BOOST_CHECK_EQUAL(int(result[2]), int(3));
    BOOST_CHECK_EQUAL(int(result[3]), int(4));
}

/// \internal_
/// Tests for compound assignment operators that works for floating
/// point types.
#define BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT(op, op_name) \
BOOST_AUTO_TEST_CASE(op_name##_ca_operator_no_fp) \
{ \
    float data[] = { 1, 2, 3, 4 }; \
    boost::compute::valarray<float> array1(data, 4); \
    boost::compute::valarray<float> array2(data, 4); \
    boost::compute::system::finish(); \
    \
    array1 op##= 1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(array1[0]), float(1.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[1]), float(2.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[2]), float(3.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[3]), float(4.0f op 1.0f), 1e-4f); \
    \
    array1 = boost::compute::valarray<float>(data, 4); \
    boost::compute::system::finish(); \
    \
    array1 op##= array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(array1[0]), float(1.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[1]), float(2.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[2]), float(3.0f op 3.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array1[3]), float(4.0f op 4.0f), 1e-4f); \
    \
    array2 op##= array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(array2[0]), float(1.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array2[1]), float(2.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array2[2]), float(3.0f op 3.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(array2[3]), float(4.0f op 4.0f), 1e-4f); \
    \
}

BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT(+, plus)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT(-, minus)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT(*, multiplies)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT(/, divides)

#undef BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT

/// \internal_
/// Tests for compound assignment operators that does NOT work for floating
/// point types.
/// Note: modulo operator works only for integer types.
#define BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(op, op_name) \
BOOST_AUTO_TEST_CASE(op_name##_ca_operator) \
{ \
    int data[] = { 1, 2, 3, 4 }; \
    boost::compute::valarray<int> array1(data, 4); \
    boost::compute::valarray<int> array2(data, 4); \
    boost::compute::system::finish(); \
    \
    array1 op##= 1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(array1[0]), int(1 op 1)); \
    BOOST_CHECK_EQUAL(int(array1[1]), int(2 op 1)); \
    BOOST_CHECK_EQUAL(int(array1[2]), int(3 op 1)); \
    BOOST_CHECK_EQUAL(int(array1[3]), int(4 op 1)); \
    \
    array1 = boost::compute::valarray<int>(data, 4); \
    boost::compute::system::finish(); \
    \
    array1 op##= array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(array1[0]), int(1 op 1)); \
    BOOST_CHECK_EQUAL(int(array1[1]), int(2 op 2)); \
    BOOST_CHECK_EQUAL(int(array1[2]), int(3 op 3)); \
    BOOST_CHECK_EQUAL(int(array1[3]), int(4 op 4)); \
    \
    array2 op##= array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(array2[0]), int(1 op 1)); \
    BOOST_CHECK_EQUAL(int(array2[1]), int(2 op 2)); \
    BOOST_CHECK_EQUAL(int(array2[2]), int(3 op 3)); \
    BOOST_CHECK_EQUAL(int(array2[3]), int(4 op 4)); \
    \
}

BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(%, modulus)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(^, bit_xor)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(&, bit_and)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(|, bit_or)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(<<, shift_left)
BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP(>>, shift_right)

#undef BOOST_COMPUTE_TEST_VALARRAY_COMPOUND_ASSIGNMENT_NO_FP

BOOST_AUTO_TEST_CASE(unary_plus_operator)
{
    int data[] = { 1, 2, 3, 4 };
    boost::compute::valarray<int> array(data, 4);
    boost::compute::system::finish();

    boost::compute::valarray<int> result = +array;
    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(int(result[0]), +(int(1)));
    BOOST_CHECK_EQUAL(int(result[1]), +(int(2)));
    BOOST_CHECK_EQUAL(int(result[2]), +(int(3)));
    BOOST_CHECK_EQUAL(int(result[3]), +(int(4)));
}

BOOST_AUTO_TEST_CASE(unary_minus_operator)
{
    int data[] = { -1, 2, 0, 4 };
    boost::compute::valarray<int> array(data, 4);
    boost::compute::system::finish();

    boost::compute::valarray<int> result = -array;
    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(int(result[0]), int(1));
    BOOST_CHECK_EQUAL(int(result[1]), int(-2));
    BOOST_CHECK_EQUAL(int(result[2]), int(0));
    BOOST_CHECK_EQUAL(int(result[3]), int(-4));
}

BOOST_AUTO_TEST_CASE(unary_bitwise_not_operator)
{
    int data[] = { 1, 2, 3, 4 };
    boost::compute::valarray<int> array(data, 4);
    boost::compute::system::finish();

    boost::compute::valarray<int> result = ~array;
    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(int(result[0]), ~(int(1)));
    BOOST_CHECK_EQUAL(int(result[1]), ~(int(2)));
    BOOST_CHECK_EQUAL(int(result[2]), ~(int(3)));
    BOOST_CHECK_EQUAL(int(result[3]), ~(int(4)));
}

BOOST_AUTO_TEST_CASE(unary_logical_not_operator)
{
    int data[] = { 1, -2, 0, 4 };
    boost::compute::valarray<int> array(data, 4);
    boost::compute::system::finish();

    boost::compute::valarray<char> result = !array;
    boost::compute::system::finish();
    BOOST_CHECK_EQUAL(bool(result[0]), !(int(1)));
    BOOST_CHECK_EQUAL(bool(result[1]), !(int(-2)));
    BOOST_CHECK_EQUAL(bool(result[2]), !(int(0)));
    BOOST_CHECK_EQUAL(bool(result[3]), !(int(4)));
}

/// \internal_
/// Tests for binary operators that works for floating
/// point types.
#define BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR(op, op_name) \
BOOST_AUTO_TEST_CASE(op_name##_binary_operator) \
{ \
    float data1[] = { 1, 2, 3, 4 }; \
    float data2[] = { 4, 2, 3, 0 }; \
    boost::compute::valarray<float> array1(data1, 4); \
    boost::compute::valarray<float> array2(data2, 4); \
    boost::compute::system::finish(); \
    \
    boost::compute::valarray<float> result = 2.0f op array1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(result[0]), float(2.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[1]), float(2.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[2]), float(2.0f op 3.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[3]), float(2.0f op 4.0f), 1e-4f); \
    \
    result = array1 op 2.0f; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(result[0]), float(1.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[1]), float(2.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[2]), float(3.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[3]), float(4.0f op 2.0f), 1e-4f); \
    \
    result = array2 op array1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_CLOSE(float(result[0]), float(4.0f op 1.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[1]), float(2.0f op 2.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[2]), float(3.0f op 3.0f), 1e-4f); \
    BOOST_CHECK_CLOSE(float(result[3]), float(0.0f op 4.0f), 1e-4f); \
}

BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR(+, plus)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR(-, minus)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR(*, multiplies)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR(/, divides)

#undef BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR

/// \internal_
/// Tests for compound assignment operators that does NOT work for floating
/// point types.
/// Note: modulo operator works only for integer types.
#define BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(op, op_name) \
BOOST_AUTO_TEST_CASE(op_name##_binary_operator) \
{ \
    int data1[] = { 1, 2, 3, 4 }; \
    int data2[] = { 4, 5, 2, 1 }; \
    boost::compute::valarray<int> array1(data1, 4); \
    boost::compute::valarray<int> array2(data2, 4); \
    boost::compute::system::finish(); \
    \
    boost::compute::valarray<int> result = 5 op array1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(result[0]), int(5 op 1)); \
    BOOST_CHECK_EQUAL(int(result[1]), int(5 op 2)); \
    BOOST_CHECK_EQUAL(int(result[2]), int(5 op 3)); \
    BOOST_CHECK_EQUAL(int(result[3]), int(5 op 4)); \
    \
    result = array1 op 5; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(result[0]), int(1 op 5)); \
    BOOST_CHECK_EQUAL(int(result[1]), int(2 op 5)); \
    BOOST_CHECK_EQUAL(int(result[2]), int(3 op 5)); \
    BOOST_CHECK_EQUAL(int(result[3]), int(4 op 5)); \
    \
    result = array1 op array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(int(result[0]), int(1 op 4)); \
    BOOST_CHECK_EQUAL(int(result[1]), int(2 op 5)); \
    BOOST_CHECK_EQUAL(int(result[2]), int(3 op 2)); \
    BOOST_CHECK_EQUAL(int(result[3]), int(4 op 1)); \
}

BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(^, bit_xor)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(&, bit_and)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(|, bit_or)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(<<, shift_left)
BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP(>>, shift_right)

#undef BOOST_COMPUTE_TEST_VALARRAY_BINARY_OPERATOR_NO_FP

/// \internal_
/// Macro for generating tests for valarray comparison operators.
#define BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(op, op_name) \
BOOST_AUTO_TEST_CASE(op_name##_comparision_operator) \
{ \
    int data1[] = { 1, 2, 0, 4 }; \
    int data2[] = { 4, 0, 2, 1 }; \
    boost::compute::valarray<int> array1(data1, 4); \
    boost::compute::valarray<int> array2(data2, 4); \
    boost::compute::system::finish(); \
    \
    boost::compute::valarray<char> result = 2 op array1; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(bool(result[0]), bool(2 op 1)); \
    BOOST_CHECK_EQUAL(bool(result[1]), bool(2 op 2)); \
    BOOST_CHECK_EQUAL(bool(result[2]), bool(2 op 0)); \
    BOOST_CHECK_EQUAL(bool(result[3]), bool(2 op 4)); \
    \
    result = array1 op 2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(bool(result[0]), bool(1 op 2)); \
    BOOST_CHECK_EQUAL(bool(result[1]), bool(2 op 2)); \
    BOOST_CHECK_EQUAL(bool(result[2]), bool(0 op 2)); \
    BOOST_CHECK_EQUAL(bool(result[3]), bool(4 op 2)); \
    \
    result = array1 op array2; \
    boost::compute::system::finish(); \
    BOOST_CHECK_EQUAL(bool(result[0]), bool(1 op 4)); \
    BOOST_CHECK_EQUAL(bool(result[1]), bool(2 op 0)); \
    BOOST_CHECK_EQUAL(bool(result[2]), bool(0 op 2)); \
    BOOST_CHECK_EQUAL(bool(result[3]), bool(4 op 1)); \
}

BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(==, equal_to)
BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(!=, not_equal_to)
BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(>, greater)
BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(<, less)
BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(>=, greater_equal)
BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(<=, less_equal)

/// \internal_
/// Macro for generating tests for valarray binary logical operators.
#define BOOST_COMPUTE_TEST_VALARRAY_LOGICAL_OPERATOR(op, op_name) \
    BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR(op, op_name)

BOOST_COMPUTE_TEST_VALARRAY_LOGICAL_OPERATOR(&&, logical_and)
BOOST_COMPUTE_TEST_VALARRAY_LOGICAL_OPERATOR(||, logical_or)

#undef BOOST_COMPUTE_TEST_VALARRAY_LOGICAL_OPERATOR

#undef BOOST_COMPUTE_TEST_VALARRAY_COMPARISON_OPERATOR

BOOST_AUTO_TEST_SUITE_END()


#include <boost/multiprecision/miller_rabin.hpp>


// Boost.Polygon library voronoi_advanced_tutorial.cpp file

//          Copyright Andrii Sydorchuk 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

// See http://www.boost.org for updates, documentation, and revision history.

#include <cmath>
#include <cstdio>
#include <ctime>
#include <string>

// This will work properly only with GCC compiler.
#include <ieee754.h>
typedef long double fpt80;

// Random generators and distributions.
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/timer/timer.hpp>

#include <boost/polygon/voronoi.hpp>
using namespace boost::polygon;

struct my_ulp_comparison {
  enum Result {
    LESS = -1,
    EQUAL = 0,
    MORE = 1
  };

  Result operator()(fpt80 a, fpt80 b, unsigned int maxUlps) const {
    if (a == b)
      return EQUAL;
    if (a > b) {
      Result res = operator()(b, a, maxUlps);
      if (res == EQUAL) return res;
      return (res == LESS) ? MORE : LESS;
    }
    ieee854_long_double lhs, rhs;
    lhs.d = a;
    rhs.d = b;
    if (lhs.ieee.negative ^ rhs.ieee.negative)
      return lhs.ieee.negative ? LESS : MORE;
    boost::uint64_t le = lhs.ieee.exponent; le =
        (le << 32) + lhs.ieee.mantissa0;
    boost::uint64_t re = rhs.ieee.exponent; re =
        (re << 32) + rhs.ieee.mantissa0;
    if (lhs.ieee.negative) {
      if (le - 1 > re)
        return LESS;
      le = (le == re) ? 0 : 1;
      le = (le << 32) + lhs.ieee.mantissa1;
      re = rhs.ieee.mantissa1;
      return (re + maxUlps < le) ? LESS : EQUAL;
    } else {
      if (le + 1 < re)
        return LESS;
      le = lhs.ieee.mantissa0;
      re = (le == re) ? 0 : 1;
      re = (re << 32) + rhs.ieee.mantissa1;
      return (le + maxUlps < re) ? LESS : EQUAL;
    }
  }
};

struct my_fpt_converter {
  template <typename T>
  fpt80 operator()(const T& that) const {
    return static_cast<fpt80>(that);
  }

  template <std::size_t N>
  fpt80 operator()(const typename detail::extended_int<N> &that) const {
    fpt80 result = 0.0;
    for (std::size_t i = 1; i <= (std::min)((std::size_t)3, that.size()); ++i) {
      if (i != 1)
        result *= static_cast<fpt80>(0x100000000ULL);
      result += that.chunks()[that.size() - i];
    }
    return (that.count() < 0) ? -result : result;
  }
};

// Voronoi diagram traits.
struct my_voronoi_diagram_traits {
  typedef fpt80 coordinate_type;
  typedef voronoi_cell<coordinate_type> cell_type;
  typedef voronoi_vertex<coordinate_type> vertex_type;
  typedef voronoi_edge<coordinate_type> edge_type;
  typedef class {
  public:
    enum { ULPS = 128 };
    bool operator()(const vertex_type &v1, const vertex_type &v2) const {
      return (ulp_cmp(v1.x(), v2.x(), ULPS) == my_ulp_comparison::EQUAL &&
              ulp_cmp(v1.y(), v2.y(), ULPS) == my_ulp_comparison::EQUAL);
    }
  private:
    my_ulp_comparison ulp_cmp;
  } vertex_equality_predicate_type;
};

// Voronoi ctype traits for 48-bit signed integer input coordinates.
struct my_voronoi_ctype_traits {
  typedef boost::int64_t int_type;
  typedef detail::extended_int<3> int_x2_type;
  typedef detail::extended_int<3> uint_x2_type;
  typedef detail::extended_int<128> big_int_type;
  typedef fpt80 fpt_type;
  typedef fpt80 efpt_type;
  typedef my_ulp_comparison ulp_cmp_type;
  typedef my_fpt_converter to_fpt_converter_type;
  typedef my_fpt_converter to_efpt_converter_type;
};

const unsigned int GENERATED_POINTS = 100;
const boost::int64_t MAX = 0x1000000000000LL;

int main() {
  boost::mt19937_64 gen(std::time(0));
  boost::random::uniform_int_distribution<boost::int64_t> distr(-MAX, MAX-1);
  voronoi_builder<boost::int64_t, my_voronoi_ctype_traits> vb;
  for (size_t i = 0; i < GENERATED_POINTS; ++i) {
    boost::int64_t x = distr(gen);
    boost::int64_t y = distr(gen);
    vb.insert_point(x, y);
  }

  printf("Constructing Voronoi diagram of %d points...\n", GENERATED_POINTS);
  boost::timer::cpu_timer t;
  voronoi_diagram<fpt80, my_voronoi_diagram_traits> vd;
  t.start();
  vb.construct(&vd);
  boost::timer::cpu_times times = t.elapsed();
  std::string ftime = boost::timer::format(times, 5, "%w");

  printf("Construction done in: %s seconds.\n", ftime.c_str());
  printf("Resulting Voronoi graph has the following stats:\n");
  printf("Number of Voronoi cells: %lu.\n", vd.num_cells());
  printf("Number of Voronoi vertices: %lu.\n", vd.num_vertices());
  printf("Number of Voronoi edges: %lu.\n", vd.num_edges());
  return 0;
}


#include "lwtnn/LightweightNeuralNetwork.hh"
#include "lwtnn/InputPreprocessor.hh"
#include "lwtnn/Stack.hh"
#include <Eigen/Dense>

#include <set>

// internal utility functions
namespace {
  using namespace Eigen;
  using namespace lwt;
}
namespace lwt {

  // ______________________________________________________________________
  // LightweightNeuralNetwork HL wrapper
  LightweightNeuralNetwork::LightweightNeuralNetwork(
    const std::vector<Input>& inputs,
    const std::vector<LayerConfig>& layers,
    const std::vector<std::string>& outputs):
    m_stack(new Stack(inputs.size(), layers)),
    m_preproc(new InputPreprocessor(inputs)),
    m_outputs(outputs.begin(), outputs.end())
  {
    if (m_outputs.size() != m_stack->n_outputs()) {
      std::string problem = "internal stack has " +
        std::to_string(m_stack->n_outputs()) + " outputs, but " +
        std::to_string(m_outputs.size()) + " were given";
      throw NNConfigurationException(problem);
    }
  }

  LightweightNeuralNetwork::~LightweightNeuralNetwork() {
    delete m_stack;
    m_stack = 0;
    delete m_preproc;
    m_preproc = 0;
  }

  lwt::ValueMap
  LightweightNeuralNetwork::compute(const lwt::ValueMap& in) const {

    // compute outputs
    const auto& preproc = *m_preproc;
    auto outvec = m_stack->compute(preproc(in));
    assert(outvec.rows() > 0);
    auto out_size = static_cast<size_t>(outvec.rows());
    assert(out_size == m_outputs.size());

    // build and return output map
    lwt::ValueMap out_map;
    for (size_t out_n = 0; out_n < out_size; out_n++) {
      out_map.emplace(m_outputs.at(out_n), outvec(out_n));
    }
    return out_map;
  }

  // ______________________________________________________________________
  // LightweightRNN

  LightweightRNN::LightweightRNN(const std::vector<Input>& inputs,
                                 const std::vector<LayerConfig>& layers,
                                 const std::vector<std::string>& outputs):
    m_stack(new ReductionStack(inputs.size(), layers)),
    m_preproc(new InputPreprocessor(inputs)),
    m_vec_preproc(new InputVectorPreprocessor(inputs)),
    m_outputs(outputs.begin(), outputs.end()),
    m_n_inputs(inputs.size())
  {
    if (m_outputs.size() != m_stack->n_outputs()) {
      throw NNConfigurationException(
        "Mismatch between NN output dimensions and output labels");
    }
  }
  LightweightRNN::~LightweightRNN() {
    delete m_stack;
    delete m_preproc;
    delete m_vec_preproc;
  }

  ValueMap LightweightRNN::reduce(const std::vector<ValueMap>& in) const {
    const auto& preproc = *m_preproc;
    MatrixXd inputs(m_n_inputs, in.size());
    for (size_t iii = 0; iii < in.size(); iii++) {
      inputs.col(iii) = preproc(in.at(iii));
    }
    auto outvec = m_stack->reduce(inputs);
    ValueMap out;
    const auto n_rows = static_cast<size_t>(outvec.rows());
    for (size_t iii = 0; iii < n_rows; iii++) {
      out.emplace(m_outputs.at(iii), outvec(iii));
    }
    return out;
  }

  // this version should be slightly faster since it only has to sort
  // the inputs once
  ValueMap LightweightRNN::reduce(const VectorMap& in) const {
    const auto& preproc = *m_vec_preproc;
    auto outvec = m_stack->reduce(preproc(in));
    ValueMap out;
    const auto n_rows = static_cast<size_t>(outvec.rows());
    for (size_t iii = 0; iii < n_rows; iii++) {
      out.emplace(m_outputs.at(iii), outvec(iii));
    }
    return out;
  }

}


//  divides.hpp
//
//  (C) Copyright 2011 Vicente J. Botet Escriba
//  Use, modification and distribution are 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).
//

#ifndef BOOST_RATIO_MPL_DIVIDES_HPP
#define BOOST_RATIO_MPL_DIVIDES_HPP

#include <boost/ratio/ratio.hpp>
#include <boost/ratio/mpl/numeric_cast.hpp>
#include <boost/mpl/divides.hpp>

namespace boost {
namespace mpl {

template<>
struct divides_impl< rational_c_tag,rational_c_tag >
{
    template< typename R1, typename R2 > struct apply
        : ratio_divide<R1, R2>
    {
    };
};
}
}

#endif  // BOOST_RATIO_MPL_DIVIDES_HPP