use outram_foam_basic_lib::matrix::SquareMatrix;
fn load_matrix_market(contents: &str) -> SquareMatrix {
let mut lines = contents.lines().filter(|l| !l.starts_with('%'));
let header = lines.next().expect("missing dimension line");
let mut parts = header.split_whitespace();
let nrows: usize = parts.next().unwrap().parse().unwrap();
let ncols: usize = parts.next().unwrap().parse().unwrap();
assert_eq!(nrows, ncols, "expected a square matrix");
let mut mat = SquareMatrix::new(nrows);
for line in lines {
let line = line.trim();
if line.is_empty() {
continue;
}
let mut parts = line.split_whitespace();
let row: usize = parts.next().unwrap().parse::<usize>().unwrap() - 1;
let col: usize = parts.next().unwrap().parse::<usize>().unwrap() - 1;
let val: f64 = parts.next().unwrap().parse().unwrap();
mat.add(row, col, val);
}
mat
}
fn mat_vec(a: &SquareMatrix, x: &[f64]) -> Vec<f64> {
let n = a.n();
(0..n)
.map(|i| (0..n).map(|j| a.get(i, j) * x[j]).sum::<f64>())
.collect()
}
fn norm_inf(v: &[f64]) -> f64 {
v.iter().map(|x| x.abs()).fold(0.0_f64, f64::max)
}
#[test]
fn nnc261_lu_roundtrip() {
let mtx_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("nnc261.mtx");
let contents = std::fs::read_to_string(&mtx_path).expect("could not read tests/nnc261.mtx");
let a = load_matrix_market(&contents);
assert_eq!(a.n(), 261, "expected a 261×261 matrix");
let n = a.n();
let x_ref: Vec<f64> = (1..=n).map(|i| i as f64).collect();
let b = mat_vec(&a, &x_ref);
let x_solved = a.solve(&b).expect("NNC261 must be non-singular");
let ax = mat_vec(&a, &x_solved);
let residual: Vec<f64> = ax.iter().zip(b.iter()).map(|(a, b)| a - b).collect();
let rel_residual = norm_inf(&residual) / norm_inf(&b);
eprintln!("NNC261 relative residual: {rel_residual:.3e} (bound 1e-10)");
assert!(
rel_residual < 1e-10,
"relative residual {rel_residual:.3e} exceeds 1e-10"
);
let err: Vec<f64> = x_solved
.iter()
.zip(x_ref.iter())
.map(|(a, b)| a - b)
.collect();
let rel_err = norm_inf(&err) / norm_inf(&x_ref);
eprintln!("NNC261 relative solution error: {rel_err:.3e} (bound 0.05, κ ≈ 1e13)");
assert!(
rel_err < 0.05,
"relative solution error {rel_err:.3e} exceeds 0.05"
);
}