124 lines
3.3 KiB
Rust
124 lines
3.3 KiB
Rust
//! 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());
|
|
}
|
|
}
|