15# include <absl/container/flat_hash_map.h>
16# include <absl/container/flat_hash_set.h>
18# include <unordered_map>
19# include <unordered_set>
43template<
class D,
class K,
size_t N = 16>
47 constexpr size_t operator()(D* d)
const noexcept {
return fe::hash(K::gid(d)); }
52 using Map = absl::flat_hash_map<D*, V, Hash>;
55 using Map = std::unordered_map<D*, V, Hash>;
64 constexpr Node(uint32_t
id) noexcept
71 constexpr Node(Node*
parent, D* def, uint32_t
id) noexcept
80 constexpr bool lt(D* d)
const noexcept {
return this->is_root() || K::tid(this->def) < K::tid(d); }
81 constexpr bool eq(D* d)
const noexcept {
return this->def == d; }
83 void dot(std::ostream& os) {
84 using namespace std::string_literals;
86 auto node2str = [](
const Node* n) {
87 return "n_"s + (n->def ? std::to_string(K::tid(n->def)) :
"root"s) +
"_"s + std::to_string(n->id);
90 std::print(os,
"{} [tooltip=\"gid: {}, min: {}\"];\n", node2str(
this), def ? K::gid(def) : 0, min);
92 for (
const auto& [_, child] : children)
93 std::print(os,
"{} -> {}\n", node2str(
this), node2str(child.get()));
94 for (
const auto& [_, child] : children)
100 constexpr bool is_root()
const noexcept {
return def ==
nullptr; }
103 [[nodiscard]]
bool contains(D* d)
noexcept {
104 size_t tid = K::tid(d), lo = min, hi = K::tid(def);
105 if (tid == lo || tid == hi)
return true;
119 Map<Arena::Ptr<Node>> children;
123 constexpr Data(
size_t size) noexcept
130 constexpr bool operator()(
const Data* d1,
const Data* d2)
const noexcept {
131 return d1->size == d2->size && std::equal(d1->begin(), d1->end(), d2->begin());
146 constexpr D** begin() noexcept {
return elems; }
147 constexpr D** end() noexcept {
return elems + size; }
148 constexpr D*
const* begin() const noexcept {
return elems; }
149 constexpr D*
const* end() const noexcept {
return elems + size; }
154 friend constexpr H AbslHashValue(H h,
const Data* d)
noexcept {
155 if (!d)
return H::combine(std::move(h), 0);
156 return H::combine_contiguous(std::move(h), d->elems, d->size);
162 using Pool = absl::flat_hash_set<const Data*, absl::Hash<const Data*>,
typename Data::Equal>;
164 using Pool = std::unordered_set<const Data*, typename Data::Hash, typename Data::Equal>;
170 enum class Tag : uintptr_t { Null, Uniq, Data, Node };
172 constexpr Set(
const Data* data) noexcept
173 : ptr_(uintptr_t(data) | uintptr_t(Tag::Data)) {}
174 constexpr Set(Node* node) noexcept
175 : ptr_(uintptr_t(node) | uintptr_t(Tag::Node)) {}
182 , ptr_(std::bit_cast<uintptr_t>(d)) {}
183 constexpr iterator(D*
const* elems) noexcept
185 , ptr_(std::bit_cast<uintptr_t>(elems)) {}
186 constexpr iterator(Node* node) noexcept
188 , ptr_(std::bit_cast<uintptr_t>(node)) {}
208 constexpr iterator& operator++() noexcept {
211 case Tag::Uniq:
return clear();
212 case Tag::Data:
return ptr_ = std::bit_cast<uintptr_t>(std::bit_cast<D* const*>(ptr_) + 1), *
this;
214 auto node = std::bit_cast<Node*>(ptr_);
219 ptr_ = std::bit_cast<uintptr_t>(node);
237 return this->tag_ == other.tag_ && this->ptr_ == other.ptr_;
245 case Tag::Uniq:
return std::bit_cast<D*>(ptr_);
246 case Tag::Data:
return *std::bit_cast<D* const*>(ptr_);
247 case Tag::Node:
return std::bit_cast<Node*>(ptr_)->def;
255 constexpr iterator&
clear() noexcept {
return *
this = {}; }
258 Tag tag_ = Tag::Null;
266 constexpr Set(
const Set&)
noexcept =
default;
267 constexpr Set(Set&&) noexcept = default;
268 constexpr Set() noexcept = default;
269 constexpr Set(D* d) noexcept
270 : ptr_(uintptr_t(d) | uintptr_t(Tag::Uniq)) {}
272 constexpr Set&
operator=(
const Set&)
noexcept =
default;
277 constexpr size_t size() const noexcept {
278 if (isa_uniq())
return 1;
279 if (
auto d = isa_data())
return d->size;
280 if (
auto n = isa_node())
return n->size;
285 constexpr bool empty() const noexcept {
286 assert(tag() != Tag::Node || !ptr<Node>()->is_root());
290 constexpr explicit operator bool() const noexcept {
return !
empty(); }
298 if (
auto u = isa_uniq())
return d == u;
300 if (
auto data = isa_data()) {
302 if (d == e)
return true;
306 if (
auto n = isa_node())
return n->contains(d);
313 if (this->
empty() || other.empty())
return false;
314 if (*
this == other)
return true;
316 auto u1 = this->isa_uniq();
317 auto u2 = other.isa_uniq();
318 if (u1)
return other.contains(u1);
321 auto d1 = this->isa_data();
322 auto d2 = other.isa_data();
324 for (
auto ai = d1->begin(), ae = d1->end(), bi = d2->begin(), be = d2->end(); ai != ae && bi != be;) {
325 if (*ai == *bi)
return true;
327 if (K::gid(*ai) < K::gid(*bi))
336 auto n1 = this->isa_node();
337 auto n2 = other.isa_node();
339 if (n1->min > K::tid(n2->def) || K::tid(n1->def) < n2->min)
return false;
340 if (n1->def == n2->def)
return true;
341 if (!n1->lca(n2)->is_root())
return true;
343 while (!n1->is_root() && !n2->is_root()) {
344 if (K::tid(n1->def) > K::tid(n2->def)) {
345 if (n1 = n1->find(n2->def); n2->def == n1->def)
return true;
348 if (n2 = n2->find(n1->def); n1->def == n2->def)
return true;
356 auto n = n1 ? n1 : n2;
357 for (
auto e : *(d1 ? d1 : d2))
358 if (n->contains(e))
return true;
367 if (
auto u = isa_uniq())
return {u};
368 if (
auto d = isa_data())
return {d->begin()};
369 if (
auto n = isa_node(); n && !n->is_root())
return {n};
374 if (
auto data = isa_data())
return iterator(data->end());
381 constexpr bool operator==(Set other)
const noexcept {
return this->ptr_ == other.ptr_; }
386 std::ostream&
stream(std::ostream& os)
const {
389 for (
auto d : *
this) {
391 if constexpr (
requires { K::stream(os, d); })
404 constexpr Tag tag() const noexcept {
return Tag(ptr_ & uintptr_t(0b11)); }
406 constexpr T* ptr() const noexcept {
407 return std::bit_cast<T*>(ptr_ & ~uintptr_t(0b11));
410 constexpr D* isa_uniq() const noexcept {
return tag() == Tag::Uniq ? ptr<D >() : nullptr; }
411 constexpr Data* isa_data() const noexcept {
return tag() == Tag::Data ? ptr<Data>() : nullptr; }
412 constexpr Node* isa_node() const noexcept {
return tag() == Tag::Node ? ptr<Node>() : nullptr; }
421 static_assert(std::forward_iterator<typename Set::iterator>);
422 static_assert(std::ranges::range<Set>);
429 : root_(make_node()) {}
443 template<std::random_access_iterator I>
445 std::sort(begin, end, gid_lt);
446 auto u = std::unique(begin, end);
447 auto size = size_t(std::distance(begin, u));
449 if (size == 0)
return {};
450 if (size == 1)
return {*begin};
453 auto [data, state] = allocate(size);
454 std::copy(begin, u, data->begin());
455 return unify(data, state);
458 return create_trie(begin, u);
462 template<std::ranges::input_range R>
464 auto v =
fe::Vector<D*>(std::ranges::begin(r), std::ranges::end(r));
465 return create(v.begin(), v.end());
469 [[nodiscard]]
Set create(std::initializer_list<D*> list) {
471 return create(v.begin(), v.end());
476 if (
auto u = s.isa_uniq()) {
477 if (d == u)
return {d};
479 auto [data, state] = allocate(2);
480 if (K::gid(d) < K::gid(u))
481 data->elems[0] = d, data->elems[1] = u;
483 data->elems[0] = u, data->elems[1] = d;
484 return unify(data, state);
487 if (
auto src = s.isa_data()) {
488 auto size = src->size;
492 if (d == e)
return s;
497 auto [scratch, state] = allocate(N + 1);
498 auto o = std::copy(src->begin(), src->end(), scratch->begin());
501 auto scratch_state = data_arena_.state();
503 auto res = create_trie(scratch->begin(), o);
504 assert(scratch_state == data_arena_.state() &&
"create_trie must only draw from node_arena_");
505 data_arena_.deallocate(state);
509 auto [dst, state] = allocate(size + 1);
510 auto i = std::upper_bound(src->begin(), src->end(), d, gid_lt);
511 auto o = std::copy(src->begin(), i, dst->begin());
513 std::copy(i, src->end(), o);
514 return unify(dst, state);
517 if (
auto n = s.isa_node()) {
518 if (n->contains(d))
return n;
527 if (s1.
empty() || s1 == s2)
return s2;
528 if (s2.
empty())
return s1;
530 if (
auto u = s1.isa_uniq())
return insert(s2, u);
531 if (
auto u = s2.isa_uniq())
return insert(s1, u);
533 auto d1 = s1.isa_data();
534 auto d2 = s2.isa_data();
541 auto [data, state] = allocate(d1->size + d2->size);
542 auto i1 = d1->begin(), e1 = d1->end();
543 auto i2 = d2->begin(), e2 = d2->end();
544 auto o = data->begin();
546 while (i1 != e1 && i2 != e2) {
547 auto g1 = K::gid(*i1);
548 auto g2 = K::gid(*i2);
556 o = std::copy(i1, e1, o);
557 o = std::copy(i2, e2, o);
559 auto size = size_t(o - data->begin());
562 auto scratch_state = data_arena_.state();
564 auto res = create_trie(data->begin(), o);
565 assert(scratch_state == data_arena_.state() &&
"create_trie must only draw from node_arena_");
566 data_arena_.deallocate(state);
570 auto excess = data->size - size;
572 return unify(data, state, excess);
575 auto n1 = s1.isa_node();
576 auto n2 = s2.isa_node();
578 if (n1->is_descendant_of(n2))
return n1;
579 if (n2->is_descendant_of(n1))
return n2;
580 return merge(n1, n2);
583 auto n = n1 ? n1 : n2;
584 for (
auto d : *(d1 ? d1 : d2))
585 if (!n->contains(d)) n =
insert(n, d);
591 if (
auto u = s.isa_uniq())
return d == u ?
Set() : s;
593 if (
auto data = s.isa_data()) {
594 auto b = data->begin(), e = data->end();
595 auto i = std::find(b, e, d);
596 if (i == e)
return s;
598 auto size = data->size - 1;
599 if (size == 0)
return {};
600 if (size == 1)
return {i == b ? b[1] : b[0]};
603 auto [new_data, state] = allocate(size);
604 std::copy(i + 1, e, std::copy(b, i, new_data->begin()));
605 return unify(new_data, state);
608 if (
auto n = s.isa_node()) {
609 if (!n->contains(d))
return n;
611 auto res =
erase(n, d);
612 if (res->size > N)
return res;
614 auto v = std::array<D*, N>();
616 for (
auto i = res; !i->is_root(); i = i->parent)
618 return create(v.begin(), o);
627 auto of = std::ofstream(
"trie.dot");
631 void dot(std::ostream& os)
const {
632 std::print(os,
"digraph {{\n");
633 std::print(os,
"ordering=out;\n");
634 std::print(os,
"node [shape=box,style=filled];\n");
636 std::print(os,
"}}\n");
642 swap(s1.data_arena_, s2.data_arena_);
643 swap(s1.node_arena_, s2.node_arena_);
644 swap(s1.pool_, s2.pool_);
645 swap(s1.root_, s2.root_);
646 swap(s1.tid_counter_, s2.tid_counter_);
647 swap(s1.id_counter_ , s2.id_counter_ );
652 D* set_tid(D* d)
noexcept {
653 assert(K::tid(d) == 0);
654 K::set_tid(d, tid_counter_++);
659 static constexpr bool gid_lt(D* d1, D* d2)
noexcept {
return K::gid(d1) < K::gid(d2); }
663 std::pair<Data*, Arena::State> allocate(
size_t size) {
664 auto bytes =
sizeof(Data) + size *
sizeof(D*);
665 auto state = data_arena_.state();
666 auto buff = data_arena_.allocate(bytes,
alignof(Data));
667 auto data =
new (buff) Data(size);
668 return {data, state};
674 assert(data->size != 0);
675 auto [i, ins] = pool_.emplace(data);
677 data_arena_.deallocate(excess *
sizeof(D*));
681 data_arena_.deallocate(state);
692 [[nodiscard]]
Set create_trie(I begin, I end) {
698 std::sort(begin, end,
699 [](D* d1, D* d2) {
return K::tid(d1) != 0 && (K::tid(d2) == 0 || K::tid(d1) < K::tid(d2)); });
702 for (
auto i = begin; i != end; ++i)
708 constexpr Node* root() const noexcept {
return root_.get(); }
709 Arena::Ptr<Node> make_node() {
return node_arena_.mk<Node>(id_counter_++); }
710 Arena::Ptr<Node> make_node(Node* parent, D* def) {
return node_arena_.mk<Node>(parent, def, id_counter_++); }
712 [[nodiscard]] Node* mount(Node* parent, D* d) {
713 assert(K::tid(d) != 0);
714 auto [i, ins] = parent->children.emplace(d,
nullptr);
715 if (ins) i->second = make_node(parent, d);
716 return i->second.get();
719 [[nodiscard]]
constexpr Node*
insert(Node* n, D* d)
noexcept {
720 if (K::tid(d) == 0)
return mount(n, set_tid(d));
721 if (n->def == d)
return n;
722 if (n->is_root() || K::tid(n->def) < K::tid(d))
return mount(n, d);
723 return mount(
insert(n->parent, d), n->def);
726 [[nodiscard]]
constexpr Node*
merge(Node* n, Node* m) {
727 if (n == m || m->is_root())
return n;
728 if (n->is_root())
return m;
729 auto nn = K::tid(n->def) < K::tid(m->def) ? n : n->parent;
730 auto mm = K::tid(n->def) > K::tid(m->def) ? m : m->parent;
731 return mount(
merge(nn, mm), K::tid(n->def) < K::tid(m->def) ? m->def : n->def);
734 [[nodiscard]] Node*
erase(Node* n, D* d) {
735 if (K::tid(d) > K::tid(n->def))
return n;
736 if (n->def == d)
return n->parent;
737 return mount(
erase(n->parent, d), n->def);
743 Arena::Ptr<Node> root_;
744 uint32_t tid_counter_ = 1;
745 uint32_t id_counter_ = 0;
std::pair< size_t, size_t > State
constexpr value_type operator*() const noexcept
constexpr value_type operator->() const noexcept
std::forward_iterator_tag iterator_category
constexpr iterator() noexcept=default
constexpr iterator operator++(int) noexcept
std::ptrdiff_t difference_type
constexpr bool operator==(iterator other) const noexcept
constexpr iterator & operator++() noexcept
constexpr iterator & clear() noexcept
constexpr iterator end() const noexcept
constexpr bool operator==(Set other) const noexcept
constexpr Set() noexcept=default
Null set.
constexpr iterator begin() const noexcept
constexpr Set & operator=(const Set &) noexcept=default
std::ostream & stream(std::ostream &os) const
bool has_intersection(Set other) const noexcept
Is ?.
friend std::ostream & operator<<(std::ostream &os, Set set)
constexpr size_t size() const noexcept
bool contains(D *d) const noexcept
Is ?.
constexpr bool empty() const noexcept
Is empty?
constexpr Set(const Set &) noexcept=default
constexpr Set(Set &&) noexcept=default
Set create(R &&r)
Create a Set wih all elements in r.
void dot(std::ostream &os) const
constexpr XTrie(XTrie &&other) noexcept
Set create(std::initializer_list< D * > list)
Create a Set wih all elements in list.
constexpr XTrie() noexcept
Set insert(Set s, D *d)
Yields .
Set create(I begin, I end)
Create a Set with all elements in [begin, end).
XTrie & operator=(const XTrie &)=delete
friend void swap(XTrie &s1, XTrie &s2) noexcept
Set erase(Set s, D *d)
Yields .
Set merge(Set s1, Set s2)
Yields .
constexpr XTrie(const XTrie &) noexcept=delete
This is an intrusive Link-Cut-Tree.
constexpr Node() noexcept=default
constexpr Node * find(const D *&k) noexcept
constexpr void link(Node *child) noexcept
Registers the edge this -> child in the aux tree.
bool contains(const D *&k) noexcept
constexpr size_t hash(size_t h) noexcept
Mixes h with murmur3 or splitmix64 - whichever matches sizeof(size_t).
Vector(I, I, A=A()) -> Vector< typename std::iterator_traits< I >::value_type, Default_Inlined_Size< typename std::iterator_traits< I >::value_type >, A >
constexpr size_t hash_begin() noexcept
Seeds a hash chain with the FNV-1 offset basis.
constexpr size_t hash_combine(size_t seed, T v) noexcept
Mixes v into seed word-wise, reusing the FNV-1 prime as multiplier.
constexpr bool operator()(const Data *d1, const Data *d2) const noexcept
constexpr size_t operator()(const Data *d) const noexcept