#include <boost/graph/compressed_sparse_row_graph.hpp>
#include <boost/graph/rmat_graph_generator.hpp>
#include <boost/random.hpp>
#include <cstddef> // size_t
#include <cstdlib> // exit
#include <cstdio> // printf
#include <unistd.h> // getopt, optarg
#include <algorithm> // min
#include <vector>
#include <iostream> // cout, endl
#include <fstream> // ofstream
#if defined(_OPENMP)
#include <omp.h>
#endif
using namespace std;

#include "hash_nbits.hpp"

// Example main arguments
// #define MARGS "-s16 -v15"
// #define MARGS "-s18 -v15" *
// #define MARGS "-s19 -v15"
// #define MARGS "-s22 -v15"

#include "lime.h"

#define DEFAULT_RMAT_SCALE 16U // log 2 size
#define DEFAULT_BLOCK_LSZ 12 // log 2 size
//#define PARTIAL 131072 // used to shorten run time for trace capture

// Use 32-bit integers for *smaller* graph
typedef unsigned int index_t; // used in adjacency list
typedef size_t gsize_t; // graph size type

typedef double* double_p;
typedef index_t* index_p;
#if defined(USE_STREAM) && defined(__linux__)
#include <allocator.h>
typedef std::vector<index_t, ACC_NS::allocator<index_t> > idx_vec_t;
typedef std::vector<idx_vec_t, ACC_NS::allocator<idx_vec_t> > graph_t;
typedef std::vector<double, ACC_NS::allocator<double> > double_vec_t;
#else
typedef std::vector<std::vector<index_t> > graph_t;
typedef std::vector<double> double_vec_t;
#endif

typedef boost::compressed_sparse_row_graph<boost::directedS, boost::no_property, boost::no_property, boost::no_property, index_t, index_t> DummyForRMAT;
typedef boost::rmat_iterator<boost::mt19937, DummyForRMAT> RMATGen;

#if defined(PARTIAL)
bool cflag = false;
#else
bool cflag = true;
#endif
bool pflag = false;
unsigned rmat_scale = DEFAULT_RMAT_SCALE;
unsigned block_sz = 1U<<DEFAULT_BLOCK_LSZ;
char *oname = NULL;

// TODO: find a better place for these globals

tick_t t0, t1, t2, t3, t4, t5, t6, t7, t8;
unsigned long long tsetup, treorg, toper, tcache;

//------------------ Support ------------------//

#if defined(USE_ACC)

#include "IndexArray.hpp"

#define MIN_EDGES 128
#define restrict __restrict__

IndexArray<index_t> dre; // Data Reorganization Engine

/**
 * Single Iteration of Page Rank
 * @param adj_list_graph  Adjacency list graph.
 *        Must be either undirected, or the transpose of a directed graph!
 * @param cur_pr_vector   Current PR vector
 * @param next_pr_vector  Next PR vector
 */
void page_rank_itr(
	graph_t& adj_list_graph,
	double_vec_t& cur_pr_vector,
	double_vec_t& next_pr_vector,
	double damping_factor,
	bool div
	)
{
	gsize_t num_vertices = adj_list_graph.size();
	gsize_t onepct = (num_vertices < 100) ? 1 : num_vertices/100;
	double jump_prob = (double(1) - damping_factor)/num_vertices;
	double_p cur_pr = cur_pr_vector.data();
	double_p next_pr = next_pr_vector.data();
	double_p restrict block = SP_NALLOC(double, block_sz/sizeof(double)); // view block
#if defined(PARTIAL)
	num_vertices = PARTIAL;
#endif

	tget(t0);
	// receive block, not needed when memory has been invalidated before entry.
	// Xil_L1DCacheInvalidateRange((INTPTR)block, block_sz);
	tget(t1);

	// Accumulate PRs for each vertex
	for (gsize_t i = 0; i < num_vertices; ++i) {
		gsize_t edges = adj_list_graph[i].size();
		index_p edge_i = adj_list_graph[i].data();
		if (pflag && i%onepct == 0) cout << i/onepct << "%\r" << flush;
		double tmp = next_pr[i];

		if (edges < MIN_EDGES) {
			// Loop over adjacency list
			for (gsize_t j = 0; j < edges; ++j) {
				tmp += cur_pr[edge_i[j]];
			}
		} else {
			tget(t2);
			// Set up view of cur_pr
			dre.setup(cur_pr, sizeof(double), edge_i, edges);
			dre.wait();
			tget(t3);
			// Loop over adjacency list
			gsize_t view_off;
			gsize_t view_end = edges * sizeof(double);
			for (view_off = 0; view_off < view_end; view_off += block_sz) {
				unsigned view_sz = min((gsize_t)block_sz, view_end-view_off);
				unsigned j;
				tget(t4);
				// fill block with indexed view
				dre.fill(block, view_sz, view_off);
				dre.wait();
				tget(t5);
				// receive block
				Xil_L1DCacheInvalidateRange((INTPTR)block, CEIL(view_sz,ALIGN_SZ));
				tget(t6);
				unsigned view_n = view_sz / sizeof(double);
				for (j = 0; j < view_n; ++j) {
					tmp += block[j];
				}
				tinc(treorg, tdiff(t5,t4));
				tinc(tcache, tdiff(t6,t5));
			}
			tinc(tsetup, tdiff(t3,t2));
		}

		// Apply damping
		tmp = jump_prob + (damping_factor * tmp);

		// Divide by out-degree
		if (div && edges) {
			tmp /= edges;
		}

		next_pr[i] = tmp;
	}
	tget(t7);
	// flush output product to memory
	host::cache_flush(next_pr, num_vertices*sizeof(double));
	// make sure to invalidate cache before delete
	CACHE_DISPOSE(dre, block, block_sz)
	tget(t8);
	SP_NFREE((double*)block);
	tinc(tcache, tdiff(t1,t0)+tdiff(t8,t7));
}

#else /* USE_ACC */

/**
 * Single Iteration of Page Rank
 * @param adj_list_graph  Adjacency list graph.
 *        Must be either undirected, or the transpose of a directed graph!
 * @param cur_pr_vector   Current PR vector
 * @param next_pr_vector  Next PR vector
 */
void page_rank_itr(
	graph_t& adj_list_graph,
	double_vec_t& cur_pr_vector,
	double_vec_t& next_pr_vector,
	double damping_factor,
	bool div
	)
{
	gsize_t num_vertices = adj_list_graph.size();
	gsize_t onepct = (num_vertices < 100) ? 1 : num_vertices/100;
	double jump_prob = (double(1) - damping_factor)/num_vertices;
	double_p cur_pr = cur_pr_vector.data();
	double_p next_pr = next_pr_vector.data();
#if defined(PARTIAL)
	num_vertices = PARTIAL;
#endif

	// Accumulate PRs for each vertex
#if defined(_OPENMP)
	#pragma omp parallel for
#endif
	for (gsize_t i = 0; i < num_vertices; ++i) {
		gsize_t edges = adj_list_graph[i].size();
		index_p edge_i = adj_list_graph[i].data();
		if (pflag && i%onepct == 0) cout << i/onepct << "%\r" << flush;
		double tmp = next_pr[i];

		// Loop over adjacency list
		for (gsize_t j = 0; j < edges; ++j) {
			tmp += cur_pr[edge_i[j]];
		}

		// Apply damping
		tmp = jump_prob + (damping_factor * tmp);

		// Divide by out-degree
		if (div && edges) {
			tmp /= edges;
		}

		next_pr[i] = tmp;
	}
	tget(t0);
	// flush output product to memory
	host::cache_flush(next_pr, num_vertices*sizeof(double));
	tget(t1);
	tinc(tcache, tdiff(t1,t0));
}

#endif /* USE_ACC */

void page_rank_itr_check(
	graph_t& adj_list_graph,
	double_vec_t& cur_pr_vector,
	double_vec_t& next_pr_vector,
	double damping_factor,
	bool div
	)
{
	gsize_t num_vertices = adj_list_graph.size();
	gsize_t onepct = (num_vertices < 100) ? 1 : num_vertices/100;
	double jump_prob = (double(1) - damping_factor)/num_vertices;
	double_p cur_pr = cur_pr_vector.data();
	double_p next_pr = next_pr_vector.data();
	gsize_t errors = 0;

	// Accumulate PRs for each vertex
	for (gsize_t i = 0; i < num_vertices; ++i) {
		gsize_t edges = adj_list_graph[i].size();
		index_p edge_i = adj_list_graph[i].data();
		if (pflag && i%onepct == 0) cout << i/onepct << "%\r" << flush;
		double tmp = 0.0; //next_pr[i];

		// Loop over adjacency list
		for (gsize_t j = 0; j < edges; ++j) {
			tmp += cur_pr[edge_i[j]];
		}

		// Apply damping
		tmp = jump_prob + (damping_factor * tmp);

		// Divide by out-degree
		if (div && edges) {
			tmp /= edges;
		}

		if (fabs(next_pr[i]-tmp) > 1e-9) {
			//cerr << " i:" << i << " found:" << next_pr[i] << " expected:" << tmp << endl;
			errors++;
		}
	}
	cerr << "errors:" << errors << endl;
}

//------------------ Main ------------------//

