#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

// ALGOLAB BGL Tutorial 2
// Flow example demonstrating
// - interior graph properties for flow algorithms
// - custom edge adder

// Compile and run with one of the following:
// g++ -std=c++11 -O2 flows.cpp -o flows ./flows
// g++ -std=c++11 -O2 -I path/to/boost_1_58_0 flows.cpp -o flows; ./flows

// Includes
// ========
// STL includes
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <cstdio>
#include <string>
// BGL includes
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
#include <boost/graph/edmonds_karp_max_flow.hpp>
#include <boost/graph/cycle_canceling.hpp>
#include <boost/graph/successive_shortest_path_nonnegative_weights.hpp>
#include <boost/graph/find_flow_cost.hpp>
#include <boost/graph/copy.hpp>
// Namespaces
// using namespace std;
using namespace boost;

#define _i std::cin
#define _o std::cout
#define _l std::endl
#define _e std::cerr

void Marathon2();

// Main function to loop over the testcases
int main(int argc, char** argv) {
	std::ios_base::sync_with_stdio(false);
	if (argc > 1) {
		std::string str("C:\\some-path\\");
		str += std::string(argv[1]) + ".in";
		freopen(str.c_str(), "r", stdin);
	}
	else freopen("C:\\some-path\\test0.in", "r", stdin);
	freopen("C:\\some-path\\out_my.out", "w", stdout);
	freopen("C:\\some-path\\err_my.out", "w", stderr);
	int T;	_i >> T;
	for (; T > 0; --T)	Marathon2();
	return 0;
}

//
// BGL Graph definitions
// =====================
// Graph Type with nested interior edge properties for Flow Algorithms
typedef	boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
	boost::property<boost::edge_capacity_t, long,
	boost::property<boost::edge_residual_capacity_t, long,
	boost::property<boost::edge_reverse_t, Traits::edge_descriptor,
	boost::property<boost::edge_weight_t, long> > > > >	Graph; // for MCMF
															   // Interior Property Maps
typedef	boost::property_map<Graph, boost::edge_capacity_t>::type		EdgeCapacityMap;
typedef boost::property_map<Graph, boost::edge_weight_t>::type			EdgeWeightMap;
typedef	boost::property_map<Graph, boost::edge_residual_capacity_t>::type	ResidualCapacityMap;
typedef	boost::property_map<Graph, boost::edge_reverse_t>::type		ReverseEdgeMap;
typedef	boost::graph_traits<Graph>::vertex_descriptor			Vertex;
typedef	boost::graph_traits<Graph>::vertex_iterator				VertexIt;
typedef	boost::graph_traits<Graph>::edge_descriptor			Edge;
typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIt;

// Custom Edge Adder Class, that holds the references
// to the graph, capacity map and reverse edge map
// ===================================================
// For ONLY Min-cost Max flow
class EdgeAdderMCMF {
	Graph &G;
	EdgeCapacityMap	&capacitymap;
	ReverseEdgeMap	&revedgemap;
	EdgeWeightMap &weightmap;

public:
	// to initialize the Object
	EdgeAdderMCMF(Graph & G, EdgeCapacityMap &capacitymap, EdgeWeightMap &weightmap, ReverseEdgeMap &revedgemap) :
		G(G), capacitymap(capacitymap), weightmap(weightmap), revedgemap(revedgemap) {}

	// to use the Function (add an edge)
	void addEdge(int from, int to, long capacity, long weight) {
		Edge e, rev_e;
		bool success;
		boost::tie(e, success) = boost::add_edge(from, to, G);
		boost::tie(rev_e, success) = boost::add_edge(to, from, G);
		capacitymap[e] = capacity;
		weightmap[e] = weight;
		capacitymap[rev_e] = 0; // reverse edge has no capacity!
		weightmap[rev_e] = -weight;
		revedgemap[e] = rev_e;
		revedgemap[rev_e] = e;
	}
};

// Custom Edge Adder Class, that holds the references
// to the graph, capacity map and reverse edge map
// for ONLY Max flow
// ===================================================
class EdgeAdderMF {
	Graph &G;
	EdgeCapacityMap	&capacitymap;
	ReverseEdgeMap	&revedgemap;

public:
	// to initialize the Object
	EdgeAdderMF(Graph & G, EdgeCapacityMap &capacitymap, ReverseEdgeMap &revedgemap) :
		G(G), capacitymap(capacitymap), revedgemap(revedgemap) {}

	// to use the Function (add an edge)
	void addEdge(int from, int to, long capacity) {
		Edge e, rev_e;
		bool success;
		boost::tie(e, success) = boost::add_edge(from, to, G);
		boost::tie(rev_e, success) = boost::add_edge(to, from, G);
		capacitymap[e] = capacity;
		capacitymap[rev_e] = 0; // reverse edge has no capacity!
		revedgemap[e] = rev_e;
		revedgemap[rev_e] = e;
	}
};

bool calcFlowMarathon(Graph &G, Vertex s, Vertex f, long dist, long qflow) {
	int n = boost::num_vertices(G) - 2;
	Vertex src = n, tgt = n + 1;
	EdgeCapacityMap cmap = boost::get(boost::edge_capacity, G);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G);
	ResidualCapacityMap rcmap = boost::get(boost::edge_residual_capacity, G);
	ReverseEdgeMap remap = boost::get(boost::edge_reverse, G);
	EdgeAdderMCMF eaG(G, cmap, wmap, remap);

	Edge e; bool suc;
	boost::tie(e, suc) = boost::edge(src, s, G);
	if (suc) cmap[e] = qflow;
	else eaG.addEdge(src, s, qflow, 0);
	boost::tie(e, suc) = boost::edge(f, tgt, G);
	if (suc) cmap[e] = qflow;
	else eaG.addEdge(f, tgt, qflow, 0);
	long flow = boost::push_relabel_max_flow(G, src, tgt);
	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	long cost = boost::find_flow_cost(G);
	if (cost == dist * qflow) return true;
	return false;
}
void Marathon2() {
	int n, m, s, f;
	_i >> n >> m >> s >> f;
	Graph G(n), Gbase(n);
	EdgeCapacityMap cmap = boost::get(boost::edge_capacity, G);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G), basewmap = boost::get(boost::edge_weight, Gbase);
	ResidualCapacityMap rcmap = boost::get(boost::edge_residual_capacity, G);
	ReverseEdgeMap remap = boost::get(boost::edge_reverse, G);
	EdgeAdderMCMF eaG(G, cmap, wmap, remap);

	for (int j = 0; j < m; j++) {
		int a, b, c, d; _i >> a >> b >> c >> d;
		if (a == b) continue;
		eaG.addEdge(a, b, c, d);
		eaG.addEdge(b, a, c, d);

		Edge e; bool suc;
		boost::tie(e, suc) = boost::add_edge(a, b, Gbase);
		basewmap[e] = d;
		boost::tie(e, suc) = boost::add_edge(b, a, Gbase);
		basewmap[e] = d;
	}
	std::vector<int> predmap(n);
	std::vector<long> distmap(n);
	boost::dijkstra_shortest_paths(Gbase, s,
		predecessor_map(boost::make_iterator_property_map(predmap.begin(), boost::get(boost::vertex_index, Gbase))).
		distance_map(boost::make_iterator_property_map(distmap.begin(), boost::get(boost::vertex_index, Gbase))));
	if (predmap[f] == f) {
		_o << 0 << _l; return;
	}
	long d = distmap[f];
	_e << "d=" << d << _l;
	long maxFlow = boost::push_relabel_max_flow(G, s, f);
	_e << "maxFlow=" << maxFlow << _l;
	boost::successive_shortest_path_nonnegative_weights(G, s, f);
	long maxFlowCost = boost::find_flow_cost(G);
	if (maxFlowCost == maxFlow * d) {
		_e << "MaxFlow Return!" << _l;
		_o << maxFlow << _l; return;
	}

	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);
	int result = 0;
	int stflow = 1, endflow = maxFlow;
	std::vector<bool> isMinCost(maxFlow + 1);
	isMinCost[stflow] = isMinCost[0] = true;
	while (true) {
		if (endflow - stflow <= 1) {
			isMinCost[endflow] = calcFlowMarathon(G, s, f, d, endflow);
			if (isMinCost[endflow]) {
				result = endflow; break;
			}
			else {
				result = stflow; break;
			}
		}
		int midflow = (stflow + endflow) / 2;
		isMinCost[midflow] = calcFlowMarathon(G, s, f, d, midflow);
		if (isMinCost[midflow]) {
			stflow = midflow;
		}
		else {
			endflow = midflow - 1;
		}
	}
	//for (int flow = 1; flow <= maxFlow; flow++) {
	//	Edge e; bool success;
	//	boost::tie(e, success) = edge(src, s, G);
	//	if (success) {
	//		cmap[e] = flow;
	//	}
	//	else {
	//		eaG.addEdge(src, s, flow, 0);
	//	}
	//	boost::tie(e, success) = edge(f, tgt, G);
	//	if (success) cmap[e] = flow;
	//	else eaG.addEdge(f, tgt, flow, 0);
	//
	//	int nowFlow = boost::push_relabel_max_flow(G, src, tgt);
	//	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	//	int nowCost = boost::find_flow_cost(G);
	//	if (nowCost > d*flow) break;
	//	result = flow;
	//}
	_o << result << _l;
}

// Week 10: avalanche / 100 pts
// 1. calc. distance from a to s
// 2. bipartite graph a->s
// 3. binary search on whether the max matching has a edges
int checkASGraph(int t, std::vector< std::vector<int> > &dist, std::vector<int>& takenout, const std::vector<int>& takenin = std::vector<int>()) {
	int a = dist.size();
	int s = dist[0].size();

	Graph ASGraph(a + s);
	EdgeWeightMap aswmap = boost::get(boost::edge_weight, ASGraph);
	ReverseEdgeMap asrevmap = boost::get(boost::edge_reverse, ASGraph);
	EdgeCapacityMap ascapmap = boost::get(boost::edge_capacity, ASGraph);
	ResidualCapacityMap asrescapmap = boost::get(boost::edge_residual_capacity, ASGraph);
	EdgeAdderMF eaG(ASGraph, ascapmap, asrevmap);
	Vertex src = boost::add_vertex(ASGraph), tgt = boost::add_vertex(ASGraph);

	for (int i = 0; i < a; i++) {
		if (!takenin.empty() && takenin[i] == 1) continue;
		for (int j = 0; j < s; j++) {
			if (dist[i][j] == -1) continue;
			if (dist[i][j] > t) continue;
			eaG.addEdge(i, a + j, 1);
		}
		eaG.addEdge(src, i, 1);
	}
	for (int j = 0; j < s; j++) {
		eaG.addEdge(a + j, tgt, 1);
	}

	int flow = boost::push_relabel_max_flow(ASGraph, src, tgt);

	if (takenout.empty()) takenout = std::vector<int>(a, 0);
	OutEdgeIt ei, eend;
	for (boost::tie(ei, eend) = boost::out_edges(src, ASGraph); ei != eend; ++ei) {
		Vertex u = boost::target(*ei, ASGraph);
		if (asrescapmap[*ei] == 0) {
			takenout[u] = 1;
		}
	}
	return flow;
}
void Avalanche() {
	int n, m, a, s, c, d;
	_i >> n >> m >> a >> s >> c >> d;
	_e << "d=" << d << _l;

	// run Dijkstra on baseGraph
	Graph baseGraph(n);
	EdgeWeightMap basewmap = boost::get(boost::edge_weight, baseGraph);
	for (int j = 0; j < m; j++) {
		char w;
		int x, y, z;
		_i >> w >> x >> y >> z;
		if (w == 'S') {
			Edge e; bool su;
			boost::tie(e, su) = boost::add_edge(x, y, baseGraph);
			basewmap[e] = z;
		}
		else if (w == 'L') {
			Edge e; bool su;
			boost::tie(e, su) = boost::add_edge(x, y, baseGraph);
			basewmap[e] = z;
			boost::tie(e, su) = boost::add_edge(y, x, baseGraph);
			basewmap[e] = z;
		}
	}

	std::vector<Vertex> agents(a), shelters(s);
	std::map< Vertex, std::vector<int> > distmaps;
	for (int i = 0; i < a; i++) _i >> agents[i];
	for (int j = 0; j < s; j++) _i >> shelters[j];

	const int BIG = 1000000000;
	int mindist = BIG, maxdist = 0;
	std::vector< std::vector<int> > ASDist(a);
	for (int i = 0; i < a; i++) {
		ASDist[i] = std::vector<int>(s);
		int ca = agents[i];
		std::map< Vertex, std::vector<int> >::iterator dmit = distmaps.find(ca);
		std::vector<int> distmap;
		if (dmit != distmaps.end()) {
			distmap = dmit->second;
		}
		else {
			std::vector<int> predmap(n);
			distmap = std::vector<int>(n);
			boost::dijkstra_shortest_paths(baseGraph, ca,
				predecessor_map(boost::make_iterator_property_map(predmap.begin(), boost::get(boost::vertex_index, baseGraph))).
				distance_map(boost::make_iterator_property_map(distmap.begin(), boost::get(boost::vertex_index, baseGraph))));

			for (int k = 0; k < n; k++) {
				if (predmap[k] == k && k != ca)
					distmap[k] = -1;
				if (k == ca)
					distmap[k] = 0;
			}
			distmaps.insert(std::make_pair(ca, distmap));
		}

		for (int j = 0; j < s; j++) {
			int cs = shelters[j];
			if (distmap[cs] == -1) {
				ASDist[i][j] = -1;
				continue;
			}
			ASDist[i][j] = distmap[cs];
			mindist = std::min(mindist, ASDist[i][j]);
			maxdist = std::max(maxdist, ASDist[i][j]);
		}
	}

	std::set<int> possiblet;
	std::vector<int> vpt;
	for (int i = 0; i < a; i++)
		for (int j = 0; j < s; j++)
			possiblet.insert(ASDist[i][j]);

	std::set<int>::iterator sit = possiblet.begin();
	for (; sit != possiblet.end(); sit++) {
		vpt.push_back(*sit);
	}
	std::vector<int>::iterator vit = vpt.begin();

	std::vector<int> dummy;
	int flow0 = checkASGraph(vpt[0], ASDist, dummy);
	if (flow0 == a) {
		_o << vpt[0] + d << _l;
		return;
	}
	int st = 0, end = 1;
	int npt = vpt.size();
	while (true) {
		int flowend = checkASGraph(vpt[end], ASDist, dummy);
		if (flowend == a) break;
		st = end;
		end = std::min(end * 2, npt - 1);
	}

	int mid = (st + end) / 2;
	//int st = 0, end = vpt.size() - 1, mid = (st + end) / 2;
	int result = 0;
	while (true) {
		std::vector<int> takenout;
		if (end - st <= 1) {
			int flowst = checkASGraph(vpt[st], ASDist, takenout);
			if (flowst == a) {
				result = vpt[st] + d;
				break;
			}
			int flowend = checkASGraph(vpt[end], ASDist, takenout);
			if (flowend == a) {
				result = vpt[end] + d;
				break;
			}
		}

		int t = vpt[mid];
		int flow = checkASGraph(t, ASDist, takenout);
		if (flow == a) {
			end = mid;
			mid = (st + end) / 2;
		}
		else {
			st = mid + 1;
			mid = (st + end) / 2;
		}
	}
	_o << result << _l;
	//for (; vit != vpt.end(); vit++) {
	//	int t = *vit;
	//	Graph ASGraph;
	//	std::vector<int> takenout;
	//	int flow = checkASGraph(t, ASDist, takenout);
	//	if (flow == a) {
	//		_o << t + d << _l;
	//		return;
	//	}
	//	else if (c == 2) {
	//		std::vector<int> newtakenout;
	//		int flow2 = checkASGraph(t + d, ASDist, newtakenout, takenout);
	//		if (flow2 + flow == a) {
	//			_o << t + d + d << _l;
	//			return;
	//		}
	//	}
	//}

	return;
}

