// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"
#include "graph_exceptions.hh"

#include <boost/lambda/bind.hpp>

#include "graph_sfdp.hh"
#include "random.hh"

using namespace std;
using namespace boost;
using namespace graph_tool;

void sfdp_layout(GraphInterface& g, boost::any pos, boost::any vweight,
                 boost::any eweight, boost::any pin, python::object spring_parms,
                 double theta, double init_step, double step_schedule,
                 size_t max_level, double epsilon, size_t max_iter,
                 bool adaptive, bool verbose, rng_t& rng)
{
    typedef ConstantPropertyMap<int32_t,GraphInterface::vertex_t> vweight_map_t;
    typedef ConstantPropertyMap<int32_t,GraphInterface::edge_t> eweight_map_t;
    typedef mpl::push_back<vertex_scalar_properties, vweight_map_t>::type
        vertex_props_t;
    typedef mpl::push_back<edge_scalar_properties, eweight_map_t>::type
        edge_props_t;

    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        group_map_t;

    double C = python::extract<double>(spring_parms[0]);
    double K = python::extract<double>(spring_parms[1]);
    double p = python::extract<double>(spring_parms[2]);
    double gamma = python::extract<double>(spring_parms[3]);
    double mu = python::extract<double>(spring_parms[4]);
    double mu_p = python::extract<double>(spring_parms[5]);
    group_map_t groups =
        any_cast<group_map_t>(python::extract<any>(spring_parms[6]));

    if(vweight.empty())
        vweight = vweight_map_t(1);
    if(eweight.empty())
        eweight = eweight_map_t(1);

    typedef property_map_type::apply<uint8_t,
                                     GraphInterface::vertex_index_map_t>::type
        pin_map_t;
    pin_map_t pin_map = any_cast<pin_map_t>(pin);

    run_action<graph_tool::detail::never_directed>()
        (g,
         std::bind(get_sfdp_layout(C, K, p, theta, gamma, mu, mu_p, init_step,
                                   step_schedule, max_level, epsilon,
                                   max_iter, adaptive),
                   placeholders::_1, placeholders::_2,
                   placeholders::_3, placeholders::_4,
                   pin_map.get_unchecked(num_vertices(g.GetGraph())),
                   groups.get_unchecked(num_vertices(g.GetGraph())), verbose,
                   std::ref(rng)),
         vertex_floating_vector_properties(), vertex_props_t(), edge_props_t())
        (pos, vweight, eweight);
}

struct do_propagate_pos
{
    template <class Graph, class CoarseGraph, class VertexMap, class PosMap,
              class RNG>
    void operator()(Graph& g, CoarseGraph* cg, VertexMap vmap,
                    boost::any acvmap, PosMap pos, boost::any acpos,
                    double delta, RNG& rng) const
    {
        typename PosMap::checked_t cpos =
            any_cast<typename PosMap::checked_t>(acpos);
        typename VertexMap::checked_t cvmap =
            any_cast<typename VertexMap::checked_t>(acvmap);
        typedef typename property_traits<VertexMap>::value_type c_t;
        typedef typename property_traits<PosMap>::value_type pos_t;
        typedef typename pos_t::value_type val_t;

        uniform_real_distribution<val_t> noise(-delta, delta);
        unordered_map<c_t, pos_t> cmap(num_vertices(*cg));

        for (auto v : vertices_range(*cg))
            cmap[cvmap[v]] = cpos[v];

        for (auto v : vertices_range(g))
        {
            pos[v] = cmap[vmap[v]];

            if (delta > 0)
            {
                for (size_t j = 0; j < pos[v].size(); ++j)
                    pos[v][j] += noise(rng);
            }
        }
    }
};

struct get_pointers
{
    template <class List>
    struct apply
    {
        typedef typename mpl::transform<List,
                                        mpl::quote1<std::add_pointer> >::type type;
    };
};

void propagate_pos(GraphInterface& gi, GraphInterface& cgi, boost::any vmap,
                   boost::any cvmap, boost::any pos, boost::any cpos,
                   double delta, rng_t& rng)
{
    typedef mpl::vector<property_map_type::apply
                            <int32_t,
                             GraphInterface::vertex_index_map_t>::type>::type
        vmaps_t;

    run_action<>()
        (gi, std::bind(do_propagate_pos(),
                       placeholders::_1, placeholders::_2, placeholders::_3,
                       cvmap, placeholders::_4, cpos, delta, std::ref(rng)),
         get_pointers::apply<graph_tool::detail::all_graph_views>::type(),
         vmaps_t(), vertex_floating_vector_properties())
        (cgi.GetGraphView(), vmap, pos);
}

struct do_propagate_pos_mivs
{
    template <class Graph, class MIVSMap, class PosMap,
              class RNG>
    void operator()(Graph& g, MIVSMap mivs, PosMap pos, double delta, RNG& rng) const
    {
        typedef typename property_traits<PosMap>::value_type pos_t;
        typedef typename pos_t::value_type val_t;

        uniform_real_distribution<val_t> noise(-delta, delta);

        for (auto v : vertices_range(g))
        {
            if (mivs[v])
                continue;
            size_t count = 0;
            for (auto a : adjacent_vertices_range(v, g))
            {
                if (!mivs[a])
                    continue;
                pos[v].resize(pos[a].size(), 0);
                for (size_t j = 0; j < pos[a].size(); ++j)
                    pos[v][j] += pos[a][j];
                ++count;
            }

            if (count == 0)
                throw ValueException("invalid MIVS! Vertex has no neighbours "
                                     "belonging to the set!");

            if (count == 1)
            {
                if (delta > 0)
                {
                    for (size_t j = 0; j < pos[v].size(); ++j)
                        pos[v][j] += noise(rng);
                }
            }
            else
            {
                for (size_t j = 0; j < pos[v].size(); ++j)
                    pos[v][j] /= count;
            }
        }
    }
};

void propagate_pos_mivs(GraphInterface& gi, boost::any mivs, boost::any pos,
                        double delta, rng_t& rng)
{
    run_action<>()
        (gi, std::bind(do_propagate_pos_mivs(),
                       placeholders::_1, placeholders::_2, placeholders::_3,
                       delta, std::ref(rng)),
         vertex_scalar_properties(), vertex_floating_vector_properties())
        (mivs, pos);
}

struct do_avg_dist
{
    template <class Graph, class PosMap>
    void operator()(Graph& g, PosMap pos, double& ad) const
    {
        int i, N = num_vertices(g);
        size_t count = 0;
        double d = 0;
        #pragma omp parallel for default(shared) private(i) \
            reduction(+: d, count)
        for (i = 0; i < N; ++i)
        {
            typename graph_traits<Graph>::vertex_descriptor v =
                vertex(i, g);
            if (v == graph_traits<Graph>::null_vertex())
                continue;
            for (auto a : adjacent_vertices_range(v, g))
            {
                d += dist(pos[v], pos[a]);
                count++;
            }
        }
        if (count > 0)
            d /= count;
        ad = d;
    }
};

double avg_dist(GraphInterface& gi, boost::any pos)
{
    double d;
    run_action<>()
        (gi, std::bind(do_avg_dist(), placeholders::_1,
                       placeholders::_2, std::ref(d)),
         vertex_scalar_vector_properties()) (pos);
    return d;
}

struct do_sanitize_pos
{
    template <class Graph, class PosMap>
    void operator()(Graph& g, PosMap pos) const
    {
        int i, N = num_vertices(g);
        #pragma omp parallel for default(shared) private(i) schedule(runtime) if (N > 100)
        for (i = 0; i < N; ++i)
        {
            typename graph_traits<Graph>::vertex_descriptor v =
                vertex(i, g);
            if (v == graph_traits<Graph>::null_vertex())
                continue;
            pos[v].resize(2);
        }
    }
};

void sanitize_pos(GraphInterface& gi, boost::any pos)
{
    run_action<>()
        (gi, std::bind(do_sanitize_pos(), placeholders::_1, placeholders::_2),
         vertex_scalar_vector_properties()) (pos);
}

#include <boost/python.hpp>

void export_sfdp()
{
    python::def("sfdp_layout", &sfdp_layout);
    python::def("propagate_pos", &propagate_pos);
    python::def("propagate_pos_mivs", &propagate_pos_mivs);
    python::def("avg_dist", &avg_dist);
    python::def("sanitize_pos", &sanitize_pos);
}


#include <boost/test/unit_test.hpp>

#include <templ/solvers/csp/MissionConstraintManager.hpp>
#include <templ/Mission.hpp>
#include <templ/solvers/csp/TransportNetwork.hpp>
#include <templ/io/MissionReader.hpp>
#include <moreorg/vocabularies/OM.hpp>

#include "../test_utils.hpp"

using namespace templ;
using namespace templ::constraints;
using namespace templ::solvers::csp;

struct MissionFixture
{
    MissionFixture()
    {
    }

    ~MissionFixture()
    {}

    void setup(const std::string& filename = "test/data/scenarios/test-mission-constraints-base.xml")
    {
        baseMission = io::MissionReader::fromFile(getRootDir() +  filename);
        baseMission.prepareTimeIntervals();

        BOOST_TEST_MESSAGE("Mission: " << baseMission.toString());

        using namespace templ::solvers::csp;
        using namespace templ::symbols::constants;
        using namespace templ::solvers::temporal;

        Location::Ptr lander = baseMission.getLocation("lander");
        Location::Ptr base1  = baseMission.getLocation("base1");
        Location::Ptr base2  = baseMission.getLocation("base2");
        Location::Ptr base3  = baseMission.getLocation("base3");

        const Interval& i0   = baseMission.getTimeInterval("t0","t1");
        const Interval& i1   = baseMission.getTimeInterval("t2","t3");
        const Interval& i2   = baseMission.getTimeInterval("t4","t5");
        const Interval& i3   = baseMission.getTimeInterval("t6","t7");

        SpaceTime::SpaceIntervalTuple tuple0(lander, i0);
        SpaceTime::SpaceIntervalTuple tuple1(base1, i1);
        SpaceTime::SpaceIntervalTuple tuple2(base2, i2);
        SpaceTime::SpaceIntervalTuple tuple3(base3, i3);

        intervals.push_back(tuple1);
        intervals.push_back(tuple2);
        intervals.push_back(tuple3);
    }

