diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml index 254c534..42b9d14 100644 --- a/.github/workflows/format.yaml +++ b/.github/workflows/format.yaml @@ -5,6 +5,7 @@ on: - '*' paths: - .github/workflows/format.yaml + - scripts/format.py - include/** - tests/include/** - tests/source/** diff --git a/.github/workflows/licence.yaml b/.github/workflows/licence.yaml new file mode 100644 index 0000000..0ac69af --- /dev/null +++ b/.github/workflows/licence.yaml @@ -0,0 +1,29 @@ +name: licence +on: + push: + branches: + - '*' + paths: + - .github/workflows/licence.yaml + - scripts/check_licence.py + - include/** + + +jobs: + build: + name: Test code formatting + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Test formatting + shell: bash + run: | + python3 scripts/check_licence.py diff --git a/.gitignore b/.gitignore index ef5c784..b1c8eda 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,8 @@ # documentation files documentation/ +# python temporary files +**/__pycache__/ + # other *tmp*/ diff --git a/LICENSE b/LICENSE index 98f4d3a..3ed4128 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License Copyright (c) 2024 Jakub Musiał +Project: "CPP-GL: General purpose header-only template graph library for C++20 and newer standards." +https://github.com/SpectraL519/cpp-gl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/include/gl/algorithm/breadth_first_search.hpp b/include/gl/algorithm/breadth_first_search.hpp index e4af2d9..f087222 100644 --- a/include/gl/algorithm/breadth_first_search.hpp +++ b/include/gl/algorithm/breadth_first_search.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "constants.hpp" diff --git a/include/gl/algorithm/coloring.hpp b/include/gl/algorithm/coloring.hpp index fdbd974..ab000c3 100644 --- a/include/gl/algorithm/coloring.hpp +++ b/include/gl/algorithm/coloring.hpp @@ -1,7 +1,10 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "detail/bfs_impl.hpp" -#include "gl/types/properties.hpp" namespace gl::algorithm { @@ -32,10 +35,10 @@ template < const bool is_bipartite = detail::bfs_impl( graph, detail::init_range(root_id), - detail::constant_unary_predicate(), // visit pred + detail::constant_unary_predicate(), // visit predicate detail::constant_binary_predicate(), // visit callback [&coloring](const vertex_type& vertex, const edge_type& in_edge) - -> std::optional { // enqueue pred + -> std::optional { // enqueue predicate if (in_edge.is_loop()) return false; diff --git a/include/gl/algorithm/constants.hpp b/include/gl/algorithm/constants.hpp index 0e9e031..2cc6ea6 100644 --- a/include/gl/algorithm/constants.hpp +++ b/include/gl/algorithm/constants.hpp @@ -1,5 +1,11 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once +#include "gl/constants.hpp" + #include namespace gl::algorithm { diff --git a/include/gl/algorithm/deapth_first_search.hpp b/include/gl/algorithm/deapth_first_search.hpp index 5abde2b..0c3d938 100644 --- a/include/gl/algorithm/deapth_first_search.hpp +++ b/include/gl/algorithm/deapth_first_search.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "constants.hpp" diff --git a/include/gl/algorithm/detail/bfs_impl.hpp b/include/gl/algorithm/detail/bfs_impl.hpp index 09e23f6..dc43d7f 100644 --- a/include/gl/algorithm/detail/bfs_impl.hpp +++ b/include/gl/algorithm/detail/bfs_impl.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "common.hpp" diff --git a/include/gl/algorithm/detail/common.hpp b/include/gl/algorithm/detail/common.hpp index 38cf4f1..1150063 100644 --- a/include/gl/algorithm/detail/common.hpp +++ b/include/gl/algorithm/detail/common.hpp @@ -1,7 +1,10 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/algorithm/types.hpp" -#include "gl/graph.hpp" namespace gl::algorithm::detail { diff --git a/include/gl/algorithm/detail/dfs_impl.hpp b/include/gl/algorithm/detail/dfs_impl.hpp index 0f465c0..5400d77 100644 --- a/include/gl/algorithm/detail/dfs_impl.hpp +++ b/include/gl/algorithm/detail/dfs_impl.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "common.hpp" @@ -25,9 +29,11 @@ void dfs_impl( if (not visit_vertex_pred(root_vertex)) return; + // prepare the vertex stack vertex_stack_type vertex_stack; vertex_stack.emplace(root_vertex.id()); + // search the graph while (not vertex_stack.empty()) { const auto vinfo = vertex_stack.top(); vertex_stack.pop(); @@ -75,6 +81,7 @@ void rdfs_impl( visit(vertex, source_id); + // recursively search vertices adjacent to the current vertex const auto vertex_id = vertex.id(); for (const auto& edge : graph.adjacent_edges(vertex_id)) { const auto& incident_vertex = edge.incident_vertex(vertex); diff --git a/include/gl/algorithm/dijkstra.hpp b/include/gl/algorithm/dijkstra.hpp index a0f1672..ff1369a 100644 --- a/include/gl/algorithm/dijkstra.hpp +++ b/include/gl/algorithm/dijkstra.hpp @@ -1,8 +1,11 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "detail/bfs_impl.hpp" #include "gl/graph_utility.hpp" -#include "gl/types/properties.hpp" #include @@ -59,7 +62,6 @@ template < paths.predecessors.at(source_id).emplace(source_id); paths.distances[source_id] = distance_type{}; - constexpr distance_type min_edge_weight_threshold = 0ull; std::optional> negative_edge; detail::pq_bfs_impl( @@ -68,15 +70,15 @@ template < return paths.distances[lhs.id] > paths.distances[rhs.id]; }, detail::init_range(source_id), - detail::constant_unary_predicate(), // visit pred + detail::constant_unary_predicate(), // visit predicate detail::constant_binary_predicate(), // visit callback [&paths, &negative_edge](const vertex_type& vertex, const edge_type& in_edge) - -> std::optional { // enqueue pred + -> std::optional { // enqueue predicate const auto vertex_id = vertex.id(); const auto source_id = in_edge.incident_vertex(vertex).id(); const auto edge_weight = get_weight(in_edge); - if (edge_weight < min_edge_weight_threshold) { + if (edge_weight < constants::zero) { negative_edge = std::cref(in_edge); return std::nullopt; } diff --git a/include/gl/algorithm/mst.hpp b/include/gl/algorithm/mst.hpp index 70d426a..921fc41 100644 --- a/include/gl/algorithm/mst.hpp +++ b/include/gl/algorithm/mst.hpp @@ -1,11 +1,12 @@ -#pragma once +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "constants.hpp" #include "detail/common.hpp" -#include "gl/types/properties.hpp" -#include #include namespace gl::algorithm { @@ -17,11 +18,11 @@ struct mst_descriptor { using weight_type = types::vertex_distance_type; mst_descriptor(const types::size_type n_vertices) { - edges.reserve(n_vertices - 1ull); + edges.reserve(n_vertices - constants::one); } std::vector> edges; - weight_type weight = static_cast(0ll); + weight_type weight = static_cast(constants::zero); }; template < @@ -64,17 +65,16 @@ template < }; // insert the edges adjacent to the root vertex to the queue - const types::id_type root_id = root_id_opt.value_or(0ull); + const types::id_type root_id = root_id_opt.value_or(constants::zero); for (const auto& edge : graph.adjacent_edges(root_id)) edge_queue.emplace(edge, root_id); // mark the root vertex as visited visited[root_id] = true; - types::size_type n_vertices_in_mst = 1ull; + types::size_type n_vertices_in_mst = constants::one; // find the mst - constexpr distance_type min_edge_weight_threshold = 0ull; while (n_vertices_in_mst < n_vertices) { const auto min_edge_info = edge_queue.top(); edge_queue.pop(); @@ -82,7 +82,7 @@ template < const auto& min_edge = min_edge_info.edge.get(); const auto min_weight = get_weight(min_edge); - if (min_weight < min_edge_weight_threshold) + if (min_weight < constants::zero) throw std::invalid_argument(std::format( "[alg::prim_mst] Found an edge with a negative weight: [{}, {} | w={}]", min_edge.first_id(), @@ -92,6 +92,7 @@ template < const auto& dest_vertex_id = get_other_vertex_id(min_edge, min_edge_info.source_id); if (not visited[dest_vertex_id]) { + // add the minimum weight edge to the mst mst.edges.emplace_back(min_edge); mst.weight += min_weight; @@ -99,6 +100,7 @@ template < ++n_vertices_in_mst; } + // enqueue all edges adjacent to the destination vertex if they lead to unvisited verties for (const auto& edge : graph.adjacent_edges(dest_vertex_id)) if (not visited[get_other_vertex_id(edge, dest_vertex_id)]) edge_queue.emplace(edge, dest_vertex_id); diff --git a/include/gl/algorithm/topological_sort.hpp b/include/gl/algorithm/topological_sort.hpp index 2f3b866..03b466c 100644 --- a/include/gl/algorithm/topological_sort.hpp +++ b/include/gl/algorithm/topological_sort.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "detail/bfs_impl.hpp" @@ -39,7 +43,7 @@ template < detail::bfs_impl( graph, source_vertex_list, - detail::constant_unary_predicate(), // visit pred + detail::constant_unary_predicate(), // visit predicate [&topological_order]( const vertex_type& vertex, const types::id_type source_id ) { // visit callback @@ -47,7 +51,7 @@ template < return true; }, [&vertex_in_deg_list](const vertex_type& vertex, const edge_type& in_edge) - -> std::optional { // enqueue pred + -> std::optional { // enqueue predicate if (in_edge.is_loop()) return false; return --vertex_in_deg_list[vertex.id()] == constants::default_size; @@ -59,7 +63,7 @@ template < if (topological_order.size() != graph.n_vertices()) return std::nullopt; - return topological_order; + return topological_order_opt; } } // namespace gl::algorithm diff --git a/include/gl/algorithm/types.hpp b/include/gl/algorithm/types.hpp index 5540bb6..d5a7721 100644 --- a/include/gl/algorithm/types.hpp +++ b/include/gl/algorithm/types.hpp @@ -1,9 +1,12 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/graph_utility.hpp" #include -#include namespace gl { diff --git a/include/gl/algorithms.hpp b/include/gl/algorithms.hpp index 91adb44..a25a40e 100644 --- a/include/gl/algorithms.hpp +++ b/include/gl/algorithms.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "algorithm/breadth_first_search.hpp" diff --git a/include/gl/attributes/force_inline.hpp b/include/gl/attributes/force_inline.hpp index 6dcde1f..3ed51c1 100644 --- a/include/gl/attributes/force_inline.hpp +++ b/include/gl/attributes/force_inline.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #ifndef gl_attr_force_inline diff --git a/include/gl/constants.hpp b/include/gl/constants.hpp index 33c78fe..682e7fa 100644 --- a/include/gl/constants.hpp +++ b/include/gl/constants.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "types/types.hpp" @@ -11,6 +15,5 @@ inline constexpr types::size_type two{2ull}; inline constexpr types::size_type default_size{zero}; inline constexpr types::size_type begin_idx{zero}; inline constexpr types::id_type initial_id{zero}; -inline constexpr types::size_type one_element{one}; } // namespace gl::constants diff --git a/include/gl/decl/graph_traits.hpp b/include/gl/decl/graph_traits.hpp index 8bcb418..cda02d8 100644 --- a/include/gl/decl/graph_traits.hpp +++ b/include/gl/decl/graph_traits.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/types/type_traits.hpp" diff --git a/include/gl/decl/impl_tags.hpp b/include/gl/decl/impl_tags.hpp index 77acb6c..8240764 100644 --- a/include/gl/decl/impl_tags.hpp +++ b/include/gl/decl/impl_tags.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/edge_tags.hpp" diff --git a/include/gl/edge_descriptor.hpp b/include/gl/edge_descriptor.hpp index 5bb6d34..ee7c789 100644 --- a/include/gl/edge_descriptor.hpp +++ b/include/gl/edge_descriptor.hpp @@ -1,17 +1,13 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once -#include "attributes/force_inline.hpp" #include "edge_tags.hpp" #include "io/format.hpp" -#include "types/type_traits.hpp" -#include "types/types.hpp" #include "vertex_descriptor.hpp" -#include -#include -#include -#include - namespace gl { template < @@ -87,6 +83,7 @@ class edge_descriptor { return this->second().id(); } + // returns the `other` vertex or throws error if the given vertex is not incident with the edge [[nodiscard]] const vertex_type& incident_vertex(const vertex_type& vertex) const { if (&vertex == &this->_vertices.first) return this->_vertices.second; @@ -103,10 +100,12 @@ class edge_descriptor { return &vertex == &this->_vertices.first or &vertex == &this->_vertices.second; } + // true if the given vertex is the `source` of the edge [[nodiscard]] gl_attr_force_inline bool is_incident_from(const vertex_type& vertex) const { return directional_tag::is_incident_from(*this, vertex); } + // true if the given vertex is the `destination` of the edge [[nodiscard]] gl_attr_force_inline bool is_incident_to(const vertex_type& vertex) const { return directional_tag::is_incident_to(*this, vertex); } diff --git a/include/gl/edge_tags.hpp b/include/gl/edge_tags.hpp index dd5a342..c136459 100644 --- a/include/gl/edge_tags.hpp +++ b/include/gl/edge_tags.hpp @@ -1,11 +1,13 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" #include "types/properties.hpp" #include "types/type_traits.hpp" -#include - namespace gl { struct directed_t; diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index a4d6b07..e882342 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -1,15 +1,15 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "constants.hpp" -#include "gl/attributes/force_inline.hpp" #include "graph_traits.hpp" #include "impl/impl_tags.hpp" -#include "io.hpp" +#include "io/stream_options_manipulator.hpp" #include "types/iterator_range.hpp" -#include "types/type_traits.hpp" -#include "types/types.hpp" -#include #include namespace gl { @@ -47,7 +47,7 @@ class graph { graph(const types::size_type n_vertices) : _impl(n_vertices) { this->_vertices.reserve(n_vertices); - for (auto vertex_id = constants::initial_id; vertex_id < n_vertices; vertex_id++) + for (auto vertex_id = constants::initial_id; vertex_id < n_vertices; ++vertex_id) this->_vertices.push_back(detail::make_vertex(vertex_id)); } @@ -116,7 +116,7 @@ class graph { this->_impl.add_vertices(n); this->_vertices.reserve(this->n_vertices() + n); - for (types::size_type _ = constants::begin_idx; _ < n; _++) + for (types::size_type _ = constants::begin_idx; _ < n; ++_) this->_vertices.push_back(detail::make_vertex(this->n_vertices())); } @@ -156,7 +156,7 @@ class graph { template > VertexRefRange> void remove_vertices_from(const VertexRefRange& vertex_ref_range) { - // can be replaced with std::grater for C++26 + // TODO [C++26]: replace with std::greater struct vertex_ref_greater_comparator { [[nodiscard]] bool operator()( const types::const_ref_wrap& lhs, @@ -178,40 +178,36 @@ class graph { [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const vertex_type& vertex) const { this->_verify_vertex(vertex); - return this->_impl.in_degree(vertex); + return this->_impl.in_degree(vertex.id()); } [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const types::id_type vertex_id ) const { - return this->_impl.in_degree(this->get_vertex(vertex_id)); + this->_verify_vertex_id(vertex_id); + return this->_impl.in_degree(vertex_id); } [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const vertex_type& vertex ) const { this->_verify_vertex(vertex); - return this->_impl.out_degree(vertex); + return this->_impl.out_degree(vertex.id()); } [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const types::id_type vertex_id ) const { - return this->_impl.out_degree(this->get_vertex(vertex_id)); + this->_verify_vertex_id(vertex_id); + return this->_impl.out_degree(vertex_id); } [[nodiscard]] gl_attr_force_inline types::size_type degree(const vertex_type& vertex) const { - if constexpr (type_traits::is_directed_v) - return this->_impl.in_degree(vertex) + this->_impl.out_degree(vertex); - else - return this->out_degree(vertex); + this->_verify_vertex(vertex); + return this->_impl.degree(vertex.id()); } [[nodiscard]] gl_attr_force_inline types::size_type degree(const types::id_type vertex_id ) const { - if constexpr (type_traits::is_directed_v) { - const auto& vertex = this->get_vertex(vertex_id); - return this->_impl.in_degree(vertex) + this->_impl.out_degree(vertex); - } - else - return this->_impl.out_degree(this->get_vertex(vertex_id)); + this->_verify_vertex_id(vertex_id); + return this->_impl.degree(vertex_id); } // --- edge methods --- @@ -483,7 +479,7 @@ class graph { std::for_each( std::next(std::begin(this->_vertices), vertex_id), this->_vertices.end(), - [](auto& v) { v->_id--; } + [](auto& v) { --v->_id; } ); } @@ -599,7 +595,7 @@ class graph { else { // read vertex properties and use them to initialze the vertices std::vector vertex_properties(n_vertices); - for (types::size_type i = constants::begin_idx; i < n_vertices; i++) + for (types::size_type i = constants::begin_idx; i < n_vertices; ++i) is >> vertex_properties[i]; this->add_vertices_with(vertex_properties); } @@ -621,7 +617,7 @@ class graph { types::id_type first_id, second_id; edge_properties_type properties; - for (types::size_type i = constants::begin_idx; i < n_edges; i++) { + for (types::size_type i = constants::begin_idx; i < n_edges; ++i) { is >> first_id >> second_id >> properties; this->add_edge(first_id, second_id, properties); } @@ -631,7 +627,7 @@ class graph { // read the edges types::id_type first_id, second_id; - for (types::size_type i = constants::begin_idx; i < n_edges; i++) { + for (types::size_type i = constants::begin_idx; i < n_edges; ++i) { is >> first_id >> second_id; this->add_edge(first_id, second_id); } diff --git a/include/gl/graph_file_io.hpp b/include/gl/graph_file_io.hpp index a07973e..c75e2c0 100644 --- a/include/gl/graph_file_io.hpp +++ b/include/gl/graph_file_io.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "graph_utility.hpp" diff --git a/include/gl/graph_io.hpp b/include/gl/graph_io.hpp index cd8df96..8957a79 100644 --- a/include/gl/graph_io.hpp +++ b/include/gl/graph_io.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "io/stream_options_manipulator.hpp" diff --git a/include/gl/graph_traits.hpp b/include/gl/graph_traits.hpp index 7fd7175..2f3429a 100644 --- a/include/gl/graph_traits.hpp +++ b/include/gl/graph_traits.hpp @@ -1,12 +1,12 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "decl/graph_traits.hpp" #include "decl/impl_tags.hpp" #include "edge_descriptor.hpp" -#include "types/traits/cache_mode.hpp" -#include "vertex_descriptor.hpp" - -#include namespace gl { @@ -57,15 +57,25 @@ using undirected_graph_traits = namespace type_traits { -template +template concept c_list_graph_traits = - c_instantiation_of - and std::same_as; + c_instantiation_of + and std::same_as; -template +template concept c_matrix_graph_traits = - c_instantiation_of - and std::same_as; + c_instantiation_of + and std::same_as; + +template +concept c_directed_graph_traits = + c_instantiation_of + and std::same_as; + +template +concept c_undirected_graph_traits = + c_instantiation_of + and std::same_as; } // namespace type_traits diff --git a/include/gl/graph_utility.hpp b/include/gl/graph_utility.hpp index 79d37b0..da25266 100644 --- a/include/gl/graph_utility.hpp +++ b/include/gl/graph_utility.hpp @@ -1,10 +1,14 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "graph.hpp" namespace gl { -// --- general utility --- +// --- general graph utility --- namespace type_traits { diff --git a/include/gl/impl/adjacency_list.hpp b/include/gl/impl/adjacency_list.hpp index 4be44b0..46f67b2 100644 --- a/include/gl/impl/adjacency_list.hpp +++ b/include/gl/impl/adjacency_list.hpp @@ -1,14 +1,15 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once -#include "gl/attributes/force_inline.hpp" #include "gl/constants.hpp" #include "gl/types/dereferencing_iterator.hpp" #include "gl/types/iterator_range.hpp" #include "gl/types/types.hpp" #include "specialized/adjacency_list.hpp" -#include - namespace gl::impl { template @@ -58,17 +59,23 @@ class adjacency_list { inline void add_vertices(const types::size_type n) { this->_list.reserve(this->n_vertices() + n); - for (types::size_type _ = constants::begin_idx; _ < n; _++) + for (types::size_type _ = constants::begin_idx; _ < n; ++_) this->_list.push_back(edge_list_type{}); } - [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const vertex_type& vertex) const { - return specialized_impl::in_degree(*this, vertex); + [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const types::id_type vertex_id + ) const { + return specialized_impl::in_degree(*this, vertex_id); + } + + [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const types::id_type vertex_id + ) const { + return specialized_impl::out_degree(*this, vertex_id); } - [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const vertex_type& vertex + [[nodiscard]] gl_attr_force_inline types::size_type degree(const types::id_type vertex_id ) const { - return this->_list[vertex.id()].size(); + return specialized_impl::degree(*this, vertex_id); } gl_attr_force_inline void remove_vertex(const vertex_type& vertex) { @@ -114,6 +121,7 @@ class adjacency_list { )) return false; + // find the edge by address const auto& adjacent_edges = this->_list[first_id]; return std::ranges::find( adjacent_edges, &edge, typename specialized_impl::address_projection{} diff --git a/include/gl/impl/adjacency_matrix.hpp b/include/gl/impl/adjacency_matrix.hpp index 929c19c..a1eea4c 100644 --- a/include/gl/impl/adjacency_matrix.hpp +++ b/include/gl/impl/adjacency_matrix.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -7,8 +11,6 @@ #include "gl/types/types.hpp" #include "specialized/adjacency_matrix.hpp" -#include - namespace gl::impl { template @@ -74,30 +76,30 @@ class adjacency_matrix { std::generate_n(std::back_inserter(row), n, _make_null_edge); } - for (types::size_type _ = constants::begin_idx; _ < n; _++) { + for (types::size_type _ = constants::begin_idx; _ < n; ++_) { auto& new_row = this->_matrix.emplace_back(); new_row.reserve(new_n_vertices); std::generate_n(std::back_inserter(new_row), new_n_vertices, _make_null_edge); } } - [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const vertex_type& vertex) const { - return std::ranges::count_if( - this->_matrix, - [](const auto& edge) { return edge != nullptr; }, - [col = vertex.id()](const auto& row) -> const edge_ptr_type& { return row[col]; } - ); + [[nodiscard]] gl_attr_force_inline types::size_type in_degree(const types::id_type vertex_id + ) const { + return specialized_impl::in_degree(*this, vertex_id); + } + + [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const types::id_type vertex_id + ) const { + return specialized_impl::out_degree(*this, vertex_id); } - [[nodiscard]] gl_attr_force_inline types::size_type out_degree(const vertex_type& vertex + [[nodiscard]] gl_attr_force_inline types::size_type degree(const types::id_type vertex_id ) const { - return std::ranges::count_if(this->_matrix[vertex.id()], [](const auto& edge) { - return edge != nullptr; - }); + return specialized_impl::degree(*this, vertex_id); } gl_attr_force_inline void remove_vertex(const vertex_type& vertex) { - specialized_impl::remove_vertex(*this, vertex); + specialized_impl::remove_vertex(*this, vertex.id()); } // --- edge methods --- diff --git a/include/gl/impl/impl_tags.hpp b/include/gl/impl/impl_tags.hpp index cf4657c..de53a6d 100644 --- a/include/gl/impl/impl_tags.hpp +++ b/include/gl/impl/impl_tags.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "adjacency_list.hpp" diff --git a/include/gl/impl/specialized/adjacency_list.hpp b/include/gl/impl/specialized/adjacency_list.hpp index 53985cf..a57b05f 100644 --- a/include/gl/impl/specialized/adjacency_list.hpp +++ b/include/gl/impl/specialized/adjacency_list.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/decl/impl_tags.hpp" @@ -5,6 +9,7 @@ #include #include #include +#include namespace gl::impl { @@ -23,6 +28,7 @@ requires std::is_invocable_r_v< [[nodiscard]] typename AdjacencyList::edge_iterator_type::iterator_type strict_find( typename AdjacencyList::edge_list_type& edge_set, const typename AdjacencyList::edge_type* edge ) { + // find the edge by address const auto it = std::ranges::find(edge_set, edge, AddressProjection{}); if (it == edge_set.end()) throw std::invalid_argument(std::format( @@ -48,22 +54,20 @@ struct directed_adjacency_list { using edge_iterator_type = typename impl_type::edge_iterator_type::iterator_type; struct address_projection { - auto operator()(const edge_type& edge) { + [[nodiscard]] gl_attr_force_inline auto operator()(const edge_type& edge) { return &edge; } - auto operator()(const edge_ptr_type& edge) { + [[nodiscard]] gl_attr_force_inline auto operator()(const edge_ptr_type& edge) { return edge.get(); } }; [[nodiscard]] static types::size_type in_degree( - const impl_type& self, const vertex_type& vertex + const impl_type& self, const types::id_type vertex_id ) { types::size_type in_deg = constants::default_size; - const auto vertex_id = vertex.id(); - - for (types::id_type i = constants::initial_id; i < self._list.size(); i++) { + for (types::id_type i = constants::initial_id; i < self._list.size(); ++i) { const auto& adj_edges = self._list[i]; if (adj_edges.empty()) continue; @@ -76,10 +80,23 @@ struct directed_adjacency_list { return in_deg; } + [[nodiscard]] gl_attr_force_inline static types::size_type out_degree( + const impl_type& self, const types::id_type vertex_id + ) { + return self._list[vertex_id].size(); + } + + [[nodiscard]] gl_attr_force_inline static types::size_type degree( + const impl_type& self, const types::id_type vertex_id + ) { + return in_degree(self, vertex_id) + out_degree(self, vertex_id); + } + static void remove_vertex(impl_type& self, const vertex_type& vertex) { const auto vertex_id = vertex.id(); - for (types::id_type i = constants::initial_id; i < self._list.size(); i++) { + // remove all edges incident to the vertex + for (types::id_type i = constants::initial_id; i < self._list.size(); ++i) { auto& adj_edges = self._list[i]; if (i == vertex_id or adj_edges.empty()) continue; @@ -91,6 +108,7 @@ struct directed_adjacency_list { adj_edges.erase(rem_subrange.begin(), rem_subrange.end()); } + // remove the list of edges incident from the vertex entirely self._n_unique_edges -= self._list[vertex_id].size(); self._list.erase(std::next(std::begin(self._list), vertex_id)); } @@ -98,7 +116,7 @@ struct directed_adjacency_list { static const edge_type& add_edge(impl_type& self, edge_ptr_type edge) { auto& adjacent_edges_first = self._list[edge->first_id()]; adjacent_edges_first.push_back(std::move(edge)); - self._n_unique_edges++; + ++self._n_unique_edges; return *adjacent_edges_first.back(); } @@ -131,7 +149,7 @@ struct directed_adjacency_list { static void remove_edge(impl_type& self, const edge_type& edge) { auto& adj_edges = self._list.at(edge.first_id()); adj_edges.erase(detail::strict_find(adj_edges, &edge)); - self._n_unique_edges--; + --self._n_unique_edges; } }; @@ -146,31 +164,48 @@ struct undirected_adjacency_list { using edge_iterator_type = typename impl_type::edge_iterator_type::iterator_type; struct address_projection { - auto operator()(const edge_type& edge) { + [[nodiscard]] gl_attr_force_inline auto operator()(const edge_type& edge) { return &edge; } - auto operator()(const edge_ptr_type& edge) { + [[nodiscard]] gl_attr_force_inline auto operator()(const edge_ptr_type& edge) { return edge.get(); } }; [[nodiscard]] gl_attr_force_inline static types::size_type in_degree( - const impl_type& self, const vertex_type& vertex + const impl_type& self, const types::id_type vertex_id + ) { + return degree(self, vertex_id); + } + + [[nodiscard]] gl_attr_force_inline static types::size_type out_degree( + const impl_type& self, const types::id_type vertex_id ) { - return self._list[vertex.id()].size(); + return degree(self, vertex_id); + } + + [[nodiscard]] static types::size_type degree( + const impl_type& self, const types::id_type vertex_id + ) { + types::size_type degree = constants::default_size; + for (const auto& edge : self._list[vertex_id]) + degree += constants::one + static_cast(edge->is_loop()); + return degree; } static void remove_vertex(impl_type& self, const vertex_type& vertex) { const auto vertex_id = vertex.id(); std::unordered_set incident_vertex_id_set; + // select the vertices incident with the removed vertex for (const auto& edge : self._list[vertex_id]) { if (edge->is_loop()) continue; // will be removed with the vertex's list incident_vertex_id_set.insert(edge->incident_vertex(vertex).id()); } + // remove all edges incident with the vertex (scan only the selected vertices) for (const auto& incident_vertex_id : incident_vertex_id_set) { auto& adj_edges = self._list[incident_vertex_id]; const auto rem_subrange = std::ranges::remove_if( @@ -179,6 +214,7 @@ struct undirected_adjacency_list { adj_edges.erase(rem_subrange.begin(), rem_subrange.end()); } + // remove the list of edges incident from the vertex entirely self._n_unique_edges -= self._list[vertex_id].size(); self._list.erase(std::next(std::begin(self._list), vertex_id)); } @@ -190,7 +226,7 @@ struct undirected_adjacency_list { self._list[edge->second_id()].push_back(edge); adjacent_edges_first.push_back(std::move(edge)); - self._n_unique_edges++; + ++self._n_unique_edges; return *adjacent_edges_first.back(); } @@ -233,16 +269,14 @@ struct undirected_adjacency_list { adj_edges_first.erase( detail::strict_find(adj_edges_first, &edge) ); - // if the edge was found in the first list, it will be in the second list + // if the edge was found in the first list, it will also be present in the second list adj_edges_second.erase(std::ranges::find(adj_edges_second, &edge, address_projection{}) ); } - self._n_unique_edges--; + --self._n_unique_edges; } }; -// common utility - template AdjacencyList> struct list_impl_traits { using type = void; diff --git a/include/gl/impl/specialized/adjacency_matrix.hpp b/include/gl/impl/specialized/adjacency_matrix.hpp index 4f8e404..8b05574 100644 --- a/include/gl/impl/specialized/adjacency_matrix.hpp +++ b/include/gl/impl/specialized/adjacency_matrix.hpp @@ -1,8 +1,13 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/decl/impl_tags.hpp" #include +#include namespace gl::impl { @@ -17,6 +22,7 @@ template AdjacencyMatrix> [[nodiscard]] typename AdjacencyMatrix::edge_ptr_type& strict_get( typename AdjacencyMatrix::matrix_type& matrix, const typename AdjacencyMatrix::edge_type* edge ) { + // get the edge and validate the address auto& matrix_element = matrix.at(edge->first_id()).at(edge->second_id()); if (edge != matrix_element.get()) throw std::invalid_argument(std::format( @@ -51,11 +57,33 @@ struct directed_adjacency_matrix { using edge_type = typename impl_type::edge_type; using edge_ptr_type = typename impl_type::edge_ptr_type; - static void remove_vertex(impl_type& self, const vertex_type& vertex) { - const auto vertex_id = vertex.id(); + [[nodiscard]] gl_attr_force_inline static types::size_type in_degree( + const impl_type& self, const types::id_type vertex_id + ) { + return std::ranges::count_if( + self._matrix, + [](const auto& edge) { return edge != nullptr; }, + [vertex_id](const auto& row) -> const edge_ptr_type& { return row[vertex_id]; } + ); + } + + [[nodiscard]] gl_attr_force_inline static types::size_type out_degree( + const impl_type& self, const types::id_type vertex_id + ) { + return std::ranges::count_if(self._matrix[vertex_id], [](const auto& edge) { + return edge != nullptr; + }); + } + + [[nodiscard]] gl_attr_force_inline static types::size_type degree( + const impl_type& self, const types::id_type vertex_id + ) { + return in_degree(self, vertex_id) + out_degree(self, vertex_id); + } + static void remove_vertex(impl_type& self, const types::id_type vertex_id) { self._n_unique_edges -= self.adjacent_edges(vertex_id).distance(); - self._matrix.erase(std::next(std::begin(self._matrix), vertex.id())); + self._matrix.erase(std::next(std::begin(self._matrix), vertex_id)); for (auto& row : self._matrix) { const auto vertex_it = std::next(std::begin(row), vertex_id); @@ -69,7 +97,7 @@ struct directed_adjacency_matrix { auto& matrix_element = self._matrix[edge->first_id()][edge->second_id()]; matrix_element = std::move(edge); - self._n_unique_edges++; + ++self._n_unique_edges; return *matrix_element; } @@ -90,7 +118,7 @@ struct directed_adjacency_matrix { static inline void remove_edge(impl_type& self, const edge_type& edge) { detail::strict_get(self._matrix, &edge) = nullptr; - self._n_unique_edges--; + --self._n_unique_edges; } }; @@ -102,11 +130,31 @@ struct undirected_adjacency_matrix { using edge_type = typename impl_type::edge_type; using edge_ptr_type = typename impl_type::edge_ptr_type; - static void remove_vertex(impl_type& self, const vertex_type& vertex) { - const auto vertex_id = vertex.id(); + [[nodiscard]] gl_attr_force_inline static types::size_type in_degree( + const impl_type& self, const types::id_type vertex_id + ) { + return degree(self, vertex_id); + } + + [[nodiscard]] gl_attr_force_inline static types::size_type out_degree( + const impl_type& self, const types::id_type vertex_id + ) { + return degree(self, vertex_id); + } + [[nodiscard]] gl_attr_force_inline static types::size_type degree( + const impl_type& self, const types::id_type vertex_id + ) { + types::size_type degree = constants::default_size; + for (const auto& edge : self._matrix[vertex_id]) + if (edge) + degree += constants::one + static_cast(edge->is_loop()); + return degree; + } + + static void remove_vertex(impl_type& self, const types::id_type vertex_id) { self._n_unique_edges -= self.adjacent_edges(vertex_id).distance(); - self._matrix.erase(std::next(std::begin(self._matrix), vertex.id())); + self._matrix.erase(std::next(std::begin(self._matrix), vertex_id)); for (auto& row : self._matrix) row.erase(std::next(std::begin(row), vertex_id)); @@ -123,7 +171,7 @@ struct undirected_adjacency_matrix { auto& matrix_element = self._matrix.at(first_id).at(second_id); matrix_element = edge; - self._n_unique_edges++; + ++self._n_unique_edges; return *matrix_element; } @@ -153,15 +201,14 @@ struct undirected_adjacency_matrix { const auto second_id = edge.second_id(); detail::strict_get(self._matrix, &edge) = nullptr; - // if the edge was found in the first matrix cell, it will be in the second matrix cell + // if the edge was found in the first matrix cell, + // it will also be present in the second matrix cell self._matrix[second_id][first_id] = nullptr; } - self._n_unique_edges--; + --self._n_unique_edges; } }; -// common utility - template AdjacencyMatrix> struct matrix_impl_traits { using type = void; diff --git a/include/gl/io.hpp b/include/gl/io.hpp index f06f5d1..a699162 100644 --- a/include/gl/io.hpp +++ b/include/gl/io.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "io/format.hpp" diff --git a/include/gl/io/format.hpp b/include/gl/io/format.hpp index b00786a..72fe8e9 100644 --- a/include/gl/io/format.hpp +++ b/include/gl/io/format.hpp @@ -1,15 +1,16 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" #include "gl/types/traits/concepts.hpp" -#include -#include - namespace gl::io { /* -Custom format functions +Custom format functions (casts to types compatible with std::formatter) Not std::formatter overloads to avoid collision with user defined std::formatter overloads */ diff --git a/include/gl/io/stream_options_manipulator.hpp b/include/gl/io/stream_options_manipulator.hpp index 179c793..27b3c23 100644 --- a/include/gl/io/stream_options_manipulator.hpp +++ b/include/gl/io/stream_options_manipulator.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" diff --git a/include/gl/topologies.hpp b/include/gl/topologies.hpp index dc2b5cd..ef044c2 100644 --- a/include/gl/topologies.hpp +++ b/include/gl/topologies.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "topology/binary_tree.hpp" diff --git a/include/gl/topology/binary_tree.hpp b/include/gl/topology/binary_tree.hpp index b9bbd3c..3f17d1e 100644 --- a/include/gl/topology/binary_tree.hpp +++ b/include/gl/topology/binary_tree.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -15,15 +19,13 @@ namespace detail { ); } -using vertex_id_range = std::vector; +constexpr types::size_type min_non_trivial_bin_tree_depth = constants::two; } // namespace detail template [[nodiscard]] GraphType complete_binary_tree(const types::size_type depth) { - constexpr types::size_type min_non_trivial_depth = 2ull; - - if (depth < min_non_trivial_depth) + if (depth < detail::min_non_trivial_bin_tree_depth) return GraphType{depth}; constexpr types::size_type base = constants::two; @@ -35,10 +37,10 @@ template const auto n_source_vertices = n_vertices - util::upow(base, i_end); - for (types::id_type source_id = constants::zero; source_id < n_source_vertices; source_id++) { + for (types::id_type source_id = constants::zero; source_id < n_source_vertices; ++source_id) { const auto destination_ids = detail::get_binary_destination_ids(source_id); graph.add_edges_from( - source_id, detail::vertex_id_range{destination_ids.first, destination_ids.second} + source_id, std::vector{destination_ids.first, destination_ids.second} ); } @@ -48,9 +50,7 @@ template template [[nodiscard]] GraphType bidirectional_complete_binary_tree(const types::size_type depth) { if constexpr (type_traits::is_directed_v) { - constexpr types::size_type min_non_trivial_depth = 2ull; - - if (depth < min_non_trivial_depth) + if (depth < detail::min_non_trivial_bin_tree_depth) return GraphType{depth}; constexpr types::size_type base = constants::two; @@ -63,10 +63,11 @@ template const auto n_source_vertices = n_vertices - util::upow(base, i_end); for (types::id_type source_id = constants::zero; source_id < n_source_vertices; - source_id++) { + ++source_id) { const auto destination_ids = detail::get_binary_destination_ids(source_id); graph.add_edges_from( - source_id, detail::vertex_id_range{destination_ids.first, destination_ids.second} + source_id, + std::vector{destination_ids.first, destination_ids.second} ); graph.add_edge(destination_ids.first, source_id); graph.add_edge(destination_ids.second, source_id); diff --git a/include/gl/topology/bipartite.hpp b/include/gl/topology/bipartite.hpp index 24c60bb..dbc5316 100644 --- a/include/gl/topology/bipartite.hpp +++ b/include/gl/topology/bipartite.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -12,9 +16,9 @@ template const auto n_vertices = n_vertices_a + n_vertices_b; GraphType graph{n_vertices}; - for (types::id_type source_id = constants::initial_id; source_id < n_vertices_a; source_id++) { + for (types::id_type source_id = constants::initial_id; source_id < n_vertices_a; ++source_id) { for (types::id_type destination_id = n_vertices_a; destination_id < n_vertices; - destination_id++) { + ++destination_id) { graph.add_edge(source_id, destination_id); if constexpr (type_traits::is_directed_v) graph.add_edge(destination_id, source_id); diff --git a/include/gl/topology/clique.hpp b/include/gl/topology/clique.hpp index a4bc0a1..3c7f552 100644 --- a/include/gl/topology/clique.hpp +++ b/include/gl/topology/clique.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -9,9 +13,9 @@ template [[nodiscard]] GraphType clique(const types::size_type n_vertices) { GraphType graph{n_vertices}; - for (types::id_type source_id = constants::initial_id; source_id < n_vertices; source_id++) { + for (types::id_type source_id = constants::initial_id; source_id < n_vertices; ++source_id) { for (types::id_type destination_id = constants::initial_id; destination_id < source_id; - destination_id++) { + ++destination_id) { graph.add_edge(source_id, destination_id); if constexpr (type_traits::is_directed_v) graph.add_edge(destination_id, source_id); diff --git a/include/gl/topology/cycle.hpp b/include/gl/topology/cycle.hpp index f4d9998..5635ea4 100644 --- a/include/gl/topology/cycle.hpp +++ b/include/gl/topology/cycle.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -9,7 +13,7 @@ template [[nodiscard]] GraphType cycle(const types::size_type n_vertices) { GraphType graph{n_vertices}; - for (types::id_type source_id = constants::initial_id; source_id < n_vertices; source_id++) + for (types::id_type source_id = constants::initial_id; source_id < n_vertices; ++source_id) graph.add_edge(source_id, (source_id + constants::one) % n_vertices); return graph; @@ -21,7 +25,7 @@ template GraphType graph{n_vertices}; for (types::id_type source_id = constants::initial_id; source_id < n_vertices; - source_id++) { + ++source_id) { const auto destination_id = (source_id + constants::one) % n_vertices; graph.add_edge(source_id, destination_id); graph.add_edge(destination_id, source_id); diff --git a/include/gl/topology/path.hpp b/include/gl/topology/path.hpp index d888e69..474016a 100644 --- a/include/gl/topology/path.hpp +++ b/include/gl/topology/path.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/constants.hpp" @@ -10,7 +14,7 @@ template GraphType graph{n_vertices}; for (types::id_type source_id = constants::initial_id; source_id < n_vertices - constants::one; - source_id++) + ++source_id) graph.add_edge(source_id, source_id + constants::one); return graph; @@ -23,7 +27,7 @@ template for (types::id_type source_id = constants::initial_id; source_id < n_vertices - constants::one; - source_id++) { + ++source_id) { const auto destination_id = source_id + constants::one; graph.add_edge(source_id, destination_id); graph.add_edge(destination_id, source_id); diff --git a/include/gl/types/dereferencing_iterator.hpp b/include/gl/types/dereferencing_iterator.hpp index af6c06f..f1c9ce3 100644 --- a/include/gl/types/dereferencing_iterator.hpp +++ b/include/gl/types/dereferencing_iterator.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -9,6 +13,12 @@ namespace gl { namespace types { +/* +A forward iterator wrapper for pointer value type iterators +The dereference operator dereferences the underlying operator + and then the held pointer object +*/ + template requires(type_traits::c_strong_ptr) class dereferencing_iterator { @@ -45,20 +55,20 @@ class dereferencing_iterator { } inline dereferencing_iterator& operator++() { - this->_it++; + ++this->_it; return *this; } inline dereferencing_iterator operator++(int) { dereferencing_iterator tmp = *this; - this->_it++; + ++this->_it; return tmp; } inline dereferencing_iterator& operator--() requires(std::bidirectional_iterator) { - this->_it--; + --this->_it; return *this; } @@ -66,7 +76,7 @@ class dereferencing_iterator { requires(std::bidirectional_iterator) { dereferencing_iterator tmp = *this; - this->_it--; + --this->_it; return tmp; } diff --git a/include/gl/types/iterator_range.hpp b/include/gl/types/iterator_range.hpp index e45171f..b792856 100644 --- a/include/gl/types/iterator_range.hpp +++ b/include/gl/types/iterator_range.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -21,6 +25,11 @@ namespace gl { namespace types { +/* +A begin, end iterator holder class used as a view to the underlying collection. +Designed to be compatible with the range-based loops and std algorithms. +*/ + template < std::forward_iterator Iterator, type_traits::c_cache_mode CacheMode = _GL_IT_RANGE_DEFAULT_CACHE_MODE> diff --git a/include/gl/types/non_null_iterator.hpp b/include/gl/types/non_null_iterator.hpp index e735468..5347aef 100644 --- a/include/gl/types/non_null_iterator.hpp +++ b/include/gl/types/non_null_iterator.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -9,6 +13,12 @@ namespace gl { namespace types { +/* +A forward iterator wrapper for pointer value type iterators +The increment/decrement operators move the underlying iterator + to the next/previous non-null element within the specified bounds +*/ + template requires(type_traits::c_strong_ptr) class non_null_iterator { @@ -30,6 +40,7 @@ class non_null_iterator { } non_null_iterator(iterator_type begin, iterator_type current, iterator_type end) + requires(std::bidirectional_iterator) : _begin(begin), _current(current), _end(end) { if (this->_current != this->_end and not *this->_current) this->_skip_null_elements_forward(); diff --git a/include/gl/types/properties.hpp b/include/gl/types/properties.hpp index 8063c55..cc7f3e5 100644 --- a/include/gl/types/properties.hpp +++ b/include/gl/types/properties.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" diff --git a/include/gl/types/traits/cache_mode.hpp b/include/gl/types/traits/cache_mode.hpp index 158edcc..8e417f0 100644 --- a/include/gl/types/traits/cache_mode.hpp +++ b/include/gl/types/traits/cache_mode.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "concepts.hpp" diff --git a/include/gl/types/traits/concepts.hpp b/include/gl/types/traits/concepts.hpp index 73a9972..06a4943 100644 --- a/include/gl/types/traits/concepts.hpp +++ b/include/gl/types/traits/concepts.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/types/types.hpp" diff --git a/include/gl/types/traits/type_extractors.hpp b/include/gl/types/traits/type_extractors.hpp index 79c2081..ae0b50a 100644 --- a/include/gl/types/traits/type_extractors.hpp +++ b/include/gl/types/traits/type_extractors.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "concepts.hpp" diff --git a/include/gl/types/type_traits.hpp b/include/gl/types/type_traits.hpp index fa45e92..9fafae9 100644 --- a/include/gl/types/type_traits.hpp +++ b/include/gl/types/type_traits.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "traits/cache_mode.hpp" diff --git a/include/gl/types/types.hpp b/include/gl/types/types.hpp index 24e3119..fdf067b 100644 --- a/include/gl/types/types.hpp +++ b/include/gl/types/types.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include diff --git a/include/gl/util/enum.hpp b/include/gl/util/enum.hpp index a9f0e7e..811ebfd 100644 --- a/include/gl/util/enum.hpp +++ b/include/gl/util/enum.hpp @@ -1,3 +1,7 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -6,6 +10,7 @@ namespace gl::util { +// TODO [C++23]: replace with std::to_underlying template requires(std::is_enum_v) [[nodiscard]] gl_attr_force_inline constexpr std::underlying_type_t to_underlying(Enum e) { diff --git a/include/gl/util/pow.hpp b/include/gl/util/pow.hpp index 3aa02ad..7a2e93f 100644 --- a/include/gl/util/pow.hpp +++ b/include/gl/util/pow.hpp @@ -1,6 +1,9 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once -#include "gl/attributes/force_inline.hpp" #include "gl/constants.hpp" #include "gl/types/types.hpp" @@ -9,6 +12,7 @@ namespace gl::util { +// exponentation function for u64 integral type [[nodiscard]] inline constexpr types::size_type upow(types::size_type base, types::size_type exp) { types::size_type result = constants::one; while (exp) { @@ -21,6 +25,7 @@ namespace gl::util { return result; } +// sum of exponents: base ^ i_begin + base ^ (i_begin + 1) + ... + base ^ (i_end) [[nodiscard]] inline types::size_type upow_sum( const types::size_type base, types::size_type i_begin, types::size_type i_end ) { diff --git a/include/gl/vertex_descriptor.hpp b/include/gl/vertex_descriptor.hpp index 0eead94..bae357e 100644 --- a/include/gl/vertex_descriptor.hpp +++ b/include/gl/vertex_descriptor.hpp @@ -1,8 +1,10 @@ +// Copyright (c) 2024 Jakub Musiał +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + #pragma once -#include "attributes/force_inline.hpp" #include "decl/graph_traits.hpp" -#include "edge_tags.hpp" #include "graph_io.hpp" #include "types/properties.hpp" #include "types/type_traits.hpp" diff --git a/scripts/check_licence.py b/scripts/check_licence.py new file mode 100644 index 0000000..1304e82 --- /dev/null +++ b/scripts/check_licence.py @@ -0,0 +1,110 @@ +import argparse +import sys +from enum import IntEnum +from pathlib import Path + +from common import find_files + + +LICENCE_INFO = [ + "// Copyright (c) 2024 Jakub Musiał", + "// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl).", + "// Licensed under the MIT License. See the LICENSE file in the project root for full license information.", +] + + +class DefaultParameters: + search_paths: list[str] = ["include"] + file_patterns: list[str] = ["*.cpp", "*.hpp", "*.c", "*.h"] + exclude_paths: list[str] = [] + help: bool = False + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-p", "--search-paths", + type=str, + default=DefaultParameters.search_paths, + nargs="*", + action="extend", + help="list of search directory paths" + ) + parser.add_argument( + "-f", "--file-patterns", + type=str, + default=DefaultParameters.file_patterns, + nargs="*", + action="extend", + help="list of file patterns to include" + ) + parser.add_argument( + "-e", "--exclude-paths", + type=str, + default=DefaultParameters.exclude_paths, + nargs="*", + action="extend", + help="list of directory paths to exclude" + ) + + return vars(parser.parse_args()) + + +class ReturnCode(IntEnum): + ok = 0 + file_to_short = -1 + missing_licence = -2 + invalid_licence = -3 + + +def check_licence(files: set[Path]) -> int: + n_files = len(files) + print(f"Files to check: {n_files}") + + return_code = None + def _set_return_code(c: ReturnCode): + nonlocal return_code + return_code = c if not return_code else return_code + + n_licence_lines = len(LICENCE_INFO) + + def _check_file(file: Path): + with open(file, "r", encoding="utf-8") as f: + lines = [line.strip() for line in f.readlines()] + n_potential_licence_lines = min(len(lines), n_licence_lines) + + if n_potential_licence_lines < n_licence_lines: + _set_return_code(ReturnCode.file_to_short) + print(f"[Licence error] File `{file}` to short") + return + + matching_lines = [lines[i] == LICENCE_INFO[i] for i in range(n_licence_lines)] + correct_licence = all(matching_lines) + if not correct_licence: + missing_info = any(matching_lines) + if missing_info: + _set_return_code(ReturnCode.invalid_licence) + print(f"[Licence error] Incomplete licence info in file `{file}`") + else: + _set_return_code(ReturnCode.missing_licence) + print(f"[Licence error] Missing licence info in file `{file}`") + + + for i, file in enumerate(files): + print(f"[{i + 1}/{n_files}] {file}") + _check_file(file) + + return return_code + + +def main( + search_paths: list[str], + file_patterns: list[str], + exclude_paths: list[str] +): + files_to_check = find_files(search_paths, file_patterns, exclude_paths) + sys.exit(check_licence(files_to_check)) + + +if __name__ == "__main__": + main(**parse_args()) diff --git a/scripts/common.py b/scripts/common.py new file mode 100644 index 0000000..cc39869 --- /dev/null +++ b/scripts/common.py @@ -0,0 +1,20 @@ +from pathlib import Path + + +def find_files( + search_paths: list[str], + file_patterns: list[str], + exclude_paths: list[str] +) -> set[Path]: + matching_files = [] + for search_path in search_paths: + path = Path(search_path) + for pattern in file_patterns: + matching_files.extend(path.rglob(pattern)) + + filtered_files = { + file for file in matching_files + if not any(str(file.parent).startswith(path) for path in exclude_paths) + } + + return filtered_files diff --git a/scripts/format.py b/scripts/format.py index bcc1bdf..e52c85c 100644 --- a/scripts/format.py +++ b/scripts/format.py @@ -1,17 +1,17 @@ import argparse -import re import subprocess import sys from pathlib import Path +from common import find_files + class DefaultParameters: - modified_files = False - search_paths = ["include", "tests"] - file_patterns = ["*.cpp", "*.hpp", "*.c", "*.h"] - exclude_paths = ["tests/external"] - check = False - help = False + modified_files: bool = False + search_paths: list[str] = ["include", "tests"] + file_patterns: list[str] = ["*.cpp", "*.hpp", "*.c", "*.h"] + exclude_paths: list[str] = ["tests/external"] + check: bool = False def parse_args(): @@ -28,6 +28,7 @@ def parse_args(): type=str, default=DefaultParameters.search_paths, nargs="*", + action="extend", help="list of search directory paths" ) parser.add_argument( @@ -35,6 +36,7 @@ def parse_args(): type=str, default=DefaultParameters.file_patterns, nargs="*", + action="extend", help="list of file patterns to include" ) parser.add_argument( @@ -42,6 +44,7 @@ def parse_args(): type=str, default=DefaultParameters.exclude_paths, nargs="*", + action="extend", help="list of directory paths to exclude" ) parser.add_argument( @@ -55,26 +58,7 @@ def parse_args(): return vars(parser.parse_args()) -def find_files( - search_paths: list[str], - file_patterns: list[str], - exclude_paths: list[str] -) -> set[Path]: - matching_files = [] - for search_path in search_paths: - path = Path(search_path) - for pattern in file_patterns: - matching_files.extend(path.rglob(pattern)) - - filtered_files = { - file for file in matching_files - if not any(str(file.parent).startswith(path) for path in exclude_paths) - } - - return filtered_files - - -def get_modified_files(found_files: set[Path]) -> set[Path]: +def get_modified_files(files: set[Path]) -> set[Path]: try: result = subprocess.run( "git diff --name-only @{u}".split(), @@ -85,17 +69,14 @@ def get_modified_files(found_files: set[Path]) -> set[Path]: ) modified_files = {Path(file) for file in result.stdout.splitlines() if file} - return modified_files & found_files + return modified_files & files except subprocess.CalledProcessError as e: print(f"Error executing git command: {e.stderr}") raise RuntimeError("Failed to retrieve the modified files.") -def run_clang_format( - files: set[Path], - check: bool -): +def run_clang_format(files: set[Path], check: bool) -> int: n_files = len(files) if check: print(f"Files to check: {n_files}") diff --git a/tests/include/alg_common.hpp b/tests/include/alg_common.hpp index e8f0b17..d73759a 100644 --- a/tests/include/alg_common.hpp +++ b/tests/include/alg_common.hpp @@ -26,7 +26,7 @@ requires(lib_tt::c_readable) ); } - for (lib_t::size_type i = 0; i < n; i++) + for (lib_t::size_type i = 0; i < n; ++i) file >> list[i]; return list; diff --git a/tests/source/test_adjacency_list.cpp b/tests/source/test_adjacency_list.cpp index 31e3e59..e218978 100644 --- a/tests/source/test_adjacency_list.cpp +++ b/tests/source/test_adjacency_list.cpp @@ -300,20 +300,20 @@ TEST_CASE_FIXTURE( ) { initialize_full_graph(); - std::function deg_proj; + std::function deg_proj; SUBCASE("in_degree") { - deg_proj = [this](const auto& vertex) { return sut.in_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.in_degree(vertex_id); }; } SUBCASE("out_degree") { - deg_proj = [this](const auto& vertex) { return sut.out_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.out_degree(vertex_id); }; } CAPTURE(deg_proj); CHECK(std::ranges::all_of( - vertices, + constants::vertex_id_view, [](const auto deg) { return deg == n_incident_edges_for_fully_connected_vertex; }, deg_proj )); @@ -321,11 +321,34 @@ TEST_CASE_FIXTURE( add_edge(constants::vertex_id_1, constants::vertex_id_1); CHECK_EQ( - deg_proj(vertices[constants::vertex_id_1]), + deg_proj(constants::vertex_id_1), n_incident_edges_for_fully_connected_vertex + constants::one ); } +TEST_CASE_FIXTURE( + test_directed_adjacency_list, + "degree should return the number of edges incident with the given vertex" +) { + initialize_full_graph(); + const auto deg_proj = [this](const auto vertex_id) { return sut.degree(vertex_id); }; + + CHECK(std::ranges::all_of( + constants::vertex_id_view, + [](const auto deg) { + return deg == constants::two * n_incident_edges_for_fully_connected_vertex; + }, + deg_proj + )); + + add_edge(constants::vertex_id_1, constants::vertex_id_1); + + CHECK_EQ( + deg_proj(constants::vertex_id_1), + constants::two * (n_incident_edges_for_fully_connected_vertex + constants::one) + ); +} + TEST_CASE_FIXTURE( test_directed_adjacency_list, "remove_vertex should remove the given vertex and all edges incident with it" @@ -623,24 +646,28 @@ TEST_CASE_FIXTURE( TEST_CASE_FIXTURE( test_undirected_adjacency_list, - "{in/out}_degree should return the number of edges incident {to/from} the given vertex" + "{in_/out_/}degree should return the number of edges incident {to/from} the given vertex" ) { initialize_full_graph(); - std::function deg_proj; + std::function deg_proj; + + SUBCASE("degree") { + deg_proj = [this](const auto vertex_id) { return sut.degree(vertex_id); }; + } SUBCASE("in_degree") { - deg_proj = [this](const auto& vertex) { return sut.in_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.in_degree(vertex_id); }; } SUBCASE("out_degree") { - deg_proj = [this](const auto& vertex) { return sut.out_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.out_degree(vertex_id); }; } CAPTURE(deg_proj); CHECK(std::ranges::all_of( - vertices, + constants::vertex_id_view, [](const auto deg) { return deg == n_incident_edges_for_fully_connected_vertex; }, deg_proj )); @@ -648,8 +675,8 @@ TEST_CASE_FIXTURE( add_edge(constants::vertex_id_1, constants::vertex_id_1); CHECK_EQ( - deg_proj(vertices[constants::vertex_id_1]), - n_incident_edges_for_fully_connected_vertex + constants::one + deg_proj(constants::vertex_id_1), + n_incident_edges_for_fully_connected_vertex + constants::two // loops counted twice ); } diff --git a/tests/source/test_adjacency_matrix.cpp b/tests/source/test_adjacency_matrix.cpp index 61c9f34..cd60c91 100644 --- a/tests/source/test_adjacency_matrix.cpp +++ b/tests/source/test_adjacency_matrix.cpp @@ -296,20 +296,20 @@ TEST_CASE_FIXTURE( ) { initialize_full_graph(); - std::function deg_proj; + std::function deg_proj; SUBCASE("in_degree") { - deg_proj = [this](const auto& vertex) { return sut.in_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.in_degree(vertex_id); }; } SUBCASE("out_degree") { - deg_proj = [this](const auto& vertex) { return sut.out_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.out_degree(vertex_id); }; } CAPTURE(deg_proj); CHECK(std::ranges::all_of( - vertices, + constants::vertex_id_view, [](const auto deg) { return deg == n_incident_edges_for_fully_connected_vertex; }, deg_proj )); @@ -317,11 +317,34 @@ TEST_CASE_FIXTURE( add_edge(constants::vertex_id_1, constants::vertex_id_1); CHECK_EQ( - deg_proj(vertices[constants::vertex_id_1]), + deg_proj(constants::vertex_id_1), n_incident_edges_for_fully_connected_vertex + constants::one ); } +TEST_CASE_FIXTURE( + test_directed_adjacency_matrix, + "degree should return the number of edges incident with the given vertex" +) { + initialize_full_graph(); + const auto deg_proj = [this](const auto vertex_id) { return sut.degree(vertex_id); }; + + CHECK(std::ranges::all_of( + constants::vertex_id_view, + [](const auto deg) { + return deg == constants::two * n_incident_edges_for_fully_connected_vertex; + }, + deg_proj + )); + + add_edge(constants::vertex_id_1, constants::vertex_id_1); + + CHECK_EQ( + deg_proj(constants::vertex_id_1), + constants::two * (n_incident_edges_for_fully_connected_vertex + constants::one) + ); +} + TEST_CASE_FIXTURE( test_directed_adjacency_matrix, "remove_vertex should remove the given vertex and all edges incident with it" @@ -562,24 +585,28 @@ TEST_CASE_FIXTURE( TEST_CASE_FIXTURE( test_undirected_adjacency_matrix, - "{in/out}_degree should return the number of edges incident {to/from} the given vertex" + "{in_/out_/}degree should return the number of edges incident {to/from/with} the given vertex" ) { initialize_full_graph(); - std::function deg_proj; + std::function deg_proj; + + SUBCASE("degree") { + deg_proj = [this](const auto vertex_id) { return sut.degree(vertex_id); }; + } SUBCASE("in_degree") { - deg_proj = [this](const auto& vertex) { return sut.in_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.in_degree(vertex_id); }; } SUBCASE("out_degree") { - deg_proj = [this](const auto& vertex) { return sut.out_degree(vertex); }; + deg_proj = [this](const auto vertex_id) { return sut.out_degree(vertex_id); }; } CAPTURE(deg_proj); CHECK(std::ranges::all_of( - vertices, + constants::vertex_id_view, [](const auto deg) { return deg == n_incident_edges_for_fully_connected_vertex; }, deg_proj )); @@ -587,8 +614,8 @@ TEST_CASE_FIXTURE( add_edge(constants::vertex_id_1, constants::vertex_id_1); CHECK_EQ( - deg_proj(vertices[constants::vertex_id_1]), - n_incident_edges_for_fully_connected_vertex + constants::one + deg_proj(constants::vertex_id_1), + n_incident_edges_for_fully_connected_vertex + constants::two // loops counted twice ); } diff --git a/tests/source/test_graph_topology_builders.cpp b/tests/source/test_graph_topology_builders.cpp index 2044678..f701ea5 100644 --- a/tests/source/test_graph_topology_builders.cpp +++ b/tests/source/test_graph_topology_builders.cpp @@ -184,16 +184,6 @@ template }; } -/* - -template -[[nodiscard]] auto predicate(const GraphType& graph) { - using vertex_type = typename GraphType::vertex_type; - return [&graph](const vertex_type&) {}; -} - -*/ - } // namespace predicate TEST_CASE_TEMPLATE_DEFINE( diff --git a/tests/source/test_vertex_degree_getters.cpp b/tests/source/test_vertex_degree_getters.cpp index 7e37965..74e849a 100644 --- a/tests/source/test_vertex_degree_getters.cpp +++ b/tests/source/test_vertex_degree_getters.cpp @@ -12,10 +12,10 @@ namespace gl_testing { TEST_SUITE_BEGIN("test_vertex_degree_getters"); -TEST_CASE("vertex degree getter tests for directed graphs") { - using traits_type = - lib::directed_graph_traits; - using sut_type = lib::graph; +TEST_CASE_TEMPLATE_DEFINE( + "vertex degree getter tests for directed graphs", TraitsType, directed_graph_traits_template +) { + using sut_type = lib::graph; using vertex_type = typename sut_type::vertex_type; const auto n_vertices = constants::n_elements_top; @@ -77,7 +77,7 @@ TEST_CASE("vertex degree getter tests for directed graphs") { sut.in_degree(vertex) == expected_in_deg_list[i] and sut.out_degree(vertex) == expected_out_deg_list[i] and sut.degree(vertex) == expected_deg_list[i]; - i++; + ++i; return result; })); @@ -88,17 +88,23 @@ TEST_CASE("vertex degree getter tests for directed graphs") { const bool result = sut.in_degree(vertex_id) == expected_in_deg_list[i] and sut.out_degree(vertex_id) == expected_out_deg_list[i] and sut.degree(vertex_id) == expected_deg_list[i]; - i++; + ++i; return result; }, transforms::extract_vertex_id )); } -TEST_CASE("vertex degree getter tests for undirected graphs") { - using traits_type = lib:: - undirected_graph_traits; - using sut_type = lib::graph; +TEST_CASE_TEMPLATE_INSTANTIATE( + directed_graph_traits_template, + lib::list_graph_traits, // directed adjacency list graph + lib::matrix_graph_traits // directed adjacency matrix graph +); + +TEST_CASE_TEMPLATE_DEFINE( + "vertex degree getter tests for undirected graphs", TraitsType, undirected_graph_traits_template +) { + using sut_type = lib::graph; using vertex_type = typename sut_type::vertex_type; const auto n_vertices = constants::n_elements_top; @@ -116,7 +122,7 @@ TEST_CASE("vertex degree getter tests for undirected graphs") { sut.add_edge(constants::first_element_idx, constants::first_element_idx); expected_deg_list = std::deque(n_vertices, n_vertices - constants::one); - expected_deg_list.front()++; + expected_deg_list.front() += constants::two; // loops counted twice } SUBCASE("cycle") { @@ -142,7 +148,7 @@ TEST_CASE("vertex degree getter tests for undirected graphs") { const bool result = sut.in_degree(vertex) == expected_deg and sut.out_degree(vertex) == expected_deg and sut.degree(vertex) == expected_deg; - i++; + ++i; return result; })); @@ -154,13 +160,19 @@ TEST_CASE("vertex degree getter tests for undirected graphs") { const bool result = sut.in_degree(vertex_id) == expected_deg and sut.out_degree(vertex_id) == expected_deg and sut.degree(vertex_id) == expected_deg; - i++; + ++i; return result; }, transforms::extract_vertex_id )); } +TEST_CASE_TEMPLATE_INSTANTIATE( + undirected_graph_traits_template, + lib::list_graph_traits, // undirected adjacency list graph + lib::matrix_graph_traits // undirected adjacency matrix graph +); + TEST_SUITE_END(); // test_vertex_degree_getters } // namespace gl_testing