#include "headers/algorithms.hpp"
#include <fstream>
#include <thread>
#include <boost/thread.hpp>
#include <windows.h>

using namespace std;
using namespace algorithms;
using namespace balancedVRP;

vector<double> x;
vector<double> y;
vector<double> weights;
vector<size_t> frequence;
vector<vector<double>> dist_mat;
vector<Transport> transports;
const double clust_capacity = 700;
const size_t count_clust = 7;

void read_transports()
{
    std::ifstream in("data_transport.csv");
    string line;
    getline(in, line);
    while (!in.eof())
    {
        double volume, cost_start, cost_by_dist;
        size_t count;
        in >> volume >> cost_start >> cost_by_dist >> count;
        transports.push_back({ volume, cost_start, cost_by_dist, count });
    }
    in.close();

    sort(transports.begin(), transports.end(), [](const Transport& a, const Transport& b)
        {
            return a.capacity < b.capacity;
            //return a.second.sep.size() < b.second.sep.size();
        });
}

void read_file()
{
    x.clear();
    y.clear();
    weights.clear();

    std::ifstream in("data_vertex.csv");
    string line;
    getline(in, line);
    double s = 0;
    //double X_mean = 0;
    //double Y_mean = 0;

    while (!in.eof())
    {
        double freq, w, X, Y;
        in >> freq >> w >> X >> Y;
        frequence.push_back(freq);
        x.push_back(X);
        y.push_back(Y);
        //X_mean += X;
        //Y_mean += Y;
        weights.push_back(w);
        s += w;
    }
    //x[0] = X_mean / (x.size() - 1);
    //y[0] = Y_mean / (y.size() - 1);
    cout << "sum volume: " << s << endl;
    in.close();
}

namespace print {
void print(const vector<int_matrix>& routs)
{
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            cout << "[";
            for (const size_t vertex : rout)
                cout << vertex << ", ";
            cout << 0 << "]," << endl;
        }
}

void print(const vector<int_matrix>& routs, const int_matrix& clusters, const size_t clust_id)
{
    cout << "[" << endl;
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            cout << "[";
            for (const size_t vertex : rout)
                cout << clusters[clust_id][vertex] << ", ";
            cout << 0 << "]," << endl;
        }
    cout << "]," << endl;
}

void print(const int_matrix& routs)
{
    for (const vector<size_t>& rout : routs)
    {
        cout << "[";
        for (const size_t vertex : rout)
            cout << vertex << ", ";
        cout << 0 << "]," << endl;
    }
}

void print_file(const vector<int_matrix>& routs)
{
    std::ofstream out("res.txt");
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            for (const size_t vertex : rout)
                out << vertex << " ";
            out << ";";
        }
    out.close();
}

void print_file(const int_matrix& routs)
{
    std::ofstream out("res.txt");
    for (const vector<size_t>& rout : routs)
    {
        for (const size_t vertex : rout)
            out << vertex << " ";
        out << ";";
    }
    out.close();
}

void print_file(const vector<int_matrix>& routs, const int_matrix& clusters, const size_t clust_id)
{
    std::ofstream out("res_clust.txt", std::ios::app);
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            for (const size_t vertex : rout)
                out << clusters[clust_id][vertex] << " ";
            out << ";";
        }
    out << ":";
    out.close();
}

void print_file(const int_matrix& routs, const int_matrix& clusters, const size_t clust_id)
{
    std::ofstream out("res_clust.txt", std::ios::app);
    for (const vector<size_t>& rout : routs)
    {
        for (const size_t vertex : rout)
            out << clusters[clust_id][vertex] << " ";
        out << ";";
    }
    out << ":";
    out.close();
}

void print_file(const vector<int_matrix>& routs, const char* name)
{
    std::ofstream out(name);
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            for (const size_t vertex : rout)
                out << vertex << " ";
            out << ";";
        }
    out.close();
}

void print_file(const int_matrix& routs, const char* name)
{
    std::ofstream out(name);
    for (const vector<size_t>& rout : routs)
    {
        for (const size_t vertex : rout)
            out << vertex << " ";
        out << ";";
    }
    out.close();
}

void print_file(const vector<int_matrix>& routs, const int_matrix& clusters,
    const size_t clust_id, const char* name)
{
    std::ofstream out(name, std::ios::app);
    for (const int_matrix& rout_mat : routs)
        for (const vector<size_t>& rout : rout_mat)
        {
            for (const size_t vertex : rout)
                out << clusters[clust_id][vertex] << " ";
            out << ";";
        }
    out << ":";
    out.close();
}

void print_file(const int_matrix& routs, const int_matrix& clusters,
    const size_t clust_id, const char* name)
{
    std::ofstream out(name, std::ios::app);
    for (const vector<size_t>& rout : routs)
    {
        for (const size_t vertex : rout)
            out << clusters[clust_id][vertex] << " ";
        out << ";";
    }
    out << ":";
    out.close();
}
}

namespace checkers {
    bool check_matrix(const vector<int_matrix>& routs, const size_t size)
    {
        vector<size_t> used(size, 0);
        for (const int_matrix& rout_mat : routs)
            for (const vector<size_t>& rout : rout_mat)
                for (const size_t vertex : rout)
                    ++used[vertex];
        bool is_twice = false;
        bool is_null = false;
        for (size_t i = 1; i < used.size(); ++i)
        {
            if (used[i] > 1)
                is_twice = true;
            if (used[i] == 0)
                is_null = true;
        }
        //cout << "check is_twice: " << is_twice << endl;
        //cout << "check is_null: " << is_null << endl;
        return !is_twice && !is_null;
    }

    bool check_matrix(const int_matrix& routs, const size_t size)
    {
        vector<size_t> used(size, 0);

        for (const vector<size_t>& rout : routs)
            for (const size_t vertex : rout)
                ++used[vertex];
        bool is_twice = false;
        bool is_null = false;
        for (size_t i = 1; i < used.size(); ++i)
        {
            if (used[i] > 1)
                is_twice = true;
            if (used[i] == 0)
                is_null = true;
        }
        //cout << "check is_twice: " << is_twice << endl;
        //cout << "check is_null: " << is_null << endl;
        return !is_twice && !is_null;
    }

    bool check_transport(const int_matrix& routs, const vector<size_t>& transports_id)
    {
        bool is_ok = true;

        for (size_t i = 0; i < routs.size(); ++i)
        {
            double w = 0;
            for (const size_t vertex : routs[i])
                w += weights[vertex];
            is_ok &= transports[transports_id[i]].capacity >= w;
        }

        return is_ok;
    }

    bool check_transport(const int_matrix& routs, const vector<size_t>& transports_id, const vector<double>& W)
    {
        bool is_ok = true;

        for (size_t i = 0; i < routs.size(); ++i)
        {
            double w = 0;
            for (const size_t vertex : routs[i])
                w += W[vertex];
            if (transports[transports_id[i]].capacity < w)
            {
                is_ok &= transports[transports_id[i]].capacity >= w;
            }

        }

        return is_ok;
    }

    bool check_transport(const int_matrix& routs, const Transport& trans, const vector<double>& W)
    {
        bool is_ok = true;

        for (size_t i = 0; i < routs.size(); ++i)
        {
            double w = 0;
            for (const size_t vertex : routs[i])
                w += W[vertex];
            if (trans.capacity < w)
                is_ok &= trans.capacity >= w;
        }

        return is_ok;
    }
}

void save_cluster()
{
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);
    auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

    std::ofstream out("cluster.txt");
    for (size_t i = 0; i < clusters.size(); i++)
    {
        double weight = 0;
        for (size_t j = 0; j < clusters[i].size(); j++)
        {
            weight += weights[clusters[i][j]];
            out << clusters[i][j] << ' ';
        }
        out << ";";
        cout << i << " claut weight: " << weight << " size: " << clusters[i].size() << endl;
    }
    out.close();

    //    sum volume : 4215.56
    //    frequence : 396
    //    acc_weight : 1225.62
    //    5441.18
}

void greedy_parallel_test()
{
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);
    auto start_time = clock();
    std::ofstream out("res_clust.txt");
    out.close();

    auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

    auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
    auto weight_inner_cluster = balancedVRP::clustering::get_weight_inner_cluster(weights, clusters);

    vector<std::shared_ptr<boost::thread>> threads;
    vector< project::GreadyBase> greedies;

    project::GreadyBase k0(
        dist_inner_cluster[0],
        utils::fill_sort_matrix(dist_inner_cluster[0]),
        weight_inner_cluster[0],
        transports);
    project::GreadyBase k1(
        dist_inner_cluster[1],
        utils::fill_sort_matrix(dist_inner_cluster[1]),
        weight_inner_cluster[1],
        transports);
    project::GreadyBase k2(
        dist_inner_cluster[2],
        utils::fill_sort_matrix(dist_inner_cluster[2]),
        weight_inner_cluster[2],
        transports);
    project::GreadyBase k3(
        dist_inner_cluster[3],
        utils::fill_sort_matrix(dist_inner_cluster[3]),
        weight_inner_cluster[3],
        transports);
    project::GreadyBase k4(
        dist_inner_cluster[4],
        utils::fill_sort_matrix(dist_inner_cluster[4]),
        weight_inner_cluster[4],
        transports);
    project::GreadyBase k5(
        dist_inner_cluster[5],
        utils::fill_sort_matrix(dist_inner_cluster[5]),
        weight_inner_cluster[5],
        transports);
    project::GreadyBase k6(
        dist_inner_cluster[6],
        utils::fill_sort_matrix(dist_inner_cluster[6]),
        weight_inner_cluster[6],
        transports);

    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k0));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k1));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k2));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k3));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k4));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k5));
    threads.push_back(std::make_shared<boost::thread>(&project::GreadyBase::run, &k6));

    for (auto t : threads) {
        t->join();
    }
    // time: 1692
    cout << "time: " << clock() - start_time;
}