    Mission baseMission;
    std::vector<SpaceTime::SpaceIntervalTuple> intervals;

    void solve(TransportNetwork* solver, Mission::Ptr& mission)
    {
        Gecode::BAB<TransportNetwork> searchEngine(solver);
        TransportNetwork* best = NULL;
        int i = 0;
        while(TransportNetwork* current = searchEngine.next())
        {
            if(i > 1)
            {
                break;
            }

            delete best;
            best = current;
            ++i;

            TransportNetwork::Solution solution = current->getSolution();
            current->saveSolution(solution, mission);

            Mission::Ptr augmentedMission = current->getAugmentedMission();
            augmentedMission->save("/tmp/templ-test-augmented-mission.xml");
        }
    }

    void test(const templ::Constraint::Ptr& constraint = templ::Constraint::Ptr())
    {
        Mission::Ptr mission(new Mission(baseMission));
        TransportNetwork* solver = new TransportNetwork(mission);
        if(constraint)
        {
            solver->addConstraint(constraint, *solver);
        }
        solve(solver, mission);
        delete solver;
    }
};

BOOST_AUTO_TEST_SUITE(mission_constraints)

BOOST_FIXTURE_TEST_CASE(min, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MIN,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals,
            2);

    test(constraint);

}

BOOST_FIXTURE_TEST_CASE(max, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MAX,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals,
            1);

    test(constraint);

}

BOOST_FIXTURE_TEST_CASE(all_distinct, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::ALL_DISTINCT,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals);

    test(constraint);

}

BOOST_FIXTURE_TEST_CASE(min_distinct, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MIN_DISTINCT,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals,
            2);

    test(constraint);
}

BOOST_FIXTURE_TEST_CASE(max_distinct, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MAX_DISTINCT,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals,
            2);

    test(constraint);
}

BOOST_FIXTURE_TEST_CASE(all_equal, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::ALL_EQUAL,
            moreorg::vocabulary::OM::resolve("Payload"),
            intervals);

    test(constraint);
}

BOOST_FIXTURE_TEST_CASE(min_function, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MIN_FUNCTION,
            moreorg::vocabulary::OM::resolve("TransportProvider"),
            intervals,
            1
            );

    test(constraint);
}

BOOST_FIXTURE_TEST_CASE(min_property, MissionFixture)
{
    setup();

    ModelConstraint::Ptr constraint = make_shared<ModelConstraint>(ModelConstraint::MIN_PROPERTY,
            moreorg::vocabulary::OM::resolve("TransportProvider"),
            intervals,
            3,
            moreorg::vocabulary::OM::resolve("transportCapacity")
            );

    test(constraint);
}

BOOST_FIXTURE_TEST_CASE(min_property_embedded, MissionFixture)
{
    setup("test/data/scenarios/test-mission-constraints-1.xml");
    test();
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright (c) 2014-2018 Bauhaus-Universitaet Weimar
// This Software is distributed under the Modified BSD License, see license.txt.
//
// Virtual Reality and Visualization Research Group
// Faculty of Media, Bauhaus-Universitaet Weimar
// http://www.uni-weimar.de/medien/vr

#include <lamure/pre/node_serializer.h>
#include <lamure/pre/serialized_surfel.h>
#include <lamure/utils.h>

#include <omp.h>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <scm/gl_core/math/math.h>
#include <iostream>

#include "tree_modifier.h"

lamure::mat4r loadMatrix(const std::string& filename)
{
    lamure::mat4r mat = lamure::mat4r::identity();
    if (filename == "eye")
        return mat;

    std::ifstream f(filename);
    if (!f.is_open()) {
        std::cerr << "Unable to open transformation file: \""
                  << filename << "\". Exiting...\n";
        exit(EXIT_FAILURE);
    }
    else {
        std::string matrix_values_string;
        std::getline(f, matrix_values_string);
        std::stringstream sstr(matrix_values_string);
        for (int i = 0; i < 16; ++i)
            sstr >> mat[i];
    }
    return scm::math::transpose(mat);
}

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

  scm::math::vec3f extend_bbx_max = scm::math::vec3f(0.0,0.0,0.0);
  scm::math::vec3f extend_bbx_min = scm::math::vec3f(0.0,0.0,0.0);

    omp_set_nested(1);

    namespace po = boost::program_options;
    namespace fs = boost::filesystem;

    const std::string exec_name = (argc > 0) ? fs::path(argv[0]).filename().string() : "";

    // parse command line options
    po::variables_map vm;
    try {
        po::options_description od("Usage: " + exec_name + " [OPTION]... KDN-FILE...\n\nAllowed Options");
        od.add_options()
            ("help,h",
             "print help message")

            ("mode,m",
             po::value<std::string>()->required(),
             "mode of operation. Available modes:\n"
             "  info\n"
             "  complement - first input is the model to be cleaned; the second model defines cleaning volume\n"
             "  hist - first input is the reference model, others models are to be adjusted\n"
             "  radius - multiply surfels' radii with a value given by option -r")

            ("input-files,i",
             po::value<std::vector<std::string>>()->composing()->required(),
             "input files in .bvh format")

            ("transforms,t",
             po::value<std::vector<std::string>>()->composing(),
             "transform files for the models (HINT: use 'eye' keyword for identity matrix)")

            ("relax-levels,l",
             po::value<int>()->default_value(0),
             "How many bvh levels to step up from the leaf level in the second "
             "model to perform intersection test")

            ("radius,r",
             po::value<float>(),
             "Radius multiplier")

            ("extend,e",
             po::value<std::vector<float>>()->multitoken(),
             "Extend bounding boxes by values x y z -x -y -z, e.g. 0.01 0.0 0.0 0.0 0.0 0.0 to extend positive x axis by 1cm. Please note that all values have to be positive.");

        po::positional_options_description pod;
        pod.add("input-files", -1);

        po::store(po::command_line_parser(argc, argv).options(od).positional(pod).run(), vm);

        if (vm.count("help")) {
            std::cout << od << std::endl;
            std::cout << "THIS APPLICATION MODIFES DATA IN .LOD FILES WITHOUT POSSIBILITY TO UNDO!\n"
                         "PLEASE BACKUP YOUR .LOD FILES TO PREVENT LOSS OF INFORMATION\n";
            return EXIT_SUCCESS;
        }
        po::notify(vm);
    }
    catch(po::error& e) {
        std::cerr << "Error: " << e.what() << std::endl
                  << "For details use " << argv[0] << " -h or --help option." << std::endl;
        return EXIT_FAILURE;
    }

    // get input filenames
    auto inp_files = vm["input-files"].as<std::vector<std::string>>();

    if(vm.count("extend")){
      auto bbx_extend_values = vm["extend"].as<std::vector<float>>();
      if(6 != bbx_extend_values.size()){
        std::cerr << argv[0] << std::endl << "Error: too few values for option -extend given." << std::endl
                  << "For details use " << argv[0] << " -h or --help option." << std::endl;
        return EXIT_FAILURE;
      }
      bool all_positive = true;
      for(const auto& v : bbx_extend_values){
	if(v < 0.0){
	  all_positive = false;
	}
      }
      if(!all_positive){
        std::cerr << argv[0] << std::endl << "Error: all values for option -extend ahve to be positive." << std::endl
                  << "For details use " << argv[0] << " -h or --help option." << std::endl;
        return EXIT_FAILURE;
      }
      // apply values for bbx_extend from cmdline
      extend_bbx_max = scm::math::vec3f(bbx_extend_values[0],bbx_extend_values[1],bbx_extend_values[2]);
      extend_bbx_min = scm::math::vec3f(bbx_extend_values[3],bbx_extend_values[4],bbx_extend_values[5]);
    }

    std::vector<std::string> inp_transforms;
    if (vm.count("transforms"))
        inp_transforms = vm["transforms"].as<std::vector<std::string>>();

    collision_detector::objectArray bvhs;

    auto trans_it = inp_transforms.begin();

    for (const auto& s : inp_files) {
        auto tr = std::make_shared<lamure::pre::bvh>(0, 0);
        tr->load_tree(s);
        if (tr->state() < lamure::pre::bvh::state_type::serialized) {
            std::cerr << "file of wrong processing state: " << lamure::pre::bvh::state_to_string(tr->state()) << std::endl;
            return EXIT_FAILURE;
        }
        tr->print_tree_properties();
        lamure::mat4r mat = lamure::mat4r::identity();

        if (trans_it != inp_transforms.end()) {
            mat = loadMatrix(*trans_it);
            ++trans_it;
        }

        bvhs.push_back(std::make_pair(tr, mat));
    }

    for (const auto& t : bvhs) {
        auto trans = scm::math::transpose(t.second);
        std::cout << "transform: ";
        for (int i = 0; i < 16; ++i)
            std::cout << trans[i] << " ";
        std::cout << std::endl;
    }

    // relative complement
    if (vm["mode"].as<std::string>() == "complement") {
        if (inp_files.size() != 2) {
            std::cerr << "Exactly two input files must be specified" << std::endl;
            return EXIT_FAILURE;
        }
        TreeModifier tm(bvhs);
        tm.complementOnFirstTree(vm["relax-levels"].as<int>(), extend_bbx_max, extend_bbx_min);
    }

    // histogram matching
    if (vm["mode"].as<std::string>() == "hist") {
        if (inp_files.size() < 2) {
            std::cerr << "At least two input files must be specified" << std::endl;
            return EXIT_FAILURE;
        }
        TreeModifier tm(bvhs);
        tm.histogrammatchSecondTree(extend_bbx_max, extend_bbx_min);
    }

    // radius multiplier
    if (vm["mode"].as<std::string>() == "radius") {
        if (!vm.count("radius")) {
            std::cerr << "Option -r is required in this mode" << std::endl;
            return EXIT_FAILURE;
        }
        TreeModifier tm(bvhs);
        tm.MultRadii(vm["radius"].as<float>());
    }

    // bvh information
    if (vm["mode"].as<std::string>() == "info") {
        for (const auto& t : bvhs) {
            for (uint32_t j = 0; j <= t.first->depth(); ++j) {
                auto range = t.first->get_node_ranges(j);

                for (size_t i = range.first; i < range.first + 4 && i < range.first + range.second; ++i) {
                    //std::cout << i << " centroid:   " << t.first->nodes()[i].centroid() << std::endl;
                    std::cout << i << " rep radius: " << t.first->nodes()[i].avg_surfel_radius() << std::endl;
                }
            } // */

            /*
            using namespace lamure;
            pre::node_serializer ser(t.first->max_surfels_per_node(), 0);
            ser.open(add_to_path(t.first->base_path(), ".lod").string(), true);

            pre::surfel::surfel_vector surfels;
            ser.read_node_immediate(surfels, 32767);

            for(const auto& s: surfels)
                    std::cout << s.radius() << std::endl;
                    // */

        }
    }

    return EXIT_SUCCESS;
}


