Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_bplus_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
49#ifndef TPL_BPLUS_TREE_H
50#define TPL_BPLUS_TREE_H
51
52#include <algorithm>
53#include <concepts>
54#include <initializer_list>
55#include <iterator>
56#include <memory>
57#include <optional>
58#include <utility>
59
60#include <ah-concepts.H>
61#include <ah-errors.H>
62#include <ahFunction.H>
63#include <tpl_array.H>
64#include <tpl_sort_utils.H>
65
66namespace Aleph {
67namespace detail {
68template <typename Compare, typename Key>
69[[nodiscard]] bool bplus_equiv(const Compare &cmp, const Key &lhs, const Key &rhs)
70{
71 return not cmp(lhs, rhs) and not cmp(rhs, lhs);
72}
73
74template <typename Key> struct BPlusTreeNode
75{
76 bool leaf = true;
79 BPlusTreeNode *next = nullptr;
80
81 explicit BPlusTreeNode(const bool is_leaf) : leaf(is_leaf) {}
82};
83} // namespace detail
84
132template <typename Key, class Compare = Aleph::less<Key>, size_t MinDegree = 16>
135{
136 static_assert(MinDegree >= 2, "BPlus_Tree requires MinDegree >= 2");
137 static_assert(std::copy_constructible<Key>, "BPlus_Tree requires copy-constructible keys");
138
140
141 static constexpr size_t MaxKeys = 2 * MinDegree - 1;
142 static constexpr size_t MinKeys = MinDegree - 1;
143
144 std::unique_ptr<Node> root_;
145 size_t size_ = 0;
146 Compare cmp_;
147
148 [[nodiscard]] size_t lower_bound_index(const Array<Key> &keys, const Key &key) const
149 {
150 size_t idx = 0;
151 while (idx < keys.size() and cmp_(keys[idx], key))
152 ++idx;
153 return idx;
154 }
155
156 [[nodiscard]] size_t upper_bound_index(const Array<Key> &keys, const Key &key) const
157 {
158 size_t idx = 0;
159 while (idx < keys.size() and (cmp_(keys[idx], key) or equals(keys[idx], key)))
160 ++idx;
161 return idx;
162 }
163
164 [[nodiscard]] size_t child_index(const Node *node, const Key &key) const
165 {
166 return upper_bound_index(node->keys, key);
167 }
168
169 [[nodiscard]] bool equals(const Key &lhs, const Key &rhs) const
170 {
172 }
173
175 {
176 for (size_t i = 1; i < keys.size(); ++i)
177 if (not cmp_(keys[i - 1], keys[i]))
178 return false;
179 return true;
180 }
181
182 template <std::input_iterator It> void insert_range(It first, It last)
183 {
184 for (; first != last; ++first)
185 insert(*first);
186 }
187
188 template <typename T, typename U> static void insert_at(Array<T> &array, const size_t idx, U &&value)
189 {
190 if (idx == array.size())
191 {
192 array.append(std::forward<U>(value));
193 return;
194 }
195
196 array.putn(1);
197 T *base = &array[0];
198 Aleph::open_gap(base, array.size(), idx, 1);
199 array(idx) = std::forward<U>(value);
200 }
201
202 template <typename T> static T erase_at(Array<T> &array, const size_t idx)
203 {
204 T ret = std::move(array(idx));
205 if (idx + 1 < array.size())
206 {
207 T *base = &array[0];
208 Aleph::close_gap(base, array.size(), idx, 1);
209 }
210 (void) array.remove_last();
211 return ret;
212 }
213
214 template <typename T> static void truncate(Array<T> &array, const size_t new_size)
215 {
216 while (array.size() > new_size)
217 (void) array.remove_last();
218 }
219
220 template <typename T> static void append_copy_range(Array<T> &dst, const Array<T> &src, const size_t from)
221 {
222 dst.reserve(dst.size() + src.size() - from);
223 for (size_t i = from; i < src.size(); ++i)
224 dst.append(src[i]);
225 }
226
227 template <typename T> static void append_move_range(Array<T> &dst, Array<T> &src, const size_t from)
228 {
229 dst.reserve(dst.size() + src.size() - from);
230 for (size_t i = from; i < src.size(); ++i)
231 dst.append(std::move(src(i)));
232 }
233
234 [[nodiscard]] const Node *leftmost_leaf(const Node *node) const noexcept
235 {
236 const Node *curr = node;
237 while (curr != nullptr and not curr->leaf)
238 curr = curr->children.get_first().get();
239 return curr;
240 }
241
242 [[nodiscard]] const Node *rightmost_leaf(const Node *node) const noexcept
243 {
244 const Node *curr = node;
245 while (curr != nullptr and not curr->leaf)
246 curr = curr->children.get_last().get();
247 return curr;
248 }
249
250 [[nodiscard]] const Key *subtree_min_ptr(const Node *node) const noexcept
251 {
252 const Node *leaf = leftmost_leaf(node);
253 if (leaf == nullptr or leaf->keys.is_empty())
254 return nullptr;
255 return &leaf->keys.get_first();
256 }
257
258 [[nodiscard]] const Key *subtree_max_ptr(const Node *node) const noexcept
259 {
260 const Node *leaf = rightmost_leaf(node);
261 if (leaf == nullptr or leaf->keys.is_empty())
262 return nullptr;
263 return &leaf->keys.get_last();
264 }
265
266 [[nodiscard]] const Key &subtree_min(const Node *node) const
267 {
268 const Key *ptr = subtree_min_ptr(node);
269 ah_runtime_error_unless(ptr != nullptr) << "BPlus_Tree invariant violated: empty subtree has no minimum";
270 return *ptr;
271 }
272
273 [[nodiscard]] size_t height_of(const Node *node) const noexcept
274 {
275 size_t height = 0;
276 for (const Node *curr = node; curr != nullptr;)
277 {
278 ++height;
279 if (curr->leaf or curr->children.is_empty())
280 break;
281 curr = curr->children.get_first().get();
282 }
283 return height;
284 }
285
286 void rebuild_separators(Node *node) const
287 {
288 if (node == nullptr or node->leaf)
289 return;
290
291 node->keys.empty();
292 node->keys.reserve(node->children.size() > 0 ? node->children.size() - 1 : 0);
293 for (size_t i = 1; i < node->children.size(); ++i)
294 node->keys.append(subtree_min(node->children[i].get()));
295 }
296
297 [[nodiscard]] std::unique_ptr<Node> clone_node(const Node *node, Node *&prev_leaf) const
298 {
299 if (node == nullptr)
300 return nullptr;
301
302 auto copy = std::make_unique<Node>(node->leaf);
303 copy->keys = node->keys;
304
305 if (node->leaf)
306 {
307 if (prev_leaf != nullptr)
308 prev_leaf->next = copy.get();
309 prev_leaf = copy.get();
310 return copy;
311 }
312
313 copy->children.reserve(node->children.size());
314 for (const auto &child : node->children)
315 copy->children.append(clone_node(child.get(), prev_leaf));
317 return copy;
318 }
319
320 [[nodiscard]] const Node *find_leaf(const Key &key) const
321 {
322 const Node *curr = root_.get();
323 while (curr != nullptr and not curr->leaf)
324 curr = curr->children[child_index(curr, key)].get();
325 return curr;
326 }
327
328 [[nodiscard]] bool contains_in(const Node *node, const Key &key) const
329 {
330 if (node == nullptr)
331 return false;
332
333 const Node *leaf = find_leaf(key);
334 if (leaf == nullptr)
335 return false;
336
337 const size_t idx = lower_bound_index(leaf->keys, key);
338 return idx < leaf->keys.size() and equals(leaf->keys[idx], key);
339 }
340
341 void split_child(Node *parent, const size_t idx)
342 {
343 Node *child = parent->children[idx].get();
344 auto right = std::make_unique<Node>(child->leaf);
345
346 if (child->leaf)
347 {
348 append_copy_range(right->keys, child->keys, MinKeys);
349 truncate(child->keys, MinKeys);
350
351 right->next = child->next;
352 child->next = right.get();
353 }
354 else
355 {
356 append_move_range(right->children, child->children, MinDegree);
357 truncate(child->children, MinDegree);
358 rebuild_separators(child);
359 rebuild_separators(right.get());
360 }
361
362 insert_at(parent->children, idx + 1, std::move(right));
363 rebuild_separators(parent);
364 }
365
366 void insert_nonfull(Node *node, const Key &key)
367 {
368 if (node->leaf)
369 {
370 const size_t idx = lower_bound_index(node->keys, key);
371 insert_at(node->keys, idx, key);
372 return;
373 }
374
375 size_t idx = child_index(node, key);
376 if (node->children[idx]->keys.size() == MaxKeys)
377 {
378 split_child(node, idx);
379 idx = child_index(node, key);
380 }
381
382 insert_nonfull(node->children[idx].get(), key);
383 rebuild_separators(node);
384 }
385
386 void borrow_from_prev(Node *parent, const size_t idx)
387 {
388 Node *child = parent->children[idx].get();
389 Node *left = parent->children[idx - 1].get();
390
391 if (child->leaf)
392 {
393 insert_at(child->keys, 0, left->keys.get_last());
394 (void) left->keys.remove_last();
395 }
396 else
397 {
398 insert_at(child->children, 0, std::move(left->children.get_last()));
399 (void) left->children.remove_last();
400 rebuild_separators(left);
401 rebuild_separators(child);
402 }
403
404 rebuild_separators(parent);
405 }
406
407 void borrow_from_next(Node *parent, const size_t idx)
408 {
409 Node *child = parent->children[idx].get();
410 Node *right = parent->children[idx + 1].get();
411
412 if (child->leaf)
413 {
414 child->keys.append(right->keys.get_first());
415 erase_at(right->keys, 0);
416 }
417 else
418 {
419 child->children.append(std::move(right->children.get_first()));
420 erase_at(right->children, 0);
421 rebuild_separators(child);
422 rebuild_separators(right);
423 }
424
425 rebuild_separators(parent);
426 }
427
428 void merge_children(Node *parent, const size_t left_idx)
429 {
430 Node *left = parent->children[left_idx].get();
431 auto right = std::move(parent->children[left_idx + 1]);
432
433 if (left->leaf)
434 {
435 append_copy_range(left->keys, right->keys, 0);
436 left->next = right->next;
437 }
438 else
439 {
440 append_move_range(left->children, right->children, 0);
441 rebuild_separators(left);
442 }
443
444 erase_at(parent->children, left_idx + 1);
445 rebuild_separators(parent);
446 }
447
448 void fix_underflow(Node *parent, const size_t idx)
449 {
450 if (parent->children[idx]->keys.size() >= MinKeys)
451 {
452 rebuild_separators(parent);
453 return;
454 }
455
456 if (idx > 0 and parent->children[idx - 1]->keys.size() > MinKeys)
457 {
458 borrow_from_prev(parent, idx);
459 return;
460 }
461
462 if (idx + 1 < parent->children.size() and parent->children[idx + 1]->keys.size() > MinKeys)
463 {
464 borrow_from_next(parent, idx);
465 return;
466 }
467
468 if (idx + 1 < parent->children.size())
469 merge_children(parent, idx);
470 else
471 merge_children(parent, idx - 1);
472 }
473
474 [[nodiscard]] bool remove_from(Node *node, const Key &key)
475 {
476 if (node->leaf)
477 {
478 const size_t idx = lower_bound_index(node->keys, key);
479 if (idx >= node->keys.size() or not equals(node->keys[idx], key))
480 return false;
481
482 erase_at(node->keys, idx);
483 return true;
484 }
485
486 const size_t idx = child_index(node, key);
487 if (const bool removed = remove_from(node->children[idx].get(), key); not removed)
488 return false;
489
490 fix_underflow(node, idx);
491 rebuild_separators(node);
492 return true;
493 }
494
496 {
497 const Node *leaf = leftmost_leaf(root_.get());
498 size_t counted = 0;
499 std::optional<Key> prev;
500
501 while (leaf != nullptr)
502 {
503 if (not leaf->leaf or leaf->keys.is_empty())
504 return false;
505
506 for (const auto &key : leaf->keys)
507 {
508 if (prev.has_value() and not cmp_(*prev, key))
509 return false;
510 prev = key;
511 ++counted;
512 }
513
514 if (leaf->next != nullptr)
515 {
516 if (not leaf->next->leaf or leaf->next->keys.is_empty())
517 return false;
518 if (not cmp_(leaf->keys.get_last(), leaf->next->keys.get_first()))
519 return false;
520 }
521
522 leaf = leaf->next;
523 }
524
525 return counted == size_;
526 }
527
528 [[nodiscard]] bool verify_node(const Node *node, const std::optional<Key> &min_key, const std::optional<Key> &max_key,
529 const size_t depth, size_t &leaf_depth, size_t &counted) const
530 {
531 if (node == nullptr)
532 return false;
533
534 if (node != root_.get())
535 {
536 if (node->keys.size() < MinKeys or node->keys.size() > MaxKeys)
537 return false;
538 }
539 else if (node->keys.size() > MaxKeys)
540 return false;
541
542 if (not strictly_sorted(node->keys))
543 return false;
544
545 if (node->leaf)
546 {
547 if (min_key.has_value() and not node->keys.is_empty() and cmp_(node->keys.get_first(), *min_key))
548 return false;
549
550 if (max_key.has_value() and not node->keys.is_empty() and not cmp_(node->keys.get_last(), *max_key))
551 return false;
552
553 counted += node->keys.size();
554
555 if (leaf_depth == static_cast<size_t>(-1))
556 leaf_depth = depth;
557 return leaf_depth == depth;
558 }
559
560 if (node->children.size() != node->keys.size() + 1 or node->children.is_empty())
561 return false;
562
563 for (size_t i = 1; i < node->children.size(); ++i)
564 {
565 const Key *expected = subtree_min_ptr(node->children[i].get());
566 if (expected == nullptr or not equals(node->keys[i - 1], *expected))
567 return false;
568 }
569
570 for (size_t i = 0; i < node->children.size(); ++i)
571 {
572 std::optional<Key> child_min = min_key;
573 std::optional<Key> child_max = max_key;
574 if (i > 0)
575 child_min = node->keys[i - 1];
576 if (i < node->keys.size())
577 child_max = node->keys[i];
578
579 if (not verify_node(node->children[i].get(), child_min, child_max, depth + 1, leaf_depth, counted))
580 return false;
581 }
582
583 const Key *first = subtree_min_ptr(node->children.get_first().get());
584 const Key *last = subtree_max_ptr(node->children.get_last().get());
585 if (first == nullptr or last == nullptr)
586 return false;
587
588 if (min_key.has_value() and cmp_(*first, *min_key))
589 return false;
590
591 if (max_key.has_value() and not cmp_(*last, *max_key))
592 return false;
593
594 return true;
595 }
596
597public:
598 using key_type = Key;
599
607 {
608 const Gen_BPlus_Tree *tree_ = nullptr;
609 const Node *leaf_ = nullptr;
610 size_t idx_ = 0;
611 std::optional<Key> last_;
612
614 {
615 while (tree_ != nullptr and leaf_ != nullptr)
616 {
617 if (idx_ >= leaf_->keys.size())
618 {
619 leaf_ = leaf_->next;
620 idx_ = 0;
621 continue;
622 }
623
624 if (last_.has_value() and tree_->cmp_(*last_, leaf_->keys[idx_]))
625 {
626 leaf_ = nullptr;
627 idx_ = 0;
628 }
629 return;
630 }
631 }
632
633 public:
638
644 : tree_(&tree), leaf_(tree.leftmost_leaf(tree.root_.get())), idx_(0)
645 {
647 }
648
656 Iterator(const Gen_BPlus_Tree &tree, const Key &first, const Key &last)
657 : tree_(&tree), leaf_(nullptr), idx_(0), last_(last)
658 {
659 ah_invalid_argument_if(tree.cmp_(last, first))
660 << "BPlus_Tree::Iterator(): invalid range (last < first)";
661
662 leaf_ = tree.find_leaf(first);
663 if (leaf_ != nullptr)
664 idx_ = tree.lower_bound_index(leaf_->keys, first);
666 }
667
673 {
674 return tree_ != nullptr and leaf_ != nullptr;
675 }
676
681 [[nodiscard]] const Key &get_curr() const
682 {
683 ah_underflow_error_if(not has_curr()) << "BPlus_Tree::Iterator::get_curr(): no current key";
684 return leaf_->keys[idx_];
685 }
686
691 void next_ne()
692 {
693 ah_underflow_error_if(not has_curr()) << "BPlus_Tree::Iterator::next_ne(): no current key";
694 ++idx_;
696 }
697
702 void next()
703 {
704 next_ne();
705 }
706
711 [[nodiscard]] const Key &operator*() const
712 {
713 return get_curr();
714 }
715
720 [[nodiscard]] const Key *operator->() const
721 {
722 return &get_curr();
723 }
724 };
725
730 explicit Gen_BPlus_Tree(const Compare &cmp = Compare()) : cmp_(cmp) {}
731
738 template <std::input_iterator It> Gen_BPlus_Tree(It first, It last, const Compare &cmp = Compare()) : cmp_(cmp)
739 {
740 insert_range(first, last);
741 }
742
748 Gen_BPlus_Tree(std::initializer_list<Key> init, const Compare &cmp = Compare())
749 : Gen_BPlus_Tree(init.begin(), init.end(), cmp)
750 {}
751
757 {
758 Node *prev_leaf = nullptr;
759 root_ = clone_node(other.root_.get(), prev_leaf);
760 }
761
766
772 {
773 if (this == &other)
774 return *this;
775
776 // Copy-and-swap: build a complete copy first so that if cmp_'s
777 // copy-assignment (or clone_node) throws, *this is left unchanged.
779 std::swap(root_, tmp.root_);
780 std::swap(size_, tmp.size_);
781 std::swap(cmp_, tmp.cmp_);
782 return *this;
783 }
784
790
796 [[nodiscard]] const Compare &key_comp() const noexcept
797 {
798 return cmp_;
799 }
800
806 [[nodiscard]] const Compare &get_compare() const noexcept
807 {
808 return key_comp();
809 }
810
816 {
817 return size_ == 0;
818 }
819
825 {
826 return empty();
827 }
828
834 {
835 return size_;
836 }
837
843 {
844 return height_of(root_.get());
845 }
846
851 {
852 root_.reset();
853 size_ = 0;
854 }
855
861 [[nodiscard]] bool contains(const Key &key) const
862 {
863 return contains_in(root_.get(), key);
864 }
865
871 [[nodiscard]] bool search(const Key &key) const
872 {
873 return contains(key);
874 }
875
883 bool insert(const Key &key)
884 {
885 if (contains(key))
886 return false;
887
888 if (root_ == nullptr)
889 {
890 root_ = std::make_unique<Node>(true);
891 root_->keys.append(key);
892 size_ = 1;
893 return true;
894 }
895
896 if (root_->keys.size() == MaxKeys)
897 {
898 auto new_root = std::make_unique<Node>(false);
899 new_root->children.append(std::move(root_));
900 split_child(new_root.get(), 0);
901 root_ = std::move(new_root);
902 }
903
904 insert_nonfull(root_.get(), key);
905 ++size_;
906 return true;
907 }
908
916 bool insert(Key &&key)
917 {
918 Key copy = std::move(key);
919 return insert(copy);
920 }
921
927 bool remove(const Key &key)
928 {
929 if (root_ == nullptr)
930 return false;
931
932 const bool removed = remove_from(root_.get(), key);
933 if (not removed)
934 return false;
935
936 --size_;
937
938 if (root_->leaf and root_->keys.is_empty())
939 root_.reset();
940 else if (not root_->leaf and root_->children.size() == 1)
941 root_ = std::move(root_->children.get_first());
942
943 return true;
944 }
945
950 [[nodiscard]] std::optional<Key> min_key() const
951 {
952 const Key *ptr = subtree_min_ptr(root_.get());
953 if (ptr == nullptr)
954 return std::nullopt;
955 return *ptr;
956 }
957
962 [[nodiscard]] std::optional<Key> max_key() const
963 {
964 const Key *ptr = subtree_max_ptr(root_.get());
965 if (ptr == nullptr)
966 return std::nullopt;
967 return *ptr;
968 }
969
975 [[nodiscard]] std::optional<Key> lower_bound(const Key &key) const
976 {
977 const Node *leaf = find_leaf(key);
978 if (leaf == nullptr)
979 return std::nullopt;
980
981 size_t idx = lower_bound_index(leaf->keys, key);
982 while (leaf != nullptr)
983 {
985 return leaf->keys[idx];
986 leaf = leaf->next;
987 idx = 0;
988 }
989
990 return std::nullopt;
991 }
992
998 [[nodiscard]] std::optional<Key> upper_bound(const Key &key) const
999 {
1000 const Node *leaf = find_leaf(key);
1001 if (leaf == nullptr)
1002 return std::nullopt;
1003
1004 size_t idx = upper_bound_index(leaf->keys, key);
1005 while (leaf != nullptr)
1006 {
1007 if (idx < leaf->keys.size())
1008 return leaf->keys[idx];
1009 leaf = leaf->next;
1010 idx = 0;
1011 }
1012
1013 return std::nullopt;
1014 }
1015
1021 {
1022 return Iterator(*this);
1023 }
1024
1032 [[nodiscard]] Iterator get_range_it(const Key &first, const Key &last) const
1033 {
1034 return Iterator(*this, first, last);
1035 }
1036
1045 [[nodiscard]] Array<Key> range(const Key &first, const Key &last) const
1046 {
1048 out.reserve(8);
1049 for (auto it = get_range_it(first, last); it.has_curr(); it.next_ne())
1050 out.append(it.get_curr());
1051
1052 return out;
1053 }
1054
1061 {
1063 out.reserve(size_ == 0 ? 1 : size_);
1064 for (auto it = get_it(); it.has_curr(); it.next_ne())
1065 out.append(it.get_curr());
1066
1067 return out;
1068 }
1069
1074 [[nodiscard]] bool verify() const
1075 {
1076 if (root_ == nullptr)
1077 return size_ == 0;
1078
1079 if (root_->keys.is_empty())
1080 return false;
1081
1082 auto leaf_depth = static_cast<size_t>(-1);
1083 size_t counted = 0;
1084 return verify_node(root_.get(), std::nullopt, std::nullopt, 1, leaf_depth, counted) and counted == size_
1086 }
1087};
1088
1094template <typename Key, class Compare = Aleph::less<Key>, size_t MinDegree = 16>
1096} // namespace Aleph
1097
1098#endif // TPL_BPLUS_TREE_H
C++20 concepts for constraining comparison functors.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:250
#define ah_underflow_error_if(C)
Throws std::underflow_error if condition holds.
Definition ah-errors.H:368
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
Standard functor implementations and comparison objects.
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
void empty() noexcept
Empties the container.
Definition tpl_array.H:336
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:348
T & get_first() noexcept
return a modifiable reference to the first element.
Definition tpl_array.H:358
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
T & get_last() noexcept
return a modifiable reference to the last element.
Definition tpl_array.H:366
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
void putn(const size_t n)
Reserve n additional logical slots in the array without value-initializing them.
Definition tpl_array.H:305
Generic filter iterator wrapper.
Lazy iterator over ordered leaf keys.
void next()
Synonym for next_ne().
const Key * operator->() const
Member-access shorthand for the current key.
const Gen_BPlus_Tree * tree_
void next_ne()
Advance to the next key.
bool has_curr() const noexcept
Return whether the iterator still points to a key.
Iterator(const Gen_BPlus_Tree &tree, const Key &first, const Key &last)
Construct an iterator over the inclusive range [first, last].
Iterator() noexcept=default
Construct an exhausted iterator.
const Key & operator*() const
Dereference the iterator.
const Key & get_curr() const
Return the current key.
Generic B+ Tree with configurable minimum degree.
Gen_BPlus_Tree & operator=(const Gen_BPlus_Tree &other)
Copy assignment.
const Key * subtree_max_ptr(const Node *node) const noexcept
const Node * rightmost_leaf(const Node *node) const noexcept
size_t upper_bound_index(const Array< Key > &keys, const Key &key) const
void merge_children(Node *parent, const size_t left_idx)
void fix_underflow(Node *parent, const size_t idx)
static T erase_at(Array< T > &array, const size_t idx)
std::optional< Key > lower_bound(const Key &key) const
Return the first key not less than key.
bool verify_leaf_chain() const
Gen_BPlus_Tree(const Compare &cmp=Compare())
Construct an empty B+ Tree.
size_t height() const noexcept
Return the current tree height.
bool remove(const Key &key)
Remove a key if present.
bool search(const Key &key) const
Synonym for contains().
Array< Key > keys() const
Materialize the tree contents in sorted order.
size_t lower_bound_index(const Array< Key > &keys, const Key &key) const
bool empty() const noexcept
Return whether the tree has no keys.
void rebuild_separators(Node *node) const
static constexpr size_t MaxKeys
Gen_BPlus_Tree(It first, It last, const Compare &cmp=Compare())
Construct a B+ Tree from an iterator range.
std::unique_ptr< Node > root_
std::optional< Key > max_key() const
Return the maximum key, if any.
bool verify() const
Verify structural B+ Tree invariants.
static void append_move_range(Array< T > &dst, Array< T > &src, const size_t from)
bool insert(const Key &key)
Insert a key if it is not already present.
Gen_BPlus_Tree(const Gen_BPlus_Tree &other)
Copy constructor performing a deep copy.
void insert_nonfull(Node *node, const Key &key)
void split_child(Node *parent, const size_t idx)
const Compare & get_compare() const noexcept
Return the comparison object (read-only).
bool remove_from(Node *node, const Key &key)
Gen_BPlus_Tree(std::initializer_list< Key > init, const Compare &cmp=Compare())
Construct a B+ Tree from an initializer list.
const Key * subtree_min_ptr(const Node *node) const noexcept
static void append_copy_range(Array< T > &dst, const Array< T > &src, const size_t from)
Gen_BPlus_Tree(Gen_BPlus_Tree &&other)=default
Move constructor.
std::optional< Key > min_key() const
Return the minimum key, if any.
void clear() noexcept
Remove every key from the tree.
Key key_type
Stored key type.
const Key & subtree_min(const Node *node) const
bool contains_in(const Node *node, const Key &key) const
const Compare & key_comp() const noexcept
Return the comparison object (read-only).
const Node * find_leaf(const Key &key) const
bool is_empty() const noexcept
Synonym for empty() in traditional Aleph style.
size_t height_of(const Node *node) const noexcept
Gen_BPlus_Tree & operator=(Gen_BPlus_Tree &&other)=default
Move assignment.
void borrow_from_next(Node *parent, const size_t idx)
static void truncate(Array< T > &array, const size_t new_size)
std::optional< Key > upper_bound(const Key &key) const
Return the first key strictly greater than key.
size_t size() const noexcept
Return the number of stored keys.
Iterator get_it() const noexcept
Return a lazy iterator over the full key order.
bool verify_node(const Node *node, const std::optional< Key > &min_key, const std::optional< Key > &max_key, const size_t depth, size_t &leaf_depth, size_t &counted) const
bool strictly_sorted(const Array< Key > &keys) const
static constexpr size_t MinKeys
bool insert(Key &&key)
Insert a key by move if it is not already present.
bool contains(const Key &key) const
Return whether a key is present.
Iterator get_range_it(const Key &first, const Key &last) const
Return a lazy iterator over an inclusive key range.
static void insert_at(Array< T > &array, const size_t idx, U &&value)
const Node * leftmost_leaf(const Node *node) const noexcept
std::unique_ptr< Node > clone_node(const Node *node, Node *&prev_leaf) const
void insert_range(It first, It last)
size_t child_index(const Node *node, const Key &key) const
Array< Key > range(const Key &first, const Key &last) const
Collect all keys in the inclusive range [first, last].
bool equals(const Key &lhs, const Key &rhs) const
void borrow_from_prev(Node *parent, const size_t idx)
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4118
bool bplus_equiv(const Compare &cmp, const Key &lhs, const Key &rhs)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
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
void open_gap(Tarray &ptr, size_t n, size_t pos=0, size_t num_entries=1)
Open a gap in an array by shifting elements right.
Definition array_utils.H:96
static bool is_leaf(BinNode< std::string > *p) noexcept
Definition Huffman.H:111
static std::atomic< bool > init
Definition hash-fct.C:53
void close_gap(T *ptr, size_t n, size_t pos, size_t num_entries=1)
Close a gap in an array by shifting elements left.
BPlusTreeNode(const bool is_leaf)
Array< std::unique_ptr< BPlusTreeNode > > children
Aleph::DynList< T > keys() const
Definition ah-dry.H:1729
Dynamic array container with automatic resizing.
Comprehensive sorting algorithms and search utilities for Aleph-w.