Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
avl.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
38#include <algorithm>
39#include <random>
40#include <set>
41#include <vector>
42
43#include <gtest/gtest.h>
44#include <numeric>
45
46#include <tpl_avl.H>
47
48using namespace Aleph;
49using namespace testing;
50
51namespace
52{
53 template <class Tree>
54 struct NodePool
55 {
56 using Node = typename Tree::Node;
57
58 std::vector<Node *> allocated;
59
60 Node * make(const typename Node::key_type & key)
61 {
62 Node * p = new Node(key);
63 allocated.push_back(p);
64 return p;
65 }
66
67 void forget(Node * p) noexcept
68 {
69 for (auto & q : allocated)
70 if (q == p)
71 {
72 q = nullptr;
73 return;
74 }
75 }
76
77 ~NodePool()
78 {
79 for (Node * p : allocated)
80 delete p;
81 }
82 };
83
84 template <class Node>
85 std::vector<typename Node::key_type> inorder_keys(Node * root)
86 {
87 std::vector<typename Node::key_type> keys;
88 Aleph::infix_for_each<Node>(root, [&] (Node * p) { keys.push_back(KEY(p)); });
89 return keys;
90 }
91}
92
94{
97
98 const std::vector<int> input{5, 3, 7, 2, 4, 6, 8};
99 for (int k : input)
100 ASSERT_NE(t.insert(pool.make(k)), nullptr);
101
102 EXPECT_TRUE(t.verify());
104
105 auto * found = t.search(4);
106 ASSERT_NE(found, nullptr);
107 EXPECT_EQ(KEY(found), 4);
108
109 std::vector<int> it_keys;
110 for (Avl_Tree<int>::Iterator it(t); it.has_curr(); it.next_ne())
111 it_keys.push_back(KEY(it.get_curr_ne()));
112
113 std::vector<int> expected = input;
114 std::sort(expected.begin(), expected.end());
116}
117
119{
122
123 auto * a = pool.make(5);
124 auto * b = pool.make(5);
125
126 ASSERT_NE(t.insert(a), nullptr);
127 EXPECT_EQ(t.insert(b), nullptr);
128
129 auto * c = pool.make(5);
130 auto * got = t.search_or_insert(c);
131 ASSERT_NE(got, nullptr);
132 EXPECT_NE(got, c);
133 EXPECT_EQ(KEY(got), 5);
134
135 EXPECT_TRUE(t.verify());
136}
137
138// Tests for optimistic search edge cases
140{
141 /*
142 * Build tree: 50
143 * / \
144 * 25 75
145 * / \
146 * 10 30
147 */
148 // Then try to insert duplicate of 25 (intermediate level)
151
152 ASSERT_NE(t.insert(pool.make(50)), nullptr);
153 ASSERT_NE(t.insert(pool.make(25)), nullptr);
154 ASSERT_NE(t.insert(pool.make(75)), nullptr);
155 ASSERT_NE(t.insert(pool.make(10)), nullptr);
156 ASSERT_NE(t.insert(pool.make(30)), nullptr);
157
158 // Try duplicate at intermediate level
159 EXPECT_EQ(t.insert(pool.make(25)), nullptr);
160 EXPECT_TRUE(t.verify());
161
162 // search_or_insert should return existing node
163 auto * dup = pool.make(25);
164 auto * found = t.search_or_insert(dup);
165 EXPECT_NE(found, dup);
166 EXPECT_EQ(KEY(found), 25);
167 EXPECT_TRUE(t.verify());
168
169 // Remove the intermediate node should work
170 auto * removed = t.remove(25);
171 ASSERT_NE(removed, nullptr);
172 EXPECT_EQ(KEY(removed), 25);
173 EXPECT_TRUE(t.verify());
174}
175
177{
178 // Build tree where duplicate search goes only left
179 // Tree: 100
180 // /
181 // 50
182 // /
183 // 25
184 // Search for 25 (only left descents, candidate should be nullptr until found)
187
188 ASSERT_NE(t.insert(pool.make(100)), nullptr);
189 ASSERT_NE(t.insert(pool.make(50)), nullptr);
190 ASSERT_NE(t.insert(pool.make(25)), nullptr);
191
192 // Insert key smaller than all - should work (no duplicate)
193 ASSERT_NE(t.insert(pool.make(10)), nullptr);
194 EXPECT_TRUE(t.verify());
195
196 // Try duplicate of leftmost
197 EXPECT_EQ(t.insert(pool.make(25)), nullptr);
198 EXPECT_TRUE(t.verify());
199}
200
202{
203 // Build a deeper tree and test duplicate at various levels
206
207 // Insert in order that creates a balanced tree
208 for (int k : {50, 25, 75, 12, 37, 62, 87, 6, 18, 31, 43})
209 ASSERT_NE(t.insert(pool.make(k)), nullptr);
210
211 EXPECT_TRUE(t.verify());
212
213 // Test duplicates at different levels
214 EXPECT_EQ(t.insert(pool.make(50)), nullptr); // root
215 EXPECT_EQ(t.insert(pool.make(25)), nullptr); // level 1
216 EXPECT_EQ(t.insert(pool.make(37)), nullptr); // level 2
217 EXPECT_EQ(t.insert(pool.make(31)), nullptr); // level 3
218
219 EXPECT_TRUE(t.verify());
220
221 // Remove and verify tree integrity
222 for (int k : {31, 37, 25, 50})
223 {
224 auto * rem = t.remove(k);
225 ASSERT_NE(rem, nullptr);
226 EXPECT_EQ(KEY(rem), k);
227 EXPECT_TRUE(t.verify());
228 }
229}
230
232{
235
236 ASSERT_NE(t.insert_dup(pool.make(5)), nullptr);
237 ASSERT_NE(t.insert_dup(pool.make(5)), nullptr);
238 ASSERT_NE(t.insert_dup(pool.make(5)), nullptr);
239
240 EXPECT_TRUE(t.verify());
241 EXPECT_EQ(inorder_keys<Avl_Tree<int>::Node>(t.getRoot()), (std::vector<int>{5, 5, 5}));
242}
243
245{
248 for (int k : {1, 2, 3})
249 ASSERT_NE(t.insert(pool.make(k)), nullptr);
250
251 EXPECT_EQ(t.remove(42), nullptr);
252 EXPECT_TRUE(t.verify());
253}
254
256{
259 for (int k : {3, 1, 4, 2})
260 ASSERT_NE(t.insert(pool.make(k)), nullptr);
261
262 auto * removed = t.remove(1);
263 ASSERT_NE(removed, nullptr);
264 EXPECT_EQ(KEY(removed), 1);
267 EXPECT_EQ(static_cast<int>(DIFF(removed)), 0);
268
269 EXPECT_TRUE(t.verify());
270
271 pool.forget(removed);
272 delete removed;
273}
274
276{
279
280 std::mt19937 rng(12345);
281 std::uniform_int_distribution<int> dist(0, 500);
282
283 std::set<int> present;
284
285 for (int i = 0; i < 300; ++i)
286 {
287 int k = dist(rng);
288 auto * p = pool.make(k);
289 auto * ins = t.insert(p);
290 if (ins == nullptr)
291 {
292 pool.forget(p);
293 delete p;
294 }
295 else
296 present.insert(k);
297
298 ASSERT_TRUE(t.verify());
299 }
300
301 for (int i = 0; i < 200; ++i)
302 {
303 int k = dist(rng);
304 auto * removed = t.remove(k);
305 if (removed != nullptr)
306 {
307 present.erase(k);
308 pool.forget(removed);
309 delete removed;
310 }
311 ASSERT_TRUE(t.verify());
312 }
313}
314
316{
317 {
320 ASSERT_NE(t.insert(pool.make(3)), nullptr);
321 ASSERT_NE(t.insert(pool.make(2)), nullptr);
322 ASSERT_NE(t.insert(pool.make(1)), nullptr); // LL
323 ASSERT_TRUE(t.verify());
324 EXPECT_EQ(inorder_keys<Avl_Tree<int>::Node>(t.getRoot()), (std::vector<int>{1, 2, 3}));
325 }
326
327 {
330 ASSERT_NE(t.insert(pool.make(1)), nullptr);
331 ASSERT_NE(t.insert(pool.make(2)), nullptr);
332 ASSERT_NE(t.insert(pool.make(3)), nullptr); // RR
333 ASSERT_TRUE(t.verify());
334 EXPECT_EQ(inorder_keys<Avl_Tree<int>::Node>(t.getRoot()), (std::vector<int>{1, 2, 3}));
335 }
336
337 {
340 ASSERT_NE(t.insert(pool.make(3)), nullptr);
341 ASSERT_NE(t.insert(pool.make(1)), nullptr);
342 ASSERT_NE(t.insert(pool.make(2)), nullptr); // LR
343 ASSERT_TRUE(t.verify());
344 EXPECT_EQ(inorder_keys<Avl_Tree<int>::Node>(t.getRoot()), (std::vector<int>{1, 2, 3}));
345 }
346
347 {
350 ASSERT_NE(t.insert(pool.make(1)), nullptr);
351 ASSERT_NE(t.insert(pool.make(3)), nullptr);
352 ASSERT_NE(t.insert(pool.make(2)), nullptr); // RL
353 ASSERT_TRUE(t.verify());
354 EXPECT_EQ(inorder_keys<Avl_Tree<int>::Node>(t.getRoot()), (std::vector<int>{1, 2, 3}));
355 }
356}
357
359{
362
363 std::mt19937 rng(123456);
364 std::uniform_int_distribution<int> dist(0, 2000);
365 std::bernoulli_distribution do_insert(0.6);
366
367 std::set<int> oracle;
368
369 for (int i = 0; i < 1500; ++i)
370 {
371 int k = dist(rng);
372 if (do_insert(rng))
373 {
374 auto * p = pool.make(k);
375 auto * ins = t.insert(p);
376 if (ins == nullptr)
377 {
378 pool.forget(p);
379 delete p;
380 }
381 else
382 oracle.insert(k);
383 }
384 else
385 {
386 if (auto * removed = t.remove(k); removed != nullptr)
387 {
388 oracle.erase(k);
389 pool.forget(removed);
390 delete removed;
391 }
392 }
393
394 ASSERT_TRUE(t.verify());
395
396 std::vector<int> expected(oracle.begin(), oracle.end());
399 }
400}
401
403{
404 struct Greater
405 {
406 bool operator()(int a, int b) const noexcept { return a > b; }
407 };
408
411
412 for (int k : {1, 2, 3, 4, 5})
413 ASSERT_NE(t.insert(pool.make(k)), nullptr);
414
415 EXPECT_TRUE(t.verify());
417 const std::vector<int> expected{5, 4, 3, 2, 1};
419
420 auto * removed = t.remove(4);
421 ASSERT_NE(removed, nullptr);
422 pool.forget(removed);
423 delete removed;
424
425 EXPECT_TRUE(t.verify());
426}
427
428// Tests the candidate_pos == avl_stack.size() branch (zero-pop path).
429// This occurs when the duplicate key is the current maximum: the last
430// descend is always to the right so candidate_pos equals the stack size
431// at the end of the loop, making to_pop == 0 and popn() is never called.
433{
436
437 // Tree: 10 -> 20 -> 30 (right-spine). 30 is the current maximum.
438 ASSERT_NE(t.insert(pool.make(10)), nullptr);
439 ASSERT_NE(t.insert(pool.make(20)), nullptr);
440 ASSERT_NE(t.insert(pool.make(30)), nullptr);
441 ASSERT_TRUE(t.verify());
442
443 // Duplicate of maximum: search goes 10->20->30->NullPtr,
444 // candidate_pos == avl_stack.size() at end, so to_pop == 0.
445 EXPECT_EQ(t.insert(pool.make(30)), nullptr);
446 EXPECT_TRUE(t.verify());
447
448 // search_or_insert must also find the existing node without corruption.
449 auto * dup = pool.make(30);
450 auto * found = t.search_or_insert(dup);
451 EXPECT_NE(found, dup);
452 EXPECT_EQ(KEY(found), 30);
453 EXPECT_TRUE(t.verify());
454}
455
456// Randomised variant: repeatedly insert many sequences that include a
457// duplicate of the current maximum to stress the zero-pop branch.
459{
460 std::mt19937 rng(77777);
461 std::uniform_int_distribution<int> n_dist(1, 50);
462
463 for (int trial = 0; trial < 200; ++trial)
464 {
467
468 int n = n_dist(rng);
469 int max_key = 0;
470 for (int i = 1; i <= n; ++i)
471 {
472 ASSERT_NE(t.insert(pool.make(i)), nullptr);
473 max_key = i;
474 }
475
476 // Insert a duplicate of the current maximum
477 EXPECT_EQ(t.insert(pool.make(max_key)), nullptr);
478 ASSERT_TRUE(t.verify()) << "Invariant violated after duplicate-max on trial " << trial;
479 }
480}
481
482// ============================================================================
483// STRESS TESTS / FUZZING
484// ============================================================================
485
486// Stress test: ascending insertion (worst case for naive BST)
488{
491
492 const int n = 10000;
493
494 for (int i = 0; i < n; ++i)
495 {
496 ASSERT_NE(t.insert(pool.make(i)), nullptr) << "Insert failed at i=" << i;
497 ASSERT_TRUE(t.verify()) << "AVL invariant violated at i=" << i;
498 }
499
500 // Verify all elements are present
501 for (int i = 0; i < n; ++i)
502 ASSERT_NE(t.search(i), nullptr) << "Element " << i << " not found";
503}
504
505// Stress test: descending insertion
507{
510
511 const int n = 10000;
512
513 for (int i = n - 1; i >= 0; --i)
514 {
515 ASSERT_NE(t.insert(pool.make(i)), nullptr);
516 ASSERT_TRUE(t.verify());
517 }
518
519 for (int i = 0; i < n; ++i)
520 ASSERT_NE(t.search(i), nullptr);
521}
522
523// Stress test: zigzag insertion pattern
525{
528
529 const int n = 5000;
530
531 // Insert in zigzag pattern: 0, n-1, 1, n-2, 2, n-3, ...
532 for (int i = 0; i < n / 2; ++i)
533 {
534 ASSERT_NE(t.insert(pool.make(i)), nullptr);
535 ASSERT_TRUE(t.verify());
536 ASSERT_NE(t.insert(pool.make(n - 1 - i)), nullptr);
537 ASSERT_TRUE(t.verify());
538 }
539
540 for (int i = 0; i < n; ++i)
541 ASSERT_NE(t.search(i), nullptr);
542}
543
544// Stress test: large scale fuzzing with oracle
546{
549
550 std::mt19937 rng(99999);
551 std::uniform_int_distribution<int> key_dist(0, 50000);
552 std::uniform_int_distribution<int> op_dist(0, 2);
553
554 std::set<int> oracle;
555
556 const int num_ops = 20000;
557
558 for (int i = 0; i < num_ops; ++i)
559 {
560 int key = key_dist(rng);
561 int op = op_dist(rng);
562
563 switch (op)
564 {
565 case 0: // Insert
566 {
567 auto * p = pool.make(key);
568 auto * ins = t.insert(p);
569 if (ins == nullptr)
570 {
571 pool.forget(p);
572 delete p;
573 EXPECT_TRUE(oracle.count(key) > 0);
574 }
575 else
576 {
577 EXPECT_TRUE(oracle.count(key) == 0);
578 oracle.insert(key);
579 }
580 break;
581 }
582 case 1: // Remove
583 {
584 auto * removed = t.remove(key);
585 if (removed != nullptr)
586 {
587 EXPECT_TRUE(oracle.count(key) > 0);
588 oracle.erase(key);
589 pool.forget(removed);
590 delete removed;
591 }
592 else
593 {
594 EXPECT_TRUE(oracle.count(key) == 0);
595 }
596 break;
597 }
598 case 2: // Search
599 {
600 auto * found = t.search(key);
601 bool in_oracle = oracle.count(key) > 0;
602 EXPECT_EQ(found != nullptr, in_oracle);
603 if (found)
604 EXPECT_EQ(KEY(found), key);
605 break;
606 }
607 }
608
609 ASSERT_TRUE(t.verify()) << "AVL invariant violated at op " << i;
610 }
611
612 // Final verification
613 std::vector<int> expected(oracle.begin(), oracle.end());
616}
617
618// Stress test: bulk insert then bulk remove
620{
623
624 const int n = 10000;
625
626 // Bulk insert
627 std::vector<int> keys(n);
628 std::iota(keys.begin(), keys.end(), 0);
629
630 std::mt19937 rng(12345);
631 std::shuffle(keys.begin(), keys.end(), rng);
632
633 for (int k : keys)
634 {
635 ASSERT_NE(t.insert(pool.make(k)), nullptr);
636 }
637
638 ASSERT_TRUE(t.verify());
639
640 // Bulk remove in different random order
641 std::shuffle(keys.begin(), keys.end(), rng);
642
643 for (int k : keys)
644 {
645 auto * removed = t.remove(k);
646 ASSERT_NE(removed, nullptr) << "Remove failed for key " << k;
647 pool.forget(removed);
648 delete removed;
649 ASSERT_TRUE(t.verify());
650 }
651
653}
654
655// Stress test: repeated insert_dup (duplicates allowed)
657{
660
661 const int num_keys = 100;
662 const int dups_per_key = 50;
663
664 // Insert many duplicates of each key
665 for (int k = 0; k < num_keys; ++k)
666 {
667 for (int d = 0; d < dups_per_key; ++d)
668 {
669 ASSERT_NE(t.insert_dup(pool.make(k)), nullptr);
670 }
671 }
672
673 ASSERT_TRUE(t.verify());
674
675 // Verify inorder traversal has correct count
678
679 // Verify sorted
680 EXPECT_TRUE(std::is_sorted(keys.begin(), keys.end()));
681}
682
683// Stress test: alternating insert and remove
685{
688
689 std::set<int> present;
690 std::mt19937 rng(54321);
691 std::uniform_int_distribution<int> dist(0, 10000);
692
693 const int n = 15000;
694
695 for (int i = 0; i < n; ++i)
696 {
697 int k = dist(rng);
698
699 if (i % 2 == 0) // Insert
700 {
701 auto * p = pool.make(k);
702 if (t.insert(p) == nullptr)
703 {
704 pool.forget(p);
705 delete p;
706 }
707 else
708 {
709 present.insert(k);
710 }
711 }
712 else // Remove
713 {
714 if (auto * removed = t.remove(k); removed)
715 {
716 present.erase(k);
717 pool.forget(removed);
718 delete removed;
719 }
720 }
721
722 ASSERT_TRUE(t.verify()) << "AVL invariant violated at i=" << i;
723 }
724
725 // Final check
726 std::vector<int> expected(present.begin(), present.end());
728}
729
730// Stress test with string keys
732{
735
736 std::mt19937 rng(11111);
737 std::uniform_int_distribution<int> len_dist(1, 30);
738 std::uniform_int_distribution<int> char_dist('a', 'z');
739
740 auto random_string = [&]() {
741 int len = len_dist(rng);
742 std::string s;
743 s.reserve(len);
744 for (int i = 0; i < len; ++i)
745 s += static_cast<char>(char_dist(rng));
746 return s;
747 };
748
749 std::set<std::string> oracle;
750
751 const int n = 5000;
752
753 for (int i = 0; i < n; ++i)
754 {
755 std::string s = random_string();
756 auto * p = pool.make(s);
757 if (t.insert(p) != nullptr)
758 oracle.insert(s);
759 else
760 {
761 pool.forget(p);
762 delete p;
763 }
764 }
765
766 ASSERT_TRUE(t.verify());
767
768 // Verify all oracle strings are present
769 for (const auto & s : oracle)
770 ASSERT_NE(t.search(s), nullptr) << "String key missing: " << s;
771}
static string random_string(std::mt19937 &rng, size_t len)
bool is_avl(Node *p)
Validate that a tree satisfies AVL properties.
Definition avlNode.H:123
#define DIFF(p)
Access the balance factor of node p.
Definition avlNode.H:95
WeightedDigraph::Node Node
@ KEY
Definition btreepic.C:169
Node * search(const Key &key) const noexcept
Search a node containing key; if found, then a pointer to the node containing it is returned; otherwi...
Definition tpl_avl.H:541
Node * search_or_insert(Node *p) noexcept
Search or insert a key.
Definition tpl_avl.H:606
Node * insert_dup(Node *p) noexcept
Insert the node p without testing for key duplicity.
Definition tpl_avl.H:629
constexpr Node *& getRoot() noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:528
Node * insert(Node *p) noexcept
Insert the node pointed by p in the tree.
Definition tpl_avl.H:569
bool verify() const noexcept
Definition tpl_avl.H:702
Node * remove(const Key &key) noexcept
Remove from an AVL tree the node containing key key.
Definition tpl_avl.H:648
Minimal std::expected-style result type for C++20.
void forget(Node *p)
Definition rand-tree.cc:94
Node * make(int key)
Definition rand-tree.cc:87
QuadNode Node
Definition quadtree.H:128
#define TEST(name)
static mt19937 rng
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::vector< int > inorder_keys(NodeT *root)
Definition rand-tree.cc:60
AVL binary search tree with nodes without a virtual destructor.
Definition tpl_avl.H:743
#define RLINK(i, n)
int keys[]
#define LLINK(i, n)
ValueArg< size_t > num_keys
Definition testHash.C:50
static int * k
AVL tree implementation (height-balanced BST).