diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 4afa8d112..3216eef23 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -37,7 +37,7 @@ jobs: # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j - name: Download previous benchmark data - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./cache key: ${{ runner.os }}-benchmark diff --git a/.github/workflows/benchmark_pr.yml b/.github/workflows/benchmark_pr.yml index 07c1f3726..e21d018fe 100644 --- a/.github/workflows/benchmark_pr.yml +++ b/.github/workflows/benchmark_pr.yml @@ -36,7 +36,7 @@ jobs: # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j - name: Download previous benchmark data - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./cache key: ${{ runner.os }}-benchmark diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml new file mode 100644 index 000000000..54aa1dc50 --- /dev/null +++ b/.github/workflows/clang_format.yml @@ -0,0 +1,26 @@ +name: clang-format Check + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + formatting-check: + name: Formatting Check + runs-on: ubuntu-latest + strategy: + matrix: + path: + - 'benchmark' + - 'examples' + - 'include/CXXGraph' + - 'test' + steps: + - uses: actions/checkout@v4 + - name: Run clang-format style check + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '16' + check-path: ${{ matrix.path }} diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 83a1584f9..fbff65379 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -56,6 +56,6 @@ jobs: # Upload the SARIF file generated in the previous step - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2759e58f9..3a7038648 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -44,7 +44,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -80,6 +80,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/flawfinder.yml b/.github/workflows/flawfinder.yml index a79cad414..8a303be94 100644 --- a/.github/workflows/flawfinder.yml +++ b/.github/workflows/flawfinder.yml @@ -33,6 +33,6 @@ jobs: output: 'flawfinder_results.sarif' - name: Upload analysis results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{github.workspace}}/flawfinder_results.sarif diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 2c8d430a5..cc19faeba 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -13,6 +13,6 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d3d2cb8d..4fb25ee0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.9) # set the project name and version -project(CXXGraph VERSION 3.0.0) +project(CXXGraph VERSION 3.1.0) configure_file(CXXGraphConfig.h.in ${PROJECT_SOURCE_DIR}/include/CXXGraph/CXXGraphConfig.h) diff --git a/README.md b/README.md index 30e2008c8..d9930e982 100755 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ If you are interested, please contact us at zigrazor@gmail.com or contribute to | :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 | diff --git a/benchmark/BellmanFord_BM.cpp b/benchmark/BellmanFord_BM.cpp index d3e349aa3..8573b894b 100644 --- a/benchmark/BellmanFord_BM.cpp +++ b/benchmark/BellmanFord_BM.cpp @@ -30,4 +30,4 @@ static void BellmanFord_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(BellmanFord_FromReadedCitHep); \ No newline at end of file +BENCHMARK(BellmanFord_FromReadedCitHep); diff --git a/benchmark/Boruvka_BM.cpp b/benchmark/Boruvka_BM.cpp index fbc52ed34..2d5d77ae4 100644 --- a/benchmark/Boruvka_BM.cpp +++ b/benchmark/Boruvka_BM.cpp @@ -26,4 +26,4 @@ static void Boruvka_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(Boruvka_FromReadedCitHep); \ No newline at end of file +BENCHMARK(Boruvka_FromReadedCitHep); diff --git a/benchmark/Connectivity_BM.cpp b/benchmark/Connectivity_BM.cpp index c5258f31f..234e9111c 100644 --- a/benchmark/Connectivity_BM.cpp +++ b/benchmark/Connectivity_BM.cpp @@ -53,4 +53,4 @@ static void StrongConnectivity_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(StrongConnectivity_FromReadedCitHep); \ No newline at end of file +BENCHMARK(StrongConnectivity_FromReadedCitHep); diff --git a/benchmark/CycleCheck_BM.cpp b/benchmark/CycleCheck_BM.cpp index 785555d2f..93a0f43c9 100644 --- a/benchmark/CycleCheck_BM.cpp +++ b/benchmark/CycleCheck_BM.cpp @@ -53,4 +53,4 @@ static void CycleCheckDFS_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(CycleCheckDFS_FromReadedCitHep); \ No newline at end of file +BENCHMARK(CycleCheckDFS_FromReadedCitHep); diff --git a/benchmark/Dial_BM.cpp b/benchmark/Dial_BM.cpp index 5d9cecf4e..b70c653c5 100644 --- a/benchmark/Dial_BM.cpp +++ b/benchmark/Dial_BM.cpp @@ -27,4 +27,4 @@ static void Dial_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(Dial_FromReadedCitHep); \ No newline at end of file +BENCHMARK(Dial_FromReadedCitHep); diff --git a/benchmark/Edge_BM.cpp b/benchmark/Edge_BM.cpp index bfeec4f96..95f8e0cfa 100644 --- a/benchmark/Edge_BM.cpp +++ b/benchmark/Edge_BM.cpp @@ -47,4 +47,4 @@ static void NodeGetNodePair(benchmark::State &state) { e.getNodePair(); } } -BENCHMARK(NodeGetNodePair); \ No newline at end of file +BENCHMARK(NodeGetNodePair); diff --git a/benchmark/FloydWarshall_BM.cpp b/benchmark/FloydWarshall_BM.cpp index 25f665eff..127d8c49d 100644 --- a/benchmark/FloydWarshall_BM.cpp +++ b/benchmark/FloydWarshall_BM.cpp @@ -1,4 +1,5 @@ #include + #include #include "CXXGraph/CXXGraph.hpp" diff --git a/benchmark/FordFulkerson_BM.cpp b/benchmark/FordFulkerson_BM.cpp index 178e5c90b..6afd9699d 100644 --- a/benchmark/FordFulkerson_BM.cpp +++ b/benchmark/FordFulkerson_BM.cpp @@ -31,4 +31,4 @@ static void FordFulkerson_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(FordFulkerson_FromReadedCitHep); \ No newline at end of file +BENCHMARK(FordFulkerson_FromReadedCitHep); diff --git a/benchmark/Kruskal_BM.cpp b/benchmark/Kruskal_BM.cpp index a91127e7c..a6d29fa0e 100644 --- a/benchmark/Kruskal_BM.cpp +++ b/benchmark/Kruskal_BM.cpp @@ -26,4 +26,4 @@ static void Kruskal_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(Kruskal_FromReadedCitHep); \ No newline at end of file +BENCHMARK(Kruskal_FromReadedCitHep); diff --git a/benchmark/Node_BM.cpp b/benchmark/Node_BM.cpp index 350bbd96a..a1604c20b 100644 --- a/benchmark/Node_BM.cpp +++ b/benchmark/Node_BM.cpp @@ -33,4 +33,4 @@ static void NodeGetData(benchmark::State &state) { n1.getData(); } } -BENCHMARK(NodeGetData); \ No newline at end of file +BENCHMARK(NodeGetData); diff --git a/benchmark/Partition_BM.cpp b/benchmark/Partition_BM.cpp index 5f2f0e46a..c4c6d69c8 100644 --- a/benchmark/Partition_BM.cpp +++ b/benchmark/Partition_BM.cpp @@ -113,4 +113,4 @@ static void PartitionEBV_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(PartitionEBV_FromReadedCitHep); \ No newline at end of file +BENCHMARK(PartitionEBV_FromReadedCitHep); diff --git a/benchmark/Prim_BM.cpp b/benchmark/Prim_BM.cpp index f4a34457f..4b1d75682 100644 --- a/benchmark/Prim_BM.cpp +++ b/benchmark/Prim_BM.cpp @@ -26,4 +26,4 @@ static void Prim_FromReadedCitHep(benchmark::State &state) { } } -BENCHMARK(Prim_FromReadedCitHep); \ No newline at end of file +BENCHMARK(Prim_FromReadedCitHep); diff --git a/benchmark/main.cpp b/benchmark/main.cpp index 2a3fbf94a..71fefa047 100644 --- a/benchmark/main.cpp +++ b/benchmark/main.cpp @@ -1,3 +1,3 @@ #include -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7b3cbc33d..2a485af5d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,5 +5,7 @@ add_subdirectory(DialExample) add_subdirectory(DijkstraExample) add_subdirectory(NetworkDynamicsExample) add_subdirectory(PartitionExample) +add_subdirectory(PrimExample) +add_subdirectory(FloydWarshallExample) endif(EXAMPLES) \ No newline at end of file diff --git a/examples/DialExample/dial_example.cpp b/examples/DialExample/dial_example.cpp index 0b06dc361..bbcde54c9 100644 --- a/examples/DialExample/dial_example.cpp +++ b/examples/DialExample/dial_example.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include using std::make_shared; diff --git a/examples/DijkstraExample/dijkstra_example.cpp b/examples/DijkstraExample/dijkstra_example.cpp index bd482ad65..4c376f207 100644 --- a/examples/DijkstraExample/dijkstra_example.cpp +++ b/examples/DijkstraExample/dijkstra_example.cpp @@ -1,5 +1,4 @@ #include - #include using std::make_shared; diff --git a/examples/FloydWarshallExample/CMakeLists.txt b/examples/FloydWarshallExample/CMakeLists.txt new file mode 100644 index 000000000..8987bd25d --- /dev/null +++ b/examples/FloydWarshallExample/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.9) +project(floydWarshallExample) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) + +add_executable(floyd_warshall floyd_warshall.cpp) +target_include_directories(floyd_warshall PUBLIC "${CMAKE_SOURCE_DIR}/include") + +target_link_libraries(floyd_warshall + pthread + ssl + crypto + z) \ No newline at end of file diff --git a/examples/FloydWarshallExample/floyd_warshall.cpp b/examples/FloydWarshallExample/floyd_warshall.cpp new file mode 100644 index 000000000..d535a376c --- /dev/null +++ b/examples/FloydWarshallExample/floyd_warshall.cpp @@ -0,0 +1,45 @@ +#include +#include + +using std::make_shared; +// example taken from +// https://www.youtube.com/watch?v=B06q2yjr-Cc +int main() { + 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::UndirectedWeightedEdge edge1(1, node0, node1, 1); + CXXGraph::UndirectedWeightedEdge edge2(2, node0, node2, 2); + CXXGraph::UndirectedWeightedEdge edge3(3, node1, node2, 6); + CXXGraph::UndirectedWeightedEdge edge4(4, node1, node3, 4); + CXXGraph::UndirectedWeightedEdge edge5(5, node2, node3, 5); + CXXGraph::UndirectedWeightedEdge edge6(6, node3, node4, 3); + + 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)); + // Can print out the edges for debugging + std::cout << edge1 << "\n"; + std::cout << edge2 << "\n"; + std::cout << edge3 << "\n"; + std::cout << edge4 << "\n"; + std::cout << edge5 << "\n"; + std::cout << edge6 << "\n"; + + CXXGraph::Graph graph(edgeSet); + CXXGraph::FWResult res = graph.floydWarshall(); + std::cout << "floyd Warshall Result: " + << "\n"; + for (auto i : res.result) { + std::cout << "distance of: " << i.first.first << " " << i.first.second + << " = " << i.second << "\n"; + } + return 0; +} diff --git a/examples/NetworkDynamicsExample/network_dynamics_example.cpp b/examples/NetworkDynamicsExample/network_dynamics_example.cpp index 522e11df9..5d7f17124 100644 --- a/examples/NetworkDynamicsExample/network_dynamics_example.cpp +++ b/examples/NetworkDynamicsExample/network_dynamics_example.cpp @@ -1,7 +1,7 @@ -#include "CXXGraph/CXXGraph.hpp" - #include +#include "CXXGraph/CXXGraph.hpp" + using std::make_shared; int main() { @@ -25,11 +25,12 @@ int main() { auto degreeMatrix = graph.getDegreeMatrix(); for (const auto& nodePair : *degreeMatrix) { - const CXXGraph::shared>& node = nodePair.first; - const std::vector& degrees = nodePair.second; + const CXXGraph::shared>& node = nodePair.first; + const std::vector& degrees = nodePair.second; - std::cout << "Node: " << node->getId() << ", Degree: " << degrees[0] << "\n"; - } + std::cout << "Node: " << node->getId() << ", Degree: " << degrees[0] + << "\n"; + } auto laplacianMatrix = graph.getLaplacianMatrix(); for (const auto& nodePair : *laplacianMatrix) { const auto& node = nodePair.first; @@ -37,11 +38,13 @@ int main() { std::cout << "Node " << node->getId() << " connected to:" << std::endl; for (const auto& neighbor : neighbors) { - if (neighbor.first == node) { - std::cout << " -> Itself" << std::endl; - } else { - std::cout << " -> Node " << neighbor.first->getId() << " with Edge ID " << (neighbor.second ? neighbor.second->getId() : -1) << std::endl; - } + if (neighbor.first == node) { + std::cout << " -> Itself" << std::endl; + } else { + std::cout << " -> Node " << neighbor.first->getId() << " with Edge ID " + << (neighbor.second ? neighbor.second->getId() : -1) + << std::endl; + } } std::cout << std::endl; } @@ -53,7 +56,8 @@ int main() { std::cout << "Transitions from Node " << node->getId() << ":" << std::endl; for (const auto& transition : transitions) { - std::cout << " -> To Node " << transition.first->getId() << " with Probability " << transition.second << std::endl; + std::cout << " -> To Node " << transition.first->getId() + << " with Probability " << transition.second << std::endl; } std::cout << std::endl; } diff --git a/examples/PartitionExample/partition_example.cpp b/examples/PartitionExample/partition_example.cpp index e359d9ce0..75e3cbd8a 100644 --- a/examples/PartitionExample/partition_example.cpp +++ b/examples/PartitionExample/partition_example.cpp @@ -19,14 +19,17 @@ int main() { // std::cout << *cit_graph_ptr << std::endl; std::cout << cit_graph_ptr->getEdgeSet().size() << std::endl; std::cout << cit_graph_ptr->getNodeSet().size() << std::endl; - auto partitionedTwo = cit_graph_ptr->partitionGraph( - CXXGraph::Partitioning::HDRF_ALG, 2, 1, 1, 1, 4); + auto partitionedTwo = + CXXGraph::Partitioning::Partitioner::partitionGraph( + *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 2, 1, 1, 1, 4); std::cout << "end partition two" << std::endl; - auto partitionedFour = cit_graph_ptr->partitionGraph( - CXXGraph::Partitioning::HDRF_ALG, 4, 1, 1, 1, 4); + auto partitionedFour = + CXXGraph::Partitioning::Partitioner::partitionGraph( + *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 4, 1, 1, 1, 4); std::cout << "end partition four" << std::endl; - auto partitionedEight = cit_graph_ptr->partitionGraph( - CXXGraph::Partitioning::HDRF_ALG, 8, 1, 1, 1, 4); + auto partitionedEight = + CXXGraph::Partitioning::Partitioner::partitionGraph( + *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 8, 1, 1, 1, 4); std::cout << "end partition eight" << std::endl; auto statsTwo = CXXGraph::Partitioning::getPartitionStats(partitionedTwo); auto statsFour = CXXGraph::Partitioning::getPartitionStats(partitionedFour); diff --git a/examples/PrimExample/CMakeLists.txt b/examples/PrimExample/CMakeLists.txt new file mode 100644 index 000000000..8908be132 --- /dev/null +++ b/examples/PrimExample/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.9) +project(PrimExample) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) + +add_executable(prim_example prim_example.cpp) +target_include_directories(prim_example PUBLIC "${CMAKE_SOURCE_DIR}/include") + +target_link_libraries(prim_example + pthread + ssl + crypto + z) \ No newline at end of file diff --git a/examples/PrimExample/prim_example.cpp b/examples/PrimExample/prim_example.cpp new file mode 100644 index 000000000..308421422 --- /dev/null +++ b/examples/PrimExample/prim_example.cpp @@ -0,0 +1,80 @@ +#include +#include + +using std::make_shared; + +// example taken from +// https://www.geeksforgeeks.org/prims-mst-for-adjacency-list-representation-greedy-algo-6/TEST(FWTest, +// test_1) +int main() { + 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(4, node1, node2, 8); + CXXGraph::UndirectedWeightedEdge edge5(5, node2, node8, 2); + CXXGraph::UndirectedWeightedEdge edge6(6, node2, node5, 4); + CXXGraph::UndirectedWeightedEdge edge7(7, node2, node3, 7); + CXXGraph::UndirectedWeightedEdge edge8(8, node3, node3, 1); + CXXGraph::UndirectedWeightedEdge edge9(9, node3, node4, 9); + CXXGraph::UndirectedWeightedEdge edge10(10, node3, node5, 14); + CXXGraph::UndirectedWeightedEdge edge11(11, node4, node5, 10); + CXXGraph::UndirectedWeightedEdge edge12(12, node5, node6, 2); + CXXGraph::UndirectedWeightedEdge edge13(13, node6, node8, 6); + CXXGraph::UndirectedWeightedEdge edge14(14, node6, node7, 1); + CXXGraph::UndirectedWeightedEdge edge15(15, node7, node8, 7); + + 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)); + edgeSet.insert(make_shared>(edge15)); + + // Can print out the edges for debugging + std::cout << edge1 << "\n"; + std::cout << edge2 << "\n"; + std::cout << edge3 << "\n"; + std::cout << edge4 << "\n"; + std::cout << edge5 << "\n"; + std::cout << edge6 << "\n"; + std::cout << edge7 << "\n"; + std::cout << edge8 << "\n"; + std::cout << edge9 << "\n"; + std::cout << edge10 << "\n"; + std::cout << edge11 << "\n"; + std::cout << edge12 << "\n"; + std::cout << edge13 << "\n"; + std::cout << edge14 << "\n"; + std::cout << edge15 << "\n"; + + CXXGraph::Graph graph(edgeSet); + auto res = graph.prim(); + std::cout << "Prim Result: " + << "\n"; + for (auto edge : res.mst) { + std::cout << edge.first << " " << edge.second << "\n"; + } + std::cout << "mstCost: " << res.mstCost << "\n"; + + return 0; +} diff --git a/include/CXXGraph/CXXGraph.hpp b/include/CXXGraph/CXXGraph.hpp index 4700ac8a5..e64fd9f87 100755 --- a/include/CXXGraph/CXXGraph.hpp +++ b/include/CXXGraph/CXXGraph.hpp @@ -2,14 +2,14 @@ #define __CXXGRAPH_H__ #include "CXXGraph/CXXGraphConfig.h" -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Graph/Graph.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Graph/Graph.h" +#include "CXXGraph/Node/Node.h" #include "CXXGraph/Partitioning/CoordinatedPartitionState.hpp" #include "CXXGraph/Partitioning/CoordinatedRecord.hpp" #include "CXXGraph/Partitioning/EBV.hpp" diff --git a/include/CXXGraph/CXXGraphConfig.h b/include/CXXGraph/CXXGraphConfig.h index 6ef7f026f..eb3aa8954 100644 --- a/include/CXXGraph/CXXGraphConfig.h +++ b/include/CXXGraph/CXXGraphConfig.h @@ -1,4 +1,4 @@ // the configured options and settings for CXXGraph #define CXXGraph_VERSION_MAJOR 3 -#define CXXGraph_VERSION_MINOR 0 +#define CXXGraph_VERSION_MINOR 1 #define CXXGraph_VERSION_PATCH 0 diff --git a/include/CXXGraph/Edge/DirectedEdge.h b/include/CXXGraph/Edge/DirectedEdge.h new file mode 100755 index 000000000..3ae44da60 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIRECTEDEDGE_H__ +#define __CXXGRAPH_DIRECTEDEDGE_H__ + +#pragma once + +#include "DirectedEdge_impl.hpp" + +#endif // __CXXGRAPH_DIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedEdge_decl.h b/include/CXXGraph/Edge/DirectedEdge_decl.h new file mode 100644 index 000000000..12329ac3a --- /dev/null +++ b/include/CXXGraph/Edge/DirectedEdge_decl.h @@ -0,0 +1,70 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIRECTEDEDGE_DECL_H__ +#define __CXXGRAPH_DIRECTEDEDGE_DECL_H__ + +#pragma once + +#include "Edge_decl.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +template +class UndirectedEdge; + +template +class DirectedEdge; +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const DirectedEdge &edge); +template +class DirectedEdge : public Edge { + public: + DirectedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2); + DirectedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2); + DirectedEdge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + DirectedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + DirectedEdge(const Edge &edge); + virtual ~DirectedEdge() = default; + const Node &getFrom() const; + const Node &getTo() const; + const std::optional isDirected() const override; + const std::optional isWeighted() const override; + // operator + explicit operator UndirectedEdge() const { + return UndirectedEdge(Edge::getId(), Edge::getNodePair()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const DirectedEdge &edge); +}; +} // namespace CXXGraph + +#endif // __CXXGRAPH_DIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedEdge.hpp b/include/CXXGraph/Edge/DirectedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 60% rename from include/CXXGraph/Edge/DirectedEdge.hpp rename to include/CXXGraph/Edge/DirectedEdge_impl.hpp index 50df9a709..1ac736044 --- a/include/CXXGraph/Edge/DirectedEdge.hpp +++ b/include/CXXGraph/Edge/DirectedEdge_impl.hpp @@ -17,56 +17,17 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_DIRECTEDEDGE_H__ -#define __CXXGRAPH_DIRECTEDEDGE_H__ +#ifndef __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ +#define __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ #pragma once -#include "Edge.hpp" +#include "DirectedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; -using std::make_unique; using std::make_shared; - -template -class UndirectedEdge; - -template -class DirectedEdge; -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const DirectedEdge &edge); -template -class DirectedEdge : public Edge { - public: - DirectedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2); - DirectedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2); - DirectedEdge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - DirectedEdge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - DirectedEdge(const Edge &edge); - virtual ~DirectedEdge() = default; - const Node &getFrom() const; - const Node &getTo() const; - const std::optional isDirected() const override; - const std::optional isWeighted() const override; - // operator - explicit operator UndirectedEdge() const { - return UndirectedEdge(Edge::getId(), Edge::getNodePair()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const DirectedEdge &edge); -}; +using std::make_unique; template DirectedEdge::DirectedEdge(const CXXGraph::id_t id, const Node &node1, @@ -74,8 +35,10 @@ DirectedEdge::DirectedEdge(const CXXGraph::id_t id, const Node &node1, : Edge(id, node1, node2) {} template -DirectedEdge::DirectedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2) : Edge(id, node1, node2) {} +DirectedEdge::DirectedEdge(const CXXGraph::id_t id, + shared> node1, + shared> node2) + : Edge(id, node1, node2) {} template DirectedEdge::DirectedEdge( @@ -122,4 +85,4 @@ std::ostream &operator<<(std::ostream &os, const DirectedEdge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_DIRECTEDEDGE_H__ +#endif // __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge.h b/include/CXXGraph/Edge/DirectedWeightedEdge.h new file mode 100755 index 000000000..e5dd687c3 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedWeightedEdge.h @@ -0,0 +1,26 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ + +#pragma once + +#include "DirectedWeightedEdge_impl.hpp" + +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h b/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h new file mode 100644 index 000000000..3a08ff063 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h @@ -0,0 +1,79 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ + +#pragma once + +#include "DirectedEdge_decl.h" +#include "Weighted.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +// Foward Declaration +template +class UndirectedWeightedEdge; + +template +class DirectedWeightedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const DirectedWeightedEdge &edge); + +template +class DirectedWeightedEdge : public DirectedEdge, public Weighted { + public: + DirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2, const double weight); + DirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2, const double weight); + DirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair, + const double weight); + DirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair, + const double weight); + DirectedWeightedEdge(const DirectedEdge &edge, const double weight); + DirectedWeightedEdge(const Edge &edge, const double weight); + DirectedWeightedEdge(const DirectedEdge &edge); + DirectedWeightedEdge(const Edge &edge); + DirectedWeightedEdge(const UndirectedWeightedEdge &edge); + virtual ~DirectedWeightedEdge() = default; + const std::optional isWeighted() const override; + // operator + explicit operator UndirectedWeightedEdge() const { + return UndirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), + Weighted::getWeight()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const DirectedWeightedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge.hpp b/include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/DirectedWeightedEdge.hpp rename to include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp index c0f2eaa07..723ebd49c --- a/include/CXXGraph/Edge/DirectedWeightedEdge.hpp +++ b/include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp @@ -16,66 +16,18 @@ /***********************************************************/ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ -#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ #pragma once -#include "DirectedEdge.hpp" -#include "Weighted.hpp" +#include "DirectedWeightedEdge_decl.h" +#include "Weighted.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; -using std::make_unique; using std::make_shared; - -// Foward Declaration -template -class UndirectedWeightedEdge; - -template -class DirectedWeightedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const DirectedWeightedEdge &edge); - -template -class DirectedWeightedEdge : public DirectedEdge, public Weighted { - public: - DirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2, const double weight); - DirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2, const double weight); - DirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair, - const double weight); - DirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair, - const double weight); - DirectedWeightedEdge(const DirectedEdge &edge, const double weight); - DirectedWeightedEdge(const Edge &edge, const double weight); - DirectedWeightedEdge(const DirectedEdge &edge); - DirectedWeightedEdge(const Edge &edge); - DirectedWeightedEdge(const UndirectedWeightedEdge &edge); - virtual ~DirectedWeightedEdge() = default; - const std::optional isWeighted() const override; - // operator - explicit operator UndirectedWeightedEdge() const { - return UndirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), - Weighted::getWeight()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const DirectedWeightedEdge &edge); -}; +using std::make_unique; template DirectedWeightedEdge::DirectedWeightedEdge(const CXXGraph::id_t id, @@ -144,4 +96,4 @@ std::ostream &operator<<(std::ostream &os, } // namespace CXXGraph -#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/Edge.h b/include/CXXGraph/Edge/Edge.h new file mode 100755 index 000000000..377886a39 --- /dev/null +++ b/include/CXXGraph/Edge/Edge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_EDGE_H__ +#define __CXXGRAPH_EDGE_H__ + +#pragma once + +#include "CXXGraph/Edge/Edge_impl.hpp" + +#endif // __CXXGRAPH_EDGE_H__ diff --git a/include/CXXGraph/Edge/Edge_decl.h b/include/CXXGraph/Edge/Edge_decl.h new file mode 100644 index 000000000..bca27aac4 --- /dev/null +++ b/include/CXXGraph/Edge/Edge_decl.h @@ -0,0 +1,77 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_EDGE_DECL_H__ +#define __CXXGRAPH_EDGE_DECL_H__ + +#pragma once + +#include +#include +#include + +#include "CXXGraph/Node/Node.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +template +class Edge; +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const Edge &edge); +template +class Edge { + private: + CXXGraph::id_t id = 0; + std::pair>, shared>> nodePair; + + public: + typedef T Node_t; + + Edge(const CXXGraph::id_t id, const Node &node1, const Node &node2); + Edge(const CXXGraph::id_t id, shared> node1, + shared> node2); + Edge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + Edge(const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + virtual ~Edge() = default; + void setFirstNode(shared> node); + void setSecondNode(shared> node); + const unsigned long long getId() const; + const std::pair>, shared>> &getNodePair() + const; + shared> getOtherNode(shared> node) const; + virtual const std::optional isDirected() const; + virtual const std::optional isWeighted() const; + // operator + virtual bool operator==(const Edge &b) const; + bool operator<(const Edge &b) const; + + friend std::ostream &operator<< <>(std::ostream &os, const Edge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_EDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/Edge.hpp b/include/CXXGraph/Edge/Edge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/Edge.hpp rename to include/CXXGraph/Edge/Edge_impl.hpp index a846c400e..0c025a49e --- a/include/CXXGraph/Edge/Edge.hpp +++ b/include/CXXGraph/Edge/Edge_impl.hpp @@ -17,17 +17,12 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_EDGE_H__ -#define __CXXGRAPH_EDGE_H__ +#ifndef __CXXGRAPH_EDGE_IMPL_H__ +#define __CXXGRAPH_EDGE_IMPL_H__ #pragma once -#include -#include -#include - -#include "CXXGraph/Node/Node.hpp" -#include "CXXGraph/Utility/id_t.hpp" +#include "CXXGraph/Edge/Edge_decl.h" namespace CXXGraph { // Smart pointers alias @@ -36,44 +31,8 @@ using unique = std::unique_ptr; template using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; - -template -class Edge; -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const Edge &edge); -template -class Edge { - private: - CXXGraph::id_t id = 0; - std::pair>, shared>> nodePair; - - public: - Edge(const CXXGraph::id_t id, const Node &node1, const Node &node2); - Edge(const CXXGraph::id_t id, shared> node1, shared> node2); - Edge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - Edge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - virtual ~Edge() = default; - void setFirstNode(shared> node); - void setSecondNode(shared> node); - const unsigned long long getId() const; - const std::pair>, shared>> &getNodePair() const; - shared> getOtherNode(shared> node) const; - virtual const std::optional isDirected() const; - virtual const std::optional isWeighted() const; - // operator - virtual bool operator==(const Edge &b) const; - bool operator<(const Edge &b) const; - // operator DirectedEdge() const { return DirectedEdge(id, nodePair); } - // operator UndirectedEdge() const { return UndirectedEdge(id, - // nodePair); } - - friend std::ostream &operator<< <>(std::ostream &os, const Edge &edge); -}; +using std::make_unique; template Edge::Edge(const CXXGraph::id_t id, const Node &node1, @@ -84,7 +43,8 @@ Edge::Edge(const CXXGraph::id_t id, const Node &node1, } template -Edge::Edge(const CXXGraph::id_t id, shared> node1, shared> node2) { +Edge::Edge(const CXXGraph::id_t id, shared> node1, + shared> node2) { this->nodePair.first = node1; this->nodePair.second = node2; this->id = id; @@ -99,8 +59,9 @@ Edge::Edge(const CXXGraph::id_t id, } template -Edge::Edge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair) +Edge::Edge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair) : nodePair(nodepair) { this->id = id; } @@ -123,8 +84,8 @@ const unsigned long long Edge::getId() const { } template -const std::pair>, shared>> &Edge::getNodePair() - const { +const std::pair>, shared>> & +Edge::getNodePair() const { return nodePair; } @@ -166,4 +127,4 @@ std::ostream &operator<<(std::ostream &os, const Edge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_EDGE_H__ +#endif // __CXXGRAPH_EDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/UndirectedEdge.h b/include/CXXGraph/Edge/UndirectedEdge.h new file mode 100755 index 000000000..6d15b7f22 --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_H__ + +#pragma once + +#include "UndirectedEdge_impl.hpp" + +#endif // __CXXGRAPH_UNDIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/UndirectedEdge_decl.h b/include/CXXGraph/Edge/UndirectedEdge_decl.h new file mode 100644 index 000000000..013d832eb --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedEdge_decl.h @@ -0,0 +1,70 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ + +#pragma once + +#include "Edge_decl.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +template +class UndirectedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const UndirectedEdge &edge); + +template +class UndirectedEdge : public Edge { + public: + UndirectedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2); + UndirectedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2); + UndirectedEdge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + UndirectedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + UndirectedEdge(const Edge &edge); + virtual ~UndirectedEdge() = default; + const Node &getNode1() const; + const Node &getNode2() const; + const std::optional isDirected() const override; + const std::optional isWeighted() const override; + // operator + explicit operator DirectedEdge() const { + return DirectedEdge(Edge::getId(), Edge::getNodePair()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const UndirectedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/UndirectedEdge.hpp b/include/CXXGraph/Edge/UndirectedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 62% rename from include/CXXGraph/Edge/UndirectedEdge.hpp rename to include/CXXGraph/Edge/UndirectedEdge_impl.hpp index 6678656bc..29510ab07 --- a/include/CXXGraph/Edge/UndirectedEdge.hpp +++ b/include/CXXGraph/Edge/UndirectedEdge_impl.hpp @@ -17,55 +17,17 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_UNDIRECTEDEDGE_H__ -#define __CXXGRAPH_UNDIRECTEDEDGE_H__ +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ #pragma once -#include "Edge.hpp" +#include "UndirectedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; -using std::make_unique; using std::make_shared; - -template -class UndirectedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const UndirectedEdge &edge); - -template -class UndirectedEdge : public Edge { - public: - UndirectedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2); - UndirectedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2); - UndirectedEdge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - UndirectedEdge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - UndirectedEdge(const Edge &edge); - virtual ~UndirectedEdge() = default; - const Node &getNode1() const; - const Node &getNode2() const; - const std::optional isDirected() const override; - const std::optional isWeighted() const override; - // operator - explicit operator DirectedEdge() const { - return DirectedEdge(Edge::getId(), Edge::getNodePair()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const UndirectedEdge &edge); -}; +using std::make_unique; template UndirectedEdge::UndirectedEdge(const CXXGraph::id_t id, const Node &node1, @@ -73,7 +35,8 @@ UndirectedEdge::UndirectedEdge(const CXXGraph::id_t id, const Node &node1, : Edge(id, node1, node2) {} template -UndirectedEdge::UndirectedEdge(const CXXGraph::id_t id, shared> node1, +UndirectedEdge::UndirectedEdge(const CXXGraph::id_t id, + shared> node1, shared> node2) : Edge(id, node1, node2) {} @@ -122,4 +85,4 @@ std::ostream &operator<<(std::ostream &os, const UndirectedEdge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_UNDIRECTEDEDGE_H__ +#endif // __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge.h b/include/CXXGraph/Edge/UndirectedWeightedEdge.h new file mode 100755 index 000000000..7f7e28cca --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ + +#pragma once + +#include "UndirectedWeightedEdge_impl.hpp" + +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h b/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h new file mode 100644 index 000000000..b0d0b9565 --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h @@ -0,0 +1,81 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ + +#pragma once + +#include "UndirectedEdge_decl.h" +#include "Weighted.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +// Foward Declaration +template +class DirectedWeightedEdge; + +template +class UndirectedWeightedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, + const UndirectedWeightedEdge &edge); + +template +class UndirectedWeightedEdge : public UndirectedEdge, public Weighted { + public: + UndirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2, const double weight); + UndirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2, const double weight); + UndirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair, + const double weight); + UndirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair, + const double weight); + UndirectedWeightedEdge(const UndirectedEdge &edge, const double weight); + UndirectedWeightedEdge(const Edge &edge, const double weight); + UndirectedWeightedEdge(const UndirectedEdge &edge); + UndirectedWeightedEdge(const Edge &edge); + UndirectedWeightedEdge(const DirectedWeightedEdge &edge); + virtual ~UndirectedWeightedEdge() = default; + const std::optional isWeighted() const override; + // operator + explicit operator DirectedWeightedEdge() const { + return DirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), + Weighted::getWeight()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const UndirectedWeightedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge.hpp b/include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/UndirectedWeightedEdge.hpp rename to include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp index 97b4ae2b7..61f331eb8 --- a/include/CXXGraph/Edge/UndirectedWeightedEdge.hpp +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp @@ -17,67 +17,17 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ -#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ #pragma once -#include "UndirectedEdge.hpp" -#include "Weighted.hpp" +#include "UndirectedWeightedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; -using std::make_unique; using std::make_shared; - -// Foward Declaration -template -class DirectedWeightedEdge; - -template -class UndirectedWeightedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, - const UndirectedWeightedEdge &edge); - -template -class UndirectedWeightedEdge : public UndirectedEdge, public Weighted { - public: - UndirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2, const double weight); - UndirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2, const double weight); - UndirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair, - const double weight); - UndirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair, - const double weight); - UndirectedWeightedEdge(const UndirectedEdge &edge, const double weight); - UndirectedWeightedEdge(const Edge &edge, const double weight); - UndirectedWeightedEdge(const UndirectedEdge &edge); - UndirectedWeightedEdge(const Edge &edge); - UndirectedWeightedEdge(const DirectedWeightedEdge &edge); - virtual ~UndirectedWeightedEdge() = default; - const std::optional isWeighted() const override; - // operator - explicit operator DirectedWeightedEdge() const { - return DirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), - Weighted::getWeight()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const UndirectedWeightedEdge &edge); -}; +using std::make_unique; template UndirectedWeightedEdge::UndirectedWeightedEdge(const CXXGraph::id_t id, @@ -146,4 +96,4 @@ std::ostream &operator<<(std::ostream &os, } // namespace CXXGraph -#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/Weighted.h b/include/CXXGraph/Edge/Weighted.h new file mode 100755 index 000000000..c657e5051 --- /dev/null +++ b/include/CXXGraph/Edge/Weighted.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_WEIGHTED_H__ +#define __CXXGRAPH_WEIGHTED_H__ + +#pragma once + +#include "Weighted_impl.hpp" + +#endif // __CXXGRAPH_WEIGHTED_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/Weighted_decl.h b/include/CXXGraph/Edge/Weighted_decl.h new file mode 100644 index 000000000..5b16a1aa1 --- /dev/null +++ b/include/CXXGraph/Edge/Weighted_decl.h @@ -0,0 +1,39 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_WEIGHTED_DECL_H__ +#define __CXXGRAPH_WEIGHTED_DECL_H__ + +#pragma once + +namespace CXXGraph { +class Weighted { + private: + double weight = 0.0; + + public: + Weighted(); + explicit Weighted(const double weight); + virtual ~Weighted() = default; + double getWeight() const; + void setWeight(const double weight); +}; +} // namespace CXXGraph + +#endif // __CXXGRAPH_WEIGHTED_DECL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/Weighted.hpp b/include/CXXGraph/Edge/Weighted_impl.hpp old mode 100755 new mode 100644 similarity index 83% rename from include/CXXGraph/Edge/Weighted.hpp rename to include/CXXGraph/Edge/Weighted_impl.hpp index cb6b717fd..327d88710 --- a/include/CXXGraph/Edge/Weighted.hpp +++ b/include/CXXGraph/Edge/Weighted_impl.hpp @@ -17,23 +17,14 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_WEIGHTED_H__ -#define __CXXGRAPH_WEIGHTED_H__ +#ifndef __CXXGRAPH_WEIGHTED_IMPL_H__ +#define __CXXGRAPH_WEIGHTED_IMPL_H__ #pragma once +#include "Weighted_decl.h" + namespace CXXGraph { -class Weighted { - private: - double weight = 0.0; - - public: - Weighted(); - explicit Weighted(const double weight); - virtual ~Weighted() = default; - double getWeight() const; - void setWeight(const double weight); -}; // inline because the implementation of non-template function in header file inline Weighted::Weighted() { weight = 0.0; } @@ -49,4 +40,4 @@ inline void Weighted::setWeight(const double weight) { this->weight = weight; } } // namespace CXXGraph -#endif // __CXXGRAPH_WEIGHTED_H__ \ No newline at end of file +#endif // __CXXGRAPH_WEIGHTED_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp b/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp new file mode 100644 index 000000000..cbb6fd036 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp @@ -0,0 +1,129 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BELLMANFORD_IMPL_H__ +#define __CXXGRAPH_BELLMANFORD_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const BellmanFordResult Graph::bellmanford(const Node &source, + const Node &target) const { + BellmanFordResult result; + result.success = false; + result.errorMessage = ""; + result.result = INF_DOUBLE; + auto nodeSet = Graph::getNodeSet(); + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist, + currentDist; + // n denotes the number of vertices in graph + auto n = nodeSet.size(); + for (const auto &elem : nodeSet) { + dist[elem] = INF_DOUBLE; + currentDist[elem] = INF_DOUBLE; + } + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + // set if node distances in two consecutive + // iterations remain the same. + auto earlyStopping = false; + // outer loop for vertex relaxation + for (int i = 0; i < n - 1; ++i) { + auto edgeSet = Graph::getEdgeSet(); + // inner loop for distance updates of + // each relaxation + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) + dist[elem.second] = dist[elem.first] + edge_weight; + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + auto flag = true; + for (const auto &[key, value] : dist) { + if (currentDist[key] != value) { + flag = false; + break; + } + } + for (const auto &[key, value] : dist) { + currentDist[key] = value; // update the current distance + } + if (flag) { + earlyStopping = true; + break; + } + } + + // check if there exists a negative cycle + if (!earlyStopping) { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edge_weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) { + result.success = true; + result.negativeCycle = true; + result.errorMessage = ""; + return result; + } + } + } + + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.negativeCycle = false; + result.result = dist[*target_node_it]; + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + result.result = -1; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_BELLMANFORD_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp new file mode 100644 index 000000000..d61aed940 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp @@ -0,0 +1,262 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include +#include +#include +#include +#include + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +BestFirstSearchResult Graph::best_first_search( + const Node &source, const Node &target) const { + BestFirstSearchResult result; + auto &nodeSet = Graph::getNodeSet(); + using pq_type = std::pair>>; + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + + std::priority_queue, std::greater> pq; + + std::vector> visited; + visited.push_back(source); + pq.push(std::make_pair(0.0, *source_node_it)); + + while (!pq.empty()) { + shared> currentNode = pq.top().second; + pq.pop(); + result.nodesInBestSearchOrder.push_back(*currentNode); + + if (*currentNode == target) { + break; + } + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + if (elem.second->isWeighted().has_value()) { + if (elem.second->isDirected().has_value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } else { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } + } else { + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + result.nodesInBestSearchOrder.clear(); + return result; + } + } + } + } + + result.success = true; + return result; +} + +template +const std::vector> Graph::concurrency_breadth_first_search( + const Node &start, size_t num_threads) const { + std::vector> bfs_result; + // check is exist node in the graph + auto &nodeSet = Graph::getNodeSet(); + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return bfs_result; + } + + std::unordered_map>, size_t, nodeHash> node_to_index; + for (const auto &node : nodeSet) { + node_to_index[node] = node_to_index.size(); + } + std::vector visited(nodeSet.size(), 0); + + // parameter limitations + if (num_threads <= 0) { + std::cout << "Error: number of threads should be greater than 0" + << std::endl; + num_threads = 2; + } + + // vector that stores vertices to be visit + std::vector>> level_tracker, next_level_tracker; + level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + + // mark the starting node as visited + visited[node_to_index[*start_node_it]] = 1; + level_tracker.push_back(*start_node_it); + + // a worker is assigned a small part of tasks for each time + // assignments of tasks in current level and updates of tasks in next + // level are inclusive + std::mutex tracker_mutex; + std::mutex next_tracker_mutex; + std::atomic assigned_tasks = 0; + int num_tasks = 1; + // unit of task assignment, which mean assign block_size tasks to a + // worker each time + int block_size = 1; + int level = 1; + + auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, + &num_tasks, &block_size]() -> std::pair { + /* + std::lock_guard tracker_guard(tracker_mutex); + int task_block_size = std::min(num_tasks - assigned_tasks, + block_size); std::pair task_block{assigned_tasks, + assigned_tasks + task_block_size}; assigned_tasks += task_block_size; + return task_block; + */ + int start = assigned_tasks.fetch_add(block_size); + int end = std::min(num_tasks, start + block_size); + return {start, end}; + }; + + auto submit_result = + [&next_level_tracker, &next_tracker_mutex]( + std::vector>> &submission) -> void { + std::lock_guard tracker_guard(next_tracker_mutex); + next_level_tracker.insert(std::end(next_level_tracker), + std::begin(submission), std::end(submission)); + }; + + // worker thread sleep until it begin to search nodes of next level + std::mutex next_level_mutex; + std::condition_variable next_level_cond; + std::atomic waiting_workers = 0; + + auto bfs_worker = [&]() -> void { + // algorithm is not done + while (!level_tracker.empty()) { + // search for nodes in a level is not done + std::vector>> local_tracker; + while (true) { + auto [start_index, end_index] = extract_tasks(); + if (start_index >= end_index) { + break; + } + + for (int i = start_index; i < end_index; ++i) { + if (cachedAdjMatrix->count(level_tracker[i])) { + for (const auto &elem : cachedAdjMatrix->at(level_tracker[i])) { + int index = (int)node_to_index[elem.first]; + if (visited[index] == 0) { + visited[index] = 1; + local_tracker.push_back(elem.first); + } + } + } + } + } + + // submit local result to global result + if (!local_tracker.empty()) { + submit_result(local_tracker); + } + + // last worker need to do preparation for the next iteration + int cur_level = level; + if (num_threads == 1 + waiting_workers.fetch_add(1)) { + swap(level_tracker, next_level_tracker); + next_level_tracker.clear(); + + // adjust block_size according to number of nodes in next level + block_size = 4; + if (level_tracker.size() <= num_threads * 4) { + block_size = std::max( + 1, static_cast(std::ceil( + static_cast(level_tracker.size()) / num_threads))); + } else if (level_tracker.size() >= num_threads * 64) { + block_size = 16; + } + + num_tasks = (int)level_tracker.size(); + waiting_workers = 0; + assigned_tasks = 0; + level = level + 1; + next_level_cond.notify_all(); + } else { + // not to wait if last worker reachs last statement before notify + // all or even further + std::unique_lock next_level_lock(next_level_mutex); + next_level_cond.wait(next_level_lock, [&level, cur_level]() { + return level != cur_level; + }); + } + } + }; + + std::vector workers; + for (int i = 0; i < num_threads - 1; ++i) { + workers.emplace_back(std::thread(bfs_worker)); + } + bfs_worker(); + + for (auto &worker : workers) { + if (worker.joinable()) { + worker.join(); + } + } + + for (const auto &visited_node : nodeSet) { + if (visited[node_to_index[visited_node]] != 0) { + bfs_result.push_back(*visited_node); + } + } + + return bfs_result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp new file mode 100644 index 000000000..39b2cd6bc --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp @@ -0,0 +1,127 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BORUVKA_IMPL_H__ +#define __CXXGRAPH_BORUVKA_IMPL_H__ + +#pragma once + +#include + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const MstResult Graph::boruvka() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + const auto n = nodeSet.size(); + + // Use std map for storing n subsets. + auto subsets = make_shared>(); + + // Initially there are n different trees. + // Finally there will be one tree that will be MST + auto numTrees = n; + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + const auto edgeSet = Graph::getEdgeSet(); + std::unordered_map edgeWeight; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) + edgeWeight[edge->getId()] = + (std::dynamic_pointer_cast(edge))->getWeight(); + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + (*subsets)[node->getId()] = set; + } + + result.mstCost = 0; // we will store the cost here + // exit when only 1 tree i.e. mst + while (numTrees > 1) { + // Everytime initialize cheapest map + // It stores index of the cheapest edge of subset. + std::unordered_map cheapest; + for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + + // Traverse through all edges and update + // cheapest of every component + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edgeId = edge->getId(); + // Find sets of two corners of current edge + auto set1 = Graph::setFind(subsets, elem.first->getId()); + auto set2 = Graph::setFind(subsets, elem.second->getId()); + + // If two corners of current edge belong to + // same set, ignore current edge + if (set1 == set2) continue; + + // Else check if current edge is closer to previous + // cheapest edges of set1 and set2 + if (cheapest[set1] == INT_MAX || + edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) + cheapest[set1] = edgeId; + + if (cheapest[set2] == INT_MAX || + edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) + cheapest[set2] = edgeId; + } + + // iterate over all the vertices and add picked + // cheapest edges to MST + for (const auto &[nodeId, edgeId] : cheapest) { + // Check if cheapest for current set exists + if (edgeId != INT_MAX) { + auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); + auto set1 = Graph::setFind(subsets, cheapestNode.first->getId()); + auto set2 = Graph::setFind(subsets, cheapestNode.second->getId()); + if (set1 == set2) continue; + result.mstCost += edgeWeight[edgeId]; + auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), + cheapestNode.second->getUserId()); + result.mst.push_back(newEdgeMST); + // take union of set1 and set2 and decrease number of trees + Graph::setUnion(subsets, set1, set2); + numTrees--; + } + } + } + result.success = true; + return result; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_BORUVKA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp new file mode 100644 index 000000000..82d0b33f0 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp @@ -0,0 +1,68 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const std::vector> Graph::breadth_first_search( + const Node &start) const { + // vector to keep track of visited nodes + std::vector> visited; + auto &nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return visited; + } + // queue that stores vertices that need to be further explored + std::queue>> tracker; + + // mark the starting node as visited + visited.push_back(start); + tracker.push(*start_node_it); + while (!tracker.empty()) { + shared> node = tracker.front(); + tracker.pop(); + if (cachedAdjMatrix->find(node) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(node)) { + // if the node is not visited then mark it as visited + // and push it to the queue + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + tracker.push(elem.first); + } + } + } + } + + return visited; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp b/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp new file mode 100644 index 000000000..9783fe3ff --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp @@ -0,0 +1,109 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_CONNECTIVITY_IMPL_H__ +#define __CXXGRAPH_CONNECTIVITY_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +bool Graph::isConnectedGraph() const { + if (!isUndirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function>)> dfs_helper = + [this, &visited, &dfs_helper](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(*(nodeSet.begin())); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; + } + } + return true; + } +} + +template +bool Graph::isStronglyConnectedGraph() const { + if (!isDirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + for (const auto &start_node : nodeSet) { + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function>)> dfs_helper = + [this, &visited, &dfs_helper](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(start_node); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; + } + } + } + return true; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_CONNECTIVITY_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp b/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp new file mode 100644 index 000000000..a66f5e2a0 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp @@ -0,0 +1,231 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_CYCLEDETECTION_IMPL_H__ +#define __CXXGRAPH_CYCLEDETECTION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +bool Graph::isCyclicDirectedGraphDFS() const { + if (!isDirectedGraph()) { + return false; + } + enum nodeStates : uint8_t { not_visited, in_stack, visited }; + auto nodeSet = Graph::getNodeSet(); + + /* State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::unordered_map state; + for (const auto &node : nodeSet) { + state[node->getId()] = not_visited; + } + int stateCounter = 0; + + // Start visiting each node. + for (const auto &node : nodeSet) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node->getId()] == not_visited) { + // Check for cycle. + std::function>, + std::unordered_map &, + shared>)> + isCyclicDFSHelper; + isCyclicDFSHelper = + [this, &isCyclicDFSHelper]( + const std::shared_ptr> adjMatrix, + std::unordered_map &states, + shared> node) { + // Add node "in_stack" state. + states[node->getId()] = in_stack; + + // If the node has children, then recursively visit all + // children of the node. + auto const it = adjMatrix->find(node); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // If state of child node is "not_visited", evaluate that + // child for presence of cycle. + auto state_of_child = states.at((std::get<0>(child))->getId()); + if (state_of_child == not_visited) { + if (isCyclicDFSHelper(adjMatrix, states, + std::get<0>(child))) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that + // there is a cycle in the graph. Return true for + // presence of the cycle. + return true; + } + } + } + + // Current node has been evaluated for the presence of cycle + // and had no cycle. Mark current node as "visited". + states[node->getId()] = visited; + // Return that current node didn't result in any cycles. + return false; + }; + if (isCyclicDFSHelper(cachedAdjMatrix, state, node)) { + return true; + } + } + } + + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; +} + +template +bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { + auto edgeSet_ptr = make_shared>(*edgeSet); + auto subset = make_shared>(); + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet_ptr) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == + (*subset).end()) { + Subset set; + set.parent = id; + set.rank = 0; + (*subset)[id] = set; + } + } + } + return Graph::containsCycle(edgeSet_ptr, subset); +} + +template +bool Graph::containsCycle(shared> edgeSet) const { + auto subset = make_shared>(); + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == + (*subset).end()) { + Subset set; + set.parent = id; + set.rank = 0; + (*subset)[id] = set; + } + } + } + return Graph::containsCycle(edgeSet, subset); +} + +template +bool Graph::containsCycle( + shared> edgeSet, + shared> subset) const { + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 == set2) return true; + Graph::setUnion(subset, set1, set2); + } + return false; +} + +template +bool Graph::isCyclicDirectedGraphBFS() const { + if (!isDirectedGraph()) { + return false; + } + auto nodeSet = Graph::getNodeSet(); + + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const &list : (*cachedAdjMatrix)) { + auto children = list.second; + for (auto const &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } + + std::queue>> can_be_solved; + for (const auto &node : nodeSet) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node->getId()]) { + can_be_solved.emplace(node); + } + } + + // Vertices that need to be traversed. + auto remain = nodeSet.size(); + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto solved = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + auto it = cachedAdjMatrix->find(solved); + if (it != cachedAdjMatrix->end()) { + for (const auto &child : it->second) { + // Check if we can visited the node safely. + if (--indegree[std::get<0>(child)->getId()] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(std::get<0>(child)); + } + } + } + } + + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); +} +} // namespace CXXGraph +#endif // __CXXGRAPH_CYCLEDETECTION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp new file mode 100644 index 000000000..6f3bcf105 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp @@ -0,0 +1,64 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const std::vector> Graph::depth_first_search( + const Node &start) const { + // vector to keep track of visited nodes + std::vector> visited; + auto nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return visited; + } + std::function>, + shared>, std::vector> &)> + explore; + explore = [&explore](const std::shared_ptr> adj, + shared> node, + std::vector> &visited) -> void { + visited.push_back(*node); + if (adj->find(node) != adj->end()) { + for (const auto &x : adj->at(node)) { + if (std::find(visited.begin(), visited.end(), *(x.first)) == + visited.end()) { + explore(adj, x.first, visited); + } + } + } + }; + explore(cachedAdjMatrix, *start_node_it, visited); + + return visited; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp new file mode 100644 index 000000000..ffebbc370 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp @@ -0,0 +1,158 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIAL_IMPL_H__ +#define __CXXGRAPH_DIAL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const DialResult Graph::dial(const Node &source, int maxWeight) const { + DialResult result; + result.success = false; + + auto nodeSet = getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + /* With each distance, iterator to that vertex in + its bucket is stored so that vertex can be deleted + in O(1) at time of updation. So + dist[i].first = distance of ith vertex from src vertex + dits[i].second = vertex i in bucket number */ + auto V = nodeSet.size(); + std::unordered_map>, + std::pair>>, nodeHash> + dist; + + // Initialize all distances as infinite (INF) + for (const auto &node : nodeSet) { + dist[node].first = std::numeric_limits::max(); + } + + // Create buckets B[]. + // B[i] keep vertex of distance label i + std::vector>>> B((maxWeight * V + 1)); + + B[0].push_back(*source_node_it); + dist[*source_node_it].first = 0; + + int idx = 0; + while (true) { + // Go sequentially through buckets till one non-empty + // bucket is found + while (idx < B.size() && B[idx].size() == 0u && idx < maxWeight * V) { + idx++; + } + + // If all buckets are empty, we are done. + if (idx == maxWeight * V) { + break; + } + + // Take top vertex from bucket and pop it + auto u = B[idx].front(); + B[idx].pop_front(); + + // Process all adjacents of extracted vertex 'u' and + // update their distanced if required. + for (const auto &i : (*cachedAdjMatrix)[u]) { + auto v = i.first; + int weight = 0; + if (i.second->isWeighted().has_value() && + i.second->isWeighted().value()) { + if (i.second->isDirected().has_value() && + i.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>(i.second); + weight = (int)dw_edge->getWeight(); + } else if (i.second->isDirected().has_value() && + !i.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + i.second); + weight = (int)udw_edge->getWeight(); + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + auto u_i = std::find_if( + dist.begin(), dist.end(), + [u](std::pair>, + std::pair>>> const &it) { + return (*u == *(it.first)); + }); + + auto v_i = std::find_if( + dist.begin(), dist.end(), + [v](std::pair>, + std::pair>>> const &it) { + return (*v == *(it.first)); + }); + long du = u_i->second.first; + long dv = v_i->second.first; + + // If there is shorted path to v through u. + if (dv > du + weight) { + // If dv is not INF then it must be in B[dv] + // bucket, so erase its entry using iterator + // in O(1) + if (dv != std::numeric_limits::max()) { + auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); + B[dv].erase(findIter); + } + + // updating the distance + dist[v].first = du + weight; + dv = dist[v].first; + + // pushing vertex v into updated distance's bucket + B[dv].push_front(v); + + // storing updated iterator in dist[v].second + dist[v].second = *(B[dv].begin()); + } + } + } + for (const auto &dist_i : dist) { + result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; + } + result.success = true; + + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_DIAL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp new file mode 100644 index 000000000..206991e3e --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp @@ -0,0 +1,152 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIJKSTRA_IMPL_H__ +#define __CXXGRAPH_DIJKSTRA_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const DijkstraResult Graph::dijkstra(const Node &source, + const Node &target) const { + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // n denotes the number of vertices in graph + auto n = cachedAdjMatrix->size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + + for (const auto &node : nodeSet) { + dist[node] = INF_DOUBLE; + } + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(std::make_pair(0.0, *source_node_it)); + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + + std::unordered_map parent; + parent[source.getUserId()] = ""; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().second; + // first element of pair denotes the distance + double currentDist = pq.top().first; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[*target_node_it]; + std::string id = target.getUserId(); + while (parent[id] != "") { + result.path.push_back(id); + id = parent[id]; + } + result.path.push_back(source.getUserId()); + std::reverse(result.path.begin(), result.path.end()); + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_DIJKSTRA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp b/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp new file mode 100644 index 000000000..02aebf06d --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp @@ -0,0 +1,108 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ +#define __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const FWResult Graph::floydWarshall() const { + FWResult result; + result.success = false; + result.errorMessage = ""; + std::unordered_map, double, + CXXGraph::pair_hash> + pairwise_dist; + const auto &nodeSet = Graph::getNodeSet(); + // create a pairwise distance matrix with distance node distances + // set to inf. Distance of node to itself is set as 0. + for (const auto &elem1 : nodeSet) { + for (const auto &elem2 : nodeSet) { + auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); + if (elem1 != elem2) + pairwise_dist[key] = INF_DOUBLE; + else + pairwise_dist[key] = 0.0; + } + } + + const auto &edgeSet = Graph::getEdgeSet(); + // update the weights of nodesfloydWarshall + // connected by edges + for (const auto &edge : edgeSet) { + const auto &elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edgeWeight = + (std::dynamic_pointer_cast(edge))->getWeight(); + auto key = + std::make_pair(elem.first->getUserId(), elem.second->getUserId()); + pairwise_dist[key] = edgeWeight; + if (edge->isDirected() == false) { + auto reverseKey = + std::make_pair(elem.second->getUserId(), elem.first->getUserId()); + pairwise_dist[reverseKey] = edgeWeight; + } + } else { + // if an edge exists but has no weight associated + // with it, we return an error message + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &k : nodeSet) { + // set all vertices as source one by one + for (const auto &src : nodeSet) { + // iterate through all vertices as destination for the + // current source + auto src_k = std::make_pair(src->getUserId(), k->getUserId()); + for (const auto &dst : nodeSet) { + // If vertex k provides a shorter path than + // src to dst, update the value of + // pairwise_dist[src_to_dst] + auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); + auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); + if (pairwise_dist[src_dst] > + (pairwise_dist[src_k] + pairwise_dist[k_dst]) && + (pairwise_dist[k_dst] != INF_DOUBLE && + pairwise_dist[src_k] != INF_DOUBLE)) + pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; + } + } + } + + result.success = true; + // presense of negative number in the diagonal indicates + // that that the graph contains a negative cycle + for (const auto &node : nodeSet) { + auto diag = std::make_pair(node->getUserId(), node->getUserId()); + if (pairwise_dist[diag] < 0.) { + result.negativeCycle = true; + return result; + } + } + result.result = std::move(pairwise_dist); + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp b/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp new file mode 100644 index 000000000..1a1ad3efc --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp @@ -0,0 +1,108 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_FORDFULKERSON_IMPL_H__ +#define __CXXGRAPH_FORDFULKERSON_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +double Graph::fordFulkersonMaxFlow(const Node &source, + const Node &target) const { + if (!isDirectedGraph()) { + return -1; + } + double maxFlow = 0; + std::unordered_map>, shared>, nodeHash> + parent; + std::unordered_map< + shared>, + std::unordered_map>, double, nodeHash>, + nodeHash> + weightMap; + // build weight map + auto edgeSet = this->getEdgeSet(); + for (const auto &edge : edgeSet) { + // The Edge are all Directed at this point because is checked at the + // start + if (edge->isWeighted().value_or(false)) { + shared> dw_edge = + std::static_pointer_cast>(edge); + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + dw_edge->getWeight(); + } else { + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + 0; // No Weighted Edge are assumed to be 0 weigthed + } + } + + // Constuct iterators for source and target nodes in nodeSet + auto nodeSet = getNodeSet(); + auto source_node_ptr = *std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + auto target_node_ptr = *std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + + auto bfs_helper = [this, &source_node_ptr, &target_node_ptr, &parent, + &weightMap]() -> bool { + std::unordered_map>, bool, nodeHash> visited; + std::queue>> queue; + queue.push(source_node_ptr); + visited[source_node_ptr] = true; + parent[source_node_ptr] = nullptr; + while (!queue.empty()) { + auto u = queue.front(); + queue.pop(); + for (auto &v : weightMap[u]) { + if (!visited[v.first] && v.second > 0) { + queue.push(v.first); + visited[v.first] = true; + parent[v.first] = u; + } + } + } + + return (visited[target_node_ptr]); + }; + // Updating the residual values of edges + while (bfs_helper()) { + double pathFlow = std::numeric_limits::max(); + for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { + auto u = parent[v]; + pathFlow = std::min(pathFlow, weightMap[u][v]); + } + for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { + auto u = parent[v]; + weightMap[u][v] -= pathFlow; + weightMap[v][u] += pathFlow; + } + // Adding the path flows + maxFlow += pathFlow; + } + + return maxFlow; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_FORDFULKERSON_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp new file mode 100644 index 000000000..3194f8458 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp @@ -0,0 +1,86 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KAHN_IMPL_H__ +#define __CXXGRAPH_KAHN_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +TopoSortResult Graph::kahn() const { + TopoSortResult result; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + const auto nodeSet = Graph::getNodeSet(); + result.nodesInTopoOrder.reserve(cachedAdjMatrix->size()); + + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + for (const auto &list : *cachedAdjMatrix) { + auto children = list.second; + for (const auto &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } + + std::queue>> topologicalOrder; + + for (const auto &node : nodeSet) { + if (!indegree[node->getId()]) { + topologicalOrder.emplace(node); + } + } + + size_t visited = 0; + while (!topologicalOrder.empty()) { + shared> currentNode = topologicalOrder.front(); + topologicalOrder.pop(); + result.nodesInTopoOrder.push_back(*currentNode); + + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &child : cachedAdjMatrix->at(currentNode)) { + if (--indegree[std::get<0>(child)->getId()] == 0) { + topologicalOrder.emplace(std::get<0>(child)); + } + } + } + visited++; + } + + if (visited != nodeSet.size()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + result.nodesInTopoOrder.clear(); + return result; + } + + result.success = true; + return result; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KAHN_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp new file mode 100644 index 000000000..faad5cb80 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp @@ -0,0 +1,128 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KOSARAJU_IMPL_H__ +#define __CXXGRAPH_KOSARAJU_IMPL_H__ + +#pragma once + +#include + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +SCCResult Graph::kosaraju() const { + SCCResult result; + result.success = false; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + auto nodeSet = getNodeSet(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + + std::stack>> st; + std::function>)> dfs_helper = + [this, &visited, &dfs_helper, &st](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + + st.push(source); + }; + + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + dfs_helper(node); + } + } + + // construct the transpose of the given graph + AdjacencyMatrix rev; + auto addElementToAdjMatrix = [&rev](shared> nodeFrom, + shared> nodeTo, + shared> edge) { + std::pair>, shared>> elem = {nodeTo, + edge}; + rev[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + shared> d_edge = + std::static_pointer_cast>(edgeSetIt); + // Add the reverse edge to the reverse adjacency matrix + addElementToAdjMatrix(d_edge->getNodePair().second, + d_edge->getNodePair().first, d_edge); + } + + visited.clear(); + + std::function>, SCCResult, int)> dfs_helper1 = + [this, &rev, &visited, &dfs_helper1]( + shared> source, SCCResult result, int sccLabel) { + // mark the vertex visited + visited[source->getId()] = true; + // Add the current vertex to the strongly connected + // component + // comp.push_back(*source); + result.sccMap[source->getId()] = sccLabel; + + // travel the neighbors + for (int i = 0; i < rev[source].size(); i++) { + shared> neighbor = rev[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper1(neighbor, result, sccLabel); + } + } + }; + + int sccLabel = 0; + while (st.size() != 0) { + auto rem = st.top(); + st.pop(); + if (visited[rem->getId()] == false) { + // std::vector> comp; + dfs_helper1(rem, result, sccLabel); + sccLabel++; + // result.stronglyConnectedComps.push_back(comp); + } + } + result.noOfComponents = sccLabel; + result.success = true; + return result; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KOSARAJU_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp new file mode 100644 index 000000000..39e69096f --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp @@ -0,0 +1,85 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KRUSKAL_IMPL_H__ +#define __CXXGRAPH_KRUSKAL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const MstResult Graph::kruskal() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + auto edgeSet = Graph::getEdgeSet(); + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + sortedEdges; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + sortedEdges.push(std::make_pair(weight, edge)); + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + auto subset = make_shared>(); + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + (*subset)[node->getId()] = set; + } + result.mstCost = 0; + while ((!sortedEdges.empty()) && (result.mst.size() < n)) { + auto [edgeWeight, cheapestEdge] = sortedEdges.top(); + sortedEdges.pop(); + auto &[first, second] = cheapestEdge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 != set2) { + result.mst.push_back( + std::make_pair(first->getUserId(), second->getUserId())); + result.mstCost += edgeWeight; + } + Graph::setUnion(subset, set1, set2); + } + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KRUSKAL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp b/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp new file mode 100644 index 000000000..c6499eae5 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp @@ -0,0 +1,112 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_PRIM_IMPL_H__ +#define __CXXGRAPH_PRIM_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const MstResult Graph::prim() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + if (!isConnectedGraph()) { + result.errorMessage = ERR_NOT_STRONG_CONNECTED; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + for (const auto &elem : (*cachedAdjMatrix)) { + dist[elem.first] = INF_DOUBLE; + } + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + auto source = *(nodeSet.begin()); + pq.push(std::make_pair(0.0, source)); + result.mstCost = 0; + std::vector doneNode; + // mark source node as done + // otherwise we get (0, 0) also in mst + doneNode.push_back(source->getId()); + // stores the parent and corresponding child node + // of the edges that are part of MST + std::unordered_map parentNode; + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().second; + auto nodeId = currentNode->getId(); + if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { + auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); + result.mst.push_back(pair); + result.mstCost += pq.top().first; + doneNode.push_back(nodeId); + } + + pq.pop(); + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if ((udw_edge->getWeight() < dist[elem.first]) && + (std::find(doneNode.begin(), doneNode.end(), + elem.first->getId()) == doneNode.end())) { + dist[elem.first] = udw_edge->getWeight(); + parentNode[elem.first->getId()] = currentNode->getUserId(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_PRIM_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp b/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp new file mode 100644 index 000000000..2aae4dec4 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp @@ -0,0 +1,198 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TARJAN_IMPL_H__ +#define __CXXGRAPH_TARJAN_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const TarjanResult Graph::tarjan(const unsigned int typeMask) const { + TarjanResult result; + result.success = false; + bool isDirected = this->isDirectedGraph(); + if (isDirected) { + // check whether targetMask is a subset of the mask for directed graph + unsigned int directedMask = TARJAN_FIND_SCC; + if ((typeMask | directedMask) != directedMask) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + } else { + // check whether targetMask is a subset of the mask for undirected graph + unsigned int undirectedMask = (TARJAN_FIND_CUTV | TARJAN_FIND_BRIDGE | + TARJAN_FIND_VBCC | TARJAN_FIND_EBCC); + if ((typeMask | undirectedMask) != undirectedMask) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } + } + + const auto &nodeSet = getNodeSet(); + std::unordered_map + discoveryTime; // the timestamp when a node is visited + std::unordered_map + lowestDisc; // the lowest discovery time of all + // reachable nodes from current node + int timestamp = 0; + CXXGraph::id_t rootId = 0; + std::stack> sccNodeStack; + std::stack> ebccNodeStack; + std::stack> vbccNodeStack; + std::unordered_set inStack; + std::function>, const shared>)> + dfs_helper = [this, typeMask, isDirected, &dfs_helper, &discoveryTime, + &lowestDisc, ×tamp, &rootId, &sccNodeStack, + &ebccNodeStack, &vbccNodeStack, &inStack, + &result](const shared> curNode, + const shared> prevEdge) { + // record the visited time of current node + discoveryTime[curNode->getId()] = timestamp; + lowestDisc[curNode->getId()] = timestamp; + timestamp++; + if (typeMask & TARJAN_FIND_SCC) { + sccNodeStack.emplace(*curNode); + inStack.emplace(curNode->getId()); + } + if (typeMask & TARJAN_FIND_EBCC) { + ebccNodeStack.emplace(*curNode); + } + if (typeMask & TARJAN_FIND_VBCC) { + vbccNodeStack.emplace(*curNode); + } + // travel the neighbors + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + int numSon = 0; + bool nodeIsAdded = + false; // whether a node has been marked as a cut vertice + for (const auto &[neighborNode, edge] : + cachedAdjMatrix->at(curNode)) { + if (!discoveryTime.count(neighborNode->getId())) { + dfs_helper(neighborNode, edge); + lowestDisc[curNode->getId()] = + std::min(lowestDisc[curNode->getId()], + lowestDisc[neighborNode->getId()]); + + if (typeMask & TARJAN_FIND_BRIDGE) { + // lowestDisc of neighbor node is larger than that of current + // node means we can travel back to a visited node only through + // this edge + if (discoveryTime[curNode->getId()] < + lowestDisc[neighborNode->getId()]) { + result.bridges.emplace_back(*edge); + } + } + + if ((typeMask & TARJAN_FIND_CUTV) && (nodeIsAdded == false)) { + if (curNode->getId() == rootId) { + numSon++; + // a root node is a cut vertices only when it connects at + // least two connected components + if (numSon == 2) { + nodeIsAdded = true; + result.cutVertices.emplace_back(*curNode); + } + } else { + if (discoveryTime[curNode->getId()] <= + lowestDisc[neighborNode->getId()]) { + nodeIsAdded = true; + result.cutVertices.emplace_back(*curNode); + } + } + } + + if (typeMask & TARJAN_FIND_VBCC) { + if (discoveryTime[curNode->getId()] <= + lowestDisc[neighborNode->getId()]) { + // if current node is a cut vertice or the root node, the vbcc + // a vertice-biconnect-component which contains the neighbor + // node + std::vector> vbcc; + while (true) { + // pop a top node out of stack until + // the neighbor node has been poped out + Node nodeAtTop = vbccNodeStack.top(); + vbccNodeStack.pop(); + vbcc.emplace_back(nodeAtTop); + if (nodeAtTop == *neighborNode) { + break; + } + } + vbcc.emplace_back(*curNode); + result.verticeBiconnectedComps.emplace_back(std::move(vbcc)); + } + } + } else if ((edge != prevEdge) && + ((isDirected == false) || + (inStack.count(neighborNode->getId())))) { + // it's not allowed to go through the previous edge back + // for a directed graph, it's also not allowed to visit + // a node that is not in stack + lowestDisc[curNode->getId()] = + std::min(lowestDisc[curNode->getId()], + lowestDisc[neighborNode->getId()]); + } + } + } + // find sccs for a undirected graph is very similar with + // find ebccs for a directed graph + if ((typeMask & TARJAN_FIND_SCC) || (typeMask & TARJAN_FIND_EBCC)) { + std::stack> &nodeStack = + (typeMask & TARJAN_FIND_SCC) ? sccNodeStack : ebccNodeStack; + if (discoveryTime[curNode->getId()] == lowestDisc[curNode->getId()]) { + std::vector> connectedComp; + while (true) { + // pop a top node out of stack until + // the current node has been poped out + Node nodeAtTop = nodeStack.top(); + nodeStack.pop(); + if (typeMask & TARJAN_FIND_SCC) { + inStack.erase(nodeAtTop.getId()); + } + connectedComp.emplace_back(nodeAtTop); + if (nodeAtTop == *curNode) { + break; + } + } + // store this component in result + (typeMask & TARJAN_FIND_SCC) + ? result.stronglyConnectedComps.emplace_back( + std::move(connectedComp)) + : result.edgeBiconnectedComps.emplace_back( + std::move(connectedComp)); + } + } + }; + + for (const auto &node : nodeSet) { + if (!discoveryTime.count(node->getId())) { + rootId = node->getId(); + dfs_helper(node, nullptr); + } + } + + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_TARJAN_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp b/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp new file mode 100644 index 000000000..83e293fe6 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp @@ -0,0 +1,78 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ +#define __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +TopoSortResult Graph::topologicalSort() const { + TopoSortResult result; + result.success = false; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else if (isCyclicDirectedGraphBFS()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + return result; + } else { + const auto &nodeSet = getNodeSet(); + std::unordered_map>, bool, nodeHash> visited; + + std::function>)> postorder_helper = + [this, &postorder_helper, &visited, + &result](shared> curNode) { + visited[curNode] = true; + + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + for (const auto &edge : cachedAdjMatrix->at(curNode)) { + const auto &nextNode = edge.first; + if (false == visited[nextNode]) { + postorder_helper(nextNode); + } + } + } + + result.nodesInTopoOrder.push_back(*curNode); + }; + + auto numNodes = cachedAdjMatrix->size(); + result.nodesInTopoOrder.reserve(numNodes); + + for (const auto &node : nodeSet) { + if (false == visited[node]) { + postorder_helper(node); + } + } + + result.success = true; + std::reverse(result.nodesInTopoOrder.begin(), + result.nodesInTopoOrder.end()); + return result; + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp b/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp new file mode 100644 index 000000000..942637450 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp @@ -0,0 +1,61 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ +#define __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +/* + * See Harry Hsu. "An algorithm for finding a minimal equivalent graph of a + * digraph.", Journal of the ACM, 22(1):11-16, January 1975 + * + * foreach x in graph.vertices + * foreach y in graph.vertices + * foreach z in graph.vertices + * delete edge xz if edges xy and yz exist + */ +template +const Graph Graph::transitiveReduction() const { + Graph result(this->edgeSet); + + CXXGraph::id_t edgeId = 0; + std::unordered_set>, nodeHash> nodes = + this->getNodeSet(); + for (auto x : nodes) { + for (auto y : nodes) { + if (this->findEdge(x, y, edgeId)) { + for (auto z : nodes) { + if (this->findEdge(y, z, edgeId)) { + if (this->findEdge(x, z, edgeId)) { + result.removeEdge(edgeId); + } + } + } + } + } + } + + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp b/include/CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp new file mode 100644 index 000000000..8a0404250 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/welshPowellColoring_impl.hpp @@ -0,0 +1,8 @@ +// +// Created by jjsm on 1/22/24. +// + +#ifndef CXXGRAPH_WELSHPOWELLCOLORING_H +#define CXXGRAPH_WELSHPOWELLCOLORING_H + +#endif // CXXGRAPH_WELSHPOWELLCOLORING_H diff --git a/include/CXXGraph/Graph/Graph.h b/include/CXXGraph/Graph/Graph.h new file mode 100644 index 000000000..7792f4fb1 --- /dev/null +++ b/include/CXXGraph/Graph/Graph.h @@ -0,0 +1,50 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_H__ +#define __CXXGRAPH_GRAPH_H__ + +#include "CXXGraph/Graph/Graph_impl.hpp" + +// Algorithm +#include "CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp" +#include "CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Boruvka_impl.hpp" +#include "CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Connectivity_impl.hpp" +#include "CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp" +#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/FloydWarshall_impl.hpp" +#include "CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kahn_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kruskal_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Prim_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Tarjan_impl.hpp" +#include "CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp" +#include "CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp" + +// IO Operation +#include "CXXGraph/Graph/IO/IOUtility_impl.hpp" +#include "CXXGraph/Graph/IO/InputOperation_impl.hpp" +#include "CXXGraph/Graph/IO/OutputOperation_impl.hpp" + +#endif // __CXXGRAPH_GRAPH_H__ diff --git a/include/CXXGraph/Graph/Graph.hpp b/include/CXXGraph/Graph/Graph.hpp deleted file mode 100644 index 424d05b06..000000000 --- a/include/CXXGraph/Graph/Graph.hpp +++ /dev/null @@ -1,4307 +0,0 @@ -/***********************************************************/ -/*** ______ ____ ______ _ ***/ -/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ -/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ -/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ -/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ -/*** |_| ***/ -/***********************************************************/ -/*** Header-Only C++ Library for Graph ***/ -/*** Representation and Algorithms ***/ -/***********************************************************/ -/*** Author: ZigRazor ***/ -/*** E-Mail: zigrazor@gmail.com ***/ -/***********************************************************/ -/*** Collaboration: ----------- ***/ -/***********************************************************/ -/*** License: AGPL v3.0 ***/ -/***********************************************************/ - -#ifndef __CXXGRAPH_GRAPH_H__ -#define __CXXGRAPH_GRAPH_H__ - -#include -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" -#include "CXXGraph/Partitioning/Partition.hpp" -#include "CXXGraph/Partitioning/PartitionAlgorithm.hpp" -#include "CXXGraph/Partitioning/Partitioner.hpp" -#include "CXXGraph/Partitioning/Utility/Globals.hpp" -#include "CXXGraph/Utility/ConstString.hpp" -#include "CXXGraph/Utility/ConstValue.hpp" -#include "CXXGraph/Utility/PointerHash.hpp" -#include "CXXGraph/Utility/Reader.hpp" -#include "CXXGraph/Utility/ThreadSafe.hpp" -#include "CXXGraph/Utility/TypeTraits.hpp" -#include "CXXGraph/Utility/Typedef.hpp" -#include "CXXGraph/Utility/Writer.hpp" - -#ifdef WITH_COMPRESSION -#include -#endif - -namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared = std::shared_ptr; - -using std::make_shared; -using std::make_unique; - -template -using T_EdgeSet = std::unordered_set>, edgeHash>; - -template -using T_NodeSet = std::unordered_set>, nodeHash>; - -namespace Partitioning { -template -class Partition; -} - -template -std::ostream &operator<<(std::ostream &o, const Graph &graph); -template -std::ostream &operator<<(std::ostream &o, const AdjacencyMatrix &adj); - -/// Class that implement the Graph. ( This class is not Thread Safe ) -template -class Graph { - private: - T_EdgeSet edgeSet = {}; - T_NodeSet isolatedNodesSet = {}; - - shared> cachedAdjMatrix; - shared> cachedDegreeMatrix; - shared> cachedLaplacianMatrix; - shared> cachedTransitionMatrix; - // Private non-const getter for the set of nodes - std::unordered_set>, nodeHash> nodeSet(); - - std::optional> getExtenstionAndSeparator( - InputOutputFormat format) const; - void writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, - std::ostream &oEdgeWeight, const char &sep, - bool writeNodeFeat, bool writeEdgeWeight) const; - void readGraphFromStream(std::istream &iGraph, std::istream &iNodeFeat, - std::istream &iEdgeWeight, bool readNodeFeat, - bool readEdgeWeight); - int writeToDot(const std::string &workingDir, const std::string &OFileName, - const std::string &graphName) const; - int readFromDot(const std::string &workingDir, const std::string &fileName); - void recreateGraph( - std::unordered_map> - &edgeMap, - std::unordered_map &edgeDirectedMap, - std::unordered_map &nodeFeatMap, - std::unordered_map &edgeWeightMap); - -#ifdef WITH_COMPRESSION - int compressFile(const std::string &inputFile, - const std::string &outputFile) const; - int decompressFile(const std::string &inputFile, - const std::string &outputFile) const; -#endif - - public: - Graph(); - Graph(const T_EdgeSet &edgeSet); - virtual ~Graph() = default; - /** - * \brief - * Function that return the Edge set of the Graph - * Note: No Thread Safe - * - * @returns a list of Edges of the graph - * - */ - virtual const T_EdgeSet &getEdgeSet() const; - /** - * \brief - * Function set the Edge Set of the Graph - * Note: No Thread Safe - * - * @param edgeSet The Edge Set - * - */ - virtual void setEdgeSet(const T_EdgeSet &edgeSet); - /** - * \brief - * Function add an Edge to the Graph Edge Set - * First check if a pointer to a node with the same userId has - * already been added, and if not add it - * Note: No Thread Safe - * - * @param edge The Edge to insert - * - */ - virtual void addEdge(const Edge *edge); - /** - * \brief - * Function add an Edge to the Graph Edge Set - * First check if a pointer to a node with the same userId has - * already been added, and if not add it - * Note: No Thread Safe - * - * @param edge The Edge to insert - * - */ - virtual void addEdge(shared> edge); - /** - * \brief - * Function that adds any number of Edges to the Graph Edge set - * Note: This is the overload needed to terminate the - * recursion - * - * @param None - * - */ - template - void addEdges(); - /** - * \brief - * Function that adds any number of Edges to the Graph Edge set - * - * @param Raw pointers or shared pointers to the Edges - * - */ - template - std::enable_if && (is_edge_ptr_v && ...), void> addEdges( - T1 edge, Tn... edges); - /** - * \brief - * Function to add a Node to the Graph Node Set - * Note: No Thread Safe - * - * @param pointer to the node - * - */ - virtual void addNode(const Node *node); - /** - * \brief - * Function to add a Node to the Graph Node Set - * Note: No Thread Safe - * - * @param shared pointer to the node - * - */ - virtual void addNode(shared> node); - /** - * \brief - * Function that adds any number of Nodes to the Graph Node set - * Note: This overload is needed to terminate the recursion - * - * @param None - * - */ - template - void addNodes(); - /** - * \brief - * Function that adds any number of Nodes to the Graph Node set - * - * @param Raw pointers or shared pointers to the Edges - * - */ - template - std::enable_if && (is_node_ptr_v && ...), void> addNodes( - T1 node, Tn... nodes); - /** - * \brief - * Function remove an Edge from the Graph Edge Set - * Note: No Thread Safe - * - * @param edgeId The Edge Id to remove - * - */ - virtual void removeEdge(const CXXGraph::id_t edgeId); - /** - * \brief - * Function to remove a Node from the Graph Node Set - * Note: No Thread Safe - * - * @param edgeId The Edge Id to remove - * - */ - virtual void removeNode(const std::string &nodeUserId); - /** - * \brief - * Finds the given edge defined by v1 and v2 within the graph. - * - * @param v1 The first vertex. - * @param v2 The second vertex. - * @param id The edge id if the edge is found. Otherwise set to 0. - * @return True if the edge exists in the graph. - */ - virtual bool findEdge(const Node *v1, const Node *v2, - CXXGraph::id_t &id) const; - /** - * \brief - * Overload of findEdge which takes shared pointers as parameters - * - * @param v1 The first vertex. - * @param v2 The second vertex. - * @param id The edge id if the edge is found. Otherwise set to 0. - * @return True if the edge exists in the graph. - */ - virtual bool findEdge(shared> v1, shared> v2, - CXXGraph::id_t &id) const; - /** - * \brief - * Function that return the Node Set of the Graph - * Note: No Thread Safe - * - * @returns a list of Nodes of the graph - * - */ - virtual const T_NodeSet getNodeSet() const; - /** - * \brief - * Function that return the Set of isolated nodes - * in the Graph - * Note: No Thread Safe - * - * @returns a list of Nodes of the graph - * - */ - virtual const T_NodeSet getIsolatedNodeSet() const; - /** - * \brief - * Function that sets the data contained in a node - * - * @param nodeUserId The userId string of the node whose data is to be changes - * @param data The new value for the node data - * - */ - virtual void setNodeData(const std::string &nodeUserId, T data); - /** - * \brief - * Function that sets the data contained in every node of the graph - * - * @param dataMap Map of the userId of every node with its new data value - * - */ - virtual void setNodeData(std::map &dataMap); - /** - * \brief - * Function that return an Edge with specific ID if Exist in the Graph - * Note: No Thread Safe - * - * @param edgeId The Edge Id to return - * @returns the Edge if exist - * - */ - virtual const std::optional>> getEdge( - const CXXGraph::id_t edgeId) const; - /** - * \brief - * Function that return a Node with specific ID if Exist in the Graph - * Note: No Thread Safe - * - * @param nodeId The Node Id to return - * @returns the Node if exist - * - */ - virtual const std::optional>> getNode( - const std::string &nodeUserId) const; - /** - * @brief This function generate a list of adjacency matrix with every element - * of the matrix contain the node where is directed the link and the Edge - * corrispondent to the link - * Note: No Thread Safe - */ - virtual shared> getAdjMatrix() const; - - virtual void cacheAdjMatrix(); - /** - * @brief This function generates a list of the degree matrix with every element - * of the matrix containing the node where the link is directed and the - * corresponding edge to the link. - * Note: No Thread Safe - */ - virtual shared> getDegreeMatrix() const; - - virtual void cacheDegreeMatrix(); - /** - * @brief This function generates a list of the Laplacian matrix with every element - * of the matrix containing the node connected to the current node and the - * corresponding edge to the link. - * Note: No Thread Safe - */ - virtual shared> getLaplacianMatrix() const; - - virtual void cacheLaplacianMatrix(); - /** - * @brief This function generates a list of the transition matrix with every element - * of the matrix containing the node that can be transitioned to from the - * current node and the probability of the transition. - * Note: No Thread Safe - */ - virtual shared> getTransitionMatrix() const; - - virtual void cacheTransitionMatrix(); - /** - * \brief This function generates a set of nodes linked to the provided node - * in a directed graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - outNeighbors(const Node *node) const; - /** - * \brief This function generates a set of nodes linked to the provided node - * in a directed graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - outNeighbors(shared> node) const; - /** - * \brief This function generates a set of nodes linked to the provided node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - inOutNeighbors(const Node *node) const; - /** - * \brief - * \brief This function generates a set of nodes linked to the provided node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - inOutNeighbors(shared> node) const; - /** - * \brief - * \brief This function generates a set of Edges going out of a node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> outEdges( - const Node *node) const; - /** - * \brief - * \brief This function generates a set of Edges going out of a node - * in any graph - * - * @param Shared pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> outEdges( - shared> node) const; - /** - * \brief - * \brief This function generates a set of Edges coming in or going out of - * a node in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> - inOutEdges(const Node *node) const; - /** - * \brief - * \brief This function generates a set of Edges coming in or going out of - * a node in any graph - * - * @param Shared pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> - inOutEdges(shared> node) const; - /** - * @brief This function finds the subset of given a nodeId - * Subset is stored in a map where keys are the hash-id of the node & values - * is the subset. - * @param subset query subset, we want to find target in this subset - * @param elem elem that we wish to find in the subset - * - * @return parent node of elem - * Note: No Thread Safe - */ - virtual CXXGraph::id_t setFind(std::unordered_map *, - const CXXGraph::id_t elem) const; - /** - * @brief This function finds the subset of given a nodeId - * Subset is stored in a map where keys are the hash-id of the node & values - * is the subset. - * @param shared pointer to subset query subset, we want to find target in - * this subset - * @param elem elem that we wish to find in the subset - * - * @return parent node of elem - * Note: No Thread Safe - */ - virtual CXXGraph::id_t setFind( - shared>, - const CXXGraph::id_t elem) const; - /** - * @brief This function modifies the original subset array - * such that it the union of two sets a and b - * @param subset original subset is modified to obtain union of a & b - * @param a parent id of set1 - * @param b parent id of set2 - * NOTE: Original subset is no longer available after union. - * Note: No Thread Safe - */ - virtual void setUnion(std::unordered_map *, - const CXXGraph::id_t set1, - const CXXGraph::id_t elem2) const; - /** - * @brief This function modifies the original subset array - * such that it the union of two sets a and b - * @param subset original subset is modified to obtain union of a & b - * @param a parent id of set1 - * @param b parent id of set2 - * NOTE: Original subset is no longer available after union. - * Note: No Thread Safe - */ - virtual void setUnion(shared>, - const CXXGraph::id_t set1, - const CXXGraph::id_t elem2) const; - /** - * @brief This function finds the eulerian path of a directed graph using - * hierholzers algorithm - * - * @return a vector containing nodes in eulerian path - * Note: No Thread Safe - */ - virtual std::shared_ptr>> eulerianPath() const; - /** - * @brief Function runs the dijkstra algorithm for some source node and - * target node in the graph and returns the shortest distance of target - * from the source. - * Note: No Thread Safe - * - * @param source source vertex - * @param target target vertex - * - * @return shortest distance if target is reachable from source else ERROR in - * case if target is not reachable from source or there is error in the - * computation. - */ - virtual const DijkstraResult dijkstra(const Node &source, - const Node &target) const; - /** - * @brief This function runs the tarjan algorithm and returns different types - * of results depending on the input parameter typeMask. - * - * @param typeMask each bit of typeMask within valid range represents a kind - * of results should be returned. - * - * Note: No Thread Safe - * - * @return The types of return include strongly connected components - * (only for directed graphs) and cut vertices、 bridges、edge - * biconnected components and vertice biconnected components - * (only for undirected graphs). - */ - virtual const TarjanResult tarjan(const unsigned int typeMask) const; - /** - * @brief Function runs the bellman-ford algorithm for some source node and - * target node in the graph and returns the shortest distance of target - * from the source. It can also detect if a negative cycle exists in the - * graph. Note: No Thread Safe - * - * @param source source vertex - * @param target target vertex - * - * @return shortest distance if target is reachable from source else ERROR in - * case if target is not reachable from source. If there is no error then also - * returns if the graph contains a negative cycle. - */ - virtual const BellmanFordResult bellmanford(const Node &source, - const Node &target) const; - /** - * @brief This function computes the transitive reduction of the graph, - * returning a graph with the property of transitive closure satisfied. It - * removes the "short-circuit" paths from a graph, leaving only the longest - * paths. Commonly used to remove duplicate edges among nodes that do not pass - * through the entire graph. - * @return A copy of the current graph with the transitive closure property - * satisfied. - * - */ - virtual const Graph transitiveReduction() const; - /** - * @brief Function runs the floyd-warshall algorithm and returns the shortest - * distance of all pair of nodes. It can also detect if a negative cycle - * exists in the graph. Note: No Thread Safe - * @return a map whose keys are node ids and values are the shortest distance. - * If there is no error then also returns if the graph contains a negative - * cycle. - */ - virtual const FWResult floydWarshall() const; - /** - * @brief Function runs the prim algorithm and returns the minimum spanning - * tree if the graph is undirected. Note: No Thread Safe - * @return a vector containing id of nodes in minimum spanning tree & cost of - * MST - */ - virtual const MstResult prim() const; - /** - * @brief Function runs the boruvka algorithm and returns the minimum spanning - * tree & cost if the graph is undirected. Note: No Thread Safe - * @return struct of type MstResult with following fields - * success: true if algorithm completed successfully ELSE false - * mst: vector containing id of nodes in minimum spanning tree & cost of MST - * mstCost: Cost of MST - * errorMessage: "" if no error ELSE report the encountered error - */ - virtual const MstResult boruvka() const; - /** - * @brief Function runs the kruskal algorithm and returns the minimum spanning - * tree if the graph is undirected. Note: No Thread Safe - * @return struct of type MstResult with following fields - * success: true if algorithm completed successfully ELSE false - * mst: vector containing id of nodes in minimum spanning tree & cost of MST - * mstCost: Cost of MST - * errorMessage: "" if no error ELSE report the encountered error - */ - virtual const MstResult kruskal() const; - /** - * \brief - * Function runs the best first search algorithm over the graph - * using an evaluation function to decide which adjacent node is - * most promising to explore - * Note: No Thread Safe - * - * @param source source node - * @param target target node - * @returns a struct with a vector of Nodes if target is reachable else ERROR - * in case if target is not reachable or there is an error in the computation. - * - */ - virtual BestFirstSearchResult best_first_search( - const Node &source, const Node &target) const; - /** - * \brief - * Function performs the breadth first search algorithm over the graph - * Note: No Thread Safe - * - * @param start Node from where traversing starts - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> breadth_first_search( - const Node &start) const; - /** - * \brief - * The multithreaded version of breadth_first_search - * It turns out to be two indepentent functions because of implemntation - * differences - * - * @param start Node from where traversing starts - * @param num_threads number of threads - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> concurrency_breadth_first_search( - const Node &start, size_t num_threads) const; - /** - * \brief - * Function performs the depth first search algorithm over the graph - * Note: No Thread Safe - * - * @param start Node from where traversing starts - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> depth_first_search( - const Node &start) const; - - /** - * \brief - * This function uses DFS to check for cycle in the graph. - * Pay Attention, this function work only with directed Graph - * Note: No Thread Safe - * - * @return true if a cycle is detected, else false. ( false is returned also - * if the graph in indirected) - */ - virtual bool isCyclicDirectedGraphDFS() const; - - /** - * \brief - * This function uses BFS to check for cycle in the graph. - * Pay Attention, this function work only with directed Graph - * Note: No Thread Safe - * - * @return true if a cycle is detected, else false. ( false is returned also - * if the graph in indirected) - */ - virtual bool isCyclicDirectedGraphBFS() const; - - /** - * @brief - * This function checks if the given set of edges - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle(const T_EdgeSet *) const; - /** - * @brief - * This function checks if the given set of edges - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle(shared>) const; - /** - * @brief - * This function checks if the given Subset - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle( - shared> edgeSet, - shared>) const; - - /** - * \brief - * This function checks if a graph is directed - * Note: No Thread Safe - * - * @return true if the graph is directed, else false. - */ - virtual bool isDirectedGraph() const; - - /** - * \brief - * This function checks if a graph is undirected - * Note: No Thread Safe - * - * @return true if the graph is undirected, else false. - */ - virtual bool isUndirectedGraph() const; - - /** - * @brief This function reverse the direction of the edges in a directed graph - */ - virtual void reverseDirectedGraph(); - - /** - * @brief This function checks if the graph is connected or not - * Applicable for Undirected Graph, for Directed Graph use the - * isStronglyConnectedGraph() function - * - * @return true if the graph is connected - * @return false otherwise - */ - virtual bool isConnectedGraph() const; - - /** - * @brief This function checks if the graph is strongly connected or not - * Applicable for Directed Graph, for Undirected Graph use the - * isConnectedGraph() function - * - * @return true if the graph is connected - * @return false otherwise - */ - virtual bool isStronglyConnectedGraph() const; - - /** - * @brief This function sort nodes in topological order. - * Applicable for Directed Acyclic Graph - * - * @return a struct with a vector of Nodes ordered topologically else ERROR in - * case of undirected or cyclic graph - */ - virtual TopoSortResult topologicalSort() const; - - /** - * @brief This function sort nodes in topological order using kahn's algorithm - * Applicable for Directed Acyclic Graph - * - * @return a struct with a vector of Nodes ordered topologically else ERROR in - * case of undirected or cyclic graph - */ - virtual TopoSortResult kahn() const; - - /** - * \brief - * This function performs performs the kosaraju algorthm on the graph to find - the strongly connected components. - * - * Mathematical definition of the problem: - * A strongly connected component (SCC) of a directed graph is a maximal - strongly connected subgraph. - - * Note: No Thread Safe - * @return a vector of vector of strongly connected components. - */ - virtual SCCResult kosaraju() const; - - /** - * \brief - * This function performs Graph Slicing based on connectivity - * - * 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. - - * Note: No Thread Safe - * @param start Node from where traversing starts - * @return a vector of nodes that belong to C but not to M. - */ - virtual const std::vector> graph_slicing(const Node &start) const; - - /** - * @brief Function runs the Dial algorithm (Optimized Dijkstra for small - * range weights) for some source node and target node in the graph and - * returns the shortest distance of target from the source. Note: No Thread - * Safe - * - * @param source source vertex - * @param maxWeight maximum weight of the edge - * - * @return shortest distance for all nodes reachable from source else ERROR in - * case there is error in the computation. - */ - virtual const DialResult dial(const Node &source, int maxWeight) const; - - /** - * @brief Function runs the Ford-Fulkerson algorithm for some source node and - * target node in the graph and returns the maximum flow of the graph - * - * @param source source vertex - * @param target target vertex - * @return double Max-Flow value or -1 in case of error - */ - virtual double fordFulkersonMaxFlow(const Node &source, - const Node &target) const; - - /** - * Welsh-Powell Coloring algorithm - * Source : - * https://www.youtube.com/watch?v=SLkyDuG1Puw&ab_channel=TheLogicalLearning - * https://www.geeksforgeeks.org/welsh-powell-graph-colouring-algorithm/ - * https://www.tutorialspoint.com/de-powell-graph-colouring-algorithm - */ - virtual std::map, int> welshPowellColoring() const; - /** - * \brief - * This function writes the graph to an output file - * Note: Not threadsafe - * - * @param format The output format of the file - * @param workingDir The parent directory of the output file - * @param OFileName The output filename - * @param compress Enables compression (requires zlib) - * @param writeNodeFeat Indicates if export also Node Features - * @param writeEdgeWeight Indicates if export also Edge Weights - * @return 0 if OK, else return a negative value - */ - virtual int writeToFile( - InputOutputFormat format = InputOutputFormat::STANDARD_CSV, - const std::string &workingDir = ".", - const std::string &OFileName = "graph", bool compress = false, - bool writeNodeFeat = false, bool writeEdgeWeight = false) const; - - virtual int writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const; - - virtual int writeToMTXFile(const std::string &workingDir, - const std::string &OFileName, char delimier) const; - - /** - * \brief - * This function reads the graph from an input file - * Note: Not threadsafe - * - * @param format The input format of the file - * @param workingDir The parent directory of the input - * file - * @param OFileName The input filename - * @param compress Enables compression (requires zlib) - * @param readNodeFeat Indicates if import also Node Features - * @param readEdgeWeight Indicates if import also Edge Weights - * @return 0 if OK, else return a negative value - */ - virtual int readFromFile( - InputOutputFormat format = InputOutputFormat::STANDARD_CSV, - const std::string &workingDir = ".", - const std::string &OFileName = "graph", bool compress = false, - bool readNodeFeat = false, bool readEdgeWeight = false); - - virtual int readFromDotFile(const std::string &workingDir, - const std::string &fileName); - - virtual int readFromMTXFile(const std::string &workingDir, - const std::string &fileName); - - /** - * \brief - * This function partition a graph in a set of partitions - * Note: No Thread Safe - * - * @param algorithm The partition algorithm - * @param numberOfPartition The number of partitions - * @return The partiton Map of the partitioned graph - */ - virtual PartitionMap partitionGraph( - const Partitioning::PartitionAlgorithm algorithm, - const unsigned int numberOfPartitions, const double param1 = 0.0, - const double param2 = 0.0, const double param3 = 0.0, - const unsigned int numberOfthreads = - std::thread::hardware_concurrency()) const; - - friend std::ostream &operator<< <>(std::ostream &os, const Graph &graph); - friend std::ostream &operator<< <>(std::ostream &os, - const AdjacencyMatrix &adj); -}; - -template -Graph::Graph() { - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); - cacheTransitionMatrix(); -} - -template -Graph::Graph(const T_EdgeSet &edgeSet) { - for (auto edgeIt : edgeSet) { - this->edgeSet.insert(edgeIt); - } - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); - cacheTransitionMatrix(); -} - -template -const T_EdgeSet &Graph::getEdgeSet() const { - return edgeSet; -} - -template -void Graph::setEdgeSet(const T_EdgeSet &edgeSet) { - this->edgeSet.clear(); - for (auto edgeIt : edgeSet) { - this->edgeSet.insert(edgeIt); - } - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); -} - -template -void Graph::addEdge(const Edge *edge) { - if (edge->isDirected().has_value() && edge->isDirected().value()) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_shared = make_shared>( - *dynamic_cast *>(edge)); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - } else { - auto edge_shared = make_shared>(*edge); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - } - } else { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_shared = make_shared>( - *dynamic_cast *>(edge)); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( - std::move(elem1)); - } else { - auto edge_shared = make_shared>(*edge); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( - std::move(elem1)); - } - } -} - -template -void Graph::addEdge(shared> edge) { - this->edgeSet.insert(edge); - - /* Adding new edge in cached adjacency matrix */ - if (edge.get()->isDirected().has_value() && - edge.get()->isDirected().value()) { - std::pair>, shared>> elem = { - edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( - std::move(elem)); - } else { - std::pair>, shared>> elem = { - edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge.get()->getNodePair().first, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back( - std::move(elem1)); - } -} - -template -template -void Graph::addEdges() { - return; -} - -template -template -std::enable_if && (is_edge_ptr_v && ...), void> Graph::addEdges( - T1 edge, Tn... edges) { - addEdge(edge); - addEdges(edges...); -} - -template -void Graph::addNode(const Node *node) { - auto node_shared = make_shared>(*node); - this->isolatedNodesSet.insert(node_shared); -} - -template -void Graph::addNode(shared> node) { - this->isolatedNodesSet.insert(node); -} - -template -template -void Graph::addNodes() { - return; -} - -template -template -std::enable_if && (is_node_ptr_v && ...), void> Graph::addNodes( - T1 node, Tn... nodes) { - addNode(node); - addNodes(nodes...); -} - -template -void Graph::removeEdge(const CXXGraph::id_t edgeId) { - auto edgeOpt = Graph::getEdge(edgeId); - if (edgeOpt.has_value()) { - /* - edgeSet.erase(std::find_if(this->edgeSet.begin(), this->edgeSet.end(), - [edgeOpt](const Edge *edge) { return (*(edgeOpt.value()) == *edge); })); - */ - edgeSet.erase(edgeSet.find(edgeOpt.value())); - int delIndex = -1; - int i = 0; - /* Removing the edge from the cached adjacency matrix */ - for (auto elem : - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first]) { - if (elem.second.get()->getId() == edgeId) { - delIndex = i; - break; - } - i++; - } - if (delIndex != -1) { - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].erase( - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first] - .begin() + - delIndex); - } - - delIndex = -1; - i = 0; - for (auto elem : - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second]) { - if (elem.second.get()->getId() == edgeId) { - delIndex = i; - break; - } - i++; - } - if (delIndex != -1) { - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].erase( - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second] - .begin() + - delIndex); - } - } -} - -template -void Graph::removeNode(const std::string &nodeUserId) { - auto nodeOpt = getNode(nodeUserId); - auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value()); - - if (nodeOpt.has_value() && isolatedNodeIt != isolatedNodesSet.end()) { - // The node is isolated - isolatedNodesSet.erase(isolatedNodeIt); - } else if (nodeOpt.has_value()) { - // The node is not isolated - // Remove the edges containing the node - auto edgeset = edgeSet; - for (auto edgeIt : edgeset) { - if (edgeIt->getNodePair().first->getUserId() == nodeUserId || - edgeIt->getNodePair().second->getUserId() == nodeUserId) { - this->removeEdge(edgeIt->getId()); - } - } - } -} - -template -bool Graph::findEdge(const Node *v1, const Node *v2, - CXXGraph::id_t &id) const { - auto v1_shared = make_shared>(*v1); - auto v2_shared = make_shared>(*v2); - - return findEdge(v1_shared, v2_shared, id); -} - -template -bool Graph::findEdge(shared> v1, shared> v2, - CXXGraph::id_t &id) const { - // This could be made faster by looking for the edge hash, assuming we hash - // based on node data, instead of a unique integer - if (cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0) { - /* Searching for the edge using cached adjacency matrix */ - - for (auto elem : (*cachedAdjMatrix)[v1]) { - if (elem.first == v2) { - id = elem.second.get()->getId(); - return true; - } - } - } else { - /* Searching for the edge using edgeset */ - - for (auto e : this->edgeSet) { - if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) { - id = e->getId(); - return true; - } - if (!e->isDirected() && - ((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) { - id = e->getId(); - return true; - } - } - } - /*for (auto e : this->edgeSet) { - if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) { - id = e->getId(); - return true; - } - if (!e->isDirected() && - ((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) { - id = e->getId(); - return true; - } - }*/ - - id = 0; - return false; -} - -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); - } - // Merge with the isolated nodes - nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); - - return nodeSet; -} - -template -const T_NodeSet Graph::getIsolatedNodeSet() const { - return isolatedNodesSet; -} - -template -void Graph::setNodeData(const std::string &nodeUserId, T data) { - auto nodeSet = this->nodeSet(); - auto nodeIt = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&nodeUserId](auto node) { return node->getUserId() == nodeUserId; }); - std::const_pointer_cast>(*nodeIt)->setData(std::move(data)); -} - -template -void Graph::setNodeData(std::map &dataMap) { - // Construct the set of all the nodes in the graph - for (auto &nodeSetIt : this->nodeSet()) { - nodeSetIt->setData(std::move(dataMap[nodeSetIt->getUserId()])); - } -} - -template -const std::optional>> Graph::getEdge( - const CXXGraph::id_t edgeId) const { - for (const auto &it : edgeSet) { - if (it->getId() == edgeId) { - return it; - } - } - - return std::nullopt; -} - -template -const std::optional>> Graph::getNode( - const std::string &nodeUserId) const { - for (const auto &it : getNodeSet()) { - if (it->getUserId() == nodeUserId) { - return it; - } - } - - return std::nullopt; -} - -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 (auto &isNodeIt : isolatedNodesSet) { - nodeSet.insert(std::const_pointer_cast>(isNodeIt)); - } - - return nodeSet; -} - -template -std::optional> Graph::getExtenstionAndSeparator( - InputOutputFormat format) const { - if (format == InputOutputFormat::STANDARD_CSV) { - return std::pair(".csv", ','); - } else if (format == InputOutputFormat::STANDARD_TSV) { - return std::pair(".tsv", '\t'); - } else { - return std::nullopt; - } -} - -template -int Graph::writeToDot(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const { - const std::string linkSymbol = "--"; - const std::string directedLinkSymbol = "->"; - - const std::string completePathToFileGraph = - workingDir + '/' + OFileName + ".dot"; - std::ofstream ofileGraph; - ofileGraph.open(completePathToFileGraph); - if (!ofileGraph.is_open()) { - // ERROR File Not Open - return -1; - } - - // Write the header of the DOT file - std::string headerLine; - if (this->isDirectedGraph()) { - headerLine = "digraph " + graphName + " {\n"; - } else { - headerLine = "graph " + graphName + " {\n"; - } - ofileGraph << headerLine; - - for (auto const &edgePtr : edgeSet) { - std::string edgeLine = ""; - if (edgePtr->isDirected().has_value() && edgePtr->isDirected().value()) { - auto directedPtr = - std::static_pointer_cast>(edgePtr); - edgeLine += '\t' + directedPtr->getFrom().getUserId() + ' '; - edgeLine += directedLinkSymbol + ' '; - edgeLine += directedPtr->getTo().getUserId(); - } else { - edgeLine += '\t' + edgePtr->getNodePair().first->getUserId() + ' '; - edgeLine += linkSymbol + ' '; - edgeLine += edgePtr->getNodePair().second->getUserId(); - } - if (edgePtr->isWeighted().has_value() && edgePtr->isWeighted().value()) { - // Weights in dot files must be integers - edgeLine += " [weight=" + - std::to_string(static_cast( - std::dynamic_pointer_cast(edgePtr) - ->getWeight())) + - ']'; - } - edgeLine += ";\n"; - ofileGraph << edgeLine; - } - ofileGraph << '}'; - ofileGraph.close(); - - return 0; -} - -// This ctype facet classifies ',' and '\t' as whitespace -struct csv_whitespace : std::ctype { - static const mask *make_table() { - // make a copy of the "C" locale table - static std::vector v(classic_table(), classic_table() + table_size); - v[','] |= space; // comma will be classified as whitespace - v['\t'] |= space; - v[' '] &= ~space; // space will not be classified as whitespace - return &v[0]; - } - csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {} -}; - -template -void Graph::writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, - std::ostream &oEdgeWeight, const char &sep, - bool writeNodeFeat, - bool writeEdgeWeight) const { - for (const auto &edge : edgeSet) { - oGraph << edge->getId() << sep << edge->getNodePair().first->getUserId() - << sep << edge->getNodePair().second->getUserId() << sep - << ((edge->isDirected().has_value() && edge->isDirected().value()) - ? 1 - : 0) - << std::endl; - } - - if (writeNodeFeat) { - auto nodeSet = getNodeSet(); - for (const auto &node : nodeSet) { - oNodeFeat << node->getUserId() << sep << node->getData() << std::endl; - } - } - - if (writeEdgeWeight) { - for (const auto &edge : edgeSet) { - oEdgeWeight - << edge->getId() << sep - << (edge->isWeighted().has_value() && edge->isWeighted().value() - ? (std::dynamic_pointer_cast(edge)) - ->getWeight() - : 0.0) - << sep - << (edge->isWeighted().has_value() && edge->isWeighted().value() ? 1 - : 0) - << std::endl; - } - } -} - -template -void Graph::readGraphFromStream(std::istream &iGraph, - std::istream &iNodeFeat, - std::istream &iEdgeWeight, bool readNodeFeat, - bool readEdgeWeight) { - std::unordered_map> - edgeMap; - std::unordered_map edgeDirectedMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeWeightMap; - - CXXGraph::id_t edgeId; - std::string nodeId1; - std::string nodeId2; - bool directed; - while (iGraph >> edgeId >> nodeId1 >> nodeId2 >> - directed) { /* loop continually */ - edgeMap[edgeId] = std::pair(nodeId1, nodeId2); - edgeDirectedMap[edgeId] = directed; - } - - if (readNodeFeat) { - std::string nodeId; - T nodeFeat; - while (iNodeFeat >> nodeId >> nodeFeat) { - nodeFeatMap[nodeId] = nodeFeat; - } - } - - if (readEdgeWeight) { - CXXGraph::id_t edgeId; - double weight; - bool weighted; - while (iEdgeWeight >> edgeId >> weight >> weighted) { /* loop continually */ - if (weighted) { - edgeWeightMap[edgeId] = weight; - } - } - } - - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); -} - -template -int Graph::readFromDot(const std::string &workingDir, - const std::string &fileName) { - // Define the edge maps - std::unordered_map> - edgeMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeDirectedMap; - std::unordered_map edgeWeightMap; - - // Define the node strings and the "garbage collector" temp string - std::string node1; - std::string node2; - std::string temp; - - // Get full path to the file and open it - const std::string completePathToFileGraph = - workingDir + '/' + fileName + ".dot"; - - // Check if the graph is directed - bool directed = false; - std::ifstream fileContentStream(completePathToFileGraph); - std::string fileContent(std::istreambuf_iterator{fileContentStream}, - {}); - if (fileContent.find("->") != std::string::npos) { - directed = true; - } - // Check if the graph is weighted - bool weighted = false; - if (fileContent.find("weight") != std::string::npos) { - weighted = true; - } - fileContentStream.close(); - - std::ifstream iFile(completePathToFileGraph); - // Write the header of the DOT file in the temp string - getline(iFile, temp); - - CXXGraph::id_t edgeId = 0; - std::string fileRow; - while (getline(iFile, fileRow)) { - // If you've reached the end of the file, stop - if (fileRow == "}") { - break; - } - - // Remove the whitespaces before the definition of the edge - while (*fileRow.begin() == ' ' || *fileRow.begin() == '\t') { - fileRow.erase(fileRow.begin()); - } - - std::stringstream row_stream(fileRow); - getline(row_stream, node1, ' '); - // Store the symbol representing the edge inside temp - getline(row_stream, temp, ' '); - if (weighted) { - getline(row_stream, node2, '['); - // Remove any whitespaces or tabs from the node string - node2.erase(std::remove(node2.begin(), node2.end(), ' '), node2.end()); - node2.erase(std::remove(node2.begin(), node2.end(), '\t'), node2.end()); - - getline(row_stream, temp, '='); - std::string weight; - getline(row_stream, weight, ']'); - // Erase any whitespaces - weight.erase(std::remove(weight.begin(), weight.end(), ' '), - weight.end()); - edgeWeightMap[edgeId] = std::stod(weight); - } else { - getline(row_stream, node2, ';'); - } - - // Save the edge and increment the edge counter - edgeMap[edgeId] = std::pair(node1, node2); - edgeDirectedMap[edgeId] = directed; - ++edgeId; - } - iFile.close(); - - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); - return 0; -} - -template -void Graph::recreateGraph( - std::unordered_map> - &edgeMap, - std::unordered_map &edgeDirectedMap, - std::unordered_map &nodeFeatMap, - std::unordered_map &edgeWeightMap) { - std::unordered_map>> nodeMap; - for (const auto &edgeIt : edgeMap) { - shared> node1(nullptr); - shared> node2(nullptr); - if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { - feat = std::move(nodeFeatMap.at(edgeIt.second.first)); - } - node1 = make_shared>(edgeIt.second.first, feat); - nodeMap[edgeIt.second.first] = node1; - } else { - node1 = nodeMap.at(edgeIt.second.first); - } - if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { - feat = std::move(nodeFeatMap.at(edgeIt.second.second)); - } - node2 = make_shared>(edgeIt.second.second, feat); - nodeMap[edgeIt.second.second] = node2; - } else { - node2 = nodeMap.at(edgeIt.second.second); - } - - if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = make_shared>( - edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } else { - auto edge = make_shared>( - edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } - } else { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = make_shared>(edgeIt.first, node1, node2); - addEdge(edge); - } else { - auto edge = make_shared>(edgeIt.first, node1, node2); - addEdge(edge); - } - } - } -} - -#ifdef WITH_COMPRESSION -template -int Graph::compressFile(const std::string &inputFile, - const std::string &outputFile) const { - std::ifstream ifs; - ifs.open(inputFile); - if (!ifs.is_open()) { - // ERROR File Not Open - return -1; - } - std::string content((std::istreambuf_iterator(ifs)), - (std::istreambuf_iterator())); - - const char *content_ptr = content.c_str(); - gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); - if (outFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); - return -1; - } - - unsigned int zippedBytes; - zippedBytes = - gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); - - ifs.close(); - gzclose(outFileZ); - return 0; -} - -template -int Graph::decompressFile(const std::string &inputFile, - const std::string &outputFile) const { - gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); - if (inFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); - return -1; - } - unsigned char unzipBuffer[8192]; - unsigned int unzippedBytes; - std::vector unzippedData; - std::ofstream ofs; - ofs.open(outputFile); - if (!ofs.is_open()) { - // ERROR File Not Open - return -1; - } - while (true) { - unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); - if (unzippedBytes > 0) { - unzippedData.insert(unzippedData.end(), unzipBuffer, - unzipBuffer + unzippedBytes); - } else { - break; - } - } - for (const auto &c : unzippedData) { - ofs << c; - } - ofs << std::endl; - ofs.close(); - gzclose(inFileZ); - return 0; -} -#endif - -template -CXXGraph::id_t Graph::setFind( - std::unordered_map *subsets, - const CXXGraph::id_t nodeId) const { - auto subsets_ptr = - make_shared>(*subsets); - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets_ptr, (*subsets)[nodeId].parent); - } - - return (*subsets)[nodeId].parent; -} - -template -CXXGraph::id_t Graph::setFind( - shared> subsets, - const CXXGraph::id_t nodeId) const { - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets, (*subsets)[nodeId].parent); - } - - return (*subsets)[nodeId].parent; -} - -template -void Graph::setUnion(std::unordered_map *subsets, - const CXXGraph::id_t elem1, - const CXXGraph::id_t elem2) const { - /* auto subsets_ptr = make_shared>(*subsets); */ - // if both sets have same parent - // then there's nothing to be done - /* if ((*subsets_ptr)[elem1].parent == (*subsets_ptr)[elem2].parent) return; - */ - /* auto elem1Parent = Graph::setFind(subsets_ptr, elem1); */ - /* auto elem2Parent = Graph::setFind(subsets_ptr, elem2); */ - /* if ((*subsets_ptr)[elem1Parent].rank < (*subsets_ptr)[elem2Parent].rank) */ - /* (*subsets_ptr)[elem1].parent = elem2Parent; */ - /* else if ((*subsets_ptr)[elem1Parent].rank > - * (*subsets_ptr)[elem2Parent].rank) */ - /* (*subsets_ptr)[elem2].parent = elem1Parent; */ - /* else { */ - /* (*subsets_ptr)[elem2].parent = elem1Parent; */ - /* (*subsets_ptr)[elem1Parent].rank++; */ - /* } */ - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } -} - -template -void Graph::setUnion( - shared> subsets, - const CXXGraph::id_t elem1, const CXXGraph::id_t elem2) const { - // if both sets have same parent - // then there's nothing to be done - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } -} - -template -std::shared_ptr>> Graph::eulerianPath() const { - const auto nodeSet = Graph::getNodeSet(); - - std::shared_ptr>> eulerPath = - std::make_shared>>(); - - 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 cachedAdjMatrix->at(n1).size() < cachedAdjMatrix->at(n2).size(); - }); - auto currentNode = *(firstNodeIt); - currentPath.push_back(currentNode); - - while (currentPath.size() > 0) { - auto &edges = cachedAdjMatrix->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::getAdjMatrix() const { - auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&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().has_value() && - edgeSetIt->isDirected().value()) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(d_edge->getNodePair().first, - d_edge->getNodePair().second, d_edge); - } else if (edgeSetIt->isDirected().has_value() && - !edgeSetIt->isDirected().value()) { - 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; - } - } - return adj; -} - -template -void Graph::cacheAdjMatrix() { - const auto adj = Graph::getAdjMatrix(); - this->cachedAdjMatrix = adj; -} - -template -shared> Graph::getDegreeMatrix() const { - auto degreeMatrix = std::make_shared>(); - - for (const auto& nodePair : *this->cachedAdjMatrix) { - const shared>& node = nodePair.first; - const std::vector>, shared>>>& neighbors = nodePair.second; - - int degree = neighbors.size(); - - (*degreeMatrix)[node] = {degree}; - } - - return degreeMatrix; -} - -template -void Graph::cacheDegreeMatrix() { - const auto degreeMatrix = Graph::getDegreeMatrix(); - this->cachedDegreeMatrix = degreeMatrix; -} - -template -shared> Graph::getLaplacianMatrix() const { - const auto adjacencyMatrix = this->cachedAdjMatrix; - const auto degreeMatrix = this->cachedDegreeMatrix; - - auto laplacianMatrix = std::make_shared>(); - for (const auto& nodePair : *adjacencyMatrix) { - const shared>& node = nodePair.first; - (*laplacianMatrix)[node] = std::vector>, shared>>>(); - } - - for (const auto& nodePair : *adjacencyMatrix) { - const shared>& node = nodePair.first; - const std::vector>, shared>>>& neighbors = nodePair.second; - - int degree = neighbors.size(); - - (*laplacianMatrix)[node].emplace_back(node, nullptr); // Insere o nó na diagonal - for (const auto& neighborPair : neighbors) { - const shared>& neighbor = neighborPair.first; - (*laplacianMatrix)[node].emplace_back(neighbor, neighborPair.second); // Insere os pares de vizinhos - } - } - - return laplacianMatrix; -} - -template -void Graph::cacheLaplacianMatrix() { - const auto laplacianMatrix = Graph::getLaplacianMatrix(); - this->cachedLaplacianMatrix = laplacianMatrix; -} - -template -shared> Graph::getTransitionMatrix() const { - const auto adjacencyMatrix = this->cachedAdjMatrix; - - auto transitionMatrix = std::make_shared>(); - for (const auto& nodePair : *adjacencyMatrix) { - const shared>& node = nodePair.first; - (*transitionMatrix)[node] = std::vector>, double>>(); - } - - for (const auto& nodePair : *adjacencyMatrix) { - const shared>& node = nodePair.first; - const std::vector>, shared>>>& neighbors = nodePair.second; - - int degree = neighbors.size(); - - double transitionProbability = 1.0 / degree; - - for (const auto& neighborPair : neighbors) { - const shared>& neighbor = neighborPair.first; - (*transitionMatrix)[node].emplace_back(neighbor, transitionProbability); - } - } - - return transitionMatrix; -} - -template -void Graph::cacheTransitionMatrix() { - const auto transitionMatrix = Graph::getTransitionMatrix(); - this->cachedTransitionMatrix = transitionMatrix; -} - -template -const std::unordered_set>, nodeHash> -Graph::outNeighbors(const Node *node) const { - auto node_shared = make_shared>(*node); - - return outNeighbors(node_shared); -} - -template -const std::unordered_set>, nodeHash> -Graph::outNeighbors(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, nodeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, nodeHash> outNeighbors; - for (auto pair : nodeEdgePairs) { - if (pair.second->isDirected().has_value() && - pair.second->isDirected().value()) { - outNeighbors.insert(pair.first); - } - } - - return outNeighbors; -} - -template -const std::unordered_set>, nodeHash> -Graph::inOutNeighbors(const Node *node) const { - auto node_shared = make_shared>(*node); - - return inOutNeighbors(node_shared); -} - -template -const std::unordered_set>, nodeHash> -Graph::inOutNeighbors(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, nodeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, nodeHash> inOutNeighbors; - for (auto pair : nodeEdgePairs) { - inOutNeighbors.insert(pair.first); - } - - return inOutNeighbors; -} - -template -const std::unordered_set>, edgeHash> Graph::outEdges( - const Node *node) const { - auto node_shared = make_shared>(*node); - - return outEdges(node_shared); -} - -template -const std::unordered_set>, edgeHash> Graph::outEdges( - shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, edgeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, edgeHash> outEdges; - for (auto pair : nodeEdgePairs) { - if (pair.second->isDirected().has_value() && - pair.second->isDirected().value()) { - outEdges.insert(pair.second); - } - } - - return outEdges; -} - -template -const std::unordered_set>, edgeHash> -Graph::inOutEdges(const Node *node) const { - auto node_shared = make_shared>(*node); - - return outEdges(node_shared); -} - -template -const std::unordered_set>, edgeHash> -Graph::inOutEdges(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, edgeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, edgeHash> outEdges; - for (auto pair : nodeEdgePairs) { - outEdges.insert(pair.second); - } - - return outEdges; -} - -template -const DijkstraResult Graph::dijkstra(const Node &source, - const Node &target) const { - DijkstraResult result; - auto nodeSet = Graph::getNodeSet(); - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // n denotes the number of vertices in graph - auto n = cachedAdjMatrix->size(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist; - - for (const auto &node : nodeSet) { - dist[node] = INF_DOUBLE; - } - - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - pq.push(std::make_pair(0.0, *source_node_it)); - - // marking the distance of source as 0 - dist[*source_node_it] = 0; - - std::unordered_map parent; - parent[source.getUserId()] = ""; - - while (!pq.empty()) { - // second element of pair denotes the node / vertex - shared> currentNode = pq.top().second; - // first element of pair denotes the distance - double currentDist = pq.top().first; - - pq.pop(); - - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - if (elem.second->isDirected().has_value() && - elem.second->isDirected().value()) { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (dw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + dw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - parent[elem.first.get()->getUserId()] = - currentNode.get()->getUserId(); - } - } else if (elem.second->isDirected().has_value() && - !elem.second->isDirected().value()) { - shared> udw_edge = - std::static_pointer_cast>( - elem.second); - if (udw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + udw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - parent[elem.first.get()->getUserId()] = - currentNode.get()->getUserId(); - } - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - } - } - if (dist[*target_node_it] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.result = dist[*target_node_it]; - std::string id = target.getUserId(); - while (parent[id] != "") { - result.path.push_back(id); - id = parent[id]; - } - result.path.push_back(source.getUserId()); - std::reverse(result.path.begin(), result.path.end()); - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - return result; -} - -template -const BellmanFordResult Graph::bellmanford(const Node &source, - const Node &target) const { - BellmanFordResult result; - result.success = false; - result.errorMessage = ""; - result.result = INF_DOUBLE; - auto nodeSet = Graph::getNodeSet(); - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist, - currentDist; - // n denotes the number of vertices in graph - auto n = nodeSet.size(); - for (const auto &elem : nodeSet) { - dist[elem] = INF_DOUBLE; - currentDist[elem] = INF_DOUBLE; - } - - // marking the distance of source as 0 - dist[*source_node_it] = 0; - // set if node distances in two consecutive - // iterations remain the same. - auto earlyStopping = false; - // outer loop for vertex relaxation - for (int i = 0; i < n - 1; ++i) { - auto edgeSet = Graph::getEdgeSet(); - // inner loop for distance updates of - // each relaxation - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) - dist[elem.second] = dist[elem.first] + edge_weight; - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - auto flag = true; - for (const auto &[key, value] : dist) { - if (currentDist[key] != value) { - flag = false; - break; - } - } - for (const auto &[key, value] : dist) { - currentDist[key] = value; // update the current distance - } - if (flag) { - earlyStopping = true; - break; - } - } - - // check if there exists a negative cycle - if (!earlyStopping) { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edge_weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) { - result.success = true; - result.negativeCycle = true; - result.errorMessage = ""; - return result; - } - } - } - - if (dist[*target_node_it] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.negativeCycle = false; - result.result = dist[*target_node_it]; - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - result.result = -1; - return result; -} - -/* - * See Harry Hsu. "An algorithm for finding a minimal equivalent graph of a - * digraph.", Journal of the ACM, 22(1):11-16, January 1975 - * - * foreach x in graph.vertices - * foreach y in graph.vertices - * foreach z in graph.vertices - * delete edge xz if edges xy and yz exist - */ -template -const Graph Graph::transitiveReduction() const { - Graph result(this->edgeSet); - - CXXGraph::id_t edgeId = 0; - std::unordered_set>, nodeHash> nodes = - this->getNodeSet(); - for (auto x : nodes) { - for (auto y : nodes) { - if (this->findEdge(x, y, edgeId)) { - for (auto z : nodes) { - if (this->findEdge(y, z, edgeId)) { - if (this->findEdge(x, z, edgeId)) { - result.removeEdge(edgeId); - } - } - } - } - } - } - - return result; -} - -template -const FWResult Graph::floydWarshall() const { - FWResult result; - result.success = false; - result.errorMessage = ""; - std::unordered_map, double, - CXXGraph::pair_hash> - pairwise_dist; - const auto &nodeSet = Graph::getNodeSet(); - // create a pairwise distance matrix with distance node distances - // set to inf. Distance of node to itself is set as 0. - for (const auto &elem1 : nodeSet) { - for (const auto &elem2 : nodeSet) { - auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); - if (elem1 != elem2) - pairwise_dist[key] = INF_DOUBLE; - else - pairwise_dist[key] = 0.0; - } - } - - const auto &edgeSet = Graph::getEdgeSet(); - // update the weights of nodes - // connected by edges - for (const auto &edge : edgeSet) { - const auto &elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edgeWeight = - (std::dynamic_pointer_cast(edge))->getWeight(); - auto key = - std::make_pair(elem.first->getUserId(), elem.second->getUserId()); - pairwise_dist[key] = edgeWeight; - } else { - // if an edge exists but has no weight associated - // with it, we return an error message - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - for (const auto &k : nodeSet) { - // set all vertices as source one by one - for (const auto &src : nodeSet) { - // iterate through all vertices as destination for the - // current source - auto src_k = std::make_pair(src->getUserId(), k->getUserId()); - for (const auto &dst : nodeSet) { - // If vertex k provides a shorter path than - // src to dst, update the value of - // pairwise_dist[src_to_dst] - auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); - auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); - if (pairwise_dist[src_dst] > - (pairwise_dist[src_k] + pairwise_dist[k_dst]) && - (pairwise_dist[k_dst] != INF_DOUBLE && - pairwise_dist[src_k] != INF_DOUBLE)) - pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; - } - } - } - - result.success = true; - // presense of negative number in the diagonal indicates - // that that the graph contains a negative cycle - for (const auto &node : nodeSet) { - auto diag = std::make_pair(node->getUserId(), node->getUserId()); - if (pairwise_dist[diag] < 0.) { - result.negativeCycle = true; - return result; - } - } - result.result = std::move(pairwise_dist); - return result; -} - -template -const MstResult Graph::prim() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - if (!isConnectedGraph()) { - result.errorMessage = ERR_NOT_STRONG_CONNECTED; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist; - for (const auto &elem : (*cachedAdjMatrix)) { - dist[elem.first] = INF_DOUBLE; - } - - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - auto source = *(nodeSet.begin()); - pq.push(std::make_pair(0.0, source)); - result.mstCost = 0; - std::vector doneNode; - // mark source node as done - // otherwise we get (0, 0) also in mst - doneNode.push_back(source->getId()); - // stores the parent and corresponding child node - // of the edges that are part of MST - std::unordered_map parentNode; - while (!pq.empty()) { - // second element of pair denotes the node / vertex - shared> currentNode = pq.top().second; - auto nodeId = currentNode->getId(); - if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { - auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); - result.mst.push_back(pair); - result.mstCost += pq.top().first; - doneNode.push_back(nodeId); - } - - pq.pop(); - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - shared> udw_edge = - std::static_pointer_cast>( - elem.second); - if ((udw_edge->getWeight() < dist[elem.first]) && - (std::find(doneNode.begin(), doneNode.end(), - elem.first->getId()) == doneNode.end())) { - dist[elem.first] = udw_edge->getWeight(); - parentNode[elem.first->getId()] = currentNode->getUserId(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - } - } - result.success = true; - return result; -} - -template -const MstResult Graph::boruvka() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - const auto n = nodeSet.size(); - - // Use std map for storing n subsets. - auto subsets = make_shared>(); - - // Initially there are n different trees. - // Finally there will be one tree that will be MST - auto numTrees = n; - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - const auto edgeSet = Graph::getEdgeSet(); - std::unordered_map edgeWeight; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) - edgeWeight[edge->getId()] = - (std::dynamic_pointer_cast(edge))->getWeight(); - else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - (*subsets)[node->getId()] = set; - } - - result.mstCost = 0; // we will store the cost here - // exit when only 1 tree i.e. mst - while (numTrees > 1) { - // Everytime initialize cheapest map - // It stores index of the cheapest edge of subset. - std::unordered_map cheapest; - for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; - - // Traverse through all edges and update - // cheapest of every component - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edgeId = edge->getId(); - // Find sets of two corners of current edge - auto set1 = Graph::setFind(subsets, elem.first->getId()); - auto set2 = Graph::setFind(subsets, elem.second->getId()); - - // If two corners of current edge belong to - // same set, ignore current edge - if (set1 == set2) continue; - - // Else check if current edge is closer to previous - // cheapest edges of set1 and set2 - if (cheapest[set1] == INT_MAX || - edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) - cheapest[set1] = edgeId; - - if (cheapest[set2] == INT_MAX || - edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) - cheapest[set2] = edgeId; - } - - // iterate over all the vertices and add picked - // cheapest edges to MST - for (const auto &[nodeId, edgeId] : cheapest) { - // Check if cheapest for current set exists - if (edgeId != INT_MAX) { - auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); - auto set1 = Graph::setFind(subsets, cheapestNode.first->getId()); - auto set2 = Graph::setFind(subsets, cheapestNode.second->getId()); - if (set1 == set2) continue; - result.mstCost += edgeWeight[edgeId]; - auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), - cheapestNode.second->getUserId()); - result.mst.push_back(newEdgeMST); - // take union of set1 and set2 and decrease number of trees - Graph::setUnion(subsets, set1, set2); - numTrees--; - } - } - } - result.success = true; - return result; -} - -template -const MstResult Graph::kruskal() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - auto edgeSet = Graph::getEdgeSet(); - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - sortedEdges; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - sortedEdges.push(std::make_pair(weight, edge)); - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - auto subset = make_shared>(); - - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - (*subset)[node->getId()] = set; - } - result.mstCost = 0; - while ((!sortedEdges.empty()) && (result.mst.size() < n)) { - auto [edgeWeight, cheapestEdge] = sortedEdges.top(); - sortedEdges.pop(); - auto &[first, second] = cheapestEdge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 != set2) { - result.mst.push_back( - std::make_pair(first->getUserId(), second->getUserId())); - result.mstCost += edgeWeight; - } - Graph::setUnion(subset, set1, set2); - } - result.success = true; - return result; -} - -template -BestFirstSearchResult Graph::best_first_search( - const Node &source, const Node &target) const { - BestFirstSearchResult result; - auto &nodeSet = Graph::getNodeSet(); - using pq_type = std::pair>>; - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - - std::priority_queue, std::greater> pq; - - std::vector> visited; - visited.push_back(source); - pq.push(std::make_pair(0.0, *source_node_it)); - - while (!pq.empty()) { - shared> currentNode = pq.top().second; - pq.pop(); - result.nodesInBestSearchOrder.push_back(*currentNode); - - if (*currentNode == target) { - break; - } - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - if (elem.second->isWeighted().has_value()) { - if (elem.second->isDirected().has_value()) { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } else { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } - } else { - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - result.nodesInBestSearchOrder.clear(); - return result; - } - } - } - } - - result.success = true; - return result; -} - -template -const std::vector> Graph::breadth_first_search( - const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto &nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return visited; - } - // queue that stores vertices that need to be further explored - std::queue>> tracker; - - // mark the starting node as visited - visited.push_back(start); - tracker.push(*start_node_it); - while (!tracker.empty()) { - shared> node = tracker.front(); - tracker.pop(); - if (cachedAdjMatrix->find(node) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(node)) { - // if the node is not visited then mark it as visited - // and push it to the queue - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - tracker.push(elem.first); - } - } - } - } - - return visited; -} - -template -const std::vector> Graph::concurrency_breadth_first_search( - const Node &start, size_t num_threads) const { - std::vector> bfs_result; - // check is exist node in the graph - auto &nodeSet = Graph::getNodeSet(); - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return bfs_result; - } - - std::unordered_map>, size_t, nodeHash> node_to_index; - for (const auto &node : nodeSet) { - node_to_index[node] = node_to_index.size(); - } - std::vector visited(nodeSet.size(), 0); - - // parameter limitations - if (num_threads <= 0) { - std::cout << "Error: number of threads should be greater than 0" - << std::endl; - num_threads = 2; - } - - // vector that stores vertices to be visit - std::vector>> level_tracker, next_level_tracker; - level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - - // mark the starting node as visited - visited[node_to_index[*start_node_it]] = 1; - level_tracker.push_back(*start_node_it); - - // a worker is assigned a small part of tasks for each time - // assignments of tasks in current level and updates of tasks in next - // level are inclusive - std::mutex tracker_mutex; - std::mutex next_tracker_mutex; - std::atomic assigned_tasks = 0; - int num_tasks = 1; - // unit of task assignment, which mean assign block_size tasks to a - // worker each time - int block_size = 1; - int level = 1; - - auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, - &num_tasks, &block_size]() -> std::pair { - /* - std::lock_guard tracker_guard(tracker_mutex); - int task_block_size = std::min(num_tasks - assigned_tasks, - block_size); std::pair task_block{assigned_tasks, - assigned_tasks + task_block_size}; assigned_tasks += task_block_size; - return task_block; - */ - int start = assigned_tasks.fetch_add(block_size); - int end = std::min(num_tasks, start + block_size); - return {start, end}; - }; - - auto submit_result = - [&next_level_tracker, &next_tracker_mutex]( - std::vector>> &submission) -> void { - std::lock_guard tracker_guard(next_tracker_mutex); - next_level_tracker.insert(std::end(next_level_tracker), - std::begin(submission), std::end(submission)); - }; - - // worker thread sleep until it begin to search nodes of next level - std::mutex next_level_mutex; - std::condition_variable next_level_cond; - std::atomic waiting_workers = 0; - - auto bfs_worker = [&]() -> void { - // algorithm is not done - while (!level_tracker.empty()) { - // search for nodes in a level is not done - std::vector>> local_tracker; - while (true) { - auto [start_index, end_index] = extract_tasks(); - if (start_index >= end_index) { - break; - } - - for (int i = start_index; i < end_index; ++i) { - if (cachedAdjMatrix->count(level_tracker[i])) { - for (const auto &elem : cachedAdjMatrix->at(level_tracker[i])) { - int index = (int)node_to_index[elem.first]; - if (visited[index] == 0) { - visited[index] = 1; - local_tracker.push_back(elem.first); - } - } - } - } - } - - // submit local result to global result - if (!local_tracker.empty()) { - submit_result(local_tracker); - } - - // last worker need to do preparation for the next iteration - int cur_level = level; - if (num_threads == 1 + waiting_workers.fetch_add(1)) { - swap(level_tracker, next_level_tracker); - next_level_tracker.clear(); - - // adjust block_size according to number of nodes in next level - block_size = 4; - if (level_tracker.size() <= num_threads * 4) { - block_size = std::max( - 1, static_cast(std::ceil( - static_cast(level_tracker.size()) / num_threads))); - } else if (level_tracker.size() >= num_threads * 64) { - block_size = 16; - } - - num_tasks = (int)level_tracker.size(); - waiting_workers = 0; - assigned_tasks = 0; - level = level + 1; - next_level_cond.notify_all(); - } else { - // not to wait if last worker reachs last statement before notify - // all or even further - std::unique_lock next_level_lock(next_level_mutex); - next_level_cond.wait(next_level_lock, [&level, cur_level]() { - return level != cur_level; - }); - } - } - }; - - std::vector workers; - for (int i = 0; i < num_threads - 1; ++i) { - workers.emplace_back(std::thread(bfs_worker)); - } - bfs_worker(); - - for (auto &worker : workers) { - if (worker.joinable()) { - worker.join(); - } - } - - for (const auto &visited_node : nodeSet) { - if (visited[node_to_index[visited_node]] != 0) { - bfs_result.push_back(*visited_node); - } - } - - return bfs_result; -} -template -const std::vector> Graph::depth_first_search( - const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return visited; - } - std::function>, - shared>, std::vector> &)> - explore; - explore = [&explore](const std::shared_ptr> adj, - shared> node, - std::vector> &visited) -> void { - visited.push_back(*node); - if (adj->find(node) != adj->end()) { - for (const auto &x : adj->at(node)) { - if (std::find(visited.begin(), visited.end(), *(x.first)) == - visited.end()) { - explore(adj, x.first, visited); - } - } - } - }; - explore(cachedAdjMatrix, *start_node_it, visited); - - return visited; -} - -template -bool Graph::isCyclicDirectedGraphDFS() const { - if (!isDirectedGraph()) { - return false; - } - enum nodeStates : uint8_t { not_visited, in_stack, visited }; - auto nodeSet = Graph::getNodeSet(); - - /* State of the node. - * - * It is a vector of "nodeStates" which represents the state node is in. - * It can take only 3 values: "not_visited", "in_stack", and "visited". - * - * Initially, all nodes are in "not_visited" state. - */ - std::unordered_map state; - for (const auto &node : nodeSet) { - state[node->getId()] = not_visited; - } - int stateCounter = 0; - - // Start visiting each node. - for (const auto &node : nodeSet) { - // If a node is not visited, only then check for presence of cycle. - // There is no need to check for presence of cycle for a visited - // node as it has already been checked for presence of cycle. - if (state[node->getId()] == not_visited) { - // Check for cycle. - std::function>, - std::unordered_map &, - shared>)> - isCyclicDFSHelper; - isCyclicDFSHelper = - [this, &isCyclicDFSHelper]( - const std::shared_ptr> adjMatrix, - std::unordered_map &states, - shared> node) { - // Add node "in_stack" state. - states[node->getId()] = in_stack; - - // If the node has children, then recursively visit all - // children of the node. - auto const it = adjMatrix->find(node); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // If state of child node is "not_visited", evaluate that - // child for presence of cycle. - auto state_of_child = states.at((std::get<0>(child))->getId()); - if (state_of_child == not_visited) { - if (isCyclicDFSHelper(adjMatrix, states, - std::get<0>(child))) { - return true; - } - } else if (state_of_child == in_stack) { - // If child node was "in_stack", then that means that - // there is a cycle in the graph. Return true for - // presence of the cycle. - return true; - } - } - } - - // Current node has been evaluated for the presence of cycle - // and had no cycle. Mark current node as "visited". - states[node->getId()] = visited; - // Return that current node didn't result in any cycles. - return false; - }; - if (isCyclicDFSHelper(cachedAdjMatrix, state, node)) { - return true; - } - } - } - - // All nodes have been safely traversed, that means there is no cycle in - // the graph. Return false. - return false; -} - -template -bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { - auto edgeSet_ptr = make_shared>(*edgeSet); - auto subset = make_shared>(); - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet_ptr) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == - (*subset).end()) { - Subset set; - set.parent = id; - set.rank = 0; - (*subset)[id] = set; - } - } - } - return Graph::containsCycle(edgeSet_ptr, subset); -} - -template -bool Graph::containsCycle(shared> edgeSet) const { - auto subset = make_shared>(); - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == - (*subset).end()) { - Subset set; - set.parent = id; - set.rank = 0; - (*subset)[id] = set; - } - } - } - return Graph::containsCycle(edgeSet, subset); -} - -template -bool Graph::containsCycle( - shared> edgeSet, - shared> subset) const { - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 == set2) return true; - Graph::setUnion(subset, set1, set2); - } - return false; -} - -template -bool Graph::isCyclicDirectedGraphBFS() const { - if (!isDirectedGraph()) { - return false; - } - auto nodeSet = Graph::getNodeSet(); - - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const &list : (*cachedAdjMatrix)) { - auto children = list.second; - for (auto const &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } - - std::queue>> can_be_solved; - for (const auto &node : nodeSet) { - // If a node doesn't have any input edges, then that node will - // definately not result in a cycle and can be visited safely. - if (!indegree[node->getId()]) { - can_be_solved.emplace(node); - } - } - - // Vertices that need to be traversed. - auto remain = nodeSet.size(); - // While there are safe nodes that we can visit. - while (!can_be_solved.empty()) { - auto solved = can_be_solved.front(); - // Visit the node. - can_be_solved.pop(); - // Decrease number of nodes that need to be traversed. - remain--; - - // Visit all the children of the visited node. - auto it = cachedAdjMatrix->find(solved); - if (it != cachedAdjMatrix->end()) { - for (const auto &child : it->second) { - // Check if we can visited the node safely. - if (--indegree[std::get<0>(child)->getId()] == 0) { - // if node can be visited safely, then add that node to - // the visit queue. - can_be_solved.emplace(std::get<0>(child)); - } - } - } - } - - // If there are still nodes that we can't visit, then it means that - // there is a cycle and return true, else return false. - return !(remain == 0); -} - -template -bool Graph::isDirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if (!(edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Undirected Edge - return false; - } - } - // No Undirected Edge - return true; -} - -template -bool Graph::isUndirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if ((edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Directed Edge - return false; - } - } - // No Directed Edge - return true; -} - -template -void Graph::reverseDirectedGraph() { - if (!isDirectedGraph()) { - throw std::runtime_error(ERR_UNDIR_GRAPH); - } - auto oldEdgeSet = Graph::getEdgeSet(); - for (const auto &edge : oldEdgeSet) { - auto &[first, second] = edge->getNodePair(); - auto id = edge->getId(); - this->removeEdge(id); - auto newEdge = std::make_shared>(id, second, first); - this->addEdge(newEdge); - } -} - -template -bool Graph::isConnectedGraph() const { - if (!isUndirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function>)> dfs_helper = - [this, &visited, &dfs_helper](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(*(nodeSet.begin())); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; - } - } - return true; - } -} - -template -bool Graph::isStronglyConnectedGraph() const { - if (!isDirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - for (const auto &start_node : nodeSet) { - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function>)> dfs_helper = - [this, &visited, &dfs_helper](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(start_node); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; - } - } - } - return true; - } -} - -template -const TarjanResult Graph::tarjan(const unsigned int typeMask) const { - TarjanResult result; - result.success = false; - bool isDirected = this->isDirectedGraph(); - if (isDirected) { - // check whether targetMask is a subset of the mask for directed graph - unsigned int directedMask = TARJAN_FIND_SCC; - if ((typeMask | directedMask) != directedMask) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - } else { - // check whether targetMask is a subset of the mask for undirected graph - unsigned int undirectedMask = (TARJAN_FIND_CUTV | TARJAN_FIND_BRIDGE | - TARJAN_FIND_VBCC | TARJAN_FIND_EBCC); - if ((typeMask | undirectedMask) != undirectedMask) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } - } - - const auto &nodeSet = getNodeSet(); - std::unordered_map - discoveryTime; // the timestamp when a node is visited - std::unordered_map - lowestDisc; // the lowest discovery time of all - // reachable nodes from current node - int timestamp = 0; - CXXGraph::id_t rootId = 0; - std::stack> sccNodeStack; - std::stack> ebccNodeStack; - std::stack> vbccNodeStack; - std::unordered_set inStack; - std::function>, const shared>)> - dfs_helper = [this, typeMask, isDirected, &dfs_helper, &discoveryTime, - &lowestDisc, ×tamp, &rootId, &sccNodeStack, - &ebccNodeStack, &vbccNodeStack, &inStack, - &result](const shared> curNode, - const shared> prevEdge) { - // record the visited time of current node - discoveryTime[curNode->getId()] = timestamp; - lowestDisc[curNode->getId()] = timestamp; - timestamp++; - if (typeMask & TARJAN_FIND_SCC) { - sccNodeStack.emplace(*curNode); - inStack.emplace(curNode->getId()); - } - if (typeMask & TARJAN_FIND_EBCC) { - ebccNodeStack.emplace(*curNode); - } - if (typeMask & TARJAN_FIND_VBCC) { - vbccNodeStack.emplace(*curNode); - } - // travel the neighbors - int numSon = 0; - bool nodeIsAdded = - false; // whether a node has been marked as a cut vertice - if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { - for (const auto &[neighborNode, edge] : - cachedAdjMatrix->at(curNode)) { - if (!discoveryTime.count(neighborNode->getId())) { - dfs_helper(neighborNode, edge); - lowestDisc[curNode->getId()] = - std::min(lowestDisc[curNode->getId()], - lowestDisc[neighborNode->getId()]); - - if (typeMask & TARJAN_FIND_BRIDGE) { - // lowestDisc of neighbor node is larger than that of current - // node means we can travel back to a visited node only through - // this edge - if (discoveryTime[curNode->getId()] < - lowestDisc[neighborNode->getId()]) { - result.bridges.emplace_back(*edge); - } - } - - if ((typeMask & TARJAN_FIND_CUTV) && (nodeIsAdded == false)) { - if (curNode->getId() == rootId) { - numSon++; - // a root node is a cut vertices only when it connects at - // least two connected components - if (numSon == 2) { - nodeIsAdded = true; - result.cutVertices.emplace_back(*curNode); - } - } else { - if (discoveryTime[curNode->getId()] <= - lowestDisc[neighborNode->getId()]) { - nodeIsAdded = true; - result.cutVertices.emplace_back(*curNode); - } - } - } - - if (typeMask & TARJAN_FIND_VBCC) { - if (discoveryTime[curNode->getId()] <= - lowestDisc[neighborNode->getId()]) { - // if current node is a cut vertice or the root node, the vbcc - // a vertice-biconnect-component which contains the neighbor - // node - std::vector> vbcc; - while (true) { - // pop a top node out of stack until - // the neighbor node has been poped out - Node nodeAtTop = vbccNodeStack.top(); - vbccNodeStack.pop(); - vbcc.emplace_back(nodeAtTop); - if (nodeAtTop == *neighborNode) { - break; - } - } - vbcc.emplace_back(*curNode); - result.verticeBiconnectedComps.emplace_back(std::move(vbcc)); - } - } - } else if ((edge != prevEdge) && - ((isDirected == false) || - (inStack.count(neighborNode->getId())))) { - // it's not allowed to go through the previous edge back - // for a directed graph, it's also not allowed to visit - // a node that is not in stack - lowestDisc[curNode->getId()] = - std::min(lowestDisc[curNode->getId()], - lowestDisc[neighborNode->getId()]); - } - } - } - // find sccs for a undirected graph is very similar with - // find ebccs for a directed graph - if ((typeMask & TARJAN_FIND_SCC) || (typeMask & TARJAN_FIND_EBCC)) { - std::stack> &nodeStack = - (typeMask & TARJAN_FIND_SCC) ? sccNodeStack : ebccNodeStack; - if (discoveryTime[curNode->getId()] == lowestDisc[curNode->getId()]) { - std::vector> connectedComp; - while (true) { - // pop a top node out of stack until - // the current node has been poped out - Node nodeAtTop = nodeStack.top(); - nodeStack.pop(); - if (typeMask & TARJAN_FIND_SCC) { - inStack.erase(nodeAtTop.getId()); - } - connectedComp.emplace_back(nodeAtTop); - if (nodeAtTop == *curNode) { - break; - } - } - // store this component in result - (typeMask & TARJAN_FIND_SCC) - ? result.stronglyConnectedComps.emplace_back( - std::move(connectedComp)) - : result.edgeBiconnectedComps.emplace_back( - std::move(connectedComp)); - } - } - }; - - for (const auto &node : nodeSet) { - if (!discoveryTime.count(node->getId())) { - rootId = node->getId(); - dfs_helper(node, nullptr); - } - } - - result.success = true; - return result; -} - -template -TopoSortResult Graph::topologicalSort() const { - TopoSortResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else if (isCyclicDirectedGraphBFS()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - return result; - } else { - const auto &nodeSet = getNodeSet(); - std::unordered_map>, bool, nodeHash> visited; - - std::function>)> postorder_helper = - [this, &postorder_helper, &visited, - &result](shared> curNode) { - visited[curNode] = true; - - if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { - for (const auto &edge : cachedAdjMatrix->at(curNode)) { - const auto &nextNode = edge.first; - if (false == visited[nextNode]) { - postorder_helper(nextNode); - } - } - } - - result.nodesInTopoOrder.push_back(*curNode); - }; - - auto numNodes = cachedAdjMatrix->size(); - result.nodesInTopoOrder.reserve(numNodes); - - for (const auto &node : nodeSet) { - if (false == visited[node]) { - postorder_helper(node); - } - } - - result.success = true; - std::reverse(result.nodesInTopoOrder.begin(), - result.nodesInTopoOrder.end()); - return result; - } -} - -template -TopoSortResult Graph::kahn() const { - TopoSortResult result; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - const auto nodeSet = Graph::getNodeSet(); - result.nodesInTopoOrder.reserve(cachedAdjMatrix->size()); - - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - for (const auto &list : *cachedAdjMatrix) { - auto children = list.second; - for (const auto &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } - - std::queue>> topologicalOrder; - - for (const auto &node : nodeSet) { - if (!indegree[node->getId()]) { - topologicalOrder.emplace(node); - } - } - - size_t visited = 0; - while (!topologicalOrder.empty()) { - shared> currentNode = topologicalOrder.front(); - topologicalOrder.pop(); - result.nodesInTopoOrder.push_back(*currentNode); - - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &child : cachedAdjMatrix->at(currentNode)) { - if (--indegree[std::get<0>(child)->getId()] == 0) { - topologicalOrder.emplace(std::get<0>(child)); - } - } - } - visited++; - } - - if (visited != nodeSet.size()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - result.nodesInTopoOrder.clear(); - return result; - } - - result.success = true; - return result; - } -} - -template -SCCResult Graph::kosaraju() const { - SCCResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - auto nodeSet = getNodeSet(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - - std::stack>> st; - std::function>)> dfs_helper = - [this, &visited, &dfs_helper, &st](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - - st.push(source); - }; - - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - dfs_helper(node); - } - } - - // construct the transpose of the given graph - AdjacencyMatrix rev; - auto addElementToAdjMatrix = [&rev](shared> nodeFrom, - shared> nodeTo, - shared> edge) { - std::pair>, shared>> elem = {nodeTo, - edge}; - rev[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - // Add the reverse edge to the reverse adjacency matrix - addElementToAdjMatrix(d_edge->getNodePair().second, - d_edge->getNodePair().first, d_edge); - } - - visited.clear(); - - std::function>, SCCResult, int)> - dfs_helper1 = - [this, &rev, &visited, &dfs_helper1](shared> source, - SCCResult result, int sccLabel) { - // mark the vertex visited - visited[source->getId()] = true; - // Add the current vertex to the strongly connected - // component - //comp.push_back(*source); - result.sccMap[source->getId()] = sccLabel; - - // travel the neighbors - for (int i = 0; i < rev[source].size(); i++) { - shared> neighbor = rev[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper1(neighbor, result, sccLabel); - } - } - }; - - int sccLabel = 0; - while (st.size() != 0) { - auto rem = st.top(); - st.pop(); - if (visited[rem->getId()] == false) { - //std::vector> comp; - dfs_helper1(rem, result, sccLabel); - sccLabel++; - //result.stronglyConnectedComps.push_back(comp); - } - } - result.noOfComponents = sccLabel; - result.success = true; - return result; - } -} - -template -const DialResult Graph::dial(const Node &source, int maxWeight) const { - DialResult result; - result.success = false; - - auto nodeSet = getNodeSet(); - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - /* With each distance, iterator to that vertex in - its bucket is stored so that vertex can be deleted - in O(1) at time of updation. So - dist[i].first = distance of ith vertex from src vertex - dits[i].second = vertex i in bucket number */ - auto V = nodeSet.size(); - std::unordered_map>, - std::pair>>, nodeHash> - dist; - - // Initialize all distances as infinite (INF) - for (const auto &node : nodeSet) { - dist[node].first = std::numeric_limits::max(); - } - - // Create buckets B[]. - // B[i] keep vertex of distance label i - std::vector>>> B((maxWeight * V + 1)); - - B[0].push_back(*source_node_it); - dist[*source_node_it].first = 0; - - int idx = 0; - while (true) { - // Go sequentially through buckets till one non-empty - // bucket is found - while (B[idx].size() == 0u && idx < maxWeight * V) { - idx++; - } - - // If all buckets are empty, we are done. - if (idx == maxWeight * V) { - break; - } - - // Take top vertex from bucket and pop it - auto u = B[idx].front(); - B[idx].pop_front(); - - // Process all adjacents of extracted vertex 'u' and - // update their distanced if required. - for (const auto &i : (*cachedAdjMatrix)[u]) { - auto v = i.first; - int weight = 0; - if (i.second->isWeighted().has_value() && - i.second->isWeighted().value()) { - if (i.second->isDirected().has_value() && - i.second->isDirected().value()) { - shared> dw_edge = - std::static_pointer_cast>(i.second); - weight = (int)dw_edge->getWeight(); - } else if (i.second->isDirected().has_value() && - !i.second->isDirected().value()) { - shared> udw_edge = - std::static_pointer_cast>( - i.second); - weight = (int)udw_edge->getWeight(); - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - auto u_i = std::find_if( - dist.begin(), dist.end(), - [u](std::pair>, - std::pair>>> const &it) { - return (*u == *(it.first)); - }); - - auto v_i = std::find_if( - dist.begin(), dist.end(), - [v](std::pair>, - std::pair>>> const &it) { - return (*v == *(it.first)); - }); - long du = u_i->second.first; - long dv = v_i->second.first; - - // If there is shorted path to v through u. - if (dv > du + weight) { - // If dv is not INF then it must be in B[dv] - // bucket, so erase its entry using iterator - // in O(1) - if (dv != std::numeric_limits::max()) { - auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); - B[dv].erase(findIter); - } - - // updating the distance - dist[v].first = du + weight; - dv = dist[v].first; - - // pushing vertex v into updated distance's bucket - B[dv].push_front(v); - - // storing updated iterator in dist[v].second - dist[v].second = *(B[dv].begin()); - } - } - } - for (const auto &dist_i : dist) { - result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; - } - result.success = true; - - return result; -} - -template -double Graph::fordFulkersonMaxFlow(const Node &source, - const Node &target) const { - if (!isDirectedGraph()) { - return -1; - } - double maxFlow = 0; - std::unordered_map>, shared>, nodeHash> - parent; - std::unordered_map< - shared>, - std::unordered_map>, double, nodeHash>, - nodeHash> - weightMap; - // build weight map - auto edgeSet = this->getEdgeSet(); - for (const auto &edge : edgeSet) { - // The Edge are all Directed at this point because is checked at the - // start - if (edge->isWeighted().value_or(false)) { - shared> dw_edge = - std::static_pointer_cast>(edge); - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - dw_edge->getWeight(); - } else { - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - 0; // No Weighted Edge are assumed to be 0 weigthed - } - } - - // Constuct iterators for source and target nodes in nodeSet - auto nodeSet = getNodeSet(); - auto source_node_ptr = *std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - auto target_node_ptr = *std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - - auto bfs_helper = [this, &source_node_ptr, &target_node_ptr, &parent, - &weightMap]() -> bool { - std::unordered_map>, bool, nodeHash> visited; - std::queue>> queue; - queue.push(source_node_ptr); - visited[source_node_ptr] = true; - parent[source_node_ptr] = nullptr; - while (!queue.empty()) { - auto u = queue.front(); - queue.pop(); - for (auto &v : weightMap[u]) { - if (!visited[v.first] && v.second > 0) { - queue.push(v.first); - visited[v.first] = true; - parent[v.first] = u; - } - } - } - - return (visited[target_node_ptr]); - }; - // Updating the residual values of edges - while (bfs_helper()) { - double pathFlow = std::numeric_limits::max(); - for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { - auto u = parent[v]; - pathFlow = std::min(pathFlow, weightMap[u][v]); - } - for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { - auto u = parent[v]; - weightMap[u][v] -= pathFlow; - weightMap[v][u] += pathFlow; - } - // Adding the path flows - maxFlow += pathFlow; - } - - return maxFlow; -} - -template -std::map, int> Graph::welshPowellColoring() const { - auto adjMatrix = *getAdjMatrix(); - std::unordered_map>, int> degreeOfVertexMap = {}; - - // Find the degree of each vertex and put them in a map - for (auto &[nodeFrom, nodeToEdgeVec] : adjMatrix) { - degreeOfVertexMap[nodeFrom] = nodeToEdgeVec.size(); - } - - // Transform the map to the vector to sort - std::vector>, int>> degreeOfVertexVector(degreeOfVertexMap.begin(), degreeOfVertexMap.end()); - - // Sort them based on the vertex degree - std::sort(degreeOfVertexVector.begin(), degreeOfVertexVector.end(), [](const auto& left, const auto& right) { - return left.second > right.second; - }); - - // Create a new map of coloring, where the keys are the nodes, and the value is the color order (assigned by integer) - std::map, int> mapOfColoring; - for (auto &[nodeFrom, _] : adjMatrix) { - mapOfColoring[*nodeFrom] = 0; - } - - // Going down the list of vertex based on degrees - for (const auto& [node, _] : degreeOfVertexVector) { - // Find the smallest unused color for the current vertex - std::vector usedColors(degreeOfVertexVector.size() + 1, 0); - - for (const auto &[neighbor, _] : adjMatrix[node]) { - usedColors[mapOfColoring[*neighbor]] = 1; - } - - // Assign the smallest unused color to the current vertex - for (int c = 1; c < usedColors.size(); c++) { - if (usedColors[c] == 0) { - mapOfColoring[*node] = c; - break; - } - } - } - - return mapOfColoring; -} - - -template -const std::vector> Graph::graph_slicing(const Node &start) const { - std::vector> result; - - auto nodeSet = Graph::getNodeSet(); - // check if start node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return result; - } - std::vector> C = Graph::depth_first_search(start); - std::deque>> C1; // complement of C i.e. nodeSet - C - for (auto const &node : nodeSet) { - // from the set of all nodes, remove nodes that exist in C - if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { - return (*node == nodeC); - }) == C.end()) - C1.push_back(node); - } - - // For all nodes in C', apply DFS - // and get the list of reachable nodes and store in M - std::vector> M; - for (auto const &node : C1) { - std::vector> reachableNodes = Graph::depth_first_search(*node); - M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); - } - // removes nodes from C that are reachable from M. - for (const auto &nodeC : C) { - if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { - return (nodeM == nodeC); - }) == M.end()) - result.push_back(nodeC); - } - return result; -} - -template -int Graph::writeToFile(InputOutputFormat format, - const std::string &workingDir, - const std::string &OFileName, bool compress, - bool writeNodeFeat, bool writeEdgeWeight) const { - int result = 0; - - // Open streams and write - auto extSep = getExtenstionAndSeparator(format); - if (!extSep) { - std::cerr << "Unknown format\n"; - return -1; - } - auto &[extension, separator] = *extSep; - - std::ofstream ofileGraph; - std::ofstream ofileNodeFeat; - std::ofstream ofileEdgeWeight; - - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - ofileGraph.open(completePathToFileGraph); - if (!ofileGraph.is_open()) { - // ERROR File Not Open - return -1; - } - - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - ofileNodeFeat.open(completePathToFileNodeFeat); - if (!ofileNodeFeat.is_open()) { - // ERROR File Not Open - return -1; - } - } - - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - ofileEdgeWeight.open(completePathToFileEdgeWeight); - if (!ofileEdgeWeight.is_open()) { - // ERROR File Not Open - std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - } - - writeGraphToStream(ofileGraph, ofileNodeFeat, ofileEdgeWeight, separator, - writeNodeFeat, writeEdgeWeight); - - // Cleanup from writing - ofileGraph.close(); - if (writeNodeFeat) ofileNodeFeat.close(); - if (writeEdgeWeight) ofileEdgeWeight.close(); - -#ifdef WITH_COMPRESSION - if (result == 0 && compress) { - auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, - &writeEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = compressFile(completePathToFileGraph, - completePathToFileGraphCompressed); - if (_result == 0) { - _result = remove(completePathToFileGraph.c_str()); - } - if (_result == 0) { - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = compressFile(completePathToFileNodeFeat, - completePathToFileNodeFeatCompressed); - if (_result == 0) { - _result = remove(completePathToFileNodeFeat.c_str()); - } - } - } - if (_result == 0) { - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = compressFile(completePathToFileEdgeWeight, - completePathToFileEdgeWeightCompressed); - if (_result == 0) { - _result = remove(completePathToFileEdgeWeight.c_str()); - } - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - auto result = _compress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - auto result = _compress(".tsv"); - } else { - // OUTPUT FORMAT NOT RECOGNIZED - result = -1; - } - } -#endif - - return result; -} - -template -int Graph::readFromFile(InputOutputFormat format, - const std::string &workingDir, - const std::string &OFileName, bool compress, - bool readNodeFeat, bool readEdgeWeight) { - int result = 0; - -#ifdef WITH_COMPRESSION - if (compress) { - auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, - &readEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = decompressFile(completePathToFileGraphCompressed, - completePathToFileGraph); - if (_result == 0) { - if (readNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = decompressFile(completePathToFileNodeFeatCompressed, - completePathToFileNodeFeat); - } - } - if (_result == 0) { - if (readEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = decompressFile(completePathToFileEdgeWeightCompressed, - completePathToFileEdgeWeight); - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - result = decompress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - result = decompress(".tsv"); - } else { - // INPUT FORMAT NOT RECOGNIZED - result = -1; - } - - if (result != 0) { - return result; - } - } -#endif - // Open streams and read - auto extSep = getExtenstionAndSeparator(format); - if (!extSep) { - std::cerr << "Unknown format\n"; - return -1; - } - auto &[extension, separator] = *extSep; - - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileNodeFeat; - std::string completePathToFileEdgeWeight; - - std::ifstream ifileGraph; - std::ifstream ifileNodeFeat; - std::ifstream ifileEdgeWeight; - - ifileGraph.open(completePathToFileGraph); - if (!ifileGraph.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open : " << completePathToFileGraph << - // std::endl; - return -1; - } - ifileGraph.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - - if (readNodeFeat) { - completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - ifileNodeFeat.open(completePathToFileNodeFeat); - if (!ifileNodeFeat.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - ifileNodeFeat.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - } - - if (readEdgeWeight) { - completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - ifileEdgeWeight.open(completePathToFileEdgeWeight); - if (!ifileEdgeWeight.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - ifileEdgeWeight.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - } - - readGraphFromStream(ifileGraph, ifileNodeFeat, ifileEdgeWeight, readNodeFeat, - readEdgeWeight); - - // Cleanup - ifileGraph.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileGraph.c_str()); -#endif - - if (readNodeFeat) { - ifileNodeFeat.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileNodeFeat.c_str()); -#endif - } - - if (readEdgeWeight) { - ifileEdgeWeight.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileEdgeWeight.c_str()); -#endif - } - - return result; -} - -template -int Graph::writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const { - return writeToDot(workingDir, OFileName, graphName); -} - -template -int Graph::writeToMTXFile(const std::string &workingDir, - const std::string &OFileName, - char delimitier) const { - // Get the full path and open the file - const std::string completePathToFileGraph = - workingDir + '/' + OFileName + ".mtx"; - std::ofstream iFile(completePathToFileGraph); - - // Write the header of the file - std::string header = "%%MatrixMarket graph"; - // Check if the adjacency matrix is symmetric, i.e., if all the edges are - // undirected - bool symmetric = !std::any_of(edgeSet.begin(), edgeSet.end(), [](auto edge) { - return (edge->isDirected().has_value() && edge->isDirected().value()); - }); - // Write in the header whether the adj matrix is symmetric or not - if (symmetric) { - header += " symmetric\n"; - } else { - header += '\n'; - } - iFile << header; - - // Write the line containing the number of nodes and edges - const std::string firstLine = - std::to_string(getNodeSet().size()) + delimitier + - std::to_string(getNodeSet().size()) + delimitier + - std::to_string(getEdgeSet().size()) + '\n'; - iFile << firstLine; - - // Construct the edges - for (const auto &edgeIt : edgeSet) { - std::string line; - line += edgeIt->getNodePair().first->getUserId() + delimitier; - line += edgeIt->getNodePair().second->getUserId() + delimitier; - if (edgeIt->isWeighted().has_value() && edgeIt->isWeighted().value()) { - line += std::to_string(edgeIt->isWeighted().value()) + '\n'; - } else { - line += std::to_string(1.) + '\n'; - } - iFile << line; - } - - iFile.close(); - return 0; -} - -template -int Graph::readFromDotFile(const std::string &workingDir, - const std::string &fileName) { - return readFromDot(workingDir, fileName); -} - -template -int Graph::readFromMTXFile(const std::string &workingDir, - const std::string &fileName) { - // Define the edge maps - std::unordered_map> - edgeMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeDirectedMap; - std::unordered_map edgeWeightMap; - - // Get full path to the file and open it - const std::string completePathToFileGraph = - workingDir + '/' + fileName + ".mtx"; - std::ifstream iFile(completePathToFileGraph); - // Check that the file is open - if (!iFile.is_open()) { - return -1; - } - - // Define the number of columns and rows in the matrix - int n_cols, n_rows; - int n_edges; - bool undirected = false; - - // From the first line of the file read the number of rows, columns and edges - std::string row_content; - getline(iFile, row_content); - if (row_content.find("symmetric") != std::string::npos) { - undirected = true; - } - - // Get rid of any commented lines between the header and the size line - while (row_content.find('%') != std::string::npos) { - getline(iFile, row_content); - } - - // From the size line of the file read the number of rows, columns and edges - std::stringstream header_stream(row_content); - std::string value; - getline(header_stream, value, ' '); - n_rows = std::stoi(value); - getline(header_stream, value, ' '); - n_cols = std::stoi(value); - getline(header_stream, value, ' '); - n_edges = std::stoi(value); - - // Since the matrix represents the adjacency matrix, it must be square - if (n_rows != n_cols) { - return -1; - } - - // Read the content of each line - std::string node1; - std::string node2; - std::string edge_weight; - CXXGraph::id_t edge_id = 0; - while (getline(iFile, row_content)) { - std::stringstream row_stream(row_content); - - // Read the content of the node ids and the weight into strings - getline(row_stream, node1, ' '); - getline(row_stream, node2, ' '); - getline(row_stream, edge_weight); - - edgeMap[edge_id] = std::pair(node1, node2); - edgeWeightMap[edge_id] = std::stod(edge_weight); - edgeDirectedMap[edge_id] = !undirected; - - // If the edge is a self-link, it must be undirected - if (node1 == node2) { - edgeDirectedMap[edge_id] = false; - } - - // Increase the edge id - ++edge_id; - } - - if (n_edges != edgeMap.size()) { - std::cout << "Error: The number of edges does not match the value provided " - "in the size line.\n"; - return -1; - } - - iFile.close(); - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); - return 0; -} - -template -PartitionMap Graph::partitionGraph( - const Partitioning::PartitionAlgorithm algorithm, - const unsigned int numberOfPartitions, const double param1, - const double param2, const double param3, - const unsigned int numberOfThreads) const { - PartitionMap partitionMap; - Partitioning::Globals globals(numberOfPartitions, algorithm, param1, param2, - param3, numberOfThreads); - auto edgeSet_ptr = make_shared>(getEdgeSet()); - globals.edgeCardinality = edgeSet_ptr->size(); - globals.vertexCardinality = this->getNodeSet().size(); - Partitioning::Partitioner partitioner(edgeSet_ptr, globals); - Partitioning::CoordinatedPartitionState partitionState = - partitioner.performCoordinatedPartition(); - partitionMap = partitionState.getPartitionMap(); - return partitionMap; -} - -template -std::ostream &operator<<(std::ostream &os, const Graph &graph) { - os << "Graph:\n"; - auto edgeList = graph.getEdgeSet(); - auto it = edgeList.begin(); - for (it; 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 AdjacencyMatrix &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_H__ diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h new file mode 100644 index 000000000..a94f66f76 --- /dev/null +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -0,0 +1,856 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_DECL_H__ +#define __CXXGRAPH_GRAPH_DECL_H__ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" +#include "CXXGraph/Utility/TypeTraits.hpp" +#include "CXXGraph/Utility/Typedef.hpp" + +#ifdef WITH_COMPRESSION +#include +#endif + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +template +using T_EdgeSet = std::unordered_set>, edgeHash>; + +template +using T_NodeSet = std::unordered_set>, nodeHash>; + +template +class Graph; + +template +std::ostream &operator<<(std::ostream &o, const Graph &graph); +template +std::ostream &operator<<(std::ostream &o, const AdjacencyMatrix &adj); + +/// Class that implement the Graph. ( This class is not Thread Safe ) +template +class Graph { + private: + T_EdgeSet edgeSet = {}; + T_NodeSet isolatedNodesSet = {}; + + shared> cachedAdjMatrix; + shared> cachedDegreeMatrix; + shared> cachedLaplacianMatrix; + shared> cachedTransitionMatrix; + // Private non-const getter for the set of nodes + std::unordered_set>, nodeHash> nodeSet(); + + std::optional> getExtenstionAndSeparator( + InputOutputFormat format) const; + void writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, + std::ostream &oEdgeWeight, const char &sep, + bool writeNodeFeat, bool writeEdgeWeight) const; + void readGraphFromStream(std::istream &iGraph, std::istream &iNodeFeat, + std::istream &iEdgeWeight, bool readNodeFeat, + bool readEdgeWeight); + int writeToDot(const std::string &workingDir, const std::string &OFileName, + const std::string &graphName) const; + int readFromDot(const std::string &workingDir, const std::string &fileName); + void recreateGraph( + std::unordered_map> + &edgeMap, + std::unordered_map &edgeDirectedMap, + std::unordered_map &nodeFeatMap, + std::unordered_map &edgeWeightMap); + +#ifdef WITH_COMPRESSION + int compressFile(const std::string &inputFile, + const std::string &outputFile) const; + int decompressFile(const std::string &inputFile, + const std::string &outputFile) const; +#endif + + public: + Graph(); + Graph(const T_EdgeSet &edgeSet); + virtual ~Graph() = default; + /** + * \brief + * Function that return the Edge set of the Graph + * Note: No Thread Safe + * + * @returns a list of Edges of the graph + * + */ + virtual const T_EdgeSet &getEdgeSet() const; + /** + * \brief + * Function set the Edge Set of the Graph + * Note: No Thread Safe + * + * @param edgeSet The Edge Set + * + */ + virtual void setEdgeSet(const T_EdgeSet &edgeSet); + /** + * \brief + * Function add an Edge to the Graph Edge Set + * First check if a pointer to a node with the same userId has + * already been added, and if not add it + * Note: No Thread Safe + * + * @param edge The Edge to insert + * + */ + virtual void addEdge(const Edge *edge); + /** + * \brief + * Function add an Edge to the Graph Edge Set + * First check if a pointer to a node with the same userId has + * already been added, and if not add it + * Note: No Thread Safe + * + * @param edge The Edge to insert + * + */ + virtual void addEdge(shared> edge); + /** + * \brief + * Function that adds any number of Edges to the Graph Edge set + * Note: This is the overload needed to terminate the + * recursion + * + * @param None + * + */ + template + void addEdges(); + /** + * \brief + * Function that adds any number of Edges to the Graph Edge set + * + * @param Raw pointers or shared pointers to the Edges + * + */ + template + std::enable_if_t && (is_edge_ptr_v && ...), void> + addEdges(T1 edge, Tn... edges); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param pointer to the node + * + */ + virtual void addNode(const Node *node); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param shared pointer to the node + * + */ + virtual void addNode(shared> node); + /** + * \brief + * Function that adds any number of Nodes to the Graph Node set + * Note: This overload is needed to terminate the recursion + * + * @param None + * + */ + template + void addNodes(); + /** + * \brief + * Function that adds any number of Nodes to the Graph Node set + * + * @param Raw pointers or shared pointers to the Edges + * + */ + template + std::enable_if_t && (is_node_ptr_v && ...), void> + addNodes(T1 node, Tn... nodes); + /** + * \brief + * Function remove an Edge from the Graph Edge Set + * Note: No Thread Safe + * + * @param edgeId The Edge Id to remove + * + */ + virtual void removeEdge(const CXXGraph::id_t edgeId); + /** + * \brief + * Function to remove a Node from the Graph Node Set + * Note: No Thread Safe + * + * @param edgeId The Edge Id to remove + * + */ + virtual void removeNode(const std::string &nodeUserId); + /** + * \brief + * Finds the given edge defined by v1 and v2 within the graph. + * + * @param v1 The first vertex. + * @param v2 The second vertex. + * @param id The edge id if the edge is found. Otherwise set to 0. + * @return True if the edge exists in the graph. + */ + virtual bool findEdge(const Node *v1, const Node *v2, + CXXGraph::id_t &id) const; + /** + * \brief + * Overload of findEdge which takes shared pointers as parameters + * + * @param v1 The first vertex. + * @param v2 The second vertex. + * @param id The edge id if the edge is found. Otherwise set to 0. + * @return True if the edge exists in the graph. + */ + virtual bool findEdge(shared> v1, shared> v2, + CXXGraph::id_t &id) const; + /** + * \brief + * Function that return the Node Set of the Graph + * Note: No Thread Safe + * + * @returns a list of Nodes of the graph + * + */ + virtual const T_NodeSet getNodeSet() const; + /** + * \brief + * Function that return the Set of isolated nodes + * in the Graph + * Note: No Thread Safe + * + * @returns a list of Nodes of the graph + * + */ + virtual const T_NodeSet getIsolatedNodeSet() const; + /** + * \brief + * Function that sets the data contained in a node + * + * @param nodeUserId The userId string of the node whose data is to be changes + * @param data The new value for the node data + * + */ + virtual void setNodeData(const std::string &nodeUserId, T data); + /** + * \brief + * Function that sets the data contained in every node of the graph + * + * @param dataMap Map of the userId of every node with its new data value + * + */ + virtual void setNodeData(std::map &dataMap); + /** + * \brief + * Function that return an Edge with specific ID if Exist in the Graph + * Note: No Thread Safe + * + * @param edgeId The Edge Id to return + * @returns the Edge if exist + * + */ + virtual const std::optional>> getEdge( + const CXXGraph::id_t edgeId) const; + /** + * \brief + * Function that return a Node with specific ID if Exist in the Graph + * Note: No Thread Safe + * + * @param nodeId The Node Id to return + * @returns the Node if exist + * + */ + virtual const std::optional>> getNode( + const std::string &nodeUserId) const; + /** + * @brief This function generate a list of adjacency matrix with every element + * of the matrix contain the node where is directed the link and the Edge + * corrispondent to the link + * Note: No Thread Safe + */ + virtual shared> getAdjMatrix() const; + + virtual void cacheAdjMatrix(); + /** + * @brief This function generates a list of the degree matrix with every + * element of the matrix containing the node where the link is directed and + * the corresponding edge to the link. Note: No Thread Safe + */ + virtual shared> getDegreeMatrix() const; + + virtual void cacheDegreeMatrix(); + /** + * @brief This function generates a list of the Laplacian matrix with every + * element of the matrix containing the node connected to the current node and + * the corresponding edge to the link. Note: No Thread Safe + */ + virtual shared> getLaplacianMatrix() const; + + virtual void cacheLaplacianMatrix(); + /** + * @brief This function generates a list of the transition matrix with every + * element of the matrix containing the node that can be transitioned to from + * the current node and the probability of the transition. Note: No Thread + * Safe + */ + virtual shared> getTransitionMatrix() const; + + virtual void cacheTransitionMatrix(); + /** + * \brief This function generates a set of nodes linked to the provided node + * in a directed graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + outNeighbors(const Node *node) const; + /** + * \brief This function generates a set of nodes linked to the provided node + * in a directed graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + outNeighbors(shared> node) const; + /** + * \brief This function generates a set of nodes linked to the provided node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + inOutNeighbors(const Node *node) const; + /** + * \brief + * \brief This function generates a set of nodes linked to the provided node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + inOutNeighbors(shared> node) const; + /** + * \brief + * \brief This function generates a set of Edges going out of a node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> outEdges( + const Node *node) const; + /** + * \brief + * \brief This function generates a set of Edges going out of a node + * in any graph + * + * @param Shared pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> outEdges( + shared> node) const; + /** + * \brief + * \brief This function generates a set of Edges coming in or going out of + * a node in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> + inOutEdges(const Node *node) const; + /** + * \brief + * \brief This function generates a set of Edges coming in or going out of + * a node in any graph + * + * @param Shared pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> + inOutEdges(shared> node) const; + /** + * @brief This function finds the subset of given a nodeId + * Subset is stored in a map where keys are the hash-id of the node & values + * is the subset. + * @param subset query subset, we want to find target in this subset + * @param elem elem that we wish to find in the subset + * + * @return parent node of elem + * Note: No Thread Safe + */ + virtual CXXGraph::id_t setFind(std::unordered_map *, + const CXXGraph::id_t elem) const; + /** + * @brief This function finds the subset of given a nodeId + * Subset is stored in a map where keys are the hash-id of the node & values + * is the subset. + * @param shared pointer to subset query subset, we want to find target in + * this subset + * @param elem elem that we wish to find in the subset + * + * @return parent node of elem + * Note: No Thread Safe + */ + virtual CXXGraph::id_t setFind( + shared>, + const CXXGraph::id_t elem) const; + /** + * @brief This function modifies the original subset array + * such that it the union of two sets a and b + * @param subset original subset is modified to obtain union of a & b + * @param a parent id of set1 + * @param b parent id of set2 + * NOTE: Original subset is no longer available after union. + * Note: No Thread Safe + */ + virtual void setUnion(std::unordered_map *, + const CXXGraph::id_t set1, + const CXXGraph::id_t elem2) const; + /** + * @brief This function modifies the original subset array + * such that it the union of two sets a and b + * @param subset original subset is modified to obtain union of a & b + * @param a parent id of set1 + * @param b parent id of set2 + * NOTE: Original subset is no longer available after union. + * Note: No Thread Safe + */ + virtual void setUnion(shared>, + const CXXGraph::id_t set1, + const CXXGraph::id_t elem2) const; + /** + * @brief This function finds the eulerian path of a directed graph using + * hierholzers algorithm + * + * @return a vector containing nodes in eulerian path + * Note: No Thread Safe + */ + virtual std::shared_ptr>> eulerianPath() const; + /** + * @brief Function runs the dijkstra algorithm for some source node and + * target node in the graph and returns the shortest distance of target + * from the source. + * Note: No Thread Safe + * + * @param source source vertex + * @param target target vertex + * + * @return shortest distance if target is reachable from source else ERROR in + * case if target is not reachable from source or there is error in the + * computation. + */ + virtual const DijkstraResult dijkstra(const Node &source, + const Node &target) const; + /** + * @brief This function runs the tarjan algorithm and returns different types + * of results depending on the input parameter typeMask. + * + * @param typeMask each bit of typeMask within valid range represents a kind + * of results should be returned. + * + * Note: No Thread Safe + * + * @return The types of return include strongly connected components + * (only for directed graphs) and cut vertices、 bridges、edge + * biconnected components and vertice biconnected components + * (only for undirected graphs). + */ + virtual const TarjanResult tarjan(const unsigned int typeMask) const; + /** + * @brief Function runs the bellman-ford algorithm for some source node and + * target node in the graph and returns the shortest distance of target + * from the source. It can also detect if a negative cycle exists in the + * graph. Note: No Thread Safe + * + * @param source source vertex + * @param target target vertex + * + * @return shortest distance if target is reachable from source else ERROR in + * case if target is not reachable from source. If there is no error then also + * returns if the graph contains a negative cycle. + */ + virtual const BellmanFordResult bellmanford(const Node &source, + const Node &target) const; + /** + * @brief This function computes the transitive reduction of the graph, + * returning a graph with the property of transitive closure satisfied. It + * removes the "short-circuit" paths from a graph, leaving only the longest + * paths. Commonly used to remove duplicate edges among nodes that do not pass + * through the entire graph. + * @return A copy of the current graph with the transitive closure property + * satisfied. + * + */ + virtual const Graph transitiveReduction() const; + /** + * @brief Function runs the floyd-warshall algorithm and returns the shortest + * distance of all pair of nodes. It can also detect if a negative cycle + * exists in the graph. Note: No Thread Safe + * @return a map whose keys are node ids and values are the shortest distance. + * If there is no error then also returns if the graph contains a negative + * cycle. + */ + virtual const FWResult floydWarshall() const; + /** + * @brief Function runs the prim algorithm and returns the minimum spanning + * tree if the graph is undirected. Note: No Thread Safe + * @return a vector containing id of nodes in minimum spanning tree & cost of + * MST + */ + virtual const MstResult prim() const; + /** + * @brief Function runs the boruvka algorithm and returns the minimum spanning + * tree & cost if the graph is undirected. Note: No Thread Safe + * @return struct of type MstResult with following fields + * success: true if algorithm completed successfully ELSE false + * mst: vector containing id of nodes in minimum spanning tree & cost of MST + * mstCost: Cost of MST + * errorMessage: "" if no error ELSE report the encountered error + */ + virtual const MstResult boruvka() const; + /** + * @brief Function runs the kruskal algorithm and returns the minimum spanning + * tree if the graph is undirected. Note: No Thread Safe + * @return struct of type MstResult with following fields + * success: true if algorithm completed successfully ELSE false + * mst: vector containing id of nodes in minimum spanning tree & cost of MST + * mstCost: Cost of MST + * errorMessage: "" if no error ELSE report the encountered error + */ + virtual const MstResult kruskal() const; + /** + * \brief + * Function runs the best first search algorithm over the graph + * using an evaluation function to decide which adjacent node is + * most promising to explore + * Note: No Thread Safe + * + * @param source source node + * @param target target node + * @returns a struct with a vector of Nodes if target is reachable else ERROR + * in case if target is not reachable or there is an error in the computation. + * + */ + virtual BestFirstSearchResult best_first_search( + const Node &source, const Node &target) const; + /** + * \brief + * Function performs the breadth first search algorithm over the graph + * Note: No Thread Safe + * + * @param start Node from where traversing starts + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> breadth_first_search( + const Node &start) const; + /** + * \brief + * The multithreaded version of breadth_first_search + * It turns out to be two indepentent functions because of implemntation + * differences + * + * @param start Node from where traversing starts + * @param num_threads number of threads + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> concurrency_breadth_first_search( + const Node &start, size_t num_threads) const; + /** + * \brief + * Function performs the depth first search algorithm over the graph + * Note: No Thread Safe + * + * @param start Node from where traversing starts + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> depth_first_search( + const Node &start) const; + + /** + * \brief + * This function uses DFS to check for cycle in the graph. + * Pay Attention, this function work only with directed Graph + * Note: No Thread Safe + * + * @return true if a cycle is detected, else false. ( false is returned also + * if the graph in indirected) + */ + virtual bool isCyclicDirectedGraphDFS() const; + + /** + * \brief + * This function uses BFS to check for cycle in the graph. + * Pay Attention, this function work only with directed Graph + * Note: No Thread Safe + * + * @return true if a cycle is detected, else false. ( false is returned also + * if the graph in indirected) + */ + virtual bool isCyclicDirectedGraphBFS() const; + + /** + * @brief + * This function checks if the given set of edges + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle(const T_EdgeSet *) const; + /** + * @brief + * This function checks if the given set of edges + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle(shared>) const; + /** + * @brief + * This function checks if the given Subset + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle( + shared> edgeSet, + shared>) const; + + /** + * \brief + * This function checks if a graph is directed + * Note: No Thread Safe + * + * @return true if the graph is directed, else false. + */ + virtual bool isDirectedGraph() const; + + /** + * \brief + * This function checks if a graph is undirected + * Note: No Thread Safe + * + * @return true if the graph is undirected, else false. + */ + virtual bool isUndirectedGraph() const; + + /** + * @brief This function reverse the direction of the edges in a directed graph + */ + virtual void reverseDirectedGraph(); + + /** + * @brief This function checks if the graph is connected or not + * Applicable for Undirected Graph, for Directed Graph use the + * isStronglyConnectedGraph() function + * + * @return true if the graph is connected + * @return false otherwise + */ + virtual bool isConnectedGraph() const; + + /** + * @brief This function checks if the graph is strongly connected or not + * Applicable for Directed Graph, for Undirected Graph use the + * isConnectedGraph() function + * + * @return true if the graph is connected + * @return false otherwise + */ + virtual bool isStronglyConnectedGraph() const; + + /** + * @brief This function sort nodes in topological order. + * Applicable for Directed Acyclic Graph + * + * @return a struct with a vector of Nodes ordered topologically else ERROR in + * case of undirected or cyclic graph + */ + virtual TopoSortResult topologicalSort() const; + + /** + * @brief This function sort nodes in topological order using kahn's algorithm + * Applicable for Directed Acyclic Graph + * + * @return a struct with a vector of Nodes ordered topologically else ERROR in + * case of undirected or cyclic graph + */ + virtual TopoSortResult kahn() const; + + /** + * \brief + * This function performs performs the kosaraju algorthm on the graph to find + the strongly connected components. + * + * Mathematical definition of the problem: + * A strongly connected component (SCC) of a directed graph is a maximal + strongly connected subgraph. + + * Note: No Thread Safe + * @return a vector of vector of strongly connected components. + */ + virtual SCCResult kosaraju() const; + + /** + * \brief + * This function performs Graph Slicing based on connectivity + * + * 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. + + * Note: No Thread Safe + * @param start Node from where traversing starts + * @return a vector of nodes that belong to C but not to M. + */ + virtual const std::vector> graph_slicing(const Node &start) const; + + /** + * @brief Function runs the Dial algorithm (Optimized Dijkstra for small + * range weights) for some source node and target node in the graph and + * returns the shortest distance of target from the source. Note: No Thread + * Safe + * + * @param source source vertex + * @param maxWeight maximum weight of the edge + * + * @return shortest distance for all nodes reachable from source else ERROR in + * case there is error in the computation. + */ + virtual const DialResult dial(const Node &source, int maxWeight) const; + + /** + * @brief Function runs the Ford-Fulkerson algorithm for some source node and + * target node in the graph and returns the maximum flow of the graph + * + * @param source source vertex + * @param target target vertex + * @return double Max-Flow value or -1 in case of error + */ + virtual double fordFulkersonMaxFlow(const Node &source, + const Node &target) const; + + /** + * \brief + * This function writes the graph to an output file + * Note: Not threadsafe + * + * @param format The output format of the file + * @param workingDir The parent directory of the output file + * @param OFileName The output filename + * @param compress Enables compression (requires zlib) + * @param writeNodeFeat Indicates if export also Node Features + * @param writeEdgeWeight Indicates if export also Edge Weights + * @return 0 if OK, else return a negative value + */ + virtual int writeToFile( + InputOutputFormat format = InputOutputFormat::STANDARD_CSV, + const std::string &workingDir = ".", + const std::string &OFileName = "graph", bool compress = false, + bool writeNodeFeat = false, bool writeEdgeWeight = false) const; + + virtual int writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const; + + virtual int writeToMTXFile(const std::string &workingDir, + const std::string &OFileName, char delimier) const; + + /** + * \brief + * This function reads the graph from an input file + * Note: Not threadsafe + * + * @param format The input format of the file + * @param workingDir The parent directory of the input + * file + * @param OFileName The input filename + * @param compress Enables compression (requires zlib) + * @param readNodeFeat Indicates if import also Node Features + * @param readEdgeWeight Indicates if import also Edge Weights + * @return 0 if OK, else return a negative value + */ + virtual int readFromFile( + InputOutputFormat format = InputOutputFormat::STANDARD_CSV, + const std::string &workingDir = ".", + const std::string &OFileName = "graph", bool compress = false, + bool readNodeFeat = false, bool readEdgeWeight = false); + + virtual int readFromDotFile(const std::string &workingDir, + const std::string &fileName); + + virtual int readFromMTXFile(const std::string &workingDir, + const std::string &fileName); + + friend std::ostream &operator<< <>(std::ostream &os, const Graph &graph); + friend std::ostream &operator<< <>(std::ostream &os, + const AdjacencyMatrix &adj); +}; + +} // namespace CXXGraph +#endif // __CXXGRAPH_GRAPH_DECL_H__ diff --git a/include/CXXGraph/Graph/Graph_impl.hpp b/include/CXXGraph/Graph/Graph_impl.hpp new file mode 100644 index 000000000..a0bcf3c66 --- /dev/null +++ b/include/CXXGraph/Graph/Graph_impl.hpp @@ -0,0 +1,880 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_IMPL_H__ +#define __CXXGRAPH_GRAPH_IMPL_H__ + +#pragma once + +#include + +#include "CXXGraph/Graph/Graph_decl.h" +#include "CXXGraph/Utility/ConstString.hpp" + +namespace CXXGraph { + +using std::make_shared; +using std::make_unique; + +template +Graph::Graph() { + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); + cacheTransitionMatrix(); +} + +template +Graph::Graph(const T_EdgeSet &edgeSet) { + for (auto edgeIt : edgeSet) { + this->edgeSet.insert(edgeIt); + } + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); + cacheTransitionMatrix(); +} + +template +const T_EdgeSet &Graph::getEdgeSet() const { + return edgeSet; +} + +template +void Graph::setEdgeSet(const T_EdgeSet &edgeSet) { + this->edgeSet.clear(); + for (auto edgeIt : edgeSet) { + this->edgeSet.insert(edgeIt); + } + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); +} + +template +void Graph::addEdge(const Edge *edge) { + if (edge->isDirected().has_value() && edge->isDirected().value()) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_shared = make_shared>( + *dynamic_cast *>(edge)); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + } else { + auto edge_shared = make_shared>(*edge); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + } + } else { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_shared = make_shared>( + *dynamic_cast *>(edge)); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); + } else { + auto edge_shared = make_shared>(*edge); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); + } + } +} + +template +void Graph::addEdge(shared> edge) { + this->edgeSet.insert(edge); + + /* Adding new edge in cached adjacency matrix */ + if (edge.get()->isDirected().has_value() && + edge.get()->isDirected().value()) { + std::pair>, shared>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + } else { + std::pair>, shared>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge.get()->getNodePair().first, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back( + std::move(elem1)); + } +} + +template +template +void Graph::addEdges() { + return; +} + +template +template +std::enable_if_t && (is_edge_ptr_v && ...), void> +Graph::addEdges(T1 edge, Tn... edges) { + addEdge(edge); + addEdges(edges...); +} + +template +void Graph::addNode(const Node *node) { + auto node_shared = make_shared>(*node); + this->isolatedNodesSet.insert(node_shared); +} + +template +void Graph::addNode(shared> node) { + this->isolatedNodesSet.insert(node); +} + +template +template +void Graph::addNodes() { + return; +} + +template +template +std::enable_if_t && (is_node_ptr_v && ...), void> +Graph::addNodes(T1 node, Tn... nodes) { + addNode(node); + addNodes(nodes...); +} + +template +void Graph::removeEdge(const CXXGraph::id_t edgeId) { + auto edgeOpt = Graph::getEdge(edgeId); + if (edgeOpt.has_value()) { + /* + edgeSet.erase(std::find_if(this->edgeSet.begin(), this->edgeSet.end(), + [edgeOpt](const Edge *edge) { return (*(edgeOpt.value()) == *edge); })); + */ + edgeSet.erase(edgeSet.find(edgeOpt.value())); + int delIndex = -1; + int i = 0; + /* Removing the edge from the cached adjacency matrix */ + for (auto elem : + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first]) { + if (elem.second.get()->getId() == edgeId) { + delIndex = i; + break; + } + i++; + } + if (delIndex != -1) { + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].erase( + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first] + .begin() + + delIndex); + } + + delIndex = -1; + i = 0; + for (auto elem : + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second]) { + if (elem.second.get()->getId() == edgeId) { + delIndex = i; + break; + } + i++; + } + if (delIndex != -1) { + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].erase( + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second] + .begin() + + delIndex); + } + } +} + +template +void Graph::removeNode(const std::string &nodeUserId) { + auto nodeOpt = getNode(nodeUserId); + auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value()); + + if (nodeOpt.has_value() && isolatedNodeIt != isolatedNodesSet.end()) { + // The node is isolated + isolatedNodesSet.erase(isolatedNodeIt); + } else if (nodeOpt.has_value()) { + // The node is not isolated + // Remove the edges containing the node + auto edgeset = edgeSet; + for (auto edgeIt : edgeset) { + if (edgeIt->getNodePair().first->getUserId() == nodeUserId || + edgeIt->getNodePair().second->getUserId() == nodeUserId) { + this->removeEdge(edgeIt->getId()); + } + } + } +} + +template +bool Graph::findEdge(const Node *v1, const Node *v2, + CXXGraph::id_t &id) const { + auto v1_shared = make_shared>(*v1); + auto v2_shared = make_shared>(*v2); + + return findEdge(v1_shared, v2_shared, id); +} + +template +bool Graph::findEdge(shared> v1, shared> v2, + CXXGraph::id_t &id) const { + // This could be made faster by looking for the edge hash, assuming we hash + // based on node data, instead of a unique integer + if (cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0) { + /* Searching for the edge using cached adjacency matrix */ + + for (auto elem : (*cachedAdjMatrix)[v1]) { + if (elem.first == v2) { + id = elem.second.get()->getId(); + return true; + } + } + } else { + /* Searching for the edge using edgeset */ + + for (auto e : this->edgeSet) { + if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) { + id = e->getId(); + return true; + } + if (!e->isDirected() && + ((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) { + id = e->getId(); + return true; + } + } + } + id = 0; + return false; +} + +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); + } + // Merge with the isolated nodes + nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); + + return nodeSet; +} + +template +const T_NodeSet Graph::getIsolatedNodeSet() const { + return isolatedNodesSet; +} + +template +void Graph::setNodeData(const std::string &nodeUserId, T data) { + auto nodeSet = this->nodeSet(); + auto nodeIt = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&nodeUserId](auto node) { return node->getUserId() == nodeUserId; }); + std::const_pointer_cast>(*nodeIt)->setData(std::move(data)); +} + +template +void Graph::setNodeData(std::map &dataMap) { + // Construct the set of all the nodes in the graph + for (auto &nodeSetIt : this->nodeSet()) { + nodeSetIt->setData(std::move(dataMap[nodeSetIt->getUserId()])); + } +} + +template +const std::optional>> Graph::getEdge( + const CXXGraph::id_t edgeId) const { + for (const auto &it : edgeSet) { + if (it->getId() == edgeId) { + return it; + } + } + + return std::nullopt; +} + +template +const std::optional>> Graph::getNode( + const std::string &nodeUserId) const { + for (const auto &it : getNodeSet()) { + if (it->getUserId() == nodeUserId) { + return it; + } + } + + return std::nullopt; +} + +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 (auto &isNodeIt : isolatedNodesSet) { + nodeSet.insert(std::const_pointer_cast>(isNodeIt)); + } + + return nodeSet; +} + +template +CXXGraph::id_t Graph::setFind( + std::unordered_map *subsets, + const CXXGraph::id_t nodeId) const { + auto subsets_ptr = + make_shared>(*subsets); + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets_ptr, (*subsets)[nodeId].parent); + } + + return (*subsets)[nodeId].parent; +} + +template +CXXGraph::id_t Graph::setFind( + shared> subsets, + const CXXGraph::id_t nodeId) const { + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets, (*subsets)[nodeId].parent); + } + + return (*subsets)[nodeId].parent; +} + +template +void Graph::setUnion(std::unordered_map *subsets, + const CXXGraph::id_t elem1, + const CXXGraph::id_t elem2) const { + /* auto subsets_ptr = make_shared>(*subsets); */ + // if both sets have same parent + // then there's nothing to be done + /* if ((*subsets_ptr)[elem1].parent == (*subsets_ptr)[elem2].parent) return; + */ + /* auto elem1Parent = Graph::setFind(subsets_ptr, elem1); */ + /* auto elem2Parent = Graph::setFind(subsets_ptr, elem2); */ + /* if ((*subsets_ptr)[elem1Parent].rank < (*subsets_ptr)[elem2Parent].rank) */ + /* (*subsets_ptr)[elem1].parent = elem2Parent; */ + /* else if ((*subsets_ptr)[elem1Parent].rank > + * (*subsets_ptr)[elem2Parent].rank) */ + /* (*subsets_ptr)[elem2].parent = elem1Parent; */ + /* else { */ + /* (*subsets_ptr)[elem2].parent = elem1Parent; */ + /* (*subsets_ptr)[elem1Parent].rank++; */ + /* } */ + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } +} + +template +void Graph::setUnion( + shared> subsets, + const CXXGraph::id_t elem1, const CXXGraph::id_t elem2) const { + // if both sets have same parent + // then there's nothing to be done + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } +} + +template +std::shared_ptr>> Graph::eulerianPath() const { + const auto nodeSet = Graph::getNodeSet(); + + std::shared_ptr>> eulerPath = + std::make_shared>>(); + + 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 cachedAdjMatrix->at(n1).size() < cachedAdjMatrix->at(n2).size(); + }); + auto currentNode = *(firstNodeIt); + currentPath.push_back(currentNode); + + while (currentPath.size() > 0) { + auto &edges = cachedAdjMatrix->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::getAdjMatrix() const { + auto adj = std::make_shared>(); + auto addElementToAdjMatrix = [&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().has_value() && + edgeSetIt->isDirected().value()) { + shared> d_edge = + std::static_pointer_cast>(edgeSetIt); + addElementToAdjMatrix(d_edge->getNodePair().first, + d_edge->getNodePair().second, d_edge); + } else if (edgeSetIt->isDirected().has_value() && + !edgeSetIt->isDirected().value()) { + 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; + } + } + return adj; +} + +template +void Graph::cacheAdjMatrix() { + const auto adj = Graph::getAdjMatrix(); + this->cachedAdjMatrix = adj; +} + +template +shared> Graph::getDegreeMatrix() const { + auto degreeMatrix = std::make_shared>(); + + for (const auto &nodePair : *this->cachedAdjMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + int degree = neighbors.size(); + + (*degreeMatrix)[node] = {degree}; + } + + return degreeMatrix; +} + +template +void Graph::cacheDegreeMatrix() { + const auto degreeMatrix = Graph::getDegreeMatrix(); + this->cachedDegreeMatrix = degreeMatrix; +} + +template +shared> Graph::getLaplacianMatrix() const { + const auto adjacencyMatrix = this->cachedAdjMatrix; + const auto degreeMatrix = this->cachedDegreeMatrix; + + auto laplacianMatrix = std::make_shared>(); + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + (*laplacianMatrix)[node] = + std::vector>, shared>>>(); + } + + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + (*laplacianMatrix)[node].emplace_back(node, + nullptr); // Insere o nó na diagonal + for (const auto &neighborPair : neighbors) { + const shared> &neighbor = neighborPair.first; + (*laplacianMatrix)[node].emplace_back( + neighbor, neighborPair.second); // Insere os pares de vizinhos + } + } + + return laplacianMatrix; +} + +template +void Graph::cacheLaplacianMatrix() { + const auto laplacianMatrix = Graph::getLaplacianMatrix(); + this->cachedLaplacianMatrix = laplacianMatrix; +} + +template +shared> Graph::getTransitionMatrix() const { + const auto adjacencyMatrix = this->cachedAdjMatrix; + + auto transitionMatrix = std::make_shared>(); + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + (*transitionMatrix)[node] = + std::vector>, double>>(); + } + + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + int degree = neighbors.size(); + + double transitionProbability = 1.0 / degree; + + for (const auto &neighborPair : neighbors) { + const shared> &neighbor = neighborPair.first; + (*transitionMatrix)[node].emplace_back(neighbor, transitionProbability); + } + } + + return transitionMatrix; +} + +template +void Graph::cacheTransitionMatrix() { + const auto transitionMatrix = Graph::getTransitionMatrix(); + this->cachedTransitionMatrix = transitionMatrix; +} + +template +const std::unordered_set>, nodeHash> +Graph::outNeighbors(const Node *node) const { + auto node_shared = make_shared>(*node); + + return outNeighbors(node_shared); +} + +template +const std::unordered_set>, nodeHash> +Graph::outNeighbors(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, nodeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, nodeHash> outNeighbors; + for (auto pair : nodeEdgePairs) { + if (pair.second->isDirected().has_value() && + pair.second->isDirected().value()) { + outNeighbors.insert(pair.first); + } + } + + return outNeighbors; +} + +template +const std::unordered_set>, nodeHash> +Graph::inOutNeighbors(const Node *node) const { + auto node_shared = make_shared>(*node); + + return inOutNeighbors(node_shared); +} + +template +const std::unordered_set>, nodeHash> +Graph::inOutNeighbors(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, nodeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, nodeHash> inOutNeighbors; + for (auto pair : nodeEdgePairs) { + inOutNeighbors.insert(pair.first); + } + + return inOutNeighbors; +} + +template +const std::unordered_set>, edgeHash> Graph::outEdges( + const Node *node) const { + auto node_shared = make_shared>(*node); + + return outEdges(node_shared); +} + +template +const std::unordered_set>, edgeHash> Graph::outEdges( + shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, edgeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, edgeHash> outEdges; + for (auto pair : nodeEdgePairs) { + if (pair.second->isDirected().has_value() && + pair.second->isDirected().value()) { + outEdges.insert(pair.second); + } + } + + return outEdges; +} + +template +const std::unordered_set>, edgeHash> +Graph::inOutEdges(const Node *node) const { + auto node_shared = make_shared>(*node); + + return outEdges(node_shared); +} + +template +const std::unordered_set>, edgeHash> +Graph::inOutEdges(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, edgeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, edgeHash> outEdges; + for (auto pair : nodeEdgePairs) { + outEdges.insert(pair.second); + } + + return outEdges; +} + +template +bool Graph::isDirectedGraph() const { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if (!(edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Undirected Edge + return false; + } + } + // No Undirected Edge + return true; +} + +template +bool Graph::isUndirectedGraph() const { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if ((edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Directed Edge + return false; + } + } + // No Directed Edge + return true; +} + +template +void Graph::reverseDirectedGraph() { + if (!isDirectedGraph()) { + throw std::runtime_error(ERR_UNDIR_GRAPH); + } + auto oldEdgeSet = Graph::getEdgeSet(); + for (const auto &edge : oldEdgeSet) { + auto &[first, second] = edge->getNodePair(); + auto id = edge->getId(); + this->removeEdge(id); + auto newEdge = std::make_shared>(id, second, first); + this->addEdge(newEdge); + } +} + +template +const std::vector> Graph::graph_slicing(const Node &start) const { + std::vector> result; + + auto nodeSet = Graph::getNodeSet(); + // check if start node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return result; + } + std::vector> C = Graph::depth_first_search(start); + std::deque>> C1; // complement of C i.e. nodeSet - C + for (auto const &node : nodeSet) { + // from the set of all nodes, remove nodes that exist in C + if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { + return (*node == nodeC); + }) == C.end()) + C1.push_back(node); + } + + // For all nodes in C', apply DFS + // and get the list of reachable nodes and store in M + std::vector> M; + for (auto const &node : C1) { + std::vector> reachableNodes = Graph::depth_first_search(*node); + M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); + } + // removes nodes from C that are reachable from M. + for (const auto &nodeC : C) { + if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { + return (nodeM == nodeC); + }) == M.end()) + result.push_back(nodeC); + } + 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 AdjacencyMatrix &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/IOUtility_impl.hpp b/include/CXXGraph/Graph/IO/IOUtility_impl.hpp new file mode 100644 index 000000000..eeb0aee78 --- /dev/null +++ b/include/CXXGraph/Graph/IO/IOUtility_impl.hpp @@ -0,0 +1,120 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_IOUTILITY_IMPL_H__ +#define __CXXGRAPH_IOUTILITY_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +std::optional> Graph::getExtenstionAndSeparator( + InputOutputFormat format) const { + if (format == InputOutputFormat::STANDARD_CSV) { + return std::pair(".csv", ','); + } else if (format == InputOutputFormat::STANDARD_TSV) { + return std::pair(".tsv", '\t'); + } else { + return std::nullopt; + } +} + +// This ctype facet classifies ',' and '\t' as whitespace +struct csv_whitespace : std::ctype { + static const mask *make_table() { + // make a copy of the "C" locale table + static std::vector v(classic_table(), classic_table() + table_size); + v[','] |= space; // comma will be classified as whitespace + v['\t'] |= space; + v[' '] &= ~space; // space will not be classified as whitespace + return &v[0]; + } + explicit csv_whitespace(std::size_t refs = 0) + : ctype(make_table(), false, refs) {} +}; + +#ifdef WITH_COMPRESSION +template +int Graph::compressFile(const std::string &inputFile, + const std::string &outputFile) const { + std::ifstream ifs; + ifs.open(inputFile); + if (!ifs.is_open()) { + // ERROR File Not Open + return -1; + } + std::string content((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + const char *content_ptr = content.c_str(); + gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); + if (outFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); + return -1; + } + + unsigned int zippedBytes; + zippedBytes = + gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); + + ifs.close(); + gzclose(outFileZ); + return zippedBytes; +} + +template +int Graph::decompressFile(const std::string &inputFile, + const std::string &outputFile) const { + gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); + if (inFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); + return -1; + } + unsigned char unzipBuffer[8192]; + std::vector unzippedData; + std::ofstream ofs; + ofs.open(outputFile); + if (!ofs.is_open()) { + // ERROR File Not Open + return -1; + } + while (true) { + unsigned int unzippedBytes; + unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); + if (unzippedBytes > 0) { + unzippedData.insert(unzippedData.end(), unzipBuffer, + unzipBuffer + unzippedBytes); + } else { + break; + } + } + for (const auto &c : unzippedData) { + ofs << c; + } + ofs << std::endl; + ofs.close(); + gzclose(inFileZ); + return 0; +} +#endif + +} // namespace CXXGraph +#endif // __CXXGRAPH_IOUTILITY_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/IO/InputOperation_impl.hpp b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp new file mode 100644 index 000000000..182ad7746 --- /dev/null +++ b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp @@ -0,0 +1,437 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_INPUTOPERATION_IMPL_H__ +#define __CXXGRAPH_INPUTOPERATION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +int Graph::readFromFile(InputOutputFormat format, + const std::string &workingDir, + const std::string &OFileName, bool compress, + bool readNodeFeat, bool readEdgeWeight) { + int result = 0; + +#ifdef WITH_COMPRESSION + if (compress) { + auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, + &readEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = decompressFile(completePathToFileGraphCompressed, + completePathToFileGraph); + if (_result == 0) { + if (readNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = decompressFile(completePathToFileNodeFeatCompressed, + completePathToFileNodeFeat); + } + } + if (_result == 0) { + if (readEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = decompressFile(completePathToFileEdgeWeightCompressed, + completePathToFileEdgeWeight); + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + result = decompress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + result = decompress(".tsv"); + } else { + // INPUT FORMAT NOT RECOGNIZED + result = -1; + } + + if (result != 0) { + return result; + } + } +#endif + // Open streams and read + auto extSep = getExtenstionAndSeparator(format); + if (!extSep) { + std::cerr << "Unknown format\n"; + return -1; + } + auto &[extension, separator] = *extSep; + + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileNodeFeat; + std::string completePathToFileEdgeWeight; + + std::ifstream ifileGraph; + std::ifstream ifileNodeFeat; + std::ifstream ifileEdgeWeight; + + ifileGraph.open(completePathToFileGraph); + if (!ifileGraph.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open : " << completePathToFileGraph << + // std::endl; + return -1; + } + ifileGraph.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + + if (readNodeFeat) { + completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + ifileNodeFeat.open(completePathToFileNodeFeat); + if (!ifileNodeFeat.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + ifileNodeFeat.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + } + + if (readEdgeWeight) { + completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + ifileEdgeWeight.open(completePathToFileEdgeWeight); + if (!ifileEdgeWeight.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + ifileEdgeWeight.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + } + + readGraphFromStream(ifileGraph, ifileNodeFeat, ifileEdgeWeight, readNodeFeat, + readEdgeWeight); + + // Cleanup + ifileGraph.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileGraph.c_str()); +#endif + + if (readNodeFeat) { + ifileNodeFeat.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileNodeFeat.c_str()); +#endif + } + + if (readEdgeWeight) { + ifileEdgeWeight.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileEdgeWeight.c_str()); +#endif + } + + return result; +} + +template +int Graph::readFromDotFile(const std::string &workingDir, + const std::string &fileName) { + return readFromDot(workingDir, fileName); +} + +template +int Graph::readFromMTXFile(const std::string &workingDir, + const std::string &fileName) { + // Define the edge maps + std::unordered_map> + edgeMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeDirectedMap; + std::unordered_map edgeWeightMap; + + // Get full path to the file and open it + const std::string completePathToFileGraph = + workingDir + '/' + fileName + ".mtx"; + std::ifstream iFile(completePathToFileGraph); + // Check that the file is open + if (!iFile.is_open()) { + return -1; + } + + // Define the number of columns and rows in the matrix + int n_cols, n_rows; + int n_edges; + bool undirected = false; + + // From the first line of the file read the number of rows, columns and edges + std::string row_content; + getline(iFile, row_content); + if (row_content.find("symmetric") != std::string::npos) { + undirected = true; + } + + // Get rid of any commented lines between the header and the size line + while (row_content.find('%') != std::string::npos) { + getline(iFile, row_content); + } + + // From the size line of the file read the number of rows, columns and edges + std::stringstream header_stream(row_content); + std::string value; + getline(header_stream, value, ' '); + n_rows = std::stoi(value); + getline(header_stream, value, ' '); + n_cols = std::stoi(value); + getline(header_stream, value, ' '); + n_edges = std::stoi(value); + + // Since the matrix represents the adjacency matrix, it must be square + if (n_rows != n_cols) { + return -1; + } + + // Read the content of each line + std::string node1; + std::string node2; + std::string edge_weight; + CXXGraph::id_t edge_id = 0; + while (getline(iFile, row_content)) { + std::stringstream row_stream(row_content); + + // Read the content of the node ids and the weight into strings + getline(row_stream, node1, ' '); + getline(row_stream, node2, ' '); + getline(row_stream, edge_weight); + + edgeMap[edge_id] = std::pair(node1, node2); + edgeWeightMap[edge_id] = std::stod(edge_weight); + edgeDirectedMap[edge_id] = !undirected; + + // If the edge is a self-link, it must be undirected + if (node1 == node2) { + edgeDirectedMap[edge_id] = false; + } + + // Increase the edge id + ++edge_id; + } + + if (n_edges != edgeMap.size()) { + std::cout << "Error: The number of edges does not match the value provided " + "in the size line.\n"; + return -1; + } + + iFile.close(); + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); + return 0; +} + +template +int Graph::readFromDot(const std::string &workingDir, + const std::string &fileName) { + // Define the edge maps + std::unordered_map> + edgeMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeDirectedMap; + std::unordered_map edgeWeightMap; + + // Define the node strings and the "garbage collector" temp string + std::string node1; + std::string node2; + std::string temp; + + // Get full path to the file and open it + const std::string completePathToFileGraph = + workingDir + '/' + fileName + ".dot"; + + // Check if the graph is directed + bool directed = false; + std::ifstream fileContentStream(completePathToFileGraph); + std::string fileContent(std::istreambuf_iterator{fileContentStream}, + {}); + if (fileContent.find("->") != std::string::npos) { + directed = true; + } + // Check if the graph is weighted + bool weighted = false; + if (fileContent.find("weight") != std::string::npos) { + weighted = true; + } + fileContentStream.close(); + + std::ifstream iFile(completePathToFileGraph); + // Write the header of the DOT file in the temp string + getline(iFile, temp); + + CXXGraph::id_t edgeId = 0; + std::string fileRow; + while (getline(iFile, fileRow)) { + // If you've reached the end of the file, stop + if (fileRow == "}") { + break; + } + + // Remove the whitespaces before the definition of the edge + while (*fileRow.begin() == ' ' || *fileRow.begin() == '\t') { + fileRow.erase(fileRow.begin()); + } + + std::stringstream row_stream(fileRow); + getline(row_stream, node1, ' '); + // Store the symbol representing the edge inside temp + getline(row_stream, temp, ' '); + if (weighted) { + getline(row_stream, node2, '['); + // Remove any whitespaces or tabs from the node string + node2.erase(std::remove(node2.begin(), node2.end(), ' '), node2.end()); + node2.erase(std::remove(node2.begin(), node2.end(), '\t'), node2.end()); + + getline(row_stream, temp, '='); + std::string weight; + getline(row_stream, weight, ']'); + // Erase any whitespaces + weight.erase(std::remove(weight.begin(), weight.end(), ' '), + weight.end()); + edgeWeightMap[edgeId] = std::stod(weight); + } else { + getline(row_stream, node2, ';'); + } + + // Save the edge and increment the edge counter + edgeMap[edgeId] = std::pair(node1, node2); + edgeDirectedMap[edgeId] = directed; + ++edgeId; + } + iFile.close(); + + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); + return 0; +} + +template +void Graph::readGraphFromStream(std::istream &iGraph, + std::istream &iNodeFeat, + std::istream &iEdgeWeight, bool readNodeFeat, + bool readEdgeWeight) { + std::unordered_map> + edgeMap; + std::unordered_map edgeDirectedMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeWeightMap; + + CXXGraph::id_t edgeId; + std::string nodeId1; + std::string nodeId2; + bool directed; + while (iGraph >> edgeId >> nodeId1 >> nodeId2 >> + directed) { /* loop continually */ + edgeMap[edgeId] = std::pair(nodeId1, nodeId2); + edgeDirectedMap[edgeId] = directed; + } + + if (readNodeFeat) { + std::string nodeId; + T nodeFeat; + while (iNodeFeat >> nodeId >> nodeFeat) { + nodeFeatMap[nodeId] = nodeFeat; + } + } + + if (readEdgeWeight) { + CXXGraph::id_t edgeId; + double weight; + bool weighted; + while (iEdgeWeight >> edgeId >> weight >> weighted) { /* loop continually */ + if (weighted) { + edgeWeightMap[edgeId] = weight; + } + } + } + + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); +} + +template +void Graph::recreateGraph( + std::unordered_map> + &edgeMap, + std::unordered_map &edgeDirectedMap, + std::unordered_map &nodeFeatMap, + std::unordered_map &edgeWeightMap) { + std::unordered_map>> nodeMap; + for (const auto &edgeIt : edgeMap) { + shared> node1(nullptr); + shared> node2(nullptr); + if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { + feat = std::move(nodeFeatMap.at(edgeIt.second.first)); + } + node1 = make_shared>(edgeIt.second.first, feat); + nodeMap[edgeIt.second.first] = node1; + } else { + node1 = nodeMap.at(edgeIt.second.first); + } + if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { + feat = std::move(nodeFeatMap.at(edgeIt.second.second)); + } + node2 = make_shared>(edgeIt.second.second, feat); + nodeMap[edgeIt.second.second] = node2; + } else { + node2 = nodeMap.at(edgeIt.second.second); + } + + if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = make_shared>( + edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } else { + auto edge = make_shared>( + edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } + } else { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = make_shared>(edgeIt.first, node1, node2); + addEdge(edge); + } else { + auto edge = make_shared>(edgeIt.first, node1, node2); + addEdge(edge); + } + } + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_INPUTOPERATION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp new file mode 100644 index 000000000..fc3dae71c --- /dev/null +++ b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp @@ -0,0 +1,288 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ +#define __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +int Graph::writeToFile(InputOutputFormat format, + const std::string &workingDir, + const std::string &OFileName, bool compress, + bool writeNodeFeat, bool writeEdgeWeight) const { + int result = 0; + + // Open streams and write + auto extSep = getExtenstionAndSeparator(format); + if (!extSep) { + std::cerr << "Unknown format\n"; + return -1; + } + auto &[extension, separator] = *extSep; + + std::ofstream ofileGraph; + std::ofstream ofileNodeFeat; + std::ofstream ofileEdgeWeight; + + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + ofileNodeFeat.open(completePathToFileNodeFeat); + if (!ofileNodeFeat.is_open()) { + // ERROR File Not Open + return -1; + } + } + + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + ofileEdgeWeight.open(completePathToFileEdgeWeight); + if (!ofileEdgeWeight.is_open()) { + // ERROR File Not Open + std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + } + + writeGraphToStream(ofileGraph, ofileNodeFeat, ofileEdgeWeight, separator, + writeNodeFeat, writeEdgeWeight); + + // Cleanup from writing + ofileGraph.close(); + if (writeNodeFeat) ofileNodeFeat.close(); + if (writeEdgeWeight) ofileEdgeWeight.close(); + +#ifdef WITH_COMPRESSION + if (result == 0 && compress) { + auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, + &writeEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = compressFile(completePathToFileGraph, + completePathToFileGraphCompressed); + if (_result > 0) { + _result = remove(completePathToFileGraph.c_str()); + } + if (_result == 0) { + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = compressFile(completePathToFileNodeFeat, + completePathToFileNodeFeatCompressed); + if (_result > 0) { + _result = remove(completePathToFileNodeFeat.c_str()); + } + } + } + if (_result == 0) { + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = compressFile(completePathToFileEdgeWeight, + completePathToFileEdgeWeightCompressed); + if (_result > 0) { + _result = remove(completePathToFileEdgeWeight.c_str()); + } + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + auto result = _compress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + auto result = _compress(".tsv"); + } else { + // OUTPUT FORMAT NOT RECOGNIZED + result = -1; + } + } +#endif + + return result; +} + +template +int Graph::writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { + return writeToDot(workingDir, OFileName, graphName); +} + +template +int Graph::writeToMTXFile(const std::string &workingDir, + const std::string &OFileName, + char delimitier) const { + // Get the full path and open the file + const std::string completePathToFileGraph = + workingDir + '/' + OFileName + ".mtx"; + std::ofstream iFile(completePathToFileGraph); + + // Write the header of the file + std::string header = "%%MatrixMarket graph"; + // Check if the adjacency matrix is symmetric, i.e., if all the edges are + // undirected + bool symmetric = !std::any_of(edgeSet.begin(), edgeSet.end(), [](auto edge) { + return (edge->isDirected().has_value() && edge->isDirected().value()); + }); + // Write in the header whether the adj matrix is symmetric or not + if (symmetric) { + header += " symmetric\n"; + } else { + header += '\n'; + } + iFile << header; + + // Write the line containing the number of nodes and edges + const std::string firstLine = + std::to_string(getNodeSet().size()) + delimitier + + std::to_string(getNodeSet().size()) + delimitier + + std::to_string(getEdgeSet().size()) + '\n'; + iFile << firstLine; + + // Construct the edges + for (const auto &edgeIt : edgeSet) { + std::string line; + line += edgeIt->getNodePair().first->getUserId() + delimitier; + line += edgeIt->getNodePair().second->getUserId() + delimitier; + if (edgeIt->isWeighted().has_value() && edgeIt->isWeighted().value()) { + line += std::to_string(edgeIt->isWeighted().value()) + '\n'; + } else { + line += std::to_string(1.) + '\n'; + } + iFile << line; + } + + iFile.close(); + return 0; +} + +template +int Graph::writeToDot(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { + const std::string linkSymbol = "--"; + const std::string directedLinkSymbol = "->"; + + const std::string completePathToFileGraph = + workingDir + '/' + OFileName + ".dot"; + std::ofstream ofileGraph; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + // Write the header of the DOT file + std::string headerLine; + if (this->isDirectedGraph()) { + headerLine = "digraph " + graphName + " {\n"; + } else { + headerLine = "graph " + graphName + " {\n"; + } + ofileGraph << headerLine; + + for (auto const &edgePtr : edgeSet) { + std::string edgeLine = ""; + if (edgePtr->isDirected().has_value() && edgePtr->isDirected().value()) { + auto directedPtr = + std::static_pointer_cast>(edgePtr); + edgeLine += '\t' + directedPtr->getFrom().getUserId() + ' '; + edgeLine += directedLinkSymbol + ' '; + edgeLine += directedPtr->getTo().getUserId(); + } else { + edgeLine += '\t' + edgePtr->getNodePair().first->getUserId() + ' '; + edgeLine += linkSymbol + ' '; + edgeLine += edgePtr->getNodePair().second->getUserId(); + } + if (edgePtr->isWeighted().has_value() && edgePtr->isWeighted().value()) { + // Weights in dot files must be integers + edgeLine += " [weight=" + + std::to_string(static_cast( + std::dynamic_pointer_cast(edgePtr) + ->getWeight())) + + ']'; + } + edgeLine += ";\n"; + ofileGraph << edgeLine; + } + ofileGraph << '}'; + ofileGraph.close(); + + return 0; +} + +template +void Graph::writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, + std::ostream &oEdgeWeight, const char &sep, + bool writeNodeFeat, + bool writeEdgeWeight) const { + for (const auto &edge : edgeSet) { + oGraph << edge->getId() << sep << edge->getNodePair().first->getUserId() + << sep << edge->getNodePair().second->getUserId() << sep + << ((edge->isDirected().has_value() && edge->isDirected().value()) + ? 1 + : 0) + << std::endl; + } + + if (writeNodeFeat) { + auto nodeSet = getNodeSet(); + for (const auto &node : nodeSet) { + oNodeFeat << node->getUserId() << sep << node->getData() << std::endl; + } + } + + if (writeEdgeWeight) { + for (const auto &edge : edgeSet) { + oEdgeWeight + << edge->getId() << sep + << (edge->isWeighted().has_value() && edge->isWeighted().value() + ? (std::dynamic_pointer_cast(edge)) + ->getWeight() + : 0.0) + << sep + << (edge->isWeighted().has_value() && edge->isWeighted().value() ? 1 + : 0) + << std::endl; + } + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Node/Node.h b/include/CXXGraph/Node/Node.h new file mode 100755 index 000000000..ec61b6856 --- /dev/null +++ b/include/CXXGraph/Node/Node.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_NODE_H__ +#define __CXXGRAPH_NODE_H__ + +#pragma once + +#include "Node_impl.hpp" + +#endif // __CXXGRAPH_NODE_H__ diff --git a/include/CXXGraph/Node/Node_decl.h b/include/CXXGraph/Node/Node_decl.h new file mode 100644 index 000000000..0618f605e --- /dev/null +++ b/include/CXXGraph/Node/Node_decl.h @@ -0,0 +1,60 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_NODE_DECL_H__ +#define __CXXGRAPH_NODE_DECL_H__ + +#pragma once +#include + +#include "CXXGraph/Utility/id_t.hpp" + +namespace CXXGraph { +template +class Node; +template +std::ostream &operator<<(std::ostream &os, const Node &node); +template +class Node { + private: + CXXGraph::id_t id = 0; + std::string userId = ""; + T data; + void setId(const std::string &); + + public: + typedef T Node_t; + + Node(const std::string &, const T &data); + // Move constructor + Node(const std::string &, T &&data) noexcept; + ~Node() = default; + const CXXGraph::id_t &getId() const; + const std::string &getUserId() const; + const T &getData() const; + void setData(T &&new_data); + // operator + bool operator==(const Node &b) const; + bool operator<(const Node &b) const; + friend std::ostream &operator<< <>(std::ostream &os, const Node &node); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_NODE_DECL_H__ diff --git a/include/CXXGraph/Node/Node.hpp b/include/CXXGraph/Node/Node_impl.hpp old mode 100755 new mode 100644 similarity index 58% rename from include/CXXGraph/Node/Node.hpp rename to include/CXXGraph/Node/Node_impl.hpp index b191344e5..eb6905c2c --- a/include/CXXGraph/Node/Node.hpp +++ b/include/CXXGraph/Node/Node_impl.hpp @@ -17,46 +17,20 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_NODE_H__ -#define __CXXGRAPH_NODE_H__ - -#pragma once - -#include "CXXGraph/Utility/id_t.hpp" +#ifndef __CXXGRAPH_NODE_IMPL_H__ +#define __CXXGRAPH_NODE_IMPL_H__ #include -#include + +#include "Node_decl.h" namespace CXXGraph { + template class Node; -template -std::ostream &operator<<(std::ostream &os, const Node &node); -template -class Node { - private: - CXXGraph::id_t id = 0; - std::string userId = ""; - T data; - void setId(const std::string &); - - public: - Node(const std::string &, const T& data); - // Move constructor - Node(const std::string &, T&& data) noexcept; - ~Node() = default; - const CXXGraph::id_t &getId() const; - const std::string &getUserId() const; - const T &getData() const; - void setData(T&& new_data); - // operator - bool operator==(const Node &b) const; - bool operator<(const Node &b) const; - friend std::ostream &operator<< <>(std::ostream &os, const Node &node); -}; template -Node::Node(const std::string& id, const T& data) { +Node::Node(const std::string &id, const T &data) { this->userId = id; // the userid is set as sha512 hash of the user provided id setId(id); @@ -64,7 +38,7 @@ Node::Node(const std::string& id, const T& data) { } template -Node::Node(const std::string& id, T&& data) noexcept { +Node::Node(const std::string &id, T &&data) noexcept { this->userId = id; // the userid is set as sha512 hash of the user provided id setId(id); @@ -73,27 +47,6 @@ Node::Node(const std::string& id, T&& data) noexcept { template void Node::setId(const std::string &inpId) { - // const unsigned char* userId = reinterpret_cast((*inpId).c_str() ); unsigned char obuf[64]; unsigned long long obuf[8]; - // SHA512(userId, (*inpId).length(), reinterpret_cast(obuf)); - /** - // Transform byte-array to string - std::stringstream shastr; - shastr << std::hex << std::setfill('0'); - int i = 0; - //unsigned long can only store 8 bytes so we truncate the hash to 8 bytes - for (const auto &byte: obuf) - { - shastr << std::setw(2) << static_cast(byte); - i++; - if (i==8) break; - } - auto idStr = shastr.str(); - // convert hex string to unsigned long long - std::istringstream iss(idStr); - iss >> std::hex >> this->id; - - **/ this->id = std::hash{}(inpId); } @@ -113,7 +66,7 @@ const T &Node::getData() const { } template -void Node::setData(T&& new_data) { +void Node::setData(T &&new_data) { this->data = std::move(new_data); } @@ -136,6 +89,7 @@ std::ostream &operator<<(std::ostream &os, const Node &node) { << " Id:\t" << node.userId << "\n Data:\t" << node.data << "\n}"; return os; } + } // namespace CXXGraph -#endif // __CXXGRAPH_NODE_H__ +#endif // __CXXGRAPH_NODE_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp b/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp old mode 100755 new mode 100644 index 22ab4052c..9e126a72a --- a/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp +++ b/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp @@ -27,10 +27,10 @@ #include #include +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Partitioning/Utility/Globals.hpp" #include "CoordinatedRecord.hpp" -#include "CXXGraph/Edge/Edge.hpp" #include "PartitionState.hpp" -#include "CXXGraph/Partitioning/Utility/Globals.hpp" #include "Record.hpp" namespace CXXGraph { @@ -38,10 +38,10 @@ namespace CXXGraph { template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { template @@ -134,8 +134,8 @@ int CoordinatedPartitionState::getMachineLoadVertices(const int m) const { return (int)machines_load_vertices.at(m); } template -void CoordinatedPartitionState::incrementMachineLoad(const int m, - shared> e) { +void CoordinatedPartitionState::incrementMachineLoad( + const int m, shared> e) { std::lock_guard lock(*machines_load_edges_mutex); machines_load_edges[m] = machines_load_edges[m] + 1; int new_value = machines_load_edges.at(m); @@ -145,8 +145,8 @@ void CoordinatedPartitionState::incrementMachineLoad(const int m, partition_map[m]->addEdge(e); } template -void CoordinatedPartitionState::incrementMachineWeight(const int m, - shared> e) { +void CoordinatedPartitionState::incrementMachineWeight( + const int m, shared> e) { std::lock_guard lock(*machines_weight_edges_mutex); double edge_weight = CXXGraph::NEGLIGIBLE_WEIGHT; if (e->isWeighted().has_value() && e->isWeighted().value()) { diff --git a/include/CXXGraph/Partitioning/CoordinatedRecord.hpp b/include/CXXGraph/Partitioning/CoordinatedRecord.hpp old mode 100755 new mode 100644 index 0d26b6104..43094c04f --- a/include/CXXGraph/Partitioning/CoordinatedRecord.hpp +++ b/include/CXXGraph/Partitioning/CoordinatedRecord.hpp @@ -24,8 +24,8 @@ #include -#include "Record.hpp" #include "CXXGraph/Utility/Typedef.hpp" +#include "Record.hpp" namespace CXXGraph { namespace Partitioning { diff --git a/include/CXXGraph/Partitioning/EBV.hpp b/include/CXXGraph/Partitioning/EBV.hpp old mode 100755 new mode 100644 index f9cece655..0bbf36c22 --- a/include/CXXGraph/Partitioning/EBV.hpp +++ b/include/CXXGraph/Partitioning/EBV.hpp @@ -26,19 +26,19 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" -#include "PartitionStrategy.hpp" +#include "CXXGraph/Edge/Edge.h" #include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "PartitionStrategy.hpp" namespace CXXGraph { // Smart pointers alias template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { /** @@ -57,7 +57,8 @@ class EBV : public PartitionStrategy { explicit EBV(const Globals &G); ~EBV(); - void performStep(shared> e, shared> Sstate) override; + void performStep(shared> e, + shared> Sstate) override; }; template EBV::EBV(const Globals &G) : GLOBALS(G) { @@ -66,7 +67,8 @@ EBV::EBV(const Globals &G) : GLOBALS(G) { template EBV::~EBV() {} template -void EBV::performStep(shared> e, shared> state){ +void EBV::performStep(shared> e, + shared> state) { GLOBALS.edgeAnalyzed++; int P = GLOBALS.numberOfPartition; diff --git a/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp b/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp old mode 100755 new mode 100644 index 21c326d0c..593687650 --- a/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp +++ b/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp @@ -25,19 +25,19 @@ #include -#include "CXXGraph/Edge/Edge.hpp" -#include "PartitionStrategy.hpp" +#include "CXXGraph/Edge/Edge.h" #include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "PartitionStrategy.hpp" namespace CXXGraph { // Smart pointers alias template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { /** @@ -55,7 +55,8 @@ class EdgeBalancedVertexCut : public PartitionStrategy { explicit EdgeBalancedVertexCut(const Globals &G); ~EdgeBalancedVertexCut(); - void performStep(shared> e, shared> Sstate) override; + void performStep(shared> e, + shared> Sstate) override; }; template EdgeBalancedVertexCut::EdgeBalancedVertexCut(const Globals &G) : GLOBALS(G) { diff --git a/include/CXXGraph/Partitioning/GreedyVertexCut.hpp b/include/CXXGraph/Partitioning/GreedyVertexCut.hpp old mode 100755 new mode 100644 index ddc7ff538..679ef384d --- a/include/CXXGraph/Partitioning/GreedyVertexCut.hpp +++ b/include/CXXGraph/Partitioning/GreedyVertexCut.hpp @@ -23,22 +23,24 @@ #include #pragma once +#include + #include #include -#include "CXXGraph/Edge/Edge.hpp" -#include "PartitionStrategy.hpp" +#include "CXXGraph/Edge/Edge.h" #include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "PartitionStrategy.hpp" namespace CXXGraph { // Smart pointers alias template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { /** @@ -55,7 +57,8 @@ class GreedyVertexCut : public PartitionStrategy { explicit GreedyVertexCut(const Globals &G); ~GreedyVertexCut(); - void performStep(shared> e, shared> Sstate) override; + void performStep(shared> e, + shared> Sstate) override; }; template diff --git a/include/CXXGraph/Partitioning/HDRF.hpp b/include/CXXGraph/Partitioning/HDRF.hpp old mode 100755 new mode 100644 index cc8ed3430..6e7849690 --- a/include/CXXGraph/Partitioning/HDRF.hpp +++ b/include/CXXGraph/Partitioning/HDRF.hpp @@ -26,9 +26,9 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" -#include "PartitionStrategy.hpp" +#include "CXXGraph/Edge/Edge.h" #include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "PartitionStrategy.hpp" namespace CXXGraph { // Smart pointers alias @@ -37,8 +37,8 @@ using unique = std::unique_ptr; template using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { /** @@ -57,7 +57,8 @@ class HDRF : public PartitionStrategy { explicit HDRF(const Globals &G); ~HDRF(); - void performStep(shared> e, shared> Sstate) override; + void performStep(shared> e, + shared> Sstate) override; }; template HDRF::HDRF(const Globals &G) : GLOBALS(G) { @@ -66,7 +67,8 @@ HDRF::HDRF(const Globals &G) : GLOBALS(G) { template HDRF::~HDRF() {} template -void HDRF::performStep(shared> e, shared> state) { +void HDRF::performStep(shared> e, + shared> state) { int P = GLOBALS.numberOfPartition; double lambda = GLOBALS.param1; double epsilon = GLOBALS.param2; diff --git a/include/CXXGraph/Partitioning/Partition.hpp b/include/CXXGraph/Partitioning/Partition.hpp old mode 100755 new mode 100644 index 42b22c823..4c65a8c09 --- a/include/CXXGraph/Partitioning/Partition.hpp +++ b/include/CXXGraph/Partitioning/Partition.hpp @@ -26,18 +26,18 @@ #include #include -#include "PartitioningStats.hpp" #include "CXXGraph/Utility/Typedef.hpp" +#include "PartitioningStats.hpp" namespace CXXGraph { // Smart pointers alias template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; template class Graph; @@ -293,7 +293,8 @@ unsigned int getNumberOfNodes(const PartitionMap &partitionMap) { std::unordered_set>, nodeHash> nodeSet; for (const auto &it : partitionMap) { - const std::unordered_set>, nodeHash> partitionNodeSet = it.second->getNodeSet(); + const std::unordered_set>, nodeHash> + partitionNodeSet = it.second->getNodeSet(); for (const auto &it2 : partitionNodeSet) { // if (std::find_if(nodeSet.begin(), nodeSet.end(), [it2](const Node // *node) @@ -337,16 +338,24 @@ std::ostream &operator<<(std::ostream &os, const Partition &partition) { (*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"; + 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 { // Should never happens os << "Wrong Edge Class" diff --git a/include/CXXGraph/Partitioning/PartitionState.hpp b/include/CXXGraph/Partitioning/PartitionState.hpp old mode 100755 new mode 100644 index 39a6dc88f..c674d46e7 --- a/include/CXXGraph/Partitioning/PartitionState.hpp +++ b/include/CXXGraph/Partitioning/PartitionState.hpp @@ -23,6 +23,7 @@ #pragma once #include + #include "Record.hpp" namespace CXXGraph { @@ -32,8 +33,8 @@ using unique = std::unique_ptr; template using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { template diff --git a/include/CXXGraph/Partitioning/PartitionStrategy.hpp b/include/CXXGraph/Partitioning/PartitionStrategy.hpp old mode 100755 new mode 100644 index 2110bdbef..dcdf60634 --- a/include/CXXGraph/Partitioning/PartitionStrategy.hpp +++ b/include/CXXGraph/Partitioning/PartitionStrategy.hpp @@ -22,7 +22,7 @@ #pragma once -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionState.hpp" namespace CXXGraph { @@ -30,16 +30,17 @@ namespace CXXGraph { template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { template class PartitionStrategy { public: - virtual void performStep(shared> t, shared> Sstate) = 0; + virtual void performStep(shared> t, + shared> Sstate) = 0; }; } // namespace Partitioning } // namespace CXXGraph diff --git a/include/CXXGraph/Partitioning/Partitioner.hpp b/include/CXXGraph/Partitioning/Partitioner.hpp old mode 100755 new mode 100644 index b629b8561..b289af79e --- a/include/CXXGraph/Partitioning/Partitioner.hpp +++ b/include/CXXGraph/Partitioning/Partitioner.hpp @@ -24,17 +24,18 @@ #pragma once #include +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Graph/Graph.h" +#include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "CXXGraph/Utility/Runnable.hpp" #include "CoordinatedPartitionState.hpp" #include "EBV.hpp" -#include "CXXGraph/Edge/Edge.hpp" #include "EdgeBalancedVertexCut.hpp" #include "GreedyVertexCut.hpp" #include "HDRF.hpp" #include "PartitionAlgorithm.hpp" #include "PartitionStrategy.hpp" #include "PartitionerThread.hpp" -#include "CXXGraph/Partitioning/Utility/Globals.hpp" -#include "CXXGraph/Utility/Runnable.hpp" #include "WeightBalancedLibra.hpp" namespace CXXGraph { @@ -42,10 +43,10 @@ namespace CXXGraph { template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; namespace Partitioning { template @@ -57,11 +58,27 @@ class Partitioner { CoordinatedPartitionState startCoordinated(); - public: Partitioner(shared> dataset, Globals &G); Partitioner(const Partitioner &other); CoordinatedPartitionState performCoordinatedPartition(); + + public: + /** + * \brief + * This function partition a graph in a set of partitions + * Note: No Thread Safe + * + * @param graph The Graph to partition + * @param algorithm The partition algorithm + * @param numberOfPartition The number of partitions + * @return The partiton Map of the partitioned graph + */ + static PartitionMap partitionGraph( + const Graph &graph, const Partitioning::PartitionAlgorithm algorithm, + const unsigned int numberOfPartitions, const double param1 = 0.0, + const double param2 = 0.0, const double param3 = 0.0, + const unsigned int numberOfthreads = std::thread::hardware_concurrency()); }; template Partitioner::Partitioner(shared> dataset, Globals &G) @@ -102,8 +119,8 @@ Partitioner::Partitioner(shared> dataset, Globals &G) vertices_degrees[v]++; } - algorithm = make_shared>(GLOBALS, weight_sum_bound, - std::move(vertices_degrees)); + algorithm = make_shared>( + GLOBALS, weight_sum_bound, std::move(vertices_degrees)); } } @@ -145,8 +162,8 @@ Partitioner::Partitioner(const Partitioner &other) { vertices_degrees[v]++; } - algorithm = make_shared>(GLOBALS, weight_sum_bound, - std::move(vertices_degrees)); + algorithm = make_shared>( + GLOBALS, weight_sum_bound, std::move(vertices_degrees)); } } @@ -164,13 +181,12 @@ CoordinatedPartitionState Partitioner::startCoordinated() { int iStart = t * subSize; int iEnd = std::min((t + 1) * subSize, (int)n); if (iEnd >= iStart) { - list_vector[t] = - std::vector>>(std::next(dataset->begin(), iStart), - std::next(dataset->begin(), iEnd)); - myRunnable[t].reset( - new PartitionerThread(list_vector[t], - make_shared>(state), - algorithm)); + list_vector[t] = std::vector>>( + std::next(dataset->begin(), iStart), + std::next(dataset->begin(), iEnd)); + myRunnable[t].reset(new PartitionerThread( + list_vector[t], make_shared>(state), + algorithm)); myThreads[t] = std::thread(&Runnable::run, myRunnable[t].get()); } } @@ -197,6 +213,25 @@ CoordinatedPartitionState Partitioner::performCoordinatedPartition() { return startCoordinated(); } +template +PartitionMap Partitioner::partitionGraph( + const Graph &graph, const Partitioning::PartitionAlgorithm algorithm, + const unsigned int numberOfPartitions, const double param1, + const double param2, const double param3, + const unsigned int numberOfThreads) { + PartitionMap partitionMap; + Partitioning::Globals globals(numberOfPartitions, algorithm, param1, param2, + param3, numberOfThreads); + auto edgeSet_ptr = make_shared>(graph.getEdgeSet()); + globals.edgeCardinality = edgeSet_ptr->size(); + globals.vertexCardinality = graph.getNodeSet().size(); + Partitioning::Partitioner partitioner(edgeSet_ptr, globals); + Partitioning::CoordinatedPartitionState partitionState = + partitioner.performCoordinatedPartition(); + partitionMap = partitionState.getPartitionMap(); + return partitionMap; +} + } // namespace Partitioning } // namespace CXXGraph diff --git a/include/CXXGraph/Partitioning/PartitionerThread.hpp b/include/CXXGraph/Partitioning/PartitionerThread.hpp old mode 100755 new mode 100644 index a33913eed..f2a740648 --- a/include/CXXGraph/Partitioning/PartitionerThread.hpp +++ b/include/CXXGraph/Partitioning/PartitionerThread.hpp @@ -25,10 +25,10 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Utility/Runnable.hpp" #include "PartitionState.hpp" #include "PartitionStrategy.hpp" -#include "CXXGraph/Utility/Runnable.hpp" namespace CXXGraph { namespace Partitioning { @@ -42,7 +42,7 @@ class PartitionerThread : public Runnable { public: PartitionerThread(std::vector>> &list, shared> state, - shared> algorithm); + shared> algorithm); ~PartitionerThread(); void run() override; @@ -50,9 +50,9 @@ class PartitionerThread : public Runnable { std::list id_partitions; }; template -PartitionerThread::PartitionerThread(std::vector>> &list, - shared> state, - shared> algorithm) { +PartitionerThread::PartitionerThread( + std::vector>> &list, shared> state, + shared> algorithm) { this->list = list; this->state = state; this->algorithm = algorithm; diff --git a/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp b/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp old mode 100755 new mode 100644 index eaf38e0dd..00698204b --- a/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp +++ b/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp @@ -24,9 +24,9 @@ #include -#include "CXXGraph/Edge/Edge.hpp" -#include "PartitionStrategy.hpp" +#include "CXXGraph/Edge/Edge.h" #include "CXXGraph/Partitioning/Utility/Globals.hpp" +#include "PartitionStrategy.hpp" namespace CXXGraph { namespace Partitioning { @@ -50,7 +50,8 @@ class WeightBalancedLibra : public PartitionStrategy { std::unordered_map &&_vertices_degrees); ~WeightBalancedLibra(); - void performStep(shared> e, shared> Sstate) override; + void performStep(shared> e, + shared> Sstate) override; }; template WeightBalancedLibra::WeightBalancedLibra( diff --git a/include/CXXGraph/Utility/PointerHash.hpp b/include/CXXGraph/Utility/PointerHash.hpp old mode 100755 new mode 100644 index 399d6dd7a..f63d3112c --- a/include/CXXGraph/Utility/PointerHash.hpp +++ b/include/CXXGraph/Utility/PointerHash.hpp @@ -25,33 +25,33 @@ #include #include -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" namespace CXXGraph { template using shared = std::shared_ptr; -// Redefine the hash functions and equality operators for shared pointers of nodes and edges +// Redefine the hash functions and equality operators for shared pointers of +// nodes and edges template struct nodeHash { size_t operator()(const shared>& node) const { return node->getId(); } - size_t operator()(const shared>& node) const { - return node->getId(); - } + size_t operator()(const shared>& node) const { return node->getId(); } }; template struct edgeHash { size_t operator()(const shared>& edge) const { - return (edge->getNodePair().first->getId()) ^ (edge->getNodePair().second->getId()); + return (edge->getNodePair().first->getId()) ^ + (edge->getNodePair().second->getId()); } }; diff --git a/include/CXXGraph/Utility/Reader.hpp b/include/CXXGraph/Utility/Reader.hpp old mode 100755 new mode 100644 index 0059e1c53..729951c18 --- a/include/CXXGraph/Utility/Reader.hpp +++ b/include/CXXGraph/Utility/Reader.hpp @@ -23,6 +23,9 @@ #pragma once // This is to make sure that this header is only included once namespace CXXGraph { +// Foward declaration +template +class Graph; /*! Interface to implement for a custom reader. */ diff --git a/include/CXXGraph/Utility/TypeTraits.hpp b/include/CXXGraph/Utility/TypeTraits.hpp index 3c47c36b5..b57d5471d 100755 --- a/include/CXXGraph/Utility/TypeTraits.hpp +++ b/include/CXXGraph/Utility/TypeTraits.hpp @@ -20,59 +20,81 @@ #ifndef __CXXGRAPH_TYPE_TRAITS__ #define __CXXGRAPH_TYPE_TRAITS__ +#include #pragma once #include -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" namespace CXXGraph { -// define is_node type trait for Nodes, Nodes pointers and shared pointers +// is_node type trait template -struct is_node : std::false_type {}; +struct is_node + : std::integral_constant< + bool, std::is_same, + typename std::remove_const::type>::value> {}; template -struct is_node> : std::true_type {}; +struct is_node : std::false_type {}; -// define is_node_ptr type trait for Node pointers and shared pointers +template +struct is_node> : std::false_type {}; + +template +inline constexpr bool is_node_v = is_node::value; + +// is_node_ptr type trait template struct is_node_ptr : std::false_type {}; template -struct is_node_ptr*> : std::true_type {}; +struct is_node_ptr : std::integral_constant::value> {}; template -struct is_node_ptr>> : std::true_type {}; +struct is_node_ptr> + : std::integral_constant::value> {}; template -inline constexpr bool is_node_ptr_v = is_node::value; +inline constexpr bool is_node_ptr_v = is_node_ptr::value; -// define is_edge type trait for Edges +// is_edge type trait template -struct is_edge : std::false_type {}; +struct is_edge + : std::integral_constant< + bool, std::is_base_of, + typename std::remove_const::type>::value> {}; template -struct is_edge> : std::true_type {}; +struct is_edge : std::false_type {}; -// define is_edge_ptr type trait for Edge pointers and shared pointers +template +struct is_edge> : std::false_type {}; + +template +inline constexpr bool is_edge_v = is_edge::value; + +// is_edge_ptr type trait template struct is_edge_ptr : std::false_type {}; template -struct is_edge_ptr*> : std::true_type {}; +struct is_edge_ptr : std::integral_constant::value> {}; template -struct is_edge_ptr>> : std::true_type {}; +struct is_edge_ptr> + : std::integral_constant::value> {}; template -inline constexpr bool is_edge_ptr_v = is_edge::value; +inline constexpr bool is_edge_ptr_v = is_edge_ptr::value; + } // namespace CXXGraph #endif diff --git a/include/CXXGraph/Utility/Typedef.hpp b/include/CXXGraph/Utility/Typedef.hpp old mode 100755 new mode 100644 index 90186bc86..bc8b27755 --- a/include/CXXGraph/Utility/Typedef.hpp +++ b/include/CXXGraph/Utility/Typedef.hpp @@ -22,8 +22,6 @@ #pragma once -#include -#include #include #include #include @@ -36,13 +34,8 @@ namespace CXXGraph { // Smart pointers alias template -using unique = std::unique_ptr; -template using shared = std::shared_ptr; -using std::make_shared; -using std::make_unique; - template class Node; @@ -199,7 +192,7 @@ struct SCCResult_struct { std::string errorMessage = ""; // message of error int noOfComponents = 0; std::unordered_map sccMap; - //Components stronglyConnectedComps; + // Components stronglyConnectedComps; }; template using SCCResult = SCCResult_struct; @@ -226,9 +219,9 @@ struct TarjanResult_struct { std::vector> cutVertices; // a vector that stores cut vertices // (valid only is a graph is undirected and // flag TARJAN_FIND_CUTV is set) - std::vector> bridges; // a vector that stores bridges - // (valid only is a graph is undirected and - // flag TRAJAN_FIND_BRIDGES is set) + std::vector> bridges; // a vector that stores bridges + // (valid only is a graph is undirected and + // flag TRAJAN_FIND_BRIDGES is set) }; template using TarjanResult = TarjanResult_struct; @@ -244,8 +237,6 @@ struct BestFirstSearchResult_struct { template using BestFirstSearchResult = BestFirstSearchResult_struct; -/// Struct that contains the information about the partitioning statistics - /////////////////////////////////////////////////////////////////////////////////// // Using Definition // /////////////////////////////////////////////////////////////// @@ -257,10 +248,8 @@ using AdjacencyMatrix = std::unordered_map< nodeHash>; template -using DegreeMatrix = std::unordered_map< - shared>, - std::vector, - nodeHash>; +using DegreeMatrix = + std::unordered_map>, std::vector, nodeHash>; template using LaplacianMatrix = std::unordered_map< @@ -269,10 +258,10 @@ using LaplacianMatrix = std::unordered_map< nodeHash>; template -using TransitionMatrix = std::unordered_map< - shared>, - std::vector>, double>>, - nodeHash>; +using TransitionMatrix = + std::unordered_map>, + std::vector>, double>>, + nodeHash>; template using PartitionMap = diff --git a/include/CXXGraph/Utility/id_t.hpp b/include/CXXGraph/Utility/id_t.hpp index f997356dd..504acd695 100644 --- a/include/CXXGraph/Utility/id_t.hpp +++ b/include/CXXGraph/Utility/id_t.hpp @@ -6,11 +6,12 @@ namespace CXXGraph { #ifdef CXXGRAPH_ID_TYPE - // Throw compiler error if the type is signed - static_assert(IS_UNSIGNED(CXXGRAPH_ID_TYPE), "CXXGRAPH_ID_TYPE must be unsigned"); +// Throw compiler error if the type is signed +static_assert(IS_UNSIGNED(CXXGRAPH_ID_TYPE), + "CXXGRAPH_ID_TYPE must be unsigned"); - typedef CXXGRAPH_ID_TYPE id_t; +typedef CXXGRAPH_ID_TYPE id_t; #else - typedef size_t id_t; +typedef size_t id_t; #endif -} +} // namespace CXXGraph diff --git a/packaging/deb/CXXGraph/DEBIAN/control b/packaging/deb/CXXGraph/DEBIAN/control index 96e875583..6b6555c4b 100755 --- a/packaging/deb/CXXGraph/DEBIAN/control +++ b/packaging/deb/CXXGraph/DEBIAN/control @@ -1,5 +1,5 @@ Package: CXXGraph -Version: 0.2.0 +Version: 3.1.0 Section: custom Priority: optional Architecture: all diff --git a/test/BFSTest.cpp b/test/BFSTest.cpp index c23349da3..27a71b035 100644 --- a/test/BFSTest.cpp +++ b/test/BFSTest.cpp @@ -8,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(BFSTest, test_1) { CXXGraph::Node node1("1", 1); @@ -310,14 +310,15 @@ TEST(BFSTest, test_13) { int randomNumber2 = (distribution(rand) % MaxValue); if (randomNumber1 != randomNumber2) { shared> newEdge = - make_shared>(index, *(nodes.at(randomNumber1)), - *(nodes.at(randomNumber2))); + make_shared>( + index, *(nodes.at(randomNumber1)), *(nodes.at(randomNumber2))); edgeSet.insert(newEdge); } } for (int i = 1; i < nodes.size(); i += 2) { - shared> newEdge = make_shared>( - edges_size + i + 1, *(nodes.at(0)), *(nodes.at(i))); + shared> newEdge = + make_shared>( + edges_size + i + 1, *(nodes.at(0)), *(nodes.at(i))); edgeSet.insert(newEdge); } CXXGraph::Graph graph(edgeSet); diff --git a/test/BellmanFordTest.cpp b/test/BellmanFordTest.cpp index b336d9721..60be0e8d2 100644 --- a/test/BellmanFordTest.cpp +++ b/test/BellmanFordTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // check if algorithm works using a complicated test case TEST(BellmanFordTest, test_1) { diff --git a/test/BestFirstSearchTest.cpp b/test/BestFirstSearchTest.cpp index 6d560020b..20fe4a0bb 100644 --- a/test/BestFirstSearchTest.cpp +++ b/test/BestFirstSearchTest.cpp @@ -8,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(BestFirstSearchTest, source_node_missing) { CXXGraph::Node node1("1", 1); diff --git a/test/BoruvkaTest.cpp b/test/BoruvkaTest.cpp index 675017b21..20d91c4ff 100644 --- a/test/BoruvkaTest.cpp +++ b/test/BoruvkaTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // minimum spanning tree can differ so instead of checking // the exact order of elements, we can check some properties diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7f8f77921..4fad9e6f7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,7 +41,7 @@ if(TEST) OPTIONS "CMAKE_POSITION_INDEPENDENT_CODE True" ) - file (GLOB TEST_FILES "*.cpp" "*.hpp") + file (GLOB TEST_FILES "*.cpp" "*.hpp" "*.h") add_executable(test_exe ${TEST_FILES}) target_compile_definitions(test_exe @@ -97,6 +97,6 @@ if(TEST) add_test(test_topological_sort test_exe --gtest_filter=TopologicalSort*) add_test(test_transitive_reductiono test_exe --gtest_filter=TransitiveReduction*) add_test(test_union_find test_exe --gtest_filter=UnionFind*) - add_test(test_union_find test_exe --gtest_filter=welshPowellColoring*) - + add_test(test_floyd_warshall test_exe --gtest_filter=FloydWarshall*) + add_test(test_welsh_powell_coloring test_exe --gtest_filter=welshPowellColoring*) endif(TEST) diff --git a/test/ConnectivityTest.cpp b/test/ConnectivityTest.cpp index 3685c9a34..90c726f08 100644 --- a/test/ConnectivityTest.cpp +++ b/test/ConnectivityTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(ConnectivityTest, test_1) { CXXGraph::Node node0("0", 0); diff --git a/test/CycleCheckTest.cpp b/test/CycleCheckTest.cpp index 3220e0f6f..6216b52d2 100644 --- a/test/CycleCheckTest.cpp +++ b/test/CycleCheckTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(CycleCheckTest, test_1) { CXXGraph::Node node1("1", 1); diff --git a/test/DFSTest.cpp b/test/DFSTest.cpp index c5853a145..388b9ff2b 100644 --- a/test/DFSTest.cpp +++ b/test/DFSTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DFSTest, test_1) { CXXGraph::Node node1("1", 1); @@ -138,3 +139,61 @@ TEST(DFSTest, test_6) { ASSERT_FALSE(std::find(res.begin(), res.end(), node3) != res.end()); ASSERT_FALSE(std::find(res.begin(), res.end(), node4) != res.end()); } + +TEST(DFSTest, test_7) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + std::pair *, const CXXGraph::Node *> pairNode( + &node1, &node2); + CXXGraph::DirectedWeightedEdge edge1(1, pairNode, 1); + CXXGraph::DirectedWeightedEdge edge2(2, node2, node3, 1); + CXXGraph::DirectedWeightedEdge edge3(3, node3, node4, 1); + CXXGraph::DirectedWeightedEdge edge4(4, node4, node1, 1); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + edgeSet.insert(make_shared>(edge4)); + CXXGraph::Graph graph(edgeSet); + std::vector> res = graph.depth_first_search(node1); + ASSERT_EQ(res.size(), 4); + ASSERT_TRUE(std::find(res.begin(), res.end(), node1) != res.end()); + ASSERT_TRUE(std::find(res.begin(), res.end(), node2) != res.end()); + ASSERT_TRUE(std::find(res.begin(), res.end(), node3) != res.end()); + ASSERT_TRUE(std::find(res.begin(), res.end(), node4) != res.end()); +} + +TEST(DFSTest, test_8) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + + CXXGraph::DirectedWeightedEdge edge1(1, node1, node2, 1); + CXXGraph::DirectedWeightedEdge edge2(2, node3, node4, 1); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + CXXGraph::Graph graph(edgeSet); + std::vector> res = graph.depth_first_search(node1); + ASSERT_EQ(res.size(), 2); + ASSERT_TRUE(std::find(res.begin(), res.end(), node1) != res.end()); + ASSERT_TRUE(std::find(res.begin(), res.end(), node2) != res.end()); + ASSERT_FALSE(std::find(res.begin(), res.end(), node3) != res.end()); + ASSERT_FALSE(std::find(res.begin(), res.end(), node4) != res.end()); +} + +TEST(DFSTest, test_9) { + CXXGraph::Node node1("1", 1); + std::pair *, const CXXGraph::Node *> pairNode( + &node1, &node1); + CXXGraph::UndirectedWeightedEdge edge1(1, pairNode, 1); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + CXXGraph::Graph graph(edgeSet); + std::vector> res = graph.depth_first_search(node1); + ASSERT_EQ(res.size(), 1); + ASSERT_TRUE(std::find(res.begin(), res.end(), node1) != res.end()); +} diff --git a/test/DOTTest.cpp b/test/DOTTest.cpp index 93d67d54d..9b5144a05 100644 --- a/test/DOTTest.cpp +++ b/test/DOTTest.cpp @@ -7,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DOTTest, WriteToDotDirectedWeighted) { // Generate a simple test graph with few nodes and edges diff --git a/test/DialTest.cpp b/test/DialTest.cpp index 92fa2f76d..782c21ca7 100644 --- a/test/DialTest.cpp +++ b/test/DialTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DialTest, test_1) { CXXGraph::Node node1("1", 1); diff --git a/test/DijkstraTest.cpp b/test/DijkstraTest.cpp index d30c13bec..4588b8b8d 100644 --- a/test/DijkstraTest.cpp +++ b/test/DijkstraTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DijkstraTest, correct_example_1) { CXXGraph::Node node1("1", 1); @@ -33,13 +34,13 @@ TEST(DijkstraTest, correct_example_1) { expected.push_back("1"); expected.push_back("2"); expected.push_back("3"); - int index=0; + int index = 0; ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 2); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } } @@ -65,13 +66,13 @@ TEST(DijkstraTest, correct_example_2) { std::vector expected; expected.push_back("1"); expected.push_back("3"); - int index=0; + int index = 0; ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 6); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } } @@ -107,55 +108,55 @@ TEST(DijkstraTest, correct_example_3) { expected.push_back("A"); expected.push_back("B"); expected.push_back("E"); - int index=0; + int index = 0; CXXGraph::Graph graph(edgeSet); CXXGraph::DijkstraResult res = graph.dijkstra(nodeC, nodeE); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 5); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); expected.push_back("C"); expected.push_back("A"); - index=0; + index = 0; res = graph.dijkstra(nodeC, nodeA); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 1); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); expected.push_back("C"); expected.push_back("A"); expected.push_back("B"); - index=0; + index = 0; res = graph.dijkstra(nodeC, nodeB); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 4); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); expected.push_back("C"); expected.push_back("D"); - index=0; + index = 0; res = graph.dijkstra(nodeC, nodeD); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 2); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } } @@ -195,43 +196,43 @@ TEST(DijkstraTest, correct_example_4) { std::vector expected; expected.push_back("0"); expected.push_back("1"); - int index=0; + int index = 0; CXXGraph::Graph graph(edgeSet); CXXGraph::DijkstraResult res = graph.dijkstra(node0, node1); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 2); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); expected.push_back("0"); expected.push_back("2"); - index=0; - + index = 0; + res = graph.dijkstra(node0, node2); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 6); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); expected.push_back("0"); expected.push_back("1"); expected.push_back("3"); - index=0; + index = 0; res = graph.dijkstra(node0, node3); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 7); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); @@ -239,14 +240,14 @@ TEST(DijkstraTest, correct_example_4) { expected.push_back("1"); expected.push_back("3"); expected.push_back("4"); - index=0; + index = 0; res = graph.dijkstra(node0, node4); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 17); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); @@ -254,14 +255,14 @@ TEST(DijkstraTest, correct_example_4) { expected.push_back("1"); expected.push_back("3"); expected.push_back("5"); - index=0; + index = 0; res = graph.dijkstra(node0, node5); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 22); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } expected.clear(); @@ -270,14 +271,14 @@ TEST(DijkstraTest, correct_example_4) { expected.push_back("3"); expected.push_back("4"); expected.push_back("6"); - index=0; + index = 0; res = graph.dijkstra(node0, node6); ASSERT_TRUE(res.success); ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 19); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } } @@ -313,7 +314,7 @@ TEST(DijkstraTest, correct_example_5) { edgeSet.insert(make_shared>(edge9)); std::vector expected; - int index=0; + int index = 0; expected.push_back("1"); expected.push_back("3"); expected.push_back("6"); @@ -324,8 +325,8 @@ TEST(DijkstraTest, correct_example_5) { ASSERT_EQ(res.errorMessage, ""); ASSERT_EQ(res.result, 20); ASSERT_EQ(res.path.size(), expected.size()); - for(auto elem:res.path){ - ASSERT_EQ(elem,expected[index]); + for (auto elem : res.path) { + ASSERT_EQ(elem, expected[index]); index++; } } diff --git a/test/DirectedEdgeTest.cpp b/test/DirectedEdgeTest.cpp index 1c8f6338d..d0b1d5bdc 100644 --- a/test/DirectedEdgeTest.cpp +++ b/test/DirectedEdgeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DirectedEdgeTest, Constructor_1) { CXXGraph::Node node1("1", 1); @@ -74,8 +74,8 @@ TEST(DirectedEdgeTest, Bool_data) { // Second constructor CXXGraph::Node node3("3", true); CXXGraph::Node node4("4", false); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::DirectedEdge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); @@ -97,8 +97,8 @@ TEST(DirectedEdgeTest, String_data) { // Second constructor CXXGraph::Node node3("3", "On"); CXXGraph::Node node4("4", "Off"); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::DirectedEdge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); diff --git a/test/DirectedWeightedEdgeTest.cpp b/test/DirectedWeightedEdgeTest.cpp index da1bb5b5c..deb6b786a 100644 --- a/test/DirectedWeightedEdgeTest.cpp +++ b/test/DirectedWeightedEdgeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(DirectedWeightedEdgeTest, Constructor_1) { CXXGraph::Node node1("1", 1); @@ -107,8 +107,8 @@ TEST(DirectedWeightedEdgeTest, Bool_data) { // Second constructor CXXGraph::Node node3("3", true); CXXGraph::Node node4("4", false); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::DirectedWeightedEdge edge2(2, pairNode, 2); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); @@ -130,8 +130,8 @@ TEST(DirectedWeightedEdgeTest, String_data) { // Second constructor CXXGraph::Node node3("3", "On"); CXXGraph::Node node4("4", "Off"); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::DirectedWeightedEdge edge2(2, pairNode, 6); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); diff --git a/test/EdgeTest.cpp b/test/EdgeTest.cpp index 9b0d4e6a8..1efd82a0e 100644 --- a/test/EdgeTest.cpp +++ b/test/EdgeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(EdgeTest, Constructor_1) { CXXGraph::Node node1("1", 1); @@ -48,8 +48,8 @@ TEST(EdgeTest, Bool_data) { // Second constructor CXXGraph::Node node3("3", true); CXXGraph::Node node4("4", false); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::Edge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); @@ -67,8 +67,8 @@ TEST(EdgeTest, String_data) { // Second constructor CXXGraph::Node node3("3", "On"); CXXGraph::Node node4("4", "Off"); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::Edge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); diff --git a/test/EulerPathTest.cpp b/test/EulerPathTest.cpp index 87da7f6c8..b57a62b5f 100644 --- a/test/EulerPathTest.cpp +++ b/test/EulerPathTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // example taken from // https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ diff --git a/test/FWTest.cpp b/test/FWTest.cpp index 7e37c1705..13580d59a 100644 --- a/test/FWTest.cpp +++ b/test/FWTest.cpp @@ -8,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm#Example TEST(FWTest, test_1) { diff --git a/test/FloydWarshallTest.cpp b/test/FloydWarshallTest.cpp new file mode 100644 index 000000000..799b9bdc5 --- /dev/null +++ b/test/FloydWarshallTest.cpp @@ -0,0 +1,91 @@ +#include + +#include "CXXGraph/CXXGraph.hpp" +#include "gtest/gtest.h" + +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +using std::make_shared; +using std::make_unique; + +// minimum spanning tree can differ so instead of checking +// the exact order of elements, we can check some properties +// like the length & cost of mst which must remain the same + +// example taken from +// https://www.geeksforgeeks.org/prims-mst-for-adjacency-list-representation-greedy-algo-6/TEST(FWTest, +// test_1) +TEST(FloydWarshallTest, test_1) { + 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>(edge5)); + 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); + CXXGraph::FWResult res = graph.floydWarshall(); + + ASSERT_TRUE(res.success); + ASSERT_FALSE(res.negativeCycle); + ASSERT_EQ(res.errorMessage, ""); +} + +TEST(FloydWarshallTest, test_2) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + + CXXGraph::UndirectedWeightedEdge edge1(1, node0, node1, 1); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + + CXXGraph::Graph graph(edgeSet); + CXXGraph::FWResult res = graph.floydWarshall(); + + auto distance_0_1 = res.result.find(std::make_pair("0", "1")); + auto distance_1_0 = res.result.find(std::make_pair("1", "0")); + + ASSERT_TRUE(res.success); + ASSERT_FALSE(res.negativeCycle); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_EQ(distance_0_1->second, distance_1_0->second); +} \ No newline at end of file diff --git a/test/FordFulkersonTest.cpp b/test/FordFulkersonTest.cpp index 47b253cd0..a3a958314 100644 --- a/test/FordFulkersonTest.cpp +++ b/test/FordFulkersonTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(FordFulkersonTest, test_1) { CXXGraph::Node node0("0", 0); diff --git a/test/GraphSlicingTest.cpp b/test/GraphSlicingTest.cpp index 05c78d50e..2c4917ab5 100644 --- a/test/GraphSlicingTest.cpp +++ b/test/GraphSlicingTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // DirectedEdge TEST(GraphSlicingTest, test_1) { diff --git a/test/GraphTest.cpp b/test/GraphTest.cpp index a475d0a59..3976be2bd 100644 --- a/test/GraphTest.cpp +++ b/test/GraphTest.cpp @@ -1,12 +1,12 @@ -#include -#include -#include #include #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" +// include it to check that the static asserts don't fail +#include "TypeTraitsTest.hpp" + // Smart pointers alias template using unique = std::unique_ptr; diff --git a/test/KahnTest.cpp b/test/KahnTest.cpp index ad8baa28d..6e84150f5 100644 --- a/test/KahnTest.cpp +++ b/test/KahnTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(KahnTest, error_cyclic_graph) { CXXGraph::Node node1("1", 1); diff --git a/test/KosarajuTest.cpp b/test/KosarajuTest.cpp index a3c129719..07a550a4f 100644 --- a/test/KosarajuTest.cpp +++ b/test/KosarajuTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // helper function to compare strongly connected components (SCC) as computed // by the algorithm with expected (correct) SCC @@ -17,9 +18,9 @@ void compareComponents(CXXGraph::SCCResult result, CXXGraph::Components& comp2) { ASSERT_EQ(result.noOfComponents, comp2.size()); - for(int i=0;i + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // minimum spanning tree can differ so instead of checking // the exact order of elements, we can check some properties diff --git a/test/MTXTest.cpp b/test/MTXTest.cpp index a92303260..3590f6416 100644 --- a/test/MTXTest.cpp +++ b/test/MTXTest.cpp @@ -7,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(MTXTest, WriteToMtxDirectedWeighted) { // Generate a simple test graph with few nodes and edges @@ -85,9 +85,10 @@ TEST(MTXTest, ReadFromMtxDirectedWeighted) { ASSERT_EQ(graph.getEdge(0).value()->isDirected(), false); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(0).value().get()) - ->getWeight(), - 1.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(0).value().get()) + ->getWeight(), + 1.); // Check the last edge ASSERT_EQ(graph.getEdge(7).value()->getNodePair().first->getUserId(), "5"); @@ -96,9 +97,10 @@ TEST(MTXTest, ReadFromMtxDirectedWeighted) { ASSERT_EQ(graph.getEdge(7).value()->isDirected(), false); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(7).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(7).value().get()) - ->getWeight(), - 12.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(7).value().get()) + ->getWeight(), + 12.); // Check an edge in the middle ASSERT_EQ(graph.getEdge(3).value()->getNodePair().first->getUserId(), "1"); @@ -107,9 +109,10 @@ TEST(MTXTest, ReadFromMtxDirectedWeighted) { ASSERT_EQ(graph.getEdge(3).value()->isDirected(), true); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(3).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(3).value().get()) - ->getWeight(), - 6.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(3).value().get()) + ->getWeight(), + 6.); } TEST(MTXTest, ReadFromMtxUndirectedWeighted) { @@ -127,9 +130,10 @@ TEST(MTXTest, ReadFromMtxUndirectedWeighted) { ASSERT_EQ(graph.getEdge(0).value()->isDirected(), false); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(0).value().get()) - ->getWeight(), - 1.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(0).value().get()) + ->getWeight(), + 1.); // Check the last edge ASSERT_EQ(graph.getEdge(7).value()->getNodePair().first->getUserId(), "5"); @@ -138,9 +142,10 @@ TEST(MTXTest, ReadFromMtxUndirectedWeighted) { ASSERT_EQ(graph.getEdge(7).value()->isDirected(), false); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(7).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(7).value().get()) - ->getWeight(), - 12.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(7).value().get()) + ->getWeight(), + 12.); // Check an edge in the middle ASSERT_EQ(graph.getEdge(3).value()->getNodePair().first->getUserId(), "1"); @@ -149,7 +154,8 @@ TEST(MTXTest, ReadFromMtxUndirectedWeighted) { ASSERT_EQ(graph.getEdge(3).value()->isDirected(), false); // Check that it's weighted and the value of the weight ASSERT_EQ(graph.getEdge(3).value()->isWeighted(), true); - ASSERT_EQ(dynamic_cast(graph.getEdge(3).value().get()) - ->getWeight(), - 6.); + ASSERT_EQ( + dynamic_cast(graph.getEdge(3).value().get()) + ->getWeight(), + 6.); } diff --git a/test/NodeTest.cpp b/test/NodeTest.cpp index a14ab7ba6..e2e400f88 100644 --- a/test/NodeTest.cpp +++ b/test/NodeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // Struct for testing Node as a template of different items struct testStruct { diff --git a/test/PartitionTest.cpp b/test/PartitionTest.cpp index 572e71ca4..cb6fb924e 100644 --- a/test/PartitionTest.cpp +++ b/test/PartitionTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "Utilities.hpp" #include "gtest/gtest.h" @@ -7,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; static auto nodes = generateRandomNodes(10000, 2); static auto edges = generateRandomEdges(10000, nodes); @@ -53,8 +54,8 @@ TEST(PartitionTest, test_1) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -73,8 +74,8 @@ TEST(PartitionTest, test_2) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -126,8 +127,9 @@ TEST(PartitionTest, test_3) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, + 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -146,8 +148,9 @@ TEST(PartitionTest, test_4) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, + 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -199,8 +202,8 @@ TEST(PartitionTest, test_5) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -219,8 +222,8 @@ TEST(PartitionTest, test_6) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -272,8 +275,8 @@ TEST(PartitionTest, test_7) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -292,8 +295,8 @@ TEST(PartitionTest, test_8) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -312,9 +315,9 @@ TEST(PartitionTest, test_9) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = - graph.partitionGraph(CXXGraph::Partitioning::PartitionAlgorithm::WB_LIBRA, - 4, 1.0, 0.0, 0.0, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::WB_LIBRA, 4, 1.0, 0.0, + 0.0, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); diff --git a/test/PrimTest.cpp b/test/PrimTest.cpp index fa764e8b9..0bdd7f317 100644 --- a/test/PrimTest.cpp +++ b/test/PrimTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // minimum spanning tree can differ so instead of checking // the exact order of elements, we can check some properties diff --git a/test/RWOutputTest.cpp b/test/RWOutputTest.cpp index ca589087e..ee492d325 100644 --- a/test/RWOutputTest.cpp +++ b/test/RWOutputTest.cpp @@ -8,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; inline bool exists_test(const std::string &name) { struct stat buffer; @@ -390,9 +390,9 @@ TEST(RWOutputTest, test_10) { readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 5); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 5); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -415,9 +415,9 @@ TEST(RWOutputTest, test_10) { !readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 6); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 6); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -773,9 +773,9 @@ TEST(RWOutputTest, test_20) { readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 5); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 5); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -798,9 +798,9 @@ TEST(RWOutputTest, test_20) { !readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 6); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 6); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -886,9 +886,9 @@ TEST(RWOutputTest, test_22) { readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 5); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 5); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -911,9 +911,9 @@ TEST(RWOutputTest, test_22) { !readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 6); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 6); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -1026,9 +1026,9 @@ TEST(RWOutputTest, test_25) { readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 5); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 5); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -1051,9 +1051,9 @@ TEST(RWOutputTest, test_25) { !readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 6); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 6); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -1193,9 +1193,9 @@ TEST(RWOutputTest, test_29) { readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 5); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 5); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); @@ -1218,9 +1218,9 @@ TEST(RWOutputTest, test_29) { !readEdgeIt->isDirected().value()); ASSERT_TRUE(readEdgeIt->isWeighted().has_value() && readEdgeIt->isWeighted().value()); - ASSERT_EQ( - (dynamic_cast(readEdgeIt.get()))->getWeight(), - 6); + ASSERT_EQ((dynamic_cast(readEdgeIt.get())) + ->getWeight(), + 6); ASSERT_EQ(readEdgeIt->getNodePair().first->getUserId(), node1.getUserId()); ASSERT_EQ(readEdgeIt->getNodePair().first->getData(), node1.getData()); diff --git a/test/TarjanTest.cpp b/test/TarjanTest.cpp index 211423208..2405609f4 100644 --- a/test/TarjanTest.cpp +++ b/test/TarjanTest.cpp @@ -116,15 +116,13 @@ TEST(TarjanTest, test_3) { for (int i = 1; i <= 18; ++i) { nodes.emplace_back(std::to_string(i), i); }; - std::vector> pairs{ - {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, - {5, 6}, {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, - {8, 9}, {9, 11}, {8, 12}, {8, 14}, {8, 15}, - {12, 13}, {14, 13}, {15, 13}, {9, 10}, {11, 10}, - {10, 16}, {10, 17}, {10, 18}, {17, 18} - }; + std::vector> pairs{ + {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, {5, 6}, + {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, {8, 9}, {9, 11}, + {8, 12}, {8, 14}, {8, 15}, {12, 13}, {14, 13}, {15, 13}, {9, 10}, + {11, 10}, {10, 16}, {10, 17}, {10, 18}, {17, 18}}; std::vector> edges; - for (const auto& [id1, id2] : pairs) { + for (const auto &[id1, id2] : pairs) { edges.emplace_back(edges.size() + 1, nodes[id1], nodes[id2], 1); } CXXGraph::T_EdgeSet edgeSet; @@ -137,9 +135,10 @@ TEST(TarjanTest, test_3) { std::vector expectRes{2, 7, 8, 10}; ASSERT_EQ(res.cutVertices.size(), expectRes.size()); - std::sort(res.cutVertices.begin(), res.cutVertices.end(), [&](const auto &node1, const auto &node2) { - return node1.getData() < node2.getData(); - }); + std::sort(res.cutVertices.begin(), res.cutVertices.end(), + [&](const auto &node1, const auto &node2) { + return node1.getData() < node2.getData(); + }); for (int i = 0; i < expectRes.size(); ++i) { ASSERT_EQ(res.cutVertices[i], nodes[expectRes[i]]); } @@ -158,15 +157,13 @@ TEST(TarjanTest, test_4) { for (int i = 1; i <= 18; ++i) { nodes.emplace_back(std::to_string(i), i); }; - std::vector> pairs{ - {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, - {5, 6}, {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, - {8, 9}, {9, 11}, {8, 12}, {8, 14}, {8, 15}, - {12, 13}, {14, 13}, {15, 13}, {9, 10}, {11, 10}, - {10, 16}, {10, 17}, {10, 18}, {17, 18} - }; + std::vector> pairs{ + {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, {5, 6}, + {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, {8, 9}, {9, 11}, + {8, 12}, {8, 14}, {8, 15}, {12, 13}, {14, 13}, {15, 13}, {9, 10}, + {11, 10}, {10, 16}, {10, 17}, {10, 18}, {17, 18}}; std::vector> edges; - for (const auto& [id1, id2] : pairs) { + for (const auto &[id1, id2] : pairs) { edges.emplace_back(edges.size() + 1, nodes[id1], nodes[id2], 1); } CXXGraph::T_EdgeSet edgeSet; @@ -178,13 +175,13 @@ TEST(TarjanTest, test_4) { ASSERT_EQ(res.success, true); std::vector>> expectRes{ - {nodes[1], nodes[2]}, {nodes[2], nodes[3], nodes[4]}, - {nodes[2], nodes[5], nodes[6], nodes[7]}, + {nodes[1], nodes[2]}, + {nodes[2], nodes[3], nodes[4]}, + {nodes[2], nodes[5], nodes[6], nodes[7]}, {nodes[7], nodes[8], nodes[9], nodes[10], nodes[11]}, {nodes[8], nodes[12], nodes[13], nodes[14], nodes[15]}, {nodes[10], nodes[16]}, - {nodes[10], nodes[17], nodes[18]} - }; + {nodes[10], nodes[17], nodes[18]}}; ASSERT_EQ(res.verticeBiconnectedComps.size(), expectRes.size()); // sort nodes in a vbcc by node id @@ -197,10 +194,11 @@ TEST(TarjanTest, test_4) { std::sort(res.verticeBiconnectedComps.begin(), res.verticeBiconnectedComps.end(), [&](const auto &scc1, const auto &scc2) { - if ((scc1.size() == 0) || (scc2.size() == 0) || (scc1[0].getData() == scc2[0].getData())) { + if ((scc1.size() == 0) || (scc2.size() == 0) || + (scc1[0].getData() == scc2[0].getData())) { return scc1.size() < scc2.size(); } - return scc1[0].getData() < scc2[0].getData(); + return scc1[0].getData() < scc2[0].getData(); }); for (int i = 0; i < res.verticeBiconnectedComps.size(); ++i) { ASSERT_EQ(res.verticeBiconnectedComps[i].size(), expectRes[i].size()); @@ -215,22 +213,20 @@ TEST(TarjanTest, test_4) { ASSERT_EQ(res.cutVertices.size(), 0); } - TEST(TarjanTest, test_5) { - // the undirected graph used in this case is a bridge tree from a tutorial on codeforce - // https://codeforces.com/blog/entry/99259 + // the undirected graph used in this case is a bridge tree from a tutorial on + // codeforce https://codeforces.com/blog/entry/99259 std::vector> nodes; nodes.emplace_back("0", 0); for (int i = 1; i <= 12; ++i) { nodes.emplace_back(std::to_string(i), i); }; - std::vector> pairs{ - {1, 2}, {2, 3}, {1, 3}, {3, 4}, {3, 9}, - {4, 5}, {5, 6}, {4, 7}, {6, 7}, {6, 8}, - {7, 8}, {9, 10}, {9, 11}, {10, 12}, {11, 12}, + std::vector> pairs{ + {1, 2}, {2, 3}, {1, 3}, {3, 4}, {3, 9}, {4, 5}, {5, 6}, {4, 7}, + {6, 7}, {6, 8}, {7, 8}, {9, 10}, {9, 11}, {10, 12}, {11, 12}, }; std::vector> edges; - for (const auto& [id1, id2] : pairs) { + for (const auto &[id1, id2] : pairs) { edges.emplace_back(edges.size() + 1, nodes[id1], nodes[id2], 1); } CXXGraph::T_EdgeSet edgeSet; @@ -243,9 +239,10 @@ TEST(TarjanTest, test_5) { std::vector expectRes{3, 4}; ASSERT_EQ(res.bridges.size(), expectRes.size()); - std::sort(res.bridges.begin(), res.bridges.end(), [&](const auto &edge1, const auto &edge2) { - return edge1.getId() < edge2.getId(); - }); + std::sort(res.bridges.begin(), res.bridges.end(), + [&](const auto &edge1, const auto &edge2) { + return edge1.getId() < edge2.getId(); + }); for (int i = 0; i < expectRes.size(); ++i) { ASSERT_EQ(res.bridges[i], edges[expectRes[i]]); } @@ -257,20 +254,19 @@ TEST(TarjanTest, test_5) { } TEST(TarjanTest, test_6) { - // the undirected graph used in this case is a bridge tree from a tutorial on codeforce - // https://codeforces.com/blog/entry/99259 + // the undirected graph used in this case is a bridge tree from a tutorial on + // codeforce https://codeforces.com/blog/entry/99259 std::vector> nodes; nodes.emplace_back("0", 0); for (int i = 1; i <= 12; ++i) { nodes.emplace_back(std::to_string(i), i); }; - std::vector> pairs{ - {1, 2}, {2, 3}, {1, 3}, {3, 4}, {3, 9}, - {4, 5}, {5, 6}, {4, 7}, {6, 7}, {6, 8}, - {7, 8}, {9, 10}, {9, 11}, {10, 12}, {11, 12}, + std::vector> pairs{ + {1, 2}, {2, 3}, {1, 3}, {3, 4}, {3, 9}, {4, 5}, {5, 6}, {4, 7}, + {6, 7}, {6, 8}, {7, 8}, {9, 10}, {9, 11}, {10, 12}, {11, 12}, }; std::vector> edges; - for (const auto& [id1, id2] : pairs) { + for (const auto &[id1, id2] : pairs) { edges.emplace_back(edges.size() + 1, nodes[id1], nodes[id2], 1); } CXXGraph::T_EdgeSet edgeSet; @@ -282,10 +278,9 @@ TEST(TarjanTest, test_6) { ASSERT_EQ(res.success, true); std::vector>> expectRes{ - {nodes[1], nodes[2], nodes[3]}, - {nodes[4], nodes[5], nodes[6], nodes[7], nodes[8]}, - {nodes[9], nodes[10], nodes[11], nodes[12]} - }; + {nodes[1], nodes[2], nodes[3]}, + {nodes[4], nodes[5], nodes[6], nodes[7], nodes[8]}, + {nodes[9], nodes[10], nodes[11], nodes[12]}}; ASSERT_EQ(res.edgeBiconnectedComps.size(), expectRes.size()); // sort nodes in a vbcc by node id @@ -295,13 +290,13 @@ TEST(TarjanTest, test_6) { }); } // sort vbccs by first node and size - std::sort(res.edgeBiconnectedComps.begin(), - res.edgeBiconnectedComps.end(), + std::sort(res.edgeBiconnectedComps.begin(), res.edgeBiconnectedComps.end(), [&](const auto &scc1, const auto &scc2) { - if ((scc1.size() == 0) || (scc2.size() == 0) || (scc1[0].getData() == scc2[0].getData())) { + if ((scc1.size() == 0) || (scc2.size() == 0) || + (scc1[0].getData() == scc2[0].getData())) { return scc1.size() < scc2.size(); } - return scc1[0].getData() < scc2[0].getData(); + return scc1[0].getData() < scc2[0].getData(); }); for (int i = 0; i < res.edgeBiconnectedComps.size(); ++i) { ASSERT_EQ(res.edgeBiconnectedComps[i].size(), expectRes[i].size()); @@ -324,15 +319,13 @@ TEST(TarjanTest, test_7) { for (int i = 1; i <= 18; ++i) { nodes.emplace_back(std::to_string(i), i); }; - std::vector> pairs{ - {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, - {5, 6}, {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, - {8, 9}, {9, 11}, {8, 12}, {8, 14}, {8, 15}, - {12, 13}, {14, 13}, {15, 13}, {9, 10}, {11, 10}, - {10, 16}, {10, 17}, {10, 18}, {17, 18} - }; + std::vector> pairs{ + {1, 2}, {2, 3}, {3, 4}, {2, 4}, {2, 5}, {2, 6}, {5, 6}, + {5, 7}, {6, 7}, {7, 8}, {7, 11}, {8, 11}, {8, 9}, {9, 11}, + {8, 12}, {8, 14}, {8, 15}, {12, 13}, {14, 13}, {15, 13}, {9, 10}, + {11, 10}, {10, 16}, {10, 17}, {10, 18}, {17, 18}}; std::vector> edges; - for (const auto& [id1, id2] : pairs) { + for (const auto &[id1, id2] : pairs) { edges.emplace_back(edges.size() + 1, nodes[id1], nodes[id2], 1); } CXXGraph::T_EdgeSet edgeSet; @@ -341,11 +334,17 @@ TEST(TarjanTest, test_7) { } CXXGraph::Graph graph(edgeSet); - CXXGraph::TarjanResult cutvRes = graph.tarjan(CXXGraph::TARJAN_FIND_CUTV); - CXXGraph::TarjanResult bridgeRes = graph.tarjan(CXXGraph::TARJAN_FIND_BRIDGE); - CXXGraph::TarjanResult vbccRes = graph.tarjan(CXXGraph::TARJAN_FIND_VBCC); - CXXGraph::TarjanResult ebccRes = graph.tarjan(CXXGraph::TARJAN_FIND_EBCC); - CXXGraph::TarjanResult res = graph.tarjan(CXXGraph::TARJAN_FIND_CUTV | CXXGraph::TARJAN_FIND_BRIDGE | CXXGraph::TARJAN_FIND_EBCC | CXXGraph::TARJAN_FIND_VBCC); + CXXGraph::TarjanResult cutvRes = + graph.tarjan(CXXGraph::TARJAN_FIND_CUTV); + CXXGraph::TarjanResult bridgeRes = + graph.tarjan(CXXGraph::TARJAN_FIND_BRIDGE); + CXXGraph::TarjanResult vbccRes = + graph.tarjan(CXXGraph::TARJAN_FIND_VBCC); + CXXGraph::TarjanResult ebccRes = + graph.tarjan(CXXGraph::TARJAN_FIND_EBCC); + CXXGraph::TarjanResult res = + graph.tarjan(CXXGraph::TARJAN_FIND_CUTV | CXXGraph::TARJAN_FIND_BRIDGE | + CXXGraph::TARJAN_FIND_EBCC | CXXGraph::TARJAN_FIND_VBCC); ASSERT_EQ(res.success, true); ASSERT_EQ(res.cutVertices.size(), cutvRes.cutVertices.size()); @@ -356,18 +355,24 @@ TEST(TarjanTest, test_7) { for (int i = 0; i < bridgeRes.cutVertices.size(); ++i) { ASSERT_EQ(bridgeRes.bridges[i], res.bridges[i]); } - ASSERT_EQ(res.edgeBiconnectedComps.size(), ebccRes.edgeBiconnectedComps.size()); + ASSERT_EQ(res.edgeBiconnectedComps.size(), + ebccRes.edgeBiconnectedComps.size()); for (int i = 0; i < res.edgeBiconnectedComps.size(); ++i) { - ASSERT_EQ(res.edgeBiconnectedComps[i].size(), ebccRes.edgeBiconnectedComps[i].size()); + ASSERT_EQ(res.edgeBiconnectedComps[i].size(), + ebccRes.edgeBiconnectedComps[i].size()); for (int j = 0; j < ebccRes.edgeBiconnectedComps[i].size(); ++j) { - ASSERT_EQ(res.edgeBiconnectedComps[i][j], ebccRes.edgeBiconnectedComps[i][j]); + ASSERT_EQ(res.edgeBiconnectedComps[i][j], + ebccRes.edgeBiconnectedComps[i][j]); } } - ASSERT_EQ(res.verticeBiconnectedComps.size(), vbccRes.verticeBiconnectedComps.size()); + ASSERT_EQ(res.verticeBiconnectedComps.size(), + vbccRes.verticeBiconnectedComps.size()); for (int i = 0; i < res.verticeBiconnectedComps.size(); ++i) { - ASSERT_EQ(res.verticeBiconnectedComps[i].size(), vbccRes.verticeBiconnectedComps[i].size()); + ASSERT_EQ(res.verticeBiconnectedComps[i].size(), + vbccRes.verticeBiconnectedComps[i].size()); for (int j = 0; j < vbccRes.verticeBiconnectedComps[i].size(); ++j) { - ASSERT_EQ(res.verticeBiconnectedComps[i][j], vbccRes.verticeBiconnectedComps[i][j]); + ASSERT_EQ(res.verticeBiconnectedComps[i][j], + vbccRes.verticeBiconnectedComps[i][j]); } } } diff --git a/test/TopologicalSortTest.cpp b/test/TopologicalSortTest.cpp index 5aed68286..a81eb435d 100644 --- a/test/TopologicalSortTest.cpp +++ b/test/TopologicalSortTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" @@ -6,10 +7,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; // topological sort test in a cyclic graph TEST(TopologicalSortTest, test_1) { diff --git a/test/TransitiveReductionTest.cpp b/test/TransitiveReductionTest.cpp index 4c12b8f0e..babd35520 100644 --- a/test/TransitiveReductionTest.cpp +++ b/test/TransitiveReductionTest.cpp @@ -1,8 +1,8 @@ +#include + #include "CXXGraph/CXXGraph.hpp" #include "gtest/gtest.h" -#include - template using shared = std::shared_ptr; using std::make_shared; diff --git a/test/TypeTraitsTest.hpp b/test/TypeTraitsTest.hpp new file mode 100644 index 000000000..7cad7009c --- /dev/null +++ b/test/TypeTraitsTest.hpp @@ -0,0 +1,144 @@ + +#ifndef test_type_traits_hpp +#define test_type_traits_hpp + +#include "CXXGraph/CXXGraph.hpp" +#include + +template +using shared = std::shared_ptr; + +// test is_node +static_assert(CXXGraph::is_node>::value); +static_assert(CXXGraph::is_node>::value); +static_assert(!CXXGraph::is_node*>::value); +static_assert(!CXXGraph::is_node*>::value); +static_assert(!CXXGraph::is_node>>::value); +static_assert(!CXXGraph::is_node>>::value); + +static_assert(CXXGraph::is_node_v>); +static_assert(CXXGraph::is_node_v>); +static_assert(!CXXGraph::is_node_v*>); +static_assert(!CXXGraph::is_node_v*>); +static_assert(!CXXGraph::is_node_v>>); +static_assert(!CXXGraph::is_node_v>>); + +// test is_node_ptr +static_assert(CXXGraph::is_node_ptr*>::value); +static_assert(CXXGraph::is_node_ptr*>::value); +static_assert(CXXGraph::is_node_ptr>>::value); +static_assert(CXXGraph::is_node_ptr>>::value); + +static_assert(CXXGraph::is_node_ptr_v*>); +static_assert(CXXGraph::is_node_ptr_v*>); +static_assert(CXXGraph::is_node_ptr_v>>); +static_assert(CXXGraph::is_node_ptr_v>>); + +// test is_edge +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(CXXGraph::is_edge>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge*>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); +static_assert(!CXXGraph::is_edge>>::value); + +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(CXXGraph::is_edge_v>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v*>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); +static_assert(!CXXGraph::is_edge_v>>); + +// test is_edge_ptr +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr*>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); +static_assert(CXXGraph::is_edge_ptr>>::value); + +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v*>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); +static_assert(CXXGraph::is_edge_ptr_v>>); + +#endif + diff --git a/test/UndirectedEdgeTest.cpp b/test/UndirectedEdgeTest.cpp index 7b5513838..243a879b1 100644 --- a/test/UndirectedEdgeTest.cpp +++ b/test/UndirectedEdgeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(UndirectedEdgeTest, Constructor_1) { CXXGraph::Node node1("1", 1); @@ -74,8 +74,8 @@ TEST(UndirectedEdgeTest, Bool_data) { // Second constructor CXXGraph::Node node3("3", true); CXXGraph::Node node4("4", false); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::UndirectedEdge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); @@ -97,8 +97,8 @@ TEST(UndirectedEdgeTest, String_data) { // Second constructor CXXGraph::Node node3("3", "On"); CXXGraph::Node node4("4", "Off"); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::UndirectedEdge edge2(2, pairNode); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); diff --git a/test/UndirectedWeightedEdgeTest.cpp b/test/UndirectedWeightedEdgeTest.cpp index 7349df589..3a081a8e8 100644 --- a/test/UndirectedWeightedEdgeTest.cpp +++ b/test/UndirectedWeightedEdgeTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(UndirectedWeightedEdgeTest, Constructor_1) { CXXGraph::Node node1("1", 1); @@ -107,8 +107,8 @@ TEST(UndirectedWeightedEdgeTest, Bool_data) { // Second constructor CXXGraph::Node node3("3", true); CXXGraph::Node node4("4", false); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::UndirectedWeightedEdge edge2(2, pairNode, 7); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); @@ -130,8 +130,8 @@ TEST(UndirectedWeightedEdgeTest, String_data) { // Second constructor CXXGraph::Node node3("3", "On"); CXXGraph::Node node4("4", "Off"); - std::pair *, const CXXGraph::Node *> pairNode( - &node3, &node4); + std::pair *, const CXXGraph::Node *> + pairNode(&node3, &node4); CXXGraph::UndirectedWeightedEdge edge2(2, pairNode, 5); /* ASSERT_EQ(edge2.getNodePair(), pairNode); */ ASSERT_EQ(*(edge2.getNodePair().first), node3); diff --git a/test/UnionFindTest.cpp b/test/UnionFindTest.cpp index e0cd80c6f..53328049d 100644 --- a/test/UnionFindTest.cpp +++ b/test/UnionFindTest.cpp @@ -5,10 +5,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; TEST(UnionFindTest, setFindTest1) { CXXGraph::Node node0("0", 0);