namespace base_test {
    void sweep_test()
    {
        const char* name = "tests/sweeping_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);
        auto x_coord = clustering::get_weight_inner_cluster(x, clusters);
        auto y_coord = clustering::get_weight_inner_cluster(y, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::Sweeping> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::Sweeping sweep(dist_inner_cluster[i], x_coord[i], y_coord[i], weight_inner_cluster[i], transports);
            sweep.run();
            results.push_back(sweep);
        }

        cout << "# sweeping" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {

            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            for (size_t j = 0; j < results[i].res.size(); j++)
                checks.push_back(checkers::check_transport(results[i].res[j], transports[j], weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void clurk_test()
    {
        const char* name = "tests/clurk_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::ClarkRight> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::ClarkRight clurk(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            clurk.run();
            results.push_back(clurk);
        }

        cout << "# clurk" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {

            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            for (size_t j = 0; j < results[i].res.size(); j++)
                checks.push_back(checkers::check_transport(results[i].res[j], transports[j], weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void greedy_test()
    {
        const char* name = "tests/greedy_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

            project::GreadyBase greedy(
                dist_inner_cluster[i],
                sort_matrix,
                weight_inner_cluster[i],
                transports);
            greedy.run();
            length += greedy.length();
            print::print_file(greedy.res, clusters, i, name);
            checks.push_back(checkers::check_matrix(greedy.res, dist_inner_cluster[i].size()));
        }

        cout << "# greedy" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;
        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;

    }

    void ant_test()
    {
        const char* name = "tests/ant_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<Ant_algorithm> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            Ant_algorithm ant(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            ant.run();
            results.push_back(ant);
        }

        cout << "# ant" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            for (size_t j = 0; j < results[i].res.size(); j++)
                checks.push_back(checkers::check_transport(results[i].res[j], transports[j], weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }
}

namespace osman_test {
    void sweep_test()
    {
        const char* name = "tests/osman_sweep_test.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);
        auto x_coord = clustering::get_weight_inner_cluster(x, clusters);
        auto y_coord = clustering::get_weight_inner_cluster(y, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::Osman> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::Sweeping sweep(dist_inner_cluster[i], x_coord[i], y_coord[i], weight_inner_cluster[i], transports);
            sweep.run();

            auto data = project::Osman::transform_data(sweep.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();
            results.push_back(osman);
        }

        cout << "# osman" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void clark_test()
    {
        const char* name = "tests/osman_clark_test.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::Osman> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::ClarkRight clurk(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            clurk.run();

            auto data = project::Osman::transform_data(clurk.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();
            results.push_back(osman);
        }

        cout << "# osman_clark_test" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void gready_test()
    {
        const char* name = "osman_gready_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::Osman> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

            project::GreadyBase greedy(
                dist_inner_cluster[i],
                sort_matrix,
                weight_inner_cluster[i],
                transports);
            greedy.run();

            auto data = project::Osman::transform_data(greedy.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();
            results.push_back(osman);
        }

        cout << "# osman" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void ant_test()
    {
        const char* name = "tests/osman_ant_test.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::Osman> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            Ant_algorithm ant(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            ant.run();

            auto data = project::Osman::transform_data(ant.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();
            results.push_back(osman);
        }

        cout << "# osman" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }
}

namespace local_test
{
    void sweep_test()
    {
        const char* name = "tests/local_sweep_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);
        auto x_coord = clustering::get_weight_inner_cluster(x, clusters);
        auto y_coord = clustering::get_weight_inner_cluster(y, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::WidhtNeighborhoodSearch> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::Sweeping sweep(dist_inner_cluster[i], x_coord[i], y_coord[i], weight_inner_cluster[i], transports);
            sweep.run();

            auto data = project::Osman::transform_data(sweep.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();

            project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, osman.res,
                osman.transport_id);

            local.run();

            results.push_back(local);
        }

        cout << "# local_sweep_res" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void clark_test()
    {
        const char* name = "tests/local_clark_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::WidhtNeighborhoodSearch> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            project::ClarkRight clurk(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            clurk.run();

            auto data = project::Osman::transform_data(clurk.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();

            project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, osman.res,
                osman.transport_id);

            local.run();

            results.push_back(local);
        }

        cout << "# local_clark_res" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void gready_test()
    {
        const char* name = "tests/local_gready_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::WidhtNeighborhoodSearch> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

            project::GreadyBase greedy(
                dist_inner_cluster[i],
                sort_matrix,
                weight_inner_cluster[i],
                transports);
            greedy.run();

            auto data = project::Osman::transform_data(greedy.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();

            project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, osman.res,
                osman.transport_id);

            local.run();

            results.push_back(local);
        }

        cout << "# local_gready_res" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }

    void ant_test()
    {
        const char* name = "tests/local_ant_res.txt";
        std::ofstream out(name);
        out.close();
        read_file();
        read_transports();
        dist_mat = utils::fill_matrix(x, y);

        unsigned int start_time = clock();
        auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

        auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
        auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

        double length = 0;
        vector<size_t> checks;
        vector<project::WidhtNeighborhoodSearch> results;
        for (size_t i = 0; i < clusters.size(); i++)
        {
            Ant_algorithm ant(dist_inner_cluster[i], weight_inner_cluster[i], transports);
            ant.run();

            auto data = project::Osman::transform_data(ant.res);

            project::Osman osman(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, data.first,
                data.second, data.first.size());

            osman.run();
            osman.add_zero_vertex();

            project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
                weight_inner_cluster[i],
                transports, osman.res,
                osman.transport_id);

            local.run();

            results.push_back(local);
        }

        cout << "# local_ant_res" << endl;
        cout << "# time: " << clock() - start_time << " millisec " << endl;

        for (size_t i = 0; i < clusters.size(); i++)
        {
            length += results[i].length();
            print::print_file(results[i].res, clusters, i, name);
            checks.push_back(checkers::check_matrix(results[i].res, dist_inner_cluster[i].size()));
            checks.push_back(checkers::check_transport(results[i].res, results[i].transport_id, weight_inner_cluster[i]));
        }

        cout << "# length: " << length << endl;
        for (size_t check : checks)
            cout << "check: " << check << endl;
    }
}

void global_local_search_cluster_test(const vector<int_matrix>& vec_routs,
    const vector<vector<size_t>>& vec_trans, const int_matrix& clusters)
{
    int_matrix routs;
    vector<size_t> trans_id;
    vector<size_t> clust_id;

    for (size_t i = 0; i < clusters.size(); i++)
        for (size_t t = 0; t < vec_routs[i].size(); t++)
        {
            const vector<size_t>& rout_i = vec_routs[i][t];
            vector<size_t> rout(rout_i.size());
            trans_id.push_back(vec_trans[i][t]);
            clust_id.push_back(i);

            for (size_t v = 0; v < rout_i.size(); v++)
                rout[v] = clusters[i][rout_i[v]];
            routs.push_back(rout);
        }

    project::GlobalWidhtNeighborhoodSearch local(dist_mat, weights,
        transports, routs, trans_id, clust_id, frequence);

    local.run();
    cout << "length: " << local.length() << endl;
    cout << "check: " << checkers::check_transport(local.res, trans_id) << endl;

    print::print_file(local.res);
}

void local_search_cluster_test_old()
{
    std::ofstream out("res_clust.txt");
    out.close();
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);
    auto clusters = balancedVRP::clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);
    clusters = project::clusters_with_multiple_duplicates(clusters, frequence);
    auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
    auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

    vector<size_t> checks;

    vector<int_matrix> vec_routs;
    vector<vector<size_t>> vec_trans;
    double sum_gready = 0;
    double sum_osman = 0;
    double sum_local= 0;

    for (size_t i = 0; i < clusters.size(); i++)
    {
        auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

        project::GreadyBase greedy(
            dist_inner_cluster[i],
            sort_matrix,
            weight_inner_cluster[i],
            transports);
        greedy.run();
        double len = greedy.length();
        cout << "greedy length: " << len << endl;
        sum_gready += len;

        auto data = project::Osman::transform_data(greedy.res);
        cout << "check transport greedy: " << checkers::check_transport(data.first, data.second, weight_inner_cluster[i]) << endl;

        project::Osman osman(dist_inner_cluster[i],
            weight_inner_cluster[i],
            transports, data.first,
            data.second, data.first.size());
        osman.run();
        osman.add_zero_vertex();
        len = osman.length();
        cout << "osman length: " << len << endl;
        sum_osman += len;
        cout << "check transport osman: " << checkers::check_transport(osman.res, osman.transport_id, weight_inner_cluster[i]) << endl;

        project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
            weight_inner_cluster[i],
            transports, osman.res,
            osman.transport_id);

        local.run();
        len = local.length();
        cout << "local length: " << len << endl;

        sum_local += len;
        print::print_file(local.res, clusters, i);
        cout << "check transport local: " << checkers::check_transport(local.res, local.transport_id, weight_inner_cluster[i]) << endl;

        checks.push_back(checkers::check_matrix(local.res, dist_inner_cluster[i].size()));

        vec_routs.push_back(local.res);
        vec_trans.push_back(local.transport_id);
    }

    cout << "sum_gready length: " << sum_gready << endl;
    cout << "sum_osman length: " << sum_osman << endl;
    cout << "sum_local length: " << sum_local << endl;
    for (size_t check : checks)
        cout << "check: " << check << endl;

    global_local_search_cluster_test(vec_routs, vec_trans, clusters);
}

void final_test()
{
    const char* name = "final_res.txt";
    std::ofstream out(name);
    out.close();
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);

    unsigned int start_time = clock();
    auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

    auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
    auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

    double length = 0;
    vector<size_t> checks;

    vector<int_matrix> vec_routs;
    vector<vector<size_t>> vec_trans;
    for (size_t i = 0; i < clusters.size(); i++)
    {
        Ant_algorithm ant(dist_inner_cluster[i], weight_inner_cluster[i], transports);
        ant.run();

        auto data = project::Osman::transform_data(ant.res);

        project::Osman osman(dist_inner_cluster[i],
            weight_inner_cluster[i],
            transports, data.first,
            data.second, data.first.size());

        osman.run();
        osman.add_zero_vertex();

        project::WidhtNeighborhoodSearch local(dist_inner_cluster[i],
            weight_inner_cluster[i],
            transports, osman.res,
            osman.transport_id);

        local.run();

        vec_routs.push_back(local.res);
        vec_trans.push_back(local.transport_id);
    }

    int_matrix routs;
    vector<size_t> trans_id;
    vector<size_t> clust_id;

    for (size_t i = 0; i < clusters.size(); i++)
        for (size_t t = 0; t < vec_routs[i].size(); t++)
        {
            const vector<size_t>& rout_i = vec_routs[i][t];
            vector<size_t> rout(rout_i.size());
            trans_id.push_back(vec_trans[i][t]);
            clust_id.push_back(i);

            for (size_t v = 0; v < rout_i.size(); v++)
                rout[v] = clusters[i][rout_i[v]];
            routs.push_back(rout);
        }

    project::GlobalWidhtNeighborhoodSearch local(dist_mat, weights,
        transports, routs, trans_id, clust_id, frequence);

    local.run();

    cout << "# final" << endl;
    cout << "# time: " << clock() - start_time << " millisec " << endl;
    cout << "# length: " << local.length() << endl;
    cout << "check: " << checkers::check_transport(local.res, trans_id) << endl;

    print::print_file(local.res, name);
}

void opt_3_test()
{
    const char* name = "tests/opt_3_greedy_res.txt";
    std::ofstream out(name);
    out.close();
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);

    unsigned int start_time = clock();
    auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

    auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
    auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

    double length = 0;
    vector<size_t> checks;
    for (size_t i = 0; i < clusters.size(); i++)
    {
        auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

        project::GreadyBase greedy(
            dist_inner_cluster[i],
            sort_matrix,
            weight_inner_cluster[i],
            transports);
        greedy.run();

        auto data = project::Osman_new::transform_data(greedy.res);
        project::Osman_new::remove_zero_vertex(data.first);
        for (size_t j = 0; j < data.first.size(); j++)
        {
            double old_len = 2000000000;
            double cur_len = 1000000000;
            while (old_len - cur_len - EPS > 0)
            {
                old_len = cur_len;
                cur_len = TSP::local_opt::opt_3(data.first[i], dist_inner_cluster[i]);
            }
        }
        project::Osman_new::add_zero_vertex(data.first);
        project::Osman_new osman(dist_inner_cluster[i],
            weight_inner_cluster[i],
            transports, data.first,
            data.second, data.first.size());
        length += osman.length();
        print::print_file(osman.res, clusters, i, name);
        checks.push_back(checkers::check_matrix(osman.res, dist_inner_cluster[i].size()));
    }

    cout << "# greedy" << endl;
    cout << "# time: " << clock() - start_time << " millisec " << endl;
    cout << "# length: " << length << endl;
    for (size_t check : checks)
        cout << "check: " << check << endl;
}

void greedy_serach_test()
{
    const char* name = "tests/greedy_serach_test.txt";
    //std::ofstream out(name);
    //out.close();
    read_file();
    read_transports();
    dist_mat = utils::fill_matrix(x, y);

    unsigned int start_time = clock();
    auto clusters = clustering::dichotomous_division_weight(dist_mat, weights, clust_capacity, count_clust);

    auto dist_inner_cluster = clustering::get_dist_inner_cluster(dist_mat, clusters);
    auto weight_inner_cluster = clustering::get_weight_inner_cluster(weights, clusters);

    double length = 0;
    vector<size_t> checks;
    for (size_t i = 0; i < clusters.size(); i++)
    {
        auto sort_matrix = utils::fill_sort_matrix(dist_inner_cluster[i]);

        project::GreadyBase greedy(
            dist_inner_cluster[i],
            sort_matrix,
            weight_inner_cluster[i],
            transports);
        //greedy.run();
        auto param = greedy.grid_search_param();
        cout << "coef_filling: " << param.first << "; coef_cut: " << param .second << endl;
        length += greedy.length();
        //print::print_file(greedy.res, clusters, i, name);
        checks.push_back(checkers::check_matrix(greedy.res, dist_inner_cluster[i].size()));
    }

    cout << "# greedy" << endl;
    cout << "# time: " << clock() - start_time << " millisec " << endl;
    cout << "# length: " << length << endl;
    for (size_t check : checks)
        cout << "check: " << check << endl;

}

int main()
{
    local_test::clark_test();
    local_test::sweep_test();
    return 0;
}


#pragma once
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>

#include <eigen_conversions/eigen_msg.h>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <Eigen/StdVector>
#include <opencv2/opencv.hpp>

#include <cv_bridge/cv_bridge.h>
#include <image_geometry/pinhole_camera_model.h>
#include <image_transport/image_transport.h>
#include <ros/ros.h>
#include <sensor_msgs/image_encodings.h>
#include <tf/transform_listener.h>

#include <sub8_msgs/TBDetectionSwitch.h>
#include <sub8_msgs/TorpBoardPoseRequest.h>
#include <mil_vision_lib/cv_tools.hpp>
#include <sub8_vision_lib/visualization.hpp>

#include <mil_tools/mil_tools.hpp>

// #define SEGMENTATION_DEBUG

/*
  Warning:
  Because of its multithreadedness, this class cannot be copy constructed.
  For examlple, the following will not compile:
    Sub8TorpedoBoardDetector tb_detector = Sub8TorpedoBoardDetector();
  Do this instead:
    Sub8TorpedoBoardDetector tb_detector();
*/

class Sub8TorpedoBoardDetector
{
public:
  Sub8TorpedoBoardDetector();
  ~Sub8TorpedoBoardDetector();

  // Public Variables
  double image_proc_scale, feature_min_distance;
  int diffusion_time, max_features, feature_block_size;
  mil_vision::Contour left_corners, right_corners;
  mil_vision::ImageWithCameraInfo left_most_recent, right_most_recent;

private:
  // Callbacks
  bool detection_activation_switch(sub8_msgs::TBDetectionSwitch::Request &req,
                                   sub8_msgs::TBDetectionSwitch::Response &resp);
  void left_image_callback(const sensor_msgs::ImageConstPtr &image_msg_ptr,
                           const sensor_msgs::CameraInfoConstPtr &info_msg_ptr);
  void right_image_callback(const sensor_msgs::ImageConstPtr &image_msg_ptr,
                            const sensor_msgs::CameraInfoConstPtr &info_msg_ptr);

  // Detection / Processing
  void run();
  void determine_torpedo_board_position();
  void segment_board(const cv::Mat &src, cv::Mat &dest, cv::Mat &dbg_img, bool draw_dbg_img = false);
  bool find_board_corners(const cv::Mat &segmented_board, std::vector<cv::Point> &corners, bool draw_dbg_left = true);
  void stereo_correspondence(const cv::Mat &gray_L, const cv::Mat &gray_R, const std::vector<cv::Point> &features_L,
                             const std::vector<cv::Point> &features_R,
                             std::vector<std::vector<int> > &corresponding_feat_idxs);

  // std::vector<cv::Point2d> project_rotated_model(Eigen::Matrix<double, 3, 4> cam_matx,
  //                                                Eigen::Quaterniond orientation);

  // ROS
  ros::NodeHandle nh;
  ros::ServiceServer detection_switch;
  ros::ServiceClient pose_client;
  image_transport::CameraSubscriber left_image_sub, right_image_sub;
  image_transport::ImageTransport image_transport;
  image_transport::Publisher debug_image_pub;
  image_geometry::PinholeCameraModel left_cam_model, right_cam_model;

  // Torpedo Board detection will be attempted when true
  bool active;

  // To prevent invalid img pointers from being passed to toCvCopy (segfault)
  boost::mutex left_mtx, right_mtx;

// Frames will be considered synchronized if their stamp difference is less than
// this (in seconds)
#if __cplusplus > 199711L
  static constexpr double sync_thresh = 0.5;
#else
  static const double sync_thresh = 0.5;
#endif

  // Goes into sequential id for pos_est srv request
  long long int run_id;

  // RVIZ
  sub::RvizVisualizer rviz;

  // DBG images will be generated and published when true
  bool generate_dbg_img;
  cv::Mat debug_image;
  cv::Rect upper_left, upper_right, lower_left, lower_right;
};

class TorpedoBoardReprojectionCost
{
public:
  TorpedoBoardReprojectionCost(cv::Matx34d &proj_L, cv::Matx34d &proj_R, std::vector<cv::Point> &corners_L,
                               std::vector<cv::Point> &corners_R);
  ~TorpedoBoardReprojectionCost();

  template <typename T>
  bool operator()(const T *const x, const T *const y, const T *const z, const T *const yaw, T *residual) const;

private:
  static std::vector<cv::Point> getProjectedCorners(double center_x, double center_y, double center_z, double yaw,
                                                    cv::Matx34d &proj_matrix);

#if __cplusplus > 199711L
  static constexpr double height_m = 1.24;  // in meters, aka(49 in.)
  static constexpr double width_m = 0.61;   // in meters, aka(24 in.)
#else
  static const double height_m = 1.24;  // in meters, aka(49 in.)
  static const double width_m = 0.61;   // in meters, aka(24 in.)
#endif

  const cv::Matx34d proj_L;
  const cv::Matx34d proj_R;

  const std::vector<cv::Point> img_corners_L;
  const std::vector<cv::Point> img_corners_R;
};

/*
  Helper Functions
*/
// Edge preserving image denoising
void anisotropic_diffusion(const cv::Mat &src, cv::Mat &dest, int t_max);

// Pick a plane from a triplet of 3 points from a vector of points
void best_plane_from_combination(const std::vector<Eigen::Vector3d> &point_list, double distance_threshold,
                                 std::vector<double> &result_coeffs);

// Calculate coefficients of plane equation from 3 points
void calc_plane_coeffs(Eigen::Vector3d &pt1, Eigen::Vector3d &pt2, Eigen::Vector3d &pt3,
                       std::vector<double> &plane_coeffs);

// Calculate distance of point to plane defined by coefficients of the plane equation of the
// form a*x + b*y + c*z + d = 0
double point_to_plane_distance(double a, double b, double c, double d, Eigen::Vector3d pt);


#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <boost/program_options.hpp>
#include <glog/logging.h>
#include "colorize.h"

// Lab: L in [0, 100]
//      a in [-127, 127]
//      b in [-127, 127]

using namespace std;

int main (int argc, char *argv[]) {
    cv::Mat lenna = cv::imread("lenna.png", CV_LOAD_IMAGE_COLOR);
    cv::Mat lenna32;
    lenna.convertTo(lenna32, CV_32FC3);
    lenna32 *= 1.0/255;
    cv::Mat Lab;
    cv::cvtColor(lenna32, Lab, CV_BGR2Lab);
    AB codec;
    cv::Mat code(lenna.rows * lenna.cols, AB::BINS, CV_32F, cv::Scalar(0));
    AB::dists_buffer_t dists;

    for (unsigned xxx = 0; xxx < 30; ++xxx) {
        int cc = 0;
        for (int i = 0; i < Lab.rows; ++i) {
            float const *row = Lab.ptr<float const>(i);
            for (int j = 0; j < Lab.cols; ++j) {
                float *bins = code.ptr<float>(cc);
                float w;
                codec.encode(row + 1, bins, &w, dists);
                row += 3;
                if (cc < 20 && xxx == 0)
                for (unsigned k = 0; k < AB::BINS; ++k) {
                    if (bins[k] != 0) {
                        cout << '(' << (i * Lab.cols + j) << ", " << k << ")\t" << bins[k] << endl;
                    }
                }
                ++cc;
            }
        }
        cout << '.' << endl;
    }
    return 0;
}


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

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

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

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

#include <qle/instruments/crossccyfixfloatmtmresetswap.hpp>
#include <qle/cashflows/floatingratefxlinkednotionalcoupon.hpp>
#include <qle/cashflows/fixedratefxlinkednotionalcoupon.hpp>

#include <boost/make_shared.hpp>
#include <ql/cashflows/fixedratecoupon.hpp>
#include <ql/cashflows/iborcoupon.hpp>
#include <ql/cashflows/simplecashflow.hpp>

using namespace QuantLib;

namespace QuantExt {

CrossCcyFixFloatMtMResetSwap::CrossCcyFixFloatMtMResetSwap(
    Real nominal, const Currency& fixedCurrency, const Schedule& fixedSchedule,
    Rate fixedRate, const DayCounter& fixedDayCount, const BusinessDayConvention& fixedPaymentBdc,
    Natural fixedPaymentLag, const Calendar& fixedPaymentCalendar, const Currency& floatCurrency,
    const Schedule& floatSchedule, const boost::shared_ptr<IborIndex>& floatIndex, Spread floatSpread,
    const BusinessDayConvention& floatPaymentBdc, Natural floatPaymentLag, const Calendar& floatPaymentCalendar,
    const boost::shared_ptr<FxIndex>& fxIdx, bool resetsOnFloatLeg, bool receiveFixed)
    : CrossCcySwap(3), nominal_(nominal), fixedCurrency_(fixedCurrency),
    fixedSchedule_(fixedSchedule), fixedRate_(fixedRate), fixedDayCount_(fixedDayCount),
    fixedPaymentBdc_(fixedPaymentBdc), fixedPaymentLag_(fixedPaymentLag), fixedPaymentCalendar_(fixedPaymentCalendar),
    floatCurrency_(floatCurrency), floatSchedule_(floatSchedule), floatIndex_(floatIndex),
    floatSpread_(floatSpread), floatPaymentBdc_(floatPaymentBdc),
    floatPaymentLag_(floatPaymentLag), floatPaymentCalendar_(floatPaymentCalendar),
    fxIndex_(fxIdx), resetsOnFloatLeg_(resetsOnFloatLeg), receiveFixed_(receiveFixed) {

    registerWith(floatIndex_);
    registerWith(fxIndex_);
    initialize();
}

void CrossCcyFixFloatMtMResetSwap::initialize() {

    // if resets on floating leg, set notional to zero, else the fixed
    Real floatNotional, fixedNotional;
    if (resetsOnFloatLeg_) {
        floatNotional = 0.0;
        fixedNotional = nominal_;
    } else {
        floatNotional = nominal_;
        fixedNotional = 0.0;
    }

    // Build the float leg
    Leg floatLeg = IborLeg(floatSchedule_, floatIndex_)
        .withNotionals(floatNotional)
        .withSpreads(floatSpread_)
        .withPaymentAdjustment(floatPaymentBdc_)
        .withPaymentLag(floatPaymentLag_)
        .withPaymentCalendar(floatPaymentCalendar_);

    // Register with each floating rate coupon
    for (Leg::const_iterator it = floatLeg.begin(); it < floatLeg.end(); ++it)
        registerWith(*it);

    // Build the fixed rate leg
    Leg fixedLeg = FixedRateLeg(fixedSchedule_)
        .withNotionals(fixedNotional)
        .withCouponRates(fixedRate_, fixedDayCount_)
        .withPaymentAdjustment(fixedPaymentBdc_)
        .withPaymentLag(fixedPaymentLag_)
        .withPaymentCalendar(fixedPaymentCalendar_);

    if (resetsOnFloatLeg_) {
        // Notional exchanges for fixed leg
        // Initial notional exchange
        Date aDate = fixedSchedule_.dates().front();
        aDate = fixedPaymentCalendar_.adjust(aDate, fixedPaymentBdc_);
        boost::shared_ptr<CashFlow> aCashflow = boost::make_shared<SimpleCashFlow>(-fixedNotional, aDate);
        fixedLeg.insert(fixedLeg.begin(), aCashflow);

        // Final notional exchange
        aDate = fixedLeg.back()->date();
        aCashflow = boost::make_shared<SimpleCashFlow>(fixedNotional, aDate);
        fixedLeg.push_back(aCashflow);

        // resetting floating leg
        for (Size j = 0; j < floatLeg.size(); ++j) {
            boost::shared_ptr<FloatingRateCoupon> coupon = boost::dynamic_pointer_cast<FloatingRateCoupon>(floatLeg[j]);
            Date fixingDate = fxIndex_->fixingCalendar().advance(coupon->accrualStartDate(),
                -static_cast<Integer>(fxIndex_->fixingDays()), Days);
            boost::shared_ptr<FloatingRateFXLinkedNotionalCoupon> fxLinkedCoupon =
                boost::make_shared<FloatingRateFXLinkedNotionalCoupon>(fixingDate, fixedNotional, fxIndex_, coupon);
            floatLeg[j] = fxLinkedCoupon;
        }

        // now build a separate leg to store the resetting notionals
        receiveFixed_ ? payer_[2] = -1.0 : payer_[2] = +1.0;
        currencies_[2] = floatCurrency_;
        for (Size j = 0; j < floatLeg.size(); j++) {
            boost::shared_ptr<Coupon> c = boost::dynamic_pointer_cast<Coupon>(floatLeg[j]);
            QL_REQUIRE(c, "Resetting XCCY - expected Coupon");
            // build a pair of notional flows, one at the start and one at the end of
            // the accrual period. Both with the same FX fixing date
            Date fixingDate = fxIndex_->fixingCalendar().advance(c->accrualStartDate(),
                -static_cast<Integer>(fxIndex_->fixingDays()), Days);
            legs_[2].push_back(boost::shared_ptr<CashFlow>(
                new FXLinkedCashFlow(c->accrualStartDate(), fixingDate, -fixedNotional, fxIndex_)));
            legs_[2].push_back(boost::shared_ptr<CashFlow>(
                new FXLinkedCashFlow(c->accrualEndDate(), fixingDate, fixedNotional, fxIndex_)));
        }
    } else {
        // Notional exchanges for floating leg
        // Initial notional exchange
        Date aDate = floatSchedule_.dates().front();
        aDate = floatPaymentCalendar_.adjust(aDate, floatPaymentBdc_);
        boost::shared_ptr<CashFlow> aCashflow = boost::make_shared<SimpleCashFlow>(-floatNotional, aDate);
        floatLeg.insert(fixedLeg.begin(), aCashflow);

        // Final notional exchange
        aDate = fixedLeg.back()->date();
        aCashflow = boost::make_shared<SimpleCashFlow>(floatNotional, aDate);
        floatLeg.push_back(aCashflow);

        // resetting fixed leg
        for (Size j = 0; j < fixedLeg.size(); ++j) {
            boost::shared_ptr<FixedRateCoupon> coupon = boost::dynamic_pointer_cast<FixedRateCoupon>(fixedLeg[j]);
            Date fixingDate = fxIndex_->fixingCalendar().advance(coupon->accrualStartDate(),
                -static_cast<Integer>(fxIndex_->fixingDays()), Days);
            boost::shared_ptr<FixedRateFXLinkedNotionalCoupon> fxLinkedCoupon =
                boost::make_shared<FixedRateFXLinkedNotionalCoupon>(fixingDate, floatNotional, fxIndex_, coupon);
            floatLeg[j] = fxLinkedCoupon;
        }

        // now build a separate leg to store the resetting notionals
        receiveFixed_ ? payer_[2] = -1.0 : payer_[2] = +1.0;
        currencies_[2] = fixedCurrency_;
        for (Size j = 0; j < fixedLeg.size(); j++) {
            boost::shared_ptr<Coupon> c = boost::dynamic_pointer_cast<Coupon>(fixedLeg[j]);
            QL_REQUIRE(c, "Resetting XCCY - expected Coupon");
            // build a pair of notional flows, one at the start and one at the end of
            // the accrual period. Both with the same FX fixing date
            Date fixingDate = fxIndex_->fixingCalendar().advance(c->accrualStartDate(),
                -static_cast<Integer>(fxIndex_->fixingDays()), Days);
            legs_[2].push_back(boost::shared_ptr<CashFlow>(
                new FXLinkedCashFlow(c->accrualStartDate(), fixingDate, -floatNotional, fxIndex_)));
            legs_[2].push_back(boost::shared_ptr<CashFlow>(
                new FXLinkedCashFlow(c->accrualEndDate(), fixingDate, floatNotional, fxIndex_)));
        }
    }

    // Deriving from cross currency swap where:
    //   First leg should hold the pay flows
    //   Second leg should hold the receive flows
    payer_[0] = -1.0;
    payer_[1] = 1.0;
    if (receiveFixed_) {
        legs_[1] = fixedLeg;
        currencies_[1] = fixedCurrency_;
        legs_[0] = floatLeg;
        currencies_[0] = floatCurrency_;
    } else {
        legs_[0] = fixedLeg;
        currencies_[0] = fixedCurrency_;
        legs_[1] = floatLeg;
        currencies_[1] = floatCurrency_;
    }

    // Register the instrument with all cashflows on each leg.
    for (Size legNo = 0; legNo < legs_.size(); legNo++) {
        Leg::iterator it;
        for (it = legs_[legNo].begin(); it != legs_[legNo].end(); ++it) {
            registerWith(*it);
        }
    }
}

void CrossCcyFixFloatMtMResetSwap::setupArguments(PricingEngine::arguments* a) const {

    CrossCcySwap::setupArguments(a);

    if (CrossCcyFixFloatMtMResetSwap::arguments* args = dynamic_cast<CrossCcyFixFloatMtMResetSwap::arguments*>(a)) {
        args->fixedRate = fixedRate_;
        args->spread = floatSpread_;
    }
}

void CrossCcyFixFloatMtMResetSwap::fetchResults(const PricingEngine::results* r) const {

    CrossCcySwap::fetchResults(r);

    // Depending on the pricing engine used, we may have CrossCcyFixFloatSwap::results
    if (const CrossCcyFixFloatMtMResetSwap::results* res = dynamic_cast<const CrossCcyFixFloatMtMResetSwap::results*>(r)) {
        // If we have CrossCcyFixFloatSwap::results from the pricing engine
        fairFixedRate_ = res->fairFixedRate;
        fairSpread_ = res->fairSpread;
    } else {
        // If not, set them to Null to indicate a calculation is needed below
        fairFixedRate_ = Null<Rate>();
        fairSpread_ = Null<Spread>();
    }

    // Calculate fair rate and spread if they are still Null here
    static Spread basisPoint = 1.0e-4;

    Size idxFixed = receiveFixed_ ? 1 : 0;
    if (fairFixedRate_ == Null<Rate>() && legBPS_[idxFixed] != Null<Real>())
        fairFixedRate_ = fixedRate_ - NPV_ / (legBPS_[idxFixed] / basisPoint);

    Size idxFloat = receiveFixed_ ? 0 : 1;
    if (fairSpread_ == Null<Spread>() && legBPS_[idxFloat] != Null<Real>())
        fairSpread_ = floatSpread_ - NPV_ / (legBPS_[idxFloat] / basisPoint);
}

void CrossCcyFixFloatMtMResetSwap::setupExpired() const {
    CrossCcySwap::setupExpired();
    fairFixedRate_ = Null<Rate>();
    fairSpread_ = Null<Spread>();
}

void CrossCcyFixFloatMtMResetSwap::arguments::validate() const {
    CrossCcySwap::arguments::validate();
    QL_REQUIRE(fixedRate != Null<Rate>(), "Fixed rate cannot be null");
    QL_REQUIRE(spread != Null<Spread>(), "Spread cannot be null");
}

void CrossCcyFixFloatMtMResetSwap::results::reset() {
    CrossCcySwap::results::reset();
    fairFixedRate = Null<Rate>();
    fairSpread = Null<Spread>();
}
} // namespace QuantExt


#include <math.h>
#include <vector>
#include "pcl_viewer.h"
#include <pcl/io/ply_io.h>
#include <pcl/common/centroid.h>
#include <pcl/common/common.h>
#include <pcl/visualization/common/common.h>
#include <boost/filesystem.hpp>
#include "pcl_octree.h"

using namespace V3D;

PCLViewer::PCLViewer(const std::string& title)
{
  m_viewer = new pcl::visualization::PCLVisualizer(title);
  m_viewer->initCameraParameters();
  m_viewer->setSize(1024,748);
  m_ptCloud = nullptr;
  m_viewer->setBackgroundColor(0.618,0.618,0.618);
}

PCLViewer::~PCLViewer()
{
  delete m_viewer;
  m_ptCloud = nullptr;
}

bool PCLViewer::IsStop() const
{
  return m_viewer->wasStopped();
}

int PCLViewer::CreateViewPort(double xmin,double ymin,double xmax,double ymax)
{
  int vp(0);
  m_viewer->createViewPort(xmin,ymin,xmax,ymax,vp);
  m_viewer->setBackgroundColor(0.618,0.618,0.618,vp);
  m_viewer->addCoordinateSystem(3,"global",vp);
  return vp;
}

double PCLViewer::BBoxRadius(const WSPointCloudPtr cloud, Eigen::Vector4f& centroid)
{
  Eigen::Vector4f min, max;
  pcl::compute3DCentroid(*cloud, centroid);
  pcl::getMinMax3D<WSPoint>(*cloud, min, max);
  double dRadius = 0.5*std::sqrt((max[0]-min[0])*(max[0]-min[0])+(max[1]-min[1])*(max[1]-min[1])+(max[2]-min[2])*(max[2]-min[2]));
  return dRadius;
}

void PCLViewer::AddPointCloud(const WSPointCloudPtr cloud, int vp)
{

  m_ptCloud = cloud;
  std::string name("cloud");
  name.append(std::to_string(vp));
  m_viewer->removePointCloud(name,vp);
  m_viewer->addPointCloud<WSPoint>(m_ptCloud,name,vp);
  m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,1,name,vp);

  // set camera position
  Eigen::Vector4f centroid;
  double dRadius = BBoxRadius(cloud, centroid);
  m_viewer->setCameraPosition(-1.2*dRadius,-1.2*dRadius,1.2*dRadius,centroid[0],centroid[1],centroid[2],0,0,1,vp);
}

void PCLViewer::AddNormals(const WSPointCloudPtr cloud, const WSPointCloudNormalPtr normal, int size, double arrow, int vp)
{
  std::string name("normal");
  name.append(std::to_string(vp));
  m_viewer->addPointCloudNormals<WSPoint, WSNormal>(cloud,normal,size,arrow,name,vp);
}

void PCLViewer::AddArrows(const WSPointCloudPtr cloud, const WSPointCloudNormalPtr normal, double length, int vp)
{
  std::string name("arrow");
  name.append(std::to_string(vp));
  for (size_t i = 0; i < cloud->points.size(); ++i)
  {
    WSPoint pt = cloud->points[i];
    WSNormal nm = normal->points[i];
    Eigen::Vector3f vec(nm.normal_x,nm.normal_y,nm.normal_z);
    Eigen::Vector3f pt1(pt.x,pt.y,pt.z);
    Eigen::Vector3f nvc = vec.normalized();
    Eigen::Vector3f pt2 = pt1 + length*nvc;
    WSPoint pte;
    pte.x = pt2[0];
    pte.y = pt2[1];
    pte.z = pt2[2];
    name.append(std::to_string(i));
    m_viewer->addArrow(pt,pte,0.0,1.0,0.0,false,name,vp);
  }
}

void PCLViewer::AddArrow(const WSPoint& startPt, const WSPoint& endPt, const std::string& name, int vp)
{
  m_viewer->addArrow(startPt,endPt,1.0,0.0,0.0,false,name,vp);
}

void PCLViewer::AddMesh(const pcl::PolygonMesh& mesh, double dRadius, const Eigen::Vector4f& centroid, int vp)
{
  if (!m_viewer->updatePolygonMesh(mesh, "mesh"))
  {
    m_viewer->addPolygonMesh(mesh, "mesh");
    m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "mesh");
    m_viewer->setCameraPosition(-1.2*dRadius,-1.2*dRadius,1.2*dRadius,centroid[0],centroid[1],centroid[2],0,0,1,vp);
  }
}

void PCLViewer::AddCoordinateSystem(const Eigen::Affine3f& camPose, int idIndex, int vp, bool removeall)
{
  std::string name("ccs");
  name.append(std::to_string(vp));
  name.append(std::to_string(idIndex));
  m_viewer->addCoordinateSystem(0.5,camPose,name,vp);
}

void PCLViewer::SpinOnce(double duration)
{
  m_viewer->spinOnce(duration);
}

void PCLViewer::Spin() const
{
  m_viewer->spin();
}

WSPointCloudPtr PCLViewer::PointCloud()
{
  return m_ptCloud;
}

WSPointCloudPtr PCLViewer::LoadPointCloud(const std::string& dir)
{
  WSPointCloudPtr cloud(new WSPointCloud());

  std::vector<std::string> allfiles;
  boost::filesystem::directory_iterator itr(dir);
  for (; itr != boost::filesystem::directory_iterator(); ++itr)
  {
    if (boost::filesystem::is_regular_file(itr->status()));
      allfiles.push_back(itr->path().string());
  }

  int all = allfiles.size();
  if (all == 0)
    return false;

  int i = 0;

  for (const auto& file : allfiles)
  {
    i++;
    std::cout << "pcl == load " << i << "/" << all << " point cloud from " << file << std::endl;
    WSPointCloudPtr temp(new WSPointCloud());
    int res = pcl::io::loadPLYFile(file, *temp);
    if(res < 0)
        std::cout << "pcl == failed to load point cloud." << std::endl;
    *cloud += *temp;
   }

   return cloud;
}

bool PCLViewer::SavePointCloud(const WSPointCloudPtr cloud, const std::string& dir)
{
  if (cloud == nullptr)
    return false;

  std::string filePath = dir+"point_cloud.ply";
  int res = pcl::io::savePLYFileASCII(filePath, *cloud);
  if (res >= 0)
    std::cout << "save as " << filePath << std::endl;
  return res >= 0;
}

void PCLViewer::AddCube(const WSPoint& point, double s, const std::string& cubeName, double r,double g, double b, int vp)
{
  m_viewer->addCube(point.x-s,point.x+s,point.y-s,point.y+s,point.z-s,point.z+s,r,g,b,cubeName,vp);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, 1, cubeName);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.2, cubeName);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 2, cubeName);
}

