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

#include <math.h>
#include <limits>
#include <random>
#include "boost/math/special_functions/log1p.hpp"

#include <algorithm>
#include <iterator>
#include <bits/stdc++.h>
#include <fstream>
#include <sstream>

#include "iabn.h"
#include "common.h"
#include "painter.h"
#include <boost/progress.hpp>

namespace bm = boost::math;

IABN::IABN(): Container()
{
    gen_wired = std::mt19937(rd());
    gen_IAB = std::mt19937(rd());  //TODO is it independent from the above?

//     m_painter = new Painter(svg_name);
//     m_painter->Start();

    std::cout << "IABN started!\n";
}

IABN::~IABN()
{

}

void IABN::generate_nodes(double area_coeff, bool b_tree)
{
    //   Create the nodes
    double node_density = 1e-4;
    std::uniform_real_distribution<> dis(0, 1);
    std::poisson_distribution<int> pd(area_coeff*def_Area*node_density);
    int num_nodes=pd(gen_IAB); // Poisson number of points
    std::cout << "Number of nodes before vicinity = " << num_nodes << std::endl;

    // Generate data on a disk with radius r with poisson point process
    double area = area_coeff*def_Area*node_density;
    double r = sqrt(1/M_PI)*sqrt(area); // radius of the disk
    double xx0=r; double yy0=r;    // center of the disk
    set_center(xx0, yy0, r);
    std::cout << "center=" << xx0 << ", " << yy0 << ", " << r << std::endl;
//     if(fixed) generate_fixed_nodes(fixed_count);
    for(int i =0;i<num_nodes;i++)
    {
        double theta=2*M_PI*(dis(gen_IAB));   // angular coordinates
        double rho=radius*sqrt(dis(gen_IAB));      // radial coordinates

        double x = center_x + rho * cos(theta);  // Convert from polar to Cartesian coordinates
        double y = center_y + rho * sin(theta);

        bool vicinity = check_neighbors(x,y);

        if(vicinity)
        {
            bs_ptr BS;
            BS = boost::shared_ptr<mmWaveBS>(new mmWaveBS(x,y, get_nextID()));
            BS->set_transmit_power(def_P_tx);
            if(!BS) std::cerr << __FUNCTION__ << std::endl;
            BS.get()->setColor(0);
            if(b_tree) m_tree.insert(std::make_pair(BS->get_loc(), BS));
            m_items.push_back(BS);
        }
    }
}

void IABN::generate_nodes(double node_density, bool fixed, int fixed_count, double wired_fractoin)
{

    //   Create the nodes
    std::uniform_real_distribution<> dis(0, 1);
    std::poisson_distribution<int> pd(def_Area*node_density);
    int num_nodes=pd(gen_IAB); // Poisson number of points
    std::cout << "Number of nodes before vicinity = " << num_nodes << std::endl;
    //     int num_nodes = 100; // Poisson number of points
    // Generate data on a disk with radius r with poisson point process
    double area = def_Area*node_density;
    double r = sqrt(1/M_PI)*sqrt(area); // radius of the disk
    double xx0=r; double yy0=r;    // center of the disk
    set_center(xx0, yy0, r);
    std::cout << "center=" << xx0 << ", " << yy0 << ", " << r << std::endl;
    if(fixed) generate_fixed_nodes(fixed_count);
    for(int i =0;i<num_nodes;i++)
    {
        double theta=2*M_PI*(dis(gen_IAB));   // angular coordinates
        double rho=radius*sqrt(dis(gen_IAB));      // radial coordinates

        double x = center_x + rho * cos(theta);  // Convert from polar to Cartesian coordinates
        double y = center_y + rho * sin(theta);

        bool vicinity = check_neighbors(x,y);

        if(vicinity)
        {
            bs_ptr BS;
            BS = boost::shared_ptr<mmWaveBS>(new mmWaveBS(x,y, get_nextID()));
            BS->set_transmit_power(def_P_tx);
            if(!BS) std::cerr << __FUNCTION__ << std::endl;
            BS.get()->setColor(0);
            m_tree.insert(std::make_pair(BS->get_loc(), BS)); //TODO maybe here!
            m_items.push_back(BS);
//             BS.get()->update_parent.connect_member(this, &IABN::listen_For_parent_update);
            if(fixed)
            {
                BS->set_backhaul_Type(Backhaul::wireless);
            }
            else
            {
                bool prob = (rand() % 100) < 100*wired_fractoin;
                if(prob)
                {
                    BS->set_backhaul_Type(Backhaul::wired);
                    BS->set_hop_count(0); BS->route_found(true);
                }
                else
                    BS->set_backhaul_Type(Backhaul::wireless);
            }
        }
    }
}

void IABN::load_nodes(std::string f_name, bool fixed, int fixed_count, double wired_fractoin)
{
    int num_nodes =0;
// 	double data[num_nodes][2];
    std::vector<std::array<double,2>> vec_data;
	std::fstream _file(f_name);

	if(_file.fail())
    {
		std::cerr << "input data file does not exis!" << std::endl;
    }
    std::string line;
	while(std::getline(_file, line) && num_nodes<200)
	{
		std::istringstream is(line);
		double num;
        std::array<double,2> arr;
        is >> arr[0];
        is>>arr[1];
        vec_data.push_back(arr);
        num_nodes++;
	}

	if(fixed) generate_fixed_nodes(fixed_count);

    //   Create the nodes

    num_nodes= vec_data.size(); // number of points

    for(int i =0;i<num_nodes;i++)
    {
        double x = center_x + radius* vec_data[i][0];  // Convert from polar to Cartesian coordinates
        double y = center_y + radius* vec_data[i][1];

        bool vicinity = check_neighbors(x,y);

        if(vicinity)
        {
            bs_ptr BS;
            BS = boost::shared_ptr<mmWaveBS>(new mmWaveBS(x,y, get_nextID()));
            BS->set_transmit_power(def_P_tx);
            if(!BS) std::cerr << __FUNCTION__ << " at " << __LINE__ <<std::endl;
            BS.get()->setColor(0);
            m_tree.insert(std::make_pair(BS->get_loc(), BS)); //TODO maybe here!
            m_items.push_back(BS);
//             BS.get()->update_parent.connect_member(this, &IABN::listen_For_parent_update);
            if(fixed)
            {
                BS->set_backhaul_Type(Backhaul::wireless);
            }
            else
            {
                bool prob = (rand() % 100) < 100*wired_fractoin;
                if(prob)
                {
                    BS->set_backhaul_Type(Backhaul::wired);
                    BS->set_hop_count(0); BS->route_found(true);
                }
                else
                    BS->set_backhaul_Type(Backhaul::wireless);
            }
        }
    }
}

void IABN::generate_fixed_nodes(int count)
{
    // Adding Fixed Wired BSs
    double delta_teta = 2*M_PI/count;
    for(int i =0;i<count;i++)
    {
        bs_ptr BS;
        double theta = i*delta_teta;
        double r2 = radius/4.;
        double x = center_x + r2 * cos(theta);  // Convert from polar to Cartesian coordinates
        double y = center_y + r2 * sin(theta);
        BS = boost::shared_ptr<mmWaveBS>(new mmWaveBS(x, y, get_nextID()));
        BS->set_transmit_power(def_P_tx);
        BS.get()->setColor(0);
        BS->set_backhaul_Type(Backhaul::wired);
        BS->set_hop_count(0);
        BS->route_found(true);
        m_tree.insert(std::make_pair(BS->get_loc(), BS));
        m_items.push_back(BS);
//         BS.get()->update_parent.connect_member(this, &IABN::listen_For_parent_update);
    }
}

/**
 * For scenario of fixed wired nodes
 */
// void IABN::update_locations()
// {
//     std::uniform_real_distribution<> dis(0, 1);
//
//     for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
//     {
//         bs_ptr mmB = (*it);
//         if(mmB->get_backhaul_Type()==Backhaul::wired)
//             continue;
//
//
//         //TODO it might not work!
//         m_tree.remove(std::make_pair(mmB->get_loc(), mmB));
// //         std::cout << "Tree size = " << tree_size(1000) << std::endl;
//         double theta=2*M_PI*(dis(gen_IAB));   // angular coordinates
//         double rho=radius*sqrt(dis(gen_IAB));      // radial coordinates
//
//         double x = center_x + rho * cos(theta);  // Convert from polar to Cartesian coordinates
//         double y = center_y + rho * sin(theta);
//         mmB->set_loc((float)x,(float)y);
//
//         m_tree.insert(std::make_pair(mmB->get_loc(), mmB));
// //         std::cout << "Tree size = " << tree_size(1000) << std::endl;
//     }
// }

/*
 * For scenario of variable location wired nodes
 */
void IABN::update_locations(bool fixed, double wired_fractoin)
{
    std::uniform_real_distribution<> dis(0, 1);

//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << "FUCK!!!" << std::endl;
        mmB->reset_load();
        if(mmB->get_backhaul_Type()==Backhaul::wired && fixed)
            continue;
        mmB->route_found(false);
//         std::cout << "Tree size = " << tree_size(1000) << std::endl;
        //TODO it might not work!
//         std::cout << "use count before = " << mmB.use_count();
        m_tree.remove(std::make_pair(mmB->get_loc(), mmB));
//         std::cout << "use count after = " << mmB.use_count();
//         std::cout << "Tree size = " << tree_size(1000) << std::endl;

        //TODO for now IAB and wired have the same distro but different density
        double theta=2*M_PI*(dis(gen_IAB));   // angular coordinates
        double rho=radius*sqrt(dis(gen_IAB));      // radial coordinates

        double x = center_x + rho * cos(theta);  // Convert from polar to Cartesian coordinates
        double y = center_y + rho * sin(theta);

        mmB->set_loc((float)x, (float)y);
        mmB->reset(); // sets hop count = -1 and no parent
        bool prob = (rand() % 100) < 100*wired_fractoin;
        if(fixed)
            mmB->set_backhaul_Type(Backhaul::wireless);
        else{
            if(prob)
            {
                mmB->set_backhaul_Type(Backhaul::wired);
                mmB->set_hop_count(0); mmB->route_found(true);
            }
            else
            {
                mmB->set_backhaul_Type(Backhaul::wireless);
            }
        }

        m_tree.insert(std::make_pair(mmB->get_loc(), mmB));
//         std::cout << "Tree size = " << tree_size(1000) << std::endl;
    }
}

void IABN::listen_For_parent_update(const update_parent_msg& msg)
{
    //TODO update the for loop
    uint32_t id = msg.id;
    int hop_cnt = msg.hop_cnt;
    point sought = point(msg.xx, msg.yy);

    std::vector<value> results;
    m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < 2*def_MAX_MMWAVE_RANGE;}), std::back_inserter(results));

