- Missing functionality and documentation

This commit is contained in:
2025-08-20 00:49:15 -04:00
parent 83f0c01e29
commit 494d766741
17 changed files with 753 additions and 352 deletions

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = fennec
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.0.2 PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
@@ -129,7 +129,7 @@ BRIEF_MEMBER_DESC = YES
# brief descriptions will be completely suppressed. # brief descriptions will be completely suppressed.
# The default value is: YES. # The default value is: YES.
REPEAT_BRIEF = YES REPEAT_BRIEF = NO
# This tag implements a quasi-intelligent brief description abbreviator that is # This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found # used to form the text in various listings. Each string in this list, if found

View File

@@ -129,7 +129,7 @@ BRIEF_MEMBER_DESC = YES
# brief descriptions will be completely suppressed. # brief descriptions will be completely suppressed.
# The default value is: YES. # The default value is: YES.
REPEAT_BRIEF = YES REPEAT_BRIEF = NO
# This tag implements a quasi-intelligent brief description abbreviator that is # This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found # used to form the text in various listings. Each string in this list, if found

View File

@@ -18,7 +18,7 @@
/// ///
/// \file array.h /// \file array.h
/// \brief statically allocated array wrapper /// \brief A header containing the definition for a static/stack allocated array
/// ///
/// ///
/// \details /// \details
@@ -132,11 +132,13 @@ struct array
/// @{ /// @{
/// ///
/// \brief /// \brief Checks if all elements in the arrays are equal
friend constexpr bool_t operator==(const array& lhs, const array& rhs) { friend constexpr bool_t operator==(const array& lhs, const array& rhs) {
return array::_compare(lhs, rhs, make_index_sequence<ElemV>{}); return array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
} }
///
/// \brief Checks if any element in the arrays is not equal
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) { friend constexpr bool_t operator!=(const array& lhs, const array& rhs) {
return not array::_compare(lhs, rhs, make_index_sequence<ElemV>{}); return not array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
} }
@@ -150,12 +152,14 @@ struct array
/// @{ /// @{
/// ///
/// \brief C++ Iterator Specification `begin()`
/// \returns A pointer to the first element of the array /// \returns A pointer to the first element of the array
constexpr ValueT* begin() { constexpr ValueT* begin() {
return elements; return elements;
} }
/// ///
/// \brief C++ Iterator Specification `end()`
/// \returns A pointer to one after the end of the array /// \returns A pointer to one after the end of the array
constexpr ValueT* end() { constexpr ValueT* end() {
return elements + ElemV; return elements + ElemV;
@@ -164,12 +168,14 @@ struct array
/// ///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const-qualified pointer to the first element of the array /// \returns A const-qualified pointer to the first element of the array
constexpr const ValueT* begin() const { constexpr const ValueT* begin() const {
return elements; return elements;
} }
/// ///
/// \brief Const C++ Iterator Specification `end()`
/// \returns A const-qualified pointer to one after the end of the array /// \returns A const-qualified pointer to one after the end of the array
constexpr const ValueT* end() const { constexpr const ValueT* end() const {
return elements + ElemV; return elements + ElemV;

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file deque.h
/// \brief A header containing the definition for a double-ended queue
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_DEQUE_H #ifndef FENNEC_CONTAINERS_DEQUE_H
#define FENNEC_CONTAINERS_DEQUE_H #define FENNEC_CONTAINERS_DEQUE_H
@@ -54,7 +66,7 @@ struct deque {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
public: public:
using value_t = TypeT; using value_t = TypeT; ///< Alias for the value type
class iterator; class iterator;
private: private:

View File

@@ -18,7 +18,7 @@
/// ///
/// \file dynarray.h /// \file dynarray.h
/// \brief dynamically allocated array wrapper /// \brief A header containing the definition for a dynamically allocated array
/// ///
/// ///
/// \details /// \details
@@ -40,7 +40,7 @@ namespace fennec
/// ///
/// ///
/// \brief Wrapper for dynamically sized arrays /// \brief Wrapper for dynamically sized and allocated arrays
/// \details /// \details
/// | Property | Value | /// | Property | Value |
/// |:----------:|:----------:| /// |:----------:|:----------:|
@@ -56,6 +56,8 @@ namespace fennec
/// | insertion | \f$O(N)\f$ | /// | insertion | \f$O(N)\f$ |
/// | deletion | \f$O(N)\f$ | /// | deletion | \f$O(N)\f$ |
/// ///
/// This structure prefers shallow moves and deep copies.
///
/// \tparam TypeT value type /// \tparam TypeT value type
template<class TypeT, class Alloc = allocator<TypeT>> template<class TypeT, class Alloc = allocator<TypeT>>
class dynarray { class dynarray {
@@ -63,8 +65,8 @@ public:
// Definitions ========================================================================================================= // Definitions =========================================================================================================
using element_t = TypeT; using value_t = TypeT; ///< Alias for the value type
using alloc_t = Alloc; using alloc_t = Alloc; ///< Alias for the allocator type
// Constructors ======================================================================================================== // Constructors ========================================================================================================
@@ -83,7 +85,7 @@ public:
/// \brief Alloc Constructor, initialize empty allocation with allocator instance. /// \brief Alloc Constructor, initialize empty allocation with allocator instance.
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some /// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
/// data. /// data.
constexpr dynarray(const alloc_t& alloc) explicit constexpr dynarray(const alloc_t& alloc)
: _alloc(8, alloc) : _alloc(8, alloc)
, _size(0) { , _size(0) {
} }
@@ -92,19 +94,19 @@ public:
/// \brief Alloc Move Constructor, initialize empty allocation with allocator instance. /// \brief Alloc Move Constructor, initialize empty allocation with allocator instance.
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some /// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
/// data. /// data.
constexpr dynarray(alloc_t&& alloc) noexcept explicit constexpr dynarray(alloc_t&& alloc) noexcept
: _alloc(8, alloc) : _alloc(8, alloc)
, _size(0) { , _size(0) {
} }
/// ///
/// \brief Sized Allocation, create an allocation of size `n` elements, /// \brief Sized Allocation, initializes a dynarray with `n` elements using the default constructor.
/// initialized with the default constructor. /// \param n The number of elements.
constexpr dynarray(size_t n) explicit constexpr dynarray(size_t n)
: _alloc(n) : _alloc(n)
, _size(n) , _size(n)
{ {
element_t* addr = _alloc.data(); value_t* addr = _alloc.data();
for(; n > 0; --n, ++addr) { for(; n > 0; --n, ++addr) {
fennec::construct(addr); fennec::construct(addr);
} }
@@ -118,7 +120,7 @@ public:
constexpr dynarray(size_t n, const alloc_t& alloc) constexpr dynarray(size_t n, const alloc_t& alloc)
: _alloc(n, alloc) : _alloc(n, alloc)
, _size(n) { , _size(n) {
element_t* addr = _alloc.data(); value_t* addr = _alloc.data();
for(; n > 0; --n, ++addr) { for(; n > 0; --n, ++addr) {
fennec::construct(addr); fennec::construct(addr);
} }
@@ -132,7 +134,7 @@ public:
constexpr dynarray(size_t n, alloc_t&& alloc) constexpr dynarray(size_t n, alloc_t&& alloc)
: _alloc(n, alloc) : _alloc(n, alloc)
, _size(n) { , _size(n) {
element_t* addr = _alloc.data(); value_t* addr = _alloc.data();
for(; n > 0; --n, ++addr) { for(; n > 0; --n, ++addr) {
fennec::construct(addr); fennec::construct(addr);
} }
@@ -142,10 +144,11 @@ public:
/// \brief Sized Allocation Copy Constructor, Create an allocation of size `n` elements, with each element /// \brief Sized Allocation Copy Constructor, Create an allocation of size `n` elements, with each element
/// constructed using the copy constructor /// constructed using the copy constructor
/// \param n the number of elements /// \param n the number of elements
/// \param val the value to copy
constexpr dynarray(size_t n, const TypeT& val) constexpr dynarray(size_t n, const TypeT& val)
: _alloc(n) : _alloc(n)
, _size(n) { , _size(n) {
element_t* addr = _alloc.data(); value_t* addr = _alloc.data();
for(; n > 0; --n, ++addr) { for(; n > 0; --n, ++addr) {
fennec::construct(addr, val); fennec::construct(addr, val);
} }
@@ -161,7 +164,7 @@ public:
/// \param n The number of objects to create /// \param n The number of objects to create
/// \param args The arguments to create each object with /// \param args The arguments to create each object with
template<typename...ArgsT> template<typename...ArgsT>
constexpr dynarray(size_t n, ArgsT&&...args) constexpr explicit dynarray(size_t n, ArgsT&&...args)
: _alloc(n) : _alloc(n)
, _size(n) { , _size(n) {
for(; n > 0; --n) { for(; n > 0; --n) {
@@ -183,15 +186,16 @@ public:
/// ///
/// \brief Move Constructor, takes ownership of the allocation /// \brief Move Constructor, takes ownership of the allocation
/// \param arr the dynarray to move /// \param arr the dynarray to move
constexpr dynarray(dynarray&& arr) constexpr dynarray(dynarray&& arr) noexcept
: _alloc(fennec::move(arr._alloc)) : _alloc(fennec::move(arr._alloc))
, _size(arr._size) { , _size(arr._size) {
arr._size = 0;
} }
/// ///
/// \brief Default Destructor, destructs all elements and frees the underlying allocation /// \brief Default Destructor, destructs all elements and frees the underlying allocation
constexpr ~dynarray() { constexpr ~dynarray() {
element_t* addr = _alloc.data(); value_t* addr = _alloc.data();
if (addr == nullptr) return; if (addr == nullptr) return;
for(int n = _size; n > 0; --n, ++addr) { for(int n = _size; n > 0; --n, ++addr) {
fennec::destruct(addr); fennec::destruct(addr);
@@ -200,6 +204,37 @@ public:
/// @} /// @}
// Assignment ==========================================================================================================
/// \name Properties
/// @{
///
/// \brief Copy Assignment Operator
/// \param arr the array to copy
/// \returns A dynarray after having copied each element of `arr`
constexpr dynarray& operator=(const dynarray& arr) {
this->clear();
_alloc.creallocate(_size = arr._size);
for (size_t i = 0; i < _size; ++i) {
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
}
return *this;
}
///
/// \brief Move Assignment Operator
/// \param arr the array to move
/// \returns A dynarray after having taken ownership of the contents of `arr`
constexpr dynarray& operator=(dynarray&& arr) noexcept {
this->clear();
_alloc = fennec::move(arr._alloc);
_size = arr._size;
arr._size = 0;
return *this;
}
/// @}
// Properties ========================================================================================================== // Properties ==========================================================================================================
@@ -397,6 +432,15 @@ public:
} }
} }
///
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
constexpr void clear() {
while (_size > 0) {
fennec::destruct(&_alloc[--_size]);
}
_alloc.deallocate();
}
/// @} /// @}
@@ -432,7 +476,7 @@ private:
_alloc.creallocate(_alloc.capacity() * 2); _alloc.creallocate(_alloc.capacity() * 2);
} }
allocation<element_t, alloc_t> _alloc; allocation<value_t, alloc_t> _alloc;
size_t _size; size_t _size;
}; };

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file graph.h
/// \brief A header containing the definition for a graph of vertices connected by edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_GRAPH_H #ifndef FENNEC_CONTAINERS_GRAPH_H
#define FENNEC_CONTAINERS_GRAPH_H #define FENNEC_CONTAINERS_GRAPH_H
@@ -28,19 +40,19 @@
/* /*
* With the directed tree we were able to cheat a little, the structure has more rules to it which allows * With the directed tree we were able to cheat a little, the structure has more rules to it which allows
* tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign * tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
* properties or rules to connections which can simply be an extension to this graph. * properties or rules to edges which can simply be an extension to this graph.
* *
* The most effective way to do this is to have a dynarray of lists, however this results in double the * The most effective way to do this is to have a dynarray of lists, however this results in double the
* memory being used. This can also result in two connection objects being created. * memory being used. This can also result in two edge objects being created.
* *
* There is no nice way to avoid the problem of mapping pins to connections * There is no nice way to avoid the problem of mapping vertices to edges
*/ */
namespace fennec namespace fennec
{ {
/// ///
/// \brief Graph Data Structure, describes sets of arbitrarily connected nodes /// \brief Graph Data Structure, describes sets of arbitrarily connected vertices
/// ///
/// \details /// \details
/// | Property | Value | /// | Property | Value |
@@ -57,44 +69,41 @@ namespace fennec
/// | insertion | \f$O(1)\f$ | /// | insertion | \f$O(1)\f$ |
/// | deletion | \f$O(N)\f$ | /// | deletion | \f$O(N)\f$ |
/// ///
/// Graphs contain nodes (sometimes called vertices) and connections. Graphs are either directed /// Graphs contain vertices and edges. Graphs are either directed
/// or undirected. This structure allows the creation of both directed and undirected connections. As /// or undirected. This structure allows the creation of both directed and undirected edges. As
/// far as what that means; a directed graph means that connections have direction, where there are connections /// far as what that means; a directed graph means that edges have direction, where there are edges
/// that are "to" and "from," rather than "between" which is used in undirected graphs. /// that are "to" and "from," rather than "between" which is used in undirected graphs.
/// ///
/// An undirected graph is connected if there is a path between every pair of nodes in the graph. /// An undirected graph is connected if there is a path between every pair of vertices in the graph.
/// ///
/// A directed graph is weakly connected if replacing all of its directed connections with undirected connections would /// A directed graph is weakly connected if replacing all of its directed edges with undirected edges would
/// produce a connected graph. We will call this "disjointed" /// produce a connected graph. We will call this "disjointed"
/// ///
/// A directed graph is semi-connected if there is a directed path p for `u` &rarr; `v` *or* `v` &rarr; `u` for every /// A directed graph is semi-connected if there is a directed path p for `u` &rarr; `v` *or* `v` &rarr; `u` for every
/// pair of nodes `u, v`. We will call this "unilateral" /// pair of vertices `u, v`. We will call this "unilateral"
/// ///
/// A directed graph is strongly-connected if there is a directed path p for `u` &rarr; `v` *and* `v` &rarr; `u` for every pair /// A directed graph is strongly-connected if there is a directed path p for `u` &rarr; `v` *and* `v` &rarr; `u` for every pair
/// of nodes `u, v`. We will call this "connected" /// of vertices `u, v`. We will call this "connected"
/// ///
/// \tparam NodeT The type associated with each node /// \tparam VertexT The type associated with each vertex
/// \tparam WeightT The type associated with each connection /// \tparam EdgeT The type associated with each edge
template<typename NodeT, typename WeightT = nullptr_t> template<typename VertexT, typename EdgeT = empty_t>
struct graph { struct graph {
public: public:
// Definitions ========================================================================================================= // Definitions =========================================================================================================
/// \name Definitions using edge_t = EdgeT; ///< Alias for the edge type
/// @{ using vertex_t = VertexT; ///< Alias for the vertex type
using weight_t = WeightT; using vertex_pool_t = object_pool<vertex_t>; ///< Alias for a pool of vertices
using node_t = NodeT; using edge_map_t = dynarray<map<size_t, size_t>>; ///< Alias for edge mapping
using conn_map_t = dynarray<map<size_t, size_t>>; using edge_pool_t = object_pool<edge_t>; ///< Alias for a pool of edges
using node_pool_t = object_pool<node_t>;
using conn_pool_t = object_pool<weight_t>;
static constexpr size_t npos = -1; static constexpr size_t npos = -1; ///< Constant for a non-existent vertex
/// @}
// Constructors ======================================================================================================== // Constructors ========================================================================================================
/// \name Constructors /// \name Constructors & Destructor
/// @{ /// @{
/// ///
@@ -132,30 +141,63 @@ public:
/// @{ /// @{
/// ///
/// \returns The number of nodes in the graph /// \returns The number of vertices in the graph
constexpr size_t num_nodes() const { constexpr size_t num_vertices() const {
return _node_pool.size(); return _vertex_pool.size();
} }
/// ///
/// \returns The number of connections in the graph /// \returns The number of edges in the graph
constexpr size_t num_connections() const { constexpr size_t num_edges() const {
return _conn_pool.size(); return _conn_pool.size();
} }
/// ///
/// \returns The capacity of the node pool /// \returns The capacity of the vertex pool
constexpr size_t capacity() const { constexpr size_t capacity() const {
return _node_pool.capacity(); return _vertex_pool.capacity();
} }
/// ///
/// \returns `true` when there are no nodes in the graph, `false` otherwise /// \returns `true` when there are no vertices in the graph, `false` otherwise
constexpr bool empty() const { constexpr bool empty() const {
return num_nodes() == 0; return num_vertices() == 0;
} }
// TODO: connected, disjoint, unilateral ///
/// \brief Checks if there exists an edge `e` that starts from `a` and ends at `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if the edge exists, `false` otherwise
constexpr bool exists(size_t a, size_t b) const {
return _conn_map[a][b] != nullptr;
}
///
/// \brief Checks if there exists an edge `e0` that starts from `a` and ends at `b` and `e1` that starts from `b`
/// and ends at `a`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
constexpr bool is_symmetric(size_t a, size_t b) const {
return exists(a, b) and exists(b, a);
}
///
/// \brief Checks if there exists an edge `e` between `a` and `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
constexpr bool is_undirected(size_t a, size_t b) const {
const auto* e0 = _conn_map[a][b];
const auto* e1 = _conn_map[b][a];
if (not (e0 != nullptr && e1 != nullptr)) {
return false;
}
return *e0 == *e1;
}
// TODO: connected, disjoint, unilateral, get_component
/// @} /// @}
@@ -166,31 +208,31 @@ public:
/// @{ /// @{
/// ///
/// \brief Node Access Operator /// \brief vertex Access Operator
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A reference to the value stored in the node /// \returns A reference to the value stored in the vertex
constexpr node_t& operator[](size_t node) { constexpr vertex_t& operator[](size_t vertex) {
return _node_pool[node]; return _vertex_pool[vertex];
} }
/// ///
/// \brief Node Const Access Operator /// \brief vertex Const Access Operator
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A reference to the value stored in the node /// \returns A reference to the value stored in the vertex
constexpr const node_t& operator[](size_t node) const { constexpr const vertex_t& operator[](size_t vertex) const {
return _node_pool[node]; return _vertex_pool[vertex];
} }
/// ///
/// \brief Connection Access Operator /// \brief edge Access Operator
/// \param a The id of the first node /// \param a The id of the first vertex
/// \param b The id of the second node /// \param b The id of the second vertex
/// \returns A pointer to the value stored in the connection, `nullptr` if not found /// \returns A pointer to the value stored in the edge, `nullptr` if not found
constexpr weight_t* operator[](size_t a, size_t b) { constexpr edge_t* operator[](size_t a, size_t b) {
if (empty()) { if (empty()) {
return nullptr; return nullptr;
} }
weight_t* it = _conn_map[a][b]; edge_t* it = _conn_map[a][b];
if (it) { if (it) {
return _conn_pool[*it]; return _conn_pool[*it];
} }
@@ -198,15 +240,15 @@ public:
} }
/// ///
/// \brief Connection Const Access Operator /// \brief edge Const Access Operator
/// \param a The id of the first node /// \param a The id of the first vertex
/// \param b The id of the second node /// \param b The id of the second vertex
/// \returns A const-qualified pointer to the value stored in the connection, `nullptr` if not found /// \returns A const-qualified pointer to the value stored in the edge, `nullptr` if not found
constexpr const weight_t* operator[](size_t a, size_t b) const { constexpr const edge_t* operator[](size_t a, size_t b) const {
if (empty()) { if (empty()) {
return nullptr; return nullptr;
} }
const weight_t* it = _conn_map[a][b]; const edge_t* it = _conn_map[a][b];
if (it) { if (it) {
return _conn_pool[*it]; return _conn_pool[*it];
} }
@@ -214,31 +256,31 @@ public:
} }
/// ///
/// \brief Getter for a list of nodes that `node` has outgoing connections to /// \brief Getter for a list of vertices `x` that `vertex` has an edge to `x...`
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A list containing all nodes `x` with connections from `node` to `x...` /// \returns A list containing all vertices `x` with edges from `vertex` to `x...`
list<size_t> outgoing(size_t node) { list<size_t> outgoing(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || node >= _conn_map.size()) { if (empty() || vertex >= _conn_map.size()) {
return res; return res;
} }
for (const auto& it : _conn_map[node]) { for (const auto& it : _conn_map[vertex]) {
res.push_back(it.first); res.push_back(it.first);
} }
return res; return res;
} }
/// ///
/// \brief Getter for a list of nodes that `node` has incoming connections from /// \brief Getter for a list of vertices `x` that `vertex` has an edge from `x...`
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A list containing all nodes `x` with connections from `x...` to `node` /// \returns A list containing all vertices `x` with edges from `x...` to `vertex`
list<size_t> incoming(size_t node) { list<size_t> incoming(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || node >= _conn_map.size()) { if (empty() || vertex >= _conn_map.size()) {
return res; return res;
} }
for (size_t n = 0; n < _conn_map.size(); ++n) { for (size_t n = 0; n < _conn_map.size(); ++n) {
if (_conn_map[n][node]) { if (_conn_map[n][vertex]) {
res.push_back(n); res.push_back(n);
} }
} }
@@ -246,16 +288,16 @@ public:
} }
/// ///
/// \brief Getter for a list of nodes `x` that `node` has a connection to and from `x...` /// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...`
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A list containing all nodes `x` that have symmetric connections with `node` /// \returns A list containing all vertices `x` that have symmetric edges with `vertex`
list<size_t> symmetric(size_t node) { list<size_t> symmetric(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || node >= _conn_map.size()) { if (empty() || vertex >= _conn_map.size()) {
return res; return res;
} }
for (const auto& it : _conn_map[node]) { for (const auto& it : _conn_map[vertex]) {
if (_conn_map[it.first][node]) { if (_conn_map[it.first][vertex]) {
res.push_back(it.first); res.push_back(it.first);
} }
} }
@@ -263,20 +305,21 @@ public:
} }
/// ///
/// \brief Getter for a list of nodes `x` that `node` has a connection to and from `x...` and share the same weight /// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...` and share the same value
/// "Joined" connections may also be referred to as "undirected." A joined, or undirected, connection may be /// \details
/// turned into a directed connection by changing the weight object associated with the connection, or by /// "Joined" edges may also be referred to as "undirected." A joined, or undirected, edge may be
/// removing one of the sub-connections. /// turned into a directed edge by changing the weight object associated with the edge, or by
/// \param node The id of the node /// removing one of the sub-edges.
/// \returns A list containing all nodes `x` that have symmetric connections with `node` /// \param vertex The id of the vertex
list<size_t> joined(size_t node) { /// \returns A list containing all vertices `x` that have symmetric edges with `vertex`
list<size_t> undirected(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || node >= _conn_map.size()) { if (empty() || vertex >= _conn_map.size()) {
return res; return res;
} }
for (const auto& it : _conn_map[node]) { for (const auto& it : _conn_map[vertex]) {
const auto* at = _conn_map[it.first][node]; const auto* at = _conn_map[it.first][vertex];
if (at == &it.second) { if (at != nullptr && *at == it.second) {
res.push_back(it.first); res.push_back(it.first);
} }
} }
@@ -284,15 +327,15 @@ public:
} }
/// ///
/// \brief Getter for the internal storage of mapped connections from this node /// \brief Getter for the internal storage of mapped edges from this vertex.
/// Use this when you want to iterate over connections that start from this node. /// Use this when you want to iterate over edges that start from this vertex.
/// \param node The id of the node /// \param vertex The id of the vertex
/// \returns A pointer to a map containing connections mapped from this node /// \returns A pointer to a map containing edges mapped from this vertex
const auto* connections(size_t node) { const auto* edges(size_t vertex) {
if (empty() || node >= _conn_map.size()) { if (empty() || vertex >= _conn_map.size()) {
return nullptr; return nullptr;
} }
return &_conn_map[node]; return &_conn_map[vertex];
} }
/// @} /// @}
@@ -304,60 +347,60 @@ public:
/// @{ /// @{
/// ///
/// \brief Move a new node into the graph /// \brief Move a new vertex into the graph
/// \param node The node to move into the graph /// \param vertex The vertex to move into the graph
/// \returns The id of the new node /// \returns The id of the new vertex
constexpr size_t insert(node_t&& node) { constexpr size_t insert(vertex_t&& vertex) {
return this->_insert(fennec::forward<node_t>(node)); return this->_insert(fennec::forward<vertex_t>(vertex));
} }
/// ///
/// \brief Copy a new node into the graph /// \brief Copy a new vertex into the graph
/// \param node The node to copy into the graph /// \param vertex The vertex to copy into the graph
/// \returns The id of the new node /// \returns The id of the new vertex
constexpr size_t insert(const node_t& node) { constexpr size_t insert(const vertex_t& vertex) {
return this->_insert(node); return this->_insert(vertex);
} }
/// ///
/// \brief Construct a new node in the graph /// \brief Construct a new vertex in the graph
/// \tparam ArgsT The types of the arguments /// \tparam ArgsT The types of the arguments
/// \param args The arguments to construct the node with /// \param args The arguments to construct the vertex with
/// \returns The id of the new node /// \returns The id of the new vertex
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) { constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...); return this->_insert(fennec::forward<ArgsT>(args)...);
} }
/// ///
/// \brief Erase a node from the graph /// \brief Erase a vertex from the graph
/// \param node The id of the node to erase /// \param vertex The id of the vertex to erase
constexpr void erase(size_t node) { constexpr void erase(size_t vertex) {
disconnect(node); cut(vertex);
_node_pool.erase(node); _vertex_pool.erase(vertex);
} }
/// ///
/// \brief Form a connection from node `a` to node `b` /// \brief Form an edge from vertex `a` to vertex `b`
/// \tparam ArgsT The argument types /// \tparam ArgsT The argument types
/// \param a The first node id /// \param a The first vertex id
/// \param b The second node id /// \param b The second vertex id
/// \param args The arguments to construct the connection with /// \param args The arguments to construct the edge with
template<typename...ArgsT> template<typename...ArgsT>
constexpr void connect(size_t a, size_t b, ArgsT&&...args) { constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
if (a == b) { if (a == b) {
return; return;
} }
if (_conn_map.size() < _node_pool.capacity()) { if (_conn_map.size() < _vertex_pool.capacity()) {
_conn_map.resize(_node_pool.capacity()); _conn_map.resize(_vertex_pool.capacity());
} }
auto it = _conn_map[a][b]; auto it = _conn_map[a][b];
size_t conn; size_t conn;
if (it != nullptr) { if (it != nullptr) {
conn = *it; conn = *it;
_conn_pool[conn] = node_t(fennec::forward<ArgsT>(args)...); _conn_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
} else { } else {
conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...); conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
} }
@@ -365,26 +408,26 @@ public:
} }
/// ///
/// \brief Form a bidirectional connection between node `a` and node `b` /// \brief Form an undirected edge between vertex `a` and vertex `b`
/// \tparam ArgsT The argument types /// \tparam ArgsT The argument types
/// \param a The first node id /// \param a The first vertex id
/// \param b The second node id /// \param b The second vertex id
/// \param args The arguments to construct the connection with /// \param args The arguments to construct the edge with
template<typename...ArgsT> template<typename...ArgsT>
constexpr void connect2(size_t a, size_t b, ArgsT&&...args) { constexpr void make_edge2(size_t a, size_t b, ArgsT&&...args) {
if (a == b) { if (a == b) {
return; return;
} }
if (_conn_map.size() < _node_pool.capacity()) { if (_conn_map.size() < _vertex_pool.capacity()) {
_conn_map.resize(_node_pool.capacity()); _conn_map.resize(_vertex_pool.capacity());
} }
auto it = _conn_map[a][b]; auto it = _conn_map[a][b];
size_t conn; size_t conn;
if (it != nullptr) { if (it != nullptr) {
conn = *it; conn = *it;
_conn_pool[conn] = node_t(fennec::forward<ArgsT>(args)...); _conn_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
} else { } else {
conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...); conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
} }
@@ -394,69 +437,78 @@ public:
} }
/// ///
/// \brief Disconnect a connection from node `a` to node `b` /// \brief Disconnect an edge from vertex `a` to vertex `b`
/// \param a The first node id /// \param a The first vertex id
/// \param b The second node id /// \param b The second vertex id
constexpr void disconnect(size_t a, size_t b) { constexpr void cut_edge(size_t a, size_t b) {
// Find the connection object // Find the edge object
const auto* it = _conn_map[a][b]; const auto* it = _conn_map[a][b];
if (not it) { if (not it) {
return; return;
} }
size_t c = *it; size_t c = *it;
// Check if bi-directional // Check if undirected
const auto* at = _conn_map[b][a]; const auto* at = _conn_map[b][a];
if (not at || *at != c) { if (not at || *at != c) {
_conn_pool.erase(c); _conn_pool.erase(c);
} }
// Erase the connection mapping // Erase the edge mapping
_conn_map[a].erase(b); _conn_map[a].erase(b);
} }
/// ///
/// \brief Disconnect a bidirectional connection between nodes `a` and `b` /// \brief Disconnect both directed edges between vertices `a` and `b`
/// \param a The first node id /// \param a The first vertex id
/// \param b The second node id /// \param b The second vertex id
constexpr void disconnect2(size_t a, size_t b) { constexpr void cut_edge2(size_t a, size_t b) {
const auto* it = _conn_map[a][b]; const auto* ita = _conn_map[a][b];
if (not it) { const auto* itb = _conn_map[a][b];
if (not (ita || itb)) {
return; return;
} }
size_t c = *it; if (ita) _conn_pool.erase(*ita);
_conn_pool.erase(c); if (itb) _conn_pool.erase(*itb);
_conn_map[a].erase(b); _conn_map[a].erase(b);
_conn_map[b].erase(a); _conn_map[b].erase(a);
} }
/// ///
/// \brief Break *all* connections to and from `n` /// \brief Break *all* edges to and from `n`
/// \param n The node id /// \param n The vertex id
void disconnect(size_t n) { void cut(size_t n) {
for (const auto it : outgoing(n)) { for (const auto it : outgoing(n)) {
disconnect(n, it); cut_edge(n, it);
} }
for (const auto it : incoming(n)) { for (const auto it : incoming(n)) {
disconnect(it, n); cut_edge(it, n);
} }
} }
///
/// \brief Clear the graph, destructing all vertices and edges.
void clear() {
_vertex_pool.clear();
_conn_pool.clear();
_conn_map.clear();
}
/// @} /// @}
// Connections ========================================================================================================= // edges =========================================================================================================
private: private:
node_pool_t _node_pool; vertex_pool_t _vertex_pool;
conn_pool_t _conn_pool; edge_pool_t _conn_pool;
conn_map_t _conn_map; edge_map_t _conn_map;
template<typename...ArgsT> template<typename...ArgsT>
size_t _insert(ArgsT&&...args) { size_t _insert(ArgsT&&...args) {
return _node_pool.emplace(fennec::forward<ArgsT>(args)...); return _vertex_pool.emplace(fennec::forward<ArgsT>(args)...);
} }
}; };

View File

@@ -18,7 +18,7 @@
/// ///
/// \file list.h /// \file list.h
/// \brief List of elements /// \brief A header containing the definition for a linked list of values
/// ///
/// ///
/// \details /// \details
@@ -72,9 +72,12 @@ private:
struct node; struct node;
public: public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>; ///< Alias for the allocator type, rebound to list nodes
using alloc_t = typename allocator_traits<Alloc>::template rebind<node>;
using value_t = TypeT; using value_t = TypeT;
using elem_t = node; using elem_t = node;
static constexpr size_t npos = -1; static constexpr size_t npos = -1;
class iterator; class iterator;
@@ -92,6 +95,28 @@ public:
: _table(), _freed(), _root(npos), _last(npos), _size(0) { : _table(), _freed(), _root(npos), _last(npos), _size(0) {
} }
///
/// \brief Copy Constructor, copies all elements in `l` with optimized layout
/// \param l The list to copy
constexpr list(const list& l)
: list() {
for (const value_t& it : l) {
this->push_back(it);
}
}
///
/// \brief Move Constructor, takes ownership of the list
/// \param l The list to move
constexpr list(list&& l) noexcept
: _table(fennec::move(l._table))
, _freed(fennec::move(l._freed))
, _root(l._root)
, _last(l._last)
, _size(l._size) {
}
/// ///
/// \brief Destructor, destructs all elements then releases the allocation. /// \brief Destructor, destructs all elements then releases the allocation.
constexpr ~list() { constexpr ~list() {
@@ -102,6 +127,37 @@ public:
/// @} /// @}
// Assignment ==========================================================================================================
/// \name Assignment
/// @{
///
/// \brief Copy Assignment Operator
/// \param l the list to copy
/// \returns `this` after having copied all elements of `l`
constexpr list& operator=(const list& l) {
this->clear();
for (const value_t& it : l) {
this->push_back(it);
}
}
///
/// \brief Move Assignment Operator
/// \param l the list to copy
/// \returns `this` after having taken ownership over the contents of `l`
constexpr list& operator=(list&& l) noexcept {
this->clear();
_table = fennec::move(l._table);
_freed = fennec::move(l._freed);
_root = l._root; _last = l._last;
_size = l._size;
}
/// @}
// Properties ========================================================================================================== // Properties ==========================================================================================================
/// \name Properties /// \name Properties
@@ -324,10 +380,54 @@ public:
_erase(_last); _erase(_last);
} }
///
/// \brief Clears the list, destructing all elements in order
constexpr void clear() {
size_t i = _root;
while (i != npos) {
fennec::destruct(_table[i]);
i = this->_next(i);
}
_table.deallocate(_table);
}
/// @} /// @}
// ITERATOR ============================================================================================================ // ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for the first element in the list
constexpr iterator begin() {
return iterator(this, _root);
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator for the end of the list
constexpr iterator end() {
return iterator(this, npos);
}
///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const iterator for the first element in the list
constexpr const_iterator begin() const {
return const_iterator(this, _root);
}
///
/// \brief Const C++ Iterator Specification `end()`
/// \returns A const iterator for the end of the list
constexpr const_iterator end() const {
return const_iterator(this, npos);
}
/// @}
/// ///
/// \brief Iterator Class /// \brief Iterator Class
class iterator { class iterator {
@@ -428,35 +528,6 @@ public:
} }
}; };
/// \name Iteration
/// @{
///
/// \returns An iterator for the first element in the list
constexpr iterator begin() {
return iterator(this, _root);
}
///
/// \returns An iterator for the end of the list
constexpr iterator end() {
return iterator(this, npos);
}
///
/// \returns A const iterator for the first element in the list
constexpr const_iterator begin() const {
return const_iterator(this, _root);
}
///
/// \returns A const iterator for the end of the list
constexpr const_iterator end() const {
return const_iterator(this, npos);
}
/// @}
private: private:
allocation<elem_t, alloc_t> _table; allocation<elem_t, alloc_t> _table;
dynarray<size_t> _freed; dynarray<size_t> _freed;

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file map.h
/// \brief A header containing the definition for a mapping of keys to values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_MAP_H #ifndef FENNEC_CONTAINERS_MAP_H
#define FENNEC_CONTAINERS_MAP_H #define FENNEC_CONTAINERS_MAP_H
@@ -235,6 +247,12 @@ public:
_set.erase(this->_find(fennec::forward<ArgsT>(args)...)); _set.erase(this->_find(fennec::forward<ArgsT>(args)...));
} }
///
/// \brief Clears the set, destructing all elements
void clear() {
_set.clear();
}
/// @} /// @}
@@ -243,10 +261,17 @@ public:
/// \name Iteration /// \name Iteration
/// @{ /// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the start of the map
constexpr iterator begin() { constexpr iterator begin() {
return _set.begin(); return _set.begin();
} }
///
/// \brief C++ Iterator Specification `end()`
/// \returns an iterator at the end of the map
constexpr iterator end() { constexpr iterator end() {
return _set.end(); return _set.end();
} }

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file multiset.h
/// \brief A header containing the definition for a set of repeating values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_MULTISET_H #ifndef FENNEC_CONTAINERS_MULTISET_H
#define FENNEC_CONTAINERS_MULTISET_H #define FENNEC_CONTAINERS_MULTISET_H
@@ -385,6 +397,29 @@ public:
// ITERATOR ============================================================================================================ // ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
/// ///
/// \brief Class for Iterating the Set /// \brief Class for Iterating the Set
class iterator { class iterator {
@@ -492,27 +527,6 @@ public:
} }
}; };
/// \name Iteration
/// @{
///
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
// PRIVATE ============================================================================================================= // PRIVATE =============================================================================================================
@@ -536,7 +550,7 @@ private:
template<typename...ArgsT> template<typename...ArgsT>
constexpr void _insert(ArgsT&&...args) { constexpr void _insert(ArgsT&&...args) {
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full if (_size == 0 or static_cast<double>(_size) / capacity() >= _load) { // expand when full
_expand(); _expand();
} }

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file object_pool.h
/// \brief A header containing the definition for a pool of objects associated by ids
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H #ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
#define FENNEC_CONTAINERS_OBJECT_POOL_H #define FENNEC_CONTAINERS_OBJECT_POOL_H
@@ -32,14 +44,36 @@ namespace fennec
/// \tparam AllocT The allocator type /// \tparam AllocT The allocator type
template<typename TypeT, typename AllocT = allocator<TypeT>> template<typename TypeT, typename AllocT = allocator<TypeT>>
struct object_pool { struct object_pool {
// Definitions =========================================================================================================
public: public:
using value_t = TypeT; using value_t = TypeT;
using elem_t = optional<TypeT>; using elem_t = optional<TypeT>;
using table_t = dynarray<elem_t, AllocT>; using table_t = dynarray<elem_t, AllocT>;
// Constructors & Destructor ===========================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty object pool
constexpr object_pool() constexpr object_pool()
: _size(0) { : _size(0) {
}; }
///
/// \brief Default Destructor, destructs objects then releases the allocation.
constexpr ~object_pool() = default;
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
/// ///
/// \returns The number of active objects in the pool /// \returns The number of active objects in the pool
@@ -53,30 +87,81 @@ public:
return _table.capacity(); return _table.capacity();
} }
///
/// \returns `true` when there are no objects in the pool, `false` otherwise
constexpr bool empty() const { constexpr bool empty() const {
return size() == 0; return size() == 0;
} }
///
/// \brief Retrieve the next id `i` that would be assigned to an object `o` were it added to the object pool
///
/// \details This can be useful if there are constant members that need to be assigned at construction.
/// \returns The id of the next inserted node
constexpr size_t next_id() const {
size_t next = _size;
if (not _freed.empty()) {
next = _freed.front();
}
return next;
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Array Access Operator
/// \param i id of the object
/// \returns a reference to the object with id `i`
constexpr value_t& operator[](size_t i) { constexpr value_t& operator[](size_t i) {
assert(i < capacity(), "Index out of Bounds!"); assert(i < capacity(), "Index out of Bounds!");
assert(_table[i], "Attempted to access Null Object."); assert(_table[i], "Attempted to access Null Object.");
return *_table[i]; return *_table[i];
} }
///
/// \brief Array Const Access Operator
/// \param i id of the object
/// \returns a const-qualified reference to the object with id `i`
constexpr const value_t& operator[](size_t i) const { constexpr const value_t& operator[](size_t i) const {
assert(i < capacity(), "Index out of Bounds!"); assert(i < capacity(), "Index out of Bounds!");
assert(_table[i], "Attempted to access Null Object."); assert(_table[i], "Attempted to access Null Object.");
return *_table[i]; return *_table[i];
} }
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion, inserts `x` into the pool
/// \param x the object to move
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(value_t&& x) { constexpr size_t insert(value_t&& x) {
return this->_insert(fennec::forward<value_t>(x)); return this->_insert(fennec::forward<value_t>(x));
} }
///
/// \brief Move Insertion, inserts a copy of `x` into the pool
/// \param x the object to copy
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(const value_t& x) { constexpr size_t insert(const value_t& x) {
return this->_insert(x); return this->_insert(x);
} }
///
/// \brief Move Insertion, inserts a copy of `x` into the pool
/// \param x the object to copy
/// \returns An integer corresponding to the id of the node
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) { constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...); return this->_insert(fennec::forward<ArgsT>(args)...);
@@ -88,14 +173,6 @@ public:
--_size; --_size;
} }
constexpr size_t next_id() const {
size_t next = _size;
if (not _freed.empty()) {
next = _freed.front();
}
return next;
}
private: private:
dynarray<elem_t, AllocT> _table; dynarray<elem_t, AllocT> _table;
list<size_t> _freed; list<size_t> _freed;

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file optional.h
/// \brief A header containing the definition for a container with an optionally present variable
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_OPTIONAL_H #ifndef FENNEC_CONTAINERS_OPTIONAL_H
#define FENNEC_CONTAINERS_OPTIONAL_H #define FENNEC_CONTAINERS_OPTIONAL_H

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file pair.h
/// \brief A header containing the definition for a container holding a pair of values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_PAIR_H #ifndef FENNEC_CONTAINERS_PAIR_H
#define FENNEC_CONTAINERS_PAIR_H #define FENNEC_CONTAINERS_PAIR_H
@@ -30,13 +42,21 @@ namespace fennec
/// ///
/// \brief Struct for holding a pair of values /// \brief Struct for holding a pair of values
/// \tparam T0 The type of the first value /// \tparam TypeT0 The type of the first value
/// \tparam T1 The type of the second value /// \tparam TypeT1 The type of the second value
template<typename T0, typename T1> template<typename TypeT0, typename TypeT1>
struct pair { struct pair {
// Members =============================================================================================================
TypeT0 first; ///< The first value in the pair
TypeT1 second; ///< The second value in the pair
// Constructors ======================================================================================================== // Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
/// ///
/// \brief Default Constructor, invokes default constructor for both elements /// \brief Default Constructor, invokes default constructor for both elements
constexpr pair() = default; constexpr pair() = default;
@@ -49,7 +69,7 @@ struct pair {
/// \brief Pair Copy Constructor /// \brief Pair Copy Constructor
/// \param x Value to copy for the first element /// \param x Value to copy for the first element
/// \param y Value to copy for the first element /// \param y Value to copy for the first element
constexpr pair(const T0& x, const T1& y) constexpr pair(const TypeT0& x, const TypeT1& y)
: first(x) : first(x)
, second(y) { , second(y) {
} }
@@ -58,9 +78,9 @@ struct pair {
/// \brief Pair Move Constructor /// \brief Pair Move Constructor
/// \param x Value to move for the first element /// \param x Value to move for the first element
/// \param y Value to move for the first element /// \param y Value to move for the first element
constexpr pair(T0&& x, T1&& y) noexcept constexpr pair(TypeT0&& x, TypeT1&& y) noexcept
: first(fennec::forward<T0>(x)) : first(fennec::forward<TypeT0>(x))
, second(fennec::forward<T1>(y)) { , second(fennec::forward<TypeT1>(y)) {
} }
/// ///
@@ -89,9 +109,14 @@ struct pair {
/// \brief Move Assignment, moves both elements /// \brief Move Assignment, moves both elements
constexpr pair& operator=(pair&&) noexcept = default; constexpr pair& operator=(pair&&) noexcept = default;
/// @}
// Comparison ========================================================================================================== // Comparison ==========================================================================================================
/// \name Comparison
/// @{
/// ///
/// \brief Equality Operator /// \brief Equality Operator
/// \param p Pair to compare with /// \param p Pair to compare with
@@ -144,18 +169,15 @@ struct pair {
return first > p.first or (first == p.first and second >= p.second); return first > p.first or (first == p.first and second >= p.second);
} }
// Members ============================================================================================================= /// @}
T0 first;
T1 second;
}; };
template<typename T0, typename T1> template<typename TypeT0, typename TypeT1>
struct hash<pair<T0, T1>> : hash<T0>, hash<T1> { struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
constexpr size_t operator()(const pair<T0, T1>& p) const { constexpr size_t operator()(const pair<TypeT0, TypeT1>& p) const {
return fennec::pair_hash( // pair the hashes of both elements return fennec::pair_hash( // pair the hashes of both elements
hash<T0>::operator()(p.first), hash<TypeT0>::operator()(p.first),
hash<T1>::operator()(p.second) hash<TypeT1>::operator()(p.second)
); );
} }
}; };

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file rdtree.h
/// \brief A header containing the definition for a tree with a root and directed edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_RDTREE_H #ifndef FENNEC_CONTAINERS_RDTREE_H
#define FENNEC_CONTAINERS_RDTREE_H #define FENNEC_CONTAINERS_RDTREE_H
@@ -50,6 +62,13 @@ protected:
size_t parent, child, prev, next; size_t parent, child, prev, next;
size_t depth, num_children; size_t depth, num_children;
constexpr node()
: value(nullopt)
, parent(npos), child(npos)
, prev(npos), next(npos)
, depth(0), num_children(0) {
}
template<typename...ArgsT> template<typename...ArgsT>
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args) constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
: value(fennec::forward<ArgsT>(args)...) : value(fennec::forward<ArgsT>(args)...)
@@ -62,7 +81,7 @@ protected:
child = npos; child = npos;
prev = npos; prev = npos;
next = npos; next = npos;
depth = npos; depth = 0;
num_children = 0; num_children = 0;
} }
}; };
@@ -71,55 +90,91 @@ public:
// Constructors ======================================================================================================== // Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Root Constructor, constructs the root node of the tree
/// \tparam ArgsT The argument types
/// \param args The arguments to construct the root with
template<typename...ArgsT> template<typename...ArgsT>
explicit constexpr rdtree(ArgsT&&...args) explicit constexpr rdtree(ArgsT&&...args)
: _table(), _freed(), _size(1) { : _table(), _freed(), _size(1) {
_table.callocate(8); _table.creallocate(8);
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...); fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
} }
///
/// \brief Copy Constructor, copies the contents of `tree`
/// \param tree the rdtree to copy
constexpr rdtree(const rdtree& tree) constexpr rdtree(const rdtree& tree)
: _table(tree._table), _freed(tree._freed), _size(tree._size) { : _table(tree._table), _freed(tree._freed), _size(tree._size) {
} }
///
/// \brief Move Constructor, takes ownership over the contents of `tree`
/// \param tree the rdtree to move
constexpr rdtree(rdtree&& tree) noexcept constexpr rdtree(rdtree&& tree) noexcept
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) { : _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
} }
/// @}
// Assignment ========================================================================================================== // Assignment ==========================================================================================================
/// \name Assignment
/// @{
///
/// \brief Copy Assignment Operator
/// \param rhs the rdtree to copy
/// \returns `this` after copying the contents of `rhs`
constexpr rdtree& operator=(const rdtree& rhs) { constexpr rdtree& operator=(const rdtree& rhs) {
for (value_t* it : this->_table) { for (value_t* it : this->_table) {
fennec::destruct(it); fennec::destruct(it);
} }
_table = rhs._table; _table = rhs._table;
_freed = rhs._freed; _freed = rhs._freed;
_size = rhs._size; _size = rhs._size;
return *this; return *this;
} }
///
/// \brief Move Assignment Operator
/// \param rhs the rdtree to move
/// \returns `this` after taking ownership over the contents of `rhs`
constexpr rdtree& operator=(rdtree&& rhs) noexcept { constexpr rdtree& operator=(rdtree&& rhs) noexcept {
for (value_t* it : _table) { for (value_t* it : _table) {
fennec::destruct(it); fennec::destruct(it);
} }
_table = fennec::move(rhs._table); _table = fennec::move(rhs._table);
_freed = fennec::move(rhs._freed); _freed = fennec::move(rhs._freed);
_size = rhs._size; _size = rhs._size;
return *this; return *this;
} }
/// @}
// Properties ========================================================================================================== // Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The number of nodes in the tree
constexpr size_t size() const { constexpr size_t size() const {
return _size; return _size;
} }
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const { constexpr size_t capacity() const {
return _table.capacity(); return _table.capacity();
} }
///
/// \returns `true` when there are no nodes in the tree, `false` otherwise
constexpr bool empty() const { constexpr bool empty() const {
return _size == 0; return _size == 0;
} }
@@ -347,6 +402,30 @@ public:
// Traversal =========================================================================================================== // Traversal ===========================================================================================================
///
/// \brief Traverse the tree using a specified order and visiting functor
///
/// \details
/// The visitor should accept a reference to a value of type `TypeT` and a `size_t` which contains the node's id.
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
///
/// \tparam OrderT The order with which to traverse the tree.
/// \tparam VisitorT The visitor, should fulfill the signature `uint8_t visit(TypeT&, size_t)`
/// \param visit The visiting object
/// \param i The node to start at
template<typename OrderT, typename VisitorT>
void traverse(VisitorT&& visit, size_t i = root) {
OrderT order;
i = order(*this, i);
while (i != npos) {
uint8_t mode = visit(*_table[i].value, i);
if (mode == traversal_control_break) {
break;
}
i = order[*this, i, mode];
}
}
struct pre_order { struct pre_order {
list<size_t> visit; list<size_t> visit;
size_t head; size_t head;
@@ -457,19 +536,6 @@ public:
} }
}; };
template<typename OrderT, typename VisitorT>
void traverse(VisitorT&& visit, size_t i = root) {
OrderT order;
i = order(*this, i);
while (i != npos) {
uint8_t mode = visit(*_table[i].value, i);
if (mode == traversal_control_break) {
break;
}
i = order[*this, i, mode];
}
}
protected: protected:
allocation<node, alloc_t> _table; allocation<node, alloc_t> _table;

