#include "WordIndexDictionary.h"
#include "Unigram_Model/TopicLearner/TypeTopicCounts.h"
#include "Unigram_Model/TopicLearner/TopicCounts.h"
#include "document.pb.h"
#include "DocumentReader.h"
#include <cstdio>
#include <cstring>
#include <string>
#include "matrixIO.h"
#include "types.h"
#include "utils.h"
#include "commons/TopicLearner/Parameter.h"
#include <fstream>
#include <armadillo>
using namespace arma;
using namespace std;

void dumpJson(string fileName, arma::mat &m)
{
	ofstream fout(fileName.c_str());
	if (m.n_cols == 1) {
		// Vector
		fout << "[";
		for (int k=0; k<m.n_rows; ++k)
		{
			fout << m(k, 0);

			if (k<m.n_rows-1)
				fout << ", ";
		}
		fout << "]";
	} else {
		// Matrix
		fout << "[";
		for (int x=0; x<m.n_rows; ++x)
		{
			fout << "[";

			for (int y=0; y<m.n_cols; ++y)
			{
				fout << m(x, y);

				if (y<m.n_cols-1)
					fout << ", ";
			}

			fout << "]";
			if (x<m.n_rows-1)
				fout << ", ";
		}
		fout << "]";
	}
	fout.close();
}

int main(int argc, char **argv)
{
	if (argc != 10)
	{
		printf("Usage: <topics> <dictionary> <ttc> <document dump prefix> <topic dump prefix> <beta> <num> <mu dump name> <cov dump name>\n");
		return 0;
	}

	int num_topics				= atoi(argv[1]);
	string dictionary_path		= string(argv[2]);
	string ttc_path				= string(argv[3]);
	string document_file_prefix	= string(argv[4]);
	string topic_file_prefix	= string(argv[5]);
	double beta					= atof(argv[6]);
	int num						= atoi(argv[7]);

        string mu_file			= string(argv[8]);
	string cov_file		= string(argv[9]);

	for (int i=0; i<argc; ++i)
	{
		puts(argv[i]);
	}

	// Read dictionary
	WordIndexDictionary dict;
	dict.initialize_from_dump(dictionary_path);
	int num_words = dict.get_num_words();

	ofstream fdict((dictionary_path + ".json").c_str());
	fdict << "[";
	for (int w=0; w<num_words; ++w)
	{
		fdict << "\"" << dict.get_word(w) << "\"";
		if (w < num_words - 1)
			fdict << ", ";
	}
	fdict << "]" << endl;
	fdict.close();

	TypeTopicCounts ttc(num_words, num_topics);
	ttc.initialize_from_dump(ttc_path, &dict);

	cerr << num_topics << " Topics." << endl;
	cerr << num_words << " Words." << endl;
	cerr << "Beta = " << beta << endl;

	ofstream fttc((ttc_path + ".json").c_str());
	ofstream fphi((ttc_path + ".phi.json").c_str());
	double **phi_wk = alloc2D<double>(num_words, num_topics);
	atomic<topic_t> *tokens_per_topic = new atomic<topic_t> [num_topics]; //Storage for n(t)
	ttc.get_counts(tokens_per_topic);
	fttc << "[";
	fphi << "[";
	arma::mat phi = zeros<mat>(num_words, num_topics);
	for (int w=0; w<num_words; ++w)
	{
		topicCounts current_topic_counts(num_topics);
		ttc.get_counts(w, &current_topic_counts);

		for (int j=0; j<current_topic_counts.length; ++j)
		{
			topic_t top = current_topic_counts.items[j].choose.top;
			cnt_t cnt = current_topic_counts.items[j].choose.cnt;

			phi_wk[w][top] += cnt;
		}

		fttc << "[";
		for (int k=0; k<num_topics; ++k)
		{
			//phi_wk[w][k] = (phi_wk[w][k] + beta) / (tokens_per_topic[k] + beta * num_words);

			fttc << phi_wk[w][k];
			if (k < num_topics-1)
				fttc << ", ";
		}
		fttc << "]" << '\n';

		if (w<num_words-1)
			fttc << ", ";

		fphi << "[";
		for (int k=0; k<num_topics; ++k)
		{
			phi_wk[w][k] = (phi_wk[w][k] + beta) / (tokens_per_topic[k] + beta * num_words);
			phi(w, k) = phi_wk[w][k];

			fphi << phi_wk[w][k];
			if (k < num_topics-1)
				fphi << ", ";
		}
		fphi << "]" << '\n';

		if (w<num_words-1)
			fphi << ", ";
	}
	fttc << "]" << endl;
	fphi << "]" << endl;
	fttc.close();
	fphi.close();

	// Read corpus & topic assignment
	double *theta = new double[num_topics];
	double *eta = new double[num_topics];

	ofstream ftheta((topic_file_prefix + ".json").c_str());
	ftheta << "{";
	unigram_document wdoc, tdoc;
	int current_doc = 0;
	for (int current_topic_file=0; current_topic_file<num; ++current_topic_file)
	{
		ostringstream sout;
		sout << "." << current_topic_file;

		DocumentReader *wrdr = new DocumentReader(document_file_prefix + sout.str());
		DocumentReader *trdr = new DocumentReader(topic_file_prefix + sout.str());

		while (wrdr->read(&wdoc) != -1)
		{
			if (current_doc != 0)
			{
				ftheta << ",";
			}
			current_doc ++;

			ftheta << "\"" << wdoc.docid() << "\" : [";
			double doc_likelihood = 0;

			trdr->read(&tdoc);

			int num_words_in_document = wdoc.body_size();

			for (int k=0; k<num_topics; ++k)
			{
				ftheta << tdoc.eta(k);
				if (k<num_topics-1)
					ftheta << ", ";
			}
			ftheta << "]\n";

		}
	}
	ftheta << "}";
	ftheta.close();

	arma::mat mu;
	cerr << "Mu file: " << mu_file << endl;
	mu.load(mu_file);

	arma::mat cov;
	cov.load(cov_file);

	arma::mat cosine_sim(num_topics, num_topics);
	for (int i=0; i<num_topics; ++i)
		for (int j=0; j<num_topics; ++j)
			cosine_sim(i, j) = dot(phi.col(i), phi.col(j)) / norm(phi.col(i), 2) / norm(phi.col(j), 2);

	mat corr(num_topics, num_topics);
	for (int x=0; x<num_topics; ++x)
	{
		for (int y=0; y<num_topics; ++y)
		{
			corr(x, y) = cov(x, y) / sqrt( cov(x, x) * cov(y, y) );
		}
	}

	dumpJson(mu_file + ".json", mu);
	dumpJson(cov_file + ".json", cov);
	dumpJson(cov_file + ".corr.json", corr);
	dumpJson(cov_file + ".cosine.json", cosine_sim);
}


#if defined(WITH_QPBO) || defined(WITH_BLOSSOM5) && defined(WITH_PLANARITY)
#include <boost/python.hpp>
#include <string>
#include "inf_def_visitor.hxx"

#include <opengm/inference/cgc.hxx>
#include <param/cgc_param.hxx>

template<class GM,class ACC>
void export_cgc(){
   using namespace boost::python;
   import_array();
   append_subnamespace("solver");

   // setup
   InfSetup setup;
   setup.cite       = "Thorsten Beier";
   setup.algType    = "multicut";

   // export parameter
   typedef opengm::CGC<GM, ACC>  PyInf;
   exportInfParam<PyInf>("_Cgc");
   // export inference
   class_< PyInf>("_Cgc",init<const GM & >())
   .def(InfSuite<PyInf>(std::string("Cgc"),setup))
   ;
}

template void export_cgc<opengm::python::GmAdder,opengm::Minimizer>();

#endif


#define _CRT_SECURE_NO_WARNINGS

#if defined(_WIN32) | defined(__WIN32__) | defined(__WIN32) | defined(_WIN64) | defined(__WIN64)
//#include <windows.h>
//#include "win_network_fcns.h"
#endif

//#include <winsock2.h>
//#include <iphlpapi.h>
//
//#pragma comment(lib, "IPHLPAPI.lib")    // Link with Iphlpapi.lib

// C/C++ includes
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <chrono>
#include <string>
#include <vector>
#include <algorithm>
#include <type_traits>

// dlib includes
#include "dlib/rand.h"
#include "dlib/matrix.h"
#include "dlib/pixel.h"
#include "dlib/image_io.h"
//#include "dlib/external/libpng/png.h"
//#include "dlib/external/libjpeg/jpeglib.h"
//#include "dlib/external/zlib/zlib.h"
#include "dlib/image_transforms.h"
#include <dlib/opencv.h>

// #include "dlib/xml_parser.h"
// #include "dlib/string.h"

// OpenCV includes
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>

// custom includes
//#include "mmaplib.h"
#include "pg.h"
//#include "mmap.h"
#include "get_current_time.h"
#include "get_platform.h"
#include "num2string.h"
#include "file_parser.h"
#include "read_binary_image.h"
#include "make_dir.h"
#include "ssim.h"
#include "dlib_matrix_threshold.h"
#include "gorgon_capture.h"
#include "modulo.h"

//#include "pso.h"
//#include "ycrcb_pixel.h"
//#include "dfd_array_cropper.h"
#include "rot_90.h"
//#include "dlib_srelu.h"
//#include "dlib_elu.h"
#include "center_cropper.h"
#include "dfd_cropper_rw.h"
#include "copy_dlib_net.h"
#include "dlib_set_learning_rates.h"

// Network includes
#include "dfd_net_v14.h"
//#include "dfd_net_v14_pso_01.h"
//#include "dfd_net_rw_v19.h"
//#include "load_dfd_rw_data.h"
//#include "load_dfd_data.h"

//#include "cyclic_analysis.h"

using namespace std;

// -------------------------------GLOBALS--------------------------------------

extern const uint32_t img_depth;
extern const uint32_t array_depth = 3;
extern const uint32_t secondary;

//extern const std::vector<std::pair<uint64_t, uint64_t>> crop_sizes;
std::string platform;
std::vector<std::array<dlib::matrix<uint16_t>, img_depth>> tr, te, trn_crop, te_crop;
//std::vector<dlib::matrix<uint16_t>> gt_train, gt_test, gt_crop, gt_te_crop;

//std::string version;
std::string net_name = "dfd_net_";
std::string net_sync_name = "dfd_sync_";
std::string logfileName = "dfd_net_";
std::string gorgon_savefile = "gorgon_dfd_";

dlib::rand rnd(time(NULL));

using mnist_net_type = dlib::loss_multiclass_log<
    dlib::fc<10,
    dlib::prelu<dlib::fc<84,
    dlib::prelu<dlib::fc<120,
    dlib::max_pool<2, 2, 2, 2, dlib::prelu<dlib::con<16, 5, 5, 1, 1,
    dlib::max_pool<2, 2, 2, 2, dlib::prelu<dlib::con<6, 5, 5, 1, 1,
    dlib::input<dlib::matrix<unsigned char>>
    >>>>>>>>>>>>;

//using mnist_net_type = dlib::loss_multiclass_log<
//    dlib::fc<10,
//    dlib::htan<dlib::fc<84,
//    dlib::sig<dlib::con<120, 5, 5, 1, 1,
//    dlib::sig<dlib::max_pool<2, 2, 2, 2, dlib::con<16, 5, 5, 1, 1,
//    dlib::sig<dlib::max_pool<2, 2, 2, 2, dlib::con<6, 5, 5, 1, 1,
//    dlib::input<dlib::matrix<unsigned char>>
//    >>>>>>>>>>>>;

using test_net_type = dlib::loss_multiclass_log_per_pixel <
    cbp3_blk<256,
    dlib::affine<
    dlib::input<std::array<dlib::matrix<uint16_t>, img_depth>>
    >>>;

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

template <typename net_type>
dlib::matrix<double, 1, 3> eval_mnist_performance(net_type &net, std::vector<dlib::matrix<unsigned char>> input_images, std::vector<unsigned long> input_labels)
{
    std::vector<unsigned long> predicted_labels = net(input_images);
    int num_right = 0;
    int num_wrong = 0;
    // And then let's see if it classified them correctly.
    for (size_t i = 0; i < input_images.size(); ++i)
    {
        if (predicted_labels[i] == input_labels[i])
            ++num_right;
        else
            ++num_wrong;

    }
    // std::cout << "training num_right: " << num_right << std::endl;
    // std::cout << "training num_wrong: " << num_wrong << std::endl;
    // std::cout << "training accuracy:  " << num_right/(double)(num_right+num_wrong) << std::endl;

    dlib::matrix<double, 1, 3> results;
    results = (double)num_right, (double)num_wrong, (double)num_right / (double)(num_right + num_wrong);

    return results;

}   // end of eval_net_performance

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