//     std::lock_guard<std::mutex> guard(m_mutex);
    BOOST_FOREACH(value const&v, results)
    {
        bs_ptr mmB = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
        if(mmB->get_parent()==id)
        {
            mmB->set_hop_count(hop_cnt+1);
//             mmB->route_found(true);
        }
    }
//     for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
//     {
//         bs_ptr mmB = (*it);
//         if(mmB->get_parent()==id)
//         {
//             mmB->set_hop_count(hop_cnt+1);
//             mmB->route_found(true);
//         }
//     }
}

void IABN::spread_hop_count()
{
    bool all_found = false;
    int counter = 0;
    while(!all_found && counter<10)
    {
        for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
        {
            bs_ptr mmB = (*it);
            if(mmB->get_hop_count()!=-1)
                mmB->update_load_hops();

        //         if(mmB->get_backhaul_Type()==Backhaul::wired)
        //             mmB->update_load_hops(0);
        }

        bool found = true;

        for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
        {
            bs_ptr mmB = (*it);
            if(mmB->get_hop_count()==-1 && mmB->get_parent()!=def_Nothing)
            {found = false; break;}
        }
        if(found)
            all_found = true;
        counter++;
    }

//     count_hops(m_arr_hops, m_failed);
}

void IABN::set_hop_counts()
{
    // set hop count of the nodes
    bool finish = false;
    int counter = 0;
    while((!finish) && (counter<10))
    {
        bool all_found = true;
//         std::lock_guard<std::mutex> guard(m_mutex);
        for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
        {
            bs_ptr mmB = (*it);
            if(!mmB) std::cerr << __func__ << std::endl;

            if(mmB->get_hop_count()!=-1)
            {
//                 std::cout << mmB->get_hop_count() << ",";
//                 mmB->emit_update_parent();
                int h = mmB->get_hop_count(); uint32_t id = mmB->getID();
                for(std::vector<bs_ptr>::iterator it2=m_items.begin(); it2!=m_items.end();++it2)
                {
                    bs_ptr mmB2 = (*it2);
                    if(mmB2->get_parent()==id)
                    {
                        mmB2->set_hop_count(h+1);
//                         mmB2->route_found(true);
                    }
                }
//                 std::vector<value> results;
//                 point sought = mmB->get_loc();
//                 m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < 2*def_MAX_MMWAVE_RANGE;}), std::back_inserter(results));
//
//                 BOOST_FOREACH(value const &v, results)
//                 {
//                     bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
//                     if(mmB2->get_parent()==mmB->getID())
//                     {
//                         mmB2->set_hop_count(mmB->get_hop_count()+1);
//                         mmB2->route_found(true);
//                     }
//                 }
            }
            else
                all_found = false;
        }

        if(all_found)
            finish = true;
        counter++;
    }

}

void IABN::draw_svg(bool b)
{
    if(b)
    {
//         m_painter->draw_node_ID(m_items);
        m_painter->update<mmWaveBS>(m_items);
    }
}

/**
 * Select path based on wired first policy
 */
void IABN::path_selection_WF()
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
//         std::cout << "use count = " << mmB.use_count();
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
            continue;

        uint32_t cid = mmB.get()->getID();
        double max_snr = -1.0;
        double max_sinr = -1.0;
        uint32_t parent = def_Nothing;

        // search for nearest neighbours
        std::vector<value> results;
        point sought = mmB->get_loc();
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < def_MAX_MMWAVE_RANGE;}),
                    std::back_inserter(results));

        point p1 = mmB->get_loc();

        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2 != cid)
            {
                double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                polygon2D poly = directional_polygon(p1, p2, mmB->get_phi_m());
                std::vector<value> vec_query;
                m_tree.query(bgi::intersects(poly), std::back_inserter(vec_query));
                double interf=0.;
                BOOST_FOREACH(value const&mz, vec_query)
                {
                    bs_ptr mmB3 = boost::dynamic_pointer_cast<mmWaveBS>(mz.second);
                    uint32_t cid3 = mmB3->getID();
                    if(cid3!=cid2 && cid3!=cid)
                    {
                        double x3 = mmB3->getX(); double y3= mmB3->getY(); point p3 = mmB3->get_loc();
                        double dist = bg::distance(sought, p3);
                        interf+= mmB->calculate_Interf_of_link(x3, y3, dist);
                    }
                }
                double d21 = bg::distance(sought, p2);
                double snr = mmB->calculate_SNR_of_link(x2, y2, d21);
                double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
                // Rules
                bool b_snr = snr>max_snr;
                bool b_parent = mmB2.get()->get_parent()!=mmB.get()->getID();
                bool b_wired = mmB2->get_backhaul_Type()==Backhaul::wired;

//TODO What if there are multiple wired nodes in its vicinity????? //FIXME
                if(b_wired)
                {
                    parent = mmB2.get()->getID();
                    max_sinr = sinr;
                    max_snr = snr;
                    break;
                }

                if(b_snr && b_parent)
                {
                    max_snr = snr;
                    max_sinr = sinr;
                    parent = mmB2.get()->getID();
                }
            }
        }
        if(parent!=def_Nothing)
        {
            mmB->set_parent(parent);
            mmB->set_SNR(max_snr);
            mmB->set_SINR(max_sinr);
            Add_load_BS(parent, mmB);
        }
    }
}

void IABN::path_selection_HQF_Interf()
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
            continue;

        uint32_t cid = mmB.get()->getID();
        double max_sinr = -1.0;
        double max_snr = -1.0;
        uint32_t parent = def_Nothing;

        // search for nearest neighbours
        std::vector<value> results;
        point sought = mmB->get_loc();
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < def_MAX_MMWAVE_RANGE;}),
                    std::back_inserter(results));

        double x1 = mmB->getX(); double y1 = mmB->getY(); point p1 = mmB->get_loc();
//         std::cout << "cid1 = " << cid << std::endl;
        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            if(mmB2)
            {
                uint32_t cid2 = mmB2.get()->getID();
//                 std::cout << "cid2 = " << cid2 << std::endl;
                if( cid2!= cid)
                {
                    double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                    polygon2D poly = directional_polygon(p1, p2, mmB->get_phi_m());
                    std::vector<value> vec_query;
                    m_tree.query(bgi::intersects(poly), std::back_inserter(vec_query));
                    double interf=0.;
                    BOOST_FOREACH(value const&mz, vec_query)
                    {
                        bs_ptr mmB3 = boost::dynamic_pointer_cast<mmWaveBS>(mz.second);
                        uint32_t cid3 = mmB3->getID();
//                         std::cout << "cid3 = " << cid3 << std::endl;
                        if(cid3!=cid2 && cid3!=cid)
                        {
                            double x3 = mmB3->getX(); double y3= mmB3->getY(); point p3 = mmB3->get_loc();
                            double dist = bg::distance(sought, p3);
                            interf+= mmB->calculate_Interf_of_link(x3, y3, dist);
                        }
                    }
                    double d21 = bg::distance(sought, p2);
                    double snr = mmB->calculate_SNR_of_link(x2,y2, d21);
                    double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
//                     // Rules
                    bool b_snr = sinr>max_sinr;
                    bool b_parent = mmB2.get()->get_parent()!=mmB.get()->getID();

                    if(b_snr && b_parent)
                    {
                        max_sinr = sinr;
                        max_snr = snr;
                        parent = mmB2.get()->getID();
                    }
                }
            }
        }
        if(parent!=def_Nothing)
        {
            mmB->set_parent(parent);
            mmB->set_SINR(max_sinr);
            mmB->set_SNR(max_snr);
            Add_load_BS(parent, mmB);
        }
    }
}

/**
 * Returns the directional triangle from p1 tp p2 considering angle of main lobe phi_m
 * phi_m should be in radian
 */
polygon2D IABN::directional_polygon(point p1, point p2, double phi_m)
{
    polygon2D poly;
    // first vertice
    poly.outer().push_back(p1);

    // Two other vertices
    double phi = atan2(p2.get<1>()-p1.get<1>(), p2.get<0>()-p1.get<0>());
    double t1 = phi - phi_m/2.;
    double t2 = phi + phi_m/2.;
    double r = def_MAX_MMWAVE_RANGE;
    point p3(p1.get<0>()+r*cos(t1), p1.get<1>()+r*sin(t1));
    point p4(p1.get<0>()+r*cos(t2), p1.get<1>()+r*sin(t2));
    poly.outer().push_back(p3);
    poly.outer().push_back(p4);

    // last vertice
    poly.outer().push_back(p1);
    return poly;
}

void IABN::path_selection_HQF()
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
            continue;

        uint32_t cid = mmB.get()->getID();
        double max_snr = -1.0; double max_sinr = -1.0;
        uint32_t parent = def_Nothing;

        // search for nearest neighbours
        std::vector<value> results;
        point sought = mmB->get_loc();
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < def_MAX_MMWAVE_RANGE;}),
                    std::back_inserter(results));

        point p1 = mmB->get_loc();
        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2 != cid)
            {
                double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                polygon2D poly = directional_polygon(p1, p2, mmB->get_phi_m());
                std::vector<value> vec_query;
                m_tree.query(bgi::intersects(poly), std::back_inserter(vec_query));
                double interf=0.;
                BOOST_FOREACH(value const&mz, vec_query)
                {
                    bs_ptr mmB3 = boost::dynamic_pointer_cast<mmWaveBS>(mz.second);
                    uint32_t cid3 = mmB3->getID();
                    if(cid3!=cid2 && cid3!=cid)
                        {
                            double x3 = mmB3->getX(); double y3= mmB3->getY(); point p3 = mmB3->get_loc();
                            double dist = bg::distance(sought, p3);
                            interf+= mmB->calculate_Interf_of_link(x3, y3, dist);
                        }
                }
                double d21 = bg::distance(sought, p2);
                double snr = mmB->calculate_SNR_of_link(x2,y2, d21);
                double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
                // Rules
                bool b_snr = snr>max_snr;
                bool b_parent = mmB2.get()->get_parent()!=mmB.get()->getID();

                if(b_snr && b_parent)
                {
                    max_snr = snr;
                    max_sinr = sinr;
                    parent = mmB2.get()->getID();
                }
            }
        }

        if(parent!=def_Nothing)
        {
            mmB->set_parent(parent);
            mmB->set_SNR(max_snr);
            mmB->set_SINR(max_sinr);
            Add_load_BS(parent, mmB);
        }
    }
}

/**
 * Select path based on position-aware policy
 */