int MAIN(int argc, char *argv[])
{
	/* * * * * * * * * * get arguments beg * * * * * * * * * */
	int opt;
	bool nok = false;

#if defined(USE_ACC)
	dre.wait(); // wait for DRE initialization
#endif
	while ((opt = getopt(argc, argv, "cps:v:o:")) != -1) {
		switch (opt) {
		case 'c':
			cflag = true;
			break;
		case 'p':
			pflag = true;
			break;
		case 's':
			rmat_scale = atoi(optarg);
			break;
		case 'v':
			block_sz = (1U << atoi(optarg));
			if (block_sz < 8) {
				fprintf(stderr, "view buffer block size must be 8 or greater.\n");
				nok = true;
			}
			break;
		case 'o':
			oname = optarg;
			break;
		default: /* '?' */
			nok = true;
		}
	}
	if (nok) {
		fprintf(stderr, "Usage: pager -cp -s<int> -v<int> -o<out_file>\n");
		fprintf(stderr, "  -c  check first iteration\n");
		fprintf(stderr, "  -p  show progress\n");
		fprintf(stderr, "  -s  RMAT scale 2^n, default: n=%d\n", DEFAULT_RMAT_SCALE);
		fprintf(stderr, "  -v  view buffer block size 2^n, default: n=%d\n", DEFAULT_BLOCK_LSZ);
		fprintf(stderr, "  -o  output page rank, default: NULL\n");
		fprintf(stderr, "\n");
		return EXIT_FAILURE;
	}
#if defined(_OPENMP)
	// to control the number of threads use: export OMP_NUM_THREADS=N
	printf("threads: %d\n", omp_get_max_threads());
#endif
#ifdef USE_SP
	if (block_sz > SP_SIZE) block_sz = SP_SIZE;
#endif
	printf("rmat scale: %u\n", rmat_scale);
#if defined(USE_ACC)
	printf("view_size: %u\n", block_sz);
	printf("min_edges: %u\n", MIN_EDGES); // threshold
#endif

	/* * * * * * * * * * get arguments end * * * * * * * * * */

	unsigned EDGE_FACTOR = 16;
	double rmat_a = 0.57;
	double rmat_b = 0.19;
	double rmat_c = 0.19;
	double rmat_d = 0.05;
	tick_t start, finish;
	double secs;

	boost::mt19937 rng;
	gsize_t num_vertices = gsize_t(1) << rmat_scale;
	gsize_t num_edges = num_vertices * EDGE_FACTOR;
	gsize_t onepct = (num_edges < 100) ? 1 : num_edges/100;

	// Start graph storage as adjacency list
	graph_t adj_list_graph(num_vertices);

	// Generate edges and insert into adjacency list
	cout << "RMAT generation, vertices:" << num_vertices << endl;
	cout << "RMAT generation, edges:" << num_edges << endl;
	cout << "RMAT generation, bytes:" << num_vertices*sizeof(void*)+num_edges*sizeof(index_t)*2 << endl;
	tget(start);
	RMATGen rmat_itr(rng, num_vertices, num_edges, rmat_a, rmat_b, rmat_c, rmat_d, false);
	for (gsize_t i = 0; i < num_edges; ++i) {
		if (pflag && i%onepct == 0) cout << i/onepct << "%\r" << flush;
		// Generates rmat edge
		std::pair<index_t, index_t> edge = *rmat_itr++;
		// Permute Vertex Labels
		edge.first  = hash_nbits(edge.first, rmat_scale);
		edge.second = hash_nbits(edge.second, rmat_scale);
		// Adds both directions to adjacency list (undirected)
		adj_list_graph[edge.first].push_back(edge.second);
		adj_list_graph[edge.second].push_back(edge.first);
	}
	tget(finish);
	secs = tsec(finish, start);
	if (secs == 0.0) secs = 1.0/TICKS_SEC;
	cout << "RMAT generation, time:" << secs << " sec, EPS:" << (double)num_edges/secs << endl;

	// Prepare for PageRank iteration
	double_vec_t cur_pr_vector(num_vertices);
	double_vec_t next_pr_vector(num_vertices);
	boost::uniform_01<double> u01_dist;
	for (gsize_t i = 0; i < adj_list_graph.size(); ++i) {
		// Initializes PR to small random number.
		// Does not need normalization, but could...
		cur_pr_vector[i] = u01_dist(rng);
		// Divide by out-degree
		if (adj_list_graph[i].size())
			cur_pr_vector[i] /= adj_list_graph[i].size();
		next_pr_vector[i] = 0;
	}
	cout << "PageRank Vectors, bytes:" << num_vertices*sizeof(double)*2 << endl;

	tsetup = treorg = tcache = 0;
	CLOCKS_EMULATE
	CACHE_BARRIER(dre)
	TRACE_START
	STATS_START

	// assume input data is in memory and not cached (flushed and invalidated)
	tget(start);
	page_rank_itr(adj_list_graph, cur_pr_vector, next_pr_vector, 0.85, false);
	// assume output data is in memory (flushed)
	tget(finish);

	CACHE_BARRIER(dre)
	STATS_STOP
	TRACE_STOP
	CLOCKS_NORMAL
	toper = tdiff(finish,start)-tsetup-treorg-tcache;
	secs = tesec(finish, start);
	if (secs == 0.0) secs = 1.0/TICKS_ESEC;
	cout << "page rank time:" << secs << " sec" << endl;
#if defined(USE_ACC)
	cout << "Setup time: " << tsetup/(double)TICKS_ESEC << " sec" << endl;
	cout << "Reorg time: " << treorg/(double)TICKS_ESEC << " sec" << endl;
#endif
	cout << "Oper. time: " << toper/(double)TICKS_ESEC << " sec" << endl;
	cout << "Cache time: " << tcache/(double)TICKS_ESEC << " sec" << endl;
	STATS_PRINT

	if (cflag) page_rank_itr_check(adj_list_graph, cur_pr_vector, next_pr_vector, 0.85, false);

	/* output page rank */
	if (oname != NULL) {
		std::ofstream ofs(oname, std::ofstream::out);
		for (double_vec_t::iterator it = next_pr_vector.begin(); it != next_pr_vector.end(); ++it)
			ofs << ' ' << *it << endl;
		ofs.close();
	}

	TRACE_CAP
	return EXIT_SUCCESS;
}


#pragma once
#include <iostream>
#include <nsgaii.hpp>
#include <evaluator.hpp>
#include <parsers.hpp>
#include <egraph.hpp>
#include <boost/filesystem.hpp>
#include <pagmo/problems/dtlz.hpp>
#include <pagmo/archipelago.hpp>
#include <random>
#include <config.hpp>
#include <chrono>
#include <unordered_map>
#include <unordered_set>

namespace OptCompiler
{
	struct FabInfo
	{
	public:
		struct Fab
		{
			Vector1d1 score;//material/precision/time
			Vector1i1 EClassIDs;
			Vector1i1 ArrIDs;
			Vector1i1 ENodeIDs;

			string vis_path;

			std::vector<std::tuple<string, int, int>> encoded_vec;//???
			VectorPI1 encoded_td;//???

			Vector1i1 order;//cutting order
			VectorPI1 order_stack;//cutting order stacks
			VectorPI1 order_merge;//cutting order merges
			std::vector<std::tuple<std::string, std::string, double, double, double>> referFE;//cutting references

			VectorPI1 joint_encode;

			Fab() {}
			Fab(const Vector1d1& score_, const Vector1i1& ArrIDs_):score(score_), ArrIDs(ArrIDs_){};

			void Clear()
			{
				Vector1d1().swap(score);
				Vector1i1().swap(EClassIDs);
				Vector1i1().swap(ArrIDs);
				Vector1i1().swap(ENodeIDs);

				std::vector<std::tuple<string, int, int>>().swap(encoded_vec);
				VectorPI1().swap(encoded_td);

				Vector1i1().swap(order);
				VectorPI1().swap(order_stack);
				VectorPI1().swap(order_merge);
				std::vector<std::tuple<std::string, std::string, double, double, double>>().swap(referFE);

				VectorPI1().swap(joint_encode);
			}
		};

		double hv;//hypervolume
		std::vector<Fab> fabs;

		int nsga_layer = -1;//ga nsga layer
		double nsga_crowding_distance = 0.0;//ga nsga crowding distance

		const Vector1i2 GetArrIDs() const;
		const Vector1d2 GetScores() const;
		const VectorPI2 GetEncodedTds() const;
		const VectorPI2 GetEgraphEncodes() const;
		const std::vector<std::vector<std::tuple<string, int, int>>> GetEncodedVecs() const;

		const void OutputLog() const;

		void Clear();
	};

	class OptimizingCompiler {
	public:
		OptimizingCompiler() = default;

		~OptimizingCompiler() = default;

		int execute(const std::string& folder, const std::vector<std::vector<std::tuple<string, int, int>>>& encoded_vecs = std::vector<std::vector<std::tuple<string, int, int>>>(), const VectorPI2& encoded_tds = VectorPI2());

		static FabInfo ReadParetoFronts(const std::string& path);

		static Vector1d2 ParetoFronts_Score(const Vector1d2& scores, Vector1i1& fronts_index = Vector1i1());

		static int ParetoFronts_Score(const Vector1d2& scores, const Vector1d1& score);
		static int ParetoFronts_Score(const std::vector<FabInfo::Fab>& fabs, const FabInfo::Fab& fab);

		static std::vector<FabInfo::Fab> ParetoFronts_Score(const std::vector<FabInfo::Fab>& fabs, Vector1i1& fronts_index = Vector1i1(), int add_fronts = 0);

		static double ComputeHV(const std::vector<std::vector<double>>& hyper_volume_scores);

	private:

		void ConstructENodes(EGraph* eg, std::unordered_map<int, EGraphParser::SubModule>& sexpProgs)
		{
			auto& cutLineId_cMaps = Config::get_instance().ecCutLineIdmaps;
			auto& enode_cMaps = Config::get_instance().ecENodeIdmaps;
			auto& arr_cMaps = Config::get_instance().ecArrIdmaps;
			auto& wlc_cMaps = Config::get_instance().ecWLCIdmaps;

			int eId = 10000;

			int eNodeId = 0;

			// Load all subprograms
			for (const auto& subMd : sexpProgs)
			{
				// loop: submodule
				auto eclassID = subMd.first;
				auto eclassID_str = Math::Functs::IntString(eclassID);
				auto ec = new EClass();//EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass EClass
				ec->SetId(eclassID_str);

				auto& cutLineId_cMap = cutLineId_cMaps[eclassID_str];
				auto& enode_cMap = enode_cMaps[eclassID_str];
				auto& arr_cMap = arr_cMaps[eclassID_str];
				auto& wlc_cMap = wlc_cMaps[eclassID_str];

				for (auto i = 0; i < subMd.second.size(); ++i)
				{
					// loop: program
					ENode* enToProg = new ENode();//ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode ENode
					ec->AddEn(enToProg);

					auto enToProgId_str = eclassID_str + "_" + Math::Functs::IntString(i);

					enToProg->SetId(enToProgId_str);
					enToProg->eclassID = eclassID;
					enToProg->cutLinesIds = cutLineId_cMap[i];
					enToProg->enodeID = enode_cMap[i];
					enToProg->arr = arr_cMap[i];
					enToProg->wlc = wlc_cMap[i];

					auto& prog = subMd.second[i];
					// for each equivalence
					for(int j=0;j<prog.size();j++)
					{
						auto eq = prog[j];

						auto ecEquiv = new EClass();//EClass EClass EClass EClass EClass EClass EClass EClass EClass
						auto ecEquivId_str = enToProgId_str + "_" + Math::Functs::IntString(j);
						ecEquiv->SetId(ecEquivId_str);
						enToProg->ecs.push_back(ecEquiv);
						// loop: equivalence

						if (eq.size() != 1)
						{
							int dsad = 0;
						}

						for(int k=0;k<eq.size();k++)
						//for (auto& ln : eq)
						{
							auto ln = eq[k];
							// loop: line
							auto en = new ENode(ln.first, ln.second);//ENode ENode ENode ENode ENode ENode ENode
							auto enId_str = ecEquivId_str + "_" + Math::Functs::IntString(k);
							en->SetId(enId_str);
							en->eclassID = eclassID;
							en->cutLinesIds = cutLineId_cMap[i];
							en->enodeID = enode_cMap[i];
							en->arr = arr_cMap[i];
							en->wlc = wlc_cMap[i];
							en->cutLineID = cutLineId_cMap[i][j];
							en->equivalenceID = j;
							en->lineID = k;
							GraphMap::get_instance().cont[&(en->ast.g)] = ProgPointer(eclassID, i);
							ecEquiv->AddEn(en);
						}

						eg->AddEc(ecEquiv);
					}
				}
				eg->AddEc(ec);
			}
		}

		void ComputeReferencePoint(pagmo::population& pop)
		{
			double refPoint[3] = { -DBL_MAX, -DBL_MAX, -DBL_MAX };

			for (auto& p : pop.get_f())
			{
				for (auto i = 0; i < 3; ++i)
				{
					if (p[i] > refPoint[i])
					{
						refPoint[i] = p[i];
					}
				}
			}
			std::cerr << "Ref Point = ";
			for (auto i = 0; i < 3; ++i)
				std::cerr << refPoint[i] << " ";
			std::cerr << std::endl;
		}

		void BuildTree(std::shared_ptr<TreeNode> node, std::deque<int>& ids)
		{
			if (!node) return;

			if (node->GetN() != -1)
			{
				node->SetN(ids.front());
				ids.erase(ids.begin());
			}

			for (auto& n : node->subNodes)
			{
				BuildTree(n, ids);
			}
		}