template <typename T>
const dlib::matrix<T> or(
    const dlib::matrix<T>& m1,
    const dlib::matrix<T>& m2
    )
{

    dlib::matrix<T> result = dlib::zeros_matrix<T>(m1.nr(), m1.nc());
    for (uint64_t r = 0; r < m1.nr(); ++r)
    {
        for (uint64_t c = 0; c < m1.nc(); ++c)
        {
            result(r, c) = (m1(r, c) > 0) | (m2(r, c) > 0);
        }
    }
        return result;
}

// ----------------------------------------------------------------------------------------
template <typename T>
const dlib::matrix<T> and(
    const dlib::matrix<T>& m1,
    const dlib::matrix<T>& m2
    )
{

    dlib::matrix<T> result = dlib::zeros_matrix<T>(m1.nr(), m1.nc());
    for (uint64_t r = 0; r < m1.nr(); ++r)
    {
        for (uint64_t c = 0; c < m1.nc(); ++c)
        {
            result(r, c) = (m1(r, c) > 0) & (m2(r, c) > 0);
        }
    }
    return result;
}
*/

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

template <typename img_type1>
void check_matrix(img_type1 img)
{
    if (dlib::is_matrix<img_type1>::value == true)
        std::cout << "matrix" << std::endl;
}

// ----------------------------------------------------------------------------------------
/*
template <typename array_type>
void calc_linkdist(array_type in, dlib::matrix<uint16_t> &d)
{
    uint64_t idx, jdx;

    // assume that the vectors are in the rows
    // i.e. each row in the matrix represents a particular input

    dlib::matrix<uint16_t> one = dlib::ones_matrix<uint16_t>(in.nr(), in.nr());
    dlib::matrix<uint16_t> links = dlib::zeros_matrix<uint16_t>(in.nr(), in.nr());
    dlib::matrix<uint16_t> found = dlib::identity_matrix<uint16_t>(in.nr());
    dlib::matrix<uint16_t> next = dlib::zeros_matrix<uint16_t>(in.nr(), in.nr());
    dlib::matrix<uint16_t> newfound = dlib::zeros_matrix<uint16_t>(in.nr(), in.nr());

    d.set_size(in.nr(), in.nr());
    dlib::set_all_elements(d, in.nr());

    d = d - in.nr()*found;

    double s = 0.0;

    // get the links
    for (idx = 0; idx < in.nr(); ++idx)
    {
        for (jdx = 0; jdx < in.nr(); ++jdx)
        {
            if (idx != jdx)
            {
                s = std::sqrt((double)(dlib::sum(dlib::squared(dlib::rowm(in, idx) - dlib::rowm(in, jdx)))));

                //sum(idx, jdx) = s;

                if (s <= 1)
                    links(idx, jdx) = 1;
            }
        }
    }

    // cycle through the inputs by row
    for (idx = 0; idx < in.nr(); ++idx)
    {
        //nextfound = (found*links) | found;
        next = dlib::matrix_cast<uint16_t>((found*links));
        next = or(next, found);

        //newfound = nextfound & ~found;
        newfound = (one - found);
        newfound = and(next, newfound);

        for (uint64_t r = 0; r < in.nr(); ++r)
        {
            for (uint64_t c = 0; c < in.nr(); ++c)
            {
                if (newfound(r, c) == 1)
                    d(r, c) = idx + 1;
            }
        }

        found = next;

    }

}

*/

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

//template<size_t from, size_t to, typename net_type1, typename net_type2>
////typename std::enable_if<from >= to>::type
//void copy_net(net_type1 &in, net_type2 &out)
//{
//    dlib::layer<to>(out).layer_details() = dlib::layer<from>(in).layer_details();
//    //copy_net<from + 1, to>(in, out);
//}

template<size_t from, size_t to, typename net_type1, typename net_type2>
void copy_layer(net_type1 &from_net, net_type2 &to_net)
{
    dlib::layer<to>(to_net).layer_details() = dlib::layer<from>(from_net).layer_details();
}

/*
namespace dlib
{

    namespace cpnet
    {
        template <size_t begin, size_t end, size_t begin2>
        struct copy_layer_loop
        {
            // this version of the recursion templte checks to see if the layer is part of the "add_layer" class
            // layers like tag layers are not
            template <typename F, typename T>
            static typename std::enable_if<!is_add_layer<F>::value>::type invoke_functor(F&& from, T&& to)
            {
                // intentionally left empty
                std::cout << "skipping: layer<" << begin << ">" << std::endl;
            }

            // this version will operate on all layers in the "add_layer" class
            // the layer details will be copied from one layer to another
            template <typename F, typename T>
            static typename std::enable_if<is_add_layer<F>::value>::type invoke_functor(F&& from, T&& to)
            {
                std::cout << "copying: layer<" << begin << ">" << std::endl;
                to.layer_details() = from.layer_details();
            }

            // this is the recursive call
            template <typename net_type1, typename net_type2>
            static void visit(
                net_type1& from_net,
                net_type2& to_net
            )
            {
                invoke_functor(layer<begin>(from_net), layer<begin2>(to_net));

                copy_layer_loop<begin + 1, end, begin2 + 1>::visit(from_net, to_net);
            }
        };

        template <size_t end, size_t begin2>
        struct copy_layer_loop<end, end, begin2>
        {

            template <typename F, typename T>
            static typename std::enable_if<!is_add_layer<F>::value>::type invoke_functor(F&& from, T&& to)
            {
                // intentionally left empty
                std::cout << "skipping: layer<" << end << ">" << std::endl;
            }

            template <typename F, typename T>
            static typename std::enable_if<is_add_layer<F>::value>::type invoke_functor(F&& from, T&& to)
            {
                std::cout << "copying: layer<" << end << ">" << std::endl;
                to.layer_details() = from.layer_details();
            }

            // Base case of recursion, i.e. the last iteration
            template <typename net_type1, typename net_type2>
            static void visit(
                net_type1& from_net,
                net_type2& to_net
            )
            {
                invoke_functor(layer<end>(from_net), layer<begin2>(to_net));
                std::cout << "copying complete!" << std::endl;
            }
        };

    }   // end of cpnet namespace

    // this most likely will be the copy net function in the end
    template <size_t b1, size_t e1, size_t b2, typename net_type1, typename net_type2>
    void copy_net(net_type1 &from_net, net_type2 &to_net)
    {
        // this does a check of the input ranges to determine if they are out of range for the input network
        static_assert(b1 <= e1, "Invalid range");
        static_assert(e1 <= net_type1::num_layers, "Invalid range");

        // begin the layer copying process
        cpnet::copy_layer_loop<b1, e1, b2>::visit(from_net, to_net);
    }

}   // end of dlib namespace

*/

//// ----------------------------------------------------------------------------------------
//
//namespace dlib
//{
//    namespace impl
//    {
//        // This is for the cases where begin != end
//        template <size_t begin, size_t end>
//        struct vllr_loop
//        {
//            // This is where the work gets done.  The odd decltype is used to check if layer contains
//            // the set_learning_rate_multiplier() fucntion
//            template<typename net_type>
//            static decltype(std::declval<net_type>().layer_details().set_learning_rate_multiplier(0))
//            set_value(net_type &net, double r1, double r2)
//            {
//                net.layer_details().set_learning_rate_multiplier(r1);
//                net.layer_details().set_bias_learning_rate_multiplier(r2);
//            }
//
//            static void set_value(...)
//            {
//                // Intentionally left blank.  This handles the layers that don't have a
//                // set_learning_rate_multiplier() function
//            }
//
//            // This is the fuction to call within the struct
//            template <typename net_type>
//            static void visit(net_type &net, double r1, double r2)
//            {
//                // set the values for the current layer
//                set_value(layer<begin>(net), r1, r2);
//                // move on and increment the begining layer
//                vllr_loop<begin + 1, end>::visit(net, r1, r2);
//            }
//        };
//
//        // This is for the cases where begin == end, i.e. the base case for the recursion
//        template <size_t end>
//        struct vllr_loop<end,end>
//        {
//
//            // This is where the work gets done.  The odd decltype is used to check if layer contains
//            // the set_learning_rate_multiplier() fucntion
//            template<typename net_type>
//            static decltype(std::declval<net_type>().layer_details().set_learning_rate_multiplier(0))
//            set_value(net_type &net, double r1, double r2)
//            {
//                net.layer_details().set_learning_rate_multiplier(r1);
//                net.layer_details().set_bias_learning_rate_multiplier(r2);
//            }
//
//            static void set_value(...)
//            {
//                // Intentionally left blank.  This handles the layers that don't have a
//                // set_learning_rate_multiplier() function
//            }
//
//            // This is the fuction to call within the struct
//            template <typename net_type>
//            static void visit(net_type &net, double r1, double r2)
//            {
//                // set the values for the current layer
//                set_value(layer<end>(net), r1, r2);
//            }
//        };
//
//    }   // end of impl namespace
//
//    // This is the main function to call when you want to set the following:
//    //   - set_learning_rate_multiplier
//    //   - set_bias_learning_rate_multiplier
//    // Call it like this: dlib::set_learning_rate<0, 3>(net, r1, r2);
//    //   - where net is your net and r1,r2 are the learning rate multipliers
//    template<size_t begin, size_t end, typename net_type>
//    void set_learning_rate(net_type &net, double r1, double r2)
//    {
//        // this does a check of the input ranges to determine if they are out of range for the input network
//        static_assert(begin <= end, "Invalid range");
//        static_assert(end <= net_type::num_layers, "Invalid range");
//
//        // begin the process of updating the learning rates
//        impl::vllr_loop<begin, end>::visit(net, r1, r2);
//    }
//
//}   // end of dlib namespace
//
//// ----------------------------------------------------------------------------------------

//template<typename net_type>
//inline decltype(std::declval<net_type>().layer_details().set_learning_rate_multiplier(0))
//set_learning_rate_impl(net_type &net, double rate)
//{
//    net.layer_details().set_learning_rate_multiplier(rate);
//    net.layer_details().set_bias_learning_rate_multiplier(rate);
//}
//
//inline void set_learning_rate_impl(...)
//{
//}
//
//template<int from, int to, typename net_type>
//typename std::enable_if<from == to>::type
//set_learning_rate(net_type& net, double rate)
//{
//    set_learning_rate_impl(dlib::layer<from>(net), rate);
//}
//
//template<int from, int to, typename net_type>
//typename std::enable_if<from != to>::type
//set_learning_rate(net_type &net, double rate)
//{
//    set_learning_rate_impl(dlib::layer<from>(net), rate);
//    set_learning_rate<from + 1, to>(net, rate);
//}

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

template <typename image_type1>
void make_random_cropping_rect(const image_type1& img, dlib::rectangle &rect_im, dlib::chip_dims dims= dlib::chip_dims(32,32))
{
    uint64_t x = 0, y = 0;

    rect_im = dlib::resize_rect(rect_im, dims.cols, dims.rows);
//    rect_gt = dlib::resize_rect(rect_gt, (long)(dims.cols / (double)scale_x), (long)(dims.rows / (double)scale_y));

    if ((unsigned long)img.nc() <= rect_im.width())
        x = 0;
    else
        x = (uint64_t)(rnd.get_integer(img.nc() - rect_im.width()));

    if ((unsigned long)img.nr() <= rect_im.height())
        y = 0;
    else
        y = (uint64_t)(rnd.get_integer(img.nr() - rect_im.height()));

    // randomly shift the box around
    dlib::point tr_off(x, y);
    rect_im = dlib::move_rect(rect_im, tr_off);

    //dlib::point gt_off(x, y);
    //rect_gt = dlib::move_rect(rect_gt, gt_off);

}	// end of make_random_cropping_rect

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

template <typename img_type, typename T>
void create_mask(img_type src, img_type &mask, T min_value, T max_value)
{
    uint64_t nr = src.nr();
    uint64_t nc = src.nc();

    mask.set_size(nr, nc);

    for (uint64_t r = 0; r < nr; ++r)
    {
        for (uint64_t c = 0; c < nc; ++c)
        {
            if ((src(r, c) >= min_value) && (src(r,c) <= max_value))
                mask(r, c) = 1;
            else
                mask(r, c) = 0;
        }
    }

}   // end of create_mask

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

