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 <tpl_array.H>
79# include <tpl_arrayStack.H>
80# include <tpl_tree_node.H>
81# include <ah-errors.H>
82
83namespace Aleph
84{
85 // ---------------------------------------------------------------
86 // Concepts
87 // ---------------------------------------------------------------
88
95 template <typename M, typename T>
96 concept LCTMonoid =
97 requires(const M & m, const T & a, const T & b)
98 {
99 { M::identity() } -> std::convertible_to<T>;
100 { M::combine(a, b) } -> std::convertible_to<T>;
101 };
102
109 template <typename L, typename T>
110 concept LCTLazyTag =
111 std::equality_comparable<typename L::tag_type> and
112 requires(const typename L::tag_type & t1,
113 const typename L::tag_type & t2,
114 const T & val, size_t cnt)
115 {
116 typename L::tag_type;
117 { L::tag_identity() } -> std::convertible_to<typename L::tag_type>;
118 { L::apply(val, t1, cnt) } -> std::convertible_to<T>;
119 { L::compose(t1, t2) } -> std::convertible_to<typename L::tag_type>;
120 };
121
122 // ---------------------------------------------------------------
123 // Built-in monoids
124 // ---------------------------------------------------------------
125
127 template <typename T>
129 {
130 static constexpr T identity() noexcept { return T{}; }
131 static constexpr T combine(const T &, const T & b) noexcept { return b; }
132 };
133
135 template <typename T>
137 {
138 using supports_counted_lazy = std::true_type;
139
140 static constexpr T identity() noexcept { return T{0}; }
141
142 static constexpr T combine(const T & a, const T & b) noexcept
143 {
144 return a + b;
145 }
146 };
147
149 template <typename T>
151 {
152 static constexpr T identity() noexcept
153 {
154 return std::numeric_limits<T>::max();
155 }
156
157 static constexpr T combine(const T & a, const T & b) noexcept
158 {
159 return a < b ? a : b;
160 }
161 };
162
164 template <typename T>
166 {
167 static constexpr T identity() noexcept
168 {
169 return std::numeric_limits<T>::lowest();
170 }
171
172 static constexpr T combine(const T & a, const T & b) noexcept
173 {
174 return a > b ? a : b;
175 }
176 };
177
179 template <typename T>
181 {
182 static constexpr T identity() noexcept { return T{0}; }
183
184 static constexpr T combine(const T & a, const T & b) noexcept
185 {
186 return a ^ b;
187 }
188 };
189
191 template <typename T>
193 {
194 static constexpr T identity() noexcept { return T{0}; }
195
196 static constexpr T combine(const T & a, const T & b) noexcept
197 {
198 T x = a < T{0} ? -a : a;
199 T y = b < T{0} ? -b : b;
200 while (y != T{0})
201 {
202 T t = y;
203 y = x % y;
204 x = t;
205 }
206 return x;
207 }
208 };
209
211 template <typename T>
213 {
214 static constexpr T identity() noexcept { return T{1}; }
215
216 static constexpr T combine(const T & a, const T & b) noexcept
217 {
218 return a * b;
219 }
220 };
221
222 // ---------------------------------------------------------------
223 // Lazy-tag policies
224 // ---------------------------------------------------------------
225
227 template <typename T>
229 {
230 using tag_type = bool;
231 static constexpr bool tag_identity() noexcept { return false; }
232 static constexpr T apply(const T & v, bool, size_t) noexcept { return v; }
233 static constexpr bool compose(bool, bool) noexcept { return false; }
234 };
235
242 template <typename T>
244 {
245 using tag_type = T;
246 static constexpr T tag_identity() noexcept { return T{0}; }
247
248 static constexpr T apply(const T & v, const T & tag, size_t cnt) noexcept
249 {
250 return v + tag * static_cast<T>(cnt);
251 }
252
253 static constexpr T compose(const T & a, const T & b) noexcept
254 {
255 return a + b;
256 }
257 };
258
268 template <typename T>
270 {
272 struct tag_type
273 {
274 T val = {};
275 bool active = false;
276
277 constexpr bool operator==(const tag_type & o) const noexcept
278 {
279 return active == o.active and (not active or val == o.val);
280 }
281 };
282
283 static constexpr tag_type tag_identity() noexcept { return {}; }
284
285 static constexpr T apply(const T & v, const tag_type & tag,
286 size_t cnt) noexcept
287 {
288 return tag.active ? tag.val * static_cast<T>(cnt) : v;
289 }
290
291 static constexpr tag_type compose(const tag_type & existing,
292 const tag_type & newer) noexcept
293 {
294 return newer.active ? newer : existing;
295 }
296 };
297
299 template <class Monoid, class = void>
300 struct monoid_supports_counted_lazy : std::false_type {};
301
303 template <class Monoid>
304 struct monoid_supports_counted_lazy<Monoid, std::void_t<typename Monoid::supports_counted_lazy>>
305 : std::bool_constant<Monoid::supports_counted_lazy::value> {};
306
308 template <class LazyTag, typename T>
309 struct is_counted_lazy_tag : std::false_type {};
310
312 template <typename T>
313 struct is_counted_lazy_tag<AddLazyTag<T>, T> : std::true_type {};
314
316 template <typename T>
317 struct is_counted_lazy_tag<AssignLazyTag<T>, T> : std::true_type {};
318
319 // ---------------------------------------------------------------
320 // Gen_Link_Cut_Tree
321 // ---------------------------------------------------------------
322
357 template <typename T = int,
358 class Monoid = DefaultMonoid<T>,
359 class LazyTag = NoLazyTag<T>>
362 {
365 "AddLazyTag::apply and AssignLazyTag::apply require a monoid "
366 "with supports_counted_lazy=true");
367
368 public:
372 struct Node
373 {
374 friend class Gen_Link_Cut_Tree;
375
376 private:
377 Node *left = nullptr;
378 Node *right = nullptr;
379 Node *parent = nullptr;
380 bool rev = false;
381 size_t sz = 1;
382
385
386 using tag_type = typename LazyTag::tag_type;
387 tag_type lazy = LazyTag::tag_identity();
388
390 [[nodiscard]] const T &get_val() const noexcept { return val; }
391
392 Node() : val(T{}), agg(T{}) {}
393 explicit Node(const T & v) : val(v), agg(v) {}
394 explicit Node(T && v) : val(std::move(v)), agg(val) {}
395 };
396
397 private:
399 size_t n_components = 0;
400
401 // ---- auxiliary predicates ----
402
403 static bool is_root(const Node *x) noexcept
404 {
405 return x->parent == nullptr or
406 (x->parent->left != x and x->parent->right != x);
407 }
408
409 static bool is_left(const Node *x) noexcept
410 {
411 return x->parent and x->parent->left == x;
412 }
413
414 // ---- lazy propagation ----
415
416 static void push(Node *x) noexcept
417 {
418 if (not x)
419 return;
420
421 if (x->rev)
422 {
423 std::swap(x->left, x->right);
424 if (x->left) x->left->rev ^= true;
425 if (x->right) x->right->rev ^= true;
426 x->rev = false;
427 }
428
429 if constexpr (not std::is_same_v<LazyTag, NoLazyTag<T>>)
430 {
431 if (not (x->lazy == LazyTag::tag_identity()))
432 {
433 auto propagate = [&](Node *c)
434 {
435 if (not c)
436 return;
437 c->val = LazyTag::apply(c->val, x->lazy, 1);
438 c->agg = LazyTag::apply(c->agg, x->lazy, c->sz);
439 c->lazy = LazyTag::compose(c->lazy, x->lazy);
440 };
441 propagate(x->left);
442 propagate(x->right);
443 x->lazy = LazyTag::tag_identity();
444 }
445 }
446 }
447
448 // ---- aggregate recomputation ----
449
450 static void pull(Node *x) noexcept
451 {
452 if (not x)
453 return;
454
455 x->sz = 1;
456 x->agg = x->val;
457
458 if (x->left)
459 {
460 x->sz += x->left->sz;
461 x->agg = Monoid::combine(x->left->agg, x->agg);
462 }
463 if (x->right)
464 {
465 x->sz += x->right->sz;
466 x->agg = Monoid::combine(x->agg, x->right->agg);
467 }
468 }
469
470 // ---- rotations ----
471
472 static void rotate(Node *x) noexcept
473 {
474 Node *p = x->parent;
475 Node *g = p->parent;
476
477 if (not is_root(p))
478 {
479 if (g->left == p)
480 g->left = x;
481 else
482 g->right = x;
483 }
484
485 if (p->left == x)
486 {
487 p->left = x->right;
488 if (x->right)
489 x->right->parent = p;
490 x->right = p;
491 }
492 else
493 {
494 p->right = x->left;
495 if (x->left)
496 x->left->parent = p;
497 x->left = p;
498 }
499
500 x->parent = g;
501 p->parent = x;
502
503 pull(p);
504 pull(x);
505 }
506
507 // ---- splay ----
508
509 static void splay(Node *x) noexcept
510 {
511 // collect ancestors within this auxiliary tree
512 // and push lazy flags top-down
513 {
514 Node *u = x;
516 while (not is_root(u))
517 {
518 stk.push(u->parent);
519 u = u->parent;
520 }
521 push(u); // push the aux-tree root
522 while (not stk.is_empty())
523 push(stk.pop());
524 push(x);
525 }
526
527 while (not is_root(x))
528 {
529 Node *p = x->parent;
530 if (not is_root(p))
531 // zig-zig or zig-zag
532 if (Node *g = p->parent; (g->left == p) == (p->left == x))
533 rotate(p); // zig-zig: rotate parent first
534 else
535 rotate(x); // zig-zag: rotate x first
536 rotate(x);
537 }
538 }
539
540 // ---- access ----
541
542 // Exposes the root-to-x preferred path. Returns the last
543 // node splayed during traversal (the LCA when preceded by
544 // a prior access).
545 static Node * access(Node *x) noexcept
546 {
547 Node *last = nullptr;
548 for (Node *u = x; u != nullptr; u = u->parent)
549 {
550 splay(u);
551 u->right = last;
552 pull(u);
553 last = u;
554 }
555 splay(x);
556 return last;
557 }
558
559 // ---- iterative in-order traversal of a splay subtree ----
560
561 template <typename Op>
562 static void inorder_traverse(Node *root, Op && op)
563 {
565 Node *cur = root;
566 while (cur or not stk.is_empty())
567 {
568 while (cur)
569 {
570 push(cur);
571 stk.push(cur);
572 cur = cur->left;
573 }
574 cur = stk.pop();
575 op(cur);
576 cur = cur->right;
577 }
578 }
579
580 public:
582 Gen_Link_Cut_Tree() = default;
583
586 {
587 for (size_t i = 0; i < nodes.size(); ++i)
588 delete nodes(i);
589 }
590
593
596
599
602
612 Node * make_vertex(const T & val = T{})
613 {
614 auto *nd = new Node(val);
615 nodes.append(nd);
616 ++n_components;
617 return nd;
618 }
619
626 {
627 auto *nd = new Node(std::move(val));
628 nodes.append(nd);
629 ++n_components;
630 return nd;
631 }
632
640 {
641 ah_invalid_argument_if(x == nullptr) << "null handle";
642
643 size_t idx = 0;
644 while (idx < nodes.size() and nodes(idx) != x)
645 ++idx;
647 << "vertex does not belong to this link-cut tree";
648
649 access(x);
650 ah_domain_error_if(x->left != nullptr or x->right != nullptr or x->parent != nullptr)
651 << "vertex must be isolated";
652
653 if (idx + 1 < nodes.size())
654 nodes(idx) = nodes(nodes.size() - 1);
656 delete x;
657 --n_components;
658 }
659
661 [[nodiscard]] size_t size() const noexcept { return nodes.size(); }
662
675 {
676 ah_invalid_argument_if(x == nullptr) << "null handle";
677 access(x);
678 x->rev ^= true;
679 push(x);
680 }
681
694 {
695 ah_invalid_argument_if(x == nullptr) << "null handle";
696 access(x);
697 while (true)
698 {
699 push(x);
700 if (x->left == nullptr)
701 break;
702 x = x->left;
703 }
704 splay(x);
705 return x;
706 }
707
718 [[nodiscard]] bool connected(Node *u, Node *v)
719 {
720 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
721 if (u == v)
722 return true;
723 find_root(u);
724 find_root(v);
725 // after find_root(v), if they were connected, u would be
726 // an ancestor of v in the aux tree → u->parent != nullptr
727 // But the standard check is simpler:
728 return find_root(u) == find_root(v);
729 }
730
746 void link(Node *u, Node *v)
747 {
748 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
749 ah_invalid_argument_if(u == v) << "self-loop";
750 make_root(u);
751 ah_domain_error_if(find_root(v) == u) << "already connected";
752 u->parent = v;
753 --n_components;
754 }
755
769 void cut(Node *u, Node *v)
770 {
771 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
772 make_root(u);
773 access(v);
774 ah_domain_error_if(v->left != u or u->parent != v or u->right != nullptr)
775 << "edge does not exist";
776 v->left = nullptr;
777 u->parent = nullptr;
778 pull(v);
779 ++n_components;
780 }
781
796 [[nodiscard]] Node * lca(Node *u, Node *v)
797 {
798 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
799 if (u == v)
800 return u;
801 access(u);
802 Node *ans = access(v);
803 // verify they were connected: after access(v),
804 // u should be reachable via parent chain from v
805 // (access(u) already exposed the root-to-u path)
806 ah_domain_error_if(find_root(u) != find_root(v)) << "not connected";
807 return ans;
808 }
809
825 {
826 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
827 make_root(u);
828 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
829 access(v);
830 return v->agg;
831 }
832
841 [[nodiscard]] size_t path_size(Node *u, Node *v)
842 {
843 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
844 make_root(u);
845 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
846 access(v);
847 return v->sz;
848 }
849
856 [[nodiscard]] const T &get_val(Node *x)
857 {
858 ah_invalid_argument_if(x == nullptr) << "null handle";
859 access(x);
860 return x->val;
861 }
862
869 void set_val(Node *x, const T & val)
870 {
871 ah_invalid_argument_if(x == nullptr) << "null handle";
872 access(x);
873 x->val = val;
874 pull(x);
875 }
876
891 void path_apply(Node *u, Node *v, const typename LazyTag::tag_type & tag)
892 requires (not std::is_same_v<LazyTag, NoLazyTag<T>>)
893 {
894 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
895 make_root(u);
896 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
897 access(v);
898 v->val = LazyTag::apply(v->val, tag, 1);
899 v->agg = LazyTag::apply(v->agg, tag, v->sz);
900 v->lazy = LazyTag::compose(v->lazy, tag);
901 }
902
903 // ---- component & tree queries ----
904
909 {
910 return n_components;
911 }
912
917 [[nodiscard]] size_t tree_size(Node *x)
918 {
919 ah_invalid_argument_if(x == nullptr) << "null handle";
920 size_t count = 0;
921 for (size_t i = 0; i < nodes.size(); ++i)
922 if (connected(nodes(i), x))
923 ++count;
924 return count;
925 }
926
932 [[nodiscard]] size_t depth(Node *x)
933 {
934 ah_invalid_argument_if(x == nullptr) << "null handle";
935 access(x);
936 return x->left ? x->left->sz : 0;
937 }
938
945 {
946 ah_invalid_argument_if(x == nullptr) << "null handle";
947 access(x);
948 if (not x->left)
949 return nullptr;
950 Node *p = x->left;
951 push(p);
952 while (p->right)
953 {
954 p = p->right;
955 push(p);
956 }
957 splay(p);
958 return p;
959 }
960
961 // ---- iteration ----
962
964 template <typename Op>
965 void for_each_node(Op && op) const
966 {
967 for (size_t i = 0; i < nodes.size(); ++i)
968 op(nodes(i));
969 }
970
976 template <typename Op>
977 void for_each_on_path(Node *u, Node *v, Op && op)
978 {
979 ah_invalid_argument_if(u == nullptr or v == nullptr) << "null handle";
980 make_root(u);
981 ah_domain_error_if(find_root(v) != u) << "vertices not connected";
982
984 for (Node *cur = v; cur != nullptr; cur = parent(cur))
986
987 for (size_t i = rev_path.size(); i > 0; --i)
988 op(rev_path(i - 1));
989 }
990
991 // ---- batch construction ----
992
1004 Array<Node *> make_path(std::initializer_list<T> vals)
1005 {
1006 Array<Node *> result;
1007 Node *prev = nullptr;
1008 for (const auto & v: vals)
1009 {
1010 auto *nd = make_vertex(v);
1011 if (prev)
1012 link(prev, nd);
1013 prev = nd;
1014 result.append(nd);
1015 }
1016 return result;
1017 }
1018
1023 Array<Node *> make_vertices(std::initializer_list<T> vals)
1024 {
1025 Array<Node *> result;
1026 for (const auto & v: vals)
1027 result.append(make_vertex(v));
1028 return result;
1029 }
1030
1035 template <typename Container>
1036 void link_edges(const Container & edges)
1037 {
1038 for (const auto & [u, v]: edges)
1039 link(u, v);
1040 }
1041
1042 // ---- snapshot export ----
1043
1062 {
1063 ah_invalid_argument_if(root == nullptr) << "null handle";
1064 make_root(root);
1065
1066 // Collect all nodes in root's component and create Tree_Node copies
1068 using TN = Tree_Node<T>;
1069
1070 for_each_node([&](Node * nd)
1071 {
1072 if (connected(nd, root))
1073 lct_nodes.append(nd);
1074 });
1075
1077 tree_nodes.putn(lct_nodes.size());
1078 for (size_t i = 0; i < tree_nodes.size(); ++i)
1079 tree_nodes(i) = nullptr;
1080
1081 auto find_idx = [&](Node * nd) -> size_t
1082 {
1083 for (size_t i = 0; i < lct_nodes.size(); ++i)
1084 if (lct_nodes(i) == nd)
1085 return i;
1086 return lct_nodes.size();
1087 };
1088
1089 size_t root_idx = find_idx(root);
1090 struct DestroyTreeDeleter
1091 {
1092 void operator()(TN * nd) const noexcept
1093 {
1094 if (nd != nullptr)
1096 }
1097 };
1098 std::unique_ptr<TN, DestroyTreeDeleter> root_guard(new TN(root->val));
1100
1101 for (size_t i = 0; i < lct_nodes.size(); ++i)
1102 {
1103 if (i == root_idx)
1104 continue;
1105 Node * par = parent(lct_nodes(i));
1106 if (par != nullptr)
1107 {
1108 size_t pi = find_idx(par);
1109 std::unique_ptr<TN> child(new TN(lct_nodes(i)->val));
1110 tree_nodes(pi)->insert_rightmost_child(child.get());
1111 tree_nodes(i) = child.release();
1112 }
1113 }
1114
1115 return root_guard.release();
1116 }
1117 };
1118
1125} // namespace Aleph
1126
1127# endif /* TPL_LINK_CUT_TREE_H */
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:522
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
void destroy_tree(Tree &tree)
Definition avl-rb-rk.cc:520
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:139
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
Generic m-ary trees.
Concept for a lazy-tag policy used in deferred path updates.
Concept for a monoidal combiner over path values.
__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:4060
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.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
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.
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
Stack implementations backed by dynamic or fixed arrays.
Dynamic array container with automatic resizing.
General tree (n-ary tree) node.