		void mutate(std::shared_ptr<TreeNode> node)
		{
			if (node->GetN() == -1)
			{
				for (auto& subNode : node->subNodes)
				{
					mutate(subNode);
				}
			}
			else
			{
				if (Config::get_instance().GetProb() < 0.02)
				{
					auto hb = static_cast<int>(node->ec->ens.size()) - 1;
					node->SetN(Config::get_instance().GetRandom(0, hb));
				}

				if (!node->IsProgram())
				{
					for (auto& subNode : node->subNodes)
					{
						mutate(subNode);
					}
				}
			}
		}

		void mutate(EncodedVec& child)
		{
			mutate(child.root);
		}

		std::string EncodeTreeLeafMain(const std::shared_ptr<TreeNode>& node)
		{
			if (!node) return "#";

			std::string str;

			if (node->val != "-1")
				str = node->val + "-" + std::to_string(node->GetN());

			if (!node->subNodes.empty())
			{
				for (auto& n : node->subNodes)
				{
					str += "," + EncodeTreeLeafMain(n);
				}
			}

			return str;
		}

		void ParseAndEvaluate(const std::string& file)
		{
			using namespace tinyxml2;
			std::string fsn = file;
			std::ofstream fileScore(fsn.append(".txt"));

			XMLDocument doc;
			doc.LoadFile(file.c_str());
			XMLElement* root = doc.FirstChildElement("root");
			for (XMLElement* e = root->FirstChildElement("Program"); e != nullptr; e = e->NextSiblingElement("Program"))
			{
				std::vector<std::string> oneProg;
				std::vector<ENode*> fullProg;
				for (auto ln = e->FirstChildElement("sexp"); ln != nullptr; ln = ln->NextSiblingElement("sexp"))
				{
					//std::cerr << ln->GetText() << std::endl;
					//oneProg.emplace_back(ln->GetText());
					std::string a1 = ln->GetText();
					std::string a2 = ln->GetText();
					auto en = new ENode(a1, a2);
					fullProg.push_back(en);
				}

				auto v = Evaluator::EvaluatePrograms(fullProg);
				std::cerr << v[0] << " " << v[1] << " " << v[2] << std::endl;
				fileScore << v[0] << " " << v[1] << " " << v[2] << std::endl;
				for (auto n : fullProg)
					delete n;
			}
			fileScore.close();
		}
	};
}


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

#ifndef GRAPH_BLOCKMODEL_MCMC_HH
#define GRAPH_BLOCKMODEL_MCMC_HH

#include "config.h"

#include <vector>

#include "graph_tool.hh"
#include "../support/graph_state.hh"
#include "graph_blockmodel_util.hh"
#include <boost/mpl/vector.hpp>

namespace graph_tool
{
using namespace boost;
using namespace std;

#define MCMC_BLOCK_STATE_params(State)                                         \
    ((__class__,&, mpl::vector<python::object>, 1))                            \
    ((state, &, State&, 0))                                                    \
    ((E,, size_t, 0))                                                          \
    ((vlist,&, std::vector<size_t>&, 0))                                       \
    ((beta,, double, 0))                                                       \
    ((c,, double, 0))                                                          \
    ((d,, double, 0))                                                          \
    ((entropy_args,, entropy_args_t, 0))                                       \
    ((allow_vacate,, bool, 0))                                                 \
    ((parallel,, bool, 0))                                                     \
    ((sequential,, bool, 0))                                                   \
    ((deterministic,, bool, 0))                                                \
    ((verbose,, bool, 0))                                                      \
    ((niter,, size_t, 0))

template <class State>
struct MCMC
{
    GEN_STATE_BASE(MCMCBlockStateBase, MCMC_BLOCK_STATE_params(State))

    template <class... Ts>
    class MCMCBlockState
        : public MCMCBlockStateBase<Ts...>
    {
    public:
        GET_PARAMS_USING(MCMCBlockStateBase<Ts...>,
                         MCMC_BLOCK_STATE_params(State))
        GET_PARAMS_TYPEDEF(Ts, MCMC_BLOCK_STATE_params(State))

        template <class... ATs,
                  typename std::enable_if_t<sizeof...(ATs) ==
                                            sizeof...(Ts)>* = nullptr>
        MCMCBlockState(ATs&&... as)
           : MCMCBlockStateBase<Ts...>(as...),
            _g(_state._g),
            _m_entries(num_vertices(_state._bg))
        {
            _state.init_mcmc(_c,
                             (_entropy_args.partition_dl ||
                              _entropy_args.degree_dl ||
                              _entropy_args.edges_dl));
        }

        typename state_t::g_t& _g;
        typename state_t::m_entries_t _m_entries;
        size_t _null_move = null_group;

        size_t node_state(size_t v)
        {
            return _state._b[v];
        }

        bool skip_node(size_t v)
        {
            return _state.node_weight(v) == 0;
        }

        size_t node_weight(size_t v)
        {
            return _state.node_weight(v);
        }

        template <class RNG>
        size_t move_proposal(size_t v, RNG& rng)
        {
            auto r = _state._b[v];

            if (!_allow_vacate && _state.is_last(v))
                return null_group;

            size_t s = _state.sample_block(v, _c, _d, rng);

            if (!_state.allow_move(r, s))
                return null_group;

            return s;
        }

        std::tuple<double, double>
        virtual_move_dS(size_t v, size_t nr)
        {
            size_t r = _state._b[v];
            if (r == nr)
                return std::make_tuple(0., 0.);

            double dS = _state.virtual_move(v, r, nr, _entropy_args,
                                            _m_entries);
            double a = 0;
            if (!std::isinf(_beta))
            {
                double pf = _state.get_move_prob(v, r, nr, _c, _d, false,
                                                 _m_entries);
                double pb = _state.get_move_prob(v, nr, r, _c, _d, true,
                                                 _m_entries);
                a = log(pb) - log(pf);
            }
            return std::make_tuple(dS, a);
        }

        void perform_move(size_t v, size_t nr)
        {
            _state.move_vertex(v, nr);
        }

        bool is_deterministic()
        {
            return _deterministic;
        }

        bool is_sequential()
        {
            return _sequential;
        }

        auto& get_vlist()
        {
            return _vlist;
        }

        double get_beta()
        {
            return _beta;
        }

        size_t get_niter()
        {
            return _niter;
        }

        void step(size_t, size_t)
        {
        }
    };
};

} // graph_tool namespace

#endif //GRAPH_BLOCKMODEL_MCMC_HH


//  (C) Copyright John Maddock 2006.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP
#define BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP

#ifdef _MSC_VER
#pragma once
#endif
#include <boost/math/tools/complex.hpp> // test for multiprecision types.

#include <iostream>
#include <utility>
#include <boost/config/no_tr1/cmath.hpp>
#include <stdexcept>

#include <boost/math/tools/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable: 4512)
#endif
#include <boost/math/tools/tuple.hpp>
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

#include <boost/math/special_functions/sign.hpp>
#include <boost/math/special_functions/next.hpp>
#include <boost/math/tools/toms748_solve.hpp>
#include <boost/math/policies/error_handling.hpp>

namespace boost {
namespace math {
namespace tools {

namespace detail {

namespace dummy {

   template<int n, class T>
   typename T::value_type get(const T&) BOOST_MATH_NOEXCEPT(T);
}

template <class Tuple, class T>
void unpack_tuple(const Tuple& t, T& a, T& b) BOOST_MATH_NOEXCEPT(T)
{
   using dummy::get;
   // Use ADL to find the right overload for get:
   a = get<0>(t);
   b = get<1>(t);
}
template <class Tuple, class T>
void unpack_tuple(const Tuple& t, T& a, T& b, T& c) BOOST_MATH_NOEXCEPT(T)
{
   using dummy::get;
   // Use ADL to find the right overload for get:
   a = get<0>(t);
   b = get<1>(t);
   c = get<2>(t);
}

template <class Tuple, class T>
inline void unpack_0(const Tuple& t, T& val) BOOST_MATH_NOEXCEPT(T)
{
   using dummy::get;
   // Rely on ADL to find the correct overload of get:
   val = get<0>(t);
}

template <class T, class U, class V>
inline void unpack_tuple(const std::pair<T, U>& p, V& a, V& b) BOOST_MATH_NOEXCEPT(T)
{
   a = p.first;
   b = p.second;
}
template <class T, class U, class V>
inline void unpack_0(const std::pair<T, U>& p, V& a) BOOST_MATH_NOEXCEPT(T)
{
   a = p.first;
}

template <class F, class T>
void handle_zero_derivative(F f,
   T& last_f0,
   const T& f0,
   T& delta,
   T& result,
   T& guess,
   const T& min,
   const T& max) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   if (last_f0 == 0)
   {
      // this must be the first iteration, pretend that we had a
      // previous one at either min or max:
      if (result == min)
      {
         guess = max;
      }
      else
      {
         guess = min;
      }
      unpack_0(f(guess), last_f0);
      delta = guess - result;
   }
   if (sign(last_f0) * sign(f0) < 0)
   {
      // we've crossed over so move in opposite direction to last step:
      if (delta < 0)
      {
         delta = (result - min) / 2;
      }
      else
      {
         delta = (result - max) / 2;
      }
   }
   else
   {
      // move in same direction as last step:
      if (delta < 0)
      {
         delta = (result - max) / 2;
      }
      else
      {
         delta = (result - min) / 2;
      }
   }
}

} // namespace

template <class F, class T, class Tol, class Policy>
std::pair<T, T> bisect(F f, T min, T max, Tol tol, boost::uintmax_t& max_iter, const Policy& pol) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<Policy>::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   T fmin = f(min);
   T fmax = f(max);
   if (fmin == 0)
   {
      max_iter = 2;
      return std::make_pair(min, min);
   }
   if (fmax == 0)
   {
      max_iter = 2;
      return std::make_pair(max, max);
   }

   //
   // Error checking:
   //
   static const char* function = "boost::math::tools::bisect<%1%>";
   if (min >= max)
   {
      return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function,
         "Arguments in wrong order in boost::math::tools::bisect (first arg=%1%)", min, pol));
   }
   if (fmin * fmax >= 0)
   {
      return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function,
         "No change of sign in boost::math::tools::bisect, either there is no root to find, or there are multiple roots in the interval (f(min) = %1%).", fmin, pol));
   }

   //
   // Three function invocations so far:
   //
   boost::uintmax_t count = max_iter;
   if (count < 3)
      count = 0;
   else
      count -= 3;

   while (count && (0 == tol(min, max)))
   {
      T mid = (min + max) / 2;
      T fmid = f(mid);
      if ((mid == max) || (mid == min))
         break;
      if (fmid == 0)
      {
         min = max = mid;
         break;
      }
      else if (sign(fmid) * sign(fmin) < 0)
      {
         max = mid;
      }
      else
      {
         min = mid;
         fmin = fmid;
      }
      --count;
   }

   max_iter -= count;

#ifdef BOOST_MATH_INSTRUMENT
   std::cout << "Bisection iteration, final count = " << max_iter << std::endl;

   static boost::uintmax_t max_count = 0;
   if (max_iter > max_count)
   {
      max_count = max_iter;
      std::cout << "Maximum iterations: " << max_iter << std::endl;
   }
#endif

   return std::make_pair(min, max);
}

template <class F, class T, class Tol>
inline std::pair<T, T> bisect(F f, T min, T max, Tol tol, boost::uintmax_t& max_iter)  BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   return bisect(f, min, max, tol, max_iter, policies::policy<>());
}

template <class F, class T, class Tol>
inline std::pair<T, T> bisect(F f, T min, T max, Tol tol) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   boost::uintmax_t m = (std::numeric_limits<boost::uintmax_t>::max)();
   return bisect(f, min, max, tol, m, policies::policy<>());
}