// Week 14, PotW: Fleetrace
void Fleetrace2() {
	typedef	boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
	typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
		boost::property<boost::edge_capacity_t, long,
		boost::property<boost::edge_residual_capacity_t, long,
		boost::property<boost::edge_reverse_t, Traits::edge_descriptor,
		boost::property<boost::edge_weight_t, long> > > > >	Graph; // for MCMF
																   // Interior Property Maps
	typedef	boost::property_map<Graph, boost::edge_capacity_t>::type		EdgeCapacityMap;
	typedef boost::property_map<Graph, boost::edge_weight_t>::type			EdgeWeightMap;
	typedef	boost::property_map<Graph, boost::edge_residual_capacity_t>::type	ResidualCapacityMap;
	typedef	boost::property_map<Graph, boost::edge_reverse_t>::type		ReverseEdgeMap;
	typedef	boost::graph_traits<Graph>::vertex_descriptor			Vertex;
	typedef	boost::graph_traits<Graph>::vertex_iterator				VertexIt;
	typedef	boost::graph_traits<Graph>::edge_descriptor			Edge;
	typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIt;

	int b, s, p; _i >> b >> s >> p;

	Graph G(b + s);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	EdgeWeightMap weightmap = boost::get(boost::edge_weight, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdderMCMF eaG(G, capacitymap, weightmap, revedgemap);
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);

	const int MAXC = 50;
	for (int i = 0; i < p; i++) {
		int bi, si, ci;
		_i >> bi >> si >> ci;
		eaG.addEdge(bi, b + si, 1, MAXC - ci);
	}
	for (int i = 0; i < b; i++) {
		eaG.addEdge(src, i, 1, 0);
	}
	for (int j = 0; j < s; j++) {
		eaG.addEdge(src, b + j, 1, MAXC);
		eaG.addEdge(b + j, tgt, 1, 0);
	}

	long flow = boost::push_relabel_max_flow(G, src, tgt);
	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	long cost = boost::find_flow_cost(G);
	long result = MAXC * flow - cost;
	_o << result << _l;
	return;

}
// Week 14: Fleetrace / 60pts
// Max weight bipartite matching -- do we need to implement Hungarian?
// no we don't
// but I can't get 100 pts because of TL.
void copyGraph(Graph & g, Graph & gc) {
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, g);
	EdgeWeightMap weightmap = boost::get(boost::edge_weight, g);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, g);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, g);

	EdgeCapacityMap capacitymap1 = boost::get(boost::edge_capacity, gc);
	EdgeWeightMap weightmap1 = boost::get(boost::edge_weight, gc);
	ReverseEdgeMap revedgemap1 = boost::get(boost::edge_reverse, gc);
	ResidualCapacityMap rescapacitymap1 = boost::get(boost::edge_residual_capacity, gc);
	EdgeAdderMCMF eaG1(gc, capacitymap1, weightmap1, revedgemap1);
	VertexIt vit, vend;
	for (boost::tie(vit, vend) = boost::vertices(g); vit != vend; vit++) {
		OutEdgeIt oi, oie;
		for (boost::tie(oi, oie) = boost::out_edges(*vit, g); oi != oie; oi++) {
			eaG1.addEdge(boost::source(*oi, gc), boost::target(*oi, gc), capacitymap[*oi], weightmap[*oi]);
		}
	}
}
int calcFlowCost(Graph &G, int fl, Vertex src, Vertex tgt, Vertex supersink) {
	//Graph G1;
	//copyGraph(G, G1);
	//EdgeCapacityMap capacitymap1 = boost::get(boost::edge_capacity, G1);
	//EdgeWeightMap weightmap1 = boost::get(boost::edge_weight, G1);
	//ReverseEdgeMap revedgemap1 = boost::get(boost::edge_reverse, G1);
	//ResidualCapacityMap rescapacitymap1 = boost::get(boost::edge_residual_capacity, G1);
	//EdgeAdderMCMF eaG1(G1, capacitymap1, weightmap1, revedgemap1);
	EdgeCapacityMap capmap = boost::get(boost::edge_capacity, G);

	//Vertex supersrc = boost::add_vertex(G1);
	//Vertex supersink = boost::add_vertex(G1);
	Edge e; bool su;
	boost::tie(e, su) = boost::edge(tgt, supersink, G);
	capmap[e] = fl;
	//eaG1.addEdge(tgt, supersink, fl, 0);

	int flow, cost;
	flow = boost::push_relabel_max_flow(G, src, supersink);
	boost::successive_shortest_path_nonnegative_weights(G, src, supersink);
	cost = boost::find_flow_cost(G);
	return cost;
}
void Fleetrace() {
	typedef	boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
	typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
		boost::property<boost::edge_capacity_t, long,
		boost::property<boost::edge_residual_capacity_t, long,
		boost::property<boost::edge_reverse_t, Traits::edge_descriptor,
		boost::property<boost::edge_weight_t, long> > > > >	Graph; // for MCMF
																   // Interior Property Maps
	typedef	boost::property_map<Graph, boost::edge_capacity_t>::type		EdgeCapacityMap;
	typedef boost::property_map<Graph, boost::edge_weight_t>::type			EdgeWeightMap;
	typedef	boost::property_map<Graph, boost::edge_residual_capacity_t>::type	ResidualCapacityMap;
	typedef	boost::property_map<Graph, boost::edge_reverse_t>::type		ReverseEdgeMap;
	typedef	boost::graph_traits<Graph>::vertex_descriptor			Vertex;
	typedef	boost::graph_traits<Graph>::vertex_iterator				VertexIt;
	typedef	boost::graph_traits<Graph>::edge_descriptor			Edge;
	typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIt;

	int b, s, p; _i >> b >> s >> p;

	Graph G(b + s);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	EdgeWeightMap weightmap = boost::get(boost::edge_weight, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdderMCMF eaG(G, capacitymap, weightmap, revedgemap);

	std::vector<int> coeffs(p), sumcoeffs(p);
	// add edges: boat->sailor
	const int MAXCOEFF = 50;
	for (int i = 0; i < p; i++) {
		int boat, sailor, coeff;
		_i >> boat >> sailor >> coeff;
		coeffs[i] = coeff;
		eaG.addEdge(boat, b + sailor, 1, MAXCOEFF - coeff);
	}
	std::sort(coeffs.begin(), coeffs.end(), [](const int &i1, const int &i2) {return i1 > i2; });
	sumcoeffs[0] = coeffs[0];
	for (int i = 1; i < p; i++) {
		sumcoeffs[i] = sumcoeffs[i - 1] + coeffs[i];
	}

	// add edges: src->boat, sailor->tgt;
	Vertex src = boost::add_vertex(G);
	Vertex tgt = boost::add_vertex(G);
	Vertex supersink = boost::add_vertex(G);
	for (int i = 0; i < b; i++) {
		eaG.addEdge(src, i, 1, 0);
	}
	for (int j = 0; j < s; j++) {
		eaG.addEdge(b + j, tgt, 1, 0);
	}
	eaG.addEdge(tgt, supersink, 0, 0);

	Graph G1;
	copyGraph(G, G1);

	int maxflow = boost::push_relabel_max_flow(G1, (int)src, (int)tgt);
	int costMaxFlow = calcFlowCost(G, maxflow, src, tgt, supersink);
	int specMaxFlow = maxflow*MAXCOEFF - costMaxFlow;
	int costNextFlow = calcFlowCost(G, maxflow - 1, src, tgt, supersink);
	int specNextFlow = (maxflow - 1)*MAXCOEFF - costNextFlow;

	_e << specMaxFlow << "mn" << specNextFlow << _l;
	if (specMaxFlow > specNextFlow) {
		_o << specMaxFlow << _l;
		return;
	}

	int maxSpect = specNextFlow, maxSpectFlow = maxflow - 1;
	int startFlow = -1, endFlow = -1, midFlow = -1;
	int step = 4;
	// 1st: linear search for an interval
	//for (int r = maxflow; r >= 1; ) {
	//	int cost = calcFlowCost(G, r, src, tgt);
	//	int spec = r * MAXCOEFF - cost;
	//	_e << "flow=" << r << ", spec=" << spec << _l;
	//	if (maxSpect <= spec) {
	//		maxSpect = spec;
	//		maxSpectFlow = r;
	//	}
	//	else {
	//		startFlow = std::min(r + 3 * step / 2, maxflow);
	//		endFlow = r;
	//		midFlow = (startFlow + endFlow) / 2;
	//		_e << "start" << startFlow << " end" << endFlow << _l;
	//		break;
	//	}
	//	step *= 2;
	//	r -= r > step ? step : 1;
	//}

	startFlow = maxSpectFlow;
	for (endFlow = 0; endFlow < maxflow; endFlow++) {
		if (sumcoeffs[endFlow] >= maxSpect) break;
	}
	endFlow++;

	midFlow = (startFlow + endFlow + 1) / 2;
	// 2nd: binary search on selected interval
	if (startFlow < 0) {
		_o << maxSpect << _l;
		return;
	}
	maxSpect = 0;
	std::vector<int> specs(startFlow - endFlow + 1);
	int base = endFlow;
	if (!specs[startFlow - base]) {
		//specs[startFlow - base] = startFlow*MAXCOEFF - calcFlowCost(G, startFlow, src, tgt, supersink);
		specs[startFlow - base] = maxSpect;
	}
	if (!specs[endFlow - base]) {
		specs[endFlow - base] = endFlow*MAXCOEFF - calcFlowCost(G, endFlow, src, tgt, supersink);
	}

	while (true) {
		if (startFlow - endFlow <= 2) {
			if (!specs[startFlow - base])
				specs[startFlow - base] = startFlow*MAXCOEFF - calcFlowCost(G, startFlow, src, tgt, supersink);
			if (!specs[endFlow - base])
				specs[endFlow - base] = endFlow*MAXCOEFF - calcFlowCost(G, endFlow, src, tgt, supersink);
			if (!specs[midFlow - base])
				specs[midFlow - base] = midFlow*MAXCOEFF - calcFlowCost(G, midFlow, src, tgt, supersink);
			maxSpect = std::max(specs[startFlow - base], std::max(specs[endFlow - base], specs[midFlow - base]));
			break;
		}

		if (!specs[midFlow - base]) {
			int midCost = calcFlowCost(G, midFlow, src, tgt, supersink);
			int midSpec = midFlow * MAXCOEFF - midCost;
			specs[midFlow - base] = midSpec;
		}
		if (specs[midFlow - base] > specs[endFlow - base] && specs[midFlow - base] < specs[startFlow - base]) {
			endFlow = midFlow + 1;
			midFlow = (startFlow + endFlow) / 2;
			continue;
		}
		else if (specs[midFlow - base] < specs[endFlow - base] && specs[midFlow - base] > specs[startFlow - base]) {
			startFlow = midFlow - 1;
			midFlow = (startFlow + endFlow) / 2;
			continue;
		}

		bool flag = false;
		for (int r = midFlow - 1; r >= endFlow; r--) {
			if (!specs[r - base]) {
				int cost = calcFlowCost(G, r, src, tgt, supersink);
				int spec = r * MAXCOEFF - cost;
				specs[r - base] = spec;
			}
			int spec = specs[r - base];
			if (spec < specs[midFlow - base]) {
				endFlow = midFlow;
				midFlow = (startFlow + endFlow + 1) / 2;
				flag = true;
				break;
			}
			else if (spec > specs[midFlow - base]) {
				startFlow = r;
				midFlow = (startFlow + endFlow + 1) / 2;
				flag = true;
				break;
			}
		}
		if (!flag) {
			endFlow = midFlow;
			midFlow = (startFlow + endFlow + 1) / 2;
		}

		_e << "pos: " << startFlow << " " << midFlow << " " << endFlow << _l;
	}

	//for (int r = startFlow; r >= endFlow; r--) {
	//	int cost = calcFlowCost(G, r, src, tgt);
	//	int spec = r * MAXCOEFF - cost;
	//	_e << "flow=" << r << ", spec=" << spec << _l;
	//	if (maxSpect <= spec) {
	//		maxSpect = spec;
	//		maxSpectFlow = r;
	//	}
	//	else {
	//		break;
	//	}
	//}

	_o << maxSpect << _l;
	_e << maxSpectFlow << " " << maxflow << _l << _l;
	return;

}

// Week 12: Casino Royale
// MCMF?
// Damn it I thought too much about DP.
// Easy version of Car Sharing
void CasinoRoyale() {
	int n, m, l; _i >> n >> m >> l;

	Graph G(n);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G);
	ReverseEdgeMap revmap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescmap = boost::get(boost::edge_residual_capacity, G);
	EdgeCapacityMap cmap = boost::get(boost::edge_capacity, G);
	EdgeAdderMCMF eaG(G, cmap, wmap, revmap);
	Vertex src = boost::add_vertex(G);

	const int MAXQ = 128;
	eaG.addEdge(src, 0, l, 0);
	for (int i = 0; i < n - 1; i++) {
		eaG.addEdge(i, i + 1, l, MAXQ);
	}

	for (int j = 0; j < m; j++) {
		int x, y, q;
		_i >> x >> y >> q;
		eaG.addEdge(x, y, 1, (y - x)*MAXQ - q);
	}

	long flow = boost::push_relabel_max_flow(G, src, n - 1);
	boost::successive_shortest_path_nonnegative_weights(G, src, n - 1);
	long cost = boost::find_flow_cost(G);
	long sumq = flow * MAXQ * (n - 1) - cost;
	_o << sumq << _l;
	return;
}

