#pragma once

#include <iostream>

#include <boost/container/flat_map.hpp>

#include <xtensor/xoperation.hpp>
#include <xtensor/xmath.hpp>
#include <xtensor/xtensor.hpp>
#include <xtensor/xexpression.hpp>
#include <xtensor/xview.hpp>

#include "nifty/ufd/ufd.hxx"
#include "nifty/graph/undirected_list_graph.hxx"
#include "nifty/graph/opt/lifted_multicut/lifted_multicut_objective.hxx"
namespace nifty{
namespace graph{
namespace opt{
namespace lifted_multicut{

    ///\cond
    namespace detail_plmc{
        template<std::size_t DIM>
        struct EvalHelper;

        template<>
        struct EvalHelper<2>{

            template<class OBJ, class D_LABELS>
            static auto evaluate(
                const OBJ & obj,
                const xt::xexpression<D_LABELS> & e_labels
            ){
                const auto & labels = e_labels.derived_cast();
                const auto & weights = obj.weights();
                const auto & offsets = obj.offsets();
                const auto & shape = obj.shape();
                const auto & n_offsets = obj.n_offsets();

                auto e = 0.0;

                for(int p0=0; p0<shape[0]; ++p0)
                for(int p1=0; p1<shape[1]; ++p1){
                    const auto label_p = labels(p0, p1);
                    for(int offset_index=0; offset_index<n_offsets; ++offset_index){
                        const int q0 = p0 + offsets(offset_index, 0);
                        const int q1 = p1 + offsets(offset_index, 1);
                        if(q0 >= 0 && q0 < shape[0]  && q1 >= 0 && q1 < shape[1]){
                            const auto label_q = labels(q0, q1);
                            if(label_p != label_q){
                                e += weights(p0, p1, offset_index);
                            }
                        }
                    }
                }
                return e;
            }
        };
    }
    ///\endcond

    template<std::size_t DIM>
    class PixelWiseLmcObjective{
    public:

        PixelWiseLmcObjective(
        ) {

        }
        template<class D_WEIGHTS, class D_OFFSETS>
        PixelWiseLmcObjective(
            const xt::xexpression<D_WEIGHTS> & e_weights,
            const xt::xexpression<D_OFFSETS> & e_offsets
        )
        :   weights_(e_weights),
            offsets_(e_offsets),
            shape_(),
            n_offsets_(),
            n_variables_()
        {
            // shape and n_offset
            const auto & wshape = weights_.shape();
            std::copy(wshape.begin(), wshape.end(), shape_.begin());
            n_offsets_ = wshape[DIM];

            // n_var
            n_variables_ = shape_[0];
            for(auto d=1; d<DIM; ++d){
                n_variables_ *= shape_[d];
            }

        }

        template<class D_LABELS>
        auto evaluate(
            const xt::xexpression<D_LABELS> & e_labels
        )const{
            return detail_plmc::EvalHelper<DIM>::evaluate(*this, e_labels);
        }
        const auto & weights()const{
            return weights_;
        }
        const auto & offsets()const{
            return offsets_;
        }
        const auto & shape()const{
            return shape_;
        }
        auto n_offsets()const{
            return n_offsets_;
        }
        auto n_variables() const {
            return n_variables_;
        }
    private:

        xt::xtensor<int,       2, xt::layout_type::row_major> offsets_;
        xt::xtensor<float, DIM+1, xt::layout_type::row_major> weights_;

        std::array<int, DIM> shape_;
        std::size_t n_offsets_;
        uint64_t n_variables_;
    };

    template<std::size_t DIM>
    class PixelWiseLmcConnetedComponentsFusion;

    template<>
    class PixelWiseLmcConnetedComponentsFusion<2>
    {
    public:
        static const std::size_t DIM = 2;

        typedef nifty::graph::UndirectedGraph<>                 CCGraphType;
        typedef LiftedMulticutObjective<CCGraphType, double >   CCObjectiveType;
        typedef LiftedMulticutBase<CCObjectiveType>             CCBaseType;
        typedef typename CCBaseType::VisitorBaseType            CVisitorBaseType;
        typedef typename CCBaseType::NodeLabelsType             CCNodeLabels;
        // factory for the lifted primal rounder
        typedef nifty::graph::opt::common::SolverFactoryBase<CCBaseType> CCLmcFactoryBase;

        PixelWiseLmcConnetedComponentsFusion(
            const PixelWiseLmcObjective<DIM> & objective,
            std::shared_ptr<CCLmcFactoryBase> solver_fatory
        )
        :   objective_(objective),
            ufd_(objective.n_variables()),
            solver_fatory_(solver_fatory)
        {

        }

        struct  Settings
        {

        };

        template<class D_LABELS_A, class D_LABELS_B>
        auto fuse(
            const xt::xexpression<D_LABELS_A>  & e_labels_a,
            const xt::xexpression<D_LABELS_B>  & e_labels_b
        ){

            ufd_.reset();

            const auto & shape = objective_.shape();
            const auto & labels_a = e_labels_a.derived_cast();
            const auto & labels_b = e_labels_b.derived_cast();

            typename xt::xtensor<int, DIM>::shape_type reshape{size_t(shape[0]), size_t(shape[1])};
            auto res = xt::xtensor<int, DIM, xt::layout_type::row_major>(reshape);

            this->merge_ufd(e_labels_a, e_labels_b);

            //
            auto e_a = objective_.evaluate(labels_a);
            auto e_b = objective_.evaluate(labels_b);
            this->do_it(res, [&](
                const auto & cc_node_labels,
                const auto & cc_energy
            ){
                if(cc_energy < std::min(e_a, e_b)){

                    auto res_iter = res.begin();
                    for(auto var=0; var<objective_.n_variables(); ++var){
                        const auto dense_var = *res_iter;
                        *res_iter = cc_node_labels[dense_var];
                        //*res_iter = cc_node_labels[to_dense[ufd_.find(var)]];
                        ++res_iter;
                    }

                }
                else if(e_a < cc_energy){
                    std::copy(labels_a.begin(), labels_a.end(), res.begin());
                }
                else{
                    std::copy(labels_b.begin(), labels_b.end(), res.begin());
                }
            });

            return res;

        }

        template<class D_LABELS>
        auto fuse(
            const xt::xexpression<D_LABELS>  & e_labels
        ){

            ufd_.reset();

            const auto & shape = objective_.shape();

            typename xt::xtensor<int, DIM>::shape_type reshape{size_t(shape[0]), size_t(shape[1])};
            auto res = xt::xtensor<int, DIM, xt::layout_type::row_major>(reshape);

            this->merge_ufd2(e_labels);

            //

            this->do_it(res, [&](
                const auto & cc_node_labels,
                const auto & cc_energy
            ){
                if(1){//cc_energy < std::min(e_a, e_b)){

                    auto res_iter = res.begin();
                    for(auto var=0; var<objective_.n_variables(); ++var){
                        const auto dense_var = *res_iter;
                        *res_iter = cc_node_labels[dense_var];
                        //*res_iter = cc_node_labels[to_dense[ufd_.find(var)]];
                        ++res_iter;
                    }

                }
                //else if(e_a < cc_energy){
                //    std::copy(labels_a.begin(), labels_a.end(), res.begin());
                //}
                //else{
                //    std::copy(labels_b.begin(), labels_b.end(), res.begin());
                //}
            });

            return res;

        }

    private:

        template<class D_LABELS_A, class D_LABELS_B>
        auto merge_ufd(
            const xt::xexpression<D_LABELS_A>  & e_labels_a,
            const xt::xexpression<D_LABELS_B>  & e_labels_b
        ){
            const auto & shape = objective_.shape();
            const auto & labels_a = e_labels_a.derived_cast();
            const auto & labels_b = e_labels_b.derived_cast();

            uint64_t node_p = 0;
            for(int p0=0; p0<shape[0]; ++p0)
            for(int p1=0; p1<shape[1]; ++p1){

                const auto p_label_a = labels_a(p0, p1);
                const auto p_label_b = labels_b(p0, p1);

                if(p0 + 1 < shape[0]){
                    const auto q_label_a = labels_a(p0+1, p1);
                    const auto q_label_b = labels_b(p0+1, p1);
                    if(p_label_a == q_label_a && p_label_b == q_label_b){
                        const auto node_q = node_p + shape[1];
                        ufd_.merge(node_p, node_q);
                    }
                }
                if(p1 + 1 < shape[1]){
                    const auto q_label_a = labels_a(p0, p1+1);
                    const auto q_label_b = labels_b(p0, p1+1);
                    if(p_label_a == q_label_a && p_label_b == q_label_b){
                        const auto node_q = node_p + 1;
                        ufd_.merge(node_p, node_q);
                    }
                }
                ++node_p;
            }
        }

        template<class D_LABELS>
        auto merge_ufd2(
            const xt::xexpression<D_LABELS>  & e_labels
        ){
            const auto & shape = objective_.shape();
            const auto & labels = e_labels.derived_cast();
            const auto n_offsets = labels.shape()[DIM];

            const auto pview = xt::view(labels,0,0,xt::all());
            const auto bla = pview(0);

            //uint64_t node_p = 0;
            //for(int p0=0; p0<shape[0]; ++p0)
            //for(int p1=0; p1<shape[1]; ++p1){
            //    if(p0 + 1 < shape[0]){
            //        bool do_merge = true;
            //        for(auto o=0; o<n_offsets; ++o){
            //            const auto p_label = labels(p0,  p1,o);
            //            const auto q_label = labels(p0+1,p1,o);
            //            if(p_label != q_label ){
            //                do_merge = false;
            //                break;
            //            }
            //        }
            //        if(do_merge){
            //            const auto node_q = node_p + shape[1];
            //            ufd_.merge(node_p, node_q);
            //        }
            //    }
            //    if(p1 + 1 < shape[1]){
            //        bool do_merge = true;
            //        for(auto o=0; o<n_offsets; ++o){
            //            const auto p_label = labels(p0, p1,  o);
            //            const auto q_label = labels(p0, p1+1,o);
            //            if(p_label != q_label ){
            //                do_merge = false;
            //                break;
            //            }
            //        }
            //        if(do_merge){
            //            const auto node_q = node_p + 1;
            //            ufd_.merge(node_p, node_q);
            //        }
            //    }
            //    ++node_p;
            //}
        }

        template<class F>
        auto do_it(
            xt::xtensor<int, DIM, xt::layout_type::row_major> & res,
            F && f
        ){

            const auto & shape = objective_.shape();
            const auto & offsets = objective_.offsets();
            const auto & weights = objective_.weights();
            const auto & n_offsets = objective_.n_offsets();
            // const auto & labels_a = e_labels_a.derived_cast();
            // const auto & labels_b = e_labels_b.derived_cast();

            // make map dense
            const auto cc_n_variables = ufd_.numberOfSets();
            boost::container::flat_map<uint64_t, uint64_t> to_dense;
            ufd_.representativeLabeling(to_dense);
            {
                auto res_iter = res.begin();
                for(auto var=0; var<objective_.n_variables(); ++var){
                    *res_iter = to_dense[ufd_.find(var)];
                    ++res_iter;
                }
            }

            // build the normal graph
            CCGraphType cc_graph(cc_n_variables);

            uint64_t node_p = 0 ;
            for(int p0=0; p0<shape[0]; ++p0)
            for(int p1=0; p1<shape[1]; ++p1){

                const auto p_label = ufd_.find(node_p);

                if(p0 + 1 < shape[0]){
                    const auto node_q = node_p + shape[1];
                    const auto q_label = ufd_.find(node_q);
                    if(p_label != q_label){
                        cc_graph.insertEdge(to_dense[p_label],to_dense[q_label]);
                    }
                }
                if(p1 + 1 < shape[1]){
                    const auto node_q = node_p + 1;
                    const auto q_label = ufd_.find(node_q);
                    if(p_label != q_label){
                        cc_graph.insertEdge(to_dense[p_label],to_dense[q_label]);
                    }
                }
                ++node_p;
            }

            CCObjectiveType cc_obj(cc_graph);

            node_p = 0 ;

            // fill the lifted obj
            for(int p0=0; p0<shape[0]; ++p0)
            for(int p1=0; p1<shape[1]; ++p1){

                const auto p_label = ufd_.find(node_p);

                for(int offset_index=0; offset_index<n_offsets; ++offset_index){
                    const int q0 = p0 + offsets(offset_index, 0);
                    const int q1 = p1 + offsets(offset_index, 1);
                    if(q0 >= 0 && q0 < shape[0]  && q1 >= 0 && q1 < shape[1]){

                        const auto node_q = q0*shape[1] + q1;
                        const auto q_label = ufd_.find(node_q);
                        if(p_label != q_label){

                            cc_obj.setCost(to_dense[p_label], to_dense[q_label],
                                weights(p0,p1,offset_index));
                        }
                    }
                }
                ++node_p;
            }

            auto solver = solver_fatory_->create(cc_obj);
            CCNodeLabels cc_node_labels(cc_graph);

            nifty::graph::opt::common::VerboseVisitor<CCBaseType> visitor;
            solver->optimize(cc_node_labels, nullptr);
            auto e_res = cc_obj.evalNodeLabels(cc_node_labels);
            delete solver;
            f(cc_node_labels, e_res);
        }

