use crate::push::vector::IntVector;
pub struct Topology {}
impl Topology {
pub fn euclidean_distance(i1: &Vec<usize>, i2: &Vec<usize>) -> Option<f32> {
if i1.len() != i2.len() {
None
} else {
let mut dist = 0.0;
for i in 0..i1.len() {
dist += (i1[i] as f32 - i2[i] as f32).powf(2.0);
}
Some(f32::sqrt(dist))
}
}
pub fn decompose_index(index: &usize, nedge: &usize, ndim: &usize) -> Option<Vec<usize>> {
let mut dindex = vec![0; *ndim];
for i in 0..*ndim {
if let Some(cp) = (*nedge).checked_pow(i as u32) {
dindex[i] = index / cp % *nedge;
} else {
return None;
}
}
Some(dindex)
}
pub fn find_neighbors(
ntotal: &usize,
ndim: &usize,
index: &usize,
radius: &f32,
) -> Option<IntVector> {
if *radius < 0.0 || *ndim < 1 || *ntotal < 1 || *index > *ntotal {
return None;
}
let nedge = f32::ceil((*ntotal as f32).powf(1.0 / *ndim as f32)) as usize;
if let Some(dindex) = Topology::decompose_index(index, &nedge, ndim) {
let mut neighbors = vec![];
for i in 0..*ntotal {
if let Some(di) = Topology::decompose_index(&i, &nedge, ndim) {
if let Some(dist) = Topology::euclidean_distance(&dindex, &di) {
if dist <= *radius {
neighbors.push(i as i32);
}
}
}
}
return Some(IntVector::new(neighbors));
} else {
return None;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decompose_index_without_overhang() {
assert_eq!(Topology::decompose_index(&14, &6, &2).unwrap(), vec![2, 2]);
assert_eq!(
Topology::decompose_index(&4, &2, &3).unwrap(),
vec![0, 0, 1]
);
assert_eq!(
Topology::decompose_index(&13, &3, &3).unwrap(),
vec![1, 1, 1]
);
assert_eq!(
Topology::decompose_index(&26, &3, &3).unwrap(),
vec![2, 2, 2]
);
assert_eq!(
Topology::decompose_index(&80, &3, &4).unwrap(),
vec![2, 2, 2, 2]
);
}
#[test]
fn euclidean_distance_calculated_when_vector_lengths_match() {
assert_eq!(
Topology::euclidean_distance(&vec![0, 0], &vec![1, 1, 1]),
None
);
assert_eq!(
Topology::euclidean_distance(&vec![0, 0], &vec![1, 1]),
Some(f32::sqrt(2.0))
);
assert_eq!(
Topology::euclidean_distance(&vec![1, 2, 4], &vec![1, 2, 4]),
Some(0.0)
);
assert_eq!(
Topology::euclidean_distance(&vec![2, 4], &vec![3, 6]),
Topology::euclidean_distance(&vec![3, 6], &vec![2, 4])
);
}
#[test]
fn find_neighbors_without_envelope() {
assert_eq!(
Topology::find_neighbors(&36, &2, &14, &1.0).unwrap(),
IntVector::new(vec![8, 13, 14, 15, 20])
);
assert_eq!(
Topology::find_neighbors(&36, &1, &14, &1.0).unwrap(),
IntVector::new(vec![13, 14, 15])
);
assert_eq!(
Topology::find_neighbors(&27, &3, &13, &f32::sqrt(3.0)).unwrap(),
IntVector::new(vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26
])
);
}
#[test]
fn find_neighbors_empty() {
assert_eq!(Topology::find_neighbors(&0, &2, &0, &1.0), None);
}
#[test]
fn find_neighbors_with_envelope() {
assert_eq!(
Topology::find_neighbors(&38, &2, &31, &f32::sqrt(2.0)).unwrap(),
IntVector::new(vec![23, 24, 25, 30, 31, 32, 37])
);
}
}