use num_traits::Float;
pub fn distance_to_space<F, T>(p1: &[T], min_bounds: &[T], max_bounds: &[T], distance: &F) -> T
where
F: Fn(&[T], &[T]) -> T,
T: Float,
{
let mut p2 = vec![T::nan(); p1.len()];
for i in 0..p1.len() {
if p1[i] > max_bounds[i] {
p2[i] = max_bounds[i];
} else if p1[i] < min_bounds[i] {
p2[i] = min_bounds[i];
} else {
p2[i] = p1[i];
}
}
distance(p1, &p2[..])
}
pub fn within_bounding_box<T>(p: &[T], min_bounds: &[T], max_bounds: &[T]) -> bool
where
T: Float,
{
for ((l, h), v) in min_bounds.iter().zip(max_bounds.iter()).zip(p) {
if v < l || v > h {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::distance_to_space;
use super::within_bounding_box;
use crate::distance::squared_euclidean;
#[test]
fn test_normal_distance_to_space() {
let dis = distance_to_space(&[0.0, 0.0], &[1.0, 1.0], &[2.0, 2.0], &squared_euclidean);
assert_eq!(dis, 2.0);
}
#[test]
fn test_distance_outside_inf() {
let dis = distance_to_space(
&[0.0, 0.0],
&[1.0, 1.0],
&[f64::INFINITY, f64::INFINITY],
&squared_euclidean,
);
assert_eq!(dis, 2.0);
}
#[test]
fn test_distance_inside_inf() {
let dis = distance_to_space(
&[2.0, 2.0],
&[f64::NEG_INFINITY, f64::NEG_INFINITY],
&[f64::INFINITY, f64::INFINITY],
&squared_euclidean,
);
assert_eq!(dis, 0.0);
}
#[test]
fn test_distance_inside_normal() {
let dis = distance_to_space(&[2.0, 2.0], &[0.0, 0.0], &[3.0, 3.0], &squared_euclidean);
assert_eq!(dis, 0.0);
}
#[test]
fn distance_to_half_space() {
let dis = distance_to_space(
&[-2.0, 0.0],
&[0.0, f64::NEG_INFINITY],
&[f64::INFINITY, f64::INFINITY],
&squared_euclidean,
);
assert_eq!(dis, 4.0);
}
#[test]
fn test_within_bounding_box() {
assert!(within_bounding_box(&[1.0, 1.0], &[0.0, 0.0], &[2.0, 2.0]));
assert!(within_bounding_box(&[1.0, 1.0], &[1.0, 1.0], &[2.0, 2.0]));
assert!(within_bounding_box(&[1.0, 1.0], &[0.0, 0.0], &[1.0, 1.0]));
assert!(!within_bounding_box(&[2.0, 2.0], &[0.0, 0.0], &[1.0, 1.0]));
assert!(!within_bounding_box(&[0.0, 0.0], &[1.0, 1.0], &[2.0, 2.0]));
}
}