use crate::distance::distance_type::DistanceType;
use crate::distance::euclidean::euclidean_distance;
use crate::distance::spherical::{SphericalTrajectoryCache, great_circle_distance_cached};
use crate::traits::{AsCoord, CoordSequence};
pub fn precompute_distance_matrix<T: CoordSequence, U: CoordSequence>(
t0: &T,
t1: &U,
dist_type: DistanceType,
) -> Vec<f64>
where
T::Coord: AsCoord,
U::Coord: AsCoord,
{
let n0 = t0.len();
let n1 = t1.len();
let mut distance_matrix = vec![0.0; n0 * n1];
match dist_type {
DistanceType::Euclidean => {
for i in 0..n0 {
for j in 0..n1 {
let p0 = t0.get(i);
let p1 = t1.get(j);
distance_matrix[i * n1 + j] = euclidean_distance(&p0, &p1);
}
}
}
DistanceType::Spherical => {
let cache0 = SphericalTrajectoryCache::from_trajectory(t0);
let cache1 = SphericalTrajectoryCache::from_trajectory(t1);
for i in 0..n0 {
for j in 0..n1 {
distance_matrix[i * n1 + j] =
great_circle_distance_cached(&cache0, i, &cache1, j);
}
}
}
}
distance_matrix
}
pub fn extract_coords<T: CoordSequence>(t: &T) -> Vec<[f64; 2]>
where
T::Coord: AsCoord,
{
let mut coords = Vec::with_capacity(t.len());
for i in 0..t.len() {
let coord = t.get(i);
coords.push([coord.x(), coord.y()]);
}
coords
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_precompute_euclidean() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 1.0], [1.0, 0.0]];
let matrix = precompute_distance_matrix(&t0, &t1, DistanceType::Euclidean);
let n1 = t1.len();
assert_eq!(matrix.len(), 4);
assert!((matrix[0 * n1 + 0] - 1.0).abs() < 1e-10);
assert!((matrix[0 * n1 + 1] - 1.0).abs() < 1e-10);
assert!((matrix[1 * n1 + 0] - 1.0).abs() < 1e-10);
assert!((matrix[1 * n1 + 1] - 1.0).abs() < 1e-10);
}
#[test]
fn test_precompute_spherical() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 1.0], [1.0, 0.0]];
let matrix = precompute_distance_matrix(&t0, &t1, DistanceType::Spherical);
let n1 = t1.len();
assert_eq!(matrix.len(), 4);
assert!(matrix[0 * n1 + 0] > 0.0);
assert!(matrix[0 * n1 + 1] > 0.0);
assert!(matrix[1 * n1 + 0] > 0.0);
assert!(matrix[1 * n1 + 1] > 0.0);
}
#[test]
fn test_precompute_empty() {
let t0: Vec<[f64; 2]> = vec![];
let t1: Vec<[f64; 2]> = vec![[0.0, 0.0]];
let matrix = precompute_distance_matrix(&t0, &t1, DistanceType::Euclidean);
assert_eq!(matrix.len(), 0);
}
#[test]
fn test_precompute_consistency() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 1.0], [1.0, 0.0]];
let matrix = precompute_distance_matrix(&t0, &t1, DistanceType::Euclidean);
let n1 = t1.len();
assert!((matrix[0 * n1 + 0] - euclidean_distance(&t0[0], &t1[0])).abs() < 1e-10);
assert!((matrix[0 * n1 + 1] - euclidean_distance(&t0[0], &t1[1])).abs() < 1e-10);
assert!((matrix[1 * n1 + 0] - euclidean_distance(&t0[1], &t1[0])).abs() < 1e-10);
assert!((matrix[1 * n1 + 1] - euclidean_distance(&t0[1], &t1[1])).abs() < 1e-10);
}
}