diff --git a/src/bigint.rs b/src/bigint.rs index 0e35edf..30b0d59 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -12,7 +12,7 @@ //! * RemAssign //! * Sub //! * SubAssign -use crate::{Sign, Unsigned}; +use crate::num::*; #[derive(Debug)] pub struct BigInt { diff --git a/src/lib.rs b/src/lib.rs index 371549e..ae658ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,146 +1,9 @@ +//! # Rusty Numbers +//! +//! Playin' with Numerics in Rust #![forbid(unsafe_code)] -use std::ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, - Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, -}; - pub mod bigint; +pub mod num; pub mod rational; pub mod seq; - -/// A Trait representing unsigned integer primitives -pub trait Unsigned: - Add - + AddAssign - + BitAnd - + BitAndAssign - + BitOr - + BitOrAssign - + BitXor - + BitXorAssign - + Div - + DivAssign - + Mul - + MulAssign - + Rem - + RemAssign - + Copy - + Shl - + ShlAssign - + Shr - + ShrAssign - + Sub - + SubAssign - + Eq - + Ord - + Not -{ - /// 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; - - /// The maximum value of the type - fn max_value() -> Self; - - /// Is this a zero value? - fn is_zero(self) -> bool; -} - -#[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_unsigned { - ($Type: ty) => { - impl Unsigned for $Type { - 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) - } - - fn max_value() -> $Type { - <$Type>::max_value() - } - - fn is_zero(self) -> bool { - self == 0 - } - } - }; -} -impl_unsigned!(u8); -impl_unsigned!(u16); -impl_unsigned!(u32); -impl_unsigned!(u64); -impl_unsigned!(u128); -impl_unsigned!(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); - } -} diff --git a/src/num.rs b/src/num.rs new file mode 100644 index 0000000..16f837e --- /dev/null +++ b/src/num.rs @@ -0,0 +1,159 @@ +//! Numeric Helper Traits +use std::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 + Div + DivAssign + Mul + MulAssign + Rem + RemAssign + Copy + Sub + SubAssign +{ +} + +/// 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; +} + +/// 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; +} + +#[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_int { + ($( $Type: ty ),* ) => { + $( + impl Int for $Type { + fn is_zero(self) -> bool { + self == 0 + } + + fn max_value() -> $Type { + <$Type>::max_value() + } + } + )* + } +} + +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) + } + } + )* + }; +} + +impl_num!(i8, u8, i16, u16, f32, i32, u32, f64, i64, u64, i128, u128, usize); +impl_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, usize); +impl_unsigned!(u8, u16, u32, u64, u128, 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); + } +} diff --git a/src/rational.rs b/src/rational.rs index 0e170ed..7a77c8a 100644 --- a/src/rational.rs +++ b/src/rational.rs @@ -11,7 +11,7 @@ //! * Sub //! * SubAssign -use crate::{Sign, Unsigned}; +use crate::num::*; use std::ops::{Mul, Neg}; #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]