//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T.Lapreste

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

#include <boost/simd/arch/detail/scalar/horner.hpp>
#include <boost/simd/arch/detail/scalar/f_log.hpp>
#include <boost/simd/arch/detail/scalar/d_log.hpp>

#endif


#pragma once

#include "utils/ph_map/perfect_hash_map_builder.hpp"
#include "io/kmers/mmapped_reader.hpp"
#include "sequence/rtseq.hpp"

#include <boost/optional.hpp>

namespace debruijn_graph {

//Kmer multiplicities types: integral values
typedef uint16_t Mpl;
static const Mpl INVALID_MPL = Mpl(-1);
typedef std::size_t Offset;
typedef typename std::vector<Mpl> MplVector;

//Contig abundancies types: arbitrary values
typedef float Abundance;
typedef float Var;

struct AbVar {
    Abundance ab; Var var;
};

std::ostream& operator<<(std::ostream& str, AbVar ab_var);

template<typename T> using Profile = std::vector<T>;

class KmerProfileIndex {
private:
    typedef typename utils::InvertableStoring::trivial_inverter InverterT;
    typedef utils::KeyStoringMap<RtSeq,
        Offset,
        kmers::kmer_index_traits<RtSeq>,
        utils::InvertableStoring> IndexT;

    static size_t sample_cnt_;

public:
    static void SetSampleCount(size_t sample_cnt) { sample_cnt_ = sample_cnt; }
    static size_t SampleCount() { return sample_cnt_; }

    class KmerProfile {

    public:
        typedef Mpl value_type;

        KmerProfile(const value_type* ptr = nullptr):
            ptr_(ptr) {
        }

        KmerProfile(const MplVector& vec):
            ptr_(&vec.front()) {
        }

        size_t size() const {
            return SampleCount();
        }

        Mpl operator[](size_t i) const {
            VERIFY(i < size());
            return ptr_[i];
        }

        const value_type* begin() const {
            return ptr_;
        }

        const value_type* end() const {
            return ptr_ + size();
        }

    private:
        const value_type* ptr_;
    };

    typedef typename IndexT::KeyType KeyType;
    typedef typename IndexT::KeyWithHash KeyWithHash;

    KmerProfileIndex(unsigned k, const std::string& index_prefix);
    KmerProfileIndex(KmerProfileIndex&& other);

    KeyWithHash Construct(const KeyType& seq) const;
    boost::optional<KmerProfile> operator[](const KeyWithHash& kwh) const;

private:
    typedef MMappedRecordArrayReader<Mpl> ProfilesT;
    InverterT inverter_;
    IndexT index_;
    boost::optional<ProfilesT> profiles_;
};

using KmerProfile = KmerProfileIndex::KmerProfile;
typedef std::vector<KmerProfile> KmerProfiles;

template<class CovVecs>
Profile<Abundance> MeanVector(const CovVecs& cov_vecs) {
    VERIFY(cov_vecs.size() != 0);
    size_t sample_cnt = cov_vecs.front().size();
    Profile<Abundance> answer(sample_cnt, 0.);

    for (const auto& cov_vec : cov_vecs) {
        for (size_t i = 0; i < sample_cnt; ++i) {
            answer[i] += cov_vec[i];
        }
    }

    for (size_t i = 0; i < sample_cnt; ++i) {
        answer[i] /= float(cov_vecs.size());
    }
    return answer;
}

template<class AbVector>
std::string PrintVector(const AbVector& mpl_vector, const std::string &delim = " ") {
    std::stringstream ss;
    std::copy(mpl_vector.begin(), mpl_vector.end(),
              std::ostream_iterator<typename AbVector::value_type>(ss, delim.c_str()));
    return ss.str();
}

template<typename T>
class ClusterAnalyzer {
public:
    typedef typename boost::optional<Profile<T>> Result;
    virtual Result operator()(const KmerProfiles& kmer_mpls) const = 0;
    virtual ~ClusterAnalyzer() {};
};

template<typename T>
class TrivialClusterAnalyzer : public ClusterAnalyzer<T> {
public:
    TrivialClusterAnalyzer() {}
    virtual typename ClusterAnalyzer<T>::Result operator()(const KmerProfiles& kmer_mpls) const override;

private:
    bool var_;
    DECL_LOGGER("TrivialClusterAnalyzer");
};

/*class SingleClusterAnalyzer : public ClusterAnalyzer {
    static const uint MAX_IT = 10;

    double coord_vise_proximity_;
    double central_clust_share_;

    bool AreClose(const KmerProfile& c, const KmerProfile& v) const;
    KmerProfiles CloseKmerMpls(const KmerProfiles& kmer_mpls, const KmerProfile& center) const;

public:
    SingleClusterAnalyzer(double coord_vise_proximity = 0.7,
                          double central_clust_share = 0.7) :
        coord_vise_proximity_(coord_vise_proximity),
        central_clust_share_(central_clust_share) {
    }

    std::unique_ptr<AbundanceVector> operator()(const KmerProfiles& kmer_mpls) const override;

private:
    DECL_LOGGER("SingleClusterAnalyzer");
};*/

template<typename T>
class ProfileCounter {
    const unsigned k_;
    std::unique_ptr<ClusterAnalyzer<T>> cluster_analyzer_;
    double min_earmark_share_;

    KmerProfileIndex profile_index_;

public:
    ProfileCounter(unsigned k,
                   std::unique_ptr<ClusterAnalyzer<T>> cluster_analyzer,
                   const std::string& index_prefix,
                   double min_earmark_share = 0.7);
    ProfileCounter(ProfileCounter&& other);

    void Init(const std::string& kmer_mpl_file);

    typename ClusterAnalyzer<T>::Result operator()(const std::string& s, const std::string& /*name*/ = "") const;

private:
    DECL_LOGGER("ProfileCounter");
};

template<typename T>
ProfileCounter<T> MakeTrivial(unsigned k, const std::string& index_prefix) {
    return ProfileCounter<T>(
        k,
        std::unique_ptr<ClusterAnalyzer<T>>(new TrivialClusterAnalyzer<T>()),
        index_prefix
    );
}

}


#include <armadillo>
#include <exception>
#include "variant.hpp"

namespace container
{
    Variant::Variant(arma::Mat<double>* const obj) :
        obj_i(nullptr),
        obj_f(obj)
    {
        ;
    }

    Variant::Variant(arma::Mat<int>* const obj) :
        obj_i(obj),
        obj_f(nullptr)
    {
        ;
    }

    Variant::~Variant()
    {
        if (this->obj_i)
        {
            delete this->obj_i;
        }
        if (this->obj_f)
        {
            delete this->obj_f;
        }
    }

    template<typename T>
    Variant_t Variant::Create(const int& nRows, const int& nCols)
    {
        throw std::invalid_argument("unknown type");
    }

    template<>
    Variant_t Variant::Create<double>(const int& nRows, const int& nCols)
    {
        auto v = new arma::Mat<double>(nRows, nCols, arma::fill::zeros);
        return new Variant(v);
    }

    template<>
    Variant_t Variant::Create<int>(const int& nRows, const int& nCols)
    {
        auto v = new arma::Mat<int>(nRows, nCols, arma::fill::zeros);
        return new Variant(v);
    }

    template<typename T>
    arma::Mat<T>* Variant::Get()
    {
        throw std::invalid_argument("unknown type");
    }

    template<>
    arma::Mat<double>* Variant::Get()
    {
        if (this->obj_f == nullptr)
        {
            throw std::invalid_argument("wrong data type");
        }
        return this->obj_f;
    }

