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