Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_patricia_trie.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
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
71#ifndef TPL_PATRICIA_TRIE_H
72#define TPL_PATRICIA_TRIE_H
73
74#include <array>
75#include <cstddef>
76#include <limits>
77#include <memory>
78#include <optional>
79#include <type_traits>
80#include <utility>
81
82#include <tpl_array.H>
83
84namespace Aleph
85{
86
87namespace patricia_trie_detail
88{
89
98template <typename UInt>
100{
102 static constexpr size_t bit_width = std::numeric_limits<UInt>::digits;
103
113 [[nodiscard]] static bool bit_at(const UInt key,
114 const size_t bit_index) noexcept
115 {
116 return ((key >> (bit_width - 1 - bit_index)) & UInt{1}) != UInt{0};
117 }
118
127 [[nodiscard]] static size_t first_differing_bit(const UInt lhs,
128 const UInt rhs) noexcept
129 {
130 const UInt diff = lhs ^ rhs;
131 for (size_t i = 0; i < bit_width; ++i)
132 if (bit_at(diff, i))
133 return i;
134 return bit_width;
135 }
136
146 template <typename Node>
147 [[nodiscard]] static const Node * leaf_for(const Node * node,
148 const UInt key) noexcept
149 {
150 while (node != nullptr and not node->leaf)
151 node = node->child[bit_at(key, node->bit_index) ? 1 : 0].get();
152 return node;
153 }
154
164 template <typename Node>
165 [[nodiscard]] static Node * leaf_for(Node * node, const UInt key) noexcept
166 {
167 return const_cast<Node *>(leaf_for(static_cast<const Node *>(node), key));
168 }
169
178 template <typename Node>
179 static void collect_keys(const Node * node, Array<UInt> & out)
180 {
181 if (node == nullptr)
182 return;
183 if (node->leaf)
184 {
185 out.append(node->key);
186 return;
187 }
188 collect_keys(node->child[0].get(), out);
189 collect_keys(node->child[1].get(), out);
190 }
191
202 template <typename Node>
203 [[nodiscard]] static bool erase_key(std::unique_ptr<Node> & root,
204 size_t & size,
205 const UInt key) noexcept
206 {
207 if (root == nullptr)
208 return false;
209
210 if (root->leaf)
211 {
212 if (root->key != key)
213 return false;
214 root.reset();
215 size = 0;
216 return true;
217 }
218
219 std::unique_ptr<Node> * parent_link = nullptr;
220 std::unique_ptr<Node> * link = &root;
221 while ((*link)->leaf == false)
222 {
223 parent_link = link;
224 link = &(*link)->child[bit_at(key, (*link)->bit_index) ? 1 : 0];
225 }
226
227 if ((*link)->key != key)
228 return false;
229
230 Node * parent = parent_link->get();
231 const size_t side = bit_at(key, parent->bit_index) ? 1 : 0;
232 *parent_link = std::move(parent->child[1 - side]);
233 --size;
234 return true;
235 }
236
247 template <typename Node>
248 [[nodiscard]] static bool subtree_matches(const Node * node,
249 const size_t bit_index,
250 const bool expected) noexcept
251 {
252 if (node == nullptr)
253 return false;
254 if (node->leaf)
255 return bit_at(node->key, bit_index) == expected;
256 return subtree_matches(node->child[0].get(), bit_index, expected) and
257 subtree_matches(node->child[1].get(), bit_index, expected);
258 }
259
271 template <typename Node>
272 [[nodiscard]] static bool check_invariants_rec(const Node * node,
273 const size_t parent_bit,
274 const bool has_parent,
275 size_t & counted) noexcept
276 {
277 if (node == nullptr)
278 return true;
279
280 if (node->leaf)
281 {
282 if constexpr (requires { node->value.has_value(); })
283 if (not node->value.has_value())
284 return false;
285 ++counted;
286 return true;
287 }
288
289 if constexpr (requires { node->value.has_value(); })
290 if (node->value.has_value())
291 return false;
292 if (node->bit_index >= bit_width)
293 return false;
294 if (has_parent and node->bit_index <= parent_bit)
295 return false;
296 if (node->child[0] == nullptr or node->child[1] == nullptr)
297 return false;
298 if (not subtree_matches(node->child[0].get(), node->bit_index, false))
299 return false;
300 if (not subtree_matches(node->child[1].get(), node->bit_index, true))
301 return false;
302
303 return check_invariants_rec(node->child[0].get(), node->bit_index, true,
304 counted) and
305 check_invariants_rec(node->child[1].get(), node->bit_index, true,
306 counted);
307 }
308};
309
310} // namespace patricia_trie_detail
311
321template <typename UInt>
322requires (std::is_integral_v<UInt> and std::is_unsigned_v<UInt> and
323 not std::is_same_v<UInt, bool>)
325{
326public:
328 using Key = UInt;
329
331 static constexpr size_t bit_width = std::numeric_limits<Key>::digits;
332
333private:
334 struct Node
335 {
336 bool leaf = true;
337 Key key = 0;
338 size_t bit_index = 0;
339 std::array<std::unique_ptr<Node>, 2> child{};
340
341 explicit Node(const Key k) noexcept : leaf(true), key(k) {}
342 explicit Node(const size_t bit) noexcept : leaf(false), bit_index(bit) {}
343 };
344
345 std::unique_ptr<Node> root_;
346 size_t size_ = 0;
348
349 [[nodiscard]] static std::unique_ptr<Node> clone_node(const Node * src)
350 {
351 if (src == nullptr)
352 return nullptr;
353 auto dst = src->leaf ? std::make_unique<Node>(src->key)
354 : std::make_unique<Node>(src->bit_index);
355 if (not src->leaf)
356 {
357 dst->child[0] = clone_node(src->child[0].get());
358 dst->child[1] = clone_node(src->child[1].get());
359 }
360 return dst;
361 }
362
363public:
367 PatriciaSet() = default;
368
372 ~PatriciaSet() = default;
373
379 : root_(clone_node(other.root_.get())), size_(other.size_)
380 {}
381
388 {
389 if (this != &other)
390 {
391 root_ = clone_node(other.root_.get());
392 size_ = other.size_;
393 }
394 return *this;
395 }
396
402 : root_(std::move(other.root_)), size_(other.size_)
403 {
404 other.size_ = 0;
405 }
406
413 {
414 if (this != &other)
415 {
416 root_ = std::move(other.root_);
417 size_ = other.size_;
418 other.size_ = 0;
419 }
420 return *this;
421 }
422
428 {
429 return size_;
430 }
431
437 {
438 return size_ == 0;
439 }
440
445 {
446 root_.reset();
447 size_ = 0;
448 }
449
455 [[nodiscard]] bool contains(const Key key) const noexcept
456 {
457 const Node * leaf = Detail::leaf_for(root_.get(), key);
458 return leaf != nullptr and leaf->key == key;
459 }
460
466 bool insert(const Key key)
467 {
468 if (root_ == nullptr)
469 {
470 root_ = std::make_unique<Node>(key);
471 size_ = 1;
472 return true;
473 }
474
475 const Node * existing = Detail::leaf_for(root_.get(), key);
476 if (existing->key == key)
477 return false;
478
479 const size_t diff_bit = Detail::first_differing_bit(key, existing->key);
480 auto * link = &root_;
481 while ((*link)->leaf == false and (*link)->bit_index < diff_bit)
482 link = &(*link)->child[Detail::bit_at(key, (*link)->bit_index) ? 1 : 0];
483
484 auto branch = std::make_unique<Node>(diff_bit);
485 const size_t side = Detail::bit_at(key, diff_bit) ? 1 : 0;
486 branch->child[side] = std::make_unique<Node>(key);
487 branch->child[1 - side] = std::move(*link);
488 *link = std::move(branch);
489 ++size_;
490 return true;
491 }
492
498 bool erase(const Key key) noexcept
499 {
500 return Detail::erase_key(root_, size_, key);
501 }
502
508 {
509 Array<Key> result;
510 result.reserve(size_);
511 Detail::collect_keys(root_.get(), result);
512 return result;
513 }
514
525 {
526 size_t counted = 0;
527 const bool ok = Detail::check_invariants_rec(root_.get(), 0, false, counted);
528 return ok and counted == size_;
529 }
530};
531
544template <typename UInt, typename T>
545requires (std::is_integral_v<UInt> and std::is_unsigned_v<UInt> and
546 not std::is_same_v<UInt, bool>)
548{
549public:
551 using Key = UInt;
552
554 using Value = T;
555
557 static constexpr size_t bit_width = std::numeric_limits<Key>::digits;
558
559private:
560 struct Node
561 {
562 bool leaf = true;
563 Key key = 0;
564 size_t bit_index = 0;
565 std::optional<Value> value;
566 std::array<std::unique_ptr<Node>, 2> child{};
567
568 template <typename U>
569 Node(const Key k, U && v) : leaf(true), key(k), value(std::forward<U>(v)) {}
570
571 explicit Node(const size_t bit) noexcept : leaf(false), bit_index(bit) {}
572 };
573
574 std::unique_ptr<Node> root_;
575 size_t size_ = 0;
577
578 [[nodiscard]] static std::unique_ptr<Node> clone_node(const Node * src)
579 requires std::is_copy_constructible_v<Value>
580 {
581 if (src == nullptr)
582 return nullptr;
583 auto dst = src->leaf ? std::make_unique<Node>(src->key, *src->value)
584 : std::make_unique<Node>(src->bit_index);
585 if (not src->leaf)
586 {
587 dst->child[0] = clone_node(src->child[0].get());
588 dst->child[1] = clone_node(src->child[1].get());
589 }
590 return dst;
591 }
592
593 template <typename U>
594 bool insert_impl(const Key key, U && value)
595 {
596 if (root_ == nullptr)
597 {
598 root_ = std::make_unique<Node>(key, std::forward<U>(value));
599 size_ = 1;
600 return true;
601 }
602
603 const Node * existing = Detail::leaf_for(root_.get(), key);
604 if (existing->key == key)
605 return false;
606
607 const size_t diff_bit = Detail::first_differing_bit(key, existing->key);
608 auto * link = &root_;
609 while ((*link)->leaf == false and (*link)->bit_index < diff_bit)
610 link = &(*link)->child[Detail::bit_at(key, (*link)->bit_index) ? 1 : 0];
611
612 auto branch = std::make_unique<Node>(diff_bit);
613 const size_t side = Detail::bit_at(key, diff_bit) ? 1 : 0;
614 branch->child[side] = std::make_unique<Node>(key, std::forward<U>(value));
615 branch->child[1 - side] = std::move(*link);
616 *link = std::move(branch);
617 ++size_;
618 return true;
619 }
620
621public:
625 PatriciaMap() = default;
626
630 ~PatriciaMap() = default;
631
637 requires std::is_copy_constructible_v<Value>
638 : root_(clone_node(other.root_.get())), size_(other.size_)
639 {}
640
647 requires std::is_copy_constructible_v<Value>
648 {
649 if (this != &other)
650 {
651 root_ = clone_node(other.root_.get());
652 size_ = other.size_;
653 }
654 return *this;
655 }
656
662 : root_(std::move(other.root_)), size_(other.size_)
663 {
664 other.size_ = 0;
665 }
666
673 {
674 if (this != &other)
675 {
676 root_ = std::move(other.root_);
677 size_ = other.size_;
678 other.size_ = 0;
679 }
680 return *this;
681 }
682
688 {
689 return size_;
690 }
691
697 {
698 return size_ == 0;
699 }
700
705 {
706 root_.reset();
707 size_ = 0;
708 }
709
717 bool insert(const Key key, const Value & value)
718 {
719 return insert_impl(key, value);
720 }
721
732 bool insert(const Key key, Value && value)
733 {
734 return insert_impl(key, std::move(value));
735 }
736
745 {
746 Value * slot = find(key);
747 if (slot != nullptr)
748 *slot = std::move(value);
749 else
750 insert_impl(key, std::move(value));
751 }
752
758 bool erase(const Key key) noexcept
759 {
760 return Detail::erase_key(root_, size_, key);
761 }
762
768 [[nodiscard]] bool contains(const Key key) const noexcept
769 {
770 return find(key) != nullptr;
771 }
772
779 [[nodiscard]] const Value * find(const Key key) const noexcept
780 {
781 const Node * leaf = Detail::leaf_for(root_.get(), key);
782 return (leaf != nullptr and leaf->key == key) ? &*leaf->value : nullptr;
783 }
784
792 [[nodiscard]] Value * find(const Key key) noexcept
793 {
794 Node * leaf = Detail::leaf_for(root_.get(), key);
795 return (leaf != nullptr and leaf->key == key) ? &*leaf->value : nullptr;
796 }
797
803 {
804 Array<Key> result;
805 result.reserve(size_);
806 Detail::collect_keys(root_.get(), result);
807 return result;
808 }
809
821 {
822 size_t counted = 0;
823 const bool ok = Detail::check_invariants_rec(root_.get(), 0, false, counted);
824 return ok and counted == size_;
825 }
826};
827
828} // namespace Aleph
829
830#endif // TPL_PATRICIA_TRIE_H
WeightedDigraph::Node Node
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Compressed bitwise map for unsigned integral keys.
bool insert(const Key key, Value &&value)
Insert key with value moved in, only if key is absent.
PatriciaMap & operator=(const PatriciaMap &other)
Deep-copy assignment.
Array< Key > keys() const
Return all stored keys.
PatriciaMap()=default
Construct an empty map.
T Value
Mapped value type stored at each leaf.
size_t size() const noexcept
Return the number of stored key-value pairs.
bool erase(const Key key) noexcept
Remove key if present.
std::unique_ptr< Node > root_
bool check_invariants() const noexcept
Verify structural invariants recursively.
bool insert_impl(const Key key, U &&value)
bool insert(const Key key, const Value &value)
Insert key with a copy of value, only if key is absent.
static std::unique_ptr< Node > clone_node(const Node *src)
void insert_or_assign(const Key key, Value value)
Insert key or overwrite the mapped value if key exists.
void clear() noexcept
Remove every key-value pair from the map.
const Value * find(const Key key) const noexcept
Look up key.
PatriciaMap(PatriciaMap &&other) noexcept
Move constructor.
PatriciaMap(const PatriciaMap &other)
Deep-copy constructor.
bool contains(const Key key) const noexcept
Check whether key is stored.
Value * find(const Key key) noexcept
Mutable lookup overload.
PatriciaMap & operator=(PatriciaMap &&other) noexcept
Move assignment.
~PatriciaMap()=default
Destructor.
bool is_empty() const noexcept
Check whether the map has no key-value pairs.
UInt Key
Key type stored by the map.
Compressed bitwise set for unsigned integral keys.
bool check_invariants() const noexcept
Verify structural invariants recursively.
static std::unique_ptr< Node > clone_node(const Node *src)
PatriciaSet(const PatriciaSet &other)
Deep-copy constructor.
size_t size() const noexcept
Return the number of stored keys.
std::unique_ptr< Node > root_
bool insert(const Key key)
Insert key if absent.
PatriciaSet & operator=(const PatriciaSet &other)
Deep-copy assignment.
bool erase(const Key key) noexcept
Remove key if present.
PatriciaSet()=default
Construct an empty set.
~PatriciaSet()=default
Destructor.
UInt Key
Key type stored by the set.
PatriciaSet(PatriciaSet &&other) noexcept
Move constructor.
void clear() noexcept
Remove every key from the set.
PatriciaSet & operator=(PatriciaSet &&other) noexcept
Move assignment.
bool is_empty() const noexcept
Check whether the set has no keys.
bool contains(const Key key) const noexcept
Check whether key is stored.
Array< Key > keys() const
Return all stored keys.
Minimal std::expected-style result type for C++20.
__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
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t size(Node *root) noexcept
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Itor find(const Itor &beg, const Itor &end, const T &value)
Find the first element equal to a value.
Definition ahAlgo.H:230
bool diff(const C1 &c1, const C2 &c2, Eq e=Eq())
Check if two containers differ.
STL namespace.
std::optional< Value > value
Node(const size_t bit) noexcept
Node(const Key k) noexcept
Node(const size_t bit) noexcept
std::array< std::unique_ptr< Node >, 2 > child
Shared fixed-width bit-trie algorithms for Patricia containers.
static size_t first_differing_bit(const UInt lhs, const UInt rhs) noexcept
Find the first bit where two keys differ.
static const Node * leaf_for(const Node *node, const UInt key) noexcept
Find the leaf reached by routing a key through a Patricia tree.
static Node * leaf_for(Node *node, const UInt key) noexcept
Mutable overload of leaf_for().
static bool erase_key(std::unique_ptr< Node > &root, size_t &size, const UInt key) noexcept
Remove a key using the shared Patricia deletion algorithm.
static bool check_invariants_rec(const Node *node, const size_t parent_bit, const bool has_parent, size_t &counted) noexcept
Recursively verify Patricia structural invariants.
static constexpr size_t bit_width
Number of significant bits in UInt.
static bool bit_at(const UInt key, const size_t bit_index) noexcept
Return the bit at a most-significant-bit-first index.
static void collect_keys(const Node *node, Array< UInt > &out)
Append all leaf keys in a Patricia subtree.
static bool subtree_matches(const Node *node, const size_t bit_index, const bool expected) noexcept
Check that every leaf in a subtree matches a routing bit.
static int * k
Dynamic array container with automatic resizing.