Skip to content

Commit 5deaaf8

Browse files
committed
taxi: Implement taxicab distance
1 parent 97d6454 commit 5deaaf8

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub mod distance;
9595
pub mod euclid;
9696
pub mod exhaustive;
9797
pub mod kd;
98+
pub mod taxi;
9899
pub mod vp;
99100

100101
mod util;

src/taxi.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Taxicab (Manhattan) distance.
2+
3+
use crate::coords::{Coordinates, CoordinateMetric, CoordinateProximity};
4+
use crate::distance::{Metric, Proximity};
5+
6+
use num_traits::{zero, Signed};
7+
8+
/// A point in taxicab space.
9+
///
10+
/// This wrapper equips any [coordinate space] with the [taxicab distance metric].
11+
///
12+
/// [coordinate space]: [Coordinates]
13+
/// [taxicab distance metric]: https://en.wikipedia.org/wiki/Taxicab_geometry
14+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15+
pub struct Taxicab<T>(pub T);
16+
17+
impl<T> Taxicab<T> {
18+
/// Wrap a point.
19+
pub fn new(point: T) -> Self {
20+
Self(point)
21+
}
22+
23+
/// Unwrap a point.
24+
pub fn inner(&self) -> &T {
25+
&self.0
26+
}
27+
28+
/// Unwrap a point.
29+
pub fn into_inner(self) -> T {
30+
self.0
31+
}
32+
}
33+
34+
impl<T: Coordinates> Coordinates for Taxicab<T> {
35+
type Value = T::Value;
36+
37+
fn dims(&self) -> usize {
38+
self.0.dims()
39+
}
40+
41+
fn coord(&self, i: usize) -> Self::Value {
42+
self.0.coord(i)
43+
}
44+
}
45+
46+
/// Compute the taxicab distance between two points.
47+
pub fn taxicab_distance<T, U>(x: T, y: U) -> T::Value
48+
where
49+
T: Coordinates,
50+
U: Coordinates<Value = T::Value>,
51+
{
52+
debug_assert!(x.dims() == y.dims());
53+
54+
let mut sum = zero();
55+
for i in 0..x.dims() {
56+
sum += (x.coord(i) - y.coord(i)).abs();
57+
}
58+
59+
sum
60+
}
61+
62+
/// The taxicab distance function.
63+
impl<T: Coordinates> Proximity for Taxicab<T> {
64+
type Distance = T::Value;
65+
66+
fn distance(&self, other: &Self) -> Self::Distance {
67+
taxicab_distance(self, other)
68+
}
69+
}
70+
71+
impl<T: Coordinates> Proximity<T> for Taxicab<T> {
72+
type Distance = T::Value;
73+
74+
fn distance(&self, other: &T) -> Self::Distance {
75+
taxicab_distance(self, other)
76+
}
77+
}
78+
79+
impl<T: Coordinates> Proximity<Taxicab<T>> for T {
80+
type Distance = T::Value;
81+
82+
fn distance(&self, other: &Taxicab<T>) -> Self::Distance {
83+
taxicab_distance(self, other)
84+
}
85+
}
86+
87+
/// Taxicab distance is a metric.
88+
impl<T: Coordinates> Metric for Taxicab<T> {}
89+
90+
impl<T: Coordinates> Metric<T> for Taxicab<T> {}
91+
92+
impl<T: Coordinates> Metric<Taxicab<T>> for T {}
93+
94+
impl<T: Coordinates> CoordinateProximity<T::Value> for Taxicab<T> {
95+
type Distance = T::Value;
96+
97+
fn distance_to_coords(&self, coords: &[T::Value]) -> Self::Distance {
98+
taxicab_distance(self, coords)
99+
}
100+
}
101+
102+
impl<T: Coordinates> CoordinateMetric<T::Value> for Taxicab<T> {}
103+
104+
#[cfg(test)]
105+
mod tests {
106+
use super::*;
107+
108+
#[test]
109+
fn test_distance() {
110+
assert_eq!(taxicab_distance([-3, 4], [4, -3]), 14);
111+
112+
assert_eq!(Taxicab([-3, 4]).distance(&Taxicab([4, -3])), 14);
113+
assert_eq!(Taxicab([-3, 4]).distance(&[4, -3]), 14);
114+
assert_eq!([-3, 4].distance(&Taxicab([4, -3])), 14);
115+
}
116+
}

0 commit comments

Comments
 (0)