void PCLViewer::AddText(const std::string& text, const std::string& id, int vp)
{
    m_viewer->addText(text,50,50,30,0.0,0.0,0.0,id,vp);
}

void PCLViewer::AddLine(const WSPoint& startPt, const WSPoint& endPt, int idx, double r,double g, double b, int vp)
{
  std::string id("line_");
  id.append(std::to_string(idx));
  m_viewer->addLine(startPt,endPt,r,g,b,id,vp);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 2, id);
}

void PCLViewer::AddPolygon(const WSPointCloudPtr& polygon, const std::string& id, double r, double g, double b, int vp)
{
  m_viewer->addPolygon<WSPoint>(polygon,r,g,b,id,vp);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, 1, id);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.2, id);
  m_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 2, id);
}


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/U.B.P.
//         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 BOOST_SIMD_ARITHMETIC_FUNCTIONS_SQRS_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_SQRS_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

namespace boost { namespace simd { namespace tag
  {
   /*!
      @brief  sqrs generic tag

      Represents the sqrs function in generic contexts.

      @par Models:
      Hierarchy
    **/
    struct sqrs_ : ext::elementwise_<sqrs_>
    {
      /// @brief Parent hierarchy
      typedef ext::elementwise_<sqrs_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_sqrs_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::sqrs_, Site> dispatching_sqrs_(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::sqrs_, Site>();
   }
   template<class... Args>
   struct impl_sqrs_;
  }
  /*!
    Computes the saturated square of its parameter.

    @par semantic:
    For any given value @c x of type @c T:

    @code
    T r = sqrs(x);
    @endcode

    is similar to:

    @code
    T r = x*x > Valmax ? Valmax : x*x;
    @endcode

    @par Alias

    @c sqrs, saturated_sqr

    @param  a0

    @return      a value of the same type as the input.

  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::sqrs_, sqrs, 1)
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::sqrs_, saturated_sqr, 1)
} }

#endif


// FILE: src/injection_process.cpp  Date Modified: March 17, 2015
// PacketGenie
// Copyright 2014 The Regents of the University of Michigan
// Dong-Hyeon Park, Ritesh Parikh, Valeria Bertacco
//
// PacketGenie is 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 "injection_process.hpp"
#include <cstdlib>
#include <boost/lexical_cast.hpp>
#include <math.h>

InjectionProcess * InjectionProcess::New(vector<string> *str_params)
{
    int error;
    // Injection Process ID is the first element
    string id = str_params->at(0);

    // Process type is the second element
    string process_type = str_params->at(1);

#if _DEBUG
    cout << "Injection Proces: id=" << id << ", "
         << "process_type=" << process_type << endl;
#endif

    InjectionProcess *inj_proc;

    if (process_type == "uniform")
        inj_proc = new InjectionProcessUniform(id, process_type);
    else if (process_type == "burst")
        inj_proc = new InjectionProcessBurst(id, process_type);
    else if (process_type == "gaussian")
        inj_proc = new InjectionProcessGaussian(id, process_type);
    else if (process_type == "onoff")
        inj_proc = new InjectionProcessOnOff(id, process_type);
    else
        inj_proc = NULL;

    if (inj_proc != NULL)
        error = inj_proc->parse_params(str_params);

    if (error)
        return NULL;
    else
        return inj_proc;

}

InjectionProcess::~InjectionProcess()
{

}

void InjectionProcess::init(int group_size)
{
      m_grp_size = group_size;
      m_ready = true;
}

// Get the per-cycle injection rate of a network at current cycle.
double InjectionProcess::get_inj_rate(long cycle)
{
    assert(m_ready);
    return 0;
}

int InjectionProcess::parse_params(vector<string> *str_params)
{
    return SUCCESS;
}

// InjectionProcess - Uniform
double InjectionProcessUniform::get_inj_rate(long cycle)
{
    assert(m_ready);
    if ( (m_end != 0) && (cycle > m_end))
	return 0;
    else if (cycle >= m_start)
    	return m_rate;
    else
	return 0;
}

int InjectionProcessUniform::parse_params(vector<string> *str_params)
{
    // Uniform should have had three parameters
    //   <inj_id> <type> <inj_rate>
    if ( str_params->size() < 3)
        return ERROR;

    m_rate = boost::lexical_cast<double>(str_params->at(2));

    if ( str_params->size() > 4 )
    {
	if (str_params->at(3) == "start")
    	    m_start = boost::lexical_cast<size_t>(str_params->at(4));

	if (str_params->at(5) == "end")
    	    m_end = boost::lexical_cast<size_t>(str_params->at(6));

    }

    return SUCCESS;

}

// InjectionProcess - Burst
double InjectionProcessBurst::get_inj_rate(long cycle)
{
    assert(m_ready);

    size_t rcycle;

    if ( (m_end != 0) && (cycle > m_end))
	return 0;
    else if (cycle >= m_start)
	rcycle = cycle - m_start;
    else
	return 0;

    long cycle_pos = rcycle % m_period;

    if (m_start_low)
        cycle_pos = m_period - cycle_pos;

    if ( cycle_pos  <= m_cycle_high )
        return m_rate_high;
    else
        return m_rate_base;

}

int InjectionProcessBurst::parse_params(vector<string> *str_params)
{
    // Burst should have had seven parameters (may also have empty strings at end)
    //   <inj_id> <type> <period> <dut_cycle> <start_low> <rate_base> <rate_high>
    //   <inj_id> <type> <period> <dut_cycle> <start_low> <rate_base> <rate_high> start <start> end < end>
    if ( str_params->size() < 7)
        return ERROR;

    m_period = boost::lexical_cast<long>(str_params->at(2));

    double dut =  boost::lexical_cast<double>(str_params->at(3));
    m_cycle_high = (dut * m_period);

    string token = str_params->at(4);
    if (token == "low" || token == "L")
        m_start_low = true;
    else if (token == "high" || token == "H")
        m_start_low = false;
    else
        return ERROR;

    m_rate_base = boost::lexical_cast<double>(str_params->at(5));
    m_rate_high = boost::lexical_cast<double>(str_params->at(6));

    if ( (str_params->size() > 7))
    {
	if (str_params->at(7) == "start")
    	    m_start = boost::lexical_cast<long>(str_params->at(8));

	if (str_params->at(9) == "end")
    	    m_end = boost::lexical_cast<long>(str_params->at(10));

    }

    return SUCCESS;
}

// InjectionProcess - Gaussian
double InjectionProcessGaussian::get_inj_rate(long cycle)
{
    assert(m_ready);
    // Compensate for offset and periodic behavior.
    // Center around the period.
    size_t rcycle;
    if ( (m_end != 0) && (cycle > m_end))
	return 0;
    else if (cycle >= m_start)
	rcycle = cycle - m_start;
    else
	return 0;

    double x_pos = ((rcycle - m_offset) % m_period);
    if (x_pos > (m_period/2.0))
        x_pos = m_period - x_pos;

    // Gaussian function:
    // f(x,sigma) = A * exp^(-(x^2)/(2*sigma^2))
    return m_rate_peak *  exp(-1.0 * pow(x_pos,2) * inv_twovar);

}

int InjectionProcessGaussian::parse_params(vector<string> *str_params)
{
    // Burst should have had seven parameters (may also have empty strings at end)
    //   <inj_id> <type> <period> <start_offset> <rate_peak> <std_dev>
    if ( str_params->size() < 6)
        return ERROR;

    m_period = boost::lexical_cast<long>(str_params->at(2));

    // Offset should not exceed the period.
    m_offset = ( boost::lexical_cast<long>(str_params->at(3)) ) % m_period;

    m_rate_peak = boost::lexical_cast<double>(str_params->at(4));
    m_std_dev = boost::lexical_cast<double>(str_params->at(5));

    inv_twovar = 1.0 / (2 * pow(m_std_dev, 2.0));

    if ( (str_params->size() > 6))
    {
	if (str_params->at(6) == "start")
    	    m_start = boost::lexical_cast<long>(str_params->at(7));

	if (str_params->at(8) == "end")
    	    m_end = boost::lexical_cast<long>(str_params->at(9));
    }
    return SUCCESS;
}

// InjectionProcess - OnOff
void InjectionProcessOnOff::init(int group_size)
{
      m_grp_size = group_size;
      m_ready = true;

      m_counter = group_size-1;
      m_prev_cyc = -1; // Not started yet

      if (m_init_state == "all_on")
      {
           m_state =  vector<int>(m_grp_size, 1);
      }
      else if (m_init_state == "all_off")
      {
           m_state =  vector<int>(m_grp_size, 0);
      }
      else if (m_init_state == "random")
      {
           for (int i = 0; i < m_grp_size; ++i)
                m_state.push_back(rand()%2);
      }
      else
          assert(false);

}

double InjectionProcessOnOff::get_inj_rate(long cycle)
{
    assert(m_ready);
    assert(cycle >= 0);

    ++m_counter;

    if (cycle != m_prev_cyc)
    {
        assert(m_counter==m_grp_size);
        m_prev_cyc = cycle;
        m_counter = 0;
    }

    assert(m_counter < m_grp_size);

    // Test for state change.
    if (m_state[m_counter]) // in On State
        m_state[m_counter] = get_rand_decimal() >= m_beta;
    else // in Off State
        m_state[m_counter] = get_rand_decimal() < m_alpha;

    if (m_state[m_counter]) // On state
        return m_r1;
    else
        return 0;
}

int InjectionProcessOnOff::parse_params(vector<string> *str_params)
{
    // Burst should have had seven parameters (may also have empty strings at end)
    //   <inj_id> <type> <alpha> <beta> <r1> <initial_state>
    if ( str_params->size() < 6)
        return ERROR;

    m_alpha = boost::lexical_cast<double>(str_params->at(2));
    m_beta  = boost::lexical_cast<double>(str_params->at(3));
    m_r1 = boost::lexical_cast<double>(str_params->at(4));

    m_init_state = str_params->at(5);

    if (m_init_state == "all_on"
        || m_init_state == "all_off"
        || m_init_state == "random" )
            return SUCCESS;

    return ERROR;
}


//==================================================================================================
/**
  Copyright 2017 NumScale SAS

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

#ifndef BOOST_SIMD_FUNCTION_SIMD_COT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SIMD_COT_HPP_INCLUDED

#include <boost/simd/function/scalar/cot.hpp>
#include <boost/simd/arch/common/generic/function/autodispatcher.hpp>
#include <boost/simd/arch/common/simd/function/cot.hpp>

#endif


#ifndef __CAMERAGL_INCLUDE__
#define __CAMERAGL_INCLUDE__

#include <Eigen/Core>
#include <Eigen/Eigen>

#include "utils.hpp"

class CameraGL {
public:

    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

        enum DIRECTION {
        UP, DOWN, LEFT, RIGHT, FORWARD, BACK
    };

    CameraGL(Eigen::Vector3f position, Eigen::Vector3f direction, Eigen::Vector3f vertical = Eigen::Vector3f(0, 1, 0));
    ~CameraGL();

    void update();
    void setProjection(float horizontalFOV, float verticalFOV, float znear, float zfar);
    const Eigen::Matrix4f& getViewProjectionMatrix() const;

    float getHorizontalFOV() const;
    float getVerticalFOV() const;

    /*
            Set an offset between the eye of the camera and its position.
            Note: Useful to use the camera as a trackball camera with z>0 and x = 0, y = 0.
            Note: coordinates are in local space.
     */
    void setOffsetFromPosition(const Eigen::Vector3f& o);
    const Eigen::Vector3f& getOffsetFromPosition() const;