void IABN::path_selection_PA()
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    //TODO check this method precisely.
    int total = m_items.size();
    boost::progress_display show_progress(total);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
            continue;

        uint32_t cid = mmB.get()->getID();
        double max_snr = -1.0;
        double max_sinr = -1.0;
        uint32_t parent = def_Nothing;

        point closest_wired = find_closest_wired(mmB->getID(), mmB->get_loc());
        double dist_wired = bg::distance(closest_wired, mmB->get_loc());
        // search for nearest neighbours
        std::vector<value> results;
        point sought = mmB->get_loc();
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < def_MAX_MMWAVE_RANGE;}),
                    std::back_inserter(results));

        point p1 = mmB->get_loc();
        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2 != cid)
            {
                double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                polygon2D poly = directional_polygon(p1, p2, mmB->get_phi_m());
                std::vector<value> vec_query;
                m_tree.query(bgi::intersects(poly), std::back_inserter(vec_query));
                double interf=0.;
                BOOST_FOREACH(value const&mz, vec_query)
                {
                    bs_ptr mmB3 = boost::dynamic_pointer_cast<mmWaveBS>(mz.second);
                    uint32_t cid3 = mmB3->getID();
                    if(cid3!=cid2 && cid3!=cid)
                    {
                        double x3 = mmB3->getX(); double y3= mmB3->getY(); point p3 = mmB3->get_loc();
                        double dist = bg::distance(sought, p3);
                        interf+= mmB->calculate_Interf_of_link(x3, y3, dist);
                    }
                }
                double d21 = bg::distance(sought, p2);
                double snr = mmB->calculate_SNR_of_link(x2,y2, d21);
                double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
                // Rules
                bool b_snr = snr>max_snr;
                bool b_parent = mmB2.get()->get_parent()!=mmB.get()->getID();
                bool b_dist = bg::distance(closest_wired, mmB2->get_loc()) <= dist_wired;

                if(b_snr && b_parent && b_dist)
                {
                    max_snr = snr;
                    max_sinr = sinr;
                    parent = mmB2.get()->getID();
                }
            }
        }

        if(parent!=def_Nothing)
        {
            mmB->set_parent(parent);
            mmB->set_SNR(max_snr);
            mmB->set_SINR(max_sinr);
            Add_load_BS(parent, mmB);
        }
        ++show_progress;
    }
}

point IABN::find_closest_wired(uint32_t id, point loc)
{
    //TODO check if the answer is not equal the input loc
    point wired;// = loc;
    double search_radius = 10; // 10 meter first search radius
    int max = radius/search_radius;

    for(int i=1;i<max;i++)
    {
        search_radius=i*search_radius;
        // search for nearest neighbours
        std::vector<value> results;
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, loc) < search_radius;}), std::back_inserter(results));
        double dis = 3*radius; uint32_t cid; bool b_found = false;
        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            if(mmB2->getID()!=id)
            {
                if(mmB2->get_backhaul_Type()==Backhaul::wired)
                {
                    if(bg::distance(loc, mmB2->get_loc()) < dis)
                    {
                        wired = mmB2->get_loc();
                        dis = bg::distance(loc, mmB2->get_loc());
                        cid = mmB2->getID();
                        b_found = true;
                    }
                }
            }
        }
        if(b_found)
        {
//             std::cout << "closest wired id for " << id  << " is " << cid << std::endl;
            return wired;
        }

    }

    std::cerr << "No wired found!!" << std::endl;
//     return wired;
}

/**
 * Path selection policy based on Maximum local rate
 */
void IABN::path_selection_MLR()
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    //TODO maybe here!
//     bool b_all_nodes_route = false;
//     int count = 0;
//     while(!b_all_nodes_route && count<10)
//     {
        for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
        {
            bs_ptr mmB = (*it);
            if(!mmB) std::cerr << __FUNCTION__ << std::endl;

            if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
                continue;

            uint32_t cid = mmB.get()->getID();
            double max_rate = -1.0;
            uint32_t parent_id = def_Nothing;

            // search for nearest neighbours
            point sought = mmB->get_loc();
            std::vector<value> results;
            m_tree.query(bgi::satisfies([&](value const&v){return bg::distance(v.first, sought)<def_MAX_MMWAVE_RANGE;}), std::back_inserter(results));

            bs_ptr parent;
            BOOST_FOREACH(value const&v, results)
            {
                bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
//                 if(mmB2->get_backhaul_Type()==Backhaul::wired)
// //                     std::cout << "here\n";
                if(cid != mmB2.get()->getID())
                {
//                     double snr = mmB.get()->calculate_SNR_of_link(mmB2.get()->getX(), mmB2.get()->getY());
                    double load = 1. + mmB2.get()->get_load_BS_count();
                    double BW =def_BW;
                    double d = def_BW/load;
                    double rate;// =d * bm::log1p(1.+snr)/bm::log1p(2.0);
    //                 std::cout << "rate= " << rate << ", ";
                    bool b_rate = rate > max_rate;
                    bool b_parent = mmB2.get()->get_parent()!=cid;

                    if(b_rate && b_parent)
                    {
                        max_rate = rate;
                        parent_id = mmB2.get()->getID();
                        parent = mmB2;
                    }
                }
            }
            if(parent_id!=def_Nothing)
            {
                mmB->set_parent(parent_id);
//                 mmB->set_SNR(max_snr);
//             mmB->set_SINR(max_sinr);
                Add_load_BS(parent_id, mmB);
            }
        }

//         b_all_nodes_route = check_all_routes();
//         count++;
//     }
//     std::cout << "The route for all nodes was found = " << b_all_nodes_route << " with " << count << " tries!" <<std::endl;
}

bool IABN::check_all_routes()
{
    bool found = true;
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB->is_route_found())
        {found = false; break;}
    }
    return found;
}

void IABN::Add_load_BS(uint32_t parent, bs_ptr bs/*uint32_t member, point loc*/)
{
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->getID()==parent)
        {
            if(mmB->getID()!=bs->get_parent())
                std::cerr << "WTF!\n";
            mmB->add_to_load_BS(bs);
        }
    }
}

int IABN::get_IAB_count()
{
    int cc = 0;
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->get_backhaul_Type()==Backhaul::wireless)
            cc++;
    }
    return cc;
}

int IABN::get_wired_count()
{
    int cc = 0;
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->get_backhaul_Type()==Backhaul::wired)
            cc++;
    }
    return cc;
};

void IABN::reset(bool fixed)
{
//     std::lock_guard<std::mutex> guard(m_mutex);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->get_backhaul_Type()==Backhaul::wireless && fixed)
            mmB->reset();
        else
            mmB->reset();
    }
}

std::vector<int> IABN::count_hops(int &max_hop, int &failed)
{

    m_failed = 0; m_max_hop = -1;
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(m_max_hop < mmB->get_hop_count())
            m_max_hop = mmB->get_hop_count();
    }
//     memset(m_arr_hops, 0, sizeof(m_arr_hops));
    std::vector<int> arr_hops; arr_hops.resize(m_max_hop+1);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB)
        {
            std::cerr << "NULL Pointer in " << __FUNCTION__ << " at " << __LINE__ << std::endl;
//             continue;
        }
        int hcnt = mmB->get_hop_count();
        if(hcnt != -1)
        {
            arr_hops[hcnt]++;
        }
        else
            m_failed++;
    }
    failed = m_failed;
    max_hop = m_max_hop;
    return arr_hops;
//     std::copy(std::begin(m_arr_hops), std::end(m_arr_hops), std::begin(hop));
//     hop = m_arr_hops;
//     std::vector<value> results;
//     point sought = point(center_x, center_y);
//     m_tree.query(bgi::satisfies([&] (value const&v){return bg::distance(v.first,sought)<radius;}), std::back_inserter(results));
//
//     BOOST_FOREACH(value const&v, results)
//     {
//         bs_ptr mmB = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
//         if(!mmB)
//         {
//             std::cerr << "NULL Pointer " << __FUNCTION__ << __LINE__ << std::endl;
// //             continue;
//         }
//         int hcnt = mmB->get_hop_count();
//         if(hcnt != -1)
//             hop[hcnt]++;
//         else
//             failed++;
//
//     }
//     std::lock_guard<std::mutex> guard(m_mutex);
//
}

// void IABN::reset_pointers()
// {
//     for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
//     {
//         bs_ptr mmB = (*it);
//         if(mmB)
//             mmB.reset();
//     }
// }

double IABN::find_SNR_bottleneck()
{
    double bottleneck = 1e10;
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->is_route_found() && mmB->get_backhaul_Type()==Backhaul::wireless)
        {
            double snr = mmB->get_SNR();
            if(snr==0.)
                std::cout << "here\n";
            if(snr<bottleneck)
                bottleneck = snr;
        }

    }
    bottleneck = 10*log10(bottleneck); // returning the SNR value in dB
    return bottleneck;
}

double IABN::find_SINR_bottleneck()
{
    double bottleneck = 1e10;
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end(); ++it)
    {
        bs_ptr mmB = (*it);
        if(mmB->is_route_found() && mmB->get_backhaul_Type()==Backhaul::wireless)
        {
            double sinr = mmB->get_SINR();
            if(sinr==0.)
                std::cout << "here\n";
            if(sinr<bottleneck)
                bottleneck = sinr;
        }

    }
    bottleneck = 10*log10(bottleneck); // returning the SNR value in dB
    return bottleneck;
}

void IABN::check_SINR_tree(matrix<double> &m)
{
    int Total_iter = m_items.size();
    boost::progress_display show_progress(Total_iter);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

//         if(mmB.get()->get_backhaul_Type()==Backhaul::wired)
//             continue;

        uint32_t cid = mmB.get()->getID();
        double max_sinr = -1.0;
        double max_snr = -1.0;
        uint32_t parent = def_Nothing;

        // search for nearest neighbours
        std::vector<value> results;
        point sought = mmB->get_loc();
        m_tree.query(bgi::satisfies([&](value const& v) {return bg::distance(v.first, sought) < def_MAX_MMWAVE_RANGE;}),
                    std::back_inserter(results));

        double x1 = mmB->getX(); double y1 = mmB->getY(); point p1 = mmB->get_loc();
//         std::cout << "cid1 = " << cid << std::endl;
        BOOST_FOREACH(value const&v, results)
        {
            bs_ptr mmB2 = boost::dynamic_pointer_cast<mmWaveBS>(v.second);
            if(mmB2)
            {
                uint32_t cid2 = mmB2.get()->getID();
//                 std::cout << "cid2 = " << cid2 << std::endl;
                if( cid2!= cid)
                {
                    double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                    double d21 = bg::distance(sought, p2);
                     polygon2D poly = directional_polygon(p1, p2, mmB->get_phi_m());
                     std::vector<value> vec_query;
                     m_tree.query(bgi::intersects(poly), std::back_inserter(vec_query));
                     double interf=0.;
                     BOOST_FOREACH(value const&mz, vec_query)
                     {
                         bs_ptr mmB3 = boost::dynamic_pointer_cast<mmWaveBS>(mz.second);
                         uint32_t cid3 = mmB3->getID(); point p3 = mmB3->get_loc();
                         double d31 = bg::distance(sought, p3);
//                         std::cout << "cid3 = " << cid3 << std::endl;
                         if(cid3!=cid2 && cid3!=cid)
                             interf+= mmB->calculate_Interf_of_link(mmB3->getX(), mmB3->getY(), d31);
                     }

//                    double snr = mmB->calculate_SNR_of_link(x2,y2, d21);
                     double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
//                      if(cid>m.size1()-1 || cid2 > m.size2()-1)
//                          std::cout << "Here!!" << std::endl;
                     // Id starts from 1, so the index of the matrix is id-1
                     m(cid-1, cid2-1) = sinr;
                }
            }
        }
        ++show_progress;
    }
}