template <typename img_type, typename mask_type, typename T>
void apply_mask(img_type src, img_type &dst, mask_type mask, T value)
{
    uint64_t nr, nc;

    //if (dlib::is_matrix<img_type>::value == true)
    //{
    //    nr = src.nr();
    //    nc = src.nc();

    //    dst.set_size(nr, nc);

    //    for (uint64_t r = 0; r < nr; ++r)
    //    {
    //        for (uint64_t c = 0; c < nc; ++c)
    //        {
    //            if (mask(r, c) == 0)
    //                dst(r, c) = value;
    //            else
    //                dst(r, c) = src(r, c);
    //        }
    //    }

    //}
    //else
    //{
        nr = src[0].nr();
        nc = src[0].nc();

        for (uint64_t idx = 0; idx < src.size(); ++idx)
        {
            dst[idx].set_size(nr, nc);

            for (uint64_t r = 0; r < nr; ++r)
            {
                for (uint64_t c = 0; c < nc; ++c)
                {
                    if (mask(r, c) == 0)
                        dst[idx](r, c) = value;
                    else
                        dst[idx](r, c) = src[idx](r, c);
                }
            }
        }

    //}

}   // end of apply_mask

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

// This block of statements defines the resnet-34 network

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = dlib::add_prev1<block<N, BN, 1, dlib::tag1<SUBNET>>>;

template <template <int, template<typename>class, int, typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = dlib::add_prev2<dlib::avg_pool<2, 2, 2, 2, dlib::skip1<dlib::tag2<block<N, BN, 2, dlib::tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET>
using block = BN<dlib::con<N, 3, 3, 1, 1, dlib::relu<BN<dlib::con<N, 3, 3, stride, stride, SUBNET>>>>>;

template <int N, typename SUBNET> using ares = dlib::relu<residual<block, N, dlib::affine, SUBNET>>;
template <int N, typename SUBNET> using ares_down = dlib::relu<residual_down<block, N, dlib::affine, SUBNET>>;

template <typename SUBNET> using level1 = ares<512, ares<512, ares_down<512, SUBNET>>>;
template <typename SUBNET> using level2 = ares<256, ares<256, ares<256, ares<256, ares<256, ares_down<256, SUBNET>>>>>>;
template <typename SUBNET> using level3 = ares<128, ares<128, ares<128, ares_down<128, SUBNET>>>>;
template <typename SUBNET> using level4 = ares<64, ares<64, ares<64, SUBNET>>>;

using anet_type = dlib::loss_multiclass_log< dlib::fc<1000, dlib::avg_pool_everything<
    level1<
    level2<
    level3<
    level4<
    dlib::max_pool<3, 3, 2, 2, dlib::relu<dlib::affine<dlib::con<64, 7, 7, 2, 2,
    dlib::input_rgb_image_sized<227>
    >>>>>>>>>>>;

using anet_type2 = dlib::loss_mmod<dlib::con<1,9,9,1,1,
    level1<
    level2<
    level3<
    level4<
    dlib::max_pool<3, 3, 2, 2, dlib::relu<dlib::affine<dlib::con<64, 7, 7, 2, 2,
    //dlib::input_rgb_image_pyramid<dlib::pyramid_down<6>>
    dlib::input<std::array<dlib::matrix<uint8_t>, array_depth>>
    >>>>>>>>>>;

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