    void setDirection(const Eigen::Vector3f& direction, const Eigen::Vector3f& vertical = Eigen::Vector3f(0, 1, 0));
    void translate(const Eigen::Vector3f& t);
    void setPosition(const Eigen::Vector3f& p);
    void rotate(const Eigen::Quaternionf& rot);
    void rotate(const Eigen::Matrix3f& m);
    void setRotation(const Eigen::Quaternionf& rot);
    void setRotation(const Eigen::Matrix3f& m);

    const Eigen::Vector3f& getPosition() const;
    const Eigen::Vector3f& getForward() const;
    const Eigen::Vector3f& getRight() const;
    const Eigen::Vector3f& getUp() const;
    const Eigen::Vector3f& getVertical() const;
    float getZNear() const;
    float getZFar() const;

    static const Eigen::Vector3f ORIGINAL_FORWARD;
    static const Eigen::Vector3f ORIGINAL_UP;
    static const Eigen::Vector3f ORIGINAL_RIGHT;

    Eigen::Matrix4f projection_;
private:
    void updateVectors();
    void updateView();
    void updateVPMatrix();

    Eigen::Vector3f offset_;

    Eigen::Vector3f position_;
    Eigen::Vector3f forward_;
    Eigen::Vector3f up_;
    Eigen::Vector3f right_;
    Eigen::Vector3f vertical_;

