pub fn timing_t_statistic(class_a: &[f64], class_b: &[f64]) -> Option<f64> {
crate::welch_t_statistic(class_a, class_b)
}
pub fn timing_passes_loose(threshold: f64, class_a: &[f64], class_b: &[f64]) -> bool {
match timing_t_statistic(class_a, class_b) {
Some(t) => t.abs() < threshold,
None => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn near_identical_classes_pass_loose_gate() {
let a: Vec<f64> = (0..200).map(|i| 1e-6 + (i % 3) as f64 * 1e-9).collect();
let b: Vec<f64> = (0..200)
.map(|i| 1e-6 + (i % 3) as f64 * 1e-9 + 1e-12)
.collect();
assert!(timing_passes_loose(4.5, &a, &b));
}
#[test]
fn separated_classes_fail_loose_gate() {
let a: Vec<f64> = (0..100).map(|i| 1.0e-6 + (i % 3) as f64 * 1e-9).collect();
let b: Vec<f64> = (0..100).map(|i| 2.0e-6 + (i % 3) as f64 * 1e-9).collect();
let t = timing_t_statistic(&a, &b).expect("well-formed classes yield a statistic");
assert!(t.abs() > 8.0, "expected the gate to fire, got t={t}");
assert!(!timing_passes_loose(8.0, &a, &b));
}
#[test]
fn fail_closed_on_too_few_samples() {
let a = [1.0e-6];
let b = [1.0e-6, 1.0000001e-6];
assert_eq!(timing_t_statistic(&a, &b), None);
assert!(!timing_passes_loose(8.0, &a, &b));
}
#[test]
fn fail_closed_on_zero_variance() {
let a = [1.0e-6, 1.0e-6, 1.0e-6];
let b = [1.0e-6, 1.0e-6, 1.0e-6];
assert_eq!(timing_t_statistic(&a, &b), None);
assert!(!timing_passes_loose(8.0, &a, &b));
}
}