// FordFulkersonTest.cpp
//
//     Created on: 29. 03. 2016
//         Author: Jan Broz
//    Modified by: Jan Uhlik
//
// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library

#include <catch2/catch.hpp>

#include <maximum_flow/FordFulkerson.hpp>

#define NetworkDef(edgeCount) const struct { unsigned nodeCnt; unsigned edgeCnt; unsigned source; unsigned sink; struct { unsigned node1; unsigned node2; unsigned capacity; } edges [ edgeCount ]; }
#define FlowDef(edgeCount) const struct { unsigned edgeCnt; struct { unsigned node1; unsigned node2; int flow; } edges [ edgeCount ]; }

NetworkDef(1) network1 = {
	2, 1,
	0, 1,
	{
		{0, 1, 5}
	}
};
FlowDef(1) dirflow1 = {
	1,
	{
		{0, 1, 5}
	}
};
FlowDef(1) undirflow1 = {
	1,
	{
		{0, 1, 5}
	}
};

//----------------------------------
NetworkDef(4) network2 = {
	4, 4,
	0, 3,
	{
		{0, 1, 1},
		{0, 2, 4},
		{1, 3, 2},
		{2, 3, 3}
	}
};
FlowDef(4) dirflow2 = {
	4,
	{
		{0, 1, 1},
		{0, 2, 3},
		{1, 3, 1},
		{2, 3, 3}
	}
};
FlowDef(4) undirflow2 = {
	4,
	{
		{0, 1, 1},
		{0, 2, 3},
		{1, 3, 1},
		{2, 3, 3}
	}
};

//----------------------------------
NetworkDef(5) network3 = {
	4, 5,
	0, 3,
	{
		{0, 1, 5},
		{0, 2, 6},
		{1, 2, 2},
		{1, 3, 7},
		{2, 3, 4}
	}
};
FlowDef(5) dirflow3 = {
	5,
	{
		{0, 1, 5},
		{0, 2, 4},
		{1, 2, 0},
		{1, 3, 5},
		{2, 3, 4}
	}
};
// TODO: Solve this test
//FlowDef(5) undirflow3 = {
//    5,
//    {
//        {0, 1, 5},
//        {0, 2, 6},
//        {1, 2, -2},
//        {1, 3, 7},
//        {2, 3, 4}
//    }
//};

//----------------------------------
NetworkDef(10) network4 = {
	7, 10,
	0, 6,
	{
		{0, 3, 12},
		{0, 1, 6},
		{1, 3, 2},
		{1, 2, 8},
		{2, 4, 7},
		{3, 4, 1},
		{3, 5, 4},
		{3, 6, 6},
		{4, 6, 7},
		{5, 6, 3}
	}
};
FlowDef(10) dirflow4 = {
	10,
	{
		{0, 3, 10},
		{0, 1, 6},
		{1, 3, 0},
		{1, 2, 6},
		{2, 4, 6},
		{3, 4, 1},
		{3, 5, 3},
		{3, 6, 6},
		{4, 6, 7},
		{5, 6, 3}
	}
};
FlowDef(10) undirflow4 = {
	10,
	{
		{0, 3, 10},
		{0, 1, 6},
		{1, 3, 0},
		{1, 2, 6},
		{2, 4, 6},
		{3, 4, 1},
		{3, 5, 3},
		{3, 6, 6},
		{4, 6, 7},
		{5, 6, 3}
	}
};

// Old implementation without templates
using AdjacencyListUndirectedGraph = graph::UndirectedGraph<node::Node,
	  edge::CapacityEdge<node::Node, int>>;
using AdjacencyListDirectedGraph = graph::DirectedGraph<node::Node,
	  edge::CapacityEdge<node::Node, int>>;
using DirectedEdge = edge::CapacityEdge<node::Node, int>;
using UndirectedEdge = edge::CapacityEdge<node::Node, int>;

template<typename Type1, typename Type2>
void testDirNetwork(unsigned netID, const Type1 *netDef, const Type2 *flowDef) {
	AdjacencyListDirectedGraph graph;
	ext::vector<node::Node> id2node(netDef->nodeCnt);
	node::Node source, sink;
	unsigned i, j;

	for (i = 0; i < netDef->nodeCnt; i++) {
		node::Node node(i);
		graph.addNode(node);
		id2node[i] = node;
		if (i == netDef->source)
			source = node;
		if (i == netDef->sink)
			sink = node;
	}
	for (i = 0; i < netDef->edgeCnt; i++) {
		graph.addEdge(id2node[netDef->edges[i].node1], id2node[netDef->edges[i].node2], netDef->edges[i].capacity);
	}

	INFO ( "testing network #" << netID );

	graph::maximum_flow::Flow flow = graph::maximum_flow::FordFulkerson::findMaximumFlow(graph, source, sink);

	for (unsigned id = 0; id < flowDef->edgeCnt; id++) {
		i = flowDef->edges[id].node1;
		j = flowDef->edges[id].node2;
		CHECK(flowDef->edges[id].flow == flow[id2node[i]][id2node[j]]);
	}
}

template<typename Type1, typename Type2>
void testUndirNetwork(unsigned netID, const Type1 *netDef, const Type2 *flowDef) {
	AdjacencyListUndirectedGraph graph;
	ext::vector<node::Node> id2node(netDef->nodeCnt);
	node::Node source, sink;
	unsigned i, j;

	for (i = 0; i < netDef->nodeCnt; i++) {
		node::Node node(i);
		graph.addNode(node);
		id2node[i] = node;
		if (i == netDef->source)
			source = node;
		if (i == netDef->sink)
			sink = node;
	}
	for (i = 0; i < netDef->edgeCnt; i++) {
		graph.addEdge(id2node[netDef->edges[i].node1], id2node[netDef->edges[i].node2], netDef->edges[i].capacity);
	}

	INFO ( "testing network #" << netID );

	graph::maximum_flow::Flow flow = graph::maximum_flow::FordFulkerson::findMaximumFlow(graph, source, sink);

	for (unsigned id = 0; id < flowDef->edgeCnt; id++) {
		i = flowDef->edges[id].node1;
		j = flowDef->edges[id].node2;
		CHECK(flowDef->edges[id].flow == flow[id2node[i]][id2node[j]]);
	}
}

TEST_CASE ( "MaxFlow FF", "[unit][graph][maxmumum_flow]" ) {
	testDirNetwork(1, &network1, &dirflow1);
	testUndirNetwork(2, &network1, &undirflow1);
	testDirNetwork(3, &network2, &dirflow2);
	testUndirNetwork(4, &network2, &undirflow2);
	testDirNetwork(5, &network3, &dirflow3);
	// TODO: solve this test
	//  testUndirNetwork(6, &network3, &undirflow3);
	testDirNetwork(7, &network4, &dirflow4);
	testUndirNetwork(8, &network4, &undirflow4);
}