template <class F, class T>
T newton_raphson_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   BOOST_MATH_STD_USING

   static const char* function = "boost::math::tools::newton_raphson_iterate<%1%>";
   if (min >= max)
   {
      return policies::raise_evaluation_error(function, "Range arguments in wrong order in boost::math::tools::newton_raphson_iterate(first arg=%1%)", min, boost::math::policies::policy<>());
   }

   T f0(0), f1, last_f0(0);
   T result = guess;

   T factor = static_cast<T>(ldexp(1.0, 1 - digits));
   T delta = tools::max_value<T>();
   T delta1 = tools::max_value<T>();
   T delta2 = tools::max_value<T>();

   //
   // We use these to sanity check that we do actually bracket a root,
   // we update these to the function value when we update the endpoints
   // of the range.  Then, provided at some point we update both endpoints
   // checking that max_range_f * min_range_f <= 0 verifies there is a root
   // to be found somewhere.  Note that if there is no root, and we approach
   // a local minima, then the derivative will go to zero, and hence the next
   // step will jump out of bounds (or at least past the minima), so this
   // check *should* happen in pathological cases.
   //
   T max_range_f = 0;
   T min_range_f = 0;

   boost::uintmax_t count(max_iter);

#ifdef BOOST_MATH_INSTRUMENT
   std::cout << "Newton_raphson_iterate, guess = " << guess << ", min = " << min << ", max = " << max
      << ", digits = " << digits << ", max_iter = " << max_iter << std::endl;
#endif

   do {
      last_f0 = f0;
      delta2 = delta1;
      delta1 = delta;
      detail::unpack_tuple(f(result), f0, f1);
      --count;
      if (0 == f0)
         break;
      if (f1 == 0)
      {
         // Oops zero derivative!!!
#ifdef BOOST_MATH_INSTRUMENT
         std::cout << "Newton iteration, zero derivative found!" << std::endl;
#endif
         detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max);
      }
      else
      {
         delta = f0 / f1;
      }
#ifdef BOOST_MATH_INSTRUMENT
      std::cout << "Newton iteration " << max_iter - count << ", delta = " << delta << std::endl;
#endif
      if (fabs(delta * 2) > fabs(delta2))
      {
         // Last two steps haven't converged.
         T shift = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
         if ((result != 0) && (fabs(shift) > fabs(result)))
         {
            delta = sign(delta) * fabs(result) * 1.1f; // Protect against huge jumps!
            //delta = sign(delta) * result; // Protect against huge jumps! Failed for negative result. https://github.com/boostorg/math/issues/216
         }
         else
            delta = shift;
         // reset delta1/2 so we don't take this branch next time round:
         delta1 = 3 * delta;
         delta2 = 3 * delta;
      }
      guess = result;
      result -= delta;
      if (result <= min)
      {
         delta = 0.5F * (guess - min);
         result = guess - delta;
         if ((result == min) || (result == max))
            break;
      }
      else if (result >= max)
      {
         delta = 0.5F * (guess - max);
         result = guess - delta;
         if ((result == min) || (result == max))
            break;
      }
      // Update brackets:
      if (delta > 0)
      {
         max = guess;
         max_range_f = f0;
      }
      else
      {
         min = guess;
         min_range_f = f0;
      }
      //
      // Sanity check that we bracket the root:
      //
      if (max_range_f * min_range_f > 0)
      {
         return policies::raise_evaluation_error(function, "There appears to be no root to be found in boost::math::tools::newton_raphson_iterate, perhaps we have a local minima near current best guess of %1%", guess, boost::math::policies::policy<>());
      }
   }while(count && (fabs(result * factor) < fabs(delta)));

   max_iter -= count;

#ifdef BOOST_MATH_INSTRUMENT
   std::cout << "Newton Raphson final iteration count = " << max_iter << std::endl;

   static boost::uintmax_t max_count = 0;
   if (max_iter > max_count)
   {
      max_count = max_iter;
      // std::cout << "Maximum iterations: " << max_iter << std::endl;
      // Puzzled what this tells us, so commented out for now?
   }
#endif

   return result;
}

template <class F, class T>
inline T newton_raphson_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   boost::uintmax_t m = (std::numeric_limits<boost::uintmax_t>::max)();
   return newton_raphson_iterate(f, guess, min, max, digits, m);
}

namespace detail {

   struct halley_step
   {
      template <class T>
      static T step(const T& /*x*/, const T& f0, const T& f1, const T& f2) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T))
      {
         using std::fabs;
         T denom = 2 * f0;
         T num = 2 * f1 - f0 * (f2 / f1);
         T delta;

         BOOST_MATH_INSTRUMENT_VARIABLE(denom);
         BOOST_MATH_INSTRUMENT_VARIABLE(num);

         if ((fabs(num) < 1) && (fabs(denom) >= fabs(num) * tools::max_value<T>()))
         {
            // possible overflow, use Newton step:
            delta = f0 / f1;
         }
         else
            delta = denom / num;
         return delta;
      }
   };

   template <class F, class T>
   T bracket_root_towards_min(F f, T guess, const T& f0, T& min, T& max, boost::uintmax_t& count) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())));

   template <class F, class T>
   T bracket_root_towards_max(F f, T guess, const T& f0, T& min, T& max, boost::uintmax_t& count) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
   {
      using std::fabs;
      //
      // Move guess towards max until we bracket the root, updating min and max as we go:
      //
      T guess0 = guess;
      T multiplier = 2;
      T f_current = f0;
      if (fabs(min) < fabs(max))
      {
         while (--count && ((f_current < 0) == (f0 < 0)))
         {
            min = guess;
            guess *= multiplier;
            if (guess > max)
            {
               guess = max;
               f_current = -f_current;  // There must be a change of sign!
               break;
            }
            multiplier *= 2;
            unpack_0(f(guess), f_current);
         }
      }
      else
      {
         //
         // If min and max are negative we have to divide to head towards max:
         //
         while (--count && ((f_current < 0) == (f0 < 0)))
         {
            min = guess;
            guess /= multiplier;
            if (guess > max)
            {
               guess = max;
               f_current = -f_current;  // There must be a change of sign!
               break;
            }
            multiplier *= 2;
            unpack_0(f(guess), f_current);
         }
      }

      if (count)
      {
         max = guess;
         if (multiplier > 16)
            return (guess0 - guess) + bracket_root_towards_min(f, guess, f_current, min, max, count);
      }
      return guess0 - (max + min) / 2;
   }

   template <class F, class T>
   T bracket_root_towards_min(F f, T guess, const T& f0, T& min, T& max, boost::uintmax_t& count) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
   {
      using std::fabs;
      //
      // Move guess towards min until we bracket the root, updating min and max as we go:
      //
      T guess0 = guess;
      T multiplier = 2;
      T f_current = f0;

      if (fabs(min) < fabs(max))
      {
         while (--count && ((f_current < 0) == (f0 < 0)))
         {
            max = guess;
            guess /= multiplier;
            if (guess < min)
            {
               guess = min;
               f_current = -f_current;  // There must be a change of sign!
               break;
            }
            multiplier *= 2;
            unpack_0(f(guess), f_current);
         }
      }
      else
      {
         //
         // If min and max are negative we have to multiply to head towards min:
         //
         while (--count && ((f_current < 0) == (f0 < 0)))
         {
            max = guess;
            guess *= multiplier;
            if (guess < min)
            {
               guess = min;
               f_current = -f_current;  // There must be a change of sign!
               break;
            }
            multiplier *= 2;
            unpack_0(f(guess), f_current);
         }
      }

      if (count)
      {
         min = guess;
         if (multiplier > 16)
            return (guess0 - guess) + bracket_root_towards_max(f, guess, f_current, min, max, count);
      }
      return guess0 - (max + min) / 2;
   }

   template <class Stepper, class F, class T>
   T second_order_root_finder(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
   {
      BOOST_MATH_STD_USING

#ifdef BOOST_MATH_INSTRUMENT
        std::cout << "Second order root iteration, guess = " << guess << ", min = " << min << ", max = " << max
        << ", digits = " << digits << ", max_iter = " << max_iter << std::endl;
#endif
      static const char* function = "boost::math::tools::halley_iterate<%1%>";
      if (min >= max)
      {
         return policies::raise_evaluation_error(function, "Range arguments in wrong order in boost::math::tools::halley_iterate(first arg=%1%)", min, boost::math::policies::policy<>());
      }

      T f0(0), f1, f2;
      T result = guess;

      T factor = ldexp(static_cast<T>(1.0), 1 - digits);
      T delta = (std::max)(T(10000000 * guess), T(10000000));  // arbitrarily large delta
      T last_f0 = 0;
      T delta1 = delta;
      T delta2 = delta;
      bool out_of_bounds_sentry = false;

   #ifdef BOOST_MATH_INSTRUMENT
      std::cout << "Second order root iteration, limit = " << factor << std::endl;
   #endif

      //
      // We use these to sanity check that we do actually bracket a root,
      // we update these to the function value when we update the endpoints
      // of the range.  Then, provided at some point we update both endpoints
      // checking that max_range_f * min_range_f <= 0 verifies there is a root
      // to be found somewhere.  Note that if there is no root, and we approach
      // a local minima, then the derivative will go to zero, and hence the next
      // step will jump out of bounds (or at least past the minima), so this
      // check *should* happen in pathological cases.
      //
      T max_range_f = 0;
      T min_range_f = 0;

      boost::uintmax_t count(max_iter);

      do {
         last_f0 = f0;
         delta2 = delta1;
         delta1 = delta;
         detail::unpack_tuple(f(result), f0, f1, f2);
         --count;

         BOOST_MATH_INSTRUMENT_VARIABLE(f0);
         BOOST_MATH_INSTRUMENT_VARIABLE(f1);
         BOOST_MATH_INSTRUMENT_VARIABLE(f2);

         if (0 == f0)
            break;
         if (f1 == 0)
         {
            // Oops zero derivative!!!
   #ifdef BOOST_MATH_INSTRUMENT
            std::cout << "Second order root iteration, zero derivative found!" << std::endl;
   #endif
            detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max);
         }
         else
         {
            if (f2 != 0)
            {
               delta = Stepper::step(result, f0, f1, f2);
               if (delta * f1 / f0 < 0)
               {
                  // Oh dear, we have a problem as Newton and Halley steps
                  // disagree about which way we should move.  Probably
                  // there is cancelation error in the calculation of the
                  // Halley step, or else the derivatives are so small
                  // that their values are basically trash.  We will move
                  // in the direction indicated by a Newton step, but
                  // by no more than twice the current guess value, otherwise
                  // we can jump way out of bounds if we're not careful.
                  // See https://svn.boost.org/trac/boost/ticket/8314.
                  delta = f0 / f1;
                  if (fabs(delta) > 2 * fabs(guess))
                     delta = (delta < 0 ? -1 : 1) * 2 * fabs(guess);
               }
            }
            else
               delta = f0 / f1;
         }
   #ifdef BOOST_MATH_INSTRUMENT
         std::cout << "Second order root iteration, delta = " << delta << std::endl;
   #endif
         T convergence = fabs(delta / delta2);
         if ((convergence > 0.8) && (convergence < 2))
         {
            // last two steps haven't converged.
            delta = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
            if ((result != 0) && (fabs(delta) > result))
               delta = sign(delta) * fabs(result) * 0.9f; // protect against huge jumps!
            // reset delta2 so that this branch will *not* be taken on the
            // next iteration:
            delta2 = delta * 3;
            delta1 = delta * 3;
            BOOST_MATH_INSTRUMENT_VARIABLE(delta);
         }
         guess = result;
         result -= delta;
         BOOST_MATH_INSTRUMENT_VARIABLE(result);

         // check for out of bounds step:
         if (result < min)
         {
            T diff = ((fabs(min) < 1) && (fabs(result) > 1) && (tools::max_value<T>() / fabs(result) < fabs(min)))
               ? T(1000)
               : (fabs(min) < 1) && (fabs(tools::max_value<T>() * min) < fabs(result))
               ? ((min < 0) != (result < 0)) ? -tools::max_value<T>() : tools::max_value<T>() : T(result / min);
            if (fabs(diff) < 1)
               diff = 1 / diff;
            if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
            {
               // Only a small out of bounds step, lets assume that the result
               // is probably approximately at min:
               delta = 0.99f * (guess - min);
               result = guess - delta;
               out_of_bounds_sentry = true; // only take this branch once!
            }
            else
            {
               if (fabs(float_distance(min, max)) < 2)
               {
                  result = guess = (min + max) / 2;
                  break;
               }
               delta = bracket_root_towards_min(f, guess, f0, min, max, count);
               result = guess - delta;
               guess = min;
               continue;
            }
         }
         else if (result > max)
         {
            T diff = ((fabs(max) < 1) && (fabs(result) > 1) && (tools::max_value<T>() / fabs(result) < fabs(max))) ? T(1000) : T(result / max);
            if (fabs(diff) < 1)
               diff = 1 / diff;
            if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
            {
               // Only a small out of bounds step, lets assume that the result
               // is probably approximately at min:
               delta = 0.99f * (guess - max);
               result = guess - delta;
               out_of_bounds_sentry = true; // only take this branch once!
            }
            else
            {
               if (fabs(float_distance(min, max)) < 2)
               {
                  result = guess = (min + max) / 2;
                  break;
               }
               delta = bracket_root_towards_max(f, guess, f0, min, max, count);
               result = guess - delta;
               guess = min;
               continue;
            }
         }
         // update brackets:
         if (delta > 0)
         {
            max = guess;
            max_range_f = f0;
         }
         else
         {
            min = guess;
            min_range_f = f0;
         }
         //
         // Sanity check that we bracket the root:
         //
         if (max_range_f * min_range_f > 0)
         {
            return policies::raise_evaluation_error(function, "There appears to be no root to be found in boost::math::tools::newton_raphson_iterate, perhaps we have a local minima near current best guess of %1%", guess, boost::math::policies::policy<>());
         }
      } while(count && (fabs(result * factor) < fabs(delta)));

      max_iter -= count;

   #ifdef BOOST_MATH_INSTRUMENT
      std::cout << "Second order root finder, final iteration count = " << max_iter << std::endl;
   #endif

      return result;
   }
} // T second_order_root_finder

