Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
lin-hash.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
33
38#include <gtest/gtest.h>
39
40#include <tpl_linHash.H>
41#include <random>
42#include <set>
43#include <vector>
44#include <algorithm>
45
46using namespace std;
47using namespace Aleph;
48
49// ============================================================================
50// Type Aliases
51// ============================================================================
52
55
56// ============================================================================
57// Empty Table Tests
58// ============================================================================
59
61{
62 Table table;
63
64 EXPECT_EQ(table.size(), 0u);
65 EXPECT_TRUE(table.is_empty());
66 EXPECT_GT(table.capacity(), 0u);
67 EXPECT_EQ(table.busy_slots(), 0u);
68 EXPECT_EQ(table.expansions(), 0u);
69}
70
72{
73 Table table;
74
75 EXPECT_EQ(table.search(10), nullptr);
76 EXPECT_EQ(table.search(0), nullptr);
77 EXPECT_EQ(table.search(-1), nullptr);
78}
79
80// ============================================================================
81// Insert Tests
82// ============================================================================
83
85{
86 Table table;
87
88 auto *bucket = new Bucket(10);
89 auto *inserted = table.insert(bucket);
90
91 EXPECT_EQ(inserted, bucket);
92 EXPECT_EQ(table.size(), 1u);
93 EXPECT_FALSE(table.is_empty());
94 EXPECT_GE(table.busy_slots(), 1u);
95}
96
98{
99 Table table;
100
101 for (int k : {5, 3, 7, 1, 4, 6, 8})
102 {
103 auto *bucket = new Bucket(k);
104 ASSERT_NE(table.insert(bucket), nullptr);
105 }
106
107 EXPECT_EQ(table.size(), 7u);
108
109 // Verify all elements can be found
110 for (int k : {5, 3, 7, 1, 4, 6, 8})
111 EXPECT_NE(table.search(k), nullptr) << "Key " << k << " not found";
112}
113
115{
116 Table table;
117
118 auto *bucket1 = new Bucket(10);
119 auto *bucket2 = new Bucket(10);
120
121 EXPECT_NE(table.insert(bucket1), nullptr);
122 EXPECT_EQ(table.insert(bucket2), nullptr); // duplicate rejected
123
124 EXPECT_EQ(table.size(), 1u);
125 delete bucket2; // cleanup rejected bucket
126}
127
129{
130 Table table(17); // small initial size
131
132 const size_t initial_capacity = table.capacity();
133
134 // Insert enough elements to trigger expansion
135 for (int k = 0; k < 100; ++k)
136 table.insert(new Bucket(k));
137
138 EXPECT_EQ(table.size(), 100u);
140 EXPECT_GT(table.expansions(), 0u);
141
142 // Verify all elements can be found
143 for (int k = 0; k < 100; ++k)
144 EXPECT_NE(table.search(k), nullptr);
145}
146
147// ============================================================================
148// Search Tests
149// ============================================================================
150
152{
153 Table table;
154
155 for (int k : {1, 2, 3, 4, 5})
156 table.insert(new Bucket(k));
157
158 for (int k : {1, 2, 3, 4, 5})
159 {
160 auto *found = table.search(k);
161 ASSERT_NE(found, nullptr);
162 EXPECT_EQ(found->get_key(), k);
163 }
164}
165
167{
168 Table table;
169
170 for (int k : {1, 3, 5})
171 table.insert(new Bucket(k));
172
173 EXPECT_EQ(table.search(2), nullptr);
174 EXPECT_EQ(table.search(4), nullptr);
175 EXPECT_EQ(table.search(0), nullptr);
176 EXPECT_EQ(table.search(6), nullptr);
177}
178
180{
181 Table table;
182
183 auto *bucket1 = new Bucket(10);
184 table.insert(bucket1);
185
186 auto *bucket2 = new Bucket(10);
187 auto *found = table.search_or_insert(bucket2);
188
190 EXPECT_EQ(table.size(), 1u);
191
192 delete bucket2; // cleanup rejected bucket
193}
194
196{
197 Table table;
198
199 table.insert(new Bucket(5));
200
201 auto *bucket = new Bucket(10);
202 auto *result = table.search_or_insert(bucket);
203
204 EXPECT_EQ(result, bucket);
205 EXPECT_EQ(table.size(), 2u);
206 EXPECT_NE(table.search(10), nullptr);
207}
208
209// ============================================================================
210// Remove Tests
211// ============================================================================
212
214{
215 Table table;
216
217 for (int k : {1, 2, 3, 4, 5})
218 table.insert(new Bucket(k));
219
220 auto *found = table.search(3);
221 ASSERT_NE(found, nullptr);
222
223 auto *removed = table.remove(found);
224 EXPECT_EQ(removed->get_key(), 3);
225 delete removed;
226
227 EXPECT_EQ(table.size(), 4u);
228 EXPECT_EQ(table.search(3), nullptr);
229}
230
232{
233 Table table;
234
235 for (int k : {5, 3, 7, 1, 4, 6, 8})
236 table.insert(new Bucket(k));
237
238 for (int k : {5, 3, 7, 1, 4, 6, 8})
239 {
240 auto *found = table.search(k);
241 ASSERT_NE(found, nullptr) << "Key " << k << " not found";
242 delete table.remove(found);
243 }
244
245 EXPECT_EQ(table.size(), 0u);
246 EXPECT_TRUE(table.is_empty());
247}
248
250{
251 Table table(17);
252
253 // Insert many elements
254 for (int k = 0; k < 200; ++k)
255 table.insert(new Bucket(k));
256
257 const size_t expanded_capacity = table.capacity();
258
259 // Remove most elements
260 for (int k = 0; k < 180; ++k)
261 {
262 auto *found = table.search(k);
263 ASSERT_NE(found, nullptr);
264 delete table.remove(found);
265 }
266
267 EXPECT_EQ(table.size(), 20u);
268 // Capacity should have contracted
270}
271
272// ============================================================================
273// Iterator Tests
274// ============================================================================
275
277{
278 Table table;
279
280 std::set<int> inserted;
281 for (int k : {5, 3, 7, 1, 4, 6, 8})
282 {
283 table.insert(new Bucket(k));
285 }
286
287 std::set<int> visited;
288 for (Table::Iterator it(table); it.has_curr(); it.next())
289 visited.insert(it.get_curr()->get_key());
290
292}
293
295{
296 Table table;
297 Table::Iterator it(table);
298
300}
301
303{
304 Table table;
305
306 for (int k : {1, 2, 3, 4, 5})
307 table.insert(new Bucket(k));
308
309 // Find and delete element 3 using iterator
310 for (Table::Iterator it(table); it.has_curr(); it.next())
311 {
312 if (it.get_curr()->get_key() == 3)
313 {
314 delete it.del();
315 break;
316 }
317 }
318
319 EXPECT_EQ(table.size(), 4u);
320 EXPECT_EQ(table.search(3), nullptr);
321}
322
324{
325 Table table;
326
327 for (int k : {1, 2, 3, 4, 5})
328 table.insert(new Bucket(k));
329
330 Table::Iterator it(table);
331 EXPECT_EQ(it.get_pos(), 0);
332
333 it.next();
334 EXPECT_EQ(it.get_pos(), 1);
335
336 it.next();
337 EXPECT_EQ(it.get_pos(), 2);
338}
339
341{
342 Table table;
343
344 for (int k : {1, 2, 3})
345 table.insert(new Bucket(k));
346
347 Table::Iterator it(table);
348 it.next();
349 it.next();
350 EXPECT_EQ(it.get_pos(), 2);
351
352 it.prev();
353 EXPECT_EQ(it.get_pos(), 1);
354
355 it.prev();
356 EXPECT_EQ(it.get_pos(), 0);
357}
358
360{
361 Table table;
362
363 for (int k : {1, 2, 3})
364 table.insert(new Bucket(k));
365
366 Table::Iterator it(table);
367
368 // Test get_curr_ne() and next_ne()
369 auto *bucket1 = it.get_curr_ne();
370 EXPECT_NE(bucket1, nullptr);
371
372 it.next_ne();
373 auto *bucket2 = it.get_curr_ne();
374 EXPECT_NE(bucket2, nullptr);
376}
377
378// ============================================================================
379// Swap Tests
380// ============================================================================
381
382// NOTE: swap() has a known issue with entries_list and embedded Dlink nodes
383// which causes use-after-free in destructors. This test uses remove_all_buckets=false
384// and manually cleans up to avoid the issue.
386{
387 // Use remove_all_buckets=false to avoid destructor cleanup issues after swap
392
393 std::vector<Bucket*> buckets1, buckets2;
394 for (int k : {1, 2, 3})
395 {
396 auto* b = new Bucket(k);
397 table1.insert(b);
398 buckets1.push_back(b);
399 }
400 for (int k : {10, 20})
401 {
402 auto* b = new Bucket(k);
403 table2.insert(b);
404 buckets2.push_back(b);
405 }
406
408
409 EXPECT_EQ(table1.size(), 2u);
410 EXPECT_EQ(table2.size(), 3u);
411
412 EXPECT_NE(table1.search(10), nullptr);
413 EXPECT_NE(table1.search(20), nullptr);
414 EXPECT_NE(table2.search(1), nullptr);
415 EXPECT_NE(table2.search(2), nullptr);
416 EXPECT_NE(table2.search(3), nullptr);
417
418 // Manual cleanup since remove_all_buckets=false
419 for (auto* b : buckets1) delete b;
420 for (auto* b : buckets2) delete b;
421}
422
423// ============================================================================
424// Empty Method Tests
425// ============================================================================
426
428{
429 Table table;
430
431 for (int k = 0; k < 50; ++k)
432 table.insert(new Bucket(k));
433
434 EXPECT_EQ(table.size(), 50u);
435
436 table.empty();
437
438 EXPECT_EQ(table.size(), 0u);
439 EXPECT_TRUE(table.is_empty());
440 EXPECT_EQ(table.expansions(), 0u);
441}
442
443// ============================================================================
444// Load Factor Tests
445// ============================================================================
446
448{
449 Table table(100);
450
451 EXPECT_FLOAT_EQ(table.current_alpha(), 0.0f);
452
453 for (int k = 0; k < 50; ++k)
454 table.insert(new Bucket(k));
455
456 float expected_alpha = 50.0f / table.capacity();
458}
459
460// ============================================================================
461// Custom Comparator Tests
462// ============================================================================
463
465{
466 bool operator()(int a, int b) const { return (a % 100) == (b % 100); }
467};
468
469// Custom hash that aligns with ModCompare
470size_t mod_hash(const int& k) { return static_cast<size_t>(std::abs(k % 100)); }
471
473{
474 // Use custom hash that aligns with ModCompare so duplicates are detected
476
477 table.insert(new LinHashBucket<int>(105));
478
479 // 205 should be "equal" to 105 with ModCompare (both hash to 5, compare equal)
480 auto *bucket = new LinHashBucket<int>(205);
481 EXPECT_EQ(table.insert(bucket), nullptr); // rejected as duplicate
482
483 delete bucket;
484
485 // Search for 5 should find 105 (5 % 100 == 105 % 100)
486 EXPECT_NE(table.search(5), nullptr);
487}
488
489// ============================================================================
490// Edge Cases
491// ============================================================================
492
494{
495 Table table;
496
497 for (int k : {-5, -3, -1, 0, 1, 3, 5})
498 table.insert(new Bucket(k));
499
500 EXPECT_EQ(table.size(), 7u);
501
502 for (int k : {-5, -3, -1, 0, 1, 3, 5})
503 EXPECT_NE(table.search(k), nullptr);
504
505 EXPECT_EQ(table.search(-2), nullptr);
506}
507
509{
510 Table table;
511
512 auto *bucket = new Bucket(42);
513 table.insert(bucket);
514
515 EXPECT_EQ(table.size(), 1u);
516 EXPECT_NE(table.search(42), nullptr);
517
518 delete table.remove(bucket);
519
520 EXPECT_EQ(table.size(), 0u);
521 EXPECT_EQ(table.search(42), nullptr);
522}
523
524// ============================================================================
525// Constructor Parameter Validation Tests
526// ============================================================================
527
535
537{
538 // upper_alpha <= lower_alpha should throw
541 0.8f, 0.4f, true, true)), // lower > upper
542 std::domain_error);
543
546 0.5f, 0.5f, true, true)), // lower == upper
547 std::domain_error);
548}
549
550// ============================================================================
551// Stress Tests
552// ============================================================================
553
555{
556 Table table;
557 std::set<int> oracle;
558
559 std::mt19937 rng(12345);
560 std::uniform_int_distribution<int> dist(0, 999);
561
562 // Insert phase
563 for (int i = 0; i < 500; ++i)
564 {
565 int k = dist(rng);
566 if (oracle.count(k) == 0)
567 {
568 ASSERT_NE(table.insert(new Bucket(k)), nullptr);
569 oracle.insert(k);
570 }
571 }
572
573 EXPECT_EQ(table.size(), oracle.size());
574
575 // Search phase
576 for (int i = 0; i < 200; ++i)
577 {
578 int k = dist(rng);
579 auto *found = table.search(k);
580 if (oracle.count(k))
581 EXPECT_NE(found, nullptr);
582 else
583 EXPECT_EQ(found, nullptr);
584 }
585
586 // Remove phase
587 for (int i = 0; i < 200; ++i)
588 {
589 int k = dist(rng);
590 auto *found = table.search(k);
591 if (oracle.count(k))
592 {
593 ASSERT_NE(found, nullptr);
594 oracle.erase(k);
595 delete table.remove(found);
596 }
597 else
598 {
599 EXPECT_EQ(found, nullptr);
600 }
601 }
602
603 EXPECT_EQ(table.size(), oracle.size());
604
605 // Verify remaining keys
606 for (int k : oracle)
607 EXPECT_NE(table.search(k), nullptr);
608}
609
611{
612 Table table;
613
614 const int N = 5000;
615
616 // Insert N elements
617 for (int k = 0; k < N; ++k)
618 table.insert(new Bucket(k));
619
620 EXPECT_EQ(table.size(), static_cast<size_t>(N));
621
622 // Verify all elements present
623 for (int k = 0; k < N; ++k)
624 EXPECT_NE(table.search(k), nullptr);
625
626 // Remove half
627 for (int k = 0; k < N; k += 2)
628 {
629 auto *found = table.search(k);
630 ASSERT_NE(found, nullptr);
631 delete table.remove(found);
632 }
633
634 EXPECT_EQ(table.size(), static_cast<size_t>(N / 2));
635
636 // Verify remaining elements
637 for (int k = 1; k < N; k += 2)
638 EXPECT_NE(table.search(k), nullptr);
639}
640
641// ============================================================================
642// LinearHashTableVtl Tests
643// ============================================================================
644
646{
648
649 for (int k : {1, 2, 3, 4, 5})
650 table.insert(new LinHashBucketVtl<int>(k));
651
652 EXPECT_EQ(table.size(), 5u);
653
654 for (int k : {1, 2, 3, 4, 5})
655 EXPECT_NE(table.search(k), nullptr);
656
657 // Destructor should properly clean up with virtual destructor
658}
659
660// ============================================================================
661// Hash Function Tests
662// ============================================================================
663
665{
666 Table table;
667
668 // Custom hash function - use std::function explicitly to avoid ambiguity
669 Table::Hash_Fct custom_hash = [](const int& k) -> size_t { return k * 17; };
670
672
673 table.insert(new Bucket(1));
674 table.insert(new Bucket(2));
675 table.insert(new Bucket(3));
676
677 EXPECT_NE(table.search(1), nullptr);
678 EXPECT_NE(table.search(2), nullptr);
679 EXPECT_NE(table.search(3), nullptr);
680}
681
683{
684 Table table;
685
686 auto hash_fct = table.get_hash_fct();
687 EXPECT_NE(hash_fct, nullptr);
688}
689
691{
692 Table table;
693
694 auto& cmp = table.get_compare();
695 EXPECT_TRUE(cmp(5, 5));
696 EXPECT_FALSE(cmp(5, 6));
697
698 const Table& const_table = table;
699 const auto& const_cmp = const_table.get_compare();
700 EXPECT_TRUE(const_cmp(10, 10));
701}
702
703// ============================================================================
704// Resize Method Tests
705// ============================================================================
706
708{
709 Table table;
710
711 // resize() is a no-op for linear hash tables (provided for compatibility)
712 size_t result = table.resize(1000);
713 EXPECT_EQ(result, table.capacity());
714}
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
DynList & swap(DynList &l) noexcept
Swap this with l.
Definition htlist.H:1448
Generic linear hash table.
const size_t & capacity() const noexcept
Returns the table capacity.
std::function< size_t(const Key &)> Hash_Fct
The hash function type.
Cmp & get_compare() noexcept
const size_t & size() const noexcept
Returns the number of elements in the table.
bool is_empty() const noexcept
return true is table is empty
void empty() noexcept
Empty the entire table.
const size_t & expansions() const noexcept
Returns the expansion level performed on the table.
float current_alpha() const noexcept
return the current table load
Bucket * remove(Bucket *bucket) noexcept
Remove bucket from table.
BucketType< Key > Bucket
The bucket type.
const size_t & busy_slots() const noexcept
Returns the number of busy slots in the table.
Bucket * insert(Bucket *bucket)
Insert bucket in the table.
Hash_Fct get_hash_fct() const noexcept
void set_hash_fct(Hash_Fct fct) noexcept
Set the internal hash function.
Bucket * search_or_insert(Bucket *bucket)
Bucket * search(const Key &key) const noexcept
Search for key in the linear hash table.
size_t resize(size_t) noexcept
Provided for generic programming compatibility.
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Bucket with virtual destructor for a hash table with collision resolution by separate chaining.
Bucket without virtual destructor for a hash table with collision resolution by separate chaining.
#define TEST(name)
static mt19937 rng
#define N
Definition fib.C:294
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4118
Table::Bucket Bucket
Definition lin-hash.cc:54
size_t mod_hash(const int &k)
Definition lin-hash.cc:470
LinearHashTable< int > Table
Definition lin-hash.cc:53
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
const float hash_default_upper_alpha
Definition hash-dry.C:40
const float hash_default_lower_alpha
Definition hash-dry.C:38
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
bool operator()(int a, int b) const
Definition lin-hash.cc:466
size_t custom_hash(const int &key)
Linear hashing with chaining.