/***************************************************************************
                          treealgs.cc  -  description
                             -------------------
    begin                : Tue Jan 31 2006
    copyright            : (C) 2005 by Knut-Helge Vik
    email                : knuthelv@ifi.uio.no
 ***************************************************************************/
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/graph/floyd_warshall_shortest.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/reverse_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <functional>
#include <fstream>
#include <sys/types.h>

#include "treealgs.h"
#include "../treealgs/dijkstra_sp.h"
#include "../treealgs/prim_mst.h"
#include "../treealgs/prim_ottc.h"
#include "../treealgs/prim_rgh_bdmst.h"
#include "../treealgs/prim_mddbst.h"
#include "../treealgs/prim_bdrbst.h"
#include "../treealgs/fheap.h"
#include "../simtime.h"
#include "../simdefs.h"
//#include "../debug.h"
#include "../graphalgs/steiner_set.h"
#include "../graphalgs/graphalgs.h"
#include "../graphalgs/meshalgs.h"
#include "../smtalgs/sph.h"
#include "../smtalgs/rsph.h"
#include "../smtalgs/dlrsph.h"
#include "../smtalgs/dsph.h"
#include "../smtalgs/dlsph_bauer.h"
#include "../smtalgs/mdsph.h"
#include "../smtalgs/mddlsph.h"
#include "../smtalgs/bdsph.h"
#include "../smtalgs/bddlsph.h"
#include "../smtalgs/adh.h"
#include "../smtalgs/dnh.h"
#include "../smtalgs/dldnh.h"
#include "../smtalgs/diameter-dnh.h"
#include "../smtalgs/rcsph.h"
#include "../smtalgs/rcdlsph.h"
#include "../smtalgs/dcsp.h"
#include "../smtalgs/dcdbsp.h"
#include "../smtalgs/dcdbsph.h"
#include "../smtalgs/dcsph.h"
#include "../network/group_info.h"

#include "../graphviz/graphviz.h"
#include "../graphstats/graphstats.h"
#include "../graphstats/recordtree.h"

using namespace std;
using namespace boost;