template <class F, class T>
T halley_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   return detail::second_order_root_finder<detail::halley_step>(f, guess, min, max, digits, max_iter);
}

template <class F, class T>
inline T halley_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   boost::uintmax_t m = (std::numeric_limits<boost::uintmax_t>::max)();
   return halley_iterate(f, guess, min, max, digits, m);
}

namespace detail {

   struct schroder_stepper
   {
      template <class T>
      static T step(const T& x, const T& f0, const T& f1, const T& f2) BOOST_NOEXCEPT_IF(BOOST_MATH_IS_FLOAT(T))
      {
         using std::fabs;
         T ratio = f0 / f1;
         T delta;
         if ((x != 0) && (fabs(ratio / x) < 0.1))
         {
            delta = ratio + (f2 / (2 * f1)) * ratio * ratio;
            // check second derivative doesn't over compensate:
            if (delta * ratio < 0)
               delta = ratio;
         }
         else
            delta = ratio;  // fall back to Newton iteration.
         return delta;
      }
   };

}

template <class F, class T>
T schroder_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   return detail::second_order_root_finder<detail::schroder_stepper>(f, guess, min, max, digits, max_iter);
}

template <class F, class T>
inline T schroder_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   boost::uintmax_t m = (std::numeric_limits<boost::uintmax_t>::max)();
   return schroder_iterate(f, guess, min, max, digits, m);
}
//
// These two are the old spelling of this function, retained for backwards compatibility just in case:
//
template <class F, class T>
T schroeder_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   return detail::second_order_root_finder<detail::schroder_stepper>(f, guess, min, max, digits, max_iter);
}

template <class F, class T>
inline T schroeder_iterate(F f, T guess, T min, T max, int digits) BOOST_NOEXCEPT_IF(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
{
   boost::uintmax_t m = (std::numeric_limits<boost::uintmax_t>::max)();
   return schroder_iterate(f, guess, min, max, digits, m);
}

#ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS
/*
   * Why do we set the default maximum number of iterations to the number of digits in the type?
   * Because for double roots, the number of digits increases linearly with the number of iterations,
   * so this default should recover full precision even in this somewhat pathological case.
   * For isolated roots, the problem is so rapidly convergent that this doesn't matter at all.
   */
template<class Complex, class F>
Complex complex_newton(F g, Complex guess, int max_iterations = std::numeric_limits<typename Complex::value_type>::digits)
{
   typedef typename Complex::value_type Real;
   using std::norm;
   using std::abs;
   using std::max;
   // z0, z1, and z2 cannot be the same, in case we immediately need to resort to Muller's Method:
   Complex z0 = guess + Complex(1, 0);
   Complex z1 = guess + Complex(0, 1);
   Complex z2 = guess;

   do {
      auto pair = g(z2);
      if (norm(pair.second) == 0)
      {
         // Muller's method. Notation follows Numerical Recipes, 9.5.2:
         Complex q = (z2 - z1) / (z1 - z0);
         auto P0 = g(z0);
         auto P1 = g(z1);
         Complex qp1 = static_cast<Complex>(1) + q;
         Complex A = q * (pair.first - qp1 * P1.first + q * P0.first);

         Complex B = (static_cast<Complex>(2) * q + static_cast<Complex>(1)) * pair.first - qp1 * qp1 * P1.first + q * q * P0.first;
         Complex C = qp1 * pair.first;
         Complex rad = sqrt(B * B - static_cast<Complex>(4) * A * C);
         Complex denom1 = B + rad;
         Complex denom2 = B - rad;
         Complex correction = (z1 - z2) * static_cast<Complex>(2) * C;
         if (norm(denom1) > norm(denom2))
         {
            correction /= denom1;
         }
         else
         {
            correction /= denom2;
         }

         z0 = z1;
         z1 = z2;
         z2 = z2 + correction;
      }
      else
      {
         z0 = z1;
         z1 = z2;
         z2 = z2 - (pair.first / pair.second);
      }

      // See: https://math.stackexchange.com/questions/3017766/constructing-newton-iteration-converging-to-non-root
      // If f' is continuous, then convergence of x_n -> x* implies f(x*) = 0.
      // This condition approximates this convergence condition by requiring three consecutive iterates to be clustered.
      Real tol = (max)(abs(z2) * std::numeric_limits<Real>::epsilon(), std::numeric_limits<Real>::epsilon());
      bool real_close = abs(z0.real() - z1.real()) < tol && abs(z0.real() - z2.real()) < tol && abs(z1.real() - z2.real()) < tol;
      bool imag_close = abs(z0.imag() - z1.imag()) < tol && abs(z0.imag() - z2.imag()) < tol && abs(z1.imag() - z2.imag()) < tol;
      if (real_close && imag_close)
      {
         return z2;
      }

   } while (max_iterations--);

   // The idea is that if we can get abs(f) < eps, we should, but if we go through all these iterations
   // and abs(f) < sqrt(eps), then roundoff error simply does not allow that we can evaluate f to < eps
   // This is somewhat awkward as it isn't scale invariant, but using the Daubechies coefficient example code,
   // I found this condition generates correct roots, whereas the scale invariant condition discussed here:
   // https://scicomp.stackexchange.com/questions/30597/defining-a-condition-number-and-termination-criteria-for-newtons-method
   // allows nonroots to be passed off as roots.
   auto pair = g(z2);
   if (abs(pair.first) < sqrt(std::numeric_limits<Real>::epsilon()))
   {
      return z2;
   }

   return { std::numeric_limits<Real>::quiet_NaN(),
            std::numeric_limits<Real>::quiet_NaN() };
}
#endif

#if !defined(BOOST_NO_CXX17_IF_CONSTEXPR)
// https://stackoverflow.com/questions/48979861/numerically-stable-method-for-solving-quadratic-equations/50065711
namespace detail
{
#if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
inline float fma_workaround(float x, float y, float z) { return ::fmaf(x, y, z); }
inline double fma_workaround(double x, double y, double z) { return ::fma(x, y, z); }
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
inline long double fma_workaround(long double x, long double y, long double z) { return ::fmal(x, y, z); }
#endif
#endif
template<class T>
inline T discriminant(T const& a, T const& b, T const& c)
{
   T w = 4 * a * c;
#if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
   T e = fma_workaround(-c, 4 * a, w);
   T f = fma_workaround(b, b, -w);
#else
   T e = std::fma(-c, 4 * a, w);
   T f = std::fma(b, b, -w);
#endif
   return f + e;
}

template<class T>
std::pair<T, T> quadratic_roots_imp(T const& a, T const& b, T const& c)
{
#if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
   using boost::math::copysign;
#else
   using std::copysign;
#endif
   using std::sqrt;
   if constexpr (std::is_floating_point<T>::value)
   {
      T nan = std::numeric_limits<T>::quiet_NaN();
      if (a == 0)
      {
         if (b == 0 && c != 0)
         {
            return std::pair<T, T>(nan, nan);
         }
         else if (b == 0 && c == 0)
         {
            return std::pair<T, T>(0, 0);
         }
         return std::pair<T, T>(-c / b, -c / b);
      }
      if (b == 0)
      {
         T x0_sq = -c / a;
         if (x0_sq < 0) {
            return std::pair<T, T>(nan, nan);
         }
         T x0 = sqrt(x0_sq);
         return std::pair<T, T>(-x0, x0);
      }
      T discriminant = detail::discriminant(a, b, c);
      // Is there a sane way to flush very small negative values to zero?
      // If there is I don't know of it.
      if (discriminant < 0)
      {
         return std::pair<T, T>(nan, nan);
      }
      T q = -(b + copysign(sqrt(discriminant), b)) / T(2);
      T x0 = q / a;
      T x1 = c / q;
      if (x0 < x1)
      {
         return std::pair<T, T>(x0, x1);
      }
      return std::pair<T, T>(x1, x0);
   }
   else if constexpr (boost::math::tools::is_complex_type<T>::value)
   {
      typename T::value_type nan = std::numeric_limits<typename T::value_type>::quiet_NaN();
      if (a.real() == 0 && a.imag() == 0)
      {
         using std::norm;
         if (b.real() == 0 && b.imag() && norm(c) != 0)
         {
            return std::pair<T, T>({ nan, nan }, { nan, nan });
         }
         else if (b.real() == 0 && b.imag() && c.real() == 0 && c.imag() == 0)
         {
            return std::pair<T, T>({ 0,0 }, { 0,0 });
         }
         return std::pair<T, T>(-c / b, -c / b);
      }
      if (b.real() == 0 && b.imag() == 0)
      {
         T x0_sq = -c / a;
         T x0 = sqrt(x0_sq);
         return std::pair<T, T>(-x0, x0);
      }
      // There's no fma for complex types:
      T discriminant = b * b - T(4) * a * c;
      T q = -(b + sqrt(discriminant)) / T(2);
      return std::pair<T, T>(q / a, c / q);
   }
   else // Most likely the type is a boost.multiprecision.
   {    //There is no fma for multiprecision, and in addition it doesn't seem to be useful, so revert to the naive computation.
      T nan = std::numeric_limits<T>::quiet_NaN();
      if (a == 0)
      {
         if (b == 0 && c != 0)
         {
            return std::pair<T, T>(nan, nan);
         }
         else if (b == 0 && c == 0)
         {
            return std::pair<T, T>(0, 0);
         }
         return std::pair<T, T>(-c / b, -c / b);
      }
      if (b == 0)
      {
         T x0_sq = -c / a;
         if (x0_sq < 0) {
            return std::pair<T, T>(nan, nan);
         }
         T x0 = sqrt(x0_sq);
         return std::pair<T, T>(-x0, x0);
      }
      T discriminant = b * b - 4 * a * c;
      if (discriminant < 0)
      {
         return std::pair<T, T>(nan, nan);
      }
      T q = -(b + copysign(sqrt(discriminant), b)) / T(2);
      T x0 = q / a;
      T x1 = c / q;
      if (x0 < x1)
      {
         return std::pair<T, T>(x0, x1);
      }
      return std::pair<T, T>(x1, x0);
   }
}
}  // namespace detail

template<class T1, class T2 = T1, class T3 = T1>
inline std::pair<typename tools::promote_args<T1, T2, T3>::type, typename tools::promote_args<T1, T2, T3>::type> quadratic_roots(T1 const& a, T2 const& b, T3 const& c)
{
   typedef typename tools::promote_args<T1, T2, T3>::type value_type;
   return detail::quadratic_roots_imp(static_cast<value_type>(a), static_cast<value_type>(b), static_cast<value_type>(c));
}

#endif

} // namespace tools
} // namespace math
} // namespace boost

