diff --git a/Cargo.toml b/Cargo.toml index b4ba322..51f50d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "orx-linked-list" -version = "1.0.0" +version = "2.0.0" edition = "2021" authors = ["orxfun "] -description = "An efficient doubly linked list using regular & references with a focus on better cache locality avoiding heap allocations by smart pointers." +description = "An efficient and recursive singly and doubly linked list implementation." license = "MIT" repository = "https://github.com/orxfun/orx-linked-list/" keywords = ["linked", "list", "vec", "array", "pinned"] categories = ["data-structures", "rust-patterns"] [dependencies] -orx-imp-vec = "1.0" -orx-split-vec = "1.2" - +orx-selfref-col = "1.0" +orx-split-vec = "2.0" [dev-dependencies] rand = "0.8" rand_chacha = "0.3" criterion = { version = "0.5", features = ["html_reports"] } +test-case = "3.3" [[bench]] -name = "mutation_ends" +name = "append" harness = false diff --git a/README.md b/README.md index 8c72b70..fdba49f 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,97 @@ # orx-linked-list -An efficient doubly linked list using regular `&` references with a focus to avoid smart pointers and improve cache locality. +An efficient and recursive singly and doubly linked list implementation. -## A. Motivation +## Variants and Time Complexity of Methods -Self referential, often recursive, collections contain an important set of useful data structures including linked lists. However, building these structures with references `&` is not possible in safe rust. +* `type SinglyLinkedList<'a, T> = List<'a, Singly, T>;` +* `type DoublyLinkedList<'a, T> = List<'a, Doubly, T>;` -Alternatively, these collections can be built using reference counted smart pointers such as `std::rc::Rc` and independent heap allocations. However, independent heap allocations is a drawback as the elements do not live close to each other leading to poor cache locality. Further, reference counted pointers have a runtime overhead. This crate makes use of [`orx_imp_vec::ImpVec`](https://crates.io/crates/orx-imp-vec) as the underlying storage. ImpVec specializes in enabling self referential collections by relying on the pinned memory location guarantee provided by the [`orx_pinned_vec::PinnedVec`](https://crates.io/crates/orx-pinned-vec). +## Time Complexity of Methods -### Standard LinkedList +| Method | Time Complexity | +| -------- | ------- | +| access to front and back of the list | **O(1)** | +| push to front and back (`Doubly` only) of the list | **O(1)** | +| pop from front and back (`Doubly` only) of the list | **O(1)** | +| insert at an arbitrary position | O(n) | +| remove from an arbitrary position | O(n) | +| append another list to the front or back of the list | **O(1)** | +| retain elements by a predicate | O(n) | +| retain and collect remove elements | O(n) | +| iteration forwards or backwards (only `Doubly`) | O(n) | -Standard `std::collections::LinkedList` implementation avoids reference counted pointers and uses `NonNull` instead, most likely to avoid this overhead. However, this leads to a risky and difficult implementation that feels more low level than it should. You may see the implementation [here](https://doc.rust-lang.org/src/alloc/collections/linked_list.rs.html). The `unsafe` keyword is used more than 60 times in this file. These are usually related to reading from and writing to memory through raw pointers. -***Motivation:*** We do not need to count references provided that all elements and inter-element references belong to the same owner or container. This is because all elements will be dropped at the same time together with their inter-element references when the container `ImpVec` is dropped. +## Examples -***Motivation:*** We should be able to define these structures without directly accessing memory through raw pointers. This is unnecessarily powerful and risky. Instead, unsafe code must be limited to methods which are specialized for and only allow defining required connections of self referential collections. - -### orx_linked_list::LinkedList - -Linked list implementation in this crate uses an `ImpVec` as the underlying storage and makes use of its specialized methods. This brings the following advantages: - -* Allows for a higher level implementation without any use of raw pointers. -* Avoids smart pointers. -* Avoids almost completely accessing through integer indices. -* All nodes belong to the same `ImpVec` living close to each other. This allows for better cache locality. -* Full fetched doubly-linked-list implementation uses the `unsafe` keyword seven times, which are repeated uses of three methods: - * `ImpVec::push_get_ref` - * `ImpVec::move_get_ref` - * `ImpVec::unsafe_truncate` (*a deref method from [`PinnedVec`](https://crates.io/crates/orx-pinned-vec)*) - -Furthermore, this implementation is more performant than the standard library implementation, a likely indicator of better cache locality. You may below the benchmark results for a series of random push/pop mutations after pushing "number of elements" elements to the list. - -https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG +```rust +use orx_linked_list::*; -However, note that the benchmark compares only the linked list implementations. `std::collections::VecDeque` is significantly more efficient than both linked lists for most operations. Therefore, it is preferrable unless the flexibility of linked list's recursive nature is not required (see `split` methods in the next section). +fn eq<'a, I: Iterator + Clone>(iter: I, slice: &[u32]) -> bool { + iter.clone().count() == slice.len() && iter.zip(slice.iter()).all(|(a, b)| a == b) +} -## B. Features +let _list: List = List::new(); +let _list = SinglyLinkedList::::new(); +let _list: List = List::new(); +let _list = DoublyLinkedList::::new(); -`orx_linked_list::LinkedList` implementation provides standard linked list opeartions such as constant time insertions and removals. Further, it reflects the **recursive** nature of the data structure through so called `LinkedListSlice`s. The caller can move to the desired element of the linked list and get the rest of the list as a linked list slice; which is nothing but an immutable linked list. Furthermore, slices can simply be `collect`ed as an owned linked list. +let mut list = DoublyLinkedList::from_iter([3, 4, 5]); +assert_eq!(list.front(), Some(&3)); +assert_eq!(list.back(), Some(&5)); +assert!(eq(list.iter(), &[3, 4, 5])); +assert!(eq(list.iter_from_back(), &[5, 4, 3])); -```rust -use orx_linked_list::*; +assert_eq!(list.pop_front(), Some(3)); +assert_eq!(list.pop_back(), Some(5)); -// BASIC -let mut list = LinkedList::new(); -list.extend(vec!['a', 'b', 'c']); +list.push_back(5); +list.push_front(3); +assert!(eq(list.iter(), &[3, 4, 5])); -assert_eq!(list.len(), 3); -assert!(list.contains(&'b')); -assert_eq!(list.index_of(&'c'), Some(2)); -assert_eq!(list.from_back_index_of(&'c'), Some(0)); +let other = DoublyLinkedList::from_iter([6, 7, 8, 9]); +list.append_back(other); +assert!(eq(list.iter(), &[3, 4, 5, 6, 7, 8, 9])); -list.push_back('d'); -assert_eq!(Some(&'d'), list.back()); +let other = DoublyLinkedList::from_iter([0, 1, 2]); +list.append_front(other); +assert!(eq(list.iter(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); -*list.get_at_mut(0).unwrap() = 'x'; +list.retain(&|x| x < &5); +assert!(eq(list.iter(), &[0, 1, 2, 3, 4])); -list.push_front('e'); -*list.front_mut().unwrap() = 'f'; +let mut odds = vec![]; +let mut collect_odds = |x| odds.push(x); +list.retain_collect(&|x| x % 2 == 0, &mut collect_odds); -_ = list.remove_at(1); -_ = list.pop_back(); -list.insert_at(0, 'x'); -list.clear(); -list.push_front('y'); -list.pop_front(); +assert!(eq(list.iter(), &[0, 2, 4])); +assert!(eq(odds.iter(), &[1, 3])); +``` -// ITER -let list: LinkedList<_> = ['a', 'b', 'c', 'd', 'e'].into_iter().collect(); +## Internal Features -let forward: Vec<_> = list.iter().copied().collect(); -assert_eq!(forward, &['a', 'b', 'c', 'd', 'e']); +`orx_linked_list::List` makes use of the safety guarantees and efficiency features of [orx-selfref-col::SelfRefCol](https://crates.io/crates/orx-selfref-col). +* `SelfRefCol` constructs its safety guarantees around the fact that all references will be among elements of the same collection. By preventing bringing in external references or leaking out references, it is safe to build the self referential collection with **regular `&` references**. +* With careful encapsulation, `SelfRefCol` prevents passing in external references to the list and leaking within list node references to outside. Once this is established, it provides methods to easily mutate inter list node references. These features allowed a very convenient implementation of the linked list in this crate with almost no use of the `unsafe` keyword, no read or writes through pointers and no access by indices. Compared to the `std::collections::LinkedList` implementation, it can be observed that `orx_linked_list::List` is a much **higher level implementation**. +* Furthermore, `orx_linked_list::List` is **significantly faster** than the standard linked list. One of the main reasons for this is the feature of `SelfRefCol` keeping all close to each other rather than at arbitrary locations in memory which leads to a better cache locality. -let backward: Vec<_> = list.iter_from_back().copied().collect(); -assert_eq!(backward, &['e', 'd', 'c', 'b', 'a']); +## Benchmarks -// SPLITS -let (left, right) = list.split(2).unwrap(); -assert_eq!(left, &['a', 'b']); -assert_eq!(right, &['c', 'd', 'e']); -// left & right are also nothing but immutable linked lists -assert_eq!(right.front(), Some(&'c')); -assert_eq!(left.back(), Some(&'b')); +### Mutation Ends -let (front, after) = list.split_front().unwrap(); -assert_eq!(front, &'a'); -assert_eq!(after, &['b', 'c', 'd', 'e']); +*You may see the benchmark at [benches/mutation_ends.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/mutation_ends.rs).* -let (before, back) = list.split_back().unwrap(); -assert_eq!(before, &['a', 'b', 'c', 'd']); -assert_eq!(back, &'e'); +This benchmark compares time performance of calls to `push_front`, `push_back`, `pop_front` and `pop_back` methods. -let (left, right) = list.split_before(&'d').unwrap(); -assert_eq!(left, &['a', 'b', 'c']); -assert_eq!(right, &['d', 'e']); +https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG -let (left, right) = list.split_after(&'d').unwrap(); -assert_eq!(left, &['a', 'b', 'c', 'd']); -assert_eq!(right, &['e']); +### Iteration -// RECURSIVE SPLITS -let (left1, left2) = left.split(1).unwrap(); -assert_eq!(left1, &['a']); -assert_eq!(left2, &['b', 'c', 'd']); +*You may see the benchmark at [benches/iter.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/iter.rs).* -// SPLIT TO OWNED -let mut left_list = left.collect(); +This benchmark compares time performance of iteration through the `iter` method. -assert_eq!(left_list, &['a', 'b', 'c', 'd']); -_ = left_list.pop_front(); -_ = left_list.pop_back(); -assert_eq!(left_list, &['b', 'c']); -``` +https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/iter.PNG ## License diff --git a/benches/append.rs b/benches/append.rs new file mode 100644 index 0000000..6f10ec7 --- /dev/null +++ b/benches/append.rs @@ -0,0 +1,104 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, BenchmarkId, Criterion, +}; +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; + +#[derive(Clone, Copy)] +enum Action { + PushBack(u32), + PushFront(u32), +} + +fn get_test_data(n: usize) -> Vec { + let mut rng = ChaCha8Rng::seed_from_u64(6523); + let vec: Vec<_> = (0..n) + .map(|_| match rng.gen::() { + x if x < 0.5 => Action::PushBack(rng.gen()), + _ => Action::PushFront(rng.gen()), + }) + .collect(); + vec +} +fn get_orx_linked_list(actions: &[Action]) -> orx_linked_list::DoublyLinkedList { + let mut list = orx_linked_list::DoublyLinkedList::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} +fn get_std_linked_list(actions: &[Action]) -> std::collections::LinkedList { + let mut list = std::collections::LinkedList::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} +fn get_std_vecdeque(actions: &[Action]) -> std::collections::VecDeque { + let mut list = std::collections::VecDeque::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} + +// variants +fn bench_orx_linked_list(group: &mut BenchmarkGroup<'_, WallTime>, data: &[Action], n: &usize) { + group.bench_with_input( + BenchmarkId::new("orx_linked_list::DoublyLinkedList", n), + n, + |b, _| { + let mut list = get_orx_linked_list(data); + b.iter(|| list.append_back(get_orx_linked_list(data))) + }, + ); +} + +fn bench(c: &mut Criterion) { + let treatments = vec![ + 1_024, + 1_024 * 4, + 1_024 * 16, + 1_024 * 16 * 4, + 1_024 * 16 * 4 * 4, + ]; + + let mut group = c.benchmark_group("append"); + + for n in &treatments { + let data = get_test_data(*n); + + bench_orx_linked_list(&mut group, &data, n); + + group.bench_with_input( + BenchmarkId::new("std::collections::LinkedList", n), + n, + |b, _| { + let mut list = get_std_linked_list(&data); + b.iter(|| list.append(&mut get_std_linked_list(&data))) + }, + ); + group.bench_with_input( + BenchmarkId::new("std::collections::VecDeque", n), + n, + |b, _| { + let mut list = get_std_vecdeque(&data); + b.iter(|| list.append(&mut get_std_vecdeque(&data))) + }, + ); + } + + group.finish(); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/benches/iter.rs b/benches/iter.rs index 0ccf553..355c255 100644 --- a/benches/iter.rs +++ b/benches/iter.rs @@ -1,141 +1,116 @@ -// use criterion::{ -// criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, BenchmarkId, Criterion, -// }; -// use orx_linked_list::MemoryUtilization; -// use rand::prelude::*; -// use rand_chacha::ChaCha8Rng; +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, BenchmarkId, Criterion, +}; +use rand::prelude::*; +use rand_chacha::ChaCha8Rng; -// #[derive(Clone, Copy)] -// enum Action { -// PushBack(u32), -// PushFront(u32), -// } +#[derive(Clone, Copy)] +enum Action { + PushBack(u32), + PushFront(u32), +} -// fn get_test_data(n: usize) -> Vec { -// let mut rng = ChaCha8Rng::seed_from_u64(6523); -// let vec: Vec<_> = (0..n) -// .map(|_| match rng.gen::() { -// x if x < 0.5 => Action::PushBack(rng.gen()), -// _ => Action::PushFront(rng.gen()), -// }) -// .collect(); -// vec -// } -// fn get_orx_linked_list(actions: &[Action]) -> orx_linked_list::LinkedList { -// let mut list = orx_linked_list::LinkedList::new(); -// for action in actions { -// match action { -// Action::PushBack(x) => list.push_back(*x), -// Action::PushFront(x) => list.push_front(*x), -// }; -// } -// list -// } -// fn get_std_linked_list(actions: &[Action]) -> std::collections::LinkedList { -// let mut list = std::collections::LinkedList::new(); -// for action in actions { -// match action { -// Action::PushBack(x) => list.push_back(*x), -// Action::PushFront(x) => list.push_front(*x), -// }; -// } -// list -// } -// fn get_std_vecdeque(actions: &[Action]) -> std::collections::VecDeque { -// let mut list = std::collections::VecDeque::new(); -// for action in actions { -// match action { -// Action::PushBack(x) => list.push_back(*x), -// Action::PushFront(x) => list.push_front(*x), -// }; -// } -// list -// } +fn get_test_data(n: usize) -> Vec { + let mut rng = ChaCha8Rng::seed_from_u64(6523); + let vec: Vec<_> = (0..n) + .map(|_| match rng.gen::() { + x if x < 0.5 => Action::PushBack(rng.gen()), + _ => Action::PushFront(rng.gen()), + }) + .collect(); + vec +} +fn get_orx_linked_list(actions: &[Action]) -> orx_linked_list::DoublyLinkedList { + let mut list = orx_linked_list::DoublyLinkedList::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} +fn get_std_linked_list(actions: &[Action]) -> std::collections::LinkedList { + let mut list = std::collections::LinkedList::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} +fn get_std_vecdeque(actions: &[Action]) -> std::collections::VecDeque { + let mut list = std::collections::VecDeque::new(); + for action in actions { + match action { + Action::PushBack(x) => list.push_back(*x), + Action::PushFront(x) => list.push_front(*x), + }; + } + list +} -// // variants -// fn bench_orx_linked_list( -// group: &mut BenchmarkGroup<'_, WallTime>, -// data: &[Action], -// n: &usize, -// mem: MemoryUtilization, -// use_iter_unordered: bool, -// ) { -// fn run(list: &orx_linked_list::LinkedList, use_iter_unordered: bool) -> u32 { -// if use_iter_unordered { -// list.iter_unordered().sum() -// } else { -// list.iter().sum() -// } -// } +// variants +fn bench_orx_linked_list(group: &mut BenchmarkGroup<'_, WallTime>, data: &[Action], n: &usize) { + fn run(list: &orx_linked_list::DoublyLinkedList) -> u32 { + list.iter().sum() + } -// let iter = if use_iter_unordered { -// ".iter_unordered()" -// } else { -// ".iter()" -// }; -// group.bench_with_input( -// BenchmarkId::new(format!("orx_linked_list::LinkedList({:?}){}", mem, iter), n), -// n, -// |b, _| { -// let list = get_orx_linked_list(data); -// b.iter(|| run(&list, use_iter_unordered)) -// }, -// ); -// } + group.bench_with_input( + BenchmarkId::new("orx_linked_list::DoublyLinkedList", n), + n, + |b, _| { + let list = get_orx_linked_list(data); + b.iter(|| run(&list)) + }, + ); +} -// fn std_linked_list(list: &std::collections::LinkedList) -> u32 { -// list.iter().sum() -// } +fn std_linked_list(list: &std::collections::LinkedList) -> u32 { + list.iter().sum() +} -// fn std_vecdeque(list: &std::collections::VecDeque) -> u32 { -// list.iter().sum() -// } +fn std_vecdeque(list: &std::collections::VecDeque) -> u32 { + list.iter().sum() +} -// fn bench(c: &mut Criterion) { -// let treatments = vec![1_024, 1_024 * 4, 1_024 * 16]; +fn bench(c: &mut Criterion) { + let treatments = vec![ + 1_024, + 1_024 * 4, + 1_024 * 16, + 1_024 * 16 * 4, + 1_024 * 16 * 4 * 4, + ]; -// let mut group = c.benchmark_group("iter"); + let mut group = c.benchmark_group("iter"); -// for n in &treatments { -// let data = get_test_data(*n); + for n in &treatments { + let data = get_test_data(*n); -// bench_orx_linked_list(&mut group, &data, n, MemoryUtilization::Lazy, false); -// bench_orx_linked_list( -// &mut group, -// &data, -// n, -// MemoryUtilization::WithThreshold(0.75), -// false, -// ); -// bench_orx_linked_list(&mut group, &data, n, MemoryUtilization::Lazy, true); -// bench_orx_linked_list( -// &mut group, -// &data, -// n, -// MemoryUtilization::WithThreshold(0.75), -// true, -// ); + bench_orx_linked_list(&mut group, &data, n); -// group.bench_with_input( -// BenchmarkId::new("std::collections::LinkedList", n), -// n, -// |b, _| { -// let list = get_std_linked_list(&data); -// b.iter(|| std_linked_list(&list)) -// }, -// ); -// group.bench_with_input( -// BenchmarkId::new("std::collections::VecDeque", n), -// n, -// |b, _| { -// let list = get_std_vecdeque(&data); -// b.iter(|| std_vecdeque(&list)) -// }, -// ); -// } + group.bench_with_input( + BenchmarkId::new("std::collections::LinkedList", n), + n, + |b, _| { + let list = get_std_linked_list(&data); + b.iter(|| std_linked_list(&list)) + }, + ); + group.bench_with_input( + BenchmarkId::new("std::collections::VecDeque", n), + n, + |b, _| { + let list = get_std_vecdeque(&data); + b.iter(|| std_vecdeque(&list)) + }, + ); + } -// group.finish(); -// } + group.finish(); +} -// criterion_group!(benches, bench); -// criterion_main!(benches); +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/benches/mutation_ends.rs b/benches/mutation_ends.rs index b32e394..0ad3165 100644 --- a/benches/mutation_ends.rs +++ b/benches/mutation_ends.rs @@ -2,7 +2,7 @@ use criterion::{ black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, BenchmarkId, Criterion, }; -use orx_linked_list::MemoryUtilization; +use orx_linked_list::*; use rand::prelude::*; use rand_chacha::ChaCha8Rng; @@ -18,14 +18,14 @@ fn get_test_data(n: usize) -> Vec { let mut rng = ChaCha8Rng::seed_from_u64(56456); let mut vec: Vec<_> = (0..n) .map(|_| match rng.gen::() { - x if x < 0.5 => Action::PushBack(rng.gen()), - _ => Action::PushFront(rng.gen()), + x if x < 0.5 => Action::PushBack(rng.gen_range(0..n) as u32), + _ => Action::PushFront(rng.gen_range(0..n) as u32), }) .collect(); for _ in 0..2 * n { let action = match rng.gen::() { - x if x < 0.25 => Action::PushBack(rng.gen()), - x if x < 0.50 => Action::PushFront(rng.gen()), + x if x < 0.25 => Action::PushBack(rng.gen_range(0..n) as u32), + x if x < 0.50 => Action::PushFront(rng.gen_range(0..n) as u32), x if x < 0.75 => Action::PopBack, _ => Action::PopFront, }; @@ -35,13 +35,8 @@ fn get_test_data(n: usize) -> Vec { } // variants -fn bench_orx_linked_list( - group: &mut BenchmarkGroup<'_, WallTime>, - data: &Vec, - n: &usize, - mem: MemoryUtilization, -) { - fn run(actions: &[Action], list: &mut orx_linked_list::LinkedList) -> u32 { +fn bench_orx_linked_list(group: &mut BenchmarkGroup<'_, WallTime>, data: &Vec, n: &usize) { + fn run(actions: &[Action], list: &mut List) -> u32 { let mut sum = 0; for action in actions { let x = match action { @@ -64,12 +59,12 @@ fn bench_orx_linked_list( } group.bench_with_input( - BenchmarkId::new(format!("orx_linked_list::LinkedList({:?})", mem), n), + BenchmarkId::new("orx_linked_list::LinkedList", n), n, |b, _| { b.iter(|| { - let mut list = orx_linked_list::LinkedList::new().with_memory_utilization(mem); - run(black_box(data), &mut list) + let mut list = List::new(); + run(black_box(data), black_box(&mut list)) }) }, ); @@ -120,16 +115,14 @@ fn std_vecdeque(actions: &[Action], list: &mut std::collections::VecDeque) } fn bench(c: &mut Criterion) { - // let treatments = vec![1_024, 1_024 * 4, 1_024 * 16]; - let treatments = vec![1_024 * 64]; + let treatments = vec![1_024, 1_024 * 16, 1_024 * 64, 1_024 * 64 * 4]; let mut group = c.benchmark_group("mutation_ends"); for n in &treatments { let data = get_test_data(*n); - bench_orx_linked_list(&mut group, &data, n, MemoryUtilization::Lazy); - bench_orx_linked_list(&mut group, &data, n, MemoryUtilization::WithThreshold(0.75)); + bench_orx_linked_list(&mut group, &data, n); group.bench_with_input( BenchmarkId::new("std::collections::LinkedList", n), @@ -137,17 +130,18 @@ fn bench(c: &mut Criterion) { |b, _| { b.iter(|| { let mut list = std::collections::LinkedList::new(); - std_linked_list(black_box(&data), &mut list) + std_linked_list(black_box(&data), black_box(&mut list)) }) }, ); + group.bench_with_input( BenchmarkId::new("std::collections::VecDeque", n), n, |b, _| { b.iter(|| { let mut list = std::collections::VecDeque::new(); - std_vecdeque(black_box(&data), &mut list) + std_vecdeque(black_box(&data), black_box(&mut list)) }) }, ); diff --git a/benches/results.xlsx b/benches/results.xlsx new file mode 100644 index 0000000..d58bfd6 Binary files /dev/null and b/benches/results.xlsx differ diff --git a/benches/results/grow.xlsx b/benches/results/grow.xlsx deleted file mode 100644 index 0ae890a..0000000 Binary files a/benches/results/grow.xlsx and /dev/null differ diff --git a/docs/img/bench_mutation_ends.PNG b/docs/img/bench_mutation_ends.PNG index 9c0b130..daa3e85 100644 Binary files a/docs/img/bench_mutation_ends.PNG and b/docs/img/bench_mutation_ends.PNG differ diff --git a/docs/img/iter.PNG b/docs/img/iter.PNG new file mode 100644 index 0000000..a16bdc9 Binary files /dev/null and b/docs/img/iter.PNG differ diff --git a/src/common_traits/clone.rs b/src/common_traits/clone.rs new file mode 100644 index 0000000..a10800d --- /dev/null +++ b/src/common_traits/clone.rs @@ -0,0 +1,74 @@ +use crate::{ + list::List, + variants::{ends::ListEnds, list_variant::ListVariant}, +}; + +impl<'a, V, T> Clone for List<'a, V, T> +where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + T: Clone, + Self: FromIterator, +{ + fn clone(&self) -> Self { + Self::from_iter(self.iter().cloned()) + } +} + +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn clone_empty() { + let list = DoublyLinkedList::::new(); + let clone = list.clone(); + assert!(clone.is_empty()); + + let list = SinglyLinkedList::::new(); + let clone = list.clone(); + assert!(clone.is_empty()); + } + + #[test] + fn clone_single() { + let mut list = DoublyLinkedList::::new(); + list.push_back('a'); + let clone = list.clone(); + + assert_eq!(1, clone.len()); + assert_eq!(Some(&'a'), clone.front()); + assert_eq!(Some(&'a'), clone.back()); + + let mut list = SinglyLinkedList::::new(); + list.push_front('a'); + let clone = list.clone(); + + assert_eq!(1, clone.len()); + assert_eq!(Some(&'a'), clone.front()); + assert_eq!(Some(&'a'), clone.back()); + } + + #[test] + fn clone_multi() { + let mut list = DoublyLinkedList::::new(); + list.push_back('a'); + list.push_back('b'); + list.push_back('c'); + let clone = list.clone(); + + assert_eq!(3, clone.len()); + assert_eq!(Some(&'a'), clone.front()); + assert_eq!(Some(&'c'), clone.back()); + + let mut list = SinglyLinkedList::::new(); + list.push_front('c'); + list.push_front('b'); + list.push_front('a'); + let clone = list.clone(); + + assert_eq!(3, clone.len()); + assert_eq!(Some(&'a'), clone.front()); + assert_eq!(Some(&'c'), clone.back()); + } +} diff --git a/src/common_traits/debug.rs b/src/common_traits/debug.rs new file mode 100644 index 0000000..6698401 --- /dev/null +++ b/src/common_traits/debug.rs @@ -0,0 +1,106 @@ +use crate::{ + list::List, + variants::{doubly::Doubly, singly::Singly}, +}; +use std::fmt::Debug; + +impl<'a, T> Debug for List<'a, Singly, T> +where + T: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SinglyLinkedList") + .field("len", &self.len()) + .field("front", &self.front()) + .field("forward", &self.iter().collect::>()) + .finish() + } +} + +impl<'a, T> Debug for List<'a, Doubly, T> +where + T: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DoublyLinkedList") + .field("len", &self.len()) + .field("front", &self.front()) + .field("back", &self.back()) + .field("forward", &self.iter().collect::>()) + .field("backward", &self.iter_from_back().collect::>()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn debug_singly_empty() { + let list: List = List::new(); + + assert_eq!( + format!("{:?}", &list), + "SinglyLinkedList { len: 0, front: None, forward: [] }" + ); + } + + #[test] + fn debug_singly_single() { + let mut list: List = List::new(); + list.push_front(42); + + assert_eq!( + format!("{:?}", &list), + "SinglyLinkedList { len: 1, front: Some(42), forward: [42] }" + ); + } + + #[test] + fn debug_singly_multi() { + let mut list: List = List::new(); + list.push_front(42); + list.push_front(1); + list.push_front(7); + + assert_eq!( + format!("{:?}", &list), + "SinglyLinkedList { len: 3, front: Some(7), forward: [7, 1, 42] }" + ); + } + + #[test] + fn debug_doubly_empty() { + let list: List = List::new(); + + assert_eq!( + format!("{:?}", &list), + "DoublyLinkedList { len: 0, front: None, back: None, forward: [], backward: [] }" + ); + } + + #[test] + fn debug_doubly_single() { + let mut list: List = List::new(); + list.push_front(42); + + assert_eq!( + format!("{:?}", &list), + "DoublyLinkedList { len: 1, front: Some(42), back: Some(42), forward: [42], backward: [42] }" + ); + } + + #[test] + fn debug_doubly_multi() { + let mut list: List = List::new(); + list.push_front(42); + list.push_front(1); + list.push_front(7); + + assert_eq!( + format!("{:?}", &list), + "DoublyLinkedList { len: 3, front: Some(7), back: Some(42), forward: [7, 1, 42], backward: [42, 1, 7] }" + ); + } +} diff --git a/src/common_traits/from_iter.rs b/src/common_traits/from_iter.rs new file mode 100644 index 0000000..c365691 --- /dev/null +++ b/src/common_traits/from_iter.rs @@ -0,0 +1,118 @@ +use crate::{Doubly, List, Singly}; +use orx_selfref_col::SelfRefCol; + +impl<'a, T> FromIterator for List<'a, Singly, T> { + fn from_iter>(iter: I) -> Self { + let mut col = SelfRefCol::from_iter(iter); + + col.move_mutate((), |x, _| { + x.set_ends([x.first_node(), x.last_node()]); + + let len = x.len(); + if len >= 2 { + let mut prev = x.first_node().expect("is-some"); + let mut current = x.get_node(1).expect("is-some"); + prev.set_next(&x, current); + for i in 2..len { + prev = current; + current = x.get_node(i).expect("is-some"); + prev.set_next(&x, current); + } + } + }); + + Self { col } + } +} + +impl<'a, T> FromIterator for List<'a, Doubly, T> { + fn from_iter>(iter: I) -> Self { + let mut col = SelfRefCol::from_iter(iter); + + col.move_mutate((), |x, _| { + x.set_ends([x.first_node(), x.last_node()]); + + let len = x.len(); + if len >= 2 { + let mut prev = x.first_node().expect("is-some"); + let mut current = x.get_node(1).expect("is-some"); + prev.set_next(&x, current); + current.set_prev(&x, prev); + for i in 2..len { + prev = current; + current = x.get_node(i).expect("is-some"); + prev.set_next(&x, current); + current.set_prev(&x, prev); + } + } + }); + + Self { col } + } +} + +#[cfg(test)] +mod tests { + use crate::*; + + use self::list::tests::{assert_empty_list, validate_both}; + + #[test] + fn empty() { + let vec: Vec = vec![]; + let singly = SinglyLinkedList::from_iter(vec.clone()); + let doubly = DoublyLinkedList::from_iter(vec); + + assert_empty_list(&singly); + assert_empty_list(&doubly); + validate_both(&singly, &doubly); + } + + #[test] + fn single() { + let vec: Vec = vec!['a']; + let singly = SinglyLinkedList::from_iter(vec.clone()); + let doubly = DoublyLinkedList::from_iter(vec.clone()); + + assert_eq!(Some(&'a'), singly.front()); + assert_eq!(Some(&'a'), singly.back()); + assert_eq!(Some(&'a'), doubly.front()); + assert_eq!(Some(&'a'), doubly.back()); + assert_eq!(&vec, singly.iter().copied().collect::>().as_slice()); + assert_eq!(&vec, doubly.iter().copied().collect::>().as_slice()); + + validate_both(&singly, &doubly); + } + + #[test] + fn double() { + let vec: Vec = vec!['a', 'b']; + let singly = SinglyLinkedList::from_iter(vec.clone()); + let doubly = DoublyLinkedList::from_iter(vec.clone()); + + assert_eq!(Some(&'a'), singly.front()); + assert_eq!(Some(&'b'), singly.back()); + assert_eq!(Some(&'a'), doubly.front()); + assert_eq!(Some(&'b'), doubly.back()); + assert_eq!(&vec, singly.iter().copied().collect::>().as_slice()); + assert_eq!(&vec, doubly.iter().copied().collect::>().as_slice()); + + validate_both(&singly, &doubly); + } + + #[test] + fn multiple() { + let vec: Vec = vec!['a', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'b']; + let singly = SinglyLinkedList::from_iter(vec.clone()); + let doubly = DoublyLinkedList::from_iter(vec.clone()); + + assert_eq!(Some(&'a'), singly.front()); + assert_eq!(Some(&'b'), singly.back()); + assert_eq!(Some(&'a'), doubly.front()); + assert_eq!(Some(&'b'), doubly.back()); + assert_eq!(&vec, singly.iter().copied().collect::>().as_slice()); + assert_eq!(&vec, doubly.iter().copied().collect::>().as_slice()); + + validate_both(&singly, &doubly); + } +} diff --git a/src/common_traits/mod.rs b/src/common_traits/mod.rs new file mode 100644 index 0000000..d08624c --- /dev/null +++ b/src/common_traits/mod.rs @@ -0,0 +1,4 @@ +mod clone; +mod debug; +mod from_iter; +mod validators; diff --git a/src/common_traits/validators.rs b/src/common_traits/validators.rs new file mode 100644 index 0000000..2d99fe9 --- /dev/null +++ b/src/common_traits/validators.rs @@ -0,0 +1,53 @@ +use crate::{ + list::List, + variants::{doubly::Doubly, ends::ListEnds, list_variant::ListVariant, singly::Singly}, +}; + +impl<'a, V, T> List<'a, V, T> +where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, +{ + #[cfg(test)] + fn validate_next(&self) { + use orx_selfref_col::NodeRefs; + let mut count = 0; + + let mut next = self.col.ends().front(); + while let Some(current) = next { + count += 1; + next = *current.next().get(); + } + + assert_eq!(count, self.len()); + } +} + +impl<'a, T> List<'a, Singly, T> { + #[cfg(test)] + pub(crate) fn validate_list(&self) { + self.validate_next(); + } +} + +impl<'a, T> List<'a, Doubly, T> { + #[cfg(test)] + pub(crate) fn validate_list(&self) { + self.validate_next(); + self.validate_prev(); + } + + #[cfg(test)] + fn validate_prev(&self) { + use orx_selfref_col::NodeRefs; + let mut count = 0; + + let mut prev = self.col.ends().back(); + while let Some(current) = prev { + count += 1; + prev = *current.prev().get(); + } + + assert_eq!(count, self.len(), "count & len mismatch"); + } +} diff --git a/src/eq.rs b/src/eq.rs deleted file mode 100644 index e18992c..0000000 --- a/src/eq.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::{linked_list_slice::LinkedListSlice, linked_list_view::LinkedListView, LinkedList}; -use std::ops::Deref; - -// view -impl<'a, T: PartialEq> PartialEq for LinkedListView<'a, T> { - fn eq(&self, other: &Self) -> bool { - match (self.len(), other.len()) { - (0, 0) => true, - (a, b) if a != b => false, - _ => { - std::ptr::eq( - self.front_node().expect("issome"), - other.front_node().expect("issome"), - ) && std::ptr::eq( - self.back_node().expect("issome"), - other.back_node().expect("issome"), - ) || self.iter().zip(other.iter()).all(|(x, y)| x == y) - } - } - } -} - -impl<'s, 'a, T: PartialEq> PartialEq> for LinkedListView<'a, T> { - fn eq(&self, other: &LinkedListSlice<'s, 'a, T>) -> bool { - let other = other.deref(); - self.eq(other.deref()) - } -} -impl<'a, T: PartialEq> PartialEq> for LinkedListView<'a, T> { - fn eq(&self, other: &LinkedList<'a, T>) -> bool { - let other = other.deref(); - self.eq(other.deref()) - } -} - -impl<'a, T: PartialEq> PartialEq<[T]> for LinkedListView<'a, T> { - fn eq(&self, other: &[T]) -> bool { - other.eq(self) - } -} - -// asref(slice) -impl<'a, T: PartialEq, S: AsRef<[T]>> PartialEq for LinkedListView<'a, T> { - fn eq(&self, other: &S) -> bool { - let other = other.as_ref(); - self.len() == other.len() && self.iter().zip(other.iter()).all(|(x, y)| x == y) - } -} - -impl<'s, 'a, T: PartialEq, S: AsRef<[T]>> PartialEq for LinkedListSlice<'s, 'a, T> { - fn eq(&self, other: &S) -> bool { - self.deref().eq(other) - } -} - -impl<'a, T: PartialEq, S: AsRef<[T]>> PartialEq for LinkedList<'a, T> { - fn eq(&self, other: &S) -> bool { - self.deref().eq(other) - } -} - -// slice -impl<'a, T: PartialEq> PartialEq> for [T] { - fn eq(&self, other: &LinkedListView<'a, T>) -> bool { - let slice = self; - other.len() == slice.len() && other.iter().zip(slice.iter()).all(|(x, y)| x == y) - } -} -impl<'s, 'a, T: PartialEq> PartialEq> for [T] { - fn eq(&self, other: &LinkedListSlice<'s, 'a, T>) -> bool { - self.eq(other.deref()) - } -} -impl<'a, T: PartialEq> PartialEq> for [T] { - fn eq(&self, other: &LinkedList<'a, T>) -> bool { - self.eq(other.deref()) - } -} - -// listslice -impl<'s, 'a, T: PartialEq> PartialEq for LinkedListSlice<'s, 'a, T> { - fn eq(&self, other: &LinkedListSlice<'s, 'a, T>) -> bool { - self.deref().eq(other.deref()) - } -} -impl<'s, 'a, T: PartialEq> PartialEq> for LinkedListSlice<'s, 'a, T> { - fn eq(&self, other: &LinkedListView<'a, T>) -> bool { - other.eq(self.deref()) - } -} -impl<'s, 'a, T: PartialEq> PartialEq> for LinkedListSlice<'s, 'a, T> { - fn eq(&self, other: &LinkedList<'a, T>) -> bool { - other.deref().eq(self.deref()) - } -} - -// list -impl<'a, T: PartialEq> PartialEq for LinkedList<'a, T> { - fn eq(&self, other: &LinkedList<'a, T>) -> bool { - self.deref().eq(other.deref()) - } -} -impl<'a, T: PartialEq> PartialEq> for LinkedList<'a, T> { - fn eq(&self, other: &LinkedListView<'a, T>) -> bool { - other.eq(self.deref()) - } -} -impl<'s, 'a, T: PartialEq> PartialEq> for LinkedList<'a, T> { - fn eq(&self, other: &LinkedListSlice<'s, 'a, T>) -> bool { - other.deref().eq(self.deref()) - } -} - -#[cfg(test)] -mod tests { - #![allow(clippy::unwrap_used)] - - use crate::LinkedList; - - #[test] - fn eq_other() { - let mut list1 = LinkedList::new(); - - assert_eq!(list1, LinkedList::new()); - - list1.push_back('a'); - list1.push_front('b'); - list1.push_front('c'); - list1.push_back('d'); - - assert_eq!(&list1, &['c', 'b', 'a', 'd']); - - assert_eq!(list1, list1); - assert_eq!(list1, list1.as_slice()); - assert_eq!(list1.as_slice(), list1); - assert_eq!(list1.as_slice(), list1.as_slice()); - - let (slice, _) = list1.split(4).unwrap(); - assert_eq!(slice, list1); - assert_eq!(list1, slice); - - let (_, slice) = list1.split(0).unwrap(); - assert_eq!(slice, list1); - assert_eq!(list1, slice); - - let (a, b) = list1.split(1).unwrap(); - assert_ne!(a, list1); - assert_ne!(b, list1); - assert_ne!(list1, a); - assert_ne!(list1, b); - - let mut list2 = LinkedList::new(); - list2.push_back('a'); - list2.push_front('b'); - list2.push_front('c'); - assert_ne!(list1, list2); - - list2.push_back('d'); - assert_eq!(list1, list2); - } - - #[test] - fn eq_asref() { - let mut list = LinkedList::new(); - - list.push_back('a'); - list.push_front('b'); - list.push_front('c'); - list.push_back('d'); - - assert_eq!(list, &['c', 'b', 'a', 'd']); - assert_eq!(list.as_slice(), &['c', 'b', 'a', 'd']); - - let (a, b) = list.split(1).expect("within bounds"); - assert_eq!(a, &['c']); - assert_eq!(b, &['b', 'a', 'd']); - } -} diff --git a/src/extend.rs b/src/extend.rs deleted file mode 100644 index 5fd246a..0000000 --- a/src/extend.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::LinkedList; - -impl<'a, T> Extend for LinkedList<'a, T> { - fn extend>(&mut self, iter: I) { - for x in iter { - self.push_back(x) - } - } -} - -impl<'a, 'b, T: Copy> Extend<&'b T> for LinkedList<'a, T> { - fn extend>(&mut self, iter: I) { - for x in iter { - self.push_back(*x) - } - } -} - -#[cfg(test)] -mod tests { - use crate::LinkedList; - - #[test] - fn extend_non_copy() { - let vec = vec!["a".to_string(), "b".to_string()]; - - let mut list = LinkedList::new(); - list.extend(vec.clone()); - - assert_eq!(&list, &vec); - - list.extend(vec.clone()); - let vec = vec![ - "a".to_string(), - "b".to_string(), - "a".to_string(), - "b".to_string(), - ]; - assert_eq!(&list, &vec); - } - - #[test] - fn extend_copy() { - let vec = vec![0, 1]; - - let mut list: LinkedList = LinkedList::new(); - list.extend(&vec); - - assert_eq!(&list, &vec); - - list.extend(3..5); - assert_eq!(&list, &[0, 1, 3, 4]); - } -} diff --git a/src/from_iter.rs b/src/from_iter.rs deleted file mode 100644 index e8fa6df..0000000 --- a/src/from_iter.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::LinkedList; - -impl<'a, T> FromIterator for LinkedList<'a, T> { - fn from_iter>(iter: I) -> Self { - let mut list = Self::new(); - for x in iter { - list.push_back(x); - } - list - } -} - -#[cfg(test)] -mod tests { - use crate::LinkedList; - - #[test] - fn collect() { - let list = LinkedList::::from_iter([0, 1, 2, 3, 4, 5].into_iter()); - assert_eq!(&list, &[0, 1, 2, 3, 4, 5]); - - let list: LinkedList<_> = (0..6).collect(); - assert_eq!(&list, &[0, 1, 2, 3, 4, 5]); - - let list: LinkedList<_> = (0..6).filter(|x| x % 2 == 0).collect(); - assert_eq!(&list, &[0, 2, 4]); - } - - #[test] - #[allow(clippy::unwrap_used)] - fn collect_from_slice() { - let list: LinkedList<_> = (0..6).collect(); - - let (a, b) = list.split(3).unwrap(); - - assert_eq!(a, &[0, 1, 2]); - assert_eq!(b, &[3, 4, 5]); - - let list_a: LinkedList<_> = a.iter().copied().collect(); - let list_b: LinkedList<_> = b.iter().copied().collect(); - - assert_eq!(&list_a, &[0, 1, 2]); - assert_eq!(&list_b, &[3, 4, 5]); - } -} diff --git a/src/iter.rs b/src/iter.rs deleted file mode 100644 index 3aed8db..0000000 --- a/src/iter.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::node::Node; -use orx_imp_vec::prelude::{SelfRefNext, SelfRefPrev}; -use std::marker::PhantomData; - -/// Linked list iteration direction. -pub trait IterDirection<'a, T> { - /// Returns the next element of the given `node` wirth respect to the iteration direction. - fn next_of(node: &'a Node<'a, T>) -> Option<&'a Node<'a, T>>; -} - -/// Forward iteration from front to the back. -pub struct IterFromFront; -impl<'a, T> IterDirection<'a, T> for IterFromFront { - #[inline(always)] - fn next_of(node: &'a Node<'a, T>) -> Option<&'a Node<'a, T>> { - node.next() - } -} - -/// Backward iteration from back to the front. -pub struct IterFromBack; -impl<'a, T> IterDirection<'a, T> for IterFromBack { - #[inline(always)] - fn next_of(node: &'a Node<'a, T>) -> Option<&'a Node<'a, T>> { - node.prev() - } -} - -pub(crate) struct IterNodes<'a, T, Direction> -where - Direction: IterDirection<'a, T>, -{ - remaining: usize, - current: Option<&'a Node<'a, T>>, - phantom: PhantomData, -} -impl<'a, T, Direction> IterNodes<'a, T, Direction> -where - Direction: IterDirection<'a, T>, -{ - pub(crate) fn new(len: usize, current: Option<&'a Node<'a, T>>) -> Self { - Self { - remaining: len, - current, - phantom: Default::default(), - } - } -} -impl<'a, T, Direction> Iterator for IterNodes<'a, T, Direction> -where - Direction: IterDirection<'a, T>, -{ - type Item = &'a Node<'a, T>; - - #[inline(always)] - fn next(&mut self) -> Option { - if self.remaining > 0 { - self.current.map(|x| { - self.current = Direction::next_of(x); - self.remaining -= 1; - x - }) - } else { - None - } - } -} - -/// Linked list iterator with the given `Direction`. -pub struct Iter<'a, T, Direction>(IterNodes<'a, T, Direction>) -where - Direction: IterDirection<'a, T>; - -impl<'a, T, Direction> From> for Iter<'a, T, Direction> -where - Direction: IterDirection<'a, T>, -{ - fn from(value: IterNodes<'a, T, Direction>) -> Self { - Self(value) - } -} - -impl<'a, T, Direction> Iterator for Iter<'a, T, Direction> -where - Direction: IterDirection<'a, T>, -{ - type Item = &'a T; - - #[inline(always)] - fn next(&mut self) -> Option { - self.0.next().map(|x| x.data_unchecked()) - } -} diff --git a/src/iterators/from_back.rs b/src/iterators/from_back.rs new file mode 100644 index 0000000..e82ddb2 --- /dev/null +++ b/src/iterators/from_back.rs @@ -0,0 +1,143 @@ +use crate::variants::doubly::Doubly; +use orx_selfref_col::{Node, NodeRefs}; + +pub struct IterFromBack<'iter, 'a, T: 'a> { + current: Option<&'iter Node<'a, Doubly, T>>, + len: usize, +} + +impl<'iter, 'a, T> IterFromBack<'iter, 'a, T> { + pub(crate) fn new(len: usize, current: Option<&'iter Node<'a, Doubly, T>>) -> Self { + Self { current, len } + } +} + +impl<'iter, 'a, T> Iterator for IterFromBack<'iter, 'a, T> { + type Item = &'iter T; + + fn next(&mut self) -> Option { + if self.len > 0 { + self.len -= 1; + let current = unsafe { self.current.unwrap_unchecked() }; + let data = unsafe { current.data().unwrap_unchecked() }; + self.current = *current.prev().get(); + Some(data) + } else { + None + } + } +} + +impl<'iter, 'a, T> ExactSizeIterator for IterFromBack<'iter, 'a, T> { + fn len(&self) -> usize { + self.len + } +} + +impl<'iter, 'a, T> Clone for IterFromBack<'iter, 'a, T> { + fn clone(&self) -> Self { + Self { + current: self.current, + len: self.len, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + list::List, + variants::{doubly::Doubly, ends::ListEnds, list_variant::ListVariant}, + }; + + fn take_list<'a, V, T>(_: List<'a, V, T>) + where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + { + } + + fn take_list_as_ref<'a, V, T>(_: &List<'a, V, T>) + where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + { + } + + #[test] + fn iter_lifetime() { + let mut doubly: List = List::default(); + + for i in 0..100 { + doubly.push_front(99 - i); + } + + take_list_as_ref(&doubly); + + let mut iter_doubly = doubly.iter_from_back(); + for i in 0..100 { + assert_eq!(Some(&(99 - i)), iter_doubly.next()); + } + + take_list_as_ref(&doubly); + + take_list(doubly); + } + + #[test] + fn iter() { + let mut doubly: List = List::default(); + + for i in 0..100 { + doubly.push_front(99 - i); + } + + let mut iter_doubly = doubly.iter_from_back(); + for i in 0..100 { + assert_eq!(Some(&(99 - i)), iter_doubly.next()); + } + + assert!(iter_doubly.next().is_none()); + assert!(iter_doubly.next().is_none()); + } + + #[test] + fn len() { + let mut doubly: List = List::default(); + + for i in 0..100 { + doubly.push_front(99 - i); + } + + let mut iter_doubly = doubly.iter_from_back(); + for i in 0..100 { + assert_eq!(100 - i, iter_doubly.len()); + + iter_doubly.next(); + } + + assert_eq!(0, iter_doubly.len()); + } + + #[test] + fn clone() { + let mut doubly: List = List::default(); + + for i in 0..100 { + doubly.push_front(99 - i); + } + + let mut iter_doubly = doubly.iter_from_back(); + for i in 0..50 { + assert_eq!(Some(&(99 - i)), iter_doubly.next()); + } + + let mut iter_doubly_2 = iter_doubly.clone(); + + for i in 50..100 { + assert_eq!(Some(&(99 - i)), iter_doubly_2.next()); + } + + assert!(iter_doubly_2.next().is_none()); + } +} diff --git a/src/iterators/from_front.rs b/src/iterators/from_front.rs new file mode 100644 index 0000000..989454b --- /dev/null +++ b/src/iterators/from_front.rs @@ -0,0 +1,190 @@ +use crate::variants::list_variant::ListVariant; +use orx_selfref_col::{Node, NodeRefSingle, NodeRefs}; + +pub struct IterFromFront<'iter, 'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Next = NodeRefSingle<'a, V, T>>, +{ + current: Option<&'iter Node<'a, V, T>>, + len: usize, +} + +impl<'iter, 'a, V, T> IterFromFront<'iter, 'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Next = NodeRefSingle<'a, V, T>>, +{ + pub(crate) fn new(len: usize, current: Option<&'iter Node<'a, V, T>>) -> Self { + Self { current, len } + } +} + +impl<'iter, 'a, V, T> Iterator for IterFromFront<'iter, 'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Next = NodeRefSingle<'a, V, T>>, +{ + type Item = &'iter T; + + fn next(&mut self) -> Option { + if self.len > 0 { + self.len -= 1; + let current = unsafe { self.current.unwrap_unchecked() }; + let data = unsafe { current.data().unwrap_unchecked() }; + self.current = *current.next().get(); + Some(data) + } else { + None + } + } +} + +impl<'iter, 'a, V, T> ExactSizeIterator for IterFromFront<'iter, 'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Next = NodeRefSingle<'a, V, T>>, +{ + fn len(&self) -> usize { + self.len + } +} + +impl<'iter, 'a, V, T> Clone for IterFromFront<'iter, 'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Next = NodeRefSingle<'a, V, T>>, +{ + fn clone(&self) -> Self { + Self { + current: self.current, + len: self.len, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + list::List, + variants::{doubly::Doubly, ends::ListEnds, list_variant::ListVariant, singly::Singly}, + }; + + fn take_list<'a, V, T>(_: List<'a, V, T>) + where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + { + } + + fn take_list_as_ref<'a, V, T>(_: &List<'a, V, T>) + where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + { + } + + #[test] + fn iter_lifetime() { + let mut singly: List = List::default(); + let mut doubly: List = List::default(); + + for i in 0..100 { + singly.push_front(99 - i); + doubly.push_front(99 - i); + } + + take_list_as_ref(&singly); + take_list_as_ref(&doubly); + + let mut iter_singly = singly.iter(); + let mut iter_doubly = doubly.iter(); + for i in 0..100 { + assert_eq!(Some(&i), iter_singly.next()); + assert_eq!(Some(&i), iter_doubly.next()); + } + + take_list_as_ref(&singly); + take_list_as_ref(&doubly); + + take_list(singly); + take_list(doubly); + } + + #[test] + fn iter() { + let mut singly: List = List::default(); + let mut doubly: List = List::default(); + + for i in 0..100 { + singly.push_front(99 - i); + doubly.push_front(99 - i); + } + + let mut iter_singly = singly.iter(); + let mut iter_doubly = doubly.iter(); + for i in 0..100 { + assert_eq!(Some(&i), iter_singly.next()); + assert_eq!(Some(&i), iter_doubly.next()); + } + + assert!(iter_singly.next().is_none()); + assert!(iter_singly.next().is_none()); + + assert!(iter_doubly.next().is_none()); + assert!(iter_doubly.next().is_none()); + } + + #[test] + fn len() { + let mut singly: List = List::default(); + let mut doubly: List = List::default(); + + for i in 0..100 { + singly.push_front(99 - i); + doubly.push_front(99 - i); + } + + let mut iter_singly = singly.iter(); + let mut iter_doubly = doubly.iter(); + for i in 0..100 { + assert_eq!(100 - i, iter_singly.len()); + assert_eq!(100 - i, iter_doubly.len()); + + iter_singly.next(); + iter_doubly.next(); + } + + assert_eq!(0, iter_singly.len()); + assert_eq!(0, iter_doubly.len()); + } + + #[test] + fn clone() { + let mut singly: List = List::default(); + let mut doubly: List = List::default(); + + for i in 0..100 { + singly.push_front(99 - i); + doubly.push_front(99 - i); + } + + let mut iter_singly = singly.iter(); + let mut iter_doubly = doubly.iter(); + for i in 0..50 { + assert_eq!(Some(&i), iter_singly.next()); + assert_eq!(Some(&i), iter_doubly.next()); + } + + let mut iter_singly_2 = iter_singly.clone(); + let mut iter_doubly_2 = iter_doubly.clone(); + + for i in 50..100 { + assert_eq!(Some(&i), iter_singly_2.next()); + assert_eq!(Some(&i), iter_doubly_2.next()); + } + + assert!(iter_singly_2.next().is_none()); + assert!(iter_doubly_2.next().is_none()); + } +} diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs new file mode 100644 index 0000000..3c3ff29 --- /dev/null +++ b/src/iterators/mod.rs @@ -0,0 +1,2 @@ +pub mod from_back; +pub mod from_front; diff --git a/src/lib.rs b/src/lib.rs index 01c15c8..5f32998 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,115 +1,97 @@ //! # orx-linked-list //! -//! An efficient doubly linked list using regular `&` references with a focus to avoid smart pointers and improve cache locality. +//! An efficient and recursive singly and doubly linked list implementation. //! -//! ## A. Motivation +//! ## Variants and Time Complexity of Methods //! -//! Self referential, often recursive, collections contain an important set of useful data structures including linked lists. However, building these structures with references `&` is not possible in safe rust. +//! * `type SinglyLinkedList<'a, T> = List<'a, Singly, T>;` +//! * `type DoublyLinkedList<'a, T> = List<'a, Doubly, T>;` //! -//! Alternatively, these collections can be built using reference counted smart pointers such as `std::rc::Rc` and independent heap allocations. However, independent heap allocations is a drawback as the elements do not live close to each other leading to poor cache locality. Further, reference counted pointers have a runtime overhead. This crate makes use of [`orx_imp_vec::ImpVec`](https://crates.io/crates/orx-imp-vec) as the underlying storage which specializes in building such collections. +//! ## Time Complexity of Methods //! -//! ### Standard LinkedList +//! | Method | Time Complexity | +//! | -------- | ------- | +//! | access to front and back of the list | **O(1)** | +//! | push to front and back (`Doubly` only) of the list | **O(1)** | +//! | pop from front and back (`Doubly` only) of the list | **O(1)** | +//! | insert at an arbitrary position | O(n) | +//! | remove from an arbitrary position | O(n) | +//! | append another list to the front or back of the list | **O(1)** | +//! | retain elements by a predicate | O(n) | +//! | retain and collect remove elements | O(n) | +//! | iteration forwards or backwards (only `Doubly`) | O(n) | //! -//! `std::collections::LinkedList` implementation avoids reference counted pointers and uses `NonNull` instead, most likely to avoid this overhead. However, this leads to a risky and difficult implementation that feels more low level than it should. You may see the implementation [here](https://doc.rust-lang.org/src/alloc/collections/linked_list.rs.html). The `unsafe` keyword is used more than 60 times in this file. These are usually related to reading from and writing to memory through raw pointers. //! -//! ***Motivation:*** We do not need to count references provided that all elements and inter-element references belong to the same owner or container. This is because all elements will be dropped at the same time together with their inter-element references when the container `ImpVec` is dropped. +//! ## Examples //! -//! ***Motivation:*** We should be able to define these structures without directly accessing memory through raw pointers. This is unnecessarily powerful and risky. Instead, unsafe code must be limited to methods which are specialized for and only allow defining required connections of self referential collections. -//! -//! ### orx_linked_list::LinkedList -//! -//! Linked list implementation in this crate uses an `ImpVec` as the underlying storage and makes use of its specialized methods. This brings the following advantages: -//! -//! * Allows for a higher level implementation without any use of raw pointers. -//! * Avoids smart pointers. -//! * Avoids almost completely accessing through integer indices. -//! * All nodes belong to the same `ImpVec` living close to each other. This allows for better cache locality. -//! * Full fetched doubly-linked-list implementation uses the `unsafe` keyword seven times, which are repeated uses of three methods: -//! * `ImpVec::push_get_ref` -//! * `ImpVec::move_get_ref` -//! * `ImpVec::unsafe_truncate` (*a deref method from [`PinnedVec`](https://crates.io/crates/orx-pinned-vec)*) -//! -//! Furthermore, this implementation is more performant than the standard library implementation, a likely indicator of better cache locality. You may below the benchmark results for a series of random push/pop mutations after pushing "number of elements" elements to the list. +//! ```rust +//! use orx_linked_list::*; //! -//! https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG +//! fn eq<'a, I: Iterator + Clone>(iter: I, slice: &[u32]) -> bool { +//! iter.clone().count() == slice.len() && iter.zip(slice.iter()).all(|(a, b)| a == b) +//! } //! -//! ## B. Features +//! let _list: List = List::new(); +//! let _list = SinglyLinkedList::::new(); +//! let _list: List = List::new(); +//! let _list = DoublyLinkedList::::new(); //! -//! `orx_linked_list::LinkedList` implementation provides standard linked list opeartions such as constant time insertions and removals. Further, it reflects the **recursive** nature of the data structure through so called `LinkedListSlice`s. The caller can move to the desired element of the linked list and get the rest of the list as a linked list slice; which is nothing but an immutable linked list. Furthermore, slices can simply be `collect`ed as an owned linked list. +//! let mut list = DoublyLinkedList::from_iter([3, 4, 5]); +//! assert_eq!(list.front(), Some(&3)); +//! assert_eq!(list.back(), Some(&5)); +//! assert!(eq(list.iter(), &[3, 4, 5])); +//! assert!(eq(list.iter_from_back(), &[5, 4, 3])); //! -//! ```rust -//! use orx_linked_list::*; +//! assert_eq!(list.pop_front(), Some(3)); +//! assert_eq!(list.pop_back(), Some(5)); //! -//! // BASIC -//! let mut list = LinkedList::new(); -//! list.extend(vec!['a', 'b', 'c']); +//! list.push_back(5); +//! list.push_front(3); +//! assert!(eq(list.iter(), &[3, 4, 5])); //! -//! assert_eq!(list.len(), 3); -//! assert!(list.contains(&'b')); -//! assert_eq!(list.index_of(&'c'), Some(2)); -//! assert_eq!(list.from_back_index_of(&'c'), Some(0)); +//! let other = DoublyLinkedList::from_iter([6, 7, 8, 9]); +//! list.append_back(other); +//! assert!(eq(list.iter(), &[3, 4, 5, 6, 7, 8, 9])); //! -//! list.push_back('d'); -//! assert_eq!(Some(&'d'), list.back()); +//! let other = DoublyLinkedList::from_iter([0, 1, 2]); +//! list.append_front(other); +//! assert!(eq(list.iter(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); //! -//! *list.get_at_mut(0).unwrap() = 'x'; +//! list.retain(&|x| x < &5); +//! assert!(eq(list.iter(), &[0, 1, 2, 3, 4])); //! -//! list.push_front('e'); -//! *list.front_mut().unwrap() = 'f'; +//! let mut odds = vec![]; +//! let mut collect_odds = |x| odds.push(x); +//! list.retain_collect(&|x| x % 2 == 0, &mut collect_odds); //! -//! _ = list.remove_at(1); -//! _ = list.pop_back(); -//! list.insert_at(0, 'x'); -//! list.clear(); -//! list.push_front('y'); -//! list.pop_front(); +//! assert!(eq(list.iter(), &[0, 2, 4])); +//! assert!(eq(odds.iter(), &[1, 3])); +//! ``` //! -//! // ITER -//! let list: LinkedList<_> = ['a', 'b', 'c', 'd', 'e'].into_iter().collect(); +//! ## Internal Features //! -//! let forward: Vec<_> = list.iter().copied().collect(); -//! assert_eq!(forward, &['a', 'b', 'c', 'd', 'e']); +//! `orx_linked_list::List` makes use of the safety guarantees and efficiency features of [orx-selfref-col::SelfRefCol](https://crates.io/crates/orx-selfref-col). +//! * `SelfRefCol` constructs its safety guarantees around the fact that all references will be among elements of the same collection. By preventing bringing in external references or leaking out references, it is safe to build the self referential collection with **regular `&` references**. +//! * With careful encapsulation, `SelfRefCol` prevents passing in external references to the list and leaking within list node references to outside. Once this is established, it provides methods to easily mutate inter list node references. These features allowed a very convenient implementation of the linked list in this crate with almost no use of the `unsafe` keyword, no read or writes through pointers and no access by indices. Compared to the `std::collections::LinkedList` implementation, it can be observed that `orx_linked_list::List` is a much **higher level implementation**. +//! * Furthermore, `orx_linked_list::List` is **significantly faster** than the standard linked list. One of the main reasons for this is the feature of `SelfRefCol` keeping all close to each other rather than at arbitrary locations in memory which leads to a better cache locality. //! -//! let backward: Vec<_> = list.iter_from_back().copied().collect(); -//! assert_eq!(backward, &['e', 'd', 'c', 'b', 'a']); +//! ## Benchmarks //! -//! // SPLITS -//! let (left, right) = list.split(2).unwrap(); -//! assert_eq!(left, &['a', 'b']); -//! assert_eq!(right, &['c', 'd', 'e']); -//! // left & right are also nothing but immutable linked lists -//! assert_eq!(right.front(), Some(&'c')); -//! assert_eq!(left.back(), Some(&'b')); +//! ### Mutation Ends //! -//! let (front, after) = list.split_front().unwrap(); -//! assert_eq!(front, &'a'); -//! assert_eq!(after, &['b', 'c', 'd', 'e']); +//! *You may see the benchmark at [benches/mutation_ends.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/mutation_ends.rs).* //! -//! let (before, back) = list.split_back().unwrap(); -//! assert_eq!(before, &['a', 'b', 'c', 'd']); -//! assert_eq!(back, &'e'); +//! This benchmark compares time performance of calls to `push_front`, `push_back`, `pop_front` and `pop_back` methods. //! -//! let (left, right) = list.split_before(&'d').unwrap(); -//! assert_eq!(left, &['a', 'b', 'c']); -//! assert_eq!(right, &['d', 'e']); +//! https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG //! -//! let (left, right) = list.split_after(&'d').unwrap(); -//! assert_eq!(left, &['a', 'b', 'c', 'd']); -//! assert_eq!(right, &['e']); +//! ### Iteration //! -//! // RECURSIVE SPLITS -//! let (left1, left2) = left.split(1).unwrap(); -//! assert_eq!(left1, &['a']); -//! assert_eq!(left2, &['b', 'c', 'd']); +//! *You may see the benchmark at [benches/iter.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/iter.rs).* //! -//! // SPLIT TO OWNED -//! let mut left_list = left.collect(); +//! This benchmark compares time performance of iteration through the `iter` method. //! -//! assert_eq!(left_list, &['a', 'b', 'c', 'd']); -//! _ = left_list.pop_front(); -//! _ = left_list.pop_back(); -//! assert_eq!(left_list, &['b', 'c']); -//! ``` +//! https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/iter.PNG //! //! ## License //! @@ -127,16 +109,11 @@ clippy::todo )] -mod eq; -mod extend; -mod from_iter; -mod iter; -mod linked_list; -mod linked_list_slice; -mod linked_list_view; -mod mem; -mod node; +mod common_traits; +mod iterators; +mod list; +mod option_utils; +mod variants; -pub use linked_list::LinkedList; -pub use linked_list_view::LinkedListView; -pub use mem::{MemoryStatus, MemoryUtilization}; +pub use list::{DoublyLinkedList, List, SinglyLinkedList}; +pub use variants::{doubly::Doubly, singly::Singly}; diff --git a/src/linked_list.rs b/src/linked_list.rs deleted file mode 100644 index 7bc40b6..0000000 --- a/src/linked_list.rs +++ /dev/null @@ -1,1287 +0,0 @@ -use crate::{ - linked_list_slice::LinkedListSlice, - linked_list_view::LinkedListView, - mem::{reclaim_memory, MemoryStatus, MemoryUtilization}, - node::Node, -}; -use orx_imp_vec::{ - prelude::{PinnedVec, SelfRefNext, SelfRefPrev}, - ImpVec, -}; -use std::{fmt::Debug, ops::Deref}; - -/// The LinkedList allows pushing and popping elements at either end in constant time. -pub struct LinkedList<'a, T> { - vec: ImpVec>, - slice: LinkedListView<'a, T>, - memory_utilization: MemoryUtilization, -} - -impl<'a, T> Deref for LinkedList<'a, T> { - type Target = LinkedListView<'a, T>; - fn deref(&self) -> &Self::Target { - &self.slice - } -} -impl<'a, T> Default for LinkedList<'a, T> { - fn default() -> Self { - Self { - vec: ImpVec::default(), - slice: Default::default(), - memory_utilization: Default::default(), - } - } -} -impl<'a, T: Debug> Debug for LinkedList<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LinkedList") - .field("slice", &self.slice) - .field("memory_utilization", &self.memory_utilization) - .finish() - } -} -impl<'a, T: Clone> Clone for LinkedList<'a, T> { - fn clone(&self) -> Self { - Self::from_iter(self.iter().cloned()).with_memory_utilization(self.memory_utilization) - } -} - -impl<'a, T> LinkedList<'a, T> { - /// Creates a new empty linked list. - pub fn new() -> Self { - Self::default() - } - - /// Converts the linked list into one with the given `memory_utilization`. - pub fn with_memory_utilization(self, memory_utilization: MemoryUtilization) -> Self { - Self { - vec: self.vec, - slice: self.slice, - memory_utilization, - } - } - - /// Returns the memory utilization settings of the list. - pub fn memory_utilization(&self) -> MemoryUtilization { - self.memory_utilization - } - - /// Returns a reference to the entire list as an immutable slice. - pub fn as_slice(&self) -> LinkedListSlice<'_, 'a, T> { - let view = LinkedListView::new( - self.slice.len(), - self.slice.front_node(), - self.slice.back_node(), - ); - LinkedListSlice::new(self, view) - } - - /// Provides a mutable reference to the back element, or None if the list is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert_eq!(list.back(), None); - /// - /// list.push_back(42); - /// assert_eq!(list.back(), Some(&42)); - /// - /// match list.back_mut() { - /// None => {}, - /// Some(x) => *x = 7, - /// } - /// assert_eq!(list.back(), Some(&7)); - /// ``` - pub fn back_mut(&mut self) -> Option<&mut T> { - self.back_node().map(|n| self.data_mut(n)) - } - - /// Provides a mutable reference to the front element, or None if the list is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert_eq!(list.front(), None); - /// - /// list.push_front(42); - /// assert_eq!(list.front(), Some(&42)); - /// - /// match list.front_mut() { - /// None => {}, - /// Some(x) => *x = 7, - /// } - /// assert_eq!(list.front(), Some(&7)); - /// ``` - pub fn front_mut(&mut self) -> Option<&mut T> { - self.front_node().map(|n| self.data_mut(n)) - } - - /// Returns a mutable reference to element at the `at` position starting from the `front`; - /// None when `at` is out of bounds. - /// - /// This operation requires *O*(*n*) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: a <-> b <-> c - /// list.push_back('b'); - /// list.push_front('a'); - /// list.push_back('c'); - /// - /// *list.get_at_mut(0).unwrap() = 'x'; - /// *list.get_at_mut(1).unwrap() = 'y'; - /// *list.get_at_mut(2).unwrap() = 'z'; - /// assert_eq!(None, list.get_at_mut(3)); - /// - /// assert_eq!(Some(&'x'), list.get_at(0)); - /// assert_eq!(Some(&'y'), list.get_at(1)); - /// assert_eq!(Some(&'z'), list.get_at(2)); - /// ``` - pub fn get_at_mut(&mut self, at: usize) -> Option<&mut T> { - self.get_node_at(at).map(|n| self.data_mut(n)) - } - - /// Appends an element to the back of a list. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// list.push_back('a'); - /// assert_eq!('a', *list.back().unwrap()); - /// - /// list.push_back('b'); - /// assert_eq!('b', *list.back().unwrap()); - /// - /// list.push_front('x'); - /// assert_eq!('b', *list.back().unwrap()); - /// ``` - pub fn push_back(&mut self, value: T) { - match self.back_node() { - None => self.push_first_node(value), - Some(old_back) => { - let node = Node::active(value, Some(old_back), None); - let back = unsafe { self.vec.push_get_ref(node) }; - self.vec.set_next(old_back, Some(back)); - self.slice = LinkedListView::new(self.len() + 1, self.front_node(), Some(back)); - } - } - } - - /// Appends an element to the back of a list. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// list.push_front('a'); - /// assert_eq!('a', *list.front().unwrap()); - /// - /// list.push_front('b'); - /// assert_eq!('b', *list.front().unwrap()); - /// - /// list.push_back('x'); - /// assert_eq!('b', *list.front().unwrap()); - /// ``` - pub fn push_front(&mut self, value: T) { - match self.front_node() { - None => self.push_first_node(value), - Some(old_front) => { - let node = Node::active(value, None, Some(old_front)); - let front = unsafe { self.vec.push_get_ref(node) }; - self.vec.set_prev(old_front, Some(front)); - self.slice = LinkedListView::new(self.len() + 1, Some(front), self.back_node()); - } - } - } - - /// Removes the last element from a list and returns it, or None if it is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: x <-> a <-> b <-> c - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_front('x'); - /// list.push_back('c'); - /// - /// assert_eq!(Some('c'), list.pop_back()); - /// assert_eq!(Some('b'), list.pop_back()); - /// assert_eq!(Some('a'), list.pop_back()); - /// assert_eq!(Some('x'), list.pop_back()); - /// assert_eq!(None, list.pop_back()); - /// assert_eq!(None, list.pop_front()); - /// ``` - pub fn pop_back(&mut self) -> Option { - match self.back_node() { - None => None, - Some(old_back) => { - let back = old_back.prev(); - self.slice = LinkedListView::new(self.len() - 1, back.and(self.front_node()), back); - Some(self.close_node(old_back)) - } - } - } - - /// Removes the last element from a list and returns it, or None if it is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: c <-> b <-> a <-> x - /// list.push_front('a'); - /// list.push_front('b'); - /// list.push_back('x'); - /// list.push_front('c'); - /// - /// assert_eq!(Some('c'), list.pop_front()); - /// assert_eq!(Some('b'), list.pop_front()); - /// assert_eq!(Some('a'), list.pop_front()); - /// assert_eq!(Some('x'), list.pop_front()); - /// assert_eq!(None, list.pop_front()); - /// assert_eq!(None, list.pop_back()); - /// ``` - pub fn pop_front(&mut self) -> Option { - match self.front_node() { - None => None, - Some(old_front) => { - let front = old_front.next(); - self.slice = - LinkedListView::new(self.len() - 1, front, front.and(self.back_node())); - Some(self.close_node(old_front)) - } - } - } - - /// Removes all elements from the LinkedList. - /// - /// This operation should compute in O(n) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: x <-> a <-> b <-> c - /// list.push_front('a'); - /// list.push_front('b'); - /// list.push_back('x'); - /// list.push_front('c'); - /// - /// assert_eq!(4, list.len()); - /// assert_eq!(Some(&'c'), list.front()); - /// assert_eq!(Some(&'x'), list.back()); - /// - /// list.clear(); - /// - /// assert!(list.is_empty()); - /// assert_eq!(None, list.front()); - /// assert_eq!(None, list.back()); - /// ``` - pub fn clear(&mut self) { - self.vec.clear(); - self.slice = LinkedListView::empty(); - } - - /// Removes the element at the given index and returns it; returns None if `at` is out of bounds. - /// - /// This operation requires *O*(*n*) time to access the `at`-th element and constant time to remove. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: x <-> a <-> b <-> c - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_front('x'); - /// list.push_back('c'); - /// - /// assert_eq!(list.remove_at(1), Some('a')); - /// assert_eq!(list.remove_at(0), Some('x')); - /// assert_eq!(list.remove_at(1), Some('c')); - /// assert_eq!(list.remove_at(0), Some('b')); - /// ``` - pub fn remove_at(&mut self, at: usize) -> Option { - self.get_node_at(at).map(|node| { - let (front, back) = match (at, self.len()) { - (_, 1) => (None, None), - (0, _) => (node.next(), self.back_node()), - (at, _) if at == self.len() - 1 => (self.front_node(), node.prev()), - _ => (self.front_node(), self.back_node()), - }; - self.slice = LinkedListView::new(self.len() - 1, front, back); - self.close_node(node) - }) - } - - /// Inserts the element at the given position. - /// - /// This operation requires *O*(*n*) time to access the `at`-th element and constant time to insert. - /// - /// # Panics - /// Panics if at > len - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: a <-> b <-> c - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// - /// list.insert_at(1, 'w'); - /// assert_eq!(vec!['a', 'w', 'b', 'c'], list.iter().copied().collect::>()); - /// ``` - pub fn insert_at(&mut self, at: usize, value: T) { - match at { - 0 => self.push_front(value), - at if at == self.len() => self.push_back(value), - at => { - let next = self.get_node_at(at).expect("out-of-bound!"); - let prev = next.prev().expect("issome since at is not 0 or n-1"); - let node = Node::active(value, Some(prev), Some(next)); - let noderef = unsafe { self.vec.push_get_ref(node) }; - self.vec.set_prev(next, Some(noderef)); - self.vec.set_next(prev, Some(noderef)); - self.slice = - LinkedListView::new(self.len() + 1, self.front_node(), self.back_node()); - } - } - } - - // mem - /// Returns the memory utilization status of the linked list's underlying storage. - pub fn memory_status(&self) -> MemoryStatus { - MemoryStatus::of_list(self.len(), self.vec.len()) - } - - /// This method reclaims the gaps which are opened due to lazy pops and removals, - /// and brings back `memory_status` to 100% in *O(n)* time complexity. - /// - /// Memory can be reclaimed by calling this method manually. - /// On the other hand, `memory_utilization` settings of the list automatically calls this method with different frequencies: - /// - /// * `MemoryUtilization::WithThreshold(x)` => calls whenever the utilization falls lower than x => recommended setting with x ~= 0.7. - /// * `MemoryUtilization::Lazy` => never calls => memory utilization might be low if there are many pops & removals. - /// * `MemoryUtilization::Eager` => calls after every pop & removal => keeps utilization at 100% but leads to linear time pops & removals which significantly slows down the process. - pub fn reclaim_memory(&mut self) { - reclaim_memory(self, self.vec.len()); - } - - // splits - /// Splits the linked list into two slices from the element at the given index. - /// Returns None if `at > self.len()`. - /// - /// This operation should compute in O(n) time to locate the `at`-th element. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split(1).unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split(0).unwrap(); - /// assert!(left.is_empty()); - /// assert_eq!(right, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split(4).unwrap(); - /// assert_eq!(left, &['a', 'b', 'c', 'd']); - /// assert!(right.is_empty()); - /// - /// assert!(list.split(5).is_none()); - /// ``` - pub fn split( - &self, - at: usize, - ) -> Option<(LinkedListSlice<'_, 'a, T>, LinkedListSlice<'_, 'a, T>)> { - self.as_slice().split(at) - } - - /// Splits the linked list into the `front` and the remaining elements. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (front, rest) = list.split_front().unwrap(); - /// - /// assert_eq!(front, &'a'); - /// assert_eq!(rest, &['b', 'c', 'd']); - /// ``` - pub fn split_front(&self) -> Option<(&T, LinkedListSlice<'_, 'a, T>)> { - self.view_split_front() - .map(|(x, y)| (x, self.as_slice().new_with_view(y))) - } - - /// Splits the linked list into elements until back and back. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (rest, back) = list.split_back().unwrap(); - /// - /// assert_eq!(back, &'d'); - /// assert_eq!(rest, &['a', 'b', 'c']); - /// ``` - pub fn split_back(&self) -> Option<(LinkedListSlice<'_, 'a, T>, &T)> { - self.view_split_back() - .map(|(x, y)| (self.as_slice().new_with_view(x), y)) - } - - // helpers - fn push_first_node(&mut self, value: T) { - debug_assert!(self.is_empty()); - - let node = unsafe { self.vec.push_get_ref(Node::active(value, None, None)) }; - self.slice = LinkedListView::new(1, Some(node), Some(node)); - } - fn data_mut(&mut self, node: &Node<'a, T>) -> &mut T { - let idx = self - .vec - .index_of(node) - .expect("issome -> node exists idx must exist"); - self.vec[idx] - .data_mut() - .expect("issome -> node exists, cannot be a vacant node") - } - - fn close_node(&mut self, node: &Node<'a, T>) -> T { - if let Some(prev) = node.prev() { - self.vec.set_next(prev, node.next()); - } - - if let Some(next) = node.next() { - self.vec.set_prev(next, node.prev()); - } - - let node_idx = self.vec.index_of(node).expect("issome"); - let data: Option = self - .vec - .replace_at(node_idx, Node::closed()) - .expect("issome node exists") - .into(); - - self.memory_utilization.reclaim(self, self.vec.len()); - - data.expect("issome data exists") - } - - // helpers - mem - pub(crate) fn is_vacant(&self, idx: usize) -> bool { - self.vec[idx].is_closed() - } - pub(crate) fn move_to_vacant_node(&mut self, occupied_idx: usize, vacant_idx: usize) { - debug_assert!(!self.is_vacant(occupied_idx)); - debug_assert!(self.is_vacant(vacant_idx)); - - let (_, node) = unsafe { - self.vec - .move_get_ref(occupied_idx, vacant_idx, Node::closed()) - } - .expect("issome"); - - if let Some(prev) = node.prev() { - self.vec.set_next(prev, Some(node)); - } else { - self.slice = LinkedListView::new(self.len(), Some(node), self.back_node()); - } - - if let Some(next) = node.next() { - self.vec.set_prev(next, Some(node)); - } else { - self.slice = LinkedListView::new(self.len(), self.front_node(), Some(node)) - } - } - pub(crate) fn truncate_vec(&mut self) { - debug_assert!(self.vec.iter().take(self.len()).all(|x| !x.is_closed())); - debug_assert!(self.vec.iter().skip(self.len()).all(|x| x.is_closed())); - - let len = self.len(); - unsafe { self.vec.unsafe_truncate(len) }; - } -} - -impl<'a, T: PartialEq> LinkedList<'a, T> { - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'a').unwrap(); - /// assert!(left.is_empty()); - /// assert_eq!(right, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'b').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c']); - /// assert_eq!(right, &['d']); - /// - /// assert!(list.split_before(&'x').is_none()); - /// ``` - pub fn split_before( - &self, - value: &T, - ) -> Option<(LinkedListSlice<'_, 'a, T>, LinkedListSlice<'_, 'a, T>)> { - self.as_slice().split_before(value) - } - - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'a').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'b').unwrap(); - /// assert_eq!(left, &['a', 'b']); - /// assert_eq!(right, &['c', 'd']); - /// - /// let (left, right) = list.split_after(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c', 'd']); - /// assert!(right.is_empty()); - /// - /// assert!(list.split_after(&'x').is_none()); - /// ``` - pub fn split_after( - &self, - value: &T, - ) -> Option<(LinkedListSlice<'_, 'a, T>, LinkedListSlice<'_, 'a, T>)> { - self.as_slice().split_after(value) - } -} - -#[cfg(test)] -pub(super) mod tests { - #![allow(clippy::unwrap_used)] - - use super::*; - use crate::linked_list_view::tests::validate_full_slice; - type List<'a, T> = LinkedList<'a, T>; - - pub(crate) fn test_with_all_memory_utilization<'a, T, A>(mut assert: A) - where - T: 'a, - A: FnMut(List<'a, T>) + 'a, - { - assert(List::new()); - assert(List::default()); - assert(List::new().with_memory_utilization(MemoryUtilization::Eager)); - assert(List::new().with_memory_utilization(MemoryUtilization::Lazy)); - assert(List::new().with_memory_utilization(MemoryUtilization::WithThreshold(0.75))); - } - pub(crate) fn storage_to_datavec(list: &List) -> Vec> { - list.vec.iter().map(|x| x.data().cloned()).collect() - } - pub(crate) fn to_vec(list: &List) -> Vec { - list.iter().cloned().collect() - } - fn to_vec_from_back(list: &List) -> Vec { - list.iter_from_back().cloned().collect() - } - pub(crate) fn nodes<'a, 'b, T>(list: &'b List<'a, T>) -> &'b ImpVec> - where - 'a: 'b, - { - &list.vec - } - - #[test] - fn new() { - fn test(mut list: List) { - validate_full_slice(&list, &list.vec); - assert!(list.get_at_mut(0).is_none()); - - assert!(list.pop_back().is_none()); - assert!(list.pop_front().is_none()); - assert!(list.remove_at(0).is_none()); - } - - test_with_all_memory_utilization(test); - - let mut list = List::new(); - list.push_back('a'); - list.push_front('b'); - list.clear(); - test(list); - } - - #[test] - fn clone() { - let mut list = LinkedList::from_iter(['a', 'b', 'c'].into_iter()); - let clone = list.clone(); - - validate_full_slice(&list, &list.vec); - validate_full_slice(&clone, &clone.vec); - assert_eq!(list, &['a', 'b', 'c']); - assert_eq!(clone, &['a', 'b', 'c']); - assert_eq!(list, clone); - - _ = list.pop_back(); - - validate_full_slice(&list, &list.vec); - validate_full_slice(&clone, &clone.vec); - assert_eq!(list, &['a', 'b']); - assert_eq!(clone, &['a', 'b', 'c']); - assert_ne!(list, clone); - - list.clear(); - list.push_back('d'); - - validate_full_slice(&list, &list.vec); - validate_full_slice(&clone, &clone.vec); - assert_eq!(list, &['d']); - assert_eq!(clone, &['a', 'b', 'c']); - assert_ne!(list, clone); - - drop(list); - - validate_full_slice(&clone, &clone.vec); - assert_eq!(clone, &['a', 'b', 'c']); - } - - #[test] - fn with_utilization() { - let list: List = List::default(); - assert_eq!(list.memory_utilization(), MemoryUtilization::default()); - - let list: List = List::new(); - assert_eq!(list.memory_utilization(), MemoryUtilization::default()); - - let list: List = List::new().with_memory_utilization(MemoryUtilization::Eager); - assert_eq!(list.memory_utilization(), MemoryUtilization::Eager); - - let list: List = List::new().with_memory_utilization(MemoryUtilization::Lazy); - assert_eq!(list.memory_utilization(), MemoryUtilization::Lazy); - - let list: List = - List::new().with_memory_utilization(MemoryUtilization::WithThreshold(0.24)); - assert_eq!( - list.memory_utilization(), - MemoryUtilization::WithThreshold(0.24) - ); - } - - #[test] - fn front_back_mut() { - fn test(mut list: List) { - assert!(list.front_mut().is_none()); - assert!(list.back_mut().is_none()); - - list.push_back('a'); - validate_full_slice(&list, &list.vec); - - *list.front_mut().unwrap() = 'x'; - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'x'), list.front()); - assert_eq!(Some(&'x'), list.back()); - - *list.back_mut().unwrap() = 'y'; - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'y'), list.front()); - assert_eq!(Some(&'y'), list.back()); - - list.push_front('x'); - validate_full_slice(&list, &list.vec); - - *list.front_mut().unwrap() = 'a'; - *list.back_mut().unwrap() = 'b'; - - assert_eq!(Some(&'a'), list.front()); - assert_eq!(Some(&'b'), list.back()); - - validate_full_slice(&list, &list.vec); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn get_at_mut() { - fn test(mut list: List) { - assert!(list.get_at_mut(0).is_none()); - - list.push_back('a'); - - *list.get_at_mut(0).unwrap() = 'x'; - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'x'), list.front()); - assert_eq!(Some(&'x'), list.back()); - assert_eq!(Some(&'x'), list.get_at(0)); - - list.push_front('y'); - - *list.get_at_mut(0).unwrap() = 'a'; - *list.get_at_mut(1).unwrap() = 'b'; - validate_full_slice(&list, &list.vec); - - assert_eq!(Some(&'a'), list.front()); - assert_eq!(Some(&'b'), list.back()); - assert_eq!(Some(&'a'), list.get_at(0)); - assert_eq!(Some(&'b'), list.get_at(1)); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn push_back() { - fn test(mut list: List) { - list.push_back('a'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'a'), list.back()); - assert_eq!(Some(&'a'), list.front()); - - list.push_back('b'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'b'), list.back()); - assert_eq!( - &'a', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!(Some(&'a'), list.front()); - assert_eq!( - &'b', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - - list.push_back('c'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'c'), list.back()); - assert_eq!( - &'b', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!( - &'a', - list.back_node() - .unwrap() - .prev() - .unwrap() - .prev() - .unwrap() - .data_unchecked() - ); - assert_eq!(Some(&'a'), list.front()); - assert_eq!( - &'b', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - assert_eq!( - &'c', - list.front_node() - .unwrap() - .next() - .unwrap() - .next() - .unwrap() - .data_unchecked() - ); - - assert_eq!( - vec!['a', 'b', 'c'], - list.iter().copied().collect::>() - ); - - assert_eq!( - vec!['c', 'b', 'a'], - list.iter_from_back().copied().collect::>() - ); - } - test_with_all_memory_utilization(test); - } - - #[test] - fn push_front() { - fn test(mut list: List) { - list.push_front('a'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'a'), list.back()); - assert_eq!(Some(&'a'), list.front()); - - list.push_front('b'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'a'), list.back()); - assert_eq!( - &'b', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!(Some(&'b'), list.front()); - assert_eq!( - &'a', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - - list.push_front('c'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'a'), list.back()); - assert_eq!( - &'b', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!( - &'c', - list.back_node() - .unwrap() - .prev() - .unwrap() - .prev() - .unwrap() - .data_unchecked() - ); - assert_eq!(Some(&'c'), list.front()); - assert_eq!( - &'b', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - assert_eq!( - &'a', - list.front_node() - .unwrap() - .next() - .unwrap() - .next() - .unwrap() - .data_unchecked() - ); - - assert_eq!(vec!['c', 'b', 'a'], to_vec(&list)); - assert_eq!(vec!['a', 'b', 'c'], to_vec_from_back(&list)); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn push_back_front() { - fn test(mut list: List) { - list.push_back('a'); - validate_full_slice(&list, &list.vec); - - list.push_front('b'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'a'), list.back()); - assert_eq!( - &'b', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!(Some(&'b'), list.front()); - assert_eq!( - &'a', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - - list.push_back('c'); - validate_full_slice(&list, &list.vec); - assert_eq!(Some(&'c'), list.back()); - assert_eq!( - &'a', - list.back_node().unwrap().prev().unwrap().data_unchecked() - ); - assert_eq!( - &'b', - list.back_node() - .unwrap() - .prev() - .unwrap() - .prev() - .unwrap() - .data_unchecked() - ); - assert_eq!(Some(&'b'), list.front()); - assert_eq!( - &'a', - list.front_node().unwrap().next().unwrap().data_unchecked() - ); - assert_eq!( - &'c', - list.front_node() - .unwrap() - .next() - .unwrap() - .next() - .unwrap() - .data_unchecked() - ); - - assert_eq!(vec!['b', 'a', 'c'], to_vec(&list)); - assert_eq!(vec!['c', 'a', 'b'], to_vec_from_back(&list)); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn pop_back() { - fn test(mut list: List) { - assert!(list.pop_back().is_none()); - - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - validate_full_slice(&list, &list.vec); - assert_eq!( - vec!['a', 'b', 'c'], - list.iter().copied().collect::>() - ); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('c'), popped); - assert_eq!(Some(&'b'), list.back()); - assert_eq!(Some(&'a'), list.front()); - assert_eq!(vec!['a', 'b'], to_vec(&list)); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('b'), popped); - assert_eq!(Some(&'a'), list.back()); - assert_eq!(Some(&'a'), list.front()); - assert_eq!(vec!['a'], to_vec(&list)); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('a'), popped); - assert_eq!(0, list.iter().count()); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert!(popped.is_none()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn pop_front() { - fn test(mut list: List) { - assert!(list.pop_front().is_none()); - - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - validate_full_slice(&list, &list.vec); - assert_eq!( - vec!['a', 'b', 'c'], - list.iter().copied().collect::>() - ); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('a'), popped); - assert_eq!(Some(&'c'), list.back()); - assert_eq!(Some(&'b'), list.front()); - assert_eq!(vec!['b', 'c'], to_vec(&list)); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('b'), popped); - assert_eq!(Some(&'c'), list.back()); - assert_eq!(Some(&'c'), list.front()); - assert_eq!(vec!['c'], to_vec(&list)); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('c'), popped); - assert_eq!(0, list.iter().count()); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert!(popped.is_none()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn pop_back_front() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('c'), popped); - assert_eq!(Some(&'b'), list.back()); - assert_eq!(Some(&'a'), list.front()); - assert_eq!(vec!['a', 'b'], to_vec(&list)); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('a'), popped); - assert_eq!(Some(&'b'), list.back()); - assert_eq!(Some(&'b'), list.front()); - assert_eq!(vec!['b'], to_vec(&list)); - - let popped = list.pop_back(); - validate_full_slice(&list, &list.vec); - assert_eq!(Some('b'), popped); - assert_eq!(0, list.iter().count()); - - let popped = list.pop_front(); - validate_full_slice(&list, &list.vec); - assert!(popped.is_none()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn remove_at() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - list.push_back('d'); - list.push_back('e'); - - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert_eq!(Some('b'), list.remove_at(1)); - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert_eq!(Some('d'), list.remove_at(2)); - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert_eq!(Some('a'), list.remove_at(0)); - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert_eq!(Some('e'), list.remove_at(1)); - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert_eq!(Some('c'), list.remove_at(0)); - assert!(list.remove_at(list.len()).is_none()); - validate_full_slice(&list, &list.vec); - - assert!(list.is_empty()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn insert_at() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - list.insert_at(0, 'x'); - assert_eq!(vec!['x', 'a', 'b', 'c'], to_vec(&list)); - validate_full_slice(&list, &list.vec); - list.pop_front(); - - list.insert_at(3, 'x'); - assert_eq!(vec!['a', 'b', 'c', 'x'], to_vec(&list)); - validate_full_slice(&list, &list.vec); - list.pop_back(); - - list.insert_at(1, 'x'); - assert_eq!(vec!['a', 'x', 'b', 'c'], to_vec(&list)); - validate_full_slice(&list, &list.vec); - list.remove_at(1); - - list.insert_at(2, 'x'); - assert_eq!(vec!['a', 'b', 'x', 'c'], to_vec(&list)); - validate_full_slice(&list, &list.vec); - list.remove_at(2); - - assert_eq!(vec!['a', 'b', 'c'], to_vec(&list)); - } - - test_with_all_memory_utilization(test); - } - - #[test] - #[should_panic] - fn insert_at_out_of_bounds() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - list.insert_at(4, 'x'); - } - test_with_all_memory_utilization(test); - } - - #[test] - fn memory_status() { - #![allow(clippy::float_cmp)] - - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - assert_eq!(list.memory_status(), MemoryStatus::of_list(3, 3)); - assert_eq!(list.memory_status().utilization(), 1.0); - - list.pop_back(); - assert_eq!(list.memory_status(), MemoryStatus::of_list(2, 3)); - assert_eq!(list.memory_status().utilization(), 2.0 / 3.0); - - list.insert_at(1, 'c'); - assert_eq!(list.memory_status(), MemoryStatus::of_list(3, 4)); - assert_eq!(list.memory_status().utilization(), 3.0 / 4.0); - - list.pop_front(); - assert_eq!(list.memory_status(), MemoryStatus::of_list(2, 4)); - assert_eq!(list.memory_status().utilization(), 2.0 / 4.0); - - list.reclaim_memory(); - assert_eq!(list.memory_status(), MemoryStatus::of_list(2, 2)); - assert_eq!(list.memory_status().utilization(), 2.0 / 2.0); - } - - #[test] - fn as_slice() { - let mut list = LinkedList::new(); - list.push_back('a'); - list.push_back('b'); - - assert_eq!(list, &['a', 'b']); - assert_eq!(list.as_slice(), &['a', 'b']); - } -} diff --git a/src/linked_list_slice.rs b/src/linked_list_slice.rs deleted file mode 100644 index 81bcd71..0000000 --- a/src/linked_list_slice.rs +++ /dev/null @@ -1,247 +0,0 @@ -use crate::{LinkedList, LinkedListView}; -use std::{fmt::Debug, ops::Deref}; - -pub struct LinkedListSlice<'s, 'a, T> { - _list: &'s LinkedList<'a, T>, - view: LinkedListView<'a, T>, -} - -impl<'s, 'a, T> Deref for LinkedListSlice<'s, 'a, T> { - type Target = LinkedListView<'a, T>; - fn deref(&self) -> &Self::Target { - &self.view - } -} - -impl<'s, 'a, T> Clone for LinkedListSlice<'s, 'a, T> { - fn clone(&self) -> Self { - Self { - _list: self._list.clone(), - view: LinkedListView::new( - self.view.len(), - self.view.front_node(), - self.view.back_node(), - ), - } - } -} - -impl<'s, 'a, T: Debug> Debug for LinkedListSlice<'s, 'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LinkedListSlice") - .field("view", &self.view) - .finish() - } -} - -impl<'s, 'a, T> LinkedListSlice<'s, 'a, T> { - // helpers - pub(crate) fn new(list: &'s LinkedList<'a, T>, view: LinkedListView<'a, T>) -> Self { - Self { _list: list, view } - } - pub(crate) fn new_with_view(&self, view: LinkedListView<'a, T>) -> Self { - Self { - _list: self._list, - view, - } - } - - // splits - /// Splits the linked list into two slices from the element at the given index. - /// Returns None if `at > self.len()`. - /// - /// This operation should compute in O(n) time to locate the `at`-th element. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split(1).unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split(0).unwrap(); - /// assert!(left.is_empty()); - /// assert_eq!(right, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split(4).unwrap(); - /// assert_eq!(left, &['a', 'b', 'c', 'd']); - /// assert!(right.is_empty()); - /// - /// assert!(list.split(5).is_none()); - /// ``` - pub fn split( - &self, - at: usize, - ) -> Option<(LinkedListSlice<'s, 'a, T>, LinkedListSlice<'s, 'a, T>)> { - self.view_split(at) - .map(|(a, b)| (self.new_with_view(a), self.new_with_view(b))) - } - - /// Splits the linked list into the `front` and the remaining elements. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (front, rest) = list.split_front().unwrap(); - /// - /// assert_eq!(front, &'a'); - /// assert_eq!(rest, &['b', 'c', 'd']); - /// ``` - pub fn split_front(&self) -> Option<(&T, LinkedListSlice<'s, 'a, T>)> { - self.view_split_front() - .map(|(x, y)| (x, self.new_with_view(y))) - } - - /// Splits the linked list into elements until back and back. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (rest, back) = list.split_back().unwrap(); - /// - /// assert_eq!(back, &'d'); - /// assert_eq!(rest, &['a', 'b', 'c']); - /// ``` - pub fn split_back(&self) -> Option<(LinkedListSlice<'s, 'a, T>, &T)> { - self.view_split_back() - .map(|(x, y)| (self.new_with_view(x), y)) - } -} - -impl<'s, 'a, T: PartialEq> LinkedListSlice<'s, 'a, T> { - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'a').unwrap(); - /// assert!(left.is_empty()); - /// assert_eq!(right, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'b').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c']); - /// assert_eq!(right, &['d']); - /// - /// assert!(list.split_before(&'x').is_none()); - /// ``` - pub fn split_before( - &self, - value: &T, - ) -> Option<(LinkedListSlice<'s, 'a, T>, LinkedListSlice<'s, 'a, T>)> { - self.view_split_before(value) - .map(|(a, b)| (self.new_with_view(a), self.new_with_view(b))) - } - - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'a').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'b').unwrap(); - /// assert_eq!(left, &['a', 'b']); - /// assert_eq!(right, &['c', 'd']); - /// - /// let (left, right) = list.split_after(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c', 'd']); - /// assert!(right.is_empty()); - /// - /// assert!(list.split_after(&'x').is_none()); - /// ``` - pub fn split_after( - &self, - value: &T, - ) -> Option<(LinkedListSlice<'s, 'a, T>, LinkedListSlice<'s, 'a, T>)> { - self.view_split_after(value) - .map(|(a, b)| (self.new_with_view(a), self.new_with_view(b))) - } -} diff --git a/src/linked_list_view.rs b/src/linked_list_view.rs deleted file mode 100644 index af6a72a..0000000 --- a/src/linked_list_view.rs +++ /dev/null @@ -1,945 +0,0 @@ -use crate::{ - iter::{Iter, IterFromBack, IterFromFront, IterNodes}, - node::Node, - LinkedList, -}; -use orx_imp_vec::prelude::{SelfRefNext, SelfRefPrev}; -use std::{cmp::Ordering, fmt::Debug}; - -/// An immutable slice of a linked list. -/// -/// Note that this is not a slice in the `std::slice` sense in terms of the memory layout. -/// Nevertheless, it corresponds to a subset of elements of an original linked list, which itself is a linked list, -/// due to the recursiveness of the data structure. -pub struct LinkedListView<'a, T> { - len: usize, - front: Option<&'a Node<'a, T>>, - back: Option<&'a Node<'a, T>>, -} - -impl<'a, T> LinkedListView<'a, T> { - /// Creates a new empty linked list slice. - pub fn empty() -> Self { - Default::default() - } - - /// Returns the length of the linked list. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert_eq!(0, list.len()); - /// - /// list.push_back('a'); - /// list.push_front('b'); - /// assert_eq!(2, list.len()); - /// - /// _ = list.pop_back(); - /// assert_eq!(1, list.len()); - /// ``` - pub fn len(&self) -> usize { - self.len - } - - /// Returns true if the LinkedList is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert!(list.is_empty()); - /// - /// list.push_front('a'); - /// list.push_front('b'); - /// assert!(!list.is_empty()); - /// - /// _ = list.pop_back(); - /// assert!(!list.is_empty()); - /// - /// list.clear(); - /// assert!(list.is_empty()); - /// ``` - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - /// Provides a reference to the back element, or None if the list is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert_eq!(list.back(), None); - /// - /// list.push_back(42); - /// assert_eq!(list.back(), Some(&42)); - /// - /// list.push_front(1); - /// assert_eq!(list.back(), Some(&42)); - /// - /// list.push_back(7); - /// assert_eq!(list.back(), Some(&7)); - /// ``` - pub fn back(&self) -> Option<&T> { - self.back.map(|node| node.data_unchecked()) - } - - /// Provides a reference to the front element, or None if the list is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// assert_eq!(list.front(), None); - /// - /// list.push_front(42); - /// assert_eq!(list.front(), Some(&42)); - /// - /// list.push_back(1); - /// assert_eq!(list.front(), Some(&42)); - /// - /// list.push_front(7); - /// assert_eq!(list.front(), Some(&7)); - /// ``` - pub fn front(&self) -> Option<&T> { - self.front.map(|node| node.data_unchecked()) - } - - /// Provides a forward iterator; - /// which starts from the front-most element to the back. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// list.push_back(1); - /// list.push_back(2); - /// list.push_front(0); - /// list.push_back(3); - /// - /// let mut iter = list.iter(); - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), None); - /// ``` - pub fn iter(&self) -> Iter<'a, T, IterFromFront> { - self.iter_nodes().into() - } - - /// Provides a backward iterator; - /// which starts from the back-most element to the front. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// list.push_back(1); - /// list.push_back(2); - /// list.push_front(0); - /// list.push_back(3); - /// - /// let mut iter = list.iter_from_back(); - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), None); - /// ``` - pub fn iter_from_back(&self) -> Iter { - self.iter_nodes_from_back().into() - } - - /// Returns a reference to element at the `at` position starting from the `front`; - /// None when `at` is out of bounds. - /// - /// This operation requires *O*(*n*) time. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// // build linked list: a <-> b <-> c - /// list.push_back('b'); - /// list.push_front('a'); - /// list.push_back('c'); - /// - /// assert_eq!(Some(&'a'), list.get_at(0)); - /// assert_eq!(Some(&'b'), list.get_at(1)); - /// assert_eq!(Some(&'c'), list.get_at(2)); - /// assert_eq!(None, list.get_at(3)); - /// ``` - pub fn get_at(&self, at: usize) -> Option<&T> { - self.get_node_at(at).map(|x| x.data_unchecked()) - } - - /// Collects the `LinkedListSlice` into a new `LinkedList`. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let list: LinkedList<_> = (0..6).collect(); - /// assert_eq!(list, &[0, 1, 2, 3, 4, 5]); - /// - /// let (_, b) = list.split_after(&1).unwrap(); - /// assert_eq!(b, &[2, 3, 4, 5]); // b: LinkedListSlice is a view of the original list - /// - /// let mut new_list = b.collect(); // new_list: LinkedList is a new list built from b - /// assert_eq!(new_list, &[2, 3, 4, 5]); - /// - /// new_list.pop_back(); - /// assert_eq!(new_list, &[2, 3, 4]); - /// - /// assert_eq!(list, &[0, 1, 2, 3, 4, 5]); - /// assert_eq!(b, &[2, 3, 4, 5]); - /// ``` - pub fn collect(&self) -> LinkedList<'a, T> - where - T: Clone, - { - self.iter().cloned().collect() - } - - // helpers - fn iter_nodes(&self) -> IterNodes<'a, T, IterFromFront> { - IterNodes::new(self.len, self.front) - } - fn iter_nodes_from_back(&self) -> IterNodes<'a, T, IterFromBack> { - IterNodes::new(self.len, self.back) - } - fn split_from_node( - &self, - node_idx: usize, - node: &'a Node<'a, T>, - ) -> (LinkedListView<'a, T>, LinkedListView<'a, T>) { - let left = Self { - front: node.prev().and(self.front), - back: node.prev(), - len: node_idx, - }; - let right = Self { - front: Some(node), - back: self.back, - len: self.len - node_idx, - }; - (left, right) - } - - // helpers - crate - pub(crate) fn new( - len: usize, - front: Option<&'a Node<'a, T>>, - back: Option<&'a Node<'a, T>>, - ) -> Self { - Self { len, back, front } - } - pub(crate) fn back_node(&self) -> Option<&'a Node<'a, T>> { - self.back - } - pub(crate) fn front_node(&self) -> Option<&'a Node<'a, T>> { - self.front - } - pub(crate) fn get_node_at(&self, at: usize) -> Option<&'a Node<'a, T>> { - match at.cmp(&self.len) { - Ordering::Less => { - let at_from_back = self.len - at - 1; - match at_from_back.cmp(&at) { - Ordering::Less => self.iter_nodes_from_back().nth(at_from_back), - _ => self.iter_nodes().nth(at), - } - } - _ => None, - } - } - /// Splits the linked list into two slices from the element at the given index. - /// Returns None if `at > self.len()`. - /// - /// This operation should compute in O(n) time to locate the `at`-th element. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - pub(crate) fn view_split( - &self, - at: usize, - ) -> Option<(LinkedListView<'a, T>, LinkedListView<'a, T>)> { - if at == self.len { - Some((Self::new(self.len, self.front, self.back), Self::empty())) - } else { - self.get_node_at(at) - .map(|node| self.split_from_node(at, node)) - } - } - - /// Splits the linked list into the `front` and the remaining elements. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - pub(crate) fn view_split_front(&self) -> Option<(&T, LinkedListView<'a, T>)> { - self.view_split(1) - .map(|(x, y)| (x.front.expect("issome").data_unchecked(), y)) - } - - /// Splits the linked list into elements until back and back. - /// Returns None if `self.is_empty()`. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - pub(crate) fn view_split_back(&self) -> Option<(LinkedListView<'a, T>, &T)> { - if self.is_empty() { - None - } else { - self.view_split(self.len - 1) - .map(|(x, y)| (x, y.front.expect("issome").data_unchecked())) - } - } -} - -impl<'a, T: PartialEq> LinkedListView<'a, T> { - /// Returns whether or not the given `value` is in the list. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// assert!(!list.contains(&'a')); - /// - /// list.push_back('a'); - /// assert!(list.contains(&'a')); - /// ``` - pub fn contains(&self, value: &T) -> bool { - self.iter().any(|x| x == value) - } - - /// Returns the index of the given `value` from the front of the list if it exists; None otherwise. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// assert!(list.index_of(&'a').is_none()); - /// - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_front('c'); - /// list.push_front('d'); - /// assert_eq!(Some(2), list.index_of(&'a')); - /// ``` - pub fn index_of(&self, value: &T) -> Option { - self.index_and_node_of(value).map(|x| x.0) - } - - /// Returns the index of the given `value` from the back of the list if it exists; None otherwise. - /// - /// # Example - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// - /// assert!(list.from_back_index_of(&'a').is_none()); - /// - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_front('c'); - /// list.push_front('d'); - /// assert_eq!(Some(1), list.from_back_index_of(&'a')); - /// ``` - pub fn from_back_index_of(&self, value: &T) -> Option { - self.iter_from_back() - .enumerate() - .find(|x| x.1 == value) - .map(|x| x.0) - } - - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'a').unwrap(); - /// assert!(left.is_empty()); - /// assert_eq!(right, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'b').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_before(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c']); - /// assert_eq!(right, &['d']); - /// - /// assert!(list.split_before(&'x').is_none()); - /// ``` - pub(crate) fn view_split_before( - &self, - value: &T, - ) -> Option<(LinkedListView<'a, T>, LinkedListView<'a, T>)> { - self.index_and_node_of(value) - .map(|(node_idx, node)| self.split_from_node(node_idx, node)) - } - - /// Splits the linked list into two slices using the first from front element with the given `value` as the pivot: - /// - /// * left slice contains elements before the first appearance of the `value`, - /// * right slice contains elements containing the `value` and all elements afterwards. - /// - /// Returns None if the value does not exist in the list. - /// - /// This operation should compute in O(n) time to locate the element with the given `value`. - /// - /// Slices being only views on the linked list are cheap. - /// - /// Note that this method does **not** mutate the list; it rather returns two immutable views on two different parts of the list. - /// - /// # Examples - /// - /// ```rust - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new(); - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// assert_eq!(list, &['a', 'b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'a').unwrap(); - /// assert_eq!(left, &['a']); - /// assert_eq!(right, &['b', 'c', 'd']); - /// - /// let (left, right) = list.split_after(&'b').unwrap(); - /// assert_eq!(left, &['a', 'b']); - /// assert_eq!(right, &['c', 'd']); - /// - /// let (left, right) = list.split_after(&'d').unwrap(); - /// assert_eq!(left, &['a', 'b', 'c', 'd']); - /// assert!(right.is_empty()); - /// - /// assert!(list.split_after(&'x').is_none()); - /// ``` - pub(crate) fn view_split_after( - &self, - value: &T, - ) -> Option<(LinkedListView<'a, T>, LinkedListView<'a, T>)> { - self.index_and_node_of(value) - .map(|(prev_idx, prev)| match prev.next() { - None => (Self::new(self.len, self.front, self.back), Self::empty()), - Some(node) => self.split_from_node(prev_idx + 1, node), - }) - } - - // helpers - #[inline(always)] - fn index_and_node_of(&self, value: &T) -> Option<(usize, &'a Node<'a, T>)> { - self.iter_nodes() - .enumerate() - .find(|x| x.1.data_unchecked() == value) - } -} - -impl<'a, T> Default for LinkedListView<'a, T> { - fn default() -> Self { - Self { - len: 0, - back: None, - front: None, - } - } -} -impl<'a, T: Debug> Debug for LinkedListView<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LinkedListSlice") - .field("len", &self.len) - .field("front", &self.front.map(|x| x.data_unchecked())) - .field("back", &self.back.map(|x| x.data_unchecked())) - .finish() - } -} - -#[cfg(test)] -pub(super) mod tests { - #![allow(clippy::unwrap_used)] - - use super::*; - use crate::{ - linked_list::{ - tests::{nodes, test_with_all_memory_utilization}, - LinkedList, - }, - node::Node, - }; - use orx_imp_vec::{ - prelude::{PinnedVec, SelfRefNext}, - ImpVec, - }; - type Slice<'a, T> = LinkedListView<'a, T>; - type List<'a, T> = LinkedList<'a, T>; - - fn validate_ref_get_idx<'a, T>(nodes: &ImpVec>, node: &'a Node<'a, T>) -> usize { - nodes.index_of(node).expect("invalid node reference") - } - fn ref_equals<'a, T>( - nodes: &ImpVec>, - first: &'a Node<'a, T>, - second: &'a Node<'a, T>, - ) -> bool { - validate_ref_get_idx(nodes, first) == validate_ref_get_idx(nodes, second) - } - pub(crate) fn validate_full_slice<'a, T>(list: &Slice<'a, T>, nodes: &ImpVec>) { - assert_eq!( - list.len, - nodes.iter().filter(|x| !x.is_closed()).count(), - "list.len is not equal to number of active nodes" - ); - - let front_prev = list.front.and_then(|x| x.prev()); - assert!(front_prev.is_none(), "list.front.prev() is not none"); - - let back_next = list.back.and_then(|x| x.next()); - assert!(back_next.is_none(), "list.back.next() is not none"); - - validate_sub_slice(list, nodes); - } - fn validate_sub_slice<'a, T>(list: &Slice<'a, T>, nodes: &ImpVec>) { - if list.len == 0 { - // empty case - assert!(list.front.is_none(), "front.is_some() when list.is_empty()"); - assert!(list.back.is_none(), "back.is_some() when list.is_empty()"); - } else if list.len == 1 { - // singleton case - assert!(list.front.is_some(), "front.is_none() when len=1"); - assert!(list.back.is_some(), "back.is_none() when len=1"); - assert!( - ref_equals(nodes, list.front.unwrap(), list.back.unwrap()), - "front != back when len=1" - ); - } else { - // len >= 2 case - assert!(list.front.is_some(), "front.is_none() when len>=2"); - assert!(list.back.is_some(), "back.is_none() when len>=2"); - assert!( - !ref_equals(nodes, list.front.unwrap(), list.back.unwrap()), - "front=back when len >= 2" - ); - - // forward - let mut current = list.front.unwrap(); - for _ in 0..list.len - 1 { - let next = current.next().expect("missing next"); - let next_prev = next.prev().expect("missing prev"); - assert!(ref_equals(nodes, current, next_prev), "next-prev mismatch"); - current = next; - } - assert!( - ref_equals(nodes, current, list.back.unwrap()), - "failed to reach back in forward iteration" - ); - - // backward - let mut current = list.back.unwrap(); - for _ in 0..list.len - 1 { - let prev = current.prev().expect("missing prev"); - let prev_next = prev.next().expect("missing next"); - assert!(ref_equals(nodes, current, prev_next), "next-prev mismatch"); - current = prev; - } - assert!( - ref_equals(nodes, current, list.front.unwrap()), - "failed to reach front in backward iteration" - ); - } - } - - pub(crate) fn collect_vec(list: &Slice<'_, T>) -> Vec { - list.iter().cloned().collect() - } - - #[test] - fn empty() { - let slice: Slice<'_, char> = Slice::empty(); - - assert_eq!(0, slice.len()); - assert!(slice.is_empty()); - assert_eq!(None, slice.back()); - assert_eq!(None, slice.front()); - - assert_eq!(0, slice.iter().count()); - assert_eq!(0, slice.iter_from_back().count()); - - assert_eq!(None, slice.get_at(0)); - - let (left, right) = slice.view_split(0).expect("is-some"); - assert!(left.is_empty()); - assert!(right.is_empty()); - - assert!(slice.view_split(1).is_none()); - } - - #[test] - fn len_isempty() { - let node = Node::active('a', None, None); - - let slice = Slice::new(1, Some(&node), Some(&node)); - assert_eq!(1, slice.len()); - assert!(!slice.is_empty()); - - let slice: Slice = Slice::new(0, None, None); - assert_eq!(0, slice.len()); - assert!(slice.is_empty()); - - let slice: Slice = Slice::empty(); - assert_eq!(0, slice.len()); - assert!(slice.is_empty()); - } - - #[test] - fn back_front() { - let slice: Slice = Slice::empty(); - assert!(slice.back().is_none()); - assert!(slice.front().is_none()); - - let node1 = Node::active('a', None, None); - - let slice1 = Slice::new(1, Some(&node1), Some(&node1)); - assert_eq!(Some(&'a'), slice1.back_node().map(|x| x.data_unchecked())); - assert_eq!(Some(&'a'), slice1.back()); - assert_eq!(Some(&'a'), slice1.front_node().map(|x| x.data_unchecked())); - assert_eq!(Some(&'a'), slice1.front()); - - let node2 = Node::active('b', None, None); - - let slice2 = Slice::new(2, Some(&node1), Some(&node2)); - assert_eq!(Some(&'a'), slice2.front_node().map(|x| x.data_unchecked())); - assert_eq!(Some(&'a'), slice2.front()); - assert_eq!(Some(&'b'), slice2.back_node().map(|x| x.data_unchecked())); - assert_eq!(Some(&'b'), slice2.back()); - } - - #[test] - fn iter() { - let slice: Slice = Slice::empty(); - assert_eq!(0, slice.iter().count()); - assert_eq!(0, slice.iter_from_back().count()); - - let node = Node::active('a', None, None); - let slice = Slice::new(1, Some(&node), Some(&node)); - assert_eq!(1, slice.iter().count()); - assert_eq!(1, slice.iter_from_back().count()); - - let mut vec = vec!['a', 'b', 'c', 'd']; - - let node3 = Node::active(vec[3], None, None); - let node2 = Node::active(vec[2], None, Some(&node3)); - let node1 = Node::active(vec[1], None, Some(&node2)); - let node0 = Node::active(vec[0], None, Some(&node1)); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - let collect: Vec<_> = slice.iter().copied().collect(); - assert_eq!(&vec, &collect); - - let node0 = Node::active(vec[0], None, None); - let node1 = Node::active(vec[1], Some(&node0), None); - let node2 = Node::active(vec[2], Some(&node1), None); - let node3 = Node::active(vec[3], Some(&node2), None); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - let collect: Vec<_> = slice.iter_from_back().copied().collect(); - vec.reverse(); - assert_eq!(&vec, &collect); - } - - #[test] - fn get_at_trivial() { - let slice: Slice = Slice::empty(); - assert!(slice.get_node_at(0).is_none()); - assert!(slice.get_at(0).is_none()); - - let node = Node::active('a', None, None); - let slice = Slice::new(1, Some(&node), Some(&node)); - assert_eq!(Some(&'a'), slice.get_at(0)); - assert_eq!(Some(&'a'), slice.get_node_at(0).map(|x| x.data_unchecked())); - assert!(slice.get_node_at(1).is_none()); - assert!(slice.get_at(1).is_none()); - } - - #[test] - fn get_at_front() { - let vec = vec!['a', 'b', 'c', 'd']; - - let node3 = Node::active(vec[3], None, None); - let node2 = Node::active(vec[2], None, Some(&node3)); - let node1 = Node::active(vec[1], None, Some(&node2)); - let node0 = Node::active(vec[0], None, Some(&node1)); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - - assert_eq!(Some(&'a'), slice.get_at(0)); - assert_eq!(Some(&'b'), slice.get_at(1)); - } - - #[test] - #[should_panic] - fn get_at_front_not_used_when_farter() { - let vec = vec!['a', 'b', 'c', 'd']; - - let node3 = Node::active(vec[3], None, None); - let node2 = Node::active(vec[2], None, Some(&node3)); - let node1 = Node::active(vec[1], None, Some(&node2)); - let node0 = Node::active(vec[0], None, Some(&node1)); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - - assert_eq!(Some(&'c'), slice.get_at(2)); - } - - #[test] - fn get_at_back() { - let vec = vec!['a', 'b', 'c', 'd']; - - let node0 = Node::active(vec[0], None, None); - let node1 = Node::active(vec[1], Some(&node0), None); - let node2 = Node::active(vec[2], Some(&node1), None); - let node3 = Node::active(vec[3], Some(&node2), None); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - - assert_eq!(Some(&'c'), slice.get_at(2)); - assert_eq!(Some(&'d'), slice.get_at(3)); - } - - #[test] - #[should_panic] - fn get_at_back_not_used_when_farter() { - let vec = vec!['a', 'b', 'c', 'd']; - - let node0 = Node::active(vec[0], None, None); - let node1 = Node::active(vec[1], Some(&node0), None); - let node2 = Node::active(vec[2], Some(&node1), None); - let node3 = Node::active(vec[3], Some(&node2), None); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - - assert_eq!(Some(&'b'), slice.get_at(1)); - } - - #[test] - fn get_at_out_of_bounds() { - let vec = vec!['a', 'b', 'c', 'd']; - - let node3 = Node::active(vec[3], None, None); - let node2 = Node::active(vec[2], None, Some(&node3)); - let node1 = Node::active(vec[1], None, Some(&node2)); - let node0 = Node::active(vec[0], None, Some(&node1)); - let slice = Slice::new(vec.len(), Some(&node0), Some(&node3)); - - assert_eq!(None, slice.get_at(4)); - } - - #[test] - fn split() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - list.push_back('d'); - - let nodes = nodes(&list); - - let (a, b) = list.split(0).unwrap(); - validate_sub_slice(&a, nodes); - validate_sub_slice(&b, nodes); - assert_eq!(Vec::::new(), collect_vec(&a)); - assert_eq!(vec!['a', 'b', 'c', 'd'], collect_vec(&b)); - - let (a, b) = list.split(1).unwrap(); - validate_sub_slice(&a, nodes); - validate_sub_slice(&b, nodes); - assert_eq!(vec!['a'], collect_vec(&a)); - assert_eq!(vec!['b', 'c', 'd'], collect_vec(&b)); - - let (a, b) = list.split(2).unwrap(); - validate_sub_slice(&a, nodes); - validate_sub_slice(&b, nodes); - assert_eq!(vec!['a', 'b'], collect_vec(&a)); - assert_eq!(vec!['c', 'd'], collect_vec(&b)); - - let (a, b) = list.split(3).unwrap(); - validate_sub_slice(&a, nodes); - validate_sub_slice(&b, nodes); - assert_eq!(vec!['a', 'b', 'c'], collect_vec(&a)); - assert_eq!(vec!['d'], collect_vec(&b)); - - let (a, b) = list.split(4).unwrap(); - validate_sub_slice(&a, nodes); - validate_sub_slice(&b, nodes); - assert_eq!(vec!['a', 'b', 'c', 'd'], collect_vec(&a)); - assert_eq!(Vec::::new(), collect_vec(&b)); - - assert!(list.split(5).is_none()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn split_front_back() { - fn test(mut list: List) { - assert!(list.split_front().is_none()); - assert!(list.split_back().is_none()); - - list.push_back('a'); - - let (front, rest) = list.split_front().unwrap(); - assert_eq!(&'a', front); - assert!(rest.is_empty()); - - let (rest, back) = list.split_back().unwrap(); - assert!(rest.is_empty()); - assert_eq!(&'a', back); - - list.push_back('b'); - - let (front, rest) = list.split_front().unwrap(); - assert_eq!(front, &'a'); - assert_eq!(rest, &['b']); - - let (rest, back) = list.split_back().unwrap(); - assert_eq!(rest, &['a']); - assert_eq!(back, &'b'); - - list.push_front('x'); - list.push_back('y'); - - let (front, rest) = list.split_front().unwrap(); - assert_eq!(front, &'x'); - assert_eq!(rest, &['a', 'b', 'y']); - - let (rest, back) = list.split_back().unwrap(); - assert_eq!(rest, &['x', 'a', 'b']); - assert_eq!(back, &'y'); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn contains_index() { - fn test(mut list: List) { - let vec = vec!['a', 'b', 'c', 'd']; - - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - list.push_back('d'); - - for (i, x) in vec.iter().enumerate() { - assert!(list.contains(x)); - assert_eq!(Some(i), list.index_of(x)); - assert_eq!(Some(vec.len() - 1 - i), list.from_back_index_of(x)); - } - - assert!(!list.contains(&'e')); - assert!(list.index_of(&'e').is_none()); - assert!(list.from_back_index_of(&'e').is_none()); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn split_before_after_multiple_matches() { - fn test(mut list: List) { - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - list.push_back('d'); - list.push_back('e'); - list.push_back('c'); - list.push_back('g'); - - let (a, b) = list.view_split_after(&'c').unwrap(); - - assert_eq!(a, &['a', 'b', 'c']); - assert_eq!(b, &['d', 'e', 'c', 'g']); - - let (a, b) = list.view_split_before(&'c').unwrap(); - - assert_eq!(a, &['a', 'b']); - assert_eq!(b, &['c', 'd', 'e', 'c', 'g']); - } - - test_with_all_memory_utilization(test); - } - - #[test] - fn split_before_after() { - fn test(mut list: List) { - let vec: Vec<_> = (0..185).collect(); - for c in &vec { - list.push_back(*c); - } - - for (i, c) in vec.iter().enumerate() { - let (a, b) = list.view_split_after(c).unwrap(); - assert_eq!(a, &vec[0..=i]); - assert_eq!(b, &vec[(i + 1)..]); - - let (a, b) = list.view_split_before(c).unwrap(); - assert_eq!(a, &vec[0..i]); - assert_eq!(b, &vec[i..]); - } - } - - test_with_all_memory_utilization(test); - } -} diff --git a/src/list.rs b/src/list.rs new file mode 100644 index 0000000..484d2df --- /dev/null +++ b/src/list.rs @@ -0,0 +1,1937 @@ +use crate::{ + iterators::{from_back::IterFromBack, from_front::IterFromFront}, + option_utils::some_only_if, + variants::{doubly::Doubly, ends::ListEnds, list_variant::ListVariant, singly::Singly}, +}; +use orx_selfref_col::{Node, NodeRefs, SelfRefCol}; +use orx_split_vec::{Recursive, SplitVec}; + +/// A singly linked [`List`] allowing pushing and popping elements at the front in constant time. +pub type SinglyLinkedList<'a, T> = List<'a, Singly, T>; + +/// A doubly linked [`List`] allowing pushing and popping elements both at the front and the back in constant time. +pub type DoublyLinkedList<'a, T> = List<'a, Doubly, T>; + +type MutKey<'rf, 'a, V, T> = + orx_selfref_col::SelfRefColMut<'rf, 'a, V, T, SplitVec, Recursive>>; +type DoublyMutKey<'rf, 'a, T> = MutKey<'rf, 'a, Doubly, T>; +type SinglyMutKey<'rf, 'a, T> = MutKey<'rf, 'a, Singly, T>; + +/// Core structure for singly and doubly linked lists: +/// * `type SinglyLinkedList<'a, T> = List<'a, Singly, T>;` +/// * `type DoublyLinkedList<'a, T> = List<'a, Doubly, T>;` +/// +/// # Examples +/// +/// Below is the simple usage of a doubly linked list. +/// +/// ```rust +/// use orx_linked_list::*; +/// +/// // empty +/// let doubly = DoublyLinkedList::::new(); +/// let doubly = List::::new(); +/// +/// // from iter +/// let doubly: DoublyLinkedList<_> = [1, 2, 3].into_iter().collect(); +/// let mut doubly: List = [1, 2, 3].into_iter().collect(); +// +/// assert_eq!(Some(&1), doubly.front()); +/// assert_eq!(Some(&3), doubly.back()); +/// +/// doubly.push_front(0); +/// doubly.push_back(4); +/// +/// assert_eq!(Some(&0), doubly.front()); +/// assert_eq!(Some(&4), doubly.back()); +/// +/// assert_eq!(Some(0), doubly.pop_front()); +/// assert_eq!(Some(4), doubly.pop_back()); +/// +/// assert_eq!(vec![1, 2, 3], doubly.iter().copied().collect::>()); +/// assert_eq!(vec![3, 2, 1], doubly.iter_from_back().copied().collect::>()); +/// ```` +/// +/// Using a singly linked list can be used instead by using the `SinglyLinkedList` type alias or changing the variant from `Doubly` to `Singly`. +/// +/// ```rust +/// use orx_linked_list::*; +/// +/// // empty +/// let singly = SinglyLinkedList::::new(); +/// let singly = List::::new(); +/// +/// // from iter +/// let singly: SinglyLinkedList<_> = [1, 2, 3].into_iter().collect(); +/// let mut singly: List = [1, 2, 3].into_iter().collect(); +// +/// assert_eq!(Some(&1), singly.front()); +/// assert_eq!(Some(&3), singly.back()); +/// +/// singly.push_front(0); +/// +/// assert_eq!(Some(&0), singly.front()); +/// +/// assert_eq!(Some(0), singly.pop_front()); +/// +/// assert_eq!(vec![1, 2, 3], singly.iter().copied().collect::>()); +/// ```` +pub struct List<'a, V, T> +where + T: 'a, + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, +{ + pub(crate) col: SelfRefCol<'a, V, T, SplitVec, Recursive>>, +} + +impl<'a, V, T> Default for List<'a, V, T> +where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, +{ + /// Creates an empty list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let list: List = List::default(); + /// assert!(list.is_empty()); + /// ``` + fn default() -> Self { + Self::new() + } +} + +impl<'a, V, T: 'a> List<'a, V, T> +where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, +{ + /// Creates an empty list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let list: List = List::new(); + /// assert!(list.is_empty()); + /// ``` + pub fn new() -> Self { + Self { + col: SelfRefCol::default(), + } + } + + // get + /// Returns the number of elements in the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// assert_eq!(0, list.len()); + /// + /// list.push_back('a'); + /// list.push_front('b'); + /// _ = list.pop_back(); + /// list.push_back('c'); + /// + /// assert_eq!(2, list.len()); + /// ``` + #[inline(always)] + pub fn len(&self) -> usize { + self.col.len() + } + + /// Returns the number of elements in the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::new(); + /// + /// assert!(list.is_empty()); + /// + /// list.push_back('a'); + /// assert!(!list.is_empty()); + /// + /// _ = list.pop_back(); + /// assert!(list.is_empty()); + /// ``` + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.col.is_empty() + } + + /// ***O(1)*** Returns a reference to the front of the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = SinglyLinkedList::new(); + /// + /// assert!(list.front().is_none()); + /// + /// list.push_front('a'); + /// assert_eq!(Some(&'a'), list.front()); + /// + /// list.push_front('b'); + /// assert_eq!(Some(&'b'), list.front()); + /// + /// _ = list.pop_front(); + /// assert_eq!(Some(&'a'), list.front()); + /// ``` + #[allow(clippy::missing_panics_doc)] + pub fn front(&self) -> Option<&T> { + self.col.ends().front().map(|n| n.data().expect("is-some")) + } + + /// ***O(1)*** Returns a reference to the back of the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::new(); + /// + /// assert!(list.back().is_none()); + /// + /// list.push_back('a'); + /// assert_eq!(Some(&'a'), list.back()); + /// + /// list.push_back('b'); + /// assert_eq!(Some(&'b'), list.back()); + /// + /// list.push_front('c'); + /// assert_eq!(Some(&'b'), list.back()); + /// + /// _ = list.pop_back(); + /// assert_eq!(Some(&'a'), list.back()); + /// ``` + #[allow(clippy::missing_panics_doc)] + pub fn back(&self) -> Option<&T> { + self.col.ends().back().map(|n| n.data().expect("is-some")) + } + + /// ***O(n)*** Returns an iterator to elements of the list from the `front` node to the back. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::new(); + /// + /// list.push_front('b'); + /// list.push_back('c'); + /// list.push_front('a'); + /// + /// let mut iter = list.iter(); + /// + /// assert_eq!(Some(&'a'), iter.next()); + /// assert_eq!(Some(&'b'), iter.next()); + /// assert_eq!(Some(&'c'), iter.next()); + /// assert!(iter.next().is_none()); + /// ``` + pub fn iter(&self) -> IterFromFront<'_, 'a, V, T> { + IterFromFront::new(self.len(), self.col.ends().front()) + } + + // mut + /// Clears the list removing all elements. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::new(); + /// + /// list.push_front('b'); + /// list.push_back('c'); + /// list.push_front('a'); + /// + /// assert_eq!(3, list.len()); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// list.clear(); + /// assert!(list.is_empty()); + /// ``` + pub fn clear(&mut self) { + self.col.clear(); + } + + /// ***O(1)*** Sets value of `front` of the list as `new_front` and returns value of the front element; returns None if list was empty. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// assert_eq!(0, list.len()); + /// + /// let prior_front = list.swap_front('a'); + /// assert!(prior_front.is_none()); + /// assert_eq!(Some(&'a'), list.front()); + /// + /// let prior_front = list.swap_front('z'); + /// assert_eq!(Some('a'), prior_front); + /// assert_eq!(Some(&'z'), list.front()); + /// ``` + pub fn swap_front(&mut self, new_front: T) -> Option { + self.col + .move_mutate_take(new_front, |x, value| match x.ends().front() { + Some(front_node) => Some(front_node.swap_data(&x, value)), + None => { + Self::push_first_node(&x, value); + None + } + }) + } + + /// ***O(1)*** Sets value of `back` of the list as `new_back` and returns value of the back element; returns None if list was empty. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// assert_eq!(0, list.len()); + /// + /// let prior_back = list.swap_back('a'); + /// assert!(prior_back.is_none()); + /// assert_eq!(Some(&'a'), list.back()); + /// + /// let prior_back = list.swap_back('z'); + /// assert_eq!(Some('a'), prior_back); + /// assert_eq!(Some(&'z'), list.back()); + /// ``` + pub fn swap_back(&mut self, new_back: T) -> Option { + self.col + .move_mutate_take(new_back, |x, value| match x.ends().back() { + Some(back_node) => Some(back_node.swap_data(&x, value)), + None => { + Self::push_first_node(&x, value); + None + } + }) + } + + /// ***O(1)*** Pops and returns the `value` at the `front` of the list; returns None if the list is empty. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// let popped = list.pop_front(); + /// assert!(popped.is_none()); + /// + /// list.push_front('a'); + /// assert_eq!(Some(&'a'), list.front()); + /// + /// let popped = list.pop_front(); + /// assert_eq!(Some('a'), popped); + /// assert!(list.is_empty()); + /// ``` + pub fn pop_front(&mut self) -> Option { + self.col.mutate_take(|x| { + x.ends().front().map(|prior_front| { + let new_front = *prior_front.next().get(); + let new_back = some_only_if(new_front.is_some(), x.ends().back()); + x.set_ends([new_front, new_back]); + + if let Some(new_front) = new_front { + new_front.clear_prev(&x); + } + + prior_front.close_node_take_data(&x) + }) + }) + } + + // helpers + /// Pushes the `value` as the first node of the list and sets both ends to this first node. + #[inline(always)] + fn push_first_node<'rf>(mut_key: &MutKey<'rf, 'a, V, T>, value: T) { + debug_assert!( + mut_key.is_empty() + && mut_key.ends().front().is_none() + && mut_key.ends().back().is_none() + ); + let node = mut_key.push_get_ref(value); + mut_key.set_ends([Some(node), Some(node)]); + } +} + +impl<'a, T: 'a> List<'a, Singly, T> { + // mut + + /// ***O(1)*** Pushes the `value` to the `front` of the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// list.push_front('a'); + /// assert_eq!(Some(&'a'), list.front()); + /// + /// list.push_front('b'); + /// assert_eq!(Some(&'b'), list.front()); + /// + /// let popped = list.pop_front(); + /// assert_eq!(Some('b'), popped); + /// assert_eq!(Some(&'a'), list.front()); + /// ``` + pub fn push_front(&mut self, value: T) { + self.col + .move_mutate(value, |x, value| match x.ends().front() { + Some(prior_front) => { + let new_front = x.push_get_ref(value); + new_front.set_next(&x, prior_front); + x.set_ends([Some(new_front), x.ends().back()]); + } + None => Self::push_first_node(&x, value), + }); + } + + /// ***O(1)*** Appends the `other` list to the `front` of this list. + /// + /// Time complexity: + /// * ***O(1)*** gets `front` of this list, say a, + /// * ***O(1)*** gets `back` of the other list, say b, + /// * ***O(1)*** connects `b -> a`. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// list.push_front('c'); + /// list.push_front('b'); + /// list.push_front('a'); + /// + /// let mut other: List = List::new(); + /// other.push_front('e'); + /// other.push_front('d'); + /// + /// list.append_front(other); + /// assert_eq!(&['d', 'e', 'a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn append_front(&mut self, other: Self) { + self.col.move_append_mutate(other.col, (), |x, y, _| { + match (x.ends().front(), y.ends().back()) { + (Some(a), Some(b)) => { + b.set_next(&x, a); + x.set_ends([y.ends().front(), x.ends().back()]); + } + (None, Some(_)) => { + x.set_ends([y.ends().front(), y.ends().back()]); + } + _ => {} + }; + }); + } + + /// ***O(1)*** Appends the `other` list to the `back` of this list. + /// + /// Time complexity: + /// * ***O(1)*** gets `back` of this list, say a, + /// * ***O(1)*** gets `front` of the other list, say b, + /// * ***O(1)*** connects `a -> b`. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// list.push_front('c'); + /// list.push_front('b'); + /// list.push_front('a'); + /// + /// let mut other: List = List::new(); + /// other.push_front('e'); + /// other.push_front('d'); + /// + /// list.append_back(other); + /// assert_eq!(&['a', 'b', 'c', 'd', 'e'], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn append_back(&mut self, other: Self) { + self.col.move_append_mutate(other.col, (), |x, y, _| { + match (x.ends().back(), y.ends().front()) { + (Some(a), Some(b)) => { + a.set_next(&x, b); + x.set_ends([x.ends().front(), y.ends().back()]); + } + (None, Some(b)) => { + x.set_ends([Some(b), y.ends().back()]); + } + _ => {} + }; + }); + } + + /// ***O(n)*** Removes and returns value of the `at`-th element in the list; returns None if list length is less than or equal to `at`. + /// + /// Time complexity: + /// * starts from the `front`, + /// * ***O(n)*** iterates until reaching the element, + /// * ***O(1)*** removes and returns the value. + /// + /// # Example + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = List::::new(); + /// + /// list.push_front('c'); + /// list.push_front('b'); + /// list.push_front('a'); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// let removed = list.remove_at(3); + /// assert!(removed.is_none()); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// let removed = list.remove_at(1); + /// assert_eq!(Some('b'), removed); + /// assert_eq!(&['a', 'c'], list.iter().copied().collect::>().as_slice()); + ///``` + pub fn remove_at(&mut self, at: usize) -> Option { + match at { + _ if at >= self.len() => None, + 0 => self.pop_front(), + _ => self.col.move_mutate_take(at, |x, at| { + let (prev, current) = Self::get_prev_and_current_at(&x, at); + prev.set_next(&x, *current.next().get()); + if at == x.len() - 1 { + x.set_ends([x.ends().front(), Some(prev)]); + } + Some(current.close_node_take_data(&x)) + }), + } + } + + /// ***O(n)*** Inserts the given `value` at the `at`-th element of the list. + /// + /// Time complexity: + /// * starts from the `front`, + /// * ***O(n)*** iterates until reaching the element, + /// * ***O(1)*** inserts the value. + /// + /// # Panics + /// + /// Panics if `at > self.len()`. + /// + /// # Example + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = List::::new(); + /// + /// list.push_front('c'); + /// list.push_front('b'); + /// list.push_front('a'); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// list.insert_at(1, 'x'); + /// assert_eq!(&['a', 'x', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + ///``` + pub fn insert_at(&mut self, at: usize, value: T) { + assert!(at <= self.len(), "out of bounds"); + match at { + 0 => self.push_front(value), + at if at == self.len() => self.col.move_mutate((at, value), |x, (at, value)| { + let new_node = x.push_get_ref(value); + x.set_ends([x.ends().front(), Some(new_node)]); + let (_, prev) = Self::get_prev_and_current_at(&x, at - 1); + prev.set_next(&x, new_node); + }), + at => self.col.move_mutate((at, value), |x, (at, value)| { + let new_node = x.push_get_ref(value); + let (prev, current) = Self::get_prev_and_current_at(&x, at); + prev.set_next(&x, new_node); + new_node.set_next(&x, current); + }), + } + } + + /// ***O(n)*** Retains only the elements specified by the predicate. + /// + /// In other words, removes all elements `e` for which `predicate(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the original order, and preserves the order of the retained elements. + /// + /// Time complexity: + /// * ***O(n)*** to iterate over all elements, + /// * ***O(1)*** to remove elements failing to satisfy the predicate. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = SinglyLinkedList::from_iter([0, 1, 2, 3, 4]); + /// + /// list.retain(&|x| *x % 2 == 0); + /// + /// assert_eq!(&[0, 2, 4], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn retain(&mut self, predicate: &Predicate) + where + Predicate: Fn(&T) -> bool, + { + self.retain_collect(predicate, &mut |_| {}); + } + + /// ***O(n)*** Retains only the elements specified by the predicate; all elements that are removed elements are collected by the provided closure. + /// + /// In other words, removes all elements `e` for which `predicate(&e)` returns false; and calls `collect(e)` on the removed values. + /// This method operates in place, visiting each element exactly once in the original order, and preserves the order of the retained elements. + /// + /// Time complexity: + /// * ***O(n)*** to iterate over all elements, + /// * ***O(1)*** to remove elements failing to satisfy the predicate and collect. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = SinglyLinkedList::from_iter([0, 1, 2, 3, 4]); + /// + /// let mut odds = vec![]; + /// let mut collect = |x| odds.push(x); + /// + /// list.retain_collect(&|x| *x % 2 == 0, &mut collect); + /// + /// assert_eq!(&[0, 2, 4], list.iter().copied().collect::>().as_slice()); + /// assert_eq!(&[1, 3], odds.as_slice()); + /// ``` + pub fn retain_collect( + &mut self, + predicate: &Predicate, + collect: &mut Collect, + ) where + Predicate: Fn(&T) -> bool, + Collect: FnMut(T), + { + fn remove<'a, T>( + mut_key: &SinglyMutKey<'_, 'a, T>, + prev: Option<&'a Node<'a, Singly, T>>, + node_to_remove: &'a Node<'a, Singly, T>, + ) -> T { + if let Some(prev) = prev { + prev.set_next(mut_key, *node_to_remove.next().get()); + } + node_to_remove.close_node_take_data_no_reclaim(mut_key) + } + + self.col + .mutate_filter_collect(predicate, collect, |x, predicate, collect| { + let mut front = x.ends().front(); + let mut back = None; + let mut back_dropped = false; + let mut current = x.ends().front(); + + if let Some(node) = current { + current = *node.next().get(); + if predicate(unsafe { node.data().unwrap_unchecked() }) { + back = Some(node); + back_dropped = false; + } else { + collect(remove(&x, back, node)); + front = None; + back_dropped = true; + } + } + + while let Some(node) = current { + current = *node.next().get(); + if predicate(unsafe { node.data().unwrap_unchecked() }) { + back = Some(node); + back_dropped = false; + if front.is_none() { + front = Some(node); + } + } else { + collect(remove(&x, back, node)); + back_dropped = true; + } + } + + if !back_dropped { + back = x.ends().back(); + } + + x.set_ends([front, back]); + x.reclaim_closed_nodes(); + }); + } + + // helpers + /// ***O(n)*** Gets the prev -> current node tuple where current is the `at`-th element. + /// + /// # Panics + /// + /// Panics if `self.len() < 2` and/or `at == 0`. + fn get_prev_and_current_at<'rf>( + mut_key: &SinglyMutKey<'rf, 'a, T>, + at: usize, + ) -> (&'a Node<'a, Singly, T>, &'a Node<'a, Singly, T>) { + let mut prev = unsafe { mut_key.ends().front().unwrap_unchecked() }; + let mut current = unsafe { prev.next().get().unwrap_unchecked() }; + for _ in 1..at { + prev = current; + current = unsafe { current.next().get().unwrap_unchecked() }; + } + + (prev, current) + } +} + +impl<'a, T: 'a> List<'a, Doubly, T> { + // get + + /// ***O(n)*** Returns an iterator to elements of the list from the `back` node to the front. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = List::::new(); + /// + /// list.push_front('b'); + /// list.push_back('c'); + /// list.push_front('a'); + /// + /// let mut iter = list.iter_from_back(); + /// + /// assert_eq!(Some(&'c'), iter.next()); + /// assert_eq!(Some(&'b'), iter.next()); + /// assert_eq!(Some(&'a'), iter.next()); + /// assert!(iter.next().is_none()); + /// ``` + pub fn iter_from_back(&self) -> IterFromBack<'_, 'a, T> { + IterFromBack::new(self.len(), self.col.ends().back()) + } + + /// ***O(1)*** Pushes the `value` to the `front` of the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// list.push_front('a'); + /// assert_eq!(Some(&'a'), list.front()); + /// + /// list.push_front('b'); + /// assert_eq!(Some(&'b'), list.front()); + /// + /// let popped = list.pop_front(); + /// assert_eq!(Some('b'), popped); + /// assert_eq!(Some(&'a'), list.front()); + /// ``` + pub fn push_front(&mut self, value: T) { + self.col + .move_mutate(value, |x, value| match x.ends().front() { + Some(prior_front) => { + let new_front = x.push_get_ref(value); + new_front.set_next(&x, prior_front); + prior_front.set_prev(&x, new_front); + x.set_ends([Some(new_front), x.ends().back()]); + } + None => Self::push_first_node(&x, value), + }); + } + + /// ***O(1)*** Pushes the `value` to the `back` of the list. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// list.push_back('a'); + /// assert_eq!(Some(&'a'), list.back()); + /// + /// list.push_back('b'); + /// assert_eq!(Some(&'b'), list.back()); + /// + /// let popped = list.pop_back(); + /// assert_eq!(Some('b'), popped); + /// assert_eq!(Some(&'a'), list.back()); + /// ``` + pub fn push_back(&mut self, value: T) { + self.col + .move_mutate(value, |x, value| match x.ends().back() { + Some(prior_back) => { + let new_back = x.push_get_ref(value); + new_back.set_prev(&x, prior_back); + prior_back.set_next(&x, new_back); + x.set_ends([x.ends().front(), Some(new_back)]); + } + None => Self::push_first_node(&x, value), + }); + } + + /// ***O(1)*** Appends the `other` list to the `front` of this list. + /// + /// Time complexity: + /// * ***O(1)*** gets `front` of this list, say a, + /// * ***O(1)*** gets `back` of the other list, say b, + /// * ***O(1)*** connects `b -> a`. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// list.push_front('b'); + /// list.push_front('a'); + /// list.push_back('c'); + /// + /// let mut other: List = List::new(); + /// other.push_back('d'); + /// other.push_back('e'); + /// + /// list.append_front(other); + /// assert_eq!(&['d', 'e', 'a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn append_front(&mut self, other: Self) { + self.col.move_append_mutate(other.col, (), |x, y, _| { + match (x.ends().front(), y.ends().back()) { + (Some(a), Some(b)) => { + b.set_next(&x, a); + a.set_prev(&x, b); + x.set_ends([y.ends().front(), x.ends().back()]); + } + (None, Some(_)) => { + x.set_ends([y.ends().front(), y.ends().back()]); + } + _ => {} + }; + }); + } + + /// ***O(1)*** Appends the `other` list to the `back` of this list. + /// + /// Time complexity: + /// * ***O(1)*** gets `back` of this list, say a, + /// * ***O(1)*** gets `front` of the other list, say b, + /// * ***O(1)*** connects `a -> b`. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// list.push_front('b'); + /// list.push_front('a'); + /// list.push_back('c'); + /// + /// let mut other: List = List::new(); + /// other.push_back('d'); + /// other.push_back('e'); + /// + /// list.append_back(other); + /// assert_eq!(&['a', 'b', 'c', 'd', 'e'], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn append_back(&mut self, other: Self) { + self.col.move_append_mutate(other.col, (), |x, y, _| { + match (x.ends().back(), y.ends().front()) { + (Some(a), Some(b)) => { + a.set_next(&x, b); + b.set_prev(&x, a); + x.set_ends([x.ends().front(), y.ends().back()]); + } + (None, Some(b)) => { + x.set_ends([Some(b), y.ends().back()]); + } + _ => {} + }; + }); + } + + /// ***O(1)*** Pops and returns the `value` at the `back` of the list; returns None if the list is empty. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list: List = List::new(); + /// + /// let popped = list.pop_back(); + /// assert!(popped.is_none()); + /// + /// list.push_back('a'); + /// assert_eq!(Some(&'a'), list.back()); + /// + /// let popped = list.pop_back(); + /// assert_eq!(Some('a'), popped); + /// assert!(list.is_empty()); + /// ``` + pub fn pop_back(&mut self) -> Option { + self.col.mutate_take(|x| { + x.ends().back().map(|prior_back| { + let new_back = *prior_back.prev().get(); + let new_front = some_only_if(new_back.is_some(), x.ends().front()); + x.set_ends([new_front, new_back]); + + if let Some(back) = new_back { + back.clear_next(&x); + } + + prior_back.close_node_take_data(&x) + }) + }) + } + + /// ***O(n)*** Removes and returns value of the `at`-th element in the list; returns None if list length is less than or equal to `at`. + /// + /// Time complexity: + /// * starts from the `front` or `back` choosing the shorter path depending on the length of the list and value of `at`, + /// * ***O(n)*** iterates until reaching the element, + /// * ***O(1)*** removes and returns the value. + /// + /// # Example + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = List::::new(); + /// + /// list.push_front('b'); + /// list.push_back('c'); + /// list.push_front('a'); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// let removed = list.remove_at(3); + /// assert!(removed.is_none()); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// let removed = list.remove_at(1); + /// assert_eq!(Some('b'), removed); + /// assert_eq!(&['a', 'c'], list.iter().copied().collect::>().as_slice()); + ///``` + pub fn remove_at(&mut self, at: usize) -> Option { + match at { + _ if at >= self.len() => None, + 0 => self.pop_front(), + _ if at == self.len() - 1 => self.pop_back(), + _ => { + let at_from_back = self.len() - 1 - at; + if at <= at_from_back { + self.col.move_mutate_take(at, |x, at| { + let current = Self::get_node_at(&x, at); + Some(Self::remove_node(&x, current)) + }) + } else { + self.col.move_mutate_take(at_from_back, |x, at_from_back| { + let current = Self::get_node_at_from_back(&x, at_from_back); + Some(Self::remove_node(&x, current)) + }) + } + } + } + } + + /// ***O(n)*** Inserts the given `value` at the `at`-th element of the list. + /// + /// Time complexity: + /// * starts from the `front` or `back` choosing the shorter path depending on the length of the list and value of `at`, + /// * ***O(n)*** iterates until reaching the element, + /// * ***O(1)*** inserts the value. + /// + /// # Panics + /// + /// Panics if `at > self.len()`. + /// + /// # Example + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = List::::new(); + /// + /// list.push_back('c'); + /// list.push_front('b'); + /// list.push_front('a'); + /// assert_eq!(&['a', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + /// + /// list.insert_at(1, 'x'); + /// assert_eq!(&['a', 'x', 'b', 'c'], list.iter().copied().collect::>().as_slice()); + ///``` + pub fn insert_at(&mut self, at: usize, value: T) { + assert!(at <= self.len(), "out of bounds"); + match at { + 0 => self.push_front(value), + _ if at == self.len() => self.push_back(value), + at => { + let at_from_back = self.len() - 1 - at; + if at <= at_from_back { + self.col.move_mutate((at, value), |x, (at, value)| { + let current = Self::get_node_at(&x, at); + Self::insert_node(&x, current, value); + }); + } else { + self.col + .move_mutate((at_from_back, value), |x, (at_from_back, value)| { + let current = Self::get_node_at_from_back(&x, at_from_back); + Self::insert_node(&x, current, value); + }); + } + } + } + } + + /// ***O(n)*** Retains only the elements specified by the predicate. + /// + /// In other words, removes all elements `e` for which `predicate(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the original order, and preserves the order of the retained elements. + /// + /// Time complexity: + /// * ***O(n)*** to iterate over all elements, + /// * ***O(1)*** to remove elements failing to satisfy the predicate. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::from_iter([0, 1, 2, 3, 4]); + /// + /// list.retain(&|x| *x % 2 == 0); + /// + /// assert_eq!(&[0, 2, 4], list.iter().copied().collect::>().as_slice()); + /// ``` + pub fn retain(&mut self, predicate: &Predicate) + where + Predicate: Fn(&T) -> bool, + { + self.retain_collect(predicate, &mut |_| {}); + } + + /// ***O(n)*** Retains only the elements specified by the predicate; all elements that are removed elements are collected by the provided closure. + /// + /// In other words, removes all elements `e` for which `predicate(&e)` returns false; and calls `collect(e)` on the removed values. + /// This method operates in place, visiting each element exactly once in the original order, and preserves the order of the retained elements. + /// + /// Time complexity: + /// * ***O(n)*** to iterate over all elements, + /// * ***O(1)*** to remove elements failing to satisfy the predicate and collect. + /// + /// # Examples + /// + /// ```rust + /// use orx_linked_list::*; + /// + /// let mut list = DoublyLinkedList::from_iter([0, 1, 2, 3, 4]); + /// + /// let mut odds = vec![]; + /// let mut collect = |x| odds.push(x); + /// + /// list.retain_collect(&|x| *x % 2 == 0, &mut collect); + /// + /// assert_eq!(&[0, 2, 4], list.iter().copied().collect::>().as_slice()); + /// assert_eq!(&[1, 3], odds.as_slice()); + /// ``` + pub fn retain_collect( + &mut self, + predicate: &Predicate, + collect: &mut Collect, + ) where + Predicate: Fn(&T) -> bool, + Collect: FnMut(T), + { + fn remove<'a, T>(mut_key: &DoublyMutKey<'_, 'a, T>, node: &'a Node<'a, Doubly, T>) -> T { + if let Some(next) = node.next().get() { + next.set_prev(mut_key, *node.prev().get()); + } + + if let Some(prev) = node.prev().get() { + prev.set_next(mut_key, *node.next().get()); + } + + node.close_node_take_data_no_reclaim(mut_key) + } + + self.col + .mutate_filter_collect(predicate, collect, |x, predicate, collect| { + let mut front = x.ends().front(); + let mut back = None; + let mut back_dropped = false; + let mut current = x.ends().front(); + + if let Some(node) = current { + current = *node.next().get(); + if predicate(unsafe { node.data().unwrap_unchecked() }) { + back = Some(node); + back_dropped = false; + } else { + collect(remove(&x, node)); + front = None; + back_dropped = true; + } + } + + while let Some(node) = current { + current = *node.next().get(); + if predicate(unsafe { node.data().unwrap_unchecked() }) { + back = Some(node); + back_dropped = false; + if front.is_none() { + front = Some(node); + } + } else { + collect(remove(&x, node)); + back_dropped = true; + } + } + + if !back_dropped { + back = x.ends().back(); + } + + x.set_ends([front, back]); + x.reclaim_closed_nodes(); + }); + } + + // helpers + /// Removes the `node` from the list, repairs the links and returns the removed value. + fn remove_node<'rf>(mut_key: &DoublyMutKey<'rf, 'a, T>, node: &'a Node<'a, Doubly, T>) -> T { + if let Some(next) = node.next().get() { + next.set_prev(mut_key, *node.prev().get()); + } + + if let Some(prev) = node.prev().get() { + prev.set_next(mut_key, *node.next().get()); + } + + node.close_node_take_data(mut_key) + } + + /// Inserts the `new_value` to the list before the given `node`. + fn insert_node<'rf>( + mut_key: &DoublyMutKey<'rf, 'a, T>, + node: &'a Node<'a, Doubly, T>, + new_value: T, + ) { + let new_node = mut_key.push_get_ref(new_value); + + if let Some(prev) = node.prev().get() { + prev.set_next(mut_key, new_node); + new_node.set_prev(mut_key, *prev); + } + + new_node.set_next(mut_key, node); + node.set_prev(mut_key, new_node); + } + + /// ***O(n)*** Gets the node at the `at`-th position. + /// + /// # Panics + /// + /// Panics if `self.is_empty()`. + fn get_node_at<'rf>(mut_key: &DoublyMutKey<'rf, 'a, T>, at: usize) -> &'a Node<'a, Doubly, T> { + let mut current = unsafe { mut_key.ends().front().unwrap_unchecked() }; + for _ in 0..at { + current = unsafe { current.next().get().unwrap_unchecked() }; + } + current + } + + /// ***O(n)*** Gets the node at the `at_from_back`-th position from the back. + /// + /// # Panics + /// + /// Panics if `self.is_empty()`. + fn get_node_at_from_back<'rf>( + mut_key: &DoublyMutKey<'rf, 'a, T>, + at_from_back: usize, + ) -> &'a Node<'a, Doubly, T> { + let mut current = unsafe { mut_key.ends().back().unwrap_unchecked() }; + for _ in 0..at_from_back { + current = unsafe { current.prev().get().unwrap_unchecked() }; + } + current + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use test_case::test_matrix; + + pub(crate) fn assert_empty_list<'a, V, T>(list: &List<'a, V, T>) + where + V: ListVariant<'a, T>, + V::Ends: ListEnds<'a, V, T>, + { + assert!(list.is_empty()); + assert_eq!(list.len(), 0); + assert!(list.col.ends().front().is_none()); + assert!(list.col.ends().back().is_none()); + assert!(list.front().is_none()); + assert!(list.back().is_none()); + assert!(list.iter().next().is_none()); + + V::validate(list); + } + + pub(crate) fn validate_both<'a, T>(singly: &List<'a, Singly, T>, doubly: &List<'a, Doubly, T>) { + Singly::validate(singly); + Doubly::validate(doubly); + } + + #[test] + fn new() { + let list: List = List::new(); + assert_empty_list(&list); + + let list: List = List::new(); + assert_empty_list(&list); + } + + #[test] + fn default() { + let list: List = List::default(); + assert_empty_list(&list); + + let list: List = List::default(); + assert_empty_list(&list); + } + + #[test] + fn len() { + let mut singly: List = List::default(); + let mut doubly: List = List::default(); + + for i in 0..100 { + assert_eq!(i, singly.len()); + assert_eq!(i, doubly.len()); + singly.push_front(i); + doubly.push_front(i); + validate_both(&singly, &doubly); + } + + for i in 0..100 { + assert_eq!(100 - i, singly.len()); + assert_eq!(100 - i, doubly.len()); + singly.pop_front(); + doubly.pop_front(); + validate_both(&singly, &doubly); + } + + assert_empty_list(&singly); + assert_empty_list(&doubly); + + for i in 0..100 { + assert_eq!(i, doubly.len()); + doubly.push_back(i); + validate_both(&singly, &doubly); + } + + for i in 0..100 { + assert_eq!(100 - i, doubly.len()); + doubly.pop_front(); + validate_both(&singly, &doubly); + } + } + + #[test] + fn singly_front() { + let mut singly: List = List::default(); + Singly::validate(&singly); + + assert!(singly.front().is_none()); + + singly.push_front(42); + Singly::validate(&singly); + + assert_eq!(Some(&42), singly.front()); + + singly.push_front(7); + Singly::validate(&singly); + + assert_eq!(Some(&7), singly.front()); + + singly.pop_front(); + Singly::validate(&singly); + + assert_eq!(Some(&42), singly.front()); + + singly.pop_front(); + Singly::validate(&singly); + + assert!(singly.front().is_none()); + + assert_empty_list(&singly); + } + + #[test] + fn singly_back() { + let mut singly = SinglyLinkedList::new(); + assert_empty_list(&singly); + + singly.push_front('x'); + assert_eq!(Some(&'x'), singly.front()); + assert_eq!(Some(&'x'), singly.back()); + + let _ = singly.pop_front(); + assert_empty_list(&singly); + + singly.push_front('c'); + singly.push_front('b'); + singly.push_front('a'); + assert_eq!(Some(&'a'), singly.front()); + assert_eq!(Some(&'c'), singly.back()); + Singly::validate(&singly); + + let _ = singly.pop_front(); + assert_eq!(Some(&'b'), singly.front()); + assert_eq!(Some(&'c'), singly.back()); + Singly::validate(&singly); + + let _ = singly.pop_front(); + assert_eq!(Some(&'c'), singly.front()); + assert_eq!(Some(&'c'), singly.back()); + Singly::validate(&singly); + + let _ = singly.pop_front(); + assert_empty_list(&singly); + + singly.push_front('c'); + singly.push_front('b'); + singly.push_front('a'); + assert_eq!(Some(&'c'), singly.back()); + Singly::validate(&singly); + + singly.insert_at(3, 'd'); + assert_eq!(Some(&'d'), singly.back()); + Singly::validate(&singly); + + _ = singly.pop_front(); + assert_eq!(Some(&'d'), singly.back()); + singly.push_front('a'); + assert_eq!(Some(&'d'), singly.back()); + Singly::validate(&singly); + + assert_eq!( + &['a', 'b', 'c', 'd'], + singly.iter().copied().collect::>().as_slice() + ); + + singly.remove_at(3); + assert_eq!( + &['a', 'b', 'c'], + singly.iter().copied().collect::>().as_slice() + ); + Singly::validate(&singly); + assert_eq!(Some(&'c'), singly.back()); + } + + #[test] + fn doubly_front_back() { + let mut doubly: List = List::default(); + Doubly::validate(&doubly); + + assert!(doubly.front().is_none()); + assert!(doubly.back().is_none()); + + doubly.push_front(42); + Doubly::validate(&doubly); + + assert_eq!(Some(&42), doubly.front()); + assert_eq!(Some(&42), doubly.back()); + + doubly.push_front(7); + Doubly::validate(&doubly); + + assert_eq!(Some(&7), doubly.front()); + assert_eq!(Some(&42), doubly.back()); + + doubly.pop_front(); + Doubly::validate(&doubly); + + assert_eq!(Some(&42), doubly.front()); + assert_eq!(Some(&42), doubly.back()); + + doubly.push_back(7); + Doubly::validate(&doubly); + + assert_eq!(Some(&42), doubly.front()); + assert_eq!(Some(&7), doubly.back()); + + doubly.pop_back(); + Doubly::validate(&doubly); + + assert_eq!(Some(&42), doubly.front()); + assert_eq!(Some(&42), doubly.back()); + + doubly.pop_back(); + Doubly::validate(&doubly); + + assert_empty_list(&doubly); + } + + #[test] + fn swap_front_back() { + let mut doubly: List = List::new(); + let mut singly: List = List::default(); + validate_both(&singly, &doubly); + + fn when_empty<'a, V: ListVariant<'a, i32>>(list: &mut List<'a, V, i32>) + where + V::Ends: ListEnds<'a, V, i32>, + { + let old = list.swap_front(42); + assert!(old.is_none()); + assert_eq!(list.front(), Some(&42)); + assert_eq!(list.back(), Some(&42)); + + list.clear(); + + let old = list.swap_back(42); + assert!(old.is_none()); + assert_eq!(list.front(), Some(&42)); + assert_eq!(list.back(), Some(&42)); + } + when_empty(&mut singly); + when_empty(&mut doubly); + validate_both(&singly, &doubly); + + fn when_single_item<'a, V: ListVariant<'a, i32>>( + list: &mut List<'a, V, i32>, + swap_front: bool, + ) where + V::Ends: ListEnds<'a, V, i32>, + { + let old = if swap_front { + list.swap_front(7) + } else { + list.swap_back(7) + }; + assert_eq!(old, Some(42)); + assert_eq!(list.front(), Some(&7)); + assert_eq!(list.back(), Some(&7)); + } + + for swap_front in [true, false] { + doubly.clear(); + singly.clear(); + doubly.push_front(42); + singly.push_front(42); + when_single_item(&mut singly, swap_front); + when_single_item(&mut doubly, swap_front); + validate_both(&singly, &doubly); + } + + fn when_multi_items<'a, V: ListVariant<'a, i32>>( + list: &mut List<'a, V, i32>, + swap_front: bool, + ) where + V::Ends: ListEnds<'a, V, i32>, + { + let prior_front = list.front().copied(); + let prior_back = list.back().copied(); + + let (expected, old) = if swap_front { + (prior_front, list.swap_front(7)) + } else { + (prior_back, list.swap_back(7)) + }; + assert_eq!(expected, old); + + if swap_front { + assert_eq!(list.front(), Some(&7)); + assert_eq!(list.back().copied(), prior_back); + } else { + assert_eq!(list.front().copied(), prior_front); + assert_eq!(list.back(), Some(&7)); + } + } + + for swap_front in [true, false] { + doubly.clear(); + singly.clear(); + doubly.push_front(0); + singly.push_front(1); + doubly.push_front(2); + singly.push_front(3); + doubly.push_front(4); + singly.push_front(5); + when_multi_items(&mut singly, swap_front); + when_multi_items(&mut doubly, swap_front); + validate_both(&singly, &doubly); + } + } + + #[test] + fn remove_at() { + let n = 2142; + let indices_to_remove = [2141, 2140, 2000, 1648, 1200, 999, 512, 444, 31, 21, 7, 1, 0]; + + let mut doubly: List = List::new(); + let mut singly: List = List::new(); + + for i in 0..n { + doubly.push_back(i); + singly.push_front(n - 1 - i); + } + + for i in indices_to_remove { + let removed = doubly.remove_at(i); + assert_eq!(Some(i), removed); + + let removed = singly.remove_at(i); + assert_eq!(Some(i), removed); + + validate_both(&singly, &doubly); + } + } + + #[test_matrix([0,1,2,6,58,548,1024,2047,3122,3123,3124])] + fn insert_at(at: usize) { + let n = 3124; + let value = 10000; + + let mut doubly: List = List::new(); + let mut singly: List = List::new(); + + for i in 0..n { + doubly.push_back(i); + singly.push_front(n - 1 - i); + } + + doubly.insert_at(at, value); + singly.insert_at(at, value); + + assert_eq!(Some(&value), doubly.iter().nth(at)); + assert_eq!(Some(&value), singly.iter().nth(at)); + + validate_both(&singly, &doubly); + } + + fn push_pop_clear_singly(n_push: usize, singly: &mut SinglyLinkedList) { + for i in 0..n_push { + singly.push_front(n_push - 1 - i); + } + Singly::validate(singly); + + while !singly.is_empty() { + singly.pop_front(); + Singly::validate(singly); + } + } + + fn push_pop_clear_doubly(n_push: usize, doubly: &mut DoublyLinkedList) { + for i in 0..n_push { + doubly.push_front(n_push - 1 - i); + } + Doubly::validate(doubly); + + while !doubly.is_empty() { + doubly.pop_front(); + Doubly::validate(doubly); + } + } + + #[test] + fn append_empty_empty() { + let n = 1314; + + let mut singly = SinglyLinkedList::::new(); + singly.append_back(SinglyLinkedList::new()); + let mut doubly = DoublyLinkedList::::new(); + doubly.append_back(DoublyLinkedList::new()); + + assert_empty_list(&singly); + assert_empty_list(&doubly); + push_pop_clear_singly(n, &mut singly); + push_pop_clear_doubly(n, &mut doubly); + + let mut singly = SinglyLinkedList::::new(); + singly.append_front(SinglyLinkedList::new()); + let mut doubly = DoublyLinkedList::::new(); + doubly.append_front(DoublyLinkedList::new()); + + assert_empty_list(&singly); + assert_empty_list(&doubly); + push_pop_clear_singly(n, &mut singly); + push_pop_clear_doubly(n, &mut doubly); + } + + #[test] + fn append_empty_nonempty() { + let n = 1572; + + fn validate<'a>( + n: usize, + singly: &mut SinglyLinkedList<'a, usize>, + doubly: &mut DoublyLinkedList<'a, usize>, + ) { + assert_eq!(n, singly.len()); + assert_eq!(n, doubly.len()); + + for (i, x) in singly.iter().enumerate() { + assert_eq!(i, *x); + } + for (i, x) in doubly.iter().enumerate() { + assert_eq!(i, *x); + } + + validate_both(singly, doubly); + push_pop_clear_singly(n, singly); + push_pop_clear_doubly(n, doubly); + } + + let mut singly = SinglyLinkedList::new(); + let mut other = SinglyLinkedList::new(); + for i in 0..n { + other.push_front(n - 1 - i); + } + singly.append_back(other); + + let mut doubly = DoublyLinkedList::new(); + let mut other = DoublyLinkedList::new(); + for i in 0..n { + other.push_front(n - 1 - i); + } + doubly.append_back(other); + + validate(n, &mut singly, &mut doubly); + + let mut singly = SinglyLinkedList::new(); + let mut other = SinglyLinkedList::new(); + for i in 0..n { + other.push_front(n - 1 - i); + } + singly.append_front(other); + + let mut doubly = DoublyLinkedList::new(); + let mut other = DoublyLinkedList::new(); + for i in 0..n { + other.push_front(n - 1 - i); + } + doubly.append_front(other); + + validate(n, &mut singly, &mut doubly); + } + + #[test] + fn append_nonempty_empty() { + fn validate<'a>( + n: usize, + singly: &mut SinglyLinkedList<'a, usize>, + doubly: &mut DoublyLinkedList<'a, usize>, + ) { + assert_eq!(n, singly.len()); + assert_eq!(n, doubly.len()); + + for (i, x) in singly.iter().enumerate() { + assert_eq!(i, *x); + } + for (i, x) in doubly.iter().enumerate() { + assert_eq!(i, *x); + } + + validate_both(singly, doubly); + push_pop_clear_singly(n, singly); + push_pop_clear_doubly(n, doubly); + } + + let n = 1044; + + let mut singly = SinglyLinkedList::new(); + for i in 0..n { + singly.push_front(n - 1 - i); + } + singly.append_back(SinglyLinkedList::new()); + + let mut doubly = DoublyLinkedList::new(); + for i in 0..n { + doubly.push_front(n - 1 - i); + } + doubly.append_back(DoublyLinkedList::new()); + + validate(n, &mut singly, &mut doubly); + } + + #[test] + fn append_back_nonempty_nonempty() { + let n1 = 1344; + let n2 = 123; + + let mut singly = SinglyLinkedList::new(); + let mut other = SinglyLinkedList::new(); + for i in 0..n1 { + other.push_front(n1 + n2 - 1 - i); + } + for i in n1..(n1 + n2) { + singly.push_front(n1 + n2 - 1 - i); + } + singly.append_back(other); + assert_eq!(n1 + n2, singly.len()); + for (i, x) in singly.iter().enumerate() { + assert_eq!(i, *x); + } + Singly::validate(&singly); + push_pop_clear_singly(n1, &mut singly); + + let mut doubly = DoublyLinkedList::new(); + let mut other = DoublyLinkedList::new(); + for i in 0..n1 { + other.push_front(n1 + n2 - 1 - i); + } + for i in n1..(n1 + n2) { + doubly.push_front(n1 + n2 - 1 - i); + } + + doubly.append_back(other); + assert_eq!(n1 + n2, doubly.len()); + + for (i, x) in doubly.iter().enumerate() { + assert_eq!(i, *x); + } + Doubly::validate(&doubly); + push_pop_clear_doubly(n1, &mut doubly); + } + + #[test] + fn append_front_nonempty_nonempty() { + let n1 = 1344; + let n2 = 123; + + let mut singly = SinglyLinkedList::new(); + let mut other = SinglyLinkedList::new(); + for i in 0..n1 { + singly.push_front(n1 + n2 - 1 - i); + } + for i in n1..(n1 + n2) { + other.push_front(n1 + n2 - 1 - i); + } + singly.append_front(other); + assert_eq!(n1 + n2, singly.len()); + for (i, x) in singly.iter().enumerate() { + assert_eq!(i, *x); + } + Singly::validate(&singly); + push_pop_clear_singly(n1, &mut singly); + + let mut doubly = DoublyLinkedList::new(); + let mut other = DoublyLinkedList::new(); + for i in 0..n1 { + doubly.push_front(n1 + n2 - 1 - i); + } + for i in n1..(n1 + n2) { + other.push_front(n1 + n2 - 1 - i); + } + + doubly.append_front(other); + assert_eq!(n1 + n2, doubly.len()); + + for (i, x) in doubly.iter().enumerate() { + assert_eq!(i, *x); + } + Doubly::validate(&doubly); + push_pop_clear_doubly(n1, &mut doubly); + } + + #[test_matrix( + [0, 1, 2, 6, 7, 35], + [ + vec![], + vec![0], + vec![0, 1, 2, 3, 4, 5, 6, 7], + vec![0, 1, 2, 3, 4, 5, 6], + vec![1, 2, 3, 4, 5, 6, 7], + ] + )] + fn retain(num_gaps: usize, values: Vec) { + for modulo in [Box::new(0), Box::new(1)] { + let mut doubly = DoublyLinkedList::from_iter(values.clone()); + let mut singly = SinglyLinkedList::from_iter(values.clone()); + for _ in 0..num_gaps { + singly.push_front(111); + doubly.push_front(111); + _ = singly.pop_front(); + _ = doubly.pop_front(); + } + + let predicate = |x: &i32| x % 2 == *modulo; + + let front = values.iter().find(|x| predicate(x)); + let back = values.iter().rev().find(|x| predicate(x)); + + doubly.retain(&predicate); + singly.retain(&predicate); + validate_both(&singly, &doubly); + + let expected_list = values + .iter() + .filter(|x| *x % 2 == *modulo) + .copied() + .collect::>(); + + assert_eq!( + expected_list, + doubly.iter().copied().collect::>().as_slice() + ); + assert_eq!( + expected_list, + singly.iter().copied().collect::>().as_slice() + ); + + assert_eq!(front, doubly.front()); + assert_eq!(back, doubly.back()); + + assert_eq!(front, singly.front()); + assert_eq!(back, singly.back()); + } + } + + #[test_matrix( + [0, 1, 2, 6, 7, 35], + [ + vec![], + vec![0], + vec![0, 1, 2, 3, 4, 5, 6, 7], + vec![0, 1, 2, 3, 4, 5, 6], + vec![1, 2, 3, 4, 5, 6, 7], + ] + )] + fn retain_collect(num_gaps: usize, values: Vec) { + for modulo in [Box::new(0), Box::new(1)] { + let mut doubly = DoublyLinkedList::from_iter(values.clone()); + let mut singly = SinglyLinkedList::from_iter(values.clone()); + for _ in 0..num_gaps { + singly.push_front(111); + doubly.push_front(111); + _ = singly.pop_front(); + _ = doubly.pop_front(); + } + + let predicate = |x: &i32| x % 2 == *modulo; + + let front = values.iter().find(|x| predicate(x)); + let back = values.iter().rev().find(|x| predicate(x)); + + let expected_collected = values + .iter() + .filter(|x| *x % 2 == (1 - *modulo)) + .copied() + .collect::>(); + + let mut collected = vec![]; + let mut collect = |x| collected.push(x); + doubly.retain_collect(&predicate, &mut collect); + assert_eq!(expected_collected, collected.as_slice()); + + let mut collected = vec![]; + let mut collect = |x| collected.push(x); + singly.retain_collect(&predicate, &mut collect); + assert_eq!(expected_collected, collected.as_slice()); + + validate_both(&singly, &doubly); + + let expected_list = values + .iter() + .filter(|x| *x % 2 == *modulo) + .copied() + .collect::>(); + + assert_eq!( + expected_list, + doubly.iter().copied().collect::>().as_slice() + ); + assert_eq!( + expected_list, + singly.iter().copied().collect::>().as_slice() + ); + + assert_eq!(front, doubly.front()); + assert_eq!(back, doubly.back()); + + assert_eq!(front, singly.front()); + assert_eq!(back, singly.back()); + } + } + + #[test] + fn asdf() { + fn eq<'a, I: Iterator + Clone>(iter: I, slice: &[u32]) -> bool { + iter.clone().count() == slice.len() && iter.zip(slice.iter()).all(|(a, b)| a == b) + } + + let _list: List = List::new(); + let _list = SinglyLinkedList::::new(); + let _list: List = List::new(); + let _list = DoublyLinkedList::::new(); + + let mut list = DoublyLinkedList::from_iter([3, 4, 5]); + assert_eq!(list.front(), Some(&3)); + assert_eq!(list.back(), Some(&5)); + assert!(eq(list.iter(), &[3, 4, 5])); + assert!(eq(list.iter_from_back(), &[5, 4, 3])); + + assert_eq!(list.pop_front(), Some(3)); + assert_eq!(list.pop_back(), Some(5)); + + list.push_back(5); + list.push_front(3); + assert!(eq(list.iter(), &[3, 4, 5])); + + let other = DoublyLinkedList::from_iter([6, 7, 8, 9]); + list.append_back(other); + assert!(eq(list.iter(), &[3, 4, 5, 6, 7, 8, 9])); + + let other = DoublyLinkedList::from_iter([0, 1, 2]); + list.append_front(other); + assert!(eq(list.iter(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); + + list.retain(&|x| x < &5); + assert!(eq(list.iter(), &[0, 1, 2, 3, 4])); + + let mut odds = vec![]; + let mut collect_odds = |x| odds.push(x); + list.retain_collect(&|x| x % 2 == 0, &mut collect_odds); + + assert!(eq(list.iter(), &[0, 2, 4])); + assert!(eq(odds.iter(), &[1, 3])); + } +} diff --git a/src/mem.rs b/src/mem.rs deleted file mode 100644 index b1f34e8..0000000 --- a/src/mem.rs +++ /dev/null @@ -1,471 +0,0 @@ -use crate::linked_list::LinkedList; - -/// `LinkedList` holds all elements close to each other in a `PinnedVec` -/// aiming for better cache locality while using thin references rather -/// than wide pointers and to reduce heap allocations. -/// -/// In order to achieve *O(1)* time complexity while avoiding smart pointers, -/// remove and pop operations are able to be `Lazy`. -/// In this case; i.e., when the strategy is set to `MemoryUtilization::Lazy`, -/// every `pop_back`, `pop_front` or `remove` method call leaves a gap in the -/// underlying vector. Status of utilization of the underlying vector can be -/// queried using the `memory_status` method and the gaps can completely be -/// reclaimed by manually calling the `memory_reclaim` method which has a time -/// complexity of *O(n)* where *n* is the length of the underlying vector. -/// -/// Being able to be lazy, it is possible to define and use different -/// strategies which would be a better fit for different situations: -/// -/// * `Lazy`: `memory_reclaim` is never called automatically: -/// * leads to the cheapest possible `pop_back`, `pop_front` or `remove` operations, -/// * however, the utilization of the vector can be low especially when -/// a large number of elements enter and exit the linked list. -/// * might be a better fit where keeping the time complexity of these operations -/// at *O(1)* is important; or when utilization is not expected to drop too low. -/// * `Eager`: every `pop_back`, `pop_front` or `remove` method call is followed -/// by a `memory_reclaim` call: -/// * this strategy keeps the vector without gaps at 100% utilization; -/// * however, abovementioned operations require *O(n)* time complexity; -/// * might be a better fit where memory is scarce and more important than -/// the increased time-complexity of these methods. -/// * `WithThreshold` (**recommended & default**): `pop_back`, `pop_front` or `remove` method call -/// is followed by a `memory_reclaim` call only if the memory utilization drops below a -/// pre-determined threshold: -/// * this strategy is a generalization of `Lazy` and `Eager` allowing to -/// select the required threshold level between memory utilization and amortized -/// time complexity of these methods. Note that setting the least memory utilization -/// to a value lower than 1.0 would still least to a constant amortized time complexity. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum MemoryUtilization { - /// With `Lazy` strategy, `memory_reclaim` is never called automatically: - /// * leads to the cheapest possible `pop_back`, `pop_front` or `remove_at` operations, - /// * however, the utilization of the vector can be low especially when - /// a large number of elements enter and exit the linked list. - /// * might be a better fit where keeping the time complexity of these operations - /// at *O(1)* is important; or when utilization is not expected to drop too low. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new() - /// .with_memory_utilization(MemoryUtilization::Lazy); - /// - /// // fill list with 4 elements - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// let util = list.memory_status(); - /// assert_eq!(4, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// - /// // remove 1 of 4 - /// _ = list.remove_at(2); - /// let util = list.memory_status(); - /// assert_eq!(3, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(0.75, util.utilization()); - /// - /// // pop 2 more - /// _ = list.pop_back(); - /// _ = list.pop_front(); - /// let util = list.memory_status(); - /// assert_eq!(1, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(0.25, util.utilization()); - /// - /// // remove the last element - /// _ = list.remove_at(0); - /// let util = list.memory_status(); - /// assert_eq!(0, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(0.00, util.utilization()); - /// ``` - Lazy, - /// With `Eager` strategy, every `pop_back`, `pop_front` or `remove_at` method call is followed - /// by a `memory_reclaim` call: - /// * this strategy keeps the vector without gaps at 100% utilization; - /// * however, abovementioned operations require *O(n)* time complexity; - /// * might be a better fit where memory is scarce and more important than - /// the increased time-complexity of these methods. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new().with_memory_utilization(MemoryUtilization::Eager); - /// - /// // fill list with 4 elements - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// let util = list.memory_status(); - /// assert_eq!(4, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// - /// // remove 1 of 4 - /// _ = list.remove_at(2); - /// let util = list.memory_status(); - /// assert_eq!(3, util.num_active_nodes); - /// assert_eq!(3, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// - /// // pop 2 more - /// _ = list.pop_back(); - /// _ = list.pop_front(); - /// let util = list.memory_status(); - /// assert_eq!(1, util.num_active_nodes); - /// assert_eq!(1, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// - /// // remove the last element - /// _ = list.remove_at(0); - /// let util = list.memory_status(); - /// assert_eq!(0, util.num_active_nodes); - /// assert_eq!(0, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// ``` - Eager, - /// With `WithThreshold`strategy, `pop_back`, `pop_front` or `remove_at` method call - /// is followed by a `memory_reclaim` call only if the memory utilization drops below the - /// pre-determined threshold: - /// * this strategy is a generalization of `Lazy` and `Eager` allowing to - /// select the required threshold level between memory utilization and amortized - /// time complexity of these methods. Note that setting the least memory utilization - /// to a value lower than 1.0 would still least to a constant amortized time complexity. - /// - /// # Examples - /// - /// ``` - /// use orx_linked_list::*; - /// - /// let mut list = LinkedList::new() - /// .with_memory_utilization(MemoryUtilization::WithThreshold(0.51)); - /// - /// // fill list with 4 elements - /// list.push_back('a'); - /// list.push_back('b'); - /// list.push_back('c'); - /// list.push_back('d'); - /// - /// let util = list.memory_status(); - /// assert_eq!(4, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// - /// // remove 1 of 4; utilization remains above the threshold - /// _ = list.remove_at(2); - /// let util = list.memory_status(); - /// assert_eq!(3, util.num_active_nodes); - /// assert_eq!(4, util.num_occupied_nodes); - /// assert_eq!(0.75, util.utilization()); - /// - /// // pop 1 more which would reduce utilization to 0.50 - /// // since it is below the treshold; the memory will be reclaimed immediately - /// _ = list.pop_back(); - /// let util = list.memory_status(); - /// assert_eq!(2, util.num_active_nodes); - /// assert_eq!(2, util.num_occupied_nodes); - /// assert_eq!(1.00, util.utilization()); - /// ``` - WithThreshold(f32), -} - -impl Default for MemoryUtilization { - fn default() -> Self { - Self::WithThreshold(0.75) - } -} - -impl MemoryUtilization { - #[inline(always)] - fn will_auto_reclaim(self, list_len: usize, storage_len: usize) -> bool { - match self { - MemoryUtilization::Eager => storage_len > list_len, - MemoryUtilization::Lazy => false, - MemoryUtilization::WithThreshold(threshold) => { - storage_len > list_len && { - let utilization = list_len as f32 / storage_len as f32; - utilization < threshold - } - } - } - } - pub(crate) fn reclaim(self, list: &mut LinkedList<'_, T>, storage_len: usize) { - if self.will_auto_reclaim(list.len(), storage_len) { - reclaim_memory(list, storage_len) - } - } -} - -/// Utilization of the underlying vector of the linked list. -/// -/// `LinkedList` holds all elements close to each other in a `PinnedVec` -/// aiming for better cache locality while using thin references rather -/// than wide pointers and to reduce heap allocations. -/// -/// In order to achieve *O(1)* time complexity while avoiding smart pointers, -/// remove and pop operations are designed to be lazy: -/// -/// * the links are immediately adjusted; however, -/// * the memory is not immediately reclaimed leaving a gap in the underlying vector. -/// -/// This method reveals the memory utilization of the underlying pinned vector -/// at any given time as the fraction of active linked list nodes to total -/// spaces used by the pinned vector. -/// -/// Some extreme examples are as follows: -/// -/// * in an push-only situation, memory utilization is equal to 1.0: -/// * `num_active_nodes == num_occupied_nodes` -/// * in a situation where each push is followed by a pop, -/// memory utilization is 0.0: -/// * `num_active_nodes == 0` -/// * `num_occupied_nodes == n`, where `n` is the number of items pushed. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MemoryStatus { - /// Number of active nodes in the linked list which is equal to `len` of the list. - pub num_active_nodes: usize, - /// Number of total node capacity used by the underlying data structure to store - /// the active nodes together with the gaps due to `pop_back`, `pop_front` and - /// `remove` calls. - pub num_occupied_nodes: usize, -} - -impl MemoryStatus { - /// Returns `num_active_nodes / num_occupied_nodes` as a measure of utilization of the memory used by the linked list. - pub fn utilization(&self) -> f32 { - if self.num_occupied_nodes == 0 { - debug_assert_eq!(0, self.num_active_nodes); - 1.0 - } else { - self.num_active_nodes as f32 / self.num_occupied_nodes as f32 - } - } - pub(crate) fn of_list(list_len: usize, storage_len: usize) -> Self { - let num_active_nodes = list_len; - let num_occupied_nodes = storage_len; - Self { - num_active_nodes, - num_occupied_nodes, - } - } -} - -pub(crate) fn reclaim_memory(list: &mut LinkedList<'_, T>, storage_len: usize) { - let mut last_occupied_idx = storage_len; - - for i in 0..storage_len { - if list.is_vacant(i) { - let vacant_idx = i; - let occupied_idx = get_last_occupied_idx(list, vacant_idx + 1, last_occupied_idx); - if let Some(occupied_idx) = occupied_idx { - last_occupied_idx = occupied_idx; - list.move_to_vacant_node(occupied_idx, vacant_idx); - } else { - break; - } - } - } - - list.truncate_vec(); -} - -fn get_last_occupied_idx(list: &LinkedList<'_, T>, start: usize, end: usize) -> Option { - (start..end).rev().find(|&i| !list.is_vacant(i)) -} - -#[cfg(test)] -mod tests { - #![allow(clippy::unwrap_used)] - - use super::*; - use crate::linked_list::{tests::storage_to_datavec, LinkedList}; - use std::fmt::Debug; - type List<'a, T> = LinkedList<'a, T>; - - fn status_of(storage: &[Option]) -> MemoryStatus { - MemoryStatus { - num_active_nodes: storage.iter().filter(|x| x.is_some()).count(), - num_occupied_nodes: storage.len(), - } - } - fn assert_storage(list: &List, expected_storage: &[Option]) - where - T: Debug + Clone + PartialEq, - { - assert_eq!(expected_storage, storage_to_datavec(list).as_slice()); - assert_eq!(status_of(expected_storage), list.memory_status()); - } - - #[test] - fn will_auto_reclaim() { - let eager = MemoryUtilization::Eager; - assert!(!eager.will_auto_reclaim(0, 0)); - assert!(!eager.will_auto_reclaim(10, 10)); - assert!(eager.will_auto_reclaim(0, 1)); - assert!(eager.will_auto_reclaim(10, 11)); - - let lazy = MemoryUtilization::Lazy; - assert!(!lazy.will_auto_reclaim(0, 0)); - assert!(!lazy.will_auto_reclaim(10, 10)); - assert!(!lazy.will_auto_reclaim(0, 1)); - assert!(!lazy.will_auto_reclaim(10, 11)); - - let threashold_eager = MemoryUtilization::WithThreshold(1.0); - assert!(!threashold_eager.will_auto_reclaim(0, 0)); - assert!(!threashold_eager.will_auto_reclaim(10, 10)); - assert!(threashold_eager.will_auto_reclaim(0, 1)); - assert!(threashold_eager.will_auto_reclaim(10, 11)); - - let threashold_lazy = MemoryUtilization::WithThreshold(0.0); - assert!(!threashold_lazy.will_auto_reclaim(0, 0)); - assert!(!threashold_lazy.will_auto_reclaim(10, 10)); - assert!(!threashold_lazy.will_auto_reclaim(0, 1)); - assert!(!threashold_lazy.will_auto_reclaim(10, 11)); - - let threashold_eager = MemoryUtilization::WithThreshold(0.25); - assert!(!threashold_eager.will_auto_reclaim(0, 0)); - assert!(!threashold_eager.will_auto_reclaim(10, 10)); - assert!(threashold_eager.will_auto_reclaim(0, 1)); - assert!(!threashold_eager.will_auto_reclaim(5, 16)); - assert!(!threashold_eager.will_auto_reclaim(4, 16)); - assert!(threashold_eager.will_auto_reclaim(3, 16)); - - let threashold_eager = MemoryUtilization::WithThreshold(0.75); - assert!(!threashold_eager.will_auto_reclaim(0, 0)); - assert!(!threashold_eager.will_auto_reclaim(10, 10)); - assert!(threashold_eager.will_auto_reclaim(0, 1)); - assert!(!threashold_eager.will_auto_reclaim(13, 16)); - assert!(!threashold_eager.will_auto_reclaim(12, 16)); - assert!(threashold_eager.will_auto_reclaim(11, 16)); - } - - #[test] - fn reclaim_when_no_holes() { - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - assert_storage(&list, &[Some('a'), Some('b'), Some('c')]); - - reclaim_memory(&mut list, 3); - - assert_storage(&list, &[Some('a'), Some('b'), Some('c')]); - } - - #[test] - fn reclaim_after_pop_back() { - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - assert_storage(&list, &[Some('a'), Some('b'), Some('c')]); - - list.push_back('d'); - _ = list.pop_back(); - assert_storage(&list, &[Some('a'), Some('b'), Some('c'), None]); - - reclaim_memory(&mut list, 4); - assert_storage(&list, &[Some('a'), Some('b'), Some('c')]); - - list.push_back('d'); - list.push_back('e'); - _ = list.pop_back(); - _ = list.pop_back(); - assert_storage(&list, &[Some('a'), Some('b'), Some('c'), None, None]); - - reclaim_memory(&mut list, 5); - assert_storage(&list, &[Some('a'), Some('b'), Some('c')]); - } - - #[test] - fn reclaim_after_pop_front() { - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - _ = list.pop_front(); - assert_storage(&list, &[None, Some('b'), Some('c')]); - - reclaim_memory(&mut list, 3); - assert_storage(&list, &[Some('c'), Some('b')]); - - reclaim_memory(&mut list, 2); - assert_storage(&list, &[Some('c'), Some('b')]); - - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - _ = list.pop_front(); - _ = list.pop_front(); - assert_storage(&list, &[None, None, Some('c')]); - - reclaim_memory(&mut list, 3); - assert_storage(&list, &[Some('c')]); - - reclaim_memory(&mut list, 1); - assert_storage(&list, &[Some('c')]); - } - - #[test] - fn reclaim_after_pop_back_front() { - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - _ = list.pop_front(); - assert_storage(&list, &[None, Some('b'), Some('c')]); - - _ = list.pop_back(); - assert_storage(&list, &[None, Some('b'), None]); - - reclaim_memory(&mut list, 3); - assert_storage(&list, &[Some('b')]); - - reclaim_memory(&mut list, 1); - assert_storage(&list, &[Some('b')]); - } - - #[test] - fn reclaim_after_remove() { - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - - _ = list.remove_at(1); - assert_storage(&list, &[Some('a'), None, Some('c')]); - - reclaim_memory(&mut list, 3); - assert_storage(&list, &[Some('a'), Some('c')]); - - let mut list = List::new().with_memory_utilization(MemoryUtilization::Lazy); - list.push_back('a'); - list.push_back('b'); - list.push_back('c'); - list.push_back('d'); - list.push_back('e'); - - _ = list.remove_at(3); - _ = list.remove_at(1); - assert_storage(&list, &[Some('a'), None, Some('c'), None, Some('e')]); - - reclaim_memory(&mut list, 5); - assert_storage(&list, &[Some('a'), Some('e'), Some('c')]); - } -} diff --git a/src/node.rs b/src/node.rs deleted file mode 100644 index 02bcfed..0000000 --- a/src/node.rs +++ /dev/null @@ -1,196 +0,0 @@ -use orx_imp_vec::prelude::{SelfRefNext, SelfRefPrev}; - -/// A linked-list node holding the data together with previous & next references. -#[derive(Clone)] -pub struct Node<'a, T> { - data: Option, - prev: Option<&'a Self>, - next: Option<&'a Self>, -} - -impl<'a, T> Node<'a, T> { - /// Creates a new active node with some `data` and optional `prev` and `next` references. - pub(crate) fn active(data: T, prev: Option<&'a Self>, next: Option<&'a Self>) -> Self { - Self { - data: Some(data), - prev, - next, - } - } - - /// Creates a clsoed node without data and empty prev & next references. - pub(crate) fn closed() -> Self { - Default::default() - } - - /// Returns whether or not this node is closed. - pub(crate) fn is_closed(&self) -> bool { - self.data.is_none() - } - - /// Returns a mutable reference to the data of the node if active; None otherwise. - pub(crate) fn data_mut(&mut self) -> Option<&mut T> { - self.data.as_mut() - } - - /// Returns a reference to the data of the node if active. - /// - /// # Panics - /// - /// Panics if the node is a closed node with missing data. - pub(crate) fn data_unchecked(&self) -> &T { - self.data.as_ref().expect("is-some") - } - - // test - #[cfg(test)] - /// Returns a reference to the data of the node if active; None otherwise. - pub(crate) fn data(&self) -> Option<&T> { - self.data.as_ref() - } -} - -impl<'a, T> Default for Node<'a, T> { - fn default() -> Self { - Self { - data: None, - prev: None, - next: None, - } - } -} - -impl<'a, T> SelfRefNext<'a> for Node<'a, T> { - #[inline(always)] - fn next(&self) -> Option<&'a Self> { - self.next - } - #[inline(always)] - fn set_next(&mut self, next: Option<&'a Self>) { - self.next = next; - } -} - -impl<'a, T> SelfRefPrev<'a> for Node<'a, T> { - #[inline(always)] - fn prev(&self) -> Option<&'a Self> { - self.prev - } - #[inline(always)] - fn set_prev(&mut self, prev: Option<&'a Self>) { - self.prev = prev; - } -} - -impl<'a, T> From> for Option { - fn from(value: Node<'a, T>) -> Self { - value.data - } -} - -#[cfg(test)] -mod tests { - use super::Node; - use orx_imp_vec::prelude::{SelfRefNext, SelfRefPrev}; - - #[test] - fn new_node() { - let other_node = Node { - data: Some('a'), - prev: None, - next: None, - }; - - let node = Node::active('b', Some(&other_node), None); - assert_eq!(Some('b'), node.data); - assert!(node.next.is_none()); - assert_eq!(Some('a'), node.prev.and_then(|x| x.data)); - - let node = Node::active('b', Some(&other_node), Some(&other_node)); - assert_eq!(Some('b'), node.data); - assert_eq!(Some('a'), node.next.and_then(|x| x.data)); - assert_eq!(Some('a'), node.prev.and_then(|x| x.data)); - } - #[test] - fn closed_default() { - fn assert_empty(node: Node) { - assert!(node.data.is_none()); - assert!(node.next.is_none()); - assert!(node.prev.is_none()); - } - assert_empty(Node::closed()); - assert_empty(Node::default()); - } - - #[test] - fn into_data() { - let node: Node<'_, char> = Node::closed(); - let data: Option = node.into(); - assert!(data.is_none()); - - let node = Node::active('a', None, None); - let data = node.into(); - assert_eq!(Some('a'), data); - } - - #[test] - fn is_closed() { - let node: Node<'_, ()> = Node::closed(); - assert!(node.is_closed()); - - let node: Node<'_, ()> = Node::default(); - assert!(node.is_closed()); - - let node = Node::active('a', None, None); - assert!(!node.is_closed()); - } - - #[test] - fn data() { - let mut node = Node::active('a', None, None); - - assert_eq!(Some(&'a'), node.data()); - assert_eq!(&'a', node.data_unchecked()); - - if let Some(data) = node.data_mut() { - *data = 'b'; - } - assert_eq!(Some(&'b'), node.data()); - assert_eq!(&'b', node.data_unchecked()); - } - #[test] - #[should_panic] - fn data_unchecked_for_closed_node() { - let node: Node<'_, char> = Node::closed(); - let _data = node.data_unchecked(); - } - - #[test] - fn set_next() { - let mut node = Node::active('a', None, None); - let other = Node::active('x', None, None); - - assert!(node.next().is_none()); - - node.set_next(Some(&other)); - - assert_eq!(Some(&'x'), node.next().and_then(|x| x.data())); - - node.set_next(None); - assert!(node.next().is_none()); - } - #[test] - fn set_prev() { - let mut node = Node::active('a', None, None); - let other = Node::active('x', None, None); - - assert!(node.prev().is_none()); - - node.set_prev(Some(&other)); - - assert_eq!(Some(&'x'), node.prev().and_then(|x| x.data())); - - node.set_prev(None); - assert!(node.prev().is_none()); - } -} diff --git a/src/option_utils.rs b/src/option_utils.rs new file mode 100644 index 0000000..1b78566 --- /dev/null +++ b/src/option_utils.rs @@ -0,0 +1,35 @@ +#[inline(always)] +pub fn some_only_if(some_condition: bool, value: Option) -> Option { + if some_condition { + value + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::some_only_if; + + #[test] + fn some_only_if_true() { + let none: Option = None; + let result = some_only_if(true, none); + assert_eq!(result, None); + + let some = Some(12); + let result = some_only_if(true, some); + assert_eq!(result, Some(12)); + } + + #[test] + fn some_only_if_false() { + let none: Option = None; + let result = some_only_if(false, none); + assert_eq!(result, None); + + let some = Some(12); + let result = some_only_if(false, some); + assert_eq!(result, None); + } +} diff --git a/src/variants/doubly.rs b/src/variants/doubly.rs new file mode 100644 index 0000000..36d9f40 --- /dev/null +++ b/src/variants/doubly.rs @@ -0,0 +1,52 @@ +use super::{ends::ListEnds, list_variant::ListVariant}; +use orx_selfref_col::{ + MemoryReclaimOnThreshold, Node, NodeDataLazyClose, NodeRefSingle, NodeRefs, NodeRefsArray, + Variant, +}; + +pub type EndsDoubly<'a, T> = NodeRefsArray<'a, 2, Doubly, T>; + +impl<'a, T> ListEnds<'a, Doubly, T> for EndsDoubly<'a, T> { + fn front(&self) -> Option<&'a Node<'a, Doubly, T>> { + self.get()[0] + } + + fn back(&self) -> Option<&'a Node<'a, Doubly, T>> { + self.get()[1] + } +} + +/// A doubly linked list variant such that: +/// * Each node contains three data elements: the value of the element, a reference to the previous node and a reference to the next node. +/// * The list keeps track of its `front` and `back`. +/// * It is possible to iterate from the `front` to the `back` of the list with `iter` method; +/// and from the `back` to the `front` with `iter_from_back` method. +pub struct Doubly; + +impl<'a, T> Variant<'a, T> for Doubly +where + T: 'a, +{ + type Storage = NodeDataLazyClose; + + type Prev = NodeRefSingle<'a, Self, T>; + + type Next = NodeRefSingle<'a, Self, T>; + + type Ends = EndsDoubly<'a, T>; + + type MemoryReclaim = MemoryReclaimOnThreshold<2>; +} + +impl<'a, T> ListVariant<'a, T> for Doubly +where + T: 'a, +{ + #[cfg(test)] + fn validate(list: &crate::list::List<'a, Self, T>) + where + Self::Ends: ListEnds<'a, Self, T>, + { + list.validate_list(); + } +} diff --git a/src/variants/ends.rs b/src/variants/ends.rs new file mode 100644 index 0000000..3f61a39 --- /dev/null +++ b/src/variants/ends.rs @@ -0,0 +1,12 @@ +use super::list_variant::ListVariant; +use orx_selfref_col::{Node, NodeRefsArray}; + +pub trait ListEnds<'a, V, T> +where + T: 'a, + V: ListVariant<'a, T, Ends = NodeRefsArray<'a, 2, V, T>>, +{ + fn front(&self) -> Option<&'a Node<'a, V, T>>; + + fn back(&self) -> Option<&'a Node<'a, V, T>>; +} diff --git a/src/variants/list_variant.rs b/src/variants/list_variant.rs new file mode 100644 index 0000000..6a2aa4f --- /dev/null +++ b/src/variants/list_variant.rs @@ -0,0 +1,19 @@ +use orx_selfref_col::{NodeDataLazyClose, NodeRefSingle, NodeRefsArray, Variant}; + +pub trait ListVariant<'a, T>: + Variant< + 'a, + T, + Storage = NodeDataLazyClose, + Next = NodeRefSingle<'a, Self, T>, + Ends = NodeRefsArray<'a, 2, Self, T>, +> +where + Self: 'a, + T: 'a, +{ + #[cfg(test)] + fn validate(list: &crate::list::List<'a, Self, T>) + where + Self::Ends: crate::variants::ends::ListEnds<'a, Self, T>; +} diff --git a/src/variants/mod.rs b/src/variants/mod.rs new file mode 100644 index 0000000..44418fc --- /dev/null +++ b/src/variants/mod.rs @@ -0,0 +1,4 @@ +pub mod doubly; +pub mod ends; +pub mod list_variant; +pub mod singly; diff --git a/src/variants/singly.rs b/src/variants/singly.rs new file mode 100644 index 0000000..2d8023d --- /dev/null +++ b/src/variants/singly.rs @@ -0,0 +1,51 @@ +use super::{ends::ListEnds, list_variant::ListVariant}; +use orx_selfref_col::{ + MemoryReclaimOnThreshold, Node, NodeDataLazyClose, NodeRefNone, NodeRefSingle, NodeRefs, + NodeRefsArray, Variant, +}; + +pub type EndsSingly<'a, T> = NodeRefsArray<'a, 2, Singly, T>; + +impl<'a, T> ListEnds<'a, Singly, T> for EndsSingly<'a, T> { + fn front(&self) -> Option<&'a Node<'a, Singly, T>> { + self.get()[0] + } + + fn back(&self) -> Option<&'a Node<'a, Singly, T>> { + self.get()[1] + } +} + +/// A singly linked list variant such that: +/// * Each node contains two data elements: the value of the element and a reference to the next node. +/// * The list keeps track of its `front`. +/// * It is possible to iterate from the `front` to the back of the list. +pub struct Singly; + +impl<'a, T> Variant<'a, T> for Singly +where + T: 'a, +{ + type Storage = NodeDataLazyClose; + + type Prev = NodeRefNone; + + type Next = NodeRefSingle<'a, Self, T>; + + type Ends = EndsSingly<'a, T>; + + type MemoryReclaim = MemoryReclaimOnThreshold<2>; +} + +impl<'a, T> ListVariant<'a, T> for Singly +where + T: 'a, +{ + #[cfg(test)] + fn validate(list: &crate::list::List<'a, Self, T>) + where + Self::Ends: crate::variants::ends::ListEnds<'a, Self, T>, + { + list.validate_list(); + } +} diff --git a/tests/pinned.rs b/tests/pinned.rs new file mode 100644 index 0000000..dd46456 --- /dev/null +++ b/tests/pinned.rs @@ -0,0 +1,69 @@ +use orx_linked_list::{Doubly, List, Singly}; + +const LARGE_LEN: usize = 1024 * 16; + +#[test] +fn large_list_singly() { + let mut list: List = List::new(); + + for i in 0..LARGE_LEN { + list.push_front(LARGE_LEN - 1 - i); + } + + for _ in 0..(LARGE_LEN / 2) { + list.pop_front(); + } + + for i in (LARGE_LEN / 2)..LARGE_LEN { + list.push_front(LARGE_LEN - 1 - i); + } + + for (i, x) in list.iter().enumerate() { + assert_eq!(i, *x); + } + + assert_eq!(LARGE_LEN, list.iter().len()); + + for i in 0..LARGE_LEN { + let x = list.pop_front(); + let x = x.expect("must be some"); + assert_eq!(i, x); + } + + assert!(list.is_empty()); +} + +#[test] +fn large_list_doubly() { + let mut list: List = List::new(); + + for i in 0..LARGE_LEN { + list.push_back(i); + } + + for _ in 0..(LARGE_LEN / 2) { + list.pop_back(); + } + + for i in (LARGE_LEN / 2)..LARGE_LEN { + list.push_back(i); + } + + for (i, x) in list.iter().enumerate() { + assert_eq!(i, *x); + } + for (i, x) in list.iter_from_back().enumerate() { + assert_eq!(LARGE_LEN - 1 - i, *x); + } + + assert_eq!(LARGE_LEN, list.iter().len()); + assert_eq!(LARGE_LEN, list.iter_from_back().len()); + + for i in 0..LARGE_LEN { + let x = list.pop_front(); + let x = x.expect("must be some"); + assert_eq!(i, x); + } + + assert!(list.is_empty()); +}