LAL: Linear Arrangement Library 23.01.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 - 2023
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 (lalemany@cs.upc.edu)
28 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
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 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office S124, 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 <cinttypes>
50#include <vector>
51#include <cmath>
52
53// lal includes
54#include <lal/detail/macros/basic_convert.hpp>
55
56namespace lal {
57namespace detail {
58
59
73template <typename T>
74class AVL {
75public:
77 struct frequencies {
79 std::size_t counter_equal;
81 std::size_t counter_larger;
83 std::size_t num_nodes_larger;
85 bool operator== (const frequencies& f) const noexcept {
86 return
87 counter_equal == f.counter_equal and
88 counter_larger == f.counter_larger and
89 num_nodes_larger == f.num_nodes_larger;
90 }
92 bool operator!= (const frequencies& f) const noexcept
93 { return not (*this == f); }
94 };
95
96public:
98 ~AVL() noexcept {
100 m_root = nullptr;
101 }
102
109 void clear() noexcept {
111 m_root = nullptr;
112 }
113
119 [[nodiscard]]
120 std::pair<const T&, frequencies> get_largest_value() const noexcept {
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>
141 frequencies freqs{0,0,0};
142 m_root = remove_rightmost<use_counter>(m_root, nullptr, freqs);
143 return freqs;
144 }
145
155 template <bool use_counter>
157 frequencies freqs{0,0,0};
158 m_root = remove_leftmost<use_counter>(m_root, nullptr, freqs);
159 return freqs;
160 }
161
167 [[nodiscard]]
168 std::pair<const T&, frequencies> get_smallest_value() const noexcept {
169#if defined DEBUG
170 assert(m_root != nullptr);
171#endif
172
173 frequencies freqs{0,0,0};
174 tree_node *n = m_root;
175 while (n->left != nullptr) {
176 freqs.counter_larger += n->node_counter + n->right_counter();
177 freqs.num_nodes_larger += 1 + n->right_size();
178 n = n->left;
179 }
180 freqs.counter_equal = n->node_counter;
181 freqs.counter_larger += n->right_counter();
182 freqs.num_nodes_larger += n->right_size();
183 return {n->key, freqs};
184 }
185
191 [[nodiscard]] frequencies element_frequency(const T& v) const noexcept
192 {
193 frequencies res{0,0,0};
194 tree_node *n = m_root;
195 while (n != nullptr) {
196 if (v == n->key) {
197 res.counter_equal = n->node_counter;
198 res.counter_larger += n->right_counter();
199 res.num_nodes_larger += n->right_size();
200 return res;
201 }
202 if (v < n->key) {
203 res.counter_larger += n->node_counter + n->right_counter();
204 res.num_nodes_larger += 1 + n->right_size();
205 n = n->left;
206 }
207 else {
208 n = n->right;
209 }
210 }
211 return res;
212 }
213
219 template <typename U>
220 [[nodiscard]] frequencies insert(U&& v) noexcept {
221 frequencies freqs{0,0,0};
222 m_root = insert(nullptr, m_root, std::forward<U>(v), freqs);
223 return freqs;
224 }
225
234 template <bool use_counter>
235 [[nodiscard]] frequencies remove(const T& v) noexcept {
236 frequencies freqs{0,0,0};
237 m_root = remove<use_counter>(m_root, v, freqs);
238 return freqs;
239 }
240
251 template <typename U>
252 void join_sorted_all_greater(U&& v) noexcept {
253 using elem_t = typename std::remove_reference_t<U>::value_type;
254 static constexpr bool rvalue = std::is_same_v<std::vector<elem_t>, U>;
255
256#if defined DEBUG
257 assert(std::is_sorted(v.begin(), v.end()));
258#endif
259
260 // do nothing if there is no data
261 if (v.size() == 0) { return; }
262
263 frequencies dummy{0,0,0};
264 if (v.size() == 1) {
265 m_root = [&]() {
266 if constexpr (rvalue) {
267 return insert(nullptr, m_root, std::forward<T>(v[0]), dummy);
268 }
269 else {
270 return insert(nullptr, m_root, v[0], dummy);
271 }
272 }();
273 return;
274 }
275
276 // Make a tree with the new info and then join the two trees.
277 tree_node *n =
278 _make_tree(std::forward<U>(v), 0, v.size() - 1, nullptr, '0');
279
280 // if our root is empty then the new
281 // node is the root of the new tree
282 if (m_root == nullptr) {
283 m_root = n;
284 return;
285 }
286
287 // easy case, we only had one element in the tree
288 if (m_root->tree_size == 1) {
289 tree_node *r = insert(nullptr, n, std::move(m_root->key), dummy);
290#if defined DEBUG
291 assert(r != nullptr);
292#endif
293 {
294 // update the counter of the leftmost node of
295 // the tree rooted at 'r'.
296 tree_node *lmost = r;
297 while (lmost->left != nullptr) { lmost = lmost->left; }
298 // obviously, copy the node_counter
300 lmost->update_count();
301 while (lmost->parent != nullptr) {
302 lmost = lmost->parent;
303 lmost->update_count();
304 }
305 }
306
308 m_root = r;
309 return;
310 }
311
312 // both 'root' and 'n' have size larger than 2
313#if defined DEBUG
314 assert(m_root->tree_size >= 2 and n->tree_size >= 2);
315#endif
316 m_root =
317 (m_root->height >= n->height ?
318 join_taller(m_root, n) :
320 );
321 }
322
327 std::size_t num_nodes() const noexcept
328 { return (m_root == nullptr ? 0 : m_root->tree_size); }
329
334 std::size_t total_elements() const noexcept
335 { return (m_root == nullptr ? 0 : m_root->tree_counter); }
336
337#if defined __LAL_INSPECT
339 bool sanity_check() const noexcept {
340 return sanity_check(m_root);
341 }
342
345 void print_tree() const noexcept {
346 print_tree(m_root, "");
347 }
348#endif
349
350private:
352 struct tree_node {
356 std::size_t node_counter = 0;
357
363 std::size_t tree_size = 0;
364
375 std::size_t tree_counter = 0;
376
378 std::size_t height = 0;
385 int64_t balance_factor = 0;
386
388 tree_node *parent = nullptr;
390 tree_node *left = nullptr;
392 tree_node *right = nullptr;
393
402 char side = '0';
403
405 [[nodiscard]] std::size_t left_size() const noexcept
406 { return (left == nullptr ? 0 : left->tree_size); }
408 [[nodiscard]] std::size_t right_size() const noexcept
409 { return (right == nullptr ? 0 : right->tree_size); }
410
412 [[nodiscard]] std::size_t left_counter() const noexcept
413 { return (left == nullptr ? 0 : left->tree_counter); }
415 [[nodiscard]] std::size_t right_counter() const noexcept
416 { return (right == nullptr ? 0 : right->tree_counter); }
417
423 const int64_t lh = (left != nullptr ? to_int64(left->height) : -1);
424 const int64_t rh = (right != nullptr ? to_int64(right->height) : -1);
425 height = to_uint64(std::max(lh, rh) + 1);
426 balance_factor = rh - lh;
427 }
428
433 void update_size() noexcept {
434 tree_size = 1 + left_size() + right_size();
435 }
436
441 void update_count() noexcept {
444 left_counter() +
446 }
447
455 void update() noexcept {
456 update_size();
457 update_count();
459 }
460
477 void replace_with(tree_node *n) noexcept {
478 if (parent != nullptr) {
479 if (side == 'l') {
480 parent->left = n;
481 }
482 else if (side == 'r') {
483 parent->right = n;
484 }
485 }
486
487 if (n == nullptr) { return; }
488
489#if defined DEBUG
490 assert(left == n or right == n);
491#endif
492
493 n->parent = parent;
494 n->side = side;
495 }
496 };
497
498private:
500 tree_node *m_root = nullptr;
501
502private:
504 void free_node(tree_node *n) const noexcept {
505 if (n == nullptr) { return; }
506 free_node(n->left);
507 free_node(n->right);
508 delete n;
509 }
510
511 /* ------------------------------- */
512 /* ROTATION AND BALANCING OF NODES */
513
520 [[nodiscard]] tree_node *right_rotation(tree_node *n) const noexcept {
521#if defined DEBUG
522 assert(n != nullptr);
523#endif
524
525 tree_node *P = n->parent;
526 tree_node *L = n->left;
527
528#if defined DEBUG
529 assert(L != nullptr);
530#endif
531
532 // update n's parent
533 // (notice P might be null, however,
534 // it is null only when n->side != 'r' and 'l')
535 if (n->side == 'r') { P->right = L; }
536 else if (n->side == 'l') { P->left = L; }
537 // parent of L is now parent of n
538 L->parent = P;
539
540 // if n is the left child, then L is
541 // the left child, same for 'right'
542 L->side = n->side;
543
544 // update A's parent, and A's side
545 n->parent = L;
546 n->side = 'r';
547
548 // update node E
549 tree_node *E = L->right;
550 n->left = E;
551 if (E != nullptr) {
552 E->side = 'l';
553 E->parent = n;
554 }
555 L->right = n;
556
557 // update nodes n ...
558 n->update();
559 // ... and L
560 L->update();
561 return L;
562 }
569 [[nodiscard]] tree_node *left_rotation(tree_node *n) const noexcept {
570#if defined DEBUG
571 assert(n != nullptr);
572#endif
573
574 tree_node *R = n->right;
575
576 // parent of n is now parent of R
577 R->parent = n->parent;
578 // update R's parent
579 if (n->side == 'r') { n->parent->right = R; }
580 else if (n->side == 'l') { n->parent->left = R; }
581 // if R is the left child, then n is
582 // the left child, ...
583 R->side = n->side;
584
585 // update A's parent, and A's side
586 n->parent = R;
587 n->side = 'l';
588
589 // update node E
590 tree_node *E = R->left;
591 n->right = E;
592 if (E != nullptr) {
593 E->side = 'r';
594 E->parent = n;
595 }
596 R->left = n;
597
598 // update nodes n ...
599 n->update();
600 // ... and R
601 R->update();
602 return R;
603 }
610 [[nodiscard]] tree_node *left_left_case(tree_node *n) const noexcept {
611 return right_rotation(n);
612 }
619 [[nodiscard]] tree_node *left_right_case(tree_node *n) const noexcept {
620 n->left = left_rotation(n->left);
621 return right_rotation(n);
622 }
629 [[nodiscard]] tree_node *right_right_case(tree_node *n) const noexcept {
630 return left_rotation(n);
631 }
638 [[nodiscard]] tree_node *right_left_case(tree_node *n) const noexcept {
639 n->right = right_rotation(n->right);
640 return left_rotation(n);
641 }
650 [[nodiscard]] tree_node *balance(tree_node *n) const noexcept {
651 if (n == nullptr) { return nullptr; }
652#if defined DEBUG
653 assert(std::abs(n->balance_factor) <= 2);
654#endif
655
656 if (std::abs(n->balance_factor) <= 1) { return n; }
657 return (
658 n->balance_factor == -2 ?
659 (n->left->balance_factor <= 0 ? left_left_case(n) : left_right_case(n)) :
660 (n->right->balance_factor >= 0 ? right_right_case(n) : right_left_case(n))
661 );
662 }
663
664 /* ----------------------------- */
665 /* INSERTION OF A SINGLE ELEMENT */
666
675 template <typename U>
676 [[nodiscard]]
678 const noexcept
679 {
680 // Find where the node with key 'x' could be located in the tree.
681 // If such vertex does not exist, then the result should be where
682 // the new node should be located at.
683 char side = '0';
684 while (n != nullptr and (not (x == n->key))) {
685 p = n;
686 if (x < n->key) {
687 // x < n->key
688 freqs.counter_larger += n->node_counter + n->right_counter();
689 freqs.num_nodes_larger += 1 + n->right_size();
690 n = n->left;
691 side = 'l';
692 }
693 else {
694 // x > n->key
695 n = n->right;
696 side = 'r';
697 }
698 }
699
700 const bool create_a_new_node = (n == nullptr);
701 if (create_a_new_node) {
702 // If the node does not exist, create a new one here
703 n = new tree_node();
704 n->key = std::forward<U>(x);
705 n->left = n->right = nullptr;
706 n->side = side;
707 n->parent = p;
708 if (side == 'l') { p->left = n; }
709 else if (side == 'r') { p->right = n; }
710 n->tree_size = n->node_counter = n->tree_counter = 1;
711 n->height = 0;
712 n->balance_factor = 0;
713 freqs.counter_equal = 1;
714 }
715 else {
716 // If the node exists, update frequencies.
717#if defined DEBUG
718 assert(n->key == x);
719#endif
720 n->node_counter += 1;
721 n->tree_counter += 1;
722
723 // accumulate frequencies
724 freqs.counter_equal = n->node_counter;
725 freqs.counter_larger += n->right_counter();
726 freqs.num_nodes_larger += n->right_size();
727 }
728
729 // This is the case where the while loop did not execute
730 // any iteration and 'n' was null since this function was
731 // called. Simply return the newly-created node.
732 if (side == '0') {
733#if defined DEBUG
734 assert(n != nullptr);
735#endif
736 return n;
737 }
738
739 if (create_a_new_node) {
740 // Update node size, height, balance factor, ...
741 // of all nodes from 'p' to the root all while
742 // balancing the trees.
743 while (p->parent != nullptr) {
744 p->update();
745 p = balance(p);
746 p = p->parent;
747 }
748 }
749 else {
750 // No need to balance nodes in this case --
751 // Traverse all the way up to the root
752 while (p->parent != nullptr) {
753 p->update();
754 p = p->parent;
755 }
756 }
757
758 p->update();
759 return balance(p);
760 }
761
762 /* --------------------------- */
763 /* REMOVAL OF A SINGLE ELEMENT */
764
777 template <bool use_counter_to_remove, bool move_in_leftmost>
778 bool delete_after_move(tree_node *n, tree_node *k, frequencies& freqs) const noexcept
779 {
780 freqs.counter_equal = n->node_counter;
781 if constexpr (move_in_leftmost) {
782 freqs.counter_larger += n->right_counter();
783 freqs.num_nodes_larger += n->right_size();
784 }
785
786 bool delete_n = false;
787 if constexpr (not use_counter_to_remove) {
788 delete_n = true;
789 }
790 else {
791 --n->node_counter;
792 --n->tree_counter;
793 delete_n = n->node_counter == 0;
794 }
795
796 if (k != nullptr) {
797 k->node_counter = n->node_counter;
798 if (delete_n) { k->key = std::move(n->key); }
799 else { k->key = n->key; }
800 }
801 return delete_n;
802 }
803
815 template <bool use_counter_to_remove>
816 [[nodiscard]]
818 const noexcept
819 {
820 if (n == nullptr) { return nullptr; }
821#if defined DEBUG
822 assert(n != nullptr);
823#endif
824
825 const auto original = n;
826
827 // special cases
828 if (n->left == nullptr) {
829 const auto delete_n =
830 delete_after_move<use_counter_to_remove, true>(n, k, freqs);
831
832 if (not delete_n) { return original; }
833
834 if (n->parent == nullptr) {
835 const auto l = n->right;
836 delete n;
837 return l;
838 }
839
840 auto p = n->parent;
841 n->replace_with(n->right);
842 delete n;
843 return p->right;
844 }
845
846 // find the rightmost node
847 while (n->left != nullptr) {
848 freqs.counter_larger += n->node_counter + n->right_counter();
849 freqs.num_nodes_larger += 1 + n->right_size();
850 n = n->left;
851 }
852 // store the parent of n
853 auto p = n->parent;
854
855 // delete n, if appropriate
856 const auto delete_n =
857 delete_after_move<use_counter_to_remove, true>(n, k, freqs);
858
859 if (delete_n) {
860 n->replace_with(n->right);
861 delete n;
862 }
863
864 // climb up the tree updating the nodes
865 while (p != original) {
866 p->update();
867 p = balance(p);
868 p = p->parent;
869 }
870 p->update();
871 return balance(p);
872 }
884 template <bool use_counter_to_remove>
885 [[nodiscard]]
887 const noexcept
888 {
889 if (n == nullptr) { return nullptr; }
890
891#if defined DEBUG
892 assert(n != nullptr);
893#endif
894
895 const auto original = n;
896
897 // special cases
898 if (n->right == nullptr) {
899 const auto delete_n =
900 delete_after_move<use_counter_to_remove, false>(n, k, freqs);
901
902 if (not delete_n) { return original; }
903
904 if (n->parent == nullptr) {
905 const auto l = n->left;
906 delete n;
907 return l;
908 }
909
910 auto p = n->parent;
911 n->replace_with(n->left);
912 delete n;
913 return p->left;
914 }
915
916 // find the rightmost node
917 while (n->right != nullptr) { n = n->right; }
918 // store the parent of n
919 auto p = n->parent;
920
921 // delete n, if appropriate
922 const auto delete_n =
923 delete_after_move<use_counter_to_remove, false>(n, k, freqs);
924
925 if (delete_n) {
926 n->replace_with(n->left);
927 delete n;
928 }
929
930 // climb up the tree updating the nodes
931 while (p != original) {
932 p->update();
933 p = balance(p);
934 p = p->parent;
935 }
936 p->update();
937 return balance(p);
938 }
939
949 template <bool use_counter_to_remove>
950 [[nodiscard]]
951 tree_node *remove(tree_node *n, const T& x, frequencies& freqs)
952 const noexcept
953 {
954 if (n == nullptr) {
955 // accumulate frequencies
956 freqs.counter_equal = 0;
957 return nullptr;
958 }
959
960 if (x < n->key) {
961 // accumulate frequencies
962 freqs.counter_larger += n->node_counter + n->right_counter();
963 freqs.num_nodes_larger += 1 + n->right_size();
964
965 // find the element in the left child.
966 n->left = remove<use_counter_to_remove>(n->left, x, freqs);
967 // update this node's size
968 n->update();
969 // balance 'n' to keep the AVL invariant
970 return balance(n);
971 }
972 if (x > n->key) {
973 // find the element in the right child.
974 n->right = remove<use_counter_to_remove>(n->right, 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
981 // found element at node 'n'
982#if defined DEBUG
983 assert(n->key == x);
984#endif
985
986 freqs.counter_equal = n->node_counter;
987 freqs.counter_larger += n->right_counter();
988 freqs.num_nodes_larger += n->right_size();
989
990 bool completely_remove = false;
991 if constexpr (not use_counter_to_remove) {
992 // Remove the node with a complete disregard for occurrences.
993 completely_remove = true;
994 }
995 else {
996#if defined DEBUG
997 assert(n->tree_counter > 0);
998 assert(n->node_counter > 0);
999#endif
1000 // Occurrences are important.
1001 n->tree_counter -= 1;
1002 n->node_counter -= 1;
1003 completely_remove = n->node_counter == 0;
1004 }
1005
1006 if (completely_remove) {
1007 // update tree
1008 tree_node *L = n->left;
1009 tree_node *R = n->right;
1010 if (L == nullptr and R == nullptr) {
1011 // nothing else to do
1012 delete n;
1013 return nullptr;
1014 }
1015 if (L != nullptr and R == nullptr) {
1016 n->replace_with(L);
1017 delete n;
1018 // L is already balanced
1019 return L;
1020 }
1021 if (L == nullptr and R != nullptr) {
1022 n->replace_with(R);
1023 delete n;
1024 // R is already balanced
1025 return R;
1026 }
1027
1028 // L != nullptr and R != nullptr
1029
1030 // find the smallest value in the right subtree,
1031 // or the largest in the left subtree,
1032 // depending on the height
1033 frequencies dummy{0,0,0};
1034 if (L->height < R->height) {
1035 // the node must be moved, so we should not use the counter
1036 n->left = remove_rightmost<false>(L, n, dummy);
1037 }
1038 else {
1039 n->right = remove_leftmost<false>(R, n, dummy);
1040 }
1041 }
1042
1043 n->update();
1044 return balance(n);
1045 }
1046
1047 /* ----------------- */
1048 /* UNION OF TWO AVLS */
1049
1058 [[nodiscard]]
1059 tree_node *join_taller(tree_node *T1, tree_node *T2) const noexcept {
1060#if defined DEBUG
1061 assert(T1 != nullptr);
1062 assert(T2 != nullptr);
1063 assert(T1->tree_size > 1 and T2->tree_size > 1);
1064#endif
1065
1066 // we need a new node anyway
1067 tree_node *x = new tree_node();
1068
1069 {
1070 // remove left-most element in T2
1071 frequencies dummy{0,0,0};
1072 T2 = remove_leftmost<false>(T2, x, dummy);
1073 }
1074 // find right-most node such that its height is either
1075 // (T2->height) or (T2->height + 1) in T1
1076 const std::size_t h = T2->height;
1077 tree_node *v = T1;
1078 std::size_t hp = v->height;
1079 while (hp > h + 1 and v != nullptr) {
1080 hp = (v->balance_factor == -1 ? hp - 2 : hp - 1);
1081 v = v->right;
1082 }
1083
1084#if defined DEBUG
1085 assert(v != nullptr);
1086#endif
1087
1088 // NOTE: 'u' is allowed to be nullptr!
1089 tree_node *u = v->parent;
1090
1091 x->parent = u;
1092 x->left = v;
1093 x->right = T2;
1094 v->parent = x;
1095 v->side = 'l';
1096 T2->side = 'r';
1097 T2->parent = x;
1098 x->update();
1099
1100 // this is why 'u' is allowed to be nullptr!
1101 if (u == nullptr) {
1102 x->side = '0';
1103 return balance(x);
1104 }
1105
1106 u->right = x;
1107 x->side = 'r';
1108
1109 // climb up the tree rebalancing
1110 x = balance(x);
1111 while (u->parent != nullptr) {
1112 u->update();
1113 u = balance(u);
1114 u = u->parent;
1115 }
1116#if defined DEBUG
1117 assert(u != nullptr);
1118#endif
1119
1120 u->update();
1121 return balance(u);
1122 }
1123
1132 [[nodiscard]]
1133 tree_node *join_shorter(tree_node *T1, tree_node *T2) const noexcept {
1134#if defined DEBUG
1135 assert(T1 != nullptr);
1136 assert(T2 != nullptr);
1137 assert(T1->tree_size > 1 and T2->tree_size > 1);
1138#endif
1139
1140 // we need a new node anyway
1141 tree_node *x = new tree_node();
1142
1143 {
1144 // remove right-most element in T1
1145 frequencies dummy{0,0,0};
1146 T1 = remove_rightmost<false>(T1, x, dummy);
1147 }
1148
1149 // find right-most node such that its height
1150 // is either (T1->height) or (T1->height + 1)
1151 // in T2
1152 const std::size_t h = T1->height;
1153 tree_node *v = T2;
1154 std::size_t hp = v->height;
1155 while (hp > h + 1 and v != nullptr) {
1156 hp = (v->balance_factor == -1 ? hp - 2 : hp - 1);
1157 v = v->left;
1158 }
1159#if defined DEBUG
1160 assert(v != nullptr);
1161#endif
1162
1163 // NOTE: 'u' is allowed to be nullptr!
1164 tree_node *u = v->parent;
1165
1166 x->parent = u;
1167 x->right = v;
1168 x->left = T1;
1169 v->parent = x;
1170 v->side = 'r';
1171 T1->side = 'l';
1172 T1->parent = x;
1173 x->update();
1174
1175 // this is why 'u' is allowed to be nullptr!
1176 if (u == nullptr) {
1177 x->side = '0';
1178 return balance(x);
1179 }
1180
1181 x->side = 'l';
1182 u->left = x;
1183
1184 // climb up the tree rebalancing
1185 x = balance(x);
1186 while (u->parent != nullptr) {
1187 u->update();
1188 u = balance(u);
1189 u = u->parent;
1190 }
1191#if defined DEBUG
1192 assert(u != nullptr);
1193#endif
1194
1195 u->update();
1196 return balance(u);
1197 }
1198
1199 /* ------ */
1200 /* OTHERS */
1201
1214 template <typename U>
1215 [[nodiscard]] tree_node *_make_tree
1216 (U&& v, std::size_t l, std::size_t r, tree_node *p, char s)
1217 const noexcept
1218 {
1219 using elem_t = typename std::remove_reference_t<U>::value_type;
1220 static constexpr bool rvalue = std::is_same_v<std::vector<elem_t>, U>;
1221
1222 // middle point
1223 const std::size_t m = (l + r)/2;
1224 // make a node with the element in the middle
1225 tree_node *n = new tree_node();
1226 if constexpr (rvalue) {
1227 n->key = std::forward<T>(v[m]);
1228 }
1229 else {
1230 n->key = v[m];
1231 }
1232 // make sure pointers are correct
1233 n->parent = p;
1234 n->side = s;
1235 // construct the subtrees
1236 n->left = (l + 1 > m ?
1237 nullptr : _make_tree(std::forward<U>(v), l, m - 1, n, 'l'));
1238 n->right = (m + 1 > r ?
1239 nullptr : _make_tree(std::forward<U>(v), m + 1, r, n, 'r'));
1240 // update the number of occurrences
1241 n->node_counter = 1;
1242 n->update();
1243 // by construction, there is no need to balance the node 'n'
1244 return n;
1245 }
1246
1247private:
1248
1249#if defined __LAL_INSPECT
1250 std::size_t exhaustive_size(tree_node *n) const noexcept {
1251 if (n == nullptr) { return 0; }
1252 return 1 + exhaustive_size(n->right) + exhaustive_size(n->left);
1253 }
1254 std::size_t exhaustive_occurrences(tree_node *n) const noexcept {
1255 if (n == nullptr) { return 0; }
1256 return n->node_counter +
1257 exhaustive_occurrences(n->right) +
1258 exhaustive_occurrences(n->left);
1259 }
1260 std::size_t exhaustive_height(tree_node *n) const noexcept {
1261 if (n == nullptr) { return 0; }
1262 if (n->left == nullptr and n->right == nullptr) { return 0; }
1263 const auto height_left = exhaustive_height(n->left);
1264 const auto height_right = exhaustive_height(n->right);
1265 return 1 + std::max(height_left, height_right);
1266 }
1267
1268 bool all_smaller_than(tree_node *n, const T& x) const noexcept {
1269 if (n == nullptr) { return true; }
1270 if (n->key > x) { return false; }
1271 return all_smaller_than(n->left, x) and all_smaller_than(n->right, x);
1272 }
1273 bool all_greater_than(tree_node *n, const T& x) const noexcept {
1274 if (n == nullptr) { return true; }
1275 if (n->key < x) { return false; }
1276 return all_greater_than(n->left, x) and all_greater_than(n->right, x);
1277 }
1278 bool check_relations(tree_node *n) const noexcept
1279 {
1280 if (n == nullptr) { return true; }
1281 const bool smaller_left = all_smaller_than(n->left, n->key);
1282 const bool greater_right = all_greater_than(n->right, n->key);
1283 if (not smaller_left or not greater_right) { return false; }
1284 return check_relations(n->left) and check_relations(n->right);
1285 }
1286 bool sanity_check(tree_node *n) const noexcept {
1287 if (n == nullptr) { return true; }
1288
1289 if (not check_relations(n)) {
1290 std::cerr
1291 << "Elements incorrectly placed in the tree.\n"
1292 << " n->key= " << n->key << '\n';
1293 return false;
1294 }
1295
1296 if (std::abs(n->balance_factor) >= 2) {
1297 std::cerr
1298 << "Incorrect balance factor.\n"
1299 << " n->key= " << n->key << '\n'
1300 << " n->balance_factor= " << n->balance_factor << '\n';
1301 return false;
1302 }
1303
1304 // HEIGHT
1305 {
1306 const auto my_height = exhaustive_height(n);
1307 if (my_height != n->height) {
1308 std::cerr
1309 << "Incorrect height.\n"
1310 << " n->key= " << n->key << '\n'
1311 << " n->height= " << n->height << '\n'
1312 << " my_height= " << my_height << '\n';
1313 return false;
1314 }
1315 }
1316
1317 // SIZES
1318 {
1319 const auto my_size = exhaustive_size(n);
1320 if (my_size != n->tree_size) {
1321 std::cerr
1322 << "Incorrect sizes.\n"
1323 << " n->key= " << n->key << '\n'
1324 << " n->tree_size= " << n->tree_size << '\n'
1325 << " my_size= " << my_size << '\n';
1326 return false;
1327 }
1328 }
1329
1330 {
1331 const auto my_size = exhaustive_size(n->left);
1332 if (my_size != n->left_size()) {
1333 std::cerr
1334 << "Incorrect sizes.\n"
1335 << " n->key= " << n->key << '\n'
1336 << " n->left_size()= " << n->left_size() << '\n'
1337 << " my_size= " << my_size << '\n';
1338 return false;
1339 }
1340 }
1341
1342 {
1343 const auto my_size = exhaustive_size(n->right);
1344 if (my_size != n->right_size()) {
1345 std::cerr
1346 << "Incorrect sizes.\n"
1347 << " n->key= " << n->key << '\n'
1348 << " n->right_size()= " << n->right_size() << '\n'
1349 << " my_size= " << my_size << '\n';
1350 return false;
1351 }
1352 }
1353
1354 // OCCURRENCES
1355 {
1356 const auto my_occurrences = exhaustive_occurrences(n);
1357 if (my_occurrences != n->tree_counter) {
1358 std::cerr
1359 << "Incorrect occurrencess.\n"
1360 << " n->key= " << n->key << '\n'
1361 << " n->tree_occurrences= " << n->tree_counter << '\n'
1362 << " my_occurrences= " << my_occurrences << '\n';
1363 return false;
1364 }
1365 }
1366
1367 {
1368 const auto my_occurrences = exhaustive_occurrences(n->left);
1369 if (my_occurrences != n->left_counter()) {
1370 std::cerr
1371 << "Incorrect occurrencess.\n"
1372 << " n->key= " << n->key << '\n'
1373 << " n->left_occurrences()= " << n->left_counter() << '\n'
1374 << " my_occurrences= " << my_occurrences << '\n';
1375 return false;
1376 }
1377 }
1378
1379 {
1380 const auto my_occurrences = exhaustive_occurrences(n->right);
1381 if (my_occurrences != n->right_counter()) {
1382 std::cerr
1383 << "Incorrect occurrencess.\n"
1384 << " n->key= " << n->key << '\n'
1385 << " n->right_occurrences()= " << n->right_counter() << '\n'
1386 << " my_occurrences= " << my_occurrences << '\n';
1387 return false;
1388 }
1389 }
1390
1391 if (n->left != nullptr) {
1392 if (n->left->key > n->key) {
1393 std::cerr
1394 << "Keys do not satisfy the order requirement.\n"
1395 << " n->key= " << n->key << '\n'
1396 << " n->left->key= " << n->left->key << '\n'
1397 << " n->key= " << n->key << '\n';
1398 return false;
1399 }
1400
1401 if (n->left->side != 'l') {
1402 std::cerr << "Wrong side for left child: '" << n->left->side << ".\n";
1403 return false;
1404 }
1405 }
1406
1407 if (n->right != nullptr) {
1408 if (n->right->key < n->key) {
1409 std::cerr
1410 << "Keys do not satisfy the order requirement.\n"
1411 << " n->key= " << n->key << '\n'
1412 << " n->right->key= " << n->right->key
1413 << " n->key= " << n->key << '\n';
1414 return false;
1415 }
1416
1417 if (n->right->side != 'r') {
1418 std::cerr << "Wrong side for right child: '" << n->right->side << ".\n";
1419 return false;
1420 }
1421 }
1422
1423 return sanity_check(n->left) and sanity_check(n->right);
1424 }
1425
1426 void print_tree(tree_node *n, const std::string& tab) const noexcept {
1427 std::cout << tab;
1428
1429 if (n == nullptr) {
1430 std::cout << "∅\n";
1431 return;
1432 }
1433
1434 std::cout
1435 << n->key
1436 << ", nc= " << n->node_counter
1437 << ", s= " << n->side
1438 << ", ls= " << n->left_size()
1439 << ", rs= " << n->right_size()
1440 << ", h= " << n->height
1441 << ", ts= " << n->tree_size
1442 << ", tc= " << n->tree_counter
1443 << ", bf= " << n->balance_factor
1444 << '\n';
1445 print_tree(n->left, tab + "| -l-: ");
1446 print_tree(n->right, tab + "| +r+: ");
1447 }
1448#endif
1449};
1450
1451template <typename T>
1452using AVL_frequencies = typename AVL<T>::frequencies;
1453
1454} // -- namespace detail
1455} // -- namespace lal
Simple class that implements an AVL tree.
Definition: avl.hpp:74
tree_node * right_right_case(tree_node *n) const noexcept
Right-right imbalance case.
Definition: avl.hpp:629
tree_node * right_left_case(tree_node *n) const noexcept
Right-left imbalance case.
Definition: avl.hpp:638
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:778
tree_node * join_taller(tree_node *T1, tree_node *T2) const noexcept
Join two AVL trees.
Definition: avl.hpp:1059
tree_node * m_root
Root of this AVL tree.
Definition: avl.hpp:500
tree_node * remove(tree_node *n, const T &x, frequencies &freqs) const noexcept
Remove an element from the tree.
Definition: avl.hpp:951
~AVL() noexcept
Destructor.
Definition: avl.hpp:98
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:886
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:1216
tree_node * insert(tree_node *p, tree_node *n, U &&x, frequencies &freqs) const noexcept
Insert element 'x' to the tree.
Definition: avl.hpp:677
frequencies element_frequency(const T &v) const noexcept
Number of occurrences associated to a given value.
Definition: avl.hpp:191
void free_node(tree_node *n) const noexcept
Deallocates the memory of node n.
Definition: avl.hpp:504
frequencies insert(U &&v) noexcept
Insert a new value v into the tree.
Definition: avl.hpp:220
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:327
frequencies remove(const T &v) noexcept
Remove an element from the tree.
Definition: avl.hpp:235
tree_node * left_rotation(tree_node *n) const noexcept
Performs a left-rotation at node n.
Definition: avl.hpp:569
void join_sorted_all_greater(U &&v) noexcept
Add to the tree the elements in the vector v.
Definition: avl.hpp:252
void clear() noexcept
Empties the tree.
Definition: avl.hpp:109
std::pair< const T &, frequencies > get_smallest_value() const noexcept
Find the largest value.
Definition: avl.hpp:168
tree_node * join_shorter(tree_node *T1, tree_node *T2) const noexcept
Join two AVL trees.
Definition: avl.hpp:1133
tree_node * left_left_case(tree_node *n) const noexcept
Left-left imbalance case.
Definition: avl.hpp:610
tree_node * balance(tree_node *n) const noexcept
Balance a node of the AVL tree.
Definition: avl.hpp:650
std::size_t total_elements() const noexcept
Total number of elements inserted.
Definition: avl.hpp:334
tree_node * left_right_case(tree_node *n) const noexcept
Left-right imbalance case.
Definition: avl.hpp:619
frequencies remove_smallest() noexcept
Remove an element from the tree.
Definition: avl.hpp:156
tree_node * right_rotation(tree_node *n) const noexcept
Performs a right-rotation at node n.
Definition: avl.hpp:520
std::pair< const T &, frequencies > get_largest_value() const noexcept
Finds the largest value.
Definition: avl.hpp:120
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:817
constexpr int64_t to_int64(const T &t) noexcept
Conversion to int64_t.
Definition: basic_convert.hpp:52
constexpr uint64_t to_uint64(const T &t) noexcept
Conversion to uint64_t.
Definition: basic_convert.hpp:57
Main namespace of the library.
Definition: basic_types.hpp:50
Frequency of a value in the tree v.
Definition: avl.hpp:77
bool operator!=(const frequencies &f) const noexcept
Different operator.
Definition: avl.hpp:92
std::size_t counter_larger
Number of occurrences of larger elements than v in the tree.
Definition: avl.hpp:81
bool operator==(const frequencies &f) const noexcept
Equality comparison.
Definition: avl.hpp:85
std::size_t num_nodes_larger
Number of nodes with a key larger than v in the tree.
Definition: avl.hpp:83
std::size_t counter_equal
Number of occurrences of v in the tree.
Definition: avl.hpp:79
Node of the tree.
Definition: avl.hpp:352
void update_height_and_balance_factor() noexcept
Calculate the height and balance factor of this node.
Definition: avl.hpp:422
T key
contents of the node
Definition: avl.hpp:354
std::size_t left_size() const noexcept
Returns the size of the left subtree.
Definition: avl.hpp:405
std::size_t right_size() const noexcept
Returns the size of the right subtree.
Definition: avl.hpp:408
tree_node * left
Pointer to the left subtree.
Definition: avl.hpp:390
void update_size() noexcept
Computes the size of the subtree rooted at this node.
Definition: avl.hpp:433
void update() noexcept
Updates information of this node.
Definition: avl.hpp:455
void update_count() noexcept
Computes the size of the subtree rooted at this node.
Definition: avl.hpp:441
std::size_t height
Maximum height of the left and right subtrees' height.
Definition: avl.hpp:378
std::size_t tree_counter
Total number of occurrences in the nodes in the subtree rooted at this node.
Definition: avl.hpp:375
std::size_t node_counter
Amount of occurrences of key.
Definition: avl.hpp:356
std::size_t right_counter() const noexcept
Returns the size of the right subtree.
Definition: avl.hpp:415
std::size_t left_counter() const noexcept
Returns the total occurrences of the left subtree.
Definition: avl.hpp:412
char side
Side of this node with respect to its parent.
Definition: avl.hpp:402
int64_t balance_factor
Balance factor of a node:
Definition: avl.hpp:385
std::size_t tree_size
Amount of nodes in the subtree rooted at this node.
Definition: avl.hpp:363
void replace_with(tree_node *n) noexcept
Replace this node with either its left or right child.
Definition: avl.hpp:477
tree_node * right
Pointer to the right subtree.
Definition: avl.hpp:392
tree_node * parent
Pointer to the parent of this node.
Definition: avl.hpp:388