use crate::{
distance::{
distance_type::DistanceType,
euclidean::euclidean_distance,
spherical::{SphericalTrajectoryCache, great_circle_distance_cached},
},
traits::{AsCoord, CoordSequence},
};
pub trait DistanceCalculator {
fn dis_between(&self, seq_a_idx: usize, seq_b_idx: usize) -> f64;
fn compute_dis_for_extra_point<C: AsCoord>(
&self,
seq_id: usize,
point_idx: usize,
anchor: Option<&C>,
) -> f64;
fn len_seq1(&self) -> usize;
fn len_seq2(&self) -> usize;
}
pub struct TrajectoryCalculator<'a, T, U>
where
T: CoordSequence + 'a,
U: CoordSequence + 'a,
{
traj1: &'a T,
traj2: &'a U,
metric: DistanceType,
cache1: Option<SphericalTrajectoryCache>,
cache2: Option<SphericalTrajectoryCache>,
}
impl<'a, T, U> TrajectoryCalculator<'a, T, U>
where
T: CoordSequence + 'a,
U: CoordSequence + 'a,
{
pub fn new(traj1: &'a T, traj2: &'a U, metric: DistanceType) -> Self
where
T::Coord: AsCoord,
U::Coord: AsCoord,
{
let (cache1, cache2) = match metric {
DistanceType::Spherical => (
Some(SphericalTrajectoryCache::from_trajectory(traj1)),
Some(SphericalTrajectoryCache::from_trajectory(traj2)),
),
DistanceType::Euclidean => (None, None),
};
Self {
traj1,
traj2,
metric,
cache1,
cache2,
}
}
}
impl<'a, T, U> DistanceCalculator for TrajectoryCalculator<'a, T, U>
where
T: CoordSequence + 'a,
U: CoordSequence + 'a,
T::Coord: AsCoord,
U::Coord: AsCoord,
{
fn dis_between(&self, idx1: usize, idx2: usize) -> f64 {
match self.metric {
DistanceType::Euclidean => {
let p1 = self.traj1.get(idx1);
let p2 = self.traj2.get(idx2);
euclidean_distance(&p1, &p2)
}
DistanceType::Spherical => {
let cache1 = self
.cache1
.as_ref()
.expect("Spherical cache not initialized");
let cache2 = self
.cache2
.as_ref()
.expect("Spherical cache not initialized");
great_circle_distance_cached(cache1, idx1, cache2, idx2)
}
}
}
fn compute_dis_for_extra_point<C: AsCoord>(
&self,
seq_id: usize,
idx: usize,
anchor: Option<&C>,
) -> f64 {
let anchor = anchor.expect("anchor must not be None");
match seq_id {
0 => {
let p = self.traj1.get(idx);
self.metric.distance(anchor, &p)
}
1 => {
let p = self.traj2.get(idx);
self.metric.distance(anchor, &p)
}
_ => panic!("Invalid seq_id"),
}
}
fn len_seq1(&self) -> usize {
self.traj1.len()
}
fn len_seq2(&self) -> usize {
self.traj2.len()
}
}
pub struct PrecomputedDistanceCalculator<'a> {
distance_matrix: &'a [f64],
seq1_len: usize,
seq2_len: usize,
seq1_extra_dists: Option<&'a [f64]>,
seq2_extra_dists: Option<&'a [f64]>,
}
impl<'a> PrecomputedDistanceCalculator<'a> {
pub fn new(distance_matrix: &'a [f64], seq1_len: usize, seq2_len: usize) -> Self {
Self {
distance_matrix,
seq1_len,
seq2_len,
seq1_extra_dists: None,
seq2_extra_dists: None,
}
}
pub fn with_extra_distances(
distance_matrix: &'a [f64],
seq1_len: usize,
seq2_len: usize,
seq1_dists: Option<&'a [f64]>,
seq2_dists: Option<&'a [f64]>,
) -> Self {
Self {
distance_matrix,
seq1_len,
seq2_len,
seq1_extra_dists: seq1_dists,
seq2_extra_dists: seq2_dists,
}
}
}
impl<'a> DistanceCalculator for PrecomputedDistanceCalculator<'a> {
fn dis_between(&self, idx1: usize, idx2: usize) -> f64 {
self.distance_matrix[idx1 * self.seq2_len + idx2]
}
fn compute_dis_for_extra_point<C: AsCoord>(
&self,
seq_id: usize,
point_idx: usize,
_anchor: Option<&C>,
) -> f64 {
match (seq_id, &self.seq1_extra_dists, &self.seq2_extra_dists) {
(0, Some(dists), _) => dists[point_idx],
(1, _, Some(dists)) => dists[point_idx],
_ => panic!("Extra distance not available for seq_id={}", seq_id),
}
}
fn len_seq1(&self) -> usize {
self.seq1_len
}
fn len_seq2(&self) -> usize {
self.seq2_len
}
}