void IABN::check_SNR_array()
{
    int Total_iter = m_items.size();
    boost::progress_display show_progress(Total_iter);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        uint32_t cid = mmB.get()->getID();
        point sought = mmB->get_loc();
        for(std::vector<bs_ptr>::iterator it2=m_items.begin(); it2!=m_items.end();++it2)
        {
            bs_ptr mmB2 = (*it2);
            if(!mmB2) std::cerr << __FUNCTION__ << std::endl;
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2!= cid)
            {
                double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
                double dist = bg::distance(sought, mmB2->get_loc());
                if ( dist < def_MAX_MMWAVE_RANGE)
                {
                    double snr = mmB->calculate_SNR_of_link(x2,y2, dist);
                }
            }
        }
        ++show_progress;
    }
}

void IABN::check_SINR_array()
{
    int Total_iter = m_items.size();
    boost::progress_display show_progress(Total_iter);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        uint32_t cid = mmB.get()->getID();
        point sought = mmB->get_loc();
        double x1 = mmB->getX(); double y1 = mmB->getY();
        for(std::vector<bs_ptr>::iterator it2=m_items.begin(); it2!=m_items.end();++it2)
        {
            bs_ptr mmB2 = (*it2);
            if(!mmB2) std::cerr << __FUNCTION__ << std::endl;
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2!= cid)
            {
                double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
//                 double d21 = bg::distance(sought, p2);
                double d21 = euclidean_dist(x1,y1,x2,y2);
                if (d21 < def_MAX_MMWAVE_RANGE)
                {
                    double interf = 0.;
                    for(std::vector<bs_ptr>::iterator it3=m_items.begin(); it3!=m_items.end();++it3)
                    {
                        bs_ptr mmB3 = (*it3);
                        if(!mmB3) std::cerr << __FUNCTION__ << std::endl;
                        uint32_t cid3 = mmB3.get()->getID();
                        double x3 = mmB3->getX(); double y3 = mmB3->getY(); point p3 = mmB3->get_loc();
                        if(cid3!=cid2 && cid3!=cid)
                        {
//                             double d31 = bg::distance(sought, p3);
                            double d31 = euclidean_dist(x1,y1,x3,y3);
                            if (d31 < def_MAX_MMWAVE_RANGE)
                            {
                                point p11 = point(x2-x1, y2-y1);
                                point p22 = point(x3-x1, y3-y1);
                                double dot = bg::dot_product<point, point>(p11, p22);
                                double teta = std::acos(dot/(d21*d31));
                                if(teta <= mmB->get_phi_m()/2.)
                                {
                                    interf+= mmB->calculate_Interf_of_link(x3, y3, d31);
                                }
                            }
                        }
                    }
                    double sinr = mmB->calculate_SINR_of_link(x2,y2, d21, interf);
                }
            }
        }
        ++show_progress;
    }
}

void IABN::check_SINR_array2()
{
    int Total_iter = m_items.size();
    boost::progress_display show_progress(Total_iter);
    for(std::vector<bs_ptr>::iterator it=m_items.begin(); it!=m_items.end();++it)
    {
        bs_ptr mmB = (*it);
        if(!mmB) std::cerr << __FUNCTION__ << std::endl;

        uint32_t cid = mmB.get()->getID();
        point sought = mmB->get_loc();
        double x1 = mmB->getX(); double y1 = mmB->getY();
        std::map<bs_ptr, double>neighbors;
//         std::vector<bs_ptr> neighbors;

        for(std::vector<bs_ptr>::iterator it2=m_items.begin(); it2!=m_items.end();++it2)
        {
            bs_ptr mmB2 = (*it2);
            if(!mmB2) std::cerr << __FUNCTION__ << std::endl;
            uint32_t cid2 = mmB2.get()->getID();
            if( cid2!= cid)
            {
                point p2 = mmB2->get_loc();
                double d21 = bg::distance(sought, p2);
                if (d21 < def_MAX_MMWAVE_RANGE)
                {
                    neighbors.insert( std::pair<bs_ptr, double>(mmB2, d21));
                }
            }
        }

        for(std::map<bs_ptr, double>::iterator it2=neighbors.begin(); it2!=neighbors.end();++it2)
        {
            bs_ptr mmB2 = it2->first;
            double d21 = it2->second;
            uint32_t cid2 = mmB2.get()->getID();
            double x2 = mmB2->getX(); double y2 = mmB2->getY(); point p2 = mmB2->get_loc();
            double interf = 0.;
            for(std::map<bs_ptr, double>::iterator it3=neighbors.begin(); it3!=neighbors.end();++it3)
            {
                bs_ptr mmB3 = it3->first;
                double d31 = it3->second;
                uint32_t cid3= mmB3.get()->getID();
                double x3 = mmB3->getX(); double y3 = mmB3->getY(); point p3 = mmB3->get_loc();
//                 double d31 = bg::distance(sought, p3);
                if(cid3!=cid2)
                {
//                     double d21 = bg::distance(sought, p2);
//                     double d31 = bg::distance(sought, p3);
                    point p11 = point(x2-x1, y2-y1);
                    point p22 = point(x3-x1, y3-y1);
                    double dot = bg::dot_product<point, point>(p11, p22);
                    double teta = std::acos(dot/(d21*d31));
                    if(teta <= mmB->get_phi_m()/2.)
                    {
                        interf+= mmB->calculate_Interf_of_link(x3, y3, d31);
                    }
                }
            }
//             double sinr = mmB->calculate_SINR_of_link(x2,y2, interf);
        }
        ++show_progress;
    }
}

matrix<double> IABN::Generate_Matrix_SINR()
{
    int num = get_IAB_count() + get_wired_count();
    // Matrix containing SINR of all links
    // Matrix is selected from boost::numeric::ublas::matrix
    matrix<double> matrix_sinr (num, num);
    for (unsigned i = 0; i < matrix_sinr.size1 (); ++ i)
        for (unsigned j = 0; j < matrix_sinr.size2 (); ++ j)
                matrix_sinr (i, j) = 0.0;
    check_SINR_tree(matrix_sinr);
    return matrix_sinr;
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_TRIGONOMETRIC_FUNCTIONS_COMPLEX_GENERIC_SINCOS_HPP_INCLUDED
#define NT2_TRIGONOMETRIC_FUNCTIONS_COMPLEX_GENERIC_SINCOS_HPP_INCLUDED
#include <nt2/trigonometric/functions/sincos.hpp>
#include <nt2/include/functions/sincos.hpp>
#include <nt2/include/functions/sinhcosh.hpp>
#include <nt2/include/functions/real.hpp>
#include <nt2/include/functions/imag.hpp>
#include <nt2/include/functions/is_imag.hpp>
#include <nt2/include/functions/is_real.hpp>
#include <nt2/include/functions/logical_and.hpp>
#include <nt2/include/functions/is_invalid.hpp>
#include <nt2/include/functions/is_inf.hpp>
#include <nt2/sdk/complex/meta/as_complex.hpp>
#include <nt2/sdk/complex/meta/as_real.hpp>
#include <nt2/sdk/meta/as_logical.hpp>
#include <nt2/include/functions/logical_or.hpp>
#include <nt2/include/functions/if_zero_else.hpp>
#include <nt2/include/functions/if_else.hpp>
#include <nt2/include/functions/any.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/nan.hpp>
#include <boost/fusion/include/std_pair.hpp>

namespace nt2 { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  (sincos_, tag::cpu_,
                             (A0),
                             (generic_ < complex_<floating_ < A0> > > )
                             (generic_ < complex_<floating_ < A0> > > )
                             (generic_ < complex_<floating_ < A0> > > )
                            )
  {
    typedef void result_type;
    inline void operator()(A0 const& a0,A0 & a1,A0 & a2) const
    {
      typedef typename meta::as_real<A0>::type rtype;
      typedef typename meta::as_logical<A0>::type ltype;
      rtype c, s, ch, sh;
      sincos(nt2::real(a0), s, c);
      sinhcosh(nt2::imag(a0), sh, ch);
      rtype r1 = if_zero_else(is_imag(a0), s*ch);
      rtype i1 = if_zero_else(is_real(a0), c*sh);
      rtype r2 = c*ch;
      rtype i2 = if_zero_else(logical_or(is_imag(a0), is_real(a0)), -s*sh);
      a1 =  A0(r1, i1);
      a2 =  A0(r2, i2);
#ifndef BOOST_SIMD_NO_INVALIDS
      ltype t = logical_and(is_invalid(real(a0)), is_inf(imag(a0)));
      if (any(t))
      {
        A0 zs = A0(Nan<rtype>(),imag(a0));
        A0 zc = A0(Inf<rtype>(),Nan<rtype>());
        a1 = if_else(t, zs, a1);
        a2 = if_else(t, zc, a2);
      }
#endif

    }
  };

  BOOST_DISPATCH_IMPLEMENT  (sincos_, tag::cpu_,
                             (A0),
                             (generic_ < dry_<floating_ < A0> > > )
                             (generic_ < dry_<floating_ < A0> > > )
                             (generic_ < dry_<floating_ < A0> > > )
                            )
  {
    typedef void result_type;
    inline void operator()(A0 const& a0,A0 & a1,A0 & a2) const
    {
      typedef typename meta::as_real<A0>::type rtype;
      rtype c, s;
      sincos(nt2::real(a0), s, c);
      a1 =  bitwise_cast<A0>(c);
      a2 =  bitwise_cast<A0>(s);
    }
  };

  BOOST_DISPATCH_IMPLEMENT  (sincos_, tag::cpu_,
                             (A0),
                             (generic_ < complex_<floating_ < A0> > > )
                             (generic_ < complex_<floating_ < A0> > > )
                            )
  {
    typedef A0 result_type;
    inline A0 operator()(A0 const& a0,A0 & a2) const
    {
      result_type a1;
      sincos(a0, a1, a2);
      return a1;
    }
  };

  BOOST_DISPATCH_IMPLEMENT  (sincos_, tag::cpu_,
                             (A0),
                             (generic_ < complex_<floating_<A0> > > )
                            )
  {
    typedef std::pair<A0, A0>           result_type;
    NT2_FUNCTOR_CALL(1)
    {
      A0 first, second;
      sincos(a0, first, second);
      return result_type(first, second);
    }
  };
} }