        const PixelWiseLmcObjective<DIM> & objective_;
        nifty::ufd::Ufd<uint64_t> ufd_;
        std::shared_ptr<CCLmcFactoryBase>  solver_fatory_;
    };

} // namespace nifty::graph::opt::lifted_multicut
} // namespace nifty::graph::opt
} // namespace nifty::graph
} // namespace nifty


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_REDUCTION_FUNCTIONS_SIMD_SSE_AVX_DOT_HPP_INCLUDED
#define BOOST_SIMD_REDUCTION_FUNCTIONS_SIMD_SSE_AVX_DOT_HPP_INCLUDED
#ifdef BOOST_SIMD_HAS_AVX_SUPPORT

#include <boost/simd/reduction/functions/dot.hpp>
#include <boost/dispatch/meta/scalar_of.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( dot_
                                    , boost::simd::tag::avx_
                                    , (A0)
                                    , ((simd_<single_<A0>,boost::simd::tag::avx_>))
                                      ((simd_<single_<A0>,boost::simd::tag::avx_>))
                                    )
  {
    typedef typename dispatch::meta::scalar_of<A0>::type result_type;
    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
      {
        A0 r = _mm256_dp_ps(a0, a1, 0xFF);
        return r[0]+r[4];
      }
  };
} } }

#endif
#endif


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

#include <iostream>

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

//#include "detail/r1cs_examples.hpp"
//#include "detail/sha256_component.hpp"

#include <nil/crypto3/algebra/curves/bls12.hpp>
#include <nil/crypto3/algebra/fields/bls12/base_field.hpp>
#include <nil/crypto3/algebra/fields/bls12/scalar_field.hpp>
#include <nil/crypto3/algebra/fields/arithmetic_params/bls12.hpp>
#include <nil/crypto3/algebra/curves/params/multiexp/bls12.hpp>
#include <nil/crypto3/algebra/curves/params/wnaf/bls12.hpp>

#include <nil/crypto3/zk/components/blueprint.hpp>
#include <nil/crypto3/zk/components/blueprint_variable.hpp>

#include <nil/crypto3/zk/snark/schemes/ppzksnark/r1cs_gg_ppzksnark.hpp>
#include <nil/crypto3/zk/snark/schemes/ppzksnark/r1cs_gg_ppzksnark/marshalling.hpp>

#include <nil/crypto3/zk/snark/algorithms/generate.hpp>
#include <nil/crypto3/zk/snark/algorithms/verify.hpp>
#include <nil/crypto3/zk/snark/algorithms/prove.hpp>

#include <nil/marshalling/status_type.hpp>

#include "sudoku.hpp"
#include "sudoku.cpp"

#include <vector>

using namespace nil::crypto3;
using namespace nil::crypto3::zk;

typedef algebra::curves::bls12<381> curve_type;
typedef typename curve_type::scalar_field_type field_type;

typedef zk::snark::r1cs_gg_ppzksnark<curve_type> scheme_type;

int main(int argc, char *argv[]) {
  string instance_filename, solution_filename,
    proving_key_filename,verification_key_filename ;
    boost::program_options::options_description options(
        "R1CS Generic Group PreProcessing Zero-Knowledge Succinct Non-interactive ARgument of Knowledge "
        "(https://eprint.iacr.org/2016/260.pdf) CLI Proof Generator");
    // clang-format off
    options.add_options()("help,h", "Display help message")
    // ("version,v", "Display version")
      ("sudoku-generate-keys", "Generate Sudoku keys")
      // ("sudoku-verify-proof", "verify Sudoku proof")
      ("sudoku-generate-proof","generate Sudoku proof for one solved instance")
      ("instance,inst", boost::program_options::value<std::string>(&instance_filename)->default_value("simple_instance.in"))
      ("solution,sol", boost::program_options::value<std::string>(&solution_filename)->default_value("solved_instance.in"))
      ("proving-key-filename,pko", boost::program_options::value<std::string>(&proving_key_filename)->default_value("provkey.bin"))
      ("verification-key-filename,pko", boost::program_options::value<std::string>(&verification_key_filename)->default_value("verifkey.bin"));
    // ("proof-output,po", boost::program_options::value<boost::filesystem::path>(&pout)->default_value("proof"))
    // ("proving-key-output,pko", boost::program_options::value<boost::filesystem::path>(&pkout)->default_value("pkey"))
    // ("verifying-key-output,vko", boost::program_options::value<boost::filesystem::path>(&vkout)->default_value("vkey"));
    // clang-format on

    boost::program_options::variables_map vm;
    boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(options).run(), vm);
    boost::program_options::notify(vm);

    if (vm.count("help") || argc < 2) {
        std::cout << options << std::endl;
        return 0;
    }

    if (vm.count("sudoku-generate-keys")) {
        generate_keys(proving_key_filename,verification_key_filename);
    };

    // if (vm.count("sudoku-verify-proof")){
    // };

    if (vm.count("sudoku-generate-proof")) {
      //std::string instance_filename = "simple_instance.in";
      //std::string solution_filename = "solved_instance.in";
      // std::cout << "Please input the sudoku instance (default is in file instance.in)" << std::endl;
      // std::cin >> instance_filename;
      std::vector<int> sudoku_instance = input_instance(instance_filename);
      // std::cout << "Please input the sudoku solved instance (default is in file solved_instance.in)" << std::endl;
      // std::cin >> solution_filename;
      std::vector<int> sudoku_solution = input_solved(solution_filename);
      prove(sudoku_instance, sudoku_solution, proving_key_filename, verification_key_filename);

    }

    return 0;
}


#include <iostream>
#include <math.h>
#include <fstream>
#include <time.h>
#include <boost/range/numeric.hpp>
#include <boost/range/adaptor/map.hpp>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>

#define h  800
#define w  800

#define output_file "output.raw"

unsigned char* readImage(const char *image, int height, int width, int bpp)
{

	unsigned char* Imagedata = new unsigned char [height*width*bpp];

	FILE *file;
	if (!(file=fopen(image,"rb"))) {
		std::cout << "Cannot open file: " << image << std::endl;
		exit(1);
	}
	fread(Imagedata, sizeof(unsigned char), height*width*bpp, file);
	fclose(file);

	return Imagedata;
}

void saveImage(const char* image, unsigned char* Imagedata, int height, int width, int bpp)
{
	FILE *file;

	if (!(file=fopen(image,"wb"))) {
		std::cout << "Cannot open file: " << image << std::endl;
		exit(1);
	}
	fwrite(Imagedata, sizeof(unsigned char), height*width*bpp, file);
	fclose(file);

	std::cout << "Filed Saved Succesfully" << std::endl;
}

int getEuclideanDist(int pixel, int mean)
{
	return abs(pixel - mean);
}

int getMeanOfCluster(std::unordered_map<int, int> cluster)
{
	std::unordered_map<int, int>::iterator itr;
	int sum(0);

	sum = boost::accumulate(cluster | boost::adaptors::map_values, 0);

	return sum/cluster.size();
}

class thread
{
public:

	int id;
	int startIndex;
	int endIndex;

	int* mean1;
	int* mean2;
	int* mean3;
	int* mean4;
	int* mean5;
	int* mean6;
	int numOfClusters;
	std::vector<std::vector <int> > *sum;
	std::vector<std::vector <int> > *no;
	unsigned char* Image1D;

};

void* K_MeansMultithreaded(void* threadP)
{
	thread* threadT = static_cast<thread*>(threadP);
	int id = threadT->id;
	int k(threadT->numOfClusters);
	std::vector<int> minDist(k, 0);
	unsigned char* Image1D = threadT->Image1D;
	int* mean1(threadT->mean1);
	int* mean2(threadT->mean2);
	int* mean3(threadT->mean3);
	int* mean4(threadT->mean4);
	int* mean5(threadT->mean5);
	int* mean6(threadT->mean6);
	int startIndex(threadT->startIndex);
	int endIndex(threadT->endIndex);
	std::vector<std::vector <int> > &sum = *threadT->sum;
	std::vector<std::vector <int> > &no = *threadT->no;

	 for (int i = 0; i < h; ++i)
	 {
	 	for (int j = startIndex; j < endIndex; ++j)
	 	{

			int pixel = Image1D[i*w + j];

			minDist[0] = (int(abs(pixel - *mean1)));
			minDist[1] = (int(abs(pixel - *mean2)));
			minDist[2] = (int(abs(pixel - *mean3)));
			minDist[3] = (int(abs(pixel - *mean4)));
			minDist[4] = (int(abs(pixel - *mean5)));
			minDist[5] = (int(abs(pixel - *mean6)));

			int min_pos = std::distance(minDist.begin(), std::min_element(minDist.begin(),minDist.end()));

			sum[id][min_pos] += pixel;
			no[id][min_pos] += 1;

	    }
	 }
	 pthread_exit(NULL);

}

struct meanValues
{
	int mean1;
	int mean2;
	int mean3;
	int mean4;
	int mean5;
	int mean6;
};

unsigned char* getFinalOutput(unsigned char* Image1D, std::vector<int> means, int numOfClusters)
{

	std::vector<int> minDist(numOfClusters, 0);
    unsigned char* Image1DOutput = new unsigned char[h*w];
    int pixel, min_pos;
	for (int i = 0; i < h; ++i)
	{
		for (int j = 0; j < w; ++j)
		{
			pixel = Image1D[i*w + j];

			minDist[0] = (int(abs(pixel - means[0])));
			minDist[1] = (int(abs(pixel - means[1])));
			minDist[2] = (int(abs(pixel - means[2])));
			minDist[3] = (int(abs(pixel - means[3])));
			minDist[4] = (int(abs(pixel - means[4])));
			minDist[5] = (int(abs(pixel - means[5])));

			min_pos = std::distance(minDist.begin(), std::min_element(minDist.begin(),minDist.end()));
			Image1DOutput[i*w + j] = means[min_pos];

		}
	}

	return Image1DOutput;
}