    template<>
    arma::Mat<int>* Variant::Get()
    {
        if (this->obj_i == nullptr)
        {
            throw std::invalid_argument("wrong data type");
        }
        return this->obj_i;
    }
}


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
///
///          Distributed under the Boost Software License, Version 1.0
///                 See accompanying file LICENSE.txt or copy at
///                     http://www.boost.org/LICENSE_1_0.txt
//////////////////////////////////////////////////////////////////////////////
#ifndef NT2_TOOLBOX_IEEE_FUNCTION_SCALAR_SUCCESSOR_HPP_INCLUDED
#define NT2_TOOLBOX_IEEE_FUNCTION_SCALAR_SUCCESSOR_HPP_INCLUDED
#include <nt2/sdk/constant/digits.hpp>
#include <nt2/sdk/constant/infinites.hpp>
#include <nt2/sdk/constant/real.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/constant/properties.hpp>
#include <nt2/sdk/constant/eps_related.hpp>
#include <boost/fusion/tuple.hpp>

#include <nt2/include/functions/oneplus.hpp>
#include <nt2/include/functions/bitinteger.hpp>
#include <nt2/include/functions/bitfloating.hpp>
#include <nt2/include/functions/tofloat.hpp>
#include <nt2/include/functions/seladd.hpp>
#include <nt2/include/functions/select.hpp>
#include <nt2/include/functions/fast_frexp.hpp>
#include <nt2/include/functions/fast_ldexp.hpp>
#include <nt2/include/functions/is_eqz.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::successor_, tag::cpu_,
                           (A0),
                           (arithmetic_<A0>)
                          )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::successor_(tag::arithmetic_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> : meta::strip<A0> {};
    NT2_FUNCTOR_CALL(1)
      {
      if (a0 != Valmax<A0>())
	return  oneplus(a0);
      else
	return Valmax<A0>();
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::successor_, tag::cpu_,
                           (A0),
                           (real_<A0>)
                          )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::successor_(tag::real_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> : meta::strip<A0> {};
    NT2_FUNCTOR_CALL(1)
    {
      return a0==Inf<A0>() ? a0 : bitfloating(oneplus(bitinteger(a0)));
    }
  };
} }

NT2_REGISTER_DISPATCH(tag::successor_, tag::cpu_,
                           (A0)(A1),
                           (arithmetic_<A0>)(integer_<A1>)
                          )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::successor_(tag::arithmetic_, tag::integer_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0,class A1>
      struct result<This(A0, A1)> : meta::strip<A0> {};
    NT2_FUNCTOR_CALL(2)
      {
      if (Valmax<A0>()-nt2::abs(a1) <  a0) return Valmax<A0>();
      return a0+nt2::abs(a1);
      }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::successor_, tag::cpu_,
		      (A0)(A1),
		      (real_<A0>)(integer_<A1>)
                          )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::successor_(tag::real_, tag::integer_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0,class A1>
      struct result<This(A0, A1)> : meta::strip<A0> {};
    NT2_FUNCTOR_CALL(2)
    {
       return a0==Inf<A0>() ? a0 : bitfloating(bitinteger(a0)+nt2::abs(a1));
    }

  };
} }

#endif
// modified by jt the 26/12/2010
// modified manually by jt the 02/01/2011


// OGL TLM on GPU
//
// A program to demo and bench mark the implementation of the TLM Theory on a GPU
// using OpenGL as the renderer and for GPGPU operations
#define NOMINMAX
#include "stdafx.h"
#include <oglwfw/WindowMgr.hpp>
#include "Camera/PerspectiveWorldCamera.hpp"
#include "Camera/PolarOrbitView.hpp"
#include "Maths/mtxlib.h"

#include "TLM Modules/TLMCommon.h"
#include "TLM Modules/ITLMRenderer.h"
#include "TLM Modules/TLMFullShader.h"
#include "TLM Modules/TLMCPUGPUHybrid.h"
#include "TLM Modules/TLMCPUOnly.h"
#include "TLM Modules/TLMMPCPUOnly.h"
#include "TLM Modules/TLMMPCPUGPUHybrid.h"

#include "Mesh/IMesh.hpp"
#include "Mesh/PlaneMesh.hpp"

#include <fstream>
#include <boost/format.hpp>	// for output stream data formatting
#include <limits>

//#include "libFrameEncoder.hpp"

struct timeDetails
{
	int framenumber;
	LONGLONG updateTime;
	LONGLONG driveTime;
	LONGLONG renderTime;
	LONGLONG totalTime;
};

int _tmain(int argc, _TCHAR* argv[])
{
	OpenGLWFW::WindowManager WinMgr;
	try
	{
		if(!WinMgr.FindCompatibleOGLMode())
			return -1;
		if(!WinMgr.FindCompatibleDisplayMode(800,600))
			return -2;

	//	WinMgr.SetFullScreen(OpenGLWFW::winprops::fullscreen);
		WinMgr.CreateWin();
		WinMgr.Show();
	}
	catch (std::bad_exception &)
	{
		return -3;
	}

	Resurrection::CameraSystem::PerspectiveWorldCamera camera(60.0f,800.0f/600.0f,1.0f,100.0f);
	vector3 pos(-4.0f, -1.0f, -4.0f);
	vector3 target(0.0f, 0.0f, 0.0f);
	float angle = 0.0f;
	Resurrection::CameraSystem::PolarOrbitView view(target,pos,DegToRad(angle - 270));
	camera.setView(&view);

	// Standard OpenGL setup stuff
	glClearColor(0.0f, 0.0f, 0.2f, 1.0f);		// This Will Clear The Background Color To kinda blue
	glClearDepth(1.0);							// Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LEQUAL);						// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);					// Turn Depth Testing On
	glShadeModel(GL_SMOOTH);					// Enables Smooth Color Shading
	glDisable(GL_BLEND);
	glDisable(GL_CULL_FACE);
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);					// Full Brightness.  50% Alpha

	TLM::ITLMRenderer * currentmode;
	// Test system needs to automate the generation of the data for each module type at each size with each mesh
	// Size : 40, 50, 100, 128, 256, 512, 1024
	// GPU based one needs to be tested at both 16bit and 32bit per pixel.

	int meshsize = 512;
	Mesh::PlaneMesh mesh(meshsize);
//	TLM::TLMFullShader module(GL_RGBA32F_ARB,mesh);
//	TLM::TLMFullShader module(GL_RGBA16F_ARB,mesh);
//	TLM::TLMCPUGPUHybrid module(mesh);
//	TLM::TLMCPUOnly module(mesh);
//	TLM::TLMMPCPUOnly module(mesh);
	TLM::TLMMPCPUGPUHybrid module(mesh);
	module.CreateResources(meshsize);
	currentmode = &module;

//	currentmode->GenerateTLMData();
#undef TLM_RECORD
#ifdef TLM_RECORD
//	FrameEncoder::TheoraEncoder *encoder = new FrameEncoder::TheoraEncoder("tlm-singlepoint-scaled.ogm",800,600);
//	FrameEncoder::TheoraEncoder *encoder = new FrameEncoder::TheoraEncoder("tlm-dualpoints-noscale.ogm",800,600);
//	FrameEncoder::TheoraEncoder *encoder = new FrameEncoder::TheoraEncoder("tlm-dualpoints-scaled.ogm",800,600);	// didn't record o.O
//	FrameEncoder::TheoraEncoder *encoder = new FrameEncoder::TheoraEncoder("tlm-singlepoint-scaled-40by40.ogm",800,600);
	FrameEncoder::TheoraEncoder *encoder = new FrameEncoder::TheoraEncoder("tlm-singlepoint-scaled-100by100.ogm",800,600);
	encoder->beginEncoding();
#endif
	size_t iter = 0;
	float frequency = 0.5f;
	int scale = 100;

	LARGE_INTEGER timestart;
	LARGE_INTEGER timeend;

	const size_t MAX_TEST_ITOR = 1000;

	timeDetails working;
	std::vector<timeDetails> details(MAX_TEST_ITOR);

	while (WinMgr.Dispatch() && iter < MAX_TEST_ITOR)
	{
		working.framenumber = iter;

		glDisable(GL_CULL_FACE);
		glDisable(GL_DEPTH_TEST);
		QueryPerformanceCounter(&timestart);
		currentmode->DriveTLMData(TLM::generateDrivingValue(iter,frequency,scale));
		QueryPerformanceCounter(&timeend);
		working.driveTime = timeend.QuadPart - timestart.QuadPart;
		QueryPerformanceCounter(&timestart);
		currentmode->GenerateTLMData();
		QueryPerformanceCounter(&timeend);
		working.updateTime = timeend.QuadPart - timestart.QuadPart;

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		view.setAngle(DegToRad(angle - 270));
		camera.updateViewMatrix();
		camera.setMatricies();
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);
		QueryPerformanceCounter(&timestart);
		currentmode->RenderTLM(vector3(0.0f,0.0f,0.0f), vector3(0.0f,0.0f,0.0f));
		QueryPerformanceCounter(&timeend);
		working.renderTime = timeend.QuadPart - timestart.QuadPart;

		// Store details
		details[iter] = working;

		angle += 0.3f;
		if(angle > 360.0f)
			angle = 0.0f;

	//	currentmode->VisualiseTextures();

#ifdef TLM_RECORD
		unsigned char * buffer = encoder->requestBuffer();
		glReadPixels(0,0,800,600,GL_RGB,GL_UNSIGNED_BYTE,buffer);
		encoder->processBuffer(buffer);
		encoder->Process();
#endif

		WinMgr.SwapBuffers();
		iter++;
	}
#ifdef TLM_RECORD
	encoder->endEncoding();
	delete encoder;
