From 5cd6cfc079be3f3415e58eb7ed39949cf8fe072b Mon Sep 17 00:00:00 2001
From: David Rosca <roscadav@fit.cvut.cz>
Date: Mon, 9 Mar 2015 10:11:19 +0100
Subject: [PATCH] Graph shortestpath algo: Add Floyd-Warshall algorithm + tests

---
 .../src/graph/shortestpath/FloydWarshall.cpp  |  93 +++++++
 .../src/graph/shortestpath/FloydWarshall.h    |  43 ++++
 .../graph/shortestpath/FloydWarshallTest.cpp  | 235 ++++++++++++++++++
 .../graph/shortestpath/FloydWarshallTest.h    |  24 ++
 4 files changed, 395 insertions(+)
 create mode 100644 alib2algo/src/graph/shortestpath/FloydWarshall.cpp
 create mode 100644 alib2algo/src/graph/shortestpath/FloydWarshall.h
 create mode 100644 alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp
 create mode 100644 alib2algo/test-src/graph/shortestpath/FloydWarshallTest.h

diff --git a/alib2algo/src/graph/shortestpath/FloydWarshall.cpp b/alib2algo/src/graph/shortestpath/FloydWarshall.cpp
new file mode 100644
index 0000000000..ec9a40bef5
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/FloydWarshall.cpp
@@ -0,0 +1,93 @@
+#include "FloydWarshall.h"
+
+#include <exception/AlibException.h>
+
+#include <limits>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+static void init_distances(const DirectedGraph &g, FloydWarshall::Result &d)
+{
+	for (const DirectedEdge &e : g.getEdges()) {
+		int &val = d[e.getFromNode()][e.getToNode()];
+		val = std::min(val, g.getEdgeValue(e));
+	}
+}
+
+static void init_distances(const UndirectedGraph &g, FloydWarshall::Result &d)
+{
+	for (const UndirectedEdge &e : g.getEdges()) {
+		int &val = d[e.getFirstNode()][e.getSecondNode()];
+		val = std::min(val, g.getEdgeValue(e));
+		d[e.getSecondNode()][e.getFirstNode()] = val;
+	}
+}
+
+template <typename T>
+static FloydWarshall::Result floydwarshall_impl(const T &graph)
+{
+	FloydWarshall::Result d;
+	const std::set<Node> &nodes = graph.getNodes();
+	const int inf = std::numeric_limits<int>::max();
+
+	for (const Node &n : nodes)
+		for (const Node &m : nodes)
+			d[n][m] = inf;
+
+	for (const Node &n : nodes)
+		d[n][n] = 0;
+
+	init_distances(graph, d);
+
+	for (const Node &k : nodes)
+		for (const Node &i : nodes)
+			if (d[i][k] != inf)
+				for (const Node &j : nodes)
+					if (d[k][j] != inf)
+						d[i][j] = std::min(d[i][j], d[i][k] + d[k][j]);
+
+	for (const Node &n : nodes)
+		if (d[n][n] < 0)
+			throw exception::AlibException("FloydWarshall: Detected negative-weight cycle");
+
+	return d;
+}
+
+FloydWarshall::Result FloydWarshall::floydwarshall(const Graph &graph)
+{
+	Result res;
+	graph.getData().Accept(static_cast<void*>(&res), FLOYD_WARSHALL);
+	return res;
+}
+
+FloydWarshall::Result FloydWarshall::floydwarshall(const DirectedGraph &graph)
+{
+	return floydwarshall_impl(graph);
+}
+
+FloydWarshall::Result FloydWarshall::floydwarshall(const UndirectedGraph &graph)
+{
+	return floydwarshall_impl(graph);
+}
+
+void FloydWarshall::Visit(void *data, const DirectedGraph &graph) const
+{
+	Result *r = static_cast<Result*>(data);
+	*r = floydwarshall(graph);
+}
+
+void FloydWarshall::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Result *r = static_cast<Result*>(data);
+	*r = floydwarshall(graph);
+}
+
+const FloydWarshall FloydWarshall::FLOYD_WARSHALL;
+
+} // namespace shortestpath
+
+} // namespace graph
diff --git a/alib2algo/src/graph/shortestpath/FloydWarshall.h b/alib2algo/src/graph/shortestpath/FloydWarshall.h
new file mode 100644
index 0000000000..f9a7ab1855
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/FloydWarshall.h
@@ -0,0 +1,43 @@
+#ifndef GRAPH_FLOYD_WARSHALL_FORD_H_
+#define GRAPH_FLOYD_WARSHALL_FORD_H_
+
+#include <unordered_map>
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+// Floyd-Warshall only works on graphs without negative-weight cycle
+// However it will detect the negative-weight cycle and throw exception
+//
+// note: negative-weight undirected edge = negative-weight cycle
+// note: std::numeric_limits<int>::max() is used as infinity
+
+class FloydWarshall : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::unordered_map<Node, std::unordered_map<Node, int>> Result;
+
+	static Result floydwarshall(const Graph &graph);
+
+	static Result floydwarshall(const DirectedGraph &graph);
+	static Result floydwarshall(const UndirectedGraph &graph);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const FloydWarshall FLOYD_WARSHALL;
+};
+
+} // namespace shortestpath
+
+} // namespace graph
+
+#endif // GRAPH_FLOYD_WARSHALL_FORD_H_
diff --git a/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp b/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp
new file mode 100644
index 0000000000..2b4c78c6e9
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp
@@ -0,0 +1,235 @@
+#include "FloydWarshallTest.h"
+
+#include "graph/shortestpath/FloydWarshall.h"
+
+#include <exception/AlibException.h>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphFloydWarshallTest);
+
+void GraphFloydWarshallTest::testSimple()
+{
+	// Common
+	graph::shortestpath::FloydWarshall::Result res;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n3), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n4), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n5), 6);
+	dg.addEdge(graph::DirectedEdge(n2, n5), 3);
+	dg.addEdge(graph::DirectedEdge(n5, n6), 2);
+
+	CPPUNIT_ASSERT_EQUAL(6, (int)dg.getNodes().size());
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
+	ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
+	ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
+	ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
+}
+
+void GraphFloydWarshallTest::testCycle()
+{
+	// Common
+	graph::shortestpath::FloydWarshall::Result res;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n3), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n4), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n5), 6);
+	dg.addEdge(graph::DirectedEdge(n2, n5), 3);
+	dg.addEdge(graph::DirectedEdge(n5, n6), 2);
+	dg.addEdge(graph::DirectedEdge(n6, n1), 1);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
+	ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
+	ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
+	ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
+	ug.addEdge(graph::UndirectedEdge(n6, n1), 1);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(3, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n6]);
+}
+
+void GraphFloydWarshallTest::testMultiEdge()
+{
+	// Common
+	graph::shortestpath::FloydWarshall::Result res;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi"), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n3), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n4), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n5), 6);
+	dg.addEdge(graph::DirectedEdge(n2, n5), 3);
+	dg.addEdge(graph::DirectedEdge(n5, n6), 2);
+	dg.addEdge(graph::DirectedEdge(n6, n1), 1);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[n1][n6]);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi"), 1);
+	ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
+	ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
+	ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
+	ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
+	ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[n1][n6]);
+}
+
+void GraphFloydWarshallTest::testNegativeValue()
+{
+	// Common
+	graph::shortestpath::FloydWarshall::Result res;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), 5);
+	dg.addEdge(graph::DirectedEdge(n1, n3), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n4), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n5), 5);
+	dg.addEdge(graph::DirectedEdge(n2, n5), -1);
+	dg.addEdge(graph::DirectedEdge(n5, n6), -2);
+
+	res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n1][n2]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n1][n3]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n1][n6]);
+}
+
+void GraphFloydWarshallTest::testNegativeCycle()
+{
+	// Common
+	bool exception;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), 5);
+	dg.addEdge(graph::DirectedEdge(n1, n3), 1);
+	dg.addEdge(graph::DirectedEdge(n1, n4), 2);
+	dg.addEdge(graph::DirectedEdge(n1, n5), 5);
+	dg.addEdge(graph::DirectedEdge(n2, n5), -1);
+	dg.addEdge(graph::DirectedEdge(n5, n6), -2);
+	dg.addEdge(graph::DirectedEdge(n6, n1), -3);
+
+	exception = false;
+	try {
+		graph::shortestpath::FloydWarshall::floydwarshall(dg);
+	} catch (exception::AlibException) {
+		exception = true;
+	}
+
+	CPPUNIT_ASSERT_EQUAL(true, exception);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), 5);
+	ug.addEdge(graph::UndirectedEdge(n1, n3), 1);
+	ug.addEdge(graph::UndirectedEdge(n1, n4), 2);
+	ug.addEdge(graph::UndirectedEdge(n2, n5), -1);
+
+	exception = false;
+	try {
+		graph::shortestpath::FloydWarshall::floydwarshall(ug);
+	} catch (exception::AlibException) {
+		exception = true;
+	}
+
+	CPPUNIT_ASSERT_EQUAL(true, exception);
+}
diff --git a/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.h b/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.h
new file mode 100644
index 0000000000..4a42ad4f38
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.h
@@ -0,0 +1,24 @@
+#ifndef FLOYD_WARSHALL_TEST_H_
+#define FLOYD_WARSHALL_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphFloydWarshallTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphFloydWarshallTest);
+	CPPUNIT_TEST(testSimple);
+	CPPUNIT_TEST(testCycle);
+	CPPUNIT_TEST(testMultiEdge);
+	CPPUNIT_TEST(testNegativeValue);
+	CPPUNIT_TEST(testNegativeCycle);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testSimple();
+	void testCycle();
+	void testMultiEdge();
+	void testNegativeValue();
+	void testNegativeCycle();
+};
+
+#endif // FLOYD_WARSHALL_TEST_H_
-- 
GitLab