int main(int argc, char const *argv[])
{
	struct timespec start, stop;
	unsigned char* Image1D = readImage(argv[1], h, w, 1);
	std::cout << "Image read 1" << std::endl;
	int p = atoi(argv[1]);
	thread threadT[p];
	int rc, startIndex, endIndex;
	pthread_t threads[p];

	unsigned char* Image1DOutput = nullptr;

	std::vector<int> means(6, 0);
	means[0] = 0;
	means[1] = 65;
	means[2] = 100;
	means[3] = 125;
	means[4] = 190;
	means[5] = 255;

	if(clock_gettime(CLOCK_REALTIME, &start) == -1)
		perror("clock gettime");

	for (int k = 0; k < 50; ++k)
	{
	std::vector<std::vector <int> > sum(p, std::vector<int>(6,0));
	std::vector<std::vector <int> > no(p, std::vector<int>(6,0));
		for (int i = 0; i < p; ++i)
		{
			startIndex = i*h/p;
		    endIndex = startIndex + h/p - 1;
		    threadT[i].startIndex = startIndex;
		    threadT[i].endIndex = endIndex;
			threadT[i].numOfClusters = 6;
			threadT[i].id = i;
			threadT[i].mean1 = &means[0];
			threadT[i].mean2 = &means[1];
			threadT[i].mean3 = &means[2];
			threadT[i].mean4 = &means[3];
			threadT[i].mean5 = &means[4];
			threadT[i].mean6 = &means[5];
			threadT[i].Image1D = Image1D;
			threadT[i].sum = ∑
			threadT[i].no = &no;

			rc = pthread_create(&threads[i], NULL, K_MeansMultithreaded, (void *)&threadT[i]);
		}

		for(int i = 0; i < p ; i++)
	    {
	    	pthread_join(threads[i], NULL);
	    }

		for (int i = 0; i < 6; ++i)
		{
			int sumAll(0);
			int lengthAll(0);
			for (int j = 0; j < p; ++j)
			{
				sumAll += sum[j][i];
				lengthAll += no[j][i];
			}

			means[i] = sumAll/lengthAll;
		}

		sum.clear();
		no.clear();

	}

	std::cout << "Means are: " << means[0] << ", " << means[1] << ", " << means[2] << ", " << means[3] << ", " << means[4] << ", " << means[5] << std::endl;

	Image1DOutput = getFinalOutput(Image1D, means, 6);

	if(clock_gettime( CLOCK_REALTIME, &stop) == -1 )
		perror("clock gettime");

	double time = (stop.tv_sec - start.tv_sec)+ (double)(stop.tv_nsec - start.tv_nsec)/1e9;
	std::cout << "time taken: " << time << std::endl;

	saveImage(output_file, Image1DOutput, h, w, 1);
	delete [] Image1D;
	delete [] Image1DOutput;
	pthread_exit(NULL);

	return 0;
}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2010 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

// import basic and product tests for deprectaed DynamicSparseMatrix
#define EIGEN_NO_DEPRECATED_WARNING
#include "sparse_basic.cpp"
#include "sparse_product.cpp"
#include <Eigen/SparseExtra>

template<typename SetterType,typename DenseType, typename Scalar, int Options>
bool test_random_setter(SparseMatrix<Scalar,Options>& sm, const DenseType& ref, const std::vector<Vector2i>& nonzeroCoords)
{
  {
    sm.setZero();
    SetterType w(sm);
    std::vector<Vector2i> remaining = nonzeroCoords;
    while(!remaining.empty())
    {
      int i = internal::random<int>(0,static_cast<int>(remaining.size())-1);
      w(remaining[i].x(),remaining[i].y()) = ref.coeff(remaining[i].x(),remaining[i].y());
      remaining[i] = remaining.back();
      remaining.pop_back();
    }
  }
  return sm.isApprox(ref);
}

template<typename SetterType,typename DenseType, typename T>
bool test_random_setter(DynamicSparseMatrix<T>& sm, const DenseType& ref, const std::vector<Vector2i>& nonzeroCoords)
{
  sm.setZero();
  std::vector<Vector2i> remaining = nonzeroCoords;
  while(!remaining.empty())
  {
    int i = internal::random<int>(0,static_cast<int>(remaining.size())-1);
    sm.coeffRef(remaining[i].x(),remaining[i].y()) = ref.coeff(remaining[i].x(),remaining[i].y());
    remaining[i] = remaining.back();
    remaining.pop_back();
  }
  return sm.isApprox(ref);
}

template<typename SparseMatrixType> void sparse_extra(const SparseMatrixType& ref)
{
  const Index rows = ref.rows();
  const Index cols = ref.cols();
  typedef typename SparseMatrixType::Scalar Scalar;
  enum { Flags = SparseMatrixType::Flags };

  double density = (std::max)(8./(rows*cols), 0.01);
  typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix;
  typedef Matrix<Scalar,Dynamic,1> DenseVector;
  Scalar eps = 1e-6;

  SparseMatrixType m(rows, cols);
  DenseMatrix refMat = DenseMatrix::Zero(rows, cols);
  DenseVector vec1 = DenseVector::Random(rows);

  std::vector<Vector2i> zeroCoords;
  std::vector<Vector2i> nonzeroCoords;
  initSparse<Scalar>(density, refMat, m, 0, &zeroCoords, &nonzeroCoords);

  if (zeroCoords.size()==0 || nonzeroCoords.size()==0)
    return;

  // test coeff and coeffRef
  for (int i=0; i<(int)zeroCoords.size(); ++i)
  {
    VERIFY_IS_MUCH_SMALLER_THAN( m.coeff(zeroCoords[i].x(),zeroCoords[i].y()), eps );
    if(internal::is_same<SparseMatrixType,SparseMatrix<Scalar,Flags> >::value)
      VERIFY_RAISES_ASSERT( m.coeffRef(zeroCoords[0].x(),zeroCoords[0].y()) = 5 );
  }
  VERIFY_IS_APPROX(m, refMat);

  m.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5);
  refMat.coeffRef(nonzeroCoords[0].x(), nonzeroCoords[0].y()) = Scalar(5);

  VERIFY_IS_APPROX(m, refMat);

  // random setter
//   {
//     m.setZero();
//     VERIFY_IS_NOT_APPROX(m, refMat);
//     SparseSetter<SparseMatrixType, RandomAccessPattern> w(m);
//     std::vector<Vector2i> remaining = nonzeroCoords;
//     while(!remaining.empty())
//     {
//       int i = internal::random<int>(0,remaining.size()-1);
//       w->coeffRef(remaining[i].x(),remaining[i].y()) = refMat.coeff(remaining[i].x(),remaining[i].y());
//       remaining[i] = remaining.back();
//       remaining.pop_back();
//     }
//   }
//   VERIFY_IS_APPROX(m, refMat);

    VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, StdMapTraits> >(m,refMat,nonzeroCoords) ));
    #ifdef EIGEN_UNORDERED_MAP_SUPPORT
    VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, StdUnorderedMapTraits> >(m,refMat,nonzeroCoords) ));
    #endif
    #ifdef _DENSE_HASH_MAP_H_
    VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, GoogleDenseHashMapTraits> >(m,refMat,nonzeroCoords) ));
    #endif
    #ifdef _SPARSE_HASH_MAP_H_
    VERIFY(( test_random_setter<RandomSetter<SparseMatrixType, GoogleSparseHashMapTraits> >(m,refMat,nonzeroCoords) ));
    #endif

  // test RandomSetter
  /*{
    SparseMatrixType m1(rows,cols), m2(rows,cols);
    DenseMatrix refM1 = DenseMatrix::Zero(rows, rows);
    initSparse<Scalar>(density, refM1, m1);
    {
      Eigen::RandomSetter<SparseMatrixType > setter(m2);
      for (int j=0; j<m1.outerSize(); ++j)
        for (typename SparseMatrixType::InnerIterator i(m1,j); i; ++i)
          setter(i.index(), j) = i.value();
    }
    VERIFY_IS_APPROX(m1, m2);
  }*/

}

template<typename SparseMatrixType>
void check_marketio()
{
  typedef Matrix<typename SparseMatrixType::Scalar, Dynamic, Dynamic> DenseMatrix;
  Index rows = internal::random<Index>(1,100);
  Index cols = internal::random<Index>(1,100);
  SparseMatrixType m1, m2;
  m1 = DenseMatrix::Random(rows, cols).sparseView();
  saveMarket(m1, "sparse_extra.mtx");
  loadMarket(m2, "sparse_extra.mtx");
  VERIFY_IS_EQUAL(DenseMatrix(m1),DenseMatrix(m2));
}

void test_sparse_extra()
{
  for(int i = 0; i < g_repeat; i++) {
    int s = Eigen::internal::random<int>(1,50);
    CALL_SUBTEST_1( sparse_extra(SparseMatrix<double>(8, 8)) );
    CALL_SUBTEST_2( sparse_extra(SparseMatrix<std::complex<double> >(s, s)) );
    CALL_SUBTEST_1( sparse_extra(SparseMatrix<double>(s, s)) );

    CALL_SUBTEST_3( sparse_extra(DynamicSparseMatrix<double>(s, s)) );
//    CALL_SUBTEST_3(( sparse_basic(DynamicSparseMatrix<double>(s, s)) ));
//    CALL_SUBTEST_3(( sparse_basic(DynamicSparseMatrix<double,ColMajor,long int>(s, s)) ));

    CALL_SUBTEST_3( (sparse_product<DynamicSparseMatrix<float, ColMajor> >()) );
    CALL_SUBTEST_3( (sparse_product<DynamicSparseMatrix<float, RowMajor> >()) );

    CALL_SUBTEST_4( (check_marketio<SparseMatrix<float,ColMajor,int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<double,ColMajor,int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<std::complex<float>,ColMajor,int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<std::complex<double>,ColMajor,int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<float,ColMajor,long int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<double,ColMajor,long int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<std::complex<float>,ColMajor,long int> >()) );
    CALL_SUBTEST_4( (check_marketio<SparseMatrix<std::complex<double>,ColMajor,long int> >()) );
    TEST_SET_BUT_UNUSED_VARIABLE(s);
  }
}


// Copyright (C) 2014 The Regents of the University of California (Regents)
// and Google, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
//     * Neither the name of The Regents or University of California, Google,
//       nor the names of its contributors may be used to endorse or promote
//       products derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Please contact the author of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu), John Flynn (jflynn@google.com)

#include "theia/sfm/pose/four_point_relative_pose_partial_rotation.h"

#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>
#include <Eigen/SVD>
#include <glog/logging.h>
#include <limits>
#include <math.h>

#include "theia/alignment/alignment.h"

