use crate::residual::residual_norm;
use crate::types::SignTuple;
#[inline]
pub fn compute_sign_tuple(norms: &[f64], k: usize) -> SignTuple {
if k == 0 || norms.is_empty() {
return SignTuple::ZERO;
}
let norm = if k < norms.len() { residual_norm(norms[k]) } else { 0.0 };
let drift = if k >= 1 && k < norms.len() {
norms[k] - norms[k - 1]
} else {
0.0
};
let slew = if k >= 2 && k < norms.len() {
let drift_prev = norms[k - 1] - norms[k - 2];
drift - drift_prev
} else {
0.0
};
SignTuple { norm, drift, slew }
}
#[inline]
pub fn drift_persistence(norms: &[f64], k: usize, window: usize) -> f64 {
if k < 1 || window == 0 {
return 0.0;
}
let start = k.saturating_sub(window);
let mut positive_count: u32 = 0;
let mut total: u32 = 0;
let mut i = start + 1;
while i <= k && i < norms.len() {
let drift = norms[i] - norms[i - 1];
if drift > 0.0 {
positive_count += 1;
}
total += 1;
i += 1;
}
if total == 0 { 0.0 } else { positive_count as f64 / total as f64 }
}
#[inline]
pub fn boundary_density(states: &[u8], k: usize, window: usize) -> f64 {
if window == 0 || k == 0 {
return 0.0;
}
let start = k.saturating_sub(window);
let mut boundary_count: u32 = 0;
let mut total: u32 = 0;
let mut i = start;
while i <= k && i < states.len() {
if states[i] == 1 {
boundary_count += 1;
}
total += 1;
i += 1;
}
if total == 0 { 0.0 } else { boundary_count as f64 / total as f64 }
}
#[inline]
pub fn slew_density(norms: &[f64], k: usize, window: usize, delta_s: f64) -> f64 {
if k < 2 || window == 0 {
return 0.0;
}
let start = if k > window { k - window } else { 2 };
let start = if start < 2 { 2 } else { start };
let mut slew_count: u32 = 0;
let mut total: u32 = 0;
let mut i = start;
while i <= k && i < norms.len() {
let drift_cur = norms[i] - norms[i - 1];
let drift_prev = norms[i - 1] - norms[i - 2];
let slew = drift_cur - drift_prev;
if slew > delta_s || slew < -delta_s {
slew_count += 1;
}
total += 1;
i += 1;
}
if total == 0 { 0.0 } else { slew_count as f64 / total as f64 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sign_tuple_basic() {
let norms = [0.0, 1.0, 3.0, 2.0];
let s = compute_sign_tuple(&norms, 2);
assert!((s.norm - 3.0).abs() < 1e-12);
assert!((s.drift - 2.0).abs() < 1e-12); let drift_prev = 1.0; assert!((s.slew - (2.0 - drift_prev)).abs() < 1e-12); }
#[test]
fn test_sign_tuple_zero_index() {
let norms = [1.0, 2.0];
let s = compute_sign_tuple(&norms, 0);
assert_eq!(s, SignTuple::ZERO);
}
#[test]
fn test_drift_persistence() {
let norms = [0.0, 1.0, 2.0, 3.0, 4.0];
assert!((drift_persistence(&norms, 4, 4) - 1.0).abs() < 1e-12);
}
}