LAL: Linear Arrangement Library 24.10.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
avl.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2024
7 *
8 * This file is part of Linear Arrangement Library. The full code is available
9 * at:
10 * https://github.com/LAL-project/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * Lluís Alemany Puig (lluis.alemany.puig@upc.edu)
28 * LQMC (Quantitative, Mathematical, and Computational Linguisitcs)
29 * CQL (Complexity and Quantitative Linguistics Lab)
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://cqllab.upc.edu/people/lalemany/
32 *
33 * Ramon Ferrer i Cancho (rferrericancho@cs.upc.edu)
34 * LQMC (Quantitative, Mathematical, and Computational Linguisitcs)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office 220, Omega building
37 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
38 * Webpage: https://cqllab.upc.edu/people/rferrericancho/
39 *
40 ********************************************************************/
41
42#pragma once
43
44// C++ includes
45#if defined DEBUG
46#include <algorithm>
47#include <cassert>
48#endif
49#include <vector>
50#include <cmath>
51
52// lal includes
53#include <lal/detail/macros/basic_convert.hpp>
54
55namespace lal {
56namespace detail {
57
58
72template <typename T>
73class AVL {
74public:
76 struct frequencies {
78 std::size_t counter_equal;
80 std::size_t counter_larger;
82 std::size_t num_nodes_larger;
84 [[nodiscard]] bool operator== (const frequencies& f) const noexcept {
85 return
86 counter_equal == f.counter_equal and
87 counter_larger == f.counter_larger and
88 num_nodes_larger == f.num_nodes_larger;
89 }
91 [[nodiscard]] bool operator!= (const frequencies& f) const noexcept
92 { return not (*this == f); }
93 };
94
95public:
97 ~AVL() noexcept {
99 m_root = nullptr;
100 }
101
108 void clear() noexcept {
110 m_root = nullptr;
111 }
112
118 [[nodiscard]] std::pair<const T&, frequencies> get_largest_value()
119 const noexcept
120 {
121#if defined DEBUG
122 assert(m_root != nullptr);
123#endif
124
125 tree_node *n = m_root;
126 while (n->right != nullptr) { n = n->right; }
127 return {n->key, frequencies{n->node_counter,0,0}};
128 }
129
139 template <bool use_counter>
140 [[nodiscard]] frequencies remove_largest() noexcept {
141 frequencies freqs{0,0,0};
143 return freqs;
144 }
145
155 template <bool use_counter>
156 [[nodiscard]] frequencies remove_smallest() noexcept {
157 frequencies freqs{0,0,0};
158 m_root = remove_leftmost<use_counter>(m_root, nullptr, freqs);
159 return freqs;
160 }
161
167 [[nodiscard]] std::pair<const T&, frequencies> get_smallest_value()
168 const noexcept
169 {
170#if defined DEBUG
171 assert(m_root != nullptr);
172#endif
173
174 frequencies freqs{0,0,0};
175 tree_node *n = m_root;
176 while (n->left != nullptr) {
177 freqs.counter_larger += n->node_counter + n->right_counter();
178 freqs.num_nodes_larger += 1 + n->right_size();
179 n = n->left;
180 }
181 freqs.counter_equal = n->node_counter;
182 freqs.counter_larger += n->right_counter();
183 freqs.num_nodes_larger += n->right_size();
184 return {n->key, freqs};
185 }
186
192 [[nodiscard]] frequencies element_frequency(const T& v) const noexcept
193 {
194 frequencies res{0,0,0};
195 tree_node *n = m_root;
196 while (n != nullptr) {
197 if (v == n->key) {
198 res.counter_equal = n->node_counter;
199 res.counter_larger += n->right_counter();
200 res.num_nodes_larger += n->right_size();
201 return res;
202 }
203 if (v < n->key) {
204 res.counter_larger += n->node_counter + n->right_counter();
205 res.num_nodes_larger += 1 + n->right_size();
206 n = n->left;
207 }
208 else {
209 n = n->right;
210 }
211 }
212 return res;
213 }
214
220 template <typename U>
221 [[nodiscard]] frequencies insert(U&& v) noexcept {
222 frequencies freqs{0,0,0};
223 m_root = insert(nullptr, m_root, std::forward<U>(v), freqs);
224 return freqs;
225 }
226
235 template <bool use_counter>
236 [[nodiscard]] frequencies remove(const T& v) noexcept {
237 frequencies freqs{0,0,0};
238 m_root = remove<use_counter>(m_root, v, freqs);
239 return freqs;
240 }
241
252 template <typename U>
253 void join_sorted_all_greater(U&& v) noexcept {
254 using elem_t = typename std::remove_reference_t<U>::value_type;
255 static constexpr bool rvalue = std::is_same_v<std::vector<elem_t>, U>;
256
257#if defined DEBUG
258 assert(std::is_sorted(v.begin(), v.end()));
259#endif
260
261 // do nothing if there is no data
262 if (v.size() == 0) { return; }
263
264 frequencies dummy{0,0,0};
265 if (v.size() == 1) {
266 m_root = [&]() {
267 if constexpr (rvalue) {
268 return insert(nullptr, m_root, std::forward<T>(v[0]), dummy);
269 }
270 else {
271 return insert(nullptr, m_root, v[0], dummy);
272 }
273 }();
274 return;
275 }
276
277 // Make a tree with the new info and then join the two trees.
278 tree_node *n =
279 _make_tree(std::forward<U>(v), 0, v.size() - 1, nullptr, '0');
280
281 // if our root is empty then the new
282 // node is the root of the new tree
283 if (m_root == nullptr) {
284 m_root = n;
285 return;
286 }
287
288 // easy case, we only had one element in the tree
289 if (m_root->tree_size == 1) {
290 tree_node *r = insert(nullptr, n, std::move(m_root->key), dummy);
291#if defined DEBUG
292 assert(r != nullptr);
293#endif
294 {
295 // update the counter of the leftmost node of
296 // the tree rooted at 'r'.
297 tree_node *lmost = r;
298 while (lmost->left != nullptr) { lmost = lmost->left; }
299 // obviously, copy the node_counter
301 lmost->update_count();
302 while (lmost->parent != nullptr) {
303 lmost = lmost->parent;
304 lmost->update_count();
305 }
306 }
307
309 m_root = r;
310 return;
311 }
312
313 // both 'root' and 'n' have size larger than 2
314#if defined DEBUG
315 assert(m_root->tree_size >= 2 and n->tree_size >= 2);
316#endif
317 m_root =
318 (m_root->height >= n->height ?
319 join_taller(m_root, n) :
321 );
322 }
323
328 [[nodiscard]] std::size_t num_nodes() const noexcept
329 { return (m_root == nullptr ? 0 : m_root->tree_size); }
330
335 [[nodiscard]] std::size_t total_elements() const noexcept
336 { return (m_root == nullptr ? 0 : m_root->tree_counter); }
337
338#if defined __LAL_DEBUG_AVL
340 [[nodiscard]] bool sanity_check() const noexcept {
341 return sanity_check(m_root);
342 }
343
346 void print_tree() const noexcept {
347 print_tree(m_root, "");
348 }
349#endif
350
351private:
353 struct tree_node {
357 std::size_t node_counter = 0;
358
364 std::size_t tree_size = 0;
365
376 std::size_t tree_counter = 0;
377
379 std::size_t height = 0;
386 int64_t balance_factor = 0;
387
389 tree_node *parent = nullptr;
391 tree_node *left = nullptr;
393 tree_node *right = nullptr;
394
403 char side = '0';
404
406 [[nodiscard]] std::size_t left_size() const noexcept
407 { return (left == nullptr ? 0 : left->tree_size); }
409 [[nodiscard]] std::size_t right_size() const noexcept
410 { return (right == nullptr ? 0 : right->tree_size); }
411
413 [[nodiscard]] std::size_t left_counter() const noexcept
414 { return (left == nullptr ? 0 : left->tree_counter); }
416 [[nodiscard]] std::size_t right_counter() const noexcept
417 { return (right == nullptr ? 0 : right->tree_counter); }
418
424 const int64_t lh = (left != nullptr ? to_int64(left->height) : -1);
425 const int64_t rh = (right != nullptr ? to_int64(right->height) : -1);
426 height = to_uint64(std::max(lh, rh) + 1);
427 balance_factor = rh - lh;
428 }
429
434 void update_size() noexcept {
435 tree_size = 1 + left_size() + right_size();
436 }
437
442 void update_count() noexcept {
445 left_counter() +
447 }
448
456 void update() noexcept {
457 update_size();
458 update_count();
460 }
461
478 void replace_with(tree_node * const n) noexcept {
479 if (parent != nullptr) {
480 if (side == 'l') {
481 parent->left = n;
482 }
483 else if (side == 'r') {
484 parent->right = n;
485 }
486 }
487
488 if (n == nullptr) { return; }
489
490#if defined DEBUG
491 assert(left == n or right == n);
492#endif
493
494 n->parent = parent;
495 n->side = side;
496 }
497 };
498
499private:
501 tree_node *m_root = nullptr;
502
503private:
505 void free_node(tree_node * const n) const noexcept {
506 if (n == nullptr) { return; }
507 free_node(n->left);
508 free_node(n->right);
509 delete n;
510 }
511
512 /* ------------------------------- */
513 /* ROTATION AND BALANCING OF NODES */
514
521 [[nodiscard]] tree_node *right_rotation(tree_node * const n) const noexcept {
522#if defined DEBUG
523 assert(n != nullptr);
524#endif
525
526 tree_node *P = n->parent;
527 tree_node *L = n->left;
528
529#if defined DEBUG
530 assert(L != nullptr);
531#endif
532
533 // update n's parent
534 // (notice P might be null, however,
535 // it is null only when n->side != 'r' and 'l')
536 if (n->side == 'r') { P->right = L; }
537 else if (n->side == 'l') { P->left = L; }
538 // parent of L is now parent of n
539 L->parent = P;
540
541 // if n is the left child, then L is
542 // the left child, same for 'right'
543 L->side = n->side;
544
545 // update A's parent, and A's side
546 n->parent = L;
547 n->side = 'r';
548
549 // update node E
550 tree_node *E = L->right;
551 n->left = E;
552 if (E != nullptr) {
553 E->side = 'l';
554 E->parent = n;
555 }
556 L->right = n;
557
558 // update nodes n ...
559 n->update();
560 // ... and L
561 L->update();
562 return L;
563 }
570 [[nodiscard]] tree_node *left_rotation(tree_node * const n) const noexcept {
571#if defined DEBUG
572 assert(n != nullptr);
573#endif
574
575 tree_node *R = n->right;
576
577 // parent of n is now parent of R
578 R->parent = n->parent;
579 // update R's parent
580 if (n->side == 'r') { n->parent->right = R; }
581 else if (n->side == 'l') { n->parent->left = R; }
582 // if R is the left child, then n is
583 // the left child, ...
584 R->side = n->side;
585
586 // update A's parent, and A's side
587 n->parent = R;
588 n->side = 'l';
589
590 // update node E
591 tree_node *E = R->left;
592 n->right = E;
593 if (E != nullptr) {
594 E->side = 'r';
595 E->parent = n;
596 }
597 R->left = n;
598
599 // update nodes n ...
600 n->update();
601 // ... and R
602 R->update();
603 return R;
604 }
611 [[nodiscard]] tree_node *left_left_case(tree_node * const n) const noexcept {
612 return right_rotation(n);
613 }
620 [[nodiscard]] tree_node *left_right_case(tree_node * const n) const noexcept {
621 n->left = left_rotation(n->left);
622 return right_rotation(n);
623 }
630 [[nodiscard]] tree_node *right_right_case(tree_node * const n) const noexcept {
631 return left_rotation(n);
632 }
639 [[nodiscard]] tree_node *right_left_case(tree_node * const n) const noexcept {
640 n->right = right_rotation(n->right);
641 return left_rotation(n);
642 }
651 [[nodiscard]] tree_node *balance(tree_node * const n) const noexcept {
652 if (n == nullptr) { return nullptr; }
653#if defined DEBUG
654 assert(std::abs(n->balance_factor) <= 2);
655#endif
656
657 if (std::abs(n->balance_factor) <= 1) { return n; }
658 return (
659 n->balance_factor == -2 ?
660 (n->left->balance_factor <= 0 ? left_left_case(n) : left_right_case(n)) :
661 (n->right->balance_factor >= 0 ? right_right_case(n) : right_left_case(n))
662 );
663 }
664
665 /* ----------------------------- */
666 /* INSERTION OF A SINGLE ELEMENT */
667
676 template <typename U>
677 [[nodiscard]] tree_node *insert
678 (
679 tree_node * p,
680 tree_node * n,
681 U&& x,
682 frequencies& freqs
683 )
684 const noexcept
685 {
686 // Find where the node with key 'x' could be located in the tree.
687 // If such vertex does not exist, then the result should be where
688 // the new node should be located at.
689 char side = '0';
690 while (n != nullptr and (not (x == n->key))) {
691 p = n;
692 if (x < n->key) {
693 // x < n->key
694 freqs.counter_larger += n->node_counter + n->right_counter();
695 freqs.num_nodes_larger += 1 + n->right_size();
696 n = n->left;
697 side = 'l';
698 }
699 else {
700 // x > n->key
701 n = n->right;
702 side = 'r';
703 }
704 }
705
706 const bool create_a_new_node = (n == nullptr);
707 if (create_a_new_node) {
708 // If the node does not exist, create a new one here
709 n = new tree_node();
710 n->key = std::forward<U>(x);
711 n->left = n->right = nullptr;
712 n->side = side;
713 n->parent = p;
714 if (side == 'l') { p->left = n; }
715 else if (side == 'r') { p->right = n; }
716 n->tree_size = n->node_counter = n->tree_counter = 1;
717 n->height = 0;
718 n->balance_factor = 0;
719 freqs.counter_equal = 1;
720 }
721 else {
722 // If the node exists, update frequencies.
723#if defined DEBUG
724 assert(n->key == x);
725#endif
726 n->node_counter += 1;
727 n->tree_counter += 1;
728
729 // accumulate frequencies
730 freqs.counter_equal = n->node_counter;
731 freqs.counter_larger += n->right_counter();
732 freqs.num_nodes_larger += n->right_size();
733 }
734
735 // This is the case where the while loop did not execute
736 // any iteration and 'n' was null since this function was
737 // called. Simply return the newly-created node.
738 if (side == '0') {
739#if defined DEBUG
740 assert(n != nullptr);
741#endif
742 return n;
743 }
744
745 if (create_a_new_node) {
746 // Update node size, height, balance factor, ...
747 // of all nodes from 'p' to the root all while
748 // balancing the trees.
749 while (p->parent != nullptr) {
750 p->update();
751 p = balance(p);
752 p = p->parent;
753 }
754 }
755 else {
756 // No need to balance nodes in this case --
757 // Traverse all the way up to the root
758 while (p->parent != nullptr) {
759 p->update();
760 p = p->parent;
761 }
762 }
763
764 p->update();
765 return balance(p);
766 }
767
768 /* --------------------------- */
769 /* REMOVAL OF A SINGLE ELEMENT */
770
783 template <bool use_counter_to_remove, bool move_in_leftmost>
784 [[nodiscard]] bool delete_after_move
785 (tree_node *n, tree_node *k, frequencies& freqs)
786 const noexcept
787 {
788 freqs.counter_equal = n->node_counter;
789 if constexpr (move_in_leftmost) {
790 freqs.counter_larger += n->right_counter();
791 freqs.num_nodes_larger += n->right_size();
792 }
793
794 bool delete_n = false;
795 if constexpr (not use_counter_to_remove) {
796 delete_n = true;
797 }
798 else {
799 --n->node_counter;
800 --n->tree_counter;
801 delete_n = n->node_counter == 0;
802 }
803
804 if (k != nullptr) {
805 k->node_counter = n->node_counter;
806 if (delete_n) { k->key = std::move(n->key); }
807 else { k->key = n->key; }
808 }
809 return delete_n;
810 }
811
823 template <bool use_counter_to_remove>
825 (tree_node *n, tree_node *k, frequencies& freqs)
826 const noexcept
827 {
828 if (n == nullptr) { return nullptr; }
829#if defined DEBUG
830 assert(n != nullptr);
831#endif
832
833 const auto original = n;
834
835 // special cases
836 if (n->left == nullptr) {
837 const auto delete_n =
839
840 if (not delete_n) { return original; }
841
842 if (n->parent == nullptr) {
843 const auto l = n->right;
844 delete n;
845 return l;
846 }
847
848 auto p = n->parent;
849 n->replace_with(n->right);
850 delete n;
851 return p->right;
852 }
853
854 // find the rightmost node
855 while (n->left != nullptr) {
856 freqs.counter_larger += n->node_counter + n->right_counter();
857 freqs.num_nodes_larger += 1 + n->right_size();
858 n = n->left;
859 }
860 // store the parent of n
861 auto p = n->parent;
862
863 // delete n, if appropriate
864 const auto delete_n =
866
867 if (delete_n) {
868 n->replace_with(n->right);
869 delete n;
870 }
871
872 // climb up the tree updating the nodes
873 while (p != original) {
874 p->update();
875 p = balance(p);
876 p = p->parent;
877 }
878 p->update();
879 return balance(p);
880 }
892 template <bool use_counter_to_remove>
894 (tree_node *n, tree_node *k, frequencies& freqs)
895 const noexcept
896 {
897 if (n == nullptr) { return nullptr; }
898
899#if defined DEBUG
900 assert(n != nullptr);
901#endif
902
903 const auto original = n;
904
905 // special cases
906 if (n->right == nullptr) {
907 const auto delete_n =
909
910 if (not delete_n) { return original; }
911
912 if (n->parent == nullptr) {
913 const auto l = n->left;
914 delete n;
915 return l;
916 }
917
918 auto p = n->parent;
919 n->replace_with(n->left);
920 delete n;
921 return p->left;
922 }
923
924 // find the rightmost node
925 while (n->right != nullptr) { n = n->right; }
926 // store the parent of n
927 auto p = n->parent;
928
929 // delete n, if appropriate
930 const auto delete_n =
932
933 if (delete_n) {
934 n->replace_with(n->left);
935 delete n;
936 }
937
938 // climb up the tree updating the nodes
939 while (p != original) {
940 p->update();
941 p = balance(p);
942 p = p->parent;
943 }
944 p->update();
945 return balance(p);
946 }
947
957 template <bool use_counter_to_remove>
958 [[nodiscard]] tree_node *remove
959 (tree_node *n, const T& x, frequencies& freqs)
960 const noexcept
961 {
962 if (n == nullptr) {
963 // accumulate frequencies
964 freqs.counter_equal = 0;
965 return nullptr;
966 }
967
968 if (x < n->key) {
969 // accumulate frequencies
970 freqs.counter_larger += n->node_counter + n->right_counter();
971 freqs.num_nodes_larger += 1 + n->right_size();
972
973 // find the element in the left child.
974 n->left = remove<use_counter_to_remove>(n->left, x, freqs);
975 // update this node's size
976 n->update();
977 // balance 'n' to keep the AVL invariant
978 return balance(n);
979 }
980 if (x > n->key) {
981 // find the element in the right child.
982 n->right = remove<use_counter_to_remove>(n->right, x, freqs);
983 // update this node's size
984 n->update();
985 // balance 'n' to keep the AVL invariant
986 return balance(n);
987 }
988
989 // found element at node 'n'
990#if defined DEBUG
991 assert(n->key == x);
992#endif
993
994 freqs.counter_equal = n->node_counter;
995 freqs.counter_larger += n->right_counter();
996 freqs.num_nodes_larger += n->right_size();
997
998 bool completely_remove = false;
999 if constexpr (not use_counter_to_remove) {
1000 // Remove the node with a complete disregard for occurrences.
1001 completely_remove = true;
1002 }
1003 else {
1004#if defined DEBUG
1005 assert(n->tree_counter > 0);
1006 assert(n->node_counter > 0);
1007#endif
1008 // Occurrences are important.
1009 n->tree_counter -= 1;
1010 n->node_counter -= 1;
1011 completely_remove = n->node_counter == 0;
1012 }
1013
1014 if (completely_remove) {
1015 // update tree
1016 tree_node *L = n->left;
1017 tree_node *R = n->right;
1018 if (L == nullptr and R == nullptr) {
1019 // nothing else to do
1020 delete n;
1021 return nullptr;
1022 }
1023 if (L != nullptr and R == nullptr) {
1024 n->replace_with(L);
1025 delete n;
1026 // L is already balanced
1027 return L;
1028 }
1029 if (L == nullptr and R != nullptr) {
1030 n->replace_with(R);
1031 delete n;
1032 // R is already balanced
1033 return R;
1034 }
1035
1036 // L != nullptr and R != nullptr
1037
1038 // find the smallest value in the right subtree,
1039 // or the largest in the left subtree,
1040 // depending on the height
1041 frequencies dummy{0,0,0};
1042 if (L->height < R->height) {
1043 // the node must be moved, so we should not use the counter
1044 n->left = remove_rightmost<false>(L, n, dummy);
1045 }
1046 else {
1047 n->right = remove_leftmost<false>(R, n, dummy);
1048 }
1049 }
1050
1051 n->update();
1052 return balance(n);
1053 }
1054
1055 /* ----------------- */
1056 /* UNION OF TWO AVLS */
1057
1066 [[nodiscard]] tree_node *join_taller(tree_node *T1, tree_node *T2)
1067 const noexcept
1068 {
1069#if defined DEBUG
1070 assert(T1 != nullptr);
1071 assert(T2 != nullptr);
1072 assert(T1->tree_size > 1 and T2->tree_size > 1);
1073#endif
1074
1075 // we need a new node anyway
1076 tree_node *x = new tree_node();
1077
1078 {
1079 // remove left-most element in T2
1080 frequencies dummy{0,0,0};
1081 T2 = remove_leftmost<false>(T2, x, dummy);
1082 }
1083 // find right-most node such that its height is either
1084 // (T2->height) or (T2->height + 1) in T1
1085 const std::size_t h = T2->height;
1086 tree_node *v = T1;
1087 std::size_t hp = v->height;
1088 while (hp > h + 1 and v != nullptr) {
1089 hp = (v->balance_factor == -1 ? hp - 2 : hp - 1);
1090 v = v->right;
1091 }
1092
1093#if defined DEBUG
1094 assert(v != nullptr);
1095#endif
1096
1097 // NOTE: 'u' is allowed to be nullptr!
1098 tree_node *u = v->parent;
1099
1100 x->parent = u;
1101 x->left = v;
1102 x->right = T2;
1103 v->parent = x;
1104 v->side = 'l';
1105 T2->side = 'r';
1106 T2->parent = x;
1107 x->update();
1108
1109 // this is why 'u' is allowed to be nullptr!
1110 if (u == nullptr) {
1111 x->side = '0';
1112 return balance(x);
1113 }
1114
1115 u->right = x;
1116 x->side = 'r';
1117
1118 // climb up the tree rebalancing
1119 x = balance(x);
1120 while (u->parent != nullptr) {
1121 u->update();
1122 u = balance(u);
1123 u = u->parent;
1124 }
1125#if defined DEBUG
1126 assert(u != nullptr);
1127#endif
1128
1129 u->update();
1130 return balance(u);
1131 }
1132
1142 const noexcept
1143 {
1144#if defined DEBUG
1145 assert(T1 != nullptr);
1146 assert(T2 != nullptr);
1147 assert(T1->tree_size > 1 and T2->tree_size > 1);
1148#endif
1149
1150 // we need a new node anyway
1151 tree_node *x = new tree_node();
1152
1153 {
1154 // remove right-most element in T1
1155 frequencies dummy{0,0,0};
1156 T1 = remove_rightmost<false>(T1, x, dummy);
1157 }
1158
1159 // find right-most node such that its height
1160 // is either (T1->height) or (T1->height + 1)
1161 // in T2
1162 const std::size_t h = T1->height;
1163 tree_node *v = T2;
1164 std::size_t hp = v->height;
1165 while (hp > h + 1 and v != nullptr) {
1166 hp = (v->balance_factor == -1 ? hp - 2 : hp - 1);
1167 v = v->left;
1168 }
1169#if defined DEBUG
1170 assert(v != nullptr);
1171#endif
1172
1173 // NOTE: 'u' is allowed to be nullptr!
1174 tree_node *u = v->parent;
1175
1176 x->parent = u;
1177 x->right = v;
1178 x->left = T1;
1179 v->parent = x;
1180 v->side = 'r';
1181 T1->side = 'l';
1182 T1->parent = x;
1183 x->update();
1184
1185 // this is why 'u' is allowed to be nullptr!
1186 if (u == nullptr) {
1187 x->side = '0';
1188 return balance(x);
1189 }
1190
1191 x->side = 'l';
1192 u->left = x;
1193
1194 // climb up the tree rebalancing
1195 x = balance(x);
1196 while (u->parent != nullptr) {
1197 u->update();
1198 u = balance(u);
1199 u = u->parent;
1200 }
1201#if defined DEBUG
1202 assert(u != nullptr);
1203#endif
1204
1205 u->update();
1206 return balance(u);
1207 }
1208
1209 /* ------ */
1210 /* OTHERS */
1211
1224 template <typename U>
1225 [[nodiscard]] tree_node *_make_tree
1226 (U&& v, std::size_t l, std::size_t r, tree_node *p, char s)
1227 const noexcept
1228 {
1229 using elem_t = typename std::remove_reference_t<U>::value_type;
1230 static constexpr bool rvalue = std::is_same_v<std::vector<elem_t>, U>;
1231
1232 // middle point
1233 const std::size_t m = (l + r)/2;
1234 // make a node with the element in the middle
1235 tree_node *n = new tree_node();
1236 if constexpr (rvalue) {
1237 n->key = std::forward<T>(v[m]);
1238 }
1239 else {
1240 n->key = v[m];
1241 }
1242 // make sure pointers are correct
1243 n->parent = p;
1244 n->side = s;
1245 // construct the subtrees
1246 n->left = (l + 1 > m ?
1247 nullptr : _make_tree(std::forward<U>(v), l, m - 1, n, 'l'));
1248 n->right = (m + 1 > r ?
1249 nullptr : _make_tree(std::forward<U>(v), m + 1, r, n, 'r'));
1250 // update the number of occurrences
1251 n->node_counter = 1;
1252 n->update();
1253 // by construction, there is no need to balance the node 'n'
1254 return n;
1255 }
1256
1257private:
1258
1259#if defined __LAL_DEBUG_AVL
1260 [[nodiscard]] std::size_t exhaustive_size(tree_node *n) const noexcept {
1261 if (n == nullptr) { return 0; }
1262 return 1 + exhaustive_size(n->right) + exhaustive_size(n->left);
1263 }
1264 [[nodiscard]] std::size_t exhaustive_occurrences(tree_node *n) const noexcept {
1265 if (n == nullptr) { return 0; }
1266 return n->node_counter +
1267 exhaustive_occurrences(n->right) +
1268 exhaustive_occurrences(n->left);
1269 }
1270 [[nodiscard]] std::size_t exhaustive_height(tree_node *n) const noexcept {
1271 if (n == nullptr) { return 0; }
1272 if (n->left == nullptr and n->right == nullptr) { return 0; }
1273 const auto height_left = exhaustive_height(n->left);
1274 const auto height_right = exhaustive_height(n->right);
1275 return 1 + std::max(height_left, height_right);
1276 }
1277
1278 [[nodiscard]] bool all_smaller_than(tree_node *n, const T& x) const noexcept {
1279 if (n == nullptr) { return true; }
1280 if (n->key > x) { return false; }
1281 return all_smaller_than(n->left, x) and all_smaller_than(n->right, x);
1282 }
1283 [[nodiscard]] bool all_greater_than(tree_node *n, const T& x) const noexcept {
1284 if (n == nullptr) { return true; }
1285 if (n->key < x) { return false; }
1286 return all_greater_than(n->left, x) and all_greater_than(n->right, x);
1287 }
1288 [[nodiscard]] bool check_relations(tree_node *n) const noexcept
1289 {
1290 if (n == nullptr) { return true; }
1291 const bool smaller_left = all_smaller_than(n->left, n->key);
1292 const bool greater_right = all_greater_than(n->right, n->key);
1293 if (not smaller_left or not greater_right) { return false; }
1294 return check_relations(n->left) and check_relations(n->right);
1295 }
1296 [[nodiscard]] bool sanity_check(tree_node *n) const noexcept {
1297 if (n == nullptr) { return true; }
1298
1299 if (not check_relations(n)) {
1300 std::cerr
1301 << "Elements incorrectly placed in the tree.\n"
1302 << " n->key= " << n->key << '\n';
1303 return false;
1304 }
1305
1306 if (std::abs(n->balance_factor) >= 2) {
1307 std::cerr
1308 << "Incorrect balance factor.\n"
1309 << " n->key= " << n->key << '\n'
1310 << " n->balance_factor= " << n->balance_factor << '\n';
1311 return false;
1312 }
1313
1314 // HEIGHT
1315 {
1316 const auto my_height = exhaustive_height(n);
1317 if (my_height != n->height) {
1318 std::cerr
1319 << "Incorrect height.\n"
1320 << " n->key= " << n->key << '\n'
1321 << " n->height= " << n->height << '\n'
1322 << " my_height= " << my_height << '\n';
1323 return false;
1324 }
1325 }
1326
1327 // SIZES
1328 {
1329 const auto my_size = exhaustive_size(n);
1330 if (my_size != n->tree_size) {
1331 std::cerr
1332 << "Incorrect sizes.\n"
1333 << " n->key= " << n->key << '\n'
1334 << " n->tree_size= " << n->tree_size << '\n'
1335 << " my_size= " << my_size << '\n';
1336 return false;
1337 }
1338 }
1339
1340 {
1341 const auto my_size = exhaustive_size(n->left);
1342 if (my_size != n->left_size()) {
1343 std::cerr
1344 << "Incorrect sizes.\n"
1345 << " n->key= " << n->key << '\n'
1346 << " n->left_size()= " << n->left_size() << '\n'
1347 << " my_size= " << my_size << '\n';
1348 return false;
1349 }
1350 }
1351
1352 {
1353 const auto my_size = exhaustive_size(n->right);
1354 if (my_size != n->right_size()) {
1355 std::cerr
1356 << "Incorrect sizes.\n"
1357 << " n->key= " << n->key << '\n'
1358 << " n->right_size()= " << n->right_size() << '\n'
1359 << " my_size= " << my_size << '\n';
1360 return false;
1361 }
1362 }
1363
1364 // OCCURRENCES
1365 {
1366 const auto my_occurrences = exhaustive_occurrences(n);
1367 if (my_occurrences != n->tree_counter) {
1368 std::cerr
1369 << "Incorrect occurrencess.\n"
1370 << " n->key= " << n->key << '\n'
1371 << " n->tree_occurrences= " << n->tree_counter << '\n'
1372 << " my_occurrences= " << my_occurrences << '\n';
1373 return false;
1374 }
1375 }
1376
1377 {
1378 const auto my_occurrences = exhaustive_occurrences(n->left);
1379 if (my_occurrences != n->left_counter()) {
1380 std::cerr
1381 << "Incorrect occurrencess.\n"
1382 << " n->key= " << n->key << '\n'
1383 << " n->left_occurrences()= " << n->left_counter() << '\n'
1384 << " my_occurrences= " << my_occurrences << '\n';
1385 return false;
1386 }
1387 }
1388
1389 {
1390 const auto my_occurrences = exhaustive_occurrences(n->right);
1391 if (my_occurrences != n->right_counter()) {
1392 std::cerr
1393 << "Incorrect occurrencess.\n"
1394 << " n->key= " << n->key << '\n'
1395 << " n->right_occurrences()= " << n->right_counter() << '\n'
1396 << " my_occurrences= " << my_occurrences << '\n';
1397 return false;
1398 }
1399 }
1400
1401 if (n->left != nullptr) {
1402 if (n->left->key > n->key) {
1403 std::cerr
1404 << "Keys do not satisfy the order requirement.\n"
1405 << " n->key= " << n->key << '\n'
1406 << " n->left->key= " << n->left->key << '\n'
1407 << " n->key= " << n->key << '\n';
1408 return false;
1409 }
1410
1411 if (n->left->side != 'l') {
1412 std::cerr << "Wrong side for left child: '" << n->left->side << ".\n";
1413 return false;
1414 }
1415 }
1416
1417 if (n->right != nullptr) {
1418 if (n->right->key < n->key) {
1419 std::cerr
1420 << "Keys do not satisfy the order requirement.\n"
1421 << " n->key= " << n->key << '\n'
1422 << " n->right->key= " << n->right->key
1423 << " n->key= " << n->key << '\n';
1424 return false;
1425 }
1426
1427 if (n->right->side != 'r') {
1428 std::cerr << "Wrong side for right child: '" << n->right->side << ".\n";
1429 return false;
1430 }
1431 }
1432
1433 return sanity_check(n->left) and sanity_check(n->right);
1434 }
1435
1436 void print_tree(tree_node *n, const std::string& tab) const noexcept {
1437 std::cout << tab;
1438
1439 if (n == nullptr) {
1440 std::cout << "∅\n";
1441 return;
1442 }
1443
1444 std::cout
1445 << n->key
1446 << ", nc= " << n->node_counter
1447 << ", s= " << n->side
1448 << ", ls= " << n->left_size()
1449 << ", rs= " << n->right_size()
1450 << ", h= " << n->height
1451 << ", ts= " << n->tree_size
1452 << ", tc= " << n->tree_counter
1453 << ", bf= " << n->balance_factor
1454 << '\n';
1455 print_tree(n->left, tab + "| -l-: ");
1456 print_tree(n->right, tab + "| +r+: ");
1457 }
1458#endif
1459};
1460
1461template <typename T>
1462using AVL_frequencies = typename AVL<T>::frequencies;
1463
1464} // -- namespace detail
1465} // -- namespace lal
Simple class that implements an AVL tree.
Definition avl.hpp:73
bool delete_after_move(tree_node *n, tree_node *k, frequencies &freqs) const noexcept
Moves the contents of n to k, when appropriate.
Definition avl.hpp:785
tree_node * left_right_case(tree_node *const n) const noexcept
Left-right imbalance case.
Definition avl.hpp:620
tree_node * join_taller(tree_node *T1, tree_node *T2) const noexcept
Join two AVL trees.
Definition avl.hpp:1066
tree_node * m_root
Root of this AVL tree.
Definition avl.hpp:501
tree_node * remove(tree_node *n, const T &x, frequencies &freqs) const noexcept
Remove an element from the tree.
Definition avl.hpp:959
~AVL() noexcept
Destructor.
Definition avl.hpp:97
tree_node * left_rotation(tree_node *const n) const noexcept
Performs a left-rotation at node n.
Definition avl.hpp:570
tree_node * remove_rightmost(tree_node *n, tree_node *k, frequencies &freqs) const noexcept
Remove the rightmost node in tree rooted at n.
Definition avl.hpp:894
tree_node * _make_tree(U &&v, std::size_t l, std::size_t r, tree_node *p, char s) const noexcept
Make an AVL tree from the elements in v.
Definition avl.hpp:1226
tree_node * insert(tree_node *p, tree_node *n, U &&x, frequencies &freqs) const noexcept
Insert element 'x' to the tree.
Definition avl.hpp:678
frequencies element_frequency(const T &v) const noexcept
Number of occurrences associated to a given value.
Definition avl.hpp:192
tree_node * balance(tree_node *const n) const noexcept
Balance a node of the AVL tree.
Definition avl.hpp:651
frequencies insert(U &&v) noexcept
Insert a new value v into the tree.
Definition avl.hpp:221
tree_node * right_rotation(tree_node *const n) const noexcept
Performs a right-rotation at node n.
Definition avl.hpp:521
frequencies remove_largest() noexcept
Remove an element from the tree.
Definition avl.hpp:140
std::size_t num_nodes() const noexcept
Size of the tree.
Definition avl.hpp:328
frequencies remove(const T &v) noexcept
Remove an element from the tree.
Definition avl.hpp:236
void free_node(tree_node *const n) const noexcept
Deallocates the memory of node n.
Definition avl.hpp:505
tree_node * left_left_case(tree_node *const n) const noexcept
Left-left imbalance case.
Definition avl.hpp:611
void join_sorted_all_greater(U &&v) noexcept
Add to the tree the elements in the vector v.
Definition avl.hpp:253
tree_node * right_left_case(tree_node *const n) const noexcept
Right-left imbalance case.
Definition avl.hpp:639
void clear() noexcept
Empties the tree.
Definition avl.hpp:108
std::pair< const T &, frequencies > get_smallest_value() const noexcept
Find the largest value.
Definition avl.hpp:167
tree_node * right_right_case(tree_node *const n) const noexcept
Right-right imbalance case.
Definition avl.hpp:630
tree_node * join_shorter(tree_node *T1, tree_node *T2) const noexcept
Join two AVL trees.
Definition avl.hpp:1141
std::size_t total_elements() const noexcept
Total number of elements inserted.
Definition avl.hpp:335
frequencies remove_smallest() noexcept
Remove an element from the tree.
Definition avl.hpp:156
std::pair< const T &, frequencies > get_largest_value() const noexcept
Finds the largest value.
Definition avl.hpp:118
tree_node * remove_leftmost(tree_node *n, tree_node *k, frequencies &freqs) const noexcept
Remove the leftmost node in tree rooted at n.
Definition avl.hpp:825
constexpr int64_t to_int64(const T &t) noexcept
Conversion to int64_t.
Definition basic_convert.hpp:57
constexpr uint64_t to_uint64(const T &t) noexcept
Conversion to uint64_t.
Definition basic_convert.hpp:52
Main namespace of the library.
Definition basic_types.hpp:48
Frequency of a value in the tree v.
Definition avl.hpp:76
bool operator!=(const frequencies &f) const noexcept
Different operator.
Definition avl.hpp:91
std::size_t counter_larger
Number of occurrences of larger elements than v in the tree.
Definition avl.hpp:80
bool operator==(const frequencies &f) const noexcept
Equality comparison.
Definition avl.hpp:84
std::size_t num_nodes_larger
Number of nodes with a key larger than v in the tree.
Definition avl.hpp:82
std::size_t counter_equal
Number of occurrences of v in the tree.
Definition avl.hpp:78
Node of the tree.
Definition avl.hpp:353
void update_height_and_balance_factor() noexcept
Calculate the height and balance factor of this node.
Definition avl.hpp:423
T key
contents of the node
Definition avl.hpp:355
std::size_t left_size() const noexcept
Returns the size of the left subtree.
Definition avl.hpp:406
std::size_t right_size() const noexcept
Returns the size of the right subtree.
Definition avl.hpp:409
tree_node * left
Pointer to the left subtree.
Definition avl.hpp:391
void update_size() noexcept
Computes the size of the subtree rooted at this node.
Definition avl.hpp:434
void update() noexcept
Updates information of this node.
Definition avl.hpp:456
void update_count() noexcept
Computes the size of the subtree rooted at this node.
Definition avl.hpp:442
std::size_t height
Maximum height of the left and right subtrees' height.
Definition avl.hpp:379
std::size_t tree_counter
Total number of occurrences in the nodes in the subtree rooted at this node.
Definition avl.hpp:376
void replace_with(tree_node *const n) noexcept
Replace this node with either its left or right child.
Definition avl.hpp:478
std::size_t node_counter
Amount of occurrences of key.
Definition avl.hpp:357
std::size_t right_counter() const noexcept
Returns the size of the right subtree.
Definition avl.hpp:416
std::size_t left_counter() const noexcept
Returns the total occurrences of the left subtree.
Definition avl.hpp:413
char side
Side of this node with respect to its parent.
Definition avl.hpp:403
int64_t balance_factor
Balance factor of a node:
Definition avl.hpp:386
std::size_t tree_size
Amount of nodes in the subtree rooted at this node.
Definition avl.hpp:364
tree_node * right
Pointer to the right subtree.
Definition avl.hpp:393
tree_node * parent
Pointer to the parent of this node.
Definition avl.hpp:389