Sequoia
Loading...
Searching...
No Matches
GraphPrimitive.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2018. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
18
19namespace sequoia
20{
21 namespace maths
22 {
23 template<network Connectivity, class Nodes>
24 class SEQUOIA_MSVC_EMPTY_BASE_HACK graph_primitive;
25
26 namespace graph_impl
27 {
28 template<network Connectivity, class Nodes>
30 {
31 public:
33 using index_type = typename graph_type::edge_index_type;
34 using difference_type = std::make_signed_t<index_type>;
35 using value_type = index_type;
36 using reference = index_type;
37
38 [[nodiscard]]
39 constexpr graph_type& graph() noexcept
40 {
41 return *m_Graph;
42 }
43
44 [[nodiscard]]
45 constexpr const graph_type& graph() const noexcept
46 {
47 return *m_Graph;
48 }
49
50 [[nodiscard]]
51 constexpr static reference get(index_type i) { return i; }
52 protected:
53 constexpr graph_dereference_policy() = default;
54
55 constexpr graph_dereference_policy(graph_type& g) : m_Graph{&g} {}
56
57 constexpr graph_dereference_policy(const graph_dereference_policy&) = default;
58 constexpr graph_dereference_policy(graph_dereference_policy&&) noexcept = default;
59
60 ~graph_dereference_policy() = default;
61
62 constexpr graph_dereference_policy& operator=(const graph_dereference_policy&) = default;
63 constexpr graph_dereference_policy& operator=(graph_dereference_policy&&) noexcept = default;
64
65 private:
66 graph_type* m_Graph{};
67 };
68
72 template<network Connectivity, class Nodes>
74 }
75
76 template<network Connectivity, class Nodes>
78 {
79 a.graph().swap_nodes(a.base_iterator(), b.base_iterator());
80 }
81
82 template<class NodeWeight>
84 {
85 NodeWeight node;
86 std::initializer_list<tree_initializer> children{};
87 };
88
89 template<class NodeWeight>
90 requires std::is_empty_v<NodeWeight>
91 struct tree_initializer<NodeWeight>
92 {
93 std::initializer_list<tree_initializer> children{};
94 };
95
96 enum class tree_link_direction {symmetric, forward, backward};
97
98 template<tree_link_direction d>
99 struct tree_link_direction_constant : std::integral_constant<tree_link_direction, d> {};
100
104
105 template<network Connectivity, class Nodes>
106 class SEQUOIA_MSVC_EMPTY_BASE_HACK graph_primitive : public Connectivity, public Nodes
107 {
108 private:
109 using node_weight_type = typename Nodes::weight_type;
110
111 public:
112 using connectivity_type = Connectivity;
113 using nodes_type = Nodes;
114 using edge_init_type = typename Connectivity::edge_init_type;
115 using edge_index_type = typename Connectivity::edge_index_type;
116 using size_type = std::common_type_t<typename Connectivity::size_type, typename Nodes::size_type>;
118
119 using edges_initializer = std::initializer_list<std::initializer_list<edge_init_type>>;
120
121 constexpr graph_primitive() = default;
122
123 constexpr graph_primitive(edges_initializer edges)
124 requires std::is_empty_v<node_weight_type>
125 : Connectivity{edges}
126 , Nodes{}
127 {}
128
129 constexpr graph_primitive(edges_initializer edges)
130 requires (!std::is_empty_v<node_weight_type> && !heterogeneous_nodes<graph_primitive> && std::is_default_constructible_v<node_weight_type>)
131 : Connectivity{edges}
132 , Nodes(edges.size())
133 {}
134
135 constexpr graph_primitive(edges_initializer edges, std::initializer_list<node_weight_type> nodeWeights)
136 requires (!std::is_empty_v<node_weight_type> && !heterogeneous_nodes<graph_primitive>)
137 : Connectivity{check_initialization(edges, nodeWeights.size(), edges.size())}
138 , Nodes{nodeWeights}
139 {}
140
141 template<class... NodeWeights>
142 requires heterogeneous_nodes<graph_primitive>
143 constexpr graph_primitive(edges_initializer edges, NodeWeights&&... nodeWeights)
144 : Connectivity{check_initialization(edges, sizeof...(NodeWeights), edges.size())}
145 , Nodes{std::forward<NodeWeights>(nodeWeights)...}
146 {}
147
148 template<tree_link_direction dir>
149 requires ( !heterogeneous_nodes<graph_primitive>
150 && ((dir == tree_link_direction::symmetric) || is_directed(Connectivity::flavour)))
152 {
153 for(const auto& tree : forest)
154 {
155 build_tree(std::numeric_limits<size_type>::max(), tree, tdc);
156 }
157 }
158
159 template<tree_link_direction dir>
160 requires ( !heterogeneous_nodes<graph_primitive>
161 && ((dir == tree_link_direction::symmetric) || is_directed(Connectivity::flavour)))
163 {
164 build_tree(std::numeric_limits<size_type>::max(), tree, tdc);
165 }
166
167 constexpr graph_primitive(const graph_primitive&) = default;
168
169 [[nodiscard]]
170 constexpr size_type size() const noexcept
171 {
172 return connectivity_type::size();
173 }
174
175 constexpr void swap_nodes(edge_index_type i, edge_index_type j)
176 {
177 if constexpr(!std::is_empty_v<node_weight_type>)
178 {
179 Nodes::swap_nodes(i, j);
180 }
181
182 Connectivity::swap_nodes(i, j);
183 }
184
185 template<class Compare>
186 constexpr void sort_nodes(Compare c)
187 {
188 sort_nodes(edge_index_type{}, Connectivity::order(), std::move(c));
189 }
190
191 template<class Compare>
192 constexpr void sort_nodes(const edge_index_type first, const edge_index_type last, Compare c)
193 {
194 sequoia::sort(pseudo_iterator{first, *this}, pseudo_iterator{last, *this}, std::move(c));
195 }
196
197 //===============================equality (not isomorphism) operators================================//
198
199 [[nodiscard]]
200 friend constexpr bool operator==(const graph_primitive&, const graph_primitive&) noexcept = default;
201 protected:
202 // Constructors with allocators
203
204 template<class N>
205 constexpr static bool enableNodeAllocation{!heterogeneous_nodes<graph_primitive> && !std::is_empty_v<N>};
206
207 template
208 <
209 alloc EdgeAllocator,
210 alloc NodeAllocator
211 >
212 requires enableNodeAllocation<node_weight_type>
213 constexpr graph_primitive(const EdgeAllocator& edgeAlloc, const NodeAllocator& nodeAlloc)
214
215 : Connectivity(edgeAlloc)
216 , Nodes(nodeAlloc)
217 {}
218
219 template
220 <
221 alloc EdgeAllocator,
222 alloc EdgePartitionsAllocator,
223 alloc NodeAllocator
224 >
225 requires (enableNodeAllocation<node_weight_type>)
226 constexpr graph_primitive(const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, const NodeAllocator& nodeAlloc)
227 : Connectivity(edgeAlloc, edgeParitionsAlloc)
228 , Nodes(nodeAlloc)
229 {}
230
231 template<alloc EdgeAllocator>
232 requires (!enableNodeAllocation<node_weight_type>)
233 constexpr graph_primitive(const EdgeAllocator& edgeAlloc)
234 : Connectivity(edgeAlloc)
235 {}
236
237 template
238 <
239 alloc EdgeAllocator,
240 alloc EdgePartitionsAllocator
241 >
242 requires(!enableNodeAllocation<node_weight_type>)
243 constexpr graph_primitive(const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc)
244 : Connectivity(edgeAlloc, edgeParitionsAlloc)
245 {}
246
247 template
248 <
249 alloc EdgeAllocator,
250 alloc NodeAllocator
251 >
252 requires enableNodeAllocation<node_weight_type>
253 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, std::initializer_list<node_weight_type> nodeWeights, const NodeAllocator& nodeAlloc)
254 : Connectivity{edges, edgeAlloc}
255 , Nodes{nodeWeights, nodeAlloc}
256 {}
257
258 template
259 <
260 alloc EdgeAllocator,
261 alloc EdgePartitionsAllocator,
262 alloc NodeAllocator
263 >
264 requires enableNodeAllocation<node_weight_type>
265 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, std::initializer_list<node_weight_type> nodeWeights, const NodeAllocator& nodeAlloc)
266 : Connectivity{edges, edgeAlloc, edgeParitionsAlloc}
267 , Nodes{nodeWeights, nodeAlloc}
268 {}
269
270 template
271 <
272 alloc EdgeAllocator,
273 alloc EdgePartitionsAllocator,
274 class... NodeWeights
275 >
276 requires heterogeneous_nodes<graph_primitive>
277 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, NodeWeights&&... nodeWeights)
278 : Connectivity{edges, edgeAlloc, edgeParitionsAlloc}
279 , Nodes{std::forward<NodeWeights>(nodeWeights)...}
280 {}
281
282 template
283 <
284 alloc EdgeAllocator,
285 alloc NodeAllocator
286 >
287 requires enableNodeAllocation<node_weight_type>
288 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, const NodeAllocator& nodeAlloc)
289 : Connectivity{edges, edgeAlloc}
290 , Nodes(edges.size(), nodeAlloc)
291 {}
292
293 template
294 <
295 alloc EdgeAllocator,
296 alloc EdgePartitionsAllocator,
297 alloc NodeAllocator
298 >
299 requires enableNodeAllocation<node_weight_type>
300 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, const NodeAllocator& nodeAlloc)
301 : Connectivity{edges, edgeAlloc, edgeParitionsAlloc}
302 , Nodes(edges.size(), nodeAlloc)
303 {}
304
305 template
306 <
307 alloc EdgeAllocator,
308 alloc EdgePartitionsAllocator
309 >
310 requires (!enableNodeAllocation<node_weight_type>)
311 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc)
312 : Connectivity{edges, edgeAlloc, edgeParitionsAlloc}
313 , Nodes{}
314 {}
315
316 template<alloc EdgeAllocator>
317 requires (!enableNodeAllocation<node_weight_type>)
318 constexpr graph_primitive(edges_initializer edges, const EdgeAllocator& edgeAlloc)
319 : Connectivity{edges, edgeAlloc}
320 , Nodes{}
321 {}
322
323 // Copy-like constructors with allocators
324
325 template
326 <
327 alloc EdgeAllocator,
328 alloc EdgePartitionsAllocator,
329 alloc NodeAllocator
330 >
331 requires enableNodeAllocation<node_weight_type>
332 constexpr graph_primitive(const graph_primitive& in, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, const NodeAllocator& nodeAlloc)
333 : Connectivity{static_cast<const Connectivity&>(in), edgeAlloc, edgeParitionsAlloc}
334 , Nodes{static_cast<const Nodes&>(in), nodeAlloc}
335 {}
336
337 template
338 <
339 alloc EdgeAllocator,
340 alloc NodeAllocator
341 >
342 requires enableNodeAllocation<node_weight_type>
343 constexpr graph_primitive(const graph_primitive& in, const EdgeAllocator& edgeAlloc, const NodeAllocator& nodeAlloc)
344 : Connectivity{static_cast<const Connectivity&>(in), edgeAlloc}
345 , Nodes{static_cast<const Nodes&>(in), nodeAlloc}
346 {}
347
348 template
349 <
350 alloc EdgeAllocator,
351 alloc EdgePartitionsAllocator
352 >
353 requires (!enableNodeAllocation<node_weight_type>)
354 constexpr graph_primitive(const graph_primitive& in, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc)
355 : Connectivity{static_cast<const Connectivity&>(in), edgeAlloc, edgeParitionsAlloc}
356 , Nodes{static_cast<const Nodes&>(in)}
357 {}
358
359 template<alloc EdgeAllocator>
360 requires (!enableNodeAllocation<node_weight_type>)
361 constexpr graph_primitive(const graph_primitive& in, const EdgeAllocator& edgeAlloc)
362 : Connectivity{static_cast<const Connectivity&>(in), edgeAlloc}
363 , Nodes{static_cast<const Nodes&>(in)}
364 {}
365
366 constexpr graph_primitive(graph_primitive&&) noexcept = default;
367
368 // Move-like constructors with allocators
369
370 template
371 <
372 alloc EdgeAllocator,
373 alloc EdgePartitionsAllocator,
374 alloc NodeAllocator
375 >
376 requires enableNodeAllocation<node_weight_type>
377 constexpr graph_primitive(graph_primitive&& in, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc, const NodeAllocator& nodeAlloc)
378 : Connectivity{static_cast<Connectivity&&>(in), edgeAlloc, edgeParitionsAlloc}
379 , Nodes{static_cast<Nodes&&>(in), nodeAlloc}
380 {}
381
382 template
383 <
384 alloc EdgeAllocator,
385 alloc NodeAllocator
386 >
387 requires enableNodeAllocation<node_weight_type>
388 constexpr graph_primitive(graph_primitive&& in, const EdgeAllocator& edgeAlloc, const NodeAllocator& nodeAlloc)
389 : Connectivity{static_cast<Connectivity&&>(in), edgeAlloc}
390 , Nodes{static_cast<Nodes&&>(in), nodeAlloc}
391 {}
392
393 template
394 <
395 alloc EdgeAllocator,
396 alloc EdgePartitionsAllocator,
397 alloc NodeAllocator
398 >
399 requires (!enableNodeAllocation<node_weight_type>)
400 constexpr graph_primitive(graph_primitive&& in, const EdgeAllocator& edgeAlloc, const EdgePartitionsAllocator& edgeParitionsAlloc)
401 : Connectivity{static_cast<Connectivity&&>(in), edgeAlloc, edgeParitionsAlloc}
402 , Nodes{static_cast<Nodes&&>(in)}
403 {}
404
405 template
406 <
407 alloc EdgeAllocator,
408 alloc NodeAllocator
409 >
410 requires (!enableNodeAllocation<node_weight_type>)
411 constexpr graph_primitive(graph_primitive&& in, const EdgeAllocator& edgeAlloc)
412 : Connectivity{static_cast<Connectivity&&>(in), edgeAlloc}
413 , Nodes{static_cast<Nodes&&>(in)}
414 {}
415
416 ~graph_primitive() = default;
417
418 constexpr graph_primitive& operator=(graph_primitive&&) noexcept = default;
419
420 constexpr graph_primitive& operator=(const graph_primitive&) = default;
421
422 void swap(graph_primitive& rhs)
423 noexcept(noexcept(this->Connectivity::swap(rhs)) && noexcept(this->Nodes::swap(rhs)))
424 {
425 Connectivity::swap(rhs);
426 Nodes::swap(rhs);
427 }
428
429 void reserve_nodes(const size_type size)
430 {
431 if constexpr(!std::is_empty_v<node_weight_type>)
432 {
433 Nodes::reserve(size);
434 }
435
436 Connectivity::reserve_nodes(size);
437 }
438
439 [[nodiscard]]
440 size_type node_capacity() const noexcept
441 {
442 if constexpr(!std::is_empty_v<node_weight_type>)
443 {
444 return std::ranges::min(Connectivity::node_capacity(), Nodes::capacity());
445 }
446 else
447 {
448 return Connectivity::node_capacity();
449 }
450 }
451
452 void shrink_to_fit()
453 {
454 if constexpr(!std::is_empty_v<node_weight_type>)
455 {
456 Nodes::shrink_to_fit();
457 }
458
459 Connectivity::shrink_to_fit();
460 }
461
462 template<class... Args>
463 size_type add_node(Args&&... args)
464 {
465 return insert_node(this->order(), std::forward<Args>(args)...);
466 }
467
468 template<class... Args>
469 size_type insert_node(const size_type pos, Args&&... args)
470 {
471 insertion_sentinel sentinel{*this, pos};
472
473 return Connectivity::insert_node(insert_node_impl(pos, std::forward<Args>(args)...));
474 }
475
476 void erase_node(const size_type node)
477 {
478 Connectivity::erase_node(node);
479 if constexpr (!std::is_empty_v<node_weight_type>) Nodes::erase_node(this->cbegin_node_weights() + node);
480 }
481
482 void clear() noexcept
483 {
484 if constexpr (!std::is_empty_v<node_weight_type>) Nodes::clear();
485 Connectivity::clear();
486 }
487
488 template<tree_link_direction dir, class... Args>
489 size_type insert_node_to_tree(tree_link_direction_constant<dir>, size_type pos, size_type parent, Args&&... args)
490 {
491 const auto n{insert_node(pos, std::forward<Args>(args)...)};
492
493 if(this->order() > 1)
494 {
495 if constexpr((dir != tree_link_direction::forward) && is_directed(Connectivity::flavour))
496 {
497 Connectivity::join(n, parent);
498 }
499
500 if constexpr(dir != tree_link_direction::backward)
501 {
502 Connectivity::join(parent, n);
503 }
504 }
505
506 return n;
507 }
508
509 template<tree_link_direction dir, class... Args>
510 size_type add_node_to_tree(tree_link_direction_constant<dir> tldc, size_type parent, Args&&... args)
511 {
512 return insert_node_to_tree(tldc, this->order(), parent, std::forward<Args>(args)...);
513 }
514 private:
515 class [[nodiscard]] insertion_sentinel
516 {
517 public:
518 insertion_sentinel(graph_primitive& g, size_type index)
519 : m_Graph{g}
520 , m_NodeIndex{index}
521 {}
522
523 ~insertion_sentinel()
524 {
525 if constexpr(!std::is_empty_v<node_weight_type>)
526 {
527 if(static_cast<Nodes&>(m_Graph).size() > static_cast<Connectivity&>(m_Graph).order())
528 m_Graph.remove_excess_node(m_NodeIndex);
529 }
530 }
531 private:
532 graph_primitive& m_Graph{};
533 size_type m_NodeIndex{};
534 };
535
536 friend insertion_sentinel;
537
538 [[nodiscard]]
539 constexpr static const edges_initializer& check_initialization(const edges_initializer& edges, std::size_t numNodes, std::size_t edgeParitions)
540 {
541 if(numNodes != edgeParitions)
542 throw std::logic_error{graph_errors::inconsistent_initialization_message(numNodes, edgeParitions)};
543
544 return edges;
545 }
546
547 template<tree_link_direction dir>
548 requires (!heterogeneous_nodes<graph_primitive> && !std::is_empty_v<node_weight_type>)
549 constexpr void build_tree(size_type root, tree_initializer<node_weight_type> tree, tree_link_direction_constant<dir> tdc)
550 {
551 if(root >= Connectivity::order()) root = add_node(tree.node);
552
553 for(const auto& child : tree.children)
554 {
555 const auto n{add_node_to_tree(tdc, root, child.node)};
556 build_tree(n, child, tdc);
557 }
558 }
559
560 template<tree_link_direction dir>
561 requires (!heterogeneous_nodes<graph_primitive> && std::is_empty_v<node_weight_type>)
562 constexpr void build_tree(size_type root, tree_initializer<node_weight_type> tree, tree_link_direction_constant<dir> tdc)
563 {
564 if(root >= Connectivity::order()) root = add_node();
565
566 for(const auto& child : tree.children)
567 {
568 const auto n{add_node_to_tree(tdc, root)};
569 build_tree(n, child, tdc);
570 }
571 }
572
573 [[nodiscard]]
574 pseudo_iterator begin() noexcept { return {edge_index_type{}, *this}; }
575
576 [[nodiscard]]
577 pseudo_iterator end() noexcept { return {Connectivity::order(), *this}; }
578
579 template<class... Args>
580 size_type insert_node_impl(const size_type pos, Args&&... args)
581 {
582 const auto node{std::ranges::min(pos, this->order())};
583 if constexpr (!std::is_empty_v<node_weight_type>)
584 {
585 Nodes::insert_node(this->cbegin_node_weights() + node, std::forward<Args>(args)...);
586 }
587
588 return node;
589 }
590
591 void remove_excess_node(size_type index)
592 {
593 if(!std::is_empty_v<node_weight_type>)
594 {
595 Nodes::erase_node(this->cbegin_node_weights() + index);
596 }
597 }
598 };
599 }
600}
A collection of constexpr algorithms.
constexpr void sort(Iter begin, Iter end, Comp comp={}, Proj proj={})
Definition: Algorithms.hpp:79
Helper for dealing with allocator propagation during copy assignment.
Implementation for a partitioned sequence of edges, which represents a graph's connectivity.
Traits and Concepts for graphs.
Definition: GraphPrimitive.hpp:107
An iterator with policies controlling dereferencing and auxiliary data.
Definition: Iterator.hpp:234
A concept for allocators.
Definition: Concepts.hpp:48
Definition: GraphPrimitive.hpp:84