Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_odhash.H
Go to the documentation of this file.
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
101# ifndef TPL_ODHASH_H
102# define TPL_ODHASH_H
103
104# include <iostream>
105# include <cstddef>
106# include <cstdint>
107
108# include <primes.H>
109# include <dlink.H>
110# include <tpl_dynArray.H>
111# include <array_it.H>
112# include <ahDry.H>
113# include <hash-dry.H>
114# include <hashDry.H>
115# include <hash-fct.H>
116# include <ah-errors.H>
117# include <ah-concepts.H>
118
119using namespace Aleph;
120
121namespace Aleph
122{
123# ifdef N
124# define NBACKUP N
125# undef N
126# endif
127
128# ifdef M
129# define MBACKUP M
130# undef M
131# endif
132
133
173 template <typename Key, class Cmp = Aleph::equal_to<Key>>
176 : public OhashCommon<ODhashTable<Key, Cmp>, Key>,
177 public GenericTraverse<ODhashTable<Key, Cmp>>,
178 public LocateFunctions<ODhashTable<Key, Cmp>, Key>,
179 public FunctionalMethods<ODhashTable<Key, Cmp>, Key>,
180 public EqualToMethod<ODhashTable<Key, Cmp>>,
181 public StlAlephIterator<ODhashTable<Key, Cmp>>
182 {
183 friend class OhashCommon<ODhashTable<Key, Cmp>, Key>;
184
185 public:
186 using Key_Type = Key;
187
188 using Item_Type = Key;
189
190 using Hash_Fct = std::function<size_t(const Key &)>;
191
192 using Hash_Fct_Ptr = size_t (*)(const Key &);
193
195
197
198 struct Bucket
199 {
200 Key key;
201 unsigned char status = EMPTY; // status EMPTY, DELETED o BUSY
202 unsigned char probe_type = NO_PROBED; // FIRST_PROBE SECOND_PROBE LINEAR_PROBE
203 unsigned int probe_counter = 0;
204
207 { /* empty */
208 }
209
210 void reset() noexcept // put all as when constructed
211 {
212 status = EMPTY;
214 probe_counter = 0;
215 }
216
223
224 friend std::ostream &operator <<(std::ostream & out, const Bucket & bucket)
225 {
226 std::string status_str;
227 switch (bucket.status)
228 {
229 case EMPTY: status_str = "EMPTY";
230 break;
231 case BUSY: status_str = "BUSY";
232 break;
233 case DELETED: status_str = "DELETED";
234 break;
235 }
236 std::string probe_type_str;
237 switch (bucket.probe_type)
238 {
239 case NO_PROBED: probe_type_str = "NO_PROBED";
240 break;
241 case FIRST_PROBE: probe_type_str = "FIRST_PROBE";
242 break;
243 case SECOND_PROBE: probe_type_str = "SECOND_PROBE";
244 break;
245 case LINEAR_PROBE: probe_type_str = "LINEAR_PROBE";
246 break;
247 }
248 return out << "Bucket at " << &bucket << '\n'
249 << "status = " << status_str << '\n'
250 << "probe_type = " << probe_type_str << '\n'
251 << "probe_counter = " << bucket.probe_counter;
252 }
253 };
254
255 Bucket *table = nullptr; //bucket arrangement
256 Hash_Fct hash_fct = nullptr; // first hash function
257 Hash_Fct second_hash_fct = nullptr; // second hash function
258
260
261 protected:
262 size_t len; // table size
265
266 private:
267 size_t N; // number of buckets occupied
269
270 Bucket * allocate_bucket(Bucket & bucket, unsigned char probe_type) noexcept
271 {
272 assert(bucket.status != BUSY);
273
274 ++N;
275 bucket.status = BUSY;
276 bucket.probe_type = probe_type;
277 ++bucket.probe_counter;
278
279 return &bucket;
280 }
281
282 void decrease_probe_counter(Bucket *bucket) noexcept
283 {
284 assert(bucket->status == BUSY or bucket->status == DELETED);
285
286 --bucket->probe_counter;
287 if (bucket->probe_counter == 0) // <mark EMPTY on the bucket?
288 bucket->status = EMPTY;
289 }
290
291 void deallocate_bucket(Bucket *bucket) noexcept
292 {
293 deallocate_bucket_with_record(bucket, [](Bucket *) {});
294 }
295
296 size_t index_forward(size_t & i) const noexcept
297 {
298 assert(i < len);
299 if (++i == len)
300 i = 0;
301 return i;
302 }
303
304 size_t index_backward(size_t & i) const noexcept
305 {
306 assert(i < len);
307 if (i-- == 0)
308 i = len - 1;
309 return i;
310 }
311
312 [[nodiscard]] bool is_valid_bucket(Bucket *bucket) const noexcept
313 {
314 if (table == nullptr)
315 return false;
316
317 const auto begin = reinterpret_cast<std::uintptr_t>(&table[0]);
318 const auto end = reinterpret_cast<std::uintptr_t>(&table[len]);
319 const auto addr = reinterpret_cast<std::uintptr_t>(bucket);
320
322 return false;
323
324 const auto offset_with_base =
325 static_cast<std::ptrdiff_t>(addr - begin);
326 return offset_with_base % sizeof(*bucket) == 0;
327 }
328
329 [[nodiscard]] size_t bucket_to_index(Bucket *bucket) const noexcept
330 {
331 assert(is_valid_bucket(bucket));
332 return bucket - &table[0];
333 }
334
336 [[nodiscard]] bool is_key_in_table(const Key & key) const noexcept
337 {
338 if (table == nullptr)
339 return false;
340
341 const auto addr = reinterpret_cast<std::uintptr_t>(&key);
342 const auto table_begin = reinterpret_cast<std::uintptr_t>(&table[0]);
343 const auto table_end = reinterpret_cast<std::uintptr_t>(&table[len]);
344
346 return false;
347
348 constexpr auto key_offset = offsetof(Bucket, key);
349 return ((addr - table_begin - key_offset) % sizeof(Bucket)) == 0;
350 }
351
353 [[nodiscard]] Bucket * key_in_table_to_bucket(const Key & key) noexcept
354 {
356 return key_to_bucket(const_cast<Key *>(&key));
357 }
358
361 template <typename RecordBucket>
363 RecordBucket && record_bucket) noexcept
364 {
365 assert(bucket->status == BUSY);
366
367 bucket->status = DELETED;
368 const Key & key = bucket->key;
369
370 const size_t i_fst = hash_fct(key) % len;
371 if (&table[i_fst] == bucket)
372 {
373 assert(Cmp () (table[i_fst].key, key));
374 assert(table[i_fst].probe_type == FIRST_PROBE);
375 }
376 else
377 {
378 const size_t i_snd = second_hash_fct(key) % len;
379 if (&table[i_snd] == bucket)
380 {
381 assert(Cmp () (table[i_snd].key, key));
382 assert(table[i_snd].probe_type == SECOND_PROBE);
385 }
386 else
387 {
392 size_t i = i_snd;
393 for (index_forward(i); &table[i] != bucket; index_forward(i))
394 {
395 assert(table[i].status != EMPTY);
396 record_bucket(&table[i]);
398 }
399 assert(Cmp () (table[i].key, key));
400 assert(table[i].probe_type == LINEAR_PROBE);
401 }
402 }
403
405 --N;
406 }
407
408 public:
409 [[nodiscard]] static Bucket * key_to_bucket(Key *rec) noexcept
410 {
411 const auto base = reinterpret_cast<std::uintptr_t>(rec);
412 const auto offset = offsetof(Bucket, key);
413 return reinterpret_cast<Bucket *>(base - offset);
414 }
415
416 [[nodiscard]] constexpr const Cmp &get_compare() const noexcept { return cmp; }
417
418 [[nodiscard]] constexpr Cmp &get_compare() noexcept { return cmp; }
419
420 void swap(ODhashTable & other) noexcept
421 {
422 std::swap(table, other.table);
423 std::swap(hash_fct, other.hash_fct);
424 std::swap(second_hash_fct, other.second_hash_fct);
425 std::swap(cmp, other.cmp);
426 std::swap(N, other.N);
427 std::swap(len, other.len);
428 }
429
430 protected:
433 const float lower, const float upper, const bool resize)
436 len(Primes::next_prime(l)),
438 N(0), with_resize(resize)
439 {
440 table = new Bucket[len];
441 }
442
443 public:
469
470 using Base::contains;
471
481
484 {
485 if (table != nullptr)
486 delete [] table;
487 }
488
496
499 {
500 assert(table != nullptr);
501 swap(other);
502 }
503
505
507 {
508 if (this == &other)
509 return *this;
510
511 if (len > other.N)
512 this->clean_table();
513 else
514 {
515 auto *new_table = new Bucket [other.len];
516 delete [] table;
518 N = 0;
519 len = other.len;
520 hash_fct = other.hash_fct;
521 second_hash_fct = other.second_hash_fct;
522 cmp = other.cmp;
523 lower_alpha = other.lower_alpha;
524 upper_alpha = other.upper_alpha;
525 with_resize = other.with_resize;
526 }
527
528 this->copy_from_table(other);
529
530 return *this;
531 }
532
534 {
535 swap(other);
536 return *this;
537 }
538
540
543 [[nodiscard]] Key * search(const Key & key) const noexcept
544 {
545 const size_t i_fst = hash_fct(key) % len; // 1st probe (1st hash function)
546 if (table[i_fst].status == EMPTY) [[unlikely]]
547 return nullptr;
548
549 if (table[i_fst].status == BUSY and cmp(table[i_fst].key, key)) [[likely]]
550 {
551 assert(table[i_fst].probe_type == FIRST_PROBE);
552 assert(table[i_fst].probe_counter > 0);
553 return &table[i_fst].key;
554 }
555
556 // Early exit: if BUSY with different key and probe_counter == 1,
557 // no other key with this first hash can exist in the table
558 if (table[i_fst].status == BUSY and table[i_fst].probe_counter == 1)
559 return nullptr;
560
561 const size_t i_snd = second_hash_fct(key) % len; // 2nd probe
562
563 // If both hashes collide to same index, skip directly to linear probing
564 if (i_fst == i_snd) [[unlikely]]
565 goto linear_probe;
566
567 if (table[i_snd].status == EMPTY)
568 return nullptr;
569
570 if (table[i_snd].status == BUSY and cmp(table[i_snd].key, key))
571 {
572 assert(table[i_snd].probe_type == SECOND_PROBE);
573 assert(table[i_snd].probe_counter > 0);
574 return &table[i_snd].key;
575 }
576
577 // Early exit: if BUSY with different key and probe_counter == 1,
578 // no other key passed through this bucket
579 if (table[i_snd].status == BUSY and table[i_snd].probe_counter == 1)
580 return nullptr;
581
583 size_t i = i_snd;
584 // Linear polling from index of 2nd hash function
585 for (size_t count = 0; count < len; ++count)
586 {
587 index_forward(i);
588 // Prefetch next bucket for better cache performance
589 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
590 switch (table[i].status)
591 {
592 case EMPTY:
593 assert(table[i].probe_counter == 0);
594 return nullptr;
595 case BUSY:
596 assert(table[i].probe_counter > 0);
597 if (cmp(table[i].key, key)) [[unlikely]]
598 {
599 assert(table[i].probe_type == LINEAR_PROBE);
600 return &table[i].key;
601 }
602 break;
603 case DELETED:
604 assert(table[i].probe_counter > 0);
605 break;
606 default: AH_ERROR("ODhashTable search: inconsistent bucket status");
607 }
608 }
609
610 return nullptr;
611 }
612
614
616 {
618 }
619
624
625 private:
626 Bucket * allocate_bucket(const Key & key) noexcept
627 {
628 assert(N < len);
629
630 const size_t i_fst = hash_fct(key) % len;
631
632 // EMPTY at first probe: safe to insert, no collision chain
633 if (table[i_fst].status == EMPTY)
635
636 // BUSY at first probe: check for duplicate
637 if (table[i_fst].status == BUSY && cmp(table[i_fst].key, key))
638 return nullptr;
639
640 const size_t i_snd = second_hash_fct(key) % len;
641
642 // EMPTY at second probe: safe to insert if first wasn't the key
643 if (table[i_snd].status == EMPTY)
644 {
645 // If first was DELETED, use it; otherwise use second
646 if (table[i_fst].status == DELETED)
650 }
651
652 // BUSY at second probe: check for duplicate
653 if (table[i_snd].status == BUSY && cmp(table[i_snd].key, key))
654 return nullptr;
655
656 // Need to do linear probing - search for duplicate and find insertion point
657 Bucket *candidate = nullptr;
658 size_t candidate_idx = 0;
659
660 // Check if first or second are candidates (DELETED)
661 if (table[i_fst].status == DELETED)
662 {
664 candidate_idx = 0; // Marker: use first probe slot
665 }
666 else if (table[i_snd].status == DELETED)
667 {
669 candidate_idx = 1; // Marker: use second probe slot
670 }
671
672 size_t i = i_snd;
673 for (size_t c = 0; c < len; ++c)
674 {
675 index_forward(i);
676 // Prefetch next bucket for better cache performance
677 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
678
679 if (table[i].status == EMPTY)
680 {
681 // End of collision chain - no duplicate, insert
682 if (candidate)
683 {
684 // Insert at the first DELETED slot found
685 if (candidate_idx == 0) // First probe slot
687 if (candidate_idx == 1) // Second probe slot
688 {
691 }
692 // Linear probe slot - need to update all counters
695 size_t j = i_snd;
696 for (size_t k = 0; k < candidate_idx - 2; ++k)
697 {
698 index_forward(j);
699 ++table[j].probe_counter;
700 }
702 }
703 // No DELETED found, insert at this EMPTY slot
706 // Increment probe_counter for all buckets between i_snd and i (exclusive)
707 size_t j = i_snd;
708 for (size_t k = 0; k < c; ++k)
709 {
710 index_forward(j);
711 ++table[j].probe_counter;
712 }
714 }
715
716 if (table[i].status == BUSY && cmp(table[i].key, key))
717 return nullptr; // Duplicate found
718
719 if (table[i].status == DELETED && candidate == nullptr)
720 {
721 candidate = &table[i];
722 candidate_idx = c + 2; // Store position (2 = after first and second)
723 }
724 }
725
726 // Table full, but might have a DELETED slot
727 if (candidate)
728 {
729 if (candidate_idx == 0)
731 if (candidate_idx == 1)
732 {
735 }
738 size_t j = i_snd;
739 for (size_t k = 0; k < candidate_idx - 2; ++k)
740 {
741 index_forward(j);
742 ++table[j].probe_counter;
743 }
745 }
746
747 return nullptr;
748 }
749
750 // search key. If found return (bucket_ptr, true), otherwise allocates and
751 // returns (new_bucket_ptr, false)
752 std::tuple<Bucket *, bool> hard_allocate_bucket(const Key & key) noexcept
753 {
754 assert(N < len);
755
756 Bucket *candidate = nullptr;
757 size_t candidate_linear_steps = 0; // How many linear steps to reach candidate
758
759 const size_t i_fst = hash_fct(key) % len;
760 switch (table[i_fst].status)
761 {
762 case EMPTY:
763 return {allocate_bucket(table[i_fst], FIRST_PROBE), false};
764 case DELETED:
766 // candidate at FIRST_PROBE, no linear steps needed
767 break;
768 case BUSY:
769 if (cmp(table[i_fst].key, key))
770 return {&table[i_fst], true}; // found existing
771 break;
772 }
773
774 const size_t i_snd = second_hash_fct(key) % len;
775 switch (table[i_snd].status)
776 {
777 case EMPTY:
778 if (candidate) // candidate is at i_fst (FIRST_PROBE)
779 return {allocate_bucket(*candidate, FIRST_PROBE), false};
781 return {allocate_bucket(table[i_snd], SECOND_PROBE), false};
782 case DELETED:
783 if (candidate == nullptr)
784 {
786 // candidate at SECOND_PROBE, no linear steps needed
787 }
788 break;
789 case BUSY:
790 if (cmp(table[i_snd].key, key))
791 return {&table[i_snd], true}; // found existing
792 break;
793 }
794
795 // Determine if candidate is at i_fst or i_snd (for probe_type later)
796 const bool candidate_at_first = (candidate == &table[i_fst]);
797 const bool candidate_at_second = (candidate == &table[i_snd]);
798
799 size_t i = i_snd;
800 for (size_t c = 0; c < len; ++c)
801 {
802 index_forward(i);
803 // Prefetch next bucket for better cache performance
804 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
805 switch (table[i].status)
806 {
807 case BUSY:
808 if (cmp(table[i].key, key))
809 return {&table[i], true}; // found existing
810 break;
811 case DELETED:
812 if (candidate == nullptr)
813 {
814 candidate = &table[i];
815 candidate_linear_steps = c + 1; // Steps from i_snd
816 }
817 break;
818 case EMPTY:
819 {
820 // End of collision chain - insert at candidate or here
821 if (candidate)
822 {
824 return {allocate_bucket(*candidate, FIRST_PROBE), false};
825
827
829 return {allocate_bucket(*candidate, SECOND_PROBE), false};
830
831 // Candidate is in LINEAR_PROBE region
833 // Increment all buckets between i_snd and candidate
834 size_t j = i_snd;
835 for (size_t k = 0; k < candidate_linear_steps - 1; ++k)
836 {
837 index_forward(j);
838 ++table[j].probe_counter;
839 }
840 return {allocate_bucket(*candidate, LINEAR_PROBE), false};
841 }
842 // No candidate found, insert at this EMPTY slot
845 // Increment all buckets between i_snd and here
846 size_t j = i_snd;
847 for (size_t k = 0; k < c; ++k)
848 {
849 index_forward(j);
850 ++table[j].probe_counter;
851 }
852 return {allocate_bucket(table[i], LINEAR_PROBE), false};
853 }
854 default: AH_ERROR("ODhashTable: Invalid bucket status");
855 break;
856 }
857 }
858
859 // All slots checked (full table), use candidate if found
860 if (candidate)
861 {
863 return {allocate_bucket(*candidate, FIRST_PROBE), false};
864
866
868 return {allocate_bucket(*candidate, SECOND_PROBE), false};
869
870 // Candidate is in LINEAR_PROBE region
872 size_t j = i_snd;
873 for (size_t k = 0; k < candidate_linear_steps - 1; ++k)
874 {
875 index_forward(j);
876 ++table[j].probe_counter;
877 }
878 return {allocate_bucket(*candidate, LINEAR_PROBE), false};
879 }
880
881 return {nullptr, false};
882 }
883
889 void remove_bucket(Bucket *bucket)
890 {
891 ah_invalid_argument_if(not is_valid_bucket(bucket)) << "key pty does not belong to hash table";
892 ah_domain_error_if(bucket->status != BUSY) << "Bucket containing key is not BUSY";
893
894 deallocate_bucket(bucket);
895 }
896
897 public:
903 void remove(const Key & key)
904 {
905 // Fast path: if key is a reference to a key inside the table
906 if (is_key_in_table(key)) [[likely]]
907 {
908 Bucket *bucket = key_in_table_to_bucket(key);
909 ah_domain_error_if(bucket->status != BUSY)
910 << "Bucket containing key is not BUSY";
911 deallocate_bucket(bucket);
912 return;
913 }
914
915 // External key: search for it (without modifying probe_counters)
916 const size_t i_fst = hash_fct(key) % len;
917
918 // Check first probe position
919 ah_domain_error_if(table[i_fst].status == EMPTY) << "Key not in hash table";
920
921 if (table[i_fst].status == BUSY and cmp(table[i_fst].key, key)) [[likely]]
922 {
924 return;
925 }
926
927 // Early exit: if BUSY with different key and probe_counter == 1,
928 // no other key with this first hash can exist in the table
929 ah_domain_error_if(table[i_fst].status == BUSY and table[i_fst].probe_counter == 1)
930 << "Key not in hash table";
931
932 const size_t i_snd = second_hash_fct(key) % len;
933
934 // If both hashes collide to same index, skip directly to linear probing
935 if (i_fst == i_snd) [[unlikely]]
937
938 // Check second probe position
939 ah_domain_error_if(table[i_snd].status == EMPTY) << "Key not in hash table";
940
941 if (table[i_snd].status == BUSY and cmp(table[i_snd].key, key))
942 {
944 return;
945 }
946
947 // Early exit: if BUSY with different key and probe_counter == 1,
948 // no other key passed through this bucket
949 ah_domain_error_if(table[i_snd].status == BUSY and table[i_snd].probe_counter == 1)
950 << "Key not in hash table";
951
953 // Linear probing (search only, no modifications until key found)
954 size_t i = i_snd;
955 for (size_t c = 0; c < len; ++c)
956 {
957 index_forward(i);
958 // Prefetch next bucket for better cache performance
959 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
960
961 switch (table[i].status)
962 {
963 case EMPTY:
964 ah_domain_error() << "Key not in hash table";
965 break; // unreachable, but silences -Wimplicit-fallthrough
966
967 case BUSY:
968 if (cmp(table[i].key, key)) [[unlikely]]
969 {
971 return;
972 }
973 break;
974
975 case DELETED:
976 break;
977 }
978 }
979
980 ah_domain_error() << "Key not in hash table";
981 }
982
984
986 {
987 DynArray<size_t> lens;
988 size_t num_busy = 0, num_deleted = 0, num_empty = 0;
989 size_t max_len = std::numeric_limits<size_t>::min();
990 for (size_t i = 0; i < len; ++i)
991 switch (table[i].status)
992 {
993 case BUSY:
994 {
995 ++num_busy;
996 const Key & key = table[i].key;
997 size_t count = 1;
998 const size_t i_fst = hash_fct(key) % len;
999 if (cmp(table[i_fst].key, key))
1000 {
1001 assert(table[i_fst].probe_type == FIRST_PROBE);
1002 assert(table[i_fst].probe_counter > 0);;
1003 }
1004 else
1005 {
1006 ++count;
1007 size_t i_snd = second_hash_fct(key) % len;
1008 if (cmp(table[i_snd].key, key))
1009 {
1010 assert(table[i_snd].probe_type == SECOND_PROBE);
1011 assert(table[i_snd].probe_counter > 0);;
1012 }
1013 else
1014 {
1015 for (size_t i = index_forward(i_snd); true;
1016 index_forward(i))
1017 {
1018 if (table[i].status == BUSY and cmp(table[i].key, key))
1019 break;
1020 ++count;
1021 }
1022 }
1023 }
1024 max_len = std::max(max_len, count);
1025 update_stat_len(lens, count);
1026 break;
1027 }
1028 case EMPTY:
1029 ++num_empty;
1030 update_stat_len(lens, 0);
1031 break;
1032 case DELETED:
1033 ++num_deleted;
1034 break;
1035 }
1036
1037 float avg = 0, sum = 0;
1038 for (size_t i = 0; i < lens.size(); ++i)
1039 {
1040 avg += lens(i) * i;
1041 sum += lens(i);
1042 }
1043
1044 avg /= sum;
1045 float var = 0;
1046 for (size_t i = 0; i < lens.size(); ++i)
1047 {
1048 const float s = i - avg;
1049 var += lens(i) * s * s;
1050 }
1051 var /= sum;
1052
1053 Stats stats;
1054 stats.num_busy = num_busy;
1055 stats.num_deleted = num_deleted;
1056 stats.num_empty = num_empty;
1057 std::swap(lens, stats.lens);
1058 stats.avg = avg;
1059 stats.var = var;
1060 stats.max_len = max_len;
1061
1062 return stats;
1063 }
1064 };
1065
1066
1067 template <typename Key, class Cmp = Aleph::equal_to<Key>>
1069
1070
1071# ifdef NBACKUP
1072# define N NBACKUP
1073# undef NBACKUP
1074# endif
1075
1076# ifdef MBACKUP
1077# define M MBACKUP
1078# undef MBACKUP
1079# endif
1080
1081# undef EMPTY
1082# undef BUSY
1083# undef DELETED
1084# undef NO_PROBED
1085# undef FIRST_PROBE
1086# undef SECOND_PROBE
1087# undef LINEAR_PROBE
1088} // end namespace Aleph
1089
1090# endif // TPL_ODHASH_H
C++20 concepts for constraining comparison functors.
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error()
Throws std::domain_error unconditionally.
Definition ah-errors.H:554
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
#define AH_ERROR(format, args...)
Print an error message (always enabled).
Definition ahDefs.H:271
DRY (Don't Repeat Yourself) utilities and macros.
#define Special_Ctors(Set_Type, Type)
Generates special constructors for containers.
Definition ahDry.H:113
Iterator wrapper for C++ raw arrays and circular buffers.
size_t size() const noexcept
Return the current dimension of array.
Open addressing hash table with double hashing collision resolution.
Definition tpl_odhash.H:182
Bucket * key_in_table_to_bucket(const Key &key) noexcept
Converts a key reference inside the table to its containing bucket.
Definition tpl_odhash.H:353
static Bucket * key_to_bucket(Key *rec) noexcept
Definition tpl_odhash.H:409
typename OhashCommon< ODhashTable< Key, Cmp >, Key >::Stats Stats
Definition tpl_odhash.H:983
size_t bucket_to_index(Bucket *bucket) const noexcept
Definition tpl_odhash.H:329
void deallocate_bucket_with_record(Bucket *bucket, RecordBucket &&record_bucket) noexcept
Template version of deallocate_bucket that accepts a callback to record modified buckets before decre...
Definition tpl_odhash.H:362
bool is_key_in_table(const Key &key) const noexcept
Returns true if the key reference points to a key inside the table.
Definition tpl_odhash.H:336
size_t(*)(const Key &) Hash_Fct_Ptr
Definition tpl_odhash.H:192
size_t index_backward(size_t &i) const noexcept
Definition tpl_odhash.H:304
void set_second_hash_fct(Hash_Fct fct) noexcept
Definition tpl_odhash.H:615
Hash_Fct second_hash_fct
Definition tpl_odhash.H:257
Key * search(const Key &key) const noexcept
searches the table for the key.
Definition tpl_odhash.H:543
void decrease_probe_counter(Bucket *bucket) noexcept
Definition tpl_odhash.H:282
constexpr Hash_Fct get_second_hash_fct() const noexcept
Definition tpl_odhash.H:613
~ODhashTable()
Free the whole hash table.
Definition tpl_odhash.H:483
std::function< size_t(const Key &)> Hash_Fct
Definition tpl_odhash.H:190
void swap(ODhashTable &other) noexcept
Definition tpl_odhash.H:420
ODhashTable(const size_t l, Hash_Fct fst_hash_fct, Hash_Fct snd_hash_fct, Cmp cpm_fct, const float lower, const float upper, const bool resize)
Definition tpl_odhash.H:431
constexpr Cmp & get_compare() noexcept
Definition tpl_odhash.H:418
ODhashTable(const ODhashTable &other)
Definition tpl_odhash.H:489
void remove_bucket(Bucket *bucket)
Delete the record from the table.
Definition tpl_odhash.H:889
bool is_valid_bucket(Bucket *bucket) const noexcept
Definition tpl_odhash.H:312
ODhashTable(ODhashTable &&other) noexcept
Definition tpl_odhash.H:497
void remove(const Key &key)
Remove a key from the hash table.
Definition tpl_odhash.H:903
Bucket * allocate_bucket(const Key &key) noexcept
Definition tpl_odhash.H:626
void set_second_hash_fct(Hash_Fct_Ptr fct) noexcept
Definition tpl_odhash.H:620
size_t index_forward(size_t &i) const noexcept
Definition tpl_odhash.H:296
static void update_stat_len(DynArray< size_t > &lens, size_t i)
Definition tpl_odhash.H:539
void deallocate_bucket(Bucket *bucket) noexcept
Definition tpl_odhash.H:291
constexpr const Cmp & get_compare() const noexcept
Definition tpl_odhash.H:416
ODhashTable(size_t len=Primes::DefaultPrime, Hash_Fct_Ptr first_hash_fct=Aleph::dft_hash_ptr_fct< Key >, Hash_Fct_Ptr second_hash_fct=Aleph::snd_hash_ptr_fct< Key >, Cmp cmp=Cmp(), float lower_alpha=hash_default_lower_alpha, float upper_alpha=hash_default_upper_alpha, bool with_resize=true)
Definition tpl_odhash.H:472
Bucket * allocate_bucket(Bucket &bucket, unsigned char probe_type) noexcept
Definition tpl_odhash.H:270
std::tuple< Bucket *, bool > hard_allocate_bucket(const Key &key) noexcept
Definition tpl_odhash.H:752
Stats stats() const
Definition tpl_odhash.H:985
ODhashTable & operator=(const ODhashTable &other)
Definition tpl_odhash.H:506
Equality test for containers.
Definition ah-dry.H:1826
Common methods to the Aleph-w ( ) containers.
Definition ah-dry.H:642
Common sequential searching methods on containers.
Definition ah-dry.H:196
LocateFunctions< Container, Type > * base() const
Definition ah-dry.H:204
CRTP mixin providing common operations for open addressing hash tables.
Definition hashDry.H:101
size_t resize(size_t new_size)
Resizes the hash table to a new capacity.
Definition hashDry.H:524
void clean_table()
Removes all entries from the table without deallocating storage.
Definition hashDry.H:166
void copy_from_table(const HashTbl &other)
Copies all entries from another hash table.
Definition hashDry.H:143
constexpr bool contains(const Key &key) const noexcept
Alias for has().
Definition hashDry.H:425
Mixin that adds STL begin()/end() and cbegin()/cend() to Aleph containers.
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
Equivalence relation constraint for equality comparators.
Definition ah-concepts.H:97
Common hash table utilities and base classes.
#define OHASH_COMMON(class_name)
Definition hash-dry.H:48
Common operations for open addressing hash tables (CRTP mixin).
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
size_t snd_hash_fct(const Key &key) noexcept
Secondary default hash: different distribution from dft_hash_fct.
Definition hash-fct.H:1081
const float hash_default_upper_alpha
Definition hash-dry.C:40
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
const float hash_default_lower_alpha
Definition hash-dry.C:38
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
const unsigned long DefaultPrime
Default prime number used when no specific size is requested.
Definition primes.C:381
Prime number utilities for hash tables and mathematical operations.
constexpr bool valid() const noexcept
Definition tpl_odhash.H:217
friend std::ostream & operator<<(std::ostream &out, const Bucket &bucket)
Definition tpl_odhash.H:224
Generic traversal of the container through its iterator.
Definition ah-dry.H:67
static int * k
Lazy and scalable dynamic array implementation.
DynList< int > l