FE 0.16.1
Fast, Effecient FrontEnds
Loading...
Searching...
No Matches
xtrie.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4
5#include <algorithm>
6#include <array>
7#include <bit>
8#include <fstream>
9#include <iostream>
10#include <print>
11#include <ranges>
12#include <string>
13#include <utility>
14
15#include <ankerl/unordered_dense.h>
16
17#include "fe/arena.h"
18#include "fe/assert.h"
19#include "fe/hash.h"
20#include "fe/lct.h"
21#include "fe/vector.h"
22
23namespace fe {
24
25/// Hash-consed sets of `D*`.
26/// Small sets are sorted arrays, large ones paths in a trie; either way, equal sets are pointer-equal.
27/// This is an *IndexedTrie* as described [here](https://dl.acm.org/doi/10.1145/3808286).
28/// @p K is a *key* trait that grants access to the two `uint32_t`s XTrie needs on `D`:
29/// ```
30/// struct Key {
31/// static uint32_t gid(const D*) noexcept; ///< Unique id; orders and hashes the elements.
32/// static uint32_t tid(const D*) noexcept; ///< Trie id; `0` means "not assigned yet".
33/// static void set_tid(const D*, uint32_t) noexcept;
34/// static std::ostream& stream(std::ostream&, const D*); ///< Optional; defaults to Key::gid.
35/// };
36/// ```
37/// @p N is the maximum size of an array set; bigger sets live in the trie.
38template<class D, class K, size_t N = 16>
39class XTrie {
40private:
41 struct Hash {
42 using is_avalanching = void;
43
44 size_t operator()(D* d) const noexcept { return ankerl::unordered_dense::hash<uint32_t>()(K::gid(d)); }
45 };
46
47 template<class V>
48 using Map = ankerl::unordered_dense::map<D*, V, Hash>;
49
50 /// Trie Node.
51 class Node : public lct::Node<Node, D*> {
52 private:
53 using LCT = lct::Node<Node, D*>;
54
55 public:
56 constexpr Node(uint32_t id) noexcept
57 : parent(nullptr)
58 , def(nullptr)
59 , size(0)
60 , min(uint32_t(-1))
61 , id(id) {}
62
63 constexpr Node(Node* parent, D* def, uint32_t id) noexcept
64 : parent(parent)
65 , def(def)
66 , size(parent->size + 1)
67 , min(parent->def ? parent->min : K::tid(def))
68 , id(id) {
69 parent->link(this);
70 }
71
72 constexpr bool lt(D* d) const noexcept { return this->is_root() || K::tid(this->def) < K::tid(d); }
73 constexpr bool eq(D* d) const noexcept { return this->def == d; }
74
75 void dot(std::ostream& os) {
76 using namespace std::string_literals;
77
78 auto node2str = [](const Node* n) {
79 return "n_"s + (n->def ? std::to_string(K::tid(n->def)) : "root"s) + "_"s + std::to_string(n->id);
80 };
81
82 std::print(os, "{} [tooltip=\"gid: {}, min: {}\"];\n", node2str(this), def ? K::gid(def) : 0, min);
83
84 for (const auto& [_, child] : children)
85 std::print(os, "{} -> {}\n", node2str(this), node2str(child.get()));
86 for (const auto& [_, child] : children)
87 child->dot(os);
88 }
89
90 ///@name Getters
91 ///@{
92 constexpr bool is_root() const noexcept { return def == nullptr; }
93
94 /// All tids on the path from the trie root to `this` live within `[Node::min, K::tid(Node::def)]`.
95 [[nodiscard]] bool contains(D* d) noexcept {
96 size_t tid = K::tid(d), lo = min, hi = K::tid(def);
97 if (tid == lo || tid == hi) return true;
98 return lo < tid && tid < hi && LCT::contains(d);
99 }
100
101 using LCT::find;
102 ///@}
103
104 Node* const parent;
105 D* const def;
106 const size_t size;
107 /// @note `uint32_t` (not `size_t`) so that it packs into the same 8 bytes as Node::id;
108 /// it only ever holds a Key::tid, which is a `uint32_t` itself.
109 const uint32_t min;
110 uint32_t const id;
111 Map<Arena::Ptr<Node>> children;
112 };
113
114 struct Data {
115 constexpr Data(size_t size) noexcept
116 : size(size) {}
117
118 size_t size;
119 D* elems[];
120
121 struct Equal {
122 constexpr bool operator()(const Data* d1, const Data* d2) const noexcept {
123 return d1->size == d2->size && std::equal(d1->begin(), d1->end(), d2->begin());
124 }
125 };
126
127 struct Hash {
128 using is_avalanching = void;
129
130 size_t operator()(const Data* d) const noexcept {
131 uint64_t h = 0;
132 for (auto e : *d)
133 h = hash_combine(h, std::bit_cast<uintptr_t>(e));
134 return h;
135 }
136 };
137
138 /// @name Iterators
139 ///@{
140 constexpr D** begin() noexcept { return elems; }
141 constexpr D** end() noexcept { return elems + size; }
142 constexpr D* const* begin() const noexcept { return elems; }
143 constexpr D* const* end() const noexcept { return elems + size; }
144 ///@}
145 };
146
147 using Pool = ankerl::unordered_dense::set<const Data*, typename Data::Hash, typename Data::Equal>;
148
149public:
150 class Set {
151 private:
152 enum class Tag : uintptr_t { Null, Uniq, Data, Node };
153
154 constexpr Set(const Data* data) noexcept
155 : ptr_(uintptr_t(data) | uintptr_t(Tag::Data)) {} ///< Data Set.
156 constexpr Set(Node* node) noexcept
157 : ptr_(uintptr_t(node) | uintptr_t(Tag::Node)) {} ///< Node set.
158
159 public:
160 class iterator {
161 private:
162 constexpr iterator(D* d) noexcept
163 : tag_(Tag::Uniq)
164 , ptr_(std::bit_cast<uintptr_t>(d)) {}
165 constexpr iterator(D* const* elems) noexcept
166 : tag_(Tag::Data)
167 , ptr_(std::bit_cast<uintptr_t>(elems)) {}
168 constexpr iterator(Node* node) noexcept
169 : tag_(Tag::Node)
170 , ptr_(std::bit_cast<uintptr_t>(node)) {}
171
172 public:
173 /// @name Iterator Properties
174 ///@{
175 using iterator_category = std::forward_iterator_tag;
176 using difference_type = std::ptrdiff_t;
177 using value_type = D*;
178 using pointer = D* const*;
179 using reference = D* const&;
180 ///@}
181
182 /// @name Construction
183 ///@{
184 constexpr iterator() noexcept = default;
185 ///@}
186
187 /// @name Increment
188 /// @note These operations only change the *view* of this Set; the Set itself is **not** modified.
189 ///@{
190 constexpr iterator& operator++() noexcept {
191 // clang-format off
192 switch (tag_) {
193 case Tag::Uniq: return clear();
194 case Tag::Data: return ptr_ = std::bit_cast<uintptr_t>(std::bit_cast<D* const*>(ptr_) + 1), *this;
195 case Tag::Node: {
196 auto node = std::bit_cast<Node*>(ptr_);
197 node = node->parent;
198 if (node->is_root())
199 clear();
200 else
201 ptr_ = std::bit_cast<uintptr_t>(node);
202 return *this;
203 }
204 default: std::unreachable();
205 }
206 // clang-format on
207 }
208
209 constexpr iterator operator++(int) noexcept {
210 auto res = *this;
211 this->operator++();
212 return res;
213 }
214 ///@}
215
216 /// @name Comparisons
217 ///@{
218 constexpr bool operator==(iterator other) const noexcept {
219 return this->tag_ == other.tag_ && this->ptr_ == other.ptr_;
220 }
221 ///@}
222
223 /// @name Dereference
224 ///@{
225 constexpr value_type operator*() const noexcept {
226 switch (tag_) {
227 case Tag::Uniq: return std::bit_cast<D*>(ptr_);
228 case Tag::Data: return *std::bit_cast<D* const*>(ptr_);
229 case Tag::Node: return std::bit_cast<Node*>(ptr_)->def;
230 default: std::unreachable();
231 }
232 }
233
234 constexpr value_type operator->() const noexcept { return this->operator*(); }
235 ///@}
236
237 constexpr iterator& clear() noexcept { return *this = {}; }
238
239 private:
240 Tag tag_ = Tag::Null;
241 uintptr_t ptr_ = 0;
242
243 friend class Set;
244 };
245
246 /// @name Construction
247 ///@{
248 constexpr Set() noexcept = default; ///< Null set
249 constexpr Set(D* d) noexcept
250 : ptr_(uintptr_t(d) | uintptr_t(Tag::Uniq)) {} ///< Uniq set.
251 ///@}
252
253 /// @name Getters
254 ///@{
255 constexpr size_t size() const noexcept {
256 if (isa_uniq()) return 1;
257 if (auto d = isa_data()) return d->size;
258 if (auto n = isa_node()) return n->size;
259 return 0; // empty
260 }
261
262 /// Is empty?
263 constexpr bool empty() const noexcept {
264 assert(tag() != Tag::Node || !ptr<Node>()->is_root());
265 return ptr_ == 0;
266 }
267
268 constexpr explicit operator bool() const noexcept { return !empty(); } ///< Not empty?
269 ///@}
270
271 /// @name Check Membership
272 ///@{
273
274 /// Is @f$d \in this@f$?.
275 bool contains(D* d) const noexcept {
276 if (auto u = isa_uniq()) return d == u;
277
278 if (auto data = isa_data()) {
279 for (auto e : *data)
280 if (d == e) return true;
281 return false;
282 }
283
284 if (auto n = isa_node()) return n->contains(d);
285
286 return false;
287 }
288
289 /// Is @f$this \cap other \neq \emptyset@f$?.
290 [[nodiscard]] bool has_intersection(Set other) const noexcept {
291 if (this->empty() || other.empty()) return false;
292 if (*this == other) return true;
293
294 auto u1 = this->isa_uniq();
295 auto u2 = other.isa_uniq();
296 if (u1) return other.contains(u1);
297 if (u2) return this->contains(u2);
298
299 auto d1 = this->isa_data();
300 auto d2 = other.isa_data();
301 if (d1 && d2) {
302 for (auto ai = d1->begin(), ae = d1->end(), bi = d2->begin(), be = d2->end(); ai != ae && bi != be;) {
303 if (*ai == *bi) return true;
304
305 if (K::gid(*ai) < K::gid(*bi))
306 ++ai;
307 else
308 ++bi;
309 }
310
311 return false;
312 }
313
314 auto n1 = this->isa_node();
315 auto n2 = other.isa_node();
316 if (n1 && n2) {
317 if (n1->min > K::tid(n2->def) || K::tid(n1->def) < n2->min) return false;
318 if (n1->def == n2->def) return true;
319 if (!n1->lca(n2)->is_root()) return true;
320
321 while (!n1->is_root() && !n2->is_root()) {
322 if (K::tid(n1->def) > K::tid(n2->def)) {
323 if (n1 = n1->find(n2->def); n2->def == n1->def) return true;
324 n1 = n1->parent;
325 } else {
326 if (n2 = n2->find(n1->def); n1->def == n2->def) return true;
327 n2 = n2->parent;
328 }
329 }
330
331 return false;
332 }
333
334 auto n = n1 ? n1 : n2;
335 for (auto e : *(d1 ? d1 : d2))
336 if (n->contains(e)) return true;
337
338 return false;
339 }
340 ///@}
341
342 /// @name Iterators
343 ///@{
344 constexpr iterator begin() const noexcept {
345 if (auto u = isa_uniq()) return {u};
346 if (auto d = isa_data()) return {d->begin()};
347 if (auto n = isa_node(); n && !n->is_root()) return {n};
348 return {};
349 }
350
351 constexpr iterator end() const noexcept {
352 if (auto data = isa_data()) return iterator(data->end());
353 return {};
354 }
355 ///@}
356
357 /// @name Comparisons
358 ///@{
359 constexpr bool operator==(Set other) const noexcept { return this->ptr_ == other.ptr_; }
360 ///@}
361
362 /// @name Output
363 ///@{
364 std::ostream& stream(std::ostream& os) const {
365 os << '{';
366 auto sep = "";
367 for (auto d : *this) {
368 os << sep;
369 if constexpr (requires { K::stream(os, d); })
370 K::stream(os, d);
371 else
372 os << K::gid(d);
373 sep = ", ";
374 }
375 return os << '}';
376 }
377
378 void dump() const { stream(std::cout) << std::endl; }
379 ///@}
380
381 private:
382 constexpr Tag tag() const noexcept { return Tag(ptr_ & uintptr_t(0b11)); }
383 template<class T>
384 constexpr T* ptr() const noexcept {
385 return std::bit_cast<T*>(ptr_ & ~uintptr_t(0b11));
386 }
387 // clang-format off
388 constexpr D* isa_uniq() const noexcept { return tag() == Tag::Uniq ? ptr<D >() : nullptr; }
389 constexpr Data* isa_data() const noexcept { return tag() == Tag::Data ? ptr<Data>() : nullptr; }
390 constexpr Node* isa_node() const noexcept { return tag() == Tag::Node ? ptr<Node>() : nullptr; }
391 // clang-format on
392
393 uintptr_t ptr_ = 0;
394
395 friend class XTrie;
396 friend std::ostream& operator<<(std::ostream& os, Set set) { return set.stream(os); }
397 };
398
399 static_assert(std::forward_iterator<typename Set::iterator>);
400 static_assert(std::ranges::range<Set>);
401
402 /// @name Construction
403 ///@{
404 XTrie& operator=(const XTrie&) = delete;
405
407 : root_(make_node()) {}
408 XTrie(const XTrie&) = delete;
409 XTrie(XTrie&& other)
410 : XTrie() {
411 swap(*this, other);
412 }
413 ///@}
414
415 /// @name Set Operations
416 /// @note These operations do **not** modify the input set(s); they create a **new** Set.
417 ///@{
418
419 /// Create a Set with all elements in `[begin, end)`.
420 /// @attention Reorders `[begin, end)` in place.
421 template<std::random_access_iterator I>
422 [[nodiscard]] Set create(I begin, I end) {
423 std::sort(begin, end, gid_lt);
424 auto u = std::unique(begin, end);
425 auto size = size_t(std::distance(begin, u));
426
427 if (size == 0) return {};
428 if (size == 1) return {*begin};
429
430 if (size <= N) {
431 auto [data, state] = allocate(size);
432 std::copy(begin, u, data->begin());
433 return unify(data, state);
434 }
435
436 return create_trie(begin, u);
437 }
438
439 /// Create a Set wih all elements in @p r.
440 template<std::ranges::input_range R>
441 [[nodiscard]] Set create(R&& r) {
442 auto v = fe::Vector<D*>(std::ranges::begin(r), std::ranges::end(r));
443 return create(v.begin(), v.end());
444 }
445
446 /// Create a Set wih all elements in @p list.
447 [[nodiscard]] Set create(std::initializer_list<D*> list) {
448 auto v = fe::Vector<D*>(list);
449 return create(v.begin(), v.end());
450 }
451
452 /// Yields @f$s \cup \{d\}@f$.
453 [[nodiscard]] Set insert(Set s, D* d) {
454 if (auto u = s.isa_uniq()) {
455 if (d == u) return {d};
456
457 auto [data, state] = allocate(2);
458 if (K::gid(d) < K::gid(u))
459 data->elems[0] = d, data->elems[1] = u;
460 else
461 data->elems[0] = u, data->elems[1] = d;
462 return unify(data, state);
463 }
464
465 if (auto src = s.isa_data()) {
466 auto size = src->size;
467 assert(size <= N);
468
469 for (auto e : *src)
470 if (d == e) return s; // already here
471
472 if (size == N) { // one more element is too much for a Data set: switch over to the trie
473 // Use the data arena as scratch space for the N + 1 elements; since create_trie only draws from
474 // node_arena_, it is ours to throw away again afterwards.
475 auto [scratch, state] = allocate(N + 1);
476 auto o = std::copy(src->begin(), src->end(), scratch->begin());
477 *o++ = d;
478#ifndef NDEBUG
479 auto scratch_state = data_arena_.state();
480#endif
481 auto res = create_trie(scratch->begin(), o);
482 assert(scratch_state == data_arena_.state() && "create_trie must only draw from node_arena_");
483 data_arena_.deallocate(state);
484 return res;
485 }
486
487 auto [dst, state] = allocate(size + 1);
488 auto i = std::upper_bound(src->begin(), src->end(), d, gid_lt); // where d belongs
489 auto o = std::copy(src->begin(), i, dst->begin());
490 *o++ = d;
491 std::copy(i, src->end(), o);
492 return unify(dst, state);
493 }
494
495 if (auto n = s.isa_node()) {
496 if (n->contains(d)) return n;
497 return insert(n, d);
498 }
499
500 return {d};
501 }
502
503 /// Yields @f$s_1 \cup s_2@f$.
504 [[nodiscard]] Set merge(Set s1, Set s2) {
505 if (s1.empty() || s1 == s2) return s2;
506 if (s2.empty()) return s1;
507
508 if (auto u = s1.isa_uniq()) return insert(s2, u);
509 if (auto u = s2.isa_uniq()) return insert(s1, u);
510
511 auto d1 = s1.isa_data();
512 auto d2 = s2.isa_data();
513 if (d1 && d2) {
514 // Both operands are ordered by gid and duplicate-free, so a linear merge yields the union directly -
515 // no sort, no std::unique pass.
516 // Its final size is only known afterwards, so allocate the upper bound `d1->size + d2->size` and merge
517 // straight into it; every dropped duplicate leaves one slot of excess at the tail that unify releases
518 // again.
519 auto [data, state] = allocate(d1->size + d2->size);
520 auto i1 = d1->begin(), e1 = d1->end();
521 auto i2 = d2->begin(), e2 = d2->end();
522 auto o = data->begin();
523
524 while (i1 != e1 && i2 != e2) {
525 auto g1 = K::gid(*i1);
526 auto g2 = K::gid(*i2);
527 if (g1 < g2)
528 *o++ = *i1++;
529 else if (g2 < g1)
530 *o++ = *i2++;
531 else
532 *o++ = *i1++, ++i2; // drop the duplicate
533 }
534 o = std::copy(i1, e1, o);
535 o = std::copy(i2, e2, o);
536
537 auto size = size_t(o - data->begin());
538 if (size > N) { // too big for a Data set: switch over to the trie
539#ifndef NDEBUG
540 auto scratch_state = data_arena_.state();
541#endif
542 auto res = create_trie(data->begin(), o); // only draws from node_arena_ ...
543 assert(scratch_state == data_arena_.state() && "create_trie must only draw from node_arena_");
544 data_arena_.deallocate(state); // ... so data is ours to throw away again
545 return res;
546 }
547
548 auto excess = data->size - size; // data->size is still the upper bound we allocated
549 data->size = size;
550 return unify(data, state, excess);
551 }
552
553 auto n1 = s1.isa_node();
554 auto n2 = s2.isa_node();
555 if (n1 && n2) {
556 if (n1->is_descendant_of(n2)) return n1;
557 if (n2->is_descendant_of(n1)) return n2;
558 return merge(n1, n2);
559 }
560
561 auto n = n1 ? n1 : n2;
562 for (auto d : *(d1 ? d1 : d2))
563 if (!n->contains(d)) n = insert(n, d);
564 return n;
565 }
566
567 /// Yields @f$s \setminus \{d\}@f$.
568 [[nodiscard]] Set erase(Set s, D* d) {
569 if (auto u = s.isa_uniq()) return d == u ? Set() : s;
570
571 if (auto data = s.isa_data()) {
572 auto b = data->begin(), e = data->end();
573 auto i = std::find(b, e, d);
574 if (i == e) return s; // not in here
575
576 auto size = data->size - 1;
577 if (size == 0) return {};
578 if (size == 1) return {i == b ? b[1] : b[0]};
579
580 assert(size <= N);
581 auto [new_data, state] = allocate(size);
582 std::copy(i + 1, e, std::copy(b, i, new_data->begin())); // copy over, skip i
583 return unify(new_data, state);
584 }
585
586 if (auto n = s.isa_node()) {
587 if (!n->contains(d)) return n;
588
589 auto res = erase(n, d);
590 if (res->size > N) return res;
591
592 auto v = std::array<D*, N>();
593 auto o = v.begin();
594 for (auto i = res; !i->is_root(); i = i->parent)
595 *o++ = i->def;
596 return create(v.begin(), o);
597 }
598
599 return {};
600 }
601 ///@}
602
603 /// @name DOT output
604 void dot() {
605 auto of = std::ofstream("trie.dot");
606 dot(of);
607 }
608
609 void dot(std::ostream& os) const {
610 std::print(os, "digraph {{\n");
611 std::print(os, "ordering=out;\n");
612 std::print(os, "node [shape=box,style=filled];\n");
613 root()->dot(os);
614 std::print(os, "}}\n");
615 }
616
617 friend void swap(XTrie& s1, XTrie& s2) noexcept {
618 using std::swap;
619 // clang-format off
620 swap(s1.data_arena_, s2.data_arena_);
621 swap(s1.node_arena_, s2.node_arena_);
622 swap(s1.pool_, s2.pool_);
623 swap(s1.root_, s2.root_);
624 swap(s1.tid_counter_, s2.tid_counter_);
625 swap(s1.id_counter_ , s2.id_counter_ );
626 // clang-format on
627 }
628
629private:
630 D* set_tid(D* d) noexcept {
631 assert(K::tid(d) == 0);
632 K::set_tid(d, tid_counter_++);
633 return d;
634 }
635
636 /// Data sets are ordered by Key::gid.
637 static constexpr bool gid_lt(D* d1, D* d2) noexcept { return K::gid(d1) < K::gid(d2); }
638
639 /// @name Data helpers
640 ///@{
641 std::pair<Data*, Arena::State> allocate(size_t size) {
642 auto bytes = sizeof(Data) + size * sizeof(D*);
643 auto state = data_arena_.state();
644 auto buff = data_arena_.allocate(bytes, alignof(Data));
645 auto data = new (buff) Data(size);
646 return {data, state};
647 }
648
649 /// Hash-conses @p data; rolls the arena back to @p state, if an equal Data is already pooled.
650 /// Pass the number of trailing elements allocated but not used as @p excess to release them again.
651 Set unify(Data* data, Arena::State state, size_t excess = 0) {
652 assert(data->size != 0);
653 auto [i, ins] = pool_.emplace(data);
654 if (ins) {
655 data_arena_.deallocate(excess * sizeof(D*)); // data is the arena's most recent allocation
656 return Set(data);
657 }
658
659 data_arena_.deallocate(state);
660 return Set(*i);
661 }
662 ///@}
663
664 /// Builds a trie Set from the *unique* elements in `[begin, end)`; reorders them in place.
665 /// @attention Must only ever draw from node_arena_.
666 /// Two callers use data_arena_ as scratch space and rewind it afterwards; drawing from data_arena_ in here
667 /// would pop pages that are still live - possibly including Data that pool_ still points at.
668 /// Both call sites assert this.
669 template<class I>
670 [[nodiscard]] Set create_trie(I begin, I end) {
671 // Sorting is a performance optimization, not a correctness requirement:
672 // insert() restores the canonical increasing-tid path from any insertion order, but only an element whose
673 // tid exceeds the current tip mounts in O(1) - otherwise it walks up and re-mounts the suffix in O(depth).
674 // Feeding the elements in ascending tid order therefore turns O(k * depth) into O(k) mounts.
675 // A tid of 0 goes last because set_tid hands out the next - and hence maximal - counter value.
676 std::sort(begin, end,
677 [](D* d1, D* d2) { return K::tid(d1) != 0 && (K::tid(d2) == 0 || K::tid(d1) < K::tid(d2)); });
678
679 auto res = root();
680 for (auto i = begin; i != end; ++i)
681 res = insert(res, *i);
682 return res;
683 }
684
685 // Trie helpers
686 Node* root() const noexcept { return root_.get(); }
687 Arena::Ptr<Node> make_node() { return node_arena_.mk<Node>(id_counter_++); }
688 Arena::Ptr<Node> make_node(Node* parent, D* def) { return node_arena_.mk<Node>(parent, def, id_counter_++); }
689
690 [[nodiscard]] Node* mount(Node* parent, D* d) {
691 assert(K::tid(d) != 0);
692 auto [i, ins] = parent->children.emplace(d, nullptr);
693 if (ins) i->second = make_node(parent, d);
694 return i->second.get();
695 }
696
697 [[nodiscard]] Node* insert(Node* n, D* d) {
698 if (K::tid(d) == 0) return mount(n, set_tid(d));
699 if (n->def == d) return n;
700 if (n->is_root() || K::tid(n->def) < K::tid(d)) return mount(n, d);
701 return mount(insert(n->parent, d), n->def);
702 }
703
704 [[nodiscard]] Node* merge(Node* n, Node* m) {
705 if (n == m || m->is_root()) return n;
706 if (n->is_root()) return m;
707 auto nn = K::tid(n->def) < K::tid(m->def) ? n : n->parent;
708 auto mm = K::tid(n->def) > K::tid(m->def) ? m : m->parent;
709 return mount(merge(nn, mm), K::tid(n->def) < K::tid(m->def) ? m->def : n->def);
710 }
711
712 [[nodiscard]] Node* erase(Node* n, D* d) {
713 if (K::tid(d) > K::tid(n->def)) return n;
714 if (n->def == d) return n->parent;
715 return mount(erase(n->parent, d), n->def);
716 }
717
718 Arena node_arena_;
719 Arena data_arena_;
720 Pool pool_;
721 Arena::Ptr<Node> root_;
722 uint32_t tid_counter_ = 1;
723 uint32_t id_counter_ = 0;
724};
725
726} // namespace fe
std::pair< size_t, size_t > State
Definition arena.h:115
This is a thin wrapper for ankerl::svector<T, N, A> which is a drop-in replacement for std::vector<T,...
Definition vector.h:25
constexpr value_type operator*() const noexcept
Definition xtrie.h:225
constexpr value_type operator->() const noexcept
Definition xtrie.h:234
std::forward_iterator_tag iterator_category
Definition xtrie.h:175
constexpr iterator() noexcept=default
constexpr iterator operator++(int) noexcept
Definition xtrie.h:209
friend class Set
Definition xtrie.h:243
std::ptrdiff_t difference_type
Definition xtrie.h:176
constexpr bool operator==(iterator other) const noexcept
Definition xtrie.h:218
constexpr iterator & operator++() noexcept
Definition xtrie.h:190
constexpr iterator & clear() noexcept
Definition xtrie.h:237
D *const & reference
Definition xtrie.h:179
constexpr iterator end() const noexcept
Definition xtrie.h:351
constexpr bool operator==(Set other) const noexcept
Definition xtrie.h:359
void dump() const
Definition xtrie.h:378
constexpr Set() noexcept=default
Null set.
constexpr iterator begin() const noexcept
Definition xtrie.h:344
std::ostream & stream(std::ostream &os) const
Definition xtrie.h:364
bool has_intersection(Set other) const noexcept
Is ?.
Definition xtrie.h:290
friend std::ostream & operator<<(std::ostream &os, Set set)
Definition xtrie.h:396
constexpr size_t size() const noexcept
Definition xtrie.h:255
friend class XTrie
Definition xtrie.h:395
bool contains(D *d) const noexcept
Is ?.
Definition xtrie.h:275
constexpr bool empty() const noexcept
Is empty?
Definition xtrie.h:263
Set create(R &&r)
Create a Set wih all elements in r.
Definition xtrie.h:441
XTrie(const XTrie &)=delete
XTrie()
Definition xtrie.h:406
void dot()
Definition xtrie.h:604
XTrie(XTrie &&other)
Definition xtrie.h:409
void dot(std::ostream &os) const
Definition xtrie.h:609
Set create(std::initializer_list< D * > list)
Create a Set wih all elements in list.
Definition xtrie.h:447
Set insert(Set s, D *d)
Yields .
Definition xtrie.h:453
Set create(I begin, I end)
Create a Set with all elements in [begin, end).
Definition xtrie.h:422
XTrie & operator=(const XTrie &)=delete
friend void swap(XTrie &s1, XTrie &s2) noexcept
Definition xtrie.h:617
Set erase(Set s, D *d)
Yields .
Definition xtrie.h:568
Set merge(Set s1, Set s2)
Yields .
Definition xtrie.h:504
This is an intrusive Link-Cut-Tree.
Definition lct.h:21
constexpr Node() noexcept=default
constexpr Node * find(const D *&k) noexcept
Definition lct.h:40
constexpr bool contains(const D *&k) noexcept
Definition lct.h:57
constexpr void link(Node *child) noexcept
Registers the edge this -> child in the aux tree.
Definition lct.h:137
Definition algo.h:17
constexpr size_t hash_combine(size_t seed, T v) noexcept
Folds v into seed like ankerl::unordered_dense hashes a tuple: one 128-bit multiply,...
Definition hash.h:26
constexpr bool operator()(const Data *d1, const Data *d2) const noexcept
Definition xtrie.h:122
size_t operator()(const Data *d) const noexcept
Definition xtrie.h:130