//! # Rusty Numbers //! //! Playin' with Numerics in Rust #![forbid(unsafe_code)] #![no_std] #[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}; pub mod bigint; pub mod num; pub mod rational; /// Calculate a number in the fibonacci sequence, /// using recursion and a lookup table /// /// Can calculate up to 186 using native unsigned 128 bit integers. /// /// Example: /// ```rust /// use rusty_numbers::mem_fibonacci; /// /// let valid = mem_fibonacci(45); // Some(1134903170) /// # assert_eq!(1134903170, mem_fibonacci(45).unwrap()); /// # assert!(valid.is_some()); /// /// let invalid = mem_fibonacci(187); // None /// # assert!(invalid.is_none()); /// ``` #[inline] pub fn mem_fibonacci(n: usize) -> Option { let mut table = [0u128; 187]; table[0] = 0; table[1] = 1; table[2] = 1; /// Actual calculating function for `fibonacci` fn f(n: usize, table: &mut [u128]) -> Option { if n < 2 { // The first values are predefined. return Some(table[n]); } 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 = f(n - 1, table)?; let b = f(n - 2, table)?; table[n] = a + b; Some(table[n]) } x => Some(x), } } f(n, &mut table) } /// 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 { 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) } } /// Calculate a number in the fibonacci sequence, /// /// 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 fibonacci(n: usize) -> Option { let mut a: u128 = 0; let mut b: u128 = 1; if matches!(n, 0 | 1) { Some(n as u128) } else { for _ in 0..n - 1 { let c: u128 = a.checked_add(b)?; a = b; b = c; } Some(b) } } /// 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()); /// ``` #[inline] pub fn it_factorial(n: usize) -> Option { 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)?; } Some(total) } } /// Calculate the value of a factorial recrursively /// /// 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 { if matches!(n, 0 | 1) { Some(1u128) } else { let prev = factorial(n - 1)?; (n as u128).checked_mul(prev) } } /// 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()); /// ``` #[cfg(feature = "std")] #[inline] pub fn approx_factorial(n: f64) -> Option { let power = (n / E).powf(n); let root = (PI * 2.0 * n).sqrt(); 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() { 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] ); } // Verify each implementation returns the same results let res = factorial(34); let res2 = it_factorial(34); assert!(res.is_some()); assert!(res2.is_some()); assert_eq!(res, res2); // Bounds checks assert!(factorial(35).is_none()); assert!(it_factorial(35).is_none()); } #[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() { // 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] ); } // Verify each implementation returns the same results let res = fibonacci(186); let res2 = mem_fibonacci(186); assert!(res.is_some()); assert!(res2.is_some()); assert_eq!(res, res2); // Bounds checks assert!(fibonacci(187).is_none()); assert!(mem_fibonacci(187).is_none()); } }