diff --git a/alib2algo/src/graph/shortestpath/BellmanFord.cpp b/alib2algo/src/graph/shortestpath/BellmanFord.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..446add8824b4ba48d96fb997093437caff023009
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/BellmanFord.cpp
@@ -0,0 +1,174 @@
+#include "BellmanFord.h"
+
+#include <exception/AlibException.h>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+class Data
+{
+public:
+	Node start;
+	BellmanFord::Result out;
+};
+
+typedef std::unordered_map<Node, std::unordered_map<Node, int>> weights_t;
+
+static int get_weight(weights_t &w, const DirectedGraph &g, const DirectedEdge &e)
+{
+	int out;
+	auto &map = w[e.getFromNode()];
+	auto search = map.find(e.getToNode());
+
+	if (search == map.end()) {
+		out = g.getEdgeValue(e);
+		map[e.getToNode()] = out;
+	} else {
+		out = search->second;
+	}
+
+	return out;
+}
+
+static BellmanFord::Result bellmanford_impl(const DirectedGraph &graph, const Node &start)
+{
+	BellmanFord::Result d; // distances
+	weights_t w;           // minimum weights
+
+	d[start] = 0;
+
+	for (unsigned i = 0; i < graph.getNodes().size(); ++i) {
+		for (const DirectedEdge &e : graph.getEdges()) {
+			int ew = get_weight(w, graph, e);
+			const Node &u = e.getFromNode();
+			const Node &v = e.getToNode();
+
+			auto du = d.find(u);
+			auto dv = d.find(v);
+
+			if (du == d.end()) {
+				continue;
+			}
+
+			if (dv == d.end() || du->second + ew < dv->second) {
+				d[v] = du->second + ew;
+			}
+		}
+	}
+
+	for (const DirectedEdge &e : graph.getEdges()) {
+		const Node &u = e.getFromNode();
+		const Node &v = e.getToNode();
+
+		if (d.at(u) + w[u][v] < d.at(v)) {
+			throw exception::AlibException("BellmanFord: Found negative-weight cycle!");
+		}
+	}
+
+	return d;
+}
+
+static int get_weight(weights_t &w, const UndirectedGraph &g, const UndirectedEdge &e)
+{
+	int out;
+	auto &map = w[e.getFirstNode()];
+	auto search = map.find(e.getSecondNode());
+
+	if (search == map.end()) {
+		out = g.getEdgeValue(e);
+		map[e.getSecondNode()] = out;
+		w[e.getSecondNode()][e.getFirstNode()] = out;
+	} else {
+		out = search->second;
+	}
+
+	return out;
+}
+
+static BellmanFord::Result bellmanford_impl(const UndirectedGraph &graph, const Node &start)
+{
+	BellmanFord::Result d; // distances
+	weights_t w;           // minimum weights
+
+	d[start] = 0;
+
+	for (unsigned i = 0; i < graph.getNodes().size(); ++i) {
+		for (const UndirectedEdge &e : graph.getEdges()) {
+			int ew = get_weight(w, graph, e);
+			const Node &u = e.getFirstNode();
+			const Node &v = e.getSecondNode();
+
+			auto f = [&ew, &d](const Node &u, const Node &v) {
+				auto du = d.find(u);
+				auto dv = d.find(v);
+
+				if (du == d.end()) {
+					return;
+				}
+
+				if (dv == d.end() || du->second + ew < dv->second) {
+					d[v] = du->second + ew;
+				}
+			};
+
+			f(u, v);
+			f(v, u);
+		}
+	}
+
+	for (const UndirectedEdge &e : graph.getEdges()) {
+		const Node &u = e.getFirstNode();
+		const Node &v = e.getSecondNode();
+
+		auto f = [&w, &d](const Node &u, const Node &v) {
+			if (d.at(u) + w[u][v] < d.at(v)) {
+				throw exception::AlibException("BellmanFord: Found negative-weight cycle!");
+			}
+		};
+
+		f(u, v);
+		f(v, u);
+
+	}
+
+	return d;
+}
+
+BellmanFord::Result BellmanFord::bellmanford(const Graph &graph, const Node &start)
+{
+	Data data;
+	data.start = start;
+	graph.getData().Accept(static_cast<void*>(&data), BELLMAN_FORD);
+	return data.out;
+}
+
+BellmanFord::Result BellmanFord::bellmanford(const DirectedGraph &graph, const Node &start)
+{
+	return bellmanford_impl(graph, start);
+}
+
+BellmanFord::Result BellmanFord::bellmanford(const UndirectedGraph &graph, const Node &start)
+{
+	return bellmanford_impl(graph, start);
+}
+
+void BellmanFord::Visit(void *data, const DirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	d.out = bellmanford(graph, d.start);
+}
+
+void BellmanFord::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	d.out = bellmanford(graph, d.start);
+}
+
+const BellmanFord BellmanFord::BELLMAN_FORD;
+
+} // namespace shortestpath
+
+} // namespace graph
diff --git a/alib2algo/src/graph/shortestpath/BellmanFord.h b/alib2algo/src/graph/shortestpath/BellmanFord.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c117f6a40c31b245e301ff4b86da6da25b6b926
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/BellmanFord.h
@@ -0,0 +1,42 @@
+#ifndef GRAPH_BELLMAN_FORD_H_
+#define GRAPH_BELLMAN_FORD_H_
+
+#include <unordered_map>
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+// Bellman-Ford 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
+
+class BellmanFord : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::unordered_map<Node, int> Result;
+
+	static Result bellmanford(const Graph &graph, const Node &start);
+
+	static Result bellmanford(const DirectedGraph &graph, const Node &start);
+	static Result bellmanford(const UndirectedGraph &graph, const Node &start);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const BellmanFord BELLMAN_FORD;
+};
+
+} // namespace shortestpath
+
+} // namespace graph
+
+#endif // GRAPH_BELLMAN_FORD_H_
diff --git a/alib2algo/src/graph/shortestpath/Dijkstra.cpp b/alib2algo/src/graph/shortestpath/Dijkstra.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..098b5abfd7ef1f96ed68623e1157c4d64b77cbac
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/Dijkstra.cpp
@@ -0,0 +1,155 @@
+#include "Dijkstra.h"
+
+#include <exception/AlibException.h>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+class Data
+{
+public:
+	Node start;
+	Dijkstra::Result out;
+};
+
+class Value
+{
+public:
+	Value(const Node &node, int value)
+		: node(node)
+		, value(value)
+	{
+	}
+
+	bool operator<(const Value &other) const
+	{
+		return value < other.value;
+	}
+
+	Node node;
+	int value;
+};
+
+typedef std::unordered_map<Node, std::unordered_map<Node, int>> weights_t;
+
+static weights_t weights(const DirectedGraph &g)
+{
+	int val;
+	weights_t w;
+
+	for (const DirectedEdge &e : g.getEdges()) {
+		auto &map = w[e.getFromNode()];
+		auto search = map.find(e.getToNode());
+
+		if (search == map.end()) {
+			val = g.getEdgeValue(e);
+			map[e.getToNode()] = val;
+		} else {
+			val = std::min(search->second, g.getEdgeValue(e));
+			search->second = val;
+		}
+
+		if (val < 0) {
+			throw exception::AlibException("Dijkstra: Found negative value!");
+		}
+	}
+
+	return w;
+}
+
+static weights_t weights(const UndirectedGraph &g)
+{
+	int val;
+	weights_t w;
+
+	for (const UndirectedEdge &e : g.getEdges()) {
+		auto &map = w[e.getFirstNode()];
+		auto search = map.find(e.getSecondNode());
+
+		if (search == map.end()) {
+			val = g.getEdgeValue(e);
+			map[e.getSecondNode()] = val;
+			w[e.getSecondNode()][e.getFirstNode()] = val;
+		} else {
+			val = std::min(search->second, g.getEdgeValue(e));
+			search->second = val;
+			w[e.getSecondNode()][e.getFirstNode()] = val;
+		}
+
+		if (val < 0) {
+			throw exception::AlibException("Dijkstra: Found negative value!");
+		}
+	}
+
+	return w;
+}
+
+template <typename T>
+static Dijkstra::Result dijkstra_impl(const T &graph, const Node &start)
+{
+	std::set<Value> q;            // priority queue
+	Dijkstra::Result d;           // distances
+	weights_t w = weights(graph); // minimum weights
+
+	d[start] = 0;
+	q.insert(Value(start, 0));
+
+	while (!q.empty()) {
+		Node u = q.begin()->node; q.erase(q.begin());
+
+		for (const Node &v : graph.neighbors(u)) {
+			int val = d.at(u) + w[u][v];
+			auto search = d.find(v);
+
+			if (search == d.end() || search->second > val) {
+				if (search != d.end()) {
+					q.erase(q.find(Value(v, search->second)));
+				}
+
+				q.insert(Value(v, val));
+				d[v] = val;
+			}
+		}
+	}
+
+	return d;
+}
+
+Dijkstra::Result Dijkstra::dijkstra(const Graph &graph, const Node &start)
+{
+	Data data;
+	data.start = start;
+	graph.getData().Accept(static_cast<void*>(&data), DIJKSTRA);
+	return data.out;
+}
+
+Dijkstra::Result Dijkstra::dijkstra(const DirectedGraph &graph, const Node &start)
+{
+	return dijkstra_impl(graph, start);
+}
+
+Dijkstra::Result Dijkstra::dijkstra(const UndirectedGraph &graph, const Node &start)
+{
+	return dijkstra_impl(graph, start);
+}
+
+void Dijkstra::Visit(void *data, const DirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	d.out = dijkstra(graph, d.start);
+}
+
+void Dijkstra::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	d.out = dijkstra(graph, d.start);
+}
+
+const Dijkstra Dijkstra::DIJKSTRA;
+
+} // namespace shortestpath
+
+} // namespace graph
diff --git a/alib2algo/src/graph/shortestpath/Dijkstra.h b/alib2algo/src/graph/shortestpath/Dijkstra.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9373e3ca3f22a3ee807abeba75669338101de46
--- /dev/null
+++ b/alib2algo/src/graph/shortestpath/Dijkstra.h
@@ -0,0 +1,39 @@
+#ifndef GRAPH_DIJKSTRA_H_
+#define GRAPH_DIJKSTRA_H_
+
+#include <unordered_map>
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace shortestpath
+{
+
+// Dijkstra only works on graphs without negative-weight edges (>= 0)
+
+class Dijkstra : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::unordered_map<Node, int> Result;
+
+	static Result dijkstra(const Graph &graph, const Node &start);
+
+	static Result dijkstra(const DirectedGraph &graph, const Node &start);
+	static Result dijkstra(const UndirectedGraph &graph, const Node &start);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const Dijkstra DIJKSTRA;
+};
+
+} // namespace shortestpath
+
+} // namespace graph
+
+#endif // GRAPH_DIJKSTRA_H_
diff --git a/alib2algo/src/graph/shortestpath/FloydWarshall.cpp b/alib2algo/src/graph/shortestpath/FloydWarshall.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec9a40bef55708cc73890ce3a0c4fb3cc13cfb1f
--- /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 0000000000000000000000000000000000000000..f9a7ab1855e24f26ed544202c6d656825891ee8d
--- /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/src/graph/sort/TopologicalSort.cpp b/alib2algo/src/graph/sort/TopologicalSort.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6556074b1c0d1274823f433e917749df83ea56ba
--- /dev/null
+++ b/alib2algo/src/graph/sort/TopologicalSort.cpp
@@ -0,0 +1,60 @@
+#include "TopologicalSort.h"
+#include "../traverse/Dfs.h"
+
+#include <exception/AlibException.h>
+
+namespace graph
+{
+
+namespace sort
+{
+
+static TopologicalSort::Result topsort_impl(const DirectedGraph &graph)
+{
+	TopologicalSort::Result s;
+	std::unordered_map<Node, bool> visited;
+
+	for (const Node &n : graph.getNodes()) {
+		traverse::Dfs::dfs(graph, n, [&s, &visited](const Node &n, const Node&, int, int) {
+			if (visited.find(n) != visited.end()) {
+				return false;
+			}
+			visited.insert({n, true});
+			s.push_front(n);
+			return true;
+		});
+	}
+
+	return s;
+}
+
+TopologicalSort::Result TopologicalSort::topologicalsort(const Graph &graph)
+{
+	Result res;
+	graph.getData().Accept(static_cast<void*>(&res), TOPOLOGICAL_SORT);
+	return res;
+}
+
+TopologicalSort::Result TopologicalSort::topologicalsort(const DirectedGraph &graph)
+{
+	return topsort_impl(graph);
+}
+
+void TopologicalSort::Visit(void *data, const DirectedGraph &graph) const
+{
+	Result *r = static_cast<Result*>(data);
+	*r = topologicalsort(graph);
+}
+
+void TopologicalSort::Visit(void *data, const UndirectedGraph &graph) const
+{
+	(void) data;
+	(void) graph;
+	throw exception::AlibException("Unsupported graph type UndirectedGraph");
+}
+
+const TopologicalSort TopologicalSort::TOPOLOGICAL_SORT;
+
+} // namespace sort
+
+} // namespace graph
diff --git a/alib2algo/src/graph/sort/TopologicalSort.h b/alib2algo/src/graph/sort/TopologicalSort.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec30472bc7ffe5cee35b32da51c0e1643b1dcf96
--- /dev/null
+++ b/alib2algo/src/graph/sort/TopologicalSort.h
@@ -0,0 +1,36 @@
+#ifndef GRAPH_TOPOLOGICAL_SORT_H_
+#define GRAPH_TOPOLOGICAL_SORT_H_
+
+#include <list>
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace sort
+{
+
+class TopologicalSort : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::list<Node> Result;
+
+	static Result topologicalsort(const Graph &graph);
+
+	static Result topologicalsort(const DirectedGraph &graph);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const TopologicalSort TOPOLOGICAL_SORT;
+};
+
+} // namespace sort
+
+} // namespace graph
+
+#endif // GRAPH_TOPOLOGICAL_SORT_H_
diff --git a/alib2algo/src/graph/spanningtree/JarnikPrim.cpp b/alib2algo/src/graph/spanningtree/JarnikPrim.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61835e6a75c6eea197cff1c62e22eefb35224f0f
--- /dev/null
+++ b/alib2algo/src/graph/spanningtree/JarnikPrim.cpp
@@ -0,0 +1,133 @@
+#include "JarnikPrim.h"
+
+#include <exception/AlibException.h>
+
+#include <limits>
+
+namespace graph
+{
+
+namespace spanningtree
+{
+
+class Data
+{
+public:
+	Node start;
+	Graph *out;
+};
+
+class Value
+{
+public:
+	Value(const Node &node, int value)
+		: node(node)
+		, value(value)
+	{
+	}
+
+	bool operator<(const Value &other) const
+	{
+		if (value == other.value) {
+			return node < other.node;
+		}
+		return value < other.value;
+	}
+
+	Node node;
+	int value;
+};
+
+typedef std::unordered_map<Node, std::unordered_map<Node, int>> weights_t;
+
+static weights_t weights(const UndirectedGraph &g)
+{
+	int val;
+	weights_t w;
+
+	for (const UndirectedEdge &e : g.getEdges()) {
+		auto &map = w[e.getFirstNode()];
+		auto search = map.find(e.getSecondNode());
+
+		if (search == map.end()) {
+			val = g.getEdgeValue(e);
+			map[e.getSecondNode()] = val;
+			w[e.getSecondNode()][e.getFirstNode()] = val;
+		} else {
+			val = std::min(search->second, g.getEdgeValue(e));
+			search->second = val;
+			w[e.getSecondNode()][e.getFirstNode()] = val;
+		}
+	}
+
+	return w;
+}
+
+static UndirectedGraph jarnikprim_impl(const UndirectedGraph &graph, const Node &start)
+{
+	UndirectedGraph res;              // spanning tree
+	std::set<Value> q;                // priority queue
+	weights_t w = weights(graph);     // minimum weights
+	std::unordered_map<Node, int> d;  // distances
+	std::unordered_map<Node, Node> p; // predecessors
+
+	for (const Node &n : graph.getNodes()) {
+		int value = n == start ? 0 : std::numeric_limits<int>::max();
+		q.insert(Value(n, value));
+		d[n] = value;
+	}
+
+	while (!q.empty()) {
+		Node u = q.begin()->node; q.erase(q.begin());
+		auto search = p.find(u);
+		if (search != p.end()) {
+			res.addEdge(UndirectedEdge(search->second, u), w[search->second][u]);
+		}
+
+		for (const Node &v : graph.neighbors(u)) {
+			int &dist = d[v];
+			if (q.find(Value(v, dist)) != q.end() && w[u][v] < dist) {
+				q.erase(q.find(Value(v, dist)));
+				p[v] = u;
+				dist = w[u][v];
+				q.insert(Value(v, dist));
+			}
+		}
+	}
+
+	return res;
+}
+
+Graph JarnikPrim::jarnikprim(const Graph &graph, const Node &start)
+{
+	Data out = { start, nullptr };
+	graph.getData().Accept(static_cast<void*>(&out), JARNIK_PRIM);
+	Graph res = std::move(*out.out);
+	delete out.out;
+	return res;
+}
+
+UndirectedGraph JarnikPrim::jarnikprim(const UndirectedGraph &graph, const Node &start)
+{
+	return jarnikprim_impl(graph, start);
+}
+
+void JarnikPrim::Visit(void *data, const DirectedGraph &graph) const
+{
+	(void) data;
+	(void) graph;
+	throw exception::AlibException("Unsupported graph type DirectedGraph");
+}
+
+void JarnikPrim::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	d.out = new Graph(jarnikprim(graph, d.start));
+	(void) d;
+}
+
+const JarnikPrim JarnikPrim::JARNIK_PRIM;
+
+} // namespace spanningtree
+
+} // namespace graph
diff --git a/alib2algo/src/graph/spanningtree/JarnikPrim.h b/alib2algo/src/graph/spanningtree/JarnikPrim.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf1273514f28f3b2e482efd17dc82ad3de861f55
--- /dev/null
+++ b/alib2algo/src/graph/spanningtree/JarnikPrim.h
@@ -0,0 +1,32 @@
+#ifndef GRAPH_JARNIK_PRIM_H_
+#define GRAPH_JARNIK_PRIM_H_
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace spanningtree
+{
+
+class JarnikPrim : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	static Graph jarnikprim(const Graph &graph, const Node &start);
+
+	static UndirectedGraph jarnikprim(const UndirectedGraph &graph, const Node &start);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const JarnikPrim JARNIK_PRIM;
+};
+
+} // namespace spanningtree
+
+} // namespace graph
+
+#endif // GRAPH_JARNIK_PRIM_H_
diff --git a/alib2algo/src/graph/traverse/Bfs.cpp b/alib2algo/src/graph/traverse/Bfs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e951bd4d76474b575a845e1a7b5de646735125ff
--- /dev/null
+++ b/alib2algo/src/graph/traverse/Bfs.cpp
@@ -0,0 +1,127 @@
+#include "Bfs.h"
+
+#include <queue>
+#include <unordered_map>
+
+namespace graph
+{
+
+namespace traverse
+{
+
+struct Data
+{
+	Node start;
+	Bfs::Function func;
+	Bfs::FunctionExt func2;
+};
+
+template <typename T>
+static void bfs_impl(const T &graph, const Node &start, Bfs::Function func)
+{
+	std::unordered_map<Node, bool> visited;
+	std::queue<Node> q;
+
+	q.push(start);
+	visited[start] = true;
+
+	while (!q.empty()) {
+		Node n = q.front(); q.pop();
+
+		if (!func(n)) {
+			return;
+		}
+
+		for (const Node &e : graph.neighbors(n)) {
+			if (visited.find(e) == visited.end()) {
+				visited[e] = true;
+				q.push(e);
+			}
+		}
+	}
+}
+
+template <typename T>
+static void bfs2_impl(const T &graph, const Node &start, Bfs::FunctionExt func)
+{
+	std::unordered_map<Node, int> distances;
+	std::unordered_map<Node, Node> predecessors;
+	std::queue<Node> q;
+
+	q.push(start);
+	distances[start] = 0;
+
+	while (!q.empty()) {
+		Node n = q.front(); q.pop();
+
+		if (!func(n, predecessors[n], distances[n])) {
+			return;
+		}
+
+		for (const Node &e : graph.neighbors(n)) {
+			if (distances.find(e) == distances.end()) {
+				distances[e] = distances.at(n) + 1;
+				predecessors[e] = n;
+				q.push(e);
+			}
+		}
+	}
+}
+
+void Bfs::bfs(const Graph &graph, const Node &start, Bfs::Function func)
+{
+	Data data = { start, func, nullptr };
+	graph.getData().Accept(static_cast<void*>(&data), BFS);
+}
+
+void Bfs::bfs(const Graph &graph, const Node &start, Bfs::FunctionExt func)
+{
+	Data data = { start, nullptr, func };
+	graph.getData().Accept(static_cast<void*>(&data), BFS);
+}
+
+void Bfs::bfs(const DirectedGraph &graph, const Node &start, Bfs::Function func)
+{
+	bfs_impl(graph, start, func);
+}
+
+void Bfs::bfs(const DirectedGraph &graph, const Node &start, Bfs::FunctionExt func)
+{
+	bfs2_impl(graph, start, func);
+}
+
+void Bfs::bfs(const UndirectedGraph &graph, const Node &start, Bfs::Function func)
+{
+	bfs_impl(graph, start, func);
+}
+
+void Bfs::bfs(const UndirectedGraph &graph, const Node &start, Bfs::FunctionExt func)
+{
+	bfs2_impl(graph, start, func);
+}
+
+void Bfs::Visit(void *data, const DirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	if (d.func) {
+		bfs(graph, d.start, d.func);
+	} else if (d.func2) {
+		bfs(graph, d.start, d.func2);
+	}
+}
+
+void Bfs::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	if (d.func) {
+		bfs(graph, d.start, d.func);
+	} else if (d.func2) {
+		bfs(graph, d.start, d.func2);
+	}
+}
+
+const Bfs Bfs::BFS;
+
+} // namespace traverse
+
+} // namespace graph
diff --git a/alib2algo/src/graph/traverse/Bfs.h b/alib2algo/src/graph/traverse/Bfs.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4a468bfe911e33812902e0b34096c27155c798a
--- /dev/null
+++ b/alib2algo/src/graph/traverse/Bfs.h
@@ -0,0 +1,44 @@
+#ifndef GRAPH_BFS_H_
+#define GRAPH_BFS_H_
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace traverse
+{
+
+// func is called for each visited node, traversal stops when returning false
+//
+// bool Function(const Node &visitedNode);
+// bool FunctionExt(const Node &visitedNode, const Node &predecessor, int distanceFromStart);
+
+class Bfs : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::function<bool(const Node&)> Function;
+	typedef std::function<bool(const Node&, const Node&, int)> FunctionExt;
+
+	static void bfs(const Graph &graph, const Node &start, Function func);
+	static void bfs(const Graph &graph, const Node &start, FunctionExt func);
+
+	static void bfs(const DirectedGraph &graph, const Node &start, Function func);
+	static void bfs(const DirectedGraph &graph, const Node &start, FunctionExt func);
+	static void bfs(const UndirectedGraph &graph, const Node &start, Function func);
+	static void bfs(const UndirectedGraph &graph, const Node &start, FunctionExt func);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const Bfs BFS;
+};
+
+} // namespace traverse
+
+} // namespace graph
+
+#endif // GRAPH_BFS_H_
diff --git a/alib2algo/src/graph/traverse/Dfs.cpp b/alib2algo/src/graph/traverse/Dfs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bef737f5c6fddcf1d87397beed76a1a28029127
--- /dev/null
+++ b/alib2algo/src/graph/traverse/Dfs.cpp
@@ -0,0 +1,121 @@
+#include "Dfs.h"
+
+#include <stack>
+#include <unordered_map>
+
+namespace graph
+{
+
+namespace traverse
+{
+
+struct Data
+{
+	Node start;
+	Dfs::Function func;
+	Dfs::FunctionExt func2;
+};
+
+template <typename T>
+static void dfs_impl(const T &graph, const Node &start, Dfs::Function func)
+{
+	std::unordered_map<Node, bool> visited;
+	std::stack<Node> s;
+
+	s.push(start);
+	visited[start] = true;
+
+	while (!s.empty()) {
+		Node n = s.top(); s.pop();
+
+		if (!func(n)) {
+			return;
+		}
+
+		for (const Node &e : graph.neighbors(n)) {
+			if (visited.find(e) == visited.end()) {
+				visited[e] = true;
+				s.push(e);
+			}
+		}
+	}
+}
+
+template <typename T>
+static bool dfs2_impl(const T &graph, const Node &n, const Node &p, std::unordered_map<Node, bool> &visited, int &time, Dfs::FunctionExt func)
+{
+	int opened = ++time;
+	visited[n] = true;
+
+	for (const Node &e : graph.neighbors(n)) {
+		if (visited.find(e) == visited.end()) {
+			if (!dfs2_impl(graph, e, n, visited, time, func)) {
+				return false;
+			}
+		}
+	}
+
+	return func(n, p, opened, ++time);
+}
+
+void Dfs::dfs(const Graph &graph, const Node &start, Dfs::Function func)
+{
+	Data data = { start, func, nullptr };
+	graph.getData().Accept(static_cast<void*>(&data), DFS);
+}
+
+void Dfs::dfs(const Graph &graph, const Node &start, Dfs::FunctionExt func)
+{
+	Data data = { start, nullptr, func };
+	graph.getData().Accept(static_cast<void*>(&data), DFS);
+}
+
+void Dfs::dfs(const DirectedGraph &graph, const Node &start, Dfs::Function func)
+{
+	dfs_impl(graph, start, func);
+}
+
+void Dfs::dfs(const DirectedGraph &graph, const Node &start, Dfs::FunctionExt func)
+{
+	int time = 0;
+	std::unordered_map<Node, bool> visited;
+	dfs2_impl(graph, start, Node(), visited, time, func);
+}
+
+void Dfs::dfs(const UndirectedGraph &graph, const Node &start, Dfs::Function func)
+{
+	dfs_impl(graph, start, func);
+}
+
+void Dfs::dfs(const UndirectedGraph &graph, const Node &start, Dfs::FunctionExt func)
+{
+	int time = 0;
+	std::unordered_map<Node, bool> visited;
+	dfs2_impl(graph, start, Node(), visited, time, func);
+}
+
+void Dfs::Visit(void *data, const DirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	if (d.func) {
+		dfs(graph, d.start, d.func);
+	} else if (d.func2) {
+		dfs(graph, d.start, d.func2);
+	}
+}
+
+void Dfs::Visit(void *data, const UndirectedGraph &graph) const
+{
+	Data d = *static_cast<Data*>(data);
+	if (d.func) {
+		dfs(graph, d.start, d.func);
+	} else if (d.func2) {
+		dfs(graph, d.start, d.func2);
+	}
+}
+
+const Dfs Dfs::DFS;
+
+} // namespace traverse
+
+} // namespace graph
diff --git a/alib2algo/src/graph/traverse/Dfs.h b/alib2algo/src/graph/traverse/Dfs.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f068d4d3bac734c2e19a64f2458e7bfc04912f1
--- /dev/null
+++ b/alib2algo/src/graph/traverse/Dfs.h
@@ -0,0 +1,44 @@
+#ifndef GRAPH_DFS_H_
+#define GRAPH_DFS_H_
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace traverse
+{
+
+// func is called for each visited node, traversal stops when returning false
+//
+// bool Function(const Node &node);
+// bool FunctionExt(const Node &node, const Node &predecessor, int openTime, int closeTime);
+
+class Dfs : public graph::VisitableGraphBase::const_visitor_type
+{
+public:
+	typedef std::function<bool(const Node&)> Function;
+	typedef std::function<bool(const Node&, const Node&, int, int)> FunctionExt;
+
+	static void dfs(const Graph &graph, const Node &start, Function func);
+	static void dfs(const Graph &graph, const Node &start, FunctionExt func);
+
+	static void dfs(const DirectedGraph &graph, const Node &start, Function func);
+	static void dfs(const DirectedGraph &graph, const Node &start, FunctionExt func);
+	static void dfs(const UndirectedGraph &graph, const Node &start, Function func);
+	static void dfs(const UndirectedGraph &graph, const Node &start, FunctionExt func);
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	static const Dfs DFS;
+};
+
+} // namespace traverse
+
+} // namespace graph
+
+#endif // GRAPH_DFS_H_
diff --git a/alib2algo/test-src/graph/shortestpath/BellmanFordTest.cpp b/alib2algo/test-src/graph/shortestpath/BellmanFordTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..64d2ceb198d47ec72c8f41c1e8aeadbc37f2b3eb
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/BellmanFordTest.cpp
@@ -0,0 +1,233 @@
+#include "BellmanFordTest.h"
+
+#include "graph/shortestpath/BellmanFord.h"
+
+#include <exception/AlibException.h>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphBellmanFordTest);
+
+void GraphBellmanFordTest::testSimple()
+{
+	// Common
+	graph::shortestpath::BellmanFord::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);
+
+	res = graph::shortestpath::BellmanFord::bellmanford(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[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::BellmanFord::bellmanford(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[n6]);
+}
+
+void GraphBellmanFordTest::testCycle()
+{
+	// Common
+	graph::shortestpath::BellmanFord::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::BellmanFord::bellmanford(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[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::BellmanFord::bellmanford(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(3, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n6]);
+}
+
+void GraphBellmanFordTest::testMultiEdge()
+{
+	// Common
+	graph::shortestpath::BellmanFord::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::BellmanFord::bellmanford(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[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::BellmanFord::bellmanford(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[n6]);
+}
+
+void GraphBellmanFordTest::testNegativeValue()
+{
+	// Common
+	graph::shortestpath::BellmanFord::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::BellmanFord::bellmanford(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n6]);
+}
+
+void GraphBellmanFordTest::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::BellmanFord::bellmanford(dg, n1);
+	} 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::BellmanFord::bellmanford(ug, n1);
+	} catch (exception::AlibException) {
+		exception = true;
+	}
+
+	CPPUNIT_ASSERT_EQUAL(true, exception);
+}
diff --git a/alib2algo/test-src/graph/shortestpath/BellmanFordTest.h b/alib2algo/test-src/graph/shortestpath/BellmanFordTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..030b8371e3e78d9246d542ca5547c34678a80091
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/BellmanFordTest.h
@@ -0,0 +1,24 @@
+#ifndef BELLMAN_FORD_TEST_H_
+#define BELLMAN_FORD_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphBellmanFordTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphBellmanFordTest);
+	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 // BELLMAN_FORD_TEST_H_
diff --git a/alib2algo/test-src/graph/shortestpath/DijkstraTest.cpp b/alib2algo/test-src/graph/shortestpath/DijkstraTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b1b2b65bf009c544a3eb3f6ddb05e6b151ddcf8
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/DijkstraTest.cpp
@@ -0,0 +1,191 @@
+#include "DijkstraTest.h"
+
+#include "graph/shortestpath/Dijkstra.h"
+
+#include <exception/AlibException.h>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphDijkstraTest);
+
+void GraphDijkstraTest::testSimple()
+{
+	// Common
+	graph::shortestpath::Dijkstra::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);
+
+	res = graph::shortestpath::Dijkstra::dijkstra(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[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::Dijkstra::dijkstra(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[n6]);
+}
+
+void GraphDijkstraTest::testCycle()
+{
+	// Common
+	graph::shortestpath::Dijkstra::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::Dijkstra::dijkstra(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(7, res[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::Dijkstra::dijkstra(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(5, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n6]);
+}
+
+void GraphDijkstraTest::testMultiEdge()
+{
+	// Common
+	graph::shortestpath::Dijkstra::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::Dijkstra::dijkstra(dg, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[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::Dijkstra::dijkstra(ug, n1);
+
+	CPPUNIT_ASSERT_EQUAL(0, res[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n2]);
+	CPPUNIT_ASSERT_EQUAL(2, res[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, res[n4]);
+	CPPUNIT_ASSERT_EQUAL(4, res[n5]);
+	CPPUNIT_ASSERT_EQUAL(6, res[n6]);
+}
+
+void GraphDijkstraTest::testNegativeValue()
+{
+	// Common
+	bool exception;
+	graph::shortestpath::Dijkstra::Result res;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2), -2);
+
+	exception = false;
+	try {
+		res = graph::shortestpath::Dijkstra::dijkstra(dg, n1);
+	} catch (exception::AlibException) {
+		exception = true;
+	}
+
+	CPPUNIT_ASSERT_EQUAL(true, exception);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), -2);
+
+	exception = false;
+	try {
+		res = graph::shortestpath::Dijkstra::dijkstra(ug, n1);
+	} catch (exception::AlibException) {
+		exception = true;
+	}
+
+	CPPUNIT_ASSERT_EQUAL(true, exception);
+}
diff --git a/alib2algo/test-src/graph/shortestpath/DijkstraTest.h b/alib2algo/test-src/graph/shortestpath/DijkstraTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6ddfb568c11897ef2d83962fd8f8d47b207b54b
--- /dev/null
+++ b/alib2algo/test-src/graph/shortestpath/DijkstraTest.h
@@ -0,0 +1,22 @@
+#ifndef DIJKSTRA_TEST_H_
+#define DIJKSTRA_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphDijkstraTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphDijkstraTest);
+	CPPUNIT_TEST(testSimple);
+	CPPUNIT_TEST(testCycle);
+	CPPUNIT_TEST(testMultiEdge);
+	CPPUNIT_TEST(testNegativeValue);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testSimple();
+	void testCycle();
+	void testMultiEdge();
+	void testNegativeValue();
+};
+
+#endif // DIJKSTRA_TEST_H_
diff --git a/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp b/alib2algo/test-src/graph/shortestpath/FloydWarshallTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b4c78c6e9bae84e84c07d11003fb9c5586ed488
--- /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 0000000000000000000000000000000000000000..4a42ad4f3806a994959e60bb86231209c0f3f5f8
--- /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_
diff --git a/alib2algo/test-src/graph/sort/TopologicalSortTest.cpp b/alib2algo/test-src/graph/sort/TopologicalSortTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b49737974af5efbc44f0044b36809af901a5059
--- /dev/null
+++ b/alib2algo/test-src/graph/sort/TopologicalSortTest.cpp
@@ -0,0 +1,53 @@
+#include "TopologicalSortTest.h"
+
+#include "graph/sort/TopologicalSort.h"
+
+#define CPPUNIT_ASSERT_EQUAL_INT(a, b) CPPUNIT_ASSERT_EQUAL(a, (int)b)
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphTopologicalSortTest);
+
+void GraphTopologicalSortTest::testSort1()
+{
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n3, n6));
+	dg.addEdge(graph::DirectedEdge(n4, n5));
+
+	graph::sort::TopologicalSort::Result res;
+	res = graph::sort::TopologicalSort::topologicalsort(dg);
+
+	CPPUNIT_ASSERT_EQUAL_INT(6, res.size());
+
+	CPPUNIT_ASSERT_EQUAL(n1, *res.begin());
+	res.erase(res.begin());
+
+	CPPUNIT_ASSERT_EQUAL(n2, *res.begin());
+	res.erase(res.begin());
+
+	CPPUNIT_ASSERT_EQUAL(n3, *res.begin());
+	res.erase(res.begin());
+
+	bool wasN4 = false;
+
+	while (!res.empty()) {
+		auto i = res.begin();
+		if (*i == n4) {
+			wasN4 = true;
+		}
+		if (*i == n5 && !wasN4) {
+			CPPUNIT_FAIL("n4 must be before n5!");
+		}
+		res.erase(i);
+	}
+}
+
diff --git a/alib2algo/test-src/graph/sort/TopologicalSortTest.h b/alib2algo/test-src/graph/sort/TopologicalSortTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc1ef01a4ee114d058885c0e5130ea62a136715d
--- /dev/null
+++ b/alib2algo/test-src/graph/sort/TopologicalSortTest.h
@@ -0,0 +1,17 @@
+#ifndef TOPOLOGICAL_SORT_TEST_H_
+#define TOPOLOGICAL_SORT_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphTopologicalSortTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphTopologicalSortTest);
+	CPPUNIT_TEST(testSort1);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testSort1();
+
+};
+
+#endif // TOPOLOGICAL_SORT_TEST_H_
diff --git a/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.cpp b/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0639b6653ea894a827329abfdfb0f2ae1cfeb43
--- /dev/null
+++ b/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.cpp
@@ -0,0 +1,42 @@
+#include "JarnikPrimTest.h"
+
+#include "graph/spanningtree/JarnikPrim.h"
+
+#include <iostream>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphJarnikPrimTest);
+
+void GraphJarnikPrimTest::testSpanningTree1()
+{
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+	graph::Node n6("n6");
+
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2), 3);
+	ug.addEdge(graph::UndirectedEdge(n1, n3), 6);
+	ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
+	ug.addEdge(graph::UndirectedEdge(n2, n4), 5);
+	ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
+	ug.addEdge(graph::UndirectedEdge(n3, n4), 5);
+	ug.addEdge(graph::UndirectedEdge(n3, n6), 2);
+	ug.addEdge(graph::UndirectedEdge(n4, n5), 6);
+	ug.addEdge(graph::UndirectedEdge(n4, n6), 4);
+	ug.addEdge(graph::UndirectedEdge(n5, n6), 6);
+
+	graph::UndirectedGraph res;
+	res = graph::spanningtree::JarnikPrim::jarnikprim(ug, n1);
+
+	graph::UndirectedGraph expected;
+	expected.addEdge(graph::UndirectedEdge(n1, n2), 3);
+	expected.addEdge(graph::UndirectedEdge(n1, n4), 1);
+	expected.addEdge(graph::UndirectedEdge(n2, n5), 3);
+	expected.addEdge(graph::UndirectedEdge(n4, n6), 4);
+	expected.addEdge(graph::UndirectedEdge(n6, n3), 2);
+
+	CPPUNIT_ASSERT(expected == res);
+}
+
diff --git a/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.h b/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..6051afd06ca13bda9dfcd1295667234ae80de64f
--- /dev/null
+++ b/alib2algo/test-src/graph/spanningtree/JarnikPrimTest.h
@@ -0,0 +1,17 @@
+#ifndef JARNIK_PRIM_TEST_H_
+#define JARNIK_PRIM_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphJarnikPrimTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphJarnikPrimTest);
+	CPPUNIT_TEST(testSpanningTree1);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testSpanningTree1();
+
+};
+
+#endif // JARNIK_PRIM_TEST_H_
diff --git a/alib2algo/test-src/graph/traverse/BfsTest.cpp b/alib2algo/test-src/graph/traverse/BfsTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..75b3e15870ad763d4c0a4b7ed0ca7924705f4db3
--- /dev/null
+++ b/alib2algo/test-src/graph/traverse/BfsTest.cpp
@@ -0,0 +1,229 @@
+#include "BfsTest.h"
+
+#include "graph/traverse/Bfs.h"
+
+#include <unordered_map>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphBfsTest);
+
+void GraphBfsTest::testTraverseAll()
+{
+	// Common
+	int counter;
+	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));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n3, n6));
+	dg.addEdge(graph::DirectedEdge(n4, n1));
+	dg.addEdge(graph::DirectedEdge(n4, n5));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(6, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	ug.addEdge(graph::UndirectedEdge(n3, n6));
+	ug.addEdge(graph::UndirectedEdge(n4, n1));
+	ug.addEdge(graph::UndirectedEdge(n4, n5));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(6, counter);
+}
+
+void GraphBfsTest::testEarlyReturn()
+{
+	// Common
+	int counter;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		if (counter == 2) {
+			return false;
+		}
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(2, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		if (counter == 2) {
+			return false;
+		}
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(2, counter);
+}
+
+void GraphBfsTest::testDisconnectedGraph()
+{
+	// Common
+	int counter;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addNode(n5);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(4, counter);
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(dg, n5, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(1, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addNode(n5);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(4, counter);
+
+	counter = 0;
+	graph::traverse::Bfs::bfs(ug, n5, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(1, counter);
+}
+
+void GraphBfsTest::testBfs2()
+{
+	// Common
+	std::unordered_map<graph::Node, int> distances;
+	std::unordered_map<graph::Node, graph::Node> predecessors;
+
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+	dg.addEdge(graph::DirectedEdge(n1, n4));
+	dg.addEdge(graph::DirectedEdge(n2, n5));
+
+	distances.clear();
+	predecessors.clear();
+
+	graph::traverse::Bfs::bfs(dg, n1, [&](const graph::Node &n, const graph::Node &p, int d) {
+		distances[n] = d;
+		predecessors[n] = p;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(0, distances[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n2]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n4]);
+	CPPUNIT_ASSERT_EQUAL(2, distances[n5]);
+
+	CPPUNIT_ASSERT_EQUAL(graph::Node(), predecessors[n1]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n2]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n3]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n4]);
+	CPPUNIT_ASSERT_EQUAL(n2, predecessors[n5]);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+	ug.addEdge(graph::UndirectedEdge(n1, n4));
+	ug.addEdge(graph::UndirectedEdge(n2, n5));
+
+	distances.clear();
+	predecessors.clear();
+
+	graph::traverse::Bfs::bfs(ug, n1, [&](const graph::Node &n, const graph::Node &p, int d) {
+		distances[n] = d;
+		predecessors[n] = p;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(0, distances[n1]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n2]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n3]);
+	CPPUNIT_ASSERT_EQUAL(1, distances[n4]);
+	CPPUNIT_ASSERT_EQUAL(2, distances[n5]);
+
+	CPPUNIT_ASSERT_EQUAL(graph::Node(), predecessors[n1]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n2]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n3]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n4]);
+	CPPUNIT_ASSERT_EQUAL(n2, predecessors[n5]);
+}
diff --git a/alib2algo/test-src/graph/traverse/BfsTest.h b/alib2algo/test-src/graph/traverse/BfsTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..64459d68b13b26a3d76b0286e7eaa28f70801a95
--- /dev/null
+++ b/alib2algo/test-src/graph/traverse/BfsTest.h
@@ -0,0 +1,22 @@
+#ifndef BFS_TEST_H_
+#define BFS_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphBfsTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphBfsTest);
+	CPPUNIT_TEST(testTraverseAll);
+	CPPUNIT_TEST(testEarlyReturn);
+	CPPUNIT_TEST(testDisconnectedGraph);
+	CPPUNIT_TEST(testBfs2);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testTraverseAll();
+	void testEarlyReturn();
+	void testDisconnectedGraph();
+	void testBfs2();
+};
+
+#endif // BFS_TEST_H_
diff --git a/alib2algo/test-src/graph/traverse/DfsTest.cpp b/alib2algo/test-src/graph/traverse/DfsTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4610da03157d1053bacd1375d0801f1ef50342cf
--- /dev/null
+++ b/alib2algo/test-src/graph/traverse/DfsTest.cpp
@@ -0,0 +1,232 @@
+#include "DfsTest.h"
+
+#include "graph/traverse/Dfs.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphDfsTest);
+
+void GraphDfsTest::testTraverseAll()
+{
+	// Common
+	int counter;
+	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));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n3, n6));
+	dg.addEdge(graph::DirectedEdge(n4, n1));
+	dg.addEdge(graph::DirectedEdge(n4, n5));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(6, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	ug.addEdge(graph::UndirectedEdge(n3, n6));
+	ug.addEdge(graph::UndirectedEdge(n4, n1));
+	ug.addEdge(graph::UndirectedEdge(n4, n5));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(6, counter);
+}
+
+void GraphDfsTest::testEarlyReturn()
+{
+	// Common
+	int counter;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		if (counter == 2) {
+			return false;
+		}
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(2, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		if (counter == 2) {
+			return false;
+		}
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(2, counter);
+}
+
+void GraphDfsTest::testDisconnectedGraph()
+{
+	// Common
+	int counter;
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addNode(n5);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(dg, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(4, counter);
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(dg, n5, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(1, counter);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addNode(n5);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(ug, n1, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(4, counter);
+
+	counter = 0;
+	graph::traverse::Dfs::dfs(ug, n5, [&](const graph::Node &) {
+		counter++;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(1, counter);
+}
+
+void GraphDfsTest::testDfs2()
+{
+	// Common
+	int counter;
+	std::unordered_map<graph::Node, int> opened;
+	std::unordered_map<graph::Node, int> closed;
+	std::unordered_map<graph::Node, graph::Node> predecessors;
+
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// Directed
+	graph::DirectedGraph dg;
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+	dg.addEdge(graph::DirectedEdge(n1, n4));
+	dg.addEdge(graph::DirectedEdge(n2, n5));
+
+	counter = 0;
+	opened.clear();
+	closed.clear();
+	predecessors.clear();
+
+	graph::traverse::Dfs::dfs(dg, n1, [&](const graph::Node &n, const graph::Node &p, int o, int c) {
+		counter++;
+		opened[n] = o;
+		closed[n] = c;
+		predecessors[n] = p;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(5, counter);
+	CPPUNIT_ASSERT_EQUAL(4, closed[n5]);
+
+	CPPUNIT_ASSERT_EQUAL(graph::Node(), predecessors[n1]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n2]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n3]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n4]);
+	CPPUNIT_ASSERT_EQUAL(n2, predecessors[n5]);
+
+	// Undirected
+	graph::UndirectedGraph ug;
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+	ug.addEdge(graph::UndirectedEdge(n1, n4));
+	ug.addEdge(graph::UndirectedEdge(n2, n5));
+
+	counter = 0;
+	opened.clear();
+	closed.clear();
+	predecessors.clear();
+
+	graph::traverse::Dfs::dfs(ug, n1, [&](const graph::Node &n, const graph::Node &p, int o, int c) {
+		counter++;
+		opened[n] = o;
+		closed[n] = c;
+		predecessors[n] = p;
+		return true;
+	});
+
+	CPPUNIT_ASSERT_EQUAL(5, counter);
+	CPPUNIT_ASSERT_EQUAL(4, closed[n5]);
+
+	CPPUNIT_ASSERT_EQUAL(graph::Node(), predecessors[n1]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n2]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n3]);
+	CPPUNIT_ASSERT_EQUAL(n1, predecessors[n4]);
+	CPPUNIT_ASSERT_EQUAL(n2, predecessors[n5]);
+}
+
diff --git a/alib2algo/test-src/graph/traverse/DfsTest.h b/alib2algo/test-src/graph/traverse/DfsTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..280368070a2c336ed49460039c8ecf105feb60c4
--- /dev/null
+++ b/alib2algo/test-src/graph/traverse/DfsTest.h
@@ -0,0 +1,22 @@
+#ifndef DFS_TEST_H_
+#define DFS_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphDfsTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphDfsTest);
+	CPPUNIT_TEST(testTraverseAll);
+	CPPUNIT_TEST(testEarlyReturn);
+	CPPUNIT_TEST(testDisconnectedGraph);
+	CPPUNIT_TEST(testDfs2);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testTraverseAll();
+	void testEarlyReturn();
+	void testDisconnectedGraph();
+	void testDfs2();
+};
+
+#endif // DFS_TEST_H_
diff --git a/alib2data/src/StringApi.cpp b/alib2data/src/StringApi.cpp
index b1ed53c2f62fe48e9f91bfa666a5297039fee5e6..48e958afd038416df161da0d30e7add4ad266a26 100644
--- a/alib2data/src/StringApi.cpp
+++ b/alib2data/src/StringApi.cpp
@@ -17,6 +17,7 @@ const regexp::RegExpFromStringParser FromStringParsers::regexpParser;
 const string::StringFromStringParser FromStringParsers::stringParser;
 const automaton::AutomatonFromStringParser FromStringParsers::automatonParser;
 const grammar::GrammarFromStringParser FromStringParsers::grammarParser;
+const graph::GraphFromStringParser FromStringParsers::graphParser;
 const primitive::PrimitiveFromStringParser FromStringParsers::primitiveParser;
 
 const label::LabelToStringComposer ToStringComposers::labelComposer;
@@ -25,6 +26,7 @@ const regexp::RegExpToStringComposer ToStringComposers::regexpComposer;
 const string::StringToStringComposer ToStringComposers::stringComposer;
 const automaton::AutomatonToStringComposer ToStringComposers::automatonComposer;
 const grammar::GrammarToStringComposer ToStringComposers::grammarComposer;
+const graph::GraphToStringComposer ToStringComposers::graphComposer;
 const primitive::PrimitiveToStringComposer ToStringComposers::primitiveComposer;
 
 alphabet::Symbol stringApi<alphabet::Symbol>::parse(std::istream& input) {
@@ -52,6 +54,14 @@ void stringApi<grammar::Grammar>::compose(std::ostream& output, const grammar::G
 	return ToStringComposers::grammarComposer.compose(output, data);
 }
 
+graph::Graph stringApi<graph::Graph>::parse(std::istream& input) {
+	return FromStringParsers::graphParser.parseGraph(input);
+}
+
+void stringApi<graph::Graph>::compose(std::ostream& output, const graph::Graph& data) {
+	return ToStringComposers::graphComposer.compose(output, data);
+}
+
 label::Label stringApi<label::Label>::parse(std::istream& input) {
 	return FromStringParsers::labelParser.parseLabel(input);
 }
diff --git a/alib2data/src/StringApi.hpp b/alib2data/src/StringApi.hpp
index cc9044f7760e59deeab5127a450d44d063f19939..752e3f1224776596a60de56f97d81f6b453d6067 100644
--- a/alib2data/src/StringApi.hpp
+++ b/alib2data/src/StringApi.hpp
@@ -14,6 +14,7 @@
 #include "string/StringFromStringParser.h"
 #include "automaton/AutomatonFromStringParser.h"
 #include "grammar/GrammarFromStringParser.h"
+#include "graph/GraphFromStringParser.h"
 #include "primitive/PrimitiveFromStringParser.h"
 
 #include "label/LabelToStringComposer.h"
@@ -22,6 +23,7 @@
 #include "string/StringToStringComposer.h"
 #include "automaton/AutomatonToStringComposer.h"
 #include "grammar/GrammarToStringComposer.h"
+#include "graph/GraphToStringComposer.h"
 #include "primitive/PrimitiveToStringComposer.h"
 
 #include "object/ObjectBase.h"
@@ -53,6 +55,12 @@ struct stringApi<grammar::Grammar> {
 	static void compose(std::ostream& output, const grammar::Grammar& data);
 };
 
+template<>
+struct stringApi<graph::Graph> {
+	static graph::Graph parse(std::istream& input);
+	static void compose(std::ostream& output, const graph::Graph& data);
+};
+
 template<>
 struct stringApi<label::Label> {
 	static label::Label parse(std::istream& input);
@@ -85,6 +93,7 @@ public:
 	static const string::StringFromStringParser stringParser;
 	static const automaton::AutomatonFromStringParser automatonParser;
 	static const grammar::GrammarFromStringParser grammarParser;
+	static const graph::GraphFromStringParser graphParser;
 	static const primitive::PrimitiveFromStringParser primitiveParser;
 
 };
@@ -97,6 +106,7 @@ public:
 	static const string::StringToStringComposer stringComposer;
 	static const automaton::AutomatonToStringComposer automatonComposer;
 	static const grammar::GrammarToStringComposer grammarComposer;
+	static const graph::GraphToStringComposer graphComposer;
 	static const primitive::PrimitiveToStringComposer primitiveComposer;
 };
 
diff --git a/alib2data/src/XmlApi.cpp b/alib2data/src/XmlApi.cpp
index ce6c606d9474e9b0edbbfb68709f07f9beaed2aa..6ec73911c9643241aa3106c9c7eb3d2a3852376d 100644
--- a/alib2data/src/XmlApi.cpp
+++ b/alib2data/src/XmlApi.cpp
@@ -15,6 +15,7 @@ const regexp::RegExpFromXMLParser FromXMLParsers::regexpParser;
 const string::StringFromXMLParser FromXMLParsers::stringParser;
 const automaton::AutomatonFromXMLParser FromXMLParsers::automatonParser;
 const grammar::GrammarFromXMLParser FromXMLParsers::grammarParser;
+const graph::GraphFromXMLParser FromXMLParsers::graphParser;
 const exception::ExceptionFromXMLParser FromXMLParsers::exceptionParser;
 const alib::ObjectFromXMLParser FromXMLParsers::objectParser;
 const container::ContainerFromXMLParser FromXMLParsers::containerParser;
@@ -26,6 +27,7 @@ const regexp::RegExpToXMLComposer ToXMLComposers::regexpComposer;
 const string::StringToXMLComposer ToXMLComposers::stringComposer;
 const automaton::AutomatonToXMLComposer ToXMLComposers::automatonComposer;
 const grammar::GrammarToXMLComposer ToXMLComposers::grammarComposer;
+const graph::GraphToXMLComposer ToXMLComposers::graphComposer;
 const alib::ObjectToXMLComposer ToXMLComposers::objectComposer;
 const exception::ExceptionToXMLComposer ToXMLComposers::exceptionComposer;
 const container::ContainerToXMLComposer ToXMLComposers::containerComposer;
@@ -82,6 +84,8 @@ const std::string Names::GRAMMAR_CSG = "CSG";
 const std::string Names::GRAMMAR_NON_CONTRACTING_GRAMMAR = "NonContractingGrammar";
 const std::string Names::GRAMMAR_CONTEXT_PRESERVING_UNRESTRICTED_GRAMMAR = "ContextPpreservingUnrestrictedGrammar";
 const std::string Names::GRAMMAR_UNRESTRICTED_GRAMMAR = "UnrestrictedGrammar";
+const std::string Names::GRAPH_DIRECTED_GRAPH = "DirectedGraph";
+const std::string Names::GRAPH_UNDIRECTED_GRAPH = "UndirectedGraph";
 const std::string Names::LABEL_PRIMITIVE_LABEL = "PrimitiveLabel";
 const std::string Names::LABEL_HEXAVIGESIMAL_LABEL = "HexavigesimalLabel";
 const std::string Names::LABEL_OBJECT_LABEL = "ObjectLabel";
@@ -750,6 +754,43 @@ void xmlApi<grammar::UnrestrictedGrammar>::compose(std::list<sax::Token>& output
 }
 
 
+graph::Graph xmlApi<graph::Graph>::parse(std::list<sax::Token>& input) {
+	return FromXMLParsers::graphParser.parseGraph(input);
+}
+
+bool xmlApi<graph::Graph>::first(const std::list<sax::Token>& input) {
+	return FromXMLParsers::graphParser.first(input);
+}
+
+void xmlApi<graph::Graph>::compose(std::list<sax::Token>& output, const graph::Graph& data) {
+	ToXMLComposers::graphComposer.compose(output, data);
+}
+
+graph::DirectedGraph xmlApi<graph::DirectedGraph>::parse(std::list<sax::Token>& input) {
+	return FromXMLParsers::graphParser.parseDirectedGraph(input);
+}
+
+bool xmlApi<graph::DirectedGraph>::first(const std::list<sax::Token>& input) {
+	return sax::FromXMLParserHelper::isToken(input, sax::Token::TokenType::START_ELEMENT, Names::GRAPH_DIRECTED_GRAPH);
+}
+
+void xmlApi<graph::DirectedGraph>::compose(std::list<sax::Token>& output, const graph::DirectedGraph& data) {
+	ToXMLComposers::graphComposer.compose(output, data);
+}
+
+graph::UndirectedGraph xmlApi<graph::UndirectedGraph>::parse(std::list<sax::Token>& input) {
+	return FromXMLParsers::graphParser.parseUndirectedGraph(input);
+}
+
+bool xmlApi<graph::UndirectedGraph>::first(const std::list<sax::Token>& input) {
+	return sax::FromXMLParserHelper::isToken(input, sax::Token::TokenType::START_ELEMENT, Names::GRAPH_UNDIRECTED_GRAPH);
+}
+
+void xmlApi<graph::UndirectedGraph>::compose(std::list<sax::Token>& output, const graph::UndirectedGraph& data) {
+	ToXMLComposers::graphComposer.compose(output, data);
+}
+
+
 label::Label xmlApi<label::Label>::parse(std::list<sax::Token>& input) {
 	return FromXMLParsers::labelParser.parseLabel(input);
 }
@@ -1245,6 +1286,14 @@ void ToXMLComposers::Visit(void* data, const grammar::UnrestrictedGrammar& gramm
 	xmlApi<grammar::UnrestrictedGrammar>::compose(*((std::list<sax::Token>*) data), grammar);
 }
 
+void ToXMLComposers::Visit(void* data, const graph::DirectedGraph& graph) const {
+	xmlApi<graph::DirectedGraph>::compose(*((std::list<sax::Token>*) data), graph);
+}
+
+void ToXMLComposers::Visit(void* data, const graph::UndirectedGraph& graph) const {
+	xmlApi<graph::UndirectedGraph>::compose(*((std::list<sax::Token>*) data), graph);
+}
+
 void ToXMLComposers::Visit(void* data, const label::PrimitiveLabel& label) const {
 	xmlApi<label::PrimitiveLabel>::compose(*((std::list<sax::Token>*) data), label);
 }
diff --git a/alib2data/src/XmlApi.hpp b/alib2data/src/XmlApi.hpp
index 1c4b5f54fd71fb5561dfb9a1d859a4aeb11095bc..8a327e44239ba2442810ec6ac60787f6fc6e675b 100644
--- a/alib2data/src/XmlApi.hpp
+++ b/alib2data/src/XmlApi.hpp
@@ -14,6 +14,7 @@
 #include "string/StringFromXMLParser.h"
 #include "automaton/AutomatonFromXMLParser.h"
 #include "grammar/GrammarFromXMLParser.h"
+#include "graph/GraphFromXMLParser.h"
 #include "object/ObjectFromXMLParser.h"
 #include "exception/ExceptionFromXMLParser.h"
 #include "primitive/PrimitiveFromXMLParser.h"
@@ -24,6 +25,7 @@
 #include "string/StringToXMLComposer.h"
 #include "automaton/AutomatonToXMLComposer.h"
 #include "grammar/GrammarToXMLComposer.h"
+#include "graph/GraphToXMLComposer.h"
 #include "object/ObjectToXMLComposer.h"
 #include "exception/ExceptionToXMLComposer.h"
 #include "primitive/PrimitiveToXMLComposer.h"
@@ -83,6 +85,8 @@ public:
 	const static std::string GRAMMAR_NON_CONTRACTING_GRAMMAR;
 	const static std::string GRAMMAR_CONTEXT_PRESERVING_UNRESTRICTED_GRAMMAR;
 	const static std::string GRAMMAR_UNRESTRICTED_GRAMMAR;
+	const static std::string GRAPH_DIRECTED_GRAPH;
+	const static std::string GRAPH_UNDIRECTED_GRAPH;
 	const static std::string LABEL_PRIMITIVE_LABEL;
 	const static std::string LABEL_HEXAVIGESIMAL_LABEL;
 	const static std::string LABEL_OBJECT_LABEL;
@@ -525,6 +529,28 @@ struct xmlApi<grammar::UnrestrictedGrammar> {
 };
 
 
+template<>
+struct xmlApi<graph::Graph> {
+	static graph::Graph parse(std::list<sax::Token>& input);
+	static bool first(const std::list<sax::Token>& input);
+	static void compose(std::list<sax::Token>& output, const graph::Graph& data);
+};
+
+template<>
+struct xmlApi<graph::DirectedGraph> {
+	static graph::DirectedGraph parse(std::list<sax::Token>& input);
+	static bool first(const std::list<sax::Token>& input);
+	static void compose(std::list<sax::Token>& output, const graph::DirectedGraph& data);
+};
+
+template<>
+struct xmlApi<graph::UndirectedGraph> {
+	static graph::UndirectedGraph parse(std::list<sax::Token>& input);
+	static bool first(const std::list<sax::Token>& input);
+	static void compose(std::list<sax::Token>& output, const graph::UndirectedGraph& data);
+};
+
+
 template<>
 struct xmlApi<label::Label> {
 	static label::Label parse(std::list<sax::Token>& input);
@@ -730,6 +756,7 @@ public:
 	static const string::StringFromXMLParser stringParser;
 	static const automaton::AutomatonFromXMLParser automatonParser;
 	static const grammar::GrammarFromXMLParser grammarParser;
+	static const graph::GraphFromXMLParser graphParser;
 	static const exception::ExceptionFromXMLParser exceptionParser;
 	static const ObjectFromXMLParser objectParser;
 	static const container::ContainerFromXMLParser containerParser;
@@ -787,6 +814,9 @@ class ToXMLComposers : public VisitableObjectBase::const_visitor_type {
 	void Visit(void*, const grammar::ContextPreservingUnrestrictedGrammar& grammar) const;
 	void Visit(void*, const grammar::UnrestrictedGrammar& grammar) const;
 
+	void Visit(void*, const graph::DirectedGraph& graph) const;
+	void Visit(void*, const graph::UndirectedGraph& graph) const;
+
 	void Visit(void*, const label::PrimitiveLabel& label) const;
 	void Visit(void*, const label::HexavigesimalLabel& label) const;
 	void Visit(void*, const label::ObjectLabel& label) const;
@@ -819,6 +849,7 @@ public:
 	static const string::StringToXMLComposer stringComposer;
 	static const automaton::AutomatonToXMLComposer automatonComposer;
 	static const grammar::GrammarToXMLComposer grammarComposer;
+	static const graph::GraphToXMLComposer graphComposer;
 	static const exception::ExceptionToXMLComposer exceptionComposer;
 	static const ObjectToXMLComposer objectComposer;
 	static const container::ContainerToXMLComposer containerComposer;
diff --git a/alib2data/src/graph/Graph.h b/alib2data/src/graph/Graph.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d47b0c256312391d97870ca0e55277bbf8d84f2
--- /dev/null
+++ b/alib2data/src/graph/Graph.h
@@ -0,0 +1,29 @@
+#ifndef GRAPH_H_
+#define GRAPH_H_
+
+#include "../std/visitor.hpp"
+#include "../std/compare.hpp"
+#include "../common/wrapper.hpp"
+#include "GraphBase.h"
+
+namespace graph {
+
+// Wrapper around graphs.
+typedef alib::wrapper<GraphBase> Graph;
+
+} // namespace graph
+
+namespace std {
+
+template<>
+struct compare<graph::Graph>
+{
+	int operator()(const graph::Graph &first, const graph::Graph &second) const
+	{
+		return first.getData().compare(second.getData());
+	}
+};
+
+} // namespace
+
+#endif // GRAPH_H_
diff --git a/alib2data/src/graph/GraphBase.h b/alib2data/src/graph/GraphBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3539fef1a6976721849661f4ea0724838dc6ee1
--- /dev/null
+++ b/alib2data/src/graph/GraphBase.h
@@ -0,0 +1,29 @@
+#ifndef GRAPH_BASE_H_
+#define GRAPH_BASE_H_
+
+#include "common/Macros.h"
+#include "../common/base.hpp"
+#include "../object/ObjectBase.h"
+
+namespace graph {
+
+class GraphBase;
+
+typedef std::acceptor_base<GraphBase, DirectedGraph, UndirectedGraph> VisitableGraphBase;
+
+// Abstract base class for all graphs.
+class GraphBase : public alib::ObjectBase, public VisitableGraphBase
+{
+public:
+	using VisitableGraphBase::Accept;
+	using alib::VisitableObjectBase::Accept;
+
+	virtual GraphBase *clone() const = 0;
+	virtual GraphBase *plunder() && = 0;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_COMPARE(GraphBase)
+
+#endif // GRAPH_BASE_H_
diff --git a/alib2data/src/graph/GraphFeatures.h b/alib2data/src/graph/GraphFeatures.h
new file mode 100644
index 0000000000000000000000000000000000000000..dba0c28394df3d076e01d41ec1ac190bf65bf5e7
--- /dev/null
+++ b/alib2data/src/graph/GraphFeatures.h
@@ -0,0 +1,13 @@
+#ifndef GRAPH_FEATURES_H_
+#define GRAPH_FEATURES_H_
+
+namespace graph {
+
+enum class FEATURES {
+	DIRECTED,
+	UNDIRECTED
+};
+
+} // namespace graph
+
+#endif // GRAPH_FEATURES_H_
diff --git a/alib2data/src/graph/GraphFromStringLexer.cpp b/alib2data/src/graph/GraphFromStringLexer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9dc82b1abbb11015a8deead10a52e121de073aa5
--- /dev/null
+++ b/alib2data/src/graph/GraphFromStringLexer.cpp
@@ -0,0 +1,106 @@
+#include "GraphFromStringLexer.h"
+
+#include <cctype>
+
+namespace graph {
+
+GraphFromStringLexer::Token GraphFromStringLexer::next(std::istream &in) const
+{
+	Token token;
+	token.type = TokenType::ERROR;
+	char character;
+
+L0:
+	character = in.get();
+
+	if (in.eof()) {
+		token.type = TokenType::TEOF;
+		return token;
+	} else if (isspace(character)) {
+		token.raw += character;
+		goto L0;
+	} else if (character == '(') {
+		token.type = TokenType::LPAR;
+		token.value += character;
+		token.raw += character;
+		return token;
+	} else if (character == ')') {
+		token.type = TokenType::RPAR;
+		token.value += character;
+		token.raw += character;
+		return token;
+	} else if (character == ':') {
+		token.type = TokenType::COLON;
+		token.value += character;
+		token.raw += character;
+		return token;
+	} else if (character == ',') {
+		token.type = TokenType::COMMA;
+		token.value += character;
+		token.raw += character;
+		return token;
+	} else if (character == '=') {
+		token.type = TokenType::EQUAL;
+		token.value += character;
+		token.raw += character;
+		return token;
+	} else if (character >= '0' && character <= '9') {
+		token.type = TokenType::INTEGER;
+		token.value += character;
+		token.raw += character;
+		goto L1;
+	} else {
+		in.putback(character);
+		putback(in, std::move(token));
+		token.type = TokenType::ERROR;
+		return token;
+	}
+
+L1:
+	character = in.get();
+	if (in.eof()) {
+		return token;
+	} else if (character >= '0' && character <= '9') {
+		token.value += character;
+		token.raw += character;
+		goto L1;
+	} else {
+		in.unget();
+		return token;
+	}
+}
+
+GraphFromStringLexer::Token GraphFromStringLexer::peek(std::istream &in) const
+{
+	Token token = next(in);
+	putback(in, token);
+	return token;
+}
+
+void GraphFromStringLexer::putback(std::istream &input, GraphFromStringLexer::Token token) const
+{
+	while (!token.raw.empty()) {
+		input.putback(token.raw.back());
+		token.raw.pop_back();
+	}
+	input.clear();
+}
+
+std::string GraphFromStringLexer::getString(std::istream &input) const
+{
+	std::string str;
+	char character;
+
+	while (input >> character) {
+		if (character == ':') {
+			input.putback(character);
+			return str;
+		}
+		str += character;
+	}
+
+	return std::string();
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/GraphFromStringLexer.h b/alib2data/src/graph/GraphFromStringLexer.h
new file mode 100644
index 0000000000000000000000000000000000000000..92e7d3f6da485b667b7312ddf98f1c30697d914a
--- /dev/null
+++ b/alib2data/src/graph/GraphFromStringLexer.h
@@ -0,0 +1,38 @@
+#ifndef GRAPH_FROM_STRING_LEXER_H_
+#define GRAPH_FROM_STRING_LEXER_H_
+
+#include <string>
+#include <sstream>
+
+namespace graph {
+
+class GraphFromStringLexer
+{
+public:
+	enum class TokenType {
+		LPAR,
+		RPAR,
+		COLON,
+		COMMA,
+		EQUAL,
+		INTEGER,
+		TEOF,
+		ERROR
+	};
+
+	struct Token {
+		TokenType type;
+		std::string value;
+		std::string raw;
+	};
+
+	Token next(std::istream &input) const;
+	Token peek(std::istream &input) const;
+	void putback(std::istream &input, Token token) const;
+
+	std::string getString(std::istream &input) const;
+};
+
+} // namespace graph
+
+#endif // GRAPH_FROM_STRING_LEXER_H_
diff --git a/alib2data/src/graph/GraphFromStringParser.cpp b/alib2data/src/graph/GraphFromStringParser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c341ea9fb2c508b878eec443bf736d1a71a3f556
--- /dev/null
+++ b/alib2data/src/graph/GraphFromStringParser.cpp
@@ -0,0 +1,249 @@
+#include "GraphFromStringParser.h"
+#include "../exception/AlibException.h"
+
+#include "../StringApi.hpp"
+
+namespace graph {
+
+Graph GraphFromStringParser::parseGraph(std::istream &input) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+	if (token.type != GraphFromStringLexer::TokenType::LPAR) {
+		throw exception::AlibException("Invalid input. Expected LPAR");
+	}
+
+	const std::string &str = lexer.getString(input);
+	if (str == "DirectedGraph") {
+		parseDelimiter(input); // :
+		Graph graph{parseDirectedGraph(input)};
+
+		if (lexer.next(input).type != GraphFromStringLexer::TokenType::RPAR) {
+			throw exception::AlibException("Invalid input. Expected RPAR");
+		}
+		return graph;
+	} else if (str == "UndirectedGraph") {
+		parseDelimiter(input); // :
+		Graph graph{parseUndirectedGraph(input)};
+
+		if (lexer.next(input).type != GraphFromStringLexer::TokenType::RPAR) {
+			throw exception::AlibException("Invalid input. Expected RPAR");
+		}
+		return graph;
+	}
+
+	throw exception::AlibException("Invalid graph type");
+}
+
+DirectedGraph GraphFromStringParser::parseDirectedGraph(std::istream &input) const
+{
+	DirectedGraph graph(parseRepresentation(input));
+	parseDelimiter(input); // :
+	parseNodes(input, graph);
+	parseDelimiter(input); // :
+	parseDirectedEdges(input, graph);
+	parseDelimiter(input); // :
+	parseNodeValues(input, graph);
+	parseDelimiter(input); // :
+	parseDirectedEdgeValues(input, graph);
+	return graph;
+}
+
+UndirectedGraph GraphFromStringParser::parseUndirectedGraph(std::istream &input) const
+{
+	UndirectedGraph graph(parseRepresentation(input));
+	parseDelimiter(input); // :
+	parseNodes(input, graph);
+	parseDelimiter(input); // :
+	parseUndirectedEdges(input, graph);
+	parseDelimiter(input); // :
+	parseNodeValues(input, graph);
+	parseDelimiter(input); // :
+	parseUndirectedEdgeValues(input, graph);
+	return graph;
+}
+
+REPRESENTATION GraphFromStringParser::parseRepresentation(std::istream &input) const
+{
+	std::string representation = lexer.getString(input);
+	if (representation.empty()) {
+		throw exception::AlibException("Invalid representation");
+	}
+	return representationFromString(representation);
+}
+
+Node GraphFromStringParser::parseNode(std::istream &input) const
+{
+	return Node(alib::stringApi<label::Label>::parse(input));
+}
+
+DirectedEdge GraphFromStringParser::parseDirectedEdge(std::istream &input) const
+{
+	Node from = parseNode(input);
+	label::Label name = alib::stringApi<label::Label>::parse(input);
+	Node to = parseNode(input);
+	return DirectedEdge(from, to, name);
+}
+
+UndirectedEdge GraphFromStringParser::parseUndirectedEdge(std::istream &input) const
+{
+	Node first = parseNode(input);
+	label::Label name = alib::stringApi<label::Label>::parse(input);
+	Node second = parseNode(input);
+	return UndirectedEdge(first, second, name);
+}
+
+template<typename T>
+void GraphFromStringParser::parseNodes(std::istream &input, T &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+
+	// Empty nodes?
+	if (token.type == GraphFromStringLexer::TokenType::COLON) {
+		return;
+	}
+	lexer.putback(input, token);
+
+	while (true) {
+		graph.addNode(parseNode(input));
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+void GraphFromStringParser::parseDirectedEdges(std::istream &input, DirectedGraph &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+
+	// Empty edges?
+	if (token.type == GraphFromStringLexer::TokenType::COLON) {
+		return;
+	}
+	lexer.putback(input, token);
+
+	while (true) {
+		graph.addEdge(parseDirectedEdge(input));
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+void GraphFromStringParser::parseUndirectedEdges(std::istream &input, UndirectedGraph &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+
+	// Empty edges?
+	if (token.type == GraphFromStringLexer::TokenType::COLON) {
+		return;
+	}
+	lexer.putback(input, token);
+
+	while (true) {
+		graph.addEdge(parseUndirectedEdge(input));
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+void GraphFromStringParser::parseDelimiter(std::istream &input) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+	if (token.type != GraphFromStringLexer::TokenType::COLON) {
+		throw exception::AlibException("Invalid input. Expected COLON");
+	}
+}
+
+int GraphFromStringParser::parseValue(std::istream &input) const
+{
+	GraphFromStringLexer::Token token = lexer.next(input);
+	if (token.type != GraphFromStringLexer::TokenType::EQUAL) {
+		throw exception::AlibException("Invalid input. Expected EQUAL");
+	}
+
+	token = lexer.next(input);
+	if (token.type != GraphFromStringLexer::TokenType::INTEGER) {
+		throw exception::AlibException("Invalid input. Expected INTEGER");
+	}
+
+	return std::stoi(token.value);
+}
+
+template<typename T>
+void GraphFromStringParser::parseNodeValues(std::istream &input, T &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.peek(input);
+
+	// Empty values?
+	if (token.type == GraphFromStringLexer::TokenType::COLON) {
+		return;
+	}
+
+	while (true) {
+		graph::Node node = parseNode(input);
+		int value = parseValue(input);
+		graph.setNodeValue(node, value);
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+void GraphFromStringParser::parseDirectedEdgeValues(std::istream &input, DirectedGraph &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.peek(input);
+
+	// Empty values?
+	if (token.type == GraphFromStringLexer::TokenType::RPAR) {
+		return;
+	}
+
+	while (true) {
+		graph::DirectedEdge node = parseDirectedEdge(input);
+		int value = parseValue(input);
+		graph.setEdgeValue(node, value);
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+void GraphFromStringParser::parseUndirectedEdgeValues(std::istream &input, UndirectedGraph &graph) const
+{
+	GraphFromStringLexer::Token token = lexer.peek(input);
+
+	// Empty values?
+	if (token.type == GraphFromStringLexer::TokenType::RPAR) {
+		return;
+	}
+
+	while (true) {
+		graph::UndirectedEdge node = parseUndirectedEdge(input);
+		int value = parseValue(input);
+		graph.setEdgeValue(node, value);
+
+		token = lexer.next(input);
+		if (token.type != GraphFromStringLexer::TokenType::COMMA) {
+			lexer.putback(input, token);
+			return;
+		}
+	}
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/GraphFromStringParser.h b/alib2data/src/graph/GraphFromStringParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..379ac016f89adfa3b36f7987e184100b15641f4d
--- /dev/null
+++ b/alib2data/src/graph/GraphFromStringParser.h
@@ -0,0 +1,53 @@
+#ifndef GRAPH_FROM_STRING_PARSER_H_
+#define GRAPH_FROM_STRING_PARSER_H_
+
+#include "Graph.h"
+#include "GraphFeatures.h"
+#include "GraphRepresentation.h"
+#include "directed/DirectedGraph.h"
+#include "undirected/UndirectedGraph.h"
+
+#include "GraphFromStringLexer.h"
+#include "GraphFeatures.h"
+
+namespace alib {
+
+template<typename T>
+struct stringApi;
+
+} // namespace alib
+
+namespace graph {
+
+class GraphFromStringParser
+{
+private:
+	Graph parseGraph(std::istream &input) const;
+	DirectedGraph parseDirectedGraph(std::istream &input) const;
+	UndirectedGraph parseUndirectedGraph(std::istream &input) const;
+
+	REPRESENTATION parseRepresentation(std::istream &input) const;
+	Node parseNode(std::istream &input) const;
+	DirectedEdge parseDirectedEdge(std::istream &input) const;
+	UndirectedEdge parseUndirectedEdge(std::istream &input) const;
+
+	template<typename T>
+	void parseNodes(std::istream &input, T &graph) const;
+	void parseDirectedEdges(std::istream &input, DirectedGraph &graph) const;
+	void parseUndirectedEdges(std::istream &input, UndirectedGraph &graph) const;
+	void parseDelimiter(std::istream &input) const;
+	int parseValue(std::istream &input) const;
+
+	template<typename T>
+	void parseNodeValues(std::istream &input, T &graph) const;
+	void parseDirectedEdgeValues(std::istream &input, DirectedGraph &graph) const;
+	void parseUndirectedEdgeValues(std::istream &input, UndirectedGraph &graph) const;
+
+	GraphFromStringLexer lexer;
+
+	template<typename T> friend class alib::stringApi;
+};
+
+} // namespace graph
+
+#endif // GRAPH_FROM_STRING_PARSER_H_
diff --git a/alib2data/src/graph/GraphFromXMLParser.cpp b/alib2data/src/graph/GraphFromXMLParser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb35275417e9c27886c7251b7d60a76cb2c4efdd
--- /dev/null
+++ b/alib2data/src/graph/GraphFromXMLParser.cpp
@@ -0,0 +1,200 @@
+#include "GraphFromXMLParser.h"
+#include "../sax/ParserException.h"
+
+#include "../XmlApi.hpp"
+
+namespace graph {
+
+bool GraphFromXMLParser::first(const std::list<sax::Token> &input) const
+{
+	if (alib::xmlApi<DirectedGraph>::first(input)) {
+		return true;
+	}
+
+	return false;
+}
+
+Graph GraphFromXMLParser::parseGraph(std::list<sax::Token> &input) const
+{
+	if (alib::xmlApi<DirectedGraph>::first(input)) {
+		return Graph(parseDirectedGraph(input));
+	} else if (alib::xmlApi<UndirectedGraph>::first(input)) {
+		return Graph(parseUndirectedGraph(input));
+	}
+
+	throw exception::AlibException();
+}
+
+DirectedGraph GraphFromXMLParser::parseDirectedGraph(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, alib::Names::GRAPH_DIRECTED_GRAPH);
+
+	DirectedGraph graph(parseRepresentation(input));
+	parseNodes(input, graph);
+	parseDirectedEdges(input, graph);
+	parseNodeValues(input, graph);
+	parseDirectedEdgeValues(input, graph);
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, alib::Names::GRAPH_DIRECTED_GRAPH);
+	return graph;
+}
+
+UndirectedGraph GraphFromXMLParser::parseUndirectedGraph(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, alib::Names::GRAPH_UNDIRECTED_GRAPH);
+
+	UndirectedGraph graph(parseRepresentation(input));
+	parseNodes(input, graph);
+	parseUndirectedEdges(input, graph);
+	parseNodeValues(input, graph);
+	parseUndirectedEdgeValues(input, graph);
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, alib::Names::GRAPH_UNDIRECTED_GRAPH);
+	return graph;
+}
+
+REPRESENTATION GraphFromXMLParser::parseRepresentation(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "representation");
+
+	REPRESENTATION r = representationFromString(popTokenData(input, sax::Token::TokenType::CHARACTER));
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "representation");
+	return r;
+}
+
+Node GraphFromXMLParser::parseNode(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "node");
+
+	Node n = Node(alib::xmlApi<label::Label>::parse(input));
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "node");
+	return n;
+}
+
+DirectedEdge GraphFromXMLParser::parseDirectedEdge(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edge");
+
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "from");
+	Node from = parseNode(input);
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "from");
+
+	label::Label name = alib::xmlApi<label::Label>::parse(input);
+
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "to");
+	Node to = parseNode(input);
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "to");
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edge");
+	return DirectedEdge(from, to, name);
+}
+
+UndirectedEdge GraphFromXMLParser::parseUndirectedEdge(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edge");
+
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "first");
+	Node first = parseNode(input);
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "first");
+
+	label::Label name = alib::xmlApi<label::Label>::parse(input);
+
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "second");
+	Node second = parseNode(input);
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "second");
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edge");
+	return UndirectedEdge(first, second, name);
+}
+
+int GraphFromXMLParser::parseValue(std::list<sax::Token> &input) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "value");
+
+	int value = std::stoi(popTokenData(input, sax::Token::TokenType::CHARACTER));
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "value");
+	return value;
+}
+
+template<typename T>
+void GraphFromXMLParser::parseNodes(std::list<sax::Token> &input, T &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "nodes");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph.addNode(parseNode(input));
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "nodes");
+}
+
+void GraphFromXMLParser::parseDirectedEdges(std::list<sax::Token> &input, DirectedGraph &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edges");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph.addEdge(parseDirectedEdge(input));
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edges");
+}
+
+void GraphFromXMLParser::parseUndirectedEdges(std::list<sax::Token> &input, UndirectedGraph &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edges");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph.addEdge(parseUndirectedEdge(input));
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edges");
+}
+
+template<typename T>
+void GraphFromXMLParser::parseNodeValues(std::list<sax::Token> &input, T &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "nodevalues");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph::Node node = parseNode(input);
+		int value = parseValue(input);
+
+		graph.setNodeValue(node, value);
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "nodevalues");
+}
+
+void GraphFromXMLParser::parseDirectedEdgeValues(std::list<sax::Token> &input, DirectedGraph &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edgevalues");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph::DirectedEdge edge = parseDirectedEdge(input);
+		int value = parseValue(input);
+
+		graph.setEdgeValue(edge, value);
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edgevalues");
+}
+
+void GraphFromXMLParser::parseUndirectedEdgeValues(std::list<sax::Token> &input, UndirectedGraph &graph) const
+{
+	popToken(input, sax::Token::TokenType::START_ELEMENT, "edgevalues");
+
+	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
+		graph::UndirectedEdge edge = parseUndirectedEdge(input);
+		int value = parseValue(input);
+
+		graph.setEdgeValue(edge, value);
+	}
+
+	popToken(input, sax::Token::TokenType::END_ELEMENT, "edgevalues");
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/GraphFromXMLParser.h b/alib2data/src/graph/GraphFromXMLParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..6dff7588316f8e958f8666a078a1443de3b3f096
--- /dev/null
+++ b/alib2data/src/graph/GraphFromXMLParser.h
@@ -0,0 +1,53 @@
+#ifndef GRAPH_FROM_XML_PARSER_H_
+#define GRAPH_FROM_XML_PARSER_H_
+
+#include "../sax/FromXMLParserHelper.h"
+#include "Graph.h"
+#include "GraphFeatures.h"
+#include "directed/DirectedGraph.h"
+#include "undirected/UndirectedGraph.h"
+#include "../sax/Token.h"
+#include "../alphabet/Symbol.h"
+
+namespace alib {
+
+template<typename T>
+struct xmlApi;
+
+} // namespace alib
+
+namespace graph {
+
+// Parser used to get Graph from XML parsed into list of tokens.
+class GraphFromXMLParser : public sax::FromXMLParserHelper
+{
+public:
+	bool first(const std::list<sax::Token> &input) const;
+
+private:
+	Graph parseGraph(std::list<sax::Token> &input) const;
+	DirectedGraph parseDirectedGraph(std::list<sax::Token> &input) const;
+	UndirectedGraph parseUndirectedGraph(std::list<sax::Token> &input) const;
+
+	REPRESENTATION parseRepresentation(std::list<sax::Token> &input) const;
+	Node parseNode(std::list<sax::Token> &input) const;
+	DirectedEdge parseDirectedEdge(std::list<sax::Token> &input) const;
+	UndirectedEdge parseUndirectedEdge(std::list<sax::Token> &input) const;
+	int parseValue(std::list<sax::Token> &input) const;
+
+	template<typename T>
+	void parseNodes(std::list<sax::Token> &input, T &graph) const;
+	void parseDirectedEdges(std::list<sax::Token> &input, DirectedGraph &graph) const;
+	void parseUndirectedEdges(std::list<sax::Token> &input, UndirectedGraph &graph) const;
+
+	template<typename T>
+	void parseNodeValues(std::list<sax::Token> &input, T &graph) const;
+	void parseDirectedEdgeValues(std::list<sax::Token> &input, DirectedGraph &graph) const;
+	void parseUndirectedEdgeValues(std::list<sax::Token> &input, UndirectedGraph &graph) const;
+
+	template<typename T> friend class alib::xmlApi;
+};
+
+} // namespace graph
+
+#endif // GRAPH_FROM_XML_PARSER_H_
diff --git a/alib2data/src/graph/GraphRepresentation.cpp b/alib2data/src/graph/GraphRepresentation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..180569fa4a51e641981d68c15f35ff987757d377
--- /dev/null
+++ b/alib2data/src/graph/GraphRepresentation.cpp
@@ -0,0 +1,31 @@
+#include "GraphRepresentation.h"
+
+namespace graph {
+
+std::string representationToString(REPRESENTATION r)
+{
+	switch (r) {
+	case REPRESENTATION::ADJACENCY_LIST:
+		return "AdjacencyList";
+	case REPRESENTATION::ADJACENCY_MATRIX:
+		return "AdjacencyMatrix";
+	case REPRESENTATION::INCIDENCE_MATRIX:
+		return "IncidenceMatrix";
+	default:
+		return "AdjacencyList";
+	}
+}
+
+REPRESENTATION representationFromString(const std::string &str)
+{
+	if (str == "AdjacencyList") {
+		return REPRESENTATION::ADJACENCY_LIST;
+	} else if (str == "AdjacencyMatrix") {
+		return REPRESENTATION::ADJACENCY_MATRIX;
+	} else if (str == "IncidenceMatrix") {
+		return REPRESENTATION::INCIDENCE_MATRIX;
+	}
+	return REPRESENTATION::ADJACENCY_LIST;
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/GraphRepresentation.h b/alib2data/src/graph/GraphRepresentation.h
new file mode 100644
index 0000000000000000000000000000000000000000..e93115b7ffbfee082510a1bdcbb3ce4bbb4b8e5a
--- /dev/null
+++ b/alib2data/src/graph/GraphRepresentation.h
@@ -0,0 +1,19 @@
+#ifndef GRAPH_REPRESENTATION_H_
+#define GRAPH_REPRESENTATION_H_
+
+#include <string>
+
+namespace graph {
+
+enum class REPRESENTATION {
+	ADJACENCY_LIST,
+	ADJACENCY_MATRIX,
+	INCIDENCE_MATRIX
+};
+
+std::string representationToString(REPRESENTATION r);
+REPRESENTATION representationFromString(const std::string &str);
+
+} // namespace graph
+
+#endif // GRAPH_REPRESENTATION_H_
diff --git a/alib2data/src/graph/GraphToStringComposer.cpp b/alib2data/src/graph/GraphToStringComposer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..83ffdb679ea0f759c01c8c9593f57034fc2d5cbe
--- /dev/null
+++ b/alib2data/src/graph/GraphToStringComposer.cpp
@@ -0,0 +1,151 @@
+#include "GraphToStringComposer.h"
+#include "../StringApi.hpp"
+
+#include <algorithm>
+
+namespace graph {
+
+void GraphToStringComposer::compose(std::ostream &out, const Graph &graph) const
+{
+	out << "(";
+	graph.getData().Accept(static_cast<void*>(&out), *this);
+	out << ")";
+}
+
+void GraphToStringComposer::Visit(void *data, const DirectedGraph &graph) const
+{
+	std::ostream &out = *static_cast<std::ostream*>(data);
+
+	out << "DirectedGraph:";
+	composeRepresentation(out, graph.getRepresentation());
+	out << ":";
+	composeNodes(out, graph.getNodes());
+	out << ":";
+	composeDirectedEdges(out, graph.getEdges());
+	out << ":";
+	composeNodeValues(out, graph);
+	out << ":";
+	composeEdgeValues(out, graph);
+}
+
+void GraphToStringComposer::Visit(void *data, const UndirectedGraph &graph) const
+{
+	std::ostream &out = *static_cast<std::ostream*>(data);
+
+	out << "UndirectedGraph:";
+	composeRepresentation(out, graph.getRepresentation());
+	out << ":";
+	composeNodes(out, graph.getNodes());
+	out << ":";
+	composeUndirectedEdges(out, graph.getEdges());
+	out << ":";
+	composeNodeValues(out, graph);
+	out << ":";
+	composeEdgeValues(out, graph);
+}
+
+void GraphToStringComposer::Visit(void *data, const Node &node) const
+{
+	std::ostream &out = *static_cast<std::ostream*>(data);
+
+	alib::stringApi<label::Label>::compose(out, node.getName());
+}
+
+void GraphToStringComposer::Visit(void *data, const DirectedEdge &edge) const
+{
+	std::ostream &out = *static_cast<std::ostream*>(data);
+
+	edge.getFromNode().Accept(static_cast<void*>(&out), *this);
+	alib::stringApi<label::Label>::compose(out, edge.getName());
+	edge.getToNode().Accept(static_cast<void*>(&out), *this);
+}
+
+void GraphToStringComposer::Visit(void *data, const UndirectedEdge &edge) const
+{
+	std::ostream &out = *static_cast<std::ostream*>(data);
+
+	edge.getFirstNode().Accept(static_cast<void*>(&out), *this);
+	alib::stringApi<label::Label>::compose(out, edge.getName());
+	edge.getSecondNode().Accept(static_cast<void*>(&out), *this);
+}
+
+void GraphToStringComposer::composeRepresentation(std::ostream &out, REPRESENTATION representation) const
+{
+	out << representationToString(representation);
+}
+
+void GraphToStringComposer::composeNodes(std::ostream &out, const std::set<Node> &nodes) const
+{
+	bool first = true;
+
+	for (const Node &node : nodes) {
+		if (first) {
+			first = false;
+		} else {
+			out << ",";
+		}
+		node.Accept(static_cast<void*>(&out), *this);
+	}
+}
+
+void GraphToStringComposer::composeDirectedEdges(std::ostream &out, const std::set<DirectedEdge> &edges) const
+{
+	bool first = true;
+
+	for (const DirectedEdge &edge : edges) {
+		if (first) {
+			first = false;
+		} else {
+			out << ",";
+		}
+		edge.Accept(static_cast<void*>(&out), *this);
+	}
+}
+
+void GraphToStringComposer::composeUndirectedEdges(std::ostream &out, const std::set<UndirectedEdge> &edges) const
+{
+	bool first = true;
+
+	for (const UndirectedEdge &edge : edges) {
+		if (first) {
+			first = false;
+		} else {
+			out << ",";
+		}
+		edge.Accept(static_cast<void*>(&out), *this);
+	}
+}
+
+template<typename T>
+void GraphToStringComposer::composeNodeValues(std::ostream &out, const T &graph) const
+{
+	bool first = true;
+
+	for (auto i : graph.nodeValues) {
+		if (first) {
+			first = false;
+		} else {
+			out << ",";
+		}
+		i.first.Accept(static_cast<void*>(&out), *this);
+		out << "=" << i.second;
+	}
+}
+
+template<typename T>
+void GraphToStringComposer::composeEdgeValues(std::ostream &out, const T &graph) const
+{
+	bool first = true;
+
+	for (auto i : graph.edgeValues) {
+		if (first) {
+			first = false;
+		} else {
+			out << ",";
+		}
+		i.first.Accept(static_cast<void*>(&out), *this);
+		out << "=" << i.second;
+	}
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/GraphToStringComposer.h b/alib2data/src/graph/GraphToStringComposer.h
new file mode 100644
index 0000000000000000000000000000000000000000..54ecda9ddcefb01f28f7079bb143b166ad2c904c
--- /dev/null
+++ b/alib2data/src/graph/GraphToStringComposer.h
@@ -0,0 +1,44 @@
+#ifndef GRAPH_TO_STRING_COMPOSER_H_
+#define GRAPH_TO_STRING_COMPOSER_H_
+
+#include <set>
+#include <string>
+
+#include "Graph.h"
+#include "GraphRepresentation.h"
+#include "common/GraphElement.h"
+#include "directed/DirectedGraph.h"
+#include "undirected/UndirectedGraph.h"
+#include "../std/visitor.hpp"
+#include "../sax/Token.h"
+
+namespace graph {
+
+class GraphToStringComposer : public VisitableGraphBase::const_visitor_type, GraphElement::const_visitor_type
+{
+public:
+	void compose(std::ostream &out, const Graph &graph) const;
+
+private:
+	void Visit(void *data, const DirectedGraph &graph) const;
+	void Visit(void *data, const UndirectedGraph &graph) const;
+
+	void Visit(void *data, const Node &node) const;
+	void Visit(void *data, const DirectedEdge &edge) const;
+	void Visit(void *data, const UndirectedEdge &edge) const;
+
+	void composeRepresentation(std::ostream &out, REPRESENTATION representation) const;
+	void composeNodes(std::ostream &out, const std::set<Node> &nodes) const;
+	void composeDirectedEdges(std::ostream &out, const std::set<DirectedEdge> &edges) const;
+	void composeUndirectedEdges(std::ostream &out, const std::set<UndirectedEdge> &edges) const;
+
+	template<typename T>
+	void composeNodeValues(std::ostream &out, const T &graph) const;
+	template<typename T>
+	void composeEdgeValues(std::ostream &out, const T &graph) const;
+
+};
+
+} // namespace graph
+
+#endif // GRAPH_TO_STRING_COMPOSER_H_
diff --git a/alib2data/src/graph/GraphToXMLComposer.cpp b/alib2data/src/graph/GraphToXMLComposer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..47b8bcde2dfd3c2ad302508e56aa7ef6d50411ed
--- /dev/null
+++ b/alib2data/src/graph/GraphToXMLComposer.cpp
@@ -0,0 +1,144 @@
+#include "GraphToXMLComposer.h"
+#include "GraphRepresentation.h"
+
+#include "../XmlApi.hpp"
+#include "../std/itos.h"
+
+namespace graph {
+
+void GraphToXMLComposer::Visit(void *data, const Node &node) const
+{
+	std::list<sax::Token> &out = *static_cast<std::list<sax::Token>*>(data);
+
+	out.push_back(sax::Token("node", sax::Token::TokenType::START_ELEMENT));
+	alib::xmlApi<label::Label>::compose(out, node.getName());
+	out.push_back(sax::Token("node", sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::Visit(void *data, const DirectedEdge &edge) const
+{
+	std::list<sax::Token> &out = *static_cast<std::list<sax::Token>*>(data);
+
+	out.push_back(sax::Token("edge", sax::Token::TokenType::START_ELEMENT));
+
+	out.push_back(sax::Token("from", sax::Token::TokenType::START_ELEMENT));
+	edge.getFromNode().Accept(static_cast<void*>(&out), *this);
+	out.push_back(sax::Token("from", sax::Token::TokenType::END_ELEMENT));
+
+	alib::xmlApi<label::Label>::compose(out, edge.getName());
+
+	out.push_back(sax::Token("to", sax::Token::TokenType::START_ELEMENT));
+	edge.getToNode().Accept(static_cast<void*>(&out), *this);
+	out.push_back(sax::Token("to", sax::Token::TokenType::END_ELEMENT));
+
+	out.push_back(sax::Token("edge", sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::Visit(void *data, const UndirectedEdge &edge) const
+{
+	std::list<sax::Token> &out = *static_cast<std::list<sax::Token>*>(data);
+
+	out.push_back(sax::Token("edge", sax::Token::TokenType::START_ELEMENT));
+
+	out.push_back(sax::Token("first", sax::Token::TokenType::START_ELEMENT));
+	edge.getFirstNode().Accept(static_cast<void*>(&out), *this);
+	out.push_back(sax::Token("first", sax::Token::TokenType::END_ELEMENT));
+
+	alib::xmlApi<label::Label>::compose(out, edge.getName());
+
+	out.push_back(sax::Token("second", sax::Token::TokenType::START_ELEMENT));
+	edge.getSecondNode().Accept(static_cast<void*>(&out), *this);
+	out.push_back(sax::Token("second", sax::Token::TokenType::END_ELEMENT));
+
+	out.push_back(sax::Token("edge", sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::compose(std::list<sax::Token> &out, const GraphBase &graph) const
+{
+	graph.Accept((void*) &out, alib::ToXMLComposers::toXMLComposers);
+}
+
+void GraphToXMLComposer::compose(std::list<sax::Token> &out, const Graph &graph) const
+{
+	graph.getData().Accept((void*) &out, alib::ToXMLComposers::toXMLComposers);
+}
+
+void GraphToXMLComposer::compose(std::list<sax::Token> &out, const DirectedGraph &graph) const
+{
+	out.push_back(sax::Token(alib::Names::GRAPH_DIRECTED_GRAPH, sax::Token::TokenType::START_ELEMENT));
+
+	composeRepresentation(out, representationToString(graph.getRepresentation()));
+	composeNodes(out, graph.getNodes());
+	composeEdges(out, graph.getEdges());
+	composeNodeValues(out, graph);
+	composeEdgeValues(out, graph);
+
+	out.push_back(sax::Token(alib::Names::GRAPH_DIRECTED_GRAPH, sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::compose(std::list<sax::Token> &out, const UndirectedGraph &graph) const
+{
+	out.push_back(sax::Token(alib::Names::GRAPH_UNDIRECTED_GRAPH, sax::Token::TokenType::START_ELEMENT));
+
+	composeRepresentation(out, representationToString(graph.getRepresentation()));
+	composeNodes(out, graph.getNodes());
+	composeEdges(out, graph.getEdges());
+	composeNodeValues(out, graph);
+	composeEdgeValues(out, graph);
+
+	out.push_back(sax::Token(alib::Names::GRAPH_UNDIRECTED_GRAPH, sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::composeRepresentation(std::list<sax::Token> &out, const std::string &representation) const
+{
+	out.push_back(sax::Token("representation", sax::Token::TokenType::START_ELEMENT));
+	out.push_back(sax::Token(representation, sax::Token::TokenType::CHARACTER));
+	out.push_back(sax::Token("representation", sax::Token::TokenType::END_ELEMENT));
+}
+
+void GraphToXMLComposer::composeNodes(std::list<sax::Token> &out, const std::set<Node> &nodes) const
+{
+	out.push_back(sax::Token("nodes", sax::Token::TokenType::START_ELEMENT));
+	for (const Node &node : nodes) {
+		node.Accept(static_cast<void*>(&out), *this);
+	}
+	out.push_back(sax::Token("nodes", sax::Token::TokenType::END_ELEMENT));
+}
+
+template<typename T>
+void GraphToXMLComposer::composeEdges(std::list<sax::Token> &out, const std::set<T> &edges) const
+{
+	out.push_back(sax::Token("edges", sax::Token::TokenType::START_ELEMENT));
+	for (const auto &edge : edges) {
+		edge.Accept(static_cast<void*>(&out), *this);
+	}
+	out.push_back(sax::Token("edges", sax::Token::TokenType::END_ELEMENT));
+}
+
+template<typename T>
+void GraphToXMLComposer::composeNodeValues(std::list<sax::Token> &out, const T &graph) const
+{
+	out.push_back(sax::Token("nodevalues", sax::Token::TokenType::START_ELEMENT));
+	for (auto i : graph.nodeValues) {
+		i.first.Accept(static_cast<void*>(&out), *this);
+		out.push_back(sax::Token("value", sax::Token::TokenType::START_ELEMENT));
+		out.push_back(sax::Token(std::itos(i.second), sax::Token::TokenType::CHARACTER));
+		out.push_back(sax::Token("value", sax::Token::TokenType::END_ELEMENT));
+	}
+	out.push_back(sax::Token("nodevalues", sax::Token::TokenType::END_ELEMENT));
+}
+
+template<typename T>
+void GraphToXMLComposer::composeEdgeValues(std::list<sax::Token> &out, const T &graph) const
+{
+	out.push_back(sax::Token("edgevalues", sax::Token::TokenType::START_ELEMENT));
+	for (auto &i : graph.edgeValues) {
+		i.first.Accept(static_cast<void*>(&out), *this);
+		out.push_back(sax::Token("value", sax::Token::TokenType::START_ELEMENT));
+		out.push_back(sax::Token(std::itos(i.second), sax::Token::TokenType::CHARACTER));
+		out.push_back(sax::Token("value", sax::Token::TokenType::END_ELEMENT));
+	}
+	out.push_back(sax::Token("edgevalues", sax::Token::TokenType::END_ELEMENT));
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/GraphToXMLComposer.h b/alib2data/src/graph/GraphToXMLComposer.h
new file mode 100644
index 0000000000000000000000000000000000000000..79bd1df88259d41893bb7bb211d87c9662de1742
--- /dev/null
+++ b/alib2data/src/graph/GraphToXMLComposer.h
@@ -0,0 +1,52 @@
+#ifndef GRAPH_TO_XML_COMPOSER_H_
+#define GRAPH_TO_XML_COMPOSER_H_
+
+#include <set>
+#include <list>
+
+#include "Graph.h"
+#include "common/GraphElement.h"
+#include "directed/DirectedGraph.h"
+#include "undirected/UndirectedGraph.h"
+#include "../std/visitor.hpp"
+#include "../sax/Token.h"
+
+namespace alib {
+
+template<typename T>
+struct xmlApi;
+
+} // namespace alib
+
+namespace graph {
+
+// This class contains methods to print XML representation of graph to the output stream.
+class GraphToXMLComposer : public GraphElement::const_visitor_type
+{
+private:
+	void Visit(void *data, const Node &node) const;
+	void Visit(void *data, const DirectedEdge &edge) const;
+	void Visit(void *data, const UndirectedEdge &edge) const;
+
+	void compose(std::list<sax::Token> &out, const GraphBase &graph) const;
+	void compose(std::list<sax::Token> &out, const Graph &graph) const;
+	void compose(std::list<sax::Token> &out, const DirectedGraph &graph) const;
+	void compose(std::list<sax::Token> &out, const UndirectedGraph &graph) const;
+
+	void composeRepresentation(std::list<sax::Token> &out, const std::string &representation) const;
+	void composeNodes(std::list<sax::Token> &out, const std::set<Node> &nodes) const;
+
+	template<typename T>
+	void composeEdges(std::list<sax::Token> &out, const std::set<T> &edges) const;
+
+	template<typename T>
+	void composeNodeValues(std::list<sax::Token> &out, const T &graph) const;
+	template<typename T>
+	void composeEdgeValues(std::list<sax::Token> &out, const T &graph) const;
+
+	template<typename T> friend class alib::xmlApi;
+};
+
+} // namespace graph
+
+#endif // GRAPH_TO_XML_COMPOSER_H_
diff --git a/alib2data/src/graph/common/GraphElement.cpp b/alib2data/src/graph/common/GraphElement.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0687b8aab79371a9f1888fd11010f54e7f2ea854
--- /dev/null
+++ b/alib2data/src/graph/common/GraphElement.cpp
@@ -0,0 +1,9 @@
+#include "GraphElement.h"
+
+namespace graph {
+
+GraphElement::GraphElement()
+{
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/common/GraphElement.h b/alib2data/src/graph/common/GraphElement.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac6a1d8ff0b02179e967acbaa0631d38d9a3585f
--- /dev/null
+++ b/alib2data/src/graph/common/GraphElement.h
@@ -0,0 +1,30 @@
+#ifndef GRAPH_ELEMENT_H_
+#define GRAPH_ELEMENT_H_
+
+// for std::hash
+#include <string>
+#include <functional>
+
+#include "../../std/visitor.hpp"
+#include "../../object/Object.h"
+
+namespace graph {
+
+class GraphElement;
+
+class Node;
+class DirectedEdge;
+class UndirectedEdge;
+
+// Abstract class representing element in the graph. Can be node or edge.
+typedef std::acceptor_base<GraphElement, Node, DirectedEdge, UndirectedEdge> VisitableGraphElement;
+
+class GraphElement : public alib::base<GraphElement, Node, DirectedEdge, UndirectedEdge>, public VisitableGraphElement
+{
+public:
+	explicit GraphElement();
+};
+
+} // namespace graph
+
+#endif // GRAPH_ELEMENT_H_
diff --git a/alib2data/src/graph/common/Macros.h b/alib2data/src/graph/common/Macros.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e172599860302c038511309d38de24d3848ae61
--- /dev/null
+++ b/alib2data/src/graph/common/Macros.h
@@ -0,0 +1,28 @@
+#ifndef GRAPH_MACROS_H_
+#define GRAPH_MACROS_H_
+
+#define GRAPH_DEFINE_STD_HASH(T) \
+	namespace std { \
+	template <> \
+	struct hash<graph::T> \
+	{ \
+		std::size_t operator()(const graph::T &t) const \
+		{ \
+			return std::hash<std::string>()(static_cast<std::string>(t)); \
+		} \
+	}; \
+	} \
+
+#define GRAPH_DEFINE_STD_COMPARE(T) \
+	namespace std { \
+	template<> \
+	struct compare<graph::T> \
+	{ \
+		int operator()(const graph::T &first, const graph::T &second) const \
+		{ \
+			return first.compare(second); \
+		} \
+	}; \
+	} \
+
+#endif // GRAPH_MACROS_H_
diff --git a/alib2data/src/graph/common/Node.cpp b/alib2data/src/graph/common/Node.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c95656d79ec8f044de3725ce2710e4599e9d374b
--- /dev/null
+++ b/alib2data/src/graph/common/Node.cpp
@@ -0,0 +1,108 @@
+#include "Node.h"
+#include "../../label/Label.h"
+
+#include <sstream>
+
+namespace graph {
+
+Node::Node()
+	: name(label::labelFrom(std::string()))
+{
+}
+
+Node::Node(const label::Label &name)
+	: name(name)
+{
+}
+
+Node::Node(label::Label &&name)
+	: name(std::move(name))
+{
+}
+
+Node::Node(int number)
+	: name(label::labelFrom(number))
+{
+}
+
+Node::Node(char character)
+	: name(label::labelFrom(character))
+{
+}
+
+Node::Node(const std::string &name)
+	: name(label::labelFrom(name))
+{
+}
+
+Node::Node(const Node &other)
+	: name(other.name)
+{
+}
+
+Node::Node(Node &&other) noexcept
+	: name(std::move(other.name))
+{
+}
+
+Node &Node::operator=(const Node &other)
+{
+	if (this == &other) {
+		return *this;
+	}
+
+	*this = Node(other);
+	return *this;
+}
+
+Node &Node::operator=(Node &&other)
+{
+	std::swap(name, other.name);
+	return *this;
+}
+
+GraphElement *Node::clone() const
+{
+	return new Node(*this);
+}
+
+GraphElement *Node::plunder() &&
+{
+	return new Node(std::move(*this));
+}
+
+const label::Label &Node::getName() const
+{
+	return name;
+}
+
+int Node::compare(const Node &other) const
+{
+	return name.getData().compare(other.name.getData());
+}
+
+void Node::operator>>(std::ostream &out) const
+{
+	dump(out);
+}
+
+Node::operator std::string() const
+{
+	std::stringstream ss;
+	ss << *this;
+	return ss.str();
+}
+
+std::ostream &operator<<(std::ostream &out, const Node &node)
+{
+	node.dump(out);
+	return out;
+}
+
+void Node::dump(std::ostream &os) const
+{
+	os << "(Node " << name << ")";
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/common/Node.h b/alib2data/src/graph/common/Node.h
new file mode 100644
index 0000000000000000000000000000000000000000..93fe69b1d8c089c5159c05cf4445e681f053fc5d
--- /dev/null
+++ b/alib2data/src/graph/common/Node.h
@@ -0,0 +1,59 @@
+#ifndef NODE_H_
+#define NODE_H_
+
+#include "Macros.h"
+#include "GraphElement.h"
+#include "../../label/Label.h"
+
+namespace graph {
+
+class Node : public std::acceptor<Node, GraphElement, GraphElement>
+{
+public:
+	explicit Node();
+	explicit Node(const label::Label &name);
+	explicit Node(label::Label &&name);
+	explicit Node(int number);
+	explicit Node(char character);
+	explicit Node(const std::string &name);
+
+	Node(const Node &other);
+	Node(Node &&other) noexcept;
+	Node &operator=(const Node &other);
+	Node &operator=(Node &&other);
+
+	GraphElement *clone() const;
+	GraphElement *plunder() &&;
+
+	const label::Label &getName() const;
+
+	int compare(const GraphElement &other) const override
+	{
+		return -other.compare(*this);
+	}
+
+	int compare(const Node &other) const;
+
+	void operator>>(std::ostream &out) const override;
+
+	operator std::string() const override;
+
+	int selfTypeId() const override
+	{
+		return typeId<Node>();
+	}
+
+	friend std::ostream &operator<<(std::ostream &out, const Node &node);
+
+private:
+	void dump(std::ostream &os) const;
+
+	label::Label name;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_HASH(Node)
+GRAPH_DEFINE_STD_COMPARE(Node)
+
+#endif // NODE_H_
diff --git a/alib2data/src/graph/directed/AdjacencyListDirectedGraph.cpp b/alib2data/src/graph/directed/AdjacencyListDirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..183b78b7d9581d7c8ff1d41d3f93bbf952839d50
--- /dev/null
+++ b/alib2data/src/graph/directed/AdjacencyListDirectedGraph.cpp
@@ -0,0 +1,200 @@
+#include "AdjacencyListDirectedGraph.h"
+#include "../../std/compare.hpp"
+
+#include <algorithm>
+
+namespace graph {
+
+template<typename T>
+inline void listRemoveOne(std::list<T> &lst, const T &item)
+{
+	lst.erase(std::find(lst.begin(), lst.end(), item));
+}
+
+std::set<Node> AdjacencyListDirectedGraph::allNodes() const
+{
+	std::set<Node> out;
+
+	for (auto i : adj) {
+		out.insert(i.first);
+	}
+
+	return out;
+}
+
+std::set<DirectedEdge> AdjacencyListDirectedGraph::allEdges() const
+{
+	return edges;
+}
+
+bool AdjacencyListDirectedGraph::hasNode(const Node &node) const
+{
+	return adj.find(node) != adj.end();
+}
+
+bool AdjacencyListDirectedGraph::hasEdge(const DirectedEdge &edge) const
+{
+	return edges.find(edge) != edges.end();
+}
+
+bool AdjacencyListDirectedGraph::hasEdge(const Node &from, const Node &to) const
+{
+	auto search = adj.find(from);
+	if (search == adj.end()) {
+		return false;
+	}
+	return std::find(search->second.begin(), search->second.end(), to) != search->second.end();
+}
+
+std::set<DirectedEdge> AdjacencyListDirectedGraph::findEdges(const Node &from, const Node &to) const
+{
+	std::set<DirectedEdge> out;
+
+	for (const DirectedEdge &e : edges) {
+		if (e.getFromNode() == from && e.getToNode() == to) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+std::set<Node> AdjacencyListDirectedGraph::neighbors(const Node &node) const
+{
+	std::set<Node> out;
+
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return out;
+	}
+
+	std::copy(search->second.begin(), search->second.end(), std::inserter(out, out.begin()));
+	return out;
+}
+
+std::set<DirectedEdge> AdjacencyListDirectedGraph::neighborEdges(const Node &node) const
+{
+	std::set<DirectedEdge> out;
+	const std::set<Node> &nghbrs = neighbors(node);
+
+	for (const DirectedEdge &e : edges) {
+		if (e.getFromNode() == node && nghbrs.find(e.getToNode()) != nghbrs.end()) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+bool AdjacencyListDirectedGraph::addNode(const Node &node)
+{
+	if (adj.find(node) != adj.end()) {
+		return false;
+	}
+
+	adj[node]; // insert key
+	return true;
+}
+
+bool AdjacencyListDirectedGraph::removeNode(const Node &node)
+{
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return false;
+	}
+
+	adj.erase(search);
+
+	auto i = edges.begin();
+	while (i != edges.end()) {
+		if (i->getFromNode() == node) {
+			edges.erase(i++);
+		} else if (i->getToNode() == node) {
+			listRemoveOne(adj[i->getFromNode()], node);
+			edges.erase(i++);
+		} else {
+			i++;
+		}
+	}
+
+	return true;
+}
+
+bool AdjacencyListDirectedGraph::addEdge(const DirectedEdge &edge)
+{
+	if (edges.find(edge) != edges.end()) {
+		return false;
+	}
+
+	edges.insert(edge);
+
+	adj[edge.getFromNode()].push_back(edge.getToNode());
+	adj[edge.getToNode()]; // insert key
+
+	return true;
+}
+
+bool AdjacencyListDirectedGraph::removeEdge(const DirectedEdge &edge)
+{
+	auto search = edges.find(edge);
+	if (search == edges.end()) {
+		return false;
+	}
+
+	edges.erase(search);
+
+	auto &lst = adj[edge.getFromNode()];
+	listRemoveOne(lst, edge.getToNode());
+	if (lst.empty()) {
+		adj.erase(edge.getFromNode());
+	}
+
+	return true;
+}
+
+void AdjacencyListDirectedGraph::copy(IDirectedGraph *other)
+{
+	AdjacencyListDirectedGraph *o = static_cast<AdjacencyListDirectedGraph*>(other);
+
+	edges = o->edges;
+	adj = o->adj;
+}
+
+int AdjacencyListDirectedGraph::compare(IDirectedGraph *other) const
+{
+	AdjacencyListDirectedGraph *o = static_cast<AdjacencyListDirectedGraph*>(other);
+
+	std::compare<decltype(edges)> comp;
+	int res = comp(edges, o->edges);
+	if (res != 0) {
+		return res;
+	}
+
+	if (adj.size() < o->adj.size()) {
+		return -1;
+	}
+
+	if (adj.size() > o->adj.size()) {
+		return 1;
+	}
+
+	for (auto i : adj) {
+		auto lst1 = i.second;
+		auto lst2 = o->adj.at(i.first);
+
+		std::set<Node> adj1;
+		std::set<Node> adj2;
+		std::copy(lst1.begin(), lst1.end(), std::inserter(adj1, adj1.begin()));
+		std::copy(lst2.begin(), lst2.end(), std::inserter(adj2, adj2.begin()));
+
+		std::compare<std::set<Node>> comp;
+		int res = comp(adj1, adj2);
+		if (res != 0) {
+			return res;
+		}
+	}
+
+	return 0;
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/directed/AdjacencyListDirectedGraph.h b/alib2data/src/graph/directed/AdjacencyListDirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ccfe90762351152eb61b60149c8b66a6461dcc4
--- /dev/null
+++ b/alib2data/src/graph/directed/AdjacencyListDirectedGraph.h
@@ -0,0 +1,57 @@
+#ifndef ADJACENCY_LIST_DIRECTED_GRAPH_H_
+#define ADJACENCY_LIST_DIRECTED_GRAPH_H_
+
+#include <list>
+#include <unordered_map>
+
+#include "IDirectedGraph.h"
+
+namespace graph {
+
+/*
+ *  AdjacencyList representation
+ *
+ *  unordered_map:
+ *    [ n1 ] -> list [ n2, n3, n4 ]
+ *    [ n2 ] -> list [ n1, n1 ]      < multi-edge
+ *    [ n3 ] -> list [ n4 ]
+ *    [ n4 ] -> list [ n1, n2 ]
+ *
+ *  edges:
+ *    n1 -> n2, n1 -> n3, n1 -> n4
+ *    n2 -> n1, n2 -> n1
+ *    ...
+ */
+
+class AdjacencyListDirectedGraph : public IDirectedGraph
+{
+public:
+	std::set<Node> allNodes() const override;
+	std::set<DirectedEdge> allEdges() const override;
+
+	bool hasNode(const Node &node) const override;
+	bool hasEdge(const DirectedEdge &edge) const override;
+
+	bool hasEdge(const Node &from, const Node &to) const override;
+	std::set<DirectedEdge> findEdges(const Node &from, const Node &to) const override;
+
+	std::set<Node> neighbors(const Node &node) const override;
+	std::set<DirectedEdge> neighborEdges(const Node &node) const override;
+
+	bool addNode(const Node &node) override;
+	bool removeNode(const Node &node) override;
+
+	bool addEdge(const DirectedEdge &edge) override;
+	bool removeEdge(const DirectedEdge &edge) override;
+
+	void copy(IDirectedGraph *other) override;
+	int compare(IDirectedGraph *other) const override;
+
+private:
+	std::set<DirectedEdge> edges;
+	std::unordered_map<Node, std::list<Node>> adj;
+};
+
+} // namespace graph
+
+#endif // ADJACENCY_LIST_DIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.cpp b/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f1598b82adf2e8341a80b0dc46eed311bf1cb72
--- /dev/null
+++ b/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.cpp
@@ -0,0 +1,174 @@
+#include "AdjacencyMatrixDirectedGraph.h"
+#include "../../std/compare.hpp"
+
+namespace graph {
+
+std::set<Node> AdjacencyMatrixDirectedGraph::allNodes() const
+{
+	std::set<Node> out;
+
+	for (auto i : adj) {
+		out.insert(i.first);
+	}
+
+	return out;
+}
+
+std::set<DirectedEdge> AdjacencyMatrixDirectedGraph::allEdges() const
+{
+	return edges;
+}
+
+bool AdjacencyMatrixDirectedGraph::hasNode(const Node &node) const
+{
+	return adj.find(node) != adj.end();
+}
+
+bool AdjacencyMatrixDirectedGraph::hasEdge(const DirectedEdge &edge) const
+{
+	return edges.find(edge) != edges.end();
+}
+
+bool AdjacencyMatrixDirectedGraph::hasEdge(const Node &from, const Node &to) const
+{
+	auto search = adj.find(from);
+	if (search == adj.end()) {
+		return false;
+	}
+	return search->second.find(to) != search->second.end();
+}
+
+std::set<DirectedEdge> AdjacencyMatrixDirectedGraph::findEdges(const Node &from, const Node &to) const
+{
+	std::set<DirectedEdge> out;
+
+	for (const DirectedEdge &e : edges) {
+		if (e.getFromNode() == from && e.getToNode() == to) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+std::set<Node> AdjacencyMatrixDirectedGraph::neighbors(const Node &node) const
+{
+	std::set<Node> out;
+
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return out;
+	}
+
+	for (auto i : search->second) {
+		if (i.second > 0) {
+			out.insert(i.first);
+		}
+	}
+
+	return out;
+}
+
+std::set<DirectedEdge> AdjacencyMatrixDirectedGraph::neighborEdges(const Node &node) const
+{
+	std::set<DirectedEdge> out;
+	const std::set<Node> &nghbrs = neighbors(node);
+
+	for (const DirectedEdge &e : edges) {
+		if (e.getFromNode() == node && nghbrs.find(e.getToNode()) != nghbrs.end()) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+bool AdjacencyMatrixDirectedGraph::addNode(const Node &node)
+{
+	if (adj.find(node) != adj.end()) {
+		return false;
+	}
+
+	adj[node]; // insert key
+	return true;
+}
+
+bool AdjacencyMatrixDirectedGraph::removeNode(const Node &node)
+{
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return false;
+	}
+
+	adj.erase(search);
+
+	auto i = edges.begin();
+	while (i != edges.end()) {
+		if (i->getFromNode() == node) {
+			edges.erase(i++);
+		} else if (i->getToNode() == node) {
+			adj[i->getFromNode()].erase(node);
+			edges.erase(i++);
+		} else {
+			i++;
+		}
+	}
+
+	return true;
+}
+
+bool AdjacencyMatrixDirectedGraph::addEdge(const DirectedEdge &edge)
+{
+	if (edges.find(edge) != edges.end()) {
+		return false;
+	}
+
+	edges.insert(edge);
+
+	adj[edge.getFromNode()][edge.getToNode()]++;
+	adj[edge.getToNode()]; // insert key
+
+	return true;
+}
+
+bool AdjacencyMatrixDirectedGraph::removeEdge(const DirectedEdge &edge)
+{
+	auto search = edges.find(edge);
+	if (search == edges.end()) {
+		return false;
+	}
+
+	edges.erase(search);
+
+	auto &map = adj[edge.getFromNode()];
+	int &val = map[edge.getToNode()];
+	if (--val <= 0) {
+		map.erase(edge.getToNode());
+		if (map.empty()) {
+			adj.erase(edge.getFromNode());
+		}
+	}
+
+	return true;
+}
+
+void AdjacencyMatrixDirectedGraph::copy(IDirectedGraph *other)
+{
+	AdjacencyMatrixDirectedGraph *o = static_cast<AdjacencyMatrixDirectedGraph*>(other);
+
+	edges = o->edges;
+	adj = o->adj;
+}
+
+int AdjacencyMatrixDirectedGraph::compare(IDirectedGraph *other) const
+{
+	AdjacencyMatrixDirectedGraph *o = static_cast<AdjacencyMatrixDirectedGraph*>(other);
+
+	auto first = std::tie(edges, adj);
+	auto second = std::tie(o->edges, o->adj);
+
+	std::compare<decltype(first)> comp;
+	return comp(first, second);
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.h b/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..277147dbee584beb60bbc2426483361cd4ab8db4
--- /dev/null
+++ b/alib2data/src/graph/directed/AdjacencyMatrixDirectedGraph.h
@@ -0,0 +1,61 @@
+#ifndef ADJACENCY_MATRIX_DIRECTED_GRAPH_H_
+#define ADJACENCY_MATRIX_DIRECTED_GRAPH_H_
+
+#include <set>
+#include <vector>
+#include <unordered_map>
+
+#include "IDirectedGraph.h"
+
+namespace graph {
+
+/*
+ *  AdjacencyMatrix representation
+ *
+ *  unordered_map:
+ *    [ -- ][ n1 ][ n2 ][ n3 ][ n4 ]
+ *    [ n1 ]  0     2     0     1
+ *    [ n2 ]  1     0     0     1
+ *    [ n3 ]  0     0     0     1
+ *    [ n4 ]  1     0     1     0
+ *
+ *  edges:
+ *    n1 -> n2, n1 -> n2, n1 -> n4
+ *    n2 -> n1, n2 -> n4
+ *    n3 -> n4
+ *    n4 -> n1, n4 -> n3
+ *
+ */
+
+class AdjacencyMatrixDirectedGraph : public IDirectedGraph
+{
+public:
+	std::set<Node> allNodes() const override;
+	std::set<DirectedEdge> allEdges() const override;
+
+	bool hasNode(const Node &node) const override;
+	bool hasEdge(const DirectedEdge &edge) const override;
+
+	bool hasEdge(const Node &from, const Node &to) const override;
+	std::set<DirectedEdge> findEdges(const Node &from, const Node &to) const override;
+
+	std::set<Node> neighbors(const Node &node) const override;
+	std::set<DirectedEdge> neighborEdges(const Node &node) const override;
+
+	bool addNode(const Node &node) override;
+	bool removeNode(const Node &node) override;
+
+	bool addEdge(const DirectedEdge &edge) override;
+	bool removeEdge(const DirectedEdge &edge) override;
+
+	void copy(IDirectedGraph *other) override;
+	int compare(IDirectedGraph *other) const override;
+
+private:
+	std::set<DirectedEdge> edges;
+	std::unordered_map<Node, std::unordered_map<Node, int>> adj;
+};
+
+} // namespace graph
+
+#endif // ADJACENCY_MATRIX_DIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/directed/DirectedEdge.cpp b/alib2data/src/graph/directed/DirectedEdge.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a05cbce7b739a837637dacfc3ced9509e370c252
--- /dev/null
+++ b/alib2data/src/graph/directed/DirectedEdge.cpp
@@ -0,0 +1,175 @@
+#include "DirectedEdge.h"
+
+#include <sstream>
+
+namespace graph {
+
+DirectedEdge::DirectedEdge()
+	: name(label::labelFrom('d'))
+{
+}
+
+DirectedEdge::DirectedEdge(const Node &from, const Node &to)
+	: from(from)
+	, to(to)
+	, name(label::labelFrom('d'))
+{
+}
+
+DirectedEdge::DirectedEdge(Node &&from, Node &&to)
+	: from(std::move(from))
+	, to(std::move(to))
+	, name(label::labelFrom('d'))
+{
+}
+
+DirectedEdge::DirectedEdge(const Node &from, const Node &to, const label::Label &name)
+	: from(from)
+	, to(to)
+	, name(name)
+{
+}
+
+DirectedEdge::DirectedEdge(Node &&from, Node &&to, label::Label &&name)
+	: from(std::move(from))
+	, to(std::move(to))
+	, name(std::move(name))
+{
+}
+
+DirectedEdge::DirectedEdge(const Node &from, const Node &to, int number)
+	: from(from)
+	, to(to)
+	, name(label::labelFrom(number))
+{
+}
+
+DirectedEdge::DirectedEdge(Node &&from, Node &&to, int number)
+	: from(std::move(from))
+	, to(std::move(to))
+	, name(label::labelFrom(number))
+{
+}
+
+DirectedEdge::DirectedEdge(const Node &from, const Node &to, char character)
+	: from(from)
+	, to(to)
+	, name(label::labelFrom(character))
+{
+}
+
+DirectedEdge::DirectedEdge(Node &&from, Node &&to, char character)
+	: from(std::move(from))
+	, to(std::move(to))
+	, name(label::labelFrom(character))
+{
+}
+
+DirectedEdge::DirectedEdge(const Node &from, const Node &to, const std::string &name)
+	: from(from)
+	, to(to)
+	, name(label::labelFrom(name))
+{
+}
+
+DirectedEdge::DirectedEdge(Node &&from, Node &&to, const std::string &name)
+	: from(std::move(from))
+	, to(std::move(to))
+	, name(label::labelFrom(name))
+{
+}
+
+DirectedEdge::DirectedEdge(const DirectedEdge &other)
+	: from(other.from)
+	, to(other.to)
+	, name(other.name)
+{
+}
+
+DirectedEdge::DirectedEdge(DirectedEdge &&other) noexcept
+	: from(std::move(other.from))
+	, to(std::move(other.to))
+	, name(std::move(other.name))
+{
+}
+
+DirectedEdge &DirectedEdge::operator=(const DirectedEdge &other)
+{
+	if (this == &other) {
+		return *this;
+	}
+
+	*this = DirectedEdge(other);
+	return *this;
+}
+
+DirectedEdge &DirectedEdge::operator=(DirectedEdge &&other)
+{
+	std::swap(from, other.from);
+	std::swap(to, other.to);
+	std::swap(name, other.name);
+	return *this;
+}
+
+GraphElement *DirectedEdge::clone() const
+{
+	return new DirectedEdge(*this);
+}
+
+GraphElement *DirectedEdge::plunder() &&
+{
+	return new DirectedEdge(std::move(*this));
+}
+
+const Node &DirectedEdge::getFromNode() const
+{
+	return from;
+}
+
+const Node &DirectedEdge::getToNode() const
+{
+	return to;
+}
+
+const label::Label &DirectedEdge::getName() const
+{
+	return name;
+}
+
+int DirectedEdge::compare(const DirectedEdge &other) const
+{
+	int res = name.getData().compare(other.name.getData());
+	if (res == 0) {
+		res = from.compare(other.from);
+		if (res == 0) {
+			res = to.compare(other.to);
+		}
+	}
+	return res;
+}
+
+void DirectedEdge::operator>>(std::ostream &out) const
+{
+	dump(out);
+}
+
+DirectedEdge::operator std::string() const
+{
+	std::stringstream ss;
+	ss << *this;
+	return ss.str();
+}
+
+std::ostream &operator<<(std::ostream &out, const DirectedEdge &node)
+{
+	node.dump(out);
+	return out;
+}
+
+void DirectedEdge::dump(std::ostream &os) const
+{
+	os << "(DirectedEdge " << from << " -> " << to << ")";
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/directed/DirectedEdge.h b/alib2data/src/graph/directed/DirectedEdge.h
new file mode 100644
index 0000000000000000000000000000000000000000..65d773d72b539a40225e35838b327178c6f03032
--- /dev/null
+++ b/alib2data/src/graph/directed/DirectedEdge.h
@@ -0,0 +1,68 @@
+#ifndef DIRECTED_EDGE_H_
+#define DIRECTED_EDGE_H_
+
+#include "../common/GraphElement.h"
+#include "../common/Macros.h"
+#include "../common/Node.h"
+
+namespace graph {
+
+class DirectedEdge : public std::acceptor<DirectedEdge, GraphElement, GraphElement>
+{
+public:
+	explicit DirectedEdge();
+	explicit DirectedEdge(const Node &from, const Node &to);
+	explicit DirectedEdge(Node &&from, Node &&to);
+	explicit DirectedEdge(const Node &from, const Node &to, const label::Label &name);
+	explicit DirectedEdge(Node &&from, Node &&to, label::Label &&name);
+	explicit DirectedEdge(const Node &from, const Node &to, int number);
+	explicit DirectedEdge(Node &&from, Node &&to, int number);
+	explicit DirectedEdge(const Node &from, const Node &to, char character);
+	explicit DirectedEdge(Node &&from, Node &&to, char character);
+	explicit DirectedEdge(const Node &from, const Node &to, const std::string &name);
+	explicit DirectedEdge(Node &&from, Node &&to, const std::string &name);
+
+	DirectedEdge(const DirectedEdge &other);
+	DirectedEdge(DirectedEdge &&other) noexcept;
+	DirectedEdge &operator=(const DirectedEdge &other);
+	DirectedEdge &operator=(DirectedEdge &&other);
+
+	GraphElement *clone() const;
+	GraphElement *plunder() &&;
+
+	const Node &getFromNode() const;
+	const Node &getToNode() const;
+	const label::Label &getName() const;
+
+	int compare(const GraphElement &other) const override
+	{
+		return -other.compare(*this);
+	}
+
+	int compare(const DirectedEdge &other) const;
+
+	void operator>>(std::ostream &out) const override;
+
+	operator std::string() const override;
+
+	int selfTypeId() const override
+	{
+		return typeId<DirectedEdge>();
+	}
+
+	friend std::ostream &operator<<(std::ostream &out, const DirectedEdge &node);
+
+private:
+	void dump(std::ostream &os) const;
+
+	Node from;
+	Node to;
+	label::Label name;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_HASH(DirectedEdge)
+GRAPH_DEFINE_STD_COMPARE(DirectedEdge)
+
+#endif // DIRECTED_EDGE_H_
diff --git a/alib2data/src/graph/directed/DirectedGraph.cpp b/alib2data/src/graph/directed/DirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7b2349c384ab2a5463068e2a338c6224de40ba2
--- /dev/null
+++ b/alib2data/src/graph/directed/DirectedGraph.cpp
@@ -0,0 +1,231 @@
+#include "DirectedGraph.h"
+#include "IDirectedGraph.h"
+#include "AdjacencyListDirectedGraph.h"
+#include "AdjacencyMatrixDirectedGraph.h"
+#include "../../std/set.hpp"
+
+#include <iostream>
+#include <algorithm>
+#include <sstream>
+
+namespace graph {
+
+DirectedGraph::DirectedGraph(REPRESENTATION representation)
+	: representation(representation)
+{
+	init();
+}
+
+DirectedGraph::~DirectedGraph() noexcept
+{
+	delete impl;
+}
+
+DirectedGraph::DirectedGraph(const DirectedGraph &other)
+	: representation(other.representation)
+	, nodeValues(other.nodeValues)
+	, edgeValues(other.edgeValues)
+{
+	init();
+	impl->copy(other.impl);
+}
+
+DirectedGraph::DirectedGraph(DirectedGraph &&other) noexcept
+	: representation(other.representation)
+	, impl(other.impl)
+	, nodeValues(std::move(other.nodeValues))
+	, edgeValues(std::move(other.edgeValues))
+{
+	other.impl = 0;
+}
+
+DirectedGraph &DirectedGraph::operator=(const DirectedGraph &other)
+{
+	if (this == &other) {
+		return *this;
+	}
+
+	*this = DirectedGraph(other);
+	return *this;
+}
+
+DirectedGraph &DirectedGraph::operator=(DirectedGraph &&other) noexcept
+{
+	this->representation = other.representation;
+	std::swap(this->impl, other.impl);
+	std::swap(this->nodeValues, other.nodeValues);
+	std::swap(this->edgeValues, other.edgeValues);
+	return *this;
+}
+
+GraphBase *DirectedGraph::clone() const
+{
+	return new DirectedGraph(*this);
+}
+
+GraphBase *DirectedGraph::plunder() &&
+{
+	return new DirectedGraph(std::move(*this));
+}
+
+REPRESENTATION DirectedGraph::getRepresentation() const
+{
+	return representation;
+}
+
+std::set<Node> DirectedGraph::getNodes() const
+{
+	return impl->allNodes();
+}
+
+bool DirectedGraph::hasNode(const Node &node) const
+{
+	return impl->hasNode(node);
+}
+
+bool DirectedGraph::hasEdge(const DirectedEdge &edge) const
+{
+	return impl->hasEdge(edge);
+}
+
+std::set<DirectedEdge> DirectedGraph::getEdges() const
+{
+	return impl->allEdges();
+}
+
+bool DirectedGraph::hasEdge(const Node &from, const Node &to) const
+{
+	return impl->hasEdge(from, to);
+}
+
+std::set<DirectedEdge> DirectedGraph::findEdges(const Node &from, const Node &to) const
+{
+	return impl->findEdges(from, to);
+}
+
+std::set<Node> DirectedGraph::neighbors(const Node &node) const
+{
+	return impl->neighbors(node);
+}
+
+std::set<DirectedEdge> DirectedGraph::neighborEdges(const Node &node) const
+{
+	return impl->neighborEdges(node);
+}
+
+bool DirectedGraph::addNode(const Node &node)
+{
+	return impl->addNode(node);
+}
+
+bool DirectedGraph::addNode(const Node &node, int value)
+{
+	bool success = addNode(node);
+	if (success) {
+		setNodeValue(node, value);
+	}
+	return success;
+}
+
+bool DirectedGraph::removeNode(const Node &node)
+{
+	bool success = impl->removeNode(node);
+	if (success) {
+		nodeValues.erase(node);
+	}
+	return success;
+}
+
+bool DirectedGraph::addEdge(const DirectedEdge &edge)
+{
+	return impl->addEdge(edge);
+}
+
+bool DirectedGraph::addEdge(const DirectedEdge &edge, int value)
+{
+	bool success = addEdge(edge);
+	if (success) {
+		setEdgeValue(edge, value);
+	}
+	return success;
+}
+
+bool DirectedGraph::removeEdge(const DirectedEdge &edge)
+{
+	bool success = impl->removeEdge(edge);
+	if (success) {
+		edgeValues.erase(edge);
+	}
+	return success;
+}
+
+int DirectedGraph::getNodeValue(const Node &node) const
+{
+	return nodeValues.at(node);
+}
+
+void DirectedGraph::setNodeValue(const Node &node, int value)
+{
+	nodeValues.insert({node, value});
+}
+
+int DirectedGraph::getEdgeValue(const DirectedEdge &edge) const
+{
+	return edgeValues.at(edge);
+}
+
+void DirectedGraph::setEdgeValue(const DirectedEdge &edge, int value)
+{
+	edgeValues.insert({edge, value});
+}
+
+void DirectedGraph::operator>>(std::ostream &out) const
+{
+	out << "(DirectedGraph " << static_cast<std::string>(*impl) << ")";
+}
+
+int DirectedGraph::compare(const DirectedGraph &other) const
+{
+	if (representation != other.representation) {
+		return static_cast<int>(representation) - static_cast<int>(other.representation);
+	}
+
+	auto first = std::tie(nodeValues, edgeValues);
+	auto second = std::tie(other.nodeValues, other.edgeValues);
+
+	std::compare<decltype(first)> comp;
+	int res = comp(first, second);
+
+	if (res != 0) {
+		return res;
+	}
+
+	return impl->compare(other.impl);
+}
+
+DirectedGraph::operator std::string() const
+{
+	std::stringstream ss;
+	ss << *this;
+	return ss.str();
+}
+
+void DirectedGraph::init()
+{
+	switch (representation) {
+	case REPRESENTATION::ADJACENCY_LIST:
+		impl = new AdjacencyListDirectedGraph();
+		break;
+
+	case REPRESENTATION::ADJACENCY_MATRIX:
+		impl = new AdjacencyMatrixDirectedGraph();
+		break;
+
+	case REPRESENTATION::INCIDENCE_MATRIX:
+
+	default:
+		impl = 0;
+	}
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/directed/DirectedGraph.h b/alib2data/src/graph/directed/DirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..5aa24d57037e10b397dc4f0f42b427785a8eb883
--- /dev/null
+++ b/alib2data/src/graph/directed/DirectedGraph.h
@@ -0,0 +1,91 @@
+#ifndef DIRECTED_GRAPH_H_
+#define DIRECTED_GRAPH_H_
+
+#include <unordered_map>
+
+#include "../GraphBase.h"
+#include "../GraphRepresentation.h"
+#include "../common/Macros.h"
+#include "../common/Node.h"
+#include "DirectedEdge.h"
+
+namespace graph {
+
+class IDirectedGraph;
+
+class DirectedGraph : public std::acceptor<DirectedGraph, VisitableGraphBase, std::acceptor<DirectedGraph, alib::VisitableObjectBase, GraphBase>>
+{
+public:
+	explicit DirectedGraph(REPRESENTATION representation = REPRESENTATION::ADJACENCY_LIST);
+	~DirectedGraph() noexcept;
+
+	DirectedGraph(const DirectedGraph &other);
+	DirectedGraph(DirectedGraph &&other) noexcept;
+	DirectedGraph &operator=(const DirectedGraph &other);
+	DirectedGraph &operator=(DirectedGraph &&other) noexcept;
+
+	GraphBase *clone() const override;
+	GraphBase *plunder() && override;
+
+	REPRESENTATION getRepresentation() const;
+
+	std::set<Node> getNodes() const;
+	std::set<DirectedEdge> getEdges() const;
+
+	bool hasNode(const Node &node) const;
+	bool hasEdge(const DirectedEdge &edge) const;
+
+	bool hasEdge(const Node &from, const Node &to) const;
+	std::set<DirectedEdge> findEdges(const Node &from, const Node &to) const;
+
+	std::set<Node> neighbors(const Node &node) const;
+	std::set<DirectedEdge> neighborEdges(const Node &node) const;
+
+	bool addNode(const Node &node);
+	bool addNode(const Node &node, int value);
+	bool removeNode(const Node &node);
+
+	bool addEdge(const DirectedEdge &edge);
+	bool addEdge(const DirectedEdge &edge, int value);
+	bool removeEdge(const DirectedEdge &edge);
+
+	int getNodeValue(const Node &node) const;
+	void setNodeValue(const Node &node, int value);
+
+	int getEdgeValue(const DirectedEdge &edge) const;
+	void setEdgeValue(const DirectedEdge &edge, int value);
+
+	int compare(const ObjectBase &other) const override
+	{
+		return -other.compare(*this);
+	}
+
+	int compare(const DirectedGraph &other) const;
+
+	void operator>>(std::ostream &out) const override;
+
+	operator std::string() const override;
+
+	int selfTypeId() const override
+	{
+		return typeId<DirectedGraph>();
+	}
+
+private:
+	void init();
+
+	REPRESENTATION representation;
+	IDirectedGraph *impl;
+
+	std::unordered_map<Node, int> nodeValues;
+	std::unordered_map<DirectedEdge, int> edgeValues;
+
+	friend class GraphToXMLComposer;
+	friend class GraphToStringComposer;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_COMPARE(DirectedGraph)
+
+#endif // DIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/directed/IDirectedGraph.cpp b/alib2data/src/graph/directed/IDirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eba90153b8b1e69895f0caf214d71a8b68ed5f54
--- /dev/null
+++ b/alib2data/src/graph/directed/IDirectedGraph.cpp
@@ -0,0 +1,23 @@
+#include "IDirectedGraph.h"
+
+#include <sstream>
+
+namespace graph {
+
+IDirectedGraph::~IDirectedGraph() noexcept
+{
+}
+
+IDirectedGraph::operator std::string() const
+{
+	std::stringstream ss;
+	for (const Node &node : allNodes()) {
+		ss << node;
+	}
+	for (const DirectedEdge &edge : allEdges()) {
+		ss << edge;
+	}
+	return ss.str();
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/directed/IDirectedGraph.h b/alib2data/src/graph/directed/IDirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..163c376b0b61ca046e3020d3830b96595f7dcec0
--- /dev/null
+++ b/alib2data/src/graph/directed/IDirectedGraph.h
@@ -0,0 +1,42 @@
+#ifndef I_DIRECTED_GRAPH_H_
+#define I_DIRECTED_GRAPH_H_
+
+#include <set>
+
+#include "../common/Node.h"
+#include "DirectedEdge.h"
+
+namespace graph {
+
+class IDirectedGraph
+{
+public:
+	virtual ~IDirectedGraph() noexcept;
+
+	virtual std::set<Node> allNodes() const = 0;
+	virtual std::set<DirectedEdge> allEdges() const = 0;
+
+	virtual bool hasNode(const Node &node) const = 0;
+	virtual bool hasEdge(const DirectedEdge &edge) const = 0;
+
+	virtual bool hasEdge(const Node &from, const Node &to) const = 0;
+	virtual std::set<DirectedEdge> findEdges(const Node &from, const Node &to) const = 0;
+
+	virtual std::set<Node> neighbors(const Node &node) const = 0;
+	virtual std::set<DirectedEdge> neighborEdges(const Node &node) const = 0;
+
+	virtual bool addNode(const Node &node) = 0;
+	virtual bool removeNode(const Node &node) = 0;
+
+	virtual bool addEdge(const DirectedEdge &edge) = 0;
+	virtual bool removeEdge(const DirectedEdge &edge) = 0;
+
+	virtual void copy(IDirectedGraph *other) = 0;
+	virtual int compare(IDirectedGraph *other) const = 0;
+
+	operator std::string() const;
+};
+
+} // namespace graph
+
+#endif // I_DIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.cpp b/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d54453729e335a1978aa2023f39eb2f426b896e
--- /dev/null
+++ b/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.cpp
@@ -0,0 +1,200 @@
+#include "AdjacencyListUndirectedGraph.h"
+#include "../../std/compare.hpp"
+#include "utils.h"
+
+namespace graph {
+
+std::set<Node> AdjacencyListUndirectedGraph::allNodes() const
+{
+	std::set<Node> out;
+
+	for (auto i : adj) {
+		out.insert(i.first);
+	}
+
+	return out;
+}
+
+std::set<UndirectedEdge> AdjacencyListUndirectedGraph::allEdges() const
+{
+	return edges;
+}
+
+bool AdjacencyListUndirectedGraph::hasNode(const Node &node) const
+{
+	return adj.find(node) != adj.end();
+}
+
+bool AdjacencyListUndirectedGraph::hasEdge(const UndirectedEdge &edge) const
+{
+	return edges.find(edge) != edges.end();
+}
+
+bool AdjacencyListUndirectedGraph::hasEdge(const Node &first, const Node &second) const
+{
+	auto search = adj.find(first);
+	if (search == adj.end()) {
+		return false;
+	}
+	return std::find(search->second.begin(), search->second.end(), second) != search->second.end();
+}
+
+std::set<UndirectedEdge> AdjacencyListUndirectedGraph::findEdges(const Node &first, const Node &second) const
+{
+	std::set<UndirectedEdge> out;
+
+	for (const UndirectedEdge &e : edges) {
+		if (edgeMatch(e, first, second)) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+std::set<Node> AdjacencyListUndirectedGraph::neighbors(const Node &node) const
+{
+	std::set<Node> out;
+
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return out;
+	}
+
+	std::copy(search->second.begin(), search->second.end(), std::inserter(out, out.begin()));
+	return out;
+}
+
+std::set<UndirectedEdge> AdjacencyListUndirectedGraph::neighborEdges(const Node &node) const
+{
+	std::set<UndirectedEdge> out;
+	const std::set<Node> &nghbrs = neighbors(node);
+
+	for (const UndirectedEdge &e : edges) {
+		if (edgeMatch(e, node, nghbrs)) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+bool AdjacencyListUndirectedGraph::addNode(const Node &node)
+{
+	if (adj.find(node) != adj.end()) {
+		return false;
+	}
+
+	adj[node]; // insert key
+	return true;
+}
+
+bool AdjacencyListUndirectedGraph::removeNode(const Node &node)
+{
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return false;
+	}
+
+	adj.erase(search);
+
+	auto i = edges.begin();
+	while (i != edges.end()) {
+		if (i->getFirstNode() == node) {
+			listRemoveOne(adj[i->getSecondNode()], node);
+			edges.erase(i++);
+		} else if (i->getSecondNode() == node) {
+			listRemoveOne(adj[i->getFirstNode()], node);
+			edges.erase(i++);
+		} else {
+			i++;
+		}
+	}
+
+	return true;
+}
+
+bool AdjacencyListUndirectedGraph::addEdge(const UndirectedEdge &edge)
+{
+	if (edges.find(edge) != edges.end()) {
+		return false;
+	}
+
+	edges.insert(edge);
+
+	adj[edge.getFirstNode()].push_back(edge.getSecondNode());
+	adj[edge.getSecondNode()].push_back(edge.getFirstNode());
+
+	return true;
+}
+
+bool AdjacencyListUndirectedGraph::removeEdge(const UndirectedEdge &edge)
+{
+	auto search = edges.find(edge);
+	if (search == edges.end()) {
+		return false;
+	}
+
+	edges.erase(search);
+
+	auto &lst1 = adj[edge.getFirstNode()];
+	listRemoveOne(lst1, edge.getSecondNode());
+	if (lst1.empty()) {
+		adj.erase(edge.getFirstNode());
+	}
+
+	auto &lst2 = adj[edge.getSecondNode()];
+	listRemoveOne(lst2, edge.getFirstNode());
+	if (lst2.empty()) {
+		adj.erase(edge.getSecondNode());
+	}
+
+	return true;
+}
+
+void AdjacencyListUndirectedGraph::copy(IUndirectedGraph *other)
+{
+	AdjacencyListUndirectedGraph *o = static_cast<AdjacencyListUndirectedGraph*>(other);
+
+	edges = o->edges;
+	adj = o->adj;
+}
+
+int AdjacencyListUndirectedGraph::compare(IUndirectedGraph *other) const
+{
+	AdjacencyListUndirectedGraph *o = static_cast<AdjacencyListUndirectedGraph*>(other);
+
+	std::compare<decltype(edges)> comp;
+	int res = comp(edges, o->edges);
+	if (res != 0) {
+		return res;
+	}
+
+	if (adj.size() < o->adj.size()) {
+		return -1;
+	}
+
+	if (adj.size() > o->adj.size()) {
+		return 1;
+	}
+
+	for (auto i : adj) {
+		auto lst1 = i.second;
+		auto lst2 = o->adj.at(i.first);
+
+		std::set<Node> adj1;
+		std::set<Node> adj2;
+		std::copy(lst1.begin(), lst1.end(), std::inserter(adj1, adj1.begin()));
+		std::copy(lst2.begin(), lst2.end(), std::inserter(adj2, adj2.begin()));
+
+		std::compare<std::set<Node>> comp;
+		int res = comp(adj1, adj2);
+		if (res != 0) {
+			return res;
+		}
+	}
+
+	return 0;
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.h b/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..066ee6636dcb70bc01bddfb8123076b28d4df562
--- /dev/null
+++ b/alib2data/src/graph/undirected/AdjacencyListUndirectedGraph.h
@@ -0,0 +1,60 @@
+#ifndef ADJACENCY_LIST_UNDIRECTED_GRAPH_H_
+#define ADJACENCY_LIST_UNDIRECTED_GRAPH_H_
+
+#include <list>
+#include <unordered_map>
+
+#include "IUndirectedGraph.h"
+
+namespace graph {
+
+/*
+ *  AdjacencyList representation
+ *
+ *  unordered_map:
+ *    [ n1 ] -> list [ n2, n2, n3, n4 ]
+ *    [ n2 ] -> list [ n1, n1, n4 ]      < multi-edge
+ *    [ n3 ] -> list [ n1, n4 ]
+ *    [ n4 ] -> list [ n1, n2, n3 ]
+ *
+ *  edges:
+ *    n1 <-> n2, n1 <-> n2, n1 <-> n3, n1 <-> n4
+ *    n2 <-> n4
+ *    n3 <-> n4
+ *
+ *  undirected edge n1 <-> n2 is represented as
+ *  directed edge n1 -> n2 and directed edge n2 -> n1
+ */
+
+class AdjacencyListUndirectedGraph : public IUndirectedGraph
+{
+public:
+	std::set<Node> allNodes() const override;
+	std::set<UndirectedEdge> allEdges() const override;
+
+	bool hasNode(const Node &node) const override;
+	bool hasEdge(const UndirectedEdge &edge) const override;
+
+	bool hasEdge(const Node &first, const Node &second) const override;
+	std::set<UndirectedEdge> findEdges(const Node &first, const Node &second) const override;
+
+	std::set<Node> neighbors(const Node &node) const override;
+	std::set<UndirectedEdge> neighborEdges(const Node &node) const override;
+
+	bool addNode(const Node &node) override;
+	bool removeNode(const Node &node) override;
+
+	bool addEdge(const UndirectedEdge &edge) override;
+	bool removeEdge(const UndirectedEdge &edge) override;
+
+	void copy(IUndirectedGraph *other) override;
+	int compare(IUndirectedGraph *other) const override;
+
+private:
+	std::set<UndirectedEdge> edges;
+	std::unordered_map<Node, std::list<Node>> adj;
+};
+
+} // namespace graph
+
+#endif // ADJACENCY_LIST_DIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.cpp b/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1d42d092176fce490483a25b2e451546a7cb248
--- /dev/null
+++ b/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.cpp
@@ -0,0 +1,185 @@
+#include "AdjacencyMatrixUndirectedGraph.h"
+#include "../../std/compare.hpp"
+#include "utils.h"
+
+namespace graph {
+
+std::set<Node> AdjacencyMatrixUndirectedGraph::allNodes() const
+{
+	std::set<Node> out;
+
+	for (auto i : adj) {
+		out.insert(i.first);
+	}
+
+	return out;
+}
+
+std::set<UndirectedEdge> AdjacencyMatrixUndirectedGraph::allEdges() const
+{
+	return edges;
+}
+
+bool AdjacencyMatrixUndirectedGraph::hasEdge(const Node &first, const Node &second) const
+{
+	auto search = adj.find(first);
+	if (search == adj.end()) {
+		return false;
+	}
+	return search->second.find(second) != search->second.end();
+}
+
+bool AdjacencyMatrixUndirectedGraph::hasNode(const Node &node) const
+{
+	return adj.find(node) != adj.end();
+}
+
+bool AdjacencyMatrixUndirectedGraph::hasEdge(const UndirectedEdge &edge) const
+{
+	return edges.find(edge) != edges.end();
+}
+
+std::set<UndirectedEdge> AdjacencyMatrixUndirectedGraph::findEdges(const Node &first, const Node &second) const
+{
+	std::set<UndirectedEdge> out;
+
+	for (const UndirectedEdge &e : edges) {
+		if (edgeMatch(e, first, second)) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+std::set<Node> AdjacencyMatrixUndirectedGraph::neighbors(const Node &node) const
+{
+	std::set<Node> out;
+
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return out;
+	}
+
+	for (auto i : search->second) {
+		if (i.second > 0) {
+			out.insert(i.first);
+		}
+	}
+
+	return out;
+}
+
+std::set<UndirectedEdge> AdjacencyMatrixUndirectedGraph::neighborEdges(const Node &node) const
+{
+	std::set<UndirectedEdge> out;
+	const std::set<Node> &nghbrs = neighbors(node);
+
+	for (const UndirectedEdge &e : edges) {
+		if (edgeMatch(e, node, nghbrs)) {
+			out.insert(e);
+		}
+	}
+
+	return out;
+}
+
+bool AdjacencyMatrixUndirectedGraph::addNode(const Node &node)
+{
+	if (adj.find(node) != adj.end()) {
+		return false;
+	}
+
+	adj[node]; // insert key
+	return true;
+}
+
+bool AdjacencyMatrixUndirectedGraph::removeNode(const Node &node)
+{
+	auto search = adj.find(node);
+	if (search == adj.end()) {
+		return false;
+	}
+
+	adj.erase(search);
+
+	auto i = edges.begin();
+	while (i != edges.end()) {
+		if (i->getFirstNode() == node) {
+			adj[i->getSecondNode()].erase(node);
+			edges.erase(i++);
+		} else if (i->getSecondNode() == node) {
+			adj[i->getFirstNode()].erase(node);
+			edges.erase(i++);
+		} else {
+			i++;
+		}
+	}
+
+	return true;
+}
+
+bool AdjacencyMatrixUndirectedGraph::addEdge(const UndirectedEdge &edge)
+{
+	if (edges.find(edge) != edges.end()) {
+		return false;
+	}
+
+	edges.insert(edge);
+
+	adj[edge.getFirstNode()][edge.getSecondNode()]++;
+	adj[edge.getSecondNode()][edge.getFirstNode()]++;
+
+	return true;
+}
+
+bool AdjacencyMatrixUndirectedGraph::removeEdge(const UndirectedEdge &edge)
+{
+	auto search = edges.find(edge);
+	if (search == edges.end()) {
+		return false;
+	}
+
+	edges.erase(search);
+
+	auto &map1 = adj[edge.getFirstNode()];
+	int &val1 = map1[edge.getSecondNode()];
+	if (--val1 <= 0) {
+		map1.erase(edge.getSecondNode());
+		if (map1.empty()) {
+			adj.erase(edge.getFirstNode());
+		}
+	}
+
+	auto &map2 = adj[edge.getSecondNode()];
+	int &val2 = map2[edge.getFirstNode()];
+	if (--val2 <= 0) {
+		map2.erase(edge.getFirstNode());
+		if (map2.empty()) {
+			adj.erase(edge.getSecondNode());
+		}
+	}
+
+	return true;
+}
+
+void AdjacencyMatrixUndirectedGraph::copy(IUndirectedGraph *other)
+{
+	AdjacencyMatrixUndirectedGraph *o = static_cast<AdjacencyMatrixUndirectedGraph*>(other);
+
+	edges = o->edges;
+	adj = o->adj;
+}
+
+int AdjacencyMatrixUndirectedGraph::compare(IUndirectedGraph *other) const
+{
+	AdjacencyMatrixUndirectedGraph *o = static_cast<AdjacencyMatrixUndirectedGraph*>(other);
+
+	auto first = std::tie(edges, adj);
+	auto second = std::tie(o->edges, o->adj);
+
+	std::compare<decltype(first)> comp;
+	return comp(first, second);
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.h b/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..a920d3dfa4c82acda851307903d7c422b0aa5f96
--- /dev/null
+++ b/alib2data/src/graph/undirected/AdjacencyMatrixUndirectedGraph.h
@@ -0,0 +1,63 @@
+#ifndef ADJACENCY_MATRIX_UNDIRECTED_GRAPH_H_
+#define ADJACENCY_MATRIX_UNDIRECTED_GRAPH_H_
+
+#include <set>
+#include <vector>
+#include <unordered_map>
+
+#include "IUndirectedGraph.h"
+
+namespace graph {
+
+/*
+ *  AdjacencyMatrix representation
+ *
+ *  unordered_map:
+ *    [ -- ][ n1 ][ n2 ][ n3 ][ n4 ]
+ *    [ n1 ]  0     2     1     1
+ *    [ n2 ]  2     0     0     1
+ *    [ n3 ]  1     0     0     1
+ *    [ n4 ]  1     1     1     0
+ *
+ *  edges:
+ *    n1 <-> n2, n1 <-> n2, n1 <-> n3, n1 <-> n4
+ *    n2 <-> n4
+ *    n3 <-> n4
+ *
+ *  undirected edge n1 <-> n2 is represented as
+ *  directed edge n1 -> n2 and directed edge n2 -> n1
+ *
+ */
+
+class AdjacencyMatrixUndirectedGraph : public IUndirectedGraph
+{
+public:
+	std::set<Node> allNodes() const override;
+	std::set<UndirectedEdge> allEdges() const override;
+
+	bool hasNode(const Node &node) const override;
+	bool hasEdge(const UndirectedEdge &edge) const override;
+
+	bool hasEdge(const Node &first, const Node &second) const override;
+	std::set<UndirectedEdge> findEdges(const Node &first, const Node &second) const override;
+
+	std::set<Node> neighbors(const Node &node) const override;
+	std::set<UndirectedEdge> neighborEdges(const Node &node) const override;
+
+	bool addNode(const Node &node) override;
+	bool removeNode(const Node &node) override;
+
+	bool addEdge(const UndirectedEdge &edge) override;
+	bool removeEdge(const UndirectedEdge &edge) override;
+
+	void copy(IUndirectedGraph *other) override;
+	int compare(IUndirectedGraph *other) const override;
+
+private:
+	std::set<UndirectedEdge> edges;
+	std::unordered_map<Node, std::unordered_map<Node, int>> adj;
+};
+
+} // namespace graph
+
+#endif // ADJACENCY_MATRIX_UNDIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/undirected/IUndirectedGraph.cpp b/alib2data/src/graph/undirected/IUndirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d8c5cec9c7f01350ba7459d8f105eae2e98a1f8a
--- /dev/null
+++ b/alib2data/src/graph/undirected/IUndirectedGraph.cpp
@@ -0,0 +1,23 @@
+#include "IUndirectedGraph.h"
+
+#include <sstream>
+
+namespace graph {
+
+IUndirectedGraph::~IUndirectedGraph() noexcept
+{
+}
+
+IUndirectedGraph::operator std::string() const
+{
+	std::stringstream ss;
+	for (const Node &node : allNodes()) {
+		ss << node;
+	}
+	for (const UndirectedEdge &edge : allEdges()) {
+		ss << edge;
+	}
+	return ss.str();
+}
+
+} // namespace graph
diff --git a/alib2data/src/graph/undirected/IUndirectedGraph.h b/alib2data/src/graph/undirected/IUndirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..3e0ad5aacc23c201ba2ff71579aefc48ec9f86d5
--- /dev/null
+++ b/alib2data/src/graph/undirected/IUndirectedGraph.h
@@ -0,0 +1,42 @@
+#ifndef I_UNDIRECTED_GRAPH_H_
+#define I_UNDIRECTED_GRAPH_H_
+
+#include <set>
+
+#include "../common/Node.h"
+#include "UndirectedEdge.h"
+
+namespace graph {
+
+class IUndirectedGraph
+{
+public:
+	virtual ~IUndirectedGraph() noexcept;
+
+	virtual std::set<Node> allNodes() const = 0;
+	virtual std::set<UndirectedEdge> allEdges() const = 0;
+
+	virtual bool hasNode(const Node &node) const = 0;
+	virtual bool hasEdge(const UndirectedEdge &edge) const = 0;
+
+	virtual bool hasEdge(const Node &first, const Node &second) const = 0;
+	virtual std::set<UndirectedEdge> findEdges(const Node &first, const Node &second) const = 0;
+
+	virtual std::set<Node> neighbors(const Node &node) const = 0;
+	virtual std::set<UndirectedEdge> neighborEdges(const Node &node) const = 0;
+
+	virtual bool addNode(const Node &node) = 0;
+	virtual bool removeNode(const Node &node) = 0;
+
+	virtual bool addEdge(const UndirectedEdge &edge) = 0;
+	virtual bool removeEdge(const UndirectedEdge &edge) = 0;
+
+	virtual void copy(IUndirectedGraph *other) = 0;
+	virtual int compare(IUndirectedGraph *other) const = 0;
+
+	operator std::string() const;
+};
+
+} // namespace graph
+
+#endif // I_UNDIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/undirected/UndirectedEdge.cpp b/alib2data/src/graph/undirected/UndirectedEdge.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..79327f8dd22c76d1805e45f482a8006f28f4886a
--- /dev/null
+++ b/alib2data/src/graph/undirected/UndirectedEdge.cpp
@@ -0,0 +1,197 @@
+#include "UndirectedEdge.h"
+
+#include <sstream>
+
+namespace graph {
+
+UndirectedEdge::UndirectedEdge()
+	: name(label::labelFrom('u'))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const Node &first, const Node &second)
+	: first(first)
+	, second(second)
+	, name(label::labelFrom('u'))
+{
+}
+
+UndirectedEdge::UndirectedEdge(Node &&first, Node &&second)
+	: first(std::move(first))
+	, second(std::move(second))
+	, name(label::labelFrom('u'))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const Node &first, const Node &second, const label::Label &name)
+	: first(first)
+	, second(second)
+	, name(name)
+{
+}
+
+UndirectedEdge::UndirectedEdge(Node &&first, Node &&second, label::Label &&name)
+	: first(std::move(first))
+	, second(std::move(second))
+	, name(std::move(name))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const Node &first, const Node &second, int number)
+	: first(first)
+	, second(second)
+	, name(label::labelFrom(number))
+{
+}
+
+UndirectedEdge::UndirectedEdge(Node &&first, Node &&second, int number)
+	: first(std::move(first))
+	, second(std::move(second))
+	, name(label::labelFrom(number))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const Node &first, const Node &second, char character)
+	: first(first)
+	, second(second)
+	, name(label::labelFrom(character))
+{
+}
+
+UndirectedEdge::UndirectedEdge(Node &&first, Node &&second, char character)
+	: first(std::move(first))
+	, second(std::move(second))
+	, name(label::labelFrom(character))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const Node &first, const Node &second, const std::string &name)
+	: first(first)
+	, second(second)
+	, name(label::labelFrom(name))
+{
+}
+
+UndirectedEdge::UndirectedEdge(Node &&first, Node &&second, const std::string &name)
+	: first(std::move(first))
+	, second(std::move(second))
+	, name(label::labelFrom(name))
+{
+}
+
+UndirectedEdge::UndirectedEdge(const UndirectedEdge &other)
+	: first(other.first)
+	, second(other.second)
+	, name(other.name)
+{
+}
+
+UndirectedEdge::UndirectedEdge(UndirectedEdge &&other) noexcept
+	: first(std::move(other.first))
+	, second(std::move(other.second))
+	, name(std::move(other.name))
+{
+}
+
+UndirectedEdge &UndirectedEdge::operator=(const UndirectedEdge &other)
+{
+	if (this == &other) {
+		return *this;
+	}
+
+	*this = UndirectedEdge(other);
+	return *this;
+}
+
+UndirectedEdge &UndirectedEdge::operator=(UndirectedEdge &&other)
+{
+	std::swap(first, other.first);
+	std::swap(second, other.second);
+	std::swap(name, other.name);
+	return *this;
+}
+
+GraphElement *UndirectedEdge::clone() const
+{
+	return new UndirectedEdge(*this);
+}
+
+GraphElement *UndirectedEdge::plunder() &&
+{
+	return new UndirectedEdge(std::move(*this));
+}
+
+const Node &UndirectedEdge::getFirstNode() const
+{
+	return first;
+}
+
+const Node &UndirectedEdge::getSecondNode() const
+{
+	return second;
+}
+
+const label::Label &UndirectedEdge::getName() const
+{
+	return name;
+}
+
+int UndirectedEdge::compare(const UndirectedEdge &other) const
+{
+	auto a = sortedNodes();
+	auto b = other.sortedNodes();
+
+	int res = name.getData().compare(other.name.getData());
+	if (res == 0) {
+		if (a.first.compare(b.second) == 0 && a.second.compare(b.first) == 0) {
+			return 0;
+		}
+		res = a.first.compare(b.first);
+		if (res == 0) {
+			res = a.second.compare(b.second);
+		}
+	}
+	return res;
+}
+
+void UndirectedEdge::operator>>(std::ostream &out) const
+{
+	dump(out);
+}
+
+UndirectedEdge::operator std::string() const
+{
+	std::stringstream ss;
+	ss << *this;
+	return ss.str();
+}
+
+std::ostream &operator<<(std::ostream &out, const UndirectedEdge &node)
+{
+	node.dump(out);
+	return out;
+}
+
+void UndirectedEdge::dump(std::ostream &os) const
+{
+	auto nodes = sortedNodes();
+	os << "(UndirectedEdge " << nodes.first << " -> " << nodes.second << ")";
+}
+
+std::pair<Node, Node> UndirectedEdge::sortedNodes() const
+{
+	std::pair<Node, Node> out;
+
+	if (first < second) {
+		out.first = first;
+		out.second = second;
+	} else {
+		out.first = second;
+		out.second = first;
+	}
+
+	return out;
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/undirected/UndirectedEdge.h b/alib2data/src/graph/undirected/UndirectedEdge.h
new file mode 100644
index 0000000000000000000000000000000000000000..379300e591de7f10112fef09ed9dedff5d48e3f2
--- /dev/null
+++ b/alib2data/src/graph/undirected/UndirectedEdge.h
@@ -0,0 +1,69 @@
+#ifndef UNDIRECTED_EDGE_H_
+#define UNDIRECTED_EDGE_H_
+
+#include "../common/GraphElement.h"
+#include "../common/Macros.h"
+#include "../common/Node.h"
+
+namespace graph {
+
+class UndirectedEdge : public std::acceptor<UndirectedEdge, GraphElement, GraphElement>
+{
+public:
+	explicit UndirectedEdge();
+	explicit UndirectedEdge(const Node &first, const Node &second);
+	explicit UndirectedEdge(Node &&first, Node &&second);
+	explicit UndirectedEdge(const Node &first, const Node &second, const label::Label &name);
+	explicit UndirectedEdge(Node &&first, Node &&second, label::Label &&name);
+	explicit UndirectedEdge(const Node &first, const Node &second, int number);
+	explicit UndirectedEdge(Node &&first, Node &&second, int number);
+	explicit UndirectedEdge(const Node &first, const Node &second, char character);
+	explicit UndirectedEdge(Node &&first, Node &&second, char character);
+	explicit UndirectedEdge(const Node &first, const Node &second, const std::string &name);
+	explicit UndirectedEdge(Node &&first, Node &&second, const std::string &name);
+
+	UndirectedEdge(const UndirectedEdge &other);
+	UndirectedEdge(UndirectedEdge &&other) noexcept;
+	UndirectedEdge &operator=(const UndirectedEdge &other);
+	UndirectedEdge &operator=(UndirectedEdge &&other);
+
+	GraphElement *clone() const;
+	GraphElement *plunder() &&;
+
+	const Node &getFirstNode() const;
+	const Node &getSecondNode() const;
+	const label::Label &getName() const;
+
+	int compare(const GraphElement &other) const override
+	{
+		return -other.compare(*this);
+	}
+
+	int compare(const UndirectedEdge &other) const;
+
+	void operator>>(std::ostream &out) const override;
+
+	operator std::string() const override;
+
+	int selfTypeId() const override
+	{
+		return typeId<UndirectedEdge>();
+	}
+
+	friend std::ostream &operator<<(std::ostream &out, const UndirectedEdge &node);
+
+private:
+	void dump(std::ostream &os) const;
+	std::pair<Node, Node> sortedNodes() const;
+
+	Node first;
+	Node second;
+	label::Label name;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_HASH(UndirectedEdge)
+GRAPH_DEFINE_STD_COMPARE(UndirectedEdge)
+
+#endif // UNDIRECTED_EDGE_H_
diff --git a/alib2data/src/graph/undirected/UndirectedGraph.cpp b/alib2data/src/graph/undirected/UndirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c35fa860067ff8372d6c1fd0496a0e9b0118fde
--- /dev/null
+++ b/alib2data/src/graph/undirected/UndirectedGraph.cpp
@@ -0,0 +1,232 @@
+#include "UndirectedGraph.h"
+#include "IUndirectedGraph.h"
+#include "AdjacencyListUndirectedGraph.h"
+#include "AdjacencyMatrixUndirectedGraph.h"
+#include "../../std/set.hpp"
+
+#include <iostream>
+#include <algorithm>
+#include <sstream>
+
+namespace graph {
+
+UndirectedGraph::UndirectedGraph(REPRESENTATION representation)
+	: representation(representation)
+{
+	init();
+}
+
+UndirectedGraph::~UndirectedGraph() noexcept
+{
+	delete impl;
+}
+
+UndirectedGraph::UndirectedGraph(const UndirectedGraph &other)
+	: representation(other.representation)
+	, nodeValues(other.nodeValues)
+	, edgeValues(other.edgeValues)
+{
+	init();
+	impl->copy(other.impl);
+}
+
+UndirectedGraph::UndirectedGraph(UndirectedGraph &&other) noexcept
+	: representation(other.representation)
+	, impl(other.impl)
+	, nodeValues(std::move(other.nodeValues))
+	, edgeValues(std::move(other.edgeValues))
+{
+	other.impl = 0;
+}
+
+UndirectedGraph &UndirectedGraph::operator=(const UndirectedGraph &other)
+{
+	if (this == &other) {
+		return *this;
+	}
+
+	*this = UndirectedGraph(other);
+	return *this;
+}
+
+UndirectedGraph &UndirectedGraph::operator=(UndirectedGraph &&other) noexcept
+{
+	this->representation = other.representation;
+	std::swap(this->impl, other.impl);
+	std::swap(this->nodeValues, other.nodeValues);
+	std::swap(this->edgeValues, other.edgeValues);
+	return *this;
+}
+
+GraphBase *UndirectedGraph::clone() const
+{
+	return new UndirectedGraph(*this);
+}
+
+GraphBase *UndirectedGraph::plunder() &&
+{
+	return new UndirectedGraph(std::move(*this));
+}
+
+REPRESENTATION UndirectedGraph::getRepresentation() const
+{
+	return representation;
+}
+
+std::set<Node> UndirectedGraph::getNodes() const
+{
+	return impl->allNodes();
+}
+
+std::set<UndirectedEdge> UndirectedGraph::getEdges() const
+{
+	return impl->allEdges();
+}
+
+bool UndirectedGraph::hasNode(const Node &node) const
+{
+	return impl->hasNode(node);
+}
+
+bool UndirectedGraph::hasEdge(const UndirectedEdge &edge) const
+{
+	return impl->hasEdge(edge);
+}
+
+bool UndirectedGraph::hasEdge(const Node &from, const Node &to) const
+{
+	return impl->hasEdge(from, to);
+}
+
+std::set<UndirectedEdge> UndirectedGraph::findEdges(const Node &from, const Node &to) const
+{
+	return impl->findEdges(from, to);
+}
+
+std::set<Node> UndirectedGraph::neighbors(const Node &node) const
+{
+	return impl->neighbors(node);
+}
+
+std::set<UndirectedEdge> UndirectedGraph::neighborEdges(const Node &node) const
+{
+	return impl->neighborEdges(node);
+}
+
+bool UndirectedGraph::addNode(const Node &node)
+{
+	return impl->addNode(node);
+}
+
+bool UndirectedGraph::addNode(const Node &node, int value)
+{
+	bool success = addNode(node);
+	if (success) {
+		setNodeValue(node, value);
+	}
+	return success;
+}
+
+bool UndirectedGraph::removeNode(const Node &node)
+{
+	bool success = impl->removeNode(node);
+	if (success) {
+		nodeValues.erase(node);
+	}
+	return success;
+}
+
+bool UndirectedGraph::addEdge(const UndirectedEdge &edge)
+{
+	return impl->addEdge(edge);
+}
+
+bool UndirectedGraph::addEdge(const UndirectedEdge &edge, int value)
+{
+	bool success = addEdge(edge);
+	if (success) {
+		setEdgeValue(edge, value);
+	}
+	return success;
+}
+
+bool UndirectedGraph::removeEdge(const UndirectedEdge &edge)
+{
+	bool success = impl->removeEdge(edge);
+	if (success) {
+		edgeValues.erase(edge);
+	}
+	return success;
+}
+
+int UndirectedGraph::getNodeValue(const Node &node) const
+{
+	return nodeValues.at(node);
+}
+
+void UndirectedGraph::setNodeValue(const Node &node, int value)
+{
+	nodeValues.insert({node, value});
+}
+
+int UndirectedGraph::getEdgeValue(const UndirectedEdge &edge) const
+{
+	return edgeValues.at(edge);
+}
+
+void UndirectedGraph::setEdgeValue(const UndirectedEdge &edge, int value)
+{
+	edgeValues.insert({edge, value});
+}
+
+void UndirectedGraph::operator>>(std::ostream &out) const
+{
+	out << "(UndirectedGraph " << static_cast<std::string>(*impl) << ")";
+}
+
+int UndirectedGraph::compare(const UndirectedGraph &other) const
+{
+	if (representation != other.representation) {
+		return static_cast<int>(representation) - static_cast<int>(other.representation);
+	}
+
+	auto first = std::tie(nodeValues, edgeValues);
+	auto second = std::tie(other.nodeValues, other.edgeValues);
+
+	std::compare<decltype(first)> comp;
+	int res = comp(first, second);
+
+	if (res != 0) {
+		return res;
+	}
+
+	return impl->compare(other.impl);
+}
+
+UndirectedGraph::operator std::string() const
+{
+	std::stringstream ss;
+	ss << *this;
+	return ss.str();
+}
+
+void UndirectedGraph::init()
+{
+	switch (representation) {
+	case REPRESENTATION::ADJACENCY_LIST:
+		impl = new AdjacencyListUndirectedGraph();
+		break;
+
+	case REPRESENTATION::ADJACENCY_MATRIX:
+		impl = new AdjacencyMatrixUndirectedGraph();
+		break;
+
+	case REPRESENTATION::INCIDENCE_MATRIX:
+
+	default:
+		impl = 0;
+	}
+}
+
+} // namespace graph
+
diff --git a/alib2data/src/graph/undirected/UndirectedGraph.h b/alib2data/src/graph/undirected/UndirectedGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fc7dad5f07921653342426c61bcf69aa97c928e
--- /dev/null
+++ b/alib2data/src/graph/undirected/UndirectedGraph.h
@@ -0,0 +1,91 @@
+#ifndef UNDIRECTED_GRAPH_H_
+#define UNDIRECTED_GRAPH_H_
+
+#include <unordered_map>
+
+#include "../GraphBase.h"
+#include "../GraphRepresentation.h"
+#include "../common/Macros.h"
+#include "../common/Node.h"
+#include "UndirectedEdge.h"
+
+namespace graph {
+
+class IUndirectedGraph;
+
+class UndirectedGraph : public std::acceptor<UndirectedGraph, VisitableGraphBase, std::acceptor<UndirectedGraph, alib::VisitableObjectBase, GraphBase>>
+{
+public:
+	explicit UndirectedGraph(REPRESENTATION representation = REPRESENTATION::ADJACENCY_LIST);
+	~UndirectedGraph() noexcept;
+
+	UndirectedGraph(const UndirectedGraph &other);
+	UndirectedGraph(UndirectedGraph &&other) noexcept;
+	UndirectedGraph &operator=(const UndirectedGraph &other);
+	UndirectedGraph &operator=(UndirectedGraph &&other) noexcept;
+
+	GraphBase *clone() const override;
+	GraphBase *plunder() && override;
+
+	REPRESENTATION getRepresentation() const;
+
+	std::set<Node> getNodes() const;
+	std::set<UndirectedEdge> getEdges() const;
+
+	bool hasNode(const Node &node) const;
+	bool hasEdge(const UndirectedEdge &edge) const;
+
+	bool hasEdge(const Node &from, const Node &to) const;
+	std::set<UndirectedEdge> findEdges(const Node &from, const Node &to) const;
+
+	std::set<Node> neighbors(const Node &node) const;
+	std::set<UndirectedEdge> neighborEdges(const Node &node) const;
+
+	bool addNode(const Node &node);
+	bool addNode(const Node &node, int value);
+	bool removeNode(const Node &node);
+
+	bool addEdge(const UndirectedEdge &edge);
+	bool addEdge(const UndirectedEdge &edge, int value);
+	bool removeEdge(const UndirectedEdge &edge);
+
+	int getNodeValue(const Node &node) const;
+	void setNodeValue(const Node &node, int value);
+
+	int getEdgeValue(const UndirectedEdge &edge) const;
+	void setEdgeValue(const UndirectedEdge &edge, int value);
+
+	int compare(const ObjectBase &other) const override
+	{
+		return -other.compare(*this);
+	}
+
+	int compare(const UndirectedGraph &other) const;
+
+	void operator>>(std::ostream &out) const override;
+
+	operator std::string() const override;
+
+	int selfTypeId() const override
+	{
+		return typeId<UndirectedGraph>();
+	}
+
+private:
+	void init();
+
+	REPRESENTATION representation;
+	IUndirectedGraph *impl;
+
+	std::unordered_map<Node, int> nodeValues;
+	std::unordered_map<UndirectedEdge, int> edgeValues;
+
+	friend class GraphToXMLComposer;
+	friend class GraphToStringComposer;
+};
+
+} // namespace graph
+
+GRAPH_DEFINE_STD_COMPARE(UndirectedGraph)
+
+#endif // UNDIRECTED_GRAPH_H_
diff --git a/alib2data/src/graph/undirected/utils.h b/alib2data/src/graph/undirected/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..706c19ba0d64658094cdda4c1bb292d8ae58d458
--- /dev/null
+++ b/alib2data/src/graph/undirected/utils.h
@@ -0,0 +1,41 @@
+#ifndef GRAPH_UTILS_H_
+#define GRAPH_UTILS_H_
+
+#include <algorithm>
+
+#include "../common/Node.h"
+#include "UndirectedEdge.h"
+
+namespace graph {
+
+static bool edgeMatch(const UndirectedEdge &edge, const Node &first, const Node &second)
+{
+	if (edge.getFirstNode() == first && edge.getSecondNode() == second) {
+		return true;
+	}
+	if (edge.getFirstNode() == second && edge.getSecondNode() == first) {
+		return true;
+	}
+	return false;
+}
+
+static bool edgeMatch(const UndirectedEdge &edge, const Node &node, const std::set<Node> &neighbors)
+{
+	if (edge.getFirstNode() == node && neighbors.find(edge.getSecondNode()) != neighbors.end()) {
+		return true;
+	}
+	if (edge.getSecondNode() == node && neighbors.find(edge.getFirstNode()) != neighbors.end()) {
+		return true;
+	}
+	return false;
+}
+
+template<typename T>
+inline void listRemoveOne(std::list<T> &lst, const T &item)
+{
+	lst.erase(std::find(lst.begin(), lst.end(), item));
+}
+
+} // namespace graph
+
+#endif // GRAPH_UTILS_H_
diff --git a/alib2data/src/object/ObjectBase.h b/alib2data/src/object/ObjectBase.h
index 04a619b2f0d71fd963b1a2bc739f45c0d7350cd8..cf2dd385cba2967fae88d3ca9a4487f60d2d80d5 100644
--- a/alib2data/src/object/ObjectBase.h
+++ b/alib2data/src/object/ObjectBase.h
@@ -64,6 +64,13 @@ class UnrestrictedGrammar;
 
 }
 
+namespace graph {
+
+class DirectedGraph;
+class UndirectedGraph;
+
+}
+
 namespace label {
 
 class PrimitiveLabel;
@@ -135,6 +142,7 @@ typedef std::acceptor_base<ObjectBase,
 			exception::AlibException,
 			automaton::DFA, automaton::NFA, automaton::MultiInitialStateNFA, automaton::EpsilonNFA, automaton::CompactNFA, automaton::ExtendedNFA, automaton::DPDA, automaton::SinglePopDPDA, automaton::InputDrivenDPDA, automaton::VisiblyPushdownDPDA, automaton::RealTimeHeightDeterministicDPDA, automaton::NPDA, automaton::SinglePopNPDA, automaton::InputDrivenNPDA, automaton::VisiblyPushdownNPDA, automaton::RealTimeHeightDeterministicNPDA, automaton::OneTapeDTM,
 			grammar::LeftLG, grammar::LeftRG, grammar::RightLG, grammar::RightRG, grammar::LG, grammar::CFG, grammar::EpsilonFreeCFG, grammar::CNF, grammar::GNF, grammar::CSG, grammar::NonContractingGrammar, grammar::ContextPreservingUnrestrictedGrammar, grammar::UnrestrictedGrammar,
+			graph::DirectedGraph, graph::UndirectedGraph,
 			label::PrimitiveLabel, label::HexavigesimalLabel, label::ObjectLabel, label::LabelSetLabel, label::LabelPairLabel, label::UniqueLabel,
 			regexp::UnboundedRegExp, regexp::FormalRegExp,
 			string::Epsilon, string::LinearString, string::CyclicString,
@@ -150,6 +158,7 @@ class ObjectBase :
 			exception::AlibException,
 			automaton::DFA, automaton::NFA, automaton::MultiInitialStateNFA, automaton::EpsilonNFA, automaton::CompactNFA, automaton::ExtendedNFA, automaton::DPDA, automaton::SinglePopDPDA, automaton::InputDrivenDPDA, automaton::VisiblyPushdownDPDA, automaton::RealTimeHeightDeterministicDPDA, automaton::NPDA, automaton::SinglePopNPDA, automaton::InputDrivenNPDA, automaton::VisiblyPushdownNPDA, automaton::RealTimeHeightDeterministicNPDA, automaton::OneTapeDTM,
 			grammar::LeftLG, grammar::LeftRG, grammar::RightLG, grammar::RightRG, grammar::LG, grammar::CFG, grammar::EpsilonFreeCFG, grammar::CNF, grammar::GNF, grammar::CSG, grammar::NonContractingGrammar, grammar::ContextPreservingUnrestrictedGrammar, grammar::UnrestrictedGrammar,
+			graph::DirectedGraph, graph::UndirectedGraph,
 			label::PrimitiveLabel, label::HexavigesimalLabel, label::ObjectLabel, label::LabelSetLabel, label::LabelPairLabel, label::UniqueLabel,
 			regexp::UnboundedRegExp, regexp::FormalRegExp,
 			string::Epsilon, string::LinearString, string::CyclicString,
diff --git a/alib2data/src/std/compare.hpp b/alib2data/src/std/compare.hpp
index 4e1e780cb0963a6144b7ef537338ec2c7f30f7dd..7a22279d586428265b91681895150e71e11a0ffd 100644
--- a/alib2data/src/std/compare.hpp
+++ b/alib2data/src/std/compare.hpp
@@ -9,9 +9,11 @@
 #define COMPARE_HPP_
 
 #include <set>
+#include <list>
 #include <vector>
 #include <string>
 #include <map>
+#include <unordered_map>
 #include <tuple>
 
 namespace std {
@@ -21,6 +23,13 @@ struct compare {
 	int operator()(T first, T second) const;
 };
 
+template<>
+struct compare<bool> {
+	int operator()(bool first, bool second) const {
+		return first - second;
+	}
+};
+
 template<>
 struct compare<int> {
 	int operator()(int first, int second) const {
@@ -50,6 +59,21 @@ struct compare<set<T>> {
 	}
 };
 
+template<class T>
+struct compare<list<T>> {
+	int operator()(const list<T>& first, const list<T>& second) const {
+		if(first.size() < second.size()) return -1;
+		if(first.size() > second.size()) return 1;
+
+		compare<T> comp;
+		for(auto iterF = first.begin(), iterS = second.begin(); iterF != first.end(); iterF++, iterS++) {
+			int res = comp(*iterF, *iterS);
+			if(res != 0) return res;
+		}
+		return 0;
+	}
+};
+
 template<class T>
 struct compare<vector<T>> {
 	int operator()(const vector<T>& first, const vector<T>& second) const {
@@ -92,6 +116,23 @@ struct compare<map<T, R>> {
 	}
 };
 
+template<class T, class R>
+struct compare<unordered_map<T, R>> {
+	int operator()(const unordered_map<T, R>& first, const unordered_map<T, R>& second) const {
+		if(first.size() < second.size()) return -1;
+		if(first.size() > second.size()) return 1;
+
+		compare<R> comp;
+		for(auto iter = first.begin(); iter != first.end(); iter++) {
+			auto search = second.find(iter->first);
+			if(search == second.end()) return -1;
+			int res = comp(iter->second, search->second);
+			if(res != 0) return res;
+		}
+		return 0;
+	}
+};
+
 template<int I, typename Tuple>
 struct compareTupleHelper;
 
diff --git a/alib2data/test-src/graph/GraphElementsTest.cpp b/alib2data/test-src/graph/GraphElementsTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0910803002eccf6f24151b3ca80258854ff87bd0
--- /dev/null
+++ b/alib2data/test-src/graph/GraphElementsTest.cpp
@@ -0,0 +1,208 @@
+#include "GraphElementsTest.h"
+
+#include "sax/SaxParseInterface.h"
+#include "sax/SaxComposeInterface.h"
+
+#include "factory/XmlDataFactory.hpp"
+#include "factory/StringDataFactory.hpp"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphElementsTest);
+
+void GraphElementsTest::testCopyConstruct()
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+
+	// DirectedEdge
+	graph::DirectedEdge de1(n1, n2);
+	graph::DirectedEdge de2(n1, n3);
+
+	graph::DirectedEdge de1_2(de1);
+	CPPUNIT_ASSERT(de1_2 == de1);
+
+	graph::DirectedEdge de2_2(de2);
+	CPPUNIT_ASSERT(de2_2 == de2);
+
+	graph::DirectedEdge de1_3(std::move(de1));
+	CPPUNIT_ASSERT(de1_3 == de1_2);
+
+	graph::DirectedEdge de2_3(std::move(de2));
+	CPPUNIT_ASSERT(de2_3 == de2_2);
+
+	// UndirectedEdge
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue2(n1, n3);
+
+	graph::UndirectedEdge ue1_2(ue1);
+	CPPUNIT_ASSERT(ue1_2 == ue1);
+
+	graph::UndirectedEdge ue2_2(ue2);
+	CPPUNIT_ASSERT(ue2_2 == ue2);
+
+	graph::UndirectedEdge ue1_3(std::move(ue1));
+	CPPUNIT_ASSERT(ue1_3 == ue1_2);
+
+	graph::UndirectedEdge ue2_3(std::move(ue2));
+	CPPUNIT_ASSERT(ue2_3 == ue2_2);
+
+	// Node
+	graph::Node n1_2(n1);
+	CPPUNIT_ASSERT(n1_2 == n1);
+
+	graph::Node n2_2(n2);
+	CPPUNIT_ASSERT(n2_2 == n2);
+
+	graph::Node n3_2(n3);
+	CPPUNIT_ASSERT(n3_2 == n3);
+
+	graph::Node n1_3(std::move(n1));
+	CPPUNIT_ASSERT(n1_3 == n1_2);
+
+	graph::Node n2_3(std::move(n2));
+	CPPUNIT_ASSERT(n2_3 == n2_2);
+
+	graph::Node n3_3(std::move(n3));
+	CPPUNIT_ASSERT(n3_3 == n3_2);
+}
+
+void GraphElementsTest::testEqual()
+{
+	// Node
+	graph::Node n1("n1");
+	graph::Node n1_("n1");
+	graph::Node n2("n2");
+	graph::Node n2_("n2");
+	graph::Node n3("n3");
+	graph::Node n3_("n3");
+
+	CPPUNIT_ASSERT(n1 == n1);
+	CPPUNIT_ASSERT(n1 == n1_);
+	CPPUNIT_ASSERT(n2 == n2);
+	CPPUNIT_ASSERT(n2 == n2_);
+	CPPUNIT_ASSERT(n3 == n3);
+	CPPUNIT_ASSERT(n3 == n3_);
+
+	CPPUNIT_ASSERT(n1 != n2);
+	CPPUNIT_ASSERT(n1 != n3);
+	CPPUNIT_ASSERT(n2 != n1);
+	CPPUNIT_ASSERT(n3 != n1);
+
+	std::compare<graph::Node> n_cmp;
+	CPPUNIT_ASSERT_EQUAL(0, n_cmp(n1, n1_));
+	CPPUNIT_ASSERT_EQUAL(0, n_cmp(n1, n1));
+	CPPUNIT_ASSERT(n_cmp(n1, n2) != 0);
+	CPPUNIT_ASSERT(n_cmp(n1, n3) != 0);
+	CPPUNIT_ASSERT(n_cmp(n1, n3_) != 0);
+
+	// DirectedEdge
+	graph::DirectedEdge de1(n1, n2);
+	graph::DirectedEdge de1_(n1, n2);
+	graph::DirectedEdge de2(n1, n3);
+	graph::DirectedEdge de2_(n1, n3);
+	graph::DirectedEdge de3(n1, n3, "a");
+	graph::DirectedEdge de3_(n1, n3, "a");
+
+	CPPUNIT_ASSERT(de1 == de1);
+	CPPUNIT_ASSERT(de1 == de1_);
+	CPPUNIT_ASSERT(de2 == de2);
+	CPPUNIT_ASSERT(de2 == de2_);
+	CPPUNIT_ASSERT(de3 == de3);
+	CPPUNIT_ASSERT(de3 == de3_);
+
+	CPPUNIT_ASSERT(de1 != de2);
+	CPPUNIT_ASSERT(de1 != de3);
+	CPPUNIT_ASSERT(de2 != de1);
+	CPPUNIT_ASSERT(de3 != de1);
+
+	std::compare<graph::DirectedEdge> d_cmp;
+	CPPUNIT_ASSERT_EQUAL(0, d_cmp(de1, de1_));
+	CPPUNIT_ASSERT_EQUAL(0, d_cmp(de1, de1));
+	CPPUNIT_ASSERT(d_cmp(de1, de2) != 0);
+	CPPUNIT_ASSERT(d_cmp(de1, de3) != 0);
+	CPPUNIT_ASSERT(d_cmp(de1, de3_) != 0);
+
+	// UndirectedEdge
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue1_(n1, n2);
+	graph::UndirectedEdge ue1_s(n2, n1);
+	graph::UndirectedEdge ue2(n1, n3);
+	graph::UndirectedEdge ue2_(n1, n3);
+	graph::UndirectedEdge ue3(n1, n3, "a");
+	graph::UndirectedEdge ue3_(n1, n3, "a");
+
+	CPPUNIT_ASSERT(ue1 == ue1);
+	CPPUNIT_ASSERT(ue1 == ue1_);
+	CPPUNIT_ASSERT(ue1 == ue1_s);
+	CPPUNIT_ASSERT(ue1_ == ue1_s);
+	CPPUNIT_ASSERT(ue2 == ue2);
+	CPPUNIT_ASSERT(ue2 == ue2_);
+	CPPUNIT_ASSERT(ue3 == ue3);
+	CPPUNIT_ASSERT(ue3 == ue3_);
+
+	CPPUNIT_ASSERT(ue1 != ue2);
+	CPPUNIT_ASSERT(ue1 != ue3);
+	CPPUNIT_ASSERT(ue2 != ue1);
+	CPPUNIT_ASSERT(ue3 != ue1);
+
+	std::compare<graph::UndirectedEdge> u_cmp;
+	CPPUNIT_ASSERT_EQUAL(0, u_cmp(ue1, ue1_));
+	CPPUNIT_ASSERT_EQUAL(0, u_cmp(ue1, ue1));
+	CPPUNIT_ASSERT_EQUAL(0, u_cmp(ue1, ue1_s));
+	CPPUNIT_ASSERT_EQUAL(0, u_cmp(ue1_, ue1_s));
+	CPPUNIT_ASSERT(u_cmp(ue1, ue2) != 0);
+	CPPUNIT_ASSERT(u_cmp(ue1, ue3) != 0);
+	CPPUNIT_ASSERT(u_cmp(ue1, ue3_) != 0);
+}
+
+// testLessThan and testHash tests the UndirectedEdge for
+// the case when UndirectedEdge(n1, n2) == UndirectedEdge(n2, n1)
+// and thus they must compare exactly the same against other edges
+// and also have the same hash
+void GraphElementsTest::testLessThan()
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// UndirectedEdge
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue1_(n2, n1);
+	graph::UndirectedEdge ue2(n1, n3);
+	graph::UndirectedEdge ue3(n1, n3);
+	graph::UndirectedEdge ue4(n1, n4);
+	graph::UndirectedEdge ue5(n5, n1);
+
+	CPPUNIT_ASSERT_EQUAL(ue1 < ue1_, ue1_ < ue1_);
+	CPPUNIT_ASSERT_EQUAL(ue1 < ue2, ue1_ < ue2);
+	CPPUNIT_ASSERT_EQUAL(ue1 < ue3, ue1_ < ue3);
+	CPPUNIT_ASSERT_EQUAL(ue1 < ue4, ue1_ < ue4);
+	CPPUNIT_ASSERT_EQUAL(ue1 < ue5, ue1_ < ue5);
+}
+
+void GraphElementsTest::testHash()
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// UndirectedEdge
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue1_(n2, n1);
+	graph::UndirectedEdge ue2(n1, n3);
+	graph::UndirectedEdge ue2_(n3, n1);
+	graph::UndirectedEdge ue3(n2, n4);
+	graph::UndirectedEdge ue3_(n4, n2);
+
+	std::hash<graph::UndirectedEdge> hash_func;
+	CPPUNIT_ASSERT_EQUAL(hash_func(ue1), hash_func(ue1_));
+	CPPUNIT_ASSERT_EQUAL(hash_func(ue2), hash_func(ue2_));
+	CPPUNIT_ASSERT_EQUAL(hash_func(ue3), hash_func(ue3_));
+}
+
diff --git a/alib2data/test-src/graph/GraphElementsTest.h b/alib2data/test-src/graph/GraphElementsTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..31c4d1311d4ed6b7cbdc36ea63765f2cffa00413
--- /dev/null
+++ b/alib2data/test-src/graph/GraphElementsTest.h
@@ -0,0 +1,25 @@
+#ifndef GRAPH_ELEMENTS_TEST_H_
+#define GRAPH_ELEMENTS_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "graph/directed/DirectedGraph.h"
+#include "graph/undirected/UndirectedGraph.h"
+
+class GraphElementsTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphElementsTest);
+	CPPUNIT_TEST(testCopyConstruct);
+	CPPUNIT_TEST(testEqual);
+	CPPUNIT_TEST(testLessThan);
+	CPPUNIT_TEST(testHash);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testCopyConstruct();
+	void testEqual();
+	void testLessThan();
+	void testHash();
+};
+
+#endif	// GRAPH_ELEMENTS_TEST_H_
diff --git a/alib2data/test-src/graph/GraphTest.cpp b/alib2data/test-src/graph/GraphTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f39266d61acc9703e15bbef2ec3844989dd40cd3
--- /dev/null
+++ b/alib2data/test-src/graph/GraphTest.cpp
@@ -0,0 +1,851 @@
+#include "GraphTest.h"
+
+#include "sax/SaxParseInterface.h"
+#include "sax/SaxComposeInterface.h"
+
+#include "factory/XmlDataFactory.hpp"
+#include "factory/StringDataFactory.hpp"
+
+#define CPPUNIT_ASSERT_EQUAL_INT(a, b) CPPUNIT_ASSERT_EQUAL(a, (int)b)
+
+#define RUN_TEST(t) \
+	std::cout << "ADJACENCY_LIST" << std::endl; \
+	t(graph::REPRESENTATION::ADJACENCY_LIST); \
+	std::cout << "ADJACENCY_MATRIX" << std::endl; \
+	t(graph::REPRESENTATION::ADJACENCY_MATRIX)
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphTest);
+
+void GraphTest::testCopyConstruct()
+{
+	RUN_TEST(testCopyConstruct_impl);
+}
+
+void GraphTest::testEqual()
+{
+	RUN_TEST(testEqual_impl);
+}
+
+void GraphTest::testXMLParser()
+{
+	RUN_TEST(testXMLParser_impl);
+}
+
+void GraphTest::testStringParser()
+{
+	RUN_TEST(testStringParser_impl);
+}
+
+void GraphTest::testAddEdge()
+{
+	RUN_TEST(testAddEdge_impl);
+}
+
+void GraphTest::testRemoveEdge()
+{
+	RUN_TEST(testRemoveEdge_impl);
+}
+
+void GraphTest::testAddNode()
+{
+	RUN_TEST(testAddNode_impl);
+}
+
+void GraphTest::testRemoveNode()
+{
+	RUN_TEST(testRemoveNode_impl);
+}
+
+void GraphTest::testNeighborEdges()
+{
+	RUN_TEST(testNeighborEdges_impl);
+}
+
+void GraphTest::testHasEdge()
+{
+	RUN_TEST(testHasEdge_impl);
+}
+
+void GraphTest::testAddEdgeValue()
+{
+	RUN_TEST(testAddEdgeValue_impl);
+}
+
+void GraphTest::testHasNodeAndEdge()
+{
+	RUN_TEST(testHasNodeAndEdge_impl);
+}
+
+void GraphTest::testCopyConstruct_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addNode(n1);
+	dg.addNode(n2);
+	dg.addNode(n3);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+
+	graph::DirectedGraph dg2(dg);
+	CPPUNIT_ASSERT(dg == dg2);
+
+	graph::DirectedGraph dg3(std::move(dg));
+	CPPUNIT_ASSERT(dg2 == dg3);
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addNode(n1);
+	ug.addNode(n2);
+	ug.addNode(n3);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+
+	graph::UndirectedGraph ug2(ug);
+	CPPUNIT_ASSERT(ug == ug2);
+
+	graph::UndirectedGraph ug3(std::move(ug));
+	CPPUNIT_ASSERT(ug2 == ug3);
+}
+
+void GraphTest::testEqual_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addNode(n1);
+	dg.addNode(n2);
+	dg.addNode(n3);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+
+	graph::DirectedGraph dg2(representation);
+	dg2.addNode(n1);
+	dg2.addNode(n2);
+	dg2.addEdge(graph::DirectedEdge(n1, n2));
+
+	CPPUNIT_ASSERT(dg != dg2);
+
+	dg2.addNode(n3);
+	CPPUNIT_ASSERT(dg != dg2);
+
+	dg2.addEdge(graph::DirectedEdge(n1, n3));
+	CPPUNIT_ASSERT(dg == dg2);
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addNode(n1);
+	ug.addNode(n2);
+	ug.addNode(n3);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+
+	graph::UndirectedGraph ug2(representation);
+	ug2.addNode(n1);
+	ug2.addNode(n2);
+	ug2.addEdge(graph::UndirectedEdge(n1, n2));
+
+	CPPUNIT_ASSERT(ug != ug2);
+
+	ug2.addNode(n3);
+	CPPUNIT_ASSERT(ug != ug2);
+
+	ug2.addEdge(graph::UndirectedEdge(n1, n3));
+	CPPUNIT_ASSERT(ug == ug2);
+}
+
+void GraphTest::testXMLParser_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addNode(n1);
+	dg.addNode(n2);
+	dg.addNode(n3);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+
+	graph::Graph dg1(dg);
+	std::string tmp = alib::XmlDataFactory::toString(dg1);
+	graph::Graph dg2 = alib::XmlDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(dg1 == dg2);
+
+	dg.setEdgeValue(graph::DirectedEdge(n1, n2), 1);
+	dg.setEdgeValue(graph::DirectedEdge(n1, n3), 2);
+
+	graph::Graph dg1_2(dg);
+	tmp = alib::XmlDataFactory::toString(dg1_2);
+	graph::Graph dg2_2 = alib::XmlDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(dg1_2 == dg2_2);
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addNode(n1);
+	ug.addNode(n2);
+	ug.addNode(n3);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+
+	graph::Graph ug1(ug);
+	tmp = alib::XmlDataFactory::toString(ug1);
+	graph::Graph ug2 = alib::XmlDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(ug1 == ug2);
+
+	ug.setEdgeValue(graph::UndirectedEdge(n1, n2), 1);
+	ug.setEdgeValue(graph::UndirectedEdge(n1, n3), 2);
+
+	graph::Graph ug1_2(ug);
+	tmp = alib::XmlDataFactory::toString(ug1_2);
+	graph::Graph ug2_2 = alib::XmlDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(ug1_2 == ug2_2);
+}
+
+void GraphTest::testStringParser_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addNode(n1);
+	dg.addNode(n2);
+	dg.addNode(n3);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+
+	graph::Graph dg1(dg);
+	std::string tmp = alib::StringDataFactory::toString(dg1);
+	graph::Graph dg2 = alib::StringDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(dg1 == dg2);
+
+	dg.setEdgeValue(graph::DirectedEdge(n1, n2), 1);
+	dg.setEdgeValue(graph::DirectedEdge(n1, n3), 2);
+
+	graph::Graph dg1_2(dg);
+	tmp = alib::StringDataFactory::toString(dg1_2);
+	graph::Graph dg2_2 = alib::StringDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(dg1_2 == dg2_2);
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addNode(n1);
+	ug.addNode(n2);
+	ug.addNode(n3);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n3));
+
+	graph::Graph ug1(ug);
+	tmp = alib::StringDataFactory::toString(ug1);
+	graph::Graph ug2 = alib::StringDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(ug1 == ug2);
+
+	ug.setEdgeValue(graph::UndirectedEdge(n1, n2), 1);
+	ug.setEdgeValue(graph::UndirectedEdge(n1, n3), 2);
+
+	graph::Graph ug1_2(ug);
+	tmp = alib::StringDataFactory::toString(ug1_2);
+	graph::Graph ug2_2 = alib::StringDataFactory::fromString<graph::Graph>(tmp);
+	CPPUNIT_ASSERT(ug1_2 == ug2_2);
+}
+
+void GraphTest::testAddEdge_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addNode(n1);
+	dg.addNode(n2);
+	dg.addNode(n3);
+	dg.addNode(n4);
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n1, n2).empty());
+
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n2, n1).empty());
+
+	// Multi-edge can only be added with non-empty label
+	CPPUNIT_ASSERT_EQUAL(false, dg.addEdge(graph::DirectedEdge(n1, n2)));
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n2, n1).empty());
+
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n2, n3).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n3, n2).empty());
+
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL_INT(3, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n4, n3).empty());
+
+	// Multi-edge can only be added with non-empty label
+	CPPUNIT_ASSERT_EQUAL(false, dg.addEdge(graph::DirectedEdge(n3, n4)));
+	CPPUNIT_ASSERT_EQUAL_INT(3, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n4, n3).empty());
+
+	dg.addEdge(graph::DirectedEdge(n3, n4, "multi-edge"));
+	CPPUNIT_ASSERT_EQUAL_INT(4, dg.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n4, n3).empty());
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addNode(n1);
+	ug.addNode(n2);
+	ug.addNode(n3);
+	ug.addNode(n4);
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n1, n2).empty());
+
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n2, n1).size());
+
+	// Multi-edge can only be added with non-empty label
+	CPPUNIT_ASSERT_EQUAL(false, ug.addEdge(graph::UndirectedEdge(n1, n2)));
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n2, n1).size());
+
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n2, n3).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n3, n2).size());
+
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL_INT(3, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n4, n3).size());
+
+	// Multi-edge can only be added with non-empty label
+	CPPUNIT_ASSERT_EQUAL(false, ug.addEdge(graph::UndirectedEdge(n3, n4)));
+	CPPUNIT_ASSERT_EQUAL_INT(3, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n4, n3).size());
+
+	ug.addEdge(graph::UndirectedEdge(n3, n4, "multi-edge"));
+	CPPUNIT_ASSERT_EQUAL_INT(4, ug.getEdges().size());
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.findEdges(n4, n3).size());
+}
+
+void GraphTest::testRemoveEdge_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n1, n3));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, dg.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(6, dg.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.neighbors(n1).size());
+	dg.removeEdge(graph::DirectedEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.neighbors(n1).size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.neighbors(n1).size());
+	dg.removeEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.neighbors(n1).size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.neighbors(n2).size());
+	dg.removeEdge(graph::DirectedEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.findEdges(n2, n3).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.neighbors(n2).empty());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.neighbors(n3).size());
+	dg.removeEdge(graph::DirectedEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.neighbors(n3).empty());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.neighbors(n4).size());
+	dg.removeEdge(graph::DirectedEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.findEdges(n4, n1).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.neighbors(n4).empty());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.neighbors(n1).size());
+	dg.removeEdge(graph::DirectedEdge(n1, n3));
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.findEdges(n1, n3).size());
+	CPPUNIT_ASSERT_EQUAL(true, dg.neighbors(n1).empty());
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, dg.getNodes().empty());
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	ug.addEdge(graph::UndirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, ug.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(5, ug.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.neighbors(n1).size());
+	ug.removeEdge(graph::UndirectedEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.neighbors(n1).size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.neighbors(n1).size());
+	ug.removeEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	CPPUNIT_ASSERT_EQUAL_INT(0, ug.findEdges(n1, n2).size());
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.neighbors(n1).size());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.neighbors(n2).size());
+	ug.removeEdge(graph::UndirectedEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL_INT(0, ug.findEdges(n2, n3).size());
+	CPPUNIT_ASSERT(ug.neighbors(n2).empty());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.neighbors(n3).size());
+	ug.removeEdge(graph::UndirectedEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL_INT(0, ug.findEdges(n3, n4).size());
+	CPPUNIT_ASSERT(ug.neighbors(n3).empty());
+
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.neighbors(n4).size());
+	ug.removeEdge(graph::UndirectedEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL_INT(0, ug.findEdges(n4, n1).size());
+	CPPUNIT_ASSERT_EQUAL(true, ug.neighbors(n4).empty());
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, ug.getNodes().empty());
+}
+
+void GraphTest::testAddNode_impl(graph::REPRESENTATION representation)
+{
+	// Directed
+	graph::DirectedGraph dg(representation);
+
+	CPPUNIT_ASSERT_EQUAL_INT(0, dg.getNodes().size());
+	dg.addNode(graph::Node("n1"));
+	CPPUNIT_ASSERT_EQUAL_INT(1, dg.getNodes().size());
+	dg.addNode(graph::Node("n2"));
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.getNodes().size());
+
+	CPPUNIT_ASSERT_EQUAL(false, dg.addNode(graph::Node("n2")));
+	CPPUNIT_ASSERT_EQUAL_INT(2, dg.getNodes().size());
+
+	dg.addNode(graph::Node("n3"));
+	CPPUNIT_ASSERT_EQUAL_INT(3, dg.getNodes().size());
+	dg.addNode(graph::Node("n4"));
+	CPPUNIT_ASSERT_EQUAL_INT(4, dg.getNodes().size());
+	dg.addNode(graph::Node("n5"));
+	CPPUNIT_ASSERT_EQUAL_INT(5, dg.getNodes().size());
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+
+	CPPUNIT_ASSERT_EQUAL_INT(0, ug.getNodes().size());
+	ug.addNode(graph::Node("n1"));
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.getNodes().size());
+	ug.addNode(graph::Node("n2"));
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.getNodes().size());
+
+	CPPUNIT_ASSERT_EQUAL(false, ug.addNode(graph::Node("n2")));
+	CPPUNIT_ASSERT_EQUAL_INT(2, ug.getNodes().size());
+
+	ug.addNode(graph::Node("n3"));
+	CPPUNIT_ASSERT_EQUAL_INT(3, ug.getNodes().size());
+	ug.addNode(graph::Node("n4"));
+	CPPUNIT_ASSERT_EQUAL_INT(4, ug.getNodes().size());
+	ug.addNode(graph::Node("n5"));
+	CPPUNIT_ASSERT_EQUAL_INT(5, ug.getNodes().size());
+}
+
+void GraphTest::testRemoveNode_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, dg.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(5, dg.getEdges().size());
+
+	dg.removeNode(n1);
+	CPPUNIT_ASSERT_EQUAL(true, dg.neighbors(n4).empty());
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n1, n2).empty());
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n4, n1).empty());
+
+	dg.removeNode(n2);
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n2, n3).empty());
+
+	dg.removeNode(n3);
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n3, n4).empty());
+
+	dg.removeNode(n4);
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL(true, dg.findEdges(n4, n1).empty());
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, dg.getNodes().empty());
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	ug.addEdge(graph::UndirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, ug.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(5, ug.getEdges().size());
+
+	ug.removeNode(n1);
+	CPPUNIT_ASSERT_EQUAL_INT(1, ug.neighbors(n4).size());
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n1, n2).empty());
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n4, n1).empty());
+
+	ug.removeNode(n2);
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n2, n3).empty());
+
+	ug.removeNode(n3);
+	CPPUNIT_ASSERT_EQUAL(true, ug.neighbors(n4).empty());
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n3, n4).empty());
+
+	ug.removeNode(n4);
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.findEdges(n4, n1).empty());
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.getEdges().empty());
+	CPPUNIT_ASSERT_EQUAL(true, ug.getNodes().empty());
+}
+
+void GraphTest::testNeighborEdges_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedEdge de1(n1, n2);
+	graph::DirectedEdge de2(n1, n2, "multi-edge");
+	graph::DirectedEdge de3(n1, n3);
+	graph::DirectedEdge de4(n2, n3);
+	graph::DirectedEdge de5(n3, n4);
+	graph::DirectedEdge de6(n4, n1);
+
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(de1);
+	dg.addEdge(de2);
+	dg.addEdge(de3);
+	dg.addEdge(de4);
+	dg.addEdge(de5);
+	dg.addEdge(de6);
+
+	std::set<graph::DirectedEdge> dexpected = { de1, de2, de3 };
+	CPPUNIT_ASSERT_EQUAL(dexpected, dg.neighborEdges(n1));
+
+	dexpected = { de4 };
+	CPPUNIT_ASSERT_EQUAL(dexpected, dg.neighborEdges(n2));
+
+	dexpected = { de5 };
+	CPPUNIT_ASSERT_EQUAL(dexpected, dg.neighborEdges(n3));
+
+	dexpected = { de6 };
+	CPPUNIT_ASSERT_EQUAL(dexpected, dg.neighborEdges(n4));
+
+	// Undirected
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue2(n1, n2, "multi-edge");
+	graph::UndirectedEdge ue3(n1, n3);
+	graph::UndirectedEdge ue4(n2, n3);
+	graph::UndirectedEdge ue5(n3, n4);
+	graph::UndirectedEdge ue6(n4, n1);
+
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(ue1);
+	ug.addEdge(ue2);
+	ug.addEdge(ue3);
+	ug.addEdge(ue4);
+	ug.addEdge(ue5);
+	ug.addEdge(ue6);
+
+	std::set<graph::UndirectedEdge> uexpected = { ue1, ue2, ue3, ue6 };
+	CPPUNIT_ASSERT_EQUAL(uexpected, ug.neighborEdges(n1));
+
+	uexpected = { ue1, ue2, ue4 };
+	CPPUNIT_ASSERT_EQUAL(uexpected, ug.neighborEdges(n2));
+
+	uexpected = { ue3, ue4, ue5 };
+	CPPUNIT_ASSERT_EQUAL(uexpected, ug.neighborEdges(n3));
+
+	uexpected = { ue5, ue6 };
+	CPPUNIT_ASSERT_EQUAL(uexpected, ug.neighborEdges(n4));
+}
+
+void GraphTest::testHasEdge_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+
+	// Directed
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(graph::DirectedEdge(n1, n2));
+	dg.addEdge(graph::DirectedEdge(n1, n2, "multi-edge"));
+	dg.addEdge(graph::DirectedEdge(n2, n3));
+	dg.addEdge(graph::DirectedEdge(n3, n4));
+	dg.addEdge(graph::DirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, dg.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(5, dg.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n1, n3));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n2, n1));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n4, n3));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(n1, n4));
+
+	// Undirected
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(graph::UndirectedEdge(n1, n2));
+	ug.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge"));
+	ug.addEdge(graph::UndirectedEdge(n2, n3));
+	ug.addEdge(graph::UndirectedEdge(n3, n4));
+	ug.addEdge(graph::UndirectedEdge(n4, n1));
+
+	CPPUNIT_ASSERT_EQUAL_INT(4, ug.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(5, ug.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n1, n2));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n2, n1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n2, n3));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n3, n2));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n3, n4));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n4, n3));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n4, n1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(n1, n4));
+
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n1, n3));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n3, n1));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n2, n4));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(n4, n2));
+}
+
+void GraphTest::testAddEdgeValue_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	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::DirectedEdge de1(n1, n2);
+	graph::DirectedEdge de2(n1, n3);
+	graph::DirectedEdge de3(n1, n4);
+	graph::DirectedEdge de4(n1, n5);
+	graph::DirectedEdge de5(n2, n5);
+	graph::DirectedEdge de6(n5, n6);
+
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(de1, 2);
+	dg.addEdge(de2, 2);
+	dg.addEdge(de3, 1);
+	dg.addEdge(de4, 6);
+	dg.addEdge(de5, 3);
+	dg.addEdge(de6, 2);
+
+	CPPUNIT_ASSERT_EQUAL_INT(6, dg.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(6, dg.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(2, dg.getEdgeValue(de1));
+	CPPUNIT_ASSERT_EQUAL(2, dg.getEdgeValue(de2));
+	CPPUNIT_ASSERT_EQUAL(1, dg.getEdgeValue(de3));
+	CPPUNIT_ASSERT_EQUAL(6, dg.getEdgeValue(de4));
+	CPPUNIT_ASSERT_EQUAL(3, dg.getEdgeValue(de5));
+	CPPUNIT_ASSERT_EQUAL(2, dg.getEdgeValue(de6));
+
+	// Undirected
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue2(n1, n3);
+	graph::UndirectedEdge ue3(n1, n4);
+	graph::UndirectedEdge ue4(n1, n5);
+	graph::UndirectedEdge ue5(n2, n5);
+	graph::UndirectedEdge ue6(n5, n6);
+
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(ue1, 2);
+	ug.addEdge(ue2, 2);
+	ug.addEdge(ue3, 1);
+	ug.addEdge(ue4, 6);
+	ug.addEdge(ue5, 3);
+	ug.addEdge(ue6, 2);
+
+	CPPUNIT_ASSERT_EQUAL_INT(6, ug.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(6, ug.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(2, ug.getEdgeValue(ue1));
+	CPPUNIT_ASSERT_EQUAL(2, ug.getEdgeValue(ue2));
+	CPPUNIT_ASSERT_EQUAL(1, ug.getEdgeValue(ue3));
+	CPPUNIT_ASSERT_EQUAL(6, ug.getEdgeValue(ue4));
+	CPPUNIT_ASSERT_EQUAL(3, ug.getEdgeValue(ue5));
+	CPPUNIT_ASSERT_EQUAL(2, ug.getEdgeValue(ue6));
+}
+
+void GraphTest::testHasNodeAndEdge_impl(graph::REPRESENTATION representation)
+{
+	// Common
+	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::DirectedEdge de1(n1, n2);
+	graph::DirectedEdge de2(n1, n3);
+	graph::DirectedEdge de3(n1, n4);
+	graph::DirectedEdge de4(n1, n5);
+	graph::DirectedEdge de5(n2, n5);
+	graph::DirectedEdge de6(n5, n6);
+
+	graph::DirectedGraph dg(representation);
+	dg.addEdge(de1, 2);
+	dg.addEdge(de2, 2);
+	dg.addEdge(de3, 1);
+	dg.addEdge(de4, 6);
+	dg.addEdge(de5, 3);
+	dg.addEdge(de6, 2);
+
+	CPPUNIT_ASSERT_EQUAL_INT(6, dg.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(6, dg.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n1));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n2));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n3));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n4));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n5));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasNode(n6));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasNode(graph::Node("n7")));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasNode(graph::Node("n8")));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasNode(graph::Node("n9")));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasNode(graph::Node("n10")));
+
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de1));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de2));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de3));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de4));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de5));
+	CPPUNIT_ASSERT_EQUAL(true, dg.hasEdge(de6));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(graph::DirectedEdge(n1, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(graph::DirectedEdge(n2, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(graph::DirectedEdge(n3, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(graph::DirectedEdge(n4, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, dg.hasEdge(graph::DirectedEdge(n1, n2, "a")));
+
+	// Undirected
+	graph::UndirectedEdge ue1(n1, n2);
+	graph::UndirectedEdge ue2(n1, n3);
+	graph::UndirectedEdge ue3(n1, n4);
+	graph::UndirectedEdge ue4(n1, n5);
+	graph::UndirectedEdge ue5(n2, n5);
+	graph::UndirectedEdge ue6(n5, n6);
+
+	graph::UndirectedGraph ug(representation);
+	ug.addEdge(ue1, 2);
+	ug.addEdge(ue2, 2);
+	ug.addEdge(ue3, 1);
+	ug.addEdge(ue4, 6);
+	ug.addEdge(ue5, 3);
+	ug.addEdge(ue6, 2);
+
+	CPPUNIT_ASSERT_EQUAL_INT(6, ug.getNodes().size());
+	CPPUNIT_ASSERT_EQUAL_INT(6, ug.getEdges().size());
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n2));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n3));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n4));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n5));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasNode(n6));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasNode(graph::Node("n7")));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasNode(graph::Node("n8")));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasNode(graph::Node("n9")));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasNode(graph::Node("n10")));
+
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue1));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue2));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue3));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue4));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue5));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(ue6));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n2, n1)));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n3, n1)));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n4, n1)));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n5, n1)));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n5, n2)));
+	CPPUNIT_ASSERT_EQUAL(true, ug.hasEdge(graph::UndirectedEdge(n6, n5)));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(graph::UndirectedEdge(n1, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(graph::UndirectedEdge(n2, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(graph::UndirectedEdge(n3, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(graph::UndirectedEdge(n4, n6)));
+	CPPUNIT_ASSERT_EQUAL(false, ug.hasEdge(graph::UndirectedEdge(n1, n2, "a")));
+}
+
diff --git a/alib2data/test-src/graph/GraphTest.h b/alib2data/test-src/graph/GraphTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ee02ff14f9947435502431199e8657f11d757b8
--- /dev/null
+++ b/alib2data/test-src/graph/GraphTest.h
@@ -0,0 +1,56 @@
+#ifndef GRAPH_TEST_H_
+#define GRAPH_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "graph/directed/DirectedGraph.h"
+#include "graph/undirected/UndirectedGraph.h"
+
+class GraphTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphTest);
+	CPPUNIT_TEST(testCopyConstruct);
+	CPPUNIT_TEST(testEqual);
+	CPPUNIT_TEST(testXMLParser);
+	CPPUNIT_TEST(testStringParser);
+	CPPUNIT_TEST(testAddEdge);
+	CPPUNIT_TEST(testRemoveEdge);
+	CPPUNIT_TEST(testAddNode);
+	CPPUNIT_TEST(testRemoveNode);
+	CPPUNIT_TEST(testNeighborEdges);
+	CPPUNIT_TEST(testHasEdge);
+	CPPUNIT_TEST(testAddEdgeValue);
+	CPPUNIT_TEST(testHasNodeAndEdge);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testCopyConstruct();
+	void testEqual();
+	void testXMLParser();
+	void testStringParser();
+
+	void testAddEdge();
+	void testRemoveEdge();
+	void testAddNode();
+	void testRemoveNode();
+	void testNeighborEdges();
+	void testHasEdge();
+	void testAddEdgeValue();
+	void testHasNodeAndEdge();
+
+	void testCopyConstruct_impl(graph::REPRESENTATION representation);
+	void testEqual_impl(graph::REPRESENTATION representation);
+	void testXMLParser_impl(graph::REPRESENTATION representation);
+	void testStringParser_impl(graph::REPRESENTATION representation);
+
+	void testAddEdge_impl(graph::REPRESENTATION representation);
+	void testRemoveEdge_impl(graph::REPRESENTATION representation);
+	void testAddNode_impl(graph::REPRESENTATION representation);
+	void testRemoveNode_impl(graph::REPRESENTATION representation);
+	void testNeighborEdges_impl(graph::REPRESENTATION representation);
+	void testHasEdge_impl(graph::REPRESENTATION representation);
+	void testAddEdgeValue_impl(graph::REPRESENTATION representation);
+	void testHasNodeAndEdge_impl(graph::REPRESENTATION representation);
+};
+
+#endif	// GRAPH_TEST_H_