//! Numeric Helper Traits #![allow(unused_comparisons)] use core::convert::TryFrom; use core::fmt::Debug; use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; /// Native number type pub trait Num: Add + AddAssign + Debug + Div + DivAssign + Mul + MulAssign + Rem + RemAssign + PartialOrd + PartialEq + Copy + Sub + SubAssign { /// Is this number type signed? fn is_signed(&self) -> bool { true } } /// Float primitive pub trait Float: Num { fn is_neg(self) -> bool; } /// Integer primitive pub trait Int: Num + BitAnd + BitAndAssign + BitOr + BitOrAssign + BitXor + BitXorAssign + Eq + Ord + Not + Shl + Shr + ShlAssign + ShrAssign { /// The maximum value of the type fn max_value() -> Self; /// Is this a zero value? fn is_zero(self) -> bool; /// Is this number less than zero? fn is_neg(self) -> bool; } /// A Trait representing unsigned integer primitives pub trait Unsigned: Int { /// Find the greatest common denominator of two numbers fn gcd(a: Self, b: Self) -> Self; /// Find the least common multiple of two numbers fn lcm(a: Self, b: Self) -> Self; fn is_signed(self) -> bool { false } } /// A Trait representing signed integer primitives pub trait Signed: Int { type Un; fn to_unsigned(self) -> Self::Un; } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Sign { Positive, Negative, } impl Default for Sign { fn default() -> Self { Sign::Positive } } impl Not for Sign { type Output = Sign; fn not(self) -> Self::Output { match self { Self::Positive => Self::Negative, Self::Negative => Self::Positive, } } } macro_rules! impl_num { ($( $Type: ty ),* ) => { $( impl Num for $Type { } )* } } macro_rules! impl_float { ($( $Type: ty ),* ) => { $( impl Float for $Type { fn is_neg(self) -> bool { self < 0.0 } } )* } } macro_rules! impl_int { ($( $Type: ty ),* ) => { $( impl Int for $Type { fn is_zero(self) -> bool { self == 0 } fn max_value() -> $Type { <$Type>::max_value() } /// Is this number less than zero? fn is_neg(self) -> bool { if self.is_signed() == false { false } else { self < 0 } } } )* } } macro_rules! impl_unsigned { ($($Type: ty),* ) => { $( impl Unsigned for $Type { /// Implementation based on https://en.wikipedia.org/wiki/Binary_GCD_algorithm fn gcd(a: $Type, b: $Type) -> $Type { if a == b { return a; } else if a == 0 { return b; } else if b == 0 { return a; } let a_even = a % 2 == 0; let b_even = b % 2 == 0; if a_even { if b_even { // Both a & b are even return Self::gcd(a >> 1, b >> 1) << 1; } else if !b_even { // b is odd return Self::gcd(a >> 1, b); } } // a is odd, b is even if (!a_even) && b_even { return Self::gcd(a, b >> 1); } if a > b { return Self::gcd((a - b) >> 1, b); } Self::gcd((b - a) >> 1, a) } fn lcm(a: $Type, b: $Type) -> $Type { if (a == 0 && b == 0) { return 0; } a * b / Self::gcd(a, b) } } )* }; } macro_rules! impl_signed { ($(($type: ty, $un_type: ty)),* ) => { $( impl Signed for $type { type Un = $un_type; fn to_unsigned(self) -> $un_type { // Converting from signed to unsigned should always be safe // when using the absolute value, especially since I'm converting // between the same bit size <$un_type>::try_from(self).unwrap() } } )* } } impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, i64, u64, i128, u128, isize, usize); impl_float!(f32, f64); impl_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); impl_unsigned!(u8, u16, u32, u64, u128, usize); impl_signed!( (i8, u8), (i16, u16), (i32, u32), (i64, u64), (i128, u128), (isize, usize) ); #[cfg(test)] mod tests { use super::*; #[test] fn test_gcd() { assert_eq!(u8::gcd(2, 2), 2); assert_eq!(u16::gcd(36, 48), 12); } #[test] fn test_lcm() { assert_eq!(usize::lcm(15, 30), 30); assert_eq!(u128::lcm(1, 5), 5); } }