#endif


#include <vector>
#include <random>
#include <iostream>
#include <chrono>
#include <boost/geometry/extensions/triangulation/strategies/cartesian/detail/precise_math.hpp>
#include "dynamic_exact.h"
#include "static_exact.h"

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;

const int samples = 10'000'000;

struct problem { std::array<double, 2> a,b,c; };

int main()
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_real_distribution<> dis(0.0, 1.0);
	int mod = 1000;
	std::cout << "1 in " << mod << " is guaranteed to require higher precision.\n";
	std::vector<problem> problems;
	problems.reserve(samples);
	for(int i=0; i < samples; ++i) {
		double bfac = 1.0, cfac = 1.0;
		if(i % mod == 0) {
			bfac = 1e20;
			cfac = 1e40;
		}
		problems.emplace_back(problem{dis(gen), dis(gen), dis(gen) * bfac, dis(gen) * bfac, dis(gen) * cfac, dis(gen) * cfac});
	}
	auto start = std::chrono::system_clock::now();
	int sum1 = 0;
	for(int i = 0; i < samples; ++i) {
		auto det = boost::geometry::detail::precise_math::orient2d<double>(problems[i].a, problems[i].b, problems[i].c);
		auto r = det > 0 ? 1 : (det < 0 ? -1 : 0);
		sum1 += r;
	}
	auto end = std::chrono::system_clock::now();
	auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
	std::cout << "Boost robust orient2:\t\t" << sum1 << "\t" << elapsed.count() << "ms\n";

	start = std::chrono::system_clock::now();
	int sum2 = 0;
	for(int i = 0; i < samples; ++i) {
	        Point_2 p(problems[i].a[0], problems[i].a[1]), q(problems[i].b[0], problems[i].b[1]), r(problems[i].c[0], problems[i].c[1]);
	        auto det = CGAL::orientation(p, q, r);
	        sum2 += det > 0 ? 1 : (det < 0 ? -1 : 0);
	}
	end = std::chrono::system_clock::now();
	elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
	std::cout << "CGAL exact predicate:\t\t" << sum2 << "\t" << elapsed.count() << "ms\n";

	start = std::chrono::system_clock::now();
	int sum3 = 0;
	for(int i = 0; i < samples; ++i) {
	        auto det = (problems[i].a[0] - problems[i].c[0]) * (problems[i].b[1] - problems[i].c[1])
			- (problems[i].a[1] - problems[i].c[1]) * (problems[i].b[0] - problems[i].c[0]);
	        sum3 += det > 0 ? 1 : (det < 0 ? -1 : 0);
	}
	end = std::chrono::system_clock::now();
	elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
	std::cout << "naive double computation:\t" << sum3 << "\t" << elapsed.count() << "ms\n";

	using A = float_wrapper<double>;
        start = std::chrono::system_clock::now();
        int sum4 = 0;
        for(int i = 0; i < samples; ++i) {
                auto det = ((A(problems[i].a[0]) + A(-problems[i].c[0])) * (A(problems[i].b[1]) + A(-problems[i].c[1]))
                        + (A(-problems[i].a[1]) + A(problems[i].c[1])) * (A(problems[i].b[0]) + A(-problems[i].c[0]))).sign();
                sum4 += det > 0 ? 1 : (det < 0 ? -1 : 0);
        }
        end = std::chrono::system_clock::now();
        elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
        std::cout << "automatically generated exact:\t" << sum4 << "\t" << elapsed.count() << "ms\n";
	return 0;
}


#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/cycle_canceling.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
#include <boost/graph/successive_shortest_path_nonnegative_weights.hpp>
#include <boost/graph/find_flow_cost.hpp>

// Graph Type with nested interior edge properties for Cost Flow Algorithms
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
                              boost::property<boost::edge_capacity_t, long,
                                              boost::property<boost::edge_residual_capacity_t, long,
                                                              boost::property<boost::edge_reverse_t, traits::edge_descriptor,
                                                                              boost::property<boost::edge_weight_t, long>>>>>
    graph; // new! weightmap corresponds to costs
typedef boost::graph_traits<graph>::edge_descriptor edge_desc;
typedef boost::graph_traits<graph>::out_edge_iterator out_edge_it; // Iterator

// Custom edge adder class
class edge_adder
{
    graph &G;

public:
    explicit edge_adder(graph &G) : G(G) {}
    void add_edge(int from, int to, long capacity, long cost)
    {
        auto c_map = boost::get(boost::edge_capacity, G);
        auto r_map = boost::get(boost::edge_reverse, G);
        auto w_map = boost::get(boost::edge_weight, G); // new!
        const edge_desc e = boost::add_edge(from, to, G).first;
        const edge_desc rev_e = boost::add_edge(to, from, G).first;
        c_map[e] = capacity;
        c_map[rev_e] = 0; // reverse edge has no capacity!
        r_map[e] = rev_e;
        r_map[rev_e] = e;
        w_map[e] = cost;      // new assign cost
        w_map[rev_e] = -cost; // new negative cost
    }
};

using namespace std;

// Idea:
// Train has l seats.
// Each seat can either be occupied by an agent or a regular passenger.
// An agent on a mission brings reward q, a regular passenger 0.
// l people board the train
// At a given stop:
// - either start mission to target-stop with value q
// - or travel to next stop without reward
void solve()
{
    int stops, missions, maxAgents;
    cin >> stops >> missions >> maxAgents;

    int maxValue = 128; // 2^7

    graph G(stops);
    edge_adder adder(G);
    auto c_map = boost::get(boost::edge_capacity, G);
    auto r_map = boost::get(boost::edge_reverse, G);
    auto rc_map = boost::get(boost::edge_residual_capacity, G);

    auto source = boost::add_vertex(G);
    auto target = boost::add_vertex(G);

    adder.add_edge(source, 0, maxAgents, 0);
    adder.add_edge(stops - 1, target, maxAgents, 0);

    for (int stop = 0; stop < stops - 1; ++stop) {
        adder.add_edge(stop, stop + 1, maxAgents, maxValue);
    }

    int u, v, q;
    for (int mission = 0; mission < missions; ++mission) {
        cin >> u >> v >> q;
        adder.add_edge(u, v, 1, maxValue * (v - u) - q);
    }

    boost::successive_shortest_path_nonnegative_weights(G, source, target);
    auto cost = boost::find_flow_cost(G);
    cost = maxAgents * maxValue * (stops - 1) - cost;
    cout << cost << endl;
}

int main()
{
    int t; cin >> t;
    for (int i = 0; i < t; ++i) {
        solve();
    }
    return 0;
}


/**
 * @file stabrk3.cc
 * @brief NPDE homework StabRK3 code
 * @author Unknown, Oliver Rietmann, Philippe Peter
 * @date 13.04.2021
 * @copyright Developed at ETH Zurich
 */

#include "stabrk3.h"

#include <Eigen/Core>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <vector>

namespace StabRK3 {

/* SAM_LISTING_BEGIN_0 */
Eigen::Vector2d PredPrey(Eigen::Vector2d y0, double T, unsigned int M) {
  double h = T / M;
  Eigen::Vector2d y = y0;

  //====================
  // Your code goes here
  //====================

  return y;
}
/* SAM_LISTING_END_0 */

/* SAM_LISTING_BEGIN_1 */
void SimulatePredPrey() {
  //====================
  // Your code goes here
  //====================
}

void PrintErrorTable(const Eigen::ArrayXd& M, const Eigen::ArrayXd& error) {
  std::cout << std::setw(15) << "N" << std::setw(15) << "error" << std::setw(15)
            << "rate" << std::endl;
  // Formatted output in C++
  for (unsigned int i = 0; i < M.size(); ++i) {
    std::cout << std::setw(15) << M(i) << std::setw(15) << error(i);
    if (i > 0) {
      std::cout << std::setw(15) << std::log2(error(i - 1) / error(i));
    }
    std::cout << std::endl;
  }
}
/* SAM_LISTING_END_1 */

}  // namespace StabRK3


//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020 Pavel Kharitonov <ipavrus@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_TIGER_FUNCTIONS_HPP
#define CRYPTO3_TIGER_FUNCTIONS_HPP

#include <boost/crypto3/hash/detail/tiger/basic_tiger_policy.hpp>
#include <boost/crypto3/detail/make_uint_t.hpp>

namespace boost {
    namespace crypto3 {
        namespace hashes {
            namespace detail {
                template<std::size_t DigestBits>
                struct tiger_functions : public basic_tiger_policy<DigestBits> {
                    typedef basic_tiger_policy<DigestBits> policy_type;

                    typedef typename policy_type::byte_type byte_type;

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

                    constexpr static const std::size_t state_bits = basic_tiger_policy<DigestBits>::state_bits;
                    constexpr static const std::size_t state_words = basic_tiger_policy<DigestBits>::state_words;
                    typedef typename basic_tiger_policy<DigestBits>::state_type state_type;

                    constexpr static const std::size_t block_bits = basic_tiger_policy<DigestBits>::block_bits;
                    constexpr static const std::size_t block_words = basic_tiger_policy<DigestBits>::block_words;
                    typedef typename basic_tiger_policy<DigestBits>::block_type block_type;

                    inline static void mix(block_type &X) {
                        X[0] -= X[7] ^ 0xA5A5A5A5A5A5A5A5;
                        X[1] ^= X[0];
                        X[2] += X[1];
                        X[3] -= X[2] ^ ((~X[1]) << 19);
                        X[4] ^= X[3];
                        X[5] += X[4];
                        X[6] -= X[5] ^ ((~X[4]) >> 23);
                        X[7] ^= X[6];

                        X[0] += X[7];
                        X[1] -= X[0] ^ ((~X[7]) << 19);
                        X[2] ^= X[1];
                        X[3] += X[2];
                        X[4] -= X[3] ^ ((~X[2]) >> 23);
                        X[5] ^= X[4];
                        X[6] += X[5];
                        X[7] -= X[6] ^ 0x0123456789ABCDEF;
                    }

                    inline static void pass(word_type &A, word_type &B, word_type &C, block_type &X, byte_type mul) {
                        C ^= X[0];
                        A -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 1)];
                        B += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 6)];
                        B *= mul;
                        A ^= X[1];
                        B -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 1)];
                        C += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 6)];
                        C *= mul;
                        B ^= X[2];
                        C -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 1)];
                        A += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 6)];
                        A *= mul;
                        C ^= X[3];
                        A -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 1)];
                        B += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 6)];
                        B *= mul;
                        A ^= X[4];
                        B -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 1)];
                        C += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 6)];
                        C *= mul;
                        B ^= X[5];
                        C -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 1)];
                        A += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(B, 6)];
                        A *= mul;
                        C ^= X[6];
                        A -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 1)];
                        B += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(C, 6)];
                        B *= mul;
                        A ^= X[7];
                        B -= policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 7)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 5)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 3)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 1)];
                        C += policy_type::sbox1[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 0)] ^
                             policy_type::sbox2[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 2)] ^
                             policy_type::sbox3[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 4)] ^
                             policy_type::sbox4[::boost::crypto3::detail::extract_uint_t<CHAR_BIT>(A, 6)];
                        C *= mul;
                    }
                };
            }   // namespace detail
        }   // namespace hashes
    }   // namespace crypto3
}    // namespace boost

