#ifndef HAVE_ENTROPY_WRAPPER_HPP
#define HAVE_ENTROPY_WRAPPER_HPP

#include <cstdint>
#include <vector>

#include <boost/python.hpp>

#include "entropy_retval.hpp"
#include "entropy_chunk.hpp"
#include "entropy.hpp"

class entropy_wrapper_t {
	private:
		chunk_vec_t				m_cvec;
		entropy_retvec_t 		m_retvec;
		entropy_retval_t 		m_whole;
		bool					m_wholeDone;
		entropy_t				m_entdev;
		std::vector< uint8_t >	m_data;
		std::vector< dist_t >	m_dist;

	protected:
	public:
		entropy_wrapper_t(std::size_t bs = 8192);
		entropy_wrapper_t(const std::vector< uint8_t >&, std::size_t bs = 8192, bool whole = false);

		~entropy_wrapper_t(void);

		void reset(void);

		void setDataOverload(const std::vector< uint8_t >&);
		void setData(const std::vector< uint8_t >&, std::size_t bs = 8192, bool whole = false);

		std::size_t getCount(void);
		std::size_t getMaxIndex(void);

		void calculate(void);
		void calculate(std::size_t);
		void calculate(std::size_t, std::size_t);

		entropy_retval_t getChunkScore(std::size_t);
		entropy_retval_t getWholeFileScore(void);
		entropy_retvec_t getAllChunkScores(void);

		entropy_retval_t getDeviation(std::size_t, std::size_t);
		entropy_retvec_t getAllDeviations(std::size_t);
		entropy_retval_t getWholeDeviation(std::size_t);

		void calculateDistribution(std::size_t, std::size_t);
		dist_vec_t getDistribution(void) { return m_dist; }
};

#endif


#include <vector>

#include <boost/shared_ptr.hpp>
#include <gflags/gflags.h>
#include <glog/logging.h>

#include <cmath>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/ultinous/projective_transformer_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void ProjectiveTransformerLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {

	//std::cout<<prefix<<"Getting output_H_ and output_W_"<<std::endl;

	output_H_ = bottom[0]->shape(2);
	if(this->layer_param_.proj_trans_param().has_output_h()) {
		output_H_ = this->layer_param_.proj_trans_param().output_h();
	}
	output_W_ = bottom[0]->shape(3);
	if(this->layer_param_.proj_trans_param().has_output_w()) {
		output_W_ = this->layer_param_.proj_trans_param().output_w();
	}

	// check the validation for the parameter theta
	CHECK(bottom[1]->count(1)  == 9) << "Theta dimension does not match" << std::endl;
	CHECK(bottom[1]->shape(0) == bottom[0]->shape(0)) << "The first dimension of theta and bottom[0] should be the same" << std::endl;

	// initialize the matrix for output grid
	//std::cout<<prefix<<"Initializing the matrix for output grid"<<std::endl;

	vector<int> shape_output(2);
	shape_output[0] = output_H_ * output_W_; shape_output[1] = 3;
	output_grid.Reshape(shape_output);

	Dtype* data = output_grid.mutable_cpu_data();
	for(int i=0; i<output_H_ * output_W_; ++i) {
		data[3 * i] = (i / output_W_) * 1.0 / output_H_ * 2 - 1;
		data[3 * i + 1] = (i % output_W_) * 1.0 / output_W_ * 2 - 1;
		data[3 * i + 2] = 1;
	}

	// initialize the matrix for input grid
	//std::cout<<prefix<<"Initializing the matrix for input grid"<<std::endl;

	vector<int> shape_input(3);
	shape_input[0] = bottom[1]->shape(0); shape_input[1] = output_H_ * output_W_; shape_input[2] = 3;
	input_grid.Reshape(shape_input);

	//std::cout<<prefix<<"Initialization finished."<<std::endl;
}

template <typename Dtype>
void ProjectiveTransformerLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {

	N = bottom[0]->shape(0);
	C = bottom[0]->shape(1);
	H = bottom[0]->shape(2);
	W = bottom[0]->shape(3);

	// reshape V
	vector<int> shape(4);

	shape[0] = N;
	shape[1] = C;
	shape[2] = output_H_;
	shape[3] = output_W_;

	top[0]->Reshape(shape);

	// reshape dTheta_tmp
	vector<int> dTheta_tmp_shape(4);

	dTheta_tmp_shape[0] = N;
	dTheta_tmp_shape[1] = 9;
	dTheta_tmp_shape[2] = 1;
	dTheta_tmp_shape[3] = output_H_ * output_W_ * C;

	dTheta_tmp.Reshape(dTheta_tmp_shape);

	// init all_ones_2
	vector<int> all_ones_2_shape(1);
	all_ones_2_shape[0] = output_H_ * output_W_ * C;
	all_ones_2.Reshape(all_ones_2_shape);

	// reshape full_theta
	vector<int> full_theta_shape(2);
	full_theta_shape[0] = N;
	full_theta_shape[1] = 9;
	full_theta.Reshape(full_theta_shape);

	vector<int> shape_input(3);
	shape_input[0] = bottom[1]->shape(0); shape_input[1] = output_H_ * output_W_; shape_input[2] = 3;
	input_grid.Reshape(shape_input);
}

template <typename Dtype>
void ProjectiveTransformerLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
CHECK(false) << "ProjectiveTransformerLayer::Forward_cpu is not implemented!";
}

template <typename Dtype>
void ProjectiveTransformerLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down,
    const vector<Blob<Dtype>*>& bottom) {
	CHECK(false) << "ProjectiveTransformerLayer::Backward_cpu is not implemented!";
}

#ifdef CPU_ONLY
STUB_GPU(ProjectiveTransformerLayer);
#endif

INSTANTIATE_CLASS(ProjectiveTransformerLayer);
REGISTER_LAYER_CLASS(ProjectiveTransformer);

}  // namespace caffe


// Copyright (C) 2009  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include "../tester.h"
#include <dlib/matrix.h>

#ifndef DLIB_USE_BLAS
#error "BLAS bindings must be used for this test to make any sense"
#endif

namespace dlib
{
    namespace blas_bindings
    {
        // This is a little screwy.  This function is used inside the BLAS
        // bindings to count how many times each of the BLAS functions get called.
#ifdef DLIB_TEST_BLAS_BINDINGS
        int& counter_dot() { static int counter = 0; return counter; }
#endif

    }
}

namespace
{
    using namespace test;
    using namespace std;
    // Declare the logger we will use in this test.  The name of the logger
    // should start with "test."
    dlib::logger dlog("test.dot");

    class blas_bindings_dot_tester : public tester
    {
    public:
        blas_bindings_dot_tester (
        ) :
            tester (
                "test_dot", // the command line argument name for this test
                "Run tests for DOT routines.", // the command line argument description
                0                     // the number of command line arguments for this test
            )
        {}

        void test_mat_bindings()
        {
            using namespace dlib;
            using namespace dlib::blas_bindings;
            matrix<double,1,0> rv(10);
            matrix<double,0,1> cv(10);
            double val;

            rv = 1; cv = 1;
            counter_dot() = 0;
            val = rv*cv;
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            rv = 1; cv = 1;
            counter_dot() = 0;
            val = rv*mat(&cv(0),cv.size());
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            rv = 1; cv = 1;
            counter_dot() = 0;
            val = trans(mat(&rv(0),rv.size()))*mat(&cv(0),cv.size());
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            std::vector<double> sv(10,1);
            rv = 1;
            counter_dot() = 0;
            val = trans(mat(&rv(0),rv.size()))*mat(sv);
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(mat(sv))*mat(sv);
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            std_vector_c<double> svc(10,1);
            counter_dot() = 0;
            val = trans(mat(svc))*mat(svc);
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);

            dlib::array<double> arr(10);
            for (unsigned int i = 0; i < arr.size(); ++i)
                arr[i] = 1;
            counter_dot() = 0;
            val = trans(mat(arr))*mat(arr);
            DLIB_TEST(val == 10);
            DLIB_TEST(counter_dot() == 1);
        }