// Week 13: Bob's Burden
// Split vertices and 3 Dijkstras
inline Vertex getVertex(int i, int j, int k) {
	return (Vertex)(i*(i + 1) / 2 + j);
}
inline bool inRange(int i, int j, int k) {
	return(i >= 0 && i < k && j >= 0 && j < k && i >= j);
}
void BobsBurden() {
	int k; _i >> k;
	std::vector< std::vector<int> > val(k);
	for (int i = 0; i < k; i++) val[i] = std::vector<int>(k);
	// k*(k+1)/2 balls
	int n = k*(k + 1) / 2;
	Graph G(n * 2); // split nodes: other_out -> v_in -> v_out -> other_in
	int nv = boost::num_vertices(G);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G);

	for (int i = 0; i < k; i++) {
		for (int j = 0; j <= i; j++) {
			int vv; _i >> vv;
			val[i][j] = vv;
			Vertex node = getVertex(i, j, k);
			Edge e; bool suc;
			boost::tie(e, suc) = boost::add_edge(node, n + node, G);
			wmap[e] = val[i][j];
		}
	}

	for (int i = 0; i < k; i++) {
		for (int j = 0; j <= i; j++) {
			Vertex v = getVertex(i, j, k);
			if (inRange(i - 1, j - 1, k)) {
				Vertex u = getVertex(i - 1, j - 1, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
			if (inRange(i, j - 1, k)) {
				Vertex u = getVertex(i, j - 1, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
			if (inRange(i - 1, j, k)) {
				Vertex u = getVertex(i - 1, j, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
			if (inRange(i, j + 1, k)) {
				Vertex u = getVertex(i, j + 1, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
			if (inRange(i + 1, j + 1, k)) {
				Vertex u = getVertex(i + 1, j + 1, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
			if (inRange(i + 1, j, k)) {
				Vertex u = getVertex(i + 1, j, k);
				Edge e; bool suc;
				boost::tie(e, suc) = boost::add_edge(n + v, u, G);
				wmap[e] = 0;
			}
		}
	}

	std::vector<Vertex> p11(nv), pk1(nv), pkk(nv);
	std::vector<long> d11(nv), dk1(nv), dkk(nv);

	//boost::connected_components(G, &d11[0]);
	//for (int r = 0; r < nv; r++) {
	//	_e << d11[r] << " ";
	//}_e << _l;

	boost::dijkstra_shortest_paths(G, getVertex(0, 0, k),
		predecessor_map(boost::make_iterator_property_map(p11.begin(), boost::get(boost::vertex_index, G))).
		distance_map(boost::make_iterator_property_map(d11.begin(), boost::get(boost::vertex_index, G))));

	boost::dijkstra_shortest_paths(G, getVertex(k - 1, 0, k),
		predecessor_map(boost::make_iterator_property_map(pk1.begin(), boost::get(boost::vertex_index, G))).
		distance_map(boost::make_iterator_property_map(dk1.begin(), boost::get(boost::vertex_index, G))));

	boost::dijkstra_shortest_paths(G, getVertex(k - 1, k - 1, k),
		predecessor_map(boost::make_iterator_property_map(pkk.begin(), boost::get(boost::vertex_index, G))).
		distance_map(boost::make_iterator_property_map(dkk.begin(), boost::get(boost::vertex_index, G))));

	long minw = 1000000000;
	for (int i = 0; i < k; i++) {
		for (int j = 0; j <= i; j++) {
			Vertex v = getVertex(i, j, k);
			long dist11 = d11[v + n], distk1 = dk1[v], distkk = dkk[v];
			//_e << i << "," << j <<": "<< dist11 << " " << distk1 << " " << distkk << _l;
			long sumw = dist11 + distk1 + distkk;
			if (minw > sumw) {
				minw = sumw;
			}
		}
	}

	_o << minw << _l;
	return;
}

// Week 4: Ant Challenge
// Multiple MST + Dijkstra
void AntChallenge() {
	typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS,
		boost::no_property, boost::property < boost::edge_weight_t, long > > UGraph;
	typedef boost::property_map < UGraph, boost::edge_weight_t >::type UWeightMap;
	typedef boost::graph_traits < UGraph >::edge_descriptor UEdge;
	typedef boost::graph_traits < UGraph >::edge_iterator UEdgeIt;
	typedef boost::graph_traits < UGraph >::out_edge_iterator UOutEdgeIt;
	typedef boost::graph_traits < UGraph >::vertex_descriptor UVertex;

	int n, e, s, a, b;
	_i >> n >> e >> s >> a >> b;

	// Construct the mst for each species
	std::vector<UGraph> graphs(s), msts(s);

	std::vector<UWeightMap> wmaps(s);
	std::vector<UVertex> hives(s);
	for (int k = 0; k < s; k++) {
		graphs[k] = UGraph(n);
		msts[k] = UGraph(n);
		wmaps[k] = boost::get(boost::edge_weight, graphs[k]);
	}
	for (int j = 0; j < e; j++) {
		int t1, t2; _i >> t1 >> t2;
		for (int k = 0; k < s; k++) {
			int w; _i >> w;
			UEdge e; bool suc;
			boost::tie(e, suc) = boost::add_edge(t1, t2, graphs[k]);
			wmaps[k][e] = w;
		}
	}

	UGraph finalGraph(n);
	UWeightMap finalWmap = boost::get(boost::edge_weight, finalGraph);
	for (int k = 0; k < s; k++) {
		// find MST
		_i >> hives[k];
		std::vector<UVertex> p(n);
		boost::prim_minimum_spanning_tree(graphs[k], &p[0]);

		// construct finalGraph = minimum of each MST
		for (int i = 0; i < n; i++) {
			if (p[i] == i)continue;
			UEdge e, efin; bool suc, sfin;
			boost::tie(e, suc) = boost::edge(p[i], i, graphs[k]);
			boost::tie(efin, sfin) = boost::edge(p[i], i, finalGraph);
			if (sfin) {
				if (finalWmap[efin] > wmaps[k][e]) {
					finalWmap[efin] = wmaps[k][e];
				}
			}
			else {
				boost::tie(efin, sfin) = boost::add_edge(p[i], i, finalGraph);
				finalWmap[efin] = wmaps[k][e];
			}
		}
	}

	std::vector<UVertex> p(n);
	std::vector<long> d(n);
	boost::dijkstra_shortest_paths(finalGraph, a,
		predecessor_map(boost::make_iterator_property_map(p.begin(), boost::get(boost::vertex_index, finalGraph))).
		distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, finalGraph))));

	_o << d[b] << _l;
	return;

}

// Week 5: Tracking / 100 pts
// Force Dijkstra to pass k times on certain edges
// Beware of the direction of edges!
void Tracking() {
	typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS,
		boost::no_property, boost::property < boost::edge_weight_t, long > > UGraph;
	typedef boost::property_map < UGraph, boost::edge_weight_t >::type UWeightMap;
	typedef boost::graph_traits < UGraph >::edge_descriptor UEdge;
	typedef boost::graph_traits < UGraph >::edge_iterator UEdgeIt;
	typedef boost::graph_traits < UGraph >::out_edge_iterator UOutEdgeIt;
	typedef boost::graph_traits < UGraph >::vertex_descriptor UVertex;
	struct MyEdge {
		int a; int b; int c;
		MyEdge(int a = 0, int b = 0, int c = 0) : a(a), b(b), c(c) {}
	};

	int n, m, k, x, y;
	_i >> n >> m >> k >> x >> y;

	UGraph G(n*(k + 1));
	int nv = boost::num_vertices(G);
	y += n*k;
	UWeightMap wmap = boost::get(boost::edge_weight, G);
	std::vector<MyEdge> riveredges;
	for (int i = 0; i < m; i++) {
		int a, b, c, d;
		_i >> a >> b >> c >> d;
		if (d == 1) {
			riveredges.push_back(MyEdge(a, b, c));
		}
		for (int l = 0; l <= k; l++) {
			Vertex va = l*n + a;
			Vertex vb = l*n + b;
			UEdge e; bool success;
			boost::tie(e, success) = boost::add_edge(va, vb, G);
			wmap[e] = c;
		}
	}
	int mr = riveredges.size();
	for (int i = 0; i < mr; i++) {
		MyEdge me = riveredges[i];
		for (int l = 0; l < k; l++) {
			Vertex va = l*n + me.a;
			Vertex vb = (l + 1)*n + me.b;
			UEdge e; bool success;
			boost::tie(e, success) = boost::add_edge(va, vb, G);
			wmap[e] = me.c;
			Vertex var = va + n;
			Vertex vbr = vb - n;
			boost::tie(e, success) = boost::add_edge(var, vbr, G);
			wmap[e] = me.c;
		}
	}

	std::vector<UVertex> p(nv);
	std::vector<long> d(nv);
	boost::dijkstra_shortest_paths(G, x,
		predecessor_map(boost::make_iterator_property_map(p.begin(), boost::get(boost::vertex_index, G))).
		distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, G))));

	_o << d[y] << _l;
	return;

}

// Week 10: Return of the Jedi / 40->40->40->100 pts
// Second MST
typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS,
	boost::no_property, boost::property < boost::edge_weight_t, long > > UGraph;
typedef boost::property_map < UGraph, boost::edge_weight_t >::type UWeightMap;
typedef boost::graph_traits < UGraph >::edge_descriptor UEdge;
typedef boost::graph_traits < UGraph >::edge_iterator UEdgeIt;
typedef boost::graph_traits < UGraph >::out_edge_iterator UOutEdgeIt;
typedef boost::graph_traits < UGraph >::vertex_descriptor UVertex;
struct MyEdge {
	int s;
	int t;
	long w;
	MyEdge(int s = 0, int t = 0, long w = 0) :s(s), t(t), w(w) {}
};
bool CompareMyEdgeWST(const MyEdge & e1, const MyEdge & e2) {
	if (e1.w < e2.w) return true;
	if (e1.w > e2.w) return false;
	if (e1.s < e2.s) return true;
	if (e1.s > e2.s) return false;
	if (e1.t < e2.t) return true;
	return false;
}
long calcMSTWeight(UGraph & G, std::vector<UVertex> &p) {
	int n = p.size();
	UWeightMap wmap = boost::get(boost::edge_weight, G);
	long mstWeight = 0;
	for (int i = 0; i < n; i++) {
		if (p[i] != i) {
			UEdge e; bool success;
			boost::tie(e, success) = boost::edge(p[i], i, G);
			if (!success) {
				boost::tie(e, success) = boost::edge(i, p[i], G);
			}
			mstWeight += wmap[e];
		}
	}
	return mstWeight;
}
void ReturnOfTheJedi() {
	typedef std::pair<int, int> E;

	int n, tat; _i >> n >> tat;
	UGraph G(n);
	UWeightMap wmap = boost::get(boost::edge_weight, G);
	//std::vector<MyEdge> myedges;
	std::vector< std::vector<int> > weights(n);
	for (int i = 0; i < n; i++) {
		weights[i] = std::vector<int>(n);
	}
	for (int i = 0; i < n - 1; i++) {
		for (int j = i + 1; j < n; j++) {
			long cost;
			_i >> cost;
			UEdge e; bool success;
			boost::tie(e, success) = boost::add_edge(i, j, G);
			wmap[e] = cost;
			//myedges.push_back(MyEdge(i, j, cost));
			weights[i][j] = weights[j][i] = cost;
		}
	}
	//std::sort(myedges.begin(), myedges.end(), CompareMyEdgeWST);

	std::vector<UVertex> p(n);
	prim_minimum_spanning_tree(G, &p[0]);
	// calc weight sum
	long mstWeight = calcMSTWeight(G, p);

	const long BIG = ((long)1 << 30);
	long minSecond = BIG;
	_e << minSecond << _l;

	// construct MST
	UGraph MST(n);
	for (int i = 0; i < n; i++) {
		if (p[i] != i) {
			boost::add_edge(p[i], i, MST);
		}
	}

	//int minSecond = 100000000;
	for (int i = 0; i < n; i++) {
		// find maximum weight edge for every v
		std::queue<Vertex> vq;
		vq.push(i);
		std::vector<long> maxw(n);
		std::vector<bool> isvisited(n);
		maxw[i] = 0;
		isvisited[i] = true;
		while (!vq.empty()) {
			Vertex u = vq.front();
			vq.pop();
			UOutEdgeIt eit, eend;
			for (boost::tie(eit, eend) = boost::out_edges(u, MST); eit != eend; ++eit) {
				Vertex v = boost::target(*eit, MST);
				if (isvisited[v]) continue;
				//UEdge e; bool success;
				//boost::tie(e, success) = boost::edge(u, v, G);
				long we = weights[u][v];
				maxw[v] = std::max(maxw[u], we);
				vq.push(v);
				isvisited[v] = true;
			}
		}
		int minAdd = 100000000;
		for (int j = i + 1; j < n; j++) {
			if (p[i] == j || p[j] == i) continue; // in MST
												  //UEdge e; bool success;
												  //boost::tie(e, success) = boost::edge(i, j, G);
			long we = weights[i][j];
			int add = we - maxw[j];
			if (add < minAdd) {
				minAdd = add;
			}
		}
		if (minAdd < minSecond)
			minSecond = minAdd;
	}
	minSecond += mstWeight;

	// another 40 pts...
	//for (int i = 0; i < n; i++) {
	//	if (p[i] != i) {
	//		UEdge e; bool success;
	//		boost::tie(e, success) = boost::edge(p[i], i, G);
	//		if (!success) {
	//			boost::tie(e, success) = boost::edge(i, p[i], G);
	//		}
	//
	//		UGraph G1(n);
	//		for (int j = 0; j < n; j++) {
	//			if (p[j] != j && j != i) {
	//				boost::add_edge(p[j], j, G1);
	//			}
	//		}
	//		std::vector<int> components(n);
	//		boost::connected_components(G1, &components[0]);

	//		int cs = std::min((int)p[i], i), ct = std::max((int)p[i], i);
	//		MyEdge curme(cs, ct, wmap[e]);
	//		_e << "check: " << cs << "->" << ct << "=";
	//		_e << wmap[e] << _l;
	//		std::vector<MyEdge>::iterator meit = std::upper_bound(myedges.begin(), myedges.end(), curme, CompareMyEdgeWST);
	//		for (; meit != myedges.end(); ++meit) {
	//			_e << meit->s << "->" << meit->t << "=" << meit->w << _l;

	//			if (components[meit->s] != components[meit->t]) {
	//				long w = meit->w;
	//				if (minSecond > (w - wmap[e])) {
	//					minSecond = (w - wmap[e]);
	//				}
	//				break;
	//			}
	//		}
	//		if (minSecond == 0) break;
	//	}
	//}
	//minSecond += mstWeight;
	//

	// 40 pts
	//for (int i = 0; i < n; i++) {
	//	if (p[i] != i) {
	//		UEdge e; bool success;
	//		boost::tie(e, success) = boost::edge(p[i], i, G);
	//		long wtmp = wmap[e];
	//		wmap[e] = BIG;
	//		std::vector<UVertex> pnow(n);
	//		prim_minimum_spanning_tree(G, &pnow[0]);
	//		long mstWnow = calcMSTWeight(G, pnow);
	//		if (minSecond > mstWnow) {
	//			minSecond = mstWnow;
	//		}
	//		wmap[e] = wtmp;
	//		_e << minSecond << _l;
	//	}
	//}
	_o << minSecond << _l;
	return;
}

// Week 11: Carsharing / 100pts
// MCMF? how to convert to non-negative costs?
// took me a while to figure out non-negative costs
// prove that it is indeed Max Flow (no need to search)
void Carsharing() {
	int N, S; _i >> N >> S;

	Graph G(S);
	EdgeCapacityMap capmap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revmap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapmap = boost::get(boost::edge_residual_capacity, G);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G);
	EdgeAdderMCMF eaG(G, capmap, wmap, revmap);
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);

	std::vector< std::map<int, Vertex> > times(S);
	const int MAXP = 100, BIG = 10000000, FINAL = 100000;
	for (int i = 0; i < S; i++) {
		int l; _i >> l;
		eaG.addEdge(src, i, l, 0);
		times[i].insert(std::make_pair(0, i));
		Vertex last = boost::add_vertex(G);
		eaG.addEdge(last, tgt, BIG, 0);
		times[i].insert(std::make_pair(FINAL, last));
	}
	for (int i = 0; i < N; i++) {
		int s, t, d, a, p;
		_i >> s >> t >> d >> a >> p;
		s--;
		t--;
		std::map<int, Vertex>::iterator mits, mitt;
		mits = times[s].find(d);
		mitt = times[t].find(a);
		Vertex depart, arrival;
		if (mits == times[s].end()) {
			depart = boost::add_vertex(G);
			times[s].insert(std::make_pair(d, depart));
		}
		else depart = mits->second;
		if (mitt == times[t].end()) {
			arrival = boost::add_vertex(G);
			times[t].insert(std::make_pair(a, arrival));
		}
		else arrival = mitt->second;

		eaG.addEdge(depart, arrival, 1, MAXP*(a - d) - p);
	}

	// add edges along every rental station
	for (int i = 0; i < S; i++) {
		std::map<int, Vertex>::iterator mit1, mit2;
		mit1 = times[i].begin();
		//Vertex first = mit1->second;
		//eaG.addEdge(i, first, BIG, MAXP);
		mit2 = times[i].begin(); ++mit2;
		for (; mit2 != times[i].end(); ++mit1, ++mit2) {
			Vertex prev = mit1->second;
			Vertex curr = mit2->second;
			eaG.addEdge(prev, curr, BIG, MAXP*(mit2->first - mit1->first));
		}
		//mit1 = times[i].end(); --mit1;
		//Vertex last = mit1->second;
		//eaG.addEdge(last, tgt, BIG, MAXP);
	}

	long flow = boost::push_relabel_max_flow(G, src, tgt);
	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	//boost::cycle_canceling(G);
	long cost = boost::find_flow_cost(G);
	long profit = flow * MAXP * FINAL - cost;
	//int profit = -cost;
	_o << profit << _l;
	return;

}

// Week 9: Real Estate Market / 100pts
// MCMF
void RealEstate() {
	int N, M, S;
	_i >> N >> M >> S;

	Graph G(N + M + S);
	EdgeCapacityMap capmap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revmap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapmap = boost::get(boost::edge_residual_capacity, G);
	EdgeWeightMap wmap = boost::get(boost::edge_weight, G);
	EdgeAdderMCMF eaG(G, capmap, wmap, revmap);
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);
	Vertex supersrc = boost::add_vertex(G), supertgt = boost::add_vertex(G);

	for (int k = 0; k < S; k++) {
		int l; _i >> l;
		eaG.addEdge(N + M + k, tgt, l, 0);
	}
	for (int j = 0; j < M; j++) {
		int s; _i >> s;
		eaG.addEdge(N + j, N + M + s - 1, 1, 0);
	}
	int bid[100][100] = {};
	const int MAXBID = 100;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			_i >> bid[i][j];
			eaG.addEdge(i, N + j, 1, MAXBID - bid[i][j]);
		}
		eaG.addEdge(src, i, 1, 0);
	}

	int flow = boost::push_relabel_max_flow(G, src, tgt);
	int bestflow = flow;
	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	int cost = boost::find_flow_cost(G);
	int revenue = flow * MAXBID - cost, bestrevenue = revenue;
	_e << _l << "N=" << N << ",S=" << S << " \tmaxflow=" << flow << " \trev=" << revenue << _l;

	//eaG.addEdge(supersrc, src, flow, 0);
	//for (int f = 1; f < flow; f++) {
	//	Edge e;
	//	bool success;
	//	boost::tie(e, success) = boost::edge(supersrc, src, G);
	//	if (success) {
	//		capmap[e] = 0;
	//	}
	//	eaG.addEdge(supersrc, src, f, 0);
	//	int cflow = boost::push_relabel_max_flow(G, supersrc, tgt);
	//	boost::successive_shortest_path_nonnegative_weights(G, supersrc, tgt);
	//	int ccost = boost::find_flow_cost(G);
	//	int crevenue = f * MAXBID - ccost;
	//	if (crevenue > bestrevenue) {
	//		bestrevenue = crevenue;
	//		bestflow = f;
	//	}
	//	_e << "flow=" << f << " \trev=" << crevenue << _l;
	//}
	_o << bestflow << " " << bestrevenue << _l;
	return;
}