View File

@@ -16,12 +16,23 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file set.h
/// \brief A header containing the definition for a set of unique values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_SET_H #ifndef FENNEC_CONTAINERS_SET_H
#define FENNEC_CONTAINERS_SET_H #define FENNEC_CONTAINERS_SET_H
// https://programming.guide/robin-hood-hashing.html // https://programming.guide/robin-hood-hashing.html
#include <fennec/containers/multiset.h>
#include <fennec/containers/optional.h> #include <fennec/containers/optional.h>
#include <fennec/containers/set.h> #include <fennec/containers/set.h>
#include <fennec/lang/compare.h> #include <fennec/lang/compare.h>
@@ -94,6 +105,7 @@ public:
/// ///
/// \brief Hash Copy Constructor, initializes empty set with a hash /// \brief Hash Copy Constructor, initializes empty set with a hash
/// \param hash the hash object
constexpr set(const hash_t& hash) constexpr set(const hash_t& hash)
: _alloc() : _alloc()
, _hash(hash) , _hash(hash)
@@ -102,18 +114,9 @@ public:
, _load(default_load) { , _load(default_load) {
} }
///
/// \brief Hash Move Constructor, initializes empty set with a hash
constexpr set(hash_t&& hash) noexcept
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
/// ///
/// \brief Alloc Copy Constructor, initializes empty set with an allocator /// \brief Alloc Copy Constructor, initializes empty set with an allocator
/// \param alloc the allocator object
constexpr set(const alloc_t& alloc) constexpr set(const alloc_t& alloc)
: _alloc(alloc) : _alloc(alloc)
, _hash() , _hash()
@@ -122,18 +125,10 @@ public:
, _load(default_load) { , _load(default_load) {
} }
///
/// \brief Alloc Move Constructor, initializes empty set with an allocator
constexpr set(alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
/// ///
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator /// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
/// \param hash the hash object
/// \param alloc the allocator object
constexpr set(const hash_t& hash, const alloc_t& alloc) constexpr set(const hash_t& hash, const alloc_t& alloc)
: _alloc(alloc) : _alloc(alloc)
, _hash(hash) , _hash(hash)
@@ -142,36 +137,6 @@ public:
, _load(default_load) { , _load(default_load) {
} }
///
/// \brief Hash Copy Alloc Move Constructor, initializes empty set with a hash and allocator
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Move Constructor, initializes empty set with a hash and allocator
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Copy Constructor, initializes empty set with a hash and allocator
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
/// ///
/// \brief Set Copy Constructor /// \brief Set Copy Constructor
/// \param set Set to copy /// \param set Set to copy
@@ -225,7 +190,7 @@ public:
/// ///
/// \returns Capacity of the set in elements /// \returns Capacity of the set in elements
constexpr size_t capacity() const { constexpr size_t capacity() const {
return _alloc.capacity(); return _alloc.size();
} }
/// @} /// @}
@@ -320,21 +285,21 @@ public:
// Modifiers =========================================================================================================== // Modifiers ===========================================================================================================
/// \name Properties /// \name Modifiers
/// @{ /// @{
/// ///
/// \brief Move Insertion /// \brief Move Insertion
/// \param val Value to insert /// \param val Value to insert
constexpr iterator insert(elem_t&& val) { constexpr iterator insert(elem_t&& val) {
return fennec::move(this->_insert(fennec::forward<elem_t>(val))); return this->_insert(fennec::forward<elem_t>(val));
} }
/// ///
/// \brief Copy Insertion /// \brief Copy Insertion
/// \param val Value to insert /// \param val Value to insert
constexpr iterator insert(const elem_t& val) { constexpr iterator insert(const elem_t& val) {
return fennec::move(this->_insert(val)); return this->_insert(val);
} }
/// ///
@@ -343,7 +308,7 @@ public:
/// \param args Arguments to construct with /// \param args Arguments to construct with
template<typename...ArgsT> template<typename...ArgsT>
constexpr iterator emplace(ArgsT&&...args) { constexpr iterator emplace(ArgsT&&...args) {
return fennec::move(this->_insert(fennec::forward<ArgsT>(args)...)); return this->_insert(fennec::forward<ArgsT>(args)...);
} }
/// ///
@@ -378,11 +343,40 @@ public:
this->erase(this->find(val)); this->erase(this->find(val));
} }
///
/// \brief
constexpr void clear() {
for (size_t i = 0; i < _alloc.capacity(); ++i) {
}
}
/// @} /// @}
// ITERATOR ============================================================================================================ // ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
/// ///
/// \brief Class for Iterating the Set /// \brief Class for Iterating the Set
class iterator { class iterator {
@@ -438,34 +432,13 @@ public:
friend set; friend set;
}; };
/// \name Iteration
/// @{
///
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
// PRIVATE ============================================================================================================= // PRIVATE =============================================================================================================
private: private:
constexpr void _expand() { constexpr void _expand() {
set cpy; // Create a new set set cpy; // Create a new set
cpy._alloc.callocate( cpy._alloc.resize(
fennec::next_prime2(_alloc.capacity()) fennec::next_prime2(_alloc.capacity())
); );
@@ -506,7 +479,7 @@ private:
return iterator(this, npos); return iterator(this, npos);
} }
allocation<node, alloc_t> _alloc; dynarray<node, alloc_t> _alloc;
hash_t _hash; hash_t _hash;
equal_t _equal; equal_t _equal;
size_t _size; size_t _size;