int main(int argc, char** argv)
{
    std::string sdate, stime;

    uint64_t idx=0, jdx=0;

    typedef std::chrono::duration<double> d_sec;
    auto start_time = chrono::system_clock::now();
    auto stop_time = chrono::system_clock::now();
    unsigned long training_duration = 1;  // number of hours to train
    auto elapsed_time = chrono::duration_cast<d_sec>(stop_time - start_time);

    std::vector<double> stop_criteria;
    uint64_t num_crops = 0;
    //std::vector<std::pair<uint64_t, uint64_t>> crop_sizes = { {1,1}, {38,148} };
    //std::vector<uint32_t> filter_num;
    //uint64_t max_one_step_count;
    std::vector<dlib::matrix<unsigned char>> training_images;
    std::vector<dlib::matrix<unsigned char>> testing_images;
    std::vector<unsigned long> training_labels;
    std::vector<unsigned long> testing_labels;

    std::vector<std::vector<std::string>> training_file;
    std::vector<std::vector<std::string>> test_file;

    std::string data_directory;
    std::string train_inputfile, test_inputfile;
    std::vector<std::pair<std::string, std::string>> tr_image_files, te_image_files;
    std::vector<dlib::matrix<uint16_t>> gt_train, gt_test;

    dlib::matrix<uint16_t> g_crop;
    dlib::rectangle rect_im, rect_gt;

    //std::array<dlib::matrix<uint16_t>, img_depth> tr_crop;

    std::ofstream DataLogStream;
    std::string platform;
    getPlatform(platform);
    std::cout << "Platform: " << platform << std::endl;

    if (platform.compare(0, 6, "Laptop") == 0)
    {
        std::cout << "Match!" << std::endl;
    }

    try
    {
        int bp = 0;

        #if defined(_WIN32) | defined(__WIN32__) | defined(__WIN32) | defined(_WIN64) | defined(__WIN64)

        #else
            std::string exe_path = get_linux_path();
            std::cout << "Path: " << exe_path << std::endl;
        #endif

        anet_type2 net_34_2;

        std::vector<std::string> labels;
        anet_type net_34;
        dlib::deserialize("../nets/resnet34_1000_imagenet_classifier.dnn") >> net_34 >> labels;

        std::cout << "net 34" << std::endl;
        std::cout << net_34 << std::endl;

        std::cout << "net 34 v2" << std::endl;
        std::cout << net_34_2 << std::endl;

        copy_net<138, 143, 137>(net_34, net_34_2);

        //copy_layer<138, 137>(net_34, net_34_2);
        //copy_layer<139, 138>(net_34, net_34_2);
        //copy_layer<140, 139>(net_34, net_34_2);
        //copy_layer<141, 140>(net_34, net_34_2);
        //copy_layer<142, 141>(net_34, net_34_2);
        //copy_layer<143, 142>(net_34, net_34_2);

        auto& tmp1 = dlib::layer<138>(net_34).layer_details().get_layer_params();
        auto& tmp2 = dlib::layer<137>(net_34_2).layer_details().get_layer_params();

        auto& tmp3 = dlib::layer<140>(net_34).layer_details().get_layer_params();
        auto& tmp4 = dlib::layer<139>(net_34_2).layer_details().get_layer_params();

        auto& tmp5 = dlib::layer<141>(net_34).layer_details().get_layer_params();
        auto& tmp6 = dlib::layer<140>(net_34_2).layer_details().get_layer_params();

        auto& tmp7 = dlib::layer<142>(net_34).layer_details();
        auto& tmp8 = dlib::layer<141>(net_34_2).layer_details();

        auto& tmp9 = dlib::layer<143>(net_34).layer_details().get_layer_params();
        auto& tmp10 = dlib::layer<142>(net_34_2).layer_details().get_layer_params();

        bp = 3;

        double r1 = 10.0, r2 = 4.0;
        dlib::set_learning_rate<130, 142>(net_34_2, r1, r2);

        std::cout << "net 34 v2" << std::endl;
        std::cout << net_34_2 << std::endl;

        bp = 4;

        //----------------------------------------------------------------
        data_directory = "D:/Projects/MNIST/data";

        // load the data in using the dlib built in function
        //dlib::load_mnist_dataset(data_directory, training_images, training_labels, testing_images, testing_labels);

        // get the location of the network
        std::string net_name;
        //net_name = "D:/IUPUI/PhD/Results/dfd_dnn/dnn_reduction/2704823.pbs01_0_nets/nets/dfd_net_v14v_61_U_32_HPC.dat";
        //net_name = "D:/IUPUI/PhD/Results/dfd_dnn/2649400.pbs01_0_nets/nets/dfd_net_v14f_61_U_32_HPC.dat";
        //net_name = "D:/IUPUI/PhD/Results/dfd_dnn/2651620.pbs01_0_nets/nets/dfd_net_v14i_61_U_32_HPC.dat";
        //net_name = "D:/IUPUI/PhD/Results/dfd_dnn/dnn_reduction/2646754.pbs01_0_nets/nets/dfd_net_v14e_61_U_32_HPC.dat";
        //net_name = "D:/Projects/MNIST/nets/mnist_net_L05_100.dat";

        //declare the network
        //dfd_net_type net;
        //mnist_net_type net;

        //// deserialize the network
        //dlib::deserialize(net_name) >> net;

        //std::cout << net << std::endl;

        bp = 1;

        //----------------------------------------------------------------
/*
        DataLogStream.open("random_cropper_selection.txt", ios::out | ios::app);

        train_inputfile = "D:/IUPUI/DfD/DfD_DNN/dfd_train_data_sm2.txt";

        // parse through the supplied training csv file
        parseCSVFile(train_inputfile, training_file);

        // the first line in this file is now the data directory
        data_directory = training_file[0][0];
        training_file.erase(training_file.begin());

        std::cout << "Loading training images..." << std::endl;

        start_time = chrono::system_clock::now();
        loadData(training_file, data_directory, tr, gt_train, tr_image_files);
        stop_time = chrono::system_clock::now();

        elapsed_time = chrono::duration_cast<d_sec>(stop_time - start_time);
        std::cout << "Loaded " << tr.size() << " training image sets in " << elapsed_time.count() / 60 << " minutes." << std::endl << std::endl;

        uint32_t crop_num = 32;
        uint64_t img_index = 0;

        for (uint32_t kdx = 0; kdx < 63500; ++kdx)
        {
            for (uint32_t mdx = 0; mdx < crop_num; ++mdx)
            {
                img_index = rnd.get_integer(tr.size());
                make_random_cropping_rect(tr[img_index][0], rect_im);
                DataLogStream << img_index << "," << tr[img_index][0].nr() << "," << tr[img_index][0].nc() << "," << rect_im.left() << "," << rect_im.top() << std::endl;
            }
        }

        DataLogStream.close();

        bp = 3;
        return 0;
        */
        //-------------------------------------------------------------------

        // setup the input image info
        //data_directory = "D:/IUPUI/Test_Data/Middlebury_Images_Third/Art/";
        //std::string f_img = "Illum2/Exp1/view1.png";
        //std::string d_img = "Illum2/Exp1/view1_lin_0.32_2.88.png";
        //std::string dm_img = "disp1.png";

        //// load the images
        //std::array<dlib::matrix<uint16_t>, img_depth> t, tm;
        //dlib::matrix<dlib::rgb_pixel> f, f_tmp, d, d_tmp;
        //dlib::matrix<uint16_t> dm_tmp, dm, mask;

        //dlib::load_image(f_tmp, (data_directory+f_img));
        //dlib::load_image(d_tmp, (data_directory+d_img));
        //dlib::load_image(dm_tmp, (data_directory + dm_img));

        ////split_channels(f_tmp, t);

        //// crop the images to the right network size
        //// get image size
        //uint64_t rows = 368;// crop_sizes[1].first;
        //uint64_t cols = 400;// crop_sizes[1].second;

        //f.set_size(rows, cols);
        //d.set_size(rows, cols);
        //dm.set_size(rows, cols);

        //// crop the image to fit into the net
        //dlib::set_subm(f, 0, 0, rows, cols) = dlib::subm(f_tmp, 0, 0, rows, cols);
        //dlib::set_subm(d, 0, 0, rows, cols) = dlib::subm(d_tmp, 0, 0, rows, cols);
        //dlib::set_subm(dm, 0, 0, rows, cols) = dlib::subm(dm_tmp, 0, 0, rows, cols);

        //// split the channels and combine
        //split_channels(f, 0, t);
        //split_channels(d, 3, t);

        //// test the mask creation
        //create_mask(dm, mask, 140, 150);

        //// test the mask overlay feature
        //apply_mask(t, tm, mask, 0);

        bp = 2;

        //dlib::matrix < dlib::rgb_pixel> t3;
        //merge_channels(t, 0, t3);

        // start looking at how to view the inards
        //const auto& test = dlib::layer<50>(dfd_net).get_output();
        //const float *t2 = test.host();
        //uint64_t n = test.num_samples();
        //uint64_t k = test.k();
        //uint64_t nr = test.nr();
        //uint64_t nc = test.nc();
        //uint64_t img_size = nr * nc;
        //uint64_t offset = 0;

        //dlib::matrix<float> o_img(nr, nc);
        //uint64_t index = 0;

        //for (uint64_t r = 0; r < nr; ++r)
        //{
        //    for (uint64_t c = 0; c < nc; ++c)
        //    {
        //        o_img(r, c) = *(t2 + (offset*img_size) + index);
        //        ++index;
        //    }
        //}

        std::string save_location;
        std::string save_name;

//-----------------------------------------------------------------
// DFD Net
/*

        std::cout << "Loading test images..." << std::endl;

        test_inputfile = "D:/IUPUI/DfD/DfD_DNN/dfd_test_data_sm2.txt";
        parseCSVFile(test_inputfile, test_file);
        data_directory = test_file[0][0];
        test_file.erase(test_file.begin());

        start_time = chrono::system_clock::now();
        loadData(test_file, data_directory, te, gt_test, te_image_files);
        stop_time = chrono::system_clock::now();

        elapsed_time = chrono::duration_cast<d_sec>(stop_time - start_time);
        std::cout << "Loaded " << te.size() << " test image sets in " << elapsed_time.count() / 60.0 << " minutes." << std::endl << std::endl;

        std::string net_version = "v14a";

        save_location = "D:/IUPUI/PhD/Results/dfd_dnn/dnn_reduction/" + net_version + "/";
        save_name = "net_" + net_version + "_";
        net_name = "D:/IUPUI/PhD/Results/dfd_dnn/dnn_reduction/" + net_version + "/nets/dfd_net_v14a_61_U_32_HPC.dat";

        dfd_net_type net;

        // deserialize the network
        dlib::deserialize(net_name) >> net;

        std::cout << net << std::endl;

        std::vector<std::string> data_name = { "art","books","reindeer" };
        std::vector<uint32_t> ti = { 4, 22, 40 };

        for (idx = 0; idx < ti.size(); ++idx)
        {
            std::cout << "Running: " << data_name[idx] << std::endl;
            make_dir(save_location, data_name[idx]);

            dlib::matrix<uint16_t> map = net(te[ti[idx]]);
/*
            gorgon_capture<50> gc_01(net);
            gc_01.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L50"));
            gc_01.save_net_output(net);
            gc_01.close_stream();

            gorgon_capture<46> gc_02(net);
            gc_02.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L46"));
            gc_02.save_net_output(net);
            gc_02.close_stream();

            gorgon_capture<44> gc_03(net);
            gc_03.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L44"));
            gc_03.save_net_output(net);
            gc_03.close_stream();

            gorgon_capture<42> gc_04(net);
            gc_04.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L42"));
            gc_04.save_net_output(net);
            gc_04.close_stream();

            gorgon_capture<38> gc_05(net);
            gc_05.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L38"));
            gc_05.save_net_output(net);
            gc_05.close_stream();

            gorgon_capture<36> gc_06(net);
            gc_06.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L36"));
            gc_06.save_net_output(net);
            gc_06.close_stream();

            gorgon_capture<34> gc_07(net);
            gc_07.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L34"));
            gc_07.save_net_output(net);
            gc_07.close_stream();

            gorgon_capture<30> gc_08(net);
            gc_08.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L30"));
            gc_08.save_net_output(net);
            gc_08.close_stream();

            gorgon_capture<28> gc_09(net);
            gc_09.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L28"));
            gc_09.save_net_output(net);
            gc_09.close_stream();

            gorgon_capture<27> gc_10(net);
            gc_10.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L27"));
            gc_10.save_net_output(net);
            gc_10.close_stream();

            //gorgon_capture<25> gc_10a(net);
            //gc_10a.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L25"));
            //gc_10a.save_net_output(net);
            //gc_10a.close_stream();

            gorgon_capture<22> gc_11(net);
            gc_11.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L22"));
            gc_11.save_net_output(net);
            gc_11.close_stream();

            gorgon_capture<18> gc_12(net);
            gc_12.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L18"));
            gc_12.save_net_output(net);
            gc_12.close_stream();

            gorgon_capture<16> gc_13(net);
            gc_13.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L16"));
            gc_13.save_net_output(net);
            gc_13.close_stream();

            gorgon_capture<15> gc_14(net);
            gc_14.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L15"));
            gc_14.save_net_output(net);
            gc_14.close_stream();

            //gorgon_capture<13> gc_14a(net);
            //gc_14a.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L13"));
            //gc_14a.save_net_output(net);
            //gc_14a.close_stream();

            gorgon_capture<10> gc_15(net);
            gc_15.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L10"));
            gc_15.save_net_output(net);
            gc_15.close_stream();

            gorgon_capture<6> gc_16(net);
            gc_16.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L06"));
            gc_16.save_net_output(net);
            gc_16.close_stream();

            gorgon_capture<4> gc_17(net);
            gc_17.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L04"));
            gc_17.save_net_output(net);
            gc_17.close_stream();

            gorgon_capture<2> gc_18(net);
            gc_18.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L02"));
            gc_18.save_net_output(net);
            gc_18.close_stream();
*/
/*
            gorgon_capture<1> gc_19(net);
            gc_19.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L01"));
            gc_19.save_net_output(net);
            gc_19.close_stream();

            //gorgon_capture<0> gc_20(net);
            //gc_20.init((save_location + data_name[idx] + "/" + save_name + data_name[idx] + "_L00"));
            //gc_20.save_net_output(net);
            //gc_20.close_stream();

        }

*/
//-----------------------------------------------------------------
// MNIST Net
/*
        //net_name = "D:/Projects/MNIST/nets/mnist_net_05_16_120_84.dat";
        //net_name = "D:/Projects/MNIST/nets/mnist_net_05_15_120_84.dat";
        //net_name = "D:/Projects/MNIST/nets/mnist_net_04_15_120_84.dat";
        //net_name = "D:/Projects/MNIST/nets/mnist_net_06_16_51_55.dat";
        net_name = "D:/Projects/MNIST/nets/mnist_net_04_13_68_55.dat";
        //net_name = "D:/Projects/MNIST/nets/mnist_net_v2_06_16_120_61.dat";
        mnist_net_type net;

        // deserialize the network
        dlib::deserialize(net_name) >> net;

        std::cout << net << std::endl;
        dlib::matrix<double, 1, 3> training_results = eval_mnist_performance(net, training_images, training_labels);
        //auto results = net(training_images);

        double avg_train_time = 0.0;
        for (uint32_t idx = 0; idx < 30; ++idx)
        {
            start_time = chrono::system_clock::now();
            //training_results = eval_net_performance(test_net, training_images, training_labels);
            net(training_images);
            stop_time = chrono::system_clock::now();
            elapsed_time = chrono::duration_cast<d_sec>(stop_time - start_time);
            avg_train_time += elapsed_time.count();
            std::cout << ".";
        }
        avg_train_time = avg_train_time / 30.0;
        std::cout << endl;

        std::cout << "------------------------------------------------------------------" << std::endl;
        std::cout << "Average run time:   " << avg_train_time << std::endl;
        std::cout << "Training num_right: " << training_results(0, 0) << std::endl;
        std::cout << "Training num_wrong: " << training_results(0, 1) << std::endl;
        std::cout << "Training accuracy:  " << training_results(0, 2) << std::endl;
        std::cout << "------------------------------------------------------------------" << std::endl;

        save_location = "D:/Projects/MNIST/results/net_04_15_072_84/";
        save_name = "net_out_";
        std::vector<uint32_t> ti = { 0,1,2,3,4,7,8,11,18,61 };//   7, 2, 1, 0, 4, 9, 5, 6, 3, 8

        for (idx = 0; idx < ti.size(); ++idx)
        {
            std::cout << "Running: " << testing_labels[ti[idx]] << std::endl;

            // run the image through the network
            unsigned long predicted_labels = net(testing_images[ti[idx]]);
            std::string number = num2str(testing_labels[ti[idx]], "%02u/");
            make_dir(save_location, number);

            gorgon_capture<11> gc_1(net);
            gc_1.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L11"));
            gc_1.save_net_output(net);
            gc_1.close_stream();

            //v2
            //gorgon_capture<10> gc_1a(net);
            //gc_1a.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L10"));
            //gc_1a.save_net_output(net);
            //gc_1a.close_stream();

            //v1
            gorgon_capture<9> gc_1a(net);
            gc_1a.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L09"));
            gc_1a.save_net_output(net);
            gc_1a.close_stream();

            gorgon_capture<8> gc_2(net);
            gc_2.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L08"));
            gc_2.save_net_output(net);
            gc_2.close_stream();

            //v2
            //gorgon_capture<7> gc_2a(net);
            //gc_2a.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L07"));
            //gc_2a.save_net_output(net);
            //gc_2a.close_stream();

            gorgon_capture<6> gc_3(net);
            gc_3.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L06"));
            gc_3.save_net_output(net);
            gc_3.close_stream();

            //v1
            gorgon_capture<5> gc_3b(net);
            gc_3b.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L05"));
            gc_3b.save_net_output(net);
            gc_3b.close_stream();

            gorgon_capture<4> gc_3a(net);
            gc_3a.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L04"));
            gc_3a.save_net_output(net);
            gc_3a.close_stream();

            //v1
            gorgon_capture<3> gc_4(net);
            gc_4.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L03"));
            gc_4.save_net_output(net);
            gc_4.close_stream();

            gorgon_capture<2> gc_4a(net);
            gc_4a.init((save_location + number + save_name + num2str(testing_labels[ti[idx]], "%02u_") + "L02"));
            gc_4a.save_net_output(net);
            gc_4a.close_stream();

        }
*/
        bp = 3;
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
//        std::cout << "Press enter to close the program." << std::endl;
//        std::cin.ignore();

    }

    std::cout << "Press Enter to close" << std::endl;
    std::cin.ignore();
	return 0;

}	// end of main


#ifndef PARMCB_DETAIL_CYCLES_HPP_
#define PARMCB_DETAIL_CYCLES_HPP_

//    Copyright (C) Dimitrios Michail 2019 - 2021.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          https://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <map>
#include <parmcb/util.hpp>
#include <parmcb/sptrees.hpp>
#include <parmcb/detail/fvs.hpp>

#include <boost/graph/connected_components.hpp>

namespace boost {

    struct bad_t {
        typedef boost::vertex_property_tag kind;
    };

    struct tree_t {
        typedef boost::vertex_property_tag kind;
    };

    struct edge_t {
        typedef boost::vertex_property_tag kind;
    };

}

namespace parmcb {

    namespace detail {

        template<class Graph, class WeightMap>
        struct HortonCyclesBuilder {

            void operator()(const Graph &g, const WeightMap &weight_map,
                    std::vector<parmcb::SPTree<Graph, WeightMap>> &trees,
                    std::vector<CandidateCycle<Graph, WeightMap>> &cycles) {
                typedef typename boost::graph_traits<Graph>::vertex_iterator VertexIt;
                VertexIt vi, viend;
                for (boost::tie(vi, viend) = boost::vertices(g); vi != viend; ++vi) {
                    auto v = *vi;
                    trees.emplace_back(trees.size(), g, boost::get(boost::vertex_index, g), weight_map, v);
                }
                for (auto &tree : trees) {
                    std::vector<CandidateCycle<Graph, WeightMap>> tree_cycles = tree.create_candidate_cycles();
                    cycles.insert(cycles.end(), tree_cycles.begin(), tree_cycles.end());
                }
            }

        };

        template<class Graph, class WeightMap>
        struct FVSCyclesBuilder {

            void operator()(const Graph &g, const WeightMap &weight_map,
                    std::vector<parmcb::SPTree<Graph, WeightMap>> &trees,
                    std::vector<CandidateCycle<Graph, WeightMap>> &cycles) {
                typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;

                std::vector<Vertex> feedback_vertex_set;
                parmcb::greedy_fvs(g, std::back_inserter(feedback_vertex_set));
                for (auto v : feedback_vertex_set) {
                    trees.emplace_back(trees.size(), g, boost::get(boost::vertex_index, g), weight_map, v);
                }
                for (auto &tree : trees) {
                    std::vector<CandidateCycle<Graph, WeightMap>> tree_cycles = tree.create_candidate_cycles();
                    cycles.insert(cycles.end(), tree_cycles.begin(), tree_cycles.end());
                }
            }

        };

        template<class Graph, class WeightMap>
        struct ISOCyclesBuilder {