    Eigen::Quaternionf rotation_;

    Eigen::Matrix4f view_;
    Eigen::Matrix4f vpMatrix_;
    float horizontalFieldOfView_;
    float verticalFieldOfView_;
    float znear_;
    float zfar_;
};

#endif


//
// sample-estimator.cpp
//
//  Created by Peter Gusev on 06 June 2016.
//  Copyright 2013-2016 Regents of the University of California
//

#include "sample-estimator.hpp"
#include <boost/assign.hpp>

#include "estimators.hpp"
#include "frame-data.hpp"
#include "statistics.hpp"

using namespace ndnrtc;
using namespace ndnrtc::statistics;
using namespace estimators;

//******************************************************************************
SampleEstimator::Estimators::_Estimators():
segNum_(Average(std::make_shared<SampleWindow>(30))),
segSize_(Average(std::make_shared<SampleWindow>(30)))
{}

SampleEstimator::Estimators::~_Estimators()
{}

//******************************************************************************
SampleEstimator::SampleEstimator(const std::shared_ptr<statistics::StatisticsStorage>& storage):
sstorage_(storage)
{
	reset();
}

SampleEstimator::~SampleEstimator()
{}

void
SampleEstimator::bootstrapSegmentNumber(double value, SampleClass st, SegmentClass dt)
{
    estimators_[std::make_pair(st,dt)].segNum_.newValue((value > 0 ? value : 1.) );
}

void SampleEstimator::bootstrapSegmentSize(double value, SampleClass st, SegmentClass dt)
{
    estimators_[std::make_pair(st,dt)].segSize_.newValue((value > 0 ? value : 1000.));
}

void
SampleEstimator::segmentArrived(const std::shared_ptr<WireSegment>& segment)
{
    if (segment->getSampleClass() == SampleClass::Key ||
        segment->getSampleClass() == SampleClass::Delta)
    {
        SampleClass st = segment->getSampleClass();
        SegmentClass dt = segment->getSegmentClass();

        estimators_[std::make_pair(st,dt)].segNum_.newValue(segment->getSlicesNum());
        estimators_[std::make_pair(st,dt)].segSize_.newValue(segment->getData()->getContent().size());

        if (st == SampleClass::Delta)
        {
            if (dt == SegmentClass::Data)
                (*sstorage_)[Indicator::SegmentsDeltaAvgNum] = segment->getSlicesNum();
            else
                (*sstorage_)[Indicator::SegmentsDeltaParityAvgNum] = segment->getSlicesNum();
        }
        else
        {
            if (dt == SegmentClass::Data)
                (*sstorage_)[Indicator::SegmentsKeyAvgNum] = segment->getSlicesNum();
            else
                (*sstorage_)[Indicator::SegmentsKeyParityAvgNum] = segment->getSlicesNum();
        }
    }
}

void
SampleEstimator::reset()
{
	const EstimatorMap m = boost::assign::map_list_of
		( std::make_pair(SampleClass::Delta, SegmentClass::Data), Estimators())
		( std::make_pair(SampleClass::Delta, SegmentClass::Parity), Estimators())
		( std::make_pair(SampleClass::Key, SegmentClass::Data), Estimators())
		( std::make_pair(SampleClass::Key, SegmentClass::Parity), Estimators());
	estimators_ = m;

    (*sstorage_)[Indicator::SegmentsDeltaAvgNum] = 0;
    (*sstorage_)[Indicator::SegmentsDeltaParityAvgNum] = 0;
    (*sstorage_)[Indicator::SegmentsKeyAvgNum] = 0;
    (*sstorage_)[Indicator::SegmentsKeyParityAvgNum] = 0;
}

double
SampleEstimator::getSegmentNumberEstimation(SampleClass st, SegmentClass dt)
{
	return estimators_[std::make_pair(st,dt)].segNum_.value();
}

double
SampleEstimator::getSegmentSizeEstimation(SampleClass st, SegmentClass dt)
{
	return estimators_[std::make_pair(st,dt)].segSize_.value();
}

#pragma mark - private


#include <boost/crc.hpp>
#include "node_id.h"

using namespace ouinet::bittorrent;
using namespace std;

static bool get_rbit(const NodeID::Buffer& buffer, size_t n) {
    return (buffer[n / CHAR_BIT] & (1 << (CHAR_BIT - 1 - (n % CHAR_BIT)))) != 0;
}

static void set_rbit(NodeID::Buffer& buffer, size_t n, bool value) {
    char bit = 1 << (CHAR_BIT - (n % CHAR_BIT) - 1);

    if (value) buffer[n / CHAR_BIT] |=  bit;
    else       buffer[n / CHAR_BIT] &= ~bit;
}

static const NodeID::Buffer& zero_buffer() {
    // zero-initialized by static linkage
    static NodeID::Buffer buf;
    return buf;
}

bool NodeID::bit(int n) const
{
    return get_rbit(buffer, n);
}

void NodeID::set_bit(int n, bool value)
{
    set_rbit(buffer, n, value);
}

const NodeID::Range& NodeID::Range::max()
{
    static Range max_range{ zero_buffer(), 0 };
    return max_range;
}

NodeID NodeID::Range::random_id() const
{
    // XXX: Use std::uniform_int_distribution instead of std::rand

    size_t s_bytes = mask / CHAR_BIT;
    size_t s_bits  = mask % CHAR_BIT;

    NodeID ret;

    for (size_t i = 0; i < ret.buffer.size(); i++) {
        if (i < s_bytes) {
            ret.buffer[i] = stencil[i];
        }
        else if (i > s_bytes) {
            ret.buffer[i] = rand() & 0xff;
        }
        else {
            ret.buffer[i] = (stencil[i] & ((0xff << (CHAR_BIT - s_bits)) & 0xff))
                          | (rand() & ((1 << (CHAR_BIT - s_bits)) - 1));
        }
    }

    return ret;
}

NodeID::Range NodeID::Range::reduce(bool bit) const {
    Range ret{stencil, mask};
    ++ret.mask;
    set_rbit(ret.stencil, ret.mask, bit);
    return ret;
}

/* static */
NodeID NodeID::zero()
{
    return NodeID{ zero_buffer() };
}

/* static */
NodeID NodeID::max()
{
    return NodeID{ Buffer{ 0xff, 0xff, 0xff, 0xff
                         , 0xff, 0xff, 0xff, 0xff
                         , 0xff, 0xff, 0xff, 0xff
                         , 0xff, 0xff, 0xff, 0xff
                         , 0xff, 0xff, 0xff, 0xff } };
}

NodeID NodeID::generate(asio::ip::address address)
{
    return generate(address, boost::none);
}

NodeID NodeID::generate( asio::ip::address address
                       , boost::optional<uint8_t> test_rnd)
{
    /*
     * Choose DHT ID based on ip address.
     * See: BEP 42
     */

    NodeID node_id;

    using CRC32C = boost::crc_optimal<32, 0x1edc6f41, 0xffffffff, 0xffffffff, true, true>;

    uint32_t checksum;

    node_id.buffer[19] = (test_rnd ? *test_rnd : std::rand()) & 0xff;

    if (address.is_v4()) {
        auto ip_bytes = address.to_v4().to_bytes();

        for (int i = 0; i < 4; i++) {
            ip_bytes[i] &= (0xff >> (6 - i * 2));
        }

        ip_bytes[0] |= ((node_id.buffer[19] & 7) << 5);

        CRC32C crc;
        crc.process_bytes(ip_bytes.data(), 4);
        checksum = crc.checksum();
    } else {
        auto ip_bytes = address.to_v6().to_bytes();

        for (int i = 0; i < 8; i++) {
            ip_bytes[i] &= (0xff >> (7 - i));
        }

        ip_bytes[0] |= ((node_id.buffer[19] & 7) << 5);

        CRC32C crc;
        crc.process_bytes(ip_bytes.data(), 8);
        checksum = crc.checksum();
    }

    node_id.buffer[0] = (checksum >> 24) & 0xff;
    node_id.buffer[1] = (checksum >> 16) & 0xff;
    node_id.buffer[2] = ((checksum >> 8) & 0xf8) | (rand() & 0x7);
    for (int i = 3; i < 19; i++) {
        node_id.buffer[i] = rand() & 0xff;
    }

    return node_id;
}

bool NodeID::closer_to(const NodeID& left, const NodeID& right) const
{
    for (size_t i = 0; i < sizeof(buffer); i++) {
        uint8_t l = left .buffer[i] ^ buffer[i];
        uint8_t r = right.buffer[i] ^ buffer[i];
        if (l < r) {
            return true;
        }
        if (r < l) {
            return false;
        }
    }
    return false;
}

NodeID NodeID::operator^(const NodeID& other) const
{
    NodeID ret;
    for (size_t i = 0; i < sizeof(buffer); i++) {
        ret.buffer[i] = buffer[i] ^ other.buffer[i];
    }
    return ret;
}

NodeID NodeID::distance_to(const NodeID& other) const
{
    NodeID ret = NodeID::zero();
    for (size_t i = 0; i < sizeof(buffer); i++) {
        ret.buffer[i] = buffer[i] ^ other.buffer[i];
    }
    return ret;
}

string NodeID::to_bitstr() const
{
    string ret;
    for (size_t i = 0; i < bit_size; ++i) {
        ret.push_back(bit(i) ? '1' : '0');
    }
    return ret;
}

ostream& ouinet::bittorrent::operator<<(ostream& os, const NodeID& id)
{
    return os << id.to_hex();
}

ostream& ouinet::bittorrent::operator<<(ostream& os, const NodeID::Range& r)
{
    return os << r.mask << ":" << util::bytes::to_hex(r.stencil);
}


#include <cassert>
#include <chrono>
#include <deque>
#include <iostream>
#include <set>
#include <unordered_set>
#include <utility> // pair

#include <boost/dynamic_bitset.hpp>
#include <sdd/order/strategies/force.hh>

#include "mc/units/concurrent_units.hh"
#include "mc/units/nopost_live.hh"
#include "mc/units/post.hh"
#include "mc/units/pre.hh"
#include "mc/units/sdd.hh"
#include "mc/units/worker.hh"

#include "compress/compress.hh"

namespace caesar { namespace mc { namespace units {

namespace chrono = std::chrono;

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

order
mk_order(const conf::caesar_configuration& conf, const pn::net& net)
{
  std::vector<unsigned int> units;
  units.reserve(net.units().size());

  std::unordered_set<unsigned int> units_added;
  units_added.reserve(net.units().size());

  // Iteration on places because we want to put units in the same order as the places they
  // contain.
  for (const auto& p : net.places())
  {
    if (not net.places_of_unit(p.unit).empty()) // Don't add empty units.
    {
      if (units_added.emplace(p.unit).second) // Check if unit has already been added.
      {
        units.push_back(p.unit);
      }
    }
  }

  if (not conf.order_force)
  {
    return {order_builder(units.begin(), units.end())};
  }
  else
  {
    // The hypergraph that stores connections between the places of the Petri net.
    sdd::force::hypergraph<sdd_conf> graph(units.cbegin(), units.cend());

    std::vector<unsigned int> identifiers;
    std::unordered_set<unsigned int> identifiers_added;
    for (const auto& transition : net.transitions())
    {
      // First check if we should add this transition to the hypergraph.
      // We don't care about transitions that only initialize the Petri net.
      bool add_transition = false;
      for (const auto& arc : transition.pre)
      {
        const auto& place = *net.places_by_id().find(arc.first);
        if ((not place.initial()) or (place.initial() and not place.pre.empty()))
        {
          add_transition = true;
          break;
        }
      }

      if (not add_transition)
      {
        continue;
      }

      for (const auto& arc : transition.pre)
      {
        const auto u = net.unit_of_place(arc.first);
        if (identifiers_added.emplace(arc.first).second)
        {
          identifiers.push_back(u);
        }
      }

      for (const auto& arc : transition.post)
      {
        const auto u = net.unit_of_place(arc.first);
        if (identifiers_added.emplace(arc.first).second)
        {
          identifiers.push_back(u);
        }
      }
      graph.add_hyperedge(identifiers.cbegin(), identifiers.cend());

      // We use this container again in the next loop.
      identifiers.clear();
      identifiers_added.clear();
    }
    // Apply the FORCE ordering strategy.
    auto force = sdd::force::worker<sdd_conf>(graph);
    return force(200);
  }
}

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

SDD
initial_state(const order& o, const pn::net& net)
{
  using flat_set = sdd::values::flat_set<unsigned int>;
  return SDD(o, [&](unsigned int unit) -> flat_set
                   {
                     std::vector<unsigned int> marked_places;
                     for (const pn::place& p : net.places_of_unit(unit))
                     {
                       if (p.initial())
                       {
                         marked_places.push_back(p.id);
                       }
                     }
                     if (marked_places.size() == 0)
                     {
                       return flat_set({0});
                     }
                     else if (marked_places.size() == 1)
                     {
                       return flat_set({marked_places[0] + 1});
                     }
                     else
                     {
                       throw std::runtime_error("More that one initial place in an unit.");
                     }
                   }
            );
}

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

homomorphism
transition_relation( const conf::caesar_configuration& conf, const order& o
                   , const pn::net& net, boost::dynamic_bitset<>& transitions_bitset)
{
  chrono::time_point<chrono::system_clock> start;
  chrono::time_point<chrono::system_clock> end;
  std::size_t elapsed;

  start = chrono::system_clock::now();
  std::set<homomorphism> operands;
  operands.insert(id<sdd_conf>());

  // When computing dead transitions, we need to start numbering them from 0 as we use this
  // value as an index in the bitset.
  unsigned int tindex = 0;

  for (const auto& transition : net.transitions())
  {
    homomorphism h_t = id<sdd_conf>();

    if (transition.post.empty() and transition.pre.empty() and conf.compute_dead_transitions)
    {
      // Empty transition, but we need to increment the transition counter.
      transitions_bitset[tindex++] = true;
    }
    else if (not transition.post.empty())
    {
      // post actions.

      // We need to create a special homomorphism for the first arc in order to detect live
      // transitions if requirerd by the configuration.
      auto arc_cit = transition.post.begin();
      if (conf.compute_dead_transitions)
      {
        const auto f = conf.check_one_safe
                     ? function( o, net.unit_of_place(arc_cit->first)
                               , post_live_safe{tindex++, transitions_bitset, arc_cit->first})
                     : function( o, net.unit_of_place(arc_cit->first)
                               , post_live{tindex++, transitions_bitset, arc_cit->first});
        h_t = composition(h_t, sdd::carrier(o, net.unit_of_place(arc_cit->first), f));

        ++arc_cit;
      }

      for (; arc_cit != transition.post.end(); ++arc_cit)
      {
        const auto f = conf.check_one_safe
                     ? function(o, net.unit_of_place(arc_cit->first), post_safe{arc_cit->first})
                     : function(o, net.unit_of_place(arc_cit->first), post{arc_cit->first});
        h_t = composition(h_t, sdd::carrier(o, net.unit_of_place(arc_cit->first), f));
      }
    }
    else if (conf.compute_dead_transitions)
    {
      // Sink transition : Card(Pre) > 0 and Card(Post) = 0
      // Target the same variable as the pre that is fired just before this fake post.
      const auto unit = net.unit_of_place(transition.pre.begin()->first);
      const auto f = function(o, unit, nopost_live{tindex++, transitions_bitset});

      h_t = composition(h_t, sdd::carrier(o, unit, f));
    }

    // pre actions.
    for (const auto& arc : transition.pre)
    {
      homomorphism f = function(o, net.unit_of_place(arc.first), pre{arc.first});
      h_t = composition(h_t, sdd::carrier(o, net.unit_of_place(arc.first), f));
    }

    operands.insert(h_t);
  }
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Transition relation time: " << elapsed << "s\n";
  }

