diff --git a/Makefile b/Makefile index d1ac8ec8..1d3cbb6c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,14 @@ # Default to 'dev' if TAGS is not specified on the command line TAGS ?= dev -.PHONY: docs build-docs serve-docs clean-docs clean +# Detect if running in GitHub Actions (or any standard CI) +ifeq ($(CI),true) + QUIET := +else + QUIET := > /dev/null 2>&1 +endif + +.PHONY: docs build-docs serve-docs clean-docs clean sync-gh-pages # Target for CI Pull Requests: Just builds to verify everything works (no deployment) build-docs: clean-docs @@ -10,12 +17,14 @@ build-docs: clean-docs uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl uv run mkdocs build --strict -# Example usage: `make docs TAGS="v2.0.0 latest --update-aliases --push"` +# Example usage: +# - Building the docs for a single tag locally: `make docs TAGS="v-dev"` +# - Building and deploying the docs: `make docs TAGS="v latest --update-aliases --push"` docs: clean-docs @echo "==> Deploying MkDocs documentation (Tags: $(TAGS))..." - doxygen Doxyfile - uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl - uv run mike deploy $(TAGS) + @doxygen Doxyfile $(QUIET) + @uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl $(QUIET) + @uv run mike deploy $(TAGS) $(QUIET) @echo "==> Documentation deployed to local gh-pages branch." serve-docs: docs @@ -24,8 +33,16 @@ serve-docs: docs clean-docs: @echo "==> Cleaning documentation build directories..." - rm -rf docs/cpp-gl/ - rm -rf site/ - rm -rf documentation/ + @rm -rf docs/cpp-gl/ + @rm -rf site/ + @rm -rf documentation/ + @echo "==> Documentation build cleaning complete!" clean: clean-docs + +sync-gh-pages: + @echo "==> Fetching latest gh-pages from remote..." + @git fetch origin gh-pages + @echo "==> Force-updating local gh-pages to match origin..." + @git branch -f gh-pages origin/gh-pages + @echo "==> gh-pages sync complete!" diff --git a/README.md b/README.md index cff4e06b..7c2619c4 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ The documentation build process utilizes the following toolchain: To build and serve the documentation locally: ```bash -make serve-docs +make serve-docs # automatically builds and serves the documentation page using the `dev` tag ``` To clean the documentation build directories: @@ -179,6 +179,13 @@ To clean the documentation build directories: make clean-docs ``` +> [!TIP] +> +> If your local `gh-branches` is out of sync with the `origin/gh-pages` branch, you can run the following command to force sync it to match the state of the origin branch: +> ```bash +> make sync-gh-pages +> ``` + > [!NOTE] > > The documentation build scripts rely on **[uv](https://docs.astral.sh/uv/)** to simplify Python dependency management within the project. Ensure `uv` is installed on the system before attempting to build the documentation. diff --git a/benchmarks/suites/hg_b_bfs.cpp b/benchmarks/suites/hg_b_bfs.cpp index 1d5a1909..e19f118c 100644 --- a/benchmarks/suites/hg_b_bfs.cpp +++ b/benchmarks/suites/hg_b_bfs.cpp @@ -100,7 +100,7 @@ bool incidence_backward_bfs( auto root_nodes = roots | std::views::transform([](const id_type root_id) { - return gl::algorithm::search_node{root_id}; + return gl::algorithm::search_node>{root_id}; }) | std::ranges::to(); diff --git a/docs/gl/algorithms/overview.md b/docs/gl/algorithms/overview.md index 82abbbde..3e5bd0bf 100644 --- a/docs/gl/algorithms/overview.md +++ b/docs/gl/algorithms/overview.md @@ -52,7 +52,7 @@ For convenience, `decision` implicitly constructs from a boolean, where `true` m ### The Search Node -By default, the active container of a search engine stores [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md) structures. This is a lightweight pair containing: +By default, the active container of a search engine stores [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md) structures. This is a lightweight pair containing: 1. `vertex_id`: The vertex currently being visited. 2. `pred_id`: The vertex from which this current vertex was reached (its parent in the traversal tree). diff --git a/docs/gl/algorithms/templates.md b/docs/gl/algorithms/templates.md index 9c9fb740..3cdf46e2 100644 --- a/docs/gl/algorithms/templates.md +++ b/docs/gl/algorithms/templates.md @@ -50,7 +50,7 @@ For a single popped node in `bfs`, `dfs`, or `pfs`, the execution flow looks exa ## Custom Node Injection (PFS) -While BFS and DFS templates strictly operate on the lightweight [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md), the Priority-First Search template often requires tracking dynamic state alongside the vertex ID. +While BFS and DFS templates strictly operate on the lightweight [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md), the Priority-First Search template often requires tracking dynamic state alongside the vertex ID. For instance, in Dijkstra's algorithm, the priority queue must sort nodes based on their accumulated distance from the starting point. You cannot sort based purely on the vertex ID. diff --git a/docs/hgl/architecture.md b/docs/hgl/architecture.md index f057bc84..4108cc7b 100644 --- a/docs/hgl/architecture.md +++ b/docs/hgl/architecture.md @@ -22,6 +22,12 @@ The HGL module is built upon the exact same zero-cost abstraction philosophy as Crucially, **the HGL module is built strictly on top of the GL module.** It acts as an architectural extension with a strict unidirectional dependency. HGL heavily relies on GL and directly reuses its core infrastructure—including ID types, type traits and concepts, I/O utilities, and underlying contiguous data structures (like [**gl::flat_jagged_vector**](../cpp-gl/classgl_1_1flat__jagged__vector.md) and [**gl::flat_matrix**](../cpp-gl/classgl_1_1flat__matrix.md)). Therefore, if you are familiar with the GL module, the HGL module's design language will feel immediately natural. +> [!IMPORTANT] +> +> The HGL module imports all generic elements (such as type traits, concepts, functions, etc.) from the `gl` namespaces to the `hgl` namespaces. The documentation for these imported elements is available in the [GL module's documentation page](../cpp-gl/group__GL.md). +> +> **NOTE:** Only elements that are **NOT graph-specific** are imported. + Conversely, the GL module remains completely standalone and entirely unaware of the HGL module or any of its components. This strict separation ensures that projects requiring only standard graph capabilities can utilize the GL module without incurring any compile-time dependencies, structural complexity, or overhead from the generalized hypergraph extensions. --- diff --git a/docs/hgl/io.md b/docs/hgl/io.md index 85d7398e..61b454fb 100644 --- a/docs/hgl/io.md +++ b/docs/hgl/io.md @@ -14,7 +14,7 @@ At its core, the library overloads the standard `operator<<` for the [**hgl::hyp > [!NOTE] Hyperedge Formatting > -> Because a hyperedge descriptor is a simple wrapper for the hyperedge's ID and optional properties, printing it directly would result in the exact same format as for vertex descriptors. In order to print the hyperedge as the set(s) of its incident vertices, you need to use the dedicated [**display**](../cpp-gl/classhgl_1_1hypergraph.md#function-display) method of the `hypergraph` class. +> Because a hyperedge descriptor is a simple wrapper for the hyperedge's ID and optional properties, printing it directly would result in the exact same format as for vertex descriptors. In order to print the hyperedge as the set(s) of its incident vertices, you need to use the dedicated [**fmt**](../cpp-gl/classhgl_1_1hypergraph.md#function-fmt) method of the `hypergraph` class. ```cpp #include @@ -27,7 +27,7 @@ int main() { auto v0 = hg.vertex(0); std::cout << "Vertex: " << v0 << '\n'; // (1)! - std::cout << "Hyperedge: " << hg.display(e1) << '\n'; // (2)! + std::cout << "Hyperedge: " << hg.fmt(e1) << '\n'; // (2)! std::cout << "Hypergraph:\n" << hg << '\n'; // (3)! } ``` diff --git a/include/gl/algorithm/spanning_tree/prim_mst.hpp b/include/gl/algorithm/spanning_tree/prim_mst.hpp index 4dcd0d25..43ff1451 100644 --- a/include/gl/algorithm/spanning_tree/prim_mst.hpp +++ b/include/gl/algorithm/spanning_tree/prim_mst.hpp @@ -22,7 +22,7 @@ namespace gl::algorithm { template struct mst_descriptor { /// @brief The type of the graph. - using graph_type = graph_val_t; + using graph_type = val_t; /// @brief The type of the edges stored in the graph. using edge_type = edge_t; /// @brief The numeric type used to represent accumulated tree weights. diff --git a/include/gl/algorithm/templates/bfs.hpp b/include/gl/algorithm/templates/bfs.hpp index 61a79be1..f7958334 100644 --- a/include/gl/algorithm/templates/bfs.hpp +++ b/include/gl/algorithm/templates/bfs.hpp @@ -74,7 +74,8 @@ namespace gl::algorithm { /// @hideparams template < traits::c_graph G, - traits::c_forward_range_of> InitQueueRangeType = std::vector>, + traits::c_forward_range_of>> InitQueueRangeType = + std::vector>>, traits::c_optional_predicate> VisitVertexPredicate = empty_callback, traits::c_optional_predicate, id_t> VisitCallback = empty_callback, traits::c_decision_predicate, const edge_t&> EnqueueNodePred = empty_callback, @@ -93,7 +94,7 @@ bool bfs( return false; // prepare the node queue - std::queue> q; + std::queue>> q; for (const auto& node : initial_queue_content) q.push(node); diff --git a/include/gl/algorithm/templates/dfs.hpp b/include/gl/algorithm/templates/dfs.hpp index 12779f75..2bfda1eb 100644 --- a/include/gl/algorithm/templates/dfs.hpp +++ b/include/gl/algorithm/templates/dfs.hpp @@ -73,7 +73,8 @@ namespace gl::algorithm { /// @hideparams template < traits::c_graph G, - traits::c_forward_range_of> InitStackRangeType = std::vector>, + traits::c_forward_range_of>> InitStackRangeType = + std::vector>>, traits::c_optional_predicate> VisitVertexPredicate = empty_callback, traits::c_optional_predicate, id_t> VisitCallback = empty_callback, traits::c_decision_predicate, const edge_t&> EnqueueNodePred = empty_callback, @@ -92,7 +93,7 @@ bool dfs( return false; // prepare the node stack - std::stack> s; + std::stack>> s; for (const auto& node : initial_stack_content) s.push(node); diff --git a/include/gl/algorithm/templates/pfs.hpp b/include/gl/algorithm/templates/pfs.hpp index 8ab3ad76..7e02bb63 100644 --- a/include/gl/algorithm/templates/pfs.hpp +++ b/include/gl/algorithm/templates/pfs.hpp @@ -87,7 +87,7 @@ namespace gl::algorithm { template < traits::c_graph G, typename PQCmp, - typename InitQueueRangeType = std::vector>, + typename InitQueueRangeType = std::vector>>, typename NodeType = std::ranges::range_value_t, traits::c_optional_predicate VisitVertexPredicate = empty_callback, traits::c_optional_predicate, id_t> VisitCallback = empty_callback, diff --git a/include/gl/algorithm/topology/topological_sort.hpp b/include/gl/algorithm/topology/topological_sort.hpp index 554f8a73..c5afbd18 100644 --- a/include/gl/algorithm/topology/topological_sort.hpp +++ b/include/gl/algorithm/topology/topological_sort.hpp @@ -73,7 +73,7 @@ template < std::vector in_degree_map = graph.in_degree_map(); // prepare the initial queue content (source vertices) - std::vector> source_vertex_list; + std::vector>> source_vertex_list; source_vertex_list.reserve(graph.n_vertices()); for (const auto id : graph.vertex_ids()) if (in_degree_map[to_idx(id)] == 0uz) diff --git a/include/gl/algorithm/util.hpp b/include/gl/algorithm/util.hpp index 76c7b417..f115aa74 100644 --- a/include/gl/algorithm/util.hpp +++ b/include/gl/algorithm/util.hpp @@ -52,9 +52,10 @@ template /// @return A container initialized with a single @ref search_node for the root vertex. template < traits::c_graph G, - traits::c_forward_range_of> InitRangeType = std::vector>> + traits::c_forward_range_of>> InitRangeType = + std::vector>>> [[nodiscard]] gl_attr_force_inline InitRangeType init_node_range(id_t root_vertex_id) { - return InitRangeType{search_node{root_vertex_id}}; + return InitRangeType{search_node>{root_vertex_id}}; } /// @ingroup GL-Algorithm diff --git a/include/gl/edge_descriptor.hpp b/include/gl/edge_descriptor.hpp index 56fad0a4..64e0d95c 100644 --- a/include/gl/edge_descriptor.hpp +++ b/include/gl/edge_descriptor.hpp @@ -106,11 +106,11 @@ class edge_descriptor final { : _id(id), _vertices(source, target), _properties(properties) {} /// @brief Implicit converting constructor from a non-const descriptor to a const descriptor. - /// @tparam NonConstProps The non-const property type. + /// @tparam MutProperties The mutable property type. /// @param other The edge descriptor to convert from. - template - requires(std::same_as) - edge_descriptor(const edge_descriptor& other) noexcept + template + requires(std::same_as) + edge_descriptor(const edge_descriptor& other) noexcept : _id(other.id()), _vertices(other._vertices) { if constexpr (traits::c_non_empty_properties) { this->_properties = other.properties(); @@ -154,7 +154,7 @@ class edge_descriptor final { template requires(traits::c_directed_edge and std::same_as, std::remove_cv_t>) [[nodiscard]] bool operator==( - const edge_descriptor& other + const edge_descriptor& other ) const noexcept { return this->_id == other.id() and (this->_vertices == other.incident_vertices()); } @@ -166,7 +166,7 @@ class edge_descriptor final { template requires(traits::c_undirected_edge and std::same_as, std::remove_cv_t>) [[nodiscard]] bool operator==( - const edge_descriptor& other + const edge_descriptor& other ) const noexcept { return this->_id == other.id() and (this->_vertices == other.incident_vertices() diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index bee8a7ca..1c0f3046 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -50,44 +50,44 @@ concept c_mut_graph = c_graph and not std::is_const_v -using graph_val_t = std::remove_cvref_t; +using val_t = std::remove_cvref_t; /// @ingroup GL-Core /// @brief Resolves the identifier type associated with the given graph type. template -using id_t = typename graph_val_t::id_type; +using id_t = typename val_t::id_type; /// @ingroup GL-Core /// @brief Resolves the appropriate vertex descriptor type (mutable or const) based on the graph's constness. template using vertex_t = std::conditional_t< std::is_const_v>, - typename graph_val_t::const_vertex_type, - typename graph_val_t::vertex_type>; + typename val_t::const_vertex_type, + typename val_t::vertex_type>; /// @ingroup GL-Core /// @brief Resolves the appropriate vertex properties type (mutable or const) based on the graph's constness. template using vertex_properties_t = std::conditional_t< std::is_const_v>, - const typename graph_val_t::vertex_properties_type, - typename graph_val_t::vertex_properties_type>; + const typename val_t::vertex_properties_type, + typename val_t::vertex_properties_type>; /// @ingroup GL-Core /// @brief Resolves the appropriate edge descriptor type (mutable or const) based on the graph's constness. template using edge_t = std::conditional_t< std::is_const_v>, - typename graph_val_t::const_edge_type, - typename graph_val_t::edge_type>; + typename val_t::const_edge_type, + typename val_t::edge_type>; /// @ingroup GL-Core /// @brief Resolves the appropriate edge properties type (mutable or const) based on the graph's constness. template using edge_properties_t = std::conditional_t< std::is_const_v>, - const typename graph_val_t::edge_properties_type, - typename graph_val_t::edge_properties_type>; + const typename val_t::edge_properties_type, + typename val_t::edge_properties_type>; namespace traits { @@ -96,28 +96,28 @@ namespace traits { /// @see gl::directed_t "directed_t" : For the directional tag used to specify directed graph configuration. template concept c_directed_graph = - c_graph and std::same_as::directional_tag, directed_t>; + c_graph and std::same_as::directional_tag, directed_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph is undirected. /// @see gl::undirected_t "undirected_t" : For the directional tag used to specify undirected graph configuration. template concept c_undirected_graph = - c_graph and std::same_as::directional_tag, undirected_t>; + c_graph and std::same_as::directional_tag, undirected_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph utilizes the standard adjacency list representation. /// @see gl::repr::list_t "list_t" : For the representation tag used to specify the standard adjacency list representation. template concept c_list_graph = - c_graph and std::same_as::representation_tag, repr::list_t>; + c_graph and std::same_as::representation_tag, repr::list_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph utilizes the flattened adjacency list representation. /// @see gl::repr::flat_list_t "flat_list_t" : For the representation tag used to specify the flattened adjacency list representation. template concept c_flat_list_graph = - c_graph and std::same_as::representation_tag, repr::flat_list_t>; + c_graph and std::same_as::representation_tag, repr::flat_list_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph utilizes any list-based adjacency representation. @@ -132,14 +132,14 @@ concept c_adjacency_list_graph = c_list_graph or c_flat_list_graph; /// @see gl::repr::matrix_t "matrix_t" : For the representation tag used to specify the standard adjacency matrix representation. template concept c_matrix_graph = - c_graph and std::same_as::representation_tag, repr::matrix_t>; + c_graph and std::same_as::representation_tag, repr::matrix_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph utilizes the flattened adjacency matrix representation. /// @see gl::repr::flat_matrix_t "flat_matrix_t" : For the representation tag used to specify the flattened adjacency matrix representation. template concept c_flat_matrix_graph = - c_graph and std::same_as::representation_tag, repr::flat_matrix_t>; + c_graph and std::same_as::representation_tag, repr::flat_matrix_t>; /// @ingroup GL-Traits /// @brief Concept checking if a graph utilizes any matrix-based adjacency representation. @@ -154,24 +154,24 @@ concept c_adjacency_matrix_graph = c_matrix_graph or c_flat_matrix_graph; /// @tparam V The type of the vertex descriptor. /// @tparam G The type of the graph. template -concept c_graph_vertex = +concept c_vertex = c_graph and c_one_of< std::remove_cvref_t, - typename graph_val_t::vertex_type, - typename graph_val_t::const_vertex_type>; + typename val_t::vertex_type, + typename val_t::const_vertex_type>; /// @ingroup GL-Traits /// @brief Concept checking if a type is a mutable or immutable edge descriptor associated with the given graph. /// @tparam E The type of the edge descriptor. /// @tparam G The type of the graph. template -concept c_graph_edge = +concept c_edge = c_graph and c_one_of< std::remove_cvref_t, - typename graph_val_t::edge_type, - typename graph_val_t::const_edge_type>; + typename val_t::edge_type, + typename val_t::const_edge_type>; } // namespace traits @@ -446,7 +446,7 @@ class graph final { /// @param vertex The descriptor of the vertex to remove. /// @throws std::invalid_argument If the vertex descriptor is invalid. /// @copydetails detail::graph_doc_anchors::remove_vertex_wrn() - gl_attr_force_inline void remove_vertex(traits::c_graph_vertex auto vertex) { + gl_attr_force_inline void remove_vertex(traits::c_vertex auto vertex) { this->remove_vertex(vertex.id()); } @@ -473,11 +473,9 @@ class graph final { /// @throws std::invalid_argument If any vertex descriptor is invalid. /// @copydetails detail::graph_doc_anchors::remove_vertex_wrn() template - requires(traits::c_forward_range and traits::c_graph_vertex, graph>) + requires(traits::c_forward_range and traits::c_vertex, graph>) gl_attr_force_inline void remove_vertices(const VertexRng& vertex_rng) { - this->remove_vertices( - vertex_rng | std::views::transform([](const auto& v) { return v.id(); }) - ); + this->remove_vertices(vertex_rng | std::views::transform(util::to_id)); } // --- vertex getters --- @@ -492,8 +490,7 @@ class graph final { /// @brief Checks if the given vertex descriptor is valid in the graph. /// @param vertex The vertex descriptor to check. /// @return `true` if it exists, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool has_vertex(traits::c_graph_vertex auto vertex - ) const { + [[nodiscard]] gl_attr_force_inline bool has_vertex(traits::c_vertex auto vertex) const { return this->has_vertex(vertex.id()); } @@ -575,7 +572,7 @@ class graph final { /// @return A view of all adjacent vertex descriptors. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto neighbors( - this auto& self, traits::c_graph_vertex auto vertex + this auto& self, traits::c_vertex auto vertex ) { return self.neighbors(vertex.id()); } @@ -595,7 +592,7 @@ class graph final { /// @param vertex The source vertex descriptor. /// @return A view of all adjacent vertex IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto neighbor_ids(traits::c_graph_vertex auto vertex + [[nodiscard]] gl_attr_force_inline auto neighbor_ids(traits::c_vertex auto vertex ) const { return this->neighbor_ids(vertex.id()); } @@ -616,7 +613,7 @@ class graph final { /// @return A view of all predecessor vertex descriptors. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline auto predecessors( - this auto& self, traits::c_graph_vertex auto vertex + this auto& self, traits::c_vertex auto vertex ) { return self.predecessors(vertex.id()); } @@ -636,8 +633,7 @@ class graph final { /// @param vertex The target vertex descriptor. /// @return A view of all predecessor vertex IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto predecessor_ids( - traits::c_graph_vertex auto vertex + [[nodiscard]] gl_attr_force_inline auto predecessor_ids(traits::c_vertex auto vertex ) const { return this->predecessor_ids(vertex.id()); } @@ -658,7 +654,7 @@ class graph final { /// @return A view of all successor vertex descriptors. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto successors( - this auto& self, traits::c_graph_vertex auto vertex + this auto& self, traits::c_vertex auto vertex ) { return self.successors(vertex.id()); } @@ -678,7 +674,7 @@ class graph final { /// @param vertex The source vertex descriptor. /// @return A view of all successor vertex IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto successor_ids(traits::c_graph_vertex auto vertex + [[nodiscard]] gl_attr_force_inline auto successor_ids(traits::c_vertex auto vertex ) const { return this->successor_ids(vertex.id()); } @@ -722,8 +718,7 @@ class graph final { /// @param vertex The vertex descriptor. /// @return The total degree. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type degree(traits::c_graph_vertex auto vertex - ) const { + [[nodiscard]] gl_attr_force_inline size_type degree(traits::c_vertex auto vertex) const { return this->degree(vertex.id()); } @@ -748,7 +743,7 @@ class graph final { /// @param vertex The vertex descriptor. /// @return The in-degree. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type in_degree(traits::c_graph_vertex auto vertex + [[nodiscard]] gl_attr_force_inline size_type in_degree(traits::c_vertex auto vertex ) const { return this->in_degree(vertex.id()); } @@ -774,8 +769,8 @@ class graph final { /// @param vertex The vertex descriptor. /// @return The out-degree. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type - out_degree(traits::c_graph_vertex auto vertex) const { + [[nodiscard]] gl_attr_force_inline size_type out_degree(traits::c_vertex auto vertex + ) const { return this->out_degree(vertex.id()); } @@ -841,7 +836,7 @@ class graph final { /// @return A descriptor representing the newly created edge. /// @throws std::invalid_argument If either vertex descriptor is invalid. /// @copydetails detail::graph_doc_anchors::add_edge_note() - gl_attr_force_inline edge_type add_edge(traits::c_graph_vertex auto source, traits::c_graph_vertex auto target) { + gl_attr_force_inline edge_type add_edge(traits::c_vertex auto source, traits::c_vertex auto target) { return this->add_edge(source.id(), target.id()); } @@ -853,7 +848,7 @@ class graph final { /// @throws std::invalid_argument If either vertex descriptor is invalid. /// @copydetails detail::graph_doc_anchors::add_edge_note() gl_attr_force_inline edge_type add_edge_with( - traits::c_graph_vertex auto source, traits::c_graph_vertex auto target, const edge_properties_type& properties + traits::c_vertex auto source, traits::c_vertex auto target, const edge_properties_type& properties ) requires(traits::c_non_empty_properties) { @@ -893,10 +888,8 @@ class graph final { /// @throws std::invalid_argument If any vertex ID is invalid. /// @copydetails detail::graph_doc_anchors::add_edge_note() template - requires(traits::c_sized_range and traits::c_graph_vertex, graph>) - void add_edges_from(traits::c_graph_vertex auto source, const TargetRng& target_rng) { - using rng_vertex_type = std::ranges::range_value_t; - + requires(traits::c_sized_range and traits::c_vertex, graph>) + void add_edges_from(traits::c_vertex auto source, const TargetRng& target_rng) { this->_verify_vertex_id(source.id()); for (auto target : target_rng) this->_verify_vertex_id(target.id()); @@ -906,10 +899,9 @@ class graph final { this->_impl.add_edges_from( std::views::iota(static_cast(prev_n_edges), this->_n_edges), source.id(), - target_rng | std::views::transform(&rng_vertex_type::id) + target_rng | std::views::transform(util::to_id) ); - if constexpr (traits::c_non_empty_properties) this->_edge_properties.resize(this->_n_edges); } @@ -980,7 +972,7 @@ class graph final { /// @return `true` if an edge exists, `false` otherwise. /// @throws std::invalid_argument If either vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline bool has_edge( - traits::c_graph_vertex auto source, traits::c_graph_vertex auto target + traits::c_vertex auto source, traits::c_vertex auto target ) const { return this->has_edge(source.id(), target.id()); } @@ -1010,9 +1002,7 @@ class graph final { /// @throws std::invalid_argument If either vertex descriptor is invalid. template [[nodiscard]] gl_attr_force_inline std::optional> edge( - this Self& self, - traits::c_graph_vertex auto source, - traits::c_graph_vertex auto target + this Self& self, traits::c_vertex auto source, traits::c_vertex auto target ) { return self.edge(source.id(), target.id()); } @@ -1042,9 +1032,7 @@ class graph final { /// @throws std::invalid_argument If either vertex descriptor is invalid. template [[nodiscard]] gl_attr_force_inline std::vector> edges( - this Self& self, - traits::c_graph_vertex auto source, - traits::c_graph_vertex auto target + this Self& self, traits::c_vertex auto source, traits::c_vertex auto target ) { return self.edges(source.id(), target.id()); } @@ -1070,7 +1058,7 @@ class graph final { /// @throws std::invalid_argument If the vertex descriptor is invalid. template [[nodiscard]] gl_attr_force_inline auto incident_edges( - this Self& self, traits::c_graph_vertex auto vertex + this Self& self, traits::c_vertex auto vertex ) { return self.incident_edges(vertex.id()); } @@ -1094,7 +1082,7 @@ class graph final { /// @throws std::invalid_argument If the vertex descriptor is invalid. template [[nodiscard]] gl_attr_force_inline auto in_edges( - this Self& self, traits::c_graph_vertex auto vertex + this Self& self, traits::c_vertex auto vertex ) { return self.in_edges(vertex.id()); } @@ -1117,7 +1105,7 @@ class graph final { /// @return A view representing the set of outgoing edges. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto out_edges( - this auto& self, traits::c_graph_vertex auto vertex + this auto& self, traits::c_vertex auto vertex ) { return self.out_edges(vertex.id()); } @@ -1189,7 +1177,7 @@ class graph final { /// @return `true` if the given vertices are adjacent, `false` otherwise. /// @throws std::invalid_argument If either vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline bool are_adjacent( - traits::c_graph_vertex auto source, traits::c_graph_vertex auto target + traits::c_vertex auto source, traits::c_vertex auto target ) const { return this->are_adjacent(source.id(), target.id()); } @@ -1222,9 +1210,8 @@ class graph final { /// @param edge The edge descriptor. /// @return `true` if the vertex is incident to the edge, `false` otherwise. /// @throws std::invalid_argument If either the vertex or the edge descriptor is invalid. - [[nodiscard]] bool are_incident( - traits::c_graph_vertex auto vertex, const edge_type& edge - ) const { + [[nodiscard]] bool are_incident(traits::c_vertex auto vertex, const edge_type& edge) + const { this->_verify_vertex_id(vertex.id()); this->_verify_edge(edge); return edge.is_incident_with(vertex.id()); @@ -1239,7 +1226,7 @@ class graph final { /// @return `true` if the vertex is incident to the edge, `false` otherwise. /// @throws std::invalid_argument If either the vertex or the edge descriptor is invalid. [[nodiscard]] gl_attr_force_inline bool are_incident( - const edge_type& edge, traits::c_graph_vertex auto vertex + const edge_type& edge, traits::c_vertex auto vertex ) const { return this->are_incident(vertex, edge); } @@ -1378,10 +1365,9 @@ class graph final { using fmt_traits = io::detail::graph_fmt_traits; - template struct concise_target_formatter { - edge_t edge; - id_t src_id; + const_edge_type edge; + id_type src_id; bool with_props; friend std::ostream& operator<<(std::ostream& os, const concise_target_formatter& proxy) { @@ -1405,16 +1391,16 @@ class graph final { return os; } - template - std::ostream& _concise_write(this Self&& self, std::ostream& os) { + std::ostream& _concise_write(this auto&& self, std::ostream& os) { using enum io::detail::option_bit; for (auto src : self.vertices()) { auto tgts = std::views::transform( self.out_edges(src.id()), - [src_id = src.id(), with_props = io::is_option_set(os, with_connection_properties)]( - const edge_t& edge - ) { return concise_target_formatter{edge, src_id, with_props}; } + [src_id = src.id(), + with_props = io::is_option_set(os, with_connection_properties)](const auto& edge) { + return concise_target_formatter{edge, src_id, with_props}; + } ); os << src << " : " << io::range_formatter(tgts) << '\n'; } @@ -1664,7 +1650,7 @@ using vertex_distance_t = typename vertex_distance::type; /// @return The specified edge weight or the unweighted graph default (1). template [[nodiscard]] gl_attr_force_inline vertex_distance_t get_weight( - const traits::c_graph_edge auto& edge + const traits::c_edge auto& edge ) { if constexpr (traits::c_weight_properties_type>) return edge.properties().weight; diff --git a/include/gl/graph_traits.hpp b/include/gl/graph_traits.hpp index 6bda6933..bbb2d540 100644 --- a/include/gl/graph_traits.hpp +++ b/include/gl/graph_traits.hpp @@ -74,9 +74,9 @@ struct graph_traits { /// @brief The type of properties associated with the edge descriptor. using edge_properties_type = std::remove_cvref_t; /// @brief The descriptor type representing an edge of a graph. - using edge_type = edge_descriptor; + using edge_type = edge_descriptor; /// @brief The descriptor type representing an immutable edge of a graph. - using const_edge_type = edge_descriptor; + using const_edge_type = edge_descriptor; }; /// @ingroup GL-Core diff --git a/include/gl/impl/adjacency_list.hpp b/include/gl/impl/adjacency_list.hpp index 28973f46..993f826e 100644 --- a/include/gl/impl/adjacency_list.hpp +++ b/include/gl/impl/adjacency_list.hpp @@ -101,8 +101,7 @@ class adjacency_list final : public adjacency_list_base_t { this->_remove_edge_impl(edge); auto removed_edge_ids = - edges | std::views::transform([](const auto& edge) { return edge.id(); }) - | std::ranges::to(); + edges | std::views::transform(util::to_id) | std::ranges::to(); this->_remap_element_ids(invalid_id, removed_edge_ids); return removed_edge_ids; } diff --git a/include/gl/impl/adjacency_matrix.hpp b/include/gl/impl/adjacency_matrix.hpp index a7637f88..3e33f2c4 100644 --- a/include/gl/impl/adjacency_matrix.hpp +++ b/include/gl/impl/adjacency_matrix.hpp @@ -84,8 +84,7 @@ class adjacency_matrix final : public adjacency_matrix_base_t { for (const auto& edge : edges) this->_remove_edge_impl(edge); auto removed_edge_ids = - edges | std::views::transform([](const auto& edge) { return edge.id(); }) - | std::ranges::to(); + edges | std::views::transform(util::to_id) | std::ranges::to(); this->_remap_element_ids(removed_edge_ids); return removed_edge_ids; } diff --git a/include/gl/traits.hpp b/include/gl/traits.hpp index 40d8a713..1551f23d 100644 --- a/include/gl/traits.hpp +++ b/include/gl/traits.hpp @@ -178,23 +178,6 @@ template concept c_random_access_range_of_cv = c_random_access_range and std::same_as>; -/// @ingroup GL-Traits -/// @brief Concept checking if a range provides valid const iterators via `cbegin()` and `cend()`. -/// @tparam R The type of the range. -template -concept c_const_range = requires(R& r) { - std::ranges::cbegin(r); - std::ranges::cend(r); -}; - -/// @ingroup GL-Traits -/// @brief Concept checking if dereferencing an iterator yields a const reference. -/// @tparam T The iterator type to check. -template -concept c_const_iterator = requires(T iter) { - { *iter } -> std::same_as&>; -}; - /// @ingroup GL-Traits /// @brief Concept checking if a type supports three-way comparison and equality operators. /// @tparam T The type to check. diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index bcfa4dfd..5ae3401a 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -12,6 +12,10 @@ namespace gl::util { +/// @ingroup GL-Util +/// @brief A transformation callback that returns a descriptor's ID. +inline constexpr auto to_id = [](const auto& descriptor) { return descriptor.id(); }; + /// @ingroup GL-Util /// @brief Safely determines the size of a range. /// diff --git a/include/gl/vertex_descriptor.hpp b/include/gl/vertex_descriptor.hpp index eac6dad1..86e3398a 100644 --- a/include/gl/vertex_descriptor.hpp +++ b/include/gl/vertex_descriptor.hpp @@ -92,11 +92,11 @@ class vertex_descriptor final { : _id(id), _properties(properties) {} /// @brief Implicit converting constructor from a non-const descriptor to a const descriptor. - /// @tparam NonConstProps The non-const property type. + /// @tparam MutProperties The mutable property type. /// @param other The vertex descriptor to convert from. - template - requires(std::same_as) - vertex_descriptor(const vertex_descriptor& other) noexcept + template + requires(std::same_as) + vertex_descriptor(const vertex_descriptor& other) noexcept : _id(other.id()) { if constexpr (traits::c_non_empty_properties) { this->_properties = other.properties(); @@ -140,7 +140,7 @@ class vertex_descriptor final { template requires(std::same_as, std::remove_cv_t>) [[nodiscard]] gl_attr_force_inline bool operator==( - const vertex_descriptor& other + const vertex_descriptor& other ) const noexcept { return this->_id == other.id(); } @@ -152,7 +152,7 @@ class vertex_descriptor final { template requires(std::same_as, std::remove_cv_t>) [[nodiscard]] gl_attr_force_inline std::strong_ordering operator<=>( - const vertex_descriptor& other + const vertex_descriptor& other ) const noexcept { return this->_id <=> other.id(); } diff --git a/include/hgl/algorithm/core.hpp b/include/hgl/algorithm/core.hpp index 92b35fb9..648c9fc4 100644 --- a/include/hgl/algorithm/core.hpp +++ b/include/hgl/algorithm/core.hpp @@ -81,7 +81,7 @@ inline constexpr no_root_t no_root = gl::algorithm::no_root; template struct search_node { /// @brief The identifier type of the hypergraph elements. - using id_type = typename H::id_type; + using id_type = id_t; /// @brief Default constructor creates an invalid node. search_node() = default; @@ -121,12 +121,54 @@ struct search_node { /// /// @tparam H The type of the hypergraph being searched. template -using search_tree = std::vector>; +using search_tree = std::vector>>; } // namespace algorithm namespace traits { +/// @ingroup HGL-Traits +/// @brief Concept checking if a given type is the empty_callback tag. +/// ### See Also +/// - [**c_empty_callback**](gl_concepts.md#gl-traits-c-empty-callback) : For the full concept documentation in the GL module. +using gl::traits::c_empty_callback; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is callable with specific arguments and returns a specific type. +/// ### See Also +/// - [**c_callback**](gl_concepts.md#gl-traits-c-callback) : For the full concept documentation in the GL module. +using gl::traits::c_callback; + +/// @ingroup HGL-Traits +/// @brief Concept allowing either a valid callback or the explicit absence of one through the use of empty_callback. +/// ### See Also +/// - [**c_optional_callback**](gl_concepts.md#gl-traits-c-optional-callback) : For the full concept documentation in the GL module. +using gl::traits::c_optional_callback; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a boolean predicate callable with specific arguments. +/// ### See Also +/// - [**c_predicate**](gl_concepts.md#gl-traits-c-predicate) : For the full concept documentation in the GL module. +using gl::traits::c_predicate; + +/// @ingroup HGL-Traits +/// @brief Concept allowing either a valid boolean predicate or the explicit absence of one through the use of empty_callback. +/// ### See Also +/// - [**c_optional_predicate**](gl_concepts.md#gl-traits-c-optional-predicate) : For the full concept documentation in the GL module. +using gl::traits::c_optional_predicate; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a predicate returning a decision. +/// ### See Also +/// - [**c_decision_predicate**](gl_concepts.md#gl-traits-c-decision-predicate) : For the full concept documentation in the GL module. +using gl::traits::c_decision_predicate; + +/// @ingroup HGL-Traits +/// @brief Concept allowing either a valid decision predicate or the explicit absence of one. +/// ### See Also +/// - [**c_optional_decision_predicate**](gl_concepts.md#gl-traits-c-optional-decision-predicate) : For the full concept documentation in the GL module. +using gl::traits::c_optional_decision_predicate; + /// @ingroup HGL-Traits /// @brief Validates if a type is a valid hypergraph search tree (a random access range of @ref hgl::algorithm::search_node "search_node"s). /// @tparam T The type to evaluate against the concept. @@ -174,12 +216,12 @@ struct traversal_policy; template struct traversal_policy { /// @brief Retrieves the hyperedges incident to the given vertex. - static auto target_hyperedges(const H& h, typename H::id_type v_id) { + static auto target_hyperedges(const val_t& h, id_t v_id) { return h.incident_hyperedge_ids(v_id); } /// @brief Retrieves the vertices incident to the given hyperedge. - static auto target_vertices(const H& h, typename H::id_type he_id) { + static auto target_vertices(const val_t& h, id_t he_id) { return h.incident_vertex_ids(he_id); } }; @@ -189,12 +231,12 @@ struct traversal_policy { template struct traversal_policy { /// @brief Retrieves the hyperedges originating from the given vertex (forward star). - static auto target_hyperedges(const H& h, typename H::id_type v_id) { + static auto target_hyperedges(const val_t& h, id_t v_id) { return h.out_hyperedge_ids(v_id); // forward star } /// @brief Retrieves the vertices targeted by the given hyperedge (head nodes). - static auto target_vertices(const H& h, typename H::id_type he_id) { + static auto target_vertices(const val_t& h, id_t he_id) { return h.head_ids(he_id); } }; @@ -204,12 +246,12 @@ struct traversal_policy { template struct traversal_policy { /// @brief Retrieves the hyperedges entering the given vertex (backward star). - static auto target_hyperedges(const H& h, typename H::id_type v_id) { + static auto target_hyperedges(const val_t& h, id_t v_id) { return h.in_hyperedge_ids(v_id); // backward star } /// @brief Retrieves the vertices originating the given hyperedge (tail nodes). - static auto target_vertices(const H& h, typename H::id_type he_id) { + static auto target_vertices(const val_t& h, id_t he_id) { return h.tail_ids(he_id); } }; diff --git a/include/hgl/algorithm/properties.hpp b/include/hgl/algorithm/properties.hpp index 97feb823..490b2fd1 100644 --- a/include/hgl/algorithm/properties.hpp +++ b/include/hgl/algorithm/properties.hpp @@ -16,21 +16,21 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Calculates the maximum degree among all vertices in a hypergraph. -[[nodiscard]] size_type max_degree(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] size_type max_degree(traits::c_hypergraph auto&& hypergraph) noexcept { const auto degrees = hypergraph.degree_map(); return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); } /// @ingroup HGL-Algorithm /// @brief Calculates the minimum degree among all vertices in a hypergraph. -[[nodiscard]] size_type min_degree(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] size_type min_degree(traits::c_hypergraph auto&& hypergraph) noexcept { const auto degrees = hypergraph.degree_map(); return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); } /// @ingroup HGL-Algorithm /// @brief Calculates the maximum out-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +[[nodiscard]] size_type max_out_degree(traits::c_bf_directed_hypergraph auto&& hypergraph ) noexcept { const auto degrees = hypergraph.out_degree_map(); return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); @@ -38,7 +38,7 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Calculates the minimum out-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +[[nodiscard]] size_type min_out_degree(traits::c_bf_directed_hypergraph auto&& hypergraph ) noexcept { const auto degrees = hypergraph.out_degree_map(); return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); @@ -46,16 +46,14 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Calculates the maximum in-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type max_in_degree(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto degrees = hypergraph.in_degree_map(); return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); } /// @ingroup HGL-Algorithm /// @brief Calculates the minimum in-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type min_in_degree(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto degrees = hypergraph.in_degree_map(); return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); } @@ -64,46 +62,42 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Calculates the rank (maximum size of any hyperedge) of a hypergraph. -[[nodiscard]] size_type rank(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] size_type rank(traits::c_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.hyperedge_size_map(); return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); } /// @ingroup HGL-Algorithm /// @brief Calculates the corank (minimum size of any hyperedge) of a hypergraph. -[[nodiscard]] size_type corank(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] size_type corank(traits::c_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.hyperedge_size_map(); return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); } /// @ingroup HGL-Algorithm /// @brief Calculates the maximum tail size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type max_tail_size(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.tail_size_map(); return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); } /// @ingroup HGL-Algorithm /// @brief Calculates the minimum tail size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type min_tail_size(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.tail_size_map(); return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); } /// @ingroup HGL-Algorithm /// @brief Calculates the maximum head size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type max_head_size(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.head_size_map(); return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); } /// @ingroup HGL-Algorithm /// @brief Calculates the minimum head size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] size_type min_head_size(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { const auto sizes = hypergraph.head_size_map(); return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); } @@ -112,44 +106,41 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given hypergraph is $k$-regular (all vertices have a degree of $k$). -[[nodiscard]] bool is_regular( - const traits::c_hypergraph auto& hypergraph, const size_type k -) noexcept { +[[nodiscard]] bool is_regular(traits::c_hypergraph auto&& hypergraph, const size_type k) noexcept { return util::all_equal(hypergraph.degree_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given hypergraph is structurally regular (all vertices have the same degree). -[[nodiscard]] bool is_regular(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] bool is_regular(traits::c_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.degree_map()); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is out-$k$-regular (all vertices have an out-degree of $k$). [[nodiscard]] bool is_out_regular( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k + traits::c_bf_directed_hypergraph auto&& hypergraph, const size_type k ) noexcept { return util::all_equal(hypergraph.out_degree_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is out-regular (all vertices have the same out-degree). -[[nodiscard]] bool is_out_regular(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] bool is_out_regular(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.out_degree_map()); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is in-$k$-regular (all vertices have an in-degree of $k$). [[nodiscard]] bool is_in_regular( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k + traits::c_bf_directed_hypergraph auto&& hypergraph, const size_type k ) noexcept { return util::all_equal(hypergraph.in_degree_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is in-regular (all vertices have the same in-degree). -[[nodiscard]] bool is_in_regular(const traits::c_bf_directed_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] bool is_in_regular(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.in_degree_map()); } @@ -157,45 +148,41 @@ namespace hgl::algorithm { /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given hypergraph is $k$-uniform (all hyperedges have a size of $k$). -[[nodiscard]] bool is_uniform( - const traits::c_hypergraph auto& hypergraph, const size_type k -) noexcept { +[[nodiscard]] bool is_uniform(traits::c_hypergraph auto&& hypergraph, const size_type k) noexcept { return util::all_equal(hypergraph.hyperedge_size_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given hypergraph is structurally uniform (all hyperedges have the exact same size). -[[nodiscard]] bool is_uniform(const traits::c_hypergraph auto& hypergraph) noexcept { +[[nodiscard]] bool is_uniform(traits::c_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.hyperedge_size_map()); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is tail-$k$-uniform (all hyperedges have a tail size of $k$). [[nodiscard]] bool is_tail_uniform( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k + traits::c_bf_directed_hypergraph auto&& hypergraph, const size_type k ) noexcept { return util::all_equal(hypergraph.tail_size_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is tail-uniform (all hyperedges have the exact same tail size). -[[nodiscard]] bool is_tail_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] bool is_tail_uniform(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.tail_size_map()); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is head-$k$-uniform (all hyperedges have a head size of $k$). [[nodiscard]] bool is_head_uniform( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k + traits::c_bf_directed_hypergraph auto&& hypergraph, const size_type k ) noexcept { return util::all_equal(hypergraph.head_size_map(), k); } /// @ingroup HGL-Algorithm /// @brief Evaluates whether the given *BF-directed* hypergraph is head-uniform (all hyperedges have the exact same head size). -[[nodiscard]] bool is_head_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { +[[nodiscard]] bool is_head_uniform(traits::c_bf_directed_hypergraph auto&& hypergraph) noexcept { return util::is_constant(hypergraph.head_size_map()); } diff --git a/include/hgl/algorithm/templates/bfs.hpp b/include/hgl/algorithm/templates/bfs.hpp index 09726100..9ceb9cef 100644 --- a/include/hgl/algorithm/templates/bfs.hpp +++ b/include/hgl/algorithm/templates/bfs.hpp @@ -57,13 +57,13 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Dir | The @ref hgl::algorithm::traversal_direction "traversal direction" (i.e., `forward` or `backward`). Relevant only for BF-directed hypergraphs. | Defaults to `forward`. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | InitQueueRangeType | A forward range of `search_node` used to prime the BFS queue. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | -/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | InitQueueRangeType | A forward range of `search_node>` used to prime the BFS queue. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | +/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node>&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node>&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// | TraverseHePredicate | Type of the callable deciding if an incident hyperedge should be traversed. | Must be one of:
- An `(id_type, id_type) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be enqueued via a specific hyperedge. | Must be one of:
- A `(const search_node&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be enqueued via a specific hyperedge. | Must be one of:
- A `(const search_node>&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param initial_queue_content The initial set of search nodes to begin the traversal from. @@ -84,16 +84,18 @@ namespace hgl::algorithm { template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, - traits::c_forward_range_of> InitQueueRangeType = std::vector>, - traits::c_optional_predicate&> VisitPredicate = empty_callback, - traits::c_optional_predicate&> VisitCallback = empty_callback, - traits::c_optional_decision_predicate - TraverseHePredicate = empty_callback, - traits::c_decision_predicate&> EnqueuePredicate = empty_callback, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of>> InitQueueRangeType = + std::vector>>, + traits::c_optional_predicate>&> VisitPredicate = empty_callback, + traits::c_optional_predicate>&> VisitCallback = empty_callback, + traits::c_optional_decision_predicate, id_t> TraverseHePredicate = empty_callback, + traits::c_decision_predicate>&> EnqueuePredicate = empty_callback, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> bool bfs( - const H& hypergraph, + H&& hypergraph, const InitQueueRangeType& initial_queue_content, const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, @@ -107,7 +109,7 @@ bool bfs( if (std::ranges::empty(initial_queue_content)) return false; - std::queue> q; + std::queue>> q; for (const auto& node : initial_queue_content) q.push(node); @@ -139,7 +141,7 @@ bool bfs( if (target_id == curr_node.vertex_id) continue; - search_node tgt_node{target_id, curr_node.vertex_id, he_id}; + search_node> tgt_node{target_id, curr_node.vertex_id, he_id}; const auto enqueue = enqueue_pred(tgt_node); if (enqueue == decision::abort) return false; diff --git a/include/hgl/algorithm/templates/dfs.hpp b/include/hgl/algorithm/templates/dfs.hpp index 8c697a0c..29e1a0c0 100644 --- a/include/hgl/algorithm/templates/dfs.hpp +++ b/include/hgl/algorithm/templates/dfs.hpp @@ -57,13 +57,13 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Dir | The @ref hgl::algorithm::traversal_direction "traversal direction" (i.e., `forward` or `backward`). Relevant only for BF-directed hypergraphs. | Defaults to `forward`. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | InitQueueRangeType | A forward range of `search_node` used to prime the DFS stack. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | -/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | InitQueueRangeType | A forward range of `search_node>` used to prime the DFS stack. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | +/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node>&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node>&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// | TraverseHePredicate | Type of the callable deciding if an incident hyperedge should be traversed. | Must be one of:
- An `(id_type, id_type) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be pushed to the stack via a specific hyperedge. | Must be one of:
- A `(const search_node&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be pushed to the stack via a specific hyperedge. | Must be one of:
- A `(const search_node>&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param initial_queue_content The initial set of search nodes to begin the traversal from. @@ -84,16 +84,18 @@ namespace hgl::algorithm { template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, - traits::c_forward_range_of> InitQueueRangeType = std::vector>, - traits::c_optional_predicate&> VisitPredicate = empty_callback, - traits::c_optional_predicate&> VisitCallback = empty_callback, - traits::c_optional_decision_predicate - TraverseHePredicate = empty_callback, - traits::c_decision_predicate&> EnqueuePredicate = empty_callback, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of>> InitQueueRangeType = + std::vector>>, + traits::c_optional_predicate>&> VisitPredicate = empty_callback, + traits::c_optional_predicate>&> VisitCallback = empty_callback, + traits::c_optional_decision_predicate, id_t> TraverseHePredicate = empty_callback, + traits::c_decision_predicate>&> EnqueuePredicate = empty_callback, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> bool dfs( - const H& hypergraph, + H&& hypergraph, const InitQueueRangeType& initial_queue_content, const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, @@ -107,7 +109,7 @@ bool dfs( if (std::ranges::empty(initial_queue_content)) return false; - std::stack> s; + std::stack>> s; for (const auto& node : initial_queue_content) s.push(node); @@ -139,7 +141,7 @@ bool dfs( if (target_id == curr_node.vertex_id) continue; - search_node tgt_node{target_id, curr_node.vertex_id, he_id}; + search_node> tgt_node{target_id, curr_node.vertex_id, he_id}; const auto enqueue = enqueue_pred(tgt_node); if (enqueue == decision::abort) return false; diff --git a/include/hgl/algorithm/traversal/backward_search.hpp b/include/hgl/algorithm/traversal/backward_search.hpp index 64ac2e26..646fdac1 100644 --- a/include/hgl/algorithm/traversal/backward_search.hpp +++ b/include/hgl/algorithm/traversal/backward_search.hpp @@ -46,8 +46,8 @@ namespace hgl::algorithm { /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | /// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | -/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The bf-directed hypergraph to traverse. /// @param root_vertices A range of initial vertex IDs to start the search from. @@ -58,24 +58,25 @@ namespace hgl::algorithm { template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, - traits::c_forward_range_of RootRange = std::vector, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of> RootRange = std::vector>, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> backward_bfs( - const H& hypergraph, + H&& hypergraph, const RootRange& root_vertices, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { - using id_type = typename H::id_type; - std::vector visited_vertices(hypergraph.n_vertices(), false); auto tail_unvisited = hypergraph.tail_size_map() | std::ranges::to(); auto stree = init_search_tree(hypergraph); auto root_queue = - root_vertices - | std::views::transform([](const id_type root_id) { return search_node{root_id}; }); + root_vertices | std::views::transform([](const id_t root_id) { + return search_node>{root_id}; + }); // clang-format off @@ -127,8 +128,8 @@ result_type> backward_bfs( /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | /// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | -/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The bf-directed hypergraph to traverse. /// @param root_vertices A range of initial vertex IDs to start the search from. @@ -139,24 +140,25 @@ result_type> backward_bfs( template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, - traits::c_forward_range_of RootRange = std::vector, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of> RootRange = std::vector>, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> backward_dfs( - const H& hypergraph, + H&& hypergraph, const RootRange& root_vertices, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { - using id_type = typename H::id_type; - std::vector visited_vertices(hypergraph.n_vertices(), false); auto tail_unvisited = hypergraph.tail_size_map() | std::ranges::to(); auto stree = init_search_tree(hypergraph); auto root_queue = - root_vertices - | std::views::transform([](const id_type root_id) { return search_node{root_id}; }); + root_vertices | std::views::transform([](const id_t root_id) { + return search_node>{root_id}; + }); // clang-format off diff --git a/include/hgl/algorithm/traversal/breadth_first_search.hpp b/include/hgl/algorithm/traversal/breadth_first_search.hpp index 41524e40..1b886b1e 100644 --- a/include/hgl/algorithm/traversal/breadth_first_search.hpp +++ b/include/hgl/algorithm/traversal/breadth_first_search.hpp @@ -46,8 +46,8 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. @@ -58,11 +58,13 @@ namespace hgl::algorithm { template < result_discriminator Result = ret, traits::c_hypergraph H, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> breadth_first_search( - const H& hypergraph, - const typename H::id_type root_vertex_id = no_root, + H&& hypergraph, + const id_t root_vertex_id = no_root, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { diff --git a/include/hgl/algorithm/traversal/depth_first_search.hpp b/include/hgl/algorithm/traversal/depth_first_search.hpp index 89e5b263..be7b66b4 100644 --- a/include/hgl/algorithm/traversal/depth_first_search.hpp +++ b/include/hgl/algorithm/traversal/depth_first_search.hpp @@ -46,8 +46,8 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. @@ -58,11 +58,13 @@ namespace hgl::algorithm { template < result_discriminator Result = ret, traits::c_hypergraph H, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> depth_first_search( - const H& hypergraph, - const typename H::id_type root_vertex_id = no_root, + H&& hypergraph, + const id_t root_vertex_id = no_root, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { diff --git a/include/hgl/algorithm/traversal/forward_search.hpp b/include/hgl/algorithm/traversal/forward_search.hpp index 1aa4e830..72a6ab10 100644 --- a/include/hgl/algorithm/traversal/forward_search.hpp +++ b/include/hgl/algorithm/traversal/forward_search.hpp @@ -46,8 +46,8 @@ namespace hgl::algorithm { /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | /// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | -/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The bf-directed hypergraph to traverse. /// @param root_vertices A range of initial vertex IDs to start the search from. @@ -58,24 +58,25 @@ namespace hgl::algorithm { template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, - traits::c_forward_range_of RootRange = std::vector, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of> RootRange = std::vector>, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> forward_bfs( - const H& hypergraph, + H&& hypergraph, const RootRange& root_vertices, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { - using id_type = typename H::id_type; - std::vector visited_vertices(hypergraph.n_vertices(), false); auto head_unvisited = hypergraph.head_size_map() | std::ranges::to(); auto stree = init_search_tree(hypergraph); auto root_queue = - root_vertices - | std::views::transform([](const id_type root_id) { return search_node{root_id}; }); + root_vertices | std::views::transform([](const id_t root_id) { + return search_node>{root_id}; + }); // clang-format off @@ -127,8 +128,8 @@ result_type> forward_bfs( /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | /// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | -/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | -/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node>&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The bf-directed hypergraph to traverse. /// @param root_vertices A range of initial vertex IDs to start the search from. @@ -139,24 +140,25 @@ result_type> forward_bfs( template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, - traits::c_forward_range_of RootRange = std::vector, - traits::c_optional_callback&> PreVisitCallback = empty_callback, - traits::c_optional_callback&> PostVisitCallback = empty_callback> + traits::c_forward_range_of> RootRange = std::vector>, + traits::c_optional_callback>&> PreVisitCallback = + empty_callback, + traits::c_optional_callback>&> PostVisitCallback = + empty_callback> result_type> forward_dfs( - const H& hypergraph, + H&& hypergraph, const RootRange& root_vertices, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { - using id_type = typename H::id_type; - std::vector visited_vertices(hypergraph.n_vertices(), false); auto head_unvisited = hypergraph.head_size_map() | std::ranges::to(); auto stree = init_search_tree(hypergraph); auto root_queue = - root_vertices - | std::views::transform([](const id_type root_id) { return search_node{root_id}; }); + root_vertices | std::views::transform([](const id_t root_id) { + return search_node>{root_id}; + }); // clang-format off diff --git a/include/hgl/algorithm/util.hpp b/include/hgl/algorithm/util.hpp index 003d42a5..0301a404 100644 --- a/include/hgl/algorithm/util.hpp +++ b/include/hgl/algorithm/util.hpp @@ -19,10 +19,9 @@ namespace hgl::algorithm { /// @param hypergraph The hypergraph instance to size the search tree against. /// @return A fully sized and initialized `search_tree` if `Result == ret`, otherwise a dummy `std::monostate`. template -[[nodiscard]] gl_attr_force_inline non_void_result_type> init_search_tree( - const H& hypergraph -) { - using return_t = non_void_result_type>; +[[nodiscard]] gl_attr_force_inline non_void_result_type>> +init_search_tree(H&& hypergraph) { + using return_t = non_void_result_type>>; if constexpr (Result == ret) return return_t(hypergraph.n_vertices()); else @@ -46,10 +45,10 @@ template /// @param root_vertex_id The ID of the starting vertex. /// @return A `std::vector` containing a single root @ref hgl::algorithm::search_node "search_node". template -[[nodiscard]] gl_attr_force_inline std::vector> init_node_range( - typename H::id_type root_vertex_id +[[nodiscard]] gl_attr_force_inline std::vector>> init_node_range( + id_t root_vertex_id ) { - return std::vector>{search_node{root_vertex_id}}; + return {search_node>{root_vertex_id}}; } /// @ingroup HGL-Algorithm @@ -59,7 +58,7 @@ template /// @return A callable predicate that returns `true` if the vertex in the node has not been visited, `false` otherwise. template [[nodiscard]] gl_attr_force_inline auto default_visit_predicate(std::vector& visited_v) { - return [&](const search_node& node) -> bool { + return [&](const search_node>& node) -> bool { return not visited_v[to_idx(node.vertex_id)]; }; } @@ -77,9 +76,9 @@ template /// @hideparams template [[nodiscard]] gl_attr_force_inline auto default_visit_callback( - std::vector& visited_v, non_void_result_type>& pred_map + std::vector& visited_v, non_void_result_type>>& pred_map ) { - return [&](const search_node& node) { + return [&](const search_node>& node) { const auto vertex_idx = to_idx(node.vertex_id); visited_v[vertex_idx] = true; if constexpr (Result == ret) @@ -134,7 +133,7 @@ template template [[nodiscard]] gl_attr_force_inline auto default_enqueue_predicate(std::vector& visited_v) { using return_t = std::conditional_t; - return [&](const search_node& node) -> return_t { + return [&](const search_node>& node) -> return_t { return return_t(not visited_v[to_idx(node.vertex_id)]); }; } diff --git a/include/hgl/constants.hpp b/include/hgl/constants.hpp index e2a7dbb1..5905b5bb 100644 --- a/include/hgl/constants.hpp +++ b/include/hgl/constants.hpp @@ -9,6 +9,7 @@ #include "gl/constants.hpp" #include "hgl/traits.hpp" +#include "hgl/types.hpp" namespace hgl { diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index 7f4b0076..8d310b1f 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -225,7 +225,7 @@ struct to_impl, repr::flat_list_t> { /// | Parameter | Description | Constraint | /// | :-------- | :---------- | :--------- | /// | TargetReprTag | The representation tag of the desired target representation (e.g., `hgl::repr::flat_list_t`). | [**c_hypergraph_repr_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-repr-tag) | -/// | Hypergraph | The type of the source hypergraph, which will be automatically deduced from the function argument. | [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) | +/// | Hypergraph | The type of the source hypergraph, which will be automatically deduced from the function argument. | [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) and must **NOT** be an Lvalue reference | /// /// @param source The hypergraph to convert. After the operation it will be left in a valid, empty state. /// @return A new hypergraph containing the moved data, structured according to `TargetReprTag`. @@ -233,6 +233,7 @@ struct to_impl, repr::flat_list_t> { /// ### See Also /// - @ref hgl::traits::swap_repr_tag "swap_repr_tag" : For the trait used to resolve the target hypergraph type with the swapped representation tag. template +requires(not std::is_lvalue_reference_v) [[nodiscard]] auto to(Hypergraph&& source) { using source_traits = typename Hypergraph::traits_type; using source_impl_tag = typename source_traits::representation_tag; diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index 0d9b89d2..0cd5551d 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -36,7 +36,65 @@ namespace traits { /// @brief Concept checking if a type is an instantiation of the generic @ref hgl::hypergraph "hypergraph" class. /// @tparam H The type to evaluate against the concept. template -concept c_hypergraph = c_instantiation_of; +concept c_hypergraph = c_instantiation_of, hypergraph>; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a constant instantiation of the generic @ref hgl::hypergraph "hypergraph" class. +/// @tparam H The type to evaluate against the concept. +template +concept c_const_hypergraph = c_hypergraph and std::is_const_v>; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a mutable instantiation of the generic @ref hgl::hypergraph "hypergraph" class. +/// @tparam H The type to evaluate against the concept. +template +concept c_mut_hypergraph = c_hypergraph and not std::is_const_v>; + +} // namespace traits + +/// @ingroup HGL-Core +/// @brief Extracts the underlying unqualified hypergraph type by removing reference and cv-qualifiers. +template +using val_t = std::remove_cvref_t; + +/// @ingroup HGL-Core +/// @brief Resolves the identifier type associated with the given hypergraph type. +template +using id_t = typename val_t::id_type; + +/// @ingroup HGL-Core +/// @brief Resolves the appropriate vertex descriptor type (mutable or const) based on the hypergraph's constness. +template +using vertex_t = std::conditional_t< + std::is_const_v>, + typename val_t::const_vertex_type, + typename val_t::vertex_type>; + +/// @ingroup HGL-Core +/// @brief Resolves the appropriate vertex properties type (mutable or const) based on the hypergraph's constness. +template +using vertex_properties_t = std::conditional_t< + std::is_const_v>, + const typename val_t::vertex_properties_type, + typename val_t::vertex_properties_type>; + +/// @ingroup HGL-Core +/// @brief Resolves the appropriate hyperedge descriptor type (mutable or const) based on the hypergraph's constness. +template +using hyperedge_t = std::conditional_t< + std::is_const_v>, + typename val_t::const_hyperedge_type, + typename val_t::hyperedge_type>; + +/// @ingroup HGL-Core +/// @brief Resolves the appropriate hyperedge properties type (mutable or const) based on the hypergraph's constness. +template +using hyperedge_properties_t = std::conditional_t< + std::is_const_v>, + const typename val_t::hyperedge_properties_type, + typename val_t::hyperedge_properties_type>; + +namespace traits { /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph is undirected. @@ -44,7 +102,7 @@ concept c_hypergraph = c_instantiation_of; /// @tparam H The type to evaluate against the concept. template concept c_undirected_hypergraph = - c_hypergraph and std::same_as; + c_hypergraph and std::same_as::directional_tag, undirected_t>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph is backward-forward (bf) directed. @@ -52,49 +110,80 @@ concept c_undirected_hypergraph = /// @tparam H The type to evaluate against the concept. template concept c_bf_directed_hypergraph = - c_hypergraph and std::same_as; + c_hypergraph and std::same_as::directional_tag, bf_directed_t>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses a standard incidence list representation. /// @tparam H The type to evaluate against the concept. template concept c_list_hypergraph = - c_hypergraph and c_hypergraph_list_repr; + c_hypergraph and c_hypergraph_list_repr::representation_tag>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses a flattened incidence list representation. /// @tparam H The type to evaluate against the concept. template concept c_flat_list_hypergraph = - c_hypergraph and c_hypergraph_flat_list_repr; + c_hypergraph and c_hypergraph_flat_list_repr::representation_tag>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses any incidence list representation (standard or flattened). /// @tparam H The type to evaluate against the concept. template concept c_incidence_list_hypergraph = - c_hypergraph and c_hypergraph_incidence_list_repr; + c_hypergraph and c_hypergraph_incidence_list_repr::representation_tag>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses a standard incidence matrix representation. /// @tparam H The type to evaluate against the concept. template concept c_matrix_hypergraph = - c_hypergraph and c_hypergraph_matrix_repr; + c_hypergraph and c_hypergraph_matrix_repr::representation_tag>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses a flattened incidence matrix representation. /// @tparam H The type to evaluate against the concept. template concept c_flat_matrix_hypergraph = - c_hypergraph and c_hypergraph_flat_matrix_repr; + c_hypergraph and c_hypergraph_flat_matrix_repr::representation_tag>; /// @ingroup HGL-Traits /// @brief Concept checking if a hypergraph uses any incidence matrix representation (standard or flattened). /// @tparam H The type to evaluate against the concept. template concept c_incidence_matrix_hypergraph = - c_hypergraph and c_hypergraph_incidence_matrix_repr; + c_hypergraph and c_hypergraph_incidence_matrix_repr::representation_tag>; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a mutable or immutable vertex descriptor associated with the given hypergraph. +/// @tparam V The type of the vertex descriptor. +/// @tparam H The type of the hypergraph. +template +concept c_vertex = + c_hypergraph + and c_one_of< + std::remove_cvref_t, + typename val_t::vertex_type, + typename val_t::const_vertex_type>; + +template +concept c_vertex_forward_range = c_forward_range and c_vertex, H>; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a mutable or immutable hyperedge descriptor associated with the given hypergraph. +/// @tparam E The type of the hyperedge descriptor. +/// @tparam H The type of the hypergraph. +template +concept c_hyperedge = + c_hypergraph + and c_one_of< + std::remove_cvref_t, + typename val_t::hyperedge_type, + typename val_t::const_hyperedge_type>; + +template +concept c_hyperedge_forward_range = + c_forward_range and c_hyperedge, H>; } // namespace traits @@ -113,6 +202,7 @@ template /// @param source The hypergraph to convert. /// @return A new hypergraph matching the target representation type with identical topology and properties. template +requires(not std::is_lvalue_reference_v) [[nodiscard]] auto to(Hypergraph&& source); namespace detail { @@ -236,33 +326,38 @@ class hypergraph final { /// @brief Type tag indicating the underlying representation model. using representation_tag = typename traits_type::representation_tag; - /// @brief The underlying representation type matching the directional tag. - using representation_type = - typename representation_tag::template representation_type; - /// @brief Integral type used to identify vertices and hyperedges. using id_type = typename traits_type::id_type; /// @brief The descriptor type representing a vertex. using vertex_type = typename traits_type::vertex_type; + /// @brief The descriptor type representing an immutable vertex. + using const_vertex_type = typename traits_type::const_vertex_type; /// @brief The user-defined property payload type associated with vertices. using vertex_properties_type = typename traits_type::vertex_properties_type; - /// @brief The container type used for storing the vertex properties mapping. - using vertex_properties_map_type = std::conditional_t< - traits::c_empty_properties, - empty_properties_map, - std::vector>; /// @brief The descriptor type representing a hyperedge. using hyperedge_type = typename traits_type::hyperedge_type; + /// @brief The descriptor type representing an immutable hyperedge. + using const_hyperedge_type = typename traits_type::const_hyperedge_type; /// @brief The user-defined property payload type associated with hyperedges. using hyperedge_properties_type = typename traits_type::hyperedge_properties_type; - /// @brief The container type used for storing hyperedge properties mapping. + +private: + using representation_type = + typename representation_tag::template representation_type; + + using vertex_properties_map_type = std::conditional_t< + traits::c_empty_properties, + empty_properties_map, + std::vector>; + using hyperedge_properties_map_type = std::conditional_t< traits::c_empty_properties, empty_properties_map, std::vector>; +public: /// @brief Constructs a hypergraph with the given number of vertices and hyperedges (empty by default). /// @param n_vertices The initial number of vertices. /// @param n_hyperedges The initial number of hyperedges. @@ -380,7 +475,7 @@ class hypergraph final { /// @param vertex The descriptor of the vertex to remove. /// @throws std::invalid_argument If the vertex descriptor is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() - gl_attr_force_inline void remove_vertex(vertex_type vertex) { + gl_attr_force_inline void remove_vertex(traits::c_vertex auto vertex) { this->remove_vertex(vertex.id()); } @@ -389,29 +484,27 @@ class hypergraph final { /// @throws std::invalid_argument If any vertex ID in the range is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() void remove_vertices(const traits::c_forward_range_of auto& vertex_id_rng) { - // sorts ids in a descending n_vertices and removes duplicate ids - std::set> vertex_id_set( - std::ranges::begin(vertex_id_rng), std::ranges::end(vertex_id_rng) - ); + auto vertex_ids = vertex_id_rng | std::ranges::to(); + // Sort in descending order and remove duplicates to prevent index shifting bugs during erasure + std::ranges::sort(vertex_ids, std::greater<>{}); + vertex_ids.erase(std::ranges::unique(vertex_ids).begin(), vertex_ids.end()); - // TODO: optimize - for (const auto vertex_id : vertex_id_set) + if (not vertex_ids.empty()) + this->_verify_vertex_id(vertex_ids.front()); + + for (const auto vertex_id : vertex_ids) this->_remove_vertex_impl(vertex_id); } /// @brief Removes a range of vertices using their descriptors. + /// @tparam VertexRng A forward range type containing hypergraph's vertex descriptors. /// @param vertex_rng A forward range containing the descriptors of vertices to remove. /// @throws std::invalid_argument If any vertex descriptor is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() - void remove_vertices(const traits::c_forward_range_of auto& vertex_rng) { - // sort vertices in a descending n_vertices (by id) and removes duplicate ids - std::set> vertex_set( - std::ranges::begin(vertex_rng), std::ranges::end(vertex_rng) - ); - - // TODO: optimize - for (const auto& vertex : vertex_set) - this->_remove_vertex_impl(vertex.id()); + gl_attr_force_inline void remove_vertices( + const traits::c_vertex_forward_range auto& vertex_rng + ) { + this->remove_vertices(vertex_rng | std::views::transform(util::to_id)); } // --- vertex getters --- @@ -424,9 +517,10 @@ class hypergraph final { } /// @brief Checks if the vertex referenced by the provided descriptor exists in the hypergraph. - /// @param vertex The descriptor to check. + /// @param vertex The vertex descriptor to check. /// @return `true` if the vertex exists, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool has_vertex(vertex_type vertex) const { + [[nodiscard]] gl_attr_force_inline bool has_vertex(traits::c_vertex auto vertex + ) const { return this->has_vertex(vertex.id()); } @@ -434,9 +528,10 @@ class hypergraph final { /// @param vertex_id The ID of the vertex. /// @return The corresponding vertex descriptor. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] vertex_type vertex(const id_type vertex_id) const { - this->_verify_vertex_id(vertex_id); - return this->vertex_unchecked(vertex_id); + template + [[nodiscard]] vertex_t vertex(this Self& self, const id_type vertex_id) { + self._verify_vertex_id(vertex_id); + return self.vertex_unchecked(vertex_id); } /// @brief Returns a descriptor of the vertex with the given bounds-checked ID. @@ -450,8 +545,11 @@ class hypergraph final { /// @param vertex_id The ID of the vertex. /// @return The corresponding `vertex_descriptor`. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] gl_attr_force_inline vertex_type at(vertex_t, const id_type vertex_id) const { - return this->vertex(vertex_id); + template + [[nodiscard]] gl_attr_force_inline vertex_t at( + this Self& self, vertex_tag, const id_type vertex_id + ) { + return self.vertex(vertex_id); } /// @brief Returns a descriptor of the vertex with the given ID without bounds checking. @@ -461,11 +559,14 @@ class hypergraph final { /// > [!WARNING] Undefined Behavior /// > /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. - [[nodiscard]] gl_attr_force_inline vertex_type vertex_unchecked(const id_type vertex_id) const { + template + [[nodiscard]] gl_attr_force_inline vertex_t vertex_unchecked( + this Self& self, const id_type vertex_id + ) noexcept { if constexpr (traits::c_non_empty_properties) - return vertex_type{vertex_id, this->_vertex_properties[vertex_id]}; + return vertex_t{vertex_id, self._vertex_properties[vertex_id]}; else - return vertex_type{vertex_id}; + return vertex_t{vertex_id}; } /// @brief Returns a descriptor of the vertex with the given ID without bounds checking. @@ -481,15 +582,17 @@ class hypergraph final { /// > [!WARNING] Undefined Behavior /// > /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. - [[nodiscard]] gl_attr_force_inline vertex_type - operator[](vertex_t, const id_type vertex_id) const { - return this->vertex_unchecked(vertex_id); + template + [[nodiscard]] gl_attr_force_inline vertex_t operator[]( + this Self& self, vertex_tag, const id_type vertex_id + ) noexcept { + return self.vertex_unchecked(vertex_id); } /// @brief Returns a lazily evaluated, random-access view of all vertex descriptors in the hypergraph. /// @return A view yielding descriptors for every vertex. - [[nodiscard]] gl_attr_force_inline auto vertices() const noexcept { - return this->vertex_ids() | std::views::transform(this->_create_vertex_descriptor()); + [[nodiscard]] gl_attr_force_inline auto vertices(this auto& self) noexcept { + return self.vertex_ids() | std::views::transform(self._create_vertex_descriptor()); } /// @brief Returns a lazily evaluated, random-access view of all active vertex IDs in the hypergraph. @@ -502,21 +605,22 @@ class hypergraph final { /// @param vertex_id The ID of the vertex. /// @return A mutable reference to the vertex's properties. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] gl_attr_force_inline vertex_properties_type& vertex_properties( - const id_type vertex_id - ) const + template + [[nodiscard]] gl_attr_force_inline vertex_properties_t& vertex_properties( + this Self& self, const id_type vertex_id + ) requires(traits::c_non_empty_properties) { - this->_verify_vertex_id(vertex_id); - return this->_vertex_properties[vertex_id]; + self._verify_vertex_id(vertex_id); + return self._vertex_properties[vertex_id]; } /// @brief Retrieves a lazily evaluated, random-access view over all vertex properties in the hypergraph. /// @return A view mapping each active vertex index to its property. - [[nodiscard]] gl_attr_force_inline auto vertex_properties_map() const noexcept + [[nodiscard]] gl_attr_force_inline auto vertex_properties_map(this auto& self) noexcept requires(traits::c_non_empty_properties) { - return std::views::all(this->_vertex_properties); + return std::views::all(self._vertex_properties); } // --- hyperedge modifiers --- @@ -575,17 +679,19 @@ class hypergraph final { /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type - add_hyperedge(const traits::c_forward_range_of auto& vertex_rng) + add_hyperedge(const traits::c_vertex_forward_range auto& vertex_rng) requires std::same_as { - return this->add_hyperedge(vertex_rng | std::views::transform(&vertex_type::id)); + return this->add_hyperedge(vertex_rng | std::views::transform(util::to_id)); } /// @brief Adds a new *undirected* hyperedge and immediately binds a list of vertices to it. + /// @tparam V The vertex descriptor type. /// @param vertices An initializer list of vertex descriptors to bind to the new hyperedge. /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() - gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertices) + template V> + gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertices) requires std::same_as { return this->add_hyperedge(std::views::all(vertices)); @@ -626,23 +732,25 @@ class hypergraph final { /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( - const traits::c_forward_range_of auto& vertex_rng, + const traits::c_vertex_forward_range auto& vertex_rng, hyperedge_properties_type properties ) requires(std::same_as and traits::c_non_empty_properties) { return this->add_hyperedge_with( - vertex_rng | std::views::transform(&vertex_type::id), std::move(properties) + vertex_rng | std::views::transform(util::to_id), std::move(properties) ); } /// @brief Adds a new *undirected* hyperedge with the given properties and immediately binds a list of vertices to it. + /// @tparam V The vertex descriptor type. /// @param vertices An initializer list of vertex descriptors to bind to the new hyperedge. /// @param properties The property payload for the new hyperedge. /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() + template V> hyperedge_type add_hyperedge_with( - std::initializer_list vertices, hyperedge_properties_type properties + std::initializer_list vertices, hyperedge_properties_type properties ) requires(std::same_as and traits::c_non_empty_properties) { @@ -658,7 +766,7 @@ class hypergraph final { const traits::c_forward_range_of auto& tail_id_rng, const traits::c_forward_range_of auto& head_id_rng ) - requires std::same_as + requires(std::same_as) { auto he = this->add_hyperedge(); this->bind_tail(tail_id_rng, he.id()); @@ -673,7 +781,7 @@ class hypergraph final { /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list tail_ids, std::initializer_list head_ids) - requires std::same_as + requires(std::same_as) { return this->add_hyperedge(std::views::all(tail_ids), std::views::all(head_ids)); } @@ -684,25 +792,28 @@ class hypergraph final { /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge( - const traits::c_forward_range_of auto& tail_rng, - const traits::c_forward_range_of auto& head_rng + const traits::c_vertex_forward_range auto& tail_rng, + const traits::c_vertex_forward_range auto& head_rng ) - requires std::same_as + requires(std::same_as) { return this->add_hyperedge( - tail_rng | std::views::transform(&vertex_type::id), - head_rng | std::views::transform(&vertex_type::id) + tail_rng | std::views::transform(util::to_id), + head_rng | std::views::transform(util::to_id) ); } /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @tparam VT The tail vertex descriptor type. + /// @tparam VH The head vertex descriptor type. /// @param tail An intializer list of vertex descriptors to bind to the new hyperedge's *tail*. /// @param head An intializer list of vertex descriptors to bind to the new hyperedge's *head*. /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() + template VT, traits::c_vertex VH> gl_attr_force_inline hyperedge_type - add_hyperedge(std::initializer_list tail, std::initializer_list head) - requires std::same_as + add_hyperedge(std::initializer_list tail, std::initializer_list head) + requires(std::same_as) { return this->add_hyperedge(std::views::all(tail), std::views::all(head)); } @@ -751,28 +862,31 @@ class hypergraph final { /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( - const traits::c_forward_range_of auto& tail_rng, - const traits::c_forward_range_of auto& head_rng, + const traits::c_vertex_forward_range auto& tail_rng, + const traits::c_vertex_forward_range auto& head_rng, hyperedge_properties_type properties ) requires(std::same_as and traits::c_non_empty_properties) { return this->add_hyperedge_with( - tail_rng | std::views::transform(&vertex_type::id), - head_rng | std::views::transform(&vertex_type::id), + tail_rng | std::views::transform(util::to_id), + head_rng | std::views::transform(util::to_id), std::move(properties) ); } /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @tparam VT The tail vertex descriptor type. + /// @tparam VH The head vertex descriptor type. /// @param tail An initializer list of vertex descriptor to bind to the new hyperedge's *tail*. /// @param head An initializer list of vertex descriptor to bind to the new hyperedge's *head*. /// @param properties The property payload for the new hyperedge. /// @return A descriptor of the newly created hyperedge. /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() + template VT, traits::c_vertex VH> gl_attr_force_inline hyperedge_type add_hyperedge_with( - std::initializer_list tail, - std::initializer_list head, + std::initializer_list tail, + std::initializer_list head, hyperedge_properties_type properties ) requires(std::same_as and traits::c_non_empty_properties) @@ -826,7 +940,7 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge to remove. /// @throws std::invalid_argument If the ID is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() - gl_attr_force_inline void remove_hyperedge(hyperedge_type hyperedge) { + gl_attr_force_inline void remove_hyperedge(traits::c_hyperedge auto hyperedge) { this->remove_hyperedge(hyperedge.id()); } @@ -834,14 +948,17 @@ class hypergraph final { /// @param hyperedge_id_rng A forward range containing the IDs of hyperedges to remove. /// @throws std::invalid_argument If any hyperedge ID in the range is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() - void remove_hyperedges_from(const traits::c_forward_range_of auto& hyperedge_id_rng) { - // sorts ids in a descending n_vertices and removes duplicate ids - std::set> hyperedge_id_set( - std::ranges::begin(hyperedge_id_rng), std::ranges::end(hyperedge_id_rng) - ); + void remove_hyperedges(const traits::c_forward_range_of auto& hyperedge_id_rng) { + auto hyperedge_ids = hyperedge_id_rng | std::ranges::to(); + // Sort in descending order and remove duplicates to prevent index shifting bugs during erasure + std::ranges::sort(hyperedge_ids, std::greater<>{}); + hyperedge_ids.erase(std::ranges::unique(hyperedge_ids).begin(), hyperedge_ids.end()); + + if (not hyperedge_ids.empty()) + this->_verify_hyperedge_id(hyperedge_ids.front()); // TODO: optimize - for (const auto hyperedge_id : hyperedge_id_set) + for (const auto hyperedge_id : hyperedge_ids) this->_remove_hyperedge_impl(hyperedge_id); } @@ -849,16 +966,10 @@ class hypergraph final { /// @param hyperedge_id_rng A forward range containing the descriptors of hyperedges to remove. /// @throws std::invalid_argument If any hyperedge ID in the range is invalid. /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() - void remove_hyperedges_from(const traits::c_forward_range_of auto& hyperedge_rng + gl_attr_force_inline void remove_hyperedges( + const traits::c_hyperedge_forward_range auto& hyperedge_rng ) { - // sort hyperedges in a descending n_vertices (by id) and removes duplicate ids - std::set> hyperedge_set( - std::ranges::begin(hyperedge_rng), std::ranges::end(hyperedge_rng) - ); - - // TODO: optimize - for (const auto& hyperedge : hyperedge_set) - this->_remove_hyperedge_impl(hyperedge.id()); + this->remove_hyperedges(hyperedge_rng | std::views::transform(util::to_id)); } // --- hyperedge getters --- @@ -873,7 +984,9 @@ class hypergraph final { /// @brief Checks if the hyperedge referenced by the provided descriptor exists in the hypergraph. /// @param hyperedge The descriptor to check. /// @return `true` if the hyperedge exists, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool has_hyperedge(hyperedge_type hyperedge) const { + [[nodiscard]] gl_attr_force_inline bool has_hyperedge( + traits::c_hyperedge auto hyperedge + ) const { return this->has_hyperedge(hyperedge.id()); } @@ -881,9 +994,10 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @return The corresponding hyperedge descriptor. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] hyperedge_type hyperedge(const id_type hyperedge_id) const { - this->_verify_hyperedge_id(hyperedge_id); - return this->hyperedge_unchecked(hyperedge_id); + template + [[nodiscard]] hyperedge_t hyperedge(this Self& self, const id_type hyperedge_id) { + self._verify_hyperedge_id(hyperedge_id); + return self.hyperedge_unchecked(hyperedge_id); } /// @brief Returns a descriptor of the hyperedge with the given *bounds-checked* ID. @@ -897,9 +1011,11 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @return The corresponding hyperedge descriptor. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] gl_attr_force_inline hyperedge_type - at(hyperedge_t, const id_type hyperedge_id) const { - return this->hyperedge(hyperedge_id); + template + [[nodiscard]] gl_attr_force_inline hyperedge_t at( + this Self& self, hyperedge_tag, const id_type hyperedge_id + ) { + return self.hyperedge(hyperedge_id); } /// @brief Returns a descriptor of the hyperedge with the given ID without bounds checking. @@ -909,12 +1025,14 @@ class hypergraph final { /// > [!WARNING] Undefined Behavior /// > /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. - [[nodiscard]] gl_attr_force_inline hyperedge_type hyperedge_unchecked(const id_type hyperedge_id - ) const { + template + [[nodiscard]] gl_attr_force_inline hyperedge_t hyperedge_unchecked( + this Self& self, const id_type hyperedge_id + ) { if constexpr (traits::c_non_empty_properties) - return hyperedge_type{hyperedge_id, this->_hyperedge_properties[hyperedge_id]}; + return hyperedge_t{hyperedge_id, self._hyperedge_properties[hyperedge_id]}; else - return hyperedge_type{hyperedge_id}; + return hyperedge_t{hyperedge_id}; } /// @brief Returns a descriptor of the hyperedge with the given ID without bounds checking. @@ -930,15 +1048,17 @@ class hypergraph final { /// > [!WARNING] Undefined Behavior /// > /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. - [[nodiscard]] gl_attr_force_inline hyperedge_type - operator[](hyperedge_t, const id_type hyperedge_id) const { - return this->hyperedge_unchecked(hyperedge_id); + template + [[nodiscard]] gl_attr_force_inline hyperedge_t operator[]( + this Self& self, hyperedge_tag, const id_type hyperedge_id + ) { + return self.hyperedge_unchecked(hyperedge_id); } /// @brief Returns a lazily evaluated, random-access view of all hyperedge descriptors in the hypergraph. /// @return A view yielding descriptors for every hyperedge. - [[nodiscard]] gl_attr_force_inline auto hyperedges() const noexcept { - return this->hyperedge_ids() | std::views::transform(this->_create_hyperedge_descriptor()); + [[nodiscard]] gl_attr_force_inline auto hyperedges(this auto& self) noexcept { + return self.hyperedge_ids() | std::views::transform(self._create_hyperedge_descriptor()); } /// @brief Returns a lazily evaluated, random-access view of all active hyperedge IDs in the hypergraph. @@ -951,21 +1071,22 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @return A mutable reference to the hyperedge's properties. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] gl_attr_force_inline hyperedge_properties_type& hyperedge_properties( - const id_type hyperedge_id - ) const + template + [[nodiscard]] gl_attr_force_inline hyperedge_properties_t& hyperedge_properties( + this Self& self, const id_type hyperedge_id + ) requires(traits::c_non_empty_properties) { - this->_verify_hyperedge_id(hyperedge_id); - return this->_hyperedge_properties[hyperedge_id]; + self._verify_hyperedge_id(hyperedge_id); + return self._hyperedge_properties[hyperedge_id]; } /// @brief Retrieves a lazily evaluated, random-access view over all hyperedge properties in the hypergraph. /// @return A view mapping each active hyperedge index to its property. - [[nodiscard]] gl_attr_force_inline auto hyperedge_properties_map() const noexcept + [[nodiscard]] gl_attr_force_inline auto hyperedge_properties_map(this auto& self) noexcept requires(traits::c_non_empty_properties) { - return std::views::all(this->_hyperedge_properties); + return std::views::all(self._hyperedge_properties); } // --- incidence modifiers --- @@ -986,7 +1107,9 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either descriptor is invalid. - gl_attr_force_inline void bind(vertex_type vertex, hyperedge_type hyperedge) + gl_attr_force_inline void bind( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) requires std::same_as { this->bind(vertex.id(), hyperedge.id()); @@ -1025,19 +1148,22 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind( - const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge + const traits::c_vertex_forward_range auto& vertex_rng, + traits::c_hyperedge auto hyperedge ) requires std::same_as { - this->bind(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); + this->bind(vertex_rng | std::views::transform(util::to_id), hyperedge.id()); } /// @brief Binds a list of vertices to a single hyperedge in an undirected hypergraph. + /// @tparam V The vertex descriptor type. /// @param vertices An initializer list of vertex descriptors. /// @param hyperedge_id The ID of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. + template V> gl_attr_force_inline void bind( - std::initializer_list vertices, hyperedge_type hyperedge + std::initializer_list vertices, traits::c_hyperedge auto hyperedge ) requires std::same_as { @@ -1077,19 +1203,22 @@ class hypergraph final { /// @param hyperedge_id_rng A forward range of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind( - vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng + traits::c_vertex auto vertex, + const traits::c_hyperedge_forward_range auto& hyperedge_rng ) requires std::same_as { - this->bind(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); + this->bind(vertex.id(), hyperedge_rng | std::views::transform(util::to_id)); } /// @brief Binds a single vertex to a list of hyperedges in an undirected hypergraph. + /// @tparam E The hyperedge descriptor type. /// @param vertex_id The descriptor of the vertex. /// @param hyperedge_id_rng An initializer list of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. + template E> gl_attr_force_inline void bind( - vertex_type vertex, std::initializer_list hyperedges + traits::c_vertex auto vertex, std::initializer_list hyperedges ) requires std::same_as { @@ -1101,7 +1230,7 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @throws std::invalid_argument If either ID is invalid. void bind_tail(const id_type vertex_id, const id_type hyperedge_id) - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); @@ -1112,8 +1241,10 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either descriptor is invalid. - gl_attr_force_inline void bind_tail(vertex_type vertex, hyperedge_type hyperedge) - requires std::same_as + gl_attr_force_inline void bind_tail( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) + requires(std::same_as) { this->bind_tail(vertex.id(), hyperedge.id()); } @@ -1125,7 +1256,7 @@ class hypergraph final { void bind_tail( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); for (const auto vertex_id : vertex_id_rng) { @@ -1141,7 +1272,7 @@ class hypergraph final { gl_attr_force_inline void bind_tail( std::initializer_list vertex_ids, const id_type hyperedge_id ) - requires std::same_as + requires(std::same_as) { this->bind_tail(std::views::all(vertex_ids), hyperedge_id); } @@ -1151,21 +1282,24 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_tail( - const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge + const traits::c_vertex_forward_range auto& vertex_rng, + traits::c_hyperedge auto hyperedge ) - requires std::same_as + requires(std::same_as) { - this->bind_tail(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); + this->bind_tail(vertex_rng | std::views::transform(util::to_id), hyperedge.id()); } /// @brief Binds a range of vertices to the *tail* of a single hyperedge in a BF-directed hypergraph. + /// @tparam V The vertex descriptor type. /// @param vertices An initializer list of vertex descriptors. /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. + template V> gl_attr_force_inline void bind_tail( - std::initializer_list vertices, hyperedge_type hyperedge + std::initializer_list vertices, traits::c_hyperedge auto hyperedge ) - requires std::same_as + requires(std::same_as) { this->bind_tail(std::views::all(vertices), hyperedge); } @@ -1177,7 +1311,7 @@ class hypergraph final { void bind_tail( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); for (const auto hyperedge_id : hyperedge_id_rng) { @@ -1193,7 +1327,7 @@ class hypergraph final { gl_attr_force_inline void bind_tail( const id_type vertex_id, std::initializer_list hyperedge_ids ) - requires std::same_as + requires(std::same_as) { this->bind_tail(vertex_id, std::views::all(hyperedge_ids)); } @@ -1203,21 +1337,24 @@ class hypergraph final { /// @param hyperedge_rng A forward range of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_tail( - vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng + traits::c_vertex auto vertex, + const traits::c_hyperedge_forward_range auto& hyperedge_rng ) - requires std::same_as + requires(std::same_as) { - this->bind_tail(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); + this->bind_tail(vertex.id(), hyperedge_rng | std::views::transform(util::to_id)); } /// @brief Binds a single vertex to the *tail* of a range of hyperedges in a BF-directed hypergraph. + /// @tparam E The hyperedge descriptor type. /// @param vertex The descriptor of the vertex. /// @param hyperedges An initializer list of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. + template E> gl_attr_force_inline void bind_tail( - vertex_type vertex, std::initializer_list hyperedges + traits::c_vertex auto vertex, std::initializer_list hyperedges ) - requires std::same_as + requires(std::same_as) { this->bind_tail(vertex, std::views::all(hyperedges)); } @@ -1227,7 +1364,7 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @throws std::invalid_argument If either ID is invalid. void bind_head(const id_type vertex_id, const id_type hyperedge_id) - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); @@ -1238,8 +1375,10 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either descriptor is invalid. - gl_attr_force_inline void bind_head(vertex_type vertex, hyperedge_type hyperedge) - requires std::same_as + gl_attr_force_inline void bind_head( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) + requires(std::same_as) { this->bind_head(vertex.id(), hyperedge.id()); } @@ -1251,7 +1390,7 @@ class hypergraph final { void bind_head( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); for (const auto vertex_id : vertex_id_rng) { @@ -1267,7 +1406,7 @@ class hypergraph final { gl_attr_force_inline void bind_head( std::initializer_list vertex_ids, const id_type hyperedge_id ) - requires std::same_as + requires(std::same_as) { this->bind_head(std::views::all(vertex_ids), hyperedge_id); } @@ -1277,21 +1416,24 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_head( - const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge + const traits::c_vertex_forward_range auto& vertex_rng, + traits::c_hyperedge auto hyperedge ) - requires std::same_as + requires(std::same_as) { - this->bind_head(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); + this->bind_head(vertex_rng | std::views::transform(util::to_id), hyperedge.id()); } /// @brief Binds a range of vertices to the *head* of a single hyperedge in a BF-directed hypergraph. + /// @tparam V The vertex descriptor type. /// @param vertices An initializer list of vertex descriptors. /// @param hyperedge The descriptor of the hyperedge. /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. + template V> gl_attr_force_inline void bind_head( - std::initializer_list vertices, hyperedge_type hyperedge + std::initializer_list vertices, traits::c_hyperedge auto hyperedge ) - requires std::same_as + requires(std::same_as) { this->bind_head(std::views::all(vertices), hyperedge); } @@ -1303,7 +1445,7 @@ class hypergraph final { void bind_head( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); for (const auto hyperedge_id : hyperedge_id_rng) { @@ -1319,7 +1461,7 @@ class hypergraph final { gl_attr_force_inline void bind_head( const id_type vertex_id, std::initializer_list hyperedge_ids ) - requires std::same_as + requires(std::same_as) { this->bind_head(vertex_id, std::views::all(hyperedge_ids)); } @@ -1329,21 +1471,24 @@ class hypergraph final { /// @param hyperedge_rng A forward range of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_head( - vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng + traits::c_vertex auto vertex, + const traits::c_hyperedge_forward_range auto& hyperedge_rng ) - requires std::same_as + requires(std::same_as) { - this->bind_head(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); + this->bind_head(vertex.id(), hyperedge_rng | std::views::transform(util::to_id)); } /// @brief Binds a single vertex to the *head* of a range of hyperedges in a BF-directed hypergraph. + /// @tparam E The hyperedge descriptor type. /// @param vertex The descriptor of the vertex. /// @param hyperedges An initializer list of hyperedge descriptors. /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. + template E> gl_attr_force_inline void bind_head( - vertex_type vertex, std::initializer_list hyperedges + traits::c_vertex auto vertex, std::initializer_list hyperedges ) - requires std::same_as + requires(std::same_as) { this->bind_head(vertex, std::views::all(hyperedges)); } @@ -1360,7 +1505,9 @@ class hypergraph final { /// @brief Unbinds a vertex from a hyperedge. /// @param vertex The descriptor of the vertex. /// @param hyperedge The descriptor of the hyperedge. - gl_attr_force_inline void unbind(vertex_type vertex, hyperedge_type hyperedge) { + gl_attr_force_inline void unbind( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) { this->unbind(vertex.id(), hyperedge.id()); } @@ -1386,7 +1533,7 @@ class hypergraph final { /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. /// @return `true` if the vertex belongs to the hyperedge, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool are_incident( - vertex_type vertex, hyperedge_type hyperedge + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge ) const { return this->are_incident(vertex.id(), hyperedge.id()); } @@ -1399,7 +1546,7 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @return `true` if the vertex is in the *tail* set of the hyperedge, `false` otherwise. [[nodiscard]] bool is_tail(const id_type vertex_id, const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); @@ -1413,9 +1560,10 @@ class hypergraph final { /// @param vertex The `vertex_descriptor` mapping to the vertex. /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. /// @return `true` if the vertex is in the *tail* set of the hyperedge, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool is_tail(vertex_type vertex, hyperedge_type hyperedge) - const - requires std::same_as + [[nodiscard]] gl_attr_force_inline bool is_tail( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) const + requires(std::same_as) { return this->is_tail(vertex.id(), hyperedge.id()); } @@ -1428,7 +1576,7 @@ class hypergraph final { /// @param hyperedge_id The ID of the hyperedge. /// @return `true` if the vertex is in the *head* set of the hyperedge, `false` otherwise. [[nodiscard]] bool is_head(const id_type vertex_id, const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); @@ -1442,9 +1590,10 @@ class hypergraph final { /// @param vertex The `vertex_descriptor` mapping to the vertex. /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. /// @return `true` if the vertex is in the *head* set of the hyperedge, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool is_head(vertex_type vertex, hyperedge_type hyperedge) - const - requires std::same_as + [[nodiscard]] gl_attr_force_inline bool is_head( + traits::c_vertex auto vertex, traits::c_hyperedge auto hyperedge + ) const + requires(std::same_as) { return this->is_head(vertex.id(), hyperedge.id()); } @@ -1455,17 +1604,21 @@ class hypergraph final { /// @param vertex_id The vertex ID. /// @return A view representing the set of incident hyperedges. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id) { - return this->incident_hyperedge_ids(vertex_id) - | std::views::transform(this->_create_hyperedge_descriptor()); + [[nodiscard]] gl_attr_force_inline auto incident_hyperedges( + this auto& self, const id_type vertex_id + ) { + return self.incident_hyperedge_ids(vertex_id) + | std::views::transform(self._create_hyperedge_descriptor()); } /// @brief Retrieves all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). /// @param vertex The vertex descriptor. /// @return A view representing the set of incident hyperedges. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(vertex_type vertex) { - return this->incident_hyperedges(vertex.id()); + [[nodiscard]] gl_attr_force_inline auto incident_hyperedges( + this auto& self, traits::c_vertex auto vertex + ) { + return self.incident_hyperedges(vertex.id()); } /// @brief Retrieves IDs of all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). @@ -1481,7 +1634,9 @@ class hypergraph final { /// @param vertex The vertex descriptor. /// @return A view representing the set of incident hyperedge IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto incident_hyperedge_ids(vertex_type vertex) const { + [[nodiscard]] gl_attr_force_inline auto incident_hyperedge_ids( + traits::c_vertex auto vertex + ) const { return this->incident_hyperedge_ids(vertex.id()); } @@ -1500,7 +1655,8 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @return The degree of the vertex. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type degree(vertex_type vertex) const { + [[nodiscard]] gl_attr_force_inline size_type degree(traits::c_vertex auto vertex + ) const { return this->degree(vertex.id()); } @@ -1514,21 +1670,23 @@ class hypergraph final { /// @param vertex_id The vertex ID. /// @return A view representing the set of outgoing hyperedges. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto out_hyperedges(this auto& self, const id_type vertex_id) + requires(std::same_as) { - return this->out_hyperedge_ids(vertex_id) - | std::views::transform(this->_create_hyperedge_descriptor()); + return self.out_hyperedge_ids(vertex_id) + | std::views::transform(self._create_hyperedge_descriptor()); } /// @brief Retrieves all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). /// @param vertex The vertex descriptor. /// @return A view representing the set of outgoing hyperedges. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto out_hyperedges(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto out_hyperedges( + this auto& self, traits::c_vertex auto vertex + ) + requires(std::same_as) { - return this->out_hyperedges(vertex.id()); + return self.out_hyperedges(vertex.id()); } /// @brief Retrieves IDs of all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). @@ -1536,7 +1694,7 @@ class hypergraph final { /// @return A view representing the set of outgoing hyperedge IDs. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] auto out_hyperedge_ids(const id_type vertex_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); return this->_impl.out_hyperedges(vertex_id); @@ -1546,8 +1704,10 @@ class hypergraph final { /// @param vertex The vertex descriptor. /// @return A view representing the set of outgoing hyperedge IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto out_hyperedge_ids(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto out_hyperedge_ids( + traits::c_vertex auto vertex + ) const + requires(std::same_as) { return this->out_hyperedge_ids(vertex.id()); } @@ -1558,7 +1718,7 @@ class hypergraph final { /// @return The out-degree of the vertex. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] size_type out_degree(const id_type vertex_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); return this->_impl.out_degree(vertex_id); @@ -1569,8 +1729,9 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @return The out-degree of the vertex. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type out_degree(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline size_type out_degree(traits::c_vertex auto vertex + ) const + requires(std::same_as) { return this->out_degree(vertex.id()); } @@ -1578,7 +1739,7 @@ class hypergraph final { /// @brief Returns a mapped array of out-degrees for all vertices. /// @return A vector where the index aligns with the vertex ID containing its out-degree. [[nodiscard]] std::vector out_degree_map() const - requires std::same_as + requires(std::same_as) { return this->_impl.out_degree_map(this->_n_vertices); } @@ -1587,21 +1748,23 @@ class hypergraph final { /// @param vertex_id The vertex ID. /// @return A view representing the set of incoming hyperedges. /// @throws std::invalid_argument If the vertex ID is invalid. - [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto in_hyperedges(this auto& self, const id_type vertex_id) + requires(std::same_as) { - return this->in_hyperedge_ids(vertex_id) - | std::views::transform(this->_create_hyperedge_descriptor()); + return self.in_hyperedge_ids(vertex_id) + | std::views::transform(self._create_hyperedge_descriptor()); } /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). /// @param vertex The vertex descriptor. /// @return A view representing the set of incoming hyperedges. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto in_hyperedges(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto in_hyperedges( + this auto& self, traits::c_vertex auto vertex + ) + requires(std::same_as) { - return this->in_hyperedges(vertex.id()); + return self.in_hyperedges(vertex.id()); } /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). @@ -1609,7 +1772,7 @@ class hypergraph final { /// @return A view representing the set of incoming hyperedge IDs. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] auto in_hyperedge_ids(const id_type vertex_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); return this->_impl.in_hyperedges(vertex_id); @@ -1619,8 +1782,10 @@ class hypergraph final { /// @param vertex The vertex descriptor. /// @return A view representing the set of incoming hyperedge IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto in_hyperedge_ids(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto in_hyperedge_ids( + traits::c_vertex auto vertex + ) const + requires(std::same_as) { return this->in_hyperedge_ids(vertex.id()); } @@ -1631,7 +1796,7 @@ class hypergraph final { /// @return The in-degree of the vertex. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] size_type in_degree(const id_type vertex_id) const - requires std::same_as + requires(std::same_as) { this->_verify_vertex_id(vertex_id); return this->_impl.in_degree(vertex_id); @@ -1642,8 +1807,9 @@ class hypergraph final { /// @param vertex The descriptor of the vertex. /// @return The in-degree of the vertex. /// @throws std::invalid_argument If the vertex descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type in_degree(vertex_type vertex) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline size_type in_degree(traits::c_vertex auto vertex + ) const + requires(std::same_as) { return this->in_degree(vertex.id()); } @@ -1651,7 +1817,7 @@ class hypergraph final { /// @brief Returns a mapped array of in-degrees for all vertices. /// @return A vector where the index aligns with the vertex ID containing its in-degree. [[nodiscard]] std::vector in_degree_map() const - requires std::same_as + requires(std::same_as) { return this->_impl.in_degree_map(this->_n_vertices); } @@ -1660,17 +1826,19 @@ class hypergraph final { /// @param hyperedge_id The hyperedge ID. /// @return A view representing the set of incident vertices. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] auto incident_vertices(const id_type hyperedge_id) const { - return this->incident_vertex_ids(hyperedge_id) - | std::views::transform(this->_create_vertex_descriptor()); + [[nodiscard]] auto incident_vertices(this auto& self, const id_type hyperedge_id) { + return self.incident_vertex_ids(hyperedge_id) + | std::views::transform(self._create_vertex_descriptor()); } /// @brief Retrieves all vertices incident with a hyperedge ($e$). /// @param hyperedge The hyperedge descriptor. /// @return A view representing the set of incident vertices. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto incident_vertices(hyperedge_type hyperedge) const { - return this->incident_vertices(hyperedge.id()); + [[nodiscard]] gl_attr_force_inline auto incident_vertices( + this auto& self, traits::c_hyperedge auto hyperedge + ) { + return self.incident_vertices(hyperedge.id()); } /// @brief Retrieves IDs of all vertices incident with a hyperedge ($e$). @@ -1686,7 +1854,9 @@ class hypergraph final { /// @param hyperedge The hyperedge descriptor. /// @return A view representing the set of incident vertex IDs. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto incident_vertex_ids(hyperedge_type hyperedge) const { + [[nodiscard]] gl_attr_force_inline auto incident_vertex_ids( + traits::c_hyperedge auto hyperedge + ) const { return this->incident_vertex_ids(hyperedge.id()); } @@ -1711,7 +1881,8 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @return The size of the hyperedge. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(hyperedge_type hyperedge) const { + [[nodiscard]] gl_attr_force_inline size_type + hyperedge_size(traits::c_hyperedge auto hyperedge) const { return this->hyperedge_size(hyperedge.id()); } @@ -1725,21 +1896,23 @@ class hypergraph final { /// @param hyperedge_id The hypepredge ID. /// @return A view representing the set of the hyperedge's tail vertices. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto tail(this auto& self, const id_type hyperedge_id) + requires(std::same_as) { - return this->tail_ids(hyperedge_id) - | std::views::transform(this->_create_vertex_descriptor()); + return self.tail_ids(hyperedge_id) + | std::views::transform(self._create_vertex_descriptor()); } /// @brief Retrieves all vertices in the *tail* set of a hyperedge ($T(e)$). /// @param hyperedge The hyperedge descriptor. /// @return A view representing the set of the hyperedge's tail vertices. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto tail(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto tail( + this auto& self, traits::c_hyperedge auto hyperedge + ) + requires(std::same_as) { - return this->tail(hyperedge.id()); + return self.tail(hyperedge.id()); } /// @brief Retrieves IDs all vertices in the *tail* set of a hyperedge ($T(e)$). @@ -1747,7 +1920,7 @@ class hypergraph final { /// @return A view representing the set of the hyperedge's tail vertex IDs. /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto tail_ids(const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.tail(hyperedge_id); @@ -1757,8 +1930,9 @@ class hypergraph final { /// @param hyperedge The hypepredge descriptor. /// @return A view representing the set of the hyperedge's tail vertex IDs. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto tail_ids(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto tail_ids(traits::c_hyperedge auto hyperedge + ) const + requires(std::same_as) { return this->tail_ids(hyperedge.id()); } @@ -1768,7 +1942,7 @@ class hypergraph final { /// @return The size of the hyperedge's *tail* set. /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.tail_size(hyperedge_id); @@ -1778,8 +1952,9 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @return The size of the hyperedge's *tail* set. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type tail_size(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline size_type + tail_size(traits::c_hyperedge auto hyperedge) const + requires(std::same_as) { return this->tail_size(hyperedge.id()); } @@ -1787,7 +1962,7 @@ class hypergraph final { /// @brief Returns a mapped array of sizes of the *tail* sets of hyperedged in a *BF-directed* hypergraph. /// @return A vector where the index aligns with the hyperedge ID containing its *tail* set size. [[nodiscard]] std::vector tail_size_map() const - requires std::same_as + requires(std::same_as) { return this->_impl.tail_size_map(this->_n_hyperedges); } @@ -1796,21 +1971,23 @@ class hypergraph final { /// @param hyperedge_id The hypepredge ID. /// @return A view representing the set of the hyperedge's head vertices. /// @throws std::invalid_argument If the hyperedge ID is invalid. - [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto head(this auto& self, const id_type hyperedge_id) + requires(std::same_as) { - return this->head_ids(hyperedge_id) - | std::views::transform(this->_create_vertex_descriptor()); + return self.head_ids(hyperedge_id) + | std::views::transform(self._create_vertex_descriptor()); } /// @brief Retrieves all vertices in the *head* set of a hyperedge ($H(e)$). /// @param hyperedge The hyperedge descriptor. /// @return A view representing the set of the hyperedge's head vertices. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto head(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto head( + this auto& self, traits::c_hyperedge auto hyperedge + ) + requires(std::same_as) { - return this->head(hyperedge.id()); + return self.head(hyperedge.id()); } /// @brief Retrieves IDs all vertices in the *head* set of a hyperedge ($H(e)$). @@ -1818,7 +1995,7 @@ class hypergraph final { /// @return A view representing the set of the hyperedge's head vertex IDs. /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto head_ids(const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.head(hyperedge_id); @@ -1828,8 +2005,9 @@ class hypergraph final { /// @param hyperedge The hypepredge descriptor. /// @return A view representing the set of the hyperedge's head vertex IDs. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline auto head_ids(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline auto head_ids(traits::c_hyperedge auto hyperedge + ) const + requires(std::same_as) { return this->head_ids(hyperedge.id()); } @@ -1839,7 +2017,7 @@ class hypergraph final { /// @return The size of the hyperedge's *head* set. /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] size_type head_size(const id_type hyperedge_id) const - requires std::same_as + requires(std::same_as) { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.head_size(hyperedge_id); @@ -1849,8 +2027,9 @@ class hypergraph final { /// @param hyperedge The descriptor of the hyperedge. /// @return The size of the hyperedge's *head* set. /// @throws std::invalid_argument If the hyperedge descriptor is invalid. - [[nodiscard]] gl_attr_force_inline size_type head_size(hyperedge_type hyperedge) const - requires std::same_as + [[nodiscard]] gl_attr_force_inline size_type + head_size(traits::c_hyperedge auto hyperedge) const + requires(std::same_as) { return this->head_size(hyperedge.id()); } @@ -1858,7 +2037,7 @@ class hypergraph final { /// @brief Returns a mapped array of sizes of the *head* sets of hyperedged in a *BF-directed* hypergraph. /// @return A vector where the index aligns with the hyperedge ID containing its *head* set size. [[nodiscard]] std::vector head_size_map() const - requires std::same_as + requires(std::same_as) { return this->_impl.head_size_map(this->_n_hyperedges); } @@ -1892,7 +2071,7 @@ class hypergraph final { /// @brief The hypergraph owning the hyperedge. const hypergraph& hg; /// @brief The hyperedge to be formatted. - const hyperedge_type hyperedge; + const_hyperedge_type hyperedge; /// @brief Stream insertion operator for undirected hyperedges. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) @@ -1920,7 +2099,7 @@ class hypergraph final { /// @brief Stream insertion operator for BF-directed hyperedges. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) - requires std::same_as + requires(std::same_as) { using io::detail::option_bit; @@ -1948,7 +2127,7 @@ class hypergraph final { /// @brief Returns a formatter object that safely encapsulates a hyperedge for stream output. /// @param hyperedge The hyperedge to format. /// @return A @ref hyperedge_formatter structure prepared for standard stream insertion. - [[nodiscard]] hyperedge_formatter display(hyperedge_type hyperedge) const { + [[nodiscard]] hyperedge_formatter fmt(traits::c_hyperedge auto hyperedge) const { return hyperedge_formatter{*this, hyperedge}; } @@ -1987,6 +2166,7 @@ class hypergraph final { /// @brief Converts a hypergraph to a different representation type. template + requires(not std::is_lvalue_reference_v) friend auto to(Hypergraph&& source); /// @brief Internal structure for dispatching representation conversion. @@ -2015,17 +2195,19 @@ class hypergraph final { this->_vertex_properties.erase(this->_vertex_properties.begin() + vertex_id); } - gl_attr_force_inline auto _create_vertex_descriptor() const noexcept + template + gl_attr_force_inline auto _create_vertex_descriptor(this Self&) noexcept requires(traits::c_empty_properties) { - return [](const id_type id) { return vertex_type{id}; }; + return [](const id_type id) { return vertex_t{id}; }; } - gl_attr_force_inline auto _create_vertex_descriptor() const noexcept + template + gl_attr_force_inline auto _create_vertex_descriptor(this Self& self) noexcept requires(traits::c_non_empty_properties) { - return [&pmap = this->_vertex_properties](const id_type id) { - return vertex_type{id, pmap[to_idx(id)]}; + return [&pmap = self._vertex_properties](const id_type id) { + return vertex_t{id, pmap[to_idx(id)]}; }; } @@ -2046,17 +2228,19 @@ class hypergraph final { this->_hyperedge_properties.erase(this->_hyperedge_properties.begin() + hyperedge_id); } - gl_attr_force_inline auto _create_hyperedge_descriptor() const noexcept + template + gl_attr_force_inline auto _create_hyperedge_descriptor(this Self&) noexcept requires(traits::c_empty_properties) { - return [](const id_type id) { return hyperedge_type{id}; }; + return [](const id_type id) { return hyperedge_t{id}; }; } - gl_attr_force_inline auto _create_hyperedge_descriptor() const noexcept + template + gl_attr_force_inline auto _create_hyperedge_descriptor(this Self& self) noexcept requires(traits::c_non_empty_properties) { - return [&pmap = this->_hyperedge_properties](const id_type id) { - return hyperedge_type{id, pmap[to_idx(id)]}; + return [&pmap = self._hyperedge_properties](const id_type id) { + return hyperedge_t{id, pmap[to_idx(id)]}; }; } @@ -2090,7 +2274,7 @@ class hypergraph final { else { os << "hyperedges:\n"; for (const auto& edge : this->hyperedges()) - os << " - " << this->display(edge) << '\n'; + os << " - " << this->fmt(edge) << '\n'; } return os; @@ -2115,7 +2299,7 @@ class hypergraph final { } else { auto hyperedges = std::views::transform(this->hyperedges(), [this](auto hyperedge) { - return this->display(hyperedge); + return this->fmt(hyperedge); }); os << "E = " << io::multiline_set_formatter(hyperedges) << '\n'; @@ -2250,7 +2434,7 @@ class hypergraph final { } void _read_hyperedges(std::istream& is, const size_type n_hyperedges, const bool with_he_props) - requires std::same_as + requires(std::same_as) { for (auto _ = 0uz; _ < n_hyperedges; ++_) { size_type tail_size, head_size; @@ -2283,19 +2467,13 @@ class hypergraph final { // --- data members --- - /// @brief The current count of initialized vertices. size_type _n_vertices = 0uz; - /// @brief The current count of initialized hyperedges. size_type _n_hyperedges = 0uz; - /// @brief The underlying representation type (matrix or list). representation_type _impl{}; - /// @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier - [[no_unique_address]] mutable vertex_properties_map_type _vertex_properties{}; - - /// @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier - [[no_unique_address]] mutable hyperedge_properties_map_type _hyperedge_properties{}; + [[no_unique_address]] vertex_properties_map_type _vertex_properties{}; + [[no_unique_address]] hyperedge_properties_map_type _hyperedge_properties{}; }; // --- general hypergraph utility --- @@ -2443,5 +2621,4 @@ void in_degree(); void out_degree(); } // namespace detail::hypergraph_doc_anchors - } // namespace hgl diff --git a/include/hgl/hypergraph_elements.hpp b/include/hgl/hypergraph_elements.hpp index bef1f593..80062da9 100644 --- a/include/hgl/hypergraph_elements.hpp +++ b/include/hgl/hypergraph_elements.hpp @@ -131,6 +131,18 @@ class hyperedge_descriptor final { requires(traits::c_non_empty_properties) : _id(id), _properties(properties) {} + /// @brief Implicit converting constructor from a non-const descriptor to a const descriptor. + /// @tparam MutProperties The mutable property type. + /// @param other The hyperedge descriptor to convert from. + template + requires(std::same_as) + hyperedge_descriptor(const hyperedge_descriptor& other) noexcept + : _id(other.id()) { + if constexpr (traits::c_non_empty_properties) { + this->_properties = other.properties(); + } + } + /// @brief Returns a special descriptor representing an invalid or uninitialized property-less hyperedge. /// @return An invalid `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline static hyperedge_descriptor invalid() noexcept @@ -162,25 +174,32 @@ class hyperedge_descriptor final { ~hyperedge_descriptor() = default; /// @brief Compares two hyperedge descriptors for equality. + /// @tparam OtherProperties The property type of the other descriptor. /// @param other The descriptor to compare against. /// @return `true` if both descriptors hold the same ID, `false` otherwise. - [[nodiscard]] bool operator==(const hyperedge_descriptor& other) const noexcept { - return this->_id == other._id; - } - - /// @brief Contextually converts the descriptor to a boolean. - /// @return `true` if the descriptor is valid, `false` otherwise. - [[nodiscard]] gl_attr_force_inline operator bool() const noexcept { - return this->is_valid(); + template + requires(std::same_as, std::remove_cv_t>) + [[nodiscard]] bool operator==(const hyperedge_descriptor& other + ) const noexcept { + return this->_id == other.id(); } /// @brief Compares two hyperedge descriptors to establish a strict ordering based on their IDs. + /// @tparam OtherProperties The property type of the other descriptor. /// @param other The descriptor to compare against. /// @return The result of the three-way comparison between the underlying IDs. + template + requires(std::same_as, std::remove_cv_t>) [[nodiscard]] gl_attr_force_inline std::strong_ordering operator<=>( - const hyperedge_descriptor& other + const hyperedge_descriptor& other ) const noexcept { - return this->_id <=> other._id; + return this->_id <=> other.id(); + } + + /// @brief Contextually converts the descriptor to a boolean. + /// @return `true` if the descriptor is valid, `false` otherwise. + [[nodiscard]] gl_attr_force_inline operator bool() const noexcept { + return this->is_valid(); } /// @brief Checks if the descriptor represents a valid hyperedge. @@ -285,29 +304,29 @@ class hyperedge_descriptor final { /// @ingroup HGL-Core /// @brief Tag type representing a vertex element in a hypergraph. -struct vertex_t {}; +struct vertex_tag {}; /// @ingroup HGL-Core /// @brief Tag type representing a hyperedge element in a hypergraph. -struct hyperedge_t {}; +struct hyperedge_tag {}; /// @ingroup HGL-Core -/// @brief A constant instance of `vertex_t` used for tagging and generic dispatching. -inline constexpr vertex_t vertex{}; +/// @brief A constant instance of `vertex_tag` used for tagging and generic dispatching. +inline constexpr vertex_tag vertex{}; /// @ingroup HGL-Core -/// @brief A constant instance of `hyperedge_t` used for tagging and generic dispatching. -inline constexpr hyperedge_t hyperedge{}; +/// @brief A constant instance of `hyperedge_tag` used for tagging and generic dispatching. +inline constexpr hyperedge_tag hyperedge{}; namespace traits { /// @ingroup HGL-Traits /// @brief Validates if a type is a valid hypergraph element tag. /// -/// The valid hypergraph element tags are @ref hgl::vertex_t "vertex_t" and @ref hgl::hyperedge_t "hyperedge_t". +/// The valid hypergraph element tags are @ref hgl::vertex_tag "vertex_tag" and @ref hgl::hyperedge_tag "hyperedge_tag". /// /// @tparam T The type to evaluate against the concept. template -concept c_hypergraph_element_tag = c_one_of; +concept c_hypergraph_element_tag = c_one_of; } // namespace traits } // namespace hgl diff --git a/include/hgl/hypergraph_traits.hpp b/include/hgl/hypergraph_traits.hpp index 85e0c7b3..05280c90 100644 --- a/include/hgl/hypergraph_traits.hpp +++ b/include/hgl/hypergraph_traits.hpp @@ -44,15 +44,19 @@ struct hypergraph_traits { /// @brief The integer type used for element identifiers. using id_type = typename representation_tag::id_type; - /// @brief The fully resolved type representing a vertex descriptor. - using vertex_type = vertex_descriptor; /// @brief The property payload type associated with vertices. - using vertex_properties_type = typename vertex_type::properties_type; + using vertex_properties_type = std::remove_cvref_t; + /// @brief The descriptor type representing a vertex of a hypergraph. + using vertex_type = vertex_descriptor; + /// @brief The descriptor type representing an immutable vertex of a hypergraph. + using const_vertex_type = vertex_descriptor; - /// @brief The fully resolved type representing a hyperedge descriptor. - using hyperedge_type = hyperedge_descriptor; /// @brief The property payload type associated with hyperedges. - using hyperedge_properties_type = typename hyperedge_type::properties_type; + using hyperedge_properties_type = std::remove_cvref_t; + /// @brief The descriptor type representing an hyperedge of a hypergraph. + using hyperedge_type = hyperedge_descriptor; + /// @brief The descriptor type representing an immutable hyperedge of a hypergraph. + using const_hyperedge_type = hyperedge_descriptor; }; /// @ingroup HGL-Core diff --git a/include/hgl/impl/flat_incidence_list.hpp b/include/hgl/impl/flat_incidence_list.hpp index 8a6c55c1..0a3d56f8 100644 --- a/include/hgl/impl/flat_incidence_list.hpp +++ b/include/hgl/impl/flat_incidence_list.hpp @@ -7,6 +7,7 @@ #include "hgl/constants.hpp" #include "hgl/decl/repr_tags.hpp" #include "hgl/directional_tags.hpp" +#include "hgl/impl/util.hpp" #include "hgl/repr/layout_tags.hpp" #include "hgl/types.hpp" #include "hgl/util.hpp" @@ -121,51 +122,51 @@ class flat_incidence_list final { // --- vertex methods --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_incident_with(vertex_id); + return this->_incident_with(vertex_id); } [[nodiscard]] gl_attr_force_inline size_type degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id); + return this->_size(vertex_id); } [[nodiscard]] gl_attr_force_inline std::vector degree_map(const size_type n_vertices ) const noexcept { - return this->_size_map(n_vertices); + return this->_size_map(n_vertices); } // --- hyperedge methods --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_incident_with(hyperedge_id); + return this->_incident_with(hyperedge_id); } [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(const id_type hyperedge_id ) const noexcept { - return this->_size(hyperedge_id); + return this->_size(hyperedge_id); } [[nodiscard]] gl_attr_force_inline std::vector hyperedge_size_map( const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges); + return this->_size_map(n_hyperedges); } // --- binding methods --- @@ -307,102 +308,102 @@ class flat_incidence_list final { // --- vertex methods : general --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } // --- vertex methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_get(vertex_id); + return this->_get(vertex_id); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id); + return this->_size(vertex_id); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices); + return this->_size_map(n_vertices); } [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const noexcept { - return this->_get(vertex_id, &flat_incidence_list::_tail_storage); + return this->_get(vertex_id, &flat_incidence_list::_tail_storage); } [[nodiscard]] size_type out_degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id, &flat_incidence_list::_tail_storage); + return this->_size(vertex_id, &flat_incidence_list::_tail_storage); } [[nodiscard]] std::vector out_degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices, &flat_incidence_list::_tail_storage); + return this->_size_map(n_vertices, &flat_incidence_list::_tail_storage); } [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const noexcept { - return this->_get(vertex_id, &flat_incidence_list::_head_storage); + return this->_get(vertex_id, &flat_incidence_list::_head_storage); } [[nodiscard]] size_type in_degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id, &flat_incidence_list::_head_storage); + return this->_size(vertex_id, &flat_incidence_list::_head_storage); } [[nodiscard]] std::vector in_degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices, &flat_incidence_list::_head_storage); + return this->_size_map(n_vertices, &flat_incidence_list::_head_storage); } // --- hyperedge methods : general --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } // --- hyperedge methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_get(hyperedge_id); + return this->_get(hyperedge_id); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id); + return this->_size(hyperedge_id); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges); + return this->_size_map(n_hyperedges); } [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const noexcept { - return this->_get(hyperedge_id, &flat_incidence_list::_tail_storage); + return this->_get(hyperedge_id, &flat_incidence_list::_tail_storage); } [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id, &flat_incidence_list::_tail_storage); + return this->_size(hyperedge_id, &flat_incidence_list::_tail_storage); } [[nodiscard]] std::vector tail_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges, &flat_incidence_list::_tail_storage); + return this->_size_map(n_hyperedges, &flat_incidence_list::_tail_storage); } [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const noexcept { - return this->_get(hyperedge_id, &flat_incidence_list::_head_storage); + return this->_get(hyperedge_id, &flat_incidence_list::_head_storage); } [[nodiscard]] size_type head_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id, &flat_incidence_list::_head_storage); + return this->_size(hyperedge_id, &flat_incidence_list::_head_storage); } [[nodiscard]] std::vector head_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges, &flat_incidence_list::_head_storage); + return this->_size_map(n_hyperedges, &flat_incidence_list::_head_storage); } // --- binding methods --- diff --git a/include/hgl/impl/flat_incidence_matrix.hpp b/include/hgl/impl/flat_incidence_matrix.hpp index b73552f5..9f5588e9 100644 --- a/include/hgl/impl/flat_incidence_matrix.hpp +++ b/include/hgl/impl/flat_incidence_matrix.hpp @@ -9,6 +9,7 @@ #include "hgl/decl/repr_tags.hpp" #include "hgl/directional_tags.hpp" #include "hgl/impl/bf_incidence.hpp" +#include "hgl/impl/util.hpp" #include "hgl/repr/layout_tags.hpp" #include "hgl/types.hpp" @@ -68,48 +69,48 @@ class flat_incidence_matrix final { // --- vertex methods --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_incident_with(vertex_id); + return this->_incident_with(vertex_id); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id); + return this->_count(vertex_id); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices); + return this->_count_map(n_vertices); } // --- hyperedge methods --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_incident_with(hyperedge_id); + return this->_incident_with(hyperedge_id); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id); + return this->_count(hyperedge_id); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges); + return this->_count_map(n_hyperedges); } // --- binding methods --- @@ -248,102 +249,102 @@ class flat_incidence_matrix final { // --- vertex methods : general --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } // --- vertex methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_query(vertex_id, bf_is_incident); + return this->_query(vertex_id, bf_is_incident); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_incident); + return this->_count(vertex_id, bf_is_incident); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_incident); + return this->_count_map(n_vertices, bf_is_incident); } [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const noexcept { - return this->_query(vertex_id, bf_is_tail); + return this->_query(vertex_id, bf_is_tail); } [[nodiscard]] size_type out_degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_tail); + return this->_count(vertex_id, bf_is_tail); } [[nodiscard]] std::vector out_degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_tail); + return this->_count_map(n_vertices, bf_is_tail); } [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const noexcept { - return this->_query(vertex_id, bf_is_head); + return this->_query(vertex_id, bf_is_head); } [[nodiscard]] size_type in_degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_head); + return this->_count(vertex_id, bf_is_head); } [[nodiscard]] std::vector in_degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_head); + return this->_count_map(n_vertices, bf_is_head); } // --- hyperedge methods : general --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } // --- hyperedge methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_query(hyperedge_id, bf_is_incident); + return this->_query(hyperedge_id, bf_is_incident); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_incident); + return this->_count(hyperedge_id, bf_is_incident); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_incident); + return this->_count_map(n_hyperedges, bf_is_incident); } [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const noexcept { - return this->_query(hyperedge_id, bf_is_tail); + return this->_query(hyperedge_id, bf_is_tail); } [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_tail); + return this->_count(hyperedge_id, bf_is_tail); } [[nodiscard]] std::vector tail_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_tail); + return this->_count_map(n_hyperedges, bf_is_tail); } [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const noexcept { - return this->_query(hyperedge_id, bf_is_head); + return this->_query(hyperedge_id, bf_is_head); } [[nodiscard]] size_type head_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_head); + return this->_count(hyperedge_id, bf_is_head); } [[nodiscard]] std::vector head_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_head); + return this->_count_map(n_hyperedges, bf_is_head); } // --- binding methods --- diff --git a/include/hgl/impl/incidence_list.hpp b/include/hgl/impl/incidence_list.hpp index 624339f0..f306256c 100644 --- a/include/hgl/impl/incidence_list.hpp +++ b/include/hgl/impl/incidence_list.hpp @@ -7,6 +7,7 @@ #include "hgl/constants.hpp" #include "hgl/decl/repr_tags.hpp" #include "hgl/directional_tags.hpp" +#include "hgl/impl/util.hpp" #include "hgl/repr/layout_tags.hpp" #include "hgl/types.hpp" #include "hgl/util.hpp" @@ -63,51 +64,51 @@ class incidence_list final { // --- vertex methods --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_incident_with(vertex_id); + return this->_incident_with(vertex_id); } [[nodiscard]] gl_attr_force_inline size_type degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id); + return this->_size(vertex_id); } [[nodiscard]] gl_attr_force_inline std::vector degree_map(const size_type n_vertices ) const noexcept { - return this->_size_map(n_vertices); + return this->_size_map(n_vertices); } // --- hyperedge methods --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_incident_with(hyperedge_id); + return this->_incident_with(hyperedge_id); } [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(const id_type hyperedge_id ) const noexcept { - return this->_size(hyperedge_id); + return this->_size(hyperedge_id); } [[nodiscard]] gl_attr_force_inline std::vector hyperedge_size_map( const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges); + return this->_size_map(n_hyperedges); } // --- binding methods --- @@ -262,102 +263,102 @@ class incidence_list final { // --- vertex methods : general --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } // --- vertex methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_get(vertex_id); + return this->_get(vertex_id); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id); + return this->_size(vertex_id); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices); + return this->_size_map(n_vertices); } [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const noexcept { - return this->_get(vertex_id, &incidence_list::_tail_storage); + return this->_get(vertex_id, &incidence_list::_tail_storage); } [[nodiscard]] size_type out_degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id, &incidence_list::_tail_storage); + return this->_size(vertex_id, &incidence_list::_tail_storage); } [[nodiscard]] std::vector out_degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices, &incidence_list::_tail_storage); + return this->_size_map(n_vertices, &incidence_list::_tail_storage); } [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const noexcept { - return this->_get(vertex_id, &incidence_list::_head_storage); + return this->_get(vertex_id, &incidence_list::_head_storage); } [[nodiscard]] size_type in_degree(const id_type vertex_id) const noexcept { - return this->_size(vertex_id, &incidence_list::_head_storage); + return this->_size(vertex_id, &incidence_list::_head_storage); } [[nodiscard]] std::vector in_degree_map(const size_type n_vertices) const noexcept { - return this->_size_map(n_vertices, &incidence_list::_head_storage); + return this->_size_map(n_vertices, &incidence_list::_head_storage); } // --- hyperedge methods : general --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } // --- hyperedge methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_get(hyperedge_id); + return this->_get(hyperedge_id); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id); + return this->_size(hyperedge_id); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges); + return this->_size_map(n_hyperedges); } [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const noexcept { - return this->_get(hyperedge_id, &incidence_list::_tail_storage); + return this->_get(hyperedge_id, &incidence_list::_tail_storage); } [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id, &incidence_list::_tail_storage); + return this->_size(hyperedge_id, &incidence_list::_tail_storage); } [[nodiscard]] std::vector tail_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges, &incidence_list::_tail_storage); + return this->_size_map(n_hyperedges, &incidence_list::_tail_storage); } [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const noexcept { - return this->_get(hyperedge_id, &incidence_list::_head_storage); + return this->_get(hyperedge_id, &incidence_list::_head_storage); } [[nodiscard]] size_type head_size(const id_type hyperedge_id) const noexcept { - return this->_size(hyperedge_id, &incidence_list::_head_storage); + return this->_size(hyperedge_id, &incidence_list::_head_storage); } [[nodiscard]] std::vector head_size_map(const size_type n_hyperedges ) const noexcept { - return this->_size_map(n_hyperedges, &incidence_list::_head_storage); + return this->_size_map(n_hyperedges, &incidence_list::_head_storage); } // --- binding methods --- diff --git a/include/hgl/impl/incidence_matrix.hpp b/include/hgl/impl/incidence_matrix.hpp index 9179e2b5..40585a0d 100644 --- a/include/hgl/impl/incidence_matrix.hpp +++ b/include/hgl/impl/incidence_matrix.hpp @@ -9,6 +9,7 @@ #include "hgl/decl/repr_tags.hpp" #include "hgl/directional_tags.hpp" #include "hgl/impl/bf_incidence.hpp" +#include "hgl/impl/util.hpp" #include "hgl/repr/layout_tags.hpp" #include "hgl/types.hpp" @@ -68,48 +69,48 @@ class incidence_matrix final { // --- vertex methods --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_incident_with(vertex_id); + return this->_incident_with(vertex_id); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id); + return this->_count(vertex_id); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices); + return this->_count_map(n_vertices); } // --- hyperedge methods --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_incident_with(hyperedge_id); + return this->_incident_with(hyperedge_id); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id); + return this->_count(hyperedge_id); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges); + return this->_count_map(n_hyperedges); } // --- binding methods --- @@ -265,102 +266,102 @@ class incidence_matrix final { // --- vertex methods : general --- gl_attr_force_inline void add_vertices(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_vertex(const id_type vertex_id) noexcept { - this->_remove(vertex_id); + this->_remove(vertex_id); } // --- vertex methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id ) const noexcept { - return this->_query(vertex_id, bf_is_incident); + return this->_query(vertex_id, bf_is_incident); } [[nodiscard]] size_type degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_incident); + return this->_count(vertex_id, bf_is_incident); } [[nodiscard]] std::vector degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_incident); + return this->_count_map(n_vertices, bf_is_incident); } [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const noexcept { - return this->_query(vertex_id, bf_is_tail); + return this->_query(vertex_id, bf_is_tail); } [[nodiscard]] size_type out_degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_tail); + return this->_count(vertex_id, bf_is_tail); } [[nodiscard]] std::vector out_degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_tail); + return this->_count_map(n_vertices, bf_is_tail); } [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const noexcept { - return this->_query(vertex_id, bf_is_head); + return this->_query(vertex_id, bf_is_head); } [[nodiscard]] size_type in_degree(const id_type vertex_id) const noexcept { - return this->_count(vertex_id, bf_is_head); + return this->_count(vertex_id, bf_is_head); } [[nodiscard]] std::vector in_degree_map(const size_type n_vertices) const noexcept { - return this->_count_map(n_vertices, bf_is_head); + return this->_count_map(n_vertices, bf_is_head); } // --- hyperedge methods : general --- gl_attr_force_inline void add_hyperedges(const size_type n) noexcept { - this->_add(n); + this->_add(n); } gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) noexcept { - this->_remove(hyperedge_id); + this->_remove(hyperedge_id); } // --- hyperedge methods : incidence queries --- [[nodiscard]] gl_attr_force_inline auto incident_vertices(const id_type hyperedge_id ) const noexcept { - return this->_query(hyperedge_id, bf_is_incident); + return this->_query(hyperedge_id, bf_is_incident); } [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_incident); + return this->_count(hyperedge_id, bf_is_incident); } [[nodiscard]] std::vector hyperedge_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_incident); + return this->_count_map(n_hyperedges, bf_is_incident); } [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const noexcept { - return this->_query(hyperedge_id, bf_is_tail); + return this->_query(hyperedge_id, bf_is_tail); } [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_tail); + return this->_count(hyperedge_id, bf_is_tail); } [[nodiscard]] std::vector tail_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_tail); + return this->_count_map(n_hyperedges, bf_is_tail); } [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const noexcept { - return this->_query(hyperedge_id, bf_is_head); + return this->_query(hyperedge_id, bf_is_head); } [[nodiscard]] size_type head_size(const id_type hyperedge_id) const noexcept { - return this->_count(hyperedge_id, bf_is_head); + return this->_count(hyperedge_id, bf_is_head); } [[nodiscard]] std::vector head_size_map(const size_type n_hyperedges ) const noexcept { - return this->_count_map(n_hyperedges, bf_is_head); + return this->_count_map(n_hyperedges, bf_is_head); } // --- binding methods --- diff --git a/include/hgl/impl/util.hpp b/include/hgl/impl/util.hpp new file mode 100644 index 00000000..0b88b9af --- /dev/null +++ b/include/hgl/impl/util.hpp @@ -0,0 +1,19 @@ +// Copyright (c) 2024-2026 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 "hgl/repr/layout_tags.hpp" + +#include + +namespace hgl::impl { + +template +using major_element_t = typename LT::major_element; + +template +using minor_element_t = typename LT::minor_element; + +} // namespace hgl::impl diff --git a/include/hgl/io/hypergraph_fio.hpp b/include/hgl/io/hypergraph_fio.hpp index 4ca99426..dd34bd8a 100644 --- a/include/hgl/io/hypergraph_fio.hpp +++ b/include/hgl/io/hypergraph_fio.hpp @@ -16,7 +16,19 @@ #include #include -namespace hgl::io { +namespace hgl { + +namespace traits { + +/// @ingroup HGL-Traits +/// @brief Concept checking if a provided type is a valid file I/O save mode. +/// ### See Also +/// - [**c_io_save_mode**](gl_concepts.md#gl-traits-c-io-save-mode) : For the full concept documentation in the GL module. +using gl::traits::c_io_save_mode; + +} // namespace traits + +namespace io { /// @ingroup HGL-IO /// @copybrief gl::io::append @@ -86,4 +98,5 @@ template return hypergraph; } -} // namespace hgl::io +} // namespace io +} // namespace hgl diff --git a/include/hgl/repr/layout_tags.hpp b/include/hgl/repr/layout_tags.hpp index 60fb3a0e..8909c35e 100644 --- a/include/hgl/repr/layout_tags.hpp +++ b/include/hgl/repr/layout_tags.hpp @@ -14,16 +14,15 @@ #include namespace hgl { - namespace repr { /// @ingroup HGL-Core /// @brief Layout tag designating vertices as the primary structural dimension of the incidence representation. struct vertex_major_t { /// @brief The major element type for this layout. - using major_element = vertex_t; + using major_element = vertex_tag; /// @brief The minor element type for this layout. - using minor_element = hyperedge_t; + using minor_element = hyperedge_tag; /// @brief Retrieves the major element from the provided arguments based on the layout rules. template @@ -55,9 +54,9 @@ struct vertex_major_t { /// @brief Layout tag designating hyperedges as the primary structural dimension of the incidence representation. struct hyperedge_major_t { /// @brief The major element type for this layout. - using major_element = hyperedge_t; + using major_element = hyperedge_tag; /// @brief The minor element type for this layout. - using minor_element = vertex_t; + using minor_element = vertex_tag; /// @brief Retrieves the major element from the provided arguments based on the layout rules. template @@ -116,17 +115,4 @@ concept c_hypergraph_asymmetric_layout_tag = c_one_of; } // namespace traits - -/// @ingroup HGL-Traits -/// @brief Extracts the major element tag type associated with a specific asymmetric layout. -/// @tparam LT The asymmetric layout tag type. -template -using major_element_t = typename LT::major_element; - -/// @ingroup HGL-Traits -/// @brief Extracts the minor element tag type associated with a specific asymmetric layout. -/// @tparam LT The asymmetric layout tag type. -template -using minor_element_t = typename LT::minor_element; - } // namespace hgl diff --git a/include/hgl/traits.hpp b/include/hgl/traits.hpp index ee0ba831..b648d510 100644 --- a/include/hgl/traits.hpp +++ b/include/hgl/traits.hpp @@ -14,8 +14,8 @@ namespace hgl { /// @ingroup HGL-Traits /// @brief Traits and concepts for the HGL module. /// -/// This namespace contains the definitions of all hypergraph-specifc concepts and type traits -/// used to constrain library types and pulls in all standard graph traits, concept checkers, +/// This namespace contains the definitions of all general and hypergraph-specifc concepts and type +/// traits used to constrain library types and pulls in all general traits, concept checkers, /// and metaprogramming utilities from `gl::traits`. Because hypergraphs share the same underlying /// implementation design and mechanisms as standard graphs, they seamlessly reuse the same /// fundamental C++20 concepts. @@ -25,7 +25,137 @@ namespace hgl { /// > To get a detailed overview of these shared utilities, please refer to the GL module's @ref GL-Traits documentation page. namespace traits { -using namespace gl::traits; +/// @ingroup HGL-Traits +/// @brief Type trait to check if a type is an instantiation of a specific class template. +/// ### See Also +/// - @ref gl::traits::is_instantiation_of "gl::traits::is_instantiation_of" : For the full trait documentation in the GL module. +using gl::traits::is_instantiation_of; + +/// @ingroup HGL-Traits +/// @brief Helper variable template for the `is_instantiation_of` trait. +/// ### See Also +/// - @ref gl::traits::is_instantiation_of_v "gl::traits::is_instantiation_of_v" : For the full trait documentation in the GL module. +using gl::traits::is_instantiation_of_v; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is an instantiation of a specific class template. +/// ### See Also +/// - [**c_instantiation_of**](gl_concepts.md#gl-traits-c-instantiation-of) : For the full concept documentation in the GL module. +using gl::traits::c_instantiation_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is exactly one of the specified types. +/// ### See Also +/// - [**c_one_of**](gl_concepts.md#gl-traits-c-one-of) : For the full concept documentation in the GL module. +using gl::traits::c_one_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type satisfies `std::ranges::range`. +/// ### See Also +/// - [**c_range**](gl_concepts.md#gl-traits-c-range) : For the full concept documentation in the GL module. +using gl::traits::c_range; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a range containing a specific value type. +/// ### See Also +/// - [**c_range_of**](gl_concepts.md#gl-traits-c-range-of) : For the full concept documentation in the GL module. +using gl::traits::c_range_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a range containing a specific value type, strictly matching cv-qualifiers. +/// ### See Also +/// - [**c_range_of_cv**](gl_concepts.md#gl-traits-c-range-of-cv) : For the full concept documentation in the GL module. +using gl::traits::c_range_of_cv; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type satisfies `std::ranges::forward_range`. +/// ### See Also +/// - [**c_forward_range**](gl_concepts.md#gl-traits-c-forward-range) : For the full concept documentation in the GL module. +using gl::traits::c_forward_range; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a forward range containing a specific value type. +/// ### See Also +/// - [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) : For the full concept documentation in the GL module. +using gl::traits::c_forward_range_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a forward range containing a specific value type, strictly matching cv-qualifiers. +/// ### See Also +/// - [**c_forward_range_of_cv**](gl_concepts.md#gl-traits-c-forward-range-of-cv) : For the full concept documentation in the GL module. +using gl::traits::c_forward_range_of_cv; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type satisfies `std::ranges::sized_range`. +/// ### See Also +/// - [**c_sized_range**](gl_concepts.md#gl-traits-c-sized-range) : For the full concept documentation in the GL module. +using gl::traits::c_sized_range; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a sized range containing a specific value type. +/// ### See Also +/// - [**c_sized_range_of**](gl_concepts.md#gl-traits-c-sized-range-of) : For the full concept documentation in the GL module. +using gl::traits::c_sized_range_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a sized range containing a specific value type, strictly matching cv-qualifiers. +/// ### See Also +/// - [**c_sized_range_of_cv**](gl_concepts.md#gl-traits-c-sized-range-of-cv) : For the full concept documentation in the GL module. +using gl::traits::c_sized_range_of_cv; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type satisfies `std::ranges::random_access_range`. +/// ### See Also +/// - [**c_random_access_range**](gl_concepts.md#gl-traits-c-random-access-range) : For the full concept documentation in the GL module. +using gl::traits::c_random_access_range; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a random access range containing a specific value type. +/// ### See Also +/// - [**c_random_access_range_of**](gl_concepts.md#gl-traits-c-random-access-range-of) : For the full concept documentation in the GL module. +using gl::traits::c_random_access_range_of; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is a random access range containing a specific value type, strictly matching cv-qualifiers. +/// ### See Also +/// - [**c_random_access_range_of_cv**](gl_concepts.md#gl-traits-c-random-access-range-of-cv) : For the full concept documentation in the GL module. +using gl::traits::c_random_access_range_of_cv; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type supports three-way comparison and equality operators. +/// ### See Also +/// - [**c_comparable**](gl_concepts.md#gl-traits-c-comparable) : For the full concept documentation in the GL module. +using gl::traits::c_comparable; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is an arithmetic type (integral or floating-point). +/// ### See Also +/// - [**c_arithmetic**](gl_concepts.md#gl-traits-c-arithmetic) : For the full concept documentation in the GL module. +using gl::traits::c_arithmetic; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type provides a valid `std::numeric_limits::max()` value. +/// ### See Also +/// - [**c_has_numeric_limits_max**](gl_concepts.md#gl-traits-c-has-numeric-limits-max) : For the full concept documentation in the GL module. +using gl::traits::c_has_numeric_limits_max; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type can be extracted from a `std::istream`. +/// ### See Also +/// - [**c_readable**](gl_concepts.md#gl-traits-c-readable) : For the full concept documentation in the GL module. +using gl::traits::c_readable; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type can be inserted into a `std::ostream`. +/// ### See Also +/// - [**c_writable**](gl_concepts.md#gl-traits-c-writable) : For the full concept documentation in the GL module. +using gl::traits::c_writable; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is an enumeration. +/// ### See Also +/// - [**c_enum**](gl_concepts.md#gl-traits-c-enum) : For the full concept documentation in the GL module. +using gl::traits::c_enum; } // namespace traits } // namespace hgl diff --git a/include/hgl/types.hpp b/include/hgl/types.hpp index e36b11ca..c18e6057 100644 --- a/include/hgl/types.hpp +++ b/include/hgl/types.hpp @@ -39,6 +39,16 @@ using gl::to_idx; /// @see gl::to_diff using gl::to_diff; +namespace traits { + +/// @ingroup HGL-Traits +/// @brief Concept defining the requirements for an identifier type. +/// ### See Also +/// - [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) : For the full concept documentation in the GL module. +using gl::traits::c_id_type; + +} // namespace traits + // --- generic data structures --- /// @ingroup HGL-Types @@ -106,4 +116,50 @@ using dynamic_properties = gl::dynamic_properties; template using weight_property = gl::weight_property; +namespace traits { + +/// @ingroup HGL-Traits +/// @brief Requirements for properties that support binary coloring algorithms. +/// ### See Also +/// - [**c_binary_color_properties_type**](gl_concepts.md#gl-traits-c-binary-color-properties-type) : For the full concept documentation in the GL module. +using gl::traits::c_binary_color_properties_type; + +/// @ingroup HGL-Traits +/// @brief Validates if a type is specifically the empty properties tag. +/// ### See Also +/// - [**c_empty_properties**](gl_concepts.md#gl-traits-c-empty-properties) : For the full concept documentation in the GL module. +using gl::traits::c_empty_properties; + +/// @ingroup HGL-Traits +/// @brief Checks if a type or component has a nested `properties_type` that is the empty properties tag. +/// ### See Also +/// - [**c_has_empty_properties**](gl_concepts.md#gl-traits-c-has-empty-properties) : For the full concept documentation in the GL module. +using gl::traits::c_has_empty_properties; + +/// @ingroup HGL-Traits +/// @brief Checks if a type or component has a nested `properties_type` that is not the empty properties tag. +/// ### See Also +/// - [**c_has_non_empty_properties**](gl_concepts.md#gl-traits-c-has-non_empty_properties) : For the full concept documentation in the GL module. +using gl::traits::c_has_non_empty_properties; + +/// @ingroup HGL-Traits +/// @brief Validates if a property type contains actual user-defined data. +/// ### See Also +/// - [**c_non_empty_properties**](gl_concepts.md#gl-traits-c-non_empty_properties) : For the full concept documentation in the GL module. +using gl::traits::c_non_empty_properties; + +/// @ingroup HGL-Traits +/// @brief Defines the minimal requirements for a type to be used as a property. +/// ### See Also +/// - [**c_properties**](gl_concepts.md#gl-traits-c-properties) : For the full concept documentation in the GL module. +using gl::traits::c_properties; + +/// @ingroup HGL-Traits +/// @brief Requirements for properties that support arithmetic weight values. +/// ### See Also +/// - [**c_weight_properties_type**](gl_concepts.md#gl-traits-c-weight-properties_type) : For the full concept documentation in the GL module. +using gl::traits::c_weight_properties_type; + +} // namespace traits + } // namespace hgl diff --git a/include/hgl/util.hpp b/include/hgl/util.hpp index bc78f712..20384b43 100644 --- a/include/hgl/util.hpp +++ b/include/hgl/util.hpp @@ -23,9 +23,12 @@ namespace hgl { /// > To get a detailed overview of these shared utilities, please refer to the GL module's @ref GL-Util documentation page. namespace util { +// TODO: add doc comments + using gl::util::all_equal; using gl::util::is_constant; using gl::util::range_size; +using gl::util::to_id; /// @ingroup HGL-Util /// @brief @copybrief gl::util::concat_view diff --git a/tests/source/gl/test_graph.cpp b/tests/source/gl/test_graph.cpp index b8deb424..e7351109 100644 --- a/tests/source/gl/test_graph.cpp +++ b/tests/source/gl/test_graph.cpp @@ -114,8 +114,6 @@ struct test_graph { using vertex_id_list = std::vector; -inline constexpr auto get_id = [](auto&& element) -> gl::default_id_type { return element.id(); }; - TEST_CASE_TEMPLATE_DEFINE("common graph structure tests", TraitsType, common_graph_traits_template) { using fixture_type = test_graph; using sut_type = typename fixture_type::sut_type; @@ -139,7 +137,7 @@ TEST_CASE_TEMPLATE_DEFINE("common graph structure tests", TraitsType, common_gra sut_type sut{constants::n_elements}; REQUIRE(std::ranges::equal( - sut.vertices() | std::views::transform(get_id), constants::vertex_id_view + sut.vertices() | std::views::transform(gl::util::to_id), constants::vertex_id_view )); REQUIRE(std::ranges::equal(sut.vertex_ids(), constants::vertex_id_view)); @@ -360,7 +358,7 @@ TEST_CASE_TEMPLATE_DEFINE("common graph structure tests", TraitsType, common_gra sut_type sut{constants::n_elements}; CHECK(std::ranges::equal( - sut.vertices(), constants::vertex_id_view, std::ranges::equal_to{}, get_id + sut.vertices(), constants::vertex_id_view, std::ranges::equal_to{}, gl::util::to_id )); } @@ -1226,7 +1224,7 @@ TEST_CASE_TEMPLATE_DEFINE( ++i; return result; }, - get_id + gl::util::to_id )); } @@ -1299,7 +1297,7 @@ TEST_CASE_TEMPLATE_DEFINE( ++i; return result; }, - get_id + gl::util::to_id )); } diff --git a/tests/source/hgl/test_hypergraph.cpp b/tests/source/hgl/test_hypergraph.cpp index 03c6abe2..dc6bff17 100644 --- a/tests/source/hgl/test_hypergraph.cpp +++ b/tests/source/hgl/test_hypergraph.cpp @@ -50,8 +50,6 @@ using add_properties = hgl::hypergraph_traits< Properties, typename HypergraphTraits::representation_tag>; -inline constexpr auto get_id = [](auto&& element) -> hgl::default_id_type { return element.id(); }; - TEST_CASE_TEMPLATE_DEFINE( "hypergraph structure tests", HypergraphTraits, hypergraph_traits_template ) { @@ -73,7 +71,9 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE_EQ(sut.n_vertices(), constants::n_vertices); REQUIRE_EQ(sut.n_hyperedges(), 0uz); - REQUIRE(rng::equal(sut.vertices() | vw::transform(get_id), constants::vertex_ids_view)); + REQUIRE( + rng::equal(sut.vertices() | vw::transform(hgl::util::to_id), constants::vertex_ids_view) + ); REQUIRE(rng::equal(sut.vertex_ids(), constants::vertex_ids_view)); CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::invalid_argument); @@ -86,12 +86,15 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE_EQ(sut.n_vertices(), constants::n_vertices); REQUIRE_EQ(sut.n_hyperedges(), constants::n_hyperedges); - REQUIRE(rng::equal(sut.vertices() | vw::transform(get_id), constants::vertex_ids_view)); + REQUIRE( + rng::equal(sut.vertices() | vw::transform(hgl::util::to_id), constants::vertex_ids_view) + ); REQUIRE(rng::equal(sut.vertex_ids(), constants::vertex_ids_view)); CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::invalid_argument); - REQUIRE(rng::equal(sut.hyperedges() | vw::transform(get_id), constants::hyperedge_ids_view) - ); + REQUIRE(rng::equal( + sut.hyperedges() | vw::transform(hgl::util::to_id), constants::hyperedge_ids_view + )); REQUIRE(rng::equal(sut.hyperedge_ids(), constants::hyperedge_ids_view)); CHECK_THROWS_AS(discard(sut.hyperedge(constants::out_of_rng_eid)), std::invalid_argument); } @@ -508,12 +511,12 @@ TEST_CASE_TEMPLATE_DEFINE( ); } - SUBCASE("remove_hyperedges_from(ids) should properly remove elements at given indices " + SUBCASE("remove_hyperedges(ids) should properly remove elements at given indices " "(ignoring duplicate indices)") { constexpr auto n_hyperedges = constants::n_hyperedges + 1uz; sut_type sut{0uz, n_hyperedges}; - sut.remove_hyperedges_from( + sut.remove_hyperedges( std::vector{constants::id1, constants::id3, constants::id1} ); @@ -521,14 +524,14 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE_EQ(sut.n_hyperedges(), expected_n_hyperedges); } - SUBCASE("remove_hyperedges_from(hyperedges) should properly remove elements at given indices " + SUBCASE("remove_hyperedges(hyperedges) should properly remove elements at given indices " "(ignoring duplicate hyperedges)") { constexpr auto n_hyperedges = constants::n_hyperedges + 1uz; sut_type sut{0uz, n_hyperedges}; const auto he1 = sut.hyperedge(constants::id1); const auto he3 = sut.hyperedge(constants::id3); - sut.remove_hyperedges_from(std::vector{he1, he3, he1}); + sut.remove_hyperedges(std::vector{he1, he3, he1}); constexpr auto expected_n_hyperedges = n_hyperedges - 2uz; REQUIRE_EQ(sut.n_hyperedges(), expected_n_hyperedges); @@ -898,7 +901,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_hyperedges(vertex_id), expected_hyperedges, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.degree(vertex_id), expected_hyperedges.size()); } @@ -922,12 +925,15 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_hyperedges(vertex_id), expected_hyperedges, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.degree(vertex_id), expected_hyperedges.size()); CHECK(std::ranges::equal( - sut.in_hyperedges(vertex_id), expected_in_hyperedges, std::equal_to{}, get_id + sut.in_hyperedges(vertex_id), + expected_in_hyperedges, + std::equal_to{}, + hgl::util::to_id )); CHECK_EQ(sut.in_degree(vertex_id), expected_in_hyperedges.size()); @@ -935,7 +941,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.out_hyperedges(vertex_id), expected_out_hyperedges, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.out_degree(vertex_id), expected_out_hyperedges.size()); } @@ -951,7 +957,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_hyperedges(vertex_id), std::vector{constants::id2, constants::id4}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.degree(vertex_id), 2uz); } @@ -964,7 +970,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_hyperedges(vertex_id), std::vector{constants::id2, constants::id4}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.degree(vertex_id), 2uz); @@ -972,7 +978,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.in_hyperedges(vertex_id), std::vector{constants::id2}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.in_degree(vertex_id), 1uz); @@ -980,7 +986,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.out_hyperedges(vertex_id), std::vector{constants::id4}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.out_degree(vertex_id), 1uz); } @@ -1031,7 +1037,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_vertices(hyperedge_id), expected_vertices, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.hyperedge_size(hyperedge_id), expected_vertices.size()); } @@ -1055,17 +1061,23 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_vertices(hyperedge_id), expected_vertices, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.hyperedge_size(hyperedge_id), expected_vertices.size()); CHECK(std::ranges::equal( - sut.head(hyperedge_id), expected_head_vertices, std::equal_to{}, get_id + sut.head(hyperedge_id), + expected_head_vertices, + std::equal_to{}, + hgl::util::to_id )); CHECK_EQ(sut.head_size(hyperedge_id), expected_head_vertices.size()); CHECK(std::ranges::equal( - sut.tail(hyperedge_id), expected_tail_vertices, std::equal_to{}, get_id + sut.tail(hyperedge_id), + expected_tail_vertices, + std::equal_to{}, + hgl::util::to_id )); CHECK_EQ(sut.tail_size(hyperedge_id), expected_tail_vertices.size()); } @@ -1081,7 +1093,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_vertices(hyperedge_id), std::vector{constants::id1, constants::id3}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.hyperedge_size(hyperedge_id), 2uz); } @@ -1094,7 +1106,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.incident_vertices(hyperedge_id), std::vector{constants::id1, constants::id3}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.hyperedge_size(hyperedge_id), 2uz); @@ -1102,7 +1114,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.head(hyperedge_id), std::vector{constants::id1}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.head_size(hyperedge_id), 1uz); @@ -1110,7 +1122,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.tail(hyperedge_id), std::vector{constants::id3}, std::equal_to{}, - get_id + hgl::util::to_id )); CHECK_EQ(sut.tail_size(hyperedge_id), 1uz); }