use crate::distance::base::DistanceCalculator;
pub fn discret_frechet<D: DistanceCalculator>(
calculator: &D,
use_full_matrix: bool,
) -> crate::distance::DpResult {
let n0 = calculator.len_seq1();
let n1 = calculator.len_seq2();
if n0 == 0 || n1 == 0 {
return crate::distance::DpResult::new(f64::MAX);
}
if use_full_matrix {
let width = n1 + 1;
let mut c = vec![0.0; (n0 + 1) * width];
for i in 1..=n0 {
c[i * width] = f64::INFINITY;
}
for val in &mut c[1..=n1] {
*val = f64::INFINITY;
}
for i in 1..=n0 {
let row_offset = i * width;
let prev_row_offset = (i - 1) * width;
for j in 1..=n1 {
let dist = calculator.dis_between(i - 1, j - 1);
let min_prev = c[row_offset + (j - 1)]
.min(c[prev_row_offset + (j - 1)])
.min(c[prev_row_offset + j]);
c[row_offset + j] = dist.max(min_prev);
}
}
crate::distance::DpResult::with_matrix(c[n0 * width + n1], c)
} else {
let mut prev_row = vec![f64::INFINITY; n1 + 1];
let mut curr_row = vec![f64::INFINITY; n1 + 1];
prev_row[0] = 0.0;
for i in 1..=n0 {
curr_row[0] = f64::INFINITY;
for j in 1..=n1 {
let dist = calculator.dis_between(i - 1, j - 1);
let min_prev = curr_row[j - 1].min(prev_row[j - 1]).min(prev_row[j]);
curr_row[j] = dist.max(min_prev);
}
std::mem::swap(&mut prev_row, &mut curr_row);
}
crate::distance::DpResult::new(prev_row[n1])
}
}
pub fn discret_frechet_euclidean<T: crate::traits::CoordSequence>(
t0: &T,
t1: &T,
use_full_matrix: bool,
) -> crate::distance::DpResult
where
T::Coord: crate::traits::AsCoord,
{
use crate::distance::base::TrajectoryCalculator;
use crate::distance::distance_type::DistanceType;
let calculator = TrajectoryCalculator::new(t0, t1, DistanceType::Euclidean);
discret_frechet(&calculator, use_full_matrix)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::distance::base::TrajectoryCalculator;
use crate::distance::distance_type::DistanceType;
#[test]
fn test_discret_frechet_simple() {
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 calculator = TrajectoryCalculator::new(&t0, &t1, DistanceType::Euclidean);
let dist = discret_frechet(&calculator, false);
println!("Discret Frechet distance: {}", dist);
assert!(dist.distance > 0.0);
}
#[test]
fn test_discret_frechet_identical() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let calculator = TrajectoryCalculator::new(&t0, &t1, DistanceType::Euclidean);
let result = discret_frechet(&calculator, false);
println!(
"Discret Frechet distance for identical trajectories: {}",
result.distance
);
assert!(result.distance < 1e-6);
}
#[test]
fn test_discret_frechet_euclidean_simple() {
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 dist = discret_frechet_euclidean(&t0, &t1, false);
println!("Discret Frechet Euclidean distance: {}", dist);
assert!(dist.distance > 0.0);
}
#[test]
fn test_discret_frechet_euclidean_identical() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let result = discret_frechet_euclidean(&t0, &t1, false);
println!(
"Discret Frechet Euclidean distance for identical trajectories: {}",
result.distance
);
assert!(result.distance < 1e-6);
}
#[test]
fn test_discret_frechet_euclidean_empty() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0]];
let t1: Vec<[f64; 2]> = vec![];
let result = discret_frechet_euclidean(&t0, &t1, false);
println!(
"Discret Frechet Euclidean distance for empty trajectory: {}",
result.distance
);
assert_eq!(result.distance, f64::MAX);
}
#[test]
fn test_discret_frechet_euclidean_single_point() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0]];
let t1: Vec<[f64; 2]> = vec![[1.0, 1.0]];
let result = discret_frechet_euclidean(&t0, &t1, false);
println!(
"Discret Frechet Euclidean distance for single point: {}",
result.distance
);
let sqrt_2 = std::f64::consts::SQRT_2;
assert!((result.distance - sqrt_2).abs() < 1e-6);
}
#[test]
fn test_discret_frechet_consistency_between_modes() {
let t0: Vec<[f64; 2]> = vec![[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]];
let t1: Vec<[f64; 2]> = vec![[0.0, 1.0], [1.0, 0.0], [2.0, 3.0]];
let calculator = TrajectoryCalculator::new(&t0, &t1, DistanceType::Euclidean);
let result_optimized = discret_frechet(&calculator, false);
let result_full = discret_frechet(&calculator, true);
assert!((result_optimized.distance - result_full.distance).abs() < 1e-10);
assert!(result_optimized.matrix.is_none());
assert!(result_full.matrix.is_some());
}
#[test]
fn test_discret_frechet_with_precomputed_distances() {
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 distance_matrix =
crate::distance::utils::precompute_distance_matrix(&t0, &t1, DistanceType::Euclidean);
let calculator = crate::distance::base::PrecomputedDistanceCalculator::new(
&distance_matrix,
t0.len(),
t1.len(),
);
let result = discret_frechet(&calculator, false);
println!(
"Discret Frechet distance with precomputed distances: {}",
result.distance
);
assert!(result.distance > 0.0);
}
}