From cbf66916f927c283f65a54c87e45656c3c2bb5eb Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 19 Feb 2020 16:22:01 -0500 Subject: [PATCH] Reorganize somewhat, add more fraction tests --- src/bigint.rs | 16 +++++- src/lib.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++- src/num.rs | 7 +++ src/rational.rs | 86 +++++++++++++------------------ src/seq.rs | 123 --------------------------------------------- tests/fractions.rs | 56 +++++++++++++++++++++ 6 files changed, 234 insertions(+), 177 deletions(-) delete mode 100644 src/seq.rs create mode 100644 tests/fractions.rs diff --git a/src/bigint.rs b/src/bigint.rs index 30b0d59..c84bd78 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -1,4 +1,4 @@ -//! Arbitrarily large integers +//! \[WIP\] Arbitrarily large integers //! //! Traits to implement: //! * Add @@ -14,7 +14,7 @@ //! * SubAssign use crate::num::*; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct BigInt { inner: Vec, sign: Sign, @@ -41,6 +41,18 @@ impl From for BigInt { } } +impl From<&str> for BigInt { + fn from(_: &str) -> Self { + unimplemented!() + } +} + +impl From for BigInt { + fn from(_: String) -> Self { + unimplemented!() + } +} + impl BigInt { pub fn new() -> Self { Self::default() diff --git a/src/lib.rs b/src/lib.rs index ae658ba..9ca4e41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,4 +6,125 @@ pub mod bigint; pub mod num; pub mod rational; -pub mod seq; + +/// 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::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![0, 1, 1, 2, 3, 5]; + + _fibonacci(n, &mut table) +} + +/// Actual calculating function for `fibonacci` +#[inline] +fn _fibonacci(n: usize, table: &mut Vec) -> Option { + match table.get(n) { + Some(x) => Some(*x), + None => { + let a = _fibonacci(n - 1, table)?; + let b = _fibonacci(n - 2, table)?; + + // Check for overflow when adding + let attempt = a.checked_add(b); + + if let Some(current) = attempt { + table.insert(n, current); + } + + attempt + } + } +} + +/// 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. +/// +/// Example: +/// ```rust +/// use rusty_numbers::factorial; +/// +/// let valid = factorial(3); // Some(6) +/// # assert_eq!(6, valid.unwrap()); +/// +/// let invalid = factorial(35); // None +/// # assert!(invalid.is_none()); +/// ``` +pub fn factorial(n: usize) -> Option { + 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 { + 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 + Some(x) => Some(*x), + None => { + // The ? suffix passes along the Option value + // to be handled later + // See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator + let prev = _factorial(n - 1, table)?; + + // Do an overflow-checked multiply + let attempt = (n as u128).checked_mul(prev); + + // If there isn't an overflow, add the result + // to the calculation table + if let Some(current) = attempt { + table.insert(n, current); + } + + attempt // Some(x) if no overflow + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[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()); + + let res = factorial(35); + assert!(res.is_none()); + } + + #[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()); + + let res = fibonacci(187); + assert!(res.is_none()); + } +} diff --git a/src/num.rs b/src/num.rs index 710bc95..beefea6 100644 --- a/src/num.rs +++ b/src/num.rs @@ -7,6 +7,7 @@ use core::ops::{ Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; +/// Represents the sign of a rational number #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Sign { /// Greater than zero, or zero @@ -285,6 +286,12 @@ impl_signed!(i8, i16, i32, i64, i128, isize); mod tests { use super::*; + #[test] + fn test_los() { + assert_eq!(4u8.left_overflowing_sub(2).0, 2u8); + assert_eq!(0u8.left_overflowing_sub(2).0, 2u8); + } + #[test] fn test_gcd() { assert_eq!(u8::gcd(2, 3), 1); diff --git a/src/rational.rs b/src/rational.rs index a00bf2b..d9ed1df 100644 --- a/src/rational.rs +++ b/src/rational.rs @@ -6,6 +6,23 @@ use std::cmp::{Ord, Ordering, PartialOrd}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; /// Type representing a fraction +/// +/// There are three basic constructors: +/// +/// ``` +/// use rusty_numbers::frac; +/// use rusty_numbers::rational::Frac; +/// +/// // Macro +/// let reduced_macro = frac!(3 / 4); +/// +/// // Frac::new (called by the macro) +/// let reduced = Frac::new(3, 4); +/// # assert_eq!(reduced_macro, reduced); +/// +/// // Frac::new_unreduced +/// let unreduced = Frac::new_unreduced(4, 16); +/// ``` #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Frac { numer: T, @@ -16,11 +33,15 @@ pub struct Frac { #[macro_export] /// Create a [Frac](rational/struct.Frac.html) type with signed or unsigned number literals /// -/// Accepts: +/// Example: +/// ``` +/// use rusty_numbers::frac; /// -/// ```no-run -/// // Fractions -/// frac!(1/3); +/// // Proper fractions +/// frac!(1 / 3); +/// +/// // Improper fractions +/// frac!(4 / 3); /// /// // Whole numbers /// frac!(5u8); @@ -116,7 +137,7 @@ impl Frac { } /// Convert the fraction to its simplest form - fn reduce(mut self) -> Self { + pub fn reduce(mut self) -> Self { let gcd = T::gcd(self.numer, self.denom); self.numer /= gcd; self.denom /= gcd; @@ -198,7 +219,7 @@ where T: Unsigned + Add + Sub + Mul + Div, { fn mul_assign(&mut self, rhs: Self) { - *self = self.clone() * rhs + *self = *self * rhs } } @@ -222,7 +243,7 @@ where T: Unsigned + Add + Sub + Mul + Div, { fn div_assign(&mut self, rhs: Self) { - *self = self.clone() / rhs + *self = *self / rhs } } @@ -268,7 +289,7 @@ where T: Unsigned + Add + Sub + Mul + Div, { fn add_assign(&mut self, rhs: Self) { - *self = self.clone() + rhs + *self = *self + rhs } } @@ -303,7 +324,11 @@ where let sign = Self::get_sign(a, b, FracOp::Subtraction); let res = Self::raw(numer, denom, sign); - if overflowed { -res } else { res } + if overflowed { + -res + } else { + res + } } } @@ -312,7 +337,7 @@ where T: Unsigned + Add + Sub + Mul + Div, { fn sub_assign(&mut self, rhs: Self) { - *self = self.clone() - rhs + *self = *self - rhs } } @@ -329,47 +354,6 @@ impl Neg for Frac { #[cfg(test)] mod tests { - use super::*; - - #[test] - fn mul_test() { - let frac1 = frac!(1 / 3u8); - let frac2 = frac!(2u8 / 3); - - let expected = frac!(2u8 / 9); - - assert_eq!(frac1 * frac2, expected); - } - - #[test] - fn add_test() { - assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2), "1/3 + 1/2"); - assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3"); - } - - #[test] - fn sub_test() { - assert_eq!(4u8.left_overflowing_sub(2).0, 2u8); - assert_eq!(0u8.left_overflowing_sub(2).0, 2u8); - assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3), "1/2 - 1/3"); - } - - #[test] - fn cmp_test() { - assert!(-frac!(5 / 3) < frac!(1 / 10_000)); - assert!(frac!(1 / 10_000) > -frac!(10)); - assert!(frac!(1 / 3) < frac!(1 / 2)); - assert_eq!(frac!(1 / 2), frac!(3 / 6)); - } - - #[test] - fn negative_fractions() { - assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3"); - assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3"); - assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - +1/3"); - assert_eq!(-frac!(1), -frac!(2 / 3) + -frac!(1 / 3), "-2/3 + -1/3"); - } - #[test] fn macro_test() { let frac1 = frac!(1 / 3); diff --git a/src/seq.rs b/src/seq.rs deleted file mode 100644 index c3c758f..0000000 --- a/src/seq.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! Sequences and other 'stock' math functions - -/// 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![0, 1, 1, 2, 3, 5]; - - _fibonacci(n, &mut table) -} - -/// Actual calculating function for `fibonacci` -#[inline] -fn _fibonacci(n: usize, table: &mut Vec) -> Option { - match table.get(n) { - Some(x) => Some(*x), - None => { - let a = _fibonacci(n - 1, table)?; - let b = _fibonacci(n - 2, table)?; - - // Check for overflow when adding - let attempt = a.checked_add(b); - - if let Some(current) = attempt { - table.insert(n, current); - } - - attempt - } - } -} - -/// 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. -/// -/// Example: -/// ```rust -/// use rusty_numbers::seq::factorial; -/// -/// let valid = factorial(3); // Some(6) -/// # assert_eq!(6, valid.unwrap()); -/// -/// let invalid = factorial(35); // None -/// # assert!(invalid.is_none()); -/// ``` -pub fn factorial(n: usize) -> Option { - 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 { - 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 - Some(x) => Some(*x), - None => { - // The ? suffix passes along the Option value - // to be handled later - // See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator - let prev = _factorial(n - 1, table)?; - - // Do an overflow-checked multiply - let attempt = (n as u128).checked_mul(prev); - - // If there isn't an overflow, add the result - // to the calculation table - if let Some(current) = attempt { - table.insert(n, current); - } - - attempt // Some(x) if no overflow - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[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()); - - let res = factorial(35); - assert!(res.is_none()); - } - - #[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()); - - let res = fibonacci(187); - assert!(res.is_none()); - } -} diff --git a/tests/fractions.rs b/tests/fractions.rs new file mode 100644 index 0000000..d4bd343 --- /dev/null +++ b/tests/fractions.rs @@ -0,0 +1,56 @@ +use rusty_numbers::frac; + +#[test] +fn mul_test() { + let frac1 = frac!(1 / 3u8); + let frac2 = frac!(2u8 / 3); + + let expected = frac!(2u8 / 9); + + assert_eq!(frac1 * frac2, expected); + + assert_eq!(frac!(0), frac!(0) * frac!(5 / 8)); +} + +#[test] +fn div_test() { + assert_eq!(frac!(1), frac!(1 / 3) / frac!(1 / 3)); + assert_eq!(frac!(1 / 9), frac!(1 / 3) / frac!(3)); +} + +#[test] +fn add_test() { + assert_eq!(frac!(5 / 8), frac!(5 / 8) + frac!(0)); + assert_eq!(frac!(5 / 6), frac!(1 / 3) + frac!(1 / 2), "1/3 + 1/2"); + assert_eq!(frac!(1 / 3), frac!(2 / 3) + -frac!(1 / 3), "2/3 + -1/3"); +} + +#[test] +fn sub_test() { + assert_eq!(frac!(5 / 8), frac!(5 / 8) - frac!(0)); + assert_eq!(frac!(1 / 6), frac!(1 / 2) - frac!(1 / 3), "1/2 - 1/3"); +} + +#[test] +fn cmp_test() { + assert!(frac!(0) < frac!(1)); + assert!(-frac!(5 / 3) < frac!(1 / 10_000)); + assert!(frac!(1 / 10_000) > -frac!(10)); + assert!(frac!(1 / 3) < frac!(1 / 2)); + assert_eq!(frac!(1 / 2), frac!(3 / 6)); +} + +#[test] +fn negative_fractions() { + assert_eq!(-frac!(1 / 3), -frac!(2 / 3) + frac!(1 / 3), "-2/3 + 1/3"); + assert_eq!(frac!(1), frac!(1 / 3) - -frac!(2 / 3), "1/3 - -2/3"); + assert_eq!(-frac!(1), -frac!(2 / 3) - frac!(1 / 3), "-2/3 - +1/3"); + assert_eq!(-frac!(1), -frac!(2 / 3) + -frac!(1 / 3), "-2/3 + -1/3"); +} + +#[test] +fn negative_mul_div() { + assert_eq!(-frac!(1 / 12), -frac!(1 / 3) * frac!(1 / 4)); + assert_eq!(-frac!(1 / 12), frac!(1 / 3) * -frac!(1 / 4)); + assert_eq!(frac!(1 / 12), -frac!(1 / 3) * -frac!(1 / 4)); +}