From bef01a8a86f8e495da7a5472e54936ec2f555fd6 Mon Sep 17 00:00:00 2001 From: Manuel Simon Date: Wed, 7 Aug 2019 20:47:22 +0200 Subject: [PATCH 1/4] Add traits `Distance` and `Norm` in order to provide abstract notions of distances between numbers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Norm` trait provides a `norm` function for types that represent a normed vector space – e.g. the absolute value of a float or the norm of a complex number. The `Distance` trait provides a `distance` function to still calculate the distance between two values if no sensible norm can be defined. --- src/dist.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 74 insertions(+) create mode 100644 src/dist.rs diff --git a/src/dist.rs b/src/dist.rs new file mode 100644 index 00000000..516b754b --- /dev/null +++ b/src/dist.rs @@ -0,0 +1,73 @@ +use std::ops::{Sub, Div, DivAssign}; + +use {Num}; + +pub trait Distance { + type Output: Num; + fn distance(&self, other: &Self) -> Self::Output; +} + +pub trait Norm: Sized { + type Output: Num; + fn norm(&self) -> ::Output; +} + +pub fn normalize + DivAssign, R: Num>(v: &mut T) { + *v /= v.norm(); +} + +pub fn normalized + Div, R: Num>(v: T) -> T { + let norm = v.norm(); + v / norm +} + +impl> Distance for T{ + type Output = ::Output; + fn distance(&self, other: &Self) -> ::Output { + (*self - *other).norm() + } +} + + +macro_rules! norm_impl_self { + ($($t:ty)*) => ($( + impl Norm for $t { + type Output = Self; + fn norm(&self) -> ::Output { + *self + } + } + )*) +} + +macro_rules! norm_impl_abs { + ($($t:ty)*) => ($( + impl Norm for $t { + type Output = Self; + fn norm(&self) -> ::Output { + self.abs() + } + } + )*) +} + +macro_rules! norm_impl_unsigned_output { + ($($t:ty, $out:ty);*) => ($( + impl Norm for $t { + type Output = $out; + fn norm(&self) -> ::Output { + self.abs() as $out + } + } + )*) +} + +norm_impl_abs!(f32 f64); +norm_impl_unsigned_output!(i8, u8; i16, u16; i32, u32; i64, u64; isize, usize); +norm_impl_self!(u8 u16 u32 u64 usize); + +#[cfg(has_i128)] +norm_impl_unsigned_output!(i128, u128); + +#[cfg(has_u128)] +norm_impl_self!(u128); \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 172e7146..60a139aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod macros; pub mod bounds; pub mod cast; +pub mod dist; pub mod float; pub mod identities; pub mod int; From cc0131d0dd5a3dddae02c8a649cc39021fc29b4a Mon Sep 17 00:00:00 2001 From: Manuel Simon Date: Wed, 7 Aug 2019 21:20:59 +0200 Subject: [PATCH 2/4] Add documentation for the `dist` module and the `Distance` and `Norm` traits. --- src/dist.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/dist.rs b/src/dist.rs index 516b754b..014a6e06 100644 --- a/src/dist.rs +++ b/src/dist.rs @@ -1,21 +1,75 @@ +//! Traits for generically calculating distances between values. +//! +//! This is often necessary in generic numeric algorithms to determine if +//! the demanded precision is reached. + use std::ops::{Sub, Div, DivAssign}; use {Num}; +/// The abstract notion of the distance between two values. +/// +/// This can be used to calculate the distance between two arbitrary +/// values even if there is no sensible definition of a norm of these. pub trait Distance { + /// The resulting type of the distance function. + /// + /// Mathematically, a norm is a mapping from 2-tuples of vectors of a vector space _V_ + /// into the non-negative real numbers, so `Output` will usually be a floating point type + /// or in some cases an unsigned integer type. type Output: Num; + + /// Calculates the distance between `self` and `other`. fn distance(&self, other: &Self) -> Self::Output; } +/// The abstract notion of the norm of a vector. +/// +/// If `Self` is `Copy` and implements `Sub`, then `Distance` will +/// be generically implemented for it. The `distance` function +/// of this generic implementation will calculate the norm of the difference +/// of the two arguments. pub trait Norm: Sized { + /// The resulting type of the norm function. + /// + /// Mathematically, a norm is a mapping from a vector space _V_ into the non-negative + /// real numbers, so `Output` will usually be a floating point type + /// or in some cases an unsigned integer type. type Output: Num; + + /// Calculates the norm of `self`. + /// + /// On signed integer and floating point values, it calls the `abs` function. + /// + /// On unsigned integer values, it simply returns the original value. fn norm(&self) -> ::Output; } +/// Normalizes the vector `v`, i.e. divides it by its norm. +/// +/// As long as the implementations of `Div` and `DivAssign` on `T` match, +/// `v` will be equal to `normalized(v)` after calling this function. +/// +/// ## Attention +/// +/// Due to numerical errors, `v` is *not* guaranteed to have exactly norm `1` +/// after calling this function. +/// +/// On integer types this function will do complete nonsense since +/// `DivAssign` is implemented as an integer division for integers. pub fn normalize + DivAssign, R: Num>(v: &mut T) { *v /= v.norm(); } +/// Normalizes the normalized vector of `v`, i.e. `v` divided by its norm. +/// +/// ## Attention +/// +/// Due to numerical errors, the result is *not* guaranteed to have exactly norm `1` +/// after calling this function. +/// +/// On integer types this function will do complete nonsense since +/// `Div` is implemented as an integer division for integers. pub fn normalized + Div, R: Num>(v: T) -> T { let norm = v.norm(); v / norm @@ -29,6 +83,8 @@ impl> Distance for T{ } +/// Generically implements `Norm` for the unsigned integer types +/// by simply returning the original value. macro_rules! norm_impl_self { ($($t:ty)*) => ($( impl Norm for $t { @@ -40,6 +96,8 @@ macro_rules! norm_impl_self { )*) } +/// Generically implements `Norm` for types with an `abs` function +/// by returning the result of this function. macro_rules! norm_impl_abs { ($($t:ty)*) => ($( impl Norm for $t { @@ -51,6 +109,9 @@ macro_rules! norm_impl_abs { )*) } +/// Generically implements `Norm` for the signed integer types +/// by calling their `abs` function and casting to the corresponding unsinged +/// integer type. macro_rules! norm_impl_unsigned_output { ($($t:ty, $out:ty);*) => ($( impl Norm for $t { From bd65144d945b75588d6567d5ffb36aca4513d4a4 Mon Sep 17 00:00:00 2001 From: Manuel Simon Date: Wed, 7 Aug 2019 21:31:35 +0200 Subject: [PATCH 3/4] Add tests for implementations of `Norm` and `Distance`. --- src/dist.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/dist.rs b/src/dist.rs index 014a6e06..d0017cc8 100644 --- a/src/dist.rs +++ b/src/dist.rs @@ -131,4 +131,36 @@ norm_impl_self!(u8 u16 u32 u64 usize); norm_impl_unsigned_output!(i128, u128); #[cfg(has_u128)] -norm_impl_self!(u128); \ No newline at end of file +norm_impl_self!(u128); + + +#[test] +fn norm_floating_point() { + assert_eq!((-2.0f32).norm(), 2.0); + assert_eq!((-3.0f64).norm(), 3.0); +} + +#[test] +fn distance_floating_point() { + assert_eq!((5.0f32).distance(&3.0), 2.0); + assert_eq!((2.0f32).distance(&-3.0), 5.0); + assert_eq!((1.0f64).distance(&4.0), 3.0); +} + +#[test] +fn norm_unsigned_integer() { + assert_eq!(2u8.norm(), 2); + assert_eq!(3u16.norm(), 3); + assert_eq!(4u32.norm(), 4); + assert_eq!(5u64.norm(), 5); + assert_eq!(6usize.norm(), 6); +} + +#[test] +fn norm_signed_integer() { + assert_eq!((-2i8).norm(), 2); + assert_eq!((-3i16).norm(), 3); + assert_eq!((-4i32).norm(), 4); + assert_eq!((-5i64).norm(), 5); + assert_eq!((-6isize).norm(), 6); +} \ No newline at end of file From 7a9571eaa940fd5068abc71b6cbd1c3f7aa9be1a Mon Sep 17 00:00:00 2001 From: Manuel Simon Date: Wed, 7 Aug 2019 21:33:38 +0200 Subject: [PATCH 4/4] Insert pub-use-statements for `Norm` and `Distance` traits. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 60a139aa..8dc0d72f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,7 @@ pub use ops::saturating::Saturating; pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingShl, WrappingShr, WrappingSub}; pub use pow::{checked_pow, pow, Pow}; pub use sign::{abs, abs_sub, signum, Signed, Unsigned}; +pub use dist::{Norm, Distance}; #[macro_use] mod macros;