            void operator()(const Graph &g, const WeightMap &weight_map,
                    std::vector<parmcb::SPTree<Graph, WeightMap>> &trees,
                    std::vector<CandidateCycle<Graph, WeightMap>> &cycles) {
                typedef typename boost::property_map<Graph, boost::vertex_index_t>::type VertexIndexMapType;
                typedef typename boost::graph_traits<Graph>::vertex_iterator VertexIt;
                typedef typename boost::graph_traits<Graph>::edge_descriptor Edge;
                typedef typename boost::property_traits<WeightMap>::value_type WeightType;

                const VertexIndexMapType &index_map = boost::get(boost::vertex_index, g);

                /*
                 * Build all shortest path trees
                 */
                std::vector<std::size_t> trees_index_map(boost::num_vertices(g));
                VertexIt ui, uiend;
                std::size_t next_tree = 0;
                for (boost::tie(ui, uiend) = boost::vertices(g); ui != uiend; ++ui) {
                    auto u = *ui;
                    auto uindex = index_map[u];
                    trees.emplace_back(next_tree, g, boost::get(boost::vertex_index, g), weight_map, u);
                    trees_index_map[uindex] = next_tree;
                    next_tree++;
                }

                /*
                 * Build all of Horton's candidate cycles
                 */
                std::vector<CandidateCycle<Graph, WeightMap>> allcycles;
                for (const auto &tree : trees) {
                    std::vector<CandidateCycle<Graph, WeightMap>> tree_cycles = tree.create_candidate_cycles();
                    allcycles.insert(allcycles.end(), tree_cycles.begin(), tree_cycles.end());
                }

                /*
                 * Create graph with candidate cycles
                 */
                typedef boost::property<boost::tree_t, std::size_t> TreeVertexProperty;
                typedef boost::property<boost::edge_t, Edge, TreeVertexProperty> EdgeVertexProperty;
                typedef boost::property<boost::bad_t, bool, EdgeVertexProperty> BadVertexProperty;
                typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, BadVertexProperty> graph_t;
                typedef typename boost::graph_traits<graph_t>::vertex_descriptor vertex_descriptor;
                typedef typename boost::graph_traits<graph_t>::vertex_iterator vertex_iterator;

                graph_t cycles_g;
                typename boost::property_map<graph_t, boost::tree_t>::type tree_map = boost::get(boost::tree_t(),
                        cycles_g);
                typename boost::property_map<graph_t, boost::edge_t>::type edge_map = boost::get(boost::edge_t(),
                        cycles_g);
                typename boost::property_map<graph_t, boost::bad_t>::type bad_map = boost::get(boost::bad_t(),
                        cycles_g);

                std::map<std::pair<std::size_t, Edge>, vertex_descriptor> cycle_to_vertex;
                for (const auto &cc : allcycles) {
                    vertex_descriptor newv = boost::add_vertex(cycles_g);
                    cycle_to_vertex[std::make_pair(cc.tree(), cc.edge())] = newv;
                    boost::put(tree_map, newv, cc.tree());
                    boost::put(edge_map, newv, cc.edge());
                    boost::put(bad_map, newv, false);
                }

                vertex_iterator alli, alliend;
                for (boost::tie(alli, alliend) = boost::vertices(cycles_g); alli != alliend; ++alli) {
                    auto tree = boost::get(tree_map, *alli);
                    auto e = boost::get(edge_map, *alli);

                    parmcb::SPTree<Graph, WeightMap> &tree_x = trees[tree];
                    auto x = tree_x.source();
                    auto u = boost::source(e, g);
                    auto v = boost::target(e, g);

                    auto first_x_u = tree_x.first(u);
                    auto first_x_v = tree_x.first(v);

                    if (first_x_u == first_x_v) {
                        continue;
                    }

                    if (x == u) {
                        boost::add_edge(*alli, cycle_to_vertex[std::make_pair(trees_index_map[index_map[v]], e)],
                                cycles_g);
                    } else {
                        auto xprime = tree_x.first(u);
                        parmcb::SPTree<Graph, WeightMap> &tree_xprime = trees[trees_index_map[index_map[xprime]]];

                        auto first_xprime_v = tree_xprime.first(v);

                        if (x == first_xprime_v) {
                            boost::add_edge(*alli,
                                    cycle_to_vertex[std::make_pair(trees_index_map[index_map[xprime]], e)], cycles_g);
                        } else {
                            parmcb::SPTree<Graph, WeightMap> &tree_v = trees[trees_index_map[index_map[v]]];
                            auto first_v_xprime = tree_v.first(xprime);
                            if (u == first_v_xprime) {
                                boost::add_edge(*alli,
                                        cycle_to_vertex[std::make_pair(trees_index_map[index_map[v]],
                                                tree_x.node(xprime)->pred())], cycles_g);
                            } else {
                                boost::put(bad_map, *alli, true);
                            }
                        }
                    }
                }

                /*
                 * Find components
                 */
                std::vector<std::size_t> components(boost::num_vertices(cycles_g));
                boost::function_property_map<parmcb::detail::VertexIndexFunctor<graph_t, std::size_t>,
                        vertex_descriptor, std::size_t&> components_map(
                        parmcb::detail::VertexIndexFunctor<graph_t, std::size_t>(components,
                                boost::get(boost::vertex_index, cycles_g)));
                std::size_t num_components = boost::connected_components(cycles_g, components_map);

                std::vector<bool> is_bad_component(num_components, false);
                for (boost::tie(alli, alliend) = boost::vertices(cycles_g); alli != alliend; ++alli) {
                    auto v = *alli;
                    std::size_t component = boost::get(components_map, v);
                    is_bad_component[component] = is_bad_component[component] || boost::get(bad_map, v);
                }

                std::vector<bool> is_in_output(num_components, false);
                for (boost::tie(alli, alliend) = boost::vertices(cycles_g); alli != alliend; ++alli) {
                    auto v = *alli;
                    std::size_t component = boost::get(components_map, v);
                    if (!is_bad_component[component] && !is_in_output[component]) {
                        auto tree = boost::get(tree_map, v);
                        auto e = boost::get(edge_map, v);
                        parmcb::SPTree<Graph, WeightMap> &tree_v = trees[tree];

                        std::shared_ptr<SPNode<Graph, WeightMap>> spnode_v = tree_v.node(boost::source(e, g));
                        if (spnode_v == nullptr) {
                            continue;
                        }
                        std::shared_ptr<SPNode<Graph, WeightMap>> spnode_u = tree_v.node(boost::target(e, g));
                        if (spnode_u == nullptr) {
                            continue;
                        }

                        WeightType cycle_weight = boost::get(weight_map, e) + spnode_v->weight() + spnode_u->weight();
                        cycles.emplace_back(tree, e, cycle_weight);
                        is_in_output[component] = true;
                    }
                }
            }
        };

    } // detail

} // parmcb

#endif


/*
 * mapr_project.hpp
 *
 *  Created on: May 13, 2020
 *      Author: Kamil Miedzinski Mateusz Grycmacher
 *	 Institute: Instute of Home, Poznan University of Technology
 */

#pragma once

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

// ROS
#include <ros/ros.h>
#include <geometry_msgs/PoseStamped.h>
#include <nav_msgs/Path.h>
#include <nav_msgs/OccupancyGrid.h>
#include <grid_map_msgs/GridMap.h>
#include <std_msgs/UInt8.h>
#include <moveit/ompl_interface/ompl_interface.h>
#include <ompl/geometric/planners/rrt/RRTstar.h>
#include <ompl/geometric/planners/rrt/RRTConnect.h>
#include <ompl/base/spaces/RealVectorStateSpace.h>
#include <ompl/base/OptimizationObjective.h>
#include <ompl/base/objectives/PathLengthOptimizationObjective.h>
#include <ompl/base/ProblemDefinition.h>
#include <ompl/base/PlannerStatus.h>
#include <ompl/base/Planner.h>
#include <ompl/base/StateSpace.h>
#include <ompl/base/StateValidityChecker.h>
#include <ompl/base/SpaceInformation.h>
#include <ompl/base/objectives/PathLengthOptimizationObjective.h>
#include <ompl/base/objectives/StateCostIntegralObjective.h>
#include <ompl/base/objectives/MaximizeMinClearanceObjective.h>
#include <ompl/base/Cost.h>

// Boost
#include <boost/thread.hpp>

// standard
#include <mutex>
#include <iostream>
#include <thread>
#include <iomanip>
#include <fstream>
#include <iostream>

namespace mapr_project {

/*!
 * 2D planner example class
 */
class Planner2D
{
public:

    /*!
   * Constructor.
   * @param nodeHandle the ROS node handle.
   */
    Planner2D(ros::NodeHandle& _nodeHandle);

    /*!
   * Destructor.
   */
    virtual ~Planner2D();

    /*!
   * plan path
   */
//    nav_msgs::Path planPath(const grid_map_msgs::GridMap& globalMap);
   // nav_msgs::Path planPath(const nav_msgs::OccupancyGrid& globalMap);
    nav_msgs::Path planPath(const grid_map_msgs::GridMap& globalMap);

    void returnPoints(std_msgs::UInt8 pStartX, std_msgs::UInt8 pStartY,
                      std_msgs::UInt8 pEndX, std_msgs::UInt8 pEndY);

private:
    /// node handle
    ros::NodeHandle& nodeHandle;

    /// problem dim
    int dim;

    /// max step length
    double maxStepLength;

    /// bounds for the x axis
    std::shared_ptr<ompl::base::RealVectorBounds> coordXBound;

    /// bounds for the y axis
    std::shared_ptr<ompl::base::RealVectorBounds> coordYBound;

    /// start position
    std::shared_ptr<ompl::base::ScopedState<>> start;

    /// goal position
    std::shared_ptr<ompl::base::ScopedState<>> goal;

    /// search space
    std::shared_ptr<ompl::base::StateSpace> space;

    /// configure node
    void configure(double point_start_x, double point_start_y, double point_end_x,double point_end_y);

    /// extract path
    nav_msgs::Path extractPath(ompl::base::ProblemDefinition* pdef);
};

} /* namespace */


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

#include "bool_complex.hpp"

#include <unordered_set>

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

#include <core/utils/program_options.hpp>
#include <core/utils/range_utils.hpp>
#include <classical/functions/npn_canonization.hpp>

using namespace boost::program_options;

namespace cirkit
{

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

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

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

bool_complex_command::bool_complex_command( const environment::ptr& env )
  : cirkit_command( env, "Computes complexities of Boolean functions" )
{
  opts.add_options()
    ( "numvars,n", value_with_default( &numvars ), "number of variables (n <= 4)" )
    ( "count,c",                                   "compute upper bound on size" )
    ( "lengths,l",                                 "compute normal lengths" )
    ( "depths,d",                                  "compute depths" )
    ( "lengths_maj",                               "compute normal lengths (MAJ)" )
    ( "depths_maj",                                "compute depths (MAJ)" )
    ( "npn",                                       "compute number of NPN classes" )
    ;
  add_positional_option( "numvars" );
}

bool bool_complex_command::execute()
{
  if ( is_set( "count" ) )
  {
    compute_ub_aig();
  }
  if ( is_set( "lengths" ) )
  {
    compute( true );
  }
  if ( is_set( "depths" ) )
  {
    compute( false );
  }
  if ( is_set( "lengths_maj" ) )
  {
    compute_maj( true, is_set( "npn" ) );
  }
  if ( is_set( "depths_maj" ) )
  {
    compute_maj( false, is_set( "npn" ) );
  }

  return true;
}

void bool_complex_command::compute( bool length )
{
  const auto none = (unsigned)-1;
  auto count      = 1u << ( ( 1u << numvars ) - 1u );

  std::vector<unsigned>              func_to_length( count, none );
  std::vector<std::vector<unsigned>> length_to_func;

  /* constant function */
  func_to_length[0u] = 0u;
  length_to_func.push_back( {0u} );

  /* one-variable functions */
  for ( auto k = 0u; k < numvars; ++k )
  {
    const auto xk = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (k + 1)))) + 1);
    func_to_length[xk] = 0;
    length_to_func.back().push_back( xk );
  }

  count -= ( 1u + numvars );
  auto current_length = 0u;

  while ( true )
  {
    ++current_length;

    length_to_func.push_back( {} );

    int j = 0u;
    int k = current_length - 1;

    do
    {
      for ( auto g = 0u; g < length_to_func[j].size(); ++g )
      {
        const auto& fg = length_to_func[j][g];
        for ( auto h = 0u; h < length_to_func[k].size(); ++h )
        {
          const auto& fh = length_to_func[k][h];

          if ( fg == fh ) { continue; }

          for ( const auto& f : { fg & fh, ~fg & fh, fg & ~fh, fg | fh, fg ^ fh } )
          {
            if ( func_to_length[f] == none )
            {
              func_to_length[f] = current_length;
              length_to_func.back().push_back( f );

              if ( --count == 0u ) { goto done; }
            }
          }
        }
      }

      ++j;
      if ( length ) { --k; }
    } while ( j <= k );
  }

