use crate::Endpoint;
use crate::Pair;
pub trait Sequence: Copy {
type EndT: Endpoint;
fn uncons(self) -> Option<(Pair<Self::EndT>, Self)>;
fn skip_to(self, x: Pair<Self::EndT>, cursor: Self) -> Self;
}
#[inline(always)]
fn compute_linear_work_factor(len: usize) -> usize {
const MIN_LINEAR_WORK: usize = 8;
const LINEAR_WORK_MASK: usize = (1usize << (1 + MIN_LINEAR_WORK / 2)) - 1;
const _: () = assert!(LINEAR_WORK_MASK.count_ones() as usize == 1 + MIN_LINEAR_WORK / 2);
2 * (len | LINEAR_WORK_MASK).ilog2() as usize
}
impl<'a, T: 'a + Endpoint> Sequence for &'a [(T, T)] {
type EndT = T;
#[inline(always)]
fn uncons(self) -> Option<((T, T), Self)> {
let head = self.first()?;
Some((*head, &self[1..]))
}
#[inline(never)]
fn skip_to(self, x: (T, T), cursor: Self) -> Self {
let doit = || {
let linear_work_factor = compute_linear_work_factor(self.len());
skip_to_linear_search(cursor, x, linear_work_factor)
.unwrap_or_else(|| skip_to_binary_search(self, x))
};
#[cfg(feature = "internal_checks")]
slice_skip_to_preconditions(self, x, cursor);
let ret = doit();
#[cfg(feature = "internal_checks")]
slice_skip_to_guarantees(ret, self, x);
ret
}
}
#[allow(dead_code)]
#[inline(always)]
fn slice_skip_to_preconditions<T: Endpoint>(this: &[(T, T)], x: (T, T), cursor: &[(T, T)]) {
use core::cmp::Ordering;
assert!(this.is_sorted_by(|x, y| T::cmp_range(*x, *y) <= Ordering::Equal));
assert!(cursor.is_sorted_by(|x, y| T::cmp_range(*x, *y) <= Ordering::Equal));
assert!(this.len() >= cursor.len());
assert!(core::ptr::eq(&this[this.len() - cursor.len()..], cursor));
if let Some(first) = this.first().copied() {
if T::cmp_range(first, x) < Ordering::Equal {
let cursor_first = cursor
.first()
.expect("cursor must start at value less than x");
assert!(T::cmp_range(*cursor_first, x) < Ordering::Equal);
} else {
assert!(this.len() == cursor.len());
assert!(core::ptr::eq(this, cursor));
}
} else {
assert!(cursor.is_empty());
}
}
#[allow(dead_code)]
#[inline(always)]
fn slice_skip_to_guarantees<T: Endpoint>(ret: &[(T, T)], this: &[(T, T)], x: (T, T)) {
use core::cmp::Ordering;
assert!(ret.len() <= this.len());
assert!(core::ptr::eq(ret, &this[this.len() - ret.len()..]));
match this.first().copied() {
Some(first) if T::cmp_range(first, x) < Ordering::Equal => {
assert!(
T::cmp_range(ret.first().copied().expect("should have initial value"), x)
< Ordering::Equal
);
if let Some(next) = ret.get(1) {
assert!(T::cmp_range(*next, x) >= Ordering::Equal);
}
}
_ => {
assert!(core::ptr::eq(ret, this));
}
}
}
#[inline(always)]
fn skip_to_linear_search<T: Endpoint>(
cursor: &[(T, T)],
needle: (T, T),
linear_work_factor: usize,
) -> Option<&[(T, T)]> {
use core::cmp::Ordering;
for (idx, y) in cursor.iter().skip(1).enumerate().take(linear_work_factor) {
if T::cmp_range(*y, needle) >= Ordering::Equal {
return Some(&cursor[idx..]);
}
}
None
}
#[inline(always)]
fn skip_to_binary_search<T: Endpoint>(haystack: &[(T, T)], needle: (T, T)) -> &[(T, T)] {
use core::cmp::Ordering;
let idx = haystack.partition_point(|x| T::cmp_range(*x, needle) < Ordering::Equal);
&haystack[idx.saturating_sub(1)..]
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod test {
use super::*;
use alloc::vec::Vec;
#[test]
fn test_skip_to_slices_subsearch() {
let mut sequence: Vec<u8> = Vec::with_capacity(200);
sequence.extend([4, 5, 6, 7]);
for i in (1..32).step_by(2) {
sequence.push(i + 1);
if i < 5 || (10..15).contains(&i) || i > 25 {
sequence.push(i + 1);
}
}
sequence.sort();
assert!(sequence[0] > 0);
assert!(*sequence.last().unwrap() < 100);
let sequence = sequence.into_iter().map(|x| (x, x)).collect::<Vec<_>>();
let sequence: &[(u8, u8)] = &sequence;
for needle in 0..=255u8 {
let needle = (needle, needle);
let hit = skip_to_binary_search(sequence, needle);
slice_skip_to_guarantees(hit, sequence, needle);
assert!(hit.len() <= sequence.len());
assert_eq!(hit, &sequence[sequence.len() - hit.len()..]);
assert!(core::ptr::eq(hit, &sequence[sequence.len() - hit.len()..]));
if needle <= sequence[0] {
assert_eq!(hit, sequence);
} else if needle > *sequence.last().unwrap() {
assert_eq!(hit, &[*sequence.last().unwrap()]);
} else {
assert!(*hit.first().unwrap() < needle);
assert!(*hit.get(1).unwrap() >= needle);
}
if let Some(linear_hit) = skip_to_linear_search(sequence, needle, usize::MAX) {
assert_eq!(hit, linear_hit);
assert!(core::ptr::eq(hit, linear_hit));
} else {
assert_eq!(hit, &[*sequence.last().unwrap()]);
}
}
}
#[test]
fn test_uncons() {
let sequence = [(1u8, 2u8), (10u8, 20u8)];
let mut sequence = &sequence[..];
{
let head;
(head, sequence) = sequence.uncons().expect("non-empty");
assert_eq!(head, (1, 2));
}
{
let head;
(head, sequence) = sequence.uncons().expect("non-empty");
assert_eq!(head, (10, 20));
}
assert_eq!(sequence.uncons(), None);
}
#[test]
fn test_skip_to_slices() {
let mut sequence: Vec<(u8, u8)> = Vec::with_capacity(200);
sequence.extend([4, 5, 6, 7].map(|x| (x, x)));
for i in (1..32).step_by(2) {
sequence.push((i + 1, i + 1));
if i < 5 || (10..15).contains(&i) || i > 25 {
sequence.push((i + 1, i + 1));
}
}
sequence.sort();
assert!(sequence[0] > (0, 0));
assert!(*sequence.last().unwrap() < (100, 100));
let sequence: &[(u8, u8)] = &sequence;
let mut cursor = sequence;
for needle in (0..=255u8).map(|x| (x, x)) {
let hit = sequence.skip_to(needle, cursor);
assert!(hit.len() <= sequence.len());
assert_eq!(hit, &sequence[sequence.len() - hit.len()..]);
assert!(core::ptr::eq(hit, &sequence[sequence.len() - hit.len()..]));
assert!(hit.len() <= cursor.len());
assert_eq!(hit, &cursor[cursor.len() - hit.len()..]);
assert!(core::ptr::eq(hit, &cursor[cursor.len() - hit.len()..]));
cursor = hit;
if needle <= sequence[0] {
assert_eq!(hit, sequence);
} else if needle > *sequence.last().unwrap() {
assert_eq!(hit, &[*sequence.last().unwrap()]);
} else {
assert!(*hit.first().unwrap() < needle);
assert!(*hit.get(1).unwrap() >= needle);
}
for cursor_start in 0..sequence.len() {
let prefix = &sequence[cursor_start..];
let other_hit = sequence.skip_to(needle, prefix);
assert_eq!(hit, other_hit);
assert!(core::ptr::eq(hit, other_hit));
if core::ptr::eq(prefix, cursor) {
break;
}
}
}
}
#[test]
fn test_skip_to_slices_empty() {
let sequence: &[(u8, u8)] = &[];
for needle in (0..=255u8).map(|x| (x, x)) {
assert_eq!(skip_to_linear_search(sequence, needle, usize::MAX), None);
assert_eq!(skip_to_binary_search(sequence, needle), sequence);
assert_eq!(sequence.skip_to(needle, sequence), sequence);
}
}
#[test]
fn test_work_factor() {
assert_eq!(compute_linear_work_factor(0), 8);
for i in 0..32 {
assert_eq!(compute_linear_work_factor(i), 8);
}
assert_eq!(compute_linear_work_factor(32), 10);
assert_eq!(compute_linear_work_factor(33), 10);
assert_eq!(compute_linear_work_factor(256), 16);
assert_eq!(compute_linear_work_factor(512), 18);
assert_eq!(compute_linear_work_factor(usize::MAX), 126);
}
}