        template <typename matrix_type, typename cv_type, typename rv_type>
        void test_dot_stuff(
            matrix_type& m,
            rv_type& rv,
            cv_type& cv
        ) const
        {
            using namespace dlib;
            using namespace dlib::blas_bindings;

            rv_type rv2;
            cv_type cv2;
            matrix_type m2;
            typedef typename matrix_type::type scalar_type;
            scalar_type val;

            counter_dot() = 0;
            m2 = rv*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = rv*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = rv*3*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = rv*trans(rv)*3;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(rv*trans(rv)*3 + trans(cv)*cv);
            DLIB_TEST(counter_dot() == 2);

            counter_dot() = 0;
            val = trans(cv)*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(cv)*trans(rv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(rv,cv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(rv,colm(cv,0));
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(cv,cv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(colm(cv,0,cv.size()),colm(cv,0));
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(rv,rv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(rv,trans(rv));
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(trans(cv),cv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = dot(trans(cv),trans(rv));
            DLIB_TEST(counter_dot() == 1);

            // This does one dot and one gemv
            counter_dot() = 0;
            val = trans(cv)*m*trans(rv);
            DLIB_TEST_MSG(counter_dot() == 1, counter_dot());

            // This does one dot and two gemv
            counter_dot() = 0;
            val = (trans(cv)*m)*(m*trans(rv));
            DLIB_TEST_MSG(counter_dot() == 1, counter_dot());

            // This does one dot and two gemv
            counter_dot() = 0;
            val = trans(cv)*m*trans(m)*trans(rv);
            DLIB_TEST_MSG(counter_dot() == 1, counter_dot());
        }

        template <typename matrix_type, typename cv_type, typename rv_type>
        void test_dot_stuff_conj(
            matrix_type& ,
            rv_type& rv,
            cv_type& cv
        ) const
        {
            using namespace dlib;
            using namespace dlib::blas_bindings;

            rv_type rv2;
            cv_type cv2;
            matrix_type m2;
            typedef typename matrix_type::type scalar_type;
            scalar_type val;

            counter_dot() = 0;
            val = conj(rv)*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(conj(cv))*cv;
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(conj(cv))*trans(rv);
            DLIB_TEST(counter_dot() == 1);

            counter_dot() = 0;
            val = trans(conj(cv))*3*trans(rv);
            DLIB_TEST(counter_dot() == 1);
        }

        void perform_test (
        )
        {
            using namespace dlib;
            typedef dlib::memory_manager<char>::kernel_1a mm;

            dlog << dlib::LINFO << "test double";
            {
                matrix<double> m = randm(4,4);
                matrix<double,1,0> rv = randm(1,4);
                matrix<double,0,1> cv = randm(4,1);
                test_dot_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test float";
            {
                matrix<float> m = matrix_cast<float>(randm(4,4));
                matrix<float,1,0> rv = matrix_cast<float>(randm(1,4));
                matrix<float,0,1> cv = matrix_cast<float>(randm(4,1));
                test_dot_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<double>";
            {
                matrix<complex<double> > m = complex_matrix(randm(4,4), randm(4,4));
                matrix<complex<double>,1,0> rv = complex_matrix(randm(1,4), randm(1,4));
                matrix<complex<double>,0,1> cv = complex_matrix(randm(4,1), randm(4,1));
                test_dot_stuff(m,rv,cv);
                test_dot_stuff_conj(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<float>";
            {
                matrix<complex<float> > m = matrix_cast<complex<float> >(complex_matrix(randm(4,4), randm(4,4)));
                matrix<complex<float>,1,0> rv = matrix_cast<complex<float> >(complex_matrix(randm(1,4), randm(1,4)));
                matrix<complex<float>,0,1> cv = matrix_cast<complex<float> >(complex_matrix(randm(4,1), randm(4,1)));
                test_dot_stuff(m,rv,cv);
                test_dot_stuff_conj(m,rv,cv);
            }

            dlog << dlib::LINFO << "test double, column major";
            {
                matrix<double,0,0,mm,column_major_layout> m = randm(4,4);
                matrix<double,1,0,mm,column_major_layout> rv = randm(1,4);
                matrix<double,0,1,mm,column_major_layout> cv = randm(4,1);
                test_dot_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test float, column major";
            {
                matrix<float,0,0,mm,column_major_layout> m = matrix_cast<float>(randm(4,4));
                matrix<float,1,0,mm,column_major_layout> rv = matrix_cast<float>(randm(1,4));
                matrix<float,0,1,mm,column_major_layout> cv = matrix_cast<float>(randm(4,1));
                test_dot_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<double>, column major";
            {
                matrix<complex<double>,0,0,mm,column_major_layout > m = complex_matrix(randm(4,4), randm(4,4));
                matrix<complex<double>,1,0,mm,column_major_layout> rv = complex_matrix(randm(1,4), randm(1,4));
                matrix<complex<double>,0,1,mm,column_major_layout> cv = complex_matrix(randm(4,1), randm(4,1));
                test_dot_stuff(m,rv,cv);
                test_dot_stuff_conj(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<float>, column major";
            {
                matrix<complex<float>,0,0,mm,column_major_layout > m = matrix_cast<complex<float> >(complex_matrix(randm(4,4), randm(4,4)));
                matrix<complex<float>,1,0,mm,column_major_layout> rv = matrix_cast<complex<float> >(complex_matrix(randm(1,4), randm(1,4)));
                matrix<complex<float>,0,1,mm,column_major_layout> cv = matrix_cast<complex<float> >(complex_matrix(randm(4,1), randm(4,1)));
                test_dot_stuff(m,rv,cv);
                test_dot_stuff_conj(m,rv,cv);
            }

            test_mat_bindings();

            print_spinner();
        }
    };

    blas_bindings_dot_tester a;

}


// Copyright (C) 2012 thomas.natschlaeger@gmail.com
//
// This file is part of the ArmaNpy library.
// It is provided without any warranty of fitness
// for any purpose. You can redistribute this file
// and/or modify it under the terms of the GNU
// Lesser General Public License (LGPL) as published
// by the Free Software Foundation, either version 3
// of the License or (at your option) any later version.
// (see http://www.opensource.org/licenses for more info)

#ifndef _EXAMPLE_HPP_
#define _EXAMPLE_HPP_

/* Cmake will define ${LIBRARY_NAME}_EXPORTS on Windows when it
configures to build a shared library. If you are going to use
another build system on windows or create the visual studio
projects by hand you need to define ${LIBRARY_NAME}_EXPORTS when
building a DLL on windows.
*/
// We are using the Visual Studio Compiler and building Shared libraries

#if defined (_WIN32)
	#if defined(examplelib_EXPORTS)
		#define DLLEXPORT __declspec(dllexport)
	#else
		#define DLLEXPORT __declspec(dllimport)
	#endif
#else
	#define DLLEXPORT
#endif

#if !defined( SWIG )
    // SWIG should not see #inlcude<armadillo> as it can not handle it
	#include <armadillo>
	#include <boost/shared_ptr.hpp>
#endif

#pragma warning(disable:4251)

class DLLEXPORT Example
 {

public:
    Example( int sz ) {
        m.randn(sz,sz);
    };

    arma::mat get(void) {
        return m;
    };

    boost::shared_ptr< arma::mat > get_sptr(void) {
        boost::shared_ptr< arma::mat > p( new arma::mat( m ) );
        return p;
    };

    void set( const arma::mat& m ) {
        this->m = m;
    };

    void rnd( unsigned s) {
        this->m.randn(s,s);
    };

    void modify( arma::mat& A, unsigned r, unsigned c ) {
        A.resize( r, c );
        A.randn( r, c );
        for( unsigned i=0; i<r; i++ ) {
            for( unsigned j=0; j<c; j++ ) {
                A(i,j) = 10.0*i+j;
            }
        }
    };

    arma::mat mult( arma::mat& A, arma::mat& B )
    {
        return A * B;
    };

private:
    arma::mat m;
};

#endif


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2018 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/>.

#ifndef GRAPH_DISTANCE_SAMPLED_HH
#define GRAPH_DISTANCE_SAMPLED_HH

#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

#include <boost/python/object.hpp>
#include <boost/python/list.hpp>
#include <boost/python/extract.hpp>

#include "histogram.hh"
#include "numpy_bind.hh"
#include "hash_map_wrap.hh"

namespace graph_tool
{
using namespace std;
using namespace boost;

// retrieves the sampled vertex-vertex distance histogram

struct no_weightS {};

template <class Map>
struct get_val_type
{
    typedef typename property_traits<Map>::value_type type;
};

template <>
struct get_val_type<no_weightS>
{
    typedef size_t type;
};

struct get_sampled_distance_histogram
{

    template <class Graph, class VertexIndex, class WeightMap, class RNG>
    void operator()(const Graph& g, VertexIndex vertex_index, WeightMap weights,
                    size_t n_samples, const vector<long double>& obins,
                    python::object& phist, RNG& rng) const
    {
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;

        // select get_vertex_dists based on the existence of weights
        typedef typename mpl::if_<std::is_same<WeightMap, no_weightS>,
                                  get_dists_bfs,
                                  get_dists_djk>::type get_vertex_dists_t;

        // distance type
        typedef typename get_val_type<WeightMap>::type val_type;
        typedef Histogram<val_type, size_t, 1> hist_t;

        std::array<vector<val_type>,1> bins;
        bins[0].resize(obins.size());
        for (size_t i = 0; i < obins.size(); ++i)
            bins[0][i] = obins[i];

        hist_t hist(bins);
        SharedHistogram<hist_t> s_hist(hist);

        vector<vertex_t> sources;
        sources.reserve(num_vertices(g));
        for (auto v : vertices_range(g))
            sources.push_back(v);
        n_samples = min(n_samples, sources.size());

        typename hist_t::point_t point;
        get_vertex_dists_t get_vertex_dists;

        #pragma omp parallel for default(shared) private(point) \
            firstprivate(s_hist) schedule(runtime) \
            if (num_vertices(g) * n_samples > OPENMP_MIN_THRESH)
        for (size_t i = 0; i < n_samples; ++i)
        {
            vertex_t v;
            #pragma omp critical
            {
                uniform_int_distribution<size_t> randint(0, sources.size()-1);
                size_t j = randint(rng);
                v = sources[j];
                swap(sources[j], sources.back());
                sources.pop_back();
            }

            unchecked_vector_property_map<val_type,VertexIndex>
                dist_map(vertex_index, num_vertices(g));

            for (auto u : vertices_range(g))
                dist_map[u] = numeric_limits<val_type>::max();

            dist_map[v] = 0;
            get_vertex_dists(g, v, vertex_index, dist_map, weights);

            for (auto v2 : vertices_range(g))
            {
                if (v2 != v && dist_map[v2] != numeric_limits<val_type>::max())
                {
                    point[0] = dist_map[v2];
                    s_hist.put_value(point);
                }
            }
        }
        s_hist.gather();

        python::list ret;
        ret.append(wrap_multi_array_owned<size_t,1>(hist.get_array()));
        ret.append(wrap_vector_owned<val_type>(hist.get_bins()[0]));
        phist = ret;
    }

    // weighted version. Use dijkstra_shortest_paths()
    struct get_dists_djk
    {
        template <class Graph, class Vertex, class VertexIndex,
                  class DistanceMap, class WeightMap>
        void operator()(const Graph& g, Vertex s, VertexIndex vertex_index,
                        DistanceMap dist_map, WeightMap weights) const
        {
            dijkstra_shortest_paths(g, s, vertex_index_map(vertex_index).
                                    weight_map(weights).distance_map(dist_map));
        }
    };

    // unweighted version. Use BFS.
    struct get_dists_bfs
    {
        template <class Graph, class Vertex, class VertexIndex,
                  class DistanceMap>
        void operator()(const Graph& g, Vertex s, VertexIndex vertex_index,
                        DistanceMap dist_map, no_weightS) const
        {
            typedef typename vprop_map_t<default_color_type>::type::unchecked_t vmap_t;
            vmap_t color_map(vertex_index, num_vertices(g));
            parallel_vertex_loop
                (g,
                 [&](auto v)
                 {
                     color_map[v] = color_traits<default_color_type>::white();
                 });
            breadth_first_visit(g, s,
                                visitor(make_bfs_visitor
                                        (record_distances(dist_map,
                                                          on_tree_edge()))).
                                color_map(color_map));
        }
    };
};

} // boost namespace

#endif // GRAPH_DISTANCE_SAMPLED_HH


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

#include <apriori.h>
#include <associationrules.h>
#include <bruteforce.h>
#include <declat.h>
#include <eclat.h>
#include <transactiondatabase.h>
#include <verticaldatabase.h>

#include "msweb.h"

using std::cout;
using std::get;
using std::string;

int main(int argc, char * argv[]) {
    // parse arguments
    if (argc != 5) {
        cout << "Usage: " << argv[0] << " algorithm minsup minconf file\n"
             << "Algorithm can be brute-force, apriori, declat or eclat.\n";
        return 1;
    }
    char algorithm = argv[1][0];
    unsigned int minsup = std::stoi(argv[2]);
    double minconf = std::stod(argv[3]);
    std::string filename = argv[4];

    // load input data
    cout << "Loading input data...\n";
    TransactionDatabase td(load_msweb_file(filename));
    cout << "Loaded " << td.nItems() << " items and " << td.nTransactions()
         << " transactions.\n";
    cout << "Finding frequent itemsets...\n";

    // find frequent items
    FrequentItemsets fi;
    if (algorithm == 'a') {
        cout << "Runnint apriori algorithm...\n";
        boost::timer::auto_cpu_timer t;
        fi = apriori(td, minsup);
    } else if (algorithm == 'b') {
        cout << "Running brute-force algorithm...\n";
        boost::timer::auto_cpu_timer t;
        fi = brute_force(td, minsup);
    } else {
        VerticalDatabase vd(td);
        if (algorithm == 'd') {
            cout << "Running declat algorithm...\n";
            boost::timer::auto_cpu_timer t;
            fi = declat(vd, minsup);
        } else {
            boost::timer::auto_cpu_timer t;
            cout << "Running eclat algorithm...\n";
            fi = eclat(vd, minsup);
        }
    }

    // find association rules
    std::vector<AssociationRule> rules;
    {
        cout << "Finding association rules...\n";
        boost::timer::auto_cpu_timer t;
        rules = association_rules(fi, minconf);
    }

    // print results
    cout << "Found " << rules.size() << " association rules:\n";
    for (const auto& rule : rules) {
        td.printItemset(cout, get<0>(rule), " ");
        cout << " -> ";
        td.printItemset(cout, get<1>(rule), " ");
        cout << " (support: " << get<2>(rule)
             << ", confidence: " << get<3>(rule) << ")\n";
    }

    return 0;
}


#ifndef ATL_UTILITY_HPP
#define ATL_UTILITY_HPP
/**
 * @file /home/ryan/programming/nnet/utility.hpp
 * @author Ryan Domigan <ryan_domigan@sutdents@uml.edu>
 * Created on Apr 10, 2014
 *
 * General purpose functions and classes, mostly copied from my nnet project.
 * Boost::range could almost replace this (the Range struct is mostly what I want from here) but doesn't
 * quite do what I want.  If it gets updated to handle parallell traversal of ranges (some kind of zip functionallity)
 * I can probably ditch this file.
 */
#include <array>
#include <tuple>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <utility>
#include <limits>

#include <boost/mpl/vector.hpp>

template<long Numerator, long Denominator, class Numeric = double>
struct rational_c {

    template<class NewNumeric>
    struct change_value_type { typedef rational_c<Numerator, Denominator, NewNumeric> type; };

    typedef Numeric Num;
    constexpr static Num value = (Num)Numerator / (Num)Denominator;
    constexpr static Num value_squared = value * value;
};

////////////////////////////////
//  _____ __  __ ____  _      //
// |_   _|  \/  |  _ \| |     //
//   | | | |\/| | |_) | |     //
//   | | | |  | |  __/| |___  //
//   |_| |_|  |_|_|   |_____| //
////////////////////////////////
namespace tmpl
{
	template<size_t N>
	struct intC : public std::integral_constant<size_t, N>::type {};

	/* Well, std library has some metaprogramming stuff now, but it's missing a lot. */
	struct _void
	{
		typedef _void type;
		/* eats constructor args so I can use it to terminate
		   recursive constructors */
		template<class ... T>
		_void(T...) {}
	};

	template<class Container, class Tag = typename Container::tag_type>
	struct Size;

	template<class Container, size_t Position, class Tag = typename Container::tag_type>
	struct AtC;

	template<class T> struct Identity { typedef T type; };

	template<class T, class U = typename T::type>
	struct IsVoid : std::false_type {};

	template<class T>
	struct IsVoid<T,_void> : std::true_type {};

	namespace detail
	{
		template<class T> struct GetDepth : public intC<T::depth + 1>::type {};

		template<> struct GetDepth<_void> : public intC<0>::type {};
	}

	template<class T>
	struct Eval : public T::type {};

	template<template<class ... U> class Fn, class ... T>
	struct Apply : Fn<typename T::type...> {};

	/**********************************/
	/*  __  __ ____  _     _     _    */
	/* |  \/  |  _ \| |   (_)___| |_  */
	/* | |\/| | |_) | |   | / __| __| */
	/* | |  | |  __/| |___| \__ \ |_  */
	/* |_|  |_|_|   |_____|_|___/\__| */
	/**********************************/
	/* meta-programming-list; a list of types which can be instantiated to make a sort of tuple.
	   Having said that; maybe I should generate a std::tuple and use the boost libs.  Well, I've already got this going;
	   so sticking with it for now
	*/
	struct MPList_tag { typedef MPList_tag tag_type; };

	template<class List> struct Car : public Identity<typename List::Car> {};
	template<> struct Car<_void> : public _void {};

	template<class List> struct Cdr : public Identity<typename List::Cdr> {};
	template<> struct Cdr<_void> : public _void {};

	template<class T, class Next>
	struct Cons : public MPList_tag {
		typedef Cons<T, Next> type;

		typedef T Car;
		typedef Next Cdr;

		/* I can use the instatiated list at run-time */
		T value;
		Next next;

		static const size_t depth = detail::GetDepth<Next>::value;
	};

	template<class ... List> struct MPList;

	template<class Head, class ... Tail>
	struct MPList<Head, Tail...> : public Cons<Head, typename MPList<Tail...>::type> {};

	template<> struct MPList<> : public _void {};

	template<class Fn, class Init, class InputList>
	struct Fold {
		static_assert( std::is_same<typename InputList::type::tag_type, MPList_tag>::value
		               , "Fold requires an MPList or Cons cell.");
		typedef typename InputList::type List;
		typedef typename
		Fn::template Apply<typename Car<List>::type
		                   , typename Fold<Fn, Init, typename Cdr<List>::type>::type
		                   > type;
	};

	template<class Fn, class Init>
	struct Fold<Fn,Init,_void> { typedef typename Init::type type; };

	template<class Flatten, class List>
	struct map_MPList {
		template<class Fn>
		void apply(Fn fn, List& ll) {
			Flatten::apply(fn, ll);

			map_MPList<Flatten, typename Cdr<List>::type
			           >::apply(fn, ll.next);
		}
	};

	template<class List>
	struct Size<List, MPList_tag> {
		const static size_t value = List::depth;
	};

	template<size_t N, class NNs>
	struct ConsNums {
		typedef NNs Next;
		static const size_t value = N;
		static const size_t depth = detail::GetDepth<Next>::value;

		typedef ConsNums<N, NNs> type;
	};

	template<size_t ... Rest> struct Nums;

	template<size_t NN, size_t ... Rest>
	struct Nums<NN, Rest...> {
		typedef ConsNums<NN, typename Nums<Rest...>::type > type; };

	template<> struct Nums<> : public _void {};

	template<class List>
	struct LengthMPList : public intC<List::depth> {};

	/****************************************************/
	/*  ____            _   __        __                */
	/* |  _ \ __ _  ___| | _\ \      / / __ __ _ _ __   */
	/* | |_) / _` |/ __| |/ /\ \ /\ / / '__/ _` | '_ \  */
	/* |  __/ (_| | (__|   <  \ V  V /| | | (_| | |_) | */
	/* |_|   \__,_|\___|_|\_\  \_/\_/ |_|  \__,_| .__/  */
	/*                                          |_|     */
	/****************************************************/
	struct PackWrap_tag { typedef PackWrap_tag tag_type; };

	/* don't instantiate.  I think I have to make it instantiable to use
	   inheritance, just don't do it. */
	template<class ... Args>
	struct PackWrap : public PackWrap_tag {
		typedef PackWrap<Args ...> type;
	};

	template<class ... Args>
	struct Size< PackWrap<Args...>, PackWrap_tag>
	{
		static constexpr size_t value = sizeof...(Args);
	};

	template<class ... Args> struct PackHead : public _void {};

	template<class Head, class ... Tail>
	struct PackHead<Head, Tail...> : public Identity<Head> {};

	template <class ... Args> struct PackTail : public _void {};

	template<class Head, class ... Tail>
	struct PackTail<Head, Tail...> : public PackWrap<Tail...> {};

	template<size_t N, class ... Args>
	struct NthArg;

	template<size_t N, class Head, class ... Tail>
	struct NthArg<N, Head, Tail...>
	{
		typedef NthArg<N - 1, Tail...> type;
	};

	template<class Head, class ... Tail>
	struct NthArg<0, Head, Tail...>
	{
		typedef Head type;
	};

	template<class ... Args>
	struct Pack2Array
	{
		static constexpr std::array< typename PackHead<Args...>::type::value_type
		                             , sizeof...(Args)
		                             > value{{ Args::value... }};
	};

	/* http://loungecpp.wikidot.com/tips-and-tricks%3Aindices` */
	template<std::size_t ...Is> struct Indexer {};

	template <std::size_t N, std::size_t... Is>
	struct BuildIndicies
		: BuildIndicies<N-1, N-1, Is...>
	{};

	template <std::size_t... Is>
	struct BuildIndicies<0, Is...>
	{ typedef Indexer<Is...> type; };
}

template<class Fn, class ... Args, std::size_t... Index>
static typename std::result_of<Fn (Args...)>::type _apply_tuple(Fn& fn, std::tuple<Args...>& args, tmpl::Indexer<Index...>)
{ return fn(std::get<Index>(args)...); }

template<class Fn, class ... Args>
static typename std::result_of<Fn (Args...)>::type apply_tuple(Fn& fn, std::tuple<Args...>& args)
{
	return _apply_tuple(fn, args, typename tmpl::BuildIndicies<sizeof...(Args)>::type {});
}

/**************************************/
/*     _                              */
/*    / \   _ __ _ __ __ _ _   _ ___  */
/*   / _ \ | '__| '__/ _` | | | / __| */
/*  / ___ \| |  | | | (_| | |_| \__ \ */
/* /_/   \_\_|  |_|  \__,_|\__, |___/ */
/*                         |___/      */
/**************************************/
template<int Start = 0, int End = 0>
struct Map {
    template<class FN, class T, class ... Aux>
    static void apply(FN fn , T& dst, Aux& ... aux) {
	const static size_t last =
	    End <= 0 ? std::min( {std::tuple_size< T >::value, std::tuple_size<Aux>::value ...} ) + End
	    : End;

	for(size_t i = Start; i < last ; ++i)
	    fn(dst[i], aux[i]...);
    }
};

template<class FN, class T, class ... Aux>
void map_array(FN fn , T& dst, Aux& ... aux) { Map<>::apply(fn,dst,aux...); }

template<class A>
std::ostream& print_array(const A& aa, std::ostream& out) {
    using namespace std;
    if(aa.size() == 0)
	return cout << "[]";

    cout << "[" << aa[0];
    Map<1>::apply([&](float ff) { out << ", " << ff; }, aa);
    return cout << "]";
}

template<class T, size_t N>
std::ostream& operator<<(const std::array<T, N>& aa, std::ostream& out) {
    return print_array(aa, out);
}

template<class A>
std::ostream& print_array(const A& aa) { return print_array(aa, std::cout); }

/*****************************/
/*  _____            _       */
/* |_   _|   _ _ __ | | ___  */
/*   | || | | | '_ \| |/ _ \ */
/*   | || |_| | |_) | |  __/ */
/*   |_| \__,_| .__/|_|\___| */
/*            |_|            */
/*****************************/
template<class FN, class Tup, size_t elem = 0, size_t last = std::tuple_size<Tup>::value>
struct MapTuple {
    static void apply(Tup& tup) {
	FN::apply( std::get<elem>(tup) );
	MapTuple<FN,Tup,elem + 1, last>::apply(tup);
    } };

template<class FN, class Tup, size_t elem>
struct MapTuple<FN, Tup, elem, elem> { static void apply(Tup&) {} };

template<class FN, class Tup>
void map_tuple(Tup& tup) { MapTuple<FN, typename std::remove_reference<Tup>::type >::apply(tup); }

template<class Tup, size_t elem = 0, size_t last = std::tuple_size<Tup>::value>
struct ForeachTuple
{
	template<class Fn>
	static void apply(Fn& fn, Tup const& tup)
	{
		fn( std::get<elem>(tup) );
		ForeachTuple<Tup,elem + 1, last>::apply(fn, tup);
	}

	template<class Fn>
	static void apply(Fn& fn, Tup& tup)
	{
		fn( std::get<elem>(tup) );
		ForeachTuple<Tup,elem + 1, last>::apply(fn, tup);
	}
};

template<class Tup, size_t elem>
struct ForeachTuple<Tup, elem, elem>
{
	template<class Fn>  static void apply(Fn&, Tup const&) {}
	template<class Fn>  static void apply(Fn&, Tup&) {}
};

template<class Fn, class Tup>
void foreach_tuple(Fn& fn, Tup const& tup)
{ ForeachTuple<typename std::remove_reference<Tup>::type >::apply(fn, tup); }

template<class Tup, size_t elem = 0, size_t last = std::tuple_size<Tup>::value>
struct AndTuple
{
	template<class Fn>
	static bool apply(Fn& fn, Tup& tup)
	{
		if(fn( std::get<elem>(tup) ))
			{ return AndTuple<Tup,elem + 1, last>::apply(fn, tup); }
		else
			{ return false; }
	}
};

template<class Tup, size_t elem>
struct AndTuple<Tup, elem, elem>
{
	template<class Fn>
	static bool apply(Fn& fn, Tup& tup) { return true; }
};

/** Fold `fn` across the elements of a tuple `tup` until either
 * fn(elem) produces a falsy value or all of fn(elem) produce true.
 *
 * @tparam Fn: Particular type of fn (must be elem-type -> boolish)
 * @tparam Tup: The tuple type
 * @param fn: function that takes the type of tups element and returns bool
 * @param tup: tuple to fold across
 * @return: true of fn returns true for all the elements of tup
 */
template<class Fn, class Tup>
bool and_tuple(Fn& fn, Tup& tup)
{ return AndTuple<typename std::remove_reference<Tup>::type >::apply(fn, tup); }

/******************************/
/*   ____                _    */
/*  / ___|___  _ __  ___| |_  */
/* | |   / _ \| '_ \/ __| __| */
/* | |__| (_) | | | \__ \ |_  */
/*  \____\___/|_| |_|___/\__| */
/******************************/
template<class T>
struct GetConstIterator : public tmpl::Identity<typename T::const_iterator> {};

template<class T>
struct GetMutableIterator : public tmpl::Identity<typename T::iterator> {};

/* Tools for dealing with const */
template<class Container, class NoRef = typename std::remove_reference<Container>::type>
struct GetIterator
    : public std::conditional< std::is_const<NoRef>::value
			       , GetConstIterator<NoRef>
			       , GetMutableIterator<NoRef>
			       >::type {};

/*********************************/
/*  ____                         */
/* |  _ \ __ _ _ __   __ _  ___  */
/* | |_) / _` | '_ \ / _` |/ _ \ */
/* |  _ < (_| | | | | (_| |  __/ */
/* |_| \_\__,_|_| |_|\__, |\___| */
/*                   |___/       */
/*********************************/
struct IncItr { template<class T> static void apply(T&& t) { ++t; } };

// Not very idomatic; the operator* returns the tuple containing all the _iterators_.
// TODO: can I construct a tuple of rvalues from the tuple of iterators?
template<class ... Types>
struct RangeItr
{
    typedef std::tuple< Types ... > value_type;
    value_type _itrs;

    RangeItr() = delete; // : _itrs(Types()...) {}
    RangeItr(Types const& ... input ) : _itrs(input ...) {}

    RangeItr& operator++()
    {
        map_tuple<IncItr>(_itrs);
        return *this;
    }
    value_type& operator*() { return _itrs; }

    /* note: only compairs the _first_ element (so I can have ranges of different sizes; stick the shortest range in
       the first position) */
    bool operator!=(const RangeItr<Types...>& other)
    {
        return std::get<0>(other._itrs) != std::get<0>(_itrs);
    }
};

template<class Itr>
struct Range_base {
    typedef Itr iterator;
    typedef const Itr const_iterator;
    Itr _begin, _end;

    typedef decltype(*_begin) value_type;

    iterator& begin() { return _begin; }
    iterator& end() { return _end; }
    const iterator& begin() const { return _begin; }
    const iterator& end() const { return _end; }
    bool empty() const { return _begin == _end; }

    Range_base() = delete;
    Range_base(const Range_base&) = default;
    ~Range_base() = default;

    Range_base(Itr begin, Itr end)
	: _begin(begin), _end(end) {}

    /* don't think this makes sense for zipped ranges */
    void push_back(value_type vv) { *_end = vv; ++_end; }
    value_type& operator[](size_t pos) { return *(_begin + pos); }
    const value_type& operator[](size_t pos) const { return *(_begin + pos); }
};

template<class T>
struct RemoveConstReference
    : public std::remove_const<typename std::remove_reference<T>::type>
{};

template<bool is_const, class ... Ranges>
struct Zipper
{
    typedef RangeItr<typename std::remove_const<typename GetIterator<Ranges>::type>::type ... > Itr;
    typedef Zipper<is_const, Ranges...> zipper_type;

    struct ref_type
        : public Range_base<Itr>
    {
        typedef typename zipper_type::Itr Itr;
        typedef Range_base<Itr> base_type;

        ref_type(Ranges&& ...  pp)
            : base_type(Itr(pp.begin() ...), Itr(pp.end()...))
        {}

        ref_type(ref_type const&) = default;
    };

    struct const_type
        : public Range_base<Itr>
    {
        typedef typename zipper_type::Itr Itr;
        typedef Range_base<Itr> base_type;

        const_type(Ranges const& ...  pp)
            : base_type(Itr(pp.begin() ...), Itr(pp.end()...))
        {}

        const_type(const_type const&) = default;
    };

    typedef typename std::conditional<is_const,
                                      const_type,
                                      ref_type
                                      >::type type;
};

template<class ... Ranges>
typename Zipper<false, Ranges...>::type zip(Ranges& ... rr)
{
    return typename Zipper<false, Ranges...>::type(std::forward<Ranges>(rr)...);
}

template<class ... Ranges>
typename Zipper<true, Ranges const...>::type zip(Ranges const& ... rr)
{
    return typename Zipper<true, Ranges const...>::type(std::forward<Ranges const>(rr)...);
}

template<class IteratorType>
struct Range : public Range_base<IteratorType> {
    typedef Range<IteratorType> type;

    typedef Range_base<IteratorType> base_type;
    typedef typename base_type::iterator iterator;
    typedef typename base_type::value_type value_type;

    Range(const std::pair<iterator,iterator>& pp)
	: base_type(pp.first, pp.second) {}

    Range(iterator aa, iterator bb)
	: base_type(aa, bb) {}

    Range() = default;
    Range(const Range&) = default;
    ~Range() = default;

    size_t size() const { return base_type::end() - base_type::begin(); }
	bool empty() const { return base_type::end() == base_type::begin(); }
};

template<typename T>
struct HasBegin
{
	template<class R> struct SFINAE {};

	template<class U> static char Test
	(SFINAE<decltype(std::declval<U>().begin())>*);

    template<class U> static int Test(...);

	typedef HasBegin<T> type;
    static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};

template<class Itr>
Range<Itr> begin_end_to_range(Itr begin, Itr end) {
    return Range<Itr>(begin, end);
}

template<class Itr>
Range<Itr> make_range(Itr begin, Itr end) {
    return Range<Itr>(begin, end);
}

template<class Container>
typename std::enable_if<
	HasBegin<Container>::value,
	tmpl::Apply<Range, GetIterator<Container>>
	>::type::type
make_range(Container& cc)
{ return make_range(cc.begin(), cc.end()); }

// Make a range with the first two elements of a std::get-able thing.
template<class Tuple>
typename std::enable_if<
	!HasBegin<Tuple>::value,
	Range<typename std::tuple_element<0, Tuple>::type>
	>::type
make_range(Tuple& tuple)
{ return make_range(std::get<0>(tuple), std::get<1>(tuple)); }

template<class T>
Range<typename GetIterator<T>::type >  slice(T&& container, size_t begin) {
    return begin_end_to_range<typename GetIterator<T>::type>(container.begin() + begin, container.end());
}

template<class T>
Range<typename GetIterator<T>::type > slice(T&& tt, int begin, int end) {
    if(end < 0) return T(tt.begin() + begin, tt.end() + end);
    else return T(tt.begin() + begin, tt.begin() + end);
}

// Count up indefinitely
struct CountingRange {
    typedef size_t value_type;

    struct Counter {
	value_type _value;
	Counter(value_type vv) : _value(vv) {}
	Counter& operator++() {
	    ++_value;
	    return *this;
	}

	value_type operator*() { return _value; }

	bool operator!=(const Counter& other) const { return other._value != _value; }
    };
    typedef Counter iterator;
    typedef Counter const_iterator;

    iterator begin() const { return Counter(0); }
    iterator end() const { return Counter(std::numeric_limits<value_type>::max()); }
};

// It would be convinient to use the new for(value:container) form and
// have the value be a pointer into the container
template<class Container>
struct PointerRange {
    struct iterator {
	typedef typename Container::iterator value_type;
	value_type value;
	iterator(value_type vv) : value(vv) {}

	value_type operator*() { return value; }
	iterator& operator++() { ++value; return *this; }
	bool operator!=(const iterator& other) { return other.value != value; }
    };

    Container& container;
    PointerRange(Container& c) : container(c) {}

    iterator begin() { return iterator(container.begin()); }
    iterator end() { return iterator(container.end()); }
};

template<class Container>
PointerRange<Container> pointer_range(Container& cc) { return PointerRange<Container>(cc); }

template<class Output, class Fn, class Input>
Output map_range(Fn fn, Input in)
{
    Output out;
    for(auto& vv : in)
        out.push_back(fn(vv));

    return out;
}

namespace detail
{
	template<class T>
	struct Deref
	{ static T& a(T* item) { return *item; } };

	template<class T>
	struct Passthrough
	{
		static T&& a(T&& item) { return std::move(item); }
		static T& a(T& item) { return std::move(item); }
	};
}

template<class T, class R = typename std::remove_pointer<T>::type>
R& deref(T&& item)
{
	return std::conditional<std::is_pointer<T>::value,
	                        detail::Deref<T>,
	                        detail::Passthrough<T>
	                        >::a(item);
}

#endif


#include "SPlisHSPlasH/Common.h"
#include <Eigen/Dense>
#include "extern/cxxopts/cxxopts.hpp"
#include "Utilities/Timing.h"
#include "Utilities/OBJLoader.h"
#include "SPlisHSPlasH/Utilities/VolumeSampling.h"
#include "Utilities/PartioReaderWriter.h"
#include "Utilities/Version.h"
#include "SPlisHSPlasH/TriangleMesh.h"
#include "Discregrid/All"

using namespace SPH;
using namespace Eigen;
using namespace std;
using namespace Utilities;

INIT_TIMING
INIT_LOGGING

// Enable memory leak detection
#ifdef _DEBUG
#ifndef EIGEN_ALIGN
#define new DEBUG_NEW
#endif
#endif

void loadObj(const std::string &filename, TriangleMesh &mesh, const Vector3r &scale);

string exePath;
string inputFile = "";
string outputFile = "";
Real radius = static_cast<Real>(0.025);
Real scale = static_cast<Real>(1.0);
Real diameter = radius*static_cast<Real>(2.0);
int mode = 0;
AlignedBox3r region;
bool useRegion = false;
std::vector<Vector3r> particles;
std::array<unsigned int, 3> resolutionSDF = { 30, 30, 30 };
Vector3r bbmin, bbmax;
std::shared_ptr<Discregrid::CubicLagrangeDiscreteGrid> distanceField;
bool invert = false;

std::istream& operator >> (std::istream& istream, AlignedBox3r &r)
{
	return istream >> std::skipws >> r.min()[0] >> r.min()[1] >> r.min()[2] >> r.max()[0] >> r.max()[1] >> r.max()[2];
}

std::ostream& operator << (std::ostream& out, const AlignedBox3r& r)
{
	out << r.min()[0] << ", " << r.min()[1] << ", " << r.min()[2] << ", " << r.max()[0] << ", " << r.max()[1] << ", " << r.max()[2];
	return out;
}

std::istream& operator >> (std::istream& istream, std::array<unsigned int, 3>& r)
{
	return istream >> std::skipws >> r[0] >> r[1] >> r[2];
}

std::ostream& operator << (std::ostream& out, const std::array<unsigned int, 3>& r)
{
	out << r[0] << ", " << r[1] << ", " << r[2];
	return out;
}

// main
int main(int argc, char **argv)
{
	REPORT_MEMORY_LEAKS;

	Utilities::logger.addSink(unique_ptr<Utilities::ConsoleSink>(new Utilities::ConsoleSink(Utilities::LogLevel::INFO)));

	LOG_INFO << "Git refspec: " << GIT_REFSPEC;
	LOG_INFO << "Git SHA1: " << GIT_SHA1;
	LOG_INFO << "Git status: " << GIT_LOCAL_STATUS;

	Timing::m_dontPrintTimes = true;

	exePath = string(argv[0]);
	const size_t npos = exePath.rfind("\\");
	if (npos != string::npos)
		exePath = exePath.substr(0, exePath.rfind("\\") + 1);
	else
		exePath = ".";

	try
	{
		cxxopts::Options options(argv[0], "VolumeSampling - Sample a volumetric geometry given by an OBJ file.");

		options.add_options()
			("h,help", "Print help")
			("i,input", "Input file (obj)", cxxopts::value<std::string>())
			("o,output", "Output file (bgeo)", cxxopts::value<std::string>())
			("r,radius", "Particle radius", cxxopts::value<Real>()->default_value("0.025"))
			("s,scale", "Scaling of input geometry", cxxopts::value<Real>()->default_value("1.0"))
			("m,mode", "Mode (regular=0, almost dense=1, dense=2)", cxxopts::value<int>()->default_value("0"))
			("region", "Region to fill with particles (e.g. --region \"0 0 0 1 1 1\"", cxxopts::value<AlignedBox3r>())
			("res", "Resolution of the Signed Distance Field (e.g. --res \"30 30 30\"", cxxopts::value<std::array<unsigned int, 3>>())
			("invert", "Invert the SDF to sample the outside of the object in the bounding box/region")
			;

		auto result = options.parse(argc, argv);

		if (result.count("help"))
		{
			LOG_INFO << options.help({ "", "Group" });
			exit(0);
		}

		if (result.count("input") && result.count("output"))
		{
			inputFile = result["input"].as<std::string>();
			LOG_INFO << "Input = " << inputFile;
			outputFile = result["output"].as<std::string>();
			LOG_INFO << "Output = " << outputFile;
		}
		else
		{
			LOG_INFO << "Input or output missing!";
			LOG_INFO << options.help({ "", "Group" });
			exit(1);
		}

		if (result.count("radius"))
			radius = result["radius"].as<Real>();
		LOG_INFO << "Radius: " << radius;

		if (result.count("scale"))
			scale = result["scale"].as<Real>();
		LOG_INFO << "Scale: " << scale;

		if (result.count("mode"))
			mode = result["mode"].as<int>();
		LOG_INFO << "Mode: " << mode;

		if (result.count("region"))
		{
			region = result["region"].as<AlignedBox3r>();
			useRegion = true;
			LOG_INFO << "Region: " << region;
		}

		if (result.count("res"))
		{
			resolutionSDF = result["res"].as<std::array<unsigned int, 3>>();
			LOG_INFO << "SDF resolution: " << resolutionSDF;
		}

		if (result.count("invert"))
		{
			invert = true;
		}
	}
	catch (const cxxopts::OptionException& e)
	{
		LOG_INFO << "error parsing options: " << e.what();
		exit(1);
	}

	TriangleMesh mesh;
	loadObj(inputFile, mesh, scale*Vector3r::Ones());

	START_TIMING("Volume sampling");
	if (useRegion)
	{
		region.min() = scale * region.min();
		region.max() = scale * region.max();
		Utilities::VolumeSampling::sampleMesh(mesh.numVertices(), mesh.getVertices().data(), mesh.numFaces(), mesh.getFaces().data(),
			radius, &region, resolutionSDF, invert, mode, particles);
	}
	else
	{
		Utilities::VolumeSampling::sampleMesh(mesh.numVertices(), mesh.getVertices().data(), mesh.numFaces(), mesh.getFaces().data(),
			radius, nullptr, resolutionSDF, invert, mode, particles);
	}
	STOP_TIMING_PRINT;
	PartioReaderWriter::writeParticles(outputFile, (unsigned int)particles.size(), particles.data(), NULL, radius);

	LOG_INFO << "Generated particles: " << particles.size();

	Timing::printAverageTimes();

	return 0;
}

void loadObj(const std::string &filename, TriangleMesh &mesh, const Vector3r &scale)
{
	std::vector<OBJLoader::Vec3f> x;
	std::vector<OBJLoader::Vec3f> normals;
	std::vector<MeshFaceIndices> faces;
	OBJLoader::Vec3f s = { (float)scale[0], (float)scale[1], (float)scale[2] };
	OBJLoader::loadObj(filename, &x, &faces, &normals, nullptr, s);

	mesh.release();
	const unsigned int nPoints = (unsigned int)x.size();
	const unsigned int nFaces = (unsigned int)faces.size();
	mesh.initMesh(nPoints, nFaces);
	for (unsigned int i = 0; i < nPoints; i++)
	{
		mesh.addVertex(Vector3r(x[i][0], x[i][1], x[i][2]));
	}
	for (unsigned int i = 0; i < nFaces; i++)
	{
		// Reduce the indices by one
		int posIndices[3];
		for (int j = 0; j < 3; j++)
		{
			posIndices[j] = faces[i].posIndices[j] - 1;
		}

		mesh.addFace(&posIndices[0]);
	}

	LOG_INFO << "Number of triangles: " << nFaces;
	LOG_INFO << "Number of vertices: " << nPoints;
}


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

#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/protect.hpp>
#include <boost/mpl/bind.hpp>
#include <boost/mpl/list.hpp>

#include <cmath>

#include <boost/safe_float/policy/check_addition_overflow.hpp>
#include <boost/safe_float.hpp>

//types to be tested
using test_types=boost::mpl::list<
    float, double, long double
>;

using namespace boost::safe_float;

/**
  This test suite checks the methods of each policy and their composition.
  */

BOOST_AUTO_TEST_SUITE( safe_float_policy_test_suite )
BOOST_AUTO_TEST_SUITE( safe_float_check_policy_test_suite )

BOOST_AUTO_TEST_SUITE( safe_float_check_addition_policy_test_suite )
BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_addition_overflow, FPT, test_types){
    //define policy
    policy::check_addition_overflow<FPT> check;
    //invalid precheck returns true on both checks
    BOOST_CHECK(check.pre_addition_check(std::numeric_limits<FPT>::infinity(), (FPT) 1));
    BOOST_CHECK(check.post_addition_check(std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK(check.post_addition_check((FPT) 1));

    BOOST_CHECK(check.pre_addition_check((FPT) 1, std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK(check.post_addition_check(std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK(check.post_addition_check((FPT) 1));

    BOOST_CHECK(check.pre_addition_check(std::numeric_limits<FPT>::infinity(), std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK(check.post_addition_check(std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK(check.post_addition_check((FPT) 1));

    //accept precheck & fail postcheck returns false on post check
    BOOST_CHECK(check.pre_addition_check((FPT) 1, (FPT) 1));
    BOOST_CHECK(! check.post_addition_check(std::numeric_limits<FPT>::infinity()));

    //accept precheck & accept postcheck
    BOOST_CHECK(check.pre_addition_check((FPT) 1, (FPT) 1));
    BOOST_CHECK(check.post_addition_check((FPT) 2));

    //check error message
    BOOST_CHECK_EQUAL(check.addition_failure_message(), std::string("Overflow to infinite on addition operation"));
}

BOOST_AUTO_TEST_SUITE_END() //check addition policy

BOOST_AUTO_TEST_SUITE( safe_float_check_combined_policies_test_suite ) //check composed policies
BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_all_overflows_combined, FPT, test_types){
    safe_float<FPT, policy::check_overflow> max = std::numeric_limits<FPT>::max(); //max+max -> OV
    safe_float<FPT, policy::check_overflow> min = std::numeric_limits<FPT>::min();  // max/min  -> OV
    safe_float<FPT, policy::check_overflow> lowest = std::numeric_limits<FPT>::lowest();  // max-lowest  -> OV

    BOOST_CHECK_THROW(max+max, std::exception);
    BOOST_CHECK_THROW(max-lowest, std::exception);
    BOOST_CHECK_THROW(max*max, std::exception);
    BOOST_CHECK_THROW(max/min, std::exception);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_all_underflows_combined, FPT, test_types){
    if (std::is_same<FPT, float>()) {
        FPT a =  4.01254977e-38f;
        FPT b = -4.01254949e-38f;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_underflow> d(a);
        safe_float<FPT, policy::check_underflow> e(b);
        safe_float<FPT, policy::check_underflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_underflow> max = std::numeric_limits<FPT>::max(); // min/max -> UF
        safe_float<FPT, policy::check_underflow> min = std::numeric_limits<FPT>::min();  // min/max  -> UF
        safe_float<FPT, policy::check_underflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else if (std::is_same<FPT, double>()) {
        FPT a =  2.2250738585072019e-308;
        FPT b = -2.2250738585072014e-308;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_underflow> d(a);
        safe_float<FPT, policy::check_underflow> e(b);
        safe_float<FPT, policy::check_underflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_underflow> max = std::numeric_limits<FPT>::max(); // min/max -> UF
        safe_float<FPT, policy::check_underflow> min = std::numeric_limits<FPT>::min();  // min/max  -> UF
        safe_float<FPT, policy::check_underflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else if (std::is_same<FPT, long double>()) {
        FPT a =  3.40132972460942461217e-4932l;
        FPT b = -3.40132972460942461181e-4932l;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_underflow> d(a);
        safe_float<FPT, policy::check_underflow> e(b);
        safe_float<FPT, policy::check_underflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_underflow> max = std::numeric_limits<FPT>::max(); // min/max -> UF
        safe_float<FPT, policy::check_underflow> min = std::numeric_limits<FPT>::min();  // min/max  -> UF
        safe_float<FPT, policy::check_underflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else {
        BOOST_ERROR("underflow test only implemented for float, double and long double");
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_all_inexactity_combined, FPT, test_types){
    //addition check
    safe_float<FPT, policy::check_inexact_rounding> a(FPT(1));
    safe_float<FPT, policy::check_inexact_rounding> b(FPT(pow(2, std::numeric_limits<FPT>::digits)));
    BOOST_CHECK_THROW(a+b, std::exception);
    //subtraction check
    safe_float<FPT, policy::check_inexact_rounding> c(std::numeric_limits<FPT>::min());
    safe_float<FPT, policy::check_inexact_rounding> d((FPT)pow(2, std::numeric_limits<FPT>::digits));
    BOOST_CHECK_THROW(d-c, std::exception);

    // multiplication check
    if(std::is_same<FPT, float>()) {
        a.set_stored_value(1.64005529880523681640625f);
        b.set_stored_value(3.1559422016143798828125f);
    } else if(std::is_same<FPT, double>()){
        a.set_stored_value(1.200941392190915113502569511183537542819976806640625);
        b.set_stored_value(1.7035518365272823704259508303948678076267242431640625);
    } else if(std::is_same<FPT, long double>()) {
        a.set_stored_value(1.48057361058650153290937312444697226965217851102352142333984375L);
        b.set_stored_value(1.8352666822131742060432435525996197611675597727298736572265625L);
    } else {
        BOOST_ERROR("Test implemented only for float, double and long double");
    }
    BOOST_CHECK_THROW(a*b, std::exception);

    // division check
    safe_float<FPT, policy::check_inexact_rounding> e = FPT(1);
    safe_float<FPT, policy::check_inexact_rounding> f(FPT(1.815170982922064060217925973717001397744752466678619384765625L));
    BOOST_CHECK_THROW(e/f, std::exception);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_all_invality_combined, FPT, test_types){
    //addition check
    safe_float<FPT, policy::check_invalid_result> a(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_invalid_result> b(-(std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK_THROW(a+b, std::exception);
    //subtraction check
    safe_float<FPT, policy::check_invalid_result> c(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_invalid_result> d((std::numeric_limits<FPT>::infinity()));
    BOOST_CHECK_THROW(c-d, std::exception);
    //multiplication check
    safe_float<FPT, policy::check_invalid_result> e(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_invalid_result> f(FPT(0));
    BOOST_CHECK_THROW(e*f, std::exception);
    //division check
    safe_float<FPT, policy::check_invalid_result> g(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_invalid_result> h(std::numeric_limits<FPT>::infinity());
    BOOST_CHECK_THROW(g/h, std::exception);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_bothflows_combined, FPT, test_types){
    safe_float<FPT, policy::check_bothflow> max = std::numeric_limits<FPT>::max(); //max+max -> OV
    safe_float<FPT, policy::check_bothflow> min = std::numeric_limits<FPT>::min();  // max/min  -> OV
    safe_float<FPT, policy::check_bothflow> lowest = std::numeric_limits<FPT>::lowest();  // max-lowest  -> OV

    BOOST_CHECK_THROW(max+max, std::exception);
    BOOST_CHECK_THROW(max-lowest, std::exception);
    BOOST_CHECK_THROW(max*max, std::exception);
    BOOST_CHECK_THROW(max/min, std::exception);

    if (std::is_same<FPT, float>()) {
        FPT a =  4.01254977e-38f;
        FPT b = -4.01254949e-38f;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_bothflow> d(a);
        safe_float<FPT, policy::check_bothflow> e(b);
        safe_float<FPT, policy::check_bothflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_bothflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else if (std::is_same<FPT, double>()) {
        FPT a =  2.2250738585072019e-308;
        FPT b = -2.2250738585072014e-308;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_bothflow> d(a);
        safe_float<FPT, policy::check_bothflow> e(b);
        safe_float<FPT, policy::check_bothflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_bothflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else if (std::is_same<FPT, long double>()) {
        FPT a =  3.40132972460942461217e-4932l;
        FPT b = -3.40132972460942461181e-4932l;
        //check the addition produces an denormal result (considered underflow)
        FPT c = a + b;
        BOOST_CHECK( std::fpclassify( c ) == FP_SUBNORMAL ) ;

        // construct safe_float version of the same two numbers
        safe_float<FPT, policy::check_bothflow> d(a);
        safe_float<FPT, policy::check_bothflow> e(b);
        safe_float<FPT, policy::check_bothflow> ne(-b);

        // check the addition throws
        BOOST_CHECK_THROW(d+e, std::exception);

        //check subtraction throws
        BOOST_CHECK_THROW(d-ne, std::exception);

        //check multiplication underflow throws
        safe_float<FPT, policy::check_bothflow> half = FPT(0.5);
        BOOST_CHECK_THROW(min*half, std::exception);

        //check division underflow to zero
        BOOST_CHECK_THROW(min/max, std::exception);

    } else {
        BOOST_ERROR("bothflow test only implemented for float, double and long double");
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_check_all_combined, FPT, test_types){
    //addition overflow
    safe_float<FPT, policy::check_all> max = std::numeric_limits<FPT>::max(); //max+max -> OV
    BOOST_CHECK_THROW(max+max, std::exception);
    //subtraction inexact result
    safe_float<FPT, policy::check_all> a(std::numeric_limits<FPT>::min());
    safe_float<FPT, policy::check_all> b((FPT)pow(2, std::numeric_limits<FPT>::digits));
    BOOST_CHECK_THROW(a-b, std::exception);
    //multiplication invalid result
    safe_float<FPT, policy::check_all> c(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_all> d(FPT(0));
    BOOST_CHECK_THROW(c*d, std::exception);
    //division by zero
    // construct safe_float producing divide by zero
    safe_float<FPT, policy::check_all> e(FPT(1));
    safe_float<FPT, policy::check_all> f(FPT(0));
    // check the division throws
    BOOST_CHECK_THROW(e/f, std::exception);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_check_all_error_message, FPT, test_types)
{
    // addition overflow
    safe_float<FPT, policy::check_all> max = std::numeric_limits<FPT>::max(); // max+max -> OV
    try
    {
        max + max;
        BOOST_ERROR("An exception is supposed to be thrown");
    }
    catch (std::exception& e)
    {
        BOOST_CHECK_EQUAL(e.what(), "Overflow to infinite on addition operation");
    }
    // subtraction inexact result
    safe_float<FPT, policy::check_all> a(std::numeric_limits<FPT>::min());
    safe_float<FPT, policy::check_all> b((FPT)pow(2, std::numeric_limits<FPT>::digits));
    try
    {
        a - b;
        BOOST_ERROR("An exception is supposed to be thrown");
    }
    catch (std::exception& e)
    {
        BOOST_CHECK_EQUAL(e.what(), "Non reversible subtraction applied");
    }
    // multiplication invalid result
    safe_float<FPT, policy::check_all> c(std::numeric_limits<FPT>::infinity());
    safe_float<FPT, policy::check_all> d(FPT(0));
    try
    {
        c* d;
        BOOST_ERROR("An exception is supposed to be thrown");
    }
    catch (std::exception& e)
    {
        BOOST_CHECK_EQUAL(e.what(), "Invalid result from arithmetic operation obtained");
        // Fail if fenv isn't available because inexact error comes first
    }
    // division by zero
    // construct safe_float producing divide by zero
    safe_float<FPT, policy::check_all> e(FPT(1));
    safe_float<FPT, policy::check_all> f(FPT(0));
    // check the division throws
    try
    {
        e / f;
        BOOST_ERROR("An exception is supposed to be thrown");
    }
    catch (std::exception& e)
    {
        BOOST_CHECK_EQUAL(e.what(), "Division by zero");
    }
}

template<typename FP>
struct policy1 {};
template<typename FP>
struct policy2 {};
template<typename FP>
struct policy3 {};
template<typename FP>
struct policy4 {};

template<typename FP>
using composed1 = boost::safe_float::policy::compose_check<policy1, policy2>::policy<FP>;
template<typename FP>
using composed2 = boost::safe_float::policy::compose_check<policy3, policy4>::policy<FP>;

template<typename FP>
using composed_all = boost::safe_float::policy::compose_check<composed1, composed2>::policy<FP>;

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_check_composed_type, FPT, test_types)
{
    using boost::safe_float::policy::compose_check;

    // Composition of composition is flattened
    BOOST_CHECK((std::is_same_v<composed_all<FPT>,
                                compose_check<policy1, policy2, policy3, policy4>::policy<FPT>>));

    BOOST_CHECK((std::is_same_v<compose_check<composed1, policy3>::policy<FPT>,
                                compose_check<policy1, policy2, policy3>::policy<FPT>>));

    // Duplicates are removed
    BOOST_CHECK((std::is_same_v<compose_check<policy2, composed2, composed_all>::policy<FPT>,
                                compose_check<policy2, policy3, policy4, policy1>::policy<FPT>>));
}

BOOST_AUTO_TEST_SUITE_END() //check composed policies

BOOST_AUTO_TEST_SUITE_END() //check policy

BOOST_AUTO_TEST_SUITE_END() // policy


//  (C) Copyright Gennadiy Rozental 2001-2015.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : tests floating point comparison algorithms
// ***************************************************************************

// Boost.Test
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

// Boost
#include <boost/mpl/list.hpp>
#include <boost/bind.hpp>

// STL
#include <functional>

using namespace boost;
using namespace boost::unit_test;
using namespace boost::test_tools;
namespace tt=boost::test_tools;

bool not_func( bool argb ) { return !argb; }

//____________________________________________________________________________//

typedef boost::mpl::list<float,double,long double> test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE( test_fp_comparizon_with_percent_tolerance, FPT, test_types )
{
#define CHECK_CLOSE( first, second, e )     \
    fp1     = static_cast<FPT>(first);      \
    fp2     = static_cast<FPT>(second);     \
    epsilon = static_cast<FPT>(e);          \
                                            \
    BOOST_TEST( fp1 == fp2, epsilon% tt::tolerance() ) \
/**/

#define CHECK_NOT_CLOSE( first, second, e ) \
    fp1     = static_cast<FPT>(first);      \
    fp2     = static_cast<FPT>(second);     \
    epsilon = static_cast<FPT>(e);          \
                                            \
    BOOST_TEST( fp1 != fp2, epsilon% tt::tolerance() ) \
/**/

    FPT fp1, fp2, epsilon;

    CHECK_CLOSE( 1, 1, 0 );

    CHECK_CLOSE( 0, 1e-20, 1e-5 );
    CHECK_CLOSE( 0, 1e-30, 1e-5 );
    CHECK_CLOSE( 0, -1e-10, 0.1 );
    CHECK_NOT_CLOSE( 0.123456, 0.123457, 1e-4 );

    CHECK_CLOSE( 0.123456, 0.123457, 1e-3 );

    CHECK_NOT_CLOSE( 0.123456, -0.123457, 1e-3 );

    CHECK_CLOSE( 1.23456e28, 1.23457e28, 1e-3 );

    CHECK_CLOSE( 1.23456e-10, 1.23457e-10, 1e-3 );
    CHECK_NOT_CLOSE( 1.111e-10, 1.112e-10, 0.0899 );
    CHECK_CLOSE( 1.112e-10, 1.111e-10, 0.1 );

    CHECK_CLOSE( 1, 1.0001, 1.1e-2 );
    CHECK_CLOSE( 1.0002, 1.0001, 1.1e-2 );

    CHECK_NOT_CLOSE( 1, 1.0002, 1.1e-2 );

#undef CHECK_CLOSE
#undef CHECK_NOT_CLOSE
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE_TEMPLATE( test_fp_comparizon_with_fraction_tolerance, FPT, test_types )
{
#define CHECK_CLOSE( first, second, e )     \
    fp1     = static_cast<FPT>(first);      \
    fp2     = static_cast<FPT>(second);     \
    epsilon = static_cast<FPT>(e);          \
                                            \
    BOOST_TEST( fp1 == fp2, tt::tolerance(epsilon) ); \
/**/

#define CHECK_NOT_CLOSE( first, second, e ) \
    fp1     = static_cast<FPT>(first);      \
    fp2     = static_cast<FPT>(second);     \
    epsilon = static_cast<FPT>(e);          \
                                            \
    BOOST_TEST( fp1 != fp2, tt::tolerance(epsilon) ); \
/**/

    FPT fp1, fp2, epsilon;

    CHECK_CLOSE( 1, 1, 0 );

    CHECK_CLOSE( 0, 1e-20, 1e-5 );
    CHECK_CLOSE( 0, 1e-30, 1e-5 );
    CHECK_CLOSE( 0, -1e-10, 0.1 );
    CHECK_NOT_CLOSE( 0.123456, 0.123457, 1e-6 );

    CHECK_CLOSE( 0.123456, 0.123457, 1e-3 );

    CHECK_NOT_CLOSE( 0.123456, -0.123457, 1e-3 );

    CHECK_CLOSE( 1.23456e28, 1.23457e28, 1e-3 );

    CHECK_CLOSE( 1.23456e-10, 1.23457e-10, 1e-3 );
    CHECK_NOT_CLOSE( 1.111e-10, 1.112e-10, 0.000899 );
    CHECK_CLOSE( 1.112e-10, 1.111e-10, 0.1 );

    CHECK_CLOSE( 1, 1.0001, 1.1e-2 );
    CHECK_CLOSE( 1.0002, 1.0001, 1.1e-2 );

    CHECK_NOT_CLOSE( 1, 1.0002, 1.1e-4 );

#undef CHECK_CLOSE
#undef CHECK_NOT_CLOSE
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_type_mismatch )
{
    BOOST_CHECK_CLOSE_FRACTION( 2., 2.1, 0.06 );
    BOOST_CHECK_CLOSE( 2.1, 2., 6. );
    BOOST_CHECK_CLOSE( 2.1, 2.f, 6. );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_strong_weak, * expected_failures( 4 ) )
{
    BOOST_TEST(1./3 == 1./2, tt::tolerance(1.));
    BOOST_TEST(1./3 == 1./2, tt::tolerance(0.4));  // will fail 1/2 > 0.4
    BOOST_TEST(1./3 == 1./2, tt::tolerance(1./3)); // will fail; both 1/3 and 1/2 > 1/3
    BOOST_TEST(1./3 != 1./2, tt::tolerance(1.));   // will fail; both 1/3 and 1/2 < 1
    BOOST_TEST(1./3 != 1./2, tt::tolerance(0.4));  // will fail 1/3 < 0.4
    BOOST_TEST(1./3 != 1./2, tt::tolerance(1./3));
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_ne,
                     * boost::unit_test::tolerance(0.01)
                     * expected_failures( 1 ) )
{
    double x = 10.00;
    double y = 10.01; // diff within tolerance
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x <= y);
    BOOST_TEST(x >= y);
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_lt,
                     * boost::unit_test::tolerance(0.01)
                     * expected_failures( 3 ) )
{
    double x = 10.00;
    double y = 10.01; // diff within tolerance
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x <= y);
    BOOST_TEST(y <= x);
    BOOST_TEST(x < y);  // fails  y ~= x
    BOOST_TEST(y < x);  // fails, y > x
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_lt_0,
                     * boost::unit_test::tolerance(0.02)
                     * expected_failures( 3 ) )
{
    double x = 0.00;
    double y = 0.01;
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x <= y);
    BOOST_TEST(y <= x);
    BOOST_TEST(x < y);  // fails, too close to 0
    BOOST_TEST(y < x);  // fails, y > x
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_le,
                     * boost::unit_test::tolerance(0.01)
                     * expected_failures( 1 ) )
{
    double x = 10.01;
    double y = 10.00; // diff within tolerance
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x <= y);
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_gt,
                     * boost::unit_test::tolerance(0.01)
                     * expected_failures( 3 ) )
{
    double x = 10.00;
    double y = 10.01; // diff within tolerance
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x > y);  // fails
    BOOST_TEST(y > x);  // fails
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_ge,
                     * boost::unit_test::tolerance(0.01)
                     * expected_failures( 1 ) )
{
    double x = 10.00;
    double y = 10.01; // diff within tolerance
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x >= y);
    BOOST_TEST(y >= x);
}

BOOST_AUTO_TEST_CASE(test_strict_order_operations_gt_0,
                     * boost::unit_test::tolerance(0.02)
                     * expected_failures( 3 ) )
{
    double x = 0.00;
    double y = 0.01;
    BOOST_TEST(x == y);
    BOOST_TEST(x != y); // fails
    BOOST_TEST(x >= y);
    BOOST_TEST(y >= x);
    BOOST_TEST(x <= y);
    BOOST_TEST(y <= x);
    BOOST_TEST(x > y);  // fails, too close to 0
    BOOST_TEST(y > x);  // fails, too close to 0
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_CHECK_SMALL )
{
    BOOST_CHECK_SMALL( 1e-6, 1e-5 );
    BOOST_CHECK_SMALL( -1e-6, 1e-5 );

    BOOST_TEST( 1e-6 != 0., 1e-7 );
}

//____________________________________________________________________________//

namespace fpc = boost::math::fpc;

BOOST_AUTO_TEST_CASE( test_close_at_tolerance )
{
    double fp1     = 1.00000001;
    double fp2     = 1.00000002;
    double epsilon = 1e-6;

    ::fpc::close_at_tolerance<double> pred( ::fpc::percent_tolerance( epsilon ), ::fpc::FPC_WEAK );
    BOOST_CHECK_PREDICATE( pred, (fp1)(fp2) );

#ifndef BOOST_TEST_NO_OLD_TOOLS
    BOOST_TEST( !check_is_close( fp1, fp2, ::fpc::percent_tolerance( epsilon ) ) );
#endif

    fp1     = 1.23456e-10;
    fp2     = 1.23457e-10;
    epsilon = 8.1e-4;

    BOOST_CHECK_PREDICATE( ::fpc::close_at_tolerance<double>( ::fpc::percent_tolerance( epsilon ), ::fpc::FPC_WEAK ), (fp1)(fp2) );

    BOOST_TEST( !::fpc::close_at_tolerance<double>( ::fpc::percent_tolerance( epsilon ) )(fp1, fp2) );
}

BOOST_AUTO_TEST_CASE( test_comparison_if_one_is_FPV, * boost::unit_test::tolerance(1E-6) )
{
  BOOST_TEST(1.000001 == 1);
  BOOST_TEST(1 == 1.000001);

  BOOST_TEST(0.000001 == 0);
  BOOST_TEST(0 == 0.000001);
}

BOOST_AUTO_TEST_CASE( test_comparison_if_one_is_FPV_2 )
{
  BOOST_TEST(1.000001 == 1, tt::tolerance(1E-6));
  BOOST_TEST(1 == 1.000001, tt::tolerance(1E-6));

  BOOST_TEST(0.000001 == 0, tt::tolerance(1E-6));
  BOOST_TEST(0 == 0.000001, tt::tolerance(1E-6));
}

BOOST_AUTO_TEST_CASE( test_check_close )
{
  // GH-162 BOOST_CHECK_CLOSE(0, smallnumber) fails
  BOOST_CHECK_SMALL(-4.37113883e-08, 1.);
  // does not compile with the old tools disabled
  // never compiled with the old tools
  //BOOST_CHECK_SMALL(-4.37113883e-08, 1);
}

// EOF


// Copyright 2010 Thomas Moulard.
//

#include <string>

#include <boost/foreach.hpp>
#include <boost/format.hpp>

#include <Eigen/Dense>

#include <dynamic-graph/debug.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/eigen-io.h>
#include <dynamic-graph/linear-algebra.h>
#include <dynamic-graph/signal-caster.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-cast-helper.h>

#include "signal-cast-registerer-libA.hh"
#include "signal-cast-registerer-libB.hh"

#define BOOST_TEST_MODULE signal_cast_registerer

#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include <iostream>

using boost::test_tools::output_test_stream;

typedef Eigen::VectorXd Vector;
typedef Eigen::MatrixXd Matrix;

struct EigenCastRegisterer_V : public dynamicgraph::SignalCastRegisterer
{
  typedef Vector bnuVector;

  EigenCastRegisterer_V () :
    SignalCastRegisterer
    (typeid(bnuVector), dispVector, castVector, traceVector)
  {}

  static boost::any castVector (std::istringstream& iss)
  {
    bnuVector res;
    iss >> res;
    return res;
  }

  static void dispVector (const boost::any& object, std::ostream& os)
  {
    const bnuVector& v = boost::any_cast<bnuVector> (object);
    os << "[ ";
    for (int i = 0; i < v.size (); ++i)
      os << v(i) << " ";
    os << " ];" << std::endl;
  }

  static void traceVector (const boost::any& object, std::ostream& os)
  {
    const bnuVector& v = boost::any_cast<bnuVector> (object);
    for (int i = 0; i < v.size (); ++i)
      os << v(i) << " ";
    os << std::endl;
  }
};

template<typename Derived>
struct EigenCastRegisterer_M : public dynamicgraph::SignalCastRegisterer
{
    typedef Matrix bnuMatrix;

    EigenCastRegisterer_M () :
        SignalCastRegisterer
        (typeid(bnuMatrix), dispMatrix, castMatrix, traceMatrix)
    {}

    static boost::any castMatrix (std::istringstream& iss)
    {
        bnuMatrix res;
        iss >> res;
        return res;
    }

    static void dispMatrix (const boost::any& object, std::ostream& os)
    {
        const bnuMatrix& m = boost::any_cast<bnuMatrix> (object);
        os << "[ ";
        for (int i = 0; i < m.rows(); ++i){
            os << "[ ";
            for (int j = 0; j < m.cols(); ++j) {
                os << m(i, j) << " ";
            }
            if (i != m.rows()-1){
                os << "]; ";
            }
            else{
                os << "] ";
            }
        }
        os << " ];" << std::endl;
    }

    static void traceMatrix (const boost::any& object, std::ostream& os)
    {
        const bnuMatrix& m = boost::any_cast<bnuMatrix> (object);
        for (int i = 0; i < m.rows(); ++i){
            for (int j = 0; j < m.cols(); ++j){
                os << m(i,j) << " ";
            }
        }
        os << std::endl;
    }
};

EigenCastRegisterer_V myVectorCast;
EigenCastRegisterer_M<int> myMatrixCast;

// Define a new cast with a type that supports streaming operators to
// and from it (this could be automated with macros).
dynamicgraph::DefaultCastRegisterer<bool> myBooleanCast;

// Check standard double cast registerer.
BOOST_AUTO_TEST_CASE (standard_double_registerer)
{
  dynamicgraph::Signal<double, int> mySignal("out");

  typedef std::pair<std::string, std::string> test_t;
  std::vector<test_t> values;

  values.push_back (std::make_pair ("42.0", "42\n"));
  values.push_back (std::make_pair ("42.5", "42.5\n"));
  values.push_back (std::make_pair ("-12.", "-12\n"));

  // Double special values.
  // FIXME: these tests are failing :(
  values.push_back (std::make_pair ("inf", "inf\n"));
  values.push_back (std::make_pair ("-inf", "-inf\n"));
  values.push_back (std::make_pair ("nan", "nan\n"));

  BOOST_FOREACH(const test_t& test, values)
    {
      // Set
      std::istringstream value (test.first);
      mySignal.set (value);

      // Get
      {
	output_test_stream output;
	mySignal.get (output);
	BOOST_CHECK (output.is_equal (test.second));
      }

      // Trace
      {
	output_test_stream output;
	mySignal.trace (output);
	BOOST_CHECK (output.is_equal (test.second));

      }
    }

  // Check invalid values.
  // FIXME: this test is failing for now.
  std::istringstream value ("This is not a valid double.");
  BOOST_CHECK_THROW (mySignal.set (value), std::exception);
}

// Check a custom cast registerer for Boost uBLAS vectors.
BOOST_AUTO_TEST_CASE (custom_vector_registerer)
{

  dynamicgraph::Signal<dynamicgraph::Vector, int> myVectorSignal("vector");

  // Print the signal name.
  {
    output_test_stream output;
    output << myVectorSignal;
    BOOST_CHECK (output.is_equal ("Sig:vector (Type Cst)"));
  }

  for (unsigned int i = 0; i < 5; ++i)
    {
      Vector  v = Vector::Unit(5,i) ;
      std::ostringstream os;
      os << v;
      std::istringstream ss ("[5]("+os.str ()+")");

      // Set signal value.
      myVectorSignal.set (ss);

      // Print out signal value.
      output_test_stream output;
      myVectorSignal.get (output);

      boost::format fmt ("[ %d %d %d %d %d  ];\n");
      fmt
	% (i == 0)
	% (i == 1)
	% (i == 2)
	% (i == 3)
	% (i == 4);

      BOOST_CHECK (output.is_equal (fmt.str ()));
    }

  //Catch Exception of ss (not good input)

  //ss[0] != "["
  try {
      std::istringstream ss ("test");
      myVectorSignal.set (ss);
  } catch(ExceptionSignal e) {
      std::cout << "Test passed : ss[0] != \"[\"";
  }

  //ss[1] != %i
    try {
        std::istringstream ss ("[test");
        myVectorSignal.set (ss);
    } catch(ExceptionSignal e) {
        std::cout << "Test passed : ss[1] != %i";
    }

  //ss[2] != "]"
    try {
        std::istringstream ss ("[5[");
        myVectorSignal.set (ss);
    } catch(ExceptionSignal e) {
        std::cout << "Test passed : ss[2] != \"]\"";
    }

    //ss[3] != "("
    try {
        std::istringstream ss ("[5]test");
        myVectorSignal.set (ss);
    } catch(ExceptionSignal e) {
        std::cout << "Test passed : ss[3] != \"(\"";
    }

    //ss[4] != ' ' || ','
    try {
        std::istringstream ss ("[5](1, ");
        myVectorSignal.set (ss);
    } catch(ExceptionSignal e) {
        BOOST_ERROR("Can't happened");
    }

    //ss[-1] != ")"
    try {
        std::istringstream ss ("[5](1,2,3,4,5]");
        myVectorSignal.set (ss);
    } catch(ExceptionSignal e) {
        std::cout << "Test passed : ss[-1] != \")\"";
    }
}

BOOST_AUTO_TEST_CASE (custom_matrix_registerer) {

    dynamicgraph::Signal<dynamicgraph::Matrix, int> myMatrixSignal("matrix");

    // Print the signal name.
    {
        output_test_stream output;
        output << myMatrixSignal;
        BOOST_CHECK (output.is_equal ("Sig:matrix (Type Cst)"));
    }

    //Catch Exception of ss (not good input)

    //ss[0] != "["
    try {
        std::istringstream ss("test");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[0] != \"[\"";
    }

    //ss[1] != %i
    try {
        std::istringstream ss("[test");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[1] != %i";
    }

    //ss[2] != ","
    try {
        std::istringstream ss("[5[");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[2] != \",\"";
    }

    //ss[3] != %i
    try {
        std::istringstream ss("[5,c");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[3] != %i";
    }

    //ss[4] != "]"
    try {
        std::istringstream ss("[5,3[");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[4] != \"]\"";
    }

    //ss[5] != "("
    try {
        std::istringstream ss("[5,3]test");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[5] != \"(\"";
    }

    //ss[6] != "("
    try {
        std::istringstream ss("[5,3](test");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[6] != \"(\"";
    }

    //ss[8] != " " || ","
    try {
        std::istringstream ss("[5,3]((1,");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        BOOST_ERROR("Can't happened");
    }

    //ss[6+n] != ")"
    try {
        std::istringstream ss("[5,3]((1,2,3]");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << ("ss[6+n] != \")\"");
    }

    //ss[-3] != ")"
    try {
        std::istringstream ss("[5,1]((1)(2)(3[");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[5] != \")\"";
    }

    //ss[-3] != ")"
    try {
        std::istringstream ss("[5,1]((1)(2)(3)[");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[5] != \")\"";
    }

    //ss[-1]!= ")"
    try {
        std::istringstream ss("[3,1]((1)(2),(3)[");
        myMatrixSignal.set(ss);
    } catch (ExceptionSignal e) {
        std::cout << "Test passed : ss[5] != \")\" and ignore \",\"";
    }

    //[...]((...))
}

// One issue with the strategy used by the
// dynamicgraph::SignalCastRegisterer is that it relies on the
// typeid. In practice, it means that two signals defined in two
// different libraries will have different typeid and one will not be
// able to plug one into the other unless the symbol have merged when
// the plug-in is loaded. See man(3) dlopen in Linux for more
// information about plug-in loading and the RTLD_GLOBAL flag
// necessary to make cast registerer work as expected.
//
// Here we make sure that two instances of the same type
// declared in two separate libraries are resolved into the
// same typeid.
/*BOOST_AUTO_TEST_CASE (typeid_issue)
{
  BOOST_CHECK (typeid(vA) == typeid(vB));
  BOOST_CHECK_EQUAL (std::string (typeid(vA).name ()),
		     std::string (typeid(vB).name ()));
}*/


// This comes from the following Boost example:
// https://www.boost.org/doc/libs/1_72_0/doc/html/chrono/users_guide.html#chrono.users_guide.examples
#include <boost/chrono.hpp>
#include <iostream>
#include <cmath>

#if defined(BOOST_NAMESPACE)
namespace boost = BOOST_NAMESPACE;
#endif

int main()
{
    boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now();

    for ( long i = 0; i < 10000000; ++i )
    std::sqrt( 123.456L ); // burn some time

    boost::chrono::duration<double> sec = boost::chrono::system_clock::now() - start;
    std::cout << "took " << sec.count() << " seconds\n";
    return 0;
}


#include <iostream>
#include <memory>
#include <string>
#include <chrono>

#include <boost/program_options.hpp>
#include <algorithms/Grover.hpp>
#include <algorithms/QFT.hpp>
#include <algorithms/Entanglement.hpp>

#include "Simulator.hpp"
#include "QFRSimulator.hpp"
#include "GroverSimulator.hpp"
#include "ShorFastSimulator.hpp"
#include "ShorSimulator.hpp"

int main(int argc, char** argv) {
    namespace po = boost::program_options;
    unsigned long long seed;

    po::options_description description("JKQ DDSIM by https://iic.jku.at/eda/ -- Allowed options");
    description.add_options()
            ("help,h", "produce help message")
            ("seed", po::value<unsigned long long>(&seed)->default_value(0), "seed for random number generator (default zero is possibly directly used as seed!)")
            ("shots", po::value<unsigned int>()->default_value(0), "number of measurements (if the algorithm does not contain non-unitary gates, weak simulation is used)")
            ("display_vector", "display the state vector")
            ("ps", "print simulation stats (applied gates, sim. time, and maximal size of the DD)")
            ("verbose", "Causes some simulators to print additional information to STDERR")
            ("benchmark", "print simulation stats in a single CSV style line (overrides --ps and suppresses most other output, please don't rely on the format across versions)")

            ("simulate_file", po::value<std::string>(), "simulate a quantum circuit given by file (detection by the file extension)")
            ("simulate_qft", po::value<unsigned int>(), "simulate Quantum Fourier Transform for given number of qubits")
            ("simulate_ghz", po::value<unsigned int>(), "simulate state preparation of GHZ state for given number of qubits")
            ("step_fidelity", po::value<double>()->default_value(1.0), "target fidelity for each approximation run (>=1 = disable approximation)")
            ("steps", po::value<unsigned int>()->default_value(1), "number of approximation steps")
            ("initial_reorder", po::value<int>()->default_value(0), "Try to find a good initial variable order (0=None, 1=Most affected qubits to the top, 2=Most affected targets to the top)")
            ("dynamic_reorder", po::value<int>()->default_value(0), "Apply reordering strategy during simulation (0=None, 1=Sifting, 2=Move2Top)")
            ("post_reorder", po::value<int>()->default_value(0), "Apply a reordering strategy after simulation (0=None, 1=Sifting)")

            ("simulate_grover", po::value<unsigned int>(), "simulate Grover's search for given number of qubits with random oracle")
            ("simulate_grover_emulated", po::value<unsigned int>(), "simulate Grover's search for given number of qubits with random oracle and emulation")
            ("simulate_grover_oracle_emulated", po::value<std::string>(), "simulate Grover's search for given number of qubits with given oracle and emulation")

            ("simulate_shor", po::value<unsigned int>(), "simulate Shor's algorithm factoring this number")
            ("simulate_shor_coprime", po::value<unsigned int>()->default_value(0), "coprime number to use with Shor's algorithm (zero randomly generates a coprime)")
            ("simulate_shor_no_emulation", "Force Shor simulator to do modular exponentiation instead of using emulation (you'll usually want emulation)")

            ("simulate_fast_shor", po::value<unsigned int>(), "simulate Shor's algorithm factoring this number with intermediate measurements")
            ("simulate_fast_shor_coprime", po::value<unsigned int>()->default_value(0), "coprime number to use with Shor's algorithm (zero randomly generates a coprime)")
            ;
    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, description), vm);

        if (vm.count("help")) {
            std::cout << description;
            return 0;
        }
        po::notify(vm);
    } catch (const po::error &e) {
        std::cerr << "[ERROR] " << e.what() << "! Try option '--help' for available commandline options.\n";
        std::exit(1);
    }

    std::unique_ptr<qc::QuantumComputation> quantumComputation;
    std::unique_ptr<Simulator> ddsim{nullptr};

    if (vm.count("simulate_file")) {
        const std::string fname = vm["simulate_file"].as<std::string>();
    	quantumComputation = std::make_unique<qc::QuantumComputation>(fname);
        ddsim = std::make_unique<QFRSimulator>(quantumComputation,
                                               vm["steps"].as<unsigned int>(), vm["step_fidelity"].as<double>(),
                                               vm["initial_reorder"].as<int>(), vm["dynamic_reorder"].as<int>(), vm["post_reorder"].as<int>(),
                                               seed);
    } else if (vm.count("simulate_qft")) {
	    const unsigned int n_qubits = vm["simulate_qft"].as<unsigned int>();
	    quantumComputation = std::make_unique<qc::QFT>(n_qubits);
        ddsim = std::make_unique<QFRSimulator>(quantumComputation,
                                               vm["steps"].as<unsigned int>(), vm["step_fidelity"].as<double>(),
                                               vm["initial_reorder"].as<int>(), vm["dynamic_reorder"].as<int>(), vm["post_reorder"].as<int>(),
                                               seed);
    } else if (vm.count("simulate_fast_shor")) {
        const unsigned int composite_number = vm["simulate_fast_shor"].as<unsigned int>();
        const unsigned int coprime = vm["simulate_fast_shor_coprime"].as<unsigned int>();
        if (seed == 0) {
            ddsim = std::make_unique<ShorFastSimulator>(composite_number, coprime, vm.count("verbose") > 0);
        } else {
            ddsim = std::make_unique<ShorFastSimulator>(composite_number, coprime, seed, vm.count("verbose") > 0);
        }
    } else if (vm.count("simulate_shor")) {
        const unsigned int composite_number = vm["simulate_shor"].as<unsigned int>();
        const unsigned int coprime = vm["simulate_shor_coprime"].as<unsigned int>();
        if (seed == 0) {
            ddsim = std::make_unique<ShorSimulator>(composite_number, coprime, vm.count("verbose") > 0);
        } else {
            ddsim = std::make_unique<ShorSimulator>(composite_number, coprime, seed, vm.count("verbose") > 0);
        }
    } else if (vm.count("simulate_grover")) {
        const unsigned int n_qubits = vm["simulate_grover"].as<unsigned int>();
        quantumComputation = std::make_unique<qc::Grover>(n_qubits, seed);
        ddsim = std::make_unique<QFRSimulator>(quantumComputation,
                                               vm["steps"].as<unsigned int>(), vm["step_fidelity"].as<double>(),
                                               vm["initial_reorder"].as<int>(), vm["dynamic_reorder"].as<int>(), vm["post_reorder"].as<int>(),
                                               seed);
    } else if (vm.count("simulate_grover_emulated")) {
        ddsim = std::make_unique<GroverSimulator>(vm["simulate_grover_emulated"].as<unsigned int>(), seed);
    } else if (vm.count("simulate_grover_oracle_emulated")) {
        ddsim = std::make_unique<GroverSimulator>(vm["simulate_grover_oracle_emulated"].as<std::string>(), seed);
    } else if (vm.count("simulate_ghz")) {
	    const unsigned int n_qubits = vm["simulate_ghz"].as<unsigned int>();
	    quantumComputation = std::make_unique<qc::Entanglement>(n_qubits);
        ddsim = std::make_unique<QFRSimulator>(quantumComputation,
                                               vm["steps"].as<unsigned int>(), vm["step_fidelity"].as<double>(),
                                               vm["initial_reorder"].as<int>(), vm["dynamic_reorder"].as<int>(), vm["post_reorder"].as<int>(),
                                               seed);
    } else {
        std::cerr << "Did not find anything to simulate. See help below.\n"
                  << description << "\n";
        return 1;
    }

    if (quantumComputation && quantumComputation->getNqubits() > dd::MAXN) {
        std::cerr << "Quantum computation contains to many qubits (limit is set to " << dd::MAXN << "). See documentation for details.\n";
        std::exit(1);
    }

    auto t1 = std::chrono::high_resolution_clock::now();
    auto m = ddsim->Simulate(vm["shots"].as<unsigned int>());
    auto t2 = std::chrono::high_resolution_clock::now();

    std::chrono::duration<float> duration_simulation = t2-t1;

    if (vm.count("benchmark")) {
        auto more_info = ddsim->AdditionalStatistics();
        std::cout << ddsim->getName() << ", "
                  << ddsim->getNumberOfQubits() << ", "
                  //<< vm["approximate"].as<float>() << ", "
                  << std::fixed << duration_simulation.count() << std::defaultfloat << ", "
                  //<< more_info["approximation_runs"] << ","
                  //<< more_info["final_fidelity"] << ", "
                  << more_info["coprime_a"] << ", "
                  << more_info["sim_result"] << ", "
                  << more_info["polr_result"] << ", "
                  << ddsim->getSeed() << ", "
                  << ddsim->getNumberOfOps() << ", "
                  << ddsim->getMaxNodeCount()
                  << "\n";
        return 0;
    }

    std::cout << "{\n";

    if (!m.empty()) {
        std::cout << "  \"measurements\": {";
        bool first_element = true;
        for(const auto& element : m)
        {
            std::cout << (first_element ? "" : ",") << "\n    \"" << element.first << "\": " << element.second;
            first_element = false;
        }
        std::cout << "\n  },\n";
    }
    if (vm.count("display_vector")) {
        std::cout << "  \"state_vector\": [";

        bool first_element = true;
        unsigned long long non_zero_entries = 0;
        for(const auto& element : ddsim->getVector()) {
            if (element.r != 0 || element.i != 0) {
                non_zero_entries++;
                std::cout << (first_element ? "" : ",") << "\n    " << std::showpos << element.r << element.i << "i" << std::noshowpos;
            } else {
                std::cout << (first_element ? "" : ",") << "\n    0";
            }
            first_element = false;
        }
        std::cout << "\n  ],\n";
        std::cout << "  \"non_zero_entries\": " << non_zero_entries << ",\n";
    }

    if (vm.count("ps")) {
        std::cout << "  \"statistics\": {\n"
                  << "    \"simulation_time\": " << std::fixed << duration_simulation.count() << std::defaultfloat << ",\n"
                  << "    \"benchmark\": \"" << ddsim->getName() << "\",\n"
                  << "    \"shots\": " << vm["shots"].as<unsigned int>() << ",\n"
                  << "    \"distinct_results\": " << m.size() << ",\n"
                  << "    \"n_qubits\": " << ddsim->getNumberOfQubits() << ",\n"
                  << "    \"applied_gates\": " << ddsim->getNumberOfOps() << ",\n"
                  << "    \"max_nodes\": " << ddsim->getMaxNodeCount() << ",\n"
                  ;
        for(const auto& item : ddsim->AdditionalStatistics()) {
            std::cout << "    \"" << item.first << "\": \"" << item.second << "\",\n";
        }
        std::cout << "    \"seed\": " << ddsim->getSeed() << "\n"
                  << "  },\n";
    }
    std::cout << "  \"dummy\": 0\n}\n"; // trailing element to make json printout easier
}


#include <boost/mpl/vector.hpp>
#include <boost/fusion/support.hpp>

typedef boost::fusion::traits::deduce_sequence <

boost::mpl::vector<int, char>

>::type seq1_t;

typedef boost::fusion::traits::deduce_sequence <

boost::fusion::vector<int, char>

>::type seq2_t;