dsa_rust/sequences/
indexed_skip_list.rs

1/*! A safe, indexed skip list
2
3# About
4Skip lists are sorted, probabalistic structures made up of logically stacked lists of varying length to allow for truncated _O(log(n))_ navigation. Canonically linked lists are built from doubly-linked lists, but this is not a defining characteristic of the ADT. Regardless of the base list representation used, the navigational algorithm results in what is essentially a logical linked list.
5
6Properly implemented skip lists provide _O(log(n))_ expected time complexity for search, insert, and removal operations. This provides a significant advantage over keeping sorted array- or link-based list invariants, which have _worst-case O(n)_ removal (average _O(n/2)_) temporal performance. Skip lists are also simpler than self-balancing tree structures, which are commonly used for sorted list and map structures. Skip lists also generally provide easier and finer-grained control when adapted for concurrent operations. There is a reason Java's `concurrentSkipListMap` is so popular.
7
8# Design
9This design uses `Vec`-backed storage for [SkipNode]s that contain a list (tower) of "next" values, and a single "previous" value that represent indexes within the backing vector.
10
11The list features a dynamic max height _h_ that is logarithmically proportional to the number of elements in the list _n_ such that _h = log(n) in the expected case_. The logarithmic growth ensures that the average search, insertion, and deletion operations remain efficient, typically with expected _O(log(n))_ time complexity.
12
13William Pugh's <a href="https://15721.courses.cs.cmu.edu/spring2018/papers/08-oltpindexes1/pugh-skiplists-cacm1990.pdf" target="_blank" rel="noopener noreferrer">original paper</a> from 1990 conveniently spells out random level, search, insert, and remove operations as pseudocode that is used to guide this module's design. Note that the pseudocode is modified from the original paper to fit the notation convention present in this module (that is, CLRS-style with ASCII characters), but is otherwise unchanged from the original paper.
14
15## The Search Algorithm
16The search algorithm as its presented in the original paper generalizes some _public-facing operation_ for list search which returns either the node representing the `value` associated with a `search_key` or a failure/nil/None value to indicate that the `search_key` is not in the list.
17```text
180    Search(list, search_key)
191      x = list.header
202      // loop invariant: x.key < search_key
213      for i = list.level downto 1 do
224        while x.forward[i].key < search_key do
235          x = x.forward[i]
246      // x.key < search_key <= x.forward[1].key
257      x = x.forward[1]
268      if x.key == search_key then return x.value
279        else return failure
28```
29It makes sense to split this algorithm into two different pieces, roughly separated at line 5. In this implementation, the first part represents a private search operation `skip_search(e)` that returns a position that is strictly < `search_key`. This sub-routine is then re-used by the `get(e)`, `contains(e)`, `insert(e)`, and `remove(e)` operations. If the list is empty, `skip_search(e)` returns `0`. An empty list contains a single sentinel node, so there is _always_ a previous node to insert a value, even if its the sentinel.
30
31The second part of the algorithm simply represents a forward iteration and an equality check with the supplied `search_key`. This second phase is represented in a public `get(e)` operation that returns the value associated with the `search_key`, if it exists in the list. The equality check is crucial to determine whether the "next" node is actually the one being searched for.
32
33## Insertion & Removal Algorithms
34The insertion and deletion algorithms re-use much of the search algorithm's first phase, so they can be abstracted into [SkipList::skip_search] operations which return the node that is strictly smaller than the "search_key", which in this case is a new entry. The rest of the algorithm creates a new `SkipNode`, generates the "tower" len with a random number generator, populates the next node array for each level, and sets a singular previous node position.
35The
36```text
37 0    Insert(list, search_key, newValue)
38 1      local update[1..MaxLevel]
39 2      x = list.header
40 3      for i = list.level downto 1 do
41 4        while x.forward[i].key < search_key do
42 5          x = x.forward[i]
43 6        // x.key < search_key <= x.forward[i].key
44 7        update[i] = x
45
46 8      x = x.forward[1]
47 9      if x.key = search_key then x.value = newValue
4810      else
4911        lvl = randomLevel()
5012        if lvl > list.level then
5113          for i = list.level + 1 to lvl do
5214            update[i] = list.header
5315          list.level = lvl
5416        x = makeNode(lvl, search_key, value)
5517        for i = 1 to level do
5618          x.forward[i] = update[i].forward[i]
5719          update[i].forward[i] = x
58```
59
60This structure uses a contiguous backing structure instead of stable pointers/Position objects. As a result the list cannot strictly maintain the original design's asymptotics. The major advantage of linked lists is _O(1)_ node insertion/removal if a handle exists to the node. Contiguous lists generally require either _O(n)_ moves for insertion/removal of arbitrary elements. However, there are two options to deal with this; either use a [Vec::swap_remove] operation for _O(1)_ removals without wasting space, or using a free list to identify and fill holes after removal. For simplicity, this structure uses the first approach, meaning that indexes are _not_ stable, and as such are not surfaced in the public API. This design keeps the space requirements in check, but changes the canonical _O(log(n))_ removal time to _O(n * height)_, which is _O(n * log(n)) expected_, and _O(n^2)_ worst case (even though the list's height is technically capped).
61
62Pugh's original removal algorithm (which is altered slightly in this implementation):
63```text
64 0    Delete(list, search_key)
65 1      local update[1..MaxLevel]
66 2      x = list.header
67 3      for i = list.level downto 1 do
68 4        while x.forward[i].key < search_key do
69 5          x = x.forward[i]
70 6        update[i] = x
71 7      x = x.forward[1]
72 8      if x.key = search_key then
73 9        for i = 1 to list.level do
7410          if update[i].forward[i] != x then break
7511          update[i].forward[i] = x.forward[i]
7612        free(x)
7713        while list.level > 1 and
7814          list.header.forward[list.level] == NIL do
7915          list.level = list.level – 1
80```
81The `remove(e)` as it exists in this module:
82```text
83 0    Delete(list, searchKey)
84
85 1      local update[0..MaxLevel] = FindPredecessors(list, searchKey)
86 2      target = update[0].forward[0]
87
88 3      // Early return for elements not in the list
89 4      if target = NIL or target.key != searchKey then
90 5        return failure
91 6      last = list.nodes.last
92
93 7      // unlink from skip structure
94 8      for i = 0 to list.level - 1 do
95 9        if update[i].forward[i] = target then
9610          update[i].forward[i] = target.forward[i]
97
9811      if target.forward[0] != NIL then
9912        target.forward[0].prev = target.prev
100
10113      removed = swap_remove(list.nodes, target)
102
10314      // fix relocated node (if any)
10415      if target < list.nodes.length then
105
10616      for each node in list.nodes do
10717        replace all forward pointers = last with target
108
10918      if node at target has forward[0] != NIL then
11019        forward[0].prev = target
111
11220      if node at target.prev = last then
11321        node.prev = target
114
11522      while list.level > 1 and list.header.forward[list.level - 1] = NIL do
11623        list.level -= 1
117
11824      return removed.value
119```
120
121## Visual Examples
122An initial, empty skip list with one level and no data:
123```text
124S0: HEAD -> None
125```
126
127Inserting the first node triggers an automatic tower level, even if it ends up empty. This provides the algorithm with a starting point:
128```text
129S1: HEAD ----------> None
130S0: HEAD -> [ 5 ] -> None
131```
132
133After inserting `['a', 'c', 'e', 'd', 'b', 'i', 'g', 'h', 'f']`, the list's `SkipNodes` might contain the following towers.
134```text
135HEAD[0]: [1, 2, 9, 7]
136a[1]: [5]
137c[2]: [4, 4]
138e[3]: [9]
139d[4]: [3, 9]
140b[5]: [2]
141i[6]: []
142g[7]: [8, 6, 6]
143h[8]: [6]
144f[9]: [7, 7, 7]
145```
146Note that its always possible to tell the last item in the list because its tower is empty. This makes sense, because the last element within the sorted arrangement can only point to `None`. As you can see by the index notation on the left-hand side of the table, the backing structure retains the insertion order; the backing structure remains unsorted.
147
148The structure simply appends elements to the backing structure, so when printed the list retains its insertion order, not its sorted arrangement. As a result, the towers appear to contain rather nonsensical values. However, if you follow the indexes from the `HEAD` node, and re-arrange the nodes into _lexicographically sorted order_, which is what the navigational algorithms in the skiplist achieve, you get the following towers.
149```text
150HEAD[0]: [1, 2, 9, 7]
151a[1]: [5]
152b[5]: [2]
153c[2]: [4, 4]
154d[4]: [3, 9]
155e[3]: [9]
156f[9]: [7, 7, 7]
157g[7]: [8, 6, 6]
158h[8]: [6]
159i[6]: []
160```
161
162When you rotate the mapping 90 degrees you can start to visualize the skip list layers as logically linked lists formed by "next" element indexes.
163```text
164L3: [ g[7] ] -> None
165L2: [ f[9] ] -> [ g[7] ] -> [ i[6] ] -> None
166L1: [ c[2] ] -> [ d[4] ] -> [ f[9] ] -> [ g[7] ] -> [ i[6] ] -> None
167L0: [ a[1] ] -> [ b[5] ] -> [ c[2] ] -> [ d[4] ] -> [ e[3] ] -> [ f[9] ] -> [ g[7] ] -> [ h[8] ] -> [ i[6] ] -> None
168```
169Finally, if you extend each "next" index reference to align with its sorted position within the list, a classical skip list diagram of towers emerges.
170```text
171L3: HEAD -------------------------------------------------------------------------> [ g[7] ] -------------------------> None
172L2: HEAD -------------------------------------------------------------> [ f[9] ] -> [ g[7] ] -------------> [ i[6] ] -> None
173L1: HEAD -------------------------> [ c[2] ] -> [ d[4] ] -------------> [ f[9] ] -> [ g[7] ] -------------> [ i[6] ] -> None
174L0: HEAD -> [ a[1] ] -> [ b[5] ] -> [ c[2] ] -> [ d[4] ] -> [ e[3] ] -> [ f[9] ] -> [ g[7] ] -> [ h[8] ] -> [ i[6] ] -> None
175```
176
177# Example code
178```rust
179    let mut list = dsa_rust::sequences::indexed_skip_list::SkipList::<char>::new();
180
181    // An unsorted list of values and a sorted version to compare against
182    let values = ['a', 'c', 'e', 'd', 'b', 'i', 'g', 'h', 'f'];
183    let sorted = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
184
185    // Inserts unsorted values into the skip list with a consuming iterator
186    for e in values.into_iter() {
187        list.insert(e)
188    }
189
190    // Illustrates that the list exists as a sorted invariant
191    for (i, e) in list.iter().enumerate() {
192        assert_eq!(e, &sorted[i]);
193    }
194
195    // Illustrates the Kth function in a 0-indexed list.
196    // That is, e occupies the 2 index for insertion order,
197    // but is the 4th element in the 0-indexed sorted arrangement.
198    assert_eq!(list.get_kth(4).unwrap(), &'e');
199
200    // Query by range using Rust's RangeBounds semantics
201    let val = ['c', 'd', 'e', 'f'];
202    for (i, e) in list.range('c'..='f').enumerate() {
203        assert_eq!(e, &val[i])
204    }
205
206```
207*/
208
209use rand::Rng; // For coin flips
210use std::borrow::Borrow; // For passing borrowed parameters
211use std::ops::{Bound, RangeBounds}; // For range iterators
212
213const MAX_HEIGHT: usize = 32;
214//const MAX_HEIGHT: usize = 10;
215
216#[derive(Clone, Debug)]
217struct SkipNode<T> {
218    value: Option<T>,                  // None for sentinel
219    next: [Option<usize>; MAX_HEIGHT], // forward links
220    prev: Option<usize>,               // back links at s0 for reverse iteration
221                                       //height: usize                      // stores the node's "tower" height
222}
223
224pub struct SkipList<T> {
225    nodes: Vec<SkipNode<T>>,
226    height: usize,
227}
228impl<T: Ord> Default for SkipList<T> {
229    fn default() -> Self {
230        Self::new()
231    }
232}
233impl<T: Ord> SkipList<T> {
234    /// Creates a new, empty SkipList.
235    pub fn new() -> Self {
236        let sentinel = SkipNode {
237            value: None,
238            next: [None; MAX_HEIGHT],
239            prev: None,
240        };
241
242        Self {
243            nodes: vec![sentinel],
244            height: 1,
245        }
246    }
247
248    /// Returns the number of elements in the list.
249    pub fn len(&self) -> usize {
250        // Even empty lists have a single HEAD node,
251        // which does not count
252        self.nodes.len() - 1
253    }
254
255    /// Wrapper for `len()` that returns a Boolean
256    /// indicating whether the list is empty.
257    pub fn is_empty(&self) -> bool {
258        self.nodes.len() - 1 == 0
259    }
260
261    /// Returns the a reference to the entry associated with the search key
262    /// if it exists in the list, otherwise returns `None` to indicate
263    /// that the key is not in the list.
264    ///
265    /// Represents Pugh's canonical `Search` operation as described in the
266    /// <a href="https://15721.courses.cs.cmu.edu/spring2018/papers/08-oltpindexes1/pugh-skiplists-cacm1990.pdf" target="_blank" rel="noopener noreferrer">original paper</a>.
267    pub fn get<Q>(&self, key: &Q) -> Option<&T>
268    where
269        Q: Ord + ?Sized,
270        T: Borrow<Q>,
271    {
272        //let idx = self.skip_search(key);
273        let idx = self.find_predecessors(key)[0];
274        let next = self.nodes[idx].next[0]?;
275        let val = self.nodes[next].value.as_ref()?;
276
277        (val.borrow() == key).then_some(val)
278    }
279
280    /// Returns a Boolean indicating whether the supplied search key
281    /// exists in the list.
282    ///
283    /// Wrapper for the public `get()` operation, which itself wraps
284    /// the private `skip_search()` operation.
285    pub fn contains<Q>(&self, key: &Q) -> bool
286    where
287        Q: Ord + ?Sized,
288        T: Borrow<Q>,
289    {
290        self.get(key).is_some()
291    }
292
293    /// Inserts a new entry into the skip list.
294    ///
295    /// Allows duplicates, where ordering is determined by insertion order
296    /// such that the most recent duplicates come before older entries.
297    pub fn insert(&mut self, entry: T) {
298        // Insert(list, search_key, newValue)
299        //   local update[1..MaxLevel]
300        //   x = list.header
301        //   for i = list.level downto 1 do
302        //     while x.forward[i].key < search_key do
303        //       x = x.forward[i]
304        //     // x.key < search_key <= x.forward[i].key
305        //     update[i] = x
306
307        // Chooses a random tower height and resets the list height
308        // if it is taller than the current list height
309        let height = self.random_height();
310        if height > self.height {
311            self.height = height;
312        }
313
314        // find_predecessors returns an array of predecessor positions
315        // at each level for the splice point where update[0] is the
316        // entry in the base list strictly < entry
317        let update = self.find_predecessors(&entry);
318        let prev_idx = update[0];
319        let new_index = self.nodes.len(); // Backing list insertion index
320        let next_idx = self.nodes[prev_idx].next[0];
321
322        self.nodes.push(SkipNode {
323            value: Some(entry),
324            next: [None; MAX_HEIGHT],
325            prev: Some(prev_idx),
326        });
327
328        // Reset the previous and current entry's next and previous
329        // positions, respectively
330        // take() only yields the number of elements in update up to
331        // the list's height providing a minimal number of loop iterations
332        for (level, _) in update.iter().enumerate().take(height) {
333            let prev_idx = update[level];
334            self.nodes[new_index].next[level] = self.nodes[prev_idx].next[level];
335            self.nodes[prev_idx].next[level] = Some(new_index);
336        }
337
338        // If there is a "next" node it must now point back to the new node
339        if let Some(next_idx) = next_idx {
340            self.nodes[next_idx].prev = Some(new_index);
341        }
342    }
343
344    /// Removes and returns the value for a given key, if it exists in
345    /// the list. Returns None if the key does not exist in the list.
346    ///
347    /// This function does not technically adhere to Pugh's original
348    /// removal algorithm. It uses [Vec::swap_remove] for simplified
349    /// backing list compaction with the side effect of re-ordering remaining
350    /// elements. The resultant removal time is therefore _O(n * height)_,
351    /// which is _O(n * log(n)) expected_, and _O(n^2)_ worst case.
352    pub fn remove<Q>(&mut self, key: &Q) -> Option<T>
353    where
354        Q: Ord + ?Sized,
355        T: Borrow<Q>,
356    {
357        //  Delete(list, search_key)
358        //    local update[1..MaxLevel]
359        //    x = list.header
360        //    for i = list.level downto 1 do
361        //      while x.forward[i].key < search_key do
362        //        x = x.forward[i]
363        //      update[i] = x
364        //    x = x.forward[1]
365        //    if x.key = search_key then
366        //      for i = 1 to list.level do
367        //        if update[i].forward[i] != x then break
368        //        update[i].forward[i] = x.forward[i]
369        //      free(x)
370        //      while list.level > 1 and
371        //        list.header.forward[list.level] == NIL do
372        //        list.level = list.level – 1
373
374        // Pre-fetch precessors for target removal node
375        // Technically O(n) but O(log(n)) expected
376        let mut update = self.find_predecessors(key);
377
378        // Check if the target is in the list, if it is, return its index
379        let target = match self.nodes[update[0]].next[0] {
380            Some(idx)
381                if self.nodes[idx]
382                    .value
383                    .as_ref()
384                    .is_some_and(|v| v.borrow() == key) =>
385            {
386                idx
387            }
388            _ => return None,
389        };
390
391        // Find the last node in the backing structure
392        let last_idx = self.nodes.len() - 1;
393
394        // Remove the prev and next positions from adjacent nodes
395        if let Some(next_idx) = self.nodes[target].next[0] {
396            self.nodes[next_idx].prev = self.nodes[target].prev;
397        }
398        for (level, val) in update.iter_mut().enumerate().take(self.height) {
399            if self.nodes[*val].next[level] == Some(target) {
400                self.nodes[*val].next[level] = self.nodes[target].next[level];
401            }
402        }
403
404        // Actual node removal
405        let removed_node = self.nodes.swap_remove(target);
406
407        // The hot loop:
408        // Set next/prev positions for the node that just got swapped
409        // into the hole left by the removal
410        //
411        // Looks quadratic with nested for loops, but realistically
412        // only requires O(n * height) worst case, where height is
413        // expected to be log(n), so realistically this is O(n * log(n)),
414        // and is expected to perform more like O(n) for sparse towers.
415        if target < self.nodes.len() {
416            // Fix next positions
417            for node in &mut self.nodes {
418                for next in node.next.iter_mut().take(self.height) {
419                    if *next == Some(last_idx) {
420                        *next = Some(target);
421                    }
422                }
423            }
424
425            // Repair adjacent backward links after relocation
426            if let Some(next_idx) = self.nodes[target].next[0] {
427                self.nodes[next_idx].prev = Some(target);
428            }
429
430            // Repair relocated predecessor reference
431            if self.nodes[target].prev == Some(last_idx) {
432                self.nodes[target].prev = Some(target);
433            }
434        }
435
436        // Reduce the list's height, in case the removed tower was tallest
437        while self.height > 1 && self.nodes[0].next[self.height - 1].is_none() {
438            self.height -= 1;
439        }
440
441        // Return just the entry, not the entire node
442        removed_node.value
443    }
444
445    /// Returns the Kth value in the list, if it exists.
446    pub fn get_kth(&self, k: usize) -> Option<&T> {
447        let mut idx = self.nodes[0].next[0];
448        let mut i = 0;
449        while let Some(current) = idx {
450            if i == k {
451                return self.nodes[current].value.as_ref();
452            }
453            idx = self.nodes[current].next[0];
454            i += 1;
455        }
456        None
457    }
458
459    /// Returns an inclusive iterator over a range of values
460    /// in the list from `start` to `end`.
461    pub fn range<Q, R>(&self, range: R) -> RangeIter<'_, T, Q, R>
462    where
463        Q: Ord + ?Sized,
464        T: Borrow<Q>,
465        R: RangeBounds<Q>,
466    {
467        // FIND FRONT
468        let front = match range.start_bound() {
469            Bound::Included(start) => self.nodes[self.find_predecessors(start)[0]].next[0],
470            Bound::Excluded(start) => {
471                let idx = self.nodes[self.find_predecessors(start)[0]].next[0];
472                if let Some(i) = idx {
473                    if self.nodes[i].value.as_ref().unwrap().borrow() == start {
474                        self.nodes[i].next[0]
475                    } else {
476                        Some(i)
477                    }
478                } else {
479                    None
480                }
481            }
482            Bound::Unbounded => self.nodes[0].next[0],
483        };
484
485        // FIND BACK
486        let back = match range.end_bound() {
487            Bound::Included(end) => {
488                // Find predecessors of 'end'.
489                // If the element at the end of the search IS 'end', that's our back.
490                // If not, the predecessor itself is our back.
491                let update = self.find_predecessors(end);
492                let candidate = self.nodes[update[0]].next[0];
493                if let Some(idx) = candidate {
494                    if self.nodes[idx].value.as_ref().unwrap().borrow() == end {
495                        Some(idx)
496                    } else {
497                        // Predicate check: Ensure we aren't returning the sentinel (idx 0)
498                        if update[0] == 0 {
499                            None
500                        } else {
501                            Some(update[0])
502                        }
503                    }
504                } else if update[0] == 0 {
505                    None
506                } else {
507                    Some(update[0])
508                }
509            }
510            Bound::Excluded(end) => {
511                let update = self.find_predecessors(end);
512                if update[0] == 0 {
513                    None
514                } else {
515                    Some(update[0])
516                }
517            }
518            Bound::Unbounded => {
519                // To find the absolute end, we find predecessors for a
520                // "theoretically infinite" value
521                // or simply walk the tallest tower to the end.
522                let mut curr = 0;
523                for level in (0..self.height).rev() {
524                    while let Some(next_idx) = self.nodes[curr].next[level] {
525                        curr = next_idx;
526                    }
527                }
528                if curr == 0 {
529                    None
530                } else {
531                    Some(curr)
532                }
533            }
534        };
535
536        RangeIter {
537            list: self,
538            front,
539            back,
540            range,
541            _marker: std::marker::PhantomData,
542        }
543    }
544
545    /// Returns an iterator over borrowed values in the list.
546    pub fn iter(&self) -> Iter<'_, T> {
547        // Walk the express lanes to find the very last node in O(log n) time
548        let mut tail = 0;
549        for level in (0..self.height).rev() {
550            while let Some(next_idx) = self.nodes[tail].next[level] {
551                tail = next_idx;
552            }
553        }
554
555        Iter {
556            list: self,
557            next: self.nodes[0].next[0], // First node after sentinel
558            prev: if tail == 0 { None } else { Some(tail) },
559        }
560    }
561
562    // Utility functions
563    ////////////////////
564
565    // Uses the external crate rand to determine the height h
566    // of a given tower which is always 1 <= h < MAX_HEIGHT
567    // by performing a series of "coin flips".
568    fn random_height(&self) -> usize {
569        let mut level = 1;
570        let mut rng = rand::rng();
571        while level < MAX_HEIGHT && rng.random::<bool>() {
572            level += 1;
573        }
574        level
575    }
576
577    // Represents the heart of the skip list. This function is used
578    // by the `insert()`, `remove()`, `range()`, `locate()` (and by
579    // proxy `contains()`) functions.
580    //
581    // Returns an array of integers representing entries for
582    // each level in the list that are strictly less than the search
583    // key at each level, where the 0th index represents the base list.
584    // The operation appears to be _O(n^2)_, but due to the list's
585    // structure only requires _O(n)_ worst case, with _O(log(n))_ expected time.
586    //
587    // Performs dual duty in regards to Pugh's original design.
588    // Useful as a basic skip_search by capturing the base list position
589    // of the entry strictly < search_key as the 0th array index in the
590    // return, as well as providing a list of previous positions at the
591    // splice/split point for insert and delete operations.
592    fn find_predecessors<Q>(&self, key: &Q) -> [usize; MAX_HEIGHT]
593    where
594        Q: Ord + ?Sized,
595        T: Borrow<Q>,
596    {
597        let mut update = [0usize; MAX_HEIGHT];
598        let mut idx = 0;
599
600        for level in (0..self.height).rev() {
601            loop {
602                match self.nodes[idx].next[level] {
603                    None => break,
604                    Some(next_idx) => {
605                        let next_val = self.nodes[next_idx].value.as_ref().unwrap();
606                        if next_val.borrow() >= key {
607                            break;
608                        }
609                        idx = next_idx;
610                    }
611                }
612            }
613            // Record the node index in the update list before descending
614            update[level] = idx;
615        }
616        update
617    }
618
619    /// Represents the heart of the skip list. This function is used
620    /// by the `get()`, `insert()`, `remove()`, `range()`, and
621    /// `contains()`) functions.
622    ///
623    /// Returns the index of the largest entry that is
624    /// strictly less than the provided key.
625    ///
626    /// NOTE: UNUSED
627    fn _skip_search<Q>(&self, key: &Q) -> usize
628    where
629        Q: Ord + ?Sized,
630        T: Borrow<Q>,
631    {
632        // Empty lists have a single sentinel node
633        if self.nodes.len() == 1 {
634            return 0;
635        };
636
637        // p = s: Always start at the HEAD node (index 0)
638        let mut pos = 0usize;
639
640        // Iterate through levels from top to bottom (the vertical "below(p)" steps)
641        for level in (0..self.height).rev() {
642            // "Scan forward" horizontally across the current level
643            while self.nodes[pos].next[level].is_some() {
644                // Peek at the NEXT node index on the current logical linked list
645                match self.nodes[pos].next[level] {
646                    // Get the next node's value safely.
647                    // If the search key is >= the forward node's value,
648                    // advance the pos to that node. If the next node is
649                    // either > key or None, break the loop, which
650                    // moves to the next level.
651                    Some(next_idx) => {
652                        let next_val = self.nodes[next_idx].value.as_ref().unwrap().borrow();
653                        if next_val < key {
654                            pos = next_idx;
655                        } else {
656                            break; // Break and descend a level
657                        }
658                    }
659                    None => {
660                        break; // Break and descend a level
661                    }
662                }
663            }
664        }
665
666        // If the position never advances beyond the sentinel,
667        // the key is not either doesn't exist in the list,
668        // or it belongs as the first element
669        pos
670    }
671
672    /// Returns the traversal path of a search key by
673    /// re-using the skip_search logic. The only difference is that
674    /// this traversal records each index and returns the path.
675    fn _traversal<Q>(&self, key: &Q) -> Vec<T>
676    where
677        Q: Ord + ?Sized,
678        T: Borrow<Q> + Clone,
679    {
680        let mut vec = Vec::new();
681
682        // p = s: Always start at the HEAD node (index 0)
683        let mut pos = 0usize;
684
685        // Iterate through levels from top to bottom (the vertical "below(p)" steps)
686        for level in (0..self.height).rev() {
687            // "Scan forward" horizontally across the current level
688            while self.nodes[pos].next[level].is_some() {
689                // Peek at the NEXT node index on the current logical linked list
690                match self.nodes[pos].next[level] {
691                    // Get the next node's value safely.
692                    // If the search key is >= the forward node's value,
693                    // advance the pos to that node. If the next node is
694                    // either > key or None, break the loop, which
695                    // moves to the next level.
696                    Some(next_idx) => {
697                        let next_val = self.nodes[next_idx].value.as_ref().unwrap().borrow();
698                        if next_val < key {
699                            vec.push(self.nodes[next_idx].value.clone().unwrap());
700                            pos = next_idx;
701                        } else {
702                            break; // Break and descend a level
703                        }
704                    }
705                    None => {
706                        break; // Break and descend a level
707                    }
708                }
709            }
710        }
711        vec
712    }
713}
714
715pub struct Iter<'a, T> {
716    list: &'a SkipList<T>,
717    next: Option<usize>,
718    prev: Option<usize>,
719}
720impl<'a, T> Iterator for Iter<'a, T> {
721    type Item = &'a T;
722
723    fn next(&mut self) -> Option<Self::Item> {
724        let idx = self.next?;
725        let value = self.list.nodes[idx].value.as_ref()?;
726
727        if self.next == self.prev {
728            self.next = None;
729            self.prev = None;
730        } else {
731            self.next = self.list.nodes[idx].next[0];
732        }
733        Some(value)
734    }
735}
736impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
737    fn next_back(&mut self) -> Option<Self::Item> {
738        let idx = self.prev?;
739        let value = self.list.nodes[idx].value.as_ref()?;
740
741        if self.prev == self.next {
742            self.next = None;
743            self.prev = None;
744        } else {
745            let prev = self.list.nodes[idx].prev;
746            // Sentinel check: don't yield index 0
747            self.prev = if prev == Some(0) { None } else { prev };
748        }
749        Some(value)
750    }
751}
752
753pub struct RangeIter<'a, T, Q, R>
754where
755    Q: ?Sized,
756    R: RangeBounds<Q>,
757{
758    list: &'a SkipList<T>,
759    front: Option<usize>, // Moves forward
760    back: Option<usize>,  // Moves backward
761    range: R,
762    _marker: std::marker::PhantomData<Q>,
763}
764impl<'a, T, Q, R> Iterator for RangeIter<'a, T, Q, R>
765where
766    Q: Ord + ?Sized,
767    T: Borrow<Q>,
768    R: RangeBounds<Q>,
769{
770    type Item = &'a T;
771
772    fn next(&mut self) -> Option<Self::Item> {
773        let idx = self.front?;
774
775        // Boundary Check
776        let value = self.list.nodes[idx].value.as_ref().unwrap();
777        if !self.range.contains(value.borrow()) {
778            self.front = None;
779            return None;
780        }
781
782        // Meet/Cross Check: If front matches back, this is the last element
783        if self.front == self.back {
784            self.front = None;
785            self.back = None;
786        } else {
787            self.front = self.list.nodes[idx].next[0];
788        }
789
790        Some(value)
791    }
792}
793impl<'a, T, Q, R> DoubleEndedIterator for RangeIter<'a, T, Q, R>
794where
795    Q: Ord + ?Sized,
796    T: Borrow<Q>,
797    R: RangeBounds<Q>,
798{
799    fn next_back(&mut self) -> Option<Self::Item> {
800        let idx = self.back?;
801
802        // Boundary Check
803        let value = self.list.nodes[idx].value.as_ref().unwrap();
804        if !self.range.contains(value.borrow()) {
805            self.back = None;
806            return None;
807        }
808
809        // Meet/Cross Check
810        if self.back == self.front {
811            self.back = None;
812            self.front = None;
813        } else {
814            // Move back, but ensure we don't land on the sentinel (idx 0)
815            let prev = self.list.nodes[idx].prev;
816            self.back = if prev == Some(0) { None } else { prev };
817        }
818
819        Some(value)
820    }
821}
822
823impl<'a, T: Ord> IntoIterator for &'a SkipList<T> {
824    type Item = &'a T;
825    type IntoIter = Iter<'a, T>;
826
827    fn into_iter(self) -> Self::IntoIter {
828        self.iter()
829    }
830}
831
832#[test]
833fn one() {
834    let mut list = SkipList::<char>::new();
835
836    // Tests basic housekeeping on empty list
837    assert_eq!(list.len(), 0);
838    assert!(list.is_empty());
839    assert!(!list.contains(&'z'));
840
841    // Inserts 9 values into the skip list
842    // with a consuming iterator, moving values
843    // into the list
844    let values = ['a', 'c', 'e', 'd', 'b', 'i', 'g', 'h', 'f'];
845    println!("Insert elements in order: {:?}", &values);
846    for e in values.into_iter() {
847        list.insert(e)
848    }
849    println!(
850        "LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}",
851        list.height,
852        list.nodes.len()
853    );
854    println!("Tower contents by insertion order, NOT sorted order:");
855    for (i, e) in list.nodes.iter().enumerate() {
856        // Collect only the Some values into a new Vec
857        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
858        let values: Vec<_> = e.next.iter().collect();
859        match e.value {
860            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
861            None => println!("HEAD [{i}]: {values:?}"),
862        }
863        //if let Some(val) = e.value {
864        //    let v = &val.to_string();
865        //    println!("{v}[{i}]: {values:?}");
866        //} else {
867        //    println!("HEAD[0]: {values:?}");
868        //}
869    }
870    println!();
871
872    // Tests that len gets updated properly
873    assert_eq!(list.len(), 9);
874    assert!(list.contains(&'g'));
875
876    // Tests basic ordering and iteration
877    // Basic iteration with iter()
878    // Clippy wants enumerate instead of external loop counter
879    let sorted = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
880    for (i, e) in list.iter().enumerate() {
881        assert_eq!(e, &sorted[i]);
882    }
883    // Double-ended iteration with rev()
884    // Clippy wants saturating_sub instead of loop counter
885    let mut i = 8;
886    for e in list.iter().rev() {
887        assert_eq!(e, &sorted[i]);
888        if i > 0 {
889            i = i.saturating_sub(1)
890        };
891    }
892    // Or if you wanna be fancy about it
893    // zip() stops as soon as one iterator ends,
894    // eliminating the need for an overflow check
895    for (e, i) in list.iter().rev().zip((0..=8).rev()) {
896        assert_eq!(e, &sorted[i]);
897    }
898
899    // Iterator inferance using the IntoIter impl
900    let mut i = 0;
901    #[allow(clippy::explicit_counter_loop)]
902    for e in &list {
903        assert_eq!(e, &sorted[i]);
904        i += 1;
905    }
906
907    // Tests the Kth function in a 0-indexed list
908    assert_eq!(list.get_kth(6).unwrap(), &'g');
909
910    // Tests the range function
911    // NOTE: char is Copy so you dont strictly need to borrow
912    // when setting range bounds, these tests illustrate both
913    // borrowing and not; Note that each bounds must match
914    // so no (&'a'..'f'), only ('a'..'f') or (&'a'..&'f')
915    // Midlist (exclusive)
916    let val = ['c', 'd', 'e'];
917    //for (i, e) in list.range(&'c', &'f').enumerate() {
918    for (i, e) in list.range('c'..'f').enumerate() {
919        assert_eq!(e, &val[i])
920    }
921    // Midlist (inclusive)
922    let val = ['c', 'd', 'e', 'f'];
923    //for (i, e) in list.range(&'c', &'f').enumerate() {
924    for (i, e) in list.range('c'..='f').enumerate() {
925        assert_eq!(e, &val[i])
926    }
927    // Start of list
928    let val = ['a', 'b', 'c', 'd', 'e', 'f'];
929    //for (i, e) in list.range(&'a', &'f').enumerate() {
930    for (i, e) in list.range(..&'f').enumerate() {
931        assert_eq!(e, &val[i])
932    }
933    // End of list
934    let val = ['e', 'f', 'g', 'h', 'i'];
935    for (i, e) in list.range(&'e'..).enumerate() {
936        assert_eq!(e, &val[i])
937    }
938
939    // Tests remove(e)
940    // Removes the first element
941    println!("Remove 'a'");
942    list.remove(&'a');
943    // Removes an arbitrary element
944    println!("Remove 'e'");
945    list.remove(&'e');
946    // Removes the last element
947    println!("Remove 'i'");
948    list.remove(&'i');
949    // List shrinks as expected
950    assert_eq!(list.len(), 6);
951    // List no longer contains elements
952    assert!(!list.contains(&'e'));
953    assert!(!list.contains(&'a'));
954    // Cant remove what isn't there!
955    assert!(list.remove(&'z').is_none());
956    // Debug prints new layout
957    print!("List updated values: [");
958    for e in list.iter() {
959        print!("{e:#?} ")
960    }
961    println!("]");
962
963    // Debug prints the tower contents
964    println!(
965        "LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}",
966        list.height,
967        list.nodes.len()
968    );
969    println!("Tower contents by insertion order, NOT sorted order:");
970    for (i, e) in list.nodes.iter().enumerate() {
971        // Collect only the Some values into a new Vec
972        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
973        let values: Vec<_> = e.next.iter().collect();
974        match e.value {
975            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
976            None => println!("HEAD [{i}]: {values:?}"),
977        }
978    }
979    println!();
980
981    // Tests skip_search(e), find_predecessors(e) and their dependencies:
982    // contains(e), get(e), and find_val(e)
983    //
984    // An element in the list
985    let node = 'h';
986    assert_eq!(list._skip_search(&node), 6); // 6 is g which is < h
987    assert_eq!(list.find_predecessors(&node)[0], 6); // 6 is g which is < h
988    assert!(list.contains(&node));
989    assert_eq!(list.get(&node).unwrap(), &'h');
990    // An element at the beginning of the list
991    let node = 'b';
992    assert_eq!(list._skip_search(&node), 0);
993    assert_eq!(list.find_predecessors(&node)[0], 0);
994    assert!(list.contains(&node));
995    assert_eq!(list.get(&node).unwrap(), &'b');
996    // An element not in the list
997    // 'j' is not in the list, but skip_search returns 6 because
998    // thats the position that 'i' lives at due to insertion order,
999    // and 'i' < 'j'
1000    let node = 'j';
1001    assert_eq!(list._skip_search(&node), 3); // 3 is h, the last entry
1002    assert_eq!(list.find_predecessors(&node)[0], 3); // 3 is h, the last entry
1003    assert!(!list.contains(&node));
1004    assert!(list.get(&node).is_none());
1005    // An element that was previously in the list, but removed
1006    let node = 'a';
1007    assert_eq!(list._skip_search(&node), 0); // belongs after HEAD
1008    assert_eq!(list.find_predecessors(&node)[0], 0); // belongs after HEAD
1009    assert!(!list.contains(&node));
1010    assert!(list.get(&node).is_none());
1011
1012    // A bunch of random list mutations to ensure coherence
1013    list.insert('p');
1014    list.insert('u');
1015    list.insert('w');
1016    list.remove(&'p');
1017    list.insert('l');
1018    list.insert('m');
1019    list.remove(&'f');
1020    list.remove(&'o');
1021    list.insert('q');
1022    list.remove(&'m');
1023    list.insert('x');
1024    list.insert('z');
1025
1026    // Visual component:
1027    // Combines contains(e) and prints traversal() as proof
1028    let trav = list._traversal(&'g');
1029    let con = list.contains(&'g');
1030    println!("Contains 'g': {con:?}");
1031    println!("Traversal: {trav:?}");
1032    let trav = list._traversal(&'j');
1033    let con = list.contains(&'j');
1034    println!("Contains 'j': {con:?}");
1035    println!("Traversal: {trav:?}");
1036    let trav = list._traversal(&'a');
1037    let con = list.contains(&'a');
1038    println!("Contains 'a': {con:?}");
1039    println!("Traversal: {trav:?}");
1040
1041    // Tests traversal ordering
1042    let l2 = ['b', 'c', 'd', 'g', 'h', 'l', 'q', 'u', 'w', 'x', 'z'];
1043    for (val, i) in list.iter().zip(0..=5) {
1044        assert_eq!(val, &l2[i]);
1045    }
1046    // Visual confirmation of correct traversal
1047    print!("List values:\n   ");
1048    for e in list.iter() {
1049        print!("{e:#?} ")
1050    }
1051    println!();
1052
1053    //panic!();
1054}
1055
1056#[test]
1057// AI-written "stress" test
1058fn test_skip_list_removal_integrity() {
1059    // Assumes your SkipList has a standard New or Default implementation
1060    let mut list = SkipList::new();
1061
1062    // 1. Insert a sequence of numbers.
1063    // This allows your natural random_height() generator to build up
1064    // a multi-level tower structure organically.
1065    let total_elements = 300;
1066    for i in 0..total_elements {
1067        list.insert(i);
1068    }
1069
1070    // 2. Remove elements from the middle of the list.
1071    // This forces swap_remove to repeatedly pull the last element of the Vec
1072    // into the newly created holes, triggering your global repair loops.
1073    for i in (50..200).step_by(2) {
1074        list.remove(&i);
1075    }
1076
1077    // 3. STRUCTURAL AUDIT
1078    // Scan every single surviving node's next pointers across every level.
1079    // If swap_remove left a stale index behind, it will point out-of-bounds.
1080    let current_len = list.nodes.len();
1081    for idx in 0..current_len {
1082        for level in 0..list.height {
1083            if let Some(next_idx) = list.nodes[idx].next[level] {
1084                // Assert 1: Out-of-bounds index protection
1085                assert!(
1086                    next_idx < current_len,
1087                    "CORRUPTION: Node at index {idx} on level {level} points to index {next_idx}, \
1088                     which is out-of-bounds for a Vec of length {current_len}!"
1089                );
1090
1091                // Assert 2: Level 0 backlink symmetric validation
1092                if level == 0 {
1093                    assert_eq!(
1094                        list.nodes[next_idx].prev,
1095                        Some(idx),
1096                        "CORRUPTION: Link symmetry broken! Node {} points forward to {}, \
1097                         but {} points backward to {:?}",
1098                        idx,
1099                        next_idx,
1100                        next_idx,
1101                        list.nodes[next_idx].prev
1102                    );
1103                }
1104            }
1105        }
1106    }
1107
1108    // 4. ALGORITHMIC AUDIT
1109    // Verify that every single element that wasn't deleted is still perfectly
1110    // searchable via skip_search. If a lane broke, skip_search will overshoot
1111    // or fail to track the element.
1112    for i in 0..total_elements {
1113        // Skip the elements we explicitly deleted
1114        if (50..200).contains(&i) && i % 2 == 0 {
1115            continue;
1116        }
1117
1118        // Your skip_search returns a valid usize position
1119        let pos = list.find_predecessors(&i)[0];
1120
1121        // Ensure the pos returned actually matches our search target
1122        // or points to its direct predecessor.
1123        if pos == 0 {
1124            // If it returned the sentinel, the first item in the list must be >= i
1125            if let Some(first_idx) = list.nodes[0].next[0] {
1126                let first_val = list.nodes[first_idx].value.as_ref().unwrap();
1127                assert!(first_val >= &i);
1128            }
1129        } else {
1130            let found_val = list.nodes[pos].value.as_ref().unwrap();
1131            assert!(
1132                found_val <= &i,
1133                "skip_search returned a node greater than the key!"
1134            );
1135        }
1136    }
1137
1138    println!(
1139        "LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}",
1140        list.height,
1141        list.nodes.len()
1142    );
1143    println!("Tower contents by insertion order, NOT sorted order:");
1144    for (i, e) in list.nodes.iter().enumerate() {
1145        // Collect only the Some values into a new Vec
1146        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
1147        let values: Vec<_> = e.next.iter().collect();
1148        match e.value {
1149            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
1150            None => println!("HEAD [{i}]: {values:?}"),
1151        }
1152    }
1153    println!();
1154    //panic!()
1155
1156    // Maximalist
1157    let array: [i32; 4] = [1i32, 2, 3, 4];
1158    let iterator: std::slice::Iter<'_, i32> = array.iter();
1159    let total: i32 = iterator.sum();
1160    assert_eq!(total, 10);
1161
1162    // Minimalist
1163    assert_eq!([1, 2, 3, 4].iter().sum::<i32>(), 10);
1164
1165    // Illustrative
1166    let array = [1, 2, 3, 4]; // Iterable object
1167    let iter = array.iter(); // Iterator
1168    let total: i32 = iter.sum(); // Iterator adapter
1169    assert_eq!(total, 10);
1170
1171    // Also illustrative
1172    let array = [1, 2, 3, 4];
1173    let mut total = 0;
1174    for e in array.iter() {
1175        total += e;
1176    }
1177    assert_eq!(total, 10);
1178
1179    // Baby bear; just right
1180    let array = [1, 2, 3, 4]; // Iterable object
1181    let total: i32 = array.iter().sum(); // Iterator and adapter
1182    assert_eq!(total, 10);
1183    assert_eq!(array, [1, 2, 3, 4]);
1184}