/// Negative squared L2 distance. Higher (less negative) is more similar.
/// We negate so that the same "higher is better" convention holds everywhere.
pub fn score(a: &[f32], b: &[f32]) -> f32 {
    debug_assert_eq!(a.len(), b.len());
    -squared_l2(a, b)
}

pub fn squared_l2(a: &[f32], b: &[f32]) -> f32 {
    a.iter().zip(b.iter()).map(|(x, y)| (x - y) * (x - y)).sum()
}

pub fn distance(a: &[f32], b: &[f32]) -> f32 {
    squared_l2(a, b).sqrt()
}

#[cfg(test)]
mod tests {
    use super::*;
    use approx::assert_relative_eq;

    #[test]
    fn identical_vectors() {
        let v = vec![1.0f32, 2.0, 3.0];
        assert_relative_eq!(score(&v, &v), 0.0, epsilon = 1e-6);
    }

    #[test]
    fn known_distance() {
        let a = vec![0.0f32, 0.0];
        let b = vec![3.0f32, 4.0];
        assert_relative_eq!(distance(&a, &b), 5.0, epsilon = 1e-6);
    }

    #[test]
    fn negated_convention() {
        let a = vec![0.0f32, 0.0];
        let b = vec![1.0f32, 0.0];
        let c = vec![2.0f32, 0.0];
        // b is closer to a than c, so score(a,b) > score(a,c)
        assert!(score(&a, &b) > score(&a, &c));
    }
}