#endif
	currentmode->DestoryResources();

	// Work out details
	timeDetails average;
	average.driveTime = 0;
	average.renderTime = 0;
	average.updateTime = 0;

	// Collect all details to build average
	for(int i = 0; i < std::min(MAX_TEST_ITOR,details.size()); i++)
	{
		average.driveTime += details[i].driveTime;
		average.renderTime += details[i].renderTime;
		average.updateTime += details[i].updateTime;
	}
	average.driveTime /= std::min(MAX_TEST_ITOR,details.size());
	average.renderTime /= std::min(MAX_TEST_ITOR,details.size());
	average.updateTime /= std::min(MAX_TEST_ITOR,details.size());

	timeDetails min;
	min.driveTime = std::numeric_limits<LONGLONG>::max();
	min.renderTime = std::numeric_limits<LONGLONG>::max();
	min.updateTime = std::numeric_limits<LONGLONG>::max();

	// Collect all details to build min times
	for(int i = 0; i < std::min(MAX_TEST_ITOR,details.size()); i++)
	{
		min.driveTime = std::min(details[i].driveTime, min.driveTime);
		min.renderTime = std::min(details[i].renderTime, min.renderTime);
		min.updateTime = std::min(details[i].updateTime, min.updateTime);
	}

	timeDetails max;
	max.driveTime = 0;
	max.renderTime = 0;
	max.updateTime = 0;

	// Collect all details to build max times
	for(int i = 0; i < std::min(MAX_TEST_ITOR,details.size()); i++)
	{
		max.driveTime = std::max(details[i].driveTime, max.driveTime);
		max.renderTime = std::max(details[i].renderTime, max.renderTime);
		max.updateTime = std::max(details[i].updateTime, max.updateTime);
	}

	std::ofstream results("results.txt");

	results << boost::format("Name : %1%\nSize : %2%\n") % currentmode->GetFriendlyName() % meshsize;
	results << boost::format("\t\tMax\tMin\tAverage\n");
	results << boost::format("Drive Time\t%1%\t%2%\t%3%\n") % max.driveTime % min.driveTime % average.driveTime;
	results << boost::format("Render Time\t%1%\t%2%\t%3%\n") % max.renderTime % min.renderTime % average.renderTime;
	results << boost::format("Update Time\t%1%\t%2%\t%3%\n") % max.updateTime % min.updateTime % average.updateTime;
	results << boost::format("Dumping full results in CSV format (drive,render,update)...\n\n");
	for(int i = 0; i < std::min(MAX_TEST_ITOR,details.size()); i++)
	{
		results << boost::format("%1%,%2%,%3%\n") % details[i].driveTime % details[i].renderTime % details[i].updateTime;
	}

	results << boost::format("--------------------------------------------------------\n\n");

	return 0;
}

OGL TLM on GPU/OGL TLM on GPU - Copy.cpp


#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/filesystem.hpp>

#include "_reg_maths.h"
#include "resampleKernel.h"

namespace bfs = boost::filesystem;

static void _test_assert(const bool value, const char condition[]) {
  if (!value) {
    std::cerr << "FAILED: " << condition << std::endl;
    std::abort();
  }
}

#define RESAMPLING_TEST_ASSERT(condition) _test_assert((condition), #condition)

static void _check_image_displacement(const nifti_image &floating, const nifti_image &warped, const float voxel_displacement, const int stride, const int interpolation) {
  const bool use_nearest = interpolation == 0;
  const int max_idx_displacement = use_nearest? int(std::round(voxel_displacement)) : int(std::ceil(voxel_displacement));
  const float *floating_begin = (const float*)(floating.data);
  const float *floating_end = floating_begin + floating.nvox;
  const float *warped_begin = (const float*)(warped.data);

  int nof_nans = 0;
  int boundary_size = 0;

  RESAMPLING_TEST_ASSERT(floating.nvox == warped.nvox);

  for (const float *pc_floating = floating_begin; pc_floating < floating_end; ++pc_floating) {
    if (pc_floating + max_idx_displacement*stride >= floating_begin && pc_floating + max_idx_displacement*stride < floating_end) {
      int offset = pc_floating - floating_begin;
      float ref_value = *(pc_floating + max_idx_displacement*stride);
      float test_value = *(warped_begin + offset);

      if (!use_nearest && voxel_displacement - std::floor(voxel_displacement) > 0.1) {
        ref_value = (*(pc_floating + (max_idx_displacement - 1)*stride) + ref_value)/2;
      }

      if (std::isfinite(test_value)) {
        RESAMPLING_TEST_ASSERT(std::fabs(ref_value - test_value) <= 1e-2*std::fabs(ref_value));
      } else {
        nof_nans += 1;
      }
    }
  }

  if (floating.ndim == 2) {
    boundary_size = (1 + (std::max)(max_idx_displacement, 1)*(interpolation + 1))*(floating.ny + floating.nx);
  } else {
    boundary_size = (7 + (std::max)(max_idx_displacement, 1)*(interpolation + 1))*(std::max)((std::max)(floating.ny*floating.nx, floating.ny*floating.nz), floating.nx*floating.nz);
  }

  RESAMPLING_TEST_ASSERT(nof_nans <= boundary_size);
}

static void _test_displacement(const std::string &image_path) {
  nifti_image const *pc_image = nifti_image_read(image_path.c_str(), 1);
  nifti_image *p_displacements = nifti_copy_nim_info(pc_image);
  std::vector<float> displacement_field(pc_image->nvox*pc_image->dim[0]);

  for (const int interp: {0, 1, 3}) {
    for (int d = 0; d < pc_image->dim[0] - int(pc_image->dim[pc_image->dim[0]] <= 1); ++d) {
      int stride = 1;

      for (int i = 0; i < d; ++i) {
        stride *= pc_image->dim[1+i];
      }

      for (const float voxel_displacement: {0.0f, 1.0f, 0.500001f, 1.50001f}) {
        const float displacement = voxel_displacement*pc_image->pixdim[1+d];

        nifti_image *p_warped;

        std::fill(displacement_field.begin(), displacement_field.end(), 0.f);
        std::fill(displacement_field.begin() + d*pc_image->nvox, displacement_field.begin() + (d + 1)*pc_image->nvox, displacement);

        p_displacements->ndim = p_displacements->dim[0] = 5;
        p_displacements->nt = p_displacements->dim[4] = 1;
        p_displacements->nu = p_displacements->dim[5] = pc_image->dim[0];
        p_displacements->nvox = pc_image->nvox*p_displacements->nu;
        p_displacements->intent_p1 = DISP_FIELD;

        p_displacements->data = (void*)(displacement_field.data());
        p_warped = resample(*p_displacements, *pc_image, interp, std::numeric_limits<float>::quiet_NaN());

        _check_image_displacement(*pc_image, *p_warped, voxel_displacement, stride, interp);

        nifti_image_free(p_warped);
      }
    }
  }
  p_displacements->data = nullptr;
  nifti_image_free(p_displacements);
  nifti_image_free(const_cast<nifti_image*>(pc_image));
}

int main(int argc, char* argv[]) {
  const bfs::path data_path = bfs::path(argv[0]).parent_path();

  _test_displacement((data_path/"data"/"test_image_2.nii").native());
  _test_displacement((data_path/"data"/"test_image_3.nii").native());

  return 0;
}


// Copyright (C) 2019 T. Zachary Laine
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP
#define BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP

#include <boost/stl_interfaces/iterator_interface.hpp>

namespace boost { namespace stl_interfaces { inline namespace v1 {

    namespace v1_dtl {
        template<typename Iter>
        constexpr auto ce_dist(Iter f, Iter l, std::random_access_iterator_tag)
            -> decltype(l - f)
        {
            return l - f;
        }
        template<typename Iter, typename Tag>
        constexpr auto ce_dist(Iter f, Iter l, Tag)
            -> decltype(std::distance(f, l))
        {
            decltype(std::distance(f, l)) retval = 0;
            for (; f != l; ++f) {
                ++retval;
            }
            return retval;
        }

        template<typename Iter>
        constexpr Iter ce_prev(Iter it)
        {
            return --it;
        }

        template<typename Iter, typename Offset>
        constexpr void
        ce_adv(Iter & f, Offset n, std::random_access_iterator_tag)
        {
            f += n;
        }
        template<typename Iter, typename Offset, typename Tag>
        constexpr void ce_adv(Iter & f, Offset n, Tag)
        {
            if (0 < n) {
                for (Offset i = 0; i < n; ++i) {
                    ++f;
                }
            } else {
                for (Offset i = 0; i < -n; ++i) {
                    --f;
                }
            }
        }
    }

