17#include <unordered_map>
52template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS = sizeof(CHAR_T) * 8>
128 std::ostream&
print(std::ostream&
os)
const noexcept;
228 std::shared_ptr<Node> next{
nullptr};
232 std::shared_ptr<Node> down{
nullptr};
489template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
496template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
504template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
510template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
516template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
522template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
525 return !(*
this ==
other);
528template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
535template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
544template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
554 return prev ? prev->lcp_next : 0;
557template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
570template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
576template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
581 static std::random_device
rd;
582 static std::mt19937
gen(
rd());
584 static std::geometric_distribution<size_t>
dist(0.5);
589template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
593 std::shared_ptr<Node>
new_head = std::make_shared<Node>();
594 std::shared_ptr<Node>
new_tail = std::make_shared<Node>();
616 m_lower_head = m_head;
621template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
625 m_head = m_head->down;
632 m_lower_head =
nullptr;
633 m_lower_tail =
nullptr;
637template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
645template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
672 auto [comparison,
next_lcp] = compare(key, next->
key, lcp);
680 if (comparison == std::strong_ordering::equal)
685 else if (comparison == std::strong_ordering::less)
715template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
719 auto [node, is_equal, lcp] = find_equal_or_successor(key,
require_level0);
722 return { is_equal ? node :
nullptr, lcp };
725template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
729 return find_first(key).node !=
nullptr;
732template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
736 return insert(key, get_random_height());
739template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
744 while (height + 1 >= m_height)
764template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
777 std::shared_ptr<Node>
child =
nullptr;
800 std::shared_ptr<Node>
new_node = std::make_shared<Node>();
816 curr->lcp_next = lcp;
838template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
854 prev->lcp_next = std::min(prev->lcp_next,
curr->lcp_next);
862 while (!m_head->down || !m_head->down->next->key)
877template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
881 return find_first(key).lcp;
884template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
887 auto [node, lcp] = find_first(key,
true);
897template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
900 std::vector<const KEY_T*>
keys;
902 auto [
curr,
_, lcp] = find_equal_or_successor(key,
true).node;
904 if (lcp != key->size())
912 while (
curr->next->key &&
curr->lcp_next >= lcp)
921template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
927 return {
nullptr,
false, 0 };
931 Node* prev =
nullptr;
948 return {
curr,
true, lcp };
959template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
962 std::vector<const KEY_T*>
keys;
972 auto [end, is_equal] = find_equal_or_successor(
key2,
true);
976 end = end->next.get();
988template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
1002 size_t lcp =
curr->lcp_next;
1031template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
1034 return ssl.print(
os);
1037template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
1042 size_t height = m_height - 1;
1045 os <<
"Height: " << height <<
"|\t";
1047 auto curr = left->next;
1052 os << *
curr->key <<
" -" <<
curr->lcp_next <<
"-> ";
1064template<
typename CHAR_T,
unsigned CHAR_SIZE_BITS>
1068 std::ostringstream
oss;
Defines the BitString class for compact character string storage and comparison.
Direction
Enumerates the possible traversal directions within the skip list structure.
@ 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.
Provides forward/backward iteration over the keys in the base level of the SkipTrie.
Node * m_node
Pointer to the current node the iterator is positioned at.
bool operator!=(const Iterator &other) const noexcept
Compares this iterator with another for inequality.
bool operator==(const Iterator &other) const noexcept
Compares this iterator with another for equality.
const KEY_T * operator->() const noexcept
Dereferences the iterator to access the key pointer of the current node.
Iterator & operator++() noexcept
Pre-increments the iterator to the next node in its designated direction.
Direction m_direction
The direction the iterator moves upon incrementing.
const KEY_T & operator*() const noexcept
Dereferences the iterator to access the key of the current node.
Implements a skip list data structure optimized for storing and retrieving BitString keys.
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.
std::shared_ptr< Node > m_lower_head
Shared pointer to the head sentinel node of the base layer (level 0).
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...
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).
size_t m_size
Stores the total number of keys (nodes) in the base level of the skip list.
Iterator end() const noexcept
Returns an iterator pointing past the last key in the skip list (the tail sentinel at the base level)...
size_t m_height
Stores the current maximum height (number of levels) of the skip list.
bool insert(const KEY_T *key) noexcept
Inserts a new key into the skip list with a randomly generated height.
bool contains(const KEY_T *key) const noexcept
Checks if a specific key exists within the skip list.
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.
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).
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.
std::shared_ptr< Node > m_head
Shared pointer to the head sentinel node of the top-most layer.
size_t height() const noexcept
Returns the current maximum height of the skip list.
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...
std::shared_ptr< Node > m_lower_tail
Shared pointer to the tail sentinel node of the base layer (level 0).
SkipTrie()
Constructs an empty SkipTrie.
Iterator begin() const noexcept
Returns an iterator pointing to the first key in the skip list (at the base level).
size_t size() const noexcept
Returns the number of keys currently stored in the skip list.
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.
static size_t get_random_height()
Generates a random height for a new node based on a geometric distribution.
bool insert(const KEY_T *key, size_t height) noexcept
Inserts a new key into the skip list with a specified height.
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 ...
void add_layer() noexcept
Adds a new, empty layer on top of the current skip list structure.
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...
bool remove(const KEY_T *key) noexcept
Removes a key from the skip list.
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.
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.
Node * node
Pointer to the node found (either exact match or the successor).
bool is_equal
True if node points to an exact match for the search key, false if it points to the successor.
size_t lcp
The LCP value computed during the search leading to this node.
Helper struct to return a Node pointer and an associated LCP value.
Node * node
Pointer to the found node (or null if not found).
size_t lcp
The LCP value computed during the search leading to this node.
Represents a single node within the SkipTrie structure.
const KEY_T * key
Pointer to the key associated with this node. The SkipTrie does not own the key data.
std::shared_ptr< Node > next
Shared pointer to the next node on the same level.
size_t lcp(Direction direction) const noexcept
Calculates the longest common prefix (LCP) with the neighbor node in the specified direction.
Node * next_node(Direction direction) const noexcept
Retrieves the neighboring node in the specified direction on the same level.
std::shared_ptr< Node > down
Shared pointer to the corresponding node on the level below. Null for base level nodes.
Node * prev
Raw pointer to the previous node on the same level.
Helper struct to return a comparison result (ordering) and an LCP value.
std::strong_ordering result
The result of the comparison (std::strong_ordering::less, equal, or greater).
size_t lcp
The Longest Common Prefix length calculated during the comparison.