Reorganize somewhat, add more fraction tests
All checks were successful
timw4mail/rusty-numbers/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2020-02-19 16:22:01 -05:00
parent 44467df38f
commit cbf66916f9
6 changed files with 234 additions and 177 deletions

View File

@ -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<usize>,
sign: Sign,
@ -41,6 +41,18 @@ impl<T: Unsigned> From<T> for BigInt {
}
}
impl From<&str> for BigInt {
fn from(_: &str) -> Self {
unimplemented!()
}
}
impl From<String> for BigInt {
fn from(_: String) -> Self {
unimplemented!()
}
}
impl BigInt {
pub fn new() -> Self {
Self::default()

View File

@ -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<u128> {
let mut table: Vec<u128> = vec![0, 1, 1, 2, 3, 5];
_fibonacci(n, &mut table)
}
/// Actual calculating function for `fibonacci`
#[inline]
fn _fibonacci(n: usize, table: &mut Vec<u128>) -> Option<u128> {
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<u128> {
let mut table: Vec<u128> = vec![1, 1, 2];
_factorial(n, &mut table)
}
/// Actual Calculation function for factoral
#[inline]
fn _factorial(n: usize, table: &mut Vec<u128>) -> Option<u128> {
match table.get(n) {
// Vec<T>.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());
}
}

View File

@ -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);

View File

@ -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<T: Unsigned = usize> {
numer: T,
@ -16,11 +33,15 @@ pub struct Frac<T: Unsigned = usize> {
#[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<T: Unsigned> Frac<T> {
}
/// 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<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
fn mul_assign(&mut self, rhs: Self) {
*self = self.clone() * rhs
*self = *self * rhs
}
}
@ -222,7 +243,7 @@ where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
fn div_assign(&mut self, rhs: Self) {
*self = self.clone() / rhs
*self = *self / rhs
}
}
@ -268,7 +289,7 @@ where
T: Unsigned + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
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<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
{
fn sub_assign(&mut self, rhs: Self) {
*self = self.clone() - rhs
*self = *self - rhs
}
}
@ -329,47 +354,6 @@ impl<T: Unsigned> Neg for Frac<T> {
#[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);

View File

@ -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<u128> {
let mut table: Vec<u128> = vec![0, 1, 1, 2, 3, 5];
_fibonacci(n, &mut table)
}
/// Actual calculating function for `fibonacci`
#[inline]
fn _fibonacci(n: usize, table: &mut Vec<u128>) -> Option<u128> {
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<u128> {
let mut table: Vec<u128> = vec![1, 1, 2];
_factorial(n, &mut table)
}
/// Actual Calculation function for factoral
#[inline]
fn _factorial(n: usize, table: &mut Vec<u128>) -> Option<u128> {
match table.get(n) {
// Vec<T>.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());
}
}

56
tests/fractions.rs Normal file
View File

@ -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));
}