use cjc_repro::KahanAccumulatorF64;
pub fn window_sum(data: &[f64], window_size: usize) -> Vec<f64> {
if window_size == 0 || window_size > data.len() {
return Vec::new();
}
let n = data.len() - window_size + 1;
let mut result = Vec::with_capacity(n);
for i in 0..n {
let mut acc = KahanAccumulatorF64::new();
for j in 0..window_size {
acc.add(data[i + j]);
}
result.push(acc.finalize());
}
result
}
pub fn window_mean(data: &[f64], window_size: usize) -> Vec<f64> {
if window_size == 0 || window_size > data.len() {
return Vec::new();
}
let n = data.len() - window_size + 1;
let ws = window_size as f64;
let mut result = Vec::with_capacity(n);
for i in 0..n {
let mut acc = KahanAccumulatorF64::new();
for j in 0..window_size {
acc.add(data[i + j]);
}
result.push(acc.finalize() / ws);
}
result
}
pub fn window_min(data: &[f64], window_size: usize) -> Vec<f64> {
if window_size == 0 || window_size > data.len() {
return Vec::new();
}
let n = data.len() - window_size + 1;
let mut result = Vec::with_capacity(n);
for i in 0..n {
let mut min_val = data[i];
for j in 1..window_size {
let v = data[i + j];
if v < min_val {
min_val = v;
}
}
result.push(min_val);
}
result
}
pub fn window_max(data: &[f64], window_size: usize) -> Vec<f64> {
if window_size == 0 || window_size > data.len() {
return Vec::new();
}
let n = data.len() - window_size + 1;
let mut result = Vec::with_capacity(n);
for i in 0..n {
let mut max_val = data[i];
for j in 1..window_size {
let v = data[i + j];
if v > max_val {
max_val = v;
}
}
result.push(max_val);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_window_sum_basic() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = window_sum(&data, 3);
assert_eq!(result, vec![6.0, 9.0, 12.0]);
}
#[test]
fn test_window_sum_full() {
let data = vec![1.0, 2.0, 3.0];
let result = window_sum(&data, 3);
assert_eq!(result, vec![6.0]);
}
#[test]
fn test_window_sum_single() {
let data = vec![1.0, 2.0, 3.0];
let result = window_sum(&data, 1);
assert_eq!(result, vec![1.0, 2.0, 3.0]);
}
#[test]
fn test_window_sum_empty_on_too_large() {
let data = vec![1.0, 2.0];
let result = window_sum(&data, 5);
assert!(result.is_empty());
}
#[test]
fn test_window_sum_empty_on_zero() {
let data = vec![1.0, 2.0, 3.0];
let result = window_sum(&data, 0);
assert!(result.is_empty());
}
#[test]
fn test_window_mean_basic() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = window_mean(&data, 3);
assert_eq!(result, vec![2.0, 3.0, 4.0]);
}
#[test]
fn test_window_min_basic() {
let data = vec![3.0, 1.0, 4.0, 1.0, 5.0];
let result = window_min(&data, 3);
assert_eq!(result, vec![1.0, 1.0, 1.0]);
}
#[test]
fn test_window_max_basic() {
let data = vec![3.0, 1.0, 4.0, 1.0, 5.0];
let result = window_max(&data, 3);
assert_eq!(result, vec![4.0, 4.0, 5.0]);
}
#[test]
fn test_window_determinism() {
let data: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let r1 = window_sum(&data, 7);
let r2 = window_sum(&data, 7);
assert_eq!(r1, r2, "window_sum must be deterministic");
}
#[test]
fn test_window_kahan_accuracy() {
let n = 1000;
let data: Vec<f64> = vec![0.1; n];
let result = window_sum(&data, n);
assert_eq!(result.len(), 1);
let err = (result[0] - 100.0).abs();
assert!(
err < 1e-12,
"Kahan sum of 1000×0.1 should be close to 100.0, got {} (err={})",
result[0], err,
);
}
}