// Week 13: Phantom Menace / 100pts
// Min Vertex Cut --> Min Edge Cut
// Split vertices into two: in--1-->out
// Who says I cannot do a problem in 20min!
void PhantomMenace() {
	int n, m, s, d;
	_i >> n >> m >> s >> d;
	Graph G(n * 2);
	EdgeCapacityMap capmap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revmap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapmap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdderMF eaG(G, capmap, revmap);
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);

	// split nodes
	for (int i = 0; i < n; i++) {
		eaG.addEdge(i, i + n, 1);
	}
	int BIG = 10000000;
	for (int j = 0; j < m; j++) {
		int x, y; _i >> x >> y;
		eaG.addEdge(x + n, y, BIG);
	}
	for (int i = 0; i < s; i++) {
		int start; _i >> start;
		eaG.addEdge(src, start, BIG);
	}
	for (int i = 0; i < d; i++) {
		int dest; _i >> dest;
		eaG.addEdge(dest + n, tgt, BIG);
	}

	long flow = boost::push_relabel_max_flow(G, src, tgt);
	_o << flow << _l;
	return;

}

// Week 10, PotW: Surveillance Photos / 100pts
// Different flow entity
// Add vertices to the graph
void SurveillancePhotos() {
	int n, m, k, l; _i >> n >> m >> k >> l;
	std::vector<int> stations(n, 0), photos(n, 0);
	int maxPhotos = 0;
	for (int i = 0; i < k; i++) {
		int pos; _i >> pos;
		stations[pos]++;
	}
	for (int j = 0; j < l; j++) {
		int pos; _i >> pos;
		photos[pos]++;
	}
	for (int i = 0; i < n; i++) {
		if (stations[i] > 0 && photos[i] > 0) {
			int selfPhotos = std::min(stations[i], photos[i]);
			maxPhotos += selfPhotos;
			stations[i] -= selfPhotos;
			photos[i] -= selfPhotos;
		}
	}

	Graph G(2 * n); // 0~n-1: original graph, n~2n-1: added vertices
	EdgeCapacityMap Gcap = boost::get(boost::edge_capacity, G);
	ResidualCapacityMap Grescap = boost::get(boost::edge_residual_capacity, G);
	ReverseEdgeMap Grev = boost::get(boost::edge_reverse, G);
	EdgeAdderMF eaG(G, Gcap, Grev);
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);

	// construct original graph
	for (int j = 0; j < m; j++) {
		int x, y; _i >> x >> y;
		eaG.addEdge(x, y, 1);
	}

	// find P that is reachable by each S
	// add edges to form a graph: src-->station--man-->photo--manw/photo-->station-->tgt
	// BFS
	for (int i = 0; i < n; i++) {
		if (stations[i] > 0) {
			eaG.addEdge(src, i + n, stations[i]);
			eaG.addEdge(i, tgt, stations[i]);
			std::queue<Vertex> vq;
			vq.push(i);
			std::vector<bool> visited(2 * n + 2); // though the only worry is visiting _tgt_...
			while (!vq.empty()) {
				Vertex u = vq.front();
				vq.pop();
				OutEdgeIt ei, eend;
				for (boost::tie(ei, eend) = boost::out_edges(u, G); ei != eend; ei++) {
					if (Gcap[*ei] == 0) continue;
					Vertex v = boost::target(*ei, G);
					if (visited[v]) continue;
					visited[v] = true;
					vq.push(v);
				}
			}
			for (int j = 0; j < n; j++) {
				if (visited[j] && photos[j]) {
					eaG.addEdge(i + n, j + n, 10000000);
				}
			}
		}
		else if (photos[i] > 0) {
			eaG.addEdge(i + n, i, photos[i]);
		}
	}
	long flow = boost::push_relabel_max_flow(G, src, tgt);
	maxPhotos += flow;
	_o << maxPhotos << _l;
	return;
}

// Week 9: Satellites / 100pts
// Bipartite Vertex cover, output the cover
void Satellites() {
	int g, s, l; _i >> g >> s >> l;
	Graph G(g + s);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdderMF eaG(G, capacitymap, revedgemap);

	for (int i = 0; i < l; i++) {
		int gg, ss; _i >> gg >> ss;
		eaG.addEdge(gg, g + ss, 1);
	}
	Vertex src = boost::add_vertex(G);
	Vertex tgt = boost::add_vertex(G);
	for (int i = 0; i < g; i++) {
		eaG.addEdge(src, i, 1);
	}
	for (int i = 0; i < s; i++) {
		eaG.addEdge(i + g, tgt, 1);
	}

	int flow = boost::push_relabel_max_flow(G, src, tgt);

	std::queue<Vertex> vq;
	std::vector<bool> visited(g + s + 2, false);
	vq.push(src);
	visited[src] = true;
	while (!vq.empty()) {
		Vertex u = vq.front();
		vq.pop();
		OutEdgeIt eit, eend;
		for (boost::tie(eit, eend) = boost::out_edges(u, G); eit != eend; eit++) {
			if (rescapacitymap[*eit] == 0) continue;
			Vertex v = boost::target(*eit, G);
			if (visited[v]) continue;
			vq.push(v);
			visited[v] = true;
		}
	}
	std::vector<int> coverS, coverG;
	for (int i = 0; i < g; i++) {
		if (!visited[i]) {
			coverG.push_back(i);
		}
	}
	for (int i = g; i < g + s; i++) {
		if (visited[i]) {
			coverS.push_back(i - g);
		}
	}
	int ncs = coverS.size(), ncg = coverG.size();
	_o << coverG.size() << " " << coverS.size() << _l;
	if (ncs + ncg > 0) {
		for (int i = 0; i < ncg; i++) {
			_o << coverG[i] << " ";
		}
		for (int j = 0; j < ncs; j++) {
			_o << coverS[j] << " ";
		}
		_o << _l;
	}
}