#endif    // CRYPTO3_TIGER_FUNCTIONS_HPP


///////////////////////////////////////////////////////////////////////////////
//  Copyright 2014 Anton Bikineev
//  Copyright 2014 Christopher Kormanyos
//  Copyright 2014 John Maddock
//  Copyright 2014 Paul Bristow
//  Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP
#define BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP

#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/hypergeometric_2F0.hpp>

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable:4127)
#endif

  namespace boost { namespace math {

  namespace detail {

     //
     // Asymptotic series based on https://dlmf.nist.gov/13.7#E1
     //
     // Note that a and b must not be negative integers, in addition
     // we require z > 0 and so apply Kummer's relation for z < 0.
     //
     template <class T, class Policy>
     inline T hypergeometric_1F1_asym_large_z_series(T a, const T& b, T z, const Policy& pol, int& log_scaling)
     {
        BOOST_MATH_STD_USING
        static const char* function = "boost::math::hypergeometric_1F1_asym_large_z_series<%1%>(%1%, %1%, %1%)";
        T prefix;
        int e, s;
        if (z < 0)
        {
           a = b - a;
           z = -z;
           prefix = 1;
        }
        else
        {
           e = z > INT_MAX ? INT_MAX : itrunc(z, pol);
           log_scaling += e;
           prefix = exp(z - e);
        }
        if ((fabs(a) < 10) && (fabs(b) < 10))
        {
           prefix *= pow(z, a) * pow(z, -b) * boost::math::tgamma(b, pol) / boost::math::tgamma(a, pol);
        }
        else
        {
           T t = log(z) * (a - b);
           e = itrunc(t, pol);
           log_scaling += e;
           prefix *= exp(t - e);

           t = boost::math::lgamma(b, &s, pol);
           e = itrunc(t, pol);
           log_scaling += e;
           prefix *= s * exp(t - e);

           t = boost::math::lgamma(a, &s, pol);
           e = itrunc(t, pol);
           log_scaling -= e;
           prefix /= s * exp(t - e);
        }
        //
        // Checked 2F0:
        //
        unsigned k = 0;
        T a1_poch(1 - a);
        T a2_poch(b - a);
        T z_mult(1 / z);
        T sum = 0;
        T abs_sum = 0;
        T term = 1;
        T last_term = 0;
        do
        {
           sum += term;
           last_term = term;
           abs_sum += fabs(sum);
           term *= a1_poch * a2_poch * z_mult;
           term /= ++k;
           a1_poch += 1;
           a2_poch += 1;
           if (fabs(sum) * boost::math::policies::get_epsilon<T, Policy>() > fabs(term))
              break;
           if(fabs(sum) / abs_sum < boost::math::policies::get_epsilon<T, Policy>())
              return boost::math::policies::raise_evaluation_error<T>(function, "Large-z asymptotic approximation to 1F1 has destroyed all the digits in the result due to cancellation.  Current best guess is %1%",
                 prefix * sum, Policy());
           if(k > boost::math::policies::get_max_series_iterations<Policy>())
              return boost::math::policies::raise_evaluation_error<T>(function, "1F1: Unable to locate solution in a reasonable time:"
                 " large-z asymptotic approximation.  Current best guess is %1%", prefix * sum, Policy());
           if((k > 10) && (fabs(term) > fabs(last_term)))
              return boost::math::policies::raise_evaluation_error<T>(function, "Large-z asymptotic approximation to 1F1 is divergent.  Current best guess is %1%", prefix * sum, Policy());
        } while (true);

        return prefix * sum;
     }

  // experimental range
  template <class T, class Policy>
  inline bool hypergeometric_1F1_asym_region(const T& a, const T& b, const T& z, const Policy&)
  {
    BOOST_MATH_STD_USING
    int half_digits = policies::digits<T, Policy>() / 2;
    bool in_region = false;

    if (fabs(a) < 0.001f)
       return false; // Haven't been able to make this work, why not?  TODO!

    //
    // We use the following heuristic, if after we have had half_digits terms
    // of the 2F0 series, we require terms to be decreasing in size by a factor
    // of at least 0.7.  Assuming the earlier terms were converging much faster
    // than this, then this should be enough to achieve convergence before the
    // series shoots off to infinity.
    //
    if (z > 0)
    {
       T one_minus_a = 1 - a;
       T b_minus_a = b - a;
       if (fabs((one_minus_a + half_digits) * (b_minus_a + half_digits) / (half_digits * z)) < 0.7)
       {
          in_region = true;
          //
          // double check that we are not divergent at the start if a,b < 0:
          //
          if ((one_minus_a < 0) || (b_minus_a < 0))
          {
             if (fabs(one_minus_a * b_minus_a / z) > 0.5)
                in_region = false;
          }
       }
    }
    else if (fabs((1 - (b - a) + half_digits) * (a + half_digits) / (half_digits * z)) < 0.7)
    {
       if ((floor(b - a) == (b - a)) && (b - a < 0))
          return false;  // Can't have a negative integer b-a.
       in_region = true;
       //
       // double check that we are not divergent at the start if a,b < 0:
       //
       T a1 = 1 - (b - a);
       if ((a1 < 0) || (a < 0))
       {
          if (fabs(a1 * a / z) > 0.5)
             in_region = false;
       }
    }
    //
    // Check for a and b negative integers as these aren't supported by the approximation:
    //
    if (in_region)
    {
       if ((a < 0) && (floor(a) == a))
          in_region = false;
       if ((b < 0) && (floor(b) == b))
          in_region = false;
       if (fabs(z) < 40)
          in_region = false;
    }
    return in_region;
  }

  } } } // namespaces

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

#endif // BOOST_MATH_HYPERGEOMETRIC_ASYM_HPP


/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2020 Scientific Computing and Imaging Institute,
   University of Utah.

   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/Algorithms/Base/AlgorithmPreconditions.h>
#include <Core/Algorithms/Math/SolveLinearSystemWithEigen.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/DenseColumnMatrix.h>
#include <Core/Datatypes/SparseRowMatrix.h>
#include <Core/Datatypes/MatrixTypeConversions.h>
#include <Eigen/Sparse>

using namespace SCIRun;
using namespace SCIRun::Core::Algorithms::Math;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core;

namespace
{
  using EigenComputationInfo = boost::error_info<struct tag_eigen_computation, Eigen::ComputationInfo>;

  template <class ColumnMatrixType, template <typename> class SolverType>
  class SolveLinearSystemAlgorithmEigenCGImpl
  {
  public:
    SolveLinearSystemAlgorithmEigenCGImpl(SharedPointer<ColumnMatrixType> rhs, double tolerance, int maxIterations) :
        tolerance_(tolerance), maxIterations_(maxIterations), rhs_(rhs) {}

    using SolutionType = ColumnMatrixType;

    template <class MatrixType>
    typename ColumnMatrixType::EigenBase solveWithEigen(const MatrixType& lhs)
    {
      SolverType<typename MatrixType::EigenBase> solver;
      solver.compute(lhs);

      if (solver.info() != Eigen::Success)
        BOOST_THROW_EXCEPTION(AlgorithmInputException()
          << LinearAlgebraErrorMessage("Eigen solver initialization was unsuccessful")
          << EigenComputationInfo(solver.info()));

      solver.setTolerance(tolerance_);
      solver.setMaxIterations(maxIterations_);
      auto solution = solver.solve(*rhs_).eval();
      tolerance_ = solver.error();
      maxIterations_ = solver.iterations();
      return solution;
    }

    double tolerance_;
    int maxIterations_;
  private:
    SharedPointer<ColumnMatrixType> rhs_;
  };
}

SolveLinearSystemAlgorithm::Outputs SolveLinearSystemAlgorithm::run(const Inputs& input, const Parameters& params) const
{
  return runImpl<Inputs, Outputs>(input, params);
}

SolveLinearSystemAlgorithm::ComplexOutputs SolveLinearSystemAlgorithm::run(const ComplexInputs& input, const Parameters& params) const
{
  return runImpl<ComplexInputs, ComplexOutputs>(input, params);
}

template <typename T>
using CG = Eigen::ConjugateGradient<T>;
// Not available yet, need to upgrade Eigen
// template <typename T>
// using LSCG = Eigen::LeastSquaresConjugateGradient<T>;
template <typename T>
using BiCG = Eigen::BiCGSTAB<T>;

template <typename In, typename Out>
Out SolveLinearSystemAlgorithm::runImpl(const In& input, const Parameters& params) const
{
  auto A = std::get<0>(input);
  ENSURE_ALGORITHM_INPUT_NOT_NULL(A, "Null input matrix");

  auto b = std::get<1>(input);
  ENSURE_ALGORITHM_INPUT_NOT_NULL(b, "Null rhs vector");

  double tolerance = std::get<0>(params);
  ENSURE_POSITIVE_DOUBLE(tolerance, "Tolerance out of range!");

  int maxIterations = std::get<1>(params);
  ENSURE_POSITIVE_INT(maxIterations, "Max iterations out of range!");

  auto method = std::get<2>(params);

  using SolutionType = DenseColumnMatrixGeneric<typename std::tuple_element<0, In>::type::element_type::value_type>;
  using AlgoTypeCG = SolveLinearSystemAlgorithmEigenCGImpl<SolutionType, CG>;
  using AlgoTypeBiCG = SolveLinearSystemAlgorithmEigenCGImpl<SolutionType, BiCG>;

  if ("cg" == method)
    return solve<AlgoTypeCG, In, Out>(input, params);
  else if ("bicg" == method)
    return solve<AlgoTypeBiCG, In, Out>(input, params);
  else
  {
    BOOST_THROW_EXCEPTION(AlgorithmProcessingException() << ErrorMessage("Need to upgrade Eigen for LSCG."));
  }
}

