cutoff_list 0.2.0

A linked-list structure where each element tracks how many predefined "cutoff points" precede or coincide with its position in the list sequence.
Documentation
use super::CutoffList;

use index_list::Index;
use quickcheck::{Arbitrary, Gen, TestResult};
use quickcheck_macros::quickcheck;

#[derive(Debug, Clone, Copy)]
enum Op {
    InsertFirst,
    InsertLast,
    ShiftToFront(usize),
    Remove(usize),
}

impl Arbitrary for Op {
    fn arbitrary(g: &mut Gen) -> Self {
        match g.choose(&[0, 1, 2, 3]).unwrap() {
            0 => Op::InsertFirst,
            1 => Op::InsertLast,
            2 => Op::ShiftToFront(usize::arbitrary(g)),
            3 => Op::Remove(usize::arbitrary(g)),
            _ => unreachable!(),
        }
    }
}

fn get_indices<T>(list: &CutoffList<T>) -> Vec<Index> {
    let mut indices = Vec::new();
    let mut idx = list.first_index();
    while idx.is_some() {
        indices.push(idx);
        idx = list.next_index(idx);
    }
    indices
}

#[quickcheck]
fn qc_cutoff_list_all_ops(cutoffs: Vec<usize>, ops: Vec<Op>) -> TestResult {
    let mut unique_cutoffs = cutoffs;
    unique_cutoffs.sort_unstable();
    unique_cutoffs.dedup();
    const MAX_CUTOFF_VAL: usize = 150;
    const MAX_CUTOFF_COUNT: usize = 40;
    unique_cutoffs.retain(|&c| c <= MAX_CUTOFF_VAL);
    unique_cutoffs.truncate(MAX_CUTOFF_COUNT);
    const MAX_OPS: usize = 250;
    let limited_ops = &ops[..ops.len().min(MAX_OPS)];

    let mut list: CutoffList<()> = CutoffList::new(unique_cutoffs);

    for op in limited_ops {
        match op {
            Op::InsertFirst => {
                list.insert_first(());
            }
            Op::InsertLast => {
                list.insert_last(());
            }
            Op::ShiftToFront(rand_val) => {
                let valid_indices = get_indices(&list);
                if !valid_indices.is_empty() {
                    let index_to_shift = valid_indices[rand_val % valid_indices.len()];
                    list.shift_to_front(index_to_shift);
                }
            }
            Op::Remove(rand_val) => {
                let valid_indices = get_indices(&list);
                if !valid_indices.is_empty() {
                    let index_to_remove = valid_indices[rand_val % valid_indices.len()];
                    list.remove(index_to_remove);
                }
            }
        }
    }

    list.validate();

    TestResult::passed()
}