done:
  for ( const auto& entry : index( length_to_func ) )
  {
    std::cout << boost::format( "[i] %s %3d: %12d" ) % ( length ? "length" : "depth" ) % entry.index % ( entry.value.size() << 1u ) << std::endl;
  }
}

void bool_complex_command::compute_ub()
{
  const auto none = (unsigned)-1;
  auto count      = 1u << ( ( 1u << numvars ) - 1u );

  std::vector<unsigned>              func_to_count( count, none );
  std::vector<std::vector<unsigned>> count_to_func;
  std::vector<unsigned>              func_to_footprint( count, 0u );

  /* constant function */
  func_to_count[0u] = 0u;
  count_to_func.push_back( {0u} );

  /* one-variable functions */
  for ( auto k = 0u; k < numvars; ++k )
  {
    const auto xk = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (k + 1)))) + 1);
    func_to_count[xk] = 0;
    count_to_func.back().push_back( xk );
  }

  /* compute initial footprints */
  count_to_func.push_back( {} );
  auto footprint = 0u;
  for ( auto op = 0u; op < 5u; ++op )
  {
    for ( auto i = 0u; i < numvars; ++i )
    {
      const auto xi = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (i + 1)))) + 1);

      for ( auto j = i + 1u; j < numvars; ++j )
      {
	const auto xj = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (j + 1)))) + 1);
	auto f = 0u;

	switch ( op )
	{
	case 0u:
	  f = xi & xj; break;
	case 1u:
	  f = ~xi & xj; break;
	case 2u:
	  f = xi & ~xj; break;
	case 3u:
	  f = xi | xj; break;
	case 4u:
	  f = xi ^ xj; break;
	default: assert( false );
	}

	func_to_footprint[f] = 1 << footprint++;
	func_to_count[f] = 1u;
	count_to_func.back().push_back( f );
      }
    }
  }
  const auto count_at_one = 5 * ( ( numvars * ( numvars - 1 ) ) / 2 );
  assert( footprint == count_at_one );

  count -= ( 1u + numvars + count_at_one );
  auto current_length = 1u;

  while ( count )
  {
    ++current_length; /* r */
    count_to_func.push_back( {} );

    for ( int j = ( current_length - 1 ) / 2; j >= 0; --j )
    {
      int k = current_length - 1 - j;

      for ( auto g = 0u; g < count_to_func[j].size(); ++g )
      {
	const auto& fg = count_to_func[j][g];
	for ( auto h = 0u; h < count_to_func[k].size(); ++h )
	{
	  const auto& fh = count_to_func[k][h];

	  if ( fg == fh ) { continue; }

	  unsigned u{}, v{};
	  if ( func_to_footprint[fg] & func_to_footprint[fh] )
	  {
	    u = current_length - 1;
	    v = func_to_footprint[fg] & func_to_footprint[fh];
	  }
	  else
	  {
	    u = current_length;
	    v = func_to_footprint[fg] | func_to_footprint[fh];
	  }

	  for ( const auto& f : { fg & fh, ~fg & fh, fg & ~fh, fg | fh, fg ^ fh } )
	  {
	    if ( func_to_count[f] == none )
	    {
	      func_to_count[f] = u;
	      func_to_footprint[f] = v;
	      count_to_func[u].push_back( f );

	      --count;
	    }
	    else if ( func_to_count[f] > u )
	    {
	      auto& old_list = count_to_func[func_to_count[f]];
	      const auto it = std::find( old_list.begin(), old_list.end(), f );
	      assert( it != old_list.end() );
	      old_list.erase( it );
	      func_to_count[f] = u;
	      func_to_footprint[f] = v;
	      count_to_func[u].push_back( f );
	    }
	    else if ( func_to_count[f] == u && func_to_footprint[f] != ( func_to_footprint[f] | v ) )
	    {
	      func_to_footprint[f] |= v;
	    }
	  }
	}
      }
    }
  }

  for ( const auto& entry : index( count_to_func ) )
  {
    std::cout << boost::format( "[i] count %3d: %12d" ) % entry.index % ( entry.value.size() << 1u ) << std::endl;
  }
}

void bool_complex_command::compute_ub_aig()
{
  const auto none = (unsigned)-1;
  auto count      = 1u << ( ( 1u << numvars ) - 1u );

  std::vector<unsigned>              func_to_count( count, none );
  std::vector<std::vector<unsigned>> count_to_func;
  std::vector<unsigned>              func_to_footprint( count, 0u );

  /* constant function */
  func_to_count[0u] = 0u;
  count_to_func.push_back( {0u} );

  /* one-variable functions */
  for ( auto k = 0u; k < numvars; ++k )
  {
    const auto xk = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (k + 1)))) + 1);
    func_to_count[xk] = 0;
    count_to_func.back().push_back( xk );
  }

  /* compute initial footprints */
  count_to_func.push_back( {} );
  auto footprint = 0u;
  for ( auto op = 0u; op < 4u; ++op )
  {
    for ( auto i = 0u; i < numvars; ++i )
    {
      const auto xi = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (i + 1)))) + 1);

      for ( auto j = i + 1u; j < numvars; ++j )
      {
	const auto xj = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (j + 1)))) + 1);
	auto f = 0u;

	switch ( op )
	{
	case 0u:
	  f = xi & xj; break;
	case 1u:
	  f = ~xi & xj; break;
	case 2u:
	  f = xi & ~xj; break;
	case 3u:
	  f = xi | xj; break;
	default: assert( false );
	}

	func_to_footprint[f] = 1 << footprint++;
	func_to_count[f] = 1u;
	count_to_func.back().push_back( f );
      }
    }
  }
  const auto count_at_one = 4 * ( ( numvars * ( numvars - 1 ) ) / 2 );
  assert( footprint == count_at_one );

  count -= ( 1u + numvars + count_at_one );
  auto current_length = 1u;

  while ( count )
  {
    ++current_length; /* r */
    count_to_func.push_back( {} );

    for ( int j = ( current_length - 1 ) / 2; j >= 0; --j )
    {
      int k = current_length - 1 - j;

      for ( auto g = 0u; g < count_to_func[j].size(); ++g )
      {
	const auto& fg = count_to_func[j][g];
	for ( auto h = 0u; h < count_to_func[k].size(); ++h )
	{
	  const auto& fh = count_to_func[k][h];

	  if ( fg == fh ) { continue; }

	  unsigned u{}, v{};
	  if ( func_to_footprint[fg] & func_to_footprint[fh] )
	  {
	    u = current_length - 1;
	    v = func_to_footprint[fg] & func_to_footprint[fh];
	  }
	  else
	  {
	    u = current_length;
	    v = func_to_footprint[fg] | func_to_footprint[fh];
	  }

	  for ( const auto& f : { fg & fh, ~fg & fh, fg & ~fh, fg | fh } )
	  {
	    if ( func_to_count[f] == none )
	    {
	      func_to_count[f] = u;
	      func_to_footprint[f] = v;
	      count_to_func[u].push_back( f );

	      --count;
	    }
	    else if ( func_to_count[f] > u )
	    {
	      auto& old_list = count_to_func[func_to_count[f]];
	      const auto it = std::find( old_list.begin(), old_list.end(), f );
	      assert( it != old_list.end() );
	      old_list.erase( it );
	      func_to_count[f] = u;
	      func_to_footprint[f] = v;
	      count_to_func[u].push_back( f );
	    }
	    else if ( func_to_count[f] == u && func_to_footprint[f] != ( func_to_footprint[f] | v ) )
	    {
	      func_to_footprint[f] |= v;
	    }
	  }
	}
      }
    }
  }

  for ( const auto& entry : index( count_to_func ) )
  {
    std::cout << boost::format( "[i] count %3d: %12d" ) % entry.index % ( entry.value.size() << 1u ) << std::endl;
  }
}

inline unsigned maj( unsigned a, unsigned b, unsigned c )
{
  return ( a & b ) | ( a & c ) | ( b & c );
}

void bool_complex_command::compute_maj( bool length, bool npn )
{
  const auto none = (unsigned)-1;
  auto count      = 1u << ( ( 1u << numvars ) - 1u );

  std::vector<unsigned>                     func_to_length( count, none );
  std::vector<std::vector<unsigned>>        length_to_func;
  std::vector<std::unordered_set<unsigned>> length_to_npn;

  /* constant function */
  func_to_length[0u] = 0u;
  length_to_func.push_back( {0u} );

  /* one-variable functions */
  for ( auto k = 0u; k < numvars; ++k )
  {
    const auto xk = ((1 << (1 << numvars)) - 1) / ((1 << (1 << (numvars - (k + 1)))) + 1);
    func_to_length[xk] = 0;
    length_to_func.back().push_back( xk );
  }

  if ( npn )
  {
    length_to_npn.push_back( std::unordered_set<unsigned>() );
    length_to_npn.back().insert( 0u );
    length_to_npn.back().insert( (1 << (1 << (numvars - 1))) - 1 );
  }

  count -= ( 1u + numvars );
  auto current_length = 0u;

  while ( true )
  {
    ++current_length;

    length_to_func.push_back( {} );

    if ( npn )
    {
      length_to_npn.push_back( std::unordered_set<unsigned>() );
    }

    auto j = 0u;
    auto k = 0u;
    int l = current_length - 1;

    do
    {
      for ( auto g = 0u; g < length_to_func[j].size(); ++g )
      {
        const auto& fg = length_to_func[j][g];
        for ( auto h = ( j == k ) ? ( g + 1u ) : 0u; h < length_to_func[k].size(); ++h )
        {
          const auto& fh = length_to_func[k][h];
          if ( fg == fh ) { continue; }

          for ( auto i = ( static_cast<int>( k ) == l ) ? ( h + 1u ) : ( ( static_cast<int>( j ) == l ) ? ( g + 1u ) : 0u ); i < length_to_func[l].size(); ++i )
          {
            const auto& fi = length_to_func[l][i];
            if ( fg == fi || fh == fi ) { continue; }

            for ( const auto& f : { maj( fg, fh, fi ), maj( fg, fh, ~fi ), maj( fg, ~fh, fi ), maj( ~fg, fh, fi ) } )
            {
              if ( func_to_length[f] == none )
              {
                func_to_length[f] = current_length;
                length_to_func.back().push_back( f );

                if ( npn )
                {
                  boost::dynamic_bitset<> phase;
                  std::vector<unsigned> perm;
                  auto tt_npn = exact_npn_canonization( tt( 1u << numvars, f ), phase, perm );

                  length_to_npn.back().insert( tt_npn.to_ulong() );
                }

                if ( --count == 0u ) { goto done; }
              }
            }
          }
        }
      }

      if ( length )
      {
        if ( j + k == ( current_length - 1 ) )
        {
          ++j;
          k = j;
        }
        else
        {
          ++k;
        }
        l = current_length - 1 - j - k;
      }
      else
      {
        if ( k == current_length - 1 )
        {
          ++j;
          k = j;
        }
        else
        {
          ++k;
        }
        if ( j == current_length )
        {
          l = -1;
        }
      }
    } while ( l >= 0 );
  }

done:
  for ( const auto& entry : index( length_to_func ) )
  {
    std::cout << boost::format( "[i] %s %3d: %12d" ) % ( length ? "length" : "depth" ) % entry.index % ( entry.value.size() << 1u );

    if ( npn )
    {
      std::cout << boost::format( ", npn classes: %5d" ) % length_to_npn[entry.index].size();
    }

    std::cout << std::endl;
  }
}

}

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


/*

Copyright (c) 2014-2020, Arvid Norberg
Copyright (c) 2016, 2020, Alden Torres
Copyright (c) 2018, Pavel Pimenov
All rights reserved.

You may use, distribute and modify this code under the terms of the BSD license,
see LICENSE file.
*/

#include "libtorrent/config.hpp"
#include "libtorrent/aux_/crc32c.hpp"
#include "libtorrent/aux_/cpuid.hpp"
#include "libtorrent/aux_/byteswap.hpp"
#include "libtorrent/aux_/disable_warnings_push.hpp"