namespace TreeAlgorithms
{
/*-----------------------------------------------------------------------

			Namespace TreeAlgorithms: buildTree(), start function

------------------------------------------------------------------------- */
bool buildTree(const TreeStructure &inputT, TreeStructure& outT, TreeAlgo treeAlgo, int src, clock_t &alg_time, uint64_t &cpu_cycles)
{
#ifdef SYSTEM_SUPPORTS_PUSH_POP
    uint64_t a, b;
	__asm __volatile( 	"push %eax\n"
						"push %ebx\n"
						"push %ecx\n"
						"push %edx\n"
						"cpuid\n"
						"pop %edx\n"
						"pop %ecx\n"
						"pop %ebx\n"
						"pop %eax\n" );
	__asm __volatile(".byte 0x0f,0x31" : "=A" (a));
#endif

	alg_time = buildTree(inputT, outT, treeAlgo, src);

#ifdef SYSTEM_SUPPORTS_PUSH_POP
	__asm __volatile(".byte 0x0f,0x31" : "=A" (b));
	cpu_cycles = b - a;
#endif

	return (isValidTree(outT, inputT));
}

clock_t buildTree(const TreeStructure &inputT, TreeStructure& outT, TreeAlgo treeAlgo, int src)
{
	// -- start debug --
	//cerr << WRITE_FUNCTION << treeAlgo << " src " << src << endl;
	ASSERTING(!inputT.V.empty());
	// -- end debug --

	clock_t start_time = ::clock();		// start algorithm timer

	// 	-- Execute Tree Algorithm --
	if(inputT.V.size() == 1)
	{
		outT = inputT;
	}
	else if(treeAlgo == SHORTEST_PATH_HEURISTIC)
	{
		ShortestPathHeuristic sph(inputT, outT);
		sph.Algorithm(src);
	}
	else if(treeAlgo == MINIMUM_DIAMETER_SHORTEST_PATH_HEURISTIC)
	{
		MDShortestPathHeuristic mdsph(inputT, outT);
		mdsph.Algorithm(src);
	}
	else if(treeAlgo == MINIMUM_DIAMETER_DEGREE_LIMITED_SHORTEST_PATH_HEURISTIC)
	{
		MDDLShortestPathHeuristic mddlsph(inputT, outT);
		mddlsph.Algorithm(src);
	}
	else if(treeAlgo == BOUNDED_DIAMETER_OTTC_SHORTEST_PATH_HEURISTIC)
	{
		BDShortestPathHeuristic bdsph(inputT, outT);
		bdsph.Algorithm(src, GlobalSimArgs::getDiameterBound());
	}
	else if(treeAlgo == BOUNDED_DIAMETER_DEGREE_LIMITED_OTTC_SHORTEST_PATH_HEURISTIC)
	{
		BDDLShortestPathHeuristic bddlsph(inputT, outT);
		bddlsph.Algorithm(src, GlobalSimArgs::getDiameterBound());
	}
	else if(treeAlgo == BOUNDED_DIAMETER_RGH_SHORTEST_PATH_HEURISTIC ||
			treeAlgo == BOUNDED_DIAMETER_RH_SHORTEST_PATH_HEURISTIC)
	{
		RandomizedSPH rsph(inputT, outT);
		rsph.Algorithm(src, GlobalSimArgs::getDiameterBound());
	}
	else if(treeAlgo == BOUNDED_DIAMETER_DEGREE_LIMITED_RGH_SHORTEST_PATH_HEURISTIC) // ||	treeAlgo == BOUNDED_DIAMETER_DEGREE_LIMITED_RH_SHORTEST_PATH_HEURISTIC)
	{
		DLRandomizedSPH dlrsph(inputT, outT);
		dlrsph.Algorithm(src, GlobalSimArgs::getDiameterBound());
	}
	else if(treeAlgo == AVERAGE_DISTANCE_HEURISTIC)
	{
		//GraphN dg;
		//GraphAlgorithms::densifyGraph(inputT, dg);
		//inputT.g = dg;
		//start_time = ::clock();

		AverageDistanceHeuristic adh(inputT, outT);
		adh.Algorithm(src);

		//start_time = ::clock() - start_time;
		//GraphN sg;
		//GraphAlgorithms::sparsifyGraph(outT.g, sg);
		//outT.g = sg;
		//TreeAlgorithms::updateTreeIdDenseToSparse(outT);
		//return start_time;
	}
	else if(treeAlgo == RADIUS_CONSTRAINED_SHORTEST_PATH_HEURISTIC)
	{
		RCShortestPathHeuristic rcsph(inputT, outT);
		rcsph.Algorithm(src, GlobalSimArgs::getDiameterBound()/2 );
	}
	else if(treeAlgo == RADIUS_CONSTRAINED_DEGREE_LIMITED_SHORTEST_PATH_HEURISTIC)
	{
		RCDLShortestPathHeuristic rcsph(inputT, outT);
		rcsph.Algorithm(src, GlobalSimArgs::getDiameterBound()/2 );
	}
	else if(treeAlgo == DELAY_CONSTRAINED_SHORTEST_PATH ||  	// steiner version
			treeAlgo == DELAY_CONSTRAINED_SHORTEST_PATH_TREE)   // member version
	{
		DelayConstrainedShortestPath dcsp(inputT, outT);
		dcsp.Algorithm(src, GlobalSimArgs::getDelayConstraint());
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == DELAY_CONSTRAINED_DEGREE_LIMITED_SHORTEST_PATH ||  	// steiner version
			treeAlgo == DELAY_CONSTRAINED_DEGREE_LIMITED_SHORTEST_PATH_TREE)   // member version
	{
		DCDBShortestPath dcdbsp(inputT, outT);
		dcdbsp.Algorithm(src, GlobalSimArgs::getDelayConstraint());
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == DELAY_CONSTRAINED_SHORTEST_PATH_HEURISTIC)
	{
		DCShortestPathHeuristic dcsph(inputT, outT);
		dcsph.Algorithm(src, GlobalSimArgs::getDelayConstraint());
	}
	else if(treeAlgo == DELAY_CONSTRAINED_DEGREE_LIMITED_SHORTEST_PATH_HEURISTIC)
	{
		DCDBSPH dcdbsph(inputT, outT);
		dcdbsph.Algorithm(src, GlobalSimArgs::getDelayConstraint());
	}
	else if(treeAlgo == DEGREE_LIMITED_SHORTEST_PATH_HEURISTIC)
	{
		DLShortestPathHeuristic dlsph(inputT, outT);
		dlsph.Algorithm(src);
	}
	else if(treeAlgo == DEGREE_LIMITED_SHORTEST_PATH_HEURISTIC_BAUER)
	{
		DLBauerShortestPathHeuristic dlsph_bauer(inputT, outT);
		dlsph_bauer.Algorithm(src);
	}
	else if(treeAlgo == BOUNDED_DIAMETER_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == BOUNDED_DIAMETER_DEGREE_LIMITED_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == BOUNDED_DIAMETER_RGH_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == BOUNDED_DIAMETER_DEGREE_LIMITED_RGH_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == MINIMUM_DIAMETER_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == MINIMUM_DIAMETER_DEGREE_LIMITED_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == DEGREE_LIMITED_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == DEGREE_LIMITED_SHORTEST_PATH_DISTANCE_NETWORK_HEURISTIC ||
			treeAlgo == DISTANCE_NETWORK_HEURISTIC)
	{
		DiameterDNH ddnh(inputT, outT);
		ddnh.Algorithm(src);
	}
	else if(treeAlgo == COMPACT_TREE)
	{
		PrimMDDBST MDDB(outT);
		MDDB.Algorithm(inputT, src, GlobalSimArgs::getDegreeConstraint());
	}
	else if(treeAlgo == BOUNDED_COMPACT_TREE)
	{
		PrimBDRBST BDRB(outT);
		BDRB.Algorithm(inputT, src, GlobalSimArgs::getDegreeConstraint(), 4);
	}
	else if(treeAlgo == PRUNED_COMPACT_TREE)
	{
		PrimMDDBST MDDB(outT);
		MDDB.Algorithm(inputT, src, GlobalSimArgs::getDegreeConstraint());
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == MINIMUM_SPANNING_TREE)
	{
		getPrimMST(inputT, outT, src);
	}
	else if(treeAlgo == DEGREE_LIMITED_MINIMUM_SPANNING_TREE)
	{
		getDBPrimMST(inputT, outT, src);
	}
	else if(treeAlgo == PRUNED_MINIMUM_SPANNING_TREE)
	{
		getPrimMST(inputT, outT, src);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == ONE_TIME_TREE_CONSTRUCTION_BDMST)
	{
		OTTC(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == MINIMUM_DIAMETER_ONE_TIME_TREE_CONSTRUCTION)
	{
		mdOTTC(inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == MINIMUM_DIAMETER_DEGREE_LIMITED_ONE_TIME_TREE_CONSTRUCTION)
	{
		mddlOTTC(inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == MINIMUM_DIAMETER_DEGREE_LIMITED_ONE_TIME_TREE_CONSTRUCTION_STEINER)
	{
		mddlOTTC(inputT, outT, src, inputT.V);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == RANDOMIZED_GREEDY_HEURISTIC_BDMST)
	{
		RGH(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == DEGREE_LIMITED_ONE_TIME_TREE_CONSTRUCTION_BDMST)
	{
		dlOTTC(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == DEGREE_LIMITED_ONE_TIME_TREE_CONSTRUCTION_BDMST_STEINER)
	{
		dlOTTC(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == DEGREE_LIMITED_RANDOMIZED_GREEDY_HEURISTIC_BDMST)
	{
		dlRGH(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
	}
	else if(treeAlgo == DEGREE_LIMITED_RANDOMIZED_GREEDY_HEURISTIC_BDMST_STEINER)
	{
		dlRGH(GlobalSimArgs::getDiameterBound(), inputT, outT, src, inputT.V);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == PRUNED_DEGREE_LIMITED_MINIMUM_SPANNING_TREE)
	{
		getDBPrimMST(inputT, outT, src);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == SHORTEST_PATH_TREE)
	{
		getDijkstraSPT(inputT, outT, src);
	}
	else if(treeAlgo == SHORTEST_PATH_TREE_STEINER)
	{
		getDijkstraSPT(inputT, outT, src);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == DEGREE_LIMITED_SHORTEST_PATH_TREE)
	{
		getDBDijkstraSPT(inputT, outT, src);
	}
	else if(treeAlgo == DEGREE_LIMITED_SHORTEST_PATH_TREE_STEINER)
	{
		getDBDijkstraSPT(inputT, outT, src);
		pruneNonTerminalLeaves(outT);
	}
	else if(treeAlgo == SERVER_SHORTEST_PATH_TREE || treeAlgo == PROXY_SHORTEST_PATH_TREE)
	{
		getServerSPT(inputT, outT, src);
	}
	else ASSERTING(0);

	if(!outT.S.empty()) pruneNonTerminalLeaves(outT);

	return (::clock() - start_time); 	// execution time
}

void recordStats(iostream& ostr, StatsContainer *SC)
{
	Statistics::recordTree(ostr, *SC);
}

void flushStats(iostream& ostr)
{
	if(!GlobalSimArgs::getSimStatFile())
	{
		ostr.flush();
		return;
	}
	ofstream outfile(GlobalSimArgs::getSimStatFile(), ios::out | ios::app);
	outfile << ostr.rdbuf();
	outfile.close();

	//stattofile.write(ostr.rdbuf(), sizeof(ostr.rdbuf()));
	//stattofile.flush();
}

bool isValidTree(const TreeStructure &outT, const TreeStructure &inputT)
{
	bool valid = true;
	if((outT.V - outT.S) != (inputT.V - inputT.S))
	{
		valid = false;
		cerr << " Missing group member in tree : " << (inputT.V - inputT.S) - (outT.V - outT.S) << endl;
	}
	if(outT.E.size() != (outT.V.size() - 1)) valid = false;

	return valid;
}

bool isValidMesh(const TreeStructure &outT, const TreeStructure &inputT)
{
	if((outT.V - outT.S) != (inputT.V - inputT.S))
	{
		cerr << " Missing group member in tree : " << (inputT.V - inputT.S) - (outT.V - outT.S) << endl;
		return false;
	}
	if(outT.E.size() < (outT.V.size() - 1)) return false;
	if(outT.V.size() > 1)
	{
		if(!checkIsPartitioned(outT.g)) return false;
	}

	return true;
}

int meshifyGraph(GraphN &g, VertexSet &V)
{
	vertex_iteratorN vit, vit_end;
    for(boost::tuples::tie(vit, vit_end) = vertices(g); vit != vit_end; ++vit)
	{
		if(getOutDegree(g, *vit) > 0) V.insert(*vit);
	}

	int vertex_degree = V.size() - 1;
	int num_edges_added = 0;
	for(VertexSet::iterator vit = V.begin(), vit_end = V.end(); vit != vit_end; ++vit)
	{
		int src = *vit;
		VertexSet outV;
		out_edge_iteratorN oit, oit_end;
        for(boost::tuples::tie(oit, oit_end) = out_edges(src, g); oit != oit_end; ++oit)
		{
			vertex_descriptorN targ = target(*oit, g);
			ASSERTING(targ != src);
			outV.insert(targ);

			if(g[*oit].weight <= 0.0001) g[*oit].weight = 1.0f;
		}

		VertexSet remV = V - outV - src;

		for(VertexSet::iterator it = remV.begin(), it_end = remV.end(); it != it_end; ++it)
		{
			int targ = *it;

			if(isEdge(g, src, targ)) continue;
			if(src == targ) continue;

			/*if(!isVertex(g, src))
			{
				addVertex(g, src);
				g[src].id = src;
			}
			if(!isVertex(g, targ))
			{
				addVertex(g, targ);
				g[targ] = VertexProp(targ, CLIENT);
			}*/
			pair<edge_descriptorN, bool> e = add_edge(src, targ, g);
			ASSERTING(e.second);

			num_edges_added++;

			EdgeProp ep;
			ep.id = pair<int, int>(src, targ);
			ep.delay = 1.0f;
			ep.weight = 1.0f;
			ep.bw = 1000.0;
			ep.cost = 1.0;
			ep.length = 1.0;
			ep.hops = 1;

			g[e.first] = ep;
		}

		ASSERTING(getOutDegree(src, g) == vertex_degree);
	}

	return num_edges_added;
}

void addVertex(GraphN &g, vertex_descriptorN v)
{
	int num = num_vertices(g);
	if(num <= v)
	{
		ASSERTING(num <= v);
		vertex_descriptorN ret;
		for(int i = num; i <= v; i++)
			ret = add_vertex(g);
		ASSERTING(ret == v);
	}
}

/*-----------------------------------------------------------------------
					Basic Tree Algorithms
------------------------------------------------------------------------- */

void getPrimMST(const TreeStructure &inputT, TreeStructure &outT, int source)
{
	ASSERTING(num_vertices(inputT.g) > 2);
	ASSERTING(num_edges(inputT.g) > 1);

    DistanceVector 	zdistance(num_vertices(inputT.g));
	ParentVector 	zparent(num_vertices(inputT.g));

	//prim_minimum_spanning_tree(g, source, &zparent[0], &zdistance[0], get(&EdgeProp::weight, g), get(vertex_index, g), default_dijkstra_visitor());
	myPrimMST(inputT.g, source, zdistance, zparent); //, inputT.V);

	outT.insertVertex(source, inputT.g);
	createTreeFromParent(inputT.g, outT, zparent);

	//cerr << WRITE_FUNCTION << endl;
	//dumpTreeStructure(outT);
}

void getDBPrimMST(const TreeStructure &inputT, TreeStructure &outT, int source)
{
	ASSERTING(num_vertices(inputT.g) > 2);
	ASSERTING(num_edges(inputT.g) > 1);

    DistanceVector 	zdistance(num_vertices(inputT.g));
	ParentVector 	zparent(num_vertices(inputT.g));

	//prim_minimum_spanning_tree(g, source, &zparent[0], &zdistance[0], get(&EdgeProp::weight, g), get(vertex_index, g), default_dijkstra_visitor());
	myDBPrimMST(inputT.g, source, zdistance, zparent, inputT.V);

	outT.insertVertex(source, inputT.g);
	createTreeFromParent(inputT.g, outT, zparent);

	//cerr << WRITE_FUNCTION << endl;
	//dumpTreeStructure(outT);
}

void getDijkstraSPT(const TreeStructure &inputT, TreeStructure &outT, int source)
{
    ASSERTING(num_vertices(inputT.g) > 1);
    ASSERTING(num_edges(inputT.g) > 0);

	DistanceVector 	zdistance(num_vertices(inputT.g));
	ParentVector 	zparent(num_vertices(inputT.g));

	outT.insertVertex(source, inputT.g);

	if(num_edges(inputT.g) == 1)
	{
		out_edge_iteratorN oit, oit_end;
        boost::tuples::tie(oit, oit_end) = out_edges(source, inputT.g);
		ASSERTING(oit != oit_end);

		int targ = target(*oit, inputT.g);

		outT.insertVertex(targ, inputT.g);
		outT.insertEdge(source, targ, inputT.g);
	}
	else // run dijkstra
	{
		//dijkstra_shortest_paths(g, source, weight_map(get(&EdgeProp::weight, g)).distance_map(&zdistance[0]).predecessor_map(&zparent[0]));
		myDijkstraSPT_TieBr(inputT.g, source, zdistance, zparent, inputT.V);
		createTreeFromParent(inputT.g, outT, zparent);

		//cerr << "Src: " << source << " Tree : " << outT << endl;
		//dumpGraph(outT.g);
		//ASSERTING(getOutDegree(source, outT.g) == outT.Ep.size());
	}
}

void getDBDijkstraSPT(const TreeStructure &inputT, TreeStructure &outT, int source)
{
    ASSERTING(num_vertices(inputT.g) > 1);
    ASSERTING(num_edges(inputT.g) > 0);

	DistanceVector 	zdistance(num_vertices(inputT.g));
	ParentVector 	zparent(num_vertices(inputT.g));

	outT.insertVertex(source, inputT.g);

	if(num_edges(inputT.g) == 1)
	{
		out_edge_iteratorN oit, oit_end;
        boost::tuples::tie(oit, oit_end) = out_edges(source, inputT.g);
		ASSERTING(oit != oit_end);

		int targ = target(*oit, inputT.g);

		outT.insertVertex(targ, inputT.g);
		outT.insertEdge(source, targ, inputT.g);
	}
	else // run dijkstra
	{
		//dijkstra_shortest_paths(g, source, weight_map(get(&EdgeProp::weight, g)).distance_map(&zdistance[0]).predecessor_map(&zparent[0]));
		myDBDijkstraSPT(inputT.g, source, zdistance, zparent, inputT.V);
		createTreeFromParent(inputT.g, outT, zparent);
	}
}

void getServerSPT(const TreeStructure &inputT, TreeStructure &outT, int source)
{
    ASSERTING(num_vertices(inputT.g) > 1);
    ASSERTING(num_edges(inputT.g) > 0);

	//cerr << WRITE_FUNCTION << " source in tree : " << source << endl;

	outT.insertVertex(source, inputT.g);

	out_edge_iteratorN oit, oit_end;
    for(boost::tuples::tie(oit, oit_end) = out_edges(source, inputT.g); oit != oit_end; ++oit)
	{
		int targ = target(*oit, inputT.g);
		assert(targ != source);
		outT.insertVertex(targ, inputT.g);
		outT.insertEdge(source, targ, inputT.g);
	}
}

void getProxySPT(const TreeStructure &inputT, TreeStructure &outT, int source)
{
    ASSERTING(num_vertices(inputT.g) > 1);
    ASSERTING(num_edges(inputT.g) > 0);

	outT.insertVertex(source, inputT.g);

	out_edge_iteratorN oit, oit_end;
    for(boost::tuples::tie(oit, oit_end) = out_edges(source, inputT.g); oit != oit_end; ++oit)
	{
		int targ = target(*oit, inputT.g);
		assert(targ != source);
		outT.insertVertex(targ, inputT.g);
		outT.insertEdge(source, targ, inputT.g);
	}
}

void getServerSPT(const GraphN &g, TreeStructure &outT, int source)
{
	outT.insertVertex(source, g);

	out_edge_iteratorN oit, oit_end;
    for(boost::tuples::tie(oit, oit_end) = out_edges(source, g); oit != oit_end; ++oit)
	{
		int targ = target(*oit, g);
		assert(targ != source);

		outT.insertVertex(targ, g);
		outT.insertEdge(source, targ, g);
	}
}

/*-----------------------------------------------------------------------
			makes index == id,	only used by ADH
------------------------------------------------------------------------- */
void updateTreeIdDenseToSparse(TreeStructure& T)
{
	VertexSet newV, newS;
	EdgeList newE;

	vsVertexMap vsmap = get(&VertexProp::vertexState, T.g);

	EdgePair::iterator peit = T.Ep.begin(), peit_end = T.Ep.end();
	for( ; peit != peit_end; ++peit)
	{
		pair<edge_descriptorN, bool> e = edge(peit->first, peit->second, T.g);
		ASSERTING(e.second);
		newE.push_back(e.first);

		int src = source(e.first, T.g), targ = target(e.first, T.g);
		newV.insert(src);
		newV.insert(targ);
		if(vsmap[src] == STEINER_POINT)	newS.insert(src);
		if(vsmap[targ] == STEINER_POINT) newS.insert(targ);

		//cerr << e.first << " " ;
		//cerr << *peit << " src " << src << " targ " << targ << endl;
	}

	// update set/list
	T.V = newV;
	T.S = newS;
	T.E = newE;

	//cerr << WRITE_FUNCTION << endl;
	//dumpTreeStructure(T);
}

/*-----------------------------------------------------------------------
  						Prune function
------------------------------------------------------------------------- */
/*void pruneNonTerminalLeaves(TreeStructure &T)
{
	bool foundLeaf = true;
	while(foundLeaf)
	{
		foundLeaf = false;
		VertexSet pruneSet;
		for(VertexSet::iterator vit = T.S.begin(), vit_end = T.S.end(); vit != vit_end; ++vit)
		{
			ASSERTING(get(&VertexProp::vertexState, T.g)[*vit] == STEINER_POINT);
			if(getOutDegree(*vit, T.g) <= 1)
			{
				pruneSet.insert(*vit);
				foundLeaf = true;
			}
		}

		for(VertexSet::iterator vit = pruneSet.begin(), vit_end = pruneSet.end(); vit != vit_end; ++vit)
		{
			//cerr << "Pruning " << *vit << endl;
			T.removeVertex(*vit);
		}
	}
	//cerr << WRITE_FUNCTION << " Members " << T.V - T.S << endl;
	//cerr << " non-terminals " << T.S << endl;
	//dumpTreeStructure(T);
	//char c = getchar();
}
*/

void pruneNonTerminalLeaves(TreeStructure &T)
{
	//cerr << WRITE_PRETTY_FUNCTION << "steiner set " << T.S << endl;
	VertexSet remV;

	VertexSet::iterator vit = T.S.begin(), vit_end = T.S.end();
	for( ; vit != vit_end; ++vit)
	{
		int sp = *vit;
		//cerr << sp << ": " << endl;

		// -- debug --
		ASSERTING(T.g[sp].vertexState == STEINER_POINT);
		// -- debug end --
		if(getOutDegree(T.g, sp) == 0)
		{
			//cerr << "Outdegree 0 " << endl;
			remV.insert(sp);
		}
		else if(getOutDegree(T.g, sp) == 1)
		{
			//cerr << "Removing sp: " << sp << endl;
			//cerr << "Removing edge(s): " ;
			out_edge_iteratorN oit, oit_end;
			while(getOutDegree(T.g, sp) == 1 && T.g[sp].vertexState == STEINER_POINT)
			{
                boost::tuples::tie(oit, oit_end) = out_edges(sp, T.g);
				remV.insert(sp);
				sp = target(*oit, T.g);
				//cerr << *oit << " " ;
				T.removeEdge(*oit);
			}
			//cerr << endl;
		}
	}

	//if(!remV.empty())
	//	cerr << WRITE_PRETTY_FUNCTION << " removing vertices : " << remV << " from tree : " << T << endl;

	// new steiner set
	vit = remV.begin();
	vit_end = remV.end();
	for( ; vit != vit_end; ++vit)
	{
		ASSERTING(T.g[*vit].vertexState == STEINER_POINT);
		T.removeVertex(*vit);
	}
}

/*-----------------------------------------------------------------------
		createTreeFromParent: creates a tree from parent vector
------------------------------------------------------------------------- */
void createTreeFromParent(const GraphN &g, TreeStructure& treeStructure, const ParentVector &zparent)
{
	ASSERTING(zparent.size() > 0);

	for(size_t i = 0; i != zparent.size(); ++i)
	{
		pair<edge_descriptorN, bool> e = edge(i, zparent[i], g);
		if(e.second)
		{
			//cerr << "[createTree] Parent [" << i << "] = " << zparent[i] << endl;
			int src = source(e.first, g), targ = target(e.first, g);
			treeStructure.insertVertex(i, g);
			treeStructure.insertEdge(src, targ, g);
		}
	}
}

/*-----------------------------------------------------------------------
	createGraphFromTree: creates a boost graph from treestructure
------------------------------------------------------------------------- */
void createGraphFromTree(GraphN& gOut, const TreeStructure &T)
{
	EdgeList::const_iterator eit = T.E.begin(), eit_end = T.E.end();
	for( ; eit != eit_end; ++eit)
	{
		int src = source(*eit, T.g), targ = target(*eit, T.g);

		pair<edge_descriptorN, bool> ep = edge(src, targ, T.g);
		ASSERTING(ep.second);

		pair<edge_descriptorN, bool> e = add_edge(src, targ, gOut);
		ASSERTING(e.second);

		gOut[e.first] = EdgeProp(T.g[ep.first]);
		gOut[src] 	  = VertexProp(T.g[src]);
		gOut[targ] 	  = VertexProp(T.g[targ]);
	}
}
/*-----------------------------------------------------------------------
					relax functions
------------------------------------------------------------------------- */
bool isRelaxWorking(const VertexSet &degreeBroken, const VertexSet &diameterBroken)
{
	if(GlobalSimArgs::getRelaxDiameter() <= 0 && degreeBroken.empty()) return false;
	if(GlobalSimArgs::getRelaxDegree() <= 0 && diameterBroken.empty()) return false;

	if(degreeBroken.empty() && diameterBroken.empty())
	{
		cerr << WRITE_FUNCTION << " relaxing did not make more nodes available " << endl;
		return false;
	}

	return true;
}

bool relaxDegreeAndDiameter(const VertexSet &degreeBroken, DistanceVector &degree_bound, const VertexSet &diameterBroken, DistanceVector &diameter_bound)
{
	bool ret1 = relaxDegree(degreeBroken, degree_bound);
	bool ret2 = relaxDiameter(diameterBroken, diameter_bound);
	if(false == ret1 && false == ret2) return false;

	//if(!isRelaxWorking(degreeBroken, diameterBroken)) return false;

	return true;
}

bool relaxDegreeAndRadius(const VertexSet &degreeBroken, DistanceVector &degree_bound, const VertexSet &radiusBroken, DistanceVector &radius_bound)
{
	bool ret1 = relaxDegree(degreeBroken, degree_bound);
	bool ret2 = relaxRadius(radiusBroken, radius_bound);
	if(false == ret1 && false == ret2) return false;

	return true;
}

bool relaxDegreeAndRadius(const VertexSet &degreeBroken, DistanceVector &degree_bound, double &bound)
{
	bool ret1 = relaxDegree(degreeBroken, degree_bound);
	bool ret2 = relaxRadius(bound);
	if(false == ret1 && false == ret2) return false;

	return true;
}
bool relaxDegree(const VertexSet &setBroken, DistanceVector &bounds)
{
	if(!GlobalSimArgs::getRelaxDegree()) return false;

	relaxSet(setBroken, bounds, GlobalSimArgs::getRelaxDegree());
	return true;
}

bool relaxDegree(const VertexSet &setBroken, ParentVector &bounds)
{
	if(!GlobalSimArgs::getRelaxDegree()) return false;

	relaxSet(setBroken, bounds, GlobalSimArgs::getRelaxDegree());
	return true;
}

bool relaxDelay(const VertexSet &setBroken, DistanceVector &bounds)
{
	if(!GlobalSimArgs::getRelaxDelay()) return false;

	relaxSet(setBroken, bounds, GlobalSimArgs::getRelaxDelay());
	return true;
}

bool relaxRadius(const VertexSet &setBroken, DistanceVector &bounds)
{
	if(!GlobalSimArgs::getRelaxDiameter()) return false;

	relaxSet(setBroken, bounds, GlobalSimArgs::getRelaxDiameter()/2);
	return true;
}

bool relaxRadius(double &bound)
{
	if(!GlobalSimArgs::getRelaxDiameter()) return false;

	bound = bound + GlobalSimArgs::getRelaxDiameter()/2;

	return true;
}

bool relaxDiameter(const VertexSet &setBroken, DistanceVector &bounds)
{
	if(!GlobalSimArgs::getRelaxDiameter()) return false;

	relaxSet(setBroken, bounds, GlobalSimArgs::getRelaxDiameter());
	return true;
}

bool relaxDiameter(double &bound)
{
	if(!GlobalSimArgs::getRelaxDiameter()) return false;

	bound = bound + GlobalSimArgs::getRelaxDiameter();

	return true;
}

void relaxSet(const VertexSet &setBroken, DistanceVector &bounds, double inc)
{
	for(VertexSet::iterator vit = setBroken.begin(), vit_end = setBroken.end(); vit != vit_end; ++vit)
	{
		bounds[*vit] = bounds[*vit] + inc;
		//cerr << "(" << *vit << " : " << bounds[*vit] << ") " ;
	}
}

void relaxSet(const VertexSet &setBroken, ParentVector &bounds, int inc)
{
	for(VertexSet::iterator vit = setBroken.begin(), vit_end = setBroken.end(); vit != vit_end; ++vit)
	{
		bounds[*vit] = bounds[*vit] + inc;
		//cerr << "(" << *vit << " : " << degree_bound[*vit] << ") " ;
	}
}
/*-----------------------------------------------------------------------
					basic/simple get functions
------------------------------------------------------------------------- */

int getAvOutDegree(const GraphN &g, const TreeStructure &T, int u)
{
	return (getDegreeConstraint(g, u) - getOutDegree(T.g, u));
}

int getAvOutDegree(const GraphN &g, const TreeStructure &T, const VertexSet &V)
{
	int ODAvNeighbors = 0;
	for(VertexSet::iterator vit = V.begin(), vit_end = V.end(); vit != vit_end; ++vit)
		ODAvNeighbors += (getDegreeConstraint(g, *vit) - getOutDegree(T.g, *vit));

	return ODAvNeighbors;
}

int getVertLargestAvOD(const GraphN &g, const TreeStructure &T, const VertexSet &V)
{
	int MaxOD = 0;
	int vert = -1;
	for(VertexSet::iterator vit = V.begin(), vit_end = V.end(); vit != vit_end; ++vit)
	{
		int ODAvNeighbors = (getDegreeConstraint(g, *vit) - getOutDegree(T.g, *vit));
		if(MaxOD < ODAvNeighbors) vert = *vit;
	}

	return vert;
}

int getMinODNeedConnectV(int numV)
{
	return ((numV - 1) * 2);
}

double getTotalCost(const TreeStructure &T)
{
	double totalCost = 0;
	EdgeList::const_iterator eit = T.E.begin(), eit_end = T.E.end();
	for( ; eit != eit_end; ++eit)
	{
		int src = source(*eit, T.g), targ = target(*eit, T.g);
		pair<edge_descriptorN, bool> ep = edge(src, targ, T.g);
		assert(ep.second);
		totalCost = totalCost + T.g[ep.first].weight;
	}
	return totalCost;
}

/*-----------------------------------------------------------------------
		finds the worst case pair-wise distance for src in tree_g
------------------------------------------------------------------------- */

double getTreeDiameter(const TreeStructure &T, int src)
{
	if(GlobalSimArgs::insertMeshAlgo() != NO_DYNAMIC_MESH_ALGO || GlobalSimArgs::removeMeshAlgo() != NO_DYNAMIC_MESH_ALGO || GlobalSimArgs::meshAlgo() != NO_GRAPH_ALGO)
	{
		return GraphAlgorithms::eccentricity_distance(T, src);
	}
	else
	{
		PathList tree_path;
		return getTreeDiameter(T.g, T.V, tree_path);
	}
}

double getTreeDiameter(const TreeStructure &T)
{
	PathList tree_path;
	return getTreeDiameter(T.g, T.V, tree_path);
}

double getTreeDiameter(const TreeStructure &T, PathList &tree_path)
{
	return getTreeDiameter(T.g, T.V, tree_path);
}

double getTreeDiameter(const GraphN &g, const VertexSet &V, PathList &tree_path)
{
	PathList path;
	double worstcase_dist = 0;

	int src = *(V.begin());
	ASSERTING(!V.empty());

	worstCaseDist(src, src, src, g, worstcase_dist, path);
	path.push_front(src);

	ASSERTING(!path.empty());

	// root r at node farthest away from src. r is a part of the diameter
	int r = *(path.rbegin());

	if(V.size() <= 2)
	{
		tree_path = path;
		return worstcase_dist;
	}

	double tree_diameter = 0;
	worstCaseDist(r, r, r, g, tree_diameter, tree_path);
	tree_path.push_front(r);

	return tree_diameter;
}

/*-----------------------------------------------------------------------
		finds the worst case pair-wise distance for src in tree_g
------------------------------------------------------------------------- */
void worstCaseDist(int src, int prev, int curr, const GraphN &tree_g, double &worstcase_dist, PathList &path)
{
    double 	temp1_wc_dist = 0, temp2_wc_dist = 0; 	// weight/distance
	PathList temp1_path, temp2_path;
	ASSERTING(isVertex(tree_g, curr));

	while(true)
	{
		for(pair<out_edge_iteratorN, out_edge_iteratorN> poeit = out_edges(curr, tree_g); poeit.first != poeit.second; ++poeit.first)
		{
			int targ = target(*(poeit.first), tree_g);
			if(targ == prev) continue;	 // don't go back

			temp1_wc_dist = tree_g[*(poeit.first)].weight;
			temp1_path.push_back(targ);
			worstCaseDist(src, curr, targ, tree_g, temp1_wc_dist, temp1_path);

			if(temp2_wc_dist < temp1_wc_dist)
			{
				temp2_wc_dist = temp1_wc_dist;
				temp2_path = temp1_path;
			}

			temp1_wc_dist = 0;
			temp1_path.clear();
		}
		if(curr != src)
		{
			worstcase_dist += temp2_wc_dist;
			path.insert(path.end(), temp2_path.begin(), temp2_path.end());
		}
		else if(curr == src && temp2_wc_dist > worstcase_dist)
		{
			worstcase_dist = temp2_wc_dist;
			path = temp2_path;
		}

		break;
	}
}

/*-----------------------------------------------------------------------
			finds the eccentricity for each node in V
------------------------------------------------------------------------- */

void worstCaseDist(const GraphN &g, const VertexSet &V, TreeDiameter &TD, int src)
{
	TD.distance = DistanceVector(num_vertices(g));
	for(VertexSet::iterator it = V.begin(), it_end = V.end(); it != it_end; ++it)
	{
		double worstcase_dist = 0;
		if(getOutDegree(g, *it) > 0)
			worstCaseDist(*it, *it, *it, g, worstcase_dist);
		TD.distance[*it] = worstcase_dist;
	}
}

/*-----------------------------------------------------------------------
			finds the worst pair wise distance from src
------------------------------------------------------------------------- */
void worstCaseDist(int src, int prev, int curr, const TreeStructure &tree, double &worstcase_dist)
{
	if(GlobalSimArgs::insertMeshAlgo() != NO_DYNAMIC_MESH_ALGO || GlobalSimArgs::removeMeshAlgo() != NO_DYNAMIC_MESH_ALGO || GlobalSimArgs::meshAlgo() != NO_GRAPH_ALGO)
	{
		worstcase_dist = GraphAlgorithms::eccentricity_distance(tree, src);
		//cerr << WRITE_FUNCTION << " eccentricity " << worstcase_dist << endl;
	}
	else
	{
		worstCaseDist(src, prev, curr, tree.g, worstcase_dist);
	}
}

void worstCaseDist(int src, int prev, int curr, const GraphN &tree_g, double &worstcase_dist)
{
	int targ = -1;
	bool done = false;
    double 	temp1_wc_dist = 0, temp2_wc_dist = 0; 	// weight/distance
	ASSERTING(isVertex(tree_g, curr));

	dEdgeMapConst wmap = get(&EdgeProp::weight, tree_g);

	while(!done)
	{
		int degree = getOutDegree(tree_g, curr);
		ASSERTING(degree > 0);

		if(degree >= 2)	// -- split -> pick one out and start search again --
		{
			out_edge_iteratorN oit, oit_end;
            for(boost::tuples::tie(oit, oit_end) = out_edges(curr, tree_g); oit != oit_end; oit++)
			{
				targ = target(*oit, tree_g);
				//targ = getTarget(tree_g[*oit].id, curr);
				if(targ == prev) continue;	 // don't go back

				//if(!isVertex(tree_g, targ))
				//{
					//dumpGraph(tree_g);
				//	cerr << " curr : " << curr << " targ : " << targ << " edge : " << *oit << " prop : " << tree_g[*oit] << " p: " << tree_g[targ] << endl;
				//}
				ASSERTING(isVertex(tree_g, targ));

				temp1_wc_dist = wmap[*oit];
				worstCaseDist(src, curr, targ, tree_g, temp1_wc_dist);

				if(temp2_wc_dist < temp1_wc_dist) temp2_wc_dist = temp1_wc_dist;

				temp1_wc_dist = 0;
			}

			if(curr != src) worstcase_dist += temp2_wc_dist;
			else if(curr == src && temp2_wc_dist > worstcase_dist)	worstcase_dist = temp2_wc_dist;
			done = true;
		}
		else if(degree == 1) 	// -- leaf node --
		{
			out_edge_iteratorN oit, oit_end;
            boost::tuples::tie(oit, oit_end) = out_edges(curr, tree_g);
			ASSERTING(oit != oit_end);
			targ = target(*oit, tree_g);

			if(prev == targ) done = true;
			else worstcase_dist = worstcase_dist + wmap[*oit];
		}
		ASSERTING(isVertex(tree_g, curr));
		ASSERTING(isVertex(tree_g, targ));

		prev = curr; curr = targ;
	}
}

int getTarget(const EdgeId &id, int src)
{
	if(id.first == src) return id.second;

	ASSERTING(id.second == src);
	return id.first;
}

int getSource(const EdgeId &id, int targ)
{
	if(id.first == targ) return id.second;

	ASSERTING(id.second == targ);
	return id.first;
}

void worstCaseCost(int src, int prev, int curr, const GraphN &tree_g, double &worstcase_dist)
{
	pair<out_edge_iteratorN, out_edge_iteratorN> poeit;
	int targ;
	bool done = false;
    double 	temp1_wc_dist = 0, temp2_wc_dist = 0; 	// weight/distance
	ASSERTING(isVertex(tree_g, curr));

	dEdgeMapConst wmap = get(&EdgeProp::cost, tree_g);

	while(!done)
	{
		int degree = getOutDegree(tree_g, curr);
		ASSERTING(degree > 0);

		if(degree >= 2)	// -- split -> pick one out and start search again --
		{
			for(poeit = out_edges(curr, tree_g); poeit.first != poeit.second; ++poeit.first)
			{
				targ = target(*(poeit.first), tree_g);
				if(targ == prev) continue;	 // don't go back

				temp1_wc_dist = wmap[*(poeit.first)];
				worstCaseCost(src, curr, targ, tree_g, temp1_wc_dist);

				if(temp2_wc_dist < temp1_wc_dist) temp2_wc_dist = temp1_wc_dist;

				temp1_wc_dist = 0;
			}

			if(curr != src) worstcase_dist += temp2_wc_dist;
			else if(curr == src && temp2_wc_dist > worstcase_dist)	worstcase_dist = temp2_wc_dist;
			done = true;
		}
		else if(degree == 1) 	// -- leaf node --
		{
			poeit = out_edges(curr, tree_g);
			targ = target(*(poeit.first), tree_g);

			if(prev == targ) done = true;
			else worstcase_dist = worstcase_dist + wmap[*(poeit.first)];
		}
		prev = curr; curr = targ;
	}
}

void worstCaseCost(int src, int prev, int curr, const GraphN &tree_g, pair<double, double> &worstcase_dist)
{
	pair<out_edge_iteratorN, out_edge_iteratorN> poeit;
	int targ;
	bool done = false;
    pair<double, double> temp1_wc_dist(0,0), temp2_wc_dist(0,0); 	// weight/distance
	ASSERTING(isVertex(tree_g, curr));

	dEdgeMapConst cmap = get(&EdgeProp::cost, tree_g); 		// .first
	dEdgeMapConst wmap = get(&EdgeProp::weight, tree_g); 	// .second

	while(!done)
	{
		int degree = getOutDegree(tree_g, curr);
		ASSERTING(degree > 0);

		if(degree >= 2)	// -- split -> pick one out and start search again --
		{
			for(poeit = out_edges(curr, tree_g); poeit.first != poeit.second; ++poeit.first)
			{
				targ = target(*(poeit.first), tree_g);
				if(targ == prev) continue;	 // don't go back

				temp1_wc_dist.first = cmap[*(poeit.first)];
				temp1_wc_dist.second = wmap[*(poeit.first)];
				worstCaseCost(src, curr, targ, tree_g, temp1_wc_dist);

				if(temp2_wc_dist.first < temp1_wc_dist.first) temp2_wc_dist.first = temp1_wc_dist.first;
				if(temp2_wc_dist.second < temp1_wc_dist.second) temp2_wc_dist.second = temp1_wc_dist.second;

				temp1_wc_dist.first = 0; temp1_wc_dist.second = 0;
			}

			if(curr != src)
			{
				worstcase_dist.first += temp2_wc_dist.first;
				worstcase_dist.second += temp2_wc_dist.second;
			}
			else
			{
				if(curr == src && temp2_wc_dist.first > worstcase_dist.first)	worstcase_dist.first = temp2_wc_dist.first;
				if(curr == src && temp2_wc_dist.second > worstcase_dist.second) worstcase_dist.second = temp2_wc_dist.second;
			}
			done = true;
		}
		else if(degree == 1) 	// -- leaf node --
		{
			poeit = out_edges(curr, tree_g);
			targ = target(*(poeit.first), tree_g);

			if(prev == targ) done = true;
			else
			{
				worstcase_dist.first = worstcase_dist.first + cmap[*(poeit.first)];
				worstcase_dist.second = worstcase_dist.second + wmap[*(poeit.first)];
			}
		}
		prev = curr; curr = targ;
	}
}

/*-----------------------------------------------------------------------
		computes the worst case pair wise distance for each vertex
		in the tree, it is the MDDBST algorithm and O(n^3)
	TODO:  - a function that updates worst case distances if, say, only
	a single vertex has been added, or a few edges ...
------------------------------------------------------------------------- */
void
wcVertexDistances(const TreeStructure &inputT, TreeDiameter& TD, int src)
{
	iVertexMapConst idmap = get(&VertexProp::id, inputT.g);

	HeapD<FHeap> heapD;
	Heap *heap = heapD.newInstance(num_vertices(inputT.g));

	VertexSet V;
	TD.distance = DistanceVector(num_vertices(inputT.g));
	TD.parent = ParentVector(num_vertices(inputT.g));

	// -- Initalize --
	for(VertexSet::iterator vit = inputT.V.begin(), vit_end = inputT.V.end(); vit != vit_end; ++vit)
	{
		pair<edge_descriptorN, bool> ep = edge(src, *vit, inputT.g);
		if(ep.second) 			TD.distance[*vit] = inputT.g[ep.first].weight;
		else if(*vit == src)	TD.distance[*vit] = 0;
		else  					TD.distance[*vit] = (std::numeric_limits<double>::max)();

		TD.parent[*vit] = src; // src is parent of each

		if(idmap[*vit] > -1)
		{
			V.insert(*vit);
			if(*vit != src)
			{
				heap->insert(*vit, TD.distance[*vit]);
			}
		}
	}

	TreeStructure T;
	T.insertVertex(src, inputT.g);
	V.erase(src);

	int num_vertices = V.size();
	while(T.V.size() < (num_vertices + 1))
	{
		int u = heap->deleteMin();

		// -- debug --
		assert(TD.parent[u] != u);
		// -- debug end --

		T.insertVertex(u, inputT.g);
		T.insertVertex(TD.parent[u], inputT.g);
		T.insertEdge(u, TD.parent[u], inputT.g);
		V.erase(u);

		// update distance for v
		VertexSet::iterator vit_in = T.V.begin(), vit_in_end = T.V.end();
		for( ; vit_in != vit_in_end; ++vit_in)
		{
			if(*vit_in == u) continue;
			TD.distance[*vit_in] = std::max(TD.distance[*vit_in], vertexDistance(T.g, TD.distance, TD.parent, u, *vit_in, src));
		}

		vit_in = V.begin(), vit_in_end = V.end();
		for( ; vit_in != vit_in_end; ++vit_in)
		{
			TD.distance[*vit_in] = MAXIMUM_WEIGHT;
			heap->decreaseKey(*vit_in, TD.distance[*vit_in]);

			VertexSet::iterator vert_it = T.V.begin(), vert_it_end = T.V.end();
			for( ; vert_it != vert_it_end; ++vert_it)
			{
				pair<edge_descriptorN, bool> ep = edge(*vit_in, *vert_it, inputT.g);
				if(!ep.second) continue;
				edge_descriptorN e = ep.first;
				int targ = target(e, inputT.g);

				if(inputT.g[e].weight + TD.distance[targ] < TD.distance[*vit_in])
				{
					TD.distance[*vit_in] = inputT.g[e].weight + TD.distance[targ];
					TD.parent[*vit_in] = targ;
					heap->decreaseKey(*vit_in, TD.distance[*vit_in]);
				}
			}
		}
	}

	delete heap;
}

double vertexDistance(const GraphN &g, const DistanceVector &rdistance, const ParentVector &rparent, int u, int v, int zsource)
{
	int traverse_vert = u;
	double dist_uv = 0;

	GraphN uv_g(rdistance.size());
	while(traverse_vert != v && traverse_vert != zsource)
	{
		pair<edge_descriptorN, bool> ep = edge(traverse_vert, rparent[traverse_vert], g);
		assert(ep.second);
		pair<edge_descriptorN, bool> e = add_edge(traverse_vert, rparent[traverse_vert], uv_g);
		assert(e.second);
		//uv_g[e.first] = g[ep.first];

		dist_uv = dist_uv + g[ep.first].weight;
		traverse_vert = rparent[traverse_vert];
	}

	if(traverse_vert == zsource && traverse_vert != v)
	{
		traverse_vert = v;
		while(traverse_vert != zsource )
		{
			assert(traverse_vert != u);

			pair<edge_descriptorN, bool> ep = edge(traverse_vert, rparent[traverse_vert], g);
			assert(ep.second);
			pair<edge_descriptorN, bool> e = edge(traverse_vert, rparent[traverse_vert], uv_g);
			if(e.second)
			{
				dist_uv = dist_uv - g[ep.first].weight;
			}
			else
			{
				dist_uv = dist_uv + g[ep.first].weight;
			}
			traverse_vert = rparent[traverse_vert];
		}
	}
	return dist_uv;
}

void createPDVectors(const GraphN &g, ParentVector &parent, DistanceVector &distance, int src)
{
	ASSERTING(parent.size() >= num_vertices(g));
	parent[src] = src;
	distance[src] = 0;
	createParentVector(src, src, g, parent, distance);

	//dumpGraph(g);
	//cerr << WRITE_FUNCTION << " Src " << src << " Distance Vector: " << endl;
	// dump distance vector
	//int i = 0;
	//for(DistanceVector::iterator it = distance.begin(), it_end = distance.end(); it != it_end; ++it, i++)
	//{
	//	if(*it > 0) cerr << i << ": " << *it << " "  << endl;
	//}
	//char c = getchar();
}

void createParentVector(int prev_v, int start_v, const GraphN &tree_g, ParentVector &parent, DistanceVector &distance)
{
	//cerr << WRITE_FUNCTION << " prev_v " << prev_v << " start_v " << start_v << endl;
	out_edge_iteratorN oit, oit_end;
    for(boost::tuples::tie(oit, oit_end) = out_edges(start_v, tree_g); oit != oit_end; ++oit)
	{
		int targ = target(*oit, tree_g);
		ASSERTING(targ != start_v);
		if(targ == prev_v) continue;

		distance[targ] = distance[start_v] + tree_g[*oit].weight;
		parent[targ] = start_v;

		ASSERTING(distance[targ] > 0);

		createParentVector(start_v, targ, tree_g, parent, distance);
	}
}

void wcDistancesOTTC(const GraphN &g, const VertexSet &treeNodes, TreeDiameter &TD, int src)
{
	dEdgeMapConst wmap = get(&EdgeProp::weight, g);
	out_edge_iteratorN oit, oit_end;
	VertexSet::iterator vit, vit_end, vit_in, vit_in_end;

	TD.parent = ParentVector(num_vertices(g));
	TD.distance = DistanceVector(num_vertices(g));
	ParentVector& near = TD.parent;
	DistanceVector& ecc = TD.distance;

	using namespace boost::numeric::ublas;
	matrix<double> dist(num_vertices(g), num_vertices(g));
	pair<int, double> nearest_vert(-1,(std::numeric_limits<double>::max)());

	for(vit = treeNodes.begin(), vit_end = treeNodes.end(); vit != vit_end; ++vit)
	{
		ecc[*vit] = 0;
		if(*vit != src)
		{
			pair<edge_descriptorN, bool> ep = edge(*vit, src, g);
			if(ep.second)
			{
				near[*vit] = src;
				if(nearest_vert.second > wmap[ep.first])
				{
					nearest_vert.first = *vit;
					nearest_vert.second = wmap[ep.first];
				}
			}
			else near[*vit] = -1;
		}
		else
		{
			near[*vit] = 0;
		}
		for(vit_in = treeNodes.begin(), vit_in_end = treeNodes.end(); vit_in != vit_in_end; ++vit_in)
			dist(*vit,*vit_in) = 0;
	}

	// -- begin --
	TreeStructure T;
	T.insertVertex(src, g[src]);
	ecc[src] = 0;
	dist(src,src) = 0;
	VertexSet notInTree = treeNodes - src;

	while(1)
	{
		int z = nearest_vert.first;

		T.insertVertex(z, g[z]);
		T.insertEdge(z, near[z], g);
		notInTree = notInTree - z;
		if(notInTree.empty()) break;

		pair<edge_descriptorN, bool> edge_z_near = edge(z, near[z], g);
		ASSERTING(edge_z_near.second);

		for(vit = T.V.begin(), vit_end = T.V.end(); vit != vit_end; ++vit)
		{
			if(dist(near[z],*vit) > 0) dist(z,*vit) = dist(near[z],*vit) + wmap[edge_z_near.first];
		}

		dist(z,z) = 0;
		ecc[z] = ecc[near[z]] + wmap[edge_z_near.first];

		dist(near[z],z) = wmap[edge_z_near.first];
		if(ecc[near[z]] <= 0) ecc[near[z]] = wmap[edge_z_near.first];

		for(vit = T.V.begin(), vit_end = T.V.end(); vit != vit_end; ++vit)
		{
			dist(*vit,z) = dist(*vit, near[z]) + wmap[edge_z_near.first];
			ecc[*vit] = std::max(ecc[*vit], dist(*vit,z));
		}

		for(vit = notInTree.begin(), vit_end = notInTree.end(); vit != vit_end; ++vit)
		{
			double curr_ecc = (std::numeric_limits<double>::max)();
			for(vit_in = T.V.begin(), vit_in_end = T.V.end(); vit_in != vit_in_end; ++vit_in)
			{
				pair<edge_descriptorN, bool> ep = edge(*vit, *vit_in, g);
				if(!ep.second) continue;

				double new_ecc = ecc[*vit_in] + wmap[ep.first];

				if(new_ecc < curr_ecc)
				{
					near[*vit] = *vit_in;
					curr_ecc = new_ecc;
				}
			}
		}

		nearest_vert = pair<int,double>(-1,(std::numeric_limits<double>::max)());

		for(vit = notInTree.begin(), vit_end = notInTree.end(); vit != vit_end; ++vit)
		{
			if(near[*vit] > -1)
			{
				pair<edge_descriptorN, bool> ep = edge(*vit, near[*vit], g);
				ASSERTING(ep.second);

				double new_ecc = ecc[near[*vit]] + wmap[ep.first];
				if(nearest_vert.second > new_ecc)
				{
					nearest_vert.first = *vit;
					nearest_vert.second = new_ecc;
				}
			}
		}
	}
}

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

				Check Tree validity and dump functions

------------------------------------------------------------------------- */
void checkTree(const TreeStructure &t)
{

#ifndef NDEBUG

	if(t.isValid() == false)
	{
		cerr << WRITE_FUNCTION << " tree not valid : " << t << endl;
		return;
	}

	if(t.V.size() <= 1)
	{
		ASSERTING(t.E.size() == 0);
		ASSERTING(t.Ep.size() == 0);
		ASSERTING(t.E.size() == num_edges(t.g));
		if(t.V.size() == 1)
			ASSERTING(t.g[*(t.V.begin())].vertexState == GROUP_MEMBER);
		return;
	}

	GraphVisualization GV;

	if(t.E.size() != (t.V.size() - 1))
	{
		VertexSet edgeV, diffV;
		edge_iteratorN eit, eit_end;
        for(boost::tuples::tie(eit, eit_end) = edges(t.g); eit !=eit_end; ++eit)
		{
			edge_descriptorN e = *eit;
			int src = source(e, t.g), targ = target(e, t.g);
			edgeV.insert(src);
			edgeV.insert(targ);
			if(!t.V.contains(src)) diffV.insert(src);
			if(!t.V.contains(targ)) diffV.insert(targ);

			bool foundP = false;
			EdgePair::const_iterator epit = t.Ep.begin(), epit_end = t.Ep.end();
			for( ; epit != epit_end; ++epit)
			{
				if(*epit == pair<int, int>(src,targ))
				{
					foundP = true;
					break;
				}
			}

			bool foundL = false;
			EdgeList::const_iterator elit = t.E.begin(), elit_end = t.E.end();
			for( ; elit != elit_end; ++elit)
			{
				int u = source(*elit, t.g), v = target(*elit, t.g);
				if(pair<int, int>(u,v) == pair<int, int>(src,targ))
				{
					foundL = true;
					break;
				}
			}
			if(foundL == false)
				cerr << "Couldn't find List " << e << endl;
			if(foundP == false)
				cerr << "Couldn't find Pair " << e << endl;
		}

		cerr << "Edge vertices " << edgeV << endl;
		cerr << "Tree vertices " << t.V << endl;
		cerr << "Diff " << edgeV - t.V << endl;
		cerr << "Diff " << t.V - edgeV << endl;
		cerr << "Diff " << diffV << endl;
		cerr << "Sizes g.edges " << num_edges(t.g) << " t.E.size() " << t.E.size() << " t.V.size() " << t.V.size() << endl;

		GV.VizTree("error_tree_viz.net", t);
	}

	edge_iteratorN edit, edit_end;
    for(boost::tuples::tie(edit, edit_end) = edges(t.g); edit != edit_end; ++edit)
	{
		edge_descriptorN e = *edit;
		int src = source(e, t.g), targ = target(e, t.g);
		ASSERTING(t.V.contains(src));
		ASSERTING(t.V.contains(targ));

		ASSERTING(src == t.g[e].id.first || src == t.g[e].id.second);
		ASSERTING(targ == t.g[e].id.first || targ == t.g[e].id.second);
	}

	if(t.S.size())	ASSERTING((t.V - t.S) != t.V);
	ASSERTING(t.V.size() > 1);
	ASSERTING(t.E.size() > 0);
	ASSERTING(t.E.size() == t.Ep.size());
	ASSERTING(t.E.size() == num_edges(t.g));
	ASSERTING(t.E.size() == (t.V.size() - 1));

	GraphAlgorithms::checkGraph(t.g);

	VertexSet::iterator vit = t.V.begin(), vit_end = t.V.begin();
	for( ; vit != vit_end; ++vit)
	{
		if(	(t.g[*vit].vertexState == NO_STATE) ||
			(t.g[*vit].vertexState == VERTEX_STATE_ERROR) ||
			(t.g[*vit].vertexFunction == NO_FUNCTION) ||
			(t.g[*vit].vertexFunction == VERTEX_FUNCTION_ERROR))
				GV.VizTree("error_tree_viz.net", t);

		if(t.g[*vit].vertexState == STEINER_POINT || t.g[*vit].vertexState == STEINER_POINT_AVAILABLE)
		{
			if(getOutDegree(*vit, t.g) <= 1) cerr << "Steiner point " << *vit << " out-degree " << getOutDegree(*vit, t.g) << " Tree : " << t << endl;
			ASSERTING(getOutDegree(*vit, t.g) > 1);
		}
		else ASSERTING(getOutDegree(*vit, t.g) > 0); // if(t.g[*vit].vertexState == GROUP_MEMBER)
		//ASSERTING(getOutDegree(*vit, t.g) <= getDegreeConstraint(t.g, *vit));

		ASSERTING(t.g[*vit].id > -1);
		ASSERTING(t.g[*vit].vertexState != NO_STATE);
		ASSERTING(t.g[*vit].vertexState != VERTEX_STATE_ERROR);
		ASSERTING(t.g[*vit].vertexFunction != NO_FUNCTION);
		ASSERTING(t.g[*vit].vertexFunction != VERTEX_FUNCTION_ERROR);
	}

	for(vit = t.S.begin(), vit_end = t.S.end(); vit != vit_end; ++vit)
	{
		if(getOutDegree(*vit, t.g) <= 1)
		{
			t.printVertexState(cerr);
			prettyPrint(t);
			cerr << "Steiner point " << *vit << " out-degree " << getOutDegree(*vit, t.g) << " Tree : " << t << endl;
		}
		if(!t.V.contains(*vit)) cerr << " steiner points " << t.S << " vertices " << t.V << endl;

		ASSERTING(t.V.contains(*vit));
		ASSERTING(t.g[*vit].vertexState == STEINER_POINT || t.g[*vit].vertexState == STEINER_POINT_AVAILABLE);

		ASSERTING(getOutDegree(*vit, t.g) > 1);
		//ASSERTING(getOutDegree(*vit, t.g) <= getDegreeConstraint(t.g, *vit));
		ASSERTING(t.g[*vit].id > -1);
	}

	EdgePair::const_iterator eit = t.Ep.begin(), eit_end = t.Ep.end();
	EdgeList::const_iterator elit = t.E.begin(), elit_end = t.E.end();
	for( ; eit != eit_end && elit != elit_end; ++elit, ++eit)
	{
		int src = eit->first;
		int targ = eit->second;

		std::pair<edge_descriptorN, bool> ep = edge(src, targ, t.g);
		ASSERTING(ep.second);

		src = source(*elit, t.g);
	   	targ = target(*elit, t.g);
		std::pair<edge_descriptorN, bool> el = edge(src, targ, t.g);
		ASSERTING(el.second);

		ASSERTING(getOutDegree(src, t.g) > 0);
		ASSERTING(getOutDegree(targ, t.g) > 0);

		//cerr << "EdgeList : " << *elit << " " << e.first << " EdgePair " << *eit << " " << el.first << endl;

		if(t.g[el.first].id.first == t.g[el.first].id.second)
			GV.VizTree("error_tree_viz.net", t);

		if( t.g[el.first].id.first == t.g[el.first].id.second ||
			t.g[el.first].cost <= 0 ||
			t.g[el.first].weight <= 0 ||
			t.g[el.first].delay <= 0  ||
			!(t.g[el.first].id == *eit) ||
			el.first != ep.first)
		{
			cerr << *eit << " == " << ep.first << " == " << t.g[ep.first].id << " == " << EdgeId(src,targ) << " == " << t.g[el.first].id << " ? " << endl;
			cerr << " Prop: Ep " << t.g[ep.first] << " E: " << t.g[el.first] ;
			print_graph(t.g, get(vertex_index, t.g));
			dumpGraph(t.g);
		}

		ASSERTING(el.first == ep.first);
		ASSERTING(t.g[el.first].id == *eit);
		ASSERTING(t.g[el.first].id.first != t.g[el.first].id.second);
		ASSERTING(t.g[el.first].cost > 0);
		ASSERTING(t.g[el.first].weight > 0);
		ASSERTING(t.g[el.first].delay > 0);
		ASSERTING(t.g[el.first].weight <= GlobalSimArgs::maxEdgeDelay_);
	}
/*
	EdgeList::iterator eit = t.E.begin(), eit_end = t.E.end();
	for( ; eit != eit_end; ++eit)
	{

		int src = source(*eit, t.g), targ = target(*eit, t.g);
		std::pair<edge_descriptorN, bool> e = edge(src, targ, t.g);
		ASSERTING(e.second);

		if(t.g[e.first].id.first == t.g[e.first].id.second)
			GV.VizTree("error_tree_viz.net", t);

		if( t.g[e.first].id.first == t.g[e.first].id.second ||
			t.g[e.first].cost <= 0 ||
			t.g[e.first].weight <= 0 ||
			t.g[e.first].delay <= 0 ||
			e.first != *eit)
		{
			cerr << *eit << "Cost: " << t.g[*eit].cost << " Weight : " << t.g[*eit].weight << endl;
			cerr << e.first << "Cost: " << t.g[e.first].cost << " Weight : " << t.g[e.first].weight << endl;
			print_graph(t.g, get(vertex_index, t.g));
			dumpGraph(t.g);
		}

		ASSERTING(e.first == *eit);
		ASSERTING(t.g[e.first].id.first != t.g[e.first].id.second);
		ASSERTING(t.g[e.first].cost > 0);
		ASSERTING(t.g[e.first].weight > 0);
		ASSERTING(t.g[e.first].delay > 0);
	}
*/

	/*
	SimVertexMap::iterator svit = t.simV.begin(), svit_end = t.simV.end();
	for( ; svit != svit_end; ++svit)
	{
		if(	(svit->second.getVertexState() == NO_STATE) ||
			(svit->second.getVertexState() == VERTEX_STATE_ERROR) ||
			(svit->second.getVertexFunction() == NO_FUNCTION) ||
			(svit->second.getVertexFunction() == VERTEX_FUNCTION_ERROR))
				GV.VizTree("error_tree_viz.net", t);

		ASSERTING(svit->second.getVertexState() != NO_STATE);
		ASSERTING(svit->second.getVertexState() != VERTEX_STATE_ERROR);
		ASSERTING(svit->second.getVertexFunction() != NO_FUNCTION);
		ASSERTING(svit->second.getVertexFunction() != VERTEX_FUNCTION_ERROR);
	}

	SimEdgeList::iterator seit = t.simE.begin(), seit_end = t.simE.end();
	for( ; seit != seit_end; ++seit)
	{
		if(seit->getWeight() < 0 || seit->getSource() == seit->getDestination())
			GV.VizTree("error_tree_viz.net", t);

		ASSERTING(seit->getWeight() > 0);
		ASSERTING(seit->getWeight() <= GlobalSimArgs::maxEdgeDelay_);
		ASSERTING(seit->getSource() != seit->getDestination());
	}
	*/
	//checkHasLoop(t);
#endif // NDEBUG
}

void checkDijkstra(const ParentVector &p, const DistanceVector &distances)
{
#ifndef NDEBUG
	bool found_parent = false;
	//std::cerr << "[SH::dumpDijkstra] Parent/Distance Map : "<< endl;
	for (std::size_t i = 0; i != p.size(); ++i)
    {
    	if (p[i] != i)
    	{
    		found_parent = true;
    		if(distances[i] <= 0 || distances[i] > MAXIMUM_WEIGHT)
    			dumpDijkstra(p, distances);
    		ASSERTING(distances[i] > 0 || distances[i] < MAXIMUM_WEIGHT);
    	}
	}
	if(found_parent == false)
	{
		dumpDijkstra(p, distances);
	}
	ASSERTING(found_parent);
#endif // NDEBUG
}

/*-----------------------------------------------------------------------
			finds out whether the tree has a loop
				NB! does not work!
------------------------------------------------------------------------- */
void checkHasLoop(const TreeStructure &T)
{
#ifndef NDEBUG
	GraphN g;
	createGraphFromTree(g, T);
	VertexSet visited;

	// start walk
	vertex_iteratorN vit, vit_end;
    for(boost::tuples::tie(vit, vit_end) = vertices(g); vit != vit_end; ++vit)
	{
		if(getOutDegree(*vit, g) > 0)
		{
			walkTree(g, *vit, *vit, *vit, visited);
			break;
		}
	}
	if(visited.contains(*vit))
	{
		boost::print_graph(g, get(vertex_index, g));
		dumpGraph(g);
	}

	ASSERTING(!visited.contains(*vit));
#endif // NDEBUG
}

void walkTree(const GraphN &g, int start, int u, int v, VertexSet &visited)
{
	out_edge_iteratorN oit, oit_end;
    for(boost::tuples::tie(oit, oit_end) = out_edges(v, g); oit != oit_end; ++oit)
	{
		if(visited.contains(start))	break;

		int w = target(*oit, g);
		ASSERTING(v != w);

		if( u != w && start == w)
		{
			visited.insert(start);
		}
		else if(!(visited.contains(w)))
		{
			visited.insert(w);
			walkTree(g, start, v, w, visited);
		}
	}
}

/*-----------------------------------------------------------------------
						dump functions
------------------------------------------------------------------------- */
void prettyPrint(const TreeStructure &T)
{
	for(VertexSet::iterator vit = T.V.begin(), vit_end = T.V.end(); vit != vit_end; ++vit)
	{
		cerr << *vit << ": " ;
		out_edge_iteratorN oit, oit_end;
        for(boost::tuples::tie(oit, oit_end) = out_edges(*vit, T.g); oit != oit_end; ++oit)
			cerr << target(*oit, T.g) << ", " ;
		cerr << endl;
	}

	cerr << " leaves : " << endl;
	for(VertexSet::iterator vit = T.V.begin(), vit_end = T.V.end(); vit != vit_end; ++vit)
	{
		if(getOutDegree(*vit, T.g) == 1)
		{
			cerr << *vit << ": " ;
			out_edge_iteratorN oit, oit_end;
            for(boost::tuples::tie(oit, oit_end) = out_edges(*vit, T.g); oit != oit_end; ++oit)
				cerr << target(*oit, T.g) << ", " ;
			cerr << endl;
		}
	}
	cerr << " centers (od > 1): " << endl;
	for(VertexSet::iterator vit = T.V.begin(), vit_end = T.V.end(); vit != vit_end; ++vit)
	{
		if(getOutDegree(*vit, T.g) > 1)
		{
			cerr << *vit << ": " ;
			out_edge_iteratorN oit, oit_end;
            for(boost::tuples::tie(oit, oit_end) = out_edges(*vit, T.g); oit != oit_end; ++oit)
				cerr << target(*oit, T.g) << ", " ;
			cerr << endl;
		}
	}
}

void dumpTreeStructure(const TreeStructure &T)
{
    iVertexMapConst idmap 	= get(&VertexProp::id, T.g);
	IndexMap   index	= get(vertex_index, T.g);

	cerr << "#V = " << T.V.size() << " #E = " << T.E.size() << " #Ep = " << T.Ep.size() << endl;

	VertexSet_it vit;
	cerr << "V (index):";
	for(vit = T.V.begin(); vit != T.V.end(); ++vit)
		cerr << " " << *vit ;

	cerr << " E (index):";
	EdgeList_cit eit;
	for(eit = T.E.begin(); eit != T.E.end(); ++eit)
	{
		cerr << " " << *eit ;
	}
	cerr << endl;

	cerr << "V (id):";
	for(vit = T.V.begin(); vit != T.V.end(); ++vit)
		cerr << " " << idmap[*vit] ;

	cerr << " E (id): ";
	EdgePair::const_iterator epit;
	for(epit = T.Ep.begin(); epit != T.Ep.end(); ++epit)
		cerr << " " << *epit ;
	cerr << endl;
}

void dumpDijkstra(const ParentVector &p, const DistanceVector &distances)
{
	std::cerr << "[dumpDijkstra] Parent/Distance Map : "<< endl;
	for (std::size_t i = 0; i != p.size(); ++i)
    {
    	if(p[i] != i && distances[i] > 0 && distances[i] < MAXIMUM_WEIGHT)
    	  std::cerr << "parent[" << i << "] = " << p[i] << " Dist: " << distances[i] << std::endl;
    	else if( p[i] == i)
    	  std::cerr << "parent[" << i << "] = no parent" << " Dist: " << distances[i] <<  std::endl;
    }
}

void dumpGraph(const GraphN &g)
{
    iVertexMapConst 	idmap 	= get(&VertexProp::id, g);
	vsVertexMapConst 	vsmap 	= get(&VertexProp::vertexState, g);
	vfVertexMapConst 	vfmap 	= get(&VertexProp::vertexFunction, g);
    dEdgeMapConst 		wm 		= get(&EdgeProp::weight, g);
	eidEdgeMapConst 	eidmap 	= get(&EdgeProp::id, g);

    cerr << "vertices(index) = ";
    pair<vertex_iteratorN, vertex_iteratorN> vp;
    for(vp = vertices(g); vp.first != vp.second; ++vp.first)
    {
		if(idmap[*vp.first] > -1)
			cerr << *vp.first << ", ";
    }
	cerr << endl;

    cerr << "edges(index) = ";
	graph_traits<GraphN>::edge_iterator ei, ei_end;
    for(boost::tuples::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    {
    	cerr << "(" << *ei << " w: " << wm[*ei] << ") ";
    }
    cerr << endl;

    cerr << "vertices(id) = ";
    for(vp = vertices(g); vp.first != vp.second; ++vp.first)
    {
		if(idmap[*vp.first] > -1)
			cerr << "(" << idmap[*vp.first] << " " << vsmap[*vp.first] << " " << vfmap[*vp.first] << ") ";
    }
    cerr << endl;

    cerr << "edges(id) = ";
    for(boost::tuples::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    {
		std::cerr << "(" << eidmap[*ei] << " w: " << wm[*ei] << ") ";
	}
    cerr << endl;
}

/*
double getPairWiseCost(const TreeStructure &T)
{
	TreeDiameter TD;
	int src = *(T.V.begin());
	wcVertexDistances(T, TD, src);

	double maxPairWise = 0;
	VertexSet::iterator vit = T.V.begin(), vit_end = T.V.end();
	for( ; vit != vit_end; ++vit)
	{
		if(maxPairWise < TD.distance[*vit])
			maxPairWise = TD.distance[*vit];
	}
	return maxPairWise;
}

int getCenterNodePW(const VertexSet &V, const GraphN &g)
{
	TreeDiameter TD;
	int src = *(V.begin());
	wcVertexDistances(g, TD, src);

	double maxPairWise = 0;
	int centerVertexPW = -1;
	VertexSet::iterator vit = V.begin(), vit_end = V.end();
	for( ; vit != vit_end; ++vit)
	{
		if(maxPairWise < TD.distance[*vit])
		{
			maxPairWise = TD.distance[*vit];
			centerVertexPW = *vit;
		}
	}
	return centerVertexPW;
}
*/

/*-----------------------------------------------------------------------
 	createBoostGraphIndex: converts g_in to a graph where index == id
			NB! Currently it is only used by ADH
------------------------------------------------------------------------- */
/*void createBoostGraphIndex(GraphN& g_out, const GraphN &g_in, TreeStructure &T)
{
	cerr << WRITE_FUNCTION << endl;
	dumpTreeStructure(T);

	EdgeList& simEdges = T.E;
	VertexSet simVertices = T.V;
	EdgeList dupEdges;

	ASSERTING(simEdges.size() && simVertices.size()); // ASSERTING size > 0
	ASSERTING(simEdges.size() == (simVertices.size() - 1));

	GraphAlgorithms::checkGraph(g_in);
	//checkTree(T);

	EdgeList::iterator eit, eit_end;
	for(eit = simEdges.begin(), eit_end = simEdges.end(); eit != eit_end; ++eit)
	{
		int src = source(*eit, g_in), targ = target(*eit, g_in);
		pair<edge_descriptorN, bool> e_in = edge(src, targ, g_in);

		ASSERTING(e_in.second);
		ASSERTING(simVertices.contains(src));
		if(!simVertices.contains(targ))
		{
			cerr << endl << simVertices << endl;
		}
		ASSERTING(simVertices.contains(targ));

		//cerr << *eit << " (" << src << "," << targ << ") " << e_in.first << endl;

		pair<edge_descriptorN, bool> e_out = edge(src, targ, g_out);
		if(e_out.second) // duplicate in simEdges
		{
			cerr << WRITE_FUNCTION << " Found Duplicate " << e_out.first << endl;
			dupEdges.push_back(e_in.first);
			//char c = getchar();
		}
		else
		{
			e_out = add_edge(src, targ, g_out);
			ASSERTING(e_out.second);
			g_out[e_out.first] = EdgeProp(g_in[e_in.first], src, targ);

			ASSERTING(g_out[e_out.first].weight > 0);
			ASSERTING(g_out[e_out.first].cost > 0);
		}
	}

	VertexSet::iterator vit, vit_end;
	for(vit = simVertices.begin(), vit_end = simVertices.end(); vit != vit_end; ++vit)
	{
		g_out[*vit] = g_in[*vit];
		if(g_out[*vit].vertexState != GROUP_MEMBER) g_out[*vit].vertexState = STEINER_POINT;

		ASSERTING(g_out[*vit].vertexFunction != NO_FUNCTION);
		ASSERTING(g_in[*vit].vertexFunction != NO_FUNCTION);
	}

	iVertexMapConst idmap = get(&VertexProp::id, g_in);

	EdgeList::iterator deit = dupEdges.begin(), deit_end = dupEdges.end();
	for( ; deit != deit_end; ++deit)
	{
		for(eit = simEdges.begin(), eit_end = simEdges.end(); eit != eit_end; ++eit)
		{
			//cerr << *eit << *deit << endl;
			if(*eit == *deit)
			{
				cerr << WRITE_FUNCTION << " EdgeList Erasing " << *eit << endl;
				simEdges.erase(eit);
				break;
			}
		}

		int src = source(*deit, g_in), targ = target(*deit, g_in);

		EdgePair::iterator peit = T.Ep.begin(), peit_end = T.Ep.end();
		for( ; peit != peit_end; ++peit)
		{
			if((peit->first == idmap[src] && peit->second == idmap[targ]) || (peit->second == idmap[src] && peit->first == idmap[targ]))
			{
				cerr << WRITE_FUNCTION << " EdgdPair Erasing " << *eit << endl;
				T.Ep.erase(peit);
				break;
			}
		}
	}
	GraphAlgorithms::checkGraph(g_out);
}
*/
/*
void createBoostGraphId(GraphN& g_out, const GraphN &g_in, TreeStructure &T)
{
	EdgePair& simEdges = T.Ep;
	VertexSet simVertices = T.V;
	EdgeList dupEdges;

	eidEdgeMapConst  eidmap = get(&EdgeProp::id, g_in);
	ASSERTING(simEdges.size() && simVertices.size()); // ASSERTING size > 0

	EdgeList::iterator elit = T.E.begin(), elit_end = T.E.end();
	EdgePair::iterator eit, eit_end;
	for(eit = simEdges.begin(), eit_end = simEdges.end(); elit != elit_end && eit != eit_end; ++eit, ++elit)
	{
		int src = source(*elit, g_in);
		int targ = target(*elit, g_in);
		pair<edge_descriptorN, bool> el_in = edge(src, targ, g_in);
		ASSERTING(el_in.second);
		pair<edge_descriptorN, bool> el2_in = edge(targ, src, g_in);
		ASSERTING(el2_in.second);
		pair<edge_descriptorN, bool> e_in = edge(eit->first, eit->second, g_in);
		ASSERTING(e_in.second);
		int src_in = source(e_in.first, g_in), targ_in = target(e_in.first, g_in);

		if(el_in.first != e_in.first || el_in.first != el2_in.first)
		{
			cerr << "El " << el_in.first << " src " << src << " targ " << targ << " Ep " << e_in.first << " src_in " << src_in << " targ_in " << targ_in << endl;
			cerr << "El prop id " << g_in[el_in.first].id << " El2 prop id " << g_in[el2_in.first].id << " Ep prop id " << g_in[e_in.first].id << endl;
			cerr << "El prop id " << eidmap[el_in.first] << " Ep prop id " << eidmap[e_in.first] << endl;
			cerr << "El prop " << g_in[el_in.first] << " El2 prop " << g_in[el2_in.first] << " Ep prop " << g_in[e_in.first] << endl;
			//dumpTreeStructure(T);
		}

		ASSERTING(el_in.first == e_in.first && el_in.first == el2_in.first);

		if(!simVertices.contains(eit->first) || !simVertices.contains(eit->second))
		{
			cerr << endl << simVertices << endl;
			cerr << *eit << endl;
		}
		ASSERTING(simVertices.contains(eit->first) && simVertices.contains(eit->second));
		ASSERTING(simVertices.contains(src_in) && simVertices.contains(targ_in));

		pair<edge_descriptorN, bool> e_out = edge(eit->first, eit->second, g_out);
		if(e_out.second) // duplicate edges
		{
			//cerr << WRITE_FUNCTION << "Found Duplicate " << e_out.first << endl;
			dupEdges.push_back(e_in.first);
		}
		else // add edge
		{
			e_out = add_edge(eit->first, eit->second, g_out);
			ASSERTING(e_out.second);
			int src_out = source(e_out.first, g_out), targ_out = target(e_out.first, g_out);
			ASSERTING(simVertices.contains(src_out) && simVertices.contains(targ_out));
			ASSERTING(src_out == src_in && targ_out == targ_in);
			ASSERTING((src_out == eit->first && targ_out == eit->second) || (src_out == eit->second && targ_out == eit->first));

			g_out[e_out.first] = g_in[e_in.first];
			g_out[src_out] = g_in[src_in];
			g_out[targ_out] = g_in[targ_in];

			if(g_out[src_out].vertexState != GROUP_MEMBER)	g_out[src_out].vertexState = STEINER_POINT;
			if(g_out[targ_out].vertexState != GROUP_MEMBER)	g_out[targ_out].vertexState = STEINER_POINT;

			ASSERTING(g_out[src_out].vertexFunction != NO_FUNCTION);
			ASSERTING(g_in[src_in].vertexFunction != NO_FUNCTION);
			ASSERTING(g_out[targ_out].vertexFunction != NO_FUNCTION);
			ASSERTING(g_in[targ_in].vertexFunction != NO_FUNCTION);
			ASSERTING(g_out[e_out.first].weight == g_in[e_in.first].weight);
		}
	}

	iVertexMapConst idmap = get(&VertexProp::id, g_in);

	EdgeList::iterator deit = dupEdges.begin(), deit_end = dupEdges.end();
	for( ; deit != deit_end; ++deit)
	{
		int src = source(*deit, g_in), targ = target(*deit, g_in);

		for(eit = simEdges.begin(), eit_end = simEdges.end(); eit != eit_end; ++eit)
		{
			if((eit->first == idmap[src] && eit->second == idmap[targ]) || (eit->second == idmap[src] && eit->first == idmap[targ]))
			{
				//cerr << WRITE_FUNCTION << " EdgePair Erasing " << *eit << endl;
				simEdges.erase(eit);
				break;
			}
		}

		EdgeList::iterator peit = T.E.begin(), peit_end = T.E.end();
		for( ; peit != peit_end; ++peit)
		{
			//cerr << *peit << *deit << endl;
			if(*peit == *deit)
			{
				//cerr << WRITE_FUNCTION << " EdgeList Erasing " << *peit << endl;
				T.E.erase(peit);
				break;
			}
		}
	}
	GraphAlgorithms::checkGraph(g_out);
}
*/

/*
void createSimGraph(GraphN g, SimGraph& simGraph)
{
	vertex_iteratorN vit, vit_end;
	for(tie(vit, vit_end) = vertices(g); vit != vit_end; ++vit)
	{
		VertexProp vp = g[*vit];
		simGraph.insertVertex(vp);
	}

	edge_iteratorN eit, eit_end;
	for(tie(eit, eit_end) = edges(g); eit != eit_end; ++eit)
	{
		EdgeProp ep = g[*eit];
		int src = source(*eit, g), targ = target(*eit, g);
		simGraph.insertEdge(src, targ, ep);
	}
}

void createBoostGraph(GraphN& g, SimGraph simGraph)
{
	ASSERTING(simGraph.Edges().size() && simGraph.Vertices().size()); // ASSERTING size > 0

	SimEdgeList::iterator eit, eit_end;
	for(eit = simGraph.Edges().begin(), eit_end = simGraph.Edges().end(); eit != eit_end; ++eit)
	{
		edge_descriptorN e = add_edge(eit->getSource(), eit->getDestination(), g).first;
		//EdgeProp ep = g[e];
		g[e] = eit->getEdgeProp();
	}

	SimVertexMap::iterator vit, vit_end;
	for(vit = simGraph.Vertices().begin(), vit_end = simGraph.Vertices().end(); vit != vit_end; ++vit)
	{
		//VertexProp vp = g[vit->second.getVertexId()];
		g[vit->second.getVertexId()] = vit->second.getVertexProp();
	}
}

void createBoostGraph(GraphN& g, SimEdgeList simEdges, SimVertexMap simVertices)
{
	ASSERTING(simEdges.size() && simVertices.size()); // ASSERTING size > 0

	SimEdgeList::iterator eit, eit_end;
	for(eit = simEdges.begin(), eit_end = simEdges.end(); eit != eit_end; ++eit)
	{
		edge_descriptorN e = add_edge(eit->getSource(), eit->getDestination(), g).first;
		g[e] = eit->getEdgeProp();
	}

	SimVertexMap::iterator vit, vit_end;
	for(vit = simVertices.begin(), vit_end = simVertices.end(); vit != vit_end; ++vit)
	{
		g[vit->second.getVertexId()] = vit->second.getVertexProp();
	}
}

void createBoostGraphIndex(GraphN& g_out, TreeStructure T)
{
	GraphN g_in = T.g;
	EdgeList simEdges = T.E;
	VertexSet simVertices = T.V;

	ASSERTING(simEdges.size() && simVertices.size()); // ASSERTING size > 0

	EdgeList::iterator eit, eit_end;
	for(eit = simEdges.begin(), eit_end = simEdges.end(); eit != eit_end; ++eit)
	{
		int src = source(*eit, g_in), targ = target(*eit, g_in);
		edge_descriptorN e = add_edge(src, targ, g_out).first;
		g_out[e] = g_in[*eit];
	}

	VertexSet::iterator vit, vit_end;
	for(vit = simVertices.begin(), vit_end = simVertices.end(); vit != vit_end; ++vit)
	{
		g_out[*vit] = g_in[*vit];
	}
}

void createSimTree(TreeStructure treeStructure, SimGraph& simTree)
{
	ASSERTING(treeStructure.V.size() != 0 || treeStructure.E.size() != 0);

	VertexSet::iterator vit, vit_end;
	for(vit = treeStructure.V.begin(), vit_end = treeStructure.V.end(); vit != vit_end; ++vit)
	{
		VertexProp vp = treeStructure.g[*vit];
		simTree.insertVertex(vp);
	}

	EdgeList::iterator eit, eit_end;
	for(eit = treeStructure.E.begin(), eit_end = treeStructure.E.end(); eit != eit_end; ++eit)
	{
		EdgeProp ep = treeStructure.g[*eit];
		int src = source(*eit, treeStructure.g), targ = target(*eit, treeStructure.g);
		simTree.insertEdge(src, targ, ep);
	}
}
*/

}; // namespace TreeAlgorithms ends


/* Copyright (c) 2015, Julian Straub <jstraub@csail.mit.edu> Licensed
 * under the MIT license.  See the license file LICENSE.
 */

#pragma once

#include <Eigen/Dense>

#include <dpMM/basemeasure.hpp>
#include <dpMM/niwSphereFull.hpp>
#include <mmf/mfPrior.hpp>
#include <mmf/mf.hpp>

template<typename T>
class MfBase : public BaseMeasure<T>
{
public:
  MfBase(const MfPrior<T>& mf0);
  MfBase(const MfPrior<T>& mf0, const MF<T> mf);
  MfBase(const MfBase<T>& mfBase);
  ~MfBase();

  virtual baseMeasureType getBaseMeasureType() const {return(MF_T); }

  virtual BaseMeasure<T>* copy(); // TODO
  virtual MfBase<T>* copyNative(); // TODO

  T logLikelihood(const Matrix<T,Dynamic,1>& x) const;
  T logLikelihood(const Matrix<T,Dynamic,Dynamic>& x, uint32_t i) const
    {return logLikelihood(x.col(i));};
// TODO
// assumes vector [N, sum(x), flatten(sum(outer(x,x)))]
  T logLikelihoodFromSS(const Matrix<T,Dynamic,1>& x) const;

  void posterior(const Matrix<T,Dynamic,Dynamic>& x, const VectorXu& z,
    uint32_t k);
// TODO
//  void posterior(const vector<Matrix<T,Dynamic,Dynamic> >&x, const
//      VectorXu& z, uint32_t k);
// TODO
  void posteriorFromSS(const vector<Matrix<T,Dynamic,1> >&x, const
      VectorXu& z, uint32_t k);
//  void posteriorFromSS(const Matrix<T,Dynamic,1> &x);
//  void sample();

// TODO
  T logPdfUnderPrior() const;
//  T logPdfUnderPriorMarginalized() const;
//  T logPdfUnderPriorMarginalizedMerged(const shared_ptr<MfBase<T> >& other) const;
//
//  T logPdfUnderPriorMarginalized(const Matrix<T,Dynamic,1>& x) {return 0.;};
// TODO
  void print() const;
  virtual uint32_t getDim() const {return 3;};

//  const Matrix<T,Dynamic,Dynamic>& scatter() const {return mf0_.scatter();};
//  const Matrix<T,Dynamic,1>& mean() const {return mf0_.mean();};
//  T count() const {return mf0_.count();};
////  T& count() {return mf0_.count_;};
//  const Matrix<T,Dynamic,1>& getMean() const {return normal_.mu_;};
//
//  const Matrix<T,Dynamic,Dynamic>& Sigma() const {return normal_.Sigma();};
  MfPrior<T> mf0_;
  MF<T> mf_;

private:
};

typedef MfBase<double> MfBased;
typedef MfBase<float> MfBasef;

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

template<typename T>
MfBase<T>::MfBase(const MfPrior<T>& mf0)
  :  mf0_(mf0), mf_(mf0.sample())
{
  mf0_.R_ = mf_.R();
};

template<typename T>
MfBase<T>::MfBase(const MfPrior<T>& mf0, const MF<T> mf)
  : mf0_(mf0), mf_(mf)
{};

template<typename T>
MfBase<T>::MfBase(const MfBase<T>& mfB)
  : mf0_(mfB.mf0_), mf_(mfB.mf_)
{};

template<typename T>
MfBase<T>::~MfBase()
{};

template<typename T>
BaseMeasure<T>* MfBase<T>::copy()
{
  MfBase<T>* mfBase = new MfBase<T>(mf0_);
  mfBase->mf_ = mf_;
//  niwSampled->normal_ = normal_;
  return mfBase;
};

template<typename T>
MfBase<T>* MfBase<T>::copyNative()
{
  MfBase<T>* mfBase = new MfBase<T>(mf0_);
  mfBase->mf_ = mf_;
//  niwSampled->normal_ = normal_;
  return mfBase;
};

template<typename T>
T MfBase<T>::logLikelihood(const Matrix<T,Dynamic,1>& x) const
{
//  normal_.print();
  T logLike = mf_.logPdf(x);
//  cout<<x.transpose()<<" -> " <<logLike<<endl;
//  cout<<x.transpose()<<" -> " <<normal_.logPdfSlower(x)<<endl;
  return logLike;
};

template<typename T>
T MfBase<T>::logLikelihoodFromSS(const Matrix<T,Dynamic,1>& x) const
{
  uint32_t D = 3;
  T count = x(0);
  Matrix<T,Dynamic,1> mean = x.middleRows(1,D);
  Matrix<T,Dynamic,Dynamic> scatter = Matrix<T,Dynamic,Dynamic>::Map(&(x.data()[(D+1)]),D-1,D-1);
  // right now this does not support actual scatter!  it supports
  // weighted directional data though.  count is the weight
  assert(scatter(0,0)==0.);
  assert(scatter(1,0)==0.);
  assert(scatter(0,1)==0.);
  assert(scatter(1,1)==0.);
  return count*mf_.logPdf(mean);
};

template<typename T>
void MfBase<T>::posterior(const Matrix<T,Dynamic,Dynamic>& x,
    const VectorXu& z, uint32_t k)
{
  mf_ = mf0_.posteriorSample(x,z,k);
};

//template<typename T>
//void MfBase<T>::posterior(const vector<Matrix<T,Dynamic,Dynamic> >&x,
//	const VectorXu& z, uint32_t k)
//{
//  mf_ = mf0_.posteriorFromSSsample(x,z,k);
//}

template<typename T>
void MfBase<T>::posteriorFromSS(const vector<Matrix<T,Dynamic,1> > &x, const VectorXu& z, uint32_t k)
{
  mf_ = mf0_.posteriorFromSSsample(x,z,k);
  //TODO
//  uint32_t D = normalS_.D_;
//  Matrix<T,Dynamic,1> w(z.size());
//  w.setZero(z.size());
//  uint32_t N = 0;
//  for (int32_t i=0; i<z.size(); ++i)
//    if(z[i] == k)
//    {
//      w[i]=x[i](0); // counts
//      ++N;
//    }
//  if(N > 0)
//  {
//    Matrix<T,Dynamic,Dynamic> q(D,N);
//    uint32_t j=0;
//    for (int32_t i=0; i<z.size(); ++i)
//      if(z[i] == k)
//      {
//        q.col(j++) = x[i].middleRows(1,D);
//      }
//    normalS_.setMean(karcherMeanWeighted<T>(normalS_.getMean(), q, w, 100));
//    //TODO: slight permutation here for mu to allow proper sampling
//    // TODO: wastefull since it computes stuff for normals that are not used in the
//    // later computations
//    Matrix<T,Dynamic,Dynamic> x_mu = S_.Log_p_north(normalS_.getMean(),q);
//    Matrix<T,Dynamic,Dynamic> outer =  Matrix<T,Dynamic,Dynamic>::Zero(D-1,D-1);
//    j=0;
//    for (int32_t i=0; i<z.size(); ++i)
//      if(z[i] == k)
//      {
//        outer += w(i)* x_mu.col(j) * x_mu.col(j).transpose();
//		j++;
//      }
//    normalS_.setSigma(iw0_.posterior(outer, w.sum()).sample());
//
//  }else{
//    normalS_.setMean(S_.sampleUnif(normalS_.pRndGen_));
//    iw0_.resetSufficientStatistics();
//    normalS_.setSigma(iw0_.sample());
//  }
////  sample();
//  //  cout<<"Delta: \n"<<iw0_.posterior(x_mu,z,k).Delta_<<endl;
//  //  cout<<"Sigma: \n"<<normalS_.Sigma()<<endl;
////  cout<<"Sigma Eigs:"<<normalS_.Sigma().eigenvalues()<<endl;
//#ifndef NDEBUG
//  cout<<"NiwSphere<T>::posterior"<<endl
//    <<normalS_.getMean().transpose()<<endl
//    <<normalS_.Sigma()<<endl;
//#endif
//
//  // old
//	normal_ = mf0_.posteriorFromSS(x,z,k).sample();
}

//template<typename T>
//void MfBase<T>::posteriorFromSS(const Matrix<T,Dynamic,1> &x)
//{
//	normal_ = mf0_.posteriorFromSS(x).sample();
//}

template<typename T>
T MfBase<T>::logPdfUnderPrior() const
{
  return mf0_.logPdf(mf_);
};

//template<typename T>
//T MfBase<T>::logPdfUnderPriorMarginalized() const
//{
//  // evaluates log pdf of sufficient statistics stored within mf0_
////  mf0_.print();
//  return mf0_.logPdfMarginalized();
//};
//
//template<typename T>
//T MfBase<T>::logPdfUnderPriorMarginalizedMerged(
//    const shared_ptr<MfBase<T> >& other) const
//{
//  return mf0_.logPdfUnderPriorMarginalizedMerged(other->mf0_);
//};
//
//template<typename T>
//void MfBase<T>::sample()
//{
//  normal_ = mf0_.posterior().sample();
//};

template<typename T>
void MfBase<T>::print() const
{
  mf_.print();
//  mf0_.posterior().print();
//  normal_.print();
};


/* boost random/mixmax.hpp header file
 *
 * Copyright Kostas Savvidis 2008-2019
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id$
 *
 * Revision history
 *  2019-04-23 created
 */

#ifndef BOOST_RANDOM_MIXMAX_HPP
#define BOOST_RANDOM_MIXMAX_HPP

#include <sstream>
#include <boost/cstdint.hpp>
#include <boost/array.hpp>

#include <boost/random/detail/seed.hpp>
#include <boost/random/detail/seed_impl.hpp>

namespace boost {
namespace random {

/**
 * Instantiations of class template mixmax_engine model,
 * \pseudo_random_number_generator .
 *  It uses the  MIXMAX generator algorithms from:
 *
 *  @blockquote
 *  G.K.Savvidy and N.G.Ter-Arutyunian,
 *  On the Monte Carlo simulation of physical systems,
 *  J.Comput.Phys. 97, 566 (1991);
 *  Preprint EPI-865-16-86, Yerevan, Jan. 1986
 *  http://dx.doi.org/10.1016/0021-9991(91)90015-D
 *
 *  K.Savvidy
 *  The MIXMAX random number generator
 *  Comp. Phys. Commun. 196 (2015), pp 161–165
 *  http://dx.doi.org/10.1016/j.cpc.2015.06.003
 *
 *  K.Savvidy and G.Savvidy
 *  Spectrum and Entropy of C-systems. MIXMAX random number generator
 *  Chaos, Solitons & Fractals, Volume 91, (2016) pp. 33–38
 *  http://dx.doi.org/10.1016/j.chaos.2016.05.003
 *  @endblockquote
 *
 * The generator crucially depends on the choice of the
 * parameters. The valid sets of parameters are from the published papers above.
 *
 */

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> // MIXMAX TEMPLATE PARAMETERS
class mixmax_engine{
public:
    // Interfaces required by C++11 std::random and boost::random
    typedef boost::uint64_t result_type ;
    BOOST_STATIC_CONSTANT(boost::uint64_t,mixmax_min=0);
    BOOST_STATIC_CONSTANT(boost::uint64_t,mixmax_max=((1ULL<<61)-1));
    BOOST_STATIC_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION() {return mixmax_min;}
    BOOST_STATIC_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION() {return mixmax_max;}
    static const bool has_fixed_range = false;
    BOOST_STATIC_CONSTANT(int,N=Ndim);     ///< The main internal parameter, size of the defining MIXMAX matrix
    // CONSTRUCTORS:
    explicit mixmax_engine();                       ///< Constructor, unit vector as initial state, acted on by A^2^512
    explicit mixmax_engine(boost::uint64_t);          ///< Constructor, one 64-bit seed
    explicit mixmax_engine(uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID );  ///< Constructor, four 32-bit seeds for 128-bit seeding flexibility
    void seed(boost::uint64_t seedval=default_seed){seed_uniquestream( &S, 0, 0, (uint32_t)(seedval>>32), (uint32_t)seedval );} ///< seed with one 64-bit seed

private: // DATATYPES
    struct rng_state_st{
        boost::array<boost::uint64_t, Ndim> V;
        boost::uint64_t sumtot;
        int counter;
    };

    typedef struct rng_state_st rng_state_t;     // struct alias
    rng_state_t S;

public: // SEEDING FUNCTIONS
    template<class It> mixmax_engine(It& first, It last) { seed(first,last); }
    BOOST_RANDOM_DETAIL_SEED_SEQ_CONSTRUCTOR(mixmax_engine,  SeedSeq, seq){ seed(seq); }

    /** Sets the state of the generator using values from an iterator range. */
    template<class It>
    void seed(It& first, It last){
        uint32_t v[4];
        detail::fill_array_int<32>(first, last, v);
        seed_uniquestream( &S, v[0], v[1], v[2], v[3]);
    }
    /** Sets the state of the generator using values from a seed_seq. */
    BOOST_RANDOM_DETAIL_SEED_SEQ_SEED(mixmax_engine, SeeqSeq, seq){
        uint32_t v[4];
        detail::seed_array_int<32>(seq, v);
        seed_uniquestream( &S, v[0], v[1], v[2], v[3]);
    }

    /** return one uint64 between min=0 and max=2^61-1 */
    boost::uint64_t operator()(){
        if (S.counter<=(Ndim-1) ){
            return S.V[S.counter++];
        }else{
            S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);
            S.counter=2;
            return S.V[1];
        }
    }

    /** Fills a range with random values */
    template<class Iter>
    void generate(Iter first, Iter last) { detail::generate_from_int(*this, first, last); }

    void discard(boost::uint64_t nsteps) { for(boost::uint64_t j = 0; j < nsteps; ++j)  (*this)(); } ///< discard n steps, required in boost::random

    /** save the state of the RNG to a stream */
    template<class CharT, class Traits>
    friend std::basic_ostream<CharT,Traits>&
    operator<< (std::basic_ostream<CharT,Traits>& ost, const mixmax_engine& me){
        ost << Ndim << " " << me.S.counter << " " << me.S.sumtot << " ";
        for (int j=0; (j< (Ndim) ); j++) {
        ost <<  (boost::uint64_t)me.S.V[j] << " ";
        }
        ost << "\n";
        ost.flush();
        return ost;
        }

    /** read the state of the RNG from a stream */
    template<class CharT, class Traits>
    friend std::basic_istream<CharT,Traits>&
    operator>> (std::basic_istream<CharT,Traits> &in, mixmax_engine& me){
        // will set std::ios::failbit if the input format is not right
        boost::array<boost::uint64_t, Ndim> vec;
        boost::uint64_t sum=0, savedsum=0, counter=0;
        in >> counter >> std::ws;
        BOOST_ASSERT(counter==Ndim);
        in >> counter >> std::ws;
        in >> savedsum >> std::ws;
        for(int j=0;j<Ndim;j++) {
        in >> std::ws >> vec[j] ;
        sum=me.MOD_MERSENNE(sum+vec[j]);
    }
    if (sum == savedsum && counter>0 && counter<Ndim){
        me.S.V=vec; me.S.counter = counter; me.S.sumtot=savedsum;
    }else{
        in.setstate(std::ios::failbit);
    }
    return in;
    }

friend bool operator==(const mixmax_engine & x,
                       const mixmax_engine & y){return x.S.counter==y.S.counter && x.S.sumtot==y.S.sumtot && x.S.V==y.S.V ;}
friend bool operator!=(const mixmax_engine & x,
                       const mixmax_engine & y){return !operator==(x,y);}

private:
BOOST_STATIC_CONSTANT(int, BITS=61);
BOOST_STATIC_CONSTANT(boost::uint64_t, M61=2305843009213693951ULL);
BOOST_STATIC_CONSTANT(boost::uint64_t, default_seed=1);
inline boost::uint64_t MOD_MERSENNE(boost::uint64_t k) {return ((((k)) & M61) + (((k)) >> BITS) );}
inline boost::uint64_t MULWU(boost::uint64_t k);
inline void seed_vielbein(rng_state_t* X, unsigned int i); // seeds with the i-th unit vector, i = 0..Ndim-1,  for testing only
inline void seed_uniquestream( rng_state_t* Xin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID );
inline boost::uint64_t iterate_raw_vec(boost::uint64_t* Y, boost::uint64_t sumtotOld);
inline boost::uint64_t apply_bigskip(boost::uint64_t* Vout, boost::uint64_t* Vin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID );
inline boost::uint64_t modadd(boost::uint64_t foo, boost::uint64_t bar);
inline boost::uint64_t fmodmulM61(boost::uint64_t cum, boost::uint64_t s, boost::uint64_t a);
};

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine()
///< constructor, with no params, seeds with seed=0,  random numbers are as good as from any other seed
{
    seed_uniquestream( &S, 0,  0, 0, default_seed);
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine(boost::uint64_t seedval){
    ///< constructor, one uint64_t seed, random numbers are statistically independent from any two distinct seeds, e.g. consecutive seeds are ok
    seed_uniquestream( &S, 0,  0,  (uint32_t)(seedval>>32), (uint32_t)seedval );
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::mixmax_engine(uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID){
    // constructor, four 32-bit seeds for 128-bit seeding flexibility
    seed_uniquestream( &S, clusterID,  machineID,  runID,  streamID );
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> uint64_t mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::MULWU (uint64_t k){ return (( (k)<<(SPECIALMUL) & M61) ^ ( (k) >> (BITS-SPECIALMUL))  )  ;}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::iterate_raw_vec(boost::uint64_t* Y, boost::uint64_t sumtotOld){
    // operates with a raw vector, uses known sum of elements of Y
    boost::uint64_t  tempP=0, tempV=sumtotOld;
    Y[0] = tempV;
    boost::uint64_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
    for (int i=1; i<Ndim; i++){
        boost::uint64_t tempPO = MULWU(tempP);
        tempV = (tempV+tempPO);
        tempP = modadd(tempP, Y[i]);
        tempV = modadd(tempV, tempP); // new Y[i] = old Y[i] + old partial * m
        Y[i] = tempV;
        sumtot += tempV; if (sumtot < tempV) {ovflow++;}
    }
    return MOD_MERSENNE(MOD_MERSENNE(sumtot) + (ovflow <<3 ));
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> void mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::seed_vielbein(rng_state_t* X, unsigned int index){
    for (int i=0; i < Ndim; i++){
        X->V[i] = 0;
    }
    if (index<Ndim) { X->V[index] = 1; }else{ X->V[0]=1; }
    X->counter = Ndim;  // set the counter to Ndim if iteration should happen right away
    X->sumtot = 1;
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> void mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::seed_uniquestream( rng_state_t* Xin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID ){
    seed_vielbein(Xin,0);
    Xin->sumtot = apply_bigskip(Xin->V.data(), Xin->V.data(),  clusterID,  machineID,  runID,   streamID );
    Xin->counter = 1;
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::apply_bigskip( boost::uint64_t* Vout, boost::uint64_t* Vin, uint32_t clusterID, uint32_t machineID, uint32_t runID, uint32_t  streamID ){
    /*
     makes a derived state vector, Vout, from the mother state vector Vin
     by skipping a large number of steps, determined by the given seeding ID's

     it is mathematically guaranteed that the substreams derived in this way from the SAME (!!!) Vin will not collide provided
     1) at least one bit of ID is different
     2) less than 10^100 numbers are drawn from the stream
     (this is good enough : a single CPU will not exceed this in the lifetime of the universe, 10^19 sec,
     even if it had a clock cycle of Planck time, 10^44 Hz )

     Caution: never apply this to a derived vector, just choose some mother vector Vin, for example the unit vector by seed_vielbein(X,0),
     and use it in all your runs, just change runID to get completely nonoverlapping streams of random numbers on a different day.

     clusterID and machineID are provided for the benefit of large organizations who wish to ensure that a simulation
     which is running in parallel on a large number of  clusters and machines will have non-colliding source of random numbers.

     did i repeat it enough times? the non-collision guarantee is absolute, not probabilistic

     */

    const    boost::uint64_t skipMat17[128][17] =
#include "boost/random/detail/mixmax_skip_N17.ipp"
    ;

    const boost::uint64_t* skipMat[128];
    BOOST_ASSERT(Ndim==17);
    for (int i=0; i<128; i++) { skipMat[i] = skipMat17[i];}

    uint32_t IDvec[4] = {streamID, runID, machineID, clusterID};
    boost::uint64_t Y[Ndim], cum[Ndim];
    boost::uint64_t sumtot=0;

    for (int i=0; i<Ndim; i++) { Y[i] = Vin[i]; sumtot = modadd( sumtot, Vin[i]); } ;
    for (int IDindex=0; IDindex<4; IDindex++) { // go from lower order to higher order ID
        uint32_t id=IDvec[IDindex];
        int r = 0;
        while (id){
            if (id & 1) {
                boost::uint64_t* rowPtr = (boost::uint64_t*)skipMat[r + IDindex*8*sizeof(uint32_t)];
                for (int i=0; i<Ndim; i++){ cum[i] = 0; }
                for (int j=0; j<Ndim; j++){              // j is lag, enumerates terms of the poly
                    // for zero lag Y is already given
                    boost::uint64_t coeff = rowPtr[j]; // same coeff for all i
                    for (int i =0; i<Ndim; i++){
                        cum[i] =  fmodmulM61( cum[i], coeff ,  Y[i] ) ;
                    }
                    sumtot = iterate_raw_vec(Y, sumtot);
                }
                sumtot=0;
                for (int i=0; i<Ndim; i++){ Y[i] = cum[i]; sumtot = modadd( sumtot, cum[i]); } ;
            }
            id = (id >> 1); r++; // bring up the r-th bit in the ID
        }
    }
    sumtot=0;
    for (int i=0; i<Ndim; i++){ Vout[i] = Y[i]; sumtot = modadd( sumtot, Y[i]); } ;  // returns sumtot, and copy the vector over to Vout
    return (sumtot) ;
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> inline boost::uint64_t mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::fmodmulM61(boost::uint64_t cum, boost::uint64_t s, boost::uint64_t a){
    // works on all platforms, including 32-bit linux, PPC and PPC64, ARM and Windows
    const boost::uint64_t MASK32=0xFFFFFFFFULL;
    boost::uint64_t o,ph,pl,ah,al;
    o=(s)*a;
    ph = ((s)>>32);
    pl = (s) & MASK32;
    ah = a>>32;
    al = a & MASK32;
    o = (o & M61) + ((ph*ah)<<3) + ((ah*pl+al*ph + ((al*pl)>>32))>>29) ;
    o += cum;
    o = (o & M61) + ((o>>61));
    return o;
}

template <int Ndim, unsigned int SPECIALMUL, boost::int64_t SPECIAL> boost::uint64_t mixmax_engine  <Ndim, SPECIALMUL, SPECIAL> ::modadd(boost::uint64_t foo, boost::uint64_t bar){
    return MOD_MERSENNE(foo+bar);
}

/* @copydoc boost::random::detail::mixmax_engine_doc */
/** Instantiation with a valid parameter set. */
typedef mixmax_engine<17,36,0>          mixmax;
}// namespace random
}// namespace boost

#endif // BOOST_RANDOM_MIXMAX_HPP


///////////////////////////////////////////////////////////////
//  Copyright Jens Maurer 2000-2021
//  Copyright Steven Watanabe 2011-2021
//  Copyright John Maddock 2015-2021
//  Copyright Matt Borland 2021
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  https://www.boost.org/LICENSE_1_0.txt
//
//  This is a C++11 compliant port of Boost.Random's implementation
//  of uniform_int_distribution. See their comments for detailed
//  descriptions

#ifndef BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
#define BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP

#include <limits>
#include <type_traits>
#include <boost/multiprecision/detail/standalone_config.hpp>
#include <boost/multiprecision/detail/assert.hpp>
#include <boost/multiprecision/traits/std_integer_traits.hpp>

namespace boost { namespace multiprecision {

namespace detail {

template <typename T, bool intrinsic>
struct make_unsigned_impl
{
    using type = typename boost::multiprecision::detail::make_unsigned<T>::type;
};

template <typename T>
struct make_unsigned_impl<T, false>
{
    using type = T;
};

template <typename T>
struct make_unsigned_mp
{
    using type = typename make_unsigned_impl<T, boost::multiprecision::detail::is_integral<T>::value>::type;
};

template <typename Engine, typename T>
T generate_uniform_int (Engine& eng, T min_value, T max_value)
{
    using range_type = typename boost::multiprecision::detail::make_unsigned_mp<T>::type;
    using base_result = typename Engine::result_type;
    using base_unsigned = typename boost::multiprecision::detail::make_unsigned_mp<base_result>::type;

    const range_type range = max_value - min_value;
    const base_result bmin = (eng.min)();
    const base_unsigned brange = (eng.max)() - (eng.min)();

    if(range == 0)
    {
        return min_value;
    }
    else if (brange < range)
    {
        for(;;)
        {
            range_type limit;
            if(range == (std::numeric_limits<range_type>::max)())
            {
                limit = range / (range_type(brange) + 1);
                if(range % (range_type(brange) + 1) == range_type(brange))
                {
                    ++limit;
                }
            }
            else
            {
                limit = (range + 1) / (range_type(brange) + 1);
            }

            range_type result = 0;
            range_type mult = 1;

            while (mult <= limit)
            {
                result += static_cast<range_type>(static_cast<range_type>(eng() - bmin) * mult);

                if(mult * range_type(brange) == range - mult + 1)
                {
                    return(result);
                }

                mult *= range_type(brange)+range_type(1);
            }

            range_type result_increment = generate_uniform_int(eng, range_type(0), range_type(range/mult));

            if(std::numeric_limits<range_type>::is_bounded && ((std::numeric_limits<range_type>::max)() / mult < result_increment))
            {
                continue;
            }

            result_increment *= mult;
            result += result_increment;

            if(result < result_increment)
            {
                continue;
            }
            if(result > range)
            {
                continue;
            }

            return result + min_value;
        }
    }
    else
    {
        using mixed_range_type =
        typename std::conditional<std::numeric_limits<range_type>::is_specialized && std::numeric_limits<base_unsigned>::is_specialized &&
                                  (std::numeric_limits<range_type>::digits >= std::numeric_limits<base_unsigned>::digits),
                                  range_type, base_unsigned>::type;

        mixed_range_type bucket_size;

        if(brange == (std::numeric_limits<base_unsigned>::max)())
        {
            bucket_size = static_cast<mixed_range_type>(brange) / (static_cast<mixed_range_type>(range)+1);
            if(static_cast<mixed_range_type>(brange) % (static_cast<mixed_range_type>(range)+1) == static_cast<mixed_range_type>(range))
            {
                ++bucket_size;
            }
        }
        else
        {
            bucket_size = static_cast<mixed_range_type>(brange + 1) / (static_cast<mixed_range_type>(range)+1);
        }

        for(;;)
        {
            mixed_range_type result = eng() - bmin;
            result /= bucket_size;

            if(result <= static_cast<mixed_range_type>(range))
            {
                return result + min_value;
            }
        }
    }
}

} // Namespace detail

template <typename Integer = int>
class uniform_int_distribution
{
private:
    Integer min_;
    Integer max_;

public:
    class param_type
    {
    private:
        Integer min_;
        Integer max_;

    public:
        explicit param_type(Integer min_val, Integer max_val) : min_ {min_val}, max_ {max_val}
        {
            BOOST_MP_ASSERT(min_ <= max_);
        }

        Integer a() const { return min_; }
        Integer b() const { return max_; }
    };

    explicit uniform_int_distribution(Integer min_arg, Integer max_arg) : min_ {min_arg}, max_ {max_arg}
    {
        BOOST_MP_ASSERT(min_ <= max_);
    }

    explicit uniform_int_distribution(const param_type& param_arg) : min_ {param_arg.a()}, max_ {param_arg.b()} {}

    Integer min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return min_; }
    Integer max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return max_; }

    Integer a() const { return min_; }
    Integer b() const { return max_; }

    param_type param() const { return param_type(min_, max_); }

    void param(const param_type& param_arg)
    {
        min_ = param_arg.a();
        max_ = param_arg.b();
    }

    template <typename Engine>
    Integer operator() (Engine& eng) const
    {
        return detail::generate_uniform_int(eng, min_, max_);
    }

    template <typename Engine>
    Integer operator() (Engine& eng, const param_type& param_arg) const
    {
        return detail::generate_uniform_int(eng, param_arg.a(), param_arg.b());
    }
};

}} // Namespaces

#endif // BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP