datastructures 0.1.0
Loading...
Searching...
No Matches
_persistent_avl.ipp
Go to the documentation of this file.
1
7
8#pragma once
9
10#include "_persistent_avl.hpp"
11
12#include <algorithm>
13#include <stack>
14#include <unordered_map>
15#include <utility>
16
17namespace datastructures {
18namespace detail {
19
20// ==== Constructors & Assigments ====
21
22template <class TKey, class TItem, class TKeyComp, class TAlloc>
24 const typename base::comparator_type &comparator,
25 const typename base::allocator_type &allocator)
26 : base(comparator, allocator) {}
27
28template <class TKey, class TItem, class TKeyComp, class TAlloc>
30 const persistent_avl_tree &other)
31 : base(other.comparator, other.allocator), based_on(other.based_on) {
32 clone_tree(other);
33}
34
35template <class TKey, class TItem, class TKeyComp, class TAlloc>
38 const persistent_avl_tree &other) {
39
40 this->comparator = other.comparator;
41 this->allocator = other.allocator;
42
43 based_on = other.based_on;
44
45 clone_tree(other);
46
47 return *this;
48}
49
50template <class TKey, class TItem, class TKeyComp, class TAlloc>
52 persistent_avl_tree &&other) noexcept
53 : base(std::move(other.comparator), std::move(other.allocator)),
54 roots(std::move(other.roots)), node_counts(std::move(other.node_counts)),
55 based_on(std::move(other.based_on)) {}
56
57template <class TKey, class TItem, class TKeyComp, class TAlloc>
60 persistent_avl_tree &&other) noexcept {
61 this->comparator = std::move(other.comparator);
62 this->allocator = std::move(other.allocator);
63
64 roots = std::move(other.roots);
65 node_counts = std::move(other.node_counts);
66 based_on = std::move(other.based_on);
67
68 return *this;
69}
70
71template <class TKey, class TItem, class TKeyComp, class TAlloc>
72persistent_avl_tree<TKey, TItem, TKeyComp,
73 TAlloc>::~persistent_avl_tree() noexcept {
74 clear_all();
75}
76
77// ==== Helper Functions ====
78
79template <class TKey, class TItem, class TKeyComp, class TAlloc>
81 const persistent_avl_tree &other) {
82
83 node_counts = other.node_counts;
84
85 std::unordered_map<node_ptr, node_ptr> old_to_new;
86 std::vector<std::pair<node_ptr, node_ptr *>> stack;
87
88 roots.assign(other.roots.size(), nullptr);
89
90 for (int version = 0; static_cast<size_t>(version) < other.roots.size();
91 ++version) {
92 node_ptr root = other.roots[version];
93
94 if (root == nullptr) {
95 continue;
96 }
97
98 if (root->version != version) {
99 roots[version] = old_to_new.at(root);
100 continue;
101 }
102
103 stack.clear();
104 stack.push_back(std::make_pair(root, &roots[version]));
105
106 while (!stack.empty()) {
107 auto [node, location] = stack.back();
108 stack.pop_back();
109
110 node_ptr new_node = copy_node(node, version);
111 old_to_new[node] = new_node;
112 *location = new_node;
113
114 if (node->left != nullptr) {
115 if (node->left->version == version) {
116 stack.push_back(
117 std::make_pair(node->left, &new_node->left));
118 }
119 else {
120 // points to previous version
121 new_node->left = old_to_new.at(node->left);
122 }
123 }
124 if (node->right != nullptr) {
125 if (node->right->version == version) {
126 stack.push_back(
127 std::make_pair(node->right, &new_node->right));
128 }
129 else {
130 // points to previous version
131 new_node->right = old_to_new.at(node->right);
132 }
133 }
134 }
135 }
136}
137
138template <class TKey, class TItem, class TKeyComp, class TAlloc>
140persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::copy_node(
141 node_ptr node, version_type version) {
142 // copy the item
143 typename base::item_type new_item = node->item;
144 node_ptr new_node = this->create_node(new_item);
145 new_node->sign = node->sign;
146 new_node->left = node->left;
147 new_node->right = node->right;
148 new_node->version = version;
149 return new_node;
150}
151
152template <class TKey, class TItem, class TKeyComp, class TAlloc>
153template <std::invocable TCallback>
155persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::copy_node(
156 node_ptr node, TCallback &&callback, version_type version) {
157 node_ptr new_node = this->create_node(std::forward<TCallback>(callback));
158 new_node->sign = node->sign;
159 new_node->left = node->left;
160 new_node->right = node->right;
161 new_node->version = version;
162 return new_node;
163}
164
165template <class TKey, class TItem, class TKeyComp, class TAlloc>
166typename persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::path_type
167persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::copy_path(
168 const path_type &path, node_ptr new_child, version_type version) {
169
170 path_type new_path;
171
172 new_path.resize(path.size(), std::make_pair(nullptr, false));
173
174 for (int i = path.size() - 1; i >= 0; --i) {
175 auto [parent, child_is_right] = path[i];
176
177 node_ptr new_parent = copy_node(parent, version);
178
179 new_path[i] = std::make_pair(new_parent, child_is_right);
180
181 if (child_is_right) {
182 new_parent->right = new_child;
183 }
184 else {
185 new_parent->left = new_child;
186 }
187
188 new_child = new_parent;
189 }
190
191 return new_path;
192}
193
194template <class TKey, class TItem, class TKeyComp, class TAlloc>
196 node_ptr parent, node_ptr child, bool child_is_right, int parent_index,
197 const path_type &parents) {
199 if (child_is_right) {
200 parent->right = child->left;
201 child->left = parent;
202 }
203 else {
204 parent->left = child->right;
205 child->right = parent;
206 }
208 if (parent_index == 0) {
209 // it's root
210 roots.back() = child;
211 }
212 else {
213 auto [grandpa, parent_is_right] = parents[parent_index - 1];
215 if (parent_is_right) {
216 grandpa->right = child;
217 }
218 else {
219 grandpa->left = child;
220 }
221 }
222}
223
224template <class TKey, class TItem, class TKeyComp, class TAlloc>
225void persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::double_rotate(
226 node_ptr parent, node_ptr child, node_ptr grand_child, bool child_is_right,
227 int parent_index, const path_type &parents) {
228
229 if (child_is_right) {
230 parent->right = grand_child->left;
231 child->left = grand_child->right;
232 grand_child->left = parent;
233 grand_child->right = child;
234 }
235 else {
236 parent->left = grand_child->right;
237 child->right = grand_child->left;
238 grand_child->left = child;
239 grand_child->right = parent;
240 }
241
242 if (parent_index == 0) {
243 roots.back() = grand_child;
244 }
245 else {
246 auto [grandpa, parent_is_right] = parents[parent_index - 1];
247
248 if (parent_is_right) {
249 grandpa->right = grand_child;
250 }
251 else {
252 grandpa->left = grand_child;
253 }
254 }
255}
256
257// ==== Access Operations ====
258
259template <class TKey, class TItem, class TKeyComp, class TAlloc>
260std::vector<
263 version_type version) const noexcept {
264 if (!is_valid_version(version)) {
265 return std::vector<node_ptr>{};
266 }
267
268 std::vector<node_ptr> result;
269 std::stack<node_ptr> stack;
270 node_ptr current = roots[version];
271
272 while (current != nullptr || !stack.empty()) {
273 while (current != nullptr) {
274 stack.push(current);
275 current = current->left;
276 }
277
278 current = stack.top();
279 stack.pop();
280
281 result.push_back(current);
282
283 current = current->right;
284 }
285
286 return result;
287}
288
289template <class TKey, class TItem, class TKeyComp, class TAlloc>
292 const key_type &key, version_type version) const noexcept {
293
294 if (!is_valid_version(version)) {
295 return nullptr;
296 }
297
298 node_ptr node = roots[version];
299
300 while (true) {
301 if (node == nullptr) {
302 return nullptr;
303 }
304 if (this->comparator(key, node->item)) {
305 node = node->left;
306 }
307 else if (this->comparator(node->item, key)) {
308 node = node->right;
309 }
310 else {
311 return node;
312 }
313 }
314}
316// ==== Update Operations ====
317
318template <class TKey, class TItem, class TKeyComp, class TAlloc>
320
321 for (version_type version = roots.size() - 1; version >= 0; --version) {
322 node_ptr node = roots[version];
323
324 while (node != nullptr && node->version == version) {
325 if (node->left != nullptr && node->left->version == version) {
326 node_ptr tmp = node->left;
327 node->left = tmp->right;
328 tmp->right = node;
329 node = tmp;
330 }
331 else {
332 node_ptr tmp = node->right;
333 this->destroy_node(node);
334 node = tmp;
335 }
336 }
337 }
338
339 roots.clear();
340 node_counts.clear();
341 based_on.clear();
342}
343
344template <class TKey, class TItem, class TKeyComp, class TAlloc>
345template <std::invocable TCallback>
346std::pair<typename persistent_avl_tree<TKey, TItem, TKeyComp, TAlloc>::node_ptr,
347 bool>
349 TCallback &&callback,
350 version_type version,
351 bool do_assign) {
352
353 if (!is_valid_version(version)) {
354 version = -1;
355 }
356
357 const version_type new_version = roots.size();
358
359 if (version == -1 || roots[version] == nullptr) {
360 const node_ptr new_root =
361 this->create_node(std::forward<TCallback>(callback));
362 new_root->version = new_version;
363
364 roots.push_back(new_root);
365 node_counts.push_back(1);
366 based_on.push_back(version);
367
368 return std::make_pair(new_root, true);
369 }
370
371 node_ptr node = roots[version];
372 node_ptr new_node = nullptr;
373 path_type parents;
374
375 while (true) {
376 if (this->comparator(key, node->item)) {
377 parents.push_back(std::make_pair(node, false));
378
379 if (node->left == nullptr) {
380 new_node = this->create_node(std::forward<TCallback>(callback));
381 new_node->version = new_version;
382 break;
383 }
384
385 node = node->left;
386 }
387 else if (this->comparator(node->item, key)) {
388 parents.push_back(std::make_pair(node, true));
389
390 if (node->right == nullptr) {
391 new_node = this->create_node(std::forward<TCallback>(callback));
392 new_node->version = new_version;
393 break;
394 }
395
396 node = node->right;
397 }
398 else {
399 if (!do_assign) {
400 return std::make_pair(node, false);
401 }
402
403 // copy the path and return the new node
404
405 new_node =
406 copy_node(node, std::forward<TCallback>(callback), new_version);
407 new_node->version = new_version;
408
409 path_type new_parents = copy_path(parents, new_node, new_version);
410
411 if (new_parents.empty()) {
412 roots.push_back(new_node);
413 }
414 else {
415 auto [last_parent, attach_right] = new_parents.back();
416 if (attach_right) {
417 last_parent->right = new_node;
418 }
419 else {
420 last_parent->left = new_node;
421 }
422 roots.push_back(new_parents[0].first);
423 }
424
425 // the node_count in this version didn't change
426 node_counts.push_back(node_counts[version]);
427 based_on.push_back(version);
428
429 return std::make_pair(new_node, false);
430 }
431 }
432
433 path_type new_parents = copy_path(parents, new_node, new_version);
434
435 auto [last_parent, attach_right] = new_parents.back();
436 if (attach_right) {
437 last_parent->right = new_node;
438 }
439 else {
440 last_parent->left = new_node;
441 }
442
443 roots.push_back(new_parents[0].first);
444 node_counts.push_back(node_counts[version] + 1);
445 based_on.push_back(version);
446
447 // now we have a correctly copied path and the newly created node, but
448 // the tree can be inbalanced.
449
450 int parent_index = static_cast<int>(new_parents.size()) - 1;
451
452 while (parent_index >= 0) {
453 auto [parent, node_is_right] = new_parents[parent_index];
454
455 const typename base::sign_type positive = node_is_right ? +1 : -1;
456
457 if (parent->sign == -positive) {
458 parent->sign = 0;
459 break; // balanced itself
460 }
461
462 if (parent->sign == positive) {
463 // we need to rotate
464
465 if (node->sign == positive) {
466 rotate(parent, node, node_is_right, parent_index, new_parents);
467
468 parent->sign = 0;
469 node->sign = 0;
470 }
471
472 else { // node->sign cannot be 0 here => must be -positive
473 node_ptr child = (positive == 1) ? node->left : node->right;
474
475 double_rotate(parent, node, child, node_is_right, parent_index,
476 new_parents);
477
478 // correct the signs for parent, node and child
479 if (child->sign == positive) {
480 node->sign = 0;
481 parent->sign = -positive;
482 }
483 else if (child->sign == -positive) {
484 node->sign = +positive;
485 parent->sign = 0;
486 }
487 else { // child is a new-born
488 node->sign = 0;
489 parent->sign = 0;
490 }
491 child->sign = 0;
492 }
493
494 break; // balanced
495 }
496
497 // parent->sign == 0
498
499 parent->sign = positive;
500 node = parent;
501 --parent_index;
502 }
503
504 return std::make_pair(new_node, true);
505}
506
507template <class TKey, class TItem, class TKeyComp, class TAlloc>
508std::pair<bool, typename persistent_avl_tree<TKey, TItem, TKeyComp,
509 TAlloc>::version_type>
511 const key_type &key, version_type version) noexcept {
512
513 if (!is_valid_version(version)) {
514 return std::make_pair(false, version);
515 }
516
517 if (roots[version] == nullptr) {
518 return std::make_pair(false, version);
519 }
520
521 // find path
522
523 node_ptr node = roots[version];
524
525 path_type parents;
526
527 while (true) {
528 if (node == nullptr) {
529 return std::make_pair(false, version);
530 }
531
532 if (this->comparator(key, node->item)) {
533 parents.push_back(std::make_pair(node, false));
534 node = node->left;
535 }
536 else if (this->comparator(node->item, key)) {
537 parents.push_back(std::make_pair(node, true));
538 node = node->right;
539 }
540 else {
541 break;
542 }
543 }
544
546
547 version_type new_version = roots.size();
548
549 node_counts.push_back(node_counts[version] - 1);
550 based_on.push_back(version);
551
552 // copy the path with the replacement at the end
553
554 path_type new_parents = copy_path(parents, nullptr, new_version);
555
556 node_ptr *path_end_location;
557
558 if (new_parents.empty()) {
559 roots.push_back(nullptr);
560 path_end_location = &roots.back();
561 }
562 else {
563 roots.push_back(new_parents[0].first);
564
565 auto [end, place_right] = new_parents.back();
566
567 if (place_right) {
568 path_end_location = &end->right;
569 }
570 else {
571 path_end_location = &end->left;
572 }
573 }
574
575 // find the replacement and the path to the replacement
576
577 node_ptr left_child = node->left;
578 node_ptr right_child = node->right;
579
580 if (left_child != nullptr && right_child != nullptr) {
581 node_ptr candidate = right_child;
582
583 if (candidate->left == nullptr) {
584 // candidate is successor
585
586 node_ptr replacement = copy_node(candidate, new_version);
587
588 replacement->left = left_child;
589 replacement->sign = node->sign;
590
591 *path_end_location = replacement;
592
593 new_parents.push_back(std::make_pair(replacement, true));
594 }
595 else {
596 path_type min_search_path;
597 node_ptr min_candidate;
598
599 while ((min_candidate = candidate->left) != nullptr) {
600 min_search_path.push_back(std::make_pair(candidate, false));
601 candidate = min_candidate;
602 }
603
604 path_type new_min_path =
605 copy_path(min_search_path, candidate->right, new_version);
606
607 node_ptr replacement = copy_node(candidate, new_version);
608
609 replacement->left = left_child;
610 replacement->right = new_min_path[0].first;
611 replacement->sign = node->sign;
612
613 *path_end_location = replacement;
614
615 new_parents.push_back(std::make_pair(replacement, true));
616
617 for (size_t i = 0; i < new_min_path.size(); ++i) {
618 new_parents.push_back(new_min_path[i]);
619 }
620 }
621 }
622 else if (left_child != nullptr || right_child != nullptr) {
623 node_ptr replacement;
624
625 if (left_child != nullptr) {
626 replacement = copy_node(left_child, new_version);
627 }
628 else {
629 replacement = copy_node(right_child, new_version);
630 }
631
632 *path_end_location = replacement;
633 }
634
635 int parent_index = static_cast<int>(new_parents.size()) - 1;
636
637 while (parent_index >= 0) {
638 auto [parent, remove_was_right] = new_parents[parent_index];
639
640 const typename base::sign_type positive = remove_was_right ? +1 : -1;
641
642 if (parent->sign == 0) {
643 parent->sign = -positive;
644 return std::make_pair(true, new_version);
645 }
646 else if (parent->sign == positive) {
647 parent->sign = 0;
648 // continue propagating
649 }
650 else {
651 // we need to rotate
652
653 const bool child_is_right = !remove_was_right;
654 node_ptr child_original =
655 child_is_right ? parent->right : parent->left;
656 node_ptr child = copy_node(child_original, new_version);
657
658 // child != nullptr, because the parents sign is -positive
659
660 if (child->sign == -positive) {
661 rotate(parent, child, child_is_right, parent_index,
662 new_parents);
663 child->sign = 0;
664 parent->sign = 0;
665 // continue propagating
666 }
667 else if (child->sign == 0) {
668 rotate(parent, child, child_is_right, parent_index,
669 new_parents);
670 child->sign = positive;
671 parent->sign = -positive;
672 return std::make_pair(true, new_version);
673 }
674 else {
675 node_ptr grand_child_original =
676 child_is_right ? child->left : child->right;
677 node_ptr grand_child =
678 copy_node(grand_child_original, new_version);
679
680 double_rotate(parent, child, grand_child, child_is_right,
681 parent_index, new_parents);
682
683 if (grand_child->sign == positive) {
684 parent->sign = 0;
685 child->sign = -positive;
686 }
687 else if (grand_child->sign == -positive) {
688 parent->sign = positive;
689 child->sign = 0;
690 }
691 else {
692 parent->sign = 0;
693 child->sign = 0;
694 }
695
696 grand_child->sign = 0;
697
698 // continue propagating
699 }
700 }
701
702 --parent_index;
703 }
704
705 return std::make_pair(true, new_version);
706}
707
708template <class TKey, class TItem, class TKeyComp, class TAlloc>
710 version_type version) const {
711
712 if (!is_valid_version(version)) {
713 return -1;
714 }
715
716 node_ptr root = roots[version];
717
718 if (root == nullptr) {
719 return -1;
720 }
721
722 int height = 0;
723 std::stack<std::pair<node_ptr, int>> stack;
724 stack.emplace(root, 0);
725
726 while (!stack.empty()) {
727 auto [node, level] = stack.top();
728 stack.pop();
729
730 height = std::max(level, height);
731
732 if (node->left != nullptr) {
733 stack.emplace(node->left, level + 1);
734 }
735
736 if (node->right != nullptr) {
737 stack.emplace(node->right, level + 1);
738 }
739 }
740
741 return height;
742}
743
744} // namespace detail
745} // namespace datastructures
Declarations of the persistent AVL tree.
Base class for avl trees.
Definition _avl_common.hpp:76
avl_sign_type sign_type
Definition _avl_common.hpp:103
TKeyComp comparator_type
Definition _avl_common.hpp:80
TAlloc allocator_type
Definition _avl_common.hpp:81
comparator_type comparator
Definition _avl_common.hpp:105
node_alloc_type allocator
Definition _avl_common.hpp:106
Persitent AVL tree implementation.
Definition _persistent_avl.hpp:97
persistent_avl_tree & operator=(const persistent_avl_tree &other)
Copies another tree.
Definition _persistent_avl.ipp:37
persistent_avl_tree(const typename base::comparator_type &comparator, const typename base::allocator_type &allocator)
Constructs new persistent AVL tree.
Definition _persistent_avl.ipp:23
std::pair< node_ptr, bool > insert(const key_type &key, TCallback &&callback, version_type version, bool do_assign)
Inserts an item to the tree.
node_ptr find(const key_type &key, version_type version) const noexcept
Finds a node with the given key in the given version, if there isn't any returns a nullptr.
Definition _persistent_avl.ipp:291
persistent_avl_version_type version_type
Definition _persistent_avl.hpp:99
std::pair< bool, version_type > erase(const key_type &key, version_type version) noexcept
Erases the node with the given key or does nothing if the key does not exist.
Definition _persistent_avl.ipp:510
void clear_all() noexcept
Erases the whole tree across versions.
Definition _persistent_avl.ipp:319
int get_height(version_type version) const
Returns the height of the tree in the given version.
Definition _persistent_avl.ipp:709
typename base::node_ptr node_ptr
Definition _persistent_avl.hpp:107
typename base::key_type key_type
Definition _persistent_avl.hpp:108
std::vector< node_ptr > list_in_order(version_type version) const noexcept
Returns a vector of nodes sorted by keys.
Definition _persistent_avl.ipp:262
Declarations of the persistent map.
Definition _avl.hpp:14