#include <boost/crc.hpp>
#if (defined _MSC_VER && _MSC_VER >= 1600 && (defined _M_IX86 || defined _M_X64))
#include <nmmintrin.h>
#endif

#include "libtorrent/aux_/disable_warnings_pop.hpp"

#if TORRENT_HAS_ARM_CRC32
#include <arm_acle.h>
#endif

namespace libtorrent::aux {

	std::uint32_t crc32c_32(std::uint32_t v)
	{
#if TORRENT_HAS_SSE
		if (aux::sse42_support)
		{
			std::uint32_t ret = 0xffffffff;
#ifdef __GNUC__
			// we can't use these because then we'd have to tell
			// -msse4.2 to gcc on the command line
//			return __builtin_ia32_crc32si(ret, v) ^ 0xffffffff;
			asm ("crc32l\t" "(%1), %0"
				: "=r"(ret)
				: "r"(&v), "0"(ret));
			return ret ^ 0xffffffff;
#else
			return _mm_crc32_u32(ret, v) ^ 0xffffffff;
#endif
		}
#endif

#if TORRENT_HAS_ARM_CRC32
		if (aux::arm_crc32c_support)
		{
			std::uint32_t ret = 0xffffffff;
			return __crc32cw(ret, v) ^ 0xffffffff;
		}
#endif

		boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
		crc.process_bytes(&v, 4);
		return crc.checksum();
	}

	std::uint32_t crc32c(std::uint64_t const* buf, int num_words)
	{
#if TORRENT_HAS_SSE
		if (aux::sse42_support)
		{
#if defined _M_AMD64 || defined __x86_64__ \
	|| defined __x86_64 || defined _M_X64 || defined __amd64__
			std::uint64_t ret = 0xffffffff;
			for (int i = 0; i < num_words; ++i)
			{
#ifdef __GNUC__
				// we can't use these because then we'd have to tell
				// -msse4.2 to gcc on the command line
//				ret = __builtin_ia32_crc32di(ret, buf[i]);
				__asm__("crc32q\t" "(%1), %0"
					: "=r"(ret)
					: "r"(buf+i), "0"(ret));
#else
				ret = _mm_crc32_u64(ret, buf[i]);
#endif
			}
			return std::uint32_t(ret) ^ 0xffffffff;
#else
			std::uint32_t ret = 0xffffffff;
			std::uint32_t const* buf0 = reinterpret_cast<std::uint32_t const*>(buf);
			for (int i = 0; i < num_words; ++i)
			{
#ifdef __GNUC__
				// we can't use these because then we'd have to tell
				// -msse4.2 to gcc on the command line
//				ret = __builtin_ia32_crc32si(ret, buf0[i*2]);
//				ret = __builtin_ia32_crc32si(ret, buf0[i*2+1]);
				asm ("crc32l\t" "(%1), %0"
					: "=r"(ret)
					: "r"(buf0+i*2), "0"(ret));
				asm ("crc32l\t" "(%1), %0"
					: "=r"(ret)
					: "r"(buf0+i*2+1), "0"(ret));
#else
				ret = _mm_crc32_u32(ret, buf0[i*2]);
				ret = _mm_crc32_u32(ret, buf0[i*2+1]);
#endif
			}
			return ret ^ 0xffffffff;
#endif // amd64 or x86
		}
#endif // x86 or amd64 and gcc or msvc

#if TORRENT_HAS_ARM_CRC32
		if (aux::arm_crc32c_support)
		{
			std::uint32_t ret = 0xffffffff;
			for (int i = 0; i < num_words; ++i)
			{
				ret = __crc32cd(ret, buf[i]);
			}
			return ret ^ 0xffffffff;
		}
#endif

		boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
		crc.process_bytes(buf, std::size_t(num_words) * 8);
		return crc.checksum();
	}
}


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

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

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_SPLIT_LOW_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_SPLIT_LOW_HPP_INCLUDED
#include <boost/simd/detail/overload.hpp>

#include <boost/simd/meta/hierarchy/simd.hpp>
#include <boost/simd/preprocessor/make.hpp>
#include <boost/simd/sdk/meta/is_upgradable.hpp>
#include <boost/simd/preprocessor/make.hpp>
#include <boost/simd/sdk/meta/is_upgradable.hpp>
#include <boost/simd/function/simd/extract.hpp>
#include <boost/simd/function/simd/make.hpp>
#include <boost/dispatch/meta/scalar_of.hpp>

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;
   BOOST_DISPATCH_OVERLOAD_IF(split_low_

                             , (typename A0, typename X)
                             , bd::cpu_
                             , bs::pack_<bd::arithmetic_<A0>, X>
                             )
   {
      using result = bd::upgrade_t<A0>;
     BOOST_FORCEINLINE result operator()(const A0& a0) const
      {
        return eval(a0,typename simd::meta::cardinal_of<result>::type());
      }
      #define M0(z, n, t) static_cast<sA1>(extract<n>(a0))
      BOOST_SIMD_PP_IMPLEMENT_WITH_MAKE(1, M0)
      #undef M0
   };

} } }

#endif


#include <set>
#include <sstream>
#include <iostream>
#include <algorithm>
using std::cout;
using std::endl;
#include <iomanip>
#include <spatialops/structured/FVStaggeredFieldTypes.h>
#include <spatialops/particles/ParticleOperators.h>
#include <spatialops/structured/FieldHelper.h>
#include <spatialops/structured/MatVecFields.h>
#include <spatialops/util/TimeLogger.h>
#include <test/TestHelper.h>
#include <stdio.h>
#include <limits.h>
#include <spatialops/structured/FieldComparisons.h>

#include <spatialops/structured/ExternalAllocators.h>

#include <cfloat>
#include <stdio.h>
#include <fstream>
#include <stdlib.h>

using namespace SpatialOps;
using namespace SpatialOps::Particle;
using std::cout;
using std::endl;

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#define DISTANCE(i,j) (pCoord.at(0)[i] - pCoord.at(0)[j]) * (pCoord.at(0)[i] - pCoord.at(0)[j]) + (pCoord.at(1)[i] - pCoord.at(1)[j]) * (pCoord.at(1)[i] - pCoord.at(1)[j])

int main( int iarg, char* carg[] )
{
    int nparticles;

    double h = 1.0f;

    bool sort = false, bf = false;

    DoubleVec gridMin (-1, -1, -1);
    DoubleVec gridMax (10, 10, 1);

    int fluid_particles(250);
    double height(5); double width(1);    // Dam Dimensions
    double dist(sqrt(height*width/fluid_particles));  // Distance (horizontally and vertically) between particles
    int xparticles(width/dist); int yparticles(height/dist); // number of particles in one row of dam
    int domain_height(5); int domain_width(5);
    h=2.5*dist;

    // initializing Dam particle positions*:
    std::vector<DoubleVec> positions;
    std::vector<IntVec> maskSet;

    double ppx,ppy,ppz;

    int i=0;
    for (int dx = 0; dx <= xparticles; dx++) // arranged such that it loops from bot left to top right (going from bottom to top)
        for (int dy = 0; dy <= yparticles; dy++)
        {
            ppx = dist*dx;
            ppy = dist*dy;
            ppz = 0;

            positions.push_back(DoubleVec(ppx,ppy,ppz));

            i++;
        }

    fluid_particles=i; // redefine number of particles
    i=i+1;

    // initializing Boundary Dam positions*:
    int d=0;
    int xbp ((domain_width+dist/1.5)/(dist));

    for (int dx = 0; dx <= xbp ; dx++) //bottom section
        for (int dy = 0; dy <= 1; dy++)
        {

            ppx = -dist/3+dist*(dx)+dist/2*(dy);
            ppy = -dist*(dy)-dist;
            ppz = 0;
            positions.push_back(DoubleVec(ppx,ppy,ppz));
            i++;
            d++;
        }

    int ybp (((domain_height+4.0*dist)/(dist)));

    for (int j = 0; j <= 1 ; j++)
        for (int dx = 0; dx <= 1 ; dx++)
            for (int dy = 0; dy <= ybp; dy++)
            {
                if (j==0){                                       // left wall
                    ppx = -dist*dx -dist;
                    ppy = -2*dist+dist*dy+dist/2*dx;
                    ppz = 0;
                    positions.push_back(DoubleVec(ppx,ppy,ppz));
                    i++;
                }
                else{
                    ppx = domain_width + dist*dx + dist;
                    ppy = -2*dist+dist*dy+dist/2*dx;
                    ppz = 0;
                    positions.push_back(DoubleVec(ppx,ppy,ppz));
                    i++;
                }
                d++;
            }

    int boundary_particles=d; // redefine number of boundary particles
    nparticles=fluid_particles+boundary_particles; // total number of particles

    const GhostData pg(0);
    const BoundaryCellInfo pbc = BoundaryCellInfo::build<SpatialOps::Particle::ParticleField>();
    const MemoryWindow pmwRS( get_window_with_ghost( IntVec(nparticles,0,0), pg, pbc) );

    FieldVector<ParticleField> properties (3, pmwRS, pbc, pg);
    FieldVector<ParticleField> out (9, pmwRS, pbc, pg);
    FieldVector<ParticleField> pCoord (3, pmwRS, pbc, pg);

    for (int i= 0; i < nparticles; i++)
    {

        pCoord.at(0)[i]    = positions[i][0];
        pCoord.at(1)[i]    = positions[i][1];
        pCoord.at(2)[i]    = positions[i][2];

        if (i>=fluid_particles){
            maskSet.push_back( IntVec(i, 0, 0) );
        }
    }

    SpatialMask<ParticleField> maskx( pCoord.at(0), maskSet );
    SpatialMask<ParticleField> masky( pCoord.at(1), maskSet );
    SpatialMask<ParticleField> maskz( pCoord.at(2), maskSet );

    // this loop initializes the vector of properties of size 4 with random values
    for (size_t i = 0; i < properties.elements(); i++)
    {
        if (i!=0)
            initialize_field(properties.at(i), 0.0, false, i * 4.251);
        else
            properties.at(i) <<= 0.0;
    }

    // initializes the output properties in the vector of fields of size 2 with 0
    for (size_t i = 0; i < out.elements(); i++)
    {
        out.at(i) <<= 0.0;
    }

    short int location = CPU_INDEX;

# ifdef ENABLE_CUDA
    pCoord.set_device_as_active(GPU_INDEX);
    properties.set_device_as_active(GPU_INDEX);
    out.set_device_as_active(GPU_INDEX);
    maskx.add_consumer(GPU_INDEX);
    masky.add_consumer(GPU_INDEX);
    maskz.add_consumer(GPU_INDEX);
    location = GPU_INDEX;
# endif

    double rho0(1000.0); double mass_p=(height*width*rho0/fluid_particles); // mass of each particle

    double pi(3.14);
    double c0 =10.0*sqrt(2*9.81*height); // reference speed of sound usually defined as 10*max(velocity)

    h=h/2;

    TestHelper status(true);

    RangeSearch rangeSearch(pmwRS, pg, pbc, gridMin, gridMax, nparticles, h, pCoord.at(0), pCoord.at(1), !bf, sort,location);

    // Simple mapped reduction
    out.at(0) <<= nebo_mapped_reduction ( properties.at(1), rangeSearch.GetCurrentState(location));

    // Mapped reduction with local() operator
    out.at(1) <<= nebo_mapped_reduction ( local(properties.at(2)), rangeSearch.GetCurrentState(location));

    // Mapped reduction with mapped_value() operator
    out.at(2) <<= nebo_mapped_reduction ( mapped_value(), rangeSearch.GetCurrentState(location));

    // Compound statement within a mapped reduction
    out.at(6) <<= nebo_mapped_reduction ( properties.at(1) * local(properties.at(2)) - mapped_value(), rangeSearch.GetCurrentState(location));

    // Complex computation
    out.at(7) <<= nebo_mapped_reduction (mass_p*
                                             ((local(properties.at(1)) - properties.at(1))*(local(pCoord.at(0))-pCoord.at(0)) // (xvelocity_ij * x_ij + ...
                                              + (local(properties.at(2)) - properties.at(2))*(local(pCoord.at(1))-pCoord.at(1))) // yvelocity_ij * y_ij) * ...
                                             *
                                             (cond( (mapped_value() > pow(10,-12)), // to avoid NaNs
                                                   cond( (mapped_value()/h <= 2),
                                                        ( 7/(4*pi*pow(h,3))) * // alpha *
                                                        (-5*mapped_value()/h    * // -5*r.ij/h *
                                                         pow(    1   -   .5  *  mapped_value()/h     ,3)) // (1- 0.5*r.ij/h)^3 /
                                                        /mapped_value() // rij
                                                        )
                                                   (0)) // else (0)
                                              (0)) // else 0                   dW/dr
                                             , rangeSearch.GetCurrentState(location));
#ifdef ENABLE_CUDA
    if (location == GPU_INDEX) {
        out.set_device_as_active(CPU_INDEX);
        properties.set_device_as_active(CPU_INDEX);
        pCoord.set_device_as_active(CPU_INDEX);
    }
#endif

    for (int i=0; i < nparticles; i++)
    {
        for (int j=0; j < nparticles; j++)
        {
            double d = DISTANCE(i,j);

            if (d >= 0.0f && d <= h * h) {
                out.at(3)[i] += properties.at(1)[j];
                out.at(4)[i] += properties.at(2)[i];
                out.at(5)[i] += std::sqrt(d);
                out.at(8)[i] += properties.at(1)[j] * properties.at(2)[i] - std::sqrt(d);
            }
        }
    }

    status(field_equal(out.at(0), out.at(3), 1e-10), "Simple Nebo Mapped Reduction test");
    status(field_equal(out.at(1), out.at(4), 0), "Nebo Mapped Reduction with local() operator test");
    status(field_equal(out.at(2), out.at(5), 1e-10), "Nebo mapped reduction with mapped_value() operator test");
    status(field_equal(out.at(6), out.at(8), 1e-10), "Nebo mapped reduction with a compound statement");

    if( status.ok() ) {
      cout << "ALL TESTS PASSED :)" << endl;
      return 0;
    }
    else {
        cout << "******************************" << endl
             << " At least one test FAILED! :(" << endl
             << "******************************" << endl;
        return -1;
    };

};