template <typename SolverType, typename In, typename Out>
Out SolveLinearSystemAlgorithm::solve(const In& input, const Parameters& params) const
{
  auto A = std::get<0>(input);
  auto b = std::get<1>(input);
  double tolerance = std::get<0>(params);
  int maxIterations = std::get<1>(params);

  SolverType impl(b, tolerance, maxIterations);

  typename SolverType::SolutionType x;
  if (matrixIs::dense(A))
  {
    auto dense = castMatrix::toDense(A);
    x = impl.solveWithEigen(*dense);
  }
  else if (matrixIs::sparse(A))
  {
    auto sparse = castMatrix::toSparse(A);
    x = impl.solveWithEigen(*sparse);
  }
  else
    BOOST_THROW_EXCEPTION(AlgorithmProcessingException() << ErrorMessage("solveWithEigen can only handle dense and sparse matrices."));

  if (x.size() != 0)
  {
    auto solution(makeShared<typename SolverType::SolutionType>(x));
    return Out(solution, impl.tolerance_, impl.maxIterations_);
  }
  else
    BOOST_THROW_EXCEPTION(AlgorithmProcessingException() << ErrorMessage("solveWithEigen produced an empty solution."));
}

AlgorithmOutput SolveLinearSystemAlgorithm::run(const AlgorithmInput&) const
{
  throw 2;
}


/*
Program to translate text to DNA and vice versa
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <boost/random/mersenne_twister.hpp>
//#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/bernoulli_distribution.hpp>
#include <boost/program_options.hpp>
#include "../include/GF2M.hpp"
#include "../include/DFT.hpp"
#include "../include/helpers.hpp"
#include "../include/encodedecode.hpp"
#include "../include/ReedSolomon.hpp"
#include <string>
#include <fstream>
#include <streambuf>

using namespace std;

namespace po = boost::program_options;

int main(int ac, char* av[])
{

// command line options
int primer_length = 0;
int numblocks = 1;
bool rev = false;
string infile;
string fastq_infile;
string outfile;
string gtruthfile;

unsigned N = 34;
unsigned K = 32;

unsigned n = 16383;
unsigned k = 10977; // k = 0.67*n

unsigned l = 4; // length of the index
unsigned nuss = 12; // number of symbols of outer code per segment

int opt;

po::options_description desc("Allowed options");
desc.add_options()
    ("help", "produce help message")
    ("encode", "encode")
    ("decode", "decode")
	("singleseq","singleseq")

    ("disturb", "draw uniformly at random from the input lines, add errors to each line")
	("input",po::value<string>(&infile)->default_value(""),"inputfile")
	("fastq_infile",po::value<string>(&fastq_infile)->default_value(""),"fastq inputfile")
	("output",po::value<string>(&outfile)->default_value(""),"outputfile")
	("groundtruth",po::value<string>(&gtruthfile)->default_value(""),"groundtruthfile")
	("reverse", po::bool_switch(&rev), "Fastq reverse sequences")
	("primer_length",po::value<int>(&primer_length)->default_value(0),"primer_length")

	("numblocks",po::value<int>(&numblocks)->default_value(1),"numblocks")
	("n",po::value<unsigned>(&n)->default_value(16383),"n")
	("k",po::value<unsigned>(&k)->default_value(10977),"k")
	("N",po::value<unsigned>(&N)->default_value(34),"N")
	("K",po::value<unsigned>(&K)->default_value(32),"K")
	("l",po::value<unsigned>(&l)->default_value(4),"l")
	("nuss",po::value<unsigned>(&nuss)->default_value(12),"nuss")
;

po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);

if (vm.count("help")) {
    cout << desc << "\n";
    return 1;
}

///// parameters

typedef unsigned long long int uint;
// inner code
const uint mi = 6;
const uint prim_poly = 91;
typedef GF2M<uint,mi,prim_poly> GFI; // GF(2^6) with primitive polynomail 91
const uint N_u = 63; // the underlying length of the shortened inner code

GFI fai = GFI(2,0); // 64 + 2 = 1000010 = x + x^5
DFT_FFT<GFI> dftgfi(N_u,fai,7,9); // 7*9 = 63
typedef RScode<GFI, DFT_FFT<GFI> > Innercode;
Innercode innercode(N,K,fai,dftgfi,N_u);

// list of primitive polynomials can be found here: https://www.partow.net/programming/polynomials/index.html

//// parameter choices for outer code of length 16383
///*
const unsigned Q = 127;
const unsigned P = 129;
const unsigned n_u = P*Q; // 16383 = 2^14 - 1, Q is prime and P = 3*43
const uint prim_poly_o = 16553; // primitive polynomial for 2^14
const unsigned mo = 14;
typedef GF2M<uint,mo,prim_poly_o> GFO;
GFO fao = GFO(66,0); // 66 = 64 + 2 = 1000010 = x + x^5 // Element of order 16383
//*/

//// parameter choices for outer code of length 4095
/*
unsigned Q = 63;
unsigned P = 65;
unsigned n_u = 4095; // 2^12 - 1; // 2^12-1 = 5*3*3*7*13
const unsigned mo = 12;
const uint prim_poly_o = 4621; // primitive polynomial for GF(2^12), x^12 + x^9 + x^3 + x^2 + 1
typedef GF2M<uint,mo,prim_poly_o> GFO;
GFO fao = GFO(10,0); // 9 = 64 + 2 = 1010 = x + x^3 // Element of order 4095; must have order 4095
//cout << "element has order:" << fao.order() << endl; // check for order of element
*/

DFT_FFT<GFO> dftgfo(n_u,fao,P,Q); // Fourier transform for the outer code

typedef RScode<GFO,DFT_FFT<GFO> > Outercode;
Outercode outercode(n,k,fao,dftgfo,n_u);

// check all the parameter choices

if (!vm.count("singleseq")) {
if (K*mi != nuss*mo+l*mi) {
	cerr << "Must have: K*mi = nuss*mo + l*mi, where mi=6, mo=14, but got " << K*mi << ", " << nuss*mo+l*mi << endl;
	return 1;
}
}

if ( K > N) {
	cerr << "Must have: K <= N"  << endl;
	return 1;
}

if ( k > n) {
	cerr << "Must have: k <= n"  << endl;
	return 1;
}

if ( n > n_u) {
	cerr << "Must have: n <= n_u"  << endl;
	return 1;
}

if ( N > N_u) {
	cerr << "Must have: N <= N_u"  << endl;
	return 1;
}

if ( l*mi < log( numblocks*n )/log( 2 ) ) {
	cerr << "Index has length " << l*mi << " bit, but require " << log( numblocks*n )/log( 2 ) << " bits" << endl;
	return 1;
}

// tell the user the parameter choices

cout << "--------------------------------" << endl;
cout << "redundancy outer code: " << float(n-k)/float(k)*100 << "\%" << endl; // " (= (n-k)/k)" << endl;
cout << "redundancy inner code: " << float(N-K)/float(K)*100 << "\%" << endl; // " (= (N-K)/K)" << endl;
cout << "--------------------------------" << endl;

// encoder/decoder
EnDecode< Innercode , Outercode > endecode(innercode,outercode,l,nuss);
EnDecodeSingleSeq< Innercode > endecodesingleseq(innercode);

/////////////////////// encode
if (vm.count("encode")) {

	cout << "start encoding.." << endl;
	if( infile == "" || outfile ==""){
		cout << "in/outfile not specified " << endl;
		return 0;
	}
	cout << "infile:  " << infile << endl;
	cout << "outfile: " << outfile << endl;

	// read data
	std::ifstream t(infile.c_str());
	std::string str((std::istreambuf_iterator<char>(t)),std::istreambuf_iterator<char>());

	// encode - determines the number of blocks required automatically
	vector<string> urn(n*numblocks);

	if( vm.count("singleseq") ){
		// store only a signle sequence
		endecodesingleseq.encode(str, urn);
		numblocks = 1;
	} else {
		endecode.encode(str, urn);
		numblocks = endecode.numblocks;
	}

	if(numblocks*k*mo*nuss < str.size()*8){
		cerr << "trying to store " << str.size()*8 << " bits, but can only store " << numblocks*k*mo*nuss << "many" << endl;
		return 1;
	}

	if( vm.count("singleseq") ){
		cout << "encoded " << str.size() << " Bytes to one DNA segment of length " << urn[0].size() << endl;
	} else {
		cout << "encoded " << str.size() << " Bytes to " << numblocks << " blocks, resulting in "
	<< urn.size() << " DNA segments of length " << urn[0].size() << " each." << endl;
    }

	ofstream out;
	out.open(outfile.c_str());
	for(unsigned i=0;i<urn.size();++i) out << urn[i] << endl;
	out.close();
	return 0;
}

/////////////////////// decode
if (vm.count("decode")) {

	if( (infile == "" && fastq_infile == "") ) {
		cout << "infile not specified " << endl;
		return 0;
	}

	if( outfile ==""){
		cout << "outfile not specified " << endl;
		return 0;
	}

	if( numblocks==0 && !vm.count("singleseq") ) {
		cout << "numblocks not specified " << endl;
		return 0;
	}

	if(fastq_infile != ""){
		cout << "fastq infile:  " << fastq_infile << endl;
	} else {
		cout << "infile:  " << infile << endl;
	}
	cout << "outfile: " << outfile << endl;
	cout << "numblocks: "<<numblocks << endl;

	vector<string> drawnseg;

	if(infile != ""){
		cout << "assume text file as input format" << endl;

		string sLine = "";
		ifstream in;
		in.open(infile.c_str());
		if (in.fail()) {
        	cerr << "Error when opening infile - does infile exist?" << endl;
			return 1;
		}

		unsigned ctr = 0;
		while (!in.eof()){
			getline(in, sLine);

			int seq_length = N*mi/2;
			if(sLine.size() >= seq_length + primer_length){
				sLine = sLine.substr(primer_length, seq_length);
				drawnseg.push_back(sLine);
				ctr++;
			}
			cout << "\rLines read: " << ctr;
		}
		cout << endl;
		//drawnseg.resize(drawnseg.size()-1); // erase the last, empty line
	} else {
		cout << "assume fastq file as input format" << endl;

		int seq_length = N*mi/2;
		ifstream in;
		in.open(fastq_infile.c_str());
		if(in.fail()){
        	cerr << "Error when opening infile - does infile exist?" << endl;
			return 1;
		}

		string sLine = "";
		vector<int> hist(400,0);
		unsigned totalctr = 0;

		int ctr = 0;
		while (!in.eof()){
			getline(in, sLine);
			ctr++;
			if(ctr % 4 == 2){
				hist[sLine.size()]++;
				totalctr++;
				if(rev){
					flipvecdir(sLine);
					fliplett(sLine);
				}

				if(sLine.size() >= seq_length + primer_length){
					sLine = sLine.substr(primer_length, seq_length + primer_length );
					drawnseg.push_back(sLine);
				}
			}
			//if(ctr == 100000000) break;
		}
		in.close();
	}

	//

	string recstr;
	cout << "start decode.." << endl;

	if( vm.count("singleseq")){
		// store only a signle sequence
		endecodesingleseq.decode(recstr, drawnseg);
	} else {
		endecode.numblocks = numblocks;
		endecode.decode(recstr, drawnseg, gtruthfile);
	}

	ofstream out;
	out.open(outfile.c_str());
	out << recstr;
	out.close();
	cout << "Wrote file of length " << recstr.size() << " Bytes" << endl;
}

