#![allow(clippy::unwrap_used, clippy::panic)]
use anomstream_core::{RcfError, ThresholdedForestBuilder};
use rand::{RngExt, SeedableRng};
use rand_chacha::ChaCha8Rng;
fn main() -> Result<(), RcfError> {
let mut detector = ThresholdedForestBuilder::<4>::new()
.num_trees(100)
.sample_size(128)
.seed(2026)
.build()?;
let mut rng = ChaCha8Rng::seed_from_u64(2026);
for _ in 0..1024 {
let p = [
50_000.0_f64 + rng.random::<f64>() * 2_000.0,
0.5 + rng.random::<f64>() * 0.1,
4.0 + rng.random::<f64>() * 0.5,
1_000.0 + rng.random::<f64>() * 100.0,
];
detector.process(p)?;
}
let observed = [50_500.0_f64, 0.55, 4.2, 25_000.0];
let baseline = detector.forensic_baseline(&observed)?;
println!("== forensic baseline ==");
println!(" live_points = {}", baseline.live_points);
let labels = ["packet_rate", "proto_ratio", "entropy ", "cardinality"];
for (d, label) in labels.iter().enumerate() {
println!(
" {label} observed = {obs:>10.2} expected = {exp:>10.2} stddev = {sd:>8.2} z = {z:+.2}",
obs = baseline.observed[d],
exp = baseline.expected[d],
sd = baseline.stddev[d],
z = baseline.zscore[d],
);
}
if let Some(idx) = baseline.argmax_abs_zscore() {
println!();
println!(
"driver dim: {label} (|z| = {z:.2})",
label = labels[idx],
z = baseline.zscore[idx].abs(),
);
}
Ok(())
}