use num_complex::Complex64;
pub(super) fn validate(impedance: &[Vec<Complex64>], kind: &str, code: u32) -> Result<(), String> {
let mut reduced = impedance.to_vec();
let mut remaining: Vec<_> = (0..reduced.len()).collect();
while !remaining.is_empty() {
let mut selected = None;
let mut largest = 0.0;
for &i in &remaining {
let magnitude = reduced[i][i].norm_sqr().sqrt();
if magnitude > largest {
largest = magnitude;
selected = Some(i);
}
}
let pivot = selected.ok_or_else(|| {
format!("native {kind} primitive inversion has a zero diagonal pivot (error {code})")
})?;
remaining.retain(|&i| i != pivot);
for &i in &remaining {
for &j in &remaining {
let correction =
scaled_division(reduced[i][pivot] * reduced[pivot][j], reduced[pivot][pivot]);
reduced[i][j] -= correction;
if !reduced[i][j].re.is_finite() || !reduced[i][j].im.is_finite() {
return Err(format!("{kind} primitive elimination became nonfinite"));
}
}
}
}
Ok(())
}
fn scaled_division(numerator: Complex64, denominator: Complex64) -> Complex64 {
if denominator.re.abs() > denominator.im.abs() {
let ratio = denominator.im / denominator.re;
let divisor = denominator.re + denominator.im * ratio;
Complex64::new(
(numerator.re + numerator.im * ratio) / divisor,
(numerator.im - numerator.re * ratio) / divisor,
)
} else {
let ratio = denominator.re / denominator.im;
let divisor = denominator.im + denominator.re * ratio;
Complex64::new(
(numerator.im + numerator.re * ratio) / divisor,
(-numerator.re + numerator.im * ratio) / divisor,
)
}
}