#endif // BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP


#include <sstream>

#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>

#include <aiRecord.h>
#include <waveformRecord.h>
#include <menuFtype.h>

#include "flame.h"

void VIOCObserver::view(const ElementVoid* elem, const StateBase* state)
{
    if(!last.get())
        last.reset(state->clone());
    else
        last->assign(*state);
}

struct SimDevMeasScalar : public SimDev
{
    std::string param;
    unsigned param_index; //!< index of named parameter in StateBase::getArray

    unsigned ndim;
    size_t dim[2];

    VIOCObserver *measure;
};

static
long measure_init_common(dbCommon *prec, const char *link)
{
    try {
        // "simname elementname param"  implies inst==0 and ndim==0
        // "simname elementname param[i]"
        // "simname elementname param[i,j]"
        // "simname elementname[inst] param"
        // "simname elementname[inst] param[i]"
        // "simname elementname[inst] param[i,j]"
        // matches between 3 and 6 groups
        static boost::regex linkpat("(\\S+) ([^\\s\\[]+)(?:\\[(\\d+)\\])? ([^\\s\\[]+)(?:\\[(\\d+)(?:,(\\d+))?\\])?");

        boost::cmatch M;
        if(!boost::regex_match(link, M, linkpat))
            throw std::runtime_error("Bad link string");

        flame::auto_ptr<SimDevMeasScalar> priv(new SimDevMeasScalar);
        priv->prec = (dbCommon*)prec;

        if(!find(SimGlobal.sims, M.str(1), priv->sim))
            throw std::runtime_error("No such simulation instance");

        size_t inst = 0;
        if(M[3].matched)
            inst = boost::lexical_cast<size_t>(M.str(3));

        priv->ndim = 0;
        if(M[5].matched) {
            priv->ndim++;
            priv->dim[0] = boost::lexical_cast<size_t>(M.str(5));
        }
        if(M[6].matched) {
            priv->ndim++;
            priv->dim[1] = boost::lexical_cast<size_t>(M.str(6));
        }

        ElementVoid* elem = priv->sim->machine->find(M.str(2), inst);
        if(!elem)
            throw std::runtime_error("No such element");
        priv->element_index = elem->index;

        priv->measure = priv->sim->get_measure(elem->index);

        priv->param = M.str(4);

        unsigned idx;
        {
            Config empty;
            flame::auto_ptr<StateBase> state(priv->sim->machine->allocState(empty));

            for(idx=0; true; idx++) {
                StateBase::ArrayInfo info;
                if(!state->getArray(idx, info))
                    throw std::runtime_error("state has no parameter");

                if(info.name==priv->param) {
                    if(info.type!=StateBase::ArrayInfo::Double)
                        throw std::runtime_error("Requested parameter must be type Double");
                    else if(info.ndim!=priv->ndim)
                        throw std::runtime_error("Requested parameter has different cardinality");

                    priv->param_index = idx;
                    break;
                }
            }
        }

        prec->dpvt = priv.release();
        return 0;
    }catch(std::exception& e){
        fprintf(stderr, "%s: init error: %s\n", prec->name, e.what());
        return -1;
    }
}

static
long measure_init_ai(aiRecord *prec)
{
    return measure_init_common((dbCommon*)prec, prec->inp.value.instio.string);
}

static
long measure_read_ai(aiRecord *prec)
{
    TRY(SimDevMeasScalar) {
        Guard G(priv->sim->lock);
        bool valid = priv->sim->valid;

        double value = 0.0;

        {
            StateBase::ArrayInfo info;

            if(priv->measure->last.get() &&
               priv->measure->last->getArray(priv->param_index, info) &&
               priv->ndim==info.ndim &&
               info.inbounds(priv->dim))
            {
                double * const arr = info.get<double>(priv->dim);
                value = *arr;

            } else {
                valid = false;
            }

        }

        prec->val = value;
        prec->udf = 0;

        if(prec->aslo!=0.0) prec->val *= prec->aslo;
        prec->val += prec->aoff;

        if(prec->linr && prec->eslo!=0.0) prec->val = prec->val*prec->eslo + prec->eoff;

        if(!valid)
            (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);

        return 2;
    } CATCH_ALARM()
}

DSET6(ai, State, measure_init_ai, Sim::io_aftersim, measure_read_ai);


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestRandomShuffle
#include <boost/test/unit_test.hpp>

#include <set>
#include <iterator>

#include <boost/compute/system.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/algorithm/random_shuffle.hpp>
#include <boost/compute/container/vector.hpp>

#include "context_setup.hpp"

namespace bc = boost::compute;

BOOST_AUTO_TEST_CASE(shuffle_int_vector)
{
    bc::vector<int> vector(context);
    vector.push_back(1);
    vector.push_back(9);
    vector.push_back(19);
    vector.push_back(29);

    std::set<int> original_values;
    for(size_t i = 0; i < vector.size(); i++){
        original_values.insert(vector[i]);
    }
    BOOST_CHECK_EQUAL(original_values.size(), size_t(4));

    bc::random_shuffle(vector.begin(), vector.end(), queue);

    std::set<int> shuffled_values;
    bc::copy(
        vector.begin(),
        vector.end(),
        std::inserter(shuffled_values, shuffled_values.begin()),
        queue
    );
    BOOST_CHECK_EQUAL(shuffled_values.size(), size_t(4));
    BOOST_VERIFY(original_values == shuffled_values);
}

BOOST_AUTO_TEST_SUITE_END()


#include "utils.h"
#include "soft_clustering.h"
#include "hard_clustering.h"
#include <type_traits>
#include <unistd.h>
#include <iostream>
#include <omp.h>
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

using namespace std;
using boost::property_tree::ptree;
namespace qi = boost::spirit::qi;
namespace po = boost::program_options;

const double CUTOFF_LOGLIK = log(1e-6);
const double MAX_ZERO_LOGLIK = log(1e-3);

struct config {
  unsigned long int conf_num;
  string in_file;
  string algorithm;
  unsigned long int max_iter;
} my_config;

void read_config(string& fname, unsigned long int conf_num) {
  my_config.conf_num = conf_num;
  boost::property_tree::ptree pt;
  boost::property_tree::ini_parser::read_ini(fname, pt);
  string strconfnum = to_string(static_cast<long long>(conf_num));
  my_config.in_file = pt.get<string>(strconfnum + ".in_file");
  my_config.algorithm = pt.get<string>(strconfnum + ".algorithm");
  my_config.max_iter = pt.get<unsigned long int>(strconfnum + ".max_iter");
}

int main(int argc, char* argv[]) {
  try {
    po::options_description desc("Configuration");
    desc.add_options()("help", "produce help message")(
        "fname", po::value<string>()->required(), "Conf file")(
        "conf_num", po::value<unsigned long int>()->required(), "Conf num");

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
      cout << desc << "\n";
      return 0;
    }
    srand((unsigned)time(0));
    string fname = vm["fname"].as<string>();
    unsigned long int conf_num = vm["conf_num"].as<unsigned long int>();
    read_config(fname, conf_num);
    cout << "Config file is read.\n";

    vector<vector<double>> raw;
    boost::iostreams::mapped_file mmap(my_config.in_file.c_str(),
                                       boost::iostreams::mapped_file::readonly);
    auto f = mmap.const_data();
    auto l = f + mmap.size();
    bool ok =
        qi::phrase_parse(f, l, (qi::double_ % ',') % qi::eol, qi::blank, raw);
    if (ok)
      std::cout << "Input file : parse success\n";
    else
      std::cerr << "Input file : parse failed: '" << std::string(f, l) << "'\n";
    if (f != l)
      std::cerr << "Input file : trailing unparsed: '" << std::string(f, l)
                << "'\n";

    unsigned long int n_samples = raw.size();
    unsigned long int n_dims = raw[0].size();
    vector<unsigned long int> label(n_samples);
    vector<vector<double>> data;
    data.resize(n_samples);
    for (size_t i = 0; i < n_samples; i++) {
      label[i] = raw[i][n_dims - 1];
      data[i].resize(n_dims - 1);
      for (size_t j = 0; j < n_dims - 1; j++) data[i][j] = raw[i][j];
    }
    set<unsigned long int> unique_labels;
    unsigned long int lbl;
    for (size_t i = 0; i < n_samples; i++) unique_labels.insert(label[i]);
    unsigned long int k = unique_labels.size();
    unsigned long int max_iter = my_config.max_iter;
    cout << "Setting k=" << k << endl;
    if (my_config.algorithm.compare("KMeans") == 0) {
      // KMeans hc(data, label, max_iter, k);
      // hc.initialize_k_plus_plus();
      // hc.fit();
      // vector<double>& nmis = hc.get_nmis();
    } else if (my_config.algorithm.compare("AdaHardCluster") == 0) {
      AdaHardCluster hc(data, label, max_iter, k);
      hc.initialize_k_plus_plus();
      hc.fit();
      vector<double>& nmis = hc.get_nmis();
    } else if (my_config.algorithm.compare("GMM") == 0) {
      // for (size_t i = 0; i < n_samples; ++i)
      //   for (unsigned long int j = 0; j < n_dims; ++j) {
      //     if (data[i][j] == 0.0) data[i][j] = 1e-9;
      //     assert(data[i][j] > 0.0);
      //   }
      // GMM sc(data, label, max_iter, k);
      // sc.initialize_k_plus_plus();
      // sc.fit();
      // vector<double>& nmis = sc.get_nmis();
    } else if (my_config.algorithm.compare("AdaCluster") == 0) {
      // for (size_t i = 0; i < n_samples; ++i)
      //   for (unsigned long int j = 0; j < n_dims; ++j) {
      //     if (data[i][j] == 0.0) data[i][j] = 1e-9;
      //     assert(data[i][j] > 0.0);
      //   }
      AdaCluster sc(data, label, max_iter, k);
      sc.initialize_k_plus_plus();
      sc.fit();
      vector<double>& nmis = sc.get_nmis();
    } else {
      cerr << "Unknown model." << endl;
    }

  } catch (exception& e) {
    cerr << "error: " << e.what() << "\n";
    return 1;
  } catch (...) {
    cerr << "Exception of unknown type!\n";
  }
  return 0;
}


