#[inline]
pub fn is_admissible(norm: f64, rho: f64) -> bool {
norm <= rho
}
#[inline]
pub fn is_boundary_zone(norm: f64, rho: f64, boundary_fraction: f64) -> bool {
norm > rho * boundary_fraction && norm <= rho
}
#[inline]
pub fn is_violation(norm: f64, rho: f64) -> bool {
norm > rho
}
pub fn compute_envelope_radius(healthy_residuals: &[f64]) -> f64 {
let n = healthy_residuals.len();
if n < 2 {
return 1.0; }
let mut sum = 0.0;
let mut i = 0;
while i < n {
sum += healthy_residuals[i];
i += 1;
}
let mean = sum / n as f64;
let mut var_sum = 0.0;
i = 0;
while i < n {
let d = healthy_residuals[i] - mean;
var_sum += d * d;
i += 1;
}
let variance = var_sum / (n - 1) as f64;
let sigma = sqrt_approx(variance);
let rho = 3.0 * sigma;
if rho < 1e-10 { 1e-10 } else { rho }
}
#[inline]
pub fn sqrt_approx_pub(x: f64) -> f64 {
sqrt_approx(x)
}
#[inline]
fn sqrt_approx(x: f64) -> f64 {
if x <= 0.0 {
return 0.0;
}
let mut guess = x * 0.5;
if guess == 0.0 {
guess = 1.0;
}
let mut i = 0;
while i < 8 {
guess = 0.5 * (guess + x / guess);
i += 1;
}
guess
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_admissible() {
assert!(is_admissible(2.0, 3.0));
assert!(is_admissible(3.0, 3.0));
assert!(!is_admissible(3.1, 3.0));
}
#[test]
fn test_boundary_zone() {
assert!(is_boundary_zone(2.0, 3.0, 0.5)); assert!(!is_boundary_zone(1.0, 3.0, 0.5)); assert!(!is_boundary_zone(3.5, 3.0, 0.5)); }
#[test]
fn test_envelope_radius() {
let data = [1.0, 2.0, 3.0, 4.0, 5.0];
let rho = compute_envelope_radius(&data);
assert!((rho - 4.743).abs() < 0.1);
}
#[test]
fn test_sqrt_approx() {
assert!((sqrt_approx(4.0) - 2.0).abs() < 1e-10);
assert!((sqrt_approx(9.0) - 3.0).abs() < 1e-10);
assert!((sqrt_approx(2.0) - core::f64::consts::SQRT_2).abs() < 1e-6);
}
}