// Week 9: Algocoon / 100pts
// Min Cut with indetermined src/tgt
void Algocoon() {
	int n, m; _i >> n >> m;

	Graph G(n);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdderMF eaG(G, capacitymap, revedgemap);

	for (int i = 0; i < m; i++) {
		int a, b, c;
		_i >> a >> b >> c;
		eaG.addEdge(a, b, c);
	}
	Vertex src = boost::add_vertex(G), tgt = boost::add_vertex(G);
	const long BIG = 100000000;
	eaG.addEdge(src, 0, BIG);
	//eaG.addEdge(n - 1, tgt, BIG);

	long minCutCost = BIG;
	int minCutTgt = 0, minCutSrc = 0;
	for (int i = 1; i < n; i++) {
		eaG.addEdge(i, tgt, BIG);
		long flow = boost::push_relabel_max_flow(G, src, tgt);
		if (flow < minCutCost) {
			minCutCost = flow;
			minCutTgt = i;
		}
		// remove the edge added
		Edge e; bool success;
		boost::tie(e, success) = boost::edge(i, tgt, G);
		if (success) {
			capacitymap[e] = 0;
		}
	}

	// 0 as target
	Edge e; bool success;
	boost::tie(e, success) = boost::edge(src, 0, G);
	capacitymap[e] = 0;
	eaG.addEdge(0, tgt, BIG);
	for (int i = 1; i < n; i++) {
		eaG.addEdge(src, i, BIG);
		long flow = boost::push_relabel_max_flow(G, src, tgt);
		if (flow < minCutCost) {
			minCutCost = flow;
			minCutTgt = 0;
			minCutSrc = i;
		}
		// remove the edge added
		Edge e; bool success;
		boost::tie(e, success) = boost::edge(src, i, G);
		if (success) {
			capacitymap[e] = 0;
		}
	}
	boost::tie(e, success) = boost::edge(0, tgt, G);
	capacitymap[e] = 0;
	_e << minCutCost << "s:" << minCutSrc << " t:" << minCutTgt << _l;
	_o << minCutCost << _l;

	// search for the list of vertices reachable
	// reachable = residual capacity > 0
	eaG.addEdge(src, minCutSrc, BIG);
	eaG.addEdge(tgt, minCutTgt, BIG);
	long flow = boost::push_relabel_max_flow(G, src, minCutTgt);
	std::queue<Vertex> vq;
	std::vector<bool> visited(n + 2);
	std::vector<Vertex> visibles;
	vq.push(src);
	visited[src] = true;
	while (!vq.empty()) {
		Vertex u = vq.front(); vq.pop();
		if (u < n) {
			visibles.push_back(u);
		}
		OutEdgeIt ei, eend;
		for (boost::tie(ei, eend) = boost::out_edges(u, G); ei != eend; ei++) {
			//_e << *ei << ": " << capacitymap[*ei] << ", " << rescapacitymap[*ei] << _l;
			if (rescapacitymap[*ei] == 0) continue;
			//_e << *ei << ": " << capacitymap[*ei] << ", " << rescapacitymap[*ei] << _l;
			Vertex v = boost::target(*ei, G);
			if (visited[v]) continue;
			vq.push(v);
			visited[v] = true;
		}
	}

	_o << visibles.size();
	int nv = visibles.size();
	while (!visibles.empty()) {
		_o << " " << visibles[nv - 1];
		nv--;
		visibles.pop_back();
	}
	_o << _l;

	return;

}

// Week 9: Canteen / 100pts
// MCMF, need to convert to non-negative costs
void Canteen() {
	typedef	boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> Traits;
	typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
		boost::property<boost::edge_capacity_t, long,
		boost::property<boost::edge_residual_capacity_t, long,
		boost::property<boost::edge_reverse_t, Traits::edge_descriptor,
		boost::property<boost::edge_weight_t, long> > > > >	Graph; // for MCMF
	typedef	boost::property_map<Graph, boost::edge_capacity_t>::type		EdgeCapacityMap;
	typedef boost::property_map<Graph, boost::edge_weight_t>::type			EdgeWeightMap;
	typedef	boost::property_map<Graph, boost::edge_residual_capacity_t>::type	ResidualCapacityMap;
	typedef	boost::property_map<Graph, boost::edge_reverse_t>::type		ReverseEdgeMap;
	typedef	boost::graph_traits<Graph>::vertex_descriptor			Vertex;
	typedef	boost::graph_traits<Graph>::vertex_iterator				VertexIt;
	typedef	boost::graph_traits<Graph>::edge_descriptor			Edge;
	typedef boost::graph_traits<Graph>::out_edge_iterator OutEdgeIt;

	int n; _i >> n;
	Graph G(n);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	EdgeWeightMap weightmap = boost::get(boost::edge_weight, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	EdgeAdderMCMF eaG(G, capacitymap, weightmap, revedgemap);

	Vertex src = boost::add_vertex(G);
	Vertex tgt = boost::add_vertex(G);

	int BIAS = 40;
	int nStudents = 0;
	for (int i = 0; i < n; i++) {
		int a, c;
		_i >> a >> c;
		eaG.addEdge(src, i, a, c);
	}
	// only bias the negative weights at the t-edges!
	for (int i = 0; i < n; i++) {
		int s, p; _i >> s >> p;
		nStudents += s;
		eaG.addEdge(i, tgt, s, BIAS - p);
	}
	for (int i = 0; i < n - 1; i++) {
		int v, e; _i >> v >> e;
		eaG.addEdge(i, i + 1, v, e);
	}

	long flow = boost::push_relabel_max_flow(G, src, tgt);
	boost::successive_shortest_path_nonnegative_weights(G, src, tgt);
	//boost::cycle_canceling(G);
	long cost = boost::find_flow_cost(G);

	if (flow >= nStudents) _o << "possible ";
	else _o << "impossible ";
	_e << flow << " " << cost << " " << BIAS*flow - cost << _l;
	_o << flow << " " << BIAS*flow - cost << _l;
	return;
}

typedef EdgeAdderMF EdgeAdder;
// Week 14: Cantonal courier
// bipartite max independent set with weight
// sum of weights in MaxIS = totalWeights - weights in MinVC
void CantonalCourier() {
	typedef long long lint;

	int Z, J;
	_i >> Z >> J;
	std::vector<int> c(Z);
	std::vector<int> p(J);

	for (int i = 0; i < Z; i++) {
		_i >> c[i];
	}
	int sump = 0;
	for (int j = 0; j < J; j++) {
		_i >> p[j];
		sump += p[j];
	}

	Graph G(J + Z);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdder eaG(G, capacitymap, revedgemap);

	// add edges: job->zone
	const int BIG = 10000000;
	for (int j = 0; j < J; j++) {
		int N; _i >> N;
		int zone = 0;
		for (int k = 0; k < N; k++) {
			_i >> zone;
			eaG.addEdge(j, J + zone, BIG);
		}
	}
	// s->job and zone->t
	Vertex s = boost::add_vertex(G);
	Vertex t = boost::add_vertex(G);
	for (int j = 0; j < J; j++) {
		eaG.addEdge(s, j, p[j]);
	}
	for (int i = 0; i < Z; i++) {
		eaG.addEdge(J + i, t, c[i]);
	}

	lint flow = boost::push_relabel_max_flow(G, s, t);
	_o << sump - flow << _l; // calculate what we want and get this
	return;
}

// Week 12: placing knights
inline bool inBoard(int i, int j, int n) {
	return i >= 0 && i < n && j >= 0 && j < n;
}
void PlacingKnights() {
	int n; _i >> n;
	int board[65][65] = {};
	int v_board[65][65] = {};
	int total_places = 0;
	int blacks = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			_i >> board[i][j];
			if (board[i][j]) {
				blacks += ((i + j) % 2 == 0);
				v_board[i][j] = total_places++;
			}
		}
	}

	// check connection--heuristics?
	bool all2conn = true;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (!board[i][j]) continue;
			int conn = 0;
			if (inBoard(i - 2, j - 1, n) && board[i - 2][j - 1]) conn++;
			if (inBoard(i - 2, j + 1, n) && board[i - 2][j + 1]) conn++;
			if (inBoard(i + 2, j - 1, n) && board[i + 2][j - 1]) conn++;
			if (inBoard(i + 2, j + 1, n) && board[i + 2][j + 1]) conn++;
			if (inBoard(i - 1, j - 2, n) && board[i - 1][j - 2]) conn++;
			if (inBoard(i - 1, j + 2, n) && board[i - 1][j + 2]) conn++;
			if (inBoard(i + 1, j - 2, n) && board[i + 1][j - 2]) conn++;
			if (inBoard(i + 1, j + 2, n) && board[i + 1][j + 2]) conn++;
			if (conn < 2) {
				all2conn = false;
				break;
			}
		}
	}
	if (all2conn) {
		_e << "all2conn!" << _l;
		_o << std::max(total_places - blacks, blacks) << _l;
		return;
	}

	Graph G(total_places);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdder eaG(G, capacitymap, revedgemap);
	for (int i = 0; i < n; i++) {
		for (int j = i % 2; j < n; j += 2) {
			// add edges of the bipartite graph
			if (!board[i][j]) continue;
			if (inBoard(i - 2, j - 1, n) && board[i - 2][j - 1]) eaG.addEdge(v_board[i][j], v_board[i - 2][j - 1], 1);
			if (inBoard(i - 2, j + 1, n) && board[i - 2][j + 1]) eaG.addEdge(v_board[i][j], v_board[i - 2][j + 1], 1);
			if (inBoard(i + 2, j - 1, n) && board[i + 2][j - 1]) eaG.addEdge(v_board[i][j], v_board[i + 2][j - 1], 1);
			if (inBoard(i + 2, j + 1, n) && board[i + 2][j + 1]) eaG.addEdge(v_board[i][j], v_board[i + 2][j + 1], 1);
			if (inBoard(i - 1, j - 2, n) && board[i - 1][j - 2]) eaG.addEdge(v_board[i][j], v_board[i - 1][j - 2], 1);
			if (inBoard(i - 1, j + 2, n) && board[i - 1][j + 2]) eaG.addEdge(v_board[i][j], v_board[i - 1][j + 2], 1);
			if (inBoard(i + 1, j - 2, n) && board[i + 1][j - 2]) eaG.addEdge(v_board[i][j], v_board[i + 1][j - 2], 1);
			if (inBoard(i + 1, j + 2, n) && board[i + 1][j + 2]) eaG.addEdge(v_board[i][j], v_board[i + 1][j + 2], 1);
		}
	}

	// add edges from the source and towards the target
	Vertex s = boost::add_vertex(G);
	Vertex t = boost::add_vertex(G);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (!board[i][j]) continue;
			if ((i + j) % 2 == 0) eaG.addEdge(s, v_board[i][j], 1);
			else eaG.addEdge(v_board[i][j], t, 1);
		}
	}

	long flow = boost::push_relabel_max_flow(G, s, t);
	_e << "flow=" << flow << _l;

	// find vertices reachable from s in Residual Graph
	std::queue<Vertex> vq;
	std::vector<bool> visited(total_places + 2);
	for (int i = 0; i < total_places + 2; i++) visited[i] = false;
	vq.push(s);
	while (!vq.empty()) {
		Vertex u = vq.front();
		visited[u] = true;
		OutEdgeIt ei, eend;
		for (boost::tie(ei, eend) = boost::out_edges(u, G); ei != eend; ei++) {
			if (rescapacitymap[*ei] == 0) continue; // residual capacity=0 --> inreachable from s in residual graph
			Vertex v = boost::target(*ei, G);
			if (visited[v]) continue;
			else vq.push(v);
		}
		vq.pop();
	}

	// count the size of maximum independent set
	int black_s = 0, black_t = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (!board[i][j]) continue;
			Vertex u = v_board[i][j];
			if ((i + j) % 2 == 0 && visited[u]) black_s++;
			else if ((i + j) % 2 == 1 && !visited[u]) black_s++;
			else if ((i + j) % 2 == 1 && visited[u]) black_t++;
			else if ((i + j) % 2 == 0 && !visited[u]) black_t++;
		}
	}
	_e << "b_s=" << black_s << ",b_t=" << black_t << _l;
	_o << std::max(black_s, black_t) << _l;
	return;
}

// Functions
// =========
// Function for an individual testcase
void testcases() {
	// Create Graph and Maps
	Graph G(4);
	EdgeCapacityMap capacitymap = boost::get(boost::edge_capacity, G);
	ReverseEdgeMap revedgemap = boost::get(boost::edge_reverse, G);
	ResidualCapacityMap rescapacitymap = boost::get(boost::edge_residual_capacity, G);
	EdgeAdder eaG(G, capacitymap, revedgemap);

	// Add edges
	eaG.addEdge(0, 1, 1); // from, to, capacity
	eaG.addEdge(0, 3, 1);
	eaG.addEdge(2, 1, 1);
	eaG.addEdge(2, 3, 1);

	// Add source and sink
	// Careful: The names 'source' and 'target' are already used for BGL's
	// functions to get the two endpoints of an edge, use 'src' and 'sink'
	// in case you remove the namespace (which we don't recommend).
	Vertex source = boost::add_vertex(G);
	Vertex target = boost::add_vertex(G);
	eaG.addEdge(source, 0, 2);
	eaG.addEdge(source, 2, 1);
	eaG.addEdge(1, target, 2);
	eaG.addEdge(3, target, 1);

	// Calculate flow
	// If not called otherwise, the flow algorithm uses the interior properties
	// - edge_capacity, edge_reverse (read access),
	// - edge_residual_capacity (read and write access).
	long flow1 = boost::push_relabel_max_flow(G, source, target);
	long flow2 = boost::edmonds_karp_max_flow(G, source, target);
	std::cout << flow1 << " == " << flow2 << std::endl;
}


#include <math.h>
#include <iostream>
#include <Eigen/Cholesky>
#include <Eigen/LU>
#include <Eigen/SVD>

#include "fastQP.h"
#include "gurobiQP.h"

#define _USE_MATH_DEFINES

#define MAX_CONSTRS 1000
#define MAX_STATE   1000
#define MAX_ITER    10

using namespace Eigen;
using namespace std;