namespace theia {

using Eigen::AngleAxisd;
using Eigen::EigenSolver;
using Eigen::JacobiSVD;
using Eigen::Map;
using Eigen::Matrix;
using Eigen::Matrix3d;
using Eigen::Matrix4d;
using Eigen::Quaterniond;
using Eigen::Vector3d;
using Eigen::Vector4d;

namespace {

bool SolveQEP(const Matrix4d& M,
              const Matrix4d& C,
              const Matrix4d& K,
              std::vector<double>* eigenvalues,
              std::vector<Vector4d>* eigenvectors) {
  // Solves the quadratic eigenvalue problem:
  //
  //   Q(s)x = 0
  //
  // where:
  //
  //   Q(s) = s^2*M + s*C + K
  //
  // Returns true if the problem could be solved, false otherwise.
  //
  // This is converted to a generalized eigenvalue as described in:
  //   http://en.wikipedia.org/wiki/Quadratic_eigenvalue_problem
  //
  // The generalized eigenvalue problem is in this form:
  //
  // [ C  K ]z = s[ -M  0  ]z
  // [-I  0 ]     [ 0   -I ]
  //
  // With eigenvector z = [ sx ] and s is the eigenvalue.
  //                      [  x ]
  //
  // The eigenvector of the quadratic eigenvalue problem can be extracted from
  // z.
  //
  // Where z is the eigenvector, s is the eigenvalue.
  // This generalized eigenvalue can be converted to a standard eigenvalue
  // problem by multiplying by the inverse of the RHS matrix. The inverse of
  // the RHS matrix is particularly simple:
  //
  // [ -inv(M)  0 ]
  // [ 0       -I ]
  //
  // So the generalized eigenvalue problem reduces to a standard eigenvalue
  // problem on the constraint matrix:
  //
  // [ -inv(M)C  -inv(M)K ]z = sz
  // [ I         0        ]

  Matrix4d inv_M;
  static const double kDeterminantThreshold = 1e-7;
  bool invert_success;
  // Check that determinant of M is larger than threshold. This threshold only
  // seems to be reached when there is no rotation. TODO(jflynn): verify.
  M.computeInverseWithCheck(inv_M, invert_success, kDeterminantThreshold);
  if (!invert_success) {
    return false;
  }

  // Negate inverse of M.
  inv_M = -1.0 * inv_M;

  // Set up constraint matrix.
  Matrix<double, 8, 8> constraint = Matrix<double, 8, 8>::Zero();
  // Set upper-left to - inv(M) * C.
  constraint.block<4, 4>(0, 0) = inv_M * C;
  // Set upper-right to -inv(M) * K.
  constraint.block<4, 4>(0, 4) = inv_M * K;
  // Set lower-left to identity.
  constraint.block<4, 4>(4, 0) = Matrix4d::Identity();

  // Extract the left eigenvectors and values from the constraint matrix.
  static const double kImagEigenValueTolerance = 1e-9;
  EigenSolver<Matrix<double, 8, 8> > eig_solver(constraint);

  for (int i = 0; i < 8; i++) {
    // Only consider the real eigenvalues and corresponding eigenvectors.
    if (fabs(eig_solver.eigenvalues()[i].imag()) < kImagEigenValueTolerance) {
      eigenvalues->push_back(eig_solver.eigenvalues()[i].real());
      eigenvectors->push_back(
          Vector4d(eig_solver.eigenvectors().col(i).tail(4).real()));
    }
  }
  return true;
}

}  // namespace

void FourPointRelativePosePartialRotation(
    const Vector3d& axis,
    const Vector3d image_one_ray_directions[4],
    const Vector3d image_one_ray_origins[4],
    const Vector3d image_two_ray_directions[4],
    const Vector3d image_two_ray_origins[4],
    std::vector<Quaterniond>* soln_rotations,
    std::vector<Vector3d>* soln_translations) {
  CHECK_DOUBLE_EQ(axis.squaredNorm(), 1.0);
  CHECK_NOTNULL(soln_rotations)->clear();
  CHECK_NOTNULL(soln_translations)->clear();

  // The generalized epipolar constraint between two sets of rays in different
  // coordinate systems (two generalized cameras).
  //
  // The rays are converted to plucker coordinates:
  //   L = [ q  ]
  //       [ p  ]
  //
  // Assuming the first coordinate system has zero rotation and translation,
  // the constraint is:
  //
  //   q2' * [t]x R * q1 + q2' * R * p1 + q1' * R * p2 = 0
  //
  // v' means transpose v, t is unknown translation, [t]x is the matrix
  // cross product form.
  //
  // Re-arranging to isolate t:
  //
  //   -(q2 x (R * q1))' * t + q2' * R * p1 + q1' * R' * p2 = 0
  //
  // In vector form:
  //
  //  [-(q2 x (R * q1))', q2' * R * p1 + q1' * R' * p2 ] * [ t ] = 0
  //                                                       [ 1 ]
  //
  // Each correspondence gives another constraint and the constraints can
  // be stacked to create a constraint matrix.
  //
  // The rotation matrix R can be parameterized, up to scale factor, as:
  //
  //   R ~ 2 * (v * v' + s[v]x) + (s^2 - 1)I
  //
  //   I = Identity matrix.
  //
  // where is v is the known (unit length) axis and s is related to the
  // unknown angle of rotation.
  //
  // The epipolar constraint holds if the rotation matrix is scaled, so the
  // rotation matrix parameterization above can be used. Each row of the
  // constraint matrix is thus a function of s^2 and s, so the constraint can be
  // written as:
  //
  //   [ M * s^2 + C *s + k ] * [ t ] = 0
  //                            [ 1 ]
  //
  // This is standard quadratic eigenvalue problem (QEP) and is solved using
  // standard methods.

  // Creates the matrices for the QEP problem.
  Matrix4d M;
  Matrix4d C;
  Matrix4d K;
  for (int i = 0; i < 4; ++i) {
    const Vector3d& q1(image_one_ray_directions[i]);
    const Vector3d p1(image_one_ray_origins[i].cross(q1));
    const Vector3d& q2(image_two_ray_directions[i]);
    const Vector3d p2(image_two_ray_origins[i].cross(q2));

    M.row(i).head(3) = -q2.cross(q1);
    M.row(i)[3] = q2.dot(p1) + q1.dot(p2);

    C.row(i).head(3) = -2.0 * q2.cross(axis.cross(q1));
    C.row(i)[3] = -2.0 * (q1.dot(axis.cross(p2)) - q2.dot(axis.cross(p1)));

    K.row(i).head(3) = -(2.0 * q1.dot(axis) * q2.cross(axis) - q2.cross(q1));
    K.row(i)[3] =
        -q2.dot(p1) - q1.dot(p2) +
        2.0 * (q2.dot(axis) * p1.dot(axis) + q1.dot(axis) * p2.dot(axis));
  }

  std::vector<double> eigenvalues;
  std::vector<Vector4d> eigenvectors;
  static const double kWTolerance = 1e-7;
  if (SolveQEP(M, C, K, &eigenvalues, &eigenvectors)) {
    // Extracts the translations and rotations from the eigenvalues and
    // eigenvectors of the QEP problem.
    for (int i = 0; i < eigenvalues.size(); ++i) {
      if (fabs(eigenvectors[i].w()) > kWTolerance) {
        Quaterniond quat(eigenvalues[i], axis[0], axis[1], axis[2]);
        quat.normalize();
        soln_rotations->push_back(quat);
        soln_translations->push_back(
            Vector3d(eigenvectors[i].x() / eigenvectors[i].w(),
                     eigenvectors[i].y() / eigenvectors[i].w(),
                     eigenvectors[i].z() / eigenvectors[i].w()));
      }
    }
  } else {
    // When there is zero rotation the vector part of the quaternion disappears
    // and it becomes ([0], 1) (where [0] is the zero vector) and the SolveQEP
    // method cannot be used.
    // However from the equations for M, C, and K above we can see that the
    // C and K matrices contain the axis, which is 0 assuming zero rotation,
    // so to solve for the translation we can directly extract the null space
    // of M.
    // Alternatively this can be derived directly from the generalized epipolar
    // constraint, after substituting identity for the rotation matrix.
    eigenvectors.clear();

    JacobiSVD<Matrix4d> svd = M.jacobiSvd(Eigen::ComputeFullV);
    const Vector4d eigenvector(svd.matrixV().col(3));

    if (fabs(eigenvector[3]) > kWTolerance) {
      soln_rotations->push_back(Quaterniond(AngleAxisd(0.0, axis)));
      soln_translations->push_back(Vector3d(eigenvector[0] / eigenvector[3],
                                            eigenvector[1] / eigenvector[3],
                                            eigenvector[2] / eigenvector[3]));
    }
  }
}

}  // namespace theia


#include "neal2_algorithm.h"

#include <Eigen/Dense>
#include <memory>
#include <stan/math/prim/fun.hpp>
#include <vector>

#include "marginal_state.pb.h"
#include "src/hierarchies/base_hierarchy.h"
#include "src/hierarchies/dependent_hierarchy.h"
#include "src/mixings/base_mixing.h"
#include "src/mixings/dependent_mixing.h"
#include "src/utils/distributions.h"
#include "src/utils/rng.h"

//! \param temp_hier Temporary hierarchy object
//! \return          Vector of evaluation of component on the provided grid
Eigen::VectorXd Neal2Algorithm::lpdf_marginal_component(
    std::shared_ptr<BaseHierarchy> temp_hier, const Eigen::MatrixXd &grid) {
  // Exploit conjugacy of hierarchy
  return temp_hier->marg_lpdf_grid(grid);
}

Eigen::VectorXd Neal2Algorithm::lpdf_marginal_component(
    std::shared_ptr<DependentHierarchy> temp_hier, const Eigen::MatrixXd &grid,
    const Eigen::MatrixXd &covariates) {
  // TODO will soon become obsolete
  return temp_hier->marg_lpdf_grid(grid, covariates);
}

Eigen::VectorXd Neal2Algorithm::get_cluster_prior_mass(
    const unsigned int data_idx) const {
  unsigned int n_data = data.rows();
  unsigned int n_clust = unique_values.size();
  Eigen::VectorXd logprior(n_clust + 1);
  if (mixing->is_dependent()) {
    auto mixcast = std::dynamic_pointer_cast<DependentMixing>(mixing);
    for (size_t j = 0; j < n_clust; j++) {
      // Probability of being assigned to an already existing cluster
      logprior(j) = mixcast->mass_existing_cluster(
          unique_values[j], mix_covariates.row(data_idx), n_data - 1, true,
          true);
    }
    // Further update with marginal component
    logprior(n_clust) = mixcast->mass_new_cluster(
        mix_covariates.row(data_idx), n_clust, n_data - 1, true, true);
  } else {
    for (size_t j = 0; j < n_clust; j++) {
      // Probability of being assigned to an already existing cluster
      logprior(j) = mixing->mass_existing_cluster(unique_values[j], n_data - 1,
                                                  true, true);
    }
    // Further update with marginal component
    logprior(n_clust) =
        mixing->mass_new_cluster(n_clust, n_data - 1, true, true);
  }
  return logprior;
}

Eigen::VectorXd Neal2Algorithm::get_cluster_lpdf(
    const unsigned int data_idx) const {
  unsigned int n_data = data.rows();
  unsigned int n_clust = unique_values.size();
  Eigen::VectorXd loglpdf(n_clust + 1);
  if (unique_values[0]->is_dependent()) {
    auto hiercast =
        std::dynamic_pointer_cast<DependentHierarchy>(unique_values[0]);
    // Probability of being assigned to a newly created cluster
    loglpdf(n_clust) =
        hiercast->marg_lpdf(data.row(data_idx), hier_covariates.row(data_idx));
    for (size_t j = 0; j < n_clust; j++) {
      hiercast =
          std::dynamic_pointer_cast<DependentHierarchy>(unique_values[j]);
      // Probability of being assigned to an already existing cluster
      loglpdf(j) = hiercast->like_lpdf(data.row(data_idx),
                                       hier_covariates.row(data_idx));
    }

  } else {
    for (size_t j = 0; j < n_clust; j++) {
      // Probability of being assigned to an already existing cluster
      loglpdf(j) = unique_values[j]->like_lpdf(data.row(data_idx));
    }
    // Probability of being assigned to a newly created cluster
    loglpdf(n_clust) = unique_values[0]->marg_lpdf(data.row(data_idx));
  }
  return loglpdf;
}

void Neal2Algorithm::print_startup_message() const {
  std::string msg = "Running Neal2 algorithm with " +
                    unique_values[0]->get_id() + " hierarchies, " +
                    mixing->get_id() + " mixing...";
  std::cout << msg << std::endl;
}

void Neal2Algorithm::initialize() {
  BaseAlgorithm::initialize();
  if (unique_values[0]->is_conjugate() == false) {
    throw std::invalid_argument(
        "This algorithm only supports conjugate hierarchies");
  }
}

