Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_link_cut_tree.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
69# ifndef TPL_LINK_CUT_TREE_H
70# define TPL_LINK_CUT_TREE_H
71
72# include <concepts>
73# include <initializer_list>
74# include <limits>
75# include <memory>
76# include <type_traits>
77# include <utility>
78# include <ah-concepts.H>
79# include <tpl_array.H>
80# include <tpl_arrayStack.H>
81# include <tpl_tree_node.H>
82# include <ah-errors.H>
83
84namespace Aleph
85{
86 // ---------------------------------------------------------------
87 // Concepts
88 // ---------------------------------------------------------------
89
97 template <typename M, typename T>
99
106 template <typename L, typename T>
107 concept LCTLazyTag =
108 std::equality_comparable<typename L::tag_type> and
109 requires(const typename L::tag_type & t1,
110 const typename L::tag_type & t2,
111 const T & val, size_t cnt)
112 {
113 typename L::tag_type;
114 { L::tag_identity() } -> std::convertible_to<typename L::tag_type>;
115 { L::apply(val, t1, cnt) } -> std::convertible_to<T>;
116 { L::compose(t1, t2) } -> std::convertible_to<typename L::tag_type>;
117 };
118
119 // ---------------------------------------------------------------
120 // Built-in monoids
121 // ---------------------------------------------------------------
122
124 template <typename T>
126 {
127 static constexpr T identity() noexcept { return T{}; }
128 static constexpr T combine(const T &, const T & b) noexcept { return b; }
129 };
130
132 template <typename T>
134 {
135 using supports_counted_lazy = std::true_type;
136
137 static constexpr T identity() noexcept { return T{0}; }
138
139 static constexpr T combine(const T & a, const T & b) noexcept
140 {
141 return a + b;
142 }
143 };
144
146 template <typename T>
148 {
149 static constexpr T identity() noexcept
150 {
151 return std::numeric_limits<T>::max();
152 }
153
154 static constexpr T combine(const T & a, const T & b) noexcept
155 {
156 return a < b ? a : b;
157 }
158 };
159
161 template <typename T>
163 {
164 static constexpr T identity() noexcept
165 {
166 return std::numeric_limits<T>::lowest();
167 }
168
169 static constexpr T combine(const T & a, const T & b) noexcept
170 {
171 return a > b ? a : b;
172 }
173 };
174
176 template <typename T>
178 {
179 static constexpr T identity() noexcept { return T{0}; }
180
181 static constexpr T combine(const T & a, const T & b) noexcept
182 {
183 return a ^ b;
184 }
185 };
186
188 template <typename T>
190 {
191 static constexpr T identity() noexcept { return T{0}; }
192
193 static constexpr T combine(const T & a, const T & b) noexcept
194 {
195 T x = a < T{0} ? -a : a;
196 T y = b < T{0} ? -b : b;
197 while (y != T{0})
198 {
199 T t = y;
200 y = x % y;
201 x = t;
202 }
203 return x;
204 }
205 };
206
208 template <typename T>
210 {
211 static constexpr T identity() noexcept { return T{1}; }
212
213 static constexpr T combine(const T & a, const T & b) noexcept
214 {
215 return a * b;
216 }
217 };
218
219 // ---------------------------------------------------------------
220 // Lazy-tag policies
221 // ---------------------------------------------------------------
222
224 template <typename T>
226 {
227 using tag_type = bool;
228 static constexpr bool tag_identity() noexcept { return false; }
229 static constexpr T apply(const T & v, bool, size_t) noexcept { return v; }
230 static constexpr bool compose(bool, bool) noexcept { return false; }
231 };
232
239 template <typename T>
241 {
242 using tag_type = T;
243 static constexpr T tag_identity() noexcept { return T{0}; }
244
245 static constexpr T apply(const T & v, const T & tag, size_t cnt) noexcept
246 {
247 return v + tag * static_cast<T>(cnt);
248 }
249
250 static constexpr T compose(const T & a, const T & b) noexcept
251 {
252 return a + b;
253 }
254 };
255
265 template <typename T>
267 {
269 struct tag_type
270 {
271 T val = {};
272 bool active = false;
273
274 constexpr bool operator==(const tag_type & o) const noexcept
275 {
276 return active == o.active and (not active or val == o.val);
277 }
278 };
279
280 static constexpr tag_type tag_identity() noexcept { return {}; }
281
282 static constexpr T apply(const T & v, const tag_type & tag,
283 size_t cnt) noexcept
284 {
285 return tag.active ? tag.val * static_cast<T>(cnt) : v;
286 }
287
288 static constexpr tag_type compose(const tag_type & existing,
289 const tag_type & newer) noexcept
290 {
291 return newer.active ? newer : existing;
292 }
293 };
294
296 template <class Monoid, class = void>
297 struct monoid_supports_counted_lazy : std::false_type {};
298
300 template <class Monoid>
301 struct monoid_supports_counted_lazy<Monoid, std::void_t<typename Monoid::supports_counted_lazy>>
302 : std::bool_constant<Monoid::supports_counted_lazy::value> {};
303
305 template <class LazyTag, typename T>
306 struct is_counted_lazy_tag : std::false_type {};
307
309 template <typename T>
310 struct is_counted_lazy_tag<AddLazyTag<T>, T> : std::true_type {};
311
313 template <typename T>
314 struct is_counted_lazy_tag<AssignLazyTag<T>, T> : std::true_type {};
315
316 // ---------------------------------------------------------------
317 // Gen_Link_Cut_Tree
318 // ---------------------------------------------------------------
319
354 template <typename T = int,
355 class Monoid = DefaultMonoid<T>,
356 class LazyTag = NoLazyTag<T>>
359 {
362 "AddLazyTag::apply and AssignLazyTag::apply require a monoid "
363 "with supports_counted_lazy=true");
364
365 public:
369 struct Node
370 {
371 friend class Gen_Link_Cut_Tree;
372
373 private:
374 Node *left = nullptr;
375 Node *right = nullptr;
376 Node *parent = nullptr;
377 bool rev = false;
378 size_t sz = 1;
379
382
383 using tag_type = typename LazyTag::tag_type;
384 tag_type lazy = LazyTag::tag_identity();
385
387 [[nodiscard]] const T &get_val() const noexcept { return val; }
388
389 Node() : val(T{}), agg(T{}) {}
390 explicit Node(const T & v) : val(v), agg(v) {}
391 explicit Node(T && v) : val(std::move(v)), agg(val) {}
392 };
393
394 private:
396 size_t n_components = 0;
397
398 // ---- auxiliary predicates ----
399
400 static bool is_root(const Node *x) noexcept
401 {
402 return x->parent == nullptr or
403 (x->parent->left != x and x->parent->right != x);
404 }
405
406 static bool is_left(const Node *x) noexcept
407 {
408 return x->parent and x->parent->left == x;
409 }
410
411 // ---- lazy propagation ----
412
413 static void push(Node *x) noexcept
414 {
415 if (not x)
416 return;
417
418 if (x->rev)
419 {
420 std::swap(x->left, x->right);
421 if (x->left) x->left->rev ^= true;
422 if (x->right) x->right->rev ^= true;
423 x->rev = false;
424 }
425
426 if constexpr (not std::is_same_v<LazyTag, NoLazyTag<T>>)
427 {
428 if (not (x->lazy == LazyTag::tag_identity()))
429 {
430 auto propagate = [&](Node *c)
431 {
432 if (not c)
433 return;
434 c->val = LazyTag::apply(c->val, x->lazy, 1);
435 c->agg = LazyTag::apply(c->agg, x->lazy, c->sz);
436 c->lazy = LazyTag::compose(c->lazy, x->lazy);
437 };
438 propagate(x->left);
439 propagate(x->right);
440 x->lazy = LazyTag::tag_identity();
441 }
442 }
443 }
444
445 // ---- aggregate recomputation ----
446
447 static void pull(Node *x) noexcept
448 {
449 if (not x)
450 return;
451
452 x->sz = 1;
453 x->agg = x->val;
454
455 if (x->left)
456 {
457 x->sz += x->left->sz;
458 x->agg = Monoid::combine(x->left->agg, x->agg);
459 }
460 if (x->right)
461 {
462 x->sz += x->right->sz;
463 x->agg = Monoid::combine(x->agg, x->right->agg);
464 }
465 }
466
467 // ---- rotations ----
468
469 static void rotate(Node *x) noexcept
470 {
471 Node *p = x->parent;
472 Node *g = p->parent;
473
474 if (not is_root(p))
475 {
476 if (g->left == p)
477 g->left = x;
478 else
479 g->right = x;
480 }
481
482 if (p->left == x)
483 {
484 p->left = x->right;
485 if (x->right)
486 x->right->parent = p;
487 x->right = p;
488 }
489 else
490 {
491 p->right = x->left;
492 if (x->left)
493 x->left->parent = p;
494 x->left = p;
495 }
496
497 x->parent = g;
498 p->parent = x;
499
500 pull(p);
501 pull(x);
502 }
503
504 // ---- splay ----
505
506 static void splay(Node *x) noexcept
507 {
508 // collect ancestors within this auxiliary tree
509 // and push lazy flags top-down
510 {
511 Node *u = x;
513 while (not is_root(u))
514 {
515 stk.push(u->parent);
516 u = u->parent;
517 }
518 push(u); // push the aux-tree root
519 while (not stk.is_empty())
520 push(stk.pop());
521 push(x);
522 }
523
524 while (not is_root(x))
525 {
526 Node *p = x->parent;
527 if (not is_root(p))
528 // zig-zig or zig-zag
529 if (Node *g = p->parent; (g->left == p) == (p->left == x))
530 rotate(p); // zig-zig: rotate parent first
531 else
532 rotate(x); // zig-zag: rotate x first
533 rotate(x);
534 }
535 }
536
537 // ---- access ----
538
539 // Exposes the root-to-x preferred path. Returns the last
540 // node splayed during traversal (the LCA when preceded by
541 // a prior access).
542 static Node * access(Node *x) noexcept
543 {
544 Node *last = nullptr;
545 for (Node *u = x; u != nullptr; u = u->parent)
546 {
547 splay(u);
548 u->right = last;
549 pull(u);
550 last = u;
551 }
552 splay(x);
553 return last;
554 }
555
556 // ---- iterative in-order traversal of a splay subtree ----
557
558 template <typename Op>
559 static void inorder_traverse(Node *root, Op && op)
560 {
562 Node *cur = root;
563 while (cur or not stk.is_empty())
564 {
565 while (cur)
566 {
567 push(cur);
568 stk.push(cur);
569 cur = cur->left;
570 }
571 cur = stk.pop();
572 op(cur);
573 cur = cur->right;
574 }
575 }
576
577 public:
579 Gen_Link_Cut_Tree() = default;
580
583 {
584 for (size_t i = 0; i < nodes.size(); ++i)
585 delete nodes(i);
586 }
587
590
593
596
599
609 Node * make_vertex(const T & val = T{})
610 {
611 auto *nd = new Node(val);
612 nodes.append(nd);
613 ++n_components;
614 return nd;
615 }
616
623 {
624 auto *nd = new Node(std::move(val));
625 nodes.append(nd);
626 ++n_components;
627 return nd;
628 }
629
637 {
638 ah_invalid_argument_if(x == nullptr) << "null handle";
639
640 size_t idx = 0;
641 while (idx < nodes.size() and nodes(idx) != x)
642 ++idx;
644 << "vertex does not belong to this link-cut tree";
645
646 access(x);
647 ah_domain_error_if(x->left != nullptr or x->right != nullptr or x->parent != nullptr)
648 << "vertex must be isolated";
649
650 if (idx + 1 < nodes.size())
651 nodes(idx) = nodes(nodes.size() - 1);
653 delete x;
654 --n_components;
655 }
656
658 [[nodiscard]] size_t size() const noexcept { return nodes.size(); }
659
672 {
673 ah_invalid_argument_if(x == nullptr) << "null handle";
674 access(x);
675 x->rev ^= true;
676 push(x);
677 }
678
691 {
692 ah_invalid_argument_if(x == nullptr) << "null handle";
693 access(x);
694 while (true)
695 {
696 push(x);
697 if (x->left == nullptr)
698 break;
699 x = x->left;
700 }
701 splay(x);
702 return x;
703 }
704
715 [[nodiscard]] bool connected(Node *u, Node *v)
716 {
717 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
718 if (u == v)
719 return true;
720 find_root(u);
721 find_root(v);
722 // after find_root(v), if they were connected, u would be
723 // an ancestor of v in the aux tree → u->parent != nullptr
724 // But the standard check is simpler:
725 return find_root(u) == find_root(v);
726 }
727
743 void link(Node *u, Node *v)
744 {
745 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
746 ah_invalid_argument_if(u == v) << "self-loop";
747 make_root(u);
748 ah_domain_error_if(find_root(v) == u) << "already connected";
749 u->parent = v;
750 --n_components;
751 }
752
766 void cut(Node *u, Node *v)
767 {
768 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
769 make_root(u);
770 access(v);
771 ah_domain_error_if(v->left != u or u->parent != v or u->right != nullptr)
772 << "edge does not exist";
773 v->left = nullptr;
774 u->parent = nullptr;
775 pull(v);
776 ++n_components;
777 }
778
793 [[nodiscard]] Node * lca(Node *u, Node *v)
794 {
795 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
796 if (u == v)
797 return u;
798 access(u);
799 Node *ans = access(v);
800 // verify they were connected: after access(v),
801 // u should be reachable via parent chain from v
802 // (access(u) already exposed the root-to-u path)
803 ah_domain_error_if(find_root(u) != find_root(v)) << "not connected";
804 return ans;
805 }
806
822 {
823 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
824 make_root(u);
825 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
826 access(v);
827 return v->agg;
828 }
829
838 [[nodiscard]] size_t path_size(Node *u, Node *v)
839 {
840 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
841 make_root(u);
842 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
843 access(v);
844 return v->sz;
845 }
846
853 [[nodiscard]] const T &get_val(Node *x)
854 {
855 ah_invalid_argument_if(x == nullptr) << "null handle";
856 access(x);
857 return x->val;
858 }
859
866 void set_val(Node *x, const T & val)
867 {
868 ah_invalid_argument_if(x == nullptr) << "null handle";
869 access(x);
870 x->val = val;
871 pull(x);
872 }
873
888 void path_apply(Node *u, Node *v, const typename LazyTag::tag_type & tag)
889 requires (not std::is_same_v<LazyTag, NoLazyTag<T>>)
890 {
891 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
892 make_root(u);
893 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
894 access(v);
895 v->val = LazyTag::apply(v->val, tag, 1);
896 v->agg = LazyTag::apply(v->agg, tag, v->sz);
897 v->lazy = LazyTag::compose(v->lazy, tag);
898 }
899
900 // ---- component & tree queries ----
901
906 {
907 return n_components;
908 }
909
914 [[nodiscard]] size_t tree_size(Node *x)
915 {
916 ah_invalid_argument_if(x == nullptr) << "null handle";
917 size_t count = 0;
918 for (size_t i = 0; i < nodes.size(); ++i)
919 if (connected(nodes(i), x))
920 ++count;
921 return count;
922 }
923
929 [[nodiscard]] size_t depth(Node *x)
930 {
931 ah_invalid_argument_if(x == nullptr) << "null handle";
932 access(x);
933 return x->left ? x->left->sz : 0;
934 }
935
942 {
943 ah_invalid_argument_if(x == nullptr) << "null handle";
944 access(x);
945 if (not x->left)
946 return nullptr;
947 Node *p = x->left;
948 push(p);
949 while (p->right)
950 {
951 p = p->right;
952 push(p);
953 }
954 splay(p);
955 return p;
956 }
957
958 // ---- iteration ----
959
961 template <typename Op>
962 void for_each_node(Op && op) const
963 {
964 for (size_t i = 0; i < nodes.size(); ++i)
965 op(nodes(i));
966 }
967
973 template <typename Op>
974 void for_each_on_path(Node *u, Node *v, Op && op)
975 {
976 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
977 make_root(u);
978 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
979
981 for (Node *cur = v; cur != nullptr; cur = parent(cur))
983
984 for (size_t i = rev_path.size(); i > 0; --i)
985 op(rev_path(i - 1));
986 }
987
988 // ---- batch construction ----
989
1001 Array<Node *> make_path(std::initializer_list<T> vals)
1002 {
1003 Array<Node *> result;
1004 Node *prev = nullptr;
1005 for (const auto & v: vals)
1006 {
1007 auto *nd = make_vertex(v);
1008 if (prev)
1009 link(prev, nd);
1010 prev = nd;
1011 result.append(nd);
1012 }
1013 return result;
1014 }
1015
1020 Array<Node *> make_vertices(std::initializer_list<T> vals)
1021 {
1022 Array<Node *> result;
1023 for (const auto & v: vals)
1024 result.append(make_vertex(v));
1025 return result;
1026 }
1027
1032 template <typename Container>
1033 void link_edges(const Container & edges)
1034 {
1035 for (const auto & [u, v]: edges)
1036 link(u, v);
1037 }
1038
1039 // ---- snapshot export ----
1040
1059 {
1060 ah_invalid_argument_if(root == nullptr) << "null handle";
1061 make_root(root);
1062
1063 // Collect all nodes in root's component and create Tree_Node copies
1065 using TN = Tree_Node<T>;
1066
1067 for_each_node([&](Node * nd)
1068 {
1069 if (connected(nd, root))
1070 lct_nodes.append(nd);
1071 });
1072
1074 tree_nodes.putn(lct_nodes.size());
1075 for (size_t i = 0; i < tree_nodes.size(); ++i)
1076 tree_nodes(i) = nullptr;
1077
1078 auto find_idx = [&](Node * nd) -> size_t
1079 {
1080 for (size_t i = 0; i < lct_nodes.size(); ++i)
1081 if (lct_nodes(i) == nd)
1082 return i;
1083 return lct_nodes.size();
1084 };
1085
1086 size_t root_idx = find_idx(root);
1087 struct DestroyTreeDeleter
1088 {
1089 void operator()(TN * nd) const noexcept
1090 {
1091 if (nd != nullptr)
1093 }
1094 };
1095 std::unique_ptr<TN, DestroyTreeDeleter> root_guard(new TN(root->val));
1097
1098 for (size_t i = 0; i < lct_nodes.size(); ++i)
1099 {
1100 if (i == root_idx)
1101 continue;
1102 Node * par = parent(lct_nodes(i));
1103 if (par != nullptr)
1104 {
1105 size_t pi = find_idx(par);
1106 std::unique_ptr<TN> child(new TN(lct_nodes(i)->val));
1107 tree_nodes(pi)->insert_rightmost_child(child.get());
1108 tree_nodes(i) = child.release();
1109 }
1110 }
1111
1112 return root_guard.release();
1113 }
1114 };
1115
1122} // namespace Aleph
1123
1124# endif /* TPL_LINK_CUT_TREE_H */
C++20 concepts hub: comparison, BST policy, and Aleph container concepts.
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
void destroy_tree(Tree &tree)
Definition avl-rb-rk.cc:521
WeightedDigraph::Node Node
Stack implemented with simple dynamic array and with bounds verification.
T & push(const T &data)
Push into stack a copy of data
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
Forward declaration used by CRTP helpers before the full node definition.
Concept for a lazy-tag policy used in deferred path updates.
Concept for a monoidal combiner over path values.
A static monoid: M::identity() and M::combine(a, b).
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
Additive lazy tag: adds a delta to every node on a path.
static constexpr T apply(const T &v, const T &tag, size_t cnt) noexcept
static constexpr T compose(const T &a, const T &b) noexcept
static constexpr T tag_identity() noexcept
Payload for the assignment lazy tag.
constexpr bool operator==(const tag_type &o) const noexcept
Assignment lazy tag: sets every node on a path to a fixed value.
static constexpr T apply(const T &v, const tag_type &tag, size_t cnt) noexcept
static constexpr tag_type compose(const tag_type &existing, const tag_type &newer) noexcept
static constexpr tag_type tag_identity() noexcept
Default (no-op) monoid for connectivity-only usage.
static constexpr T identity() noexcept
static constexpr T combine(const T &, const T &b) noexcept
GCD monoid: identity = 0 (since gcd(0, x) = x), combine = gcd.
static constexpr T combine(const T &a, const T &b) noexcept
static constexpr T identity() noexcept
Max monoid: identity = numeric lowest, combine = max(a, b).
static constexpr T identity() noexcept
static constexpr T combine(const T &a, const T &b) noexcept
Min monoid: identity = numeric max, combine = min(a, b).
static constexpr T combine(const T &a, const T &b) noexcept
static constexpr T identity() noexcept
No-op lazy tag (default — no deferred path updates).
static constexpr bool tag_identity() noexcept
static constexpr T apply(const T &v, bool, size_t) noexcept
static constexpr bool compose(bool, bool) noexcept
Product monoid: identity = 1, combine = a * b.
static constexpr T combine(const T &a, const T &b) noexcept
static constexpr T identity() noexcept
Sum monoid: identity = 0, combine = a + b.
static constexpr T combine(const T &a, const T &b) noexcept
std::true_type supports_counted_lazy
static constexpr T identity() noexcept
XOR monoid: identity = 0, combine = a ^ b.
static constexpr T identity() noexcept
static constexpr T combine(const T &a, const T &b) noexcept
Metafunction to check if a tag is a counted lazy tag.
Metafunction to check if a monoid supports counted lazy tags.
Stack implementations backed by dynamic or fixed arrays.
Dynamic array container with automatic resizing.
General tree (n-ary tree) node.