From f98a208478088ae1b5a853708f4504da9822bfbf Mon Sep 17 00:00:00 2001 From: brghash Date: Sat, 6 Sep 2025 17:54:51 +0100 Subject: [PATCH 01/11] Implemented type trait to permit compilation of non-extractable types such as pointers: Fixes #339. Added /bigobj for MSVC compiler. --- CMakeLists.txt | 4 +++- include/CXXGraph/Graph/Graph_decl.h | 9 +++++++++ include/CXXGraph/Graph/IO/InputOperation_impl.hpp | 11 ++++++++--- test/GraphTest.cpp | 12 ++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a1b002585..0bbbe98b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,9 @@ if(DEBUG) ) endif(DEBUG) -if (NOT MSVC) +if (MSVC) + add_compile_options(/bigobj) +else () option(SANITIZE "Enable Sanitize" OFF) if(SANITIZE) add_compile_options( diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h index 8149d503f..7c1bdc6d3 100644 --- a/include/CXXGraph/Graph/Graph_decl.h +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -111,6 +111,15 @@ class Graph { std::unordered_map &nodeFeatMap, std::unordered_map &edgeWeightMap); + // Type trait used to compile allow compilation when T is not extractable + template + struct is_istream_extractable : std::false_type {}; + + template + struct is_istream_extractable< + T, std::void_t() >> + std::declval())>> : std::true_type {}; + #ifdef WITH_COMPRESSION int compressFile(const std::string &inputFile, const std::string &outputFile) const; diff --git a/include/CXXGraph/Graph/IO/InputOperation_impl.hpp b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp index 2088906ce..1dde2c70c 100644 --- a/include/CXXGraph/Graph/IO/InputOperation_impl.hpp +++ b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp @@ -355,9 +355,14 @@ void Graph::readGraphFromStream(std::istream &iGraph, if (readNodeFeat) { std::string nodeId; - T nodeFeat; - while (iNodeFeat >> nodeId >> nodeFeat) { - nodeFeatMap[nodeId] = nodeFeat; + if constexpr (is_istream_extractable::value) { + T nodeFeat; + while (iNodeFeat >> nodeId >> nodeFeat) { + nodeFeatMap[nodeId] = nodeFeat; + } + } else { + std::cout << "Warning: Cannot read node features for type T — operator>> " + "not supported.\n"; } } diff --git a/test/GraphTest.cpp b/test/GraphTest.cpp index 2591649ea..e5cf49296 100644 --- a/test/GraphTest.cpp +++ b/test/GraphTest.cpp @@ -1457,3 +1457,15 @@ TEST(GraphTest, set_data_isolated) { ASSERT_EQ(nodeIt->getData(), data_values[nodeIt->getUserId()]); } } + +TEST(GraphTest, type_trait_not_extractable) { + CXXGraph::Node node1("1", nullptr); + CXXGraph::Node node2("2", nullptr); + CXXGraph::Edge edge(1, node1, node2); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge)); + + CXXGraph::Graph graph(edgeSet); + ASSERT_EQ(graph.getEdgeSet(), edgeSet); +} \ No newline at end of file From 84adec402befbac9632df656f798dcfcd87ce182 Mon Sep 17 00:00:00 2001 From: brghash Date: Sun, 7 Sep 2025 17:10:51 +0100 Subject: [PATCH 02/11] Harmonised test naming scheme --- test/BoruvkaTest.cpp | 8 +++--- test/DijkstraTest.cpp | 57 +++++++++++++++++++++++-------------------- test/GraphTest.cpp | 36 +++++++++++++-------------- test/NodeTest.cpp | 8 +++--- 4 files changed, 56 insertions(+), 53 deletions(-) diff --git a/test/BoruvkaTest.cpp b/test/BoruvkaTest.cpp index 15e37f352..661eeb3a0 100644 --- a/test/BoruvkaTest.cpp +++ b/test/BoruvkaTest.cpp @@ -185,7 +185,7 @@ TEST(BoruvkaTest, test_4) { // example taken from // https://www.geeksforgeeks.org/prims-mst-for-adjacency-list-representation-greedy-algo-6/ -TEST(BoruvkaDeterministicTest, test_1) { +TEST(BoruvkaTest, deterministic_test_1) { CXXGraph::Node node0("0", 0); CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -238,7 +238,7 @@ TEST(BoruvkaDeterministicTest, test_1) { // example taken from // https://www.gatevidyalay.com/prims-algorithm-prim-algorithm-example/ -TEST(BoruvkaDeterministicTest, test_2) { +TEST(BoruvkaTest, deterministic_test_2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -278,7 +278,7 @@ TEST(BoruvkaDeterministicTest, test_2) { // example taken from // https://www.gatevidyalay.com/prims-algorithm-prim-algorithm-example/ -TEST(BoruvkaDeterministicTest, test_3) { +TEST(BoruvkaTest, deterministic_test_3) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -323,7 +323,7 @@ TEST(BoruvkaDeterministicTest, test_3) { } // test for directed and no weighted edge errors -TEST(BoruvkaDeterministicTest, test_4) { +TEST(BoruvkaTest, deterministic_test_4) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); diff --git a/test/DijkstraTest.cpp b/test/DijkstraTest.cpp index 326a771d4..afacd39d3 100644 --- a/test/DijkstraTest.cpp +++ b/test/DijkstraTest.cpp @@ -12,7 +12,7 @@ using shared = std::shared_ptr; using std::make_shared; using std::make_unique; -TEST(DijkstraTest, correct_example_1) { +TEST(DijkstraTest, test_1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -45,7 +45,7 @@ TEST(DijkstraTest, correct_example_1) { } } -TEST(DijkstraTest, correct_example_2) { +TEST(DijkstraTest, test_2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -77,7 +77,7 @@ TEST(DijkstraTest, correct_example_2) { } } -TEST(DijkstraTest, correct_example_3) { +TEST(DijkstraTest, test_3) { // Example from // https://www.analyticssteps.com/blogs/dijkstras-algorithm-shortest-path-algorithm CXXGraph::Node nodeA("A", 1); @@ -161,7 +161,7 @@ TEST(DijkstraTest, correct_example_3) { } } -TEST(DijkstraTest, correct_example_4) { +TEST(DijkstraTest, test_4) { // Example from // https://www.freecodecamp.org/news/dijkstras-shortest-path-algorithm-visual-introduction/ CXXGraph::Node node0("0", 1); @@ -283,7 +283,7 @@ TEST(DijkstraTest, correct_example_4) { } } -TEST(DijkstraTest, correct_example_5) { +TEST(DijkstraTest, test_5) { // Example from https://es.wikipedia.org/wiki/Algoritmo_de_Dijkstra CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 1); @@ -436,9 +436,7 @@ TEST(DijkstraTest, target_not_connected_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -// Deterministic versions of algorithm: - -TEST(DijkstraDeterministicTest, correct_example_1) { +TEST(DijkstraTest, deterministic_test_1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -471,7 +469,7 @@ TEST(DijkstraDeterministicTest, correct_example_1) { } } -TEST(DijkstraDeterministicTest, correct_example_2) { +TEST(DijkstraTest, deterministic_test_2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -503,7 +501,7 @@ TEST(DijkstraDeterministicTest, correct_example_2) { } } -TEST(DijkstraDeterministicTest, correct_example_3) { +TEST(DijkstraTest, deterministic_test_3) { // Example from // https://www.analyticssteps.com/blogs/dijkstras-algorithm-shortest-path-algorithm CXXGraph::Node nodeA("A", 1); @@ -587,7 +585,7 @@ TEST(DijkstraDeterministicTest, correct_example_3) { } } -TEST(DijkstraDeterministicTest, correct_example_4) { +TEST(DijkstraTest, deterministic_test_4) { // Example from // https://www.freecodecamp.org/news/dijkstras-shortest-path-algorithm-visual-introduction/ CXXGraph::Node node0("0", 1); @@ -709,7 +707,7 @@ TEST(DijkstraDeterministicTest, correct_example_4) { } } -TEST(DijkstraDeterministicTest, correct_example_5) { +TEST(DijkstraTest, deterministic_test_5) { // Example from https://es.wikipedia.org/wiki/Algoritmo_de_Dijkstra CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 1); @@ -757,7 +755,7 @@ TEST(DijkstraDeterministicTest, correct_example_5) { } } -TEST(DijkstraDeterministicTest, non_weigthed_node_test) { +TEST(DijkstraTest, deterministic_non_weigthed_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -780,7 +778,7 @@ TEST(DijkstraDeterministicTest, non_weigthed_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministicTest, negative_weigthed_node_test) { +TEST(DijkstraTest, deterministic_negative_weigthed_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -803,7 +801,7 @@ TEST(DijkstraDeterministicTest, negative_weigthed_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministicTest, unreachable_node_test) { +TEST(DijkstraTest, deterministic_unreachable_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -822,7 +820,7 @@ TEST(DijkstraDeterministicTest, unreachable_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministicTest, source_not_connected_test) { +TEST(DijkstraTest, deterministic_source_not_connected_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -842,7 +840,7 @@ TEST(DijkstraDeterministicTest, source_not_connected_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministicTest, target_not_connected_test) { +TEST(DijkstraTest, deterministic_target_not_connected_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -862,7 +860,7 @@ TEST(DijkstraDeterministicTest, target_not_connected_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministic2Test, correct_example_1) { +TEST(DijkstraTest, deterministic2_test_1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -895,7 +893,7 @@ TEST(DijkstraDeterministic2Test, correct_example_1) { } } -TEST(DijkstraDeterministic2Test, correct_example_2) { +TEST(DijkstraTest, deterministic2_test_2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -927,7 +925,7 @@ TEST(DijkstraDeterministic2Test, correct_example_2) { } } -TEST(DijkstraDeterministic2Test, correct_example_3) { +TEST(DijkstraTest, deterministic2_test_3) { // Example from // https://www.analyticssteps.com/blogs/dijkstras-algorithm-shortest-path-algorithm CXXGraph::Node nodeA("A", 1); @@ -1011,7 +1009,7 @@ TEST(DijkstraDeterministic2Test, correct_example_3) { } } -TEST(DijkstraDeterministic2Test, correct_example_4) { +TEST(DijkstraTest, deterministic2_test_4) { // Example from // https://www.freecodecamp.org/news/dijkstras-shortest-path-algorithm-visual-introduction/ CXXGraph::Node node0("0", 1); @@ -1133,7 +1131,7 @@ TEST(DijkstraDeterministic2Test, correct_example_4) { } } -TEST(DijkstraDeterministic2Test, correct_example_5) { +TEST(DijkstraTest, deterministic2_test_5) { // Example from https://es.wikipedia.org/wiki/Algoritmo_de_Dijkstra CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 1); @@ -1181,7 +1179,7 @@ TEST(DijkstraDeterministic2Test, correct_example_5) { } } -TEST(DijkstraDeterministic2Test, non_weigthed_node_test) { +TEST(DijkstraTest, deterministic2_non_weigthed_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1204,7 +1202,7 @@ TEST(DijkstraDeterministic2Test, non_weigthed_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministic2Test, negative_weigthed_node_test) { +TEST(DijkstraTest, deterministic2_negative_weigthed_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1227,7 +1225,7 @@ TEST(DijkstraDeterministic2Test, negative_weigthed_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministic2Test, unreachable_node_test) { +TEST(DijkstraTest, deterministic2_unreachable_node_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1246,7 +1244,7 @@ TEST(DijkstraDeterministic2Test, unreachable_node_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministic2Test, source_not_connected_test) { +TEST(DijkstraTest, deterministic2_source_not_connected_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1266,7 +1264,7 @@ TEST(DijkstraDeterministic2Test, source_not_connected_test) { ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } -TEST(DijkstraDeterministic2Test, target_not_connected_test) { +TEST(DijkstraTest, deterministic2_target_not_connected_test) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1285,3 +1283,8 @@ TEST(DijkstraDeterministic2Test, target_not_connected_test) { ASSERT_EQ(res.errorMessage, CXXGraph::ERR_TARGET_NODE_NOT_IN_GRAPH); ASSERT_EQ(res.result, CXXGraph::INF_DOUBLE); } + + + + + diff --git a/test/GraphTest.cpp b/test/GraphTest.cpp index e5cf49296..f5633e86f 100644 --- a/test/GraphTest.cpp +++ b/test/GraphTest.cpp @@ -723,7 +723,7 @@ TEST(GraphTest, set_data) { } } -TEST(GraphTest, test_outNodes) { +TEST(GraphTest, outNodes) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -782,7 +782,7 @@ TEST(GraphTest, test_outNodes) { } // Test the overload that takes shared_ptr as input -TEST(GraphTest, test_outNodes_shared) { +TEST(GraphTest, outNodes_shared) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -832,7 +832,7 @@ TEST(GraphTest, test_outNodes_shared) { } } -TEST(GraphTest, test_inOutNodes) { +TEST(GraphTest, inOutNodes) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -879,7 +879,7 @@ TEST(GraphTest, test_inOutNodes) { } // Test the overload that takes shared_ptr as input -TEST(GraphTest, test_inOutNodes_shared) { +TEST(GraphTest, inOutNodes_shared) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -919,7 +919,7 @@ TEST(GraphTest, test_inOutNodes_shared) { } } -TEST(InOutNodesTest, test_outEdges) { +TEST(GraphTest, outEdges) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -968,7 +968,7 @@ TEST(InOutNodesTest, test_outEdges) { } } -TEST(InOutNodesTest, test_outEdges_shared) { +TEST(GraphTest, outEdges_shared) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1024,7 +1024,7 @@ TEST(InOutNodesTest, test_outEdges_shared) { ASSERT_EQ(edges.size(), 0); } -TEST(InOutNodesTest, test_inOutEdges) { +TEST(GraphTest, inOutEdges) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1068,7 +1068,7 @@ TEST(InOutNodesTest, test_inOutEdges) { } } -TEST(InOutNodesTest, test_inOutEdges_shared) { +TEST(GraphTest, inOutEdges_shared) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1121,7 +1121,7 @@ TEST(InOutNodesTest, test_inOutEdges_shared) { ASSERT_EQ(edges.size(), 0); } -TEST(ReverseDirectedGraphTest, test_function) { +TEST(GraphTest, reverse_directed) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1152,7 +1152,7 @@ TEST(ReverseDirectedGraphTest, test_function) { } } -TEST(ReverseDirectedGraphTest, test_exception) { +TEST(GraphTest, reverse_directed_exception) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1174,7 +1174,7 @@ TEST(ReverseDirectedGraphTest, test_exception) { ASSERT_THROW(mixedGraph.reverseDirectedGraph(), std::runtime_error); } -TEST(IsolatedNodeGraphTest, Test_AddNode1) { +TEST(GraphTest, isolated_node_AddNode1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1197,7 +1197,7 @@ TEST(IsolatedNodeGraphTest, Test_AddNode1) { ASSERT_EQ(graph.getEdgeSet().size(), 3); } -TEST(IsolatedNodeGraphTest, Test_AddNode2) { +TEST(GraphTest, isolated_node_AddNode2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1220,7 +1220,7 @@ TEST(IsolatedNodeGraphTest, Test_AddNode2) { ASSERT_EQ(graph.getEdgeSet().size(), 3); } -TEST(IsolatedNodeGraphTest, Test_AddNodes1) { +TEST(GraphTest, isolated_node_AddNodes1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1246,7 +1246,7 @@ TEST(IsolatedNodeGraphTest, Test_AddNodes1) { ASSERT_EQ(graph.getEdgeSet().size(), 3); } -TEST(IsolatedNodeGraphTest, Test_AddNodes2) { +TEST(GraphTest, isolated_node_AddNodes2) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1270,7 +1270,7 @@ TEST(IsolatedNodeGraphTest, Test_AddNodes2) { ASSERT_EQ(graph.getEdgeSet().size(), 3); } -TEST(TestRemoveNode, Test_isolatedNode) { +TEST(GraphTest, remove_node_isolatedNode) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1305,7 +1305,7 @@ TEST(TestRemoveNode, Test_isolatedNode) { ASSERT_EQ(graph.getEdgeSet().size(), 3); } -TEST(TestRemoveNode, Test_connectedNode) { +TEST(GraphTest, remove_node_connectedNode) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); @@ -1340,7 +1340,7 @@ TEST(TestRemoveNode, Test_connectedNode) { ASSERT_EQ(graph.getEdgeSet().size(), 1); } -TEST(TestRemoveNode, Test_removeInvalidNode) { +TEST(GraphTest, remove_node_removeInvalidNode) { /** Test to call the remove_node function on a node that was never added. In * this case getNode will return an optional that is nullptr*/ // Create a graph with 3 nodes and 3 edges. @@ -1384,7 +1384,7 @@ TEST(TestRemoveNode, Test_removeInvalidNode) { ASSERT_EQ(graph.getEdgeSet().size(), 2); } -TEST(TestGetNode, Test_1) { +TEST(GraphTest, get_node_1) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); CXXGraph::Node node3("3", 3); diff --git a/test/NodeTest.cpp b/test/NodeTest.cpp index 033acb1cd..8da433469 100644 --- a/test/NodeTest.cpp +++ b/test/NodeTest.cpp @@ -32,7 +32,7 @@ std::ostream &operator<<(std::ostream &os, const testStruct &TestStruct) { return os; } -TEST(StringNodeTest, StringConstructor) { +TEST(NodeTest, StringConstructor) { // Testing constructor and getters with different types of data int intTest = 42; float floatTest = 3.14f; @@ -74,7 +74,7 @@ TEST(StringNodeTest, StringConstructor) { ASSERT_EQ(structNode.getData(), structTest); } -TEST(StringNodeTest, StringeqOperator) { +TEST(NodeTest, StringeqOperator) { // Testing equal operator with different types of data int int1 = 2; int int2 = 2; @@ -102,7 +102,7 @@ TEST(StringNodeTest, StringeqOperator) { #include -TEST(StringNodeTest, StringltOperator) { +TEST(NodeTest, StringltOperator) { // Testing Less Than operator with different types of data int int1 = 2; int int2 = 2; @@ -126,7 +126,7 @@ TEST(StringNodeTest, StringltOperator) { ASSERT_FALSE(nodeDouble1 < nodeDouble2); // Same id different data } -TEST(StringNodeTest, StringprintOperator) { +TEST(NodeTest, StringprintOperator) { // Testing << operator with different types of data std::string nodesId = "1"; int testInt = 2; From 3f9eed551d9f8244a802fc71bae06640b364e96b Mon Sep 17 00:00:00 2001 From: brghash Date: Sun, 7 Sep 2025 17:20:19 +0100 Subject: [PATCH 03/11] Added commented out test in ConectivityTest --- test/ConnectivityTest.cpp | 98 ++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/test/ConnectivityTest.cpp b/test/ConnectivityTest.cpp index 90c726f08..c37304a74 100644 --- a/test/ConnectivityTest.cpp +++ b/test/ConnectivityTest.cpp @@ -215,60 +215,54 @@ TEST(ConnectivityTest, test_4) { ASSERT_FALSE(graph.isConnectedGraph()); } -///////////////////////////////////////////////////////// -// Not a possible test until not implemented addNode function. All the -// undirected graph can be only connected with this interface. -/* -TEST(ConnectivityTest, test_5) -{ - CXXGraph::Node node0("0", 0); - CXXGraph::Node node1("1", 1); - CXXGraph::Node node2("2", 2); - CXXGraph::Node node3("3", 3); - CXXGraph::Node node4("4", 4); - CXXGraph::Node node5("5", 5); - CXXGraph::Node node6("6", 6); - CXXGraph::Node node7("7", 7); - CXXGraph::Node node8("8", 8); - - CXXGraph::UndirectedWeightedEdge edge1(1, node0, node1, 4); - CXXGraph::UndirectedWeightedEdge edge2(2, node0, node7, 8); - CXXGraph::UndirectedWeightedEdge edge3(3, node1, node7, 11); - CXXGraph::UndirectedWeightedEdge edge4(3, node1, node2, 8); - CXXGraph::UndirectedWeightedEdge edge5(4, node7, node8, 7); - CXXGraph::UndirectedWeightedEdge edge6(3, node7, node6, 1); - CXXGraph::UndirectedWeightedEdge edge7(3, node8, node2, 2); - CXXGraph::UndirectedWeightedEdge edge8(3, node8, node6, 6); - CXXGraph::UndirectedWeightedEdge edge9(3, node2, node5, 4); - CXXGraph::UndirectedWeightedEdge edge10(3, node2, node3, 7); - CXXGraph::UndirectedWeightedEdge edge11(3, node6, node5, 2); - CXXGraph::UndirectedWeightedEdge edge12(3, node3, node4, 9); - CXXGraph::UndirectedWeightedEdge edge13(3, node3, node5, 14); - CXXGraph::UndirectedWeightedEdge edge14(3, node5, node4, 10); - - CXXGraph::T_EdgeSet edgeSet; - edgeSet.insert(&edge1); - edgeSet.insert(&edge2); - edgeSet.insert(&edge3); - edgeSet.insert(&edge4); - edgeSet.insert(&edge5); - edgeSet.insert(&edge6); - edgeSet.insert(&edge7); - edgeSet.insert(&edge8); - edgeSet.insert(&edge9); - edgeSet.insert(&edge10); - edgeSet.insert(&edge11); - edgeSet.insert(&edge12); - edgeSet.insert(&edge13); - edgeSet.insert(&edge14); - - CXXGraph::Graph graph(edgeSet); - - ASSERT_FALSE(graph.isConnectedGraph()); +TEST(ConnectivityTest, test_5) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + CXXGraph::Node node5("5", 5); + CXXGraph::Node node6("6", 6); + CXXGraph::Node node7("7", 7); + CXXGraph::Node node8("8", 8); + + CXXGraph::UndirectedWeightedEdge edge1(1, node0, node1, 4); + CXXGraph::UndirectedWeightedEdge edge2(2, node0, node7, 8); + CXXGraph::UndirectedWeightedEdge edge3(3, node1, node7, 11); + CXXGraph::UndirectedWeightedEdge edge4(3, node1, node2, 8); + CXXGraph::UndirectedWeightedEdge edge5(4, node7, node8, 7); + CXXGraph::UndirectedWeightedEdge edge6(3, node7, node6, 1); + CXXGraph::UndirectedWeightedEdge edge7(3, node8, node2, 2); + CXXGraph::UndirectedWeightedEdge edge8(3, node8, node6, 6); + CXXGraph::UndirectedWeightedEdge edge9(3, node2, node5, 4); + CXXGraph::UndirectedWeightedEdge edge10(3, node2, node3, 7); + CXXGraph::UndirectedWeightedEdge edge11(3, node6, node5, 2); + CXXGraph::UndirectedWeightedEdge edge12(3, node3, node4, 9); + CXXGraph::UndirectedWeightedEdge edge13(3, node3, node5, 14); + CXXGraph::UndirectedWeightedEdge edge14(3, node5, node4, 10); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + edgeSet.insert(make_shared>(edge4)); + edgeSet.insert(make_shared>(edge5)); + edgeSet.insert(make_shared>(edge6)); + edgeSet.insert(make_shared>(edge7)); + edgeSet.insert(make_shared>(edge8)); + edgeSet.insert(make_shared>(edge9)); + edgeSet.insert(make_shared>(edge10)); + edgeSet.insert(make_shared>(edge11)); + edgeSet.insert(make_shared>(edge12)); + edgeSet.insert(make_shared>(edge13)); + edgeSet.insert(make_shared>(edge14)); + + + CXXGraph::Graph graph(edgeSet); + + ASSERT_FALSE(graph.isConnectedGraph()); } -*/ -//////////////////////////////////////////////////////////////////// TEST(ConnectivityTest, test_6) { CXXGraph::Node node0("0", 0); From 79de01f96b2d82c7c08c2fa47e9a3231f3dd5384 Mon Sep 17 00:00:00 2001 From: brghash Date: Sun, 7 Sep 2025 18:36:30 +0100 Subject: [PATCH 04/11] Improved getNodeSet and getAdjList : Fixes #367 --- include/CXXGraph/Graph/Graph_impl.hpp | 116 ++++++++++++++------------ 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/include/CXXGraph/Graph/Graph_impl.hpp b/include/CXXGraph/Graph/Graph_impl.hpp index 1b072fe58..767feea12 100644 --- a/include/CXXGraph/Graph/Graph_impl.hpp +++ b/include/CXXGraph/Graph/Graph_impl.hpp @@ -176,6 +176,9 @@ void Graph::removeEdge(const CXXGraph::id_t edgeId) { if (delIndex != -1) { (*cachedAdjListOut)[from].erase((*cachedAdjListOut)[from].begin() + delIndex); + if ((*cachedAdjListOut)[from].empty()) { + cachedAdjListOut->erase(from); + } } delIndex = -1; @@ -189,9 +192,14 @@ void Graph::removeEdge(const CXXGraph::id_t edgeId) { } if (delIndex != -1) { (*cachedAdjListIn)[to].erase((*cachedAdjListIn)[to].begin() + delIndex); + if ((*cachedAdjListIn)[to].empty()) { + cachedAdjListIn->erase(to); + } } - if (!edgeOpt.value().get()->isDirected().value_or(true)) { + // If the edge is just he base class Edge it is treated as an undirected + // edge + if (!edgeOpt.value().get()->isDirected().value_or(false)) { delIndex = -1; i = 0; for (auto elem : (*cachedAdjListOut)[to]) { @@ -204,6 +212,9 @@ void Graph::removeEdge(const CXXGraph::id_t edgeId) { if (delIndex != -1) { (*cachedAdjListOut)[to].erase((*cachedAdjListOut)[to].begin() + delIndex); + if ((*cachedAdjListOut)[to].empty()) { + cachedAdjListOut->erase(to); + } } delIndex = -1; @@ -218,6 +229,9 @@ void Graph::removeEdge(const CXXGraph::id_t edgeId) { if (delIndex != -1) { (*cachedAdjListIn)[from].erase((*cachedAdjListIn)[from].begin() + delIndex); + if ((*cachedAdjListIn)[from].empty()) { + cachedAdjListIn->erase(from); + } } } } @@ -294,12 +308,16 @@ template const T_NodeSet Graph::getNodeSet() const { T_NodeSet nodeSet; - for (const auto &edgeSetIt : edgeSet) { - nodeSet.insert(edgeSetIt->getNodePair().first); - nodeSet.insert(edgeSetIt->getNodePair().second); + for (const auto &adjListInIt : *cachedAdjListIn) { + nodeSet.insert(adjListInIt.first); } - // Merge with the isolated nodes - nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); + + for (const auto &adjListOutIt : *cachedAdjListOut) { + nodeSet.insert(adjListOutIt.first); + } + + // Add the isolated nodes + nodeSet.insert(isolatedNodesSet.begin(), isolatedNodesSet.end()); return nodeSet; } @@ -308,9 +326,6 @@ template const T_NodeVector Graph::getNodeVector() const { auto &nodeSet = getNodeSet(); T_NodeVector nodeVector(nodeSet.begin(), nodeSet.end()); - // Merge with the isolated nodes - nodeVector.insert(nodeVector.end(), this->isolatedNodesSet.begin(), - this->isolatedNodesSet.end()); return nodeVector; } @@ -364,12 +379,15 @@ const std::optional>> Graph::getNode( template std::unordered_set>, nodeHash> Graph::nodeSet() { std::unordered_set>, nodeHash> nodeSet; - for (auto &edgeSetIt : edgeSet) { - nodeSet.insert( - std::const_pointer_cast>(edgeSetIt->getNodePair().first)); - nodeSet.insert( - std::const_pointer_cast>(edgeSetIt->getNodePair().second)); + + for (const auto &adjListInIt : *cachedAdjListIn) { + nodeSet.insert(std::const_pointer_cast>(adjListInIt.first)); } + + for (const auto &adjListOutIt : *cachedAdjListOut) { + nodeSet.insert(std::const_pointer_cast> (adjListOutIt.first)); + } + for (auto &isNodeIt : isolatedNodesSet) { nodeSet.insert(std::const_pointer_cast>(isNodeIt)); } @@ -506,29 +524,25 @@ std::shared_ptr>> Graph::eulerianPath() const { template shared> Graph::getAdjListOut() const { auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&adj](shared> nodeFrom, - shared> nodeTo, - shared> edge) { + auto addElementToAdjList = [&adj](shared> nodeFrom, + shared> nodeTo, + shared> edge) { std::pair>, shared>> elem = {nodeTo, edge}; (*adj)[nodeFrom].push_back(std::move(elem)); }; for (const auto &edgeSetIt : edgeSet) { - if (edgeSetIt->isDirected().value_or(false)) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(d_edge->getNodePair().first, - d_edge->getNodePair().second, d_edge); - } else if (!edgeSetIt->isDirected().value_or(true)) { - shared> ud_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(ud_edge->getNodePair().first, - ud_edge->getNodePair().second, ud_edge); - addElementToAdjMatrix(ud_edge->getNodePair().second, - ud_edge->getNodePair().first, ud_edge); - } else { // is a simple edge we cannot create adj matrix - return adj; - } + shared> edge = + std::static_pointer_cast>(edgeSetIt); + + addElementToAdjList(edge->getNodePair().first, edge->getNodePair().second, + edge); + + // If the type is the base Edge type then it is treated as an undirected + // edge + if (!edgeSetIt->isDirected().value_or(false)) + addElementToAdjList(edge->getNodePair().second, edge->getNodePair().first, + edge); } return adj; } @@ -536,39 +550,35 @@ shared> Graph::getAdjListOut() const { template shared> Graph::getAdjListIn() const { auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&adj](shared> nodeTo, - shared> nodeFrom, - shared> edge) { + auto addElementToAdjList = [&adj](shared> nodeFrom, + shared> nodeTo, + shared> edge) { std::pair>, shared>> elem = {nodeFrom, edge}; (*adj)[nodeTo].push_back(std::move(elem)); }; for (const auto &edgeSetIt : edgeSet) { - if (edgeSetIt->isDirected().value_or(false)) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(d_edge->getNodePair().second, - d_edge->getNodePair().first, d_edge); - } else if (!edgeSetIt->isDirected().value_or(true)) { - shared> ud_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(ud_edge->getNodePair().second, - ud_edge->getNodePair().first, ud_edge); - addElementToAdjMatrix(ud_edge->getNodePair().first, - ud_edge->getNodePair().second, ud_edge); - } else { // is a simple edge we cannot create adj list - return adj; - } + shared> edge = + std::static_pointer_cast>(edgeSetIt); + + addElementToAdjList(edge->getNodePair().first, edge->getNodePair().second, + edge); + + // If the type is the base Edge type then it is treated as an undirected + // edge + if (!edgeSetIt->isDirected().value_or(false)) + addElementToAdjList(edge->getNodePair().second, edge->getNodePair().first, + edge); } return adj; } template void Graph::cacheAdjLists() { - const auto adjOut = Graph::getAdjListOut(); + const auto adjOut = getAdjListOut(); this->cachedAdjListOut = adjOut; - const auto adjIn = Graph::getAdjListIn(); + const auto adjIn = getAdjListIn(); this->cachedAdjListIn = adjIn; } @@ -846,7 +856,7 @@ Graph::inOrOutEdges(shared> node) const { template bool Graph::isDirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); + auto edgeSet = getEdgeSet(); for (const auto &edge : edgeSet) { if (!(edge->isDirected().has_value() && edge->isDirected().value())) { // Found Undirected Edge From f1e29016dacbf7bc9c4d5e269d0c8d4fa148334e Mon Sep 17 00:00:00 2001 From: brghash Date: Sun, 7 Sep 2025 19:06:26 +0100 Subject: [PATCH 05/11] Moved some functionality out of Graph_impl to lighten the file. --- .../Graph/Algorithm/EulerianPath_impl.hpp | 73 +++++++++++ include/CXXGraph/Graph/Graph.h | 1 + include/CXXGraph/Graph/Graph_impl.hpp | 113 ------------------ .../Graph/IO/OutputOperation_impl.hpp | 70 +++++++++++ 4 files changed, 144 insertions(+), 113 deletions(-) create mode 100644 include/CXXGraph/Graph/Algorithm/EulerianPath_impl.hpp diff --git a/include/CXXGraph/Graph/Algorithm/EulerianPath_impl.hpp b/include/CXXGraph/Graph/Algorithm/EulerianPath_impl.hpp new file mode 100644 index 000000000..312d6c443 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/EulerianPath_impl.hpp @@ -0,0 +1,73 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: MPL v2.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_EULERIAN_PATH_IMPL_H__ +#define __CXXGRAPH_EULERIAN_PATH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +std::shared_ptr>> Graph::eulerianPath() const { + const auto nodeSet = Graph::getNodeSet(); + + std::shared_ptr>> eulerPath = + std::make_shared>>(); + + // unused + // bool undirected = this->isUndirectedGraph(); + + std::vector>> currentPath; + // The starting node is the only node which has more outgoing than ingoing + // links + auto firstNodeIt = std::max_element(nodeSet.begin(), nodeSet.end(), + [this](auto n1, auto n2) { + return cachedAdjListOut->at(n1).size() < + cachedAdjListOut->at(n2).size(); + }); + auto currentNode = *(firstNodeIt); + currentPath.push_back(currentNode); + + while (currentPath.size() > 0) { + auto &edges = cachedAdjListOut->at(currentNode); + // we keep removing the edges that + // have been traversed from the adjacency list + if (edges.size()) { + auto firstEdge = edges.back().second; + + shared> nextNodeId; + nextNodeId = firstEdge->getOtherNode(currentNode); + + currentPath.push_back(nextNodeId); + currentNode = nextNodeId; + edges.pop_back(); + } else { + eulerPath->push_back(*currentNode); + currentNode = currentPath.back(); + currentPath.pop_back(); + } + } + return eulerPath; +} +} // namespace CXXGraph + +#endif // __CXXGRAPH_EULERIAN_PATH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Graph.h b/include/CXXGraph/Graph/Graph.h index 28da014a1..a9ed9cced 100644 --- a/include/CXXGraph/Graph/Graph.h +++ b/include/CXXGraph/Graph/Graph.h @@ -35,6 +35,7 @@ #include "CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp" #include "CXXGraph/Graph/Algorithm/Dial_impl.hpp" #include "CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp" +#include "CXXGraph/Graph/Algorithm/EulerianPath_impl.hpp" #include "CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp" #include "CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp" #include "CXXGraph/Graph/Algorithm/HopcroftKarp_impl.hpp" diff --git a/include/CXXGraph/Graph/Graph_impl.hpp b/include/CXXGraph/Graph/Graph_impl.hpp index 767feea12..adabb3d1b 100644 --- a/include/CXXGraph/Graph/Graph_impl.hpp +++ b/include/CXXGraph/Graph/Graph_impl.hpp @@ -478,49 +478,6 @@ void Graph::setUnion( } } -template -std::shared_ptr>> Graph::eulerianPath() const { - const auto nodeSet = Graph::getNodeSet(); - - std::shared_ptr>> eulerPath = - std::make_shared>>(); - - // unused - // bool undirected = this->isUndirectedGraph(); - - std::vector>> currentPath; - // The starting node is the only node which has more outgoing than ingoing - // links - auto firstNodeIt = std::max_element(nodeSet.begin(), nodeSet.end(), - [this](auto n1, auto n2) { - return cachedAdjListOut->at(n1).size() < - cachedAdjListOut->at(n2).size(); - }); - auto currentNode = *(firstNodeIt); - currentPath.push_back(currentNode); - - while (currentPath.size() > 0) { - auto &edges = cachedAdjListOut->at(currentNode); - // we keep removing the edges that - // have been traversed from the adjacency list - if (edges.size()) { - auto firstEdge = edges.back().second; - - shared> nextNodeId; - nextNodeId = firstEdge->getOtherNode(currentNode); - - currentPath.push_back(nextNodeId); - currentNode = nextNodeId; - edges.pop_back(); - } else { - eulerPath->push_back(*currentNode); - currentNode = currentPath.back(); - currentPath.pop_back(); - } - } - return eulerPath; -} - template shared> Graph::getAdjListOut() const { auto adj = std::make_shared>(); @@ -934,75 +891,5 @@ const std::vector> Graph::graph_slicing(const Node &start) const { return result; } -template -std::ostream &operator<<(std::ostream &os, const Graph &graph) { - os << "Graph:\n"; - auto edgeList = graph.getEdgeSet(); - for (auto it = edgeList.begin(); it != edgeList.end(); ++it) { - if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { - // Edge Case - os << **it << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) - << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) << "\n"; - } else if (!((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) - << "\n"; - } else if (!((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) << "\n"; - } else { - os << *it << "\n"; - } - } - return os; -} - -template -std::ostream &operator<<(std::ostream &os, const AdjacencyList &adj) { - os << "Adjacency Matrix:\n"; - unsigned long max_column = 0; - for (const auto &it : adj) { - if (it.second.size() > max_column) { - max_column = (unsigned long)it.second.size(); - } - } - if (max_column == 0) { - os << "ERROR in Print\n"; - return os; - } else { - os << "|--|"; - for (unsigned long i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - for (const auto &it : adj) { - os << "|N" << it.first->getId() << "|"; - for (const auto &it2 : it.second) { - os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; - } - os << "\n|--|"; - for (unsigned long i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - } - } - return os; -} - } // namespace CXXGraph #endif // __CXXGRAPH_GRAPH_IMPL_H__ diff --git a/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp index 056bc9823..fa2ce39f9 100644 --- a/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp +++ b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp @@ -284,5 +284,75 @@ void Graph::writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, } } +template +std::ostream &operator<<(std::ostream &os, const Graph &graph) { + os << "Graph:\n"; + auto edgeList = graph.getEdgeSet(); + for (auto it = edgeList.begin(); it != edgeList.end(); ++it) { + if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { + // Edge Case + os << **it << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) + << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) << "\n"; + } else if (!((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) + << "\n"; + } else if (!((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) << "\n"; + } else { + os << *it << "\n"; + } + } + return os; +} + +template +std::ostream &operator<<(std::ostream &os, const AdjacencyList &adj) { + os << "Adjacency Matrix:\n"; + unsigned long max_column = 0; + for (const auto &it : adj) { + if (it.second.size() > max_column) { + max_column = (unsigned long)it.second.size(); + } + } + if (max_column == 0) { + os << "ERROR in Print\n"; + return os; + } else { + os << "|--|"; + for (unsigned long i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + for (const auto &it : adj) { + os << "|N" << it.first->getId() << "|"; + for (const auto &it2 : it.second) { + os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; + } + os << "\n|--|"; + for (unsigned long i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + } + } + return os; +} + } // namespace CXXGraph #endif // __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ From 586eae0dd9650c0ba28943cc5353bc8e4d0edd2c Mon Sep 17 00:00:00 2001 From: brghash Date: Wed, 10 Sep 2025 17:03:08 +0100 Subject: [PATCH 06/11] Correctly formatted header gaurds --- include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp | 2 +- include/CXXGraph/Utility/PointerHash.hpp | 2 +- include/CXXGraph/Utility/TypeTraits.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp index c23ee5a09..f32978fe1 100644 --- a/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp @@ -72,4 +72,4 @@ const BronKerboschResult Graph::bron_kerbosch() const { } } // namespace CXXGraph -#endif \ No newline at end of file +#endif // __CXXGRAPH_BRONKERBOSCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Utility/PointerHash.hpp b/include/CXXGraph/Utility/PointerHash.hpp index 963e88227..d0503eb75 100644 --- a/include/CXXGraph/Utility/PointerHash.hpp +++ b/include/CXXGraph/Utility/PointerHash.hpp @@ -81,4 +81,4 @@ bool operator==(shared> p1, shared> p2) { } } // namespace CXXGraph -#endif +#endif // __CXXGRAPH_POINTER_HASH__ diff --git a/include/CXXGraph/Utility/TypeTraits.hpp b/include/CXXGraph/Utility/TypeTraits.hpp index 87fa05b75..0c643439b 100755 --- a/include/CXXGraph/Utility/TypeTraits.hpp +++ b/include/CXXGraph/Utility/TypeTraits.hpp @@ -113,4 +113,4 @@ inline constexpr bool all_are_edge_ptrs_v = all_are_edge_ptrs::value; } // namespace CXXGraph -#endif +#endif // __CXXGRAPH_TYPE_TRAITS__ From f707adb23e5a9f1bfaa414503c66a2c2d33ecf5d Mon Sep 17 00:00:00 2001 From: brghash Date: Wed, 10 Sep 2025 20:20:06 +0100 Subject: [PATCH 07/11] Added an example and links to more in the README file. --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2f4abf21..58d1b161c 100755 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ If you are interested, please contact us at zigrazor@gmail.com or contribute to - [Classes Explanation](#classes-explanation) - [Requirements](#requirements) - [How to use](#how-to-use) - - [Example](#example) + - [Examples](#examples) - [Unit-Test Execution](#unit-test-execution) - [Google Test Installation](#google-test-installation) - [How to Compile Test](#how-to-compile-test) @@ -164,9 +164,39 @@ The Classes Explanation can be found in the [Classes Section](https://rawcdn.git To use the library **simply put the header file where you need it.** It's that easy! -## Example +## Examples + +In this example, the shortest path between nodeA and nodeC is obtained using Dijkstra's algorithm. + +```cpp +#include +#include "CXXGraph/CXXGraph.hpp" + +int main(){ + CXXGraph::Node nodeA("A", 1); + CXXGraph::Node nodeB("B", 2); + CXXGraph::Node nodeC("C", 3); + + CXXGraph::DirectedWeightedEdge edge1(1, nodeA, nodeB, 1); + CXXGraph::DirectedWeightedEdge edge2(2, nodeB, nodeC, 1); + CXXGraph::UndirectedWeightedEdge edge3(3, nodeA, nodeC, 6); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + + CXXGraph::Graph graph(edgeSet); + CXXGraph::DijkstraResult res = graph.dijkstra(nodeA, nodeC); + + for(auto node_user_id : res.path){ + std::cout << node_user_id << '\n'; + } +} +``` + +See more examples in the [examples folder](https://github.com/ZigRazor/CXXGraph/tree/master/examples). -Work in Progress ## Unit-Test Execution From aea3585081fc86ea576a8f94834a7c54c30b05b0 Mon Sep 17 00:00:00 2001 From: brghash Date: Thu, 18 Sep 2025 12:45:41 +0100 Subject: [PATCH 08/11] Improved README by reordering and decluttering to make it clearer. Removed implementation and description of algorithms, redirecting the reader to the website and Deoxygen docs. --- README.md | 390 +++++++++++------------------------------------------- 1 file changed, 75 insertions(+), 315 deletions(-) diff --git a/README.md b/README.md index 58d1b161c..9a1b46935 100755 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ ## Introduction **CXXGraph** is a comprehensive C++ library that manages graph algorithms. This header-only library serves as an alternative to the [Boost Graph Library (BGL)](https://www.boost.org/doc/libs/1_77_0/libs/graph/doc/index.html). +[CXXGraph Website](https://zigrazor.github.io/CXXGraph/) + ## We are Looking for... **We are looking for:** @@ -29,35 +31,18 @@ If you are interested, please contact us at zigrazor@gmail.com or contribute to this project. We are waiting for you! -## Roadmap - -| Completed | Description | Date of Completition | -| :-------: | :---------- | :-------------------: | -| :heavy_check_mark: | Release 0.4.0 | Oct 7, 2022 | -| :heavy_check_mark: | Release 0.5.0 | Mar 23, 2023 | -| :heavy_check_mark: | First Stable Release 1.0.0 | Mar 28, 2023 | -| :heavy_check_mark: | Release 1.0.1 | May 7, 2023 | -| :heavy_check_mark: | Release 1.1.0 | May 8, 2023 | -| :heavy_check_mark: | Stable Release 2.0.0 | Jun 1, 2023 | -| :heavy_check_mark: | Stable Release 3.0.0 | Nov 3, 2023 | -| :heavy_check_mark: | Release 3.1.0 | Jan 9, 2023 | -| :memo: | Introduce Hypergraph [#122](https://github.com/ZigRazor/CXXGraph/issues/122) | TBD | -| :memo: | Stable Release 4.0.0 | TBD | ## Table of Contents - [CXXGraph](#cxxgraph) - [Introduction](#introduction) - - [Hacktoberfest 2k22](#hacktoberfest-2k22) - [We are Looking for...](#we-are-looking-for) - - [Roadmap](#roadmap) - [Table of Contents](#table-of-contents) - [Install and Uninstall](#install-and-uninstall) - [Install Linux Tarballs](#install-linux-tarballs) - [Install RPM](#install-rpm) - [Install DEB](#install-deb) - [Install From Source](#install-from-source) - - [Classes Explanation](#classes-explanation) - [Requirements](#requirements) - [How to use](#how-to-use) - [Examples](#examples) @@ -76,39 +61,15 @@ If you are interested, please contact us at zigrazor@gmail.com or contribute to - [(Fedora/CentOS/RedHat)](#fedoracentosredhat) - [DEB](#deb) - [(Debian/Ubuntu)](#debianubuntu) - - [Algorithm Explanation](#algorithm-explanation) - - [Dijkstra](#dijkstra) - - [Dial](#dial) - - [Prim's Algorithm](#prims-algorithm) - - [BFS](#bfs) - - [DFS](#dfs) - - [Best First Search](#best-first-search) - - [Cycle Detection](#cycle-detection) - - [Bellman-Ford](#bellman-ford) - - [Floyd Warshall](#floyd-warshall) - - [Transitive Reduction](#transitive-reduction) - - [Kruskal Algorithm](#kruskal-algorithm) - - [Borůvka's Algorithm](#borůvkas-algorithm) - - [Graph Slicing based on connectivity](#graph-slicing-based-on-connectivity) - - [Ford-Fulkerson Algorithm](#ford-fulkerson-algorithm) - - [Hopcroft-Karp Algorithm](#hopcroft-karp-algorithm) - - [Kosaraju's Algorithm](#kosarajus-algorithm) - - [Kahn's Algorithm](#kahns-algorithm) - - [Partition Algorithm Explanation](#partition-algorithm-explanation) - - [Vertex-Cut](#vertex-cut) - - [Edge Balanced Vertex-Cut](#edge-balanced-vertex-cut) - - [Greedy Vertex-Cut](#greedy-vertex-cut) - - [HDRF](#hdrf) - - [EBV](#ebv) + - [Algorithms, Classes and Network Dynamics](#algorithms-classes-and-network-dynamics) - [How to contribute](#how-to-contribute) - - [Site](#site) + - [Roadmap](#roadmap) - [Contact](#contact) - [Support](#support) - [References](#references) - [Credits](#credits) - [Contributors](#contributors) - [Cite Us](#cite-us) - - [Hacktoberfest 2k21](#hacktoberfest-2k21) - [Other Details](#other-details) - [Author](#author) @@ -151,10 +112,6 @@ For self-compiled installations using CMake, execute the following from the comm `$ sudo make install` -## Classes Explanation - -The Classes Explanation can be found in the [Classes Section](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/classes.html) of the [Doxygen Documentation](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/index.html) - ## Prerequisites - The minimum C++ standard required is **C++17** @@ -162,7 +119,9 @@ The Classes Explanation can be found in the [Classes Section](https://rawcdn.git ## How to use -To use the library **simply put the header file where you need it.** It's that easy! +To use the library **simply include the header file `CXXGraph.hpp`**, (make sure to add the [include folder](https://github.com/ZigRazor/CXXGraph/tree/master/include) to your compiler's inlcude path). + +CXXGraph revolves around the graph object which contains nodes and edges. This object can then be manipulated with a wide variety of algorithms. Please see the [examples section](#examples), [examples folder](https://github.com/ZigRazor/CXXGraph/tree/master/examples) and [website](https://zigrazor.github.io/CXXGraph/) for more information ## Examples @@ -320,281 +279,80 @@ $ cd packaging/deb $ ./make_deb.sh ``` -## Algorithm Explanation - -### Dijkstra - -[Graph Dijkstras Shortest Path Algorithm(Dijkstra's Shortest Path)](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) -**[Dijkstra's Algorithm]**(https://www.interviewbit.com/blog/find-shortest-path-dijkstras-algorithm/) is used to find the shortest path from a source node to all other reachable nodes in the graph. The algorithm initially assumes all the nodes are unreachable from the given source node so we mark the distances of all nodes as infinity. -(infinity) from source node (INF / infinity denotes unable to reach). - -### Dial - -Dial specialization of dijkstra’s algorithm. - -When edge weights are small integers (bounded by a parameter *C*), specialized queues which take advantage of this fact can be used to speed up Dijkstra's algorithm. The first algorithm of this type was Dial's algorithm (Dial 1969) for graphs with positive integer edge weights, which uses a bucket queue to obtain a running time -*O(|E|+|V|C)*.([source wikipedia](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Specialized_variants)) - -Below is complete algorithm: - - 1. Maintains some buckets, numbered 0, 1, 2,…,wV. - 2. Bucket k contains all temporarily labeled nodes with distance equal to k. - 3. Nodes in each bucket are represented by list of vertices. - 4. Buckets 0, 1, 2,..wV are checked sequentially until the first non-empty bucket is found. Each node contained in the first non-empty bucket has the minimum distance label by definition. - 5. One by one, these nodes with minimum distance label are permanently labeled and deleted from the bucket during the scanning process. - 6. Thus operations involving vertex include: - - Checking if a bucket is empty - - Adding a vertex to a bucket - - Deleting a vertex from a bucket. - 7. The position of a temporarily labeled vertex in the buckets is updated accordingly when the distance label of a vertex changes. - 8. Process repeated until all vertices are permanently labeled (or distances of all vertices are finalized). - -At this [link](https://ocw.mit.edu/courses/sloan-school-of-management/15-082j-network-optimization-fall-2010/animations/MIT15_082JF10_av07.pdf) you can find a step-by-step illustrations. - -### Prim's Algorithm - -[Prim's Algorithm](https://en.wikipedia.org/wiki/Prim%27s_algorithm) -Prim's Algorithm is is a greedy algorithm that finds a minimum spanning tree for a weighted undirected graph. This means it finds a subset of the edges that forms a tree that includes every vertex, where the total weight of all the edges in the tree is minimized. The algorithm operates by building this tree one vertex at a time, from an arbitrary starting vertex, at each step adding the cheapest possible connection from the tree to another vertex. - -Steps: - -1. Initialize a tree with a single vertex, chosen arbitrarily from the graph. -2. Grow the tree by one edge: of the edges that connect the tree to vertices not yet in the tree, find the minimum-weight edge, and transfer it to the tree. -3. Repeat step 2 (until all vertices are in the tree). - -### BFS - -(Breadth First Search) -[Breadth First Search Algorithm(Breadth First Search)](https://en.wikipedia.org/wiki/Breadth-first_search) -**Breadth First Search**, also quoted as **BFS**, is a Graph Traversal Algorithm. Time Complexity O(|V| + |E|) where V are the number of vertices and E are the number of edges in the graph. -Applications of Breadth First Search are : - - 1. Finding shortest path between two vertices say u and v, with path length measured by number of edges (an advantage over depth first search algorithm) - 2. Ford-Fulkerson Method for computing the maximum flow in a flow network. - 3. Testing bipartiteness of a graph. - 4. Cheney's Algorithm, Copying garbage collection. - - And there are many more... - -### DFS - -(Depth First Search) -[Depth First Search Algorithm (Depth First Search)](https://en.wikipedia.org/wiki/Depth-first_search) -**Depth First Search**, also quoted as **DFS**, is a Graph Traversal Algorithm. Time Complexity O(|V| + |E|) where V is number of vertices and E is number of edges in graph. -Application of Depth First Search are: - - 1. Finding connected components - 2. Finding 2-(edge or vertex)-connected components. - 3. Finding 3-(edge or vertex)-connected components. - 4. Finding the bridges of a graph. - 5. Generating words in order to plot the limit set of a group. - 6. Finding strongly connected components. - - And there are many more... - -### Best First Search - -[Best First Search](https://en.wikipedia.org/wiki/Best-first_search) -Best First Search is a class of search algorithms which traverses the graph by exploring the most promising node chosen according to an evaluation function. The worst-case time complexity is O(n * log n) where n is the number of nodes in the graph. - -### Cycle Detection - -[Cycle (graph theory)](https://en.wikipedia.org/wiki/Cycle_(graph_theory)) - -The existence of a cycle in directed and undirected graphs can be determined by whether depth-first search (DFS) finds an edge that points to an ancestor of the current vertex (it contains a back edge). All the back edges which DFS skips over are part of cycles. In an undirected graph, the edge to the parent of a node should not be counted as a back edge, but finding any other already visited vertex will indicate a back edge. In the case of undirected graphs, only O(n) time is required to find a cycle in an n-vertex graph, since at most n − 1 edges can be tree edges. - -Many topological sorting algorithms will detect cycles too, since those are obstacles for topological order to exist. Also, if a directed graph has been divided into strongly connected components, cycles only exist within the components and not between them, since cycles are strongly connected. - -For directed graphs, distributed message based algorithms can be used. These algorithms rely on the idea that a message sent by a vertex in a cycle will come back to itself. Distributed cycle detection algorithms are useful for processing large-scale graphs using a distributed graph processing system on a computer cluster (or supercomputer). - -Applications of cycle detection include the use of wait-for graphs to detect deadlocks in concurrent systems. - -### Bellman-Ford - -[Bellman-Ford Algorithm](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) can be used to find the shortest distance between a source and a target node. Time Complexity O(|V| . |E|) where V is number of vertices and E is number of edges in graph which is higher than Dijkstra's shortest path algorithm. The time complexity of dijkstra's algorithm is O(|E| + |V| log |v| ). The advantage of bellman-ford over dijkstra is that it can handle graphs with negative edge weights. Further, if the graph contains a negative weight cycle then the algorithm can detect and report the presense of negative cycle. - -[This video](https://www.youtube.com/watch?v=24HziTZ8_xo) gives a nice overview of the algorithm implementation. This [MIT lecture](https://courses.csail.mit.edu/6.006/spring11/lectures/lec15.pdf) gives a proof of Bellman-Ford's correctness & its ability to detect negative cycles. -Applications: - -- Distanceâ€vector routing protocol -- Routing Information Protocol (RIP) -- Interior Gateway Routing Protocol (IGRP) - -### Floyd Warshall - -[Floyd Warshall Algorithm](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm) - -We initialize the solution matrix same as the input graph matrix as a first step. Then we update the solution matrix by considering all vertices as an intermediate vertex. The idea is to one by one pick all vertices and updates all shortest paths which include the picked vertex as an intermediate vertex in the shortest path. When we pick vertex number k as an intermediate vertex, we already have considered vertices {0, 1, 2, .. k-1} as intermediate vertices. For every pair (i, j) of the source and destination vertices respectively, there are two possible cases. - -1) k is not an intermediate vertex in shortest path from i to j. We keep the value of dist[i][j] as it is. -2) k is an intermediate vertex in shortest path from i to j. We update the value of dist[i][j] as dist[i][k] + dist[k][j] if dist[i][j] > dist[i][k] + dist[k][j] - -### Transitive Reduction - -[Transitive Reduction](https://en.wikipedia.org/wiki/Transitive_reduction) +## Algorithms, Classes and Network Dynamics -This algorithm is used to construct a directed graph with the same reachability and satisfies transitive closure, with as few edges as possible. More concretely, it creates a minimum equivalent graph with as few edges as possible, removing "short-circuit" paths through the graph. +Both the [Doxygen documentation](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/index.html) and the [website](https://zigrazor.github.io/CXXGraph/) provide implementation and explanation information on the [classes](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/classes.html) and [algorithms](https://zigrazor.github.io/CXXGraph/component-explanation/regular-algorithm) of CXXGraph. -This is done by iterating through each node-pair, checking to see if two edges exist that leads out of the first node OR out of the last node, removing the node-pair edge if it exists. +#### Classes -In pseudocode: -foreach x in graph.vertices - foreach y in graph.vertices - foreach z in graph.vertices - delete edge xz if edges xy and yz exist +The Classes Explanation can be found in the [classes section](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/classes.html) of the [Doxygen documentation](https://rawcdn.githack.com/ZigRazor/CXXGraph/master/docs/html/index.html). -Our implementation has if gates that do early checking for edges in multiple places, which gives it a slightly faster runtime than the cubic pseudocode here. +#### Network Dynamics +More information can be found [here](https://zigrazor.github.io/CXXGraph/component-explanation/network-dynamics). -### Kruskal Algorithm +- Adjacency Matrix +- Degree Matrix +- Laplacian Matrix +- Transition Matrix -[Kruskal Algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm) can be used to find the minimum spanning forest of an undirected edge-weighted graph. Time Complexity O(E log E) = O(E log V) where V is number of vertices and E is number of edges in graph. The main speed limitation for this algorithm is sorting the edges. +### Algorithms +The following is a list of all the implemented algorithms, more information on the algorithms can be found [here](https://zigrazor.github.io/CXXGraph/component-explanation/regular-algorithm). +#### Graph Traversal Algorithms. -For a quick understanding of the algorithm procedure, check [this video](https://www.youtube.com/watch?v=71UQH7Pr9kU). -Some of the real life applications are: +- Breadth First Search (BFS) +- Depth First Search (DFS) +- Best First Search (a heuristic-based traversal) +- Bron–Kerbosch Algorithm (for finding maximal cliques; DFS-based) -- LAN/TV Network -- Tour Operations -- Water/gas pipe network -- Electric grid +#### Shortest Path Algorithms -Other algorithms to find the minimum spanning forest are Prim's algorithm or Borůvka's algorithm. +- Dijkstra's Algorithm (single-source shortest path, non-negative weights) +- Bellman-Ford Algorithm (handles negative weights) +- Floyd–Warshall Algorithm (all-pairs shortest path) +- Dial's Algorithm (optimized Dijkstra for small integer weights) -### Borůvka's Algorithm +#### Minimum Spanning Tree Algorithms -[Borůvka's Algorithm](https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm#) is a greedy algorithm that can be used for finding a minimum spanning tree in a graph, or a minimum spanning forest in the case of a graph that is not connected. +- Prim's Algorithm +- Kruskal's Algorithm +- Borůvka's Algorithm -The algorithm begins by finding the minimum-weight edge incident to each vertex of the graph, and adding all of those edges to the forest. Then, it repeats a similar process of finding the minimum-weight edge from each tree constructed so far to a different tree, and adding all of those edges to the forest. Each repetition of this process reduces the number of trees, within each connected component of the graph, to at most half of this former value, so after logarithmically many repetitions the process finishes. When it does, the set of edges it has added forms the minimum spanning forest. +#### Network Flow Algorithms -Borůvka's algorithm can be shown to take O(log V) iterations of the outer loop until it terminates, and therefore to run in time O(E log V), where E is the number of edges, and V is the number of vertices in G (assuming E ≥ V). +- Ford–Fulkerson Algorithm (maximum flow) +- Hopcroft–Karp Algorithm (maximum bipartite matching) -### Graph Slicing based on connectivity +#### Connectivity and Component Detection -Mathematical definition of the problem: -Let G be the set of nodes in a graph and n be a given node in that set. -Let C be the non-strict subset of G containing both n and all nodes reachable -from n, and let C' be its complement. There's a third set M, which is the -non-strict subset of C containing all nodes that are reachable from any node in C'. -The problem consists of finding all nodes that belong to C but not to M. +- Kosaraju's Algorithm (strongly connected components in directed graphs) +- Tarjan's Algorithm (strongly connected components or articulation points) +- Connectivity (general graph connectivity checking) +- Cycle Detection -Currently implemented Algorithm: +#### Topological & Dependency Sorting -- Use DFS to find all nodes reachable from n. These are elements of set C. -- Initialize C' to be complement of C (i.e. all nodes - nodes that are in C) -- For all nodes in C', apply DFS and get the list of reachable nodes. This is set M. -- Finally removes nodes from C that belong to M. This is our solution. +- Topological Sort +- Kahn’s Algorithm (BFS-based topological sorting) +- Tarjan’s Algorithm (DFS-based topological sorting) -Application: +#### Eulerian Path/Cycle Detection +- Hierholzer's Algorithm -This algorithm is used in garbage collection systems to decide which other objects need to be released, given that one object is about to be released. +#### Graph Transformation +- Transitive Reduction (reduce graph to essential edges while preserving reachability) -### Ford-Fulkerson Algorithm +#### Graph Coloring Algorithms +- Welsh–Powell Coloring Algorithm -[Ford-Fulkerson Algorithm](https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm) is a greedy algorithm for finding a maximum flow in a flow network. -The idea behind the algorithm is as follows: as long as there is a path from the source (start node) to the sink (end node), with available capacity on all edges in the path, we send flow along one of the paths. Then we find another path, and so on. A path with available capacity is called an augmenting path. -### Hopcroft-Karp Algorithm +#### Partition Algorithms +- Vertex-Cut +- Edge Balanced Vertex-Cut +- Edge Balanced Vertex-Cut based on this [paper](https://arxiv.org/abs/2010.09007) +- Greedy Vertex-Cut +- High Degree Replicated First -[Hopcroft-Karp Algorithm](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) is an algorithm that finds the maximum cardinality matching in a bipartite graph in O(E√V) time. It repeatedly finds augmenting paths of shortest length using BFS, then uses DFS to find a maximal set of vertex-disjoint augmenting paths of that length. -The algorithm operates in phases: - -1. **BFS Phase**: Find the shortest augmenting path length from unmatched left vertices to unmatched right vertices. If no augmenting path exists, the current matching is maximum. -2. **DFS Phase**: Use DFS to find a maximal set of vertex-disjoint augmenting paths of the shortest length found in the BFS phase. -3. **Augmentation**: Add all found augmenting paths to the matching simultaneously. - -This process repeats until no more augmenting paths exist. Each iteration increases the matching size by at least one, and there are at most O(√V) iterations, giving the overall O(E√V) time complexity. - -### Kosaraju's Algorithm -[Kosaraju's Algorithm](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm) is a linear time algorithm to find the strongly connected components of a directed graph. It is based on the idea that if one is able to reach a vertex v starting from vertex u, then one should be able to reach vertex u starting from vertex v and if such is the case, one can say that vertices u and v are strongly connected - they are in a strongly connected sub-graph. Following is an example: - -1). Create an empty stack ‘S’ and do DFS traversal of a graph. In DFS traversal, after calling recursive DFS for adjacent vertices of a vertex, push the vertex to stack. -2). Reverse directions of all arcs to obtain the transpose graph. -3). One by one pop a vertex from S while S is not empty. Let the popped vertex be ‘v’. Take v as source and do DFS (call DFSUtil(v)). The DFS starting from v prints strongly connected component of v. - -### Kahn's Algorithm -[Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm) finds topological -ordering by iteratively removing nodes in the graph which have no incoming edges. When a node is removed from the graph, it is added to the topological ordering and all its edges are removed allowing for the next set of nodes with no incoming edges to be selected. -### Welsh Powell Coloring Algorithms -[Welsh Powell Coloring algorithm](https://www.geeksforgeeks.org/welsh-powell-graph-colouring-algorithm/) is a greedy vertex coloring algorithm. This algorithm is also used to find the chromatic number of a graph. - -Welsh Powell Algorithm consists of following steps : - -1. Find the degree of each vertex. -2. List the vertices in order of descending degrees. -3. Colour the first vertex with color 1. -4. Move down the list and color all the vertices not connected to the coloured vertex, with the same color. -5. Repeat step 4 on all uncolored vertices with a new color, in descending order of degrees until all the vertices are coloured. - Hi there, I'm creating a pull request to merge the Welsh Powell Coloring algorithm into the master branch. - -The algorithm returns a std::map result that assigns each node to a color ordered by integers. -Users can also query the minimum chromatic order of the graph by querying the highest value from the resulting map. -```C++ -std::map result = graph.welshPowellColoring(); -auto chromatic_color = std::max_element(result.begin(), result.end(), - [](const auto& lhs, const auto& rhs) { - return lhs.second < rhs.second; - } -``` -The minimum coloring starts from 1 instead of 0. - -The algorithm assumes the graph to be undirected. All sources and inspirations are linked in the declaration of the algorithm and test cases. -## Partition Algorithm Explanation - -### Vertex-Cut - -A vertex-cut partitioning divides edges of a graph into equal size partitions. The vertices that hold the endpoints of an edge are also placed in the same partition as the edge itself. However, the vertices are not unique across partitions and might have to be replicated (cut), due to the distribution of their edge across different partitions. - -Replication factor quantifies how many vertices are replicated over computers compared with the the number of vertices of the original input graph. - -### Edge Balanced Vertex-Cut - -This Algorithm is a simple vertex-cut in Round-Robin fashion. -It takes the original graph edges and assign them to the partitions, dividing it in equal(or similar) size. This algorithm does not take care of optimization in vertex replication ( Replication Factor) but only balance the edge in the partitions. - -### Greedy Vertex-Cut - -Greedy partitioning algorithms uses the entire history of the edge assignments to make the next decision. -The algorithm stores the set of partitions A(v) to which each already observed vertex v has been assigned and the current partition sizes. -When processing edge e ∈ E connecting vertices vi, vj ∈ V , the greedy algorithm follows this simple set of rules: - -- Rule 1: If neither vi nor vj have been assigned to a partition, then e is placed in the partition with the smallest size in P. -- Rule 2: If only one of the two vertices has been already assigned (without loss of generality assume that vi is the assigned vertex) then e is placed in the partition with the smallest size in A(vi). -- Rule 3: If A(vi) ∩ A(vj ) 6= ∅, then edge e is placed in the partition with the smallest size in A(vi) ∩ A(vj). -- Rule 4: If A(vi) != ∅, A(vj ) != ∅ and A(vi)∩A(vj ) = ∅, then e is placed in the partition with the smallest size in A(vi)∪A(vj) and a new vertex replica is created accordingly. - -### HDRF - -High Degree (are) Replicated First(HDRF) Algorithm is a greedy vertex-cut algorithm as described by this [paper](https://www.fabiopetroni.com/Download/petroni2015HDRF.pdf). -This Algorithm try to optimize Replication Factor by using the history of the edge assignements amd the incremental vertex degree. -With a function that take in consideration this two factors calculate the best partition to assign the analyzed edge. -The replica created are based on the degree of the verteices, and the vertices replicated are probably a so called "Hub-Node", which are the vertices with higher degree. - -### EBV - -Efficient and Balanced Vertex-cut(EBV) is an offline vertex-cut algorithm as described by this [paper](https://arxiv.org/abs/2010.09007). -This algorithm try to balance the partitions with respect to the number of edges and vertices of each partitions and the Replication Factor. -It apply a formula to evaluate the partition in which assigns the edge that take into consideration also the total number of edges and vertices of the graph. -The evaluation formula is the following: - -```math -Eva(u,v)(i) =I(u ∈ keep[i]) + I(v ∈ keep[i]) +α * \frac{ecount[i]}{(|E|/p)} + β * \frac{vcount[i]}{(|V|/p)} -``` - -The lowest value is taken as partition Id. - -## Network Dynamics - -### Degree Matrix - -The Degree Matrix is a square matrix that provides insights into the connectivity of nodes in a graph. For directed graphs, it reflects the number of incoming and outgoing edges for each node, while for undirected graphs, it represents the number of edges incident to each node. - -### Laplacian Matrix - -The Laplacian Matrix is a square matrix derived from the adjacency matrix and degree matrix of a graph. It is instrumental in analyzing various properties of the graph, such as connectedness, the count of spanning trees, and other spectral characteristics. - -### Transition Matrix - -The Transition Matrix is commonly used in the study of Markov Chains and stochastic processes. Within the context of a graph, it denotes the probabilities of transitioning from one node to another, often based on the edge weights or predetermined criteria. This matrix finds applications in various fields such as network analysis, machine learning, and optimization. ## How to contribute @@ -604,13 +362,25 @@ If you want to change the code, fix an issue, or implement a new feature please If you want to discuss new features or you have any questions or suggestions about the library, please open a [Discussion](https://github.com/ZigRazor/CXXGraph/discussions) or simply chat on [![Join the chat at https://gitter.im/CXXGraph-Community/community](https://badges.gitter.im/CXXGraph-Community/community.svg)](https://gitter.im/CXXGraph-Community/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -## Stars History -[![Star History Chart](https://api.star-history.com/svg?repos=ZigRazor/CXXGraph&type=Date)](https://star-history.com/#ZigRazor/CXXGraph&Date) +## Roadmap + +| Completed | Description | Date of Completition | +| :-------: | :---------- | :-------------------: | +| :heavy_check_mark: | Release 0.4.0 | Oct 7, 2022 | +| :heavy_check_mark: | Release 0.5.0 | Mar 23, 2023 | +| :heavy_check_mark: | First Stable Release 1.0.0 | Mar 28, 2023 | +| :heavy_check_mark: | Release 1.0.1 | May 7, 2023 | +| :heavy_check_mark: | Release 1.1.0 | May 8, 2023 | +| :heavy_check_mark: | Stable Release 2.0.0 | Jun 1, 2023 | +| :heavy_check_mark: | Stable Release 3.0.0 | Nov 3, 2023 | +| :heavy_check_mark: | Release 3.1.0 | Jan 9, 2023 | +| :memo: | Introduce Hypergraph [#122](https://github.com/ZigRazor/CXXGraph/issues/122) | TBD | +| :memo: | Stable Release 4.0.0 | TBD | -## Site +## Stars History -[CXXGraph Site](https://zigrazor.github.io/CXXGraph/) +[![Star History Chart](https://api.star-history.com/svg?repos=ZigRazor/CXXGraph&type=Date)](https://star-history.com/#ZigRazor/CXXGraph&Date) ## Contact @@ -657,20 +427,10 @@ Thank you to all the people who have already contributed to CXXGraph! If you use this software please follow the [CITATION](https://github.com/ZigRazor/CXXGraph/blob/master/CITATION) instructions. Thank you! -## Hacktoberfest 2k21 - -We participated at Hacktoberfest 2021. Thank you to all the contributors! - -## Hacktoberfest 2k22 - -We participated at Hacktoberfest 2022. Thank you to all the contributors! - -## Hacktoberfest 2k23 - -We participated at Hacktoberfest 2023. Thank you to all the contributors! - ## Other Details +We participated in Hacktoberfest 2021, 2022 and 2023. Thank you to all the contributors! + View the [Estimated Value of the Project](https://www.openhub.net/p/CXXGraph/estimated_cost) ## Author From a44c30b07c3787fc232074318e8ff36e6f49d368 Mon Sep 17 00:00:00 2001 From: brghash Date: Fri, 19 Sep 2025 12:57:25 +0100 Subject: [PATCH 09/11] Amended the capitalisation of WelshPowell --- include/CXXGraph/Graph/Graph.h | 2 +- ...elshPowellColoringTest.cpp => WelshPowellColoringTest.cpp} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename test/{welshPowellColoringTest.cpp => WelshPowellColoringTest.cpp} (97%) diff --git a/include/CXXGraph/Graph/Graph.h b/include/CXXGraph/Graph/Graph.h index a9ed9cced..e51f826a4 100644 --- a/include/CXXGraph/Graph/Graph.h +++ b/include/CXXGraph/Graph/Graph.h @@ -46,7 +46,7 @@ #include "CXXGraph/Graph/Algorithm/Tarjan_impl.hpp" #include "CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp" #include "CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp" -#include "CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp" +#include "CXXGraph/Graph/Algorithm/WelshPowellColoring_impl.hpp" // IO Operation #include "CXXGraph/Graph/IO/IOUtility_impl.hpp" diff --git a/test/welshPowellColoringTest.cpp b/test/WelshPowellColoringTest.cpp similarity index 97% rename from test/welshPowellColoringTest.cpp rename to test/WelshPowellColoringTest.cpp index 3d44a1e38..b391bc952 100644 --- a/test/welshPowellColoringTest.cpp +++ b/test/WelshPowellColoringTest.cpp @@ -1,7 +1,7 @@ #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" -TEST(welshPowellColoring, one_edge_two_nodes_undirected) { +TEST(WelshPowellColoring, one_edge_two_nodes_undirected) { CXXGraph::Node node1("1", 1); CXXGraph::Node node2("2", 2); @@ -28,7 +28,7 @@ TEST(welshPowellColoring, one_edge_two_nodes_undirected) { ASSERT_EQ(highest_coloring_order, 2); } -TEST(welshPowellColoring, undirected_simon_james) { +TEST(WelshPowellColoring, undirected_simon_james) { // https://www.youtube.com/watch?v=CQIW2mLfG04&ab_channel=SimonJames CXXGraph::Node nodeA("a", 1); CXXGraph::Node nodeB("b", 1); From bde35da12336ad18c0a7d09e2fd5cb9fbbdfc888 Mon Sep 17 00:00:00 2001 From: brghash Date: Fri, 19 Sep 2025 13:24:34 +0100 Subject: [PATCH 10/11] Forced renaming of WelshPowell, git was case-insensitive :( --- ...{welshPowellColoring_impl.hpp => WelshPowellColoring_impl.hpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/CXXGraph/Graph/Algorithm/{welshPowellColoring_impl.hpp => WelshPowellColoring_impl.hpp} (100%) diff --git a/include/CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp b/include/CXXGraph/Graph/Algorithm/WelshPowellColoring_impl.hpp similarity index 100% rename from include/CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp rename to include/CXXGraph/Graph/Algorithm/WelshPowellColoring_impl.hpp From dfb86e7cdacfa90fad6b31aca088a47852630df7 Mon Sep 17 00:00:00 2001 From: brghash Date: Fri, 19 Sep 2025 13:30:37 +0100 Subject: [PATCH 11/11] Fixed shadow template parameter --- include/CXXGraph/Graph/Graph_decl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h index 7c1bdc6d3..b0720d8ec 100644 --- a/include/CXXGraph/Graph/Graph_decl.h +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -112,13 +112,13 @@ class Graph { std::unordered_map &edgeWeightMap); // Type trait used to compile allow compilation when T is not extractable - template + template struct is_istream_extractable : std::false_type {}; - template + template struct is_istream_extractable< - T, std::void_t() >> - std::declval())>> : std::true_type {}; + U, std::void_t() >> + std::declval())>> : std::true_type {}; #ifdef WITH_COMPRESSION int compressFile(const std::string &inputFile,