void Neal2Algorithm::sample_allocations() {
  // Initialize relevant values
  unsigned int n_data = data.rows();
  auto &rng = bayesmix::Rng::Instance().get();
  // Loop over data points
  for (size_t i = 0; i < n_data; i++) {
    unsigned int n_clust = unique_values.size();
    bool singleton = (unique_values[allocations[i]]->get_card() <= 1);
    // Remove datum from cluster
    remove_datum_from_hierarchy(i, unique_values[allocations[i]]);
    // Compute probabilities of clusters in log-space
    Eigen::VectorXd logprobas =
        get_cluster_prior_mass(i) + get_cluster_lpdf(i);
    // Draw a NEW value for datum allocation
    unsigned int c_new =
        bayesmix::categorical_rng(stan::math::softmax(logprobas), rng, 0);
    unsigned int c_old = allocations[i];
    if (c_new == n_clust) {
      std::shared_ptr<BaseHierarchy> new_unique = unique_values[0]->clone();
      add_datum_to_hierarchy(i, new_unique);
      // Generate new unique values with posterior sampling
      new_unique->sample_given_data();
      unique_values.push_back(new_unique);
      allocations[i] = unique_values.size() - 1;
    } else {
      allocations[i] = c_new;
      add_datum_to_hierarchy(i, unique_values[c_new]);
    }
    if (singleton) {
      // Relabel allocations so that they are consecutive numbers
      for (auto &c : allocations) {
        if (c > c_old) {
          c -= 1;
        }
      }
      unique_values.erase(unique_values.begin() + c_old);
    }
  }
}

void Neal2Algorithm::sample_unique_values() {
  for (auto &clus : unique_values) clus->sample_given_data();
}


#include "lwtnn/LightweightGraph.hh"
#include "lwtnn/InputPreprocessor.hh"
#include "lwtnn/Graph.hh"
#include <Eigen/Dense>

namespace {
  using namespace Eigen;
  using namespace lwt;

  // utility functions
  typedef LightweightGraph::NodeMap NodeMap;
  typedef InputPreprocessor IP;
  typedef std::vector<std::pair<std::string, IP*> > Preprocs;
  std::vector<VectorXd> get_input_vectors(const NodeMap& nodes,
                                          const Preprocs& preprocs) {
    std::vector<VectorXd> input_vectors;
    for (const auto& proc: preprocs) {
      if (!nodes.count(proc.first)) {
        throw NNEvaluationException("Can't find node " + proc.first);
      }
      const auto& preproc = *proc.second;
      input_vectors.emplace_back(preproc(nodes.at(proc.first)));
    }
    return input_vectors;
  }
  typedef LightweightGraph::SeqNodeMap SeqNodeMap;
  typedef InputVectorPreprocessor IVP;
  typedef std::vector<std::pair<std::string, IVP*> > VecPreprocs;
  std::vector<MatrixXd> get_input_seq(const SeqNodeMap& nodes,
                                      const VecPreprocs& preprocs) {
    std::vector<MatrixXd> input_mats;
    for (const auto& proc: preprocs) {
      if (!nodes.count(proc.first)) {
        throw NNEvaluationException("Can't find node " + proc.first);
      }
      const auto& preproc = *proc.second;
      input_mats.emplace_back(preproc(nodes.at(proc.first)));
    }
    return input_mats;
  }

}
namespace lwt {
  // ______________________________________________________________________
  // Lightweight Graph

  typedef LightweightGraph::NodeMap NodeMap;
  LightweightGraph::LightweightGraph(const GraphConfig& config,
                                     std::string default_output):
    m_graph(new Graph(config.nodes, config.layers))
  {
    for (const auto& node: config.inputs) {
      m_preprocs.emplace_back(
        node.name, new InputPreprocessor(node.variables));
    }
    for (const auto& node: config.input_sequences) {
      m_vec_preprocs.emplace_back(
        node.name, new InputVectorPreprocessor(node.variables));
    }
    size_t output_n = 0;
    for (const auto& node: config.outputs) {
      m_outputs.emplace_back(node.second.node_index, node.second.labels);
      m_output_indices.emplace(node.first, output_n);
      output_n++;
    }
    if (default_output.size() > 0) {
      if (!m_output_indices.count(default_output)) {
        throw NNConfigurationException("no output node" + default_output);
      }
      m_default_output = m_output_indices.at(default_output);
    } else if (output_n == 1) {
      m_default_output = 0;
    } else {
      throw NNConfigurationException("you must specify a default output");
    }
  }

  LightweightGraph::~LightweightGraph() {
    delete m_graph;
    for (auto& preproc: m_preprocs) {
      delete preproc.second;
      preproc.second = 0;
    }
    for (auto& preproc: m_vec_preprocs) {
      delete preproc.second;
      preproc.second = 0;
    }
  }

  ValueMap LightweightGraph::compute(const NodeMap& nodes,
                                     const SeqNodeMap& seq) const {
    return compute(nodes, seq, m_default_output);
  }
  ValueMap LightweightGraph::compute(const NodeMap& nodes,
                                     const SeqNodeMap& seq,
                                     const std::string& output) const {
    if (!m_output_indices.count(output)) {
      throw NNEvaluationException("no output node " + output);
    }
    return compute(nodes, seq, m_output_indices.at(output));
  }
  ValueMap LightweightGraph::compute(const NodeMap& nodes,
                                     const SeqNodeMap& seq,
                                     size_t idx) const {
    VectorSource source(get_input_vectors(nodes, m_preprocs),
                        get_input_seq(seq, m_vec_preprocs));
    VectorXd result = m_graph->compute(source, m_outputs.at(idx).first);
    const std::vector<std::string>& labels = m_outputs.at(idx).second;
    std::map<std::string, double> output;
    for (size_t iii = 0; iii < labels.size(); iii++) {
      output[labels.at(iii)] = result(iii);
    }
    return output;
  }

  VectorMap LightweightGraph::scan(const NodeMap& nodes,
                                     const SeqNodeMap& seq) const {
    return scan(nodes, seq, m_default_output);
  }
  VectorMap LightweightGraph::scan(const NodeMap& nodes,
                                     const SeqNodeMap& seq,
                                     const std::string& output) const {
    if (!m_output_indices.count(output)) {
      throw NNEvaluationException("no output node " + output);
    }
    return scan(nodes, seq, m_output_indices.at(output));
  }
  VectorMap LightweightGraph::scan(const NodeMap& nodes,
                                     const SeqNodeMap& seq,
                                     size_t idx) const {
    VectorSource source(get_input_vectors(nodes, m_preprocs),
                        get_input_seq(seq, m_vec_preprocs));
    MatrixXd result = m_graph->scan(source, m_outputs.at(idx).first);
    const std::vector<std::string>& labels = m_outputs.at(idx).second;
    std::map<std::string, std::vector<double> > output;
    for (size_t iii = 0; iii < labels.size(); iii++) {
      VectorXd row = result.row(iii);
      std::vector<double> out_vector(row.data(), row.data() + row.size());
      output[labels.at(iii)] = out_vector;
    }
    return output;
  }

}


#include <boost/config.hpp>
#if defined(BOOST_MSVC)
#   pragma warning(disable: 4127)

// turn off checked iterators to avoid performance hit
#   if !defined(__SGI_STL_PORT)  &&  !defined(_DEBUG)
#       define _SECURE_SCL 0
#       define _HAS_ITERATOR_DEBUGGING 0
#   endif
#endif
#include <bits/stdc++.h>
using namespace std;
#include "mapreduce.hpp"

vector<vector<double>> intermediate_pg(40000);
vector<vector<int>> outgoing_links(40000);
vector<double> pageranks(40000, 0.0f);
vector<double> pageranks_up(40000, 0.0f);
vector<double> sumlist;

int num_edges=0;
int num_webpages=20000;
double dp = 0.0f;

namespace dangling {

template<typename MapTask>
class get_data1 : mapreduce::detail::noncopyable
{
  public:
    get_data1() : sequence_(0)
    {
    }

    bool const setup_key(typename MapTask::key_type &key)
    {
        key = sequence_++;
        return key < num_webpages;
    }

    bool const get_data(typename MapTask::key_type const &key, typename MapTask::value_type &value)
    {
        value = pageranks[key];
        return true;
    }

  private:
    unsigned sequence_;
};

struct map_task : public mapreduce::map_task<unsigned,double>
{
    template<typename Runtime>
    void operator()(Runtime &runtime, key_type const &key, value_type const &value) const
    {
        int sizes = outgoing_links[key].size();
        if (sizes==0){
            runtime.emit_intermediate(key/key, pageranks[key]);
        }
    }
};

struct reduce_task: public mapreduce::reduce_task<unsigned,double >
{
    template<typename Runtime, typename It>
    void operator()(Runtime &runtime, key_type const &key, It it, It ite) const
    {
        value_type results(*it);
        double k  = 0.0f;
        for (It it1=it; it1!=ite; ++it1)
        {
            double r = *it1;
            k += r;
        }
        results = k;

        runtime.emit(key, results);
    }
};

typedef
mapreduce::job<dangling::map_task,
               dangling::reduce_task,
               mapreduce::null_combiner,
               dangling::get_data1<dangling::map_task>
> job;

}

namespace pagerank {

template<typename MapTask>
class datanodes1 : mapreduce::detail::noncopyable
{
  public:
    datanodes1() : sequence_(0)
    {
    }

    bool const setup_key(typename MapTask::key_type &key)
    {
        key = sequence_++;
        return key < num_webpages;
    }

    bool const get_data(typename MapTask::key_type const &key, typename MapTask::value_type &value)
    {
        value = pageranks[key];
        return true;
    }

  private:
    unsigned sequence_;
};

struct map_task : public mapreduce::map_task<unsigned,double>
{
    template<typename Runtime>
    void operator()(Runtime &runtime, key_type const &key, value_type const &value) const
    {
        int sizes = outgoing_links[key].size();
        if (sizes==0){
            dp+=value;
        }
        runtime.emit_intermediate(key, 0);
        for (int i =0; i<sizes; i++)
        {
            typename Runtime::reduce_task_type::key_type const emit_key = outgoing_links[key][i];
            double temp = double(value/sizes);
            runtime.emit_intermediate(emit_key, temp);
        }
    }
};

struct reduce_task : public mapreduce::reduce_task<unsigned, double >
{
    template<typename Runtime, typename It>
    void operator()(Runtime &runtime, key_type const &key, It it, It ite) const
    {
        value_type results(*it);
        double k  = 0.0f;
        for (It it1=it; it1!=ite; ++it1)
        {
            double r = *it1;
            k += r;
        }
        results = k;

        runtime.emit(key, results);
    }
};

typedef
mapreduce::job<pagerank::map_task,
               pagerank::reduce_task,
               mapreduce::null_combiner,
               pagerank::datanodes1<pagerank::map_task>
> job;

}

int main(int argc, char *argv[])
{
    double alpha = 0.85;
    double conv = 0.0001;
    int a,b,i;
    ifstream fopen;
    fopen.open("barabasi-20000.txt");
    while(!fopen.eof()){
        fopen>>a>>b;
        outgoing_links[a].push_back(b);
    }
    fopen.close();
    // int num_edges = 0;
    // cin>>num_webpages;
    // cin>>num_edges;
    // while(num_edges--){
    //     cin>>a>>b;
    //     outgoing_links[a].push_back(b);
    // }
    double pgr = double(1.0f/num_webpages);
    for(int i=0; i<num_webpages; i++){
        pageranks[i] = pgr;
    }

    mapreduce::specification spec;

    spec.reduce_tasks = std::max(1U, std::thread::hardware_concurrency());

    while(true){
        dangling::job::datasource_type get_data1;
        dangling::job job1(get_data1, spec);
        mapreduce::results result1;
    #ifdef _DEBUG
        job1.run<mapreduce::schedule_policy::sequential<dangling::job> >(result1);
    #else
        job1.run<mapreduce::schedule_policy::cpu_parallel<dangling::job> >(result1);
    #endif
        auto it=job1.begin_results();
        dp = it->second;
        // cout<<"----------dp here--------------------------"<<endl;
        // cout<<dp<<endl;

        pagerank::job::datasource_type datanodes1;
        pagerank::job job(datanodes1, spec);
        mapreduce::results result;
        bool converging = true;
    #ifdef _DEBUG
        job.run<mapreduce::schedule_policy::sequential<pagerank::job> >(result);
    #else
        job.run<mapreduce::schedule_policy::cpu_parallel<pagerank::job> >(result);
    #endif
        for (auto it=job.begin_results(); it!=job.end_results(); ++it){
            double conv_temp = pageranks[it->first];
            pageranks[it->first] = (it->second*alpha) + (dp*alpha/num_webpages) + (1-alpha)/num_webpages;
            // std::cout << it->first<<" "<<((it->second))<<endl;
            if((conv_temp-pageranks[i])>conv)
                converging = false;
        }
        if(converging){
            break;
        }
    }

    for(int i =0; i<num_webpages; i++){
    cout<<i<<" "<<pageranks[i]<<endl;
    }

    return 0;
}


