diff --git a/src/bigint.rs b/src/bigint.rs index a2791e7..0e35edf 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -34,7 +34,7 @@ impl From for BigInt { let mut new = Self::default(); if n > T::max_value() { - new.inner = BigInt::split(n); + new.split(n); } new @@ -42,24 +42,21 @@ impl From for BigInt { } impl BigInt { + pub fn new() -> Self { + Self::default() + } + /// Split an unsigned number into BigInt parts - fn split(num: T) -> Vec { + fn split(&mut self, num: T) -> Vec { // Pretty easy if you don't actually need to split the value! if num < T::max_value() { - return vec![T::into()]; + todo!(); + // return vec![num as usize]; } todo!(); } } -impl BigInt { - pub fn new() -> Self { - Self::default() - } -} - #[cfg(test)] -mod tests { - -} \ No newline at end of file +mod tests {} diff --git a/src/lib.rs b/src/lib.rs index 40cef1d..371549e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,64 @@ #![forbid(unsafe_code)] -use std::ops::Not; +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 rational; pub mod seq; -/// Dummy trait for implementing generics on unsigned number types -pub trait Unsigned: PartialEq + PartialOrd {} +/// 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; -impl Unsigned for u8 {} -impl Unsigned for u16 {} -impl Unsigned for u32 {} -impl Unsigned for u64 {} -impl Unsigned for usize {} -impl Unsigned for u128 {} + /// Find the least common multiple of two numbers + fn lcm(a: Self, b: Self) -> Self; -#[derive(Debug, Copy, Clone)] + /// 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 + Negative, +} + +impl Default for Sign { + fn default() -> Self { + Sign::Positive + } } impl Not for Sign { @@ -29,15 +68,79 @@ impl Not for Sign { match self { Self::Positive => Self::Negative, Self::Negative => Self::Positive, - _ => unreachable!() } } } +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 it_works() { - assert_eq!(2 + 2, 4); + 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 be05b00..0e170ed 100644 --- a/src/rational.rs +++ b/src/rational.rs @@ -12,15 +12,9 @@ //! * SubAssign use crate::{Sign, Unsigned}; -use std::ops::Neg; +use std::ops::{Mul, Neg}; -#[derive(Debug, Copy, Clone)] -pub enum FracType { - Proper(T, Frac), - Improper(Frac), -} - -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] pub struct Frac { numer: T, denom: T, @@ -30,14 +24,59 @@ pub struct Frac { impl Frac { /// Create a new rational number pub fn new(n: T, d: T) -> Self { + if d.is_zero() { + panic!("Fraction can not have a zero denominator"); + } + Frac { numer: n, denom: d, sign: Sign::Positive, } + .reduce() + } + + pub fn new_neg(n: T, d: T) -> Self { + let mut frac = Frac::new(n, d); + frac.sign = Sign::Negative; + + frac + } + + fn reduce(mut self) -> Self { + let gcd = T::gcd(self.numer, self.denom); + self.numer /= gcd; + self.denom /= gcd; + + self } } +macro_rules! impl_mul { + ($Type: ty) => { + impl Mul for Frac<$Type> { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + let numer = self.numer * rhs.numer; + let denom = self.denom * rhs.denom; + + // Figure out the sign + if self.sign != rhs.sign { + Self::new_neg(numer, denom) + } else { + Self::new(numer, denom) + } + } + } + }; +} +impl_mul!(u8); +impl_mul!(u16); +impl_mul!(u32); +impl_mul!(u64); +impl_mul!(usize); + impl Neg for Frac { type Output = Self; @@ -50,6 +89,4 @@ impl Neg for Frac { } #[cfg(test)] -mod tests { - -} \ No newline at end of file +mod tests {} diff --git a/src/seq.rs b/src/seq.rs index 85cf0a7..90721ff 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -1,11 +1,23 @@ -///! Sequences and other 'stock' math functions +//! Sequences and other 'stock' math functions -///! Calculate a number in the fibonacci sequence, -///! using a lookup table for better worst-case performance. +/// Calculate a number in the fibonacci sequence, +/// using a lookup table for better worst-case performance. /// /// Can calculate up to 186 using native unsigned 128 bit integers. +/// +/// Example +/// ```rust +/// use rusty_numbers::seq::fibonacci; +/// +/// let valid = fibonacci(45); // Some(1134903170) +/// # assert_eq!(1134903170, fibonacci(45).unwrap()); +/// # assert!(valid.is_some()); +/// +/// let invalid = fibonacci(187); // None +/// # assert!(invalid.is_none()); +/// ``` pub fn fibonacci(n: usize) -> Option { - let mut table: Vec = vec![]; + let mut table: Vec = vec![0, 1, 1, 2, 3, 5]; _fibonacci(n, &mut table) } @@ -31,20 +43,19 @@ fn _fibonacci(n: usize, table: &mut Vec) -> Option { } } -///! Calculate the value of a factorial, -///! using a lookup table for better worst-case performance. +/// Calculate the value of a factorial, +/// using a lookup table for better worst-case performance. /// /// Can calculate up to 34! using native unsigned 128 bit integers. -/// If the result overflows, an error message will be displayed. pub fn factorial(n: usize) -> Option { - let mut table: Vec = vec![0, 1, 1, 2, 3, 5]; + let mut table: Vec = vec![1, 1, 2]; _factorial(n, &mut table) } /// Actual Calculation function for factoral #[inline] -fn _factorial (n: usize, table: &mut Vec) -> Option { +fn _factorial(n: usize, table: &mut Vec) -> Option { match table.get(n) { // Vec.get returns a Option with a reference to the value, // so deref and wrap in Some() for proper return type @@ -75,6 +86,10 @@ mod tests { #[test] fn test_factorial() { + assert_eq!(1, factorial(0).unwrap()); + assert_eq!(1, factorial(1).unwrap()); + assert_eq!(6, factorial(3).unwrap()); + let res = factorial(34); assert!(res.is_some()); @@ -84,6 +99,10 @@ mod tests { #[test] fn test_fibonacci() { + assert_eq!(0, fibonacci(0).unwrap()); + assert_eq!(1, fibonacci(1).unwrap()); + assert_eq!(1, fibonacci(2).unwrap()); + let res = fibonacci(186); assert!(res.is_some());