From 7cd38604a7d91097ad2eee583f59a7e70f21e5ae Mon Sep 17 00:00:00 2001 From: Medusa Slockbower Date: Mon, 18 Aug 2025 19:41:08 -0400 Subject: [PATCH] - More Documentation --- include/fennec/containers/graph.h | 246 +++++++++++++++--- include/fennec/langproc/filesystem/file.h | 5 + test/CMakeLists.txt | 1 + .../performance/test_iterator_visitor.h | 22 ++ 4 files changed, 239 insertions(+), 35 deletions(-) create mode 100644 test/tests/containers/performance/test_iterator_visitor.h diff --git a/include/fennec/containers/graph.h b/include/fennec/containers/graph.h index f883482..98ff5b9 100644 --- a/include/fennec/containers/graph.h +++ b/include/fennec/containers/graph.h @@ -41,6 +41,12 @@ namespace fennec /// /// \brief Graph Data Structure, describes sets of arbitrarily connected nodes +/// +/// \details +/// Graphs contain nodes (sometimes called vertices) and connections. Graphs are either directed +/// or undirected. This structure allows the creation of both directed and undirected connections. As +/// far as what that means; a directed graph means that connections have direction, where there are connections +/// that are "to" and "from," rather than "between" which is used in undirected graphs There are . /// \tparam NodeT The type associated with each node /// \tparam WeightT The type associated with each connection template @@ -78,8 +84,19 @@ public: /// \name Assignment Operators /// @{ + + /// + /// \brief Copy Assignment Operator + /// \param g The graph to copy + /// \returns A reference to this after assigning g constexpr graph& operator=(const graph& g) = default; + + /// + /// \brief Move Assignment Operator + /// \param g The graph to copy + /// \returns A reference to this after assigning g constexpr graph& operator=(graph&& g) = default; + /// @} @@ -87,66 +104,169 @@ public: /// \name Properties /// @{ + + /// + /// \returns The number of nodes in the graph constexpr size_t num_nodes() const { return _node_pool.size(); } + /// + /// \returns The number of connections in the graph constexpr size_t num_connections() const { return _conn_pool.size(); } + /// + /// \returns The capacity of the node pool constexpr size_t capacity() const { return _node_pool.capacity(); } + /// + /// \returns `true` when there are no nodes in the graph, `false` otherwise constexpr bool empty() const { return num_nodes() == 0; } + /// @} -// Nodes =============================================================================================================== + // Access ============================================================================================================== + /// \name Access + /// @{ + + /// + /// \brief Node Access Operator + /// \param node The id of the node + /// \returns A reference to the value stored in the node + constexpr node_t& operator[](size_t node) { + return _node_pool[node]; + } + + /// + /// \brief Node Const Access Operator + /// \param node The id of the node + /// \returns A reference to the value stored in the node + constexpr const node_t& operator[](size_t node) const { + return _node_pool[node]; + } + + /// + /// \brief Connection Access Operator + /// \param a The id of the first node + /// \param b The id of the second node + /// \returns A reference to the value stored in the connection + constexpr weight_t& operator[](size_t a, size_t b) { + weight_t* it = _conn_map[a][b]; + assertd(it, "Element not Found!"); + return _conn_pool[*it]; + } + + /// + /// \brief Connection Const Access Operator + /// \param a The id of the first node + /// \param b The id of the second node + /// \returns A const-qualified reference to the value stored in the connection + constexpr const weight_t& operator[](size_t a, size_t b) const { + weight_t* it = _conn_map[a][b]; + assertd(it, "Element not Found!"); + return _conn_pool[*it]; + } + + /// + /// \brief Getter for a list of nodes that `node` has outgoing connections to + /// \param node The id of the node + /// \returns A list containing all nodes `x` with connections from `node` to `x...` + list outgoing(size_t node) { + list res; + for (const auto& it : _conn_map[node]) { + res.push_back(it.first); + } + return res; + } + + /// + /// \brief Getter for a list of nodes that `node` has incoming connections from + /// \param node The id of the node + /// \returns A list containing all nodes `x` with connections from `x...` to `node` + list incoming(size_t node) { + list res; + for (int n = 0; n < _conn_map.size(); ++n) { + if (_conn_map[n][node]) { + res.push_back(n); + } + } + return res; + } + + /// + /// \brief + /// \param node A list of all nodes `x` that have symmetric connections with `node`, i.e. `node` has a connection + /// both to and from `x...` + /// \returns A list containing all nodes `x` that have symmetric connections with `node` + list symmetric(size_t node) { + list res; + for (const auto& it : _conn_map[node]) { + if (_conn_map[it.first][node]) { + res.push_back(it.first); + } + } + return res; + } + + + + + /// @} + + +// Modifiers =========================================================================================================== + + /// \name Modifiers + /// @{ + + /// + /// \brief Move a new node into the graph + /// \param node The node to move into the graph + /// \returns The id of the new node constexpr size_t insert(node_t&& node) { return this->_insert(fennec::forward(node)); } + /// + /// \brief Copy a new node into the graph + /// \param node The node to copy into the graph + /// \returns The id of the new node constexpr size_t insert(const node_t& node) { return this->_insert(node); } + /// + /// \brief Construct a new node in the graph + /// \tparam ArgsT The types of the arguments + /// \param args The arguments to construct the node with + /// \returns The id of the new node template constexpr size_t emplace(ArgsT&&...args) { return this->_insert(fennec::forward(args)...); } + /// + /// \brief Erase a node from the graph + /// \param node The id of the node to erase constexpr void erase(size_t node) { disconnect(node); _node_pool.erase(node); } - constexpr node_t& operator[](size_t node) { - return _node_pool[node]; - } - - constexpr const node_t& operator[](size_t node) const { - return _node_pool[node]; - } - -// Connections ========================================================================================================= - - list> connections(size_t n) { - list> conns; - for (size_t i = 0; i < _conn_map.size(); ++i) { - for (auto it : _conn_map[i]) { - if (i == n || it.first == n) { - conns.emplace_back(i, n); - } - } - } - return conns; - } - + /// + /// \brief Form a connection from node `a` to node `b` + /// \tparam ArgsT The argument types + /// \param a The first node id + /// \param b The second node id + /// \param args The arguments to construct the connection with template constexpr void connect(size_t a, size_t b, ArgsT&&...args) { if (a == b) { @@ -165,11 +285,66 @@ public: } else { conn = _conn_pool.emplace(fennec::forward(args)...); } - _conn_map[a].emplace(b, conn); } + /// + /// \brief Form a bidirectional connection between node `a` and node `b` + /// \tparam ArgsT The argument types + /// \param a The first node id + /// \param b The second node id + /// \param args The arguments to construct the connection with + template + constexpr void connect2(size_t a, size_t b, ArgsT&&...args) { + if (a == b) { + return; + } + + if (_conn_map.size() < _node_pool.capacity()) { + _conn_map.resize(_node_pool.capacity()); + } + + auto it = _conn_map[a][b]; + size_t conn; + if (it != nullptr) { + conn = *it; + _conn_pool[conn] = node_t(fennec::forward(args)...); + } else { + conn = _conn_pool.emplace(fennec::forward(args)...); + } + + _conn_map[a].emplace(b, conn); + _conn_map[b].emplace(a, conn); + } + + /// + /// \brief Disconnect a connection from node `a` to node `b` + /// \param a The first node id + /// \param b The second node id constexpr void disconnect(size_t a, size_t b) { + + // Find the connection object + const auto* it = _conn_map[a][b]; + if (not it) { + return; + } + size_t c = *it; + + // Check if bi-directional + const auto* at = _conn_map[b][a]; + if (not at || *at != c) { + _conn_pool.erase(c); + } + + // Erase the connection mapping + _conn_map[a].erase(b); + } + + /// + /// \brief Disconnect a bidirectional connection between nodes `a` and `b` + /// \param a The first node id + /// \param b The second node id + constexpr void disconnect2(size_t a, size_t b) { const auto* it = _conn_map[a][b]; if (not it) { return; @@ -177,24 +352,25 @@ public: size_t c = *it; _conn_pool.erase(c); _conn_map[a].erase(b); + _conn_map[b].erase(a); } + /// + /// \brief Break *all* connections to and from `n` + /// \param n The node id void disconnect(size_t n) { - const auto conns = connections(n); - for (const auto& conn : conns) { - disconnect(conn.first, conn.second); + for (const auto it : outgoing(n)) { + disconnect(n, it); + } + for (const auto it : incoming(n)) { + disconnect(it, n); } } - constexpr weight_t& operator[](size_t a, size_t b) { - weight_t* it = _conn_map[a][b]; - assertd(it, "Element not Found!"); - return _conn_pool[*it]; - } + /// @} - constexpr const weight_t& operator[](size_t a, size_t b) const { - return _conn_pool[_conn_map[a][b]]; - } + +// Connections ========================================================================================================= private: diff --git a/include/fennec/langproc/filesystem/file.h b/include/fennec/langproc/filesystem/file.h index c4f5787..4179df1 100644 --- a/include/fennec/langproc/filesystem/file.h +++ b/include/fennec/langproc/filesystem/file.h @@ -297,6 +297,11 @@ public: } +// Printing Operations ================================================================================================= + + + + // Error Handling ====================================================================================================== const char* get_error() const { return _error; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a2d3f27..1dd7f5a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_C_STANDARD 23) add_executable(fennec-test main.cpp + tests/containers/performance/test_iterator_visitor.h ) target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}" diff --git a/test/tests/containers/performance/test_iterator_visitor.h b/test/tests/containers/performance/test_iterator_visitor.h new file mode 100644 index 0000000..e09e610 --- /dev/null +++ b/test/tests/containers/performance/test_iterator_visitor.h @@ -0,0 +1,22 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H +#define FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H + +#endif // FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H \ No newline at end of file