//template <typename tA, typename tB, typename tC, typename tD, typename tE, typename tF, typename tG>
//int fastQPThatTakesQinv(vector< MatrixBase<tA>* > QinvblkDiag, const MatrixBase<tB>& f, const MatrixBase<tC>& Aeq, const MatrixBase<tD>& beq, const MatrixBase<tE>& Ain, const MatrixBase<tF>& bin, set<int>& active, MatrixBase<tG>& x)
int fastQPThatTakesQinv(vector< MatrixXd* > QinvblkDiag, const VectorXd& f, const MatrixXd& Aeq, const VectorXd& beq, const MatrixXd& Ain, const VectorXd& bin, set<int>& active, VectorXd& x)
{
  int i,d;
  int iterCnt = 0;

  int M_in = bin.size();
  int M = Aeq.rows();
  int N = Aeq.cols();

  if (f.rows() != N) { cerr << "size of f (" << f.rows() << " by " << f.cols() << ") doesn't match cols of Aeq (" << Aeq.rows() << " by " << Aeq.cols() << ")" << endl; return 2; }
  if (beq.rows() !=M) { cerr << "size of beq doesn't match rows of Aeq" << endl; return 2; }
  if (Ain.cols() !=N) { cerr << "cols of Ain doesn't match cols of Aeq" << endl; return 2; };
  if (bin.rows() != Ain.rows()) { cerr << "bin rows doesn't match Ain rows" << endl; return 2; };
  if (x.rows() != N) { cerr << "x doesn't match Aeq" << endl; return 2; }
  int n_active = active.size();

  MatrixXd Aact = MatrixXd(n_active, N);
  VectorXd bact = VectorXd(n_active);

  MatrixXd QinvAteq(N,M);
  VectorXd minusQinvf(N);

  // calculate a bunch of stuff that is constant during each iteration
  int startrow=0;
//  for (typename vector< MatrixBase<tA>* >::iterator iterQinv=QinvblkDiag.begin(); iterQinv!=QinvblkDiag.end(); iterQinv++) {
//  	MatrixBase<tA> *thisQinv = *iterQinv;
  for (vector< MatrixXd* >::iterator iterQinv=QinvblkDiag.begin(); iterQinv!=QinvblkDiag.end(); iterQinv++) {
  	MatrixXd *thisQinv = *iterQinv;
  	int numRow = thisQinv->rows();
  	int numCol = thisQinv->cols();

  	if (numRow == 1 || numCol == 1) {  // it's a vector
  		d = numRow*numCol;
  		if (M>0) QinvAteq.block(startrow,0,d,M)= thisQinv->asDiagonal()*Aeq.block(0,startrow,M,d).transpose();  // Aeq.transpoODse().block(startrow,0,d,N)
			minusQinvf.segment(startrow,d) = -thisQinv->cwiseProduct(f.segment(startrow,d));
			startrow=startrow+d;
  	} else { // potentially dense matrix
			d = numRow;
			if (numRow!=numCol) {
					cerr << "Q is not square! " << numRow << "x" << numCol << "\n";
					return -2;
			}
			if (M>0) QinvAteq.block(startrow,0,d,M) = thisQinv->operator*(Aeq.block(0,startrow,M,d).transpose());  // Aeq.transpose().block(startrow,0,d,N)
			minusQinvf.segment(startrow,d) = -thisQinv->operator*(f.segment(startrow,d));
			startrow=startrow+d;
		}
  	if (startrow>N) {
			cerr << "Q is too big!" << endl;
			return -2;
		}
  }
  if (startrow!=N) { cerr << "Q is the wrong size.  Got " << startrow << "by" << startrow << " but needed " << N << "by" << N << endl; return -2; }

  MatrixXd A;
  VectorXd b;
  MatrixXd QinvAt;
  VectorXd lam, lamIneq;
  VectorXd violated(M_in);
  VectorXd violation;

  while(1) {
    iterCnt++;

    n_active = active.size();
    Aact.resize(n_active,N);
    bact.resize(n_active);

    i=0;
    for (set<int>::iterator iter=active.begin(); iter!=active.end(); iter++) {
    	if (*iter<0 || *iter>=Ain.rows()) {
    		return -3;  // active set is invalid.  exit quietly, because this is expected behavior in normal operation (e.g. it means I should immediately kick out to gurobi)
    	}
      Aact.row(i) = Ain.row(*iter);
      bact(i++) = bin(*iter);
    }

    A.resize(Aeq.rows() + Aact.rows(),N);
    b.resize(beq.size() + bact.size());
    A << Aeq,Aact;
    b << beq,bact;

    if (A.rows() > 0) {
      //Solve H * [x;lam] = [-f;b] using Schur complements, H = [Q,At';A,0];
      QinvAt.resize(QinvAteq.rows(), QinvAteq.cols() + Aact.rows());

      if (n_active>0) {
				int startrow=0;
				for (vector< MatrixXd* >::iterator iterQinv=QinvblkDiag.begin(); iterQinv!=QinvblkDiag.end(); iterQinv++) {
					MatrixXd* thisQinv = (*iterQinv);
					d = thisQinv->rows();
					int numCol = thisQinv->cols();

					if (numCol == 1) {  // it's a vector
						QinvAt.block(startrow,0,d,M+n_active) << QinvAteq.block(startrow,0,d,M), thisQinv->asDiagonal()*Aact.block(0,startrow,n_active,d).transpose();
					} else { // it's a matrix
						QinvAt.block(startrow,0,d,M+n_active) << QinvAteq.block(startrow,0,d,M), thisQinv->operator*(Aact.block(0,startrow,n_active,d).transpose());
					}

					startrow=startrow+d;
				}
      } else {
      	QinvAt = QinvAteq;
      }

      lam.resize(QinvAt.cols());
      lam = -(A*QinvAt).ldlt().solve(b + (f.transpose()*QinvAt).transpose());
      x = minusQinvf - QinvAt*lam;
      lamIneq = lam.tail(lam.size() - M);
    } else {
      x = minusQinvf;
      lamIneq.resize(0);
    }

    if(Ain.rows() == 0) {
      active.clear();
      break;
    }

    set<int> new_active;

    violation = Ain*x - bin;
    for (i=0; i<M_in; i++)
      if (violation(i) >= 1e-6)
      	new_active.insert(i);

    bool all_pos_mults = true;
    for (i=0; i<n_active; i++) {
    	if (lamIneq(i)<0) {
    		all_pos_mults = false;
    		break;
    	}
    }
    if (new_active.empty() && all_pos_mults) {
    	// existing active was AOK
    	break;
    }

    i=0;
    set<int>::iterator iter=active.begin(), tmp;
    while (iter!=active.end()) { // to accomodating inloop erase
  		tmp = iter++;
    	if (lamIneq(i++)<0) {
    		active.erase(tmp);
    	}
    }
    active.insert(new_active.begin(),new_active.end());

    if (iterCnt > MAX_ITER) {
      //Default to calling this method
//      cout << "FastQP max iter reached." << endl;
//       mexErrMsgIdAndTxt("Drake:approximateIKmex:Error", "Max iter reached. Problem is likely infeasible");
      return -1;
    }
  }
  return iterCnt;
}

//template <typename tA, typename tB, typename tC, typename tD, typename tE, typename tF, typename tG>
//int fastQP(vector< MatrixBase<tA>* > QblkDiag, const MatrixBase<tB>& f, const MatrixBase<tC>& Aeq, const MatrixBase<tD>& beq, const MatrixBase<tE>& Ain, const MatrixBase<tF>& bin, set<int>& active, MatrixBase<tG>& x)
int fastQP(vector< MatrixXd* > QblkDiag, const VectorXd& f, const MatrixXd& Aeq, const VectorXd& beq, const MatrixXd& Ain, const VectorXd& bin, set<int>& active, VectorXd& x)
{
  /* min 1/2 * x'QblkDiag'x + f'x s.t A x = b, Ain x <= bin
   * using active set method.  Iterative solve a linearly constrained
   * quadratic minimization problem where linear constraints include
   * Ain(active,:)x == bin(active).  Quit if all dual variables associated
   * with these equations are positive (i.e. they satisfy KKT conditions).
   *
   * Note:
   * fails if QP is infeasible.
   * active == initial rows of Ain to treat as equations.
   * Frank Permenter - June 6th 2013
   *
   * @retval  if feasible then iterCnt, else -1 for infeasible, -2 for input error
   */

	int N = f.rows();

  MatrixXd* Qinv = new MatrixXd[QblkDiag.size()];
  vector< MatrixXd* > Qinvmap;

	#define REG 1e-13
  // calculate a bunch of stuff that is constant during each iteration
  int startrow=0;
  //typedef typename vector< MatrixBase<tA> >::iterator Qiterator;

  int i=0;
  for (vector< MatrixXd* >::iterator iterQ=QblkDiag.begin(); iterQ!=QblkDiag.end(); iterQ++) {
  	MatrixXd* thisQ = *iterQ;
  	int numRow = thisQ->rows();
  	int numCol = thisQ->cols();

  	if (numCol == 1) {  // it's a vector
  		VectorXd Qdiag_mod = thisQ->operator+(VectorXd::Constant(numRow,REG)); // regularize
  		Qinv[i] = Qdiag_mod.cwiseInverse();
  		Qinvmap.push_back( &Qinv[i] );
  		startrow=startrow+numRow;
		} else { // potentially dense matrix
			if (numRow!=numCol) {
				if (numRow==1)
					cerr << "diagonal Q's must be set as column vectors" << endl;
				else
					cerr << "Q is not square! " << numRow << "x" << numCol << endl;
				return -2;
			}

			MatrixXd Q_mod = thisQ->operator+(REG*MatrixXd::Identity(numRow,numRow));
			Qinv[i] = Q_mod.inverse();
  		Qinvmap.push_back( &Qinv[i] );
  		startrow=startrow+numRow;
		}
//  	cout << "Qinv{" << i << "} = " << Qinv[i] << endl;
		if (startrow>N) {
			cerr << "Q is too big!" << endl;
			return -2;
		}
		i++;
  }
  if (startrow!=N) { cerr << "Q is the wrong size.  Got " << startrow << "by" << startrow << " but needed " << N << "by" << N << endl; return -2; }

  int info = fastQPThatTakesQinv(Qinvmap,f,Aeq,beq,Ain,bin,active,x);

  delete[] Qinv;
  return info;
}

/* Example call (allocate inequality matrix, call function, resize inequalites:
  VectorXd binBnd = VectorXd(2*N);
  AinBnd.setZero();
  int numIneq = boundToIneq(ub,lb,AinBnd,binBnd);
  AinBnd.resize(numIneq,N);
  binBnd.resize(numIneq);
*/
/*
int boundToIneq(const VectorXd& uB,const VectorXd& lB, MatrixXd& Ain, VectorXd& bin)
{
    int rCnt = 0;
    int cCnt = 0;

    if (uB.rows()+lB.rows() > A.rows() ) {
        cerr << "not enough memory allocated";
    }

    if (uB.rows()+lB.rows() > b.rows() ) {
        cerr << "not enough memory allocated";
    }

    for (int i = 0; i < lB.rows(); i++ ) {
        if (!isinf(lB(i))) {
            cout << lB(i);
            cout << i;
            Ain(rCnt,cCnt++) = -1;//lB(i);
            bin(rCnt++) = -lB(i);
        }
    }
    cCnt = 0;
    for (int i = 0; i < uB.rows(); i++ ) {
        if (!isinf(uB(i))) {
            Ain(rCnt,cCnt++) = 1;//uB(i);
            bin(rCnt++) = uB(i);
        }
    }

    //resizing inside function all causes exception (why??)
    //A.resize(rCnt,uB.rows());
    return rCnt;
}
*/

template <typename DerivedA,typename DerivedB>
int myGRBaddconstrs(GRBmodel *model, MatrixBase<DerivedA> const & A, MatrixBase<DerivedB> const & b, char sense, double sparseness_threshold = 1e-14)
{
  int i,j,nnz,error=0;
/*
  // todo: it seems like I should just be able to do something like this:
  SparseMatrix<double,RowMajor> sparseAeq(Aeq.sparseView());
  sparseAeq.makeCompressed();
  error = GRBaddconstrs(model,nq_con,sparseAeq.nonZeros(),sparseAeq.InnerIndices(),sparseAeq.OuterStarts(),sparseAeq.Values(),beq.data(),NULL);
*/

  int *cind = new int[A.cols()];
  double* cval = new double[A.cols()];
  for (i=0; i<A.rows(); i++) {
    nnz=0;
    for (j=0; j<A.cols(); j++) {
      if (abs(A(i,j))>sparseness_threshold) {
        cval[nnz] = A(i,j);
        cind[nnz++] = j;
      }
    }
    error = GRBaddconstr(model,nnz,cind,cval,sense,b(i),NULL);
    if (error) break;
  }

  delete[] cind;
  delete[] cval;
  return error;
}