View File

@@ -16,16 +16,30 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file traversal.h
/// \brief a header containing constants and utilities related to traversal
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_TRAVERSAL_H #ifndef FENNEC_CONTAINERS_TRAVERSAL_H
#define FENNEC_CONTAINERS_TRAVERSAL_H #define FENNEC_CONTAINERS_TRAVERSAL_H
namespace fennec namespace fennec
{ {
///
/// \brief A set of constants used in the traverser-visitor pattern
enum traversal_control_ { enum traversal_control_ {
traversal_control_continue = 0, traversal_control_continue = 0,
traversal_control_jump_over, traversal_control_break = 1,
traversal_control_break traversal_control_jump_over = 2,
}; };
} }

View File

@@ -16,6 +16,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file tuple.h
/// \brief A header containing the definition for a container with multiple values of differing types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_TUPLE_H #ifndef FENNEC_CONTAINERS_TUPLE_H
#define FENNEC_CONTAINERS_TUPLE_H #define FENNEC_CONTAINERS_TUPLE_H

View File

@@ -249,6 +249,7 @@ namespace fennec
using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type
using size_t = size_t; ///< \brief Unsigned Integer Type Returned By `sizeof`, `sizeof...`, and `alignof` using size_t = size_t; ///< \brief Unsigned Integer Type Returned By `sizeof`, `sizeof...`, and `alignof`
using ptrdiff_t = __PTRDIFF_TYPE__; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers using ptrdiff_t = __PTRDIFF_TYPE__; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers
struct empty_t {};
class undefined_t; ///< \brief undefined class for SFINAE class undefined_t; ///< \brief undefined class for SFINAE