/// @file
///
/// A linear solver for parameters from the normal equations
///
/// @copyright (c) 2007 CSIRO
/// Australia Telescope National Facility (ATNF)
/// Commonwealth Scientific and Industrial Research Organisation (CSIRO)
/// PO Box 76, Epping NSW 1710, Australia
/// atnf-enquiries@csiro.au
///
/// This file is part of the ASKAP software distribution.
///
/// The ASKAP software distribution is free software: you can redistribute it
/// and/or modify it under the terms of the GNU General Public License as
/// published by the Free Software Foundation; either version 2 of the License,
/// or (at your option) any later version.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program; if not, write to the Free Software
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
///
/// @author Tim Cornwell <tim.cornwell@csiro.au>
///
#include <fitting/LinearSolver.h>

#include <askap/AskapError.h>
#include <profile/AskapProfiler.h>
#include <boost/config.hpp>

#include <casacore/casa/aips.h>
#include <casacore/casa/Arrays/Array.h>
#include <casacore/casa/Arrays/Matrix.h>
#include <casacore/casa/Arrays/Vector.h>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_linalg.h>

#include <askap/AskapLogging.h>
ASKAP_LOGGER(logger, ".linearsolver");

#include <iostream>

#include <string>
#include <map>

#include <cmath>
using std::abs;
using std::map;
using std::string;

namespace askap
{
  namespace scimath
  {
    BOOST_CONSTEXPR_OR_CONST double LinearSolver::KeepAllSingularValues;

    /// @brief Constructor
    /// @details Optionally, it is possible to limit the condition number of
    /// normal equation matrix to a given number.
    /// @param maxCondNumber maximum allowed condition number of the range
    /// of the normal equation matrix for the SVD algorithm. Effectively this
    /// puts the limit on the singular values, which are considered to be
    /// non-zero (all greater than the largest singular value divided by this
    /// condition number threshold). Default is 1e3. Put a negative number
    /// if you don't want to drop any singular values (may be a not very wise
    /// thing to do!). A very large threshold has the same effect. Zero
    /// threshold is not allowed and will cause an exception.
    LinearSolver::LinearSolver(double maxCondNumber) :
           itsMaxCondNumber(maxCondNumber)
    {
      ASKAPASSERT(itsMaxCondNumber!=0);
    };

    void LinearSolver::init()
    {
      resetNormalEquations();
    }

/// @brief test that all matrix elements are below tolerance by absolute value
/// @details This is a helper method to test all matrix elements
/// @param[in] matr matrix to test
/// @param[in] tolerance tolerance on the element absolute values
/// @return true if all elements are zero within the tolerance
bool LinearSolver::allMatrixElementsAreZeros(const casa::Matrix<double> &matr, const double tolerance)
{
  for (casa::uInt row = 0; row < matr.nrow(); ++row) {
       for (casa::uInt col = 0; col < matr.ncolumn(); ++col) {
            if (abs(matr(row,col)) > tolerance) {
                return false;
            }
       }
  }
  return true;
}

/// @brief extract an independent subset of parameters
/// @details This method analyses the normal equations and forms a subset of
/// parameters which can be solved for independently. Although the SVD is more than
/// capable of dealing with degeneracies, it is often too slow if the number of parameters is large.
/// This method essentially gives the solver a hint based on the structure of the equations
/// @param[in] names names for parameters to choose from
/// @param[in] tolerance tolerance on the matrix elements to decide whether they can be considered independent
/// @return names of parameters in this subset
std::vector<std::string> LinearSolver::getIndependentSubset(std::vector<std::string> &names, const double tolerance) const
{
   ASKAPTRACE("LinearSolver::getIndependentSubset");
   ASKAPDEBUGASSERT(names.size() > 0);
   std::vector<std::string> resultNames;
   resultNames.reserve(names.size());
   resultNames.push_back(names[0]);
   for (std::vector<std::string>::const_iterator ci = ++names.begin(); ci != names.end(); ++ci) {
        for (std::vector<std::string>::const_iterator ciRes = resultNames.begin(); ciRes != resultNames.end(); ++ciRes) {
             const casa::Matrix<double>& nm1 = normalEquations().normalMatrix(*ci, *ciRes);
             const casa::Matrix<double>& nm2 = normalEquations().normalMatrix(*ciRes, *ci);
             if (!allMatrixElementsAreZeros(nm1,tolerance) || !allMatrixElementsAreZeros(nm2,tolerance)) {
                 // this parameter (iterated in the outer loop) belongs to the same subset
                 resultNames.push_back(*ci);
                 break;
             }
        }
   }
   return resultNames;
}

/// @brief solve for a subset of parameters
/// @details This method is used in solveNormalEquations
/// @param[in] params parameters to be updated
/// @param[in] quality Quality of the solution
/// @param[in] names names of the parameters to solve for
std::pair<double,double>  LinearSolver::solveSubsetOfNormalEquations(Params &params, Quality& quality,
                   const std::vector<std::string> &names) const
{
    ASKAPTRACE("LinearSolver::solveSubsetOfNormalEquations");
    std::pair<double,double> result(0.,0.);

// Solving A^T Q^-1 V = (A^T Q^-1 A) P

    int nParameters = 0;

    std::vector<std::pair<string, int> > indices(names.size());
    {
      std::vector<std::pair<string, int> >::iterator it = indices.begin();
      for (vector<string>::const_iterator cit=names.begin(); cit!=names.end(); ++cit,++it)
      {
        ASKAPDEBUGASSERT(it != indices.end());
        it->second = nParameters;
        it->first = *cit;
        ASKAPLOG_DEBUG_STR(logger, "Processing "<<*cit<<" "<<nParameters);
        const casa::uInt newParameters = normalEquations().dataVector(*cit).nelements();
        nParameters += newParameters;
        ASKAPDEBUGASSERT((params.isFree(*cit) ? params.value(*cit).nelements() : newParameters) == newParameters);
      }
    }
    ASKAPLOG_DEBUG_STR(logger, "Done");
    ASKAPCHECK(nParameters>0, "No free parameters in a subset of normal equations");

    ASKAPDEBUGASSERT(indices.size() > 0);

    // Convert the normal equations to gsl format
    gsl_matrix * A = gsl_matrix_alloc (nParameters, nParameters);
    gsl_vector * B = gsl_vector_alloc (nParameters);
    gsl_vector * X = gsl_vector_alloc (nParameters);

    for (std::vector<std::pair<string, int> >::const_iterator indit2=indices.begin();indit2!=indices.end(); ++indit2)  {
        for (std::vector<std::pair<string, int> >::const_iterator indit1=indices.begin();indit1!=indices.end(); ++indit1)  {
             // Axes are dof, dof for each parameter
             // Take a deep breath for const-safe indexing into the double layered map
             const casa::Matrix<double>& nm = normalEquations().normalMatrix(indit1->first, indit2->first);

             for (size_t row=0; row<nm.nrow(); ++row)  {
                  for (size_t col=0; col<nm.ncolumn(); ++col) {
                       const double elem = nm(row,col);
                       ASKAPCHECK(!std::isnan(elem), "Normal matrix seems to have NaN for row = "<<row<<" and col = "<<col<<", this shouldn't happem!");
                       gsl_matrix_set(A, row+(indit1->second), col+(indit2->second), elem);
                       //   std::cout << "A " << row << " " << col << " " << nm(row,col) << std::endl;
                  }
             }
         }
    }

    for (std::vector<std::pair<string, int> >::const_iterator indit1=indices.begin();indit1!=indices.end(); ++indit1) {
        const casa::Vector<double> &dv = normalEquations().dataVector(indit1->first);
        for (size_t row=0; row<dv.nelements(); ++row) {
             const double elem = dv(row);
             ASKAPCHECK(!std::isnan(elem), "Data vector seems to have NaN for row = "<<row<<", this shouldn't happem!");
             gsl_vector_set(B, row+(indit1->second), elem);
//          std::cout << "B " << row << " " << dv(row) << std::endl;
        }
    }

      /*
      // temporary code to export matrices, which cause problems with the GSL
      // to write up a clear case
      {
        std::ofstream os("dbg.dat");
        os<<nParameters<<std::endl;
        for (int row=0;row<nParameters;++row) {
             for (int col=0;col<nParameters;++col) {
                  if (col) {
                      os<<" ";
                  }
                  os<<gsl_matrix_get(A,row,col);
             }
             os<<std::endl;
        }
      }
      // end of the temporary code
      */

    if(algorithm()=="SVD")  {
         gsl_matrix * V = gsl_matrix_alloc (nParameters, nParameters);
         ASKAPDEBUGASSERT(V!=NULL);
         gsl_vector * S = gsl_vector_alloc (nParameters);
         ASKAPDEBUGASSERT(S!=NULL);
         gsl_vector * work = gsl_vector_alloc (nParameters);
         ASKAPDEBUGASSERT(work!=NULL);

         const int status = gsl_linalg_SV_decomp (A, V, S, work);
         ASKAPCHECK(status == 0, "gsl_linalg_SV_decomp failed, status = "<<status);

         // a hack for now. For some reason, for some matrices gsl_linalg_SV_decomp may return NaN as singular value, perhaps some
         // numerical precision issue inside SVD. Although it needs to be investigated further  (see ASKAPSDP-2270), for now trying
         // to replace those singular values with zeros to exclude them from processing. Note, singular vectors may also contain NaNs
         for (int i=0; i<nParameters; ++i) {
              if (std::isnan(gsl_vector_get(S,i))) {
                  gsl_vector_set(S,i,0.);
              }
              for (int k=0; k < nParameters; ++k) {
                   ASKAPCHECK(!std::isnan(gsl_matrix_get(V,i,k)), "NaN in V: i="<<i<<" k="<<k);
              }
         }

         // end of the hack

         //SVDecomp (A, V, S);

         // code to put a limit on the condition number of the system
         const double singularValueLimit = nParameters>1 ?
                     gsl_vector_get(S,0)/itsMaxCondNumber : -1.;
         for (int i=1; i<nParameters; ++i) {
              if (gsl_vector_get(S,i)<singularValueLimit) {
                  gsl_vector_set(S,i,0.);
              }
         }

        /*
        // temporary code for debugging
        {
          std::ofstream os("dbg2.dat");
          for (int i=0; i<nParameters; ++i) {
               os<<i<<" "<<gsl_vector_get(S,i)<<std::endl;
          }

          //std::cout<<"new singular value spectrum is ready"<<std::endl;
          //char tst;
          //std::cin>>tst;

        }
        // end of temporary code
        */

         gsl_vector * X = gsl_vector_alloc(nParameters);
         ASKAPDEBUGASSERT(X!=NULL);

         const int solveStatus = gsl_linalg_SV_solve (A, V, S, B, X);
         ASKAPCHECK(solveStatus == 0, "gsl_linalg_SV_solve failed");

// Now find the statistics for the decomposition
         int rank=0;
         double smin = 1e50;
         double smax = 0.0;
         for (int i=0;i<nParameters; ++i) {
              const double sValue = std::abs(gsl_vector_get(S, i));
              ASKAPCHECK(!std::isnan(sValue), "Got NaN as a singular value for normal matrix, this shouldn't happen S[i]="<<gsl_vector_get(S,i)<<" parameter "<<i<<" singularValueLimit="<<singularValueLimit);
              if(sValue>0.0) {
                 ++rank;
                 if ((sValue>smax) || (i == 0)) {
                     smax=sValue;
                 }
                 if ((sValue<smin) || (i == 0)) {
                     smin=sValue;
                 }
               }
         }
         result.first = smin;
         result.second = smax;

         quality.setDOF(nParameters);
         quality.setRank(rank);
         quality.setCond(smax/smin);
         if(rank==nParameters) {
            quality.setInfo("SVD decomposition rank complete");
         } else {
            quality.setInfo("SVD decomposition rank deficient");
         }

// Update the parameters for the calculated changes. Exploit reference
// semantics of casa::Array.
         std::vector<std::pair<string, int> >::const_iterator indit;
         for (indit=indices.begin();indit!=indices.end();++indit) {
              casa::IPosition vecShape(1, params.value(indit->first).nelements());
              casa::Vector<double> value(params.value(indit->first).reform(vecShape));
              for (size_t i=0; i<value.nelements(); ++i)  {
//          	   std::cout << value(i) << " " << gsl_vector_get(X, indit->second+i) << std::endl;
                   const double adjustment = gsl_vector_get(X, indit->second+i);
                   ASKAPCHECK(!std::isnan(adjustment), "Solution resulted in NaN as an update for parameter "<<(indit->second + i));
                   value(i) += adjustment;
              }
          }
          gsl_vector_free(S);
          gsl_vector_free(work);
          gsl_matrix_free(V);
    } else {
        quality.setInfo("Cholesky decomposition");
        gsl_linalg_cholesky_decomp(A);
        gsl_linalg_cholesky_solve(A, B, X);
// Update the parameters for the calculated changes
        std::vector<std::pair<string, int> >::const_iterator indit;
        for (indit=indices.begin();indit!=indices.end();++indit)
        {
          casa::IPosition vecShape(1, params.value(indit->first).nelements());
          casa::Vector<double> value(params.value(indit->first).reform(vecShape));
          for (size_t i=0; i<value.nelements(); ++i)  {
               value(i)+=gsl_vector_get(X, indit->second+i);
          }
        }
    }

// Free up gsl storage
    gsl_vector_free(B);
    gsl_matrix_free(A);
    gsl_vector_free(X);
    return result;
}