//template <typename tA, typename tB, typename tC, typename tD, typename tE>
//GRBmodel* gurobiQP(GRBenv *env, vector< MatrixBase<tA>* > QblkDiag, VectorXd& f, const MatrixBase<tB>& Aeq, const MatrixBase<tC>& beq, const MatrixBase<tD>& Ain, const MatrixBase<tE>& bin, VectorXd& lb, VectorXd& ub, set<int>& active, VectorXd& x)
GRBmodel* gurobiQP(GRBenv *env, vector< MatrixXd* > QblkDiag, VectorXd& f, const MatrixXd& Aeq, const VectorXd& beq,
  const MatrixXd& Ain, const VectorXd& bin, VectorXd& lb, VectorXd& ub, set<int>& active, VectorXd& x, double active_set_slack_tolerance)
{
	// Note: f,lb, and ub are VectorXd instead of const MatrixBase templates because i want to be able to call f.data() on them

	// NOTE:  this allocates memory for a new GRBmodel and returns it. (you should delete this object when you're done with it)
	// NOTE:  by convention here, the active set indices correspond to Ain,bin first, then lb, then ub.

  GRBmodel *model = NULL;

  int method;  GRBgetintparam(env,"method",&method);

  int i,j,nparams = f.rows(),Qi,Qj;
  double *lbdata = NULL, *ubdata=NULL;
  if (lb.rows()==nparams) lbdata = lb.data();
  if (ub.rows()==nparams) ubdata = ub.data();
  CGE (GRBnewmodel(env,&model,"QP",nparams,NULL,lbdata,ubdata,NULL,NULL), env);

  int startrow=0,d;
  for (vector< MatrixXd* >::iterator iterQ=QblkDiag.begin(); iterQ!=QblkDiag.end(); iterQ++) {
  	MatrixXd* Q=*iterQ;

    // WARNING:  If there are no constraints, then gurobi clearly solves a different problem: min 1/2 x'Qx + f'x
    //  				 This is very strange; see the solveWGUROBI method in QuadraticProgram
    if (method==2) //&& (Aeq.rows()+Ain.rows()>0))
      *Q = .5* (*Q);

  	if (Q->rows() == 1 || Q->cols() == 1) {  // it's a vector
  		d = Q->rows()*Q->cols();
  	  for (i=0; i<d; i++) {
  	  	Qi=i+startrow;
  	  	CGE (GRBaddqpterms(model,1,&Qi,&Qi,&(Q->operator()(i))), env);
  	  }
  	  startrow=startrow+d;
  	} else { // potentially dense matrix
  		d = Q->rows();
  		if (d!=Q->cols()) {
  			cerr << "Q is not square! " << Q->rows() << "x" << Q->cols() << "\n";
  			return NULL;
  		}

  	  for (i=0; i<d; i++)
    	  for (j=0; j<d; j++) {
    	  	Qi=i+startrow; Qj = j+startrow;
    	  	CGE (GRBaddqpterms(model,1,&Qi,&Qj,&(Q->operator()(i,j))), env);
    	  }
  	  startrow=startrow+d;
  	}
  	if (startrow>nparams) {
  		cerr << "Q is too big!" << endl;
  		return NULL;
  	}
  }

  CGE (GRBsetdblattrarray(model,"Obj",0,nparams,f.data()), env);

  if (Aeq.rows()>0) CGE (myGRBaddconstrs(model,Aeq,beq,GRB_EQUAL, 1e-18), env);
  if (Ain.rows()>0) CGE (myGRBaddconstrs(model,Ain,bin,GRB_LESS_EQUAL, 1e-18), env);

  CGE (GRBupdatemodel(model), env);
  CGE (GRBoptimize(model), env);

  CGE (GRBgetdblattrarray(model, GRB_DBL_ATTR_X, 0, nparams, x.data()), env);

  VectorXd slack(Ain.rows());
  CGE (GRBgetdblattrarray(model, "Slack", Aeq.rows(), Ain.rows(), slack.data()), env);

  int offset=0;
  active.clear();
  for (int i=0; i<Ain.rows(); i++) {
  	if (slack(i)<active_set_slack_tolerance)
  		active.insert(i);
  }
  offset = Ain.rows();
  if (lb.rows()==nparams)
  	for (int i=0; i<nparams; i++)
  		if (x(i)-lb(i) < active_set_slack_tolerance)
  			active.insert(offset+i);
  if (ub.rows()==nparams)
  	for (int i=0; i<nparams; i++)
  		if (ub(i)-x(i) < active_set_slack_tolerance)
  			active.insert(offset+i+nparams);

  return model;
}

GRBmodel* gurobiActiveSetQP(GRBenv *env, vector< MatrixXd* > QblkDiag, VectorXd& f, const MatrixXd& Aeq, const VectorXd& beq,
  const MatrixXd& Ain, const VectorXd& bin, VectorXd& lb, VectorXd& ub, int* &vbasis, int vbasis_len, int* &cbasis, int cbasis_len, VectorXd& x)
{
  // NOTE:  this allocates memory for a new GRBmodel and returns it. (you should delete this object when you're done with it)
  // NOTE:  by convention here, the active set indices correspond to Ain,bin first, then lb, then ub.
  GRBmodel *model = NULL;

  int method;  GRBgetintparam(env,"method",&method);
  if (!(method==0 || method==1)) {
    cerr<< "gurobiActiveSetQP: method should be 0 or 1" << endl;
    return NULL;
  }

  int i,j,nparams = f.rows(),Qi,Qj;
  double *lbdata = NULL, *ubdata=NULL;
  if (lb.rows()==nparams) lbdata = lb.data();
  if (ub.rows()==nparams) ubdata = ub.data();
  CGE (GRBnewmodel(env,&model,"QP",nparams,NULL,lbdata,ubdata,NULL,NULL), env);

  int startrow=0,d;
  for (vector< MatrixXd* >::iterator iterQ=QblkDiag.begin(); iterQ!=QblkDiag.end(); iterQ++) {
    MatrixXd* Q=*iterQ;

	*Q = .5* (*Q);

    if (Q->rows() == 1 || Q->cols() == 1) {  // it's a vector
      d = Q->rows()*Q->cols();
      for (i=0; i<d; i++) {
        Qi=i+startrow;
        CGE (GRBaddqpterms(model,1,&Qi,&Qi,&(Q->operator()(i))), env);
      }
      startrow=startrow+d;
    } else { // potentially dense matrix
      d = Q->rows();
      if (d!=Q->cols()) {
        cerr << "Q is not square! " << Q->rows() << "x" << Q->cols() << "\n";
        return NULL;
      }

      for (i=0; i<d; i++)
        for (j=0; j<d; j++) {
          Qi=i+startrow; Qj = j+startrow;
          CGE (GRBaddqpterms(model,1,&Qi,&Qj,&(Q->operator()(i,j))), env);
        }
      startrow=startrow+d;
    }
    if (startrow>nparams) {
      cerr << "Q is too big!" << endl;
      return NULL;
    }
  }

  CGE (GRBsetdblattrarray(model,"Obj",0,nparams,f.data()), env);

  if (Aeq.rows()>0) CGE (myGRBaddconstrs(model,Aeq,beq,GRB_EQUAL, 1e-18), env);
  if (Ain.rows()>0) CGE (myGRBaddconstrs(model,Ain,bin,GRB_LESS_EQUAL, 1e-18), env);

  CGE (GRBupdatemodel(model), env);

  int numvars; CGE(GRBgetintattr(model,"NumVars",&numvars), env);
  if (numvars == vbasis_len) {
    CGE (GRBsetintattrarray(model, "VBasis", 0, numvars, vbasis), env);
  }
  else {
    delete[] vbasis;
    vbasis = new int[numvars];
  }

  int numconstr; CGE(GRBgetintattr(model,"NumConstrs",&numconstr),env);
  if (numconstr == cbasis_len) {
    CGE (GRBsetintattrarray(model, "CBasis", 0, numconstr, cbasis), env);
  }
  else {
    delete[] cbasis;
    cbasis = new int[numconstr];
  }
  CGE (GRBoptimize(model), env);

  CGE (GRBgetdblattrarray(model, GRB_DBL_ATTR_X, 0, nparams, x.data()), env);

  CGE (GRBgetintattrarray(model, "VBasis", 0, numvars, vbasis), env);
  CGE (GRBgetintattrarray(model, "CBasis", 0, numconstr, cbasis), env);

  return model;
}

/*
template int fastQP(vector< MatrixBase<MatrixXd>* > QblkDiag, const MatrixBase< Map<VectorXd> >&, const MatrixBase< Map<MatrixXd> >&, const MatrixBase< Map<VectorXd> >&, const MatrixBase< Map<MatrixXd> >&, const MatrixBase< Map<VectorXd> >&, set<int>&, MatrixBase< Map<VectorXd> >&);
template GRBmodel* gurobiQP(GRBenv *env, vector< MatrixBase<MatrixXd>* > QblkDiag, VectorXd& f, const MatrixBase< Map<MatrixXd> >& Aeq, const MatrixBase< Map<VectorXd> >& beq, const MatrixBase< Map<MatrixXd> >& Ain, const MatrixBase< Map<VectorXd> >&bin, VectorXd& lb, VectorXd& ub, set<int>&, VectorXd&);
template GRBmodel* gurobiQP(GRBenv *env, vector< MatrixBase<MatrixXd>* > QblkDiag, VectorXd& f, const MatrixBase< MatrixXd >& Aeq, const MatrixBase< VectorXd >& beq, const MatrixBase< MatrixXd >& Ain, const MatrixBase< VectorXd >&bin, VectorXd&lb, VectorXd&ub, set<int>&, VectorXd&);
*/

/*
template int fastQP(vector< MatrixBase< VectorXd > >, const MatrixBase< VectorXd >&, const MatrixBase< Matrix<double,-1,-1,RowMajor,1000,-1> >&, const MatrixBase< Matrix<double,-1,1,0,1000,1> >&, const MatrixBase< Matrix<double,-1,-1,RowMajor,1000,-1> >&, const MatrixBase< Matrix<double,-1,1,0,1000,1> >&, set<int>&, MatrixBase< VectorXd >&);
*/


#include "../gain_utils.h"

#include <UnitTest++/UnitTest++.h>
#include <iostream>
#include <sstream>
#include <boost/filesystem.hpp>

#include <cmath>
using namespace coela;
using namespace std;
/*
SUITE(gain_utils){
    string test_suite_output_dir= string(UnitTestSuite::GetSuiteName()) + "_tests/";

    TEST(Run_Standard_Suite_Setup){
        cout << "*** \""<< UnitTestSuite::GetSuiteName() <<"\" unit tests running ***" <<endl;
        boost::filesystem::create_directories(test_suite_output_dir);
    }

    TEST(histogram_convolution){
        map<int, double> single_point_hist;
        double first_point_count=10000;

        double sigma = 10.0;
        int peak_val = 100;
        for (int i = -10*sigma+peak_val; i< 10*sigma+peak_val; ++i){
            single_point_hist[i] = 0;
        }
        single_point_hist[peak_val] = first_point_count;

        map<int, double> simple_conv =
            gain_utils::convolve_histogram_with_gaussian(single_point_hist, 10.0, 50);

        //The convolved output is only valid in regions where we can apply a full kernel
        // i.e. a subset of the original histogram
        int original_min = gain_utils::get_min_value_pair(single_point_hist).first;

        int convolved_min = gain_utils::get_min_value_pair(simple_conv).first;
        CHECK(convolved_min > original_min);

        double convolved_counts = gain_utils::sum_counts_in_histogram(simple_conv);
        CHECK_CLOSE(first_point_count, convolved_counts, first_point_count*0.005);

        gain_utils::write_histogram_data_to_file(single_point_hist,
                test_suite_output_dir+"single_point_hist.txt");
        gain_utils::write_histogram_data_to_file(simple_conv,
                test_suite_output_dir+"single_point_conv_hist.txt");

        map<int, double> exp_hist;
        for (int x=101; x!=16000; ++x){
            exp_hist[x] = 0.05 * exp(-x/10.0);
        }

        map<int, double> exp_conv=
            gain_utils::convolve_histogram_with_gaussian(exp_hist, 10.0, 50);

        gain_utils::write_histogram_data_to_file(exp_hist,
                test_suite_output_dir+"exp_hist.txt");
        gain_utils::write_histogram_data_to_file(exp_conv,
                test_suite_output_dir+"exp_conv_hist.txt");

    }

    TEST(CICIR_histogram){
        map<int,long> blank_hist;
        for (int value=0; value!=1500; value++){
            blank_hist[value] = 0;
        }

        gain_utils::FullEmccdHistogramFitFCN full_model(blank_hist);
        double gain= 100;
        int N_CICIR_pix =  500000;
        int max_val_to_generate = 12000;
        int N_EM_stages = 604;

        map<int, double> CICIR_hist =
            full_model.get_CICIR_model_histogram(gain, N_CICIR_pix ,
                                            N_EM_stages,
                                            max_val_to_generate);

        double hist_counts = gain_utils::sum_counts_in_histogram(CICIR_hist);
//        cerr<<"hist counts " << hist_counts / N_CICIR_pix <<endl;
        CHECK_CLOSE(N_CICIR_pix , hist_counts, N_CICIR_pix*0.01);

        double mean_CICIR_gain =0.0;
        for (map<int,double>::const_iterator it=CICIR_hist.begin();
                it!=CICIR_hist.end(); ++it){
            mean_CICIR_gain += it->first*it->second;
        }
        mean_CICIR_gain /=hist_counts;
        double expected_mean_gain = gain / log(gain);
        CHECK_CLOSE(expected_mean_gain, mean_CICIR_gain, expected_mean_gain*0.05);

//        cout<<" Mean gain: " <<mean_gain<<"; Expected mean gain: "<< expected_mean_gain<<endl;

    }

    TEST(calculate_thresholded_SNR_runs){
//        const double RO=10;
//        const double gain= 100;
//        const double light_level=0.1;
//        const double dark_current_level=0.05;
//        const double CICIR_rate=0.05;
//        const double threshold=0.3;
//
//        gain_utils::ThresholdedSnrCalculator calc1(
//                RO,
//                gain,
//                light_level,
//                dark_current_level,
//                CICIR_rate
//                );
//
//        double SNR = calc1.calc_SNR_at_threshold(threshold);
    }
    TEST(thresholded_SNR_converges_to_ideal_case){

        const double RO=1;
        const double gain= 100;
        const double light_level=0.1;
        const double CICIR_rate=0.00;
        const double threshold=0.1;

        gain_utils::ThresholdedSnrCalculator ideal_case(
                RO,
                gain,
                light_level,
                CICIR_rate
                );

        double SNR = ideal_case.calc_SNR_at_threshold(threshold);

        double ideal_SNR = sqrt(light_level);
//        cerr<<"ideal SNR" <<ideal_SNR<<endl;
//        cerr<<"calc SNR" <<SNR<<endl;

        CHECK(SNR < ideal_SNR);
        CHECK_CLOSE(ideal_SNR, SNR, ideal_SNR*0.1);
    }

//
//    TEST(readout_model_consistency){
//        gain_utils::GainData model_pars;
//        model_pars.bias_pedestal = 25;
//        model_pars.readout_sigma = 12.5;
//        model_pars.N_dark_pix = 100000;
//
//        model_pars.N_light_pix=0;
//        model_pars.gain=100;
//        model_pars.N_serial_CIC_pix=0;
//
//        vector<double> model_pars_vec;
//        model_pars_vec.push_back(model_pars.bias_pedestal);
//        model_pars_vec.push_back(model_pars.N_dark_pix);
//        model_pars_vec.push_back(model_pars.readout_sigma);
//        model_pars_vec.push_back(model_pars.N_light_pix);
//        model_pars_vec.push_back(model_pars.gain);
//        model_pars_vec.push_back(model_pars.N_serial_CIC_pix);
//
//        map<int,long> blank_hist;
//        for (int value=0; value!=15000; value++){
//            blank_hist[value] = 0;
//        }
//        gain_utils::FullEmccdHistogramFitFCN full_model(blank_hist);
//
//        map<int, double> model_hist = full_model.get_model_histogram(model_pars_vec);
//
//        map<int, long> approx_model_hist;
//        for (map<int,double>::const_iterator it = model_hist.begin();
//                it!=model_hist.end(); ++it){
//            approx_model_hist[ it->first ] = rint(it->second);
//        }
//
//
//        vector<double> gpars = gain_utils::fit_readout_gaussian(approx_model_hist);
//
//        CHECK_CLOSE(model_pars.bias_pedestal, gpars[0], 1.0);
//        CHECK_CLOSE(model_pars.readout_sigma, gpars[2], 1.0);
//
//        cerr<<"model / fit bias:" << model_pars.bias_pedestal <<" , "<< gpars[0]<<endl;
//        cerr<<"model / fit ROsig:" << model_pars.readout_sigma <<" , "<< gpars[2]<<endl;
//
//        //Now check the full fit works too:
//    }

}
 */