/******************************************************************************
 * Copyright (C) 2014 by Jerome Maye                                          *
 * jerome.maye@gmail.com                                                      *
 ******************************************************************************/

#include "aslam/calibration/time-delay/design-variables/OdometryDesignVariables.h"

#include <boost/make_shared.hpp>

#include <sm/kinematics/EulerAnglesYawPitchRoll.hpp>
#include <sm/kinematics/quaternion_algebra.hpp>

#include <sm/PropertyTree.hpp>

#include <aslam/backend/EuclideanPoint.hpp>
#include <aslam/backend/RotationQuaternion.hpp>
#include <aslam/backend/Scalar.hpp>
#include <aslam/backend/GenericScalar.hpp>
#include <aslam/backend/FixedPointNumber.hpp>

#include <aslam/calibration/core/OptimizationProblem.h>

using namespace sm;
using namespace sm::kinematics;
using namespace aslam::backend;

namespace aslam {
  namespace calibration {

/******************************************************************************/
/* Constructors and Destructor                                                */
/******************************************************************************/

    OdometryDesignVariables::OdometryDesignVariables(const PropertyTree&
        config) {
      b = boost::make_shared<Scalar>(config.getDouble(
        "intrinsics/halfWheelBase"));
      b->setActive(true);

      k_l = boost::make_shared<Scalar>(config.getDouble(
        "intrinsics/lwCoefficient"));
      k_l->setActive(true);
      k_r = boost::make_shared<Scalar>(config.getDouble(
        "intrinsics/rwCoefficient"));
      k_r->setActive(true);

      v_r_vp = boost::make_shared<EuclideanPoint>(Eigen::Vector3d(
        config.getDouble("extrinsics/translation/x"),
        config.getDouble("extrinsics/translation/y"),
        config.getDouble("extrinsics/translation/z")));
      if (config.getBool("extrinsics/translation/active"))
        v_r_vp->setActive(true);
      else
        v_r_vp->setActive(false);
      const EulerAnglesYawPitchRoll ypr;
      v_R_p = boost::make_shared<RotationQuaternion>(
        ypr.parametersToRotationMatrix(
        Eigen::Vector3d(config.getDouble("extrinsics/rotation/yaw"),
        config.getDouble("extrinsics/rotation/pitch"),
        config.getDouble("extrinsics/rotation/roll"))));
      if (config.getBool("extrinsics/rotation/active"))
        v_R_p->setActive(true);
      else
        v_R_p->setActive(false);

      t_l = boost::make_shared<TimeDesignVariable>(
          Time::Integer(config.getInt("intrinsics/lwTimeDelay")));
      t_r = boost::make_shared<TimeDesignVariable>(
          Time::Integer(config.getInt("intrinsics/rwTimeDelay")));
      if (config.getBool("intrinsics/timeDelaysActive")) {
        t_l->setActive(true);
        t_r->setActive(true);
      }
      else {
        t_l->setActive(false);
        t_r->setActive(false);
      }

    }

/******************************************************************************/
/* Methods                                                                    */
/******************************************************************************/

    void OdometryDesignVariables::addToBatch(const BatchSP& batch, size_t
        groupId) {
      batch->addDesignVariable(b, groupId);
      batch->addDesignVariable(k_l, groupId);
      batch->addDesignVariable(t_l, groupId);
      batch->addDesignVariable(k_r, groupId);
      batch->addDesignVariable(t_r, groupId);
      batch->addDesignVariable(v_r_vp, groupId);
      batch->addDesignVariable(v_R_p, groupId);
    }

    Eigen::VectorXd OdometryDesignVariables::getParameters() const {
      Eigen::VectorXd params(11);
      params(0) = b->getParameters()(0);
      params(1) = k_l->getParameters()(0);
      params(2) = t_l->getParameters()(0);
      params(3) = k_r->getParameters()(0);
      params(4) = t_r->getParameters()(0);
      params.segment<3>(5) = v_r_vp->toEuclidean();
      const EulerAnglesYawPitchRoll ypr;
      params.segment<3>(8) = ypr.rotationMatrixToParameters(
        quat2r(v_R_p->getQuaternion()));
      return params;
    }

/******************************************************************************/
/* Stream methods                                                             */
/******************************************************************************/

    void OdometryDesignVariables::read(std::istream& stream) {
    }

    void OdometryDesignVariables::write(std::ostream& stream) const {
      stream << "b: " << b->getParameters() << std::endl;
      stream << "k_l: " << k_l->getParameters() << std::endl;
      stream << "t_l: " << t_l->getParameters() << std::endl;
      stream << "k_r: " << k_r->getParameters() << std::endl;
      stream << "t_r: " << t_r->getParameters() << std::endl;
      stream << "v_r_vp: " << v_r_vp->toEuclidean().transpose() << std::endl;
      const EulerAnglesYawPitchRoll ypr;
      stream << "v_R_p: " << ypr.rotationMatrixToParameters(
        quat2r(v_R_p->getQuaternion())).transpose() << std::endl;
    }

    void OdometryDesignVariables::read(std::ifstream& stream) {
    }

    void OdometryDesignVariables::write(std::ofstream& stream) const {
    }

  }
}


/**
 * @file
 */

#include <atomic>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#ifndef KVCG_MODEL_HH
#define KVCG_MODEL_HH

namespace pt = boost::property_tree;

namespace kvgpu {

    template<typename K>
    struct Model {
        /**
         * Return true if should be cached
         * @param key
         * @param hash
         * @return
         */
        virtual bool operator()(K key, unsigned hash) const = 0;
    };

    template<typename K>
    struct SimplModel : public Model<K> {

        SimplModel() : value(10000000) {

        }

        explicit SimplModel(int v) : value(v) {

        }

        explicit SimplModel(const std::string &filename) {
            pt::ptree root;
            pt::read_json(filename, root);
            value = root.get<int>("value");
        }

        SimplModel(const SimplModel<K> &other) {
            value = other.value;
        }

        /**
         * Return true if should be cached
         * @param key
         * @param hash
         * @return
         */
        bool operator()(K key, unsigned hash) const {
            return hash < value;
        }

    private:
        int value;
    };

    template<typename K>
    struct AnalyticalModel : public Model<K> {

        AnalyticalModel() : threshold(1.5e-5), size(100000), pred(new std::atomic<float *>()) {
            *pred = new float[size];
            for (int i = 0; i < size; i++) {
                (*pred)[i] = 1.0 / size;
            }
        }

        explicit AnalyticalModel(float v) : threshold(v), size(100000), pred(new std::atomic<float *>()) {
            *pred = new float[size];
            for (int i = 0; i < size; i++) {
                (*pred)[i] = 1.0 / size;
            }
        }

        explicit AnalyticalModel(const std::string &filename) : pred(new std::atomic<float *>()) {
            pt::ptree root;
            pt::read_json(filename, root);
            threshold = root.get<int>("threshold");
            size = root.get<size_t>("size");
            *pred = new float[size];

            for (int i = 0; i < size; i++) {
                (*pred)[i] = root.get<float>("pred" + std::to_string(i));
            }

        }

        AnalyticalModel(const AnalyticalModel<K> &other) {
            threshold = other.threshold;
            size = other.size;
            pred = other.pred;
        }

        void train(std::vector <std::pair<K, unsigned>> v) {
            for (int i = 0; i < size; i++) {
                (*pred)[i] = 0.0;
            }
            for (auto &elm : v) {
                (*pred)[elm.second % size]++;
            }
            for (int i = 0; i < size; i++) {
                (*pred)[i] /= v.size();
            }
        }

        void persist(const std::string &filename) const {
            pt::ptree root;

            root.put("threshold", threshold);
            root.put("size", size);
            for (int i = 0; i < size; i++) {
                root.put("pred" + std::to_string(i), (*pred)[i]);
            }

            pt::write_json(filename, root);
        }

        /**
         * Return true if should be cached
         * @param key
         * @param hash
         * @return
         */
        bool operator()(K key, unsigned hash) const {
            return (*pred)[hash % size] >= threshold;
        }

    private:
        float threshold;
        size_t size;
        std::atomic<float *> *pred;
    };

}

#endif //KVCG_MODEL_HH


// Copyright (C) 2014-2016 Vicente J. Botet Escriba
//
//  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)

// <experimental/n_functor.hpp>
// <experimental/utility.hpp> tuple

#include <experimental/tuple.hpp>
#include <experimental/n_functor.hpp>
#include <experimental/product_type.hpp>

#include <boost/detail/lightweight_test.hpp>

int twice(int i) {
  return 2*i;
}

int main()
{
#if __cplusplus >= 201402L
  namespace stde = std::experimental;

  using T = std::tuple<int, int>;

  static_assert(stde::is_product_type_v<T>, "ERROR");
  static_assert(stde::is_type_constructible_v<T>, "ERROR");
  static_assert(std::is_base_of<stde::n_functor::tag, stde::n_functor::traits<std::tuple<stde::_t>>> ::value, "ERROR");
  static_assert(stde::is_n_functor<std::tuple<stde::_t>>::value, "ERROR");

  {
    int v=1;
    T x = std::make_tuple(v,v);
    BOOST_TEST(std::get<0>(x) == 1);
    BOOST_TEST(std::get<1>(x) == 1);
    auto call = std::make_tuple(twice,twice);
    T y = stde::n_functor::transform(x, call);
    BOOST_TEST(std::get<0>(y) == 2);
    BOOST_TEST(std::get<1>(y) == 2);
  }
#endif

  return ::boost::report_errors();
}


// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/any_of.hpp>
#include <boost/hana/assert.hpp>
#include <boost/hana/bool.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/find_if.hpp>
#include <boost/hana/fwd/at.hpp>
#include <boost/hana/fwd/drop_front.hpp>
#include <boost/hana/fwd/is_empty.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/optional.hpp>
namespace hana = boost::hana;

// A simple infinite Iterable.
template <int i>
struct counter { };

namespace boost { namespace hana {
    template <int i>
    struct at_impl<counter<i>> {
        template <typename N>
        static constexpr auto apply(counter<i>, N const&) {
            return hana::int_c<i + N::value>;
        }
    };

    template <int i>
    struct drop_front_impl<counter<i>> {
        template <typename N>
        static constexpr auto apply(counter<i>, N) { return counter<i + N::value>{}; }
    };

    template <int i>
    struct is_empty_impl<counter<i>> {
        static constexpr auto apply(counter<i>) { return hana::false_c; }
    };
}}

int main() {
    // find_if and any_of should short-circuit and stop even though the
    // Iterable is infinite.
    BOOST_HANA_CONSTANT_CHECK(hana::any_of(counter<1>{}, hana::equal.to(hana::int_c<4>)));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::find_if(counter<1>{}, hana::equal.to(hana::int_c<4>)),
        hana::just(hana::int_c<4>)
    ));
}


// Copyright 2018-2019 Henry Schreiner and Hans Dembinski
//
// Distributed under the 3-Clause BSD License.  See accompanying
// file LICENSE or https://github.com/scikit-hep/boost-histogram for details.

#pragma once

#include <bh_python/metadata.hpp>

#include <boost/core/nvp.hpp>
#include <boost/histogram/axis/regular.hpp>

namespace bh = boost::histogram;

namespace axis {

/// Mimics the numpy behavoir exactly.
class regular_numpy : public bh::axis::regular<double, bh::use_default, metadata_t> {
    using value_type = double;
    double stop_;

