use crate::error::{DsfbError, Result};
#[inline]
pub fn compute_residuals(
observation: &[f64],
baseline: &[f64],
missing_mask: &[bool],
output: &mut [f64],
) -> Result<()> {
let n = observation.len();
if baseline.len() != n || missing_mask.len() != n || output.len() != n {
return Err(DsfbError::DimensionMismatch {
expected: n,
got: baseline.len(),
});
}
let mut i = 0;
while i < n {
if missing_mask[i] {
output[i] = 0.0;
} else {
output[i] = observation[i] - baseline[i];
}
i += 1;
}
Ok(())
}
#[inline]
pub fn residual_norm(r: f64) -> f64 {
if r < 0.0 { -r } else { r }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_residual_basic() {
let obs = [1.0, 2.0, 3.0];
let base = [0.5, 1.5, 3.0];
let mask = [false, false, false];
let mut out = [0.0; 3];
compute_residuals(&obs, &base, &mask, &mut out).expect("should succeed");
assert!((out[0] - 0.5).abs() < 1e-12);
assert!((out[1] - 0.5).abs() < 1e-12);
assert!((out[2] - 0.0).abs() < 1e-12);
}
#[test]
fn test_residual_missing() {
let obs = [999.0, 2.0];
let base = [0.0, 1.0];
let mask = [true, false];
let mut out = [0.0; 2];
compute_residuals(&obs, &base, &mask, &mut out).expect("should succeed");
assert!((out[0] - 0.0).abs() < 1e-12); assert!((out[1] - 1.0).abs() < 1e-12);
}
#[test]
fn test_dimension_mismatch() {
let obs = [1.0, 2.0];
let base = [1.0];
let mask = [false, false];
let mut out = [0.0; 2];
assert!(compute_residuals(&obs, &base, &mask, &mut out).is_err());
}
}