220 lines
4.9 KiB
Rust
220 lines
4.9 KiB
Rust
//! # Rusty Numbers
|
|
//!
|
|
//! Playin' with Numerics in Rust
|
|
#![forbid(unsafe_code)]
|
|
|
|
#[cfg_attr(tarpaulin, skip)]
|
|
pub mod bigint;
|
|
pub mod num;
|
|
pub mod rational;
|
|
|
|
/// 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());
|
|
/// ```
|
|
#[inline]
|
|
pub fn mem_fibonacci(n: usize) -> Option<u128> {
|
|
let mut table = [0u128; 187];
|
|
table[0] = 0;
|
|
table[1] = 1;
|
|
table[2] = 1;
|
|
|
|
_mem_fibonacci(n, &mut table)
|
|
}
|
|
|
|
/// Actual calculating function for `fibonacci`
|
|
#[inline]
|
|
fn _mem_fibonacci(n: usize, table: &mut [u128]) -> Option<u128> {
|
|
if n > 186 {
|
|
return None;
|
|
}
|
|
|
|
match table[n] {
|
|
// The lookup array starts out zeroed, so a zero
|
|
// is a not yet calculated value
|
|
0 => {
|
|
let a = _mem_fibonacci(n - 1, table)?;
|
|
let b = _mem_fibonacci(n - 2, table)?;
|
|
|
|
table[n] = a + b;
|
|
|
|
Some(table[n])
|
|
},
|
|
x => Some(x)
|
|
}
|
|
}
|
|
|
|
/// Calculate a number in the fibonacci sequence,
|
|
/// using naive recursion
|
|
///
|
|
/// REALLY SLOW
|
|
///
|
|
/// Can calculate up to 186 using native unsigned 128 bit integers.
|
|
#[inline]
|
|
pub fn rec_fibonacci(n: usize) -> Option<u128> {
|
|
match n {
|
|
0 => Some(0),
|
|
1 => Some(1),
|
|
n => {
|
|
let a = rec_fibonacci(n - 1)?;
|
|
let b = rec_fibonacci(n - 2)?;
|
|
|
|
a.checked_add(b)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Calculate a number in the fibonacci sequence,
|
|
/// using iteration
|
|
///
|
|
/// Can calculate up to 186
|
|
#[inline]
|
|
pub fn fibonacci(n: usize) -> Option<u128> {
|
|
let mut a: u128 = 0;
|
|
let mut b: u128 = 1;
|
|
|
|
match n {
|
|
0 => Some(a),
|
|
1 => Some(b),
|
|
_ => {
|
|
for _ in 0..n - 1 {
|
|
let c: u128 = a.checked_add(b)?;
|
|
|
|
a = b;
|
|
b = c;
|
|
}
|
|
|
|
Some(b)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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());
|
|
/// ```
|
|
#[inline]
|
|
pub fn mem_factorial(n: usize) -> Option<u128> {
|
|
let mut table = [0u128; 35];
|
|
table[0] = 1;
|
|
table[1] = 1;
|
|
table[2] = 2;
|
|
|
|
|
|
_mem_factorial(n, &mut table)
|
|
}
|
|
|
|
/// Actual Calculation function for factoral
|
|
#[inline]
|
|
fn _mem_factorial(n: usize, table: &mut [u128]) -> Option<u128> {
|
|
if n > 34 {
|
|
return None
|
|
}
|
|
|
|
match table[n] {
|
|
// Empty spaces in the lookup array are zero-filled
|
|
0 => {
|
|
// 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 = _mem_factorial(n - 1, table)?;
|
|
|
|
table[n] = (n as u128) * prev;
|
|
|
|
Some(table[n])
|
|
},
|
|
x => Some(x),
|
|
}
|
|
}
|
|
|
|
/// Calculate the value of a factorial,
|
|
///
|
|
/// 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());
|
|
/// ```
|
|
#[inline]
|
|
pub fn factorial(n: usize) -> Option<u128> {
|
|
match n {
|
|
0 => Some(1u128),
|
|
1 => Some(1u128),
|
|
_ => {
|
|
let prev = factorial(n - 1)?;
|
|
|
|
(n as u128).checked_mul(prev)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[cfg_attr(tarpaulin, skip)]
|
|
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);
|
|
let res2 = mem_factorial(34);
|
|
assert!(res.is_some());
|
|
assert_eq!(res.unwrap(), res2.unwrap());
|
|
|
|
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(20);
|
|
let res2 = rec_fibonacci(20);
|
|
assert_eq!(res, res2);
|
|
|
|
let res = fibonacci(186);
|
|
let res2 = mem_fibonacci(186);
|
|
assert!(res.is_some());
|
|
assert!(res2.is_some());
|
|
assert_eq!(res.unwrap(), res2.unwrap());
|
|
|
|
let res = fibonacci(187);
|
|
assert!(res.is_none());
|
|
}
|
|
}
|