  public:
    regular_numpy(unsigned n, value_type start, value_type stop, metadata_t meta = {})
        : regular(n, start, stop, meta)
        , stop_(stop) {}

    regular_numpy()
        : regular()
        , stop_(0) {}

    bh::axis::index_type index(value_type v) const {
        return v <= stop_ ? std::min(regular::index(v), size() - 1) : regular::index(v);
    }

    template <class Archive>
    void serialize(Archive& ar, unsigned version) {
        regular::serialize(ar, version);
        ar& boost::make_nvp("stop", stop_);
    }
};

} // namespace axis


#include "graph.hpp"

#include <sstream>

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

using namespace graph;

BOOST_AUTO_TEST_CASE(printing_stream_operator_list_graph) {
	using Graph = list::Graph<NoProperty, NoProperty>;

	Graph myGraph{{"4", "5"}, {"6", "3"}, {"2", "4"}, {"5", "2"}, {"6", "4"}, {"3", "3"}};

	std::string expected = "\"4\" -> \"5\"\n\"5\" -> \"2\"\n\"6\" -> \"3\"\n\"6\" -> \"4\"\n\"3\" "
	                       "-> \"3\"\n\"2\" -> \"4\"\n";
	std::ostringstream result;

	result << myGraph;
	BOOST_CHECK_EQUAL(result.str(), expected);
}

BOOST_AUTO_TEST_CASE(printing_stream_operator_matrix_graph) {
	using Graph = matrix::Graph<NoProperty, NoProperty>;

	Graph myGraph{{"4", "5"}, {"6", "3"}, {"2", "4"}, {"5", "2"}, {"6", "4"}, {"3", "3"}};

	std::string expected = "\"2\" -> \"4\"\n\"3\" -> \"3\"\n\"4\" -> \"5\"\n\"5\" -> \"2\"\n\"6\" "
	                       "-> \"3\"\n\"6\" -> \"4\"\n";
	std::ostringstream result;

	result << myGraph;
	BOOST_CHECK_EQUAL(result.str(), expected);
}

BOOST_AUTO_TEST_CASE(printing_make_digraph_list_graph) {
	using Graph = list::Graph<NoProperty, NoProperty>;

	Graph myGraph{{"4", "5"}, {"6", "3"}, {"2", "4"}, {"5", "2"}, {"6", "4"}, {"3", "3"}};

	std::string expected = "digraph myGraph {\n\"4\" -> \"5\"\n\"5\" -> \"2\"\n\"6\" -> "
	                       "\"3\"\n\"6\" -> \"4\"\n\"3\" -> \"3\"\n\"2\" -> \"4\"\n}\n";

	BOOST_CHECK_EQUAL(makeDigraph("myGraph", myGraph), expected);
}

BOOST_AUTO_TEST_CASE(printing_make_digraph_matrix_graph) {
	using Graph = matrix::Graph<NoProperty, NoProperty>;

	Graph myGraph{{"4", "5"}, {"6", "3"}, {"2", "4"}, {"5", "2"}, {"6", "4"}, {"3", "3"}};

	std::string expected = "digraph myGraph {\n\"2\" -> \"4\"\n\"3\" -> \"3\"\n\"4\" -> "
	                       "\"5\"\n\"5\" -> \"2\"\n\"6\" -> \"3\"\n\"6\" -> \"4\"\n}\n";

	BOOST_CHECK_EQUAL(makeDigraph("myGraph", myGraph), expected);
}

BOOST_AUTO_TEST_CASE(printing_weighted_edge_list_graph) {
	using Graph = list::Graph<NoProperty, WeightedProperty>;

	Graph myGraph{{"4", "5", WeightedProperty{1}},
	              {"6", "3", WeightedProperty{2}},
	              {"2", "4", WeightedProperty{3}},
	              {"5", "2", WeightedProperty{4}},
	              {"6", "4", WeightedProperty{5}},
	              {"3", "3", WeightedProperty{6}}};

	std::string expected = "digraph myGraph {\n"
	                       "\"4\" -> \"5\" [label=\"1\"]\n"
	                       "\"5\" -> \"2\" [label=\"4\"]\n"
	                       "\"6\" -> \"3\" [label=\"2\"]\n"
	                       "\"6\" -> \"4\" [label=\"5\"]\n"
	                       "\"3\" -> \"3\" [label=\"6\"]\n"
	                       "\"2\" -> \"4\" [label=\"3\"]\n"
	                       "}\n";

	BOOST_CHECK_EQUAL(makeDigraph("myGraph", myGraph), expected);
}

BOOST_AUTO_TEST_CASE(printing_weighted_edge_matrix_graph) {
	using Graph = matrix::Graph<NoProperty, WeightedProperty>;

	Graph myGraph{{"4", "5", WeightedProperty{1}},
	              {"6", "3", WeightedProperty{2}},
	              {"2", "4", WeightedProperty{3}},
	              {"5", "2", WeightedProperty{4}},
	              {"6", "4", WeightedProperty{5}},
	              {"3", "3", WeightedProperty{6}}};

	std::string expected = "digraph myGraph {\n"
	                       "\"2\" -> \"4\" [label=\"3\"]\n"
	                       "\"3\" -> \"3\" [label=\"6\"]\n"
	                       "\"4\" -> \"5\" [label=\"1\"]\n"
	                       "\"5\" -> \"2\" [label=\"4\"]\n"
	                       "\"6\" -> \"3\" [label=\"2\"]\n"
	                       "\"6\" -> \"4\" [label=\"5\"]\n"
	                       "}\n";

	BOOST_CHECK_EQUAL(makeDigraph("myGraph", myGraph), expected);
}


/*    Copyright (c) 2010-2015, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      110207    B. Romgens        File created.
 *      110215    K. Kumar          Minor modifications to layout, comments
 *                                  and variable-naming.
 *      110411    K. Kumar          Added unit test for
 *                                  convertCartesianToSpherical( ) function.
 *      110701    K. Kumar          Updated failing tests with relative errors.
 *      110708    K. Kumar          Added unit tests for computeSampleMean( )
 *                                  and computeSampleVariance( ) functions.
 *      110905    S. Billemont      Reorganized includes.
 *                                  Moved (con/de)structors and getter/setters to header.
 *      111111    K. Kumar          Strange error with convertCylindricalToCartesian function;
 *                                  achieved precision of results is less than machine precision,
 *                                  fixed by using slightly larger precision tolerance.
 *      120202    K. Kumar          Separated from unitTestBasicMathematics.cpp into new
 *                                  Interpolators sub-directory.
 *      120529    E.A.G. Heeren     Boostified unit test.
 *      120615    T. Secretin       Minor layout changes.
 *      120716    D. Dirkx          Updated with interpolator architecture.
 *
 *    References
 *
 *    Notes
 *
 */

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>
#include "Tudat/Mathematics/Interpolators/linearInterpolator.h"

#include <Eigen/Core>

#include "Tudat/Basics/testMacros.h"
#include "Tudat/InputOutput/matrixTextFileReader.h"
#include "Tudat/InputOutput/basicInputOutput.h"

namespace tudat
{
namespace unit_tests
{

BOOST_AUTO_TEST_SUITE( test_linear_interpolation )

// Test implementation of linear interpolation function for data vectors.
BOOST_AUTO_TEST_CASE( test_linearInterpolation_vector )
{
    // Set vectors of data.
    const Eigen::Vector3d sortedIndependentVariables( 0.0, 1.0, 3.0 );
    const Eigen::Vector3d associatedDependentVariables( -20.0, 20.0, 21.0 );

    // Test 1: Test vector data with expected result of 0.0.
    {
        // Set target independent value in vector data.
        const double targetIndependentVariableValue = 0.5;

        // Compute interpolation.
        const double interpolatedValue = interpolators::computeLinearInterpolation(
                    sortedIndependentVariables, associatedDependentVariables,
                    targetIndependentVariableValue );

        // Verify that interpolated value corresponds to expected value (0.0).
        BOOST_CHECK_SMALL( std::fabs( interpolatedValue - 0.0 ),
                           std::numeric_limits< double >::min( ) );
    }

    // Test 2: Test vector data with expected result of 20.5.

    // Set target independent value in vector data.
    const double targetIndependentVariableValue = 2.0;

    // Compute interpolation.
    const double interpolatedValue = interpolators::computeLinearInterpolation(
                sortedIndependentVariables, associatedDependentVariables,
                targetIndependentVariableValue );

    // Verify that interpolated value corresponds to expected value (20.5).
    BOOST_CHECK_SMALL( std::fabs( interpolatedValue - 20.5 ),
                       std::numeric_limits< double >::min( ) );
}

// Test linear interpolation with map of vectors with keys as independent variable.
BOOST_AUTO_TEST_CASE( test_linearInterpolation_map )
{
    // Declare map of data and vectors for map value.
    std::map < double, Eigen::VectorXd > sortedIndepedentAndDependentVariables;
    const Eigen::Vector3d vectorOne( 10.0, -10.0, 70.0 );
    const Eigen::Vector3d vectorTwo( 20.0, -5.0, 80.0 );
    const Eigen::Vector3d vectorThree( 30.0, 60.0, 90.0 );

    // Set map values in map using vector data.
    sortedIndepedentAndDependentVariables[ 0.0 ] = vectorOne;
    sortedIndepedentAndDependentVariables[ 1.0 ] = vectorTwo;
    sortedIndepedentAndDependentVariables[ 2.0 ] = vectorThree;

    // Set target independent variable value for interpolation.
    const double targetIndependentVariableValue = 1.5;

    // Compute interpolation.
    Eigen::Vector3d interpolatedVector = interpolators::computeLinearInterpolation(
                sortedIndepedentAndDependentVariables,
                targetIndependentVariableValue );

    // Check that interpolated values correspond to expected elements [25, 27.5, 85].
    BOOST_CHECK_SMALL( std::fabs( interpolatedVector( 0 ) - 25.0 ),
                       std::numeric_limits< double >::min( ) );

    BOOST_CHECK_SMALL( std::fabs( interpolatedVector( 1 ) - 27.5 ),
                       std::numeric_limits< double >::min( ) );

    BOOST_CHECK_SMALL( std::fabs( interpolatedVector( 2 ) - 85.0 ),
                       std::numeric_limits< double >::min( ) );
}

// Test linear interpolation from benchmark values generetd by Matlab, interpolating the
// error function.
BOOST_AUTO_TEST_CASE( test_linearInterpolation_matlab_compare )
{
    using namespace interpolators;

    // Load input data used for generating matlab interpolation.
    Eigen::MatrixXd inputData = input_output::readMatrixFromFile(
                input_output::getTudatRootPath( ) +
                "Mathematics/Interpolators/UnitTests/interpolator_test_input_data.dat","," );

    // Put data in STL vectors.
    std::vector< double > independentVariableValues;
    std::vector< double > dependentVariableValues;

    for ( int i = 0; i < inputData.rows( ); i++ )
    {
        independentVariableValues.push_back( inputData( i, 0 ) );
        dependentVariableValues.push_back( inputData( i, 1 ) );
    }

    // Create linear interpolator using hunting algorithm.
    LinearInterpolatorDouble linearInterpolator(
                independentVariableValues, dependentVariableValues, huntingAlgorithm );

    // Load points at which interpolator is to be evaluated and data generated by Matlab.
    Eigen::MatrixXd benchmarkData = input_output::readMatrixFromFile(
                input_output::getTudatRootPath( ) +
                "Mathematics/Interpolators/UnitTests/linear_interpolator_test_output_data.dat",
                "," );

    // Perform interpolation for required data points.
    Eigen::VectorXd outputData = Eigen::VectorXd( benchmarkData.rows( ) );
    for ( int i = 0; i < outputData.rows( ); i++ )
    {
        outputData[ i ] = linearInterpolator.interpolate( benchmarkData( i, 0 ) );
    }

    TUDAT_CHECK_MATRIX_CLOSE_FRACTION( benchmarkData.block( 0, 1, benchmarkData.rows( ), 1 ),
                                       outputData, 1.0E-13 );

    // Create linear interpolator, now with nearest neighbvour search.
    linearInterpolator = LinearInterpolatorDouble(
                independentVariableValues, dependentVariableValues, binarySearch );

    // Perform interpolation for required data points.
    for ( int i = 0; i < outputData.rows( ); i++ )
    {
        outputData[ i ] = linearInterpolator.interpolate( benchmarkData( i, 0 ) );
    }

    TUDAT_CHECK_MATRIX_CLOSE_FRACTION( benchmarkData.block( 0, 1, benchmarkData.rows( ), 1 ),
                                       outputData, 1.0E-13 );
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests
} // namespace tudat


#ifndef _blend_hpp_
#define _blend_hpp_

#include <boost/gil/gil_all.hpp>

struct alpha24_blend
{
	short alpha;
	alpha24_blend(short alpha) : alpha(alpha){}

