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
299        // Insert(list, search_key, newValue)
300        //   local update[1..MaxLevel]
301        //   x = list.header
302        //   for i = list.level downto 1 do
303        //     while x.forward[i].key < search_key do
304        //       x = x.forward[i]
305        //     // x.key < search_key <= x.forward[i].key
306        //     update[i] = x
307
308
309        // Chooses a random tower height and resets the list height
310        // if it is taller than the current list height
311        let height = self.random_height();
312        if height > self.height {
313            self.height = height;
314        }
315
316        // find_predecessors returns an array of predecessor positions
317        // at each level for the splice point where update[0] is the 
318        // entry in the base list strictly < entry
319        let update = self.find_predecessors(&entry);
320        let prev_idx = update[0];
321        let new_index = self.nodes.len(); // Backing list insertion index
322        let next_idx = self.nodes[prev_idx].next[0];
323
324        self.nodes.push(SkipNode {
325            value: Some(entry),
326            next: [None; MAX_HEIGHT],
327            prev: Some(prev_idx),
328        });
329
330        // Reset the previous and current entry's next and previous 
331        // positions, respectively
332        // take() only yields the number of elements in update up to 
333        // the list's height providing a minimal number of loop iterations
334        for (level, _) in update.iter().enumerate().take(height) {
335            let prev_idx = update[level];
336            self.nodes[new_index].next[level] = self.nodes[prev_idx].next[level];
337            self.nodes[prev_idx].next[level] = Some(new_index);
338        }
339
340        // If there is a "next" node it must now point back to the new node
341        if let Some(next_idx) = next_idx {
342            self.nodes[next_idx].prev = Some(new_index);
343        }
344    }
345
346    /// Removes and returns the value for a given key, if it exists in 
347    /// the list. Returns None if the key does not exist in the list. 
348    /// 
349    /// This function does not technically adhere to Pugh's original 
350    /// removal algorithm. It uses [Vec::swap_remove] for simplified 
351    /// backing list compaction with the side effect of re-ordering remaining 
352    /// elements. The resultant removal time is therefore _O(n * height)_, 
353    /// which is _O(n * log(n)) expected_, and _O(n^2)_ worst case.
354    pub fn remove<Q>(&mut self, key: &Q) -> Option<T>
355    where
356        Q: Ord + ?Sized,
357        T: Borrow<Q>,
358    {
359
360        //  Delete(list, search_key)
361        //    local update[1..MaxLevel]
362        //    x = list.header
363        //    for i = list.level downto 1 do
364        //      while x.forward[i].key < search_key do
365        //        x = x.forward[i]
366        //      update[i] = x
367        //    x = x.forward[1]
368        //    if x.key = search_key then
369        //      for i = 1 to list.level do
370        //        if update[i].forward[i] != x then break
371        //        update[i].forward[i] = x.forward[i]
372        //      free(x)
373        //      while list.level > 1 and
374        //        list.header.forward[list.level] == NIL do
375        //        list.level = list.level – 1
376
377        // Pre-fetch precessors for target removal node
378        // Technically O(n) but O(log(n)) expected
379        let mut update = self.find_predecessors(key);
380    
381        // Check if the target is in the list, if it is, return its index
382        let target = match self.nodes[update[0]].next[0] {
383            Some(idx)
384                if self.nodes[idx]
385                    .value
386                    .as_ref()
387                    .is_some_and(|v| v.borrow() == key) =>
388            {
389                idx
390            }
391            _ => return None,
392        };
393    
394        // Find the last node in the backing structure
395        let last_idx = self.nodes.len() - 1;
396    
397        // Remove the prev and next positions from adjacent nodes
398        if let Some(next_idx) = self.nodes[target].next[0] {
399            self.nodes[next_idx].prev = self.nodes[target].prev;
400        }
401        for (level, val) in update.iter_mut().enumerate().take(self.height) {
402            if self.nodes[*val].next[level] == Some(target) {
403                self.nodes[*val].next[level] = self.nodes[target].next[level];
404            }
405        }
406    
407        // Actual node removal
408        let removed_node = self.nodes.swap_remove(target);
409    
410        // The hot loop:
411        // Set next/prev positions for the node that just got swapped 
412        // into the hole left by the removal
413        //
414        // Looks quadratic with nested for loops, but realistically 
415        // only requires O(n * height) worst case, where height is
416        // expected to be log(n), so realistically this is O(n * log(n)),
417        // and is expected to perform more like O(n) for sparse towers.
418        if target < self.nodes.len() {
419            // Fix next positions
420            for node in &mut self.nodes {
421                for next in node.next.iter_mut().take(self.height) {
422                    if *next == Some(last_idx) {
423                        *next = Some(target);
424                    }
425                }
426            }
427
428            // Repair adjacent backward links after relocation
429            if let Some(next_idx) = self.nodes[target].next[0] {
430                self.nodes[next_idx].prev = Some(target);
431            }
432            
433            // Repair relocated predecessor reference
434            if self.nodes[target].prev == Some(last_idx) {
435                self.nodes[target].prev = Some(target);
436            }        
437        }
438
439        // Reduce the list's height, in case the removed tower was tallest
440        while self.height > 1 &&
441            self.nodes[0].next[self.height - 1].is_none()
442        {
443            self.height -= 1;
444        }
445
446        // Return just the entry, not the entire node
447        removed_node.value
448    }
449
450    /// Returns the Kth value in the list, if it exists.
451    pub fn get_kth(&self, k: usize) -> Option<&T> {
452        let mut idx = self.nodes[0].next[0];
453        let mut i = 0;
454        while let Some(current) = idx {
455            if i == k {
456                return self.nodes[current].value.as_ref();
457            }
458            idx = self.nodes[current].next[0];
459            i += 1;
460        }
461        None
462    }
463
464    /// Returns an inclusive iterator over a range of values
465    /// in the list from `start` to `end`.
466    pub fn range<Q, R>(&self, range: R) -> RangeIter<'_, T, Q, R>
467    where
468        Q: Ord + ?Sized,
469        T: Borrow<Q>,
470        R: RangeBounds<Q>,
471    {
472        // FIND FRONT
473        let front = match range.start_bound() {
474            Bound::Included(start) => self.nodes[self.find_predecessors(start)[0]].next[0],
475            Bound::Excluded(start) => {
476                let idx = self.nodes[self.find_predecessors(start)[0]].next[0];
477                if let Some(i) = idx {
478                    if self.nodes[i].value.as_ref().unwrap().borrow() == start {
479                        self.nodes[i].next[0]
480                    } else {
481                        Some(i)
482                    }
483                } else {
484                    None
485                }
486            }
487            Bound::Unbounded => self.nodes[0].next[0],
488        };
489
490        // FIND BACK
491        let back = match range.end_bound() {
492            Bound::Included(end) => {
493                // Find predecessors of 'end'.
494                // If the element at the end of the search IS 'end', that's our back.
495                // If not, the predecessor itself is our back.
496                let update = self.find_predecessors(end);
497                let candidate = self.nodes[update[0]].next[0];
498                if let Some(idx) = candidate {
499                    if self.nodes[idx].value.as_ref().unwrap().borrow() == end {
500                        Some(idx)
501                    } else {
502                        // Predicate check: Ensure we aren't returning the sentinel (idx 0)
503                        if update[0] == 0 {
504                            None
505                        } else {
506                            Some(update[0])
507                        }
508                    }
509                } else if update[0] == 0 {
510                    None
511                } else {
512                    Some(update[0])
513                }
514            }
515            Bound::Excluded(end) => {
516                let update = self.find_predecessors(end);
517                if update[0] == 0 {
518                    None
519                } else {
520                    Some(update[0])
521                }
522            }
523            Bound::Unbounded => {
524                // To find the absolute end, we find predecessors for a
525                // "theoretically infinite" value
526                // or simply walk the tallest tower to the end.
527                let mut curr = 0;
528                for level in (0..self.height).rev() {
529                    while let Some(next_idx) = self.nodes[curr].next[level] {
530                        curr = next_idx;
531                    }
532                }
533                if curr == 0 {
534                    None
535                } else {
536                    Some(curr)
537                }
538            }
539        };
540
541        RangeIter {
542            list: self,
543            front,
544            back,
545            range,
546            _marker: std::marker::PhantomData,
547        }
548    }
549
550    /// Returns an iterator over borrowed values in the list.
551    pub fn iter(&self) -> Iter<'_, T> {
552        // Walk the express lanes to find the very last node in O(log n) time
553        let mut tail = 0;
554        for level in (0..self.height).rev() {
555            while let Some(next_idx) = self.nodes[tail].next[level] {
556                tail = next_idx;
557            }
558        }
559
560        Iter {
561            list: self,
562            next: self.nodes[0].next[0], // First node after sentinel
563            prev: if tail == 0 { None } else { Some(tail) },
564        }
565    }
566
567    // Utility functions
568    ////////////////////
569
570    // Uses the external crate rand to determine the height h
571    // of a given tower which is always 1 <= h < MAX_HEIGHT
572    // by performing a series of "coin flips".
573    fn random_height(&self) -> usize {
574        let mut level = 1;
575        let mut rng = rand::rng();
576        while level < MAX_HEIGHT && rng.random::<bool>() {
577            level += 1;
578        }
579        level
580    }
581
582    // Represents the heart of the skip list. This function is used
583    // by the `insert()`, `remove()`, `range()`, `locate()` (and by
584    // proxy `contains()`) functions.
585    //
586    // Returns an array of integers representing entries for 
587    // each level in the list that are strictly less than the search 
588    // key at each level, where the 0th index represents the base list. 
589    // The operation appears to be _O(n^2)_, but due to the list's 
590    // structure only requires _O(n)_ worst case, with _O(log(n))_ expected time.
591    //
592    // Performs dual duty in regards to Pugh's original design.
593    // Useful as a basic skip_search by capturing the base list position
594    // of the entry strictly < search_key as the 0th array index in the 
595    // return, as well as providing a list of previous positions at the 
596    // splice/split point for insert and delete operations.
597    fn find_predecessors<Q>(&self, key: &Q) -> [usize; MAX_HEIGHT]
598    where
599        Q: Ord + ?Sized,
600        T: Borrow<Q>,
601    {
602        let mut update = [0usize; MAX_HEIGHT];
603        let mut idx = 0;
604
605        for level in (0..self.height).rev() {
606            loop {
607                match self.nodes[idx].next[level] {
608                    None => break,
609                    Some(next_idx) => {
610                        let next_val = self.nodes[next_idx].value.as_ref().unwrap();
611                        if next_val.borrow() >= key {
612                            break;
613                        }
614                        idx = next_idx;
615                    }
616                }
617            }
618            // Record the node index in the update list before descending
619            update[level] = idx; 
620        }
621        update
622    }
623
624    /// Represents the heart of the skip list. This function is used
625    /// by the `get()`, `insert()`, `remove()`, `range()`, and 
626    /// `contains()`) functions.
627    ///
628    /// Returns the index of the largest entry that is 
629    /// strictly less than the provided key.
630    ///
631    /// NOTE: UNUSED
632    fn _skip_search<Q>(&self, key: &Q) -> usize
633    where
634        Q: Ord + ?Sized,
635        T: Borrow<Q>,
636    {
637        // Empty lists have a single sentinel node
638        if self.nodes.len() == 1 { return 0 };
639
640        // p = s: Always start at the HEAD node (index 0)
641        let mut pos = 0usize;
642
643        // Iterate through levels from top to bottom (the vertical "below(p)" steps)
644        for level in (0..self.height).rev() {
645            // "Scan forward" horizontally across the current level
646            while self.nodes[pos].next[level].is_some() {
647                // Peek at the NEXT node index on the current logical linked list
648                match self.nodes[pos].next[level] {
649                    // Get the next node's value safely.
650                    // If the search key is >= the forward node's value,
651                    // advance the pos to that node. If the next node is
652                    // either > key or None, break the loop, which
653                    // moves to the next level.
654                    Some(next_idx) => {
655                        let next_val = self.nodes[next_idx].value.as_ref().unwrap().borrow();
656                        if next_val < key {
657                            pos = next_idx;
658                        } else {
659                            break; // Break and descend a level
660                        }
661                    }
662                    None => {
663                        break; // Break and descend a level
664                    }
665                }
666            }
667        }
668
669        // If the position never advances beyond the sentinel, 
670        // the key is not either doesn't exist in the list, 
671        // or it belongs as the first element
672        pos
673    }
674
675    /// Returns the traversal path of a search key by
676    /// re-using the skip_search logic. The only difference is that
677    /// this traversal records each index and returns the path.
678    fn _traversal<Q>(&self, key: &Q) -> Vec<T>
679    where
680        Q: Ord + ?Sized,
681        T: Borrow<Q> + Clone,
682    {
683        let mut vec = Vec::new();
684
685        // p = s: Always start at the HEAD node (index 0)
686        let mut pos = 0usize;
687
688        // Iterate through levels from top to bottom (the vertical "below(p)" steps)
689        for level in (0..self.height).rev() {
690            // "Scan forward" horizontally across the current level
691            while self.nodes[pos].next[level].is_some() {
692                // Peek at the NEXT node index on the current logical linked list
693                match self.nodes[pos].next[level] {
694                    // Get the next node's value safely.
695                    // If the search key is >= the forward node's value,
696                    // advance the pos to that node. If the next node is
697                    // either > key or None, break the loop, which
698                    // moves to the next level.
699                    Some(next_idx) => {
700                        let next_val = self.nodes[next_idx].value.as_ref().unwrap().borrow();
701                        if next_val < key {
702                            vec.push(self.nodes[next_idx].value.clone().unwrap());
703                            pos = next_idx;
704                        } else {
705                            break; // Break and descend a level
706                        }
707                    }
708                    None => {
709                        break; // Break and descend a level
710                    }
711                }
712            }
713        }
714        vec
715    }
716}
717
718pub struct Iter<'a, T> {
719    list: &'a SkipList<T>,
720    next: Option<usize>,
721    prev: Option<usize>,
722}
723impl<'a, T> Iterator for Iter<'a, T> {
724    type Item = &'a T;
725
726    fn next(&mut self) -> Option<Self::Item> {
727        let idx = self.next?;
728        let value = self.list.nodes[idx].value.as_ref()?;
729
730        if self.next == self.prev {
731            self.next = None;
732            self.prev = None;
733        } else {
734            self.next = self.list.nodes[idx].next[0];
735        }
736        Some(value)
737    }
738}
739impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
740    fn next_back(&mut self) -> Option<Self::Item> {
741        let idx = self.prev?;
742        let value = self.list.nodes[idx].value.as_ref()?;
743
744        if self.prev == self.next {
745            self.next = None;
746            self.prev = None;
747        } else {
748            let prev = self.list.nodes[idx].prev;
749            // Sentinel check: don't yield index 0
750            self.prev = if prev == Some(0) { None } else { prev };
751        }
752        Some(value)
753    }
754}
755
756pub struct RangeIter<'a, T, Q, R>
757where
758    Q: ?Sized,
759    R: RangeBounds<Q>,
760{
761    list: &'a SkipList<T>,
762    front: Option<usize>, // Moves forward
763    back: Option<usize>,  // Moves backward
764    range: R,
765    _marker: std::marker::PhantomData<Q>,
766}
767impl<'a, T, Q, R> Iterator for RangeIter<'a, T, Q, R>
768where
769    Q: Ord + ?Sized,
770    T: Borrow<Q>,
771    R: RangeBounds<Q>,
772{
773    type Item = &'a T;
774
775    fn next(&mut self) -> Option<Self::Item> {
776        let idx = self.front?;
777
778        // Boundary Check
779        let value = self.list.nodes[idx].value.as_ref().unwrap();
780        if !self.range.contains(value.borrow()) {
781            self.front = None;
782            return None;
783        }
784
785        // Meet/Cross Check: If front matches back, this is the last element
786        if self.front == self.back {
787            self.front = None;
788            self.back = None;
789        } else {
790            self.front = self.list.nodes[idx].next[0];
791        }
792
793        Some(value)
794    }
795}
796impl<'a, T, Q, R> DoubleEndedIterator for RangeIter<'a, T, Q, R>
797where
798    Q: Ord + ?Sized,
799    T: Borrow<Q>,
800    R: RangeBounds<Q>,
801{
802    fn next_back(&mut self) -> Option<Self::Item> {
803        let idx = self.back?;
804
805        // Boundary Check
806        let value = self.list.nodes[idx].value.as_ref().unwrap();
807        if !self.range.contains(value.borrow()) {
808            self.back = None;
809            return None;
810        }
811
812        // Meet/Cross Check
813        if self.back == self.front {
814            self.back = None;
815            self.front = None;
816        } else {
817            // Move back, but ensure we don't land on the sentinel (idx 0)
818            let prev = self.list.nodes[idx].prev;
819            self.back = if prev == Some(0) { None } else { prev };
820        }
821
822        Some(value)
823    }
824}
825
826impl<'a, T: Ord> IntoIterator for &'a SkipList<T> {
827    type Item = &'a T;
828    type IntoIter = Iter<'a, T>;
829
830    fn into_iter(self) -> Self::IntoIter {
831        self.iter()
832    }
833}
834
835#[test]
836fn one() {
837    let mut list = SkipList::<char>::new();
838
839    // Tests basic housekeeping on empty list
840    assert_eq!(list.len(), 0);
841    assert!(list.is_empty());
842    assert!(!list.contains(&'z'));
843
844    // Inserts 9 values into the skip list
845    // with a consuming iterator, moving values
846    // into the list
847    let values = ['a', 'c', 'e', 'd', 'b', 'i', 'g', 'h', 'f'];
848    println!("Insert elements in order: {:?}", &values);
849    for e in values.into_iter() {
850        list.insert(e)
851    }
852    println!("LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}", list.height, list.nodes.len());
853    println!("Tower contents by insertion order, NOT sorted order:");
854    for (i, e) in list.nodes.iter().enumerate() {
855        // Collect only the Some values into a new Vec
856        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
857        let values: Vec<_> = e.next.iter().collect();
858        match e.value {
859            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
860            None => println!("HEAD [{i}]: {values:?}"),
861        }
862        //if let Some(val) = e.value {
863        //    let v = &val.to_string();
864        //    println!("{v}[{i}]: {values:?}");
865        //} else {
866        //    println!("HEAD[0]: {values:?}");
867        //}
868    }
869    println!();
870
871    // Tests that len gets updated properly
872    assert_eq!(list.len(), 9);
873    assert!(list.contains(&'g'));
874
875    // Tests basic ordering and iteration
876    // Basic iteration with iter()
877    // Clippy wants enumerate instead of external loop counter
878    let sorted = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
879    for (i, e) in list.iter().enumerate() {
880        assert_eq!(e, &sorted[i]);
881    }
882    // Double-ended iteration with rev()
883    // Clippy wants saturating_sub instead of loop counter
884    let mut i = 8;
885    for e in list.iter().rev() {
886        assert_eq!(e, &sorted[i]);
887        if i > 0 {
888            i = i.saturating_sub(1)
889        };
890    }
891    // Or if you wanna be fancy about it
892    // zip() stops as soon as one iterator ends,
893    // eliminating the need for an overflow check
894    for (e, i) in list.iter().rev().zip((0..=8).rev()) {
895        assert_eq!(e, &sorted[i]);
896    }
897
898    // Iterator inferance using the IntoIter impl
899    let mut i = 0;
900    #[allow(clippy::explicit_counter_loop)]
901    for e in &list {
902        assert_eq!(e, &sorted[i]);
903        i += 1;
904    }
905
906    // Tests the Kth function in a 0-indexed list
907    assert_eq!(list.get_kth(6).unwrap(), &'g');
908
909    // Tests the range function
910    // NOTE: char is Copy so you dont strictly need to borrow
911    // when setting range bounds, these tests illustrate both
912    // borrowing and not; Note that each bounds must match
913    // so no (&'a'..'f'), only ('a'..'f') or (&'a'..&'f')
914    // Midlist (exclusive)
915    let val = ['c', 'd', 'e'];
916    //for (i, e) in list.range(&'c', &'f').enumerate() {
917    for (i, e) in list.range('c'..'f').enumerate() {
918        assert_eq!(e, &val[i])
919    }
920    // Midlist (inclusive)
921    let val = ['c', 'd', 'e', 'f'];
922    //for (i, e) in list.range(&'c', &'f').enumerate() {
923    for (i, e) in list.range('c'..='f').enumerate() {
924        assert_eq!(e, &val[i])
925    }
926    // Start of list
927    let val = ['a', 'b', 'c', 'd', 'e', 'f'];
928    //for (i, e) in list.range(&'a', &'f').enumerate() {
929    for (i, e) in list.range(..&'f').enumerate() {
930        assert_eq!(e, &val[i])
931    }
932    // End of list
933    let val = ['e', 'f', 'g', 'h', 'i'];
934    for (i, e) in list.range(&'e'..).enumerate() {
935        assert_eq!(e, &val[i])
936    }
937
938    // Tests remove(e)
939    // Removes the first element
940    println!("Remove 'a'");
941    list.remove(&'a');
942    // Removes an arbitrary element
943    println!("Remove 'e'");
944    list.remove(&'e');
945    // Removes the last element
946    println!("Remove 'i'");
947    list.remove(&'i');
948    // List shrinks as expected
949    assert_eq!(list.len(), 6);
950    // List no longer contains elements
951    assert!(!list.contains(&'e'));
952    assert!(!list.contains(&'a'));
953    // Cant remove what isn't there!
954    assert!(list.remove(&'z').is_none());
955    // Debug prints new layout
956    print!("List updated values: [");
957    for e in list.iter() {
958        print!("{e:#?} ")
959    }
960    println!("]");
961
962    // Debug prints the tower contents
963    println!("LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}", list.height, list.nodes.len());
964    println!("Tower contents by insertion order, NOT sorted order:");
965    for (i, e) in list.nodes.iter().enumerate() {
966        // Collect only the Some values into a new Vec
967        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
968        let values: Vec<_> = e.next.iter().collect();
969        match e.value {
970            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
971            None => println!("HEAD [{i}]: {values:?}"),
972        }
973    }
974    println!();
975
976    // Tests skip_search(e), find_predecessors(e) and their dependencies:
977    // contains(e), get(e), and find_val(e)
978    //
979    // An element in the list
980    let node = 'h';
981    assert_eq!(list._skip_search(&node), 6); // 6 is g which is < h
982    assert_eq!(list.find_predecessors(&node)[0], 6); // 6 is g which is < h
983    assert!(list.contains(&node));
984    assert_eq!(list.get(&node).unwrap(), &'h');
985    // An element at the beginning of the list
986    let node = 'b';
987    assert_eq!(list._skip_search(&node), 0); 
988    assert_eq!(list.find_predecessors(&node)[0], 0); 
989    assert!(list.contains(&node));
990    assert_eq!(list.get(&node).unwrap(), &'b');
991    // An element not in the list
992    // 'j' is not in the list, but skip_search returns 6 because
993    // thats the position that 'i' lives at due to insertion order,
994    // and 'i' < 'j'
995    let node = 'j';
996    assert_eq!(list._skip_search(&node), 3); // 3 is h, the last entry
997    assert_eq!(list.find_predecessors(&node)[0], 3); // 3 is h, the last entry
998    assert!(!list.contains(&node));
999    assert!(list.get(&node).is_none());
1000    // An element that was previously in the list, but removed
1001    let node = 'a';
1002    assert_eq!(list._skip_search(&node), 0); // belongs after HEAD
1003    assert_eq!(list.find_predecessors(&node)[0], 0); // belongs after HEAD
1004    assert!(!list.contains(&node));
1005    assert!(list.get(&node).is_none());
1006
1007    // A bunch of random list mutations to ensure coherence
1008    list.insert('p');
1009    list.insert('u');
1010    list.insert('w');
1011    list.remove(&'p');
1012    list.insert('l');
1013    list.insert('m');
1014    list.remove(&'f');
1015    list.remove(&'o');
1016    list.insert('q');
1017    list.remove(&'m');
1018    list.insert('x');
1019    list.insert('z');
1020
1021    // Visual component:
1022    // Combines contains(e) and prints traversal() as proof
1023    let trav = list._traversal(&'g');
1024    let con = list.contains(&'g');
1025    println!("Contains 'g': {con:?}");
1026    println!("Traversal: {trav:?}");
1027    let trav = list._traversal(&'j');
1028    let con = list.contains(&'j');
1029    println!("Contains 'j': {con:?}");
1030    println!("Traversal: {trav:?}");
1031    let trav = list._traversal(&'a');
1032    let con = list.contains(&'a');
1033    println!("Contains 'a': {con:?}");
1034    println!("Traversal: {trav:?}");
1035
1036    // Tests traversal ordering
1037    let l2 = ['b', 'c', 'd', 'g', 'h', 'l', 'q', 'u', 'w', 'x', 'z'];
1038    for (val, i) in list.iter().zip(0..=5) {
1039        assert_eq!(val, &l2[i]);
1040    }
1041    // Visual confirmation of correct traversal
1042    print!("List values:\n   ");
1043    for e in list.iter() {
1044        print!("{e:#?} ")
1045    }
1046    println!();
1047
1048    //panic!();
1049}
1050
1051#[test]
1052// AI-written "stress" test
1053fn test_skip_list_removal_integrity() {
1054    // Assumes your SkipList has a standard New or Default implementation
1055    let mut list = SkipList::new();
1056    
1057    // 1. Insert a sequence of numbers.
1058    // This allows your natural random_height() generator to build up 
1059    // a multi-level tower structure organically.
1060    let total_elements = 300;
1061    for i in 0..total_elements {
1062        list.insert(i);
1063    }
1064
1065    // 2. Remove elements from the middle of the list.
1066    // This forces swap_remove to repeatedly pull the last element of the Vec
1067    // into the newly created holes, triggering your global repair loops.
1068    for i in (50..200).step_by(2) {
1069        list.remove(&i);
1070    }
1071
1072    // 3. STRUCTURAL AUDIT
1073    // Scan every single surviving node's next pointers across every level.
1074    // If swap_remove left a stale index behind, it will point out-of-bounds.
1075    let current_len = list.nodes.len();
1076    for idx in 0..current_len {
1077        for level in 0..list.height {
1078            if let Some(next_idx) = list.nodes[idx].next[level] {
1079                
1080                // Assert 1: Out-of-bounds index protection
1081                assert!(
1082                    next_idx < current_len,
1083                    "CORRUPTION: Node at index {idx} on level {level} points to index {next_idx}, \
1084                     which is out-of-bounds for a Vec of length {current_len}!"
1085                );
1086
1087                // Assert 2: Level 0 backlink symmetric validation
1088                if level == 0 {
1089                    assert_eq!(
1090                        list.nodes[next_idx].prev,
1091                        Some(idx),
1092                        "CORRUPTION: Link symmetry broken! Node {} points forward to {}, \
1093                         but {} points backward to {:?}",
1094                        idx, next_idx, next_idx, list.nodes[next_idx].prev
1095                    );
1096                }
1097            }
1098        }
1099    }
1100
1101    // 4. ALGORITHMIC AUDIT
1102    // Verify that every single element that wasn't deleted is still perfectly 
1103    // searchable via skip_search. If a lane broke, skip_search will overshoot 
1104    // or fail to track the element.
1105    for i in 0..total_elements {
1106        // Skip the elements we explicitly deleted
1107        if (50..200).contains(&i) && i % 2 == 0 {
1108            continue;
1109        }
1110
1111        // Your skip_search returns a valid usize position
1112        let pos = list.find_predecessors(&i)[0];
1113        
1114        // Ensure the pos returned actually matches our search target 
1115        // or points to its direct predecessor.
1116        if pos == 0 {
1117            // If it returned the sentinel, the first item in the list must be >= i
1118            if let Some(first_idx) = list.nodes[0].next[0] {
1119                let first_val = list.nodes[first_idx].value.as_ref().unwrap();
1120                assert!(first_val >= &i);
1121            }
1122        } else {
1123            let found_val = list.nodes[pos].value.as_ref().unwrap();
1124            assert!(found_val <= &i, "skip_search returned a node greater than the key!");
1125        }
1126    }
1127
1128    println!("LIST DIAGNOSTICS: \n\theight: {}\n\tlength: {}", list.height, list.nodes.len());
1129    println!("Tower contents by insertion order, NOT sorted order:");
1130    for (i, e) in list.nodes.iter().enumerate() {
1131        // Collect only the Some values into a new Vec
1132        //let values: Vec<_> = e.next.iter().filter_map(|&x| x).collect();
1133        let values: Vec<_> = e.next.iter().collect();
1134        match e.value {
1135            Some(val) => println!("{val:>04} [{i}]: {values:?}"),
1136            None => println!("HEAD [{i}]: {values:?}"),
1137        }
1138    }
1139    println!();
1140    //panic!()
1141
1142    // Maximalist
1143    let array: [i32; 4] = [1i32, 2, 3, 4];
1144    let iterator: std::slice::Iter<'_, i32> = array.iter();
1145    let total: i32 = iterator.sum();
1146    assert_eq!(total, 10);
1147
1148    // Minimalist
1149    assert_eq!([1, 2, 3, 4].iter().sum::<i32>(), 10);
1150
1151    // Illustrative
1152    let array = [1, 2, 3, 4]; // Iterable object
1153    let iter = array.iter(); // Iterator
1154    let total: i32 = iter.sum(); // Iterator adapter
1155    assert_eq!(total, 10);
1156
1157    // Also illustrative
1158    let array = [1, 2, 3, 4];
1159    let mut total = 0;
1160    for e in array.iter() {
1161        total += e;
1162    }
1163    assert_eq!(total, 10);
1164
1165    // Baby bear; just right
1166    let array = [1, 2, 3, 4]; // Iterable object
1167    let total: i32 = array.iter().sum(); // Iterator and adapter
1168    assert_eq!(total, 10);
1169    assert_eq!(array, [1, 2, 3, 4]);
1170
1171}
1172