#[derive(Debug, Clone)]
pub struct DistanceMatrix {
pub(super) n: usize,
pub(super) data: Vec<f64>,
pub(super) square: bool,
}
impl DistanceMatrix {
pub fn len(&self) -> usize {
self.n
}
pub fn is_empty(&self) -> bool {
self.n == 0
}
#[inline]
pub fn get(&self, i: usize, j: usize) -> f64 {
debug_assert!(i < self.n && j < self.n);
if self.square {
return self.data[i * self.n + j];
}
match i.cmp(&j) {
std::cmp::Ordering::Equal => 0.0,
std::cmp::Ordering::Greater => self.data[i * (i - 1) / 2 + j],
std::cmp::Ordering::Less => self.data[j * (j - 1) / 2 + i],
}
}
#[inline]
pub(super) fn lower_row(&self, i: usize) -> &[f64] {
let start = if self.square {
i * self.n
} else {
i * (i - 1) / 2
};
&self.data[start..start + i]
}
pub(crate) fn count_edges_at(&self, threshold: f64) -> usize {
(1..self.n)
.map(|i| {
self.lower_row(i)
.iter()
.filter(|d| d.is_finite() && **d <= threshold)
.count()
})
.sum()
}
pub fn enclosing_radius(&self) -> f64 {
if self.n < 2 {
return 0.0;
}
let mut row_max = vec![0.0f64; self.n];
for i in 1..self.n {
let mut max_i = 0.0f64;
for (m, &d) in row_max[..i].iter_mut().zip(self.lower_row(i)) {
max_i = max_i.max(d);
*m = m.max(d);
}
row_max[i] = max_i;
}
row_max.into_iter().fold(f64::INFINITY, f64::min)
}
}
#[cfg(test)]
thread_local! {
pub(crate) static SQUARE_BUILDS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
}
impl DistanceMatrix {
#[cfg(test)]
pub(crate) fn is_square(&self) -> bool {
self.square
}
pub(crate) fn to_square(&self) -> Self {
#[cfg(test)]
SQUARE_BUILDS.with(|c| c.set(c.get() + 1));
let n = self.n;
let mut data = vec![0.0f64; n * n];
for i in 1..n {
let row = self.lower_row(i);
data[i * n..i * n + i].copy_from_slice(row);
for (j, &d) in row.iter().enumerate() {
data[j * n + i] = d;
}
}
Self {
n,
data,
square: true,
}
}
}