Zip and Skip Tries
Loading...
Searching...
No Matches
SkipTrie.hpp
Go to the documentation of this file.
1
10#pragma once
11
12#include <cmath> // for log2
13#include <cstddef> // for size_t
14#include <memory> // for std::shared_ptr
15#include <random> // for random height generation
16#include <vector> // for std::vector
17#include <unordered_map> // for std::unordered_map
18#include <fstream> // for std::ostream
19#include <string> // for std::string
20#include <sstream> // for std::ostringstream in to_string
21#include <compare> // for std::strong_ordering
22
23#include "BitString.cuh"
24
31enum class Direction
32{
33 FORWARD,
34 BACKWARD,
35 INPLACE
36};
37
52template<typename CHAR_T, unsigned CHAR_SIZE_BITS = sizeof(CHAR_T) * 8>
54{
55public:
58
65
73 static size_t get_random_height();
74
84 bool insert(const KEY_T* key) noexcept;
85
98 bool insert(const KEY_T* key, size_t height) noexcept;
99
107 bool contains(const KEY_T* key) const noexcept;
108
119 bool remove(const KEY_T* key) noexcept;
120
128 std::ostream& print(std::ostream& os) const noexcept;
129
136 std::string to_string() const noexcept;
137
142 size_t size() const noexcept { return m_size; }
143
150 size_t height() const noexcept { return m_height; }
151
161 size_t lcp(const KEY_T* key) const noexcept;
162
173 size_t lcp_with_others(const KEY_T* key) const noexcept;
174
186 std::vector<const KEY_T*> suffix_search(const KEY_T* key) const noexcept;
187
198 std::vector<const KEY_T*> range_search(const KEY_T* key1, const KEY_T* key2) const noexcept;
199
209 std::unordered_map<size_t, size_t> get_lcp_group_sizes() const noexcept;
210
213 size_t m_size;
215 size_t m_height;
216
224 {
226 const KEY_T* key = nullptr;
228 std::shared_ptr<Node> next{nullptr};
230 Node* prev = nullptr;
232 std::shared_ptr<Node> down{nullptr};
234 size_t lcp_next{0};
235
241 size_t lcp(Direction direction) const noexcept;
242
248 Node* next_node(Direction direction) const noexcept;
249 };
250
252 std::shared_ptr<Node> m_head = nullptr;
254 std::shared_ptr<Node> m_lower_head = nullptr;
256 std::shared_ptr<Node> m_lower_tail = nullptr;
257
266
274
292
306
313 {
317 size_t lcp;
318 };
319
329 virtual NodeLCP find_first(const KEY_T* key, const bool require_level0 = false) const noexcept;
330
345
357 virtual EqualOrSuccessor find_equal_or_successor(const KEY_T* key, const bool require_level0) const noexcept;
358
365 {
367 std::strong_ordering result;
369 size_t lcp;
370 };
371
383 virtual ResultLCP compare(const KEY_T* key1, const KEY_T* key2, size_t lcp) const noexcept;
384
385public:
393 {
394 public:
400 Iterator(Node* node, Direction direction) noexcept;
401
406 Iterator& operator++() noexcept;
407
413 Iterator operator++(int) noexcept;
414
420
426
432 bool operator==(const Iterator& other) const noexcept;
433
439 bool operator!=(const Iterator& other) const noexcept;
440
441 private:
446 };
447
452 Iterator begin() const noexcept;
453
458 Iterator end() const noexcept;
459};
460
474
475
476// ========================================================================== //
477// Implementation Details //
478// ========================================================================== //
479
480// --- Iterator Implementation ---
481
485{
486 // Constructor body intentionally empty
487}
488
489template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
495
496template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
498{
499 Iterator temp = *this;
500 ++(*this); // Use pre-increment
501 return temp;
502}
503
504template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
509
510template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
515
516template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
518{
519 return m_node == other.m_node;
520}
521
522template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
524{
525 return !(*this == other); // Delegate to operator==
526}
527
528template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
530{
531 // Returns iterator starting from the first actual node after the head sentinel at the base level.
532 return Iterator(m_lower_head->next.get(), Direction::FORWARD);
533}
534
535template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
537{
538 // Returns iterator pointing to the tail sentinel at the base level.
540}
541
542// --- Node Implementation ---
543
544template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
546{
548 {
549 // LCP with the next node is stored directly.
550 return lcp_next;
551 }
552
553 // LCP with the previous node is stored in the previous node's lcp_next.
554 return prev ? prev->lcp_next : 0;
555}
556
557template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
559{
561 {
562 return next.get();
563 }
564
565 return prev;
566}
567
568// --- SkipTrie Implementation ---
569
570template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
575
576template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
578{
579 // Use static variables for random number generation.
580 // Note: Not thread-safe if multiple threads call this concurrently.
581 static std::random_device rd; // Obtain a random seed from the hardware device
582 static std::mt19937 gen(rd()); // Seed the default random engine
583 // static std::mt19937 gen(1); // Use for deterministic testing (fixed seed)
584 static std::geometric_distribution<size_t> dist(0.5); // p=0.5 gives 50% chance of 0, 25% of 1, etc.
585
586 return dist(gen);
587}
588
589template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
591{
592 // Create new sentinel nodes for the layer.
593 std::shared_ptr<Node> new_head = std::make_shared<Node>();
594 std::shared_ptr<Node> new_tail = std::make_shared<Node>();
595
596 // Link the new head down to the old head (or null if no layers yet).
597 new_head->down = m_head;
598 // Link the new tail down to the old tail (or null if no layers yet).
599 // Access old tail via old head's next pointer.
600 if (m_head)
601 {
602 new_tail->down = m_head->next;
603 }
604 // Link new head and tail horizontally.
605 new_head->next = new_tail;
606 new_tail->prev = new_head.get();
607
608 // Update the main head pointer to the new head.
609 m_head = new_head;
610 // Increment the height counter.
611 ++m_height;
612
613 // If this is the very first layer added, set the lower head/tail pointers.
614 if (!m_lower_head)
615 {
616 m_lower_head = m_head;
617 m_lower_tail = new_tail;
618 }
619}
620
621template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
623{
624 // Move the head pointer down to the next layer.
625 m_head = m_head->down;
626 // Decrement the height.
627 --m_height;
628
629 // If height becomes 0, reset lower head/tail pointers.
630 if (m_height == 0)
631 {
632 m_lower_head = nullptr;
633 m_lower_tail = nullptr;
634 }
635}
636
637template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
638auto SkipTrie<CHAR_T, CHAR_SIZE_BITS>::compare(const KEY_T* key1, const KEY_T* key2, size_t lcp) const noexcept -> ResultLCP
639{
640 // Delegate comparison to the BitString's sequential compare method.
641 auto [comparison, next_lcp] = key1->seq_k_compare(*key2, lcp);
642 return { comparison, next_lcp };
643}
644
645template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
647{
648 // If already found inplace, no need to iterate.
650 {
651 return Direction::INPLACE;
652 }
653
654 // Get the next node in the current direction.
655 Node* next = curr->next_node(direction);
656
657 // Iterate while the next node exists and the LCP allows potential match/crossing.
658 // Check `next->key` to ensure we don't compare against the tail sentinel.
659 // `curr->lcp(direction)` gives LCP between `curr` and `next`.
660 while (next->key && curr->lcp(direction) >= lcp)
661 {
662 // Optimization: If LCP between curr and next is strictly greater than current path LCP,
663 // we know the target key cannot be between curr and next based on prefixes. Skip over next.
664 if (curr->lcp(direction) > lcp)
665 {
666 curr = next;
667 next = curr->next_node(direction);
668 continue;
669 }
670
671 // If LCPs are equal, we must perform a full comparison starting from `lcp`.
672 auto [comparison, next_lcp] = compare(key, next->key, lcp);
673 // Update the path LCP with the result of the comparison.
674 lcp = next_lcp;
675
676 // Move to the next node.
677 curr = next;
678
679 // Check comparison result:
680 if (comparison == std::strong_ordering::equal)
681 {
682 // Exact match found.
683 return Direction::INPLACE;
684 }
685 else if (comparison == std::strong_ordering::less)
686 {
687 // Key is less than curr->key.
689 {
690 // If moving forward, we've gone past the insertion point. Need to go backward on level below.
691 return Direction::BACKWARD;
692 }
693 // If already moving backward, continue moving backward.
694 }
695 else // comparison == std::strong_ordering::greater
696 {
697 // Key is greater than curr->key.
699 {
700 // If moving backward, we've gone past the insertion point. Need to go forward on level below.
701 return Direction::FORWARD;
702 }
703 // If already moving forward, continue moving forward.
704 }
705
706 // Get the next node for the next iteration.
707 next = curr->next_node(direction);
708 }
709
710 // Loop finished: `next` is either the tail sentinel or `curr->lcp(direction) < lcp`.
711 // The correct direction to proceed on the level below is the current `direction`.
712 return direction;
713}
714
715template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
717{
718 // Use find_equal_or_successor to perform the search.
719 auto [node, is_equal, lcp] = find_equal_or_successor(key, require_level0);
720
721 // Return the node only if an exact match was found.
722 return { is_equal ? node : nullptr, lcp };
723}
724
725template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
727{
728 // Check if find_first returns a non-null node.
729 return find_first(key).node != nullptr;
730}
731
732template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
734{
735 // Insert with a randomly generated height.
736 return insert(key, get_random_height());
737}
738
739template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
740bool SkipTrie<CHAR_T, CHAR_SIZE_BITS>::insert(const KEY_T* key, size_t height) noexcept
741{
742 // Ensure the skip list has enough layers for the specified height.
743 // Need height + 1 levels (0 to height). m_height is number of levels.
744 while (height + 1 >= m_height)
745 {
746 add_layer();
747 }
748
749 // Call the recursive insertion helper. Starts from top head node (m_head).
750 // Initial direction is FORWARD, initial LCP is 0, current height starts at m_height - 1.
751 // insert_recursive returns nullptr if key already exists.
752 bool already_exists = insert_recursive(key, m_head.get(), height, Direction::FORWARD, 0, m_height - 1) == nullptr;
753
754 if (already_exists)
755 {
756 return false; // Key was not inserted because it already existed.
757 }
758
759 // Increment size only if insertion was successful.
760 ++m_size;
761 return true;
762}
763
764template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
765std::shared_ptr<typename SkipTrie<CHAR_T, CHAR_SIZE_BITS>::Node> SkipTrie<CHAR_T, CHAR_SIZE_BITS>::insert_recursive(const KEY_T* key, Node* curr, size_t height, Direction direction, size_t lcp, size_t current_height) noexcept
766{
767 // Find the predecessor node (`curr`) on the current level.
768 Direction next_direction = iter_layer(key, curr, direction, lcp);
769
770 // If iter_layer found an exact match, return nullptr to signal duplication.
772 {
773 return nullptr;
774 }
775
776 // Recursively insert on the level below, unless we are at the base level.
777 std::shared_ptr<Node> child = nullptr;
778 if (current_height > 0)
779 {
780 child = insert_recursive(key, curr->down.get(), height, next_direction, lcp, current_height - 1);
781
782 // If recursive call returned nullptr, propagate nullptr up.
783 if (!child)
784 {
785 return nullptr;
786 }
787 }
788
789
790 // If the current level is above the target insertion height, just return the child pointer
791 // (or nullptr if base level) without inserting at this level.
792 if (current_height > height)
793 {
794 return child;
795 }
796
797 // --- Insert node at the current level ---
798
799 // Create the new node.
800 std::shared_ptr<Node> new_node = std::make_shared<Node>();
801 new_node->key = key;
802 new_node->down = child; // Link down to the node created/returned from the level below.
803
804 // Link the new node horizontally based on the direction determined by iter_layer.
805 // `curr` is the predecessor node found by iter_layer.
806 switch (next_direction)
807 {
808 case Direction::FORWARD: // Insert new_node after curr
809 new_node->next = curr->next;
810 new_node->prev = curr;
811 curr->next->prev = new_node.get(); // Update next node's prev pointer
812 curr->next = new_node;
813
814 // Update LCP values.
815 new_node->lcp_next = curr->lcp_next; // New node inherits LCP curr had with its old next.
816 curr->lcp_next = lcp; // LCP between curr and new_node is the final LCP from iter_layer.
817 break;
818 case Direction::BACKWARD: // Insert new_node before curr (i.e., after curr->prev)
819 new_node->next = curr->prev->next; // Should be `curr` itself
820 new_node->prev = curr->prev;
821 curr->prev->next = new_node; // Update previous node's next pointer
822 curr->prev = new_node.get();
823
824 // Update LCP values.
825 new_node->lcp_next = lcp; // LCP between new_node and curr is the final LCP from iter_layer.
826 // new_node->prev->lcp_next remains unchanged (LCP between prev and new_node).
827 break;
829 // This case should not be reached here due to the check after iter_layer.
830 // If it were, it would indicate an error.
831 break;
832 }
833
834 // Return the newly created node so the level above can link its `down` pointer.
835 return new_node;
836}
837
838template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
840{
841 Node* curr = find_first(key).node;
842
843 // If key not found, return false.
844 if (!curr)
845 {
846 return false;
847 }
848
849 // remove the node and all its children
850 while (curr)
851 {
852 auto prev = curr->prev;
853 auto next = curr->next;
854 prev->lcp_next = std::min(prev->lcp_next, curr->lcp_next);
855 curr = curr->down.get();
856
857 prev->next = next;
858 next->prev = prev;
859 }
860
861 // remove empty layers
862 while (!m_head->down || !m_head->down->next->key)
863 {
864 remove_layer();
865
866 if (m_height == 0)
867 {
868 break;
869 }
870 }
871
872 --m_size;
873
874 return true;
875}
876
877template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
878size_t SkipTrie<CHAR_T, CHAR_SIZE_BITS>::lcp(const KEY_T* key) const noexcept
879{
880 // Find the node (or insertion point) and return the LCP computed during search.
881 return find_first(key).lcp;
882}
883
884template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
886{
887 auto [node, lcp] = find_first(key, true);
888
889 if (!node)
890 {
891 return lcp;
892 }
893
894 return std::max(node->lcp(Direction::FORWARD), node->lcp(Direction::BACKWARD));
895}
896
897template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
898std::vector<const BitString<CHAR_T, CHAR_SIZE_BITS>*> SkipTrie<CHAR_T, CHAR_SIZE_BITS>::suffix_search(const KEY_T* key) const noexcept
899{
900 std::vector<const KEY_T*> keys;
901
902 auto [curr, _, lcp] = find_equal_or_successor(key, true).node;
903
904 if (lcp != key->size())
905 {
906 return keys; // No keys with this prefix found.
907 }
908
909 keys.push_back(curr->key);
910
911 // iterate through the list until the edge lcp is less than the key length
912 while (curr->next->key && curr->lcp_next >= lcp)
913 {
914 curr = curr->next.get();
915 keys.push_back(curr->key);
916 }
917
918 return keys;
919}
920
921template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
923{
924 // Handle empty list case.
925 if (!m_head)
926 {
927 return { nullptr, false, 0 };
928 }
929
930 Node* curr = m_head.get();
931 Node* prev = nullptr;
933 size_t lcp = 0;
934
935 // Traverse down the levels.
936 while (curr)
937 {
938 // Find predecessor on current level.
939 Direction next_direction = iter_layer(key, curr, direction, lcp);
940
942 {
943 while (require_level0 && curr->down)
944 {
945 curr = curr->down.get();
946 }
947
948 return { curr, true, lcp };
949 }
950
951 prev = curr;
952 curr = curr->down.get();
954 }
955
956 return { direction == Direction::FORWARD ? prev->next.get() : prev, false, lcp };
957}
958
959template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
960std::vector<const BitString<CHAR_T, CHAR_SIZE_BITS>*> SkipTrie<CHAR_T, CHAR_SIZE_BITS>::range_search(const KEY_T* key1, const KEY_T* key2) const noexcept
961{
962 std::vector<const KEY_T*> keys;
963
964 // Find the starting node (>= key1) at the base level.
965 Node* curr = find_equal_or_successor(key1, true).node;
966
967 if (!curr)
968 {
969 return keys;
970 }
971
972 auto [end, is_equal] = find_equal_or_successor(key2, true);
973
974 if (is_equal)
975 {
976 end = end->next.get();
977 }
978
979 while (curr != end)
980 {
981 keys.push_back(curr->key);
982 curr = curr->next.get();
983 }
984
985 return keys;
986}
987
988template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
990{
991 std::unordered_map<size_t, size_t> lcp_group_sizes;
992
993 std::vector<size_t> lcps_we_are_greater_than;
994 std::vector<size_t> index_first_encountered;
995
996 Node* curr = m_lower_head->next.get();
997
998 size_t i = 0;
999
1000 while (curr->key)
1001 {
1002 size_t lcp = curr->lcp_next;
1003
1004 while (!lcps_we_are_greater_than.empty() && lcp < lcps_we_are_greater_than.back())
1005 {
1006 size_t popped_lcp = lcps_we_are_greater_than.back();
1007 lcps_we_are_greater_than.pop_back();
1008
1009 size_t group_size = i - index_first_encountered.back();
1010 index_first_encountered.pop_back();
1011
1013 }
1014
1015 if (lcps_we_are_greater_than.empty() || lcp > lcps_we_are_greater_than.back())
1016 {
1017 lcps_we_are_greater_than.push_back(lcp);
1018 index_first_encountered.push_back(i);
1019 }
1020 // If current_lcp is equal to top, do nothing (extend the current group).
1021
1022 // Move to the next node and increment the gap index.
1023 curr = curr->next.get();
1024 }
1025
1026 return lcp_group_sizes;
1027}
1028
1029// --- Stream Operator Implementation ---
1030
1031template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
1032std::ostream& operator<<(std::ostream& os, const SkipTrie<CHAR_T, CHAR_SIZE_BITS>& ssl) {
1033 // Delegate to the print method.
1034 return ssl.print(os);
1035}
1036
1037template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
1038std::ostream& SkipTrie<CHAR_T, CHAR_SIZE_BITS>::print(std::ostream& os) const noexcept
1039{
1040 // print layer by layer
1041 auto left = m_head;
1042 size_t height = m_height - 1;
1043 while (left)
1044 {
1045 os << "Height: " << height << "|\t";
1046 os << "HEAD -0-> ";
1047 auto curr = left->next;
1048 while (curr->next)
1049 {
1050 // Print the key and the LCP to the next node.
1051 // Assumes KEY_T has an overloaded operator<<.
1052 os << *curr->key << " -" << curr->lcp_next << "-> ";
1053 curr = curr->next;
1054 }
1055
1056 os << "TAIL\n";
1057 left = left->down;
1058 --height;
1059 }
1060
1061 return os;
1062}
1063
1064template<typename CHAR_T, unsigned CHAR_SIZE_BITS>
1066{
1067 // Use a string stream to capture the output of the print method.
1068 std::ostringstream oss;
1069 print(oss);
1070 return oss.str();
1071}
Defines the BitString class for compact character string storage and comparison.
Direction
Enumerates the possible traversal directions within the skip list structure.
Definition SkipTrie.hpp:32
@ BACKWARD
Represents traversal towards the beginning of the list (predecessor nodes).
@ FORWARD
Represents traversal towards the end of the list (successor nodes).
@ INPLACE
Indicates no traversal needed, typically when the exact key is found.
std::ostream & operator<<(std::ostream &os, const SkipTrie< CHAR_T, CHAR_SIZE_BITS > &ssl)
A class to store strings compactly into integers, packing multiple characters into a single word.
Definition BitString.cuh:58
Provides forward/backward iteration over the keys in the base level of the SkipTrie.
Definition SkipTrie.hpp:393
Node * m_node
Pointer to the current node the iterator is positioned at.
Definition SkipTrie.hpp:443
bool operator!=(const Iterator &other) const noexcept
Compares this iterator with another for inequality.
Definition SkipTrie.hpp:523
bool operator==(const Iterator &other) const noexcept
Compares this iterator with another for equality.
Definition SkipTrie.hpp:517
const KEY_T * operator->() const noexcept
Dereferences the iterator to access the key pointer of the current node.
Definition SkipTrie.hpp:511
Iterator & operator++() noexcept
Pre-increments the iterator to the next node in its designated direction.
Definition SkipTrie.hpp:490
Direction m_direction
The direction the iterator moves upon incrementing.
Definition SkipTrie.hpp:445
const KEY_T & operator*() const noexcept
Dereferences the iterator to access the key of the current node.
Definition SkipTrie.hpp:505
Implements a skip list data structure optimized for storing and retrieving BitString keys.
Definition SkipTrie.hpp:54
virtual NodeLCP find_first(const KEY_T *key, const bool require_level0=false) const noexcept
Finds the node containing the exact key, returning the node and the search path LCP.
Definition SkipTrie.hpp:716
std::shared_ptr< Node > m_lower_head
Shared pointer to the head sentinel node of the base layer (level 0).
Definition SkipTrie.hpp:254
size_t lcp(const KEY_T *key) const noexcept
Calculates the Longest Common Prefix (LCP) between a given key and its position in the skip list sear...
Definition SkipTrie.hpp:878
std::ostream & print(std::ostream &os) const noexcept
Prints a textual representation of the skip list structure, layer by layer, to an output stream.
void remove_layer() noexcept
Removes the top-most layer of the skip list if it is empty (contains only sentinels).
Definition SkipTrie.hpp:622
size_t m_size
Stores the total number of keys (nodes) in the base level of the skip list.
Definition SkipTrie.hpp:213
Iterator end() const noexcept
Returns an iterator pointing past the last key in the skip list (the tail sentinel at the base level)...
Definition SkipTrie.hpp:536
size_t m_height
Stores the current maximum height (number of levels) of the skip list.
Definition SkipTrie.hpp:215
bool insert(const KEY_T *key) noexcept
Inserts a new key into the skip list with a randomly generated height.
Definition SkipTrie.hpp:733
bool contains(const KEY_T *key) const noexcept
Checks if a specific key exists within the skip list.
Definition SkipTrie.hpp:726
std::string to_string() const noexcept
Generates a string representation of the skip list structure.
std::vector< const KEY_T * > suffix_search(const KEY_T *key) const noexcept
Finds all keys in the skip list that have the given key as a prefix.
Definition SkipTrie.hpp:898
std::vector< const KEY_T * > range_search(const KEY_T *key1, const KEY_T *key2) const noexcept
Finds all keys within a specified lexicographical range [key1, key2).
Definition SkipTrie.hpp:960
std::unordered_map< size_t, size_t > get_lcp_group_sizes() const noexcept
Calculates the sizes of groups of consecutive nodes sharing the same LCP value at the base level.
Definition SkipTrie.hpp:989
std::shared_ptr< Node > m_head
Shared pointer to the head sentinel node of the top-most layer.
Definition SkipTrie.hpp:252
size_t height() const noexcept
Returns the current maximum height of the skip list.
Definition SkipTrie.hpp:150
size_t lcp_with_others(const KEY_T *key) const noexcept
Calculates the maximum Longest Common Prefix (LCP) between a given key and its immediate neighbors (p...
Definition SkipTrie.hpp:885
std::shared_ptr< Node > m_lower_tail
Shared pointer to the tail sentinel node of the base layer (level 0).
Definition SkipTrie.hpp:256
SkipTrie()
Constructs an empty SkipTrie.
Definition SkipTrie.hpp:571
Iterator begin() const noexcept
Returns an iterator pointing to the first key in the skip list (at the base level).
Definition SkipTrie.hpp:529
size_t size() const noexcept
Returns the number of keys currently stored in the skip list.
Definition SkipTrie.hpp:142
virtual EqualOrSuccessor find_equal_or_successor(const KEY_T *key, const bool require_level0) const noexcept
Finds the node containing the exact key or its immediate successor.
Definition SkipTrie.hpp:922
static size_t get_random_height()
Generates a random height for a new node based on a geometric distribution.
Definition SkipTrie.hpp:577
bool insert(const KEY_T *key, size_t height) noexcept
Inserts a new key into the skip list with a specified height.
Definition SkipTrie.hpp:740
Direction iter_layer(const KEY_T *key, Node *&curr, Direction direction, size_t &lcp) const noexcept
Iterates horizontally along a single layer to find the node preceding the insertion/search point for ...
Definition SkipTrie.hpp:646
void add_layer() noexcept
Adds a new, empty layer on top of the current skip list structure.
Definition SkipTrie.hpp:590
std::shared_ptr< Node > insert_recursive(const KEY_T *key, Node *curr, size_t height, Direction direction, size_t lcp, size_t current_height) noexcept
Recursively finds the correct position and inserts links for a new node at a specific level and below...
Definition SkipTrie.hpp:765
bool remove(const KEY_T *key) noexcept
Removes a key from the skip list.
Definition SkipTrie.hpp:839
virtual ResultLCP compare(const KEY_T *key1, const KEY_T *key2, size_t lcp) const noexcept
Compares two keys starting from a known common prefix length.
Definition SkipTrie.hpp:638
T * alloc_to_device(size_t size)
Allocates memory on the CUDA device.
Helper struct to return a Node pointer, a flag indicating equality, and an LCP value.
Definition SkipTrie.hpp:337
Node * node
Pointer to the node found (either exact match or the successor).
Definition SkipTrie.hpp:339
bool is_equal
True if node points to an exact match for the search key, false if it points to the successor.
Definition SkipTrie.hpp:341
size_t lcp
The LCP value computed during the search leading to this node.
Definition SkipTrie.hpp:343
Helper struct to return a Node pointer and an associated LCP value.
Definition SkipTrie.hpp:313
Node * node
Pointer to the found node (or null if not found).
Definition SkipTrie.hpp:315
size_t lcp
The LCP value computed during the search leading to this node.
Definition SkipTrie.hpp:317
Represents a single node within the SkipTrie structure.
Definition SkipTrie.hpp:224
const KEY_T * key
Pointer to the key associated with this node. The SkipTrie does not own the key data.
Definition SkipTrie.hpp:226
std::shared_ptr< Node > next
Shared pointer to the next node on the same level.
Definition SkipTrie.hpp:228
size_t lcp(Direction direction) const noexcept
Calculates the longest common prefix (LCP) with the neighbor node in the specified direction.
Definition SkipTrie.hpp:545
Node * next_node(Direction direction) const noexcept
Retrieves the neighboring node in the specified direction on the same level.
Definition SkipTrie.hpp:558
std::shared_ptr< Node > down
Shared pointer to the corresponding node on the level below. Null for base level nodes.
Definition SkipTrie.hpp:232
Node * prev
Raw pointer to the previous node on the same level.
Definition SkipTrie.hpp:230
Helper struct to return a comparison result (ordering) and an LCP value.
Definition SkipTrie.hpp:365
std::strong_ordering result
The result of the comparison (std::strong_ordering::less, equal, or greater).
Definition SkipTrie.hpp:367
size_t lcp
The Longest Common Prefix length calculated during the comparison.
Definition SkipTrie.hpp:369