use crate::prelude::CppMapError;
use crate::prelude::SkipList;
use crate::skiplist::{HEAD_INDEX, IsLessThan};
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Debug;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) struct RoundedTen(pub(crate) i32);
impl IsLessThan for RoundedTen {
fn is_less_than(&self, other: &Self) -> bool {
self < other
}
}
impl Ord for RoundedTen {
fn cmp(&self, other: &Self) -> Ordering {
(self.0 / 10).cmp(&(other.0 / 10))
}
}
impl PartialOrd for RoundedTen {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for RoundedTen {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[allow(dead_code)]
fn test_free_pool_uniqueness<K, V>(list: &SkipList<K, V>)
where
K: Debug + Ord + Clone + IsLessThan,
V: Debug + Clone,
{
let mut l2: Vec<usize> = list.free_index_pool.iter().copied().collect();
l2.sort();
l2.dedup();
assert_eq!(
list.free_index_pool.len(),
l2.len(),
"free_index_pool_ contains duplicate elements"
);
assert!(
list.free_index_pool
.iter()
.all(|&i| list.nodes[i].kv.is_none())
);
}
#[allow(dead_code)]
pub fn test_head_and_tail_is_some<K, V>(list: &SkipList<K, V>)
where
K: Debug + Ord + Clone + IsLessThan,
V: Debug + Clone,
{
assert!(list.first().is_some());
assert!(list.last().is_some());
assert!(list.is_pos_valid(list.first()));
assert!(list.is_pos_valid(list.last()));
}
#[allow(dead_code)]
pub fn test_head_and_tail_is_none<K, V>(list: &SkipList<K, V>)
where
K: Debug + Ord + Clone + IsLessThan,
V: Debug + Clone,
{
assert!(list.first().is_none());
assert!(list.last().is_none());
}
#[test]
fn test_remove() {
let mut list = SkipList::default();
let _ = list.insert(1, "one");
let _ = list.insert(2, "two");
let _ = list.insert(3, "three");
assert_eq!(list.remove(&2).unwrap().1, "two");
test_free_pool_uniqueness(&list);
list.debug_print();
assert_eq!(list.get(&2), None);
assert_eq!(list.remove(&2), None); test_free_pool_uniqueness(&list);
test_head_and_tail_is_some(&list);
}
#[test]
fn test_free_pool_reuse() -> Result<(), CppMapError> {
let mut list = SkipList::default();
let _ = list.insert(1, "one")?;
let _ = list.insert(2, "two")?;
let initial_len = list.nodes.len();
let _ = list.remove(&1);
test_free_pool_uniqueness(&list);
test_head_and_tail_is_some(&list);
let _ = list.remove(&2);
test_free_pool_uniqueness(&list);
assert!(!list.free_index_pool.is_empty());
let _ = list.insert(3, "three")?;
assert!(list.nodes.len() <= initial_len);
test_head_and_tail_is_some(&list);
Ok(())
}
#[test]
fn test_remove_head() {
let mut list = SkipList::default();
let _ = list.insert(1, "one");
let _ = list.insert(2, "two");
assert_eq!(list.remove(&1), Some((1, "one")));
test_free_pool_uniqueness(&list);
assert_eq!(list.get(&1), None);
assert_eq!(list.get(&2), Some(&"two"));
test_head_and_tail_is_some(&list);
}
#[test]
fn test_remove_tail() {
let mut list = SkipList::default();
let _ = list.insert(1, "one");
let _ = list.insert(2, "two");
assert_eq!(list.remove(&2), Some((2, "two")));
test_free_pool_uniqueness(&list);
test_head_and_tail_is_some(&list);
assert_eq!(list.get(&2), None);
assert_eq!(list.get(&1), Some(&"one"));
}
#[test]
fn test_get_by_index() -> Result<(), CppMapError> {
let mut list = SkipList::default();
let index1 = list.insert(1, "one")?;
let _ = list.insert(2, "two")?;
let index = list.first();
if index.is_some() {
assert_eq!(list.get_at(index1), Some((&1, &"one")));
} else {
panic!("Index not found");
}
Ok(())
}
#[test]
fn test_multiple_operations() -> Result<(), CppMapError> {
let mut list = SkipList::default();
let _ = list.insert(1, "one")?;
let _ = list.insert(2, "two")?;
let _ = list.insert(3, "three")?;
assert_eq!(list.remove(&2), Some((2, "two")));
assert_eq!(list.get(&2), None);
let _ = list.insert(4, "four")?;
assert_eq!(list.get(&4), Some(&"four"));
assert_eq!(list.remove(&1), Some((1, "one")));
test_free_pool_uniqueness(&list);
assert_eq!(list.remove(&3), Some((3, "three")));
test_free_pool_uniqueness(&list);
test_head_and_tail_is_some(&list);
assert_eq!(list.remove(&4), Some((4, "four")));
test_free_pool_uniqueness(&list);
test_head_and_tail_is_none(&list);
let _ = list.insert(3, "three")?;
let _ = list.insert(1, "one")?;
let _ = list.insert(0, "zero")?;
Ok(())
}
#[test]
fn test_remove_current_maintains_invariants() {
let mut list = SkipList::default();
let _ = list.insert(1, 1.0);
let _ = list.insert(2, 2.0);
let _ = list.insert(3, 3.0);
let mut cursor = list.lower_bound(&2);
assert_eq!(list.get_v_at(cursor.unwrap()), Some(2.0).as_ref());
let removed = list.remove_by_index(&mut cursor);
assert_eq!(removed, Some((2, 2.0)));
test_free_pool_uniqueness(&list);
test_head_and_tail_is_some(&list);
list.iter().for_each(|node| print!("{:?} ", node.1));
assert_eq!(list.len(), 2);
let cursor = list.lower_bound(&1);
let cursor = list.next_pos(cursor).unwrap();
assert_eq!(list.get_k_at(cursor), Some(&3));
let cursor = list.lower_bound(&3);
let cursor = list.prev_pos(cursor).unwrap();
assert_eq!(list.get_v_at(cursor), Some(&1.0));
}
#[test]
fn test_change_key_of_node() -> Result<(), CppMapError> {
let mut list = SkipList::default();
let _ = list.insert(RoundedTen(10), 10.0)?;
let _ = list.insert(RoundedTen(20), 20.0)?;
let mut cursor = list.first();
assert!(list.is_pos_valid(cursor));
assert_eq!(list.get_k_at(cursor.unwrap()), Some(&RoundedTen(10)));
let _ = list.change_key_of_node(cursor.unwrap(), RoundedTen(11))?;
assert_eq!(list.get_k_at(cursor.unwrap()), Some(&RoundedTen(11)));
cursor = list.next_pos(cursor);
assert!(list.is_pos_valid(cursor));
assert_eq!(list.get_k_at(cursor.unwrap()), Some(&RoundedTen(20)));
let _ = list.change_key_of_node(cursor.unwrap(), RoundedTen(21))?;
assert_eq!(list.get_k_at(cursor.unwrap()), Some(&RoundedTen(21)));
cursor = list.next_pos(cursor);
assert!(!list.is_pos_valid(cursor));
list.clear();
Ok(())
}
#[test]
#[allow(clippy::just_underscores_and_digits)]
fn test_sequential_find_position_1() -> Result<(), CppMapError> {
for _i in 0..100 {
let mut list = SkipList::default();
let _50 = list.insert(RoundedTen(50), 50.0)?;
list.validate();
let _10 = list.insert(RoundedTen(10), 10.0)?;
list.validate();
let _30 = list.insert(RoundedTen(30), 30.0)?;
list.validate();
let _40 = list.insert(RoundedTen(40), 40.0)?;
list.validate();
let _20 = list.insert(RoundedTen(20), 20.0)?;
list.validate();
let indices = [_10, _20, _30, _40, _50];
for hint in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(0), hint.0),
Some(HEAD_INDEX),
"failed when searching from {hint:?}",
);
}
for hint in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(20), hint.0),
Some(_10.0)
);
}
for hint in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(30), hint.0),
Some(_20.0)
);
}
for hint in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(40), hint.0),
Some(_30.0)
);
}
for index in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(70), index.0),
Some(_50.0)
);
}
for hint in indices {
assert_eq!(
list.sequential_find_position_(&RoundedTen(60), hint.0),
Some(_50.0)
);
}
}
Ok(())
}
#[test]
#[allow(clippy::just_underscores_and_digits)]
fn test_sequential_find_position_2() -> Result<(), CppMapError> {
for _i in 0..100 {
let mut list = SkipList::default();
let _0 = list.insert(0, 0.0)?;
list.debug_print();
list.validate();
let mut _1 = Some(list.insert(1, 1.0)?);
list.validate();
let _2 = list.insert(2, 2.0)?;
list.validate();
let _ = list.remove_by_index(&mut _1);
list.validate();
let indices = [_0, _2];
for hint in indices {
assert_eq!(list.sequential_find_position_(&2, hint.0), _1.map(|x| x.0));
}
}
Ok(())
}