pub struct FindChangePoints<F: Fn(u64) -> usize> {
func: F,
current: u64,
prev_value: usize,
}
impl<F: Fn(u64) -> usize> FindChangePoints<F> {
#[must_use]
pub fn new(func: F) -> Self {
Self {
func,
current: 0,
prev_value: usize::MAX,
}
}
}
impl<F: Fn(u64) -> usize> Iterator for FindChangePoints<F> {
type Item = (u64, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.current == 0 && self.prev_value == usize::MAX {
self.prev_value = (self.func)(0);
return Some((0, self.prev_value));
}
let mut step = 1;
loop {
if u64::MAX - self.current <= step {
return None;
}
let new_val = (self.func)(self.current + step);
debug_assert!(
new_val >= self.prev_value,
"Function is not monotonic as f({}) = {} < {} = f({})",
self.current + step,
new_val,
self.prev_value,
self.current,
);
if new_val != self.prev_value {
break;
}
step = step.saturating_mul(2);
}
let mut left = self.current + step / 2;
let mut right = self.current + step;
while left < right {
let mid = left + (right - left) / 2;
let mid_val = (self.func)(mid);
debug_assert!(
mid_val >= self.prev_value,
"Function is not monotonic as f({}) = {} < {} = f({})",
mid,
mid_val,
self.prev_value,
self.current,
);
if mid_val == self.prev_value {
left = mid + 1;
} else {
right = mid;
}
}
let new_value = (self.func)(left);
debug_assert!(
new_value >= self.prev_value,
"Function is not monotonic as f({}) = {} < {} = f({})",
left,
new_value,
self.prev_value,
self.current,
);
self.current = left;
self.prev_value = new_value;
Some((self.current, new_value))
}
}
#[cfg(test)]
mod tests {
use super::FindChangePoints;
#[test]
fn test_find_change_points() {
test_func(crate::codes::len_gamma);
test_func(crate::codes::len_delta);
test_func(crate::codes::len_omega);
test_func(|x| crate::codes::len_zeta(x, 3));
test_func(|x| crate::codes::len_pi(x, 3));
}
fn test_func(func: impl Fn(u64) -> usize) {
for (first, len) in FindChangePoints::new(&func) {
assert_eq!(func(first), len);
if first > 0 {
assert_ne!(func(first - 1), len);
}
}
}
}