|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
|
pub struct Point { |
|
|
dims: Vec<f32>, |
|
|
} |
|
|
|
|
|
impl Point { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn new(dims: Vec<f32>) -> Self { |
|
|
Self { dims } |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn origin(dims: usize) -> Self { |
|
|
Self { |
|
|
dims: vec![0.0; dims], |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dimensionality(&self) -> usize { |
|
|
self.dims.len() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dims(&self) -> &[f32] { |
|
|
&self.dims |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dims_mut(&mut self) -> &mut [f32] { |
|
|
&mut self.dims |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn magnitude(&self) -> f32 { |
|
|
self.dims.iter().map(|x| x * x).sum::<f32>().sqrt() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn is_normalized(&self) -> bool { |
|
|
let mag = self.magnitude(); |
|
|
(mag - 1.0).abs() < 0.001 |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn normalize(&self) -> Self { |
|
|
let mag = self.magnitude(); |
|
|
if mag == 0.0 { |
|
|
return self.clone(); |
|
|
} |
|
|
Self { |
|
|
dims: self.dims.iter().map(|x| x / mag).collect(), |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn add(&self, other: &Point) -> Self { |
|
|
assert_eq!( |
|
|
self.dimensionality(), |
|
|
other.dimensionality(), |
|
|
"Points must have same dimensionality" |
|
|
); |
|
|
Self { |
|
|
dims: self |
|
|
.dims |
|
|
.iter() |
|
|
.zip(other.dims.iter()) |
|
|
.map(|(a, b)| a + b) |
|
|
.collect(), |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn scale(&self, scalar: f32) -> Self { |
|
|
Self { |
|
|
dims: self.dims.iter().map(|x| x * scalar).collect(), |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
#[cfg(test)] |
|
|
mod tests { |
|
|
use super::*; |
|
|
|
|
|
#[test] |
|
|
fn test_new_point() { |
|
|
let p = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
assert_eq!(p.dimensionality(), 3); |
|
|
assert_eq!(p.dims(), &[1.0, 2.0, 3.0]); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_origin() { |
|
|
let origin = Point::origin(768); |
|
|
assert_eq!(origin.dimensionality(), 768); |
|
|
assert!(origin.dims().iter().all(|&x| x == 0.0)); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_magnitude() { |
|
|
let p = Point::new(vec![3.0, 4.0]); |
|
|
assert!((p.magnitude() - 5.0).abs() < 0.0001); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_normalize() { |
|
|
let p = Point::new(vec![3.0, 4.0]); |
|
|
let normalized = p.normalize(); |
|
|
assert!(normalized.is_normalized()); |
|
|
assert!((normalized.dims()[0] - 0.6).abs() < 0.0001); |
|
|
assert!((normalized.dims()[1] - 0.8).abs() < 0.0001); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_normalize_zero() { |
|
|
let p = Point::origin(3); |
|
|
let normalized = p.normalize(); |
|
|
assert_eq!(normalized.dims(), &[0.0, 0.0, 0.0]); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_add() { |
|
|
let a = Point::new(vec![1.0, 2.0]); |
|
|
let b = Point::new(vec![3.0, 4.0]); |
|
|
let c = a.add(&b); |
|
|
assert_eq!(c.dims(), &[4.0, 6.0]); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_scale() { |
|
|
let p = Point::new(vec![1.0, 2.0]); |
|
|
let scaled = p.scale(2.0); |
|
|
assert_eq!(scaled.dims(), &[2.0, 4.0]); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
#[should_panic(expected = "same dimensionality")] |
|
|
fn test_add_different_dims_panics() { |
|
|
let a = Point::new(vec![1.0, 2.0]); |
|
|
let b = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
let _ = a.add(&b); |
|
|
} |
|
|
} |
|
|
|