// Copyright Oleg Maximenko 2014.
// 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://github.com/svgpp/svgpp for library home page.

#pragma once

#include <svgpp/definitions.hpp>
#include <svgpp/parser/detail/common.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi_grammar.hpp>

namespace svgpp
{

namespace qi = boost::spirit::qi;

template <
  class PropertySource,
  class Iterator,
  class AngleFactory,
  class Number = double
>
class angle_grammar:
  public qi::grammar<Iterator, typename AngleFactory::angle_type(), qi::locals<Number> >
{
  typedef angle_grammar<PropertySource, Iterator, AngleFactory, Number> this_type;
public:
  typedef typename AngleFactory::angle_type angle_type;

  angle_grammar()
    : this_type::grammar(rule_)
  {
    namespace phx = boost::phoenix;
    using qi::_1;
    using qi::_a;
    using qi::_val;
    using qi::lit;

    rule_
        =   number_ [_a = _1]
            >>  detail::no_case_if_css(PropertySource())
                [
                    ( lit("grad")
                          [_val = phx::bind(&angle_grammar::call_make_angle<tag::angle_units::grad>, _a)]
                    | lit("rad")
                          [_val = phx::bind(&angle_grammar::call_make_angle<tag::angle_units::rad>, _a)]
                    | ( -lit("deg") )
                          [_val = phx::bind(&angle_grammar::call_make_angle<tag::angle_units::deg>, _a)]
                    )
                ];
  }

private:
  typename this_type::start_type rule_;
  qi::real_parser<Number, detail::number_policies<Number, PropertySource> > number_;

  template<class UnitsTag>
  static angle_type call_make_angle(Number value)
  {
    return AngleFactory::create(value, UnitsTag());
  }
};

}


/**
   \file aga.hpp
   \brief asexual genetic algorithm method
   \author Junhua Gu
*/

#ifndef AGA_METHOD
#define AGA_METHOD
#define OPT_HEADER
#include <core/optimizer.hpp>
//#include <blitz/array.h>
#include <limits>
#include <cstdlib>
#include <core/opt_traits.hpp>
#include <cassert>
#include <cmath>
#include <ctime>
#include <vector>
#include <algorithm>
/*
 *
*/
#include <iostream>
using std::cout;
using std::endl;

namespace opt_utilities
{

  template <typename rT,typename pT>
  struct vp_pair
  {
    rT v;
    pT p;
  };

  template <typename rT,typename pT>
  class vp_comp
  {
  public:
    bool operator()(const vp_pair<rT,pT>& x1,
		    const vp_pair<rT,pT>& x2)
    {
      return x1.v<x2.v;
    }
  };

  /**
     \brief Implement of the asexual genetic algorithm
     2009A&A...501.1259C
     http://adsabs.harvard.edu/abs/2009arXiv0905.3712C
     \tparam rT return type of the object function
     \tparam pT parameter type of the object function
   */
  template <typename rT,typename pT>
  class aga_method
    :public opt_method<rT,pT>
  {
  public:
    typedef pT array1d_type;
  private:
    int n1,n2,n0;
    func_obj<rT,pT>* p_fo;
    optimizer<rT,pT>* p_optimizer;
    rT threshold;
    pT lower_bound;
    pT upper_bound;

    typename element_type_trait<pT>::element_type decay_factor;
    pT reproduction_box;
    std::vector<vp_pair<rT,pT> > samples;
    std::vector<pT> buffer;
    mutable bool bstop;
  private:
    typename element_type_trait<pT>::element_type uni_rand
    (typename element_type_trait<pT>::element_type x1,
     typename element_type_trait<pT>::element_type x2)
    {
      return rand()/(double)RAND_MAX*(x2-x1)+x1;
    }

  private:
    const char* do_get_type_name()const
    {
      return "asexual genetic algorithm";
    }

    rT func(const pT& x)
    {
      assert(p_fo!=0);
      return p_fo->eval(x);
    }

  public:
    aga_method(int _n1,int _n2)
      :n1(_n1),n2(_n2),n0(n1*n2+n1),
       p_fo(0),p_optimizer(0),threshold(1e-4),
       decay_factor(.999),
       samples(n1*n2+n1)
    {
    }

    aga_method()
      :n1(50),n2(20),n0(n1*n2+n1),
       p_fo(0),p_optimizer(0),threshold(1e-4),
       decay_factor(.999),
       samples(n1*n2+n1)
    {
    }

    virtual ~aga_method()
    {
    };

    aga_method(const aga_method<rT,pT>& rhs)
      :n1(rhs.n1),n2(rhs.n2),n0(rhs.n0),
       p_fo(rhs.p_fo),p_optimizer(rhs.p_optimizer),
       threshold(rhs.threshold),
       decay_factor(rhs.decay_factor),
       samples(rhs.samples)
    {
    }

    aga_method<rT,pT>& operator=(const aga_method<rT,pT>& rhs)
    {
      threshold=rhs.threshold;
      p_fo=rhs.p_fo;
      p_optimizer=rhs.p_optimizer;
      samples=rhs.samples;
      n1=rhs.n1;
      n2=rhs.n2;
      n0=rhs.n0;
    }

    void set_decay_factor(typename element_type_trait<pT>::element_type _decay_factor)
    {
      decay_factor=_decay_factor;
    }

    opt_method<rT,pT>* do_clone()const
    {
      return new aga_method<rT,pT>(*this);
    }

    void do_set_start_point(const array1d_type& p)
    {
      for(size_t i=0;i<samples.size();++i)
	{
	  //  cout<<i<<" ";
	  resize(samples[i].p,get_size(p));
	  //	  std::cout<<samples[i].p.size()<<std::endl;;
	  for(size_t j=0;j<get_size(p);++j)
	    {
	      set_element(samples[i].p,j,
			  uni_rand(get_element(lower_bound,j),
				   get_element(upper_bound,j))
			  );
	    }
	}

    }

    array1d_type do_get_start_point()const
    {
      return array1d_type();
    }

    void do_set_lower_limit(const array1d_type& p)
    {
      opt_eq(lower_bound,p);
    }

    array1d_type do_get_lower_limit()const
    {
      return lower_bound;
    }

    void do_set_upper_limit(const array1d_type& p)
    {
      opt_eq(upper_bound,p);
    }

    array1d_type do_get_upper_limit()const
    {
      return upper_bound;
    }

    void do_set_precision(rT t)
    {
      threshold=t;
    }

    rT do_get_precision()const
    {
      return threshold;
    }

    void do_set_optimizer(optimizer<rT,pT>& o)
    {
      p_optimizer=&o;
      p_fo=p_optimizer->ptr_func_obj();
    }

    bool iter()
    {
      rT sum2=0;
      rT sum=0;
      for(size_t i=0;i<samples.size();++i)
	{
	  samples[i].v=func(samples[i].p);
	  sum2+=samples[i].v*samples[i].v;
	  sum+=samples[i].v;
	}

      std::sort(samples.begin(),samples.end(),vp_comp<rT,pT>());
      if(sum2/samples.size()-pow(sum/samples.size(),2)<threshold)
	{
	  return false;
	}
      pT lb(get_size(samples[0].p));
      pT ub(get_size(samples[0].p));
      for(int i=0;i<n2&&!bstop;++i)
	{
	  pT p(samples[i].p);
	  for(size_t j=0;j<get_size(p);++j)
	    {
	      if(i==0)
		{
		  ub[j]=p[j];
		  lb[j]=p[j];
		}
	      ub[j]=std::max(ub[j],p[j]);
	      lb[j]=std::min(lb[j],p[j]);

	      set_element(p,j,
			  get_element(p,j)+
			  uni_rand(-get_element(reproduction_box,j),
				   get_element(reproduction_box,j)));
	      if(get_element(p,j)>get_element(upper_bound,j))
		{
		  set_element(p,j,get_element(upper_bound,j));
		}
	      if(get_element(p,j)<get_element(lower_bound,j))
		{
		  set_element(p,j,get_element(lower_bound,j));
		}
	    }
	  buffer[i]=p;
	}
      if(bstop)
	{
	  return false;
	}
      for(int i=0;i<n1&&!bstop;++i)
	{
	  for(int j=0;j<n2&&!bstop;++j)
	    {
	      pT p(samples[i].p);
	      for(size_t k=0;k<get_size(p);++k)
		{
		  set_element(samples[i*n2+j+n1].p,k,
			      (get_element(samples[i].p,k)+
			       get_element(buffer[j],k))/2.);

		  ub[k]=std::max(ub[k],samples[i*n2+j+n1].p[k]);
		  lb[k]=std::min(lb[k],samples[i*n2+j+n1].p[k]);
		}
	    }
	}
      if(bstop)
	{
	  return false;
	}
      double n_per_dim=pow((double)n0,1./get_size(lower_bound));
      for(size_t i=0;i<get_size(reproduction_box);++i)
	{
	  //	  set_element(reproduction_box,i,
	  //get_element(reproduction_box,i)*decay_factor);
	  set_element(reproduction_box,i,
		      (get_element(ub,i)-
		       get_element(ub,i))/n_per_dim);

	}
      return true;
    }

    pT do_optimize()
    {
      bstop=false;
      srand(time(0));
      buffer.resize(n2);
      double n_per_dim=pow((double)n0,1./get_size(lower_bound));
      resize(reproduction_box,get_size(lower_bound));

      for(size_t i=0;i<get_size(lower_bound);++i)
	{

	  set_element(reproduction_box,i,
		      (get_element(upper_bound,i)-
		       get_element(lower_bound,i))/n_per_dim);
	}

      while(iter()&&!bstop){}

      return samples.begin()->p;
    }

    void do_stop()
    {
      bstop=true;
    }

  };

}

#endif
//EOF


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.

// Copyright (c) 2016-2018 Oracle and/or its affiliates.
// Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HPP
#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HPP

#include <boost/geometry/formulas/area_formulas.hpp>
#include <boost/geometry/srs/sphere.hpp>
#include <boost/geometry/strategies/area.hpp>
#include <boost/geometry/strategies/spherical/get_radius.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace area
{

/*!
\brief Spherical area calculation
\ingroup strategies
\details Calculates area on the surface of a sphere using the trapezoidal rule
\tparam RadiusTypeOrSphere \tparam_radius_or_sphere
\tparam CalculationType \tparam_calculation

\qbk{
[heading See also]
[link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)]
}
*/
template
<
    typename RadiusTypeOrSphere = double,
    typename CalculationType = void
>
class spherical
{
    // Enables special handling of long segments
    static const bool LongSegment = false;

public:
    template <typename Geometry>
    struct result_type
        : strategy::area::detail::result_type
            <
                Geometry,
                CalculationType
            >
    {};

    template <typename Geometry>
    class state
    {
        friend class spherical;

        typedef typename result_type<Geometry>::type return_type;

    public:
        inline state()
            : m_sum(0)
            , m_crosses_prime_meridian(0)
        {}

    private:
        template <typename RadiusType>
        inline return_type area(RadiusType const& r) const
        {
            return_type result;
            return_type radius = r;

            // Encircles pole
            if(m_crosses_prime_meridian % 2 == 1)
            {
                size_t times_crosses_prime_meridian
                        = 1 + (m_crosses_prime_meridian / 2);

                result = return_type(2)
                         * geometry::math::pi<return_type>()
                         * times_crosses_prime_meridian
                         - geometry::math::abs(m_sum);

                if(geometry::math::sign<return_type>(m_sum) == 1)
                {
                    result = - result;
                }

            } else {
                result =  m_sum;
            }

            result *= radius * radius;

            return result;
        }

        return_type m_sum;

        // Keep track if encircles some pole
        size_t m_crosses_prime_meridian;
    };

public :

    // For backward compatibility reasons the radius is set to 1
    inline spherical()
        : m_radius(1.0)
    {}

    template <typename RadiusOrSphere>
    explicit inline spherical(RadiusOrSphere const& radius_or_sphere)
        : m_radius(strategy_detail::get_radius
                    <
                        RadiusOrSphere
                    >::apply(radius_or_sphere))
    {}

    template <typename PointOfSegment, typename Geometry>
    inline void apply(PointOfSegment const& p1,
                      PointOfSegment const& p2,
                      state<Geometry>& st) const
    {
        if (! geometry::math::equals(get<0>(p1), get<0>(p2)))
        {
            typedef geometry::formula::area_formulas
                <
                    typename result_type<Geometry>::type
                > area_formulas;

            st.m_sum += area_formulas::template spherical<LongSegment>(p1, p2);

            // Keep track whenever a segment crosses the prime meridian
            if (area_formulas::crosses_prime_meridian(p1, p2))
            {
                st.m_crosses_prime_meridian++;
            }
        }
    }

    template <typename Geometry>
    inline typename result_type<Geometry>::type
        result(state<Geometry> const& st) const
    {
        return st.area(m_radius);
    }

private :
    typename strategy_detail::get_radius
        <
            RadiusTypeOrSphere
        >::type m_radius;
};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

namespace services
{

template <>
struct default_strategy<spherical_equatorial_tag>
{
    typedef strategy::area::spherical<> type;
};

// Note: spherical polar coordinate system requires "get_as_radian_equatorial"
template <>
struct default_strategy<spherical_polar_tag>
{
    typedef strategy::area::spherical<> type;
};

} // namespace services

#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

}} // namespace strategy::area

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HPP