pub trait ResidualSource {
fn residual_norms(&self) -> &[f32];
fn snr_estimate_db(&self) -> f32;
fn sample_count(&self) -> usize;
}
pub struct SliceSource<'a> {
norms: &'a [f32],
snr_db: f32,
}
impl<'a> SliceSource<'a> {
#[inline]
pub const fn new(norms: &'a [f32], snr_db: f32) -> Self {
Self { norms, snr_db }
}
}
impl<'a> ResidualSource for SliceSource<'a> {
#[inline]
fn residual_norms(&self) -> &[f32] { self.norms }
#[inline]
fn snr_estimate_db(&self) -> f32 { self.snr_db }
#[inline]
fn sample_count(&self) -> usize { self.norms.len() }
}
pub struct RingSource<const N: usize> {
buffer: [f32; N],
head: usize,
count: usize,
snr_db: f32,
}
impl<const N: usize> RingSource<N> {
pub const fn new(snr_db: f32) -> Self {
Self {
buffer: [0.0; N],
head: 0,
count: 0,
snr_db,
}
}
pub fn push(&mut self, norm: f32) {
self.buffer[self.head] = norm;
self.head = (self.head + 1) % N;
if self.count < N { self.count += 1; }
}
pub fn set_snr_db(&mut self, snr_db: f32) {
self.snr_db = snr_db;
}
}
impl<const N: usize> ResidualSource for RingSource<N> {
fn residual_norms(&self) -> &[f32] {
&self.buffer[..self.count.min(N)]
}
fn snr_estimate_db(&self) -> f32 { self.snr_db }
fn sample_count(&self) -> usize { self.count.min(N) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice_source_returns_original_data() {
let data = [0.1_f32, 0.2, 0.3, 0.4];
let src = SliceSource::new(&data, 15.0);
assert_eq!(src.residual_norms(), &data);
assert_eq!(src.snr_estimate_db(), 15.0);
assert_eq!(src.sample_count(), 4);
}
#[test]
fn ring_source_accumulates() {
let mut ring = RingSource::<4>::new(10.0);
ring.push(0.1);
ring.push(0.2);
assert_eq!(ring.sample_count(), 2);
ring.push(0.3);
ring.push(0.4);
ring.push(0.5); assert_eq!(ring.sample_count(), 4);
}
#[test]
fn ring_source_is_residual_source() {
let mut ring = RingSource::<8>::new(20.0);
for i in 0..5 {
ring.push(i as f32 * 0.1);
}
let src: &dyn ResidualSource = ˚
assert_eq!(src.sample_count(), 5);
assert_eq!(src.snr_estimate_db(), 20.0);
assert_eq!(src.residual_norms().len(), 5);
}
#[test]
fn zero_copy_no_allocation() {
let original = [1.0_f32, 2.0, 3.0];
let src = SliceSource::new(&original, 0.0);
let borrowed = src.residual_norms();
assert_eq!(borrowed.as_ptr(), original.as_ptr());
}
}