use std::{
iter::Sum,
ops::{AddAssign, Sub, SubAssign},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CumulFreqTable<F = usize> {
sums: Box<[F]>,
}
impl<F> super::CumulFreqTable<F> for CumulFreqTable<F>
where
F: std::convert::From<u8> + Copy + AddAssign + SubAssign + Sub<Output = F> + Sum + PartialOrd,
{
fn new(len: usize) -> Self {
assert!(len > 0, "table must be non-empty");
Self {
sums: vec![0.into(); len].into_boxed_slice(),
}
}
fn with_freq(len: usize, init: F) -> Self {
assert!(len > 0, "table must be non-empty");
let mut sums = vec![0.into(); len].into_boxed_slice();
let mut total = init;
for sum in sums.iter_mut() {
*sum = total;
total += init;
}
Self { sums }
}
fn len(&self) -> usize {
self.sums.len()
}
fn add(&mut self, pos: usize, val: F) {
assert!(pos < self.sums.len(), "pos out of bounds");
for sum in self.sums[pos..].iter_mut() {
*sum += val;
}
}
fn sub(&mut self, pos: usize, val: F) {
assert!(pos < self.sums.len(), "pos out of bounds");
for sum in self.sums[pos..].iter_mut() {
*sum -= val;
}
}
fn sum(&self, pos: usize) -> F {
assert!(pos < self.sums.len(), "pos out of bounds");
self.sums[pos]
}
fn total(&self) -> F {
let r = self.sums.last().copied();
unsafe { r.unwrap_unchecked() }
}
fn freq(&self, pos: usize) -> F {
assert!(pos < self.sums.len(), "pos out of bounds");
if pos == 0 {
self.sums[0]
} else {
self.sums[pos] - self.sums[pos - 1]
}
}
fn find_by_sum(&self, sum: F) -> usize {
let r = self.sums.iter().position(|&i_sum| i_sum >= sum);
unsafe { r.unwrap_unchecked() }
}
fn scale<C: Fn(F) -> F>(&mut self, scale_freq: C) {
let mut psum: F = 0.into();
let mut spsum: F = 0.into();
for sum in self.sums.iter_mut() {
spsum += scale_freq(*sum - psum);
psum = std::mem::replace(sum, spsum);
}
}
}