////////////////////////// disturb
if (vm.count("disturb")) {

	// take M random draws uniformly at random and disturb those

	const unsigned M = n*numblocks*6;
	const float substprob = 0.0005; // substitution error probability
	//const unsigned M = n*numblocks*20;
	//const float substprob = 0.0005; // substitution error probability
	cout << "disturb:" << endl;
	cout << "\tDraw " << M << " many times" << endl;
	cout << "\tsubstitution error probability " << substprob << endl;

	if(infile == "" || outfile =="") {
		cout << "in/outfile not specified " << endl;
		return 0;
	}
	cout << "infile:  " << infile << endl;
	cout << "outfile: " << outfile << endl;

	vector<string> urn;

	string sLine = "";
	ifstream in;
	in.open(infile.c_str());
	while (!in.eof()){
		getline(in, sLine);
		urn.push_back(sLine);
	}
	urn.resize(urn.size()-1); // erase the last, empty line

	boost::mt19937 rng;
	boost::uniform_int<> unif(0,urn.size()-1); // distribution that maps to 0,..,urn.size()-1
	boost::uniform_int<> unif_N(0,N-1); // uniform distribution over {0,..,N-1}
	boost::uniform_int<> unif_4(0,4-1); // uniform distribution over {0,1,2,3}
	boost::bernoulli_distribution<> bern(substprob);
	//boost::bernoulli_distribution<> faircoin(0.5);

	char nucl[] = "ACGT";

	vector<string> drawnseg(M);

	ofstream out;
	out.open(outfile.c_str());

	// draw M times
	string tmpstr;
	for(unsigned i=0;i<M;++i){
		unsigned randind = unif(rng);
		tmpstr = urn[randind];

		// introduce errors
		// introduce on error per inner cw
		for(unsigned j=0;j<1;++j){
			tmpstr[unif_N(rng)] = nucl[unif_4(rng)];
		}

		unsigned ctr = 0;
		for(unsigned j=0;j<tmpstr.size();++j)
			if(bern(rng)) {
				tmpstr[j] = nucl[unif_4(rng)];
				ctr++;
			}
			//cout << ctr << endl;
		//if(faircoin(rng)) flipvecdir(tmpstr); // flip every second

		// write to file
		out << tmpstr;
		if(i!= M-1) out << endl;
	}
	out.close();

}

}


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

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

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

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

#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <ored/marketdata/marketimpl.hpp>
#include <ored/portfolio/builders/fxoption.hpp>
#include <ored/portfolio/enginedata.hpp>
#include <ored/portfolio/fxoption.hpp>
#include <oret/toplevelfixture.hpp>
#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/daycounters/actualactual.hpp>

using namespace QuantLib;
using namespace boost::unit_test_framework;
using namespace std;
using namespace ore::data;
using namespace ore::data;

namespace {

class TestMarket : public MarketImpl {
public:
    TestMarket() {
        asof_ = Date(3, Feb, 2016);

        // build discount
        yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] = flatRateYts(0.025);
        yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "USD")] = flatRateYts(0.03);

        // add fx rates
        fxSpots_[Market::defaultConfiguration].addQuote("EURUSD", Handle<Quote>(boost::make_shared<SimpleQuote>(1.2)));

        // build fx vols
        fxVols_[make_pair(Market::defaultConfiguration, "EURUSD")] = flatRateFxv(0.10);
    }

private:
    Handle<YieldTermStructure> flatRateYts(Real forward) {
        boost::shared_ptr<YieldTermStructure> yts(new FlatForward(0, NullCalendar(), forward, ActualActual()));
        return Handle<YieldTermStructure>(yts);
    }
    Handle<BlackVolTermStructure> flatRateFxv(Volatility forward) {
        boost::shared_ptr<BlackVolTermStructure> fxv(new BlackConstantVol(0, NullCalendar(), forward, ActualActual()));
        return Handle<BlackVolTermStructure>(fxv);
    }
};
} // namespace

BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)

BOOST_AUTO_TEST_SUITE(FXOptionTests)

// FX Option test, example from Wystup, section 1.2.6, page 28
BOOST_AUTO_TEST_CASE(testFXOptionPrice) {

    BOOST_TEST_MESSAGE("Testing FXOption Price...");

    Date today = Settings::instance().evaluationDate();

    // build market
    boost::shared_ptr<Market> market = boost::make_shared<TestMarket>();
    Settings::instance().evaluationDate() = market->asofDate();

    // build FXOption - expiry in 1 Year
    OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20170203"));
    OptionData optionDataPremiumUSD("Long", "Call", "European", true, vector<string>(1, "20170203"), "Cash", "",
                                    10000.0, "USD", "20170203");
    OptionData optionDataPremiumEUR("Long", "Call", "European", true, vector<string>(1, "20170203"), "Cash", "",
                                    10000.0, "EUR", "20170203");
    Envelope env("CP1");
    FxOption fxOption(env, optionData, "EUR", 1000000, // bought
                      "USD", 1250000);                 // sold
    FxOption fxOptionPremiumUSD(env, optionDataPremiumUSD, "EUR", 1000000, "USD", 1250000);
    FxOption fxOptionPremiumEUR(env, optionDataPremiumEUR, "EUR", 1000000, "USD", 1250000);

    // NPV currency = sold currency = USD

    Real expectedNPV_USD = 29148.0;
    Real expectedNPV_EUR = 24290.0;
    Real expectedNPV_USD_Premium_USD = 19495.6;
    Real expectedNPV_USD_Premium_EUR = 17496.4;

    // Build and price
    boost::shared_ptr<EngineData> engineData = boost::make_shared<EngineData>();
    engineData->model("FxOption") = "GarmanKohlhagen";
    engineData->engine("FxOption") = "AnalyticEuropeanEngine";
    boost::shared_ptr<EngineFactory> engineFactory = boost::make_shared<EngineFactory>(engineData, market);
    engineFactory->registerBuilder(boost::make_shared<FxOptionEngineBuilder>());

    fxOption.build(engineFactory);
    fxOptionPremiumUSD.build(engineFactory);
    fxOptionPremiumEUR.build(engineFactory);

    Real npv = fxOption.instrument()->NPV();
    Real npv_prem_usd = fxOptionPremiumUSD.instrument()->NPV();
    Real npv_prem_eur = fxOptionPremiumEUR.instrument()->NPV();

    BOOST_TEST_MESSAGE("FX Option, NPV Currency " << fxOption.npvCurrency());
    BOOST_TEST_MESSAGE("NPV =                     " << npv);
    BOOST_TEST_MESSAGE("NPV with premium in USD = " << npv_prem_usd);
    BOOST_TEST_MESSAGE("NPV with premium in EUR = " << npv_prem_eur);

    // Check NPV matches expected values. Expected value from Wystup is rounded at each step of
    // calculation so we get a difference of $50 here.
    QL_REQUIRE(fxOption.npvCurrency() == "USD", "unexpected NPV currency");
    if (fxOption.npvCurrency() == "EUR") {
        BOOST_CHECK_CLOSE(npv, expectedNPV_EUR, 0.2);
    } else if (fxOption.npvCurrency() == "USD") {
        BOOST_CHECK_CLOSE(npv, expectedNPV_USD, 0.2);
        BOOST_CHECK_CLOSE(npv_prem_usd, expectedNPV_USD_Premium_USD, 0.001);
        BOOST_CHECK_CLOSE(npv_prem_eur, expectedNPV_USD_Premium_EUR, 0.001);
    } else {
        BOOST_FAIL("Unexpected FX Option npv currency " << fxOption.npvCurrency());
    }

    Settings::instance().evaluationDate() = today; // reset
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()


#include "unit_square_mapping.h"

#include <array>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

namespace noises {
namespace nodes {
namespace mappings {

    UnitSquareMapping::UnitSquareMapping()
    {
        inputs().add("Grid", SocketType::attribute);

        outputs().add("Mapped", ConnectionDataType::value<float, 2>(), SocketType::attribute);

        input("Grid").set_accepts(ConnectionDataType::any());

        output_ = &output("Mapped");
    }

    std::string UnitSquareMapping::node_name() const
    {
        return "Unit Square Mapping";
    }

    void UnitSquareMapping::execute_uniforms(const CompositeDataBuffer&, DataBuffer &output) const
    {
        //TODO put this in utils
        //extracts the width and height out of the tag and puts them in the scratch buffer for execute_atttributes to use

        static const boost::regex finder("\\[([0-9]+),([0-9]+)\\]");

        std::string tag = output.attribute_info().tag();
        std::string::const_iterator start = tag.begin(), end = tag.end();
        boost::match_results<std::string::const_iterator> what;

        if(!boost::regex_search(start, end, what, finder, boost::match_default))
            throw std::logic_error("PixelMapping requires a tag to identify the data, or tag is missing [width,height]");

        int width = boost::lexical_cast<int>(static_cast<std::string>(what[1]));
        int height = boost::lexical_cast<int>(static_cast<std::string>(what[2]));

        std::array<int, 2> dimensions { width, height };
        output.set_scratch<std::array<int, 2>>(0, dimensions);
    }

    void UnitSquareMapping::execute_attributes(const CompositeDataBuffer&, DataBuffer &output, DataBuffer::size_type index) const
    {
        std::array<int, 2> dimensions = output.get_scratch<std::array<int, 2>>(0);
        std::div_t row_column = std::div(index, dimensions[0]);

        float x = static_cast<float>(row_column.rem) / static_cast<float>(dimensions[0]);
        x = (x * 2) - 1; //-1 to 1

        float y = static_cast<float>(row_column.quot) / static_cast<float>(dimensions[1]);
        y = (y * 2) - 1;

        std::array<float, 2> x_y_array { x, y };

        output.set_attribute<float, 2>(*output_, index, x_y_array);
    }

}}}


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

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

namespace bs = boost::simd;

template<typename T, std::size_t N, typename Env>
void test( Env&, brigand::bool_<false> const& )
{}

template<typename T, std::size_t N, typename Env>
void test( Env& $, brigand::bool_<true> const& = {} )
{
  std::array<T,N> ref;
  for(std::size_t i=0;i<N;++i) ref[i] = T(1+i);

  bs::pack<T,N>   value (&ref[0]  , &ref[0]+N   );
  bs::pack<T,N/2> second(&ref[0]+N/2, &ref[0]+N );

  STF_EQUAL( bs::slice_high(value), second);
}

STF_CASE_TPL("slice_high pack<T,N> into a pack<T,N/2>", STF_NUMERIC_TYPES)
{
  static const std::size_t N = bs::pack<T>::static_size;

  test<T,N>($);
  test<T,N/2>($, brigand::bool_<(N/2 > 1)>{});
  test<T,N*2>($);
}