  start = chrono::system_clock::now();
  const auto res = sdd::rewrite(o, fixpoint(sum(o, operands.cbegin(), operands.cend())));
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Rewrite time: " << elapsed << "s\n";
  }

  return res;
}

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

SDD
state_space( const conf::caesar_configuration& conf, const order& o, SDD m
           , homomorphism h)
{
  chrono::time_point<chrono::system_clock> start = chrono::system_clock::now();
  const auto res = h(o, m);
  chrono::time_point<chrono::system_clock> end = chrono::system_clock::now();
  const std::size_t elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();
  if (conf.show_time)
  {
    std::cout << "State space computation time: " << elapsed << "s\n";
  }
  return res;
}

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

worker::worker(const conf::caesar_configuration& c)
  : conf(c)
{}

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

void
worker::operator()(const pn::net& net)
const
{
  sdd_conf sconf;
  sconf.hom_cache_size = 16000000;
  sconf.sdd_sum_cache_size = 16000000;
  auto manager = sdd::init<sdd_conf>(sconf);
  boost::dynamic_bitset<> transitions_bitset(net.transitions().size());

  const auto o = mk_order(conf, net);
  assert(not o.empty() && "Empty order");

  if (conf.order_show)
  {
    std::cout << o << std::endl;
  }

  const SDD m0 = initial_state(o, net);
  const homomorphism h = transition_relation(conf, o, net, transitions_bitset);

  const SDD m = state_space(conf, o, m0, h);

  if (conf.check_one_safe)
  {
    std::cout << "Petri net is 1-safe\n";
  }

  if (conf.show_nb_states)
  {
    std::cout << m.size().template convert_to<long double>() << " states\n";
  }

  if (conf.compute_dead_transitions and net.transitions().size())
  {
    compress compressor;
    for (std::size_t i = 0; i < net.transitions().size(); ++i)
    {
      compressor.dump_compression(transitions_bitset[i] ? '0' : '1');
    }
    compressor.dump_compression('\n');
  }

  if (conf.compute_concurrent_units)
  {
    compute_concurrent_units(conf, net, o, m);
  }
}

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

}}} // namespace caesar::mc::units