    /// @brief solve for parameters
    /// The solution is constructed from the normal equations and given
    /// parameters are updated. If there are no free parameters in the
    /// given Params class, all unknowns in the normal
    /// equatons will be solved for.
    /// @param[in] params parameters to be updated
    /// @param[in] quality Quality of solution
    /// @note This is fully general solver for the normal equations for any shape
    /// parameters.
    bool LinearSolver::solveNormalEquations(Params &params, Quality& quality)
    {
      ASKAPTRACE("LinearSolver::solveNormalEquations");

// Solving A^T Q^-1 V = (A^T Q^-1 A) P

// Find all the free parameters
      vector<string> names(params.freeNames());
      if (names.size() == 0) {
          // list of parameters is empty, will solve for all
          // unknowns in the equation
          names = normalEquations().unknowns();
      }
      ASKAPCHECK(names.size()>0, "No free parameters in Linear Solver");

      if (names.size() < 100) {
          // no need to extract independent blocks if number of unknowns is small
          solveSubsetOfNormalEquations(params,quality,names);
      } else {
          while (names.size() > 0) {
              const std::vector<std::string> subsetNames = getIndependentSubset(names,1e-6);
              if (subsetNames.size() == names.size()) {
                  names.resize(0);
              } else {
                  // remove the elements corresponding to the current subset from the list of names prepared for the
                  // following integration
                  std::vector<size_t> indicesToRemove(subsetNames.size(),subsetNames.size());
                  for (size_t index = 0,indexToRemove = 0; index < names.size(); ++index) {
                       if (find(subsetNames.begin(),subsetNames.end(),names[index]) != subsetNames.end()) {
                           ASKAPDEBUGASSERT(indexToRemove < indicesToRemove.size());
                           indicesToRemove[indexToRemove++] = index;
                       }
                  }

                  // the following could be done more elegantly/faster, but leave the optimisation to later time
                  for (std::vector<size_t>::const_reverse_iterator ci = indicesToRemove.rbegin(); ci!=indicesToRemove.rend(); ++ci) {
                       ASKAPDEBUGASSERT(*ci < names.size());
                       names.erase(names.begin() + *ci);
                  }
              }
              solveSubsetOfNormalEquations(params,quality, subsetNames);
          }
      }

      return true;
    };

    Solver::ShPtr LinearSolver::clone() const
    {
      return Solver::ShPtr(new LinearSolver(*this));
    }

  }
}


/***************************************************************************
* Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht          *
* Copyright (c) QuantStack                                                 *
*                                                                          *
* Distributed under the terms of the BSD 3-Clause License.                 *
*                                                                          *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

#include <benchmark/benchmark.h>

#ifdef HAS_XTENSOR
#include "xtensor/xnoalias.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xrandom.hpp"
#include "xtensor/xtensor.hpp"
#include "xtensor/xarray.hpp"
#endif

#ifdef HAS_EIGEN
#include <Eigen/Dense>
#include <Eigen/Core>
#endif

#ifdef HAS_BLITZ
#include <blitz/array.h>
#endif

#define RANGE 3, 1000
#define MULTIPLIER 8

#ifdef HAS_XTENSOR
void Construct2D_XTensor(benchmark::State& state)
{
    for (auto _ : state)
    {
        xt::xtensor<double, 2> vTensor({state.range(0), state.range(0)});
        benchmark::DoNotOptimize(vTensor);
    }
}
BENCHMARK(Construct2D_XTensor)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_EIGEN
void Construct2D_Eigen(benchmark::State& state)
{
    using namespace Eigen;
    for (auto _ : state)
    {
        MatrixXd vMatrix(state.range(0), state.range(0));
        benchmark::DoNotOptimize(vMatrix);
    }
}
BENCHMARK(Construct2D_Eigen)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_BLITZ
void Construct2D_Blitz(benchmark::State& state)
{
    using namespace blitz;
    for (auto _ : state)
    {
        Array<double, 2> vArray(state.range(0), state.range(0));
        benchmark::DoNotOptimize(vArray);
    }
}
BENCHMARK(Construct2D_Blitz)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_XTENSOR
void ConstructRandom2D_XTensor(benchmark::State& state)
{
    for (auto _ : state)
    {
        xt::xtensor<double, 2> vTensor = xt::random::rand<double>({state.range(0), state.range(0)});
        benchmark::DoNotOptimize(vTensor);
    }
}
BENCHMARK(ConstructRandom2D_XTensor)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_EIGEN
void ConstructRandom2D_Eigen(benchmark::State& state)
{
    using namespace Eigen;
    for (auto _ : state)
    {
        MatrixXd vMatrix = MatrixXd::Random(state.range(0), state.range(0));
        benchmark::DoNotOptimize(vMatrix);
    }
}
BENCHMARK(ConstructRandom2D_Eigen)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_XTENSOR
void ConstructView2d_XTensor(benchmark::State& state)
{
    using namespace xt;

    xtensor<double,2> vA = random::rand<double>({state.range(0), state.range(0)});

    for (auto _ : state)
    {
        auto vAView = xt::view(vA, all(), all());
        benchmark::DoNotOptimize(vAView);
    }
}
BENCHMARK(ConstructView2d_XTensor)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#ifdef HAS_EIGEN
void ConstructView2d_Eigen(benchmark::State& state)
{
    using namespace Eigen;
    MatrixXd vA = MatrixXd::Random(state.range(0), state.range(0));

    for (auto _ : state)
    {
        auto vAView = vA.topLeftCorner(state.range(0), state.range(0));
        benchmark::DoNotOptimize(vAView);
    }
}
BENCHMARK(ConstructView2d_Eigen)->RangeMultiplier(MULTIPLIER)->Range(RANGE);
#endif

#undef RANGE
#undef MULTIPLIER


/*
  [auto_generated]
  boost/numeric/odeint/util/multi_array_adaption.hpp

  [begin_description]
  tba.
  [end_description]

  Copyright 2009-2012 Karsten Ahnert
  Copyright 2009-2012 Mario Mulansky

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

#ifndef BOOST_NUMERIC_ODEINT_UTIL_MULTI_ARRAY_ADAPTION_HPP_DEFINED
#define BOOST_NUMERIC_ODEINT_UTIL_MULTI_ARRAY_ADAPTION_HPP_DEFINED

#include <boost/numeric/odeint/util/is_resizeable.hpp>
#include <boost/numeric/odeint/util/resize.hpp>
#include <boost/numeric/odeint/util/same_size.hpp>

#include <boost/mpl/and.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/multi_array.hpp>

namespace boost {
namespace numeric {
namespace odeint {

template< typename T >
struct is_multi_array
{
    typedef boost::false_type type;
    const static bool value = type::value;
};

template< typename T >
struct is_resizeable_multi_array
{
    typedef boost::false_type type;
    const static bool value = type::value;
};

template< typename V , size_t Dim , typename A >
struct is_multi_array< boost::multi_array< V , Dim , A > >
{
    typedef boost::true_type type;
    const static bool value = type::value;
};

template< typename V , size_t Dim , typename A >
struct is_resizeable_multi_array< boost::multi_array< V , Dim , A > >
{
    typedef boost::true_type type;
    const static bool value = type::value;
};

template< typename T  >
struct is_resizeable_sfinae< T , typename boost::enable_if< typename is_resizeable_multi_array< T >::type >::type >
{
    typedef boost::true_type type;
    const static bool value = type::value;
};

template< typename T1 , typename T2  >
struct same_size_impl_sfinae< T1 , T2 ,
                       typename boost::enable_if<
                           typename boost::mpl::and_<
                               is_multi_array< T1 > ,
                               is_multi_array< T2 > ,
                               boost::mpl::bool_< T1::dimensionality == T2::dimensionality >
                           >::type
                       >::type >
{
    static bool same_size( T1 const &x1 , T2 const &x2 )
    {
        for( size_t i=0 ; i<T1::dimensionality ; ++i )
        {
            if( x1.shape()[i] != x2.shape()[i] ) return false;
            if( x1.index_bases()[i] != x2.index_bases()[i] ) return false;
        }
        return true;
    }
};

template< typename T1 , typename T2 >
struct resize_impl_sfinae< T1 , T2 ,
                    typename boost::enable_if<
                        typename boost::mpl::and_<
                            is_resizeable_multi_array< T1 > ,
                            is_multi_array< T2 > ,
                            boost::mpl::bool_< T1::dimensionality == T2::dimensionality >
                        >::type
                    >::type >
{
    static void resize( T1 &x1 , const T2 &x2 )
    {
        boost::array< int , T1::dimensionality > extents;
        for( size_t i=0 ; i<T1::dimensionality ; ++i ) extents[i] = x2.shape()[i];
        x1.resize( extents );
        boost::array< int , T1::dimensionality > origins;
        for( size_t i=0 ; i<T1::dimensionality ; ++i ) origins[i] = x2.index_bases()[i];
        x1.reindex( origins );
    }
};

} // namespace odeint
} // namespace numeric
} // namespace boost

#endif // BOOST_NUMERIC_ODEINT_UTIL_MULTI_ARRAY_ADAPTION_HPP_DEFINED