fennec
Loading...
Searching...
No Matches
graph.h
Go to the documentation of this file.
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
30
31#ifndef FENNEC_CONTAINERS_GRAPH_H
32#define FENNEC_CONTAINERS_GRAPH_H
33
39
40/*
41 * With the directed tree we were able to cheat a little, the structure has more rules to it which allows
42 * tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
43 * properties or rules to edges which can simply be an extension to this graph.
44 *
45 * The most effective way to do this is to have a dynarray of lists, however this results in double the
46 * memory being used. This can also result in two edge objects being created.
47 *
48 * There is no nice way to avoid the problem of mapping vertices to edges
49 */
50
51namespace fennec
52{
53
90template<typename VertexT, typename EdgeT = empty_t>
91struct graph {
92public:
93// Definitions =========================================================================================================
94
95 using edge_t = EdgeT;
96 using vertex_t = VertexT;
100
101 static constexpr size_t npos = -1;
102
103
104// Constructors ========================================================================================================
105
108
111 constexpr graph() = default;
112
115 constexpr ~graph() = default;
116
118
119
122
127 constexpr graph& operator=(const graph& g) = default;
128
133 constexpr graph& operator=(graph&& g) = default;
134
136
137
138// Properties ==========================================================================================================
139
142
145 constexpr size_t num_vertices() const {
146 return _vertex_pool.size();
147 }
148
151 constexpr size_t num_edges() const {
152 return _edge_pool.size();
153 }
154
157 constexpr size_t capacity() const {
158 return _vertex_pool.capacity();
159 }
160
163 constexpr bool empty() const {
164 return num_vertices() == 0;
165 }
166
172 constexpr bool exists(size_t a, size_t b) const {
173 return _edge_map[a][b] != nullptr;
174 }
175
182 constexpr bool is_symmetric(size_t a, size_t b) const {
183 return exists(a, b) and exists(b, a);
184 }
185
191 constexpr bool is_undirected(size_t a, size_t b) const {
192 const auto* e0 = _edge_map[a][b];
193 const auto* e1 = _edge_map[b][a];
194 if (not (e0 != nullptr && e1 != nullptr)) {
195 return false;
196 }
197 return *e0 == *e1;
198 }
199
200 // TODO: connected, disjoint, unilateral, get_component
201
203
204
205// Access ==============================================================================================================
206
209
214 constexpr vertex_t& operator[](size_t vertex) {
215 return _vertex_pool[vertex];
216 }
217
222 constexpr const vertex_t& operator[](size_t vertex) const {
223 return _vertex_pool[vertex];
224 }
225
231 constexpr edge_t* operator[](size_t a, size_t b) {
232 if (empty()) {
233 return nullptr;
234 }
235 edge_t* it = _edge_map[a][b];
236 if (it) {
237 return _edge_pool[*it];
238 }
239 return nullptr;
240 }
241
247 constexpr const edge_t* operator[](size_t a, size_t b) const {
248 if (empty()) {
249 return nullptr;
250 }
251 const edge_t* it = _edge_map[a][b];
252 if (it) {
253 return _edge_pool[*it];
254 }
255 return nullptr;
256 }
257
262 list<size_t> outgoing(size_t vertex) {
263 list<size_t> res;
264 if (empty() || vertex >= _edge_map.size()) {
265 return res;
266 }
267 for (const auto& it : _edge_map[vertex]) {
268 res.push_back(it.first);
269 }
270 return res;
271 }
272
277 list<size_t> incoming(size_t vertex) {
278 list<size_t> res;
279 if (empty() || vertex >= _edge_map.size()) {
280 return res;
281 }
282 for (size_t n = 0; n < _edge_map.size(); ++n) {
283 if (_edge_map[n][vertex]) {
284 res.push_back(n);
285 }
286 }
287 return res;
288 }
289
294 list<size_t> symmetric(size_t vertex) {
295 list<size_t> res;
296 if (empty() || vertex >= _edge_map.size()) {
297 return res;
298 }
299 for (const auto& it : _edge_map[vertex]) {
300 if (_edge_map[it.first][vertex]) {
301 res.push_back(it.first);
302 }
303 }
304 return res;
305 }
306
315 list<size_t> undirected(size_t vertex) {
316 list<size_t> res;
317 if (empty() || vertex >= _edge_map.size()) {
318 return res;
319 }
320 for (const auto& it : _edge_map[vertex]) {
321 const auto* at = _edge_map[it.first][vertex];
322 if (at != nullptr && *at == it.second) {
323 res.push_back(it.first);
324 }
325 }
326 return res;
327 }
328
334 const auto* edges(size_t vertex) {
335 if (empty() || vertex >= _edge_map.size()) {
336 return nullptr;
337 }
338 return &_edge_map[vertex];
339 }
340
342
343
344// Modifiers ===========================================================================================================
345
348
353 constexpr size_t insert(vertex_t&& vertex) {
354 return this->_insert(fennec::forward<vertex_t>(vertex));
355 }
356
361 constexpr size_t insert(const vertex_t& vertex) {
362 return this->_insert(vertex);
363 }
364
370 template<typename...ArgsT>
371 constexpr size_t emplace(ArgsT&&...args) {
372 return this->_insert(fennec::forward<ArgsT>(args)...);
373 }
374
378 constexpr void erase(size_t vertex) {
379 cut(vertex);
380 _vertex_pool.erase(vertex);
381 }
382
389 template<typename...ArgsT>
390 constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
391 if (a == b) {
392 return;
393 }
394
395 if (_edge_map.size() < _vertex_pool.capacity()) {
396 _edge_map.resize(_vertex_pool.capacity());
397 }
398
399 auto it = _edge_map[a][b];
400 size_t conn;
401 if (it != nullptr) {
402 conn = *it;
403 _edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
404 } else {
405 conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
406 }
407 _edge_map[a].emplace(b, conn);
408 }
409
416 template<typename...ArgsT>
417 constexpr void make_edge2(size_t a, size_t b, ArgsT&&...args) {
418 if (a == b) {
419 return;
420 }
421
422 if (_edge_map.size() < _vertex_pool.capacity()) {
423 _edge_map.resize(_vertex_pool.capacity());
424 }
425
426 auto it = _edge_map[a][b];
427 size_t conn;
428 if (it != nullptr) {
429 conn = *it;
430 _edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
431 } else {
432 conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
433 }
434
435 _edge_map[a].emplace(b, conn);
436 _edge_map[b].emplace(a, conn);
437 }
438
443 constexpr void cut_edge(size_t a, size_t b) {
444
445 // Find the edge object
446 const auto* it = _edge_map[a][b];
447 if (not it) {
448 return;
449 }
450 size_t c = *it;
451
452 // Check if undirected
453 const auto* at = _edge_map[b][a];
454 if (not at || *at != c) {
455 _edge_pool.erase(c);
456 }
457
458 // Erase the edge mapping
459 _edge_map[a].erase(b);
460 }
461
466 constexpr void cut_edge2(size_t a, size_t b) {
467 const auto* ita = _edge_map[a][b];
468 const auto* itb = _edge_map[a][b];
469 if (not (ita || itb)) {
470 return;
471 }
472 if (ita) _edge_pool.erase(*ita);
473 if (itb) _edge_pool.erase(*itb);
474 _edge_map[a].erase(b);
475 _edge_map[b].erase(a);
476 }
477
481 void cut(size_t n) {
482 for (const auto it : outgoing(n)) {
483 cut_edge(n, it);
484 }
485 for (const auto it : incoming(n)) {
486 cut_edge(it, n);
487 }
488 }
489
492 void clear() {
493 _vertex_pool.clear();
494 _edge_pool.clear();
495 _edge_map.clear();
496 }
497
499
500
501// edges =========================================================================================================
502
503
504private:
505 vertex_pool_t _vertex_pool;
506 edge_pool_t _edge_pool;
507 edge_map_t _edge_map;
508
509 template<typename...ArgsT>
510 size_t _insert(ArgsT&&...args) {
511 return _vertex_pool.emplace(fennec::forward<ArgsT>(args)...);
512 }
513
514};
515
516}
517
518#endif // FENNEC_CONTAINERS_GRAPH_H
A header containing the definition for a dynamically allocated array.
A header containing the definition for a linked list of values.
A header containing the definition for a mapping of keys to values.
A header containing the definition for a pool of objects associated by ids.
A header containing the definition for a set of unique values.
constexpr void clear()
Clears the contents of the dynarray, destructing all elements and releasing the allocation.
Definition dynarray.h:534
constexpr void emplace(size_t i, ArgsT &&...args)
Emplace Insertion.
Definition dynarray.h:460
constexpr size_t size() const
Definition dynarray.h:331
constexpr void resize(size_t n)
Resize the dynarray, invoking the default constructor for all new elements.
Definition dynarray.h:512
Graph Data Structure, describes sets of arbitrarily connected vertices.
Definition graph.h:91
void cut(size_t n)
Break all edges to and from n
Definition graph.h:481
constexpr void make_edge(size_t a, size_t b, ArgsT &&...args)
Form an edge from vertex a to vertex b
Definition graph.h:390
constexpr size_t emplace(ArgsT &&...args)
Construct a new vertex in the graph.
Definition graph.h:371
constexpr void erase(size_t vertex)
Erase a vertex from the graph.
Definition graph.h:378
constexpr const edge_t * operator[](size_t a, size_t b) const
edge Const Access Operator
Definition graph.h:247
constexpr graph & operator=(const graph &g)=default
Copy Assignment Operator.
VertexT vertex_t
Alias for the vertex type.
Definition graph.h:96
constexpr const vertex_t & operator[](size_t vertex) const
vertex Const Access Operator
Definition graph.h:222
void clear()
Clear the graph, destructing all vertices and edges.
Definition graph.h:492
constexpr graph & operator=(graph &&g)=default
Move Assignment Operator.
constexpr size_t capacity() const
Definition graph.h:157
constexpr ~graph()=default
Destructor.
list< size_t > symmetric(size_t vertex)
Getter for a list of vertices x that vertex has an edge to and from x...
Definition graph.h:294
list< size_t > incoming(size_t vertex)
Getter for a list of vertices x that vertex has an edge from x...
Definition graph.h:277
constexpr size_t insert(vertex_t &&vertex)
Move a new vertex into the graph.
Definition graph.h:353
constexpr bool is_undirected(size_t a, size_t b) const
Checks if there exists an edge e between a and b
Definition graph.h:191
const auto * edges(size_t vertex)
Getter for the internal storage of mapped edges from this vertex. Use this when you want to iterate o...
Definition graph.h:334
object_pool< vertex_t > vertex_pool_t
Alias for a pool of vertices.
Definition graph.h:97
EdgeT edge_t
Alias for the edge type.
Definition graph.h:95
constexpr void make_edge2(size_t a, size_t b, ArgsT &&...args)
Form an undirected edge between vertex a and vertex b
Definition graph.h:417
list< size_t > outgoing(size_t vertex)
Getter for a list of vertices x that vertex has an edge to x...
Definition graph.h:262
object_pool< edge_t > edge_pool_t
Alias for a pool of edges.
Definition graph.h:99
constexpr bool is_symmetric(size_t a, size_t b) const
Checks if there exists an edge e0 that starts from a and ends at b and e1 that starts from b and ends...
Definition graph.h:182
list< size_t > undirected(size_t vertex)
Getter for a list of vertices x that vertex has an edge to and from x... and share the same value.
Definition graph.h:315
constexpr size_t num_vertices() const
Definition graph.h:145
constexpr bool empty() const
Definition graph.h:163
constexpr void cut_edge2(size_t a, size_t b)
Disconnect both directed edges between vertices a and b
Definition graph.h:466
constexpr void cut_edge(size_t a, size_t b)
Disconnect an edge from vertex a to vertex b
Definition graph.h:443
dynarray< map< size_t, size_t > > edge_map_t
Alias for edge mapping.
Definition graph.h:98
constexpr size_t num_edges() const
Definition graph.h:151
constexpr size_t insert(const vertex_t &vertex)
Copy a new vertex into the graph.
Definition graph.h:361
constexpr bool exists(size_t a, size_t b) const
Checks if there exists an edge e that starts from a and ends at b
Definition graph.h:172
constexpr edge_t * operator[](size_t a, size_t b)
edge Access Operator
Definition graph.h:231
constexpr vertex_t & operator[](size_t vertex)
vertex Access Operator
Definition graph.h:214
static constexpr size_t npos
Constant for a non-existent vertex.
Definition graph.h:101
constexpr graph()=default
Default Constructor, initializes empty graph.
Data Structure defining lists of elements.
Definition list.h:67
constexpr size_t push_back(const value_t &x)
Push Back Copy.
Definition list.h:342
constexpr size_t emplace(ArgsT &&...args)
Emplacement, constructs a new object using args...
Definition object_pool.h:170
constexpr void clear()
Clear the object pool.
Definition object_pool.h:185
constexpr size_t capacity() const
Definition object_pool.h:90
constexpr void erase(size_t i)
Erase an object from the pool.
Definition object_pool.h:177
constexpr size_t size() const
Definition object_pool.h:84