use super::Matrix;
use crate::{to_i32, Norm};
extern "C" {
fn c_dlange(norm_code: i32, m: *const i32, n: *const i32, a: *const f64, lda: *const i32, work: *mut f64) -> f64;
}
pub fn mat_norm(a: &Matrix, kind: Norm) -> f64 {
let (m, n) = a.dims();
if m == 0 || n == 0 {
return 0.0;
}
let m_i32 = to_i32(m);
let n_i32 = to_i32(n);
let lda = m_i32;
let mut work = if kind == Norm::Inf { vec![0.0; m] } else { Vec::new() };
let norm_code = kind as i32;
unsafe { c_dlange(norm_code, &m_i32, &n_i32, a.as_data().as_ptr(), &lda, work.as_mut_ptr()) }
}
#[cfg(test)]
mod tests {
use super::{mat_norm, Matrix};
use crate::{approx_eq, Norm};
#[test]
fn mat_norm_works() {
let a_0x0 = Matrix::new(0, 0);
let a_0x1 = Matrix::new(0, 1);
let a_1x0 = Matrix::new(1, 0);
assert_eq!(mat_norm(&a_0x0, Norm::One), 0.0);
assert_eq!(mat_norm(&a_0x0, Norm::Inf), 0.0);
assert_eq!(mat_norm(&a_0x0, Norm::Euc), 0.0);
assert_eq!(mat_norm(&a_0x0, Norm::Fro), 0.0);
assert_eq!(mat_norm(&a_0x0, Norm::Max), 0.0);
assert_eq!(mat_norm(&a_0x1, Norm::One), 0.0);
assert_eq!(mat_norm(&a_0x1, Norm::Inf), 0.0);
assert_eq!(mat_norm(&a_0x1, Norm::Fro), 0.0);
assert_eq!(mat_norm(&a_0x1, Norm::Max), 0.0);
assert_eq!(mat_norm(&a_1x0, Norm::One), 0.0);
assert_eq!(mat_norm(&a_1x0, Norm::Inf), 0.0);
assert_eq!(mat_norm(&a_1x0, Norm::Fro), 0.0);
assert_eq!(mat_norm(&a_1x0, Norm::Max), 0.0);
#[rustfmt::skip]
let a = Matrix::from(&[
[ 5.0, -4.0, 2.0],
[-1.0, 2.0, 3.0],
[-2.0, 1.0, 0.0],
]);
approx_eq(mat_norm(&a, Norm::One), 8.0, 1e-15);
approx_eq(mat_norm(&a, Norm::Inf), 11.0, 1e-15);
approx_eq(mat_norm(&a, Norm::Euc), 8.0, 1e-15);
approx_eq(mat_norm(&a, Norm::Fro), 8.0, 1e-15);
approx_eq(mat_norm(&a, Norm::Max), 5.0, 1e-15);
#[rustfmt::skip]
let diff = Matrix::from(&[
[ 0.56, -0.36, -0.04],
[ 0.91, -0.87, -0.66],
[-0.36, 0.23, 0.93],
]);
assert_eq!(mat_norm(&diff, Norm::Inf), 2.44);
assert_eq!(mat_norm(&diff, Norm::One), 1.83);
approx_eq(mat_norm(&diff, Norm::Fro), 1.87, 0.01);
}
}