use std::ops::Range;
pub fn find_subrange<T>(
slice: &[T],
eventually_true: impl Fn(&T) -> bool,
eventually_false: impl Fn(&T) -> bool,
) -> Range<usize> {
let start = slice.partition_point(|t| !eventually_true(t));
let end = slice.partition_point(eventually_false);
start..end
}
pub fn exponential_partition_point<T>(slice: &[T], eventually_false: impl Fn(&T) -> bool) -> usize {
let mut test_idx = 0;
let mut step = 1;
let mut to_test = slice;
let mut skipped = 0;
while test_idx < to_test.len() {
let test = eventually_false(&to_test[test_idx]);
if test {
to_test = &to_test[test_idx..];
skipped += test_idx;
test_idx = step;
step *= 2;
} else {
to_test = &to_test[..test_idx];
break;
}
}
skipped + to_test.partition_point(eventually_false)
}
pub fn shift_range(range: Range<usize>, offset: usize) -> Range<usize> {
let start = range.start.min(range.end);
let new_end = range
.end
.checked_add(offset)
.expect("offset too large to shift range by");
debug_assert!(start.checked_add(offset).is_some());
let new_start = start + offset;
new_start..new_end
}
#[cfg(test)]
#[test]
fn test_exponential_partition() {
#[track_caller]
fn test_property<T>(slice: &[T], eventually_false: impl Fn(&T) -> bool) {
assert_eq!(
slice.partition_point(&eventually_false),
exponential_partition_point(slice, &eventually_false)
);
}
let v = [1, 2, 3, 3, 5, 6, 7];
let is_small = |&x: &_| x < 5;
test_property(&v, is_small);
let a = [2, 4, 8];
let is_small = |&x: &_| x < 100;
test_property(&a, is_small);
let a: [i32; 0] = [];
test_property(&a, is_small);
let s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
let num = 42;
test_property(&s, |&x| x <= num);
}