	template <typename T>
	void operator()(T& dst, const T src)
	{
		dst = ((dst + 1) * alpha >> 8) -
			((src + 1) * alpha >> 8) +
				src;
	}
};

template <typename blend_t, typename pixel_t, typename grayview_t, typename view_t> inline
void copy(pixel_t pixel, const grayview_t& grayview, const view_t& view)
{
	using namespace boost::gil;
	BOOST_ASSERT(grayview.width() == view.width());

	typedef typename view_t::x_iterator x_iterator_t;
	typedef typename grayview_t::x_iterator x_iterator2_t;

	for (int y = 0; y < view.height(); ++y)
	{
        	x_iterator_t it_view = view.row_begin(y);
        	x_iterator2_t it_gray = grayview.row_begin(y);
		for (int x = 0; x < view.width(); ++x)
		{
			pixel_t dst = pixel;
			static_for_each(dst, it_view[x], blend_t(it_gray[x]));
			it_view[x] = dst;
		}
	}
}

template <typename blend_t, typename grayview_t, typename view_t> inline
void copy(const grayview_t& gray, const view_t& bottom, const view_t& top)
{
	using namespace boost::gil;
	BOOST_ASSERT(gray.width() == bottom.width());
	BOOST_ASSERT(bottom.width() == top.width());

	typedef typename view_t::x_iterator x_iterator_t;
	typedef typename grayview_t::x_iterator x_iterator2_t;
	typedef typename view_t::value_type value_type_t;

	for (int y = 0; y < top.height(); ++y)
	{
        	x_iterator_t it_top = top.row_begin(y);
        	x_iterator_t it_bot = bottom.row_begin(y);
        	x_iterator2_t it_gray = gray.row_begin(y);
		for (int x = 0; x < top.width(); ++x)
		{
			value_type_t dst = it_bot[x];
			static_for_each(dst, it_top[x], blend_t(it_gray[x]));
			it_top[x] = dst;
		}
	}
}

#endif


/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman
    Copyright (c) 2011 Jan Frederick Eick

    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)
==============================================================================*/
#if !defined(BOOST_SPIRIT_X3_UINT_APR_17_2006_0901AM)
#define BOOST_SPIRIT_X3_UINT_APR_17_2006_0901AM

#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/support/numeric_utils/extract_int.hpp>
#include <cstdint>

namespace boost {
namespace spirit {
namespace x3 {
///////////////////////////////////////////////////////////////////////////
template <typename T, unsigned Radix = 10, unsigned MinDigits = 1,
          int MaxDigits = -1>
struct uint_parser : parser<uint_parser<T, Radix, MinDigits, MaxDigits>> {
  // check template parameter 'Radix' for validity
  static_assert((Radix >= 2 && Radix <= 36), "Error Unsupported Radix");

  typedef T attribute_type;
  static bool const has_attribute = true;

  template <typename Iterator, typename Context, typename Attribute>
  bool parse(Iterator &first, Iterator const &last, Context const &context,
             unused_type, Attribute &attr) const {
    typedef extract_uint<T, Radix, MinDigits, MaxDigits> extract;
    x3::skip_over(first, last, context);
    return extract::call(first, last, attr);
  }
};

#define BOOST_SPIRIT_X3_UINT_PARSER(uint_type, name)                           \
  typedef uint_parser<uint_type> name##type;                                   \
  name##type const name = {};                                                  \
  /***/

BOOST_SPIRIT_X3_UINT_PARSER(unsigned long, ulong_)
BOOST_SPIRIT_X3_UINT_PARSER(unsigned short, ushort_)
BOOST_SPIRIT_X3_UINT_PARSER(unsigned int, uint_)
BOOST_SPIRIT_X3_UINT_PARSER(unsigned long long, ulong_long)

BOOST_SPIRIT_X3_UINT_PARSER(uint8_t, uint8)
BOOST_SPIRIT_X3_UINT_PARSER(uint16_t, uint16)
BOOST_SPIRIT_X3_UINT_PARSER(uint32_t, uint32)
BOOST_SPIRIT_X3_UINT_PARSER(uint64_t, uint64)

#undef BOOST_SPIRIT_X3_UINT_PARSER

#define BOOST_SPIRIT_X3_UINT_PARSER(uint_type, radix, name)                    \
  typedef uint_parser<uint_type, radix> name##type;                            \
  name##type const name = name##type();                                        \
  /***/

BOOST_SPIRIT_X3_UINT_PARSER(unsigned, 2, bin)
BOOST_SPIRIT_X3_UINT_PARSER(unsigned, 8, oct)
BOOST_SPIRIT_X3_UINT_PARSER(unsigned, 16, hex)

#undef BOOST_SPIRIT_X3_UINT_PARSER

} // namespace x3
} // namespace spirit
} // namespace boost

#endif


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

#ifndef __H__OBJ_PARSER_FREE_FORM_ATTRIBUTE_STATE__H__
#define __H__OBJ_PARSER_FREE_FORM_ATTRIBUTE_STATE__H__

#include <boost/fusion/adapted.hpp>
#include <cstdint>
#include <vector>

//------------------------------------------------------------------------------------------

enum class OBJFreeFormType
{
    None = 0,
    BasisMatrix,
    Bezier,
    BSpline,
    Cardinal,
    Taylor
};

/**
 * \struct OBJFreeFormAttributeState
 */
struct OBJFreeFormAttributeState
{
    OBJFreeFormAttributeState()
        : type(OBJFreeFormType::None),
          rational(false),
          degreeU(0),
          degreeV(0),
          stepU(0),
          stepV(0),
          mergeGroupNumber(0),
          mergeGroupResolution(0.0f)
    {

    }

    //--------------------------------------------------------------------

    OBJFreeFormType type;                   ///<
    bool rational;                          ///<

    int32_t degreeU;                        ///<
    int32_t degreeV;                        ///<

    int32_t stepU;                          ///<
    int32_t stepV;                          ///<

    int32_t mergeGroupNumber;               ///< Merging group number. A value of 0 indicates no adjacency detection.
    float mergeGroupResolution;             ///< Maximum distance between two merged surfaces. Must be greater than 0.0 if merging is on.

    std::vector<float> basisMatrixU;        ///<
    std::vector<float> basisMatrixV;        ///<
};

//------------------------------------------------------------------------------------------

#endif


#include <exception>
#include <iostream>
#include <string>
#include <vector>

#include <boost/program_options.hpp>

#include <frovedis.hpp>
#include <frovedis/ml/tree/tree_model.hpp>

namespace po = boost::program_options;
using namespace frovedis;

template <typename T>
void do_prune(
  const std::string& input_path, const std::string& output_path,
  const T min_info_gain, const bool binary_mode
) {
  time_spent timer(DEBUG);
  decision_tree_model<T> input_model, output_model;

  if (binary_mode) {
    timer.reset();
    input_model.loadbinary(input_path);
    timer.show("load model: ");
    output_model = input_model.prune(min_info_gain);
    timer.show("prune tree: ");
    output_model.savebinary(output_path);
    timer.show("save model: ");
  } else {
    timer.reset();
    input_model.load(input_path);
    timer.show("load model: ");
    output_model = input_model.prune(min_info_gain);
    timer.show("prune tree: ");
    output_model.save(output_path);
    timer.show("save model: ");
  }

  RLOG(TRACE) << "<BEFORE>" << std::endl << input_model << std::endl;
  RLOG(TRACE) << "<AFTER>" << std::endl << output_model;
}

template <typename T>
void do_prune(const po::variables_map& argmap) {
  do_prune<T>(
    argmap["input"].as<std::string>(),
    argmap["output"].as<std::string>(),
    argmap["min-infogain"].as<double>(),
    argmap.count("binary")
  );
}

po::variables_map parse(int argc, char** argv) {
  po::options_description opt_desc("");
  opt_desc.add_options()
    ("help,h", "show this help message and exit");

  po::options_description reqarg_desc("required arguments");
  reqarg_desc.add_options()
    ("input,i", po::value<std::string>(), "an input model")
    ("output,o", po::value<std::string>(), "an output model");

  po::options_description optarg_desc("optional arguments");
  optarg_desc.add_options()
    ("min-infogain,g", po::value<double>()->default_value(0.0),
     "the minimum information gain (threshold)")
    ("binary", "use binary input/output")
    ("double", "use double precision")
    ("verbose", "set log-level to DEBUG")
    ("trace", "set log-level to TRACE");

  opt_desc.add(reqarg_desc).add(optarg_desc);
  po::variables_map argmap;
  try {
    po::store(po::command_line_parser(argc, argv)
                .options(opt_desc)
//              .allow_unregistered()
                .run(),
              argmap);
    po::notify(argmap);
  } catch (const po::error_with_option_name& e) {
    std::cerr << e.what() << std::endl;
    finalizefrovedis(1);
  }

  // help message
  if (argmap.count("help")) {
    std::cerr << opt_desc;
    finalizefrovedis(0);
  }

  // check required arguments
  bool missing = false;
  for (const auto opt: reqarg_desc.options()) {
    const std::string& name = opt->long_name();
    if (!argmap.count(name)) {
      std::cerr << "option '--" << name << "' is required" << std::endl;
      missing = true;
    }
  }
  if (missing) { finalizefrovedis(1); }

  return argmap;
}

int main(int argc, char** argv) {
  use_frovedis use(argc, argv);

  const auto argmap = parse(argc, argv);
  if (argmap.count("verbose")) { set_loglevel(DEBUG); }
  if (argmap.count("trace")) { set_loglevel(TRACE); }

  try {
    if (argmap.count("double")) {
      do_prune<double>(argmap);
    } else {
      do_prune<float>(argmap);
    }
  } catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    finalizefrovedis(1);
  }

  return 0;
}


/**
 * Author: Dominic Liao-McPherson
 * Contact: dliaomcp@umich.edu
 *
 * This file is part of the static-linalg library.
 * Copyright (C) 2018-2019 University of Michigan.
 *
 * This software is distributed under the BSD-3-Clause license.
 * You should have received a LICENSE file along with this program.
 * If not see: <https://opensource.org/licenses/BSD-3-Clause>
 */

#include "TestingUtils.h"
#include <Eigen/Dense>
#include "StaticMatrix.h"

namespace testutils{
// used to compute a norm of the difference between an eigen matrix and a StaticMatrix
double DiffNorm(const StaticMatrix &A, const Eigen::Ref<const Eigen::MatrixXd> &B){
	int m = A.rows();
	int n = A.cols();

	double a = 0;
	for(int i = 0;i<m;i++){
		for(int j = 0;j<n;j++){
			a += abs(A(i,j) - B(i,j));
		}
	}
	return a;
}

void CopyEig(const StaticMatrix &A, Eigen::MatrixXd &B){
	int m = A.rows();
	int n = A.cols();

	for(int i = 0;i<m;i++){
		for(int j = 0;j<n;j++){
			B(i,j) = A(i,j);
		}
	}
}

} // namespace testutils