    /** This type is very similar to the C++20 version of
        `std::reverse_iterator`; it is `constexpr`-, `noexcept`-, and
        proxy-friendly. */
    template<typename BidiIter>
    struct reverse_iterator
        : iterator_interface<
              reverse_iterator<BidiIter>,
#if 201703L < __cplusplus && defined(__cpp_lib_ranges)
              typename std::iterator_traits<BidiIter>::iterator_concept,
#else
              typename std::iterator_traits<BidiIter>::iterator_category,
#endif
              typename std::iterator_traits<BidiIter>::value_type,
              typename std::iterator_traits<BidiIter>::reference,
              typename std::iterator_traits<BidiIter>::pointer,
              typename std::iterator_traits<BidiIter>::difference_type>
    {
        constexpr reverse_iterator() noexcept(noexcept(BidiIter())) : it_() {}
        constexpr reverse_iterator(BidiIter it) noexcept(
            noexcept(BidiIter(it))) :
            it_(it)
        {}
        template<
            typename BidiIter2,
            typename E = std::enable_if_t<
                std::is_convertible<BidiIter2, BidiIter>::value>>
        reverse_iterator(reverse_iterator<BidiIter2> const & it) : it_(it.it_)
        {}

        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR auto
        operator-(reverse_iterator lhs, reverse_iterator rhs) noexcept(
            noexcept(v1_dtl::ce_dist(
                lhs.it_,
                rhs.it_,
                typename std::iterator_traits<BidiIter>::iterator_category{})))
        {
            return -v1_dtl::ce_dist(
                rhs.it_,
                lhs.it_,
                typename std::iterator_traits<BidiIter>::iterator_category{});
        }

        constexpr typename std::iterator_traits<BidiIter>::reference
        operator*() const noexcept(
            noexcept(std::prev(v1_dtl::ce_prev(std::declval<BidiIter &>()))))
        {
            return *v1_dtl::ce_prev(it_);
        }

        constexpr reverse_iterator & operator+=(
            typename std::iterator_traits<BidiIter>::difference_type
                n) noexcept(noexcept(v1_dtl::
                                         ce_adv(
                                             std::declval<BidiIter &>(),
                                             -n,
                                             typename std::iterator_traits<
                                                 BidiIter>::
                                                 iterator_category{})))
        {
            v1_dtl::ce_adv(
                it_,
                -n,
                typename std::iterator_traits<BidiIter>::iterator_category{});
            return *this;
        }

        constexpr BidiIter base() const noexcept { return it_; }

    private:
        friend access;
        constexpr BidiIter & base_reference() noexcept { return it_; }
        constexpr BidiIter const & base_reference() const noexcept
        {
            return it_;
        }

        template<typename BidiIter2>
        friend struct reverse_iterator;

        BidiIter it_;
    };

    template<typename BidiIter>
    constexpr auto operator==(
        reverse_iterator<BidiIter> lhs,
        reverse_iterator<BidiIter>
            rhs) noexcept(noexcept(lhs.base() == rhs.base()))
        -> decltype(rhs.base() == lhs.base())
    {
        return lhs.base() == rhs.base();
    }

    template<typename BidiIter1, typename BidiIter2>
    constexpr auto operator==(
        reverse_iterator<BidiIter1> lhs,
        reverse_iterator<BidiIter2>
            rhs) noexcept(noexcept(lhs.base() == rhs.base()))
        -> decltype(rhs.base() == lhs.base())
    {
        return lhs.base() == rhs.base();
    }

    /** Makes a `reverse_iterator<BidiIter>` from an iterator of type
        `Bidiiter`. */
    template<typename BidiIter>
    auto make_reverse_iterator(BidiIter it)
    {
        return reverse_iterator<BidiIter>(it);
    }

}}}

#if 201703L < __cplusplus && defined(__cpp_lib_concepts) || BOOST_STL_INTERFACES_DOXYGEN

namespace boost { namespace stl_interfaces { namespace v2 {

    /** A template alias for `std::reverse_iterator`.  This only exists to
        make migration from Boost.STLInterfaces to C++20 easier; switch to the
        one in `std` as soon as you can. */
    template<typename BidiIter>
    using reverse_iterator = std::reverse_iterator<BidiIter>;

    /** Makes a `reverse_iterator<BidiIter>` from an iterator of type
        `Bidiiter`.  This only exists to make migration from
        Boost.STLInterfaces to C++20 easier; switch to the one in `std` as
        soon as you can. */
    template<typename BidiIter>
    auto make_reverse_iterator(BidiIter it)
    {
        return reverse_iterator<BidiIter>(it);
    }

}}}

#endif

#endif


#include <iostream>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Main
#include <boost/test/unit_test.hpp>
#include <vector>
#include <thread>

#include "ingestion_pipeline.h"

using namespace Akumuli;

struct ConnectionMock : DbConnection {
    int cntp;
    int cntt;
    aku_Status write(const aku_Sample &sample) {
        if (sample.timestamp == 1) {
            cntt += 1;
            cntp += (int)sample.paramid;
        } else {
            BOOST_ERROR("Invalid value!");
        }
        return AKU_SUCCESS;
    }

    std::shared_ptr<DbCursor> search(std::string query) {
        throw "not implemented";
    }

    int param_id_to_series(aku_ParamId id, char *buffer, size_t buffer_size) {
        throw "not implemented";
    }

    aku_Status series_to_param_id(const char *name, size_t size, aku_Sample *sample) {
        throw "not implemented";
    }
};

BOOST_AUTO_TEST_CASE(Test_spout_in_single_thread) {

        std::shared_ptr<ConnectionMock> con = std::make_shared<ConnectionMock>();
        con->cntp = 0;
        con->cntt = 0;
        auto pipeline = std::make_shared<IngestionPipeline>(con, AKU_LINEAR_BACKOFF);
        pipeline->start();
        auto spout = pipeline->make_spout();
        int sump = 0;
        int sumt = 0;
        for (int i = 0; i < 10000; i++) {
            sump += i;
            sumt += 1;
            aku_Sample sample = { 1ul, (aku_ParamId)i };
            spout->write(sample);
        }
        pipeline->stop();
        BOOST_REQUIRE_EQUAL(con->cntt, sumt);
        BOOST_REQUIRE_EQUAL(con->cntp, sump);
}


#include <cstdint>
#include <exception>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>

#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/zlib.hpp>

#include <zlib.h>

using std::begin;
using std::end;

namespace base64 {
    namespace bi = boost::archive::iterators;
    using base64_encode_it = bi::base64_from_binary<bi::transform_width<std::string::const_iterator, 6, 8>>;
    using base64_decode_it = bi::transform_width<bi::binary_from_base64<std::string::const_iterator>, 8, 6>;

    std::string encode(std::string const& data) {
        std::string encoded(base64_encode_it(begin(data)), base64_encode_it(end(data)));
        encoded.append((3 - data.size() % 3) % 3, '=');
        return encoded;
    }

    std::string decode(std::string const& data) {
        return std::string(base64_decode_it(begin(data)), base64_decode_it(end(data)));
    }
}

namespace zlib {
    namespace io = boost::iostreams;

    std::string compress(std::string const& data) {
        std::stringstream compressed, decompressed;
        decompressed << data;

        io::filtering_streambuf<io::input> out;
        out.push(io::zlib_compressor());
        out.push(decompressed);

        io::copy(out, compressed);
        return compressed.str();
    }

    std::string decompress(std::string const& data) {
        std::stringstream compressed, decompressed;
        compressed << data;

        io::filtering_streambuf<io::input> in;
        in.push(io::zlib_decompressor());
        in.push(compressed);

        io::copy(in, decompressed);
        return decompressed.str();
    }
}

std::string compress_to_base64(std::string const& data) {
    return base64::encode(zlib::compress(data));
}

std::string decompress_from_base64(std::string const& data) {
    return zlib::decompress(base64::decode(data));
}

int main() {
    auto const data = "12345678900";
    auto const encoded = compress_to_base64(data);
    auto const decoded = decompress_from_base64(encoded);

    std::cout << encoded << '\n'
              << decoded << '\n';
}


// All content Copyright (C) 2018 Genomics plc
#ifndef INTERVAL_TREE_HPP
#define INTERVAL_TREE_HPP

#include <functional>
#include <cassert>
#include <cstdint>
#include <memory>
#include <set>
#include <sstream>

#include "utils/logging.hpp"
#include "interval.hpp"
#include "utils/exceptions.hpp"
#include <boost/container/flat_set.hpp>

namespace wecall
{
namespace utils
{

    /// A binary tree that holds half-interval classes. Each node of the tree has a central point and stores (shared
    /// pointers to)
    // interval classes which contain that central point.
    /// Each node has a left and right bound. All intervals strickly contained in this range are either stored at this
    /// node
    // or within its children.
    template < typename _IntervalType,
               typename _IntervalLeftBoundaryComp,  ///<  These sort the intervals on each node.
               typename _IntervalRightBoundaryComp,
               typename _IntervalLeftBoundaryValue = _IntervalLeftBoundaryComp,  // Ideally should use default.
               typename _IntervalRightBoundaryValue = _IntervalRightBoundaryComp >
    class IntervalTree
    {
    public:
        class iterator;
        typedef std::pair< iterator, iterator > iterator_pair;
        typedef _IntervalType interval_type;
        typedef std::shared_ptr< interval_type > pointer;
        typedef interval_type & reference;

    private:
        class IntervalTreeNode;
        typedef std::shared_ptr< IntervalTreeNode > node_shared_ptr;
        typedef IntervalTreeNode * node_ptr;

        typedef _IntervalLeftBoundaryValue left_interval_value;
        typedef _IntervalRightBoundaryValue right_interval_value;
        typedef boost::container::flat_multiset< pointer, _IntervalLeftBoundaryComp > left_sorted_set;
        typedef boost::container::flat_multiset< pointer, _IntervalRightBoundaryComp > right_sorted_set;

        typedef typename boost::container::flat_multiset< pointer >::const_iterator
            set_iterator;  // Used for both left_sorted_set and right_sorted_set

    public:
        /// Construct an interval Tree for easily retrieving intervals that overlap a given interval
        ///
        /// @param left Default lower bound the interval tree spans. Gets decreased if necessary
        /// @param right Default upper bound the interval tree spans. Gets increased if necessary.
        IntervalTree( const int64_t & left, const int64_t & right )
            : m_root( std::make_shared< IntervalTreeNode >( Interval( left, right ), nullptr ) )
        {
        }

        /// Disabled copy constructor. No copying of interval trees.
        IntervalTree( const IntervalTree & rhs ) = delete;

