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}