rusty-numbers/src/lib.rs

294 lines
6.8 KiB
Rust
Raw Normal View History

//! # Rusty Numbers
//!
//! Playin' with Numerics in Rust
2020-02-12 22:29:57 -05:00
#![forbid(unsafe_code)]
2020-04-16 14:07:12 -04:00
#![no_std]
2020-02-12 22:29:57 -05:00
2020-04-16 14:07:12 -04:00
#[cfg(all(feature = "alloc", not(feature = "std")))]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(feature = "std")]
use core::f64::consts::{E, PI};
2020-02-12 22:29:57 -05:00
pub mod bigint;
pub mod num;
2020-02-12 22:29:57 -05:00
pub mod rational;
/// Calculate a number in the fibonacci sequence,
2020-02-24 13:49:09 -05:00
/// using recursion and a lookup table
///
/// Can calculate up to 186 using native unsigned 128 bit integers.
///
/// Example:
/// ```rust
2020-02-24 13:49:09 -05:00
/// use rusty_numbers::mem_fibonacci;
///
2020-02-24 13:49:09 -05:00
/// let valid = mem_fibonacci(45); // Some(1134903170)
/// # assert_eq!(1134903170, mem_fibonacci(45).unwrap());
/// # assert!(valid.is_some());
///
2020-02-24 13:49:09 -05:00
/// let invalid = mem_fibonacci(187); // None
/// # assert!(invalid.is_none());
/// ```
2020-02-20 17:13:46 -05:00
#[inline]
pub fn mem_fibonacci(n: usize) -> Option<u128> {
let mut table = [0u128; 187];
table[0] = 0;
table[1] = 1;
table[2] = 1;
2020-02-24 13:49:09 -05:00
/// Actual calculating function for `fibonacci`
fn f(n: usize, table: &mut [u128]) -> Option<u128> {
if n < 2 {
// The first values are predefined.
return Some(table[n]);
}
2020-02-21 13:15:05 -05:00
2020-02-24 13:49:09 -05:00
if n > 186 {
return None;
}
2020-02-24 13:49:09 -05:00
match table[n] {
// The lookup array starts out zeroed, so a zero
// is a not yet calculated value
0 => {
let a = f(n - 1, table)?;
let b = f(n - 2, table)?;
2020-02-24 13:49:09 -05:00
table[n] = a + b;
2020-02-24 13:49:09 -05:00
Some(table[n])
}
x => Some(x),
2020-02-21 13:15:05 -05:00
}
}
2020-02-24 13:49:09 -05:00
f(n, &mut table)
}
2020-02-20 17:13:46 -05:00
/// Calculate a number in the fibonacci sequence,
/// using naive recursion
///
/// REALLY SLOW
///
2020-02-20 17:13:46 -05:00
/// Can calculate up to 186 using native unsigned 128 bit integers.
#[inline]
pub fn rec_fibonacci(n: usize) -> Option<u128> {
if matches!(n, 0 | 1) {
Some(n as u128)
} else {
let a = rec_fibonacci(n - 1)?;
let b = rec_fibonacci(n - 2)?;
a.checked_add(b)
2020-02-20 17:13:46 -05:00
}
}
/// Calculate a number in the fibonacci sequence,
///
2020-02-24 13:49:09 -05:00
/// 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());
/// ```
2020-02-20 17:13:46 -05:00
#[inline]
pub fn fibonacci(n: usize) -> Option<u128> {
let mut a: u128 = 0;
let mut b: u128 = 1;
2020-02-20 17:13:46 -05:00
if matches!(n, 0 | 1) {
Some(n as u128)
} else {
for _ in 0..n - 1 {
let c: u128 = a.checked_add(b)?;
2020-02-20 17:13:46 -05:00
a = b;
b = c;
2020-02-20 17:13:46 -05:00
}
Some(b)
2020-02-20 17:13:46 -05:00
}
}
2020-02-21 13:15:05 -05:00
/// Calculate the value of a factorial iteratively
///
/// 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());
/// ```
2020-02-20 17:13:46 -05:00
#[inline]
2020-02-21 13:15:05 -05:00
pub fn it_factorial(n: usize) -> Option<u128> {
let mut total: u128 = 1;
if matches!(n, 0 | 1) {
Some(1u128)
} else {
for x in 1..=n {
total = total.checked_mul(x as u128)?;
2020-02-21 13:15:05 -05:00
}
Some(total)
}
}
2020-02-21 13:15:05 -05:00
/// Calculate the value of a factorial recrursively
2020-02-20 17:13:46 -05:00
///
/// 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());
/// ```
2020-02-20 17:13:46 -05:00
#[inline]
pub fn factorial(n: usize) -> Option<u128> {
if matches!(n, 0 | 1) {
Some(1u128)
} else {
let prev = factorial(n - 1)?;
2020-02-20 17:13:46 -05:00
(n as u128).checked_mul(prev)
2020-02-20 17:13:46 -05:00
}
}
2020-03-05 16:24:33 -05:00
/// Approximates a factorial using Stirling's approximation
///
/// Based on https://en.wikipedia.org/wiki/Stirling's_approximation
///
/// Can approximate up to ~ 170.624!
///
/// Example:
/// ```rust
/// use rusty_numbers::approx_factorial;
///
/// let valid = approx_factorial(170.6); // Some(..)
/// # assert!(valid.is_some());
///
/// let invalid = approx_factorial(171.0); // None
/// # assert!(invalid.is_none());
/// ```
2020-04-16 14:07:12 -04:00
#[cfg(feature = "std")]
2020-03-05 16:24:33 -05:00
#[inline]
pub fn approx_factorial(n: f64) -> Option<f64> {
let power = (n / E).powf(n);
let root = (PI * 2.0 * n).sqrt();
2020-03-05 16:24:33 -05:00
let res = power * root;
if res >= core::f64::MIN && res <= core::f64::MAX {
Some(res)
} else {
None
}
}
#[cfg(test)]
#[cfg_attr(tarpaulin, skip)]
mod tests {
use super::*;
#[test]
fn test_factorial() {
2020-02-21 13:15:05 -05:00
for pair in [[1usize, 0], [1, 1], [2, 2], [6, 3]].iter() {
assert_eq!(
Some(pair[0] as u128),
factorial(pair[1]),
"{}! should be {}",
pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
it_factorial(pair[1]),
"{}! should be {}",
pair[1],
pair[0]
);
}
2020-02-21 13:15:05 -05:00
// Verify each implementation returns the same results
let res = factorial(34);
2020-02-21 13:15:05 -05:00
let res2 = it_factorial(34);
assert!(res.is_some());
2020-02-21 13:15:05 -05:00
assert!(res2.is_some());
assert_eq!(res, res2);
2020-02-21 13:15:05 -05:00
// Bounds checks
assert!(factorial(35).is_none());
assert!(it_factorial(35).is_none());
}
2020-04-16 14:07:12 -04:00
#[cfg(feature = "std")]
#[test]
fn test_approx_factorial() {
assert!(approx_factorial(170.624).is_some());
assert!(approx_factorial(1.0).is_some());
assert!(approx_factorial(170.7).is_none());
}
#[test]
fn test_fibonacci() {
2020-02-21 13:15:05 -05:00
// Sanity checking
for pair in [[0usize, 0], [1, 1], [1, 2], [2, 3]].iter() {
assert_eq!(
Some(pair[0] as u128),
fibonacci(pair[1]),
"fibonacci({}) should be {}",
pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
mem_fibonacci(pair[1]),
"fibonacci({}) should be {}",
pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
rec_fibonacci(pair[1]),
"fibonacci({}) should be {}",
pair[1],
pair[0]
);
}
2020-02-21 13:15:05 -05:00
// Verify each implementation returns the same results
let res = fibonacci(186);
let res2 = mem_fibonacci(186);
assert!(res.is_some());
2020-02-20 17:13:46 -05:00
assert!(res2.is_some());
2020-02-21 13:15:05 -05:00
assert_eq!(res, res2);
2020-02-21 13:15:05 -05:00
// Bounds checks
assert!(fibonacci(187).is_none());
assert!(mem_fibonacci(187).is_none());
}
}