#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <fstream>
#include <cmath>
#include <getopt.h>
#include <vector>
#include <unordered_map>

#include <boost/config.hpp>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/undirected_graph.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

using namespace std;

typedef float Weight;
typedef boost::property<boost::edge_weight_t, Weight> WeightProperty;
typedef boost::property<boost::vertex_name_t, std::string> NameProperty;

typedef boost::adjacency_list < boost::listS, boost::vecS, boost::directedS,NameProperty, WeightProperty > Graph;

typedef boost::graph_traits < Graph >::vertex_descriptor Vertex;

typedef boost::property_map < Graph, boost::vertex_index_t >::type IndexMap;
typedef boost::property_map < Graph, boost::vertex_name_t >::type NameMap;

typedef boost::iterator_property_map < Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
typedef boost::iterator_property_map < Weight*, IndexMap, Weight, Weight& > DistanceMap;

// typedef boost::property<boost::edge_weight_t, double> EdgeWeightProperty;
// typedef boost::adjacency_list<boost::listS, boost::vecS,boost::bidirectionalS,boost::no_property,EdgeWeightProperty> Graph;

// typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;

std::vector<std::string> split(std::string const & s, size_t count)
{
  vector<string> ret;

  for(unsigned int i = 0;i < s.length();i+=count)
  {
    ret.push_back(s.substr(i,count));
  }
    return ret;
}

char* getCharExpr(string s)
{
    char *a=new char[s.size()+1];
    a[s.size()]=0;
    memcpy(a,s.c_str(),s.size());
    return a;
}

void print_usage()
{
	printf("Usage: misasm -g gfa -l hic_links\n");
}

int main(int argc, char* argv[])
{
	int option = 0;
	string gfa="", hic_links="";
	while((option = getopt(argc,argv,"g:l:")) != -1)
	{
		switch(option)
		{
			case 'l':
			{
				//cout<<optarg<<endl;
				hic_links = optarg;
				break;
			}
			case 'g':
			{
				//cout<<optarg<<endl;
				gfa = optarg;
				break;
			}
			default:
			{
				//cout<<"here"<<endl;
				print_usage();
				exit(EXIT_FAILURE);
			}
		}
	}

	if(gfa == "" || hic_links == "")
	{
		print_usage();
		exit(EXIT_FAILURE);
	}

	unordered_map<string,int> unitig2id;
	unordered_map<int,string> id2unitig;
	int id = 0;
	Graph g;
	string line;
	ifstream edges(getCharExpr(gfa));
	while(getline(edges,line))
	{
		string u,v,y,z,a,b;
		double w = 1.0;
		istringstream iss(line);
		/*
		S       tig00000042     *       LN:i:30165
		*/
		if(line[0] == 'S')
		{
			iss >> u >> v >> y >> z;
			if(unitig2id.find(v+":FOW") == unitig2id.end())
			{
				unitig2id[v+":FOW"] = id;
				id2unitig[id] = v+":FOW";
				id++;
			}
			if(unitig2id.find(v+":REV") == unitig2id.end())
			{
				unitig2id[v+":REV"] = id;
				id2unitig[id] = v+":REV";
				id++;
			}
		}
		/*
		L       tig00000014     -       tig00000001     -       5I1M5I
		*/
		if(line[0] == 'L')
		{
			iss >> u >> v >> y >> z >> a >> b;
			string contig1 = v;
			string contig2 = z;
			string or1 = y;
			string or2 = a;
			string v1,v2,v3,v4;
			//cout<<or1<<"\t"<<or2<<endl;
			if(or1 == "+" && or2 == "+")
			{
				v1 = contig1 + ":FOW";
				v2 = contig2 + ":FOW";
				v3 = contig2 + ":REV";
				v4 = contig1 + ":REV";
			}
			if(or1 == "+" && or2 == "-")
			{
				v1 = contig1 + ":FOW";
				v2 = contig2 + ":REV";
				v3 = contig2 + ":FOW";
				v4 = contig1 + ":REV";
			}
			if(or1 == "-" && or2 == "+")
			{
				v1 = contig1 + ":REV";
				v2 = contig2 + ":FOW";
				v3 = contig2 + ":REV";
				v4 = contig1 + ":FOW";
			}
			if(or1 == "-" && or2 == "-")
			{
				v1 = contig1 + ":REV";
				v2 = contig2 + ":REV";
				v3 = contig2 + ":FOW";
				v4 = contig1 + ":FOW";
			}
			boost::add_edge(unitig2id[v1],unitig2id[v2],w,g);
			boost::add_edge(unitig2id[v3],unitig2id[v4],w,g);
		}
	}
	edges.close();
	cerr<<"Done loading GFA file"<<endl;
	cerr<<"Number of Nodes = "<<boost::num_vertices(g)<<endl;
	cerr<<"Number of Edges = "<<boost::num_edges(g)<<endl;

	/*
	Assume that HiC links are given with in the decreasing order of weights.
	Also, process only those shortest path queries for which no link is added before.
	This would reduce tons of computation time
	*/

    unordered_map<string,bool> added;
	ifstream hic(getCharExpr(hic_links));
	while(getline(hic,line))
	{
		string a,b,x,h;
		double c,d,e,f;
		istringstream iss(line);
		iss >> a >> b >> c >> d >> e >> f >> x >> h;
		if(e < 1)
		{
			cout<<line<<endl;
            continue;
		}
		string u = a.substr(0,a.length()-2);
		string v = b.substr(0,b.length()-2);
        if(unitig2id.find(u+":FOW") == unitig2id.end() && unitig2id.find(u+":REV") == unitig2id.end() && unitig2id.find(v+":FOW") == unitig2id.end() && unitig2id.find(v+":REV") == unitig2id.end())
        {
            cout<<line<<endl;
            continue;
        }
		// Create things for Dijkstra
		std::vector<Vertex> predecessorsFOW(boost::num_vertices(g)); // To store parents
		std::vector<Weight> distancesFOW(boost::num_vertices(g)); // To store distances

		IndexMap indexMap = boost::get(boost::vertex_index, g);
		PredecessorMap predecessorMapFOW(&predecessorsFOW[0], indexMap);
		DistanceMap distanceMapFOW(&distancesFOW[0], indexMap);

		boost::dijkstra_shortest_paths(g, unitig2id[u + ":FOW"], boost::distance_map(distanceMapFOW).predecessor_map(predecessorMapFOW));

		std::vector<Vertex> predecessorsREV(boost::num_vertices(g)); // To store parents
		std::vector<Weight> distancesREV(boost::num_vertices(g)); // To store distances

		PredecessorMap predecessorMapREV(&predecessorsREV[0], indexMap);
		DistanceMap distanceMapREV(&distancesREV[0], indexMap);

		boost::dijkstra_shortest_paths(g, unitig2id[u + ":REV"], boost::distance_map(distanceMapREV).predecessor_map(predecessorMapREV));

		long minpath = -1;
		string ori;
		double ratio = 0.1;
		vector<string> orientations;
		orientations.push_back("BB");
		orientations.push_back("BE");
		orientations.push_back("EB");
		orientations.push_back("EE");
		bool done = false;
		bool printed = false;
    bool inlimits = false;
    for(unsigned int i = 0;i < orientations.size();i++)
		{
            printed = false;
			string orientation = orientations[i];
			string v1,v2;
			if(orientation == "BB")
			{
				v1 = u + ":REV";
				v2 = v + ":FOW";
			}
			if(orientation == "BE")
			{
				v1 = u + ":REV";
				v2 = v + ":REV";
			}
			if(orientation == "EB")
			{
				v1 = u + ":FOW";
				v2 = v + ":FOW";
			}
			if(orientation == "EE")
			{
				v1 = u + ":FOW";
				v2 = v + ":REV";
			}
			// cout<<boost::edge(unitig2id[v1],unitig2id[v2],g).second<<endl;
			if(added.find(a) == added.end() && added.find(b) == added.end())
			{
				if(boost::edge(unitig2id[v1],unitig2id[v2],g).second)
				{
					//cout<<line<<endl;
					//cout<<"EDGE exists"<<endl;
                    added[u] = true;
					added[v] = true;
					cout<<u<<":"<<orientation[0]<<"\t"<<v<<":"<<orientation[1]<<"\t"<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<x<<"\t"<<h<<endl;
					done = true;
					printed = true;
                    break;
				}
				else
				{
					//cout<<"here"<<endl;
					//string linkorient = string(a[a.length()-1]) + string(b[b.length()-1]);
					//extract contig name
				    //cout<<"here"<<endl;
					long path_len;
					if(orientation == "BB" || orientation == "BE")
					{
						path_len = distanceMapREV[unitig2id[v2]];
					    //cout<<"PATH LENGTH = "<<path_len<<endl;
                    }
					else
					{
						path_len = distanceMapFOW[unitig2id[v2]];
                        //cout<<"PATH LENGTH = "<<path_len<<endl;
					}
					if(path_len < INT_MAX && path_len > INT_MIN)
					{
                        //cout<<"Path Length = "<<path_len<<endl;
                        inlimits=true;
						if(minpath == -1)
						{
                            //cout<<"setting"<<endl;
                            //cout<<line<<endl;
                            //cout<<orientation<<endl;
							minpath = path_len;
							ori = orientation;
						}
						else
						{
							if(path_len < minpath)
							{
								ratio = 1.0*path_len/minpath;
								minpath = path_len;
								ori = orientation;
							}
						}
					}
				}
			}
		}
		//if(!done &&  minpath < INT_MAX && minpath > INT_MIN)
		//{
		// 	cout<<ratio<<"\t"<<minpath<<endl;
		//}
        if(!inlimits)
        {
            continue;
        }
		if(ratio > 0 && ratio < 1 && minpath < INT_MAX && minpath > INT_MIN && !done)
		{
			//cout<<ratio<<"\t"<<minpath<<endl;
			//cout<<"Where we found the paths in graph"<<endl;
            cout<<u+":"+ori[0]<<'\t'<<v+":"+ori[1]<<'\t'<<c<<"\t"<<d<<"\t"<<e<<"\t"<<f<<"\t"<<x<<"\t"<<h<<endl;
		    //cout << line << endl;
			added[a] = true;
            printed = true;
			added[b] = true;
		}
		else
		{
            //cout<<"here"<<endl;
			if(!printed)
            {
                cout<<line<<endl;
			    added[a] = true;
			    added[b] = true;
            }
		}
        //cout<<"======================="<<endl;
	}

	// BGL_FORALL_VERTICES(v, g, Graph)
 //  	{
 //  		cout<<id2unitig[1]<<"\t"<<id2unitig[v]<<"\t"<<distanceMap[v]<<endl;
 //  	}
	return 0;
}