use nexus_stats::{Direction, detection::CusumF64, smoothing::EmaF64, statistics::WelfordF64};
fn main() {
let samples: &[f64] = &[
98.0, 102.0, 97.0, 103.0, 101.0, 99.0, 100.0, 104.0, 96.0, 101.0, 98.0, 103.0, 99.0, 102.0, 100.0, 97.0, 101.0, 104.0, 98.0, 100.0, 118.0, 122.0, 119.0, 121.0, 120.0, 123.0, 117.0, 121.0, 119.0, 122.0, 120.0, 118.0, 123.0, 121.0, 119.0, 122.0, 120.0, 117.0, 121.0, 120.0, ];
let mut cusum = CusumF64::builder(100.0)
.slack(5.0)
.threshold(30.0)
.build()
.unwrap();
let mut ema = EmaF64::builder().span(10).build().unwrap();
let mut stats = WelfordF64::new();
println!("sample raw ema mean std_dev cusum");
println!("------ --- --- ---- ------- -----");
for (i, &sample) in samples.iter().enumerate() {
let shift = cusum.update(sample).unwrap();
let smoothed = ema.update(sample).unwrap();
stats.update(sample).unwrap();
let ema_str = smoothed.map_or_else(|| " - ".into(), |v| format!("{v:6.1}"));
let mean_str = stats
.mean()
.map_or_else(|| " - ".into(), |v| format!("{v:6.1}"));
let sd_str = stats
.std_dev()
.map_or_else(|| " - ".into(), |v| format!("{v:6.1}"));
let shift_str = match shift {
Some(Direction::Rising) => " ↑ SHIFT",
Some(Direction::Falling) => " ↓ SHIFT",
Some(Direction::Neutral) => "",
None => " (warmup)",
};
println!(" {i:3} {sample:5.1} {ema_str} {mean_str} {sd_str} {shift_str}");
}
println!("\nFinal statistics:");
println!(" samples: {}", stats.count());
println!(" mean: {:.2}", stats.mean().unwrap());
println!(" std_dev: {:.2}", stats.std_dev().unwrap());
}