        /// Disabled assigment operator. No copying of interval trees.
        IntervalTree & operator=( const IntervalTree & rhs ) = delete;

    public:
        /// Inserts a new interval pointer into the tree.
        /// interval will get inserted at the first node which has central value contained in the interval.
        ///
        /// @param intervalPtr Insert a std::shared_ptr to class of interval type.
        void insert( pointer intervalPtr )
        {
            const Interval newInterval( left_interval_value()( intervalPtr ), right_interval_value()( intervalPtr ) );
            this->widenTreeBoundary( newInterval );
            m_root->insert( intervalPtr, newInterval );
        }

        /// @return Returns true if IntervalTree is empty.
        bool empty() const { return m_root->empty(); }

        /// @return A string that identifies the tree for debugging purposes.
        std::string toString() const
        {
            std::stringstream sstrThisID;
            sstrThisID << "IntervalTree spanning region:- " << m_root->getBoundaries().toString();
            return sstrThisID.str();
        }

        /// Returns a pair of iterators. Iterating from the begin will filter out interval_types not overlapping @left
        /// and @right
        /// Uses the default end().
        ///
        /// @param left The lower bound of the interval. No intervals strickly to left of this are reached through
        /// iterator
        /// @paran right The upper bound of the interval. No intervals strickly to right of this are reached through
        /// iterator
        /// @return A pair of iterators pointing to the begin and end of query range
        iterator_pair getSubRange( const Interval & queryInterval ) const
        {
            const auto iterators = std::make_pair( begin( queryInterval ), end() );
            return iterators;
        }

        /// Returns a pair of iterators. Iterating from the first element will not filter any interval_types.
        ///
        iterator_pair getFullRange() const { return std::make_pair( begin(), end() ); }

        /// @return Return an iterator pointing to leftmost interval_type of tree.
        iterator begin() const
        {
            Interval queryInterval = m_root->getBoundaries();
            iterator beginIter = begin( queryInterval );
            return beginIter;
        }

        /// @return Return a default end iterator. Used regardless of the filter on the tree.
        iterator end() const { return iterator(); }

    private:
        /// Changes all the left/right boundary values of outmost nodes. Doesn't rebalance tree in current
        /// implementation.
        ///
        /// @param newInterval Resets boundaries to this if needed.
        void widenTreeBoundary( const Interval & newInterval )
        {
            Interval currentBound = m_root->getBoundaries();

            if ( newInterval.start() < currentBound.start() )
            {
                m_root->setLeftBoundary( newInterval );
            }

            if ( newInterval.end() > currentBound.end() )
            {
                m_root->setRightBoundary( newInterval );
            }
        }

        /// @param queryInterval Iterator will skip intervals that do not overlap this.
        /// @return An Iterator pointing to the first overlapping interval in the tree.
        iterator begin( const Interval & queryInterval ) const
        {
            return iterator( m_root->leftMostChild( queryInterval ), queryInterval );
        }

    private:
        node_shared_ptr m_root;  ///< All nodes of binary-tree stem from this

    public:
        /// A forward iterator for the IntervalTree class. Controls interation within and across nodes.
        /// Note: This is unlike standard stl iterators because tree data is not naturally stored sequentially.
        /// This is because the data is not well-ordered. The nodes containing the data are well-ordered using a
        /// depth-first symmetric
        //  traversal.
        /// The data contained on each node is only well-ordered for a given query-interval
        //  (conditioned on where the central point of the node is in relation to the query-interval).
        /// So, the iterator contains a query interval which it uses well-order & filter the data.
        ///
        /// Uses default copy constructor etc.
        class iterator : public std::iterator< std::forward_iterator_tag, interval_type >
        {
        public:
            /// Construct placeholder iterator for end().
            iterator() : m_nodePtr( nullptr ), m_nodeIterator(), m_queryInterval( 0, 1 ) {}

            /// Construct iterator from particular node. It will skip to first data that overlaps the query interval
            explicit iterator( node_ptr nodePtr, const Interval & queryInterval )
                : m_nodePtr( nodePtr ),
                  m_nodeIterator( nodePtr->begin( queryInterval ) ),
                  m_queryInterval( queryInterval )
            {
                this->skipToNextOverlap();
            }

            /// No implementation on how to compare iterators with different query-intervals.
            bool operator==( const iterator & other ) const
            {
                return m_nodePtr == other.m_nodePtr and m_nodeIterator == other.m_nodeIterator;
            }

            /// @return Return true if iterator is not equal to this.
            bool operator!=( const iterator & other ) const { return not( *this == other ); }

            /// Incrementing moves iterator to point to next interval that overlaps.
            // Two iterators may point to the same value but increment diffently depending on their initial range.
            /// Iterating between two iterators with different query-intervals is not well-defined as they would have a
            // different ordering of the tree-node data.
            iterator & operator++()
            {
                ++m_nodeIterator;
                skipToNextOverlap();
                return *this;
            }

            iterator operator++( int )
            {
                iterator tmp = *this;
                operator++();
                return tmp;
            }

            reference operator*() const { return m_nodeIterator.operator*(); }
            reference operator*() { return m_nodeIterator.operator*(); }

            /// Uses the node iterator which points to the actual data.
            pointer operator->() { return m_nodeIterator.operator->(); }

            void moveToBeginOfSubrange( const Interval & newInterval )
            {
                //                assert( newInterval.start() >= m_queryInterval.start() );

                m_queryInterval = newInterval;
                this->moveToNodeBegin();
                this->skipToNextOverlap();
            }

        private:
            /// Skips to first node/node::iterator pair that still overlaps.
            void skipToNextOverlap()
            {
                while ( m_nodePtr != nullptr and m_nodeIterator.isEnd( m_queryInterval ) )
                {
                    m_nodePtr = m_nodePtr->next( m_queryInterval );
                    this->moveToNodeBegin();
                }
            }

            /// Moves parameter m_nodeIterator to point to first interval that overlaps the query interval.
            void moveToNodeBegin()
            {
                if ( m_nodePtr != nullptr )
                {
                    m_nodeIterator = m_nodePtr->begin( m_queryInterval );
                }

                else
                {
                    m_nodeIterator = typename IntervalTreeNode::iterator();
                }
            }

        private:
            node_ptr m_nodePtr;  ///< Node currently iterating on
            typename IntervalTreeNode::iterator m_nodeIterator;
            Interval m_queryInterval;
        };

    private:
        class IntervalTreeNode
        {

        public:
            class iterator;

        public:
            /// Construct an IntervalTreeNode. Node + child-node stores intervals strictly in [left, right).
            // Only intervals containing central point ((left + right)/2) are stored at this level. Rest are stored in
            // descendants.
            ///
            /// @param [in] nodeInterval The interval that the node will span.
            // All intervals that are subsets of this are contained on this node or its children/grandchildren
            /// @param [in] nextAncestor A node_ptr to its first ancestor that is after it. E.g. parent for a left-node.
            explicit IntervalTreeNode( const Interval & nodeInterval, node_ptr nextAncestor )
                : m_nodeInterval( nodeInterval ),
                  m_centre( nodeInterval.centre() ),
                  m_leftChildNode( nullptr ),
                  m_rightChildNode( nullptr ),
                  m_nextAncestor( nextAncestor )
            {
            }

            IntervalTreeNode( const IntervalTreeNode & rhs ) = delete;
            IntervalTreeNode & operator=( const IntervalTreeNode & ) = delete;

            /// @return True if node & descendant nodes contain no intervals.
            bool empty() const
            {
                bool leftIntervalsEmpty = ( m_leftChildNode == nullptr ) ? true : m_leftChildNode->empty();
                bool rightIntervalsEmpty = ( m_rightChildNode == nullptr ) ? true : m_rightChildNode->empty();

                return m_allIntervals.empty() and leftIntervalsEmpty and rightIntervalsEmpty;
            }

            /// @param [in] newIntervalPtr The new pointer to add to the tree.
            /// @param [in] newInterval The interval that the new pointer spans.
            void insert( pointer newIntervalPtr, const Interval & newInterval )
            {
                // interval coming from pure insertion.
                if ( newInterval.start() == m_centre and newInterval.end() == m_centre )
                {
                    m_allIntervals.insert( newIntervalPtr );
                }

                // m_centre in [leftIntervalValue, rightIntervalValue)
                else if ( newInterval.contains( m_centre ) )
                {
                    m_allIntervals.insert( newIntervalPtr );
                    m_intervalsLeftSorted.insert( newIntervalPtr );
                    m_intervalsRightSorted.insert( newIntervalPtr );
                    // Sanity check of input
                    assert( m_intervalsLeftSorted.size() == m_intervalsRightSorted.size() );
                }

                else if ( newInterval.start() > m_centre )
                {
                    if ( m_rightChildNode == nullptr )
                    {
                        // Right-children inherit nextAncestor
                        m_rightChildNode = std::make_shared< IntervalTreeNode >(
                            Interval( m_centre, m_nodeInterval.end() ), m_nextAncestor );
                    }

                    m_rightChildNode->insert( newIntervalPtr, newInterval );
                }

                else if ( newInterval.end() <= m_centre )  // Working with half-intervals.
                {
                    if ( m_leftChildNode == nullptr )
                    {
                        // Left-childre have parent == nextAncestor
                        m_leftChildNode =
                            std::make_shared< IntervalTreeNode >( Interval( m_nodeInterval.start(), m_centre ), this );
                    }

                    m_leftChildNode->insert( newIntervalPtr, newInterval );
                }

                else
                {
                    WECALL_LOG( DEBUG, "Unable to insert interval= " << newInterval.toString() << " On "
                                                                      << this->toString() );
                    throw utils::wecall_exception( "Unable to insert interval" );
                }
            }

            /// Returns the descendant that is only descended through left-children and of greated depth whilst still
            /// having potential
            // to contain overlapping intervals.
            node_ptr leftMostChild( const Interval & queryInterval )
            {
                // All intervals in m_leftChildNode are to left of m_centre by construction.
                if ( m_leftChildNode == nullptr or queryInterval.start() >= m_centre )
                {
                    return this;
                }

                else
                {
                    return m_leftChildNode->leftMostChild( queryInterval );
                }
            }

            /// @return The next node in the tree. For parent, left-child and right-child order is:- left-child, parent
            /// then right-child.
            /// Right-child and all its descendants only contain intervals with left-value >= m_centre by construction.
            //  So, can quickly rule out
            /// These options by going passing to **ancestor in this case.
            /// **This is the uniquely defined ancestor with a centre further to the right. E.g. for a left-child node
            /// its parent.
            node_ptr next( const Interval & queryInterval )
            {
                // All intervals in m_rightChildNode are strickly to right of m_centre by construction.
                if ( m_rightChildNode == nullptr or queryInterval.end() <= m_centre )
                {
                    return m_nextAncestor;
                }

                else
                {
                    return m_rightChildNode->leftMostChild( queryInterval );
                }
            }

            /// @return The nodes outer boundaries. All intervals in this node and decendants are subintervals of this.
            Interval getBoundaries() const { return m_nodeInterval; }

            /// @param [in] newInterval Adjust node right-boundary to this
            void setRightBoundary( const Interval & newInterval )
            {
                m_nodeInterval = Interval( m_nodeInterval.start(), newInterval.end() );

                if ( m_rightChildNode != nullptr )
                {
                    m_rightChildNode->setRightBoundary( newInterval );
                }
            }

            /// @param [in] newInterval Adjust node left-boundary to this.
            void setLeftBoundary( const Interval & newInterval )
            {
                m_nodeInterval = Interval( newInterval.start(), m_nodeInterval.end() );

                if ( m_leftChildNode != nullptr )
                {
                    m_leftChildNode->setLeftBoundary( newInterval );
                }
            }

            /// @return String that uniquely identifies the node within the tree.
            std::string toString() const
            {
                std::stringstream sstrThisID;
                sstrThisID << "Node interval:- " << m_nodeInterval.toString() << ", Centre=" << m_centre;
                return sstrThisID.str();
            }

            /// If the centre of the node is contained in our queryInterval then all intervals at this node intersect
            /// the queryInterval.
            // Default to using left sorted intervals
            /// If the centre is to the right of our queryInterval. Iterate ascendingly (by left_interval_value)
            //  until there is no longer intersection.
            /// If the centre is to the (strict) left of our queryInterval. Iterate descendingly (by
            /// right_interval_value)
            // until there is no longer intersecion.
            ///
            /// @param [in] queryInterval The interval which we are testing for intersection
            iterator begin( const Interval & queryInterval ) const
            {
                if ( queryInterval.overlaps( Interval( m_centre, m_centre ) ) )
                {
                    return iterator( m_allIntervals.cbegin(), m_allIntervals.cend(), iterator::iterator_type::ALL );
                }

                else if ( queryInterval.contains( m_centre ) )
                {
                    return iterator( m_intervalsLeftSorted.cbegin(), m_intervalsLeftSorted.cend(),
                                     iterator::iterator_type::ALL );
                }

                else if ( queryInterval.end() <= m_centre )
                {
                    return iterator( m_intervalsLeftSorted.cbegin(), m_intervalsLeftSorted.cend(),
                                     iterator::iterator_type::FROM_LEFT );
                }

                else if ( queryInterval.start() > m_centre )
                {
                    return iterator( m_intervalsRightSorted.cbegin(), m_intervalsRightSorted.cend(),
                                     iterator::iterator_type::FROM_RIGHT );
                }

                else
                {
                    WECALL_LOG( FATAL, "Error finding overlaps for interval " << queryInterval.toString() << " on "
                                                                               << this->toString() );
                    throw utils::wecall_exception( "Error finding overlaps for interval" );
                }
            }

        private:
            Interval
                m_nodeInterval;  ///< All intervals stored at this node and in decendants are sub-intervals of this.

            const int64_t m_centre;  ///< All intervals stored at this node contain this value.

            node_shared_ptr m_leftChildNode;   ///< Contains all sub-intervals of [m_left, m_centre)
            node_shared_ptr m_rightChildNode;  ///< Contains all sub-intervals of (m_centre, m_right)

            node_ptr m_nextAncestor;  ///< Next node when ascending the tree. If on a left-node = parent.
            // If on right-node = parent's m_nextGrandparent. If on the root = nullptr.

            left_sorted_set m_allIntervals;
            left_sorted_set
                m_intervalsLeftSorted;  ///< Intervals stored ascendingly by left-interval value. No pure insertions
            right_sorted_set
                m_intervalsRightSorted;  ///< Intervals stored descendingly by right-interval value. No pure insertions

        public:
            /// Forward iterator to iterate over either the intervals ordered by left interval value or the
            // intervals reverse ordered by right-interval value.
            /// Want copy and asignment operators to follow be the default
            class iterator : public std::iterator< std::forward_iterator_tag, interval_type >
            {

            public:
                enum class iterator_type : char
                {
                    FROM_LEFT = 1,
                    FROM_RIGHT = 2,
                    ALL = 3
                };

            public:
                iterator() : m_setIterator( NULL ), m_setEnd( NULL ), m_iteratorType( iterator_type::ALL ) {}

                explicit iterator( const set_iterator & internalIterator,
                                   const set_iterator & setEnd,
                                   const iterator_type & iteratorType )
                    : m_setIterator( internalIterator ), m_setEnd( setEnd ), m_iteratorType( iteratorType )
                {
                }

                bool isEnd( const Interval & queryInterval ) const
                {
                    if ( m_iteratorType == iterator_type::ALL )
                    {
                        return m_setIterator == m_setEnd;
                    }

                    else if ( m_iteratorType == iterator_type::FROM_LEFT )
                    {
                        return ( m_setIterator == m_setEnd or
                                 queryInterval.end() <= left_interval_value()( *m_setIterator ) );
                    }

                    else if ( m_iteratorType == iterator_type::FROM_RIGHT )
                    {
                        return ( m_setIterator == m_setEnd or
                                 right_interval_value()( *m_setIterator ) <= queryInterval.start() );
                    }

                    else
                    {
                        return true;
                    }
                }

                /// Uses the underlining multiset iterator's comparison. For looping this is done internally
                bool operator==( const iterator & other ) const { return m_setIterator == other.m_setIterator; }

                bool operator!=( const iterator & other ) const { return not( *this == other ); }

                iterator & operator++()
                {
                    ++m_setIterator;
                    return *this;
                }

                iterator operator++( int )
                {
                    iterator tmp = *this;
                    operator++();
                    return tmp;
                }

                /// Node stores shared_ptrs. So, need to dereference twice to obtain a reference to the object from an
                /// iterator
                reference operator*() const { return **m_setIterator; }
                reference operator*() { return **m_setIterator; }

                /// @return A shared_ptr to an interval contained in the node.
                pointer operator->() { return *m_setIterator; }

                /// @return The type of iteration this is performing
                iterator_type type() const { return m_iteratorType; }

            private:
                set_iterator m_setIterator;
                set_iterator m_setEnd;
                iterator_type m_iteratorType;
            };
        };  // End IntervalTreeNode declaration
    };      // End IntervalTree declaration
}
}

#endif


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

#include "foreach_gate.hpp"

#include <boost/assign/std/vector.hpp>
#include <boost/dynamic_bitset.hpp>

#include <core/utils/bitset_utils.hpp>

#include <reversible/functions/add_gates.hpp>

using namespace boost::assign;

namespace cirkit
{

/******************************************************************************
 * Types                                                                      *
 ******************************************************************************/

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/

/**
 * @param target     Target line
 * @param controls   Controls is a bit-vector of size n-1, where n is the number of lines.
 *                   It ignores the position of the target line and is adjusted by the
 *                   function.
 * @param polarities The size of this bit-vector corresponds to the number of 1's in controls.
 */
circuit create_gate_circuit( unsigned target, boost::dynamic_bitset<> controls, boost::dynamic_bitset<> polarities )
{
  circuit c( controls.size() + 1u );

  gate::control_container cont;

  /* bpos iterates over the 1's in controls, pos iterates over the positions in polarities */
  auto bpos = controls.find_first();
  auto pos = 0u;
  while ( bpos != boost::dynamic_bitset<>::npos )
  {
    auto line = bpos < target ? bpos : bpos + 1u;
    cont += make_var( line, polarities[pos] );
    bpos = controls.find_next( bpos );
    ++pos;
  }

  append_toffoli( c, cont, target );

  return c;
}

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

void foreach_gate( unsigned num_lines, bool with_negated, const std::function<void(const circuit&)>& func )
{
  assert( num_lines );

  /* iterate over all targets */
  for ( auto target = 0u; target < num_lines; ++target )
  {
    boost::dynamic_bitset<> controls( num_lines - 1u );

    do {
      auto count = controls.count();

      if ( !with_negated )
      {
        func( create_gate_circuit( target, controls, boost::dynamic_bitset<>( count, ( 1 << count ) - 1u ) ) );
      }
      else
      {
        boost::dynamic_bitset<> polarities( count );
        do {
          func( create_gate_circuit( target, controls, polarities ) );
          inc( polarities );
        } while ( !polarities.none() );
      }

      inc( controls );
    } while ( !controls.none() );
  }
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End: