trueno 0.16.4

High-performance SIMD compute library with GPU support for matrix operations
use super::*;

fn edge_cases() -> Vec<(NormMethod, &'static str, Vec<f32>, f32)> {
    let l2_large: Vec<f32> = (0..1024).map(|i| (i as f32) * 0.01).collect();
    let l2_large_exp = l2_large.iter().map(|x| x * x).sum::<f32>().sqrt();

    vec![
        (norm_l2, "l2-pythagorean", vec![3.0, 4.0], 5.0),
        (norm_l2, "l2-empty", vec![], 0.0),
        (norm_l2, "l2-unit", vec![1.0, 0.0, 0.0], 1.0),
        (norm_l2, "l2-single", vec![7.0], 7.0),
        (norm_l2, "l2-neg", vec![-5.0], 5.0),
        (norm_l2, "l2-zeros", vec![0.0, 0.0, 0.0, 0.0], 0.0),
        (norm_l2, "l2-mixed", vec![3.0, -4.0, 0.0], 5.0),
        (
            norm_l2,
            "l2-5elem",
            vec![1.0, 2.0, 3.0, 4.0, 5.0],
            (1.0 + 4.0 + 9.0 + 16.0 + 25.0_f32).sqrt(),
        ),
        (norm_l2, "l2-large", l2_large, l2_large_exp),
        (norm_l1, "l1-basic", vec![3.0, -4.0, 5.0], 12.0),
        (norm_l1, "l1-empty", vec![], 0.0),
        (norm_l1, "l1-single-neg", vec![-7.0], 7.0),
        (norm_l1, "l1-zeros", vec![0.0, 0.0, 0.0], 0.0),
        (norm_l1, "l1-positive", vec![1.0, 2.0, 3.0, 4.0], 10.0),
        (norm_l1, "l1-all-neg", vec![-1.0, -2.0, -3.0], 6.0),
        (norm_l1, "l1-non-aligned", vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0], 28.0),
        (
            norm_l1,
            "l1-large",
            (0..512).map(|i| if i % 2 == 0 { 1.0 } else { -1.0 }).collect(),
            512.0,
        ),
        (norm_linf, "linf-basic", vec![3.0, -7.0, 5.0, -2.0], 7.0),
        (norm_linf, "linf-empty", vec![], 0.0),
        (norm_linf, "linf-all-neg", vec![-1.0, -5.0, -3.0], 5.0),
        (norm_linf, "linf-single", vec![-42.0], 42.0),
        (norm_linf, "linf-zeros", vec![0.0, 0.0, 0.0], 0.0),
        (norm_linf, "linf-end", vec![1.0, 2.0, 3.0, 100.0], 100.0),
        (norm_linf, "linf-begin", vec![-100.0, 2.0, 3.0, 4.0], 100.0),
        (norm_linf, "linf-equal", vec![5.0, 5.0, 5.0, 5.0], 5.0),
        (norm_linf, "linf-non-aligned", vec![1.0, -9.0, 3.0, -4.0, 5.0], 9.0),
        (
            norm_linf,
            "linf-large",
            {
                let mut d: Vec<f32> = (0..256).map(|i| (i as f32) * 0.01).collect();
                d[200] = -99.9;
                d
            },
            99.9,
        ),
    ]
}

#[test]
fn test_all_norm_edge_cases() {
    for (method, label, data, expected) in edge_cases() {
        let result = method(&Vector::from_slice(&data)).unwrap();
        let tol = if data.len() > 256 { 1e-2 } else { 1e-5 };
        assert!((result - expected).abs() <= tol, "{label}: expected {expected}, got {result}");
    }
}

#[test]
fn test_norm_l2_very_small_values() {
    let v = Vector::from_slice(&[1e-20, 1e-20, 1e-20]);
    let norm = v.norm_l2().unwrap();
    assert!(norm > 0.0 && norm < 1e-10);
}

#[test]
fn test_norm_ordering_property() {
    for data in [
        vec![3.0, -4.0, 5.0, -2.0, 1.0],
        (0..100).map(|i| ((i as f32) * 0.37).sin()).collect::<Vec<_>>(),
    ] {
        let v = Vector::from_slice(&data);
        let l1 = v.norm_l1().unwrap();
        let l2 = v.norm_l2().unwrap();
        let linf = v.norm_linf().unwrap();
        assert!(linf <= l2 + 1e-4, "L-inf ({linf}) should be <= L2 ({l2})");
        assert!(l2 <= l1 + 1e-4, "L2 ({l2}) should be <= L1 ({l1})");
    }
}