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_