From adbdc3b8eb59e76617d6113bd741adb164951f6f Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 11 Mar 2022 13:24:30 -0500 Subject: [PATCH] Split benchmark functions into modules --- src/factorial.rs | 85 ++++++++++++++ src/fibonacci.rs | 103 +++++++++++++++++ src/gcd.rs | 94 ++++++++++++++++ src/lib.rs | 286 +---------------------------------------------- src/parallel.rs | 3 + 5 files changed, 291 insertions(+), 280 deletions(-) create mode 100644 src/factorial.rs create mode 100644 src/fibonacci.rs create mode 100644 src/gcd.rs create mode 100644 src/parallel.rs diff --git a/src/factorial.rs b/src/factorial.rs new file mode 100644 index 0000000..f18923e --- /dev/null +++ b/src/factorial.rs @@ -0,0 +1,85 @@ +use std::f64::consts::{E, PI}; + +/// Calculate the value of a factorial iteratively +/// +/// Can calculate up to 34! using native unsigned 128 bit integers. +/// +/// Example: +/// ```rust +/// use rusty_fib_facts::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 recursively +/// +/// Can calculate up to 34! using native unsigned 128 bit integers. +/// +/// Example: +/// ```rust +/// use rusty_fib_facts::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_fib_facts::approx_factorial; +/// +/// let valid = approx_factorial(170.6); // Some(..) +/// # assert!(valid.is_some()); +/// +/// let invalid = approx_factorial(171.0); // None +/// # assert!(invalid.is_none()); +/// ``` +#[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 >= std::f64::MIN && res <= std::f64::MAX { + Some(res) + } else { + None + } +} \ No newline at end of file diff --git a/src/fibonacci.rs b/src/fibonacci.rs new file mode 100644 index 0000000..a19f575 --- /dev/null +++ b/src/fibonacci.rs @@ -0,0 +1,103 @@ +/// 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_fib_facts::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_fib_facts::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) + } +} diff --git a/src/gcd.rs b/src/gcd.rs new file mode 100644 index 0000000..f03a42d --- /dev/null +++ b/src/gcd.rs @@ -0,0 +1,94 @@ +use std::cmp::{max, min}; + +pub trait UnsignedGCD { + /// Find the greatest common denominator of two numbers + fn gcd(a: Self, b: Self) -> Self; + + /// Euclid gcd algorithm + fn e_gcd(a: Self, b: Self) -> Self; + + /// Stein gcd algorithm + fn stein_gcd(a: Self, b: Self) -> Self; + + /// Find the least common multiple of two numbers + fn lcm(a: Self, b: Self) -> Self; +} + +macro_rules! impl_unsigned { + ($($Type: ty),* ) => { + $( + impl UnsignedGCD for $Type { + /// Implementation based on + /// [Binary GCD algorithm](https://en.wikipedia.org/wiki/Binary_GCD_algorithm) + fn gcd(a: $Type, b: $Type) -> $Type { + if a == b { + return a; + } else if a == 0 { + return b; + } else if b == 0 { + return a; + } + + let a_even = a % 2 == 0; + let b_even = b % 2 == 0; + + if a_even { + if b_even { + // Both a & b are even + return Self::gcd(a >> 1, b >> 1) << 1; + } else if !b_even { + // b is odd + return Self::gcd(a >> 1, b); + } + } + + // a is odd, b is even + if (!a_even) && b_even { + return Self::gcd(a, b >> 1); + } + + if a > b { + return Self::gcd((a - b) >> 1, b); + } + + Self::gcd((b - a) >> 1, a) + } + + fn e_gcd(x: $Type, y: $Type) -> $Type { + let mut x = x; + let mut y = y; + while y != 0 { + let t = y; + y = x % y; + x = t; + } + x + } + + fn stein_gcd(a: Self, b: Self) -> Self { + match ((a, b), (a & 1, b & 1)) { + ((x, y), _) if x == y => y, + ((0, x), _) | ((x, 0), _) => x, + ((x, y), (0, 1)) | ((y, x), (1, 0)) => Self::stein_gcd(x >> 1, y), + ((x, y), (0, 0)) => Self::stein_gcd(x >> 1, y >> 1) << 1, + ((x, y), (1, 1)) => { + let (x, y) = (min(x, y), max(x, y)); + Self::stein_gcd((y - x) >> 1, x) + } + _ => unreachable!(), + } + } + + fn lcm(a: $Type, b: $Type) -> $Type { + if (a == 0 && b == 0) { + return 0; + } + + a * b / Self::gcd(a, b) + } + } + )* + }; +} + +impl_unsigned!(u8, u16, u32, u64, u128, usize); \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a799a39..31bfa0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,291 +3,17 @@ //! Implementations of common algorithms to benchmark #![forbid(unsafe_code)] +mod gcd; pub mod parallel; +pub mod factorial; +pub mod fibonacci; -use std::cmp::{max, min}; -use std::f64::consts::{E, PI}; -/// 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_fib_facts::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; +pub use gcd::UnsignedGCD; +pub use factorial::*; +pub use fibonacci::*; - /// 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_fib_facts::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_fib_facts::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 recursively -/// -/// Can calculate up to 34! using native unsigned 128 bit integers. -/// -/// Example: -/// ```rust -/// use rusty_fib_facts::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_fib_facts::approx_factorial; -/// -/// let valid = approx_factorial(170.6); // Some(..) -/// # assert!(valid.is_some()); -/// -/// let invalid = approx_factorial(171.0); // None -/// # assert!(invalid.is_none()); -/// ``` -#[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 >= std::f64::MIN && res <= std::f64::MAX { - Some(res) - } else { - None - } -} - -pub trait UnsignedGCD { - /// Find the greatest common denominator of two numbers - fn gcd(a: Self, b: Self) -> Self; - - /// Euclid gcd algorithm - fn e_gcd(a: Self, b: Self) -> Self; - - /// Stein gcd algorithm - fn stein_gcd(a: Self, b: Self) -> Self; - - /// Find the least common multiple of two numbers - fn lcm(a: Self, b: Self) -> Self; -} - -macro_rules! impl_unsigned { - ($($Type: ty),* ) => { - $( - impl UnsignedGCD for $Type { - /// Implementation based on - /// [Binary GCD algorithm](https://en.wikipedia.org/wiki/Binary_GCD_algorithm) - fn gcd(a: $Type, b: $Type) -> $Type { - if a == b { - return a; - } else if a == 0 { - return b; - } else if b == 0 { - return a; - } - - let a_even = a % 2 == 0; - let b_even = b % 2 == 0; - - if a_even { - if b_even { - // Both a & b are even - return Self::gcd(a >> 1, b >> 1) << 1; - } else if !b_even { - // b is odd - return Self::gcd(a >> 1, b); - } - } - - // a is odd, b is even - if (!a_even) && b_even { - return Self::gcd(a, b >> 1); - } - - if a > b { - return Self::gcd((a - b) >> 1, b); - } - - Self::gcd((b - a) >> 1, a) - } - - fn e_gcd(x: $Type, y: $Type) -> $Type { - let mut x = x; - let mut y = y; - while y != 0 { - let t = y; - y = x % y; - x = t; - } - x - } - - fn stein_gcd(a: Self, b: Self) -> Self { - match ((a, b), (a & 1, b & 1)) { - ((x, y), _) if x == y => y, - ((0, x), _) | ((x, 0), _) => x, - ((x, y), (0, 1)) | ((y, x), (1, 0)) => Self::stein_gcd(x >> 1, y), - ((x, y), (0, 0)) => Self::stein_gcd(x >> 1, y >> 1) << 1, - ((x, y), (1, 1)) => { - let (x, y) = (min(x, y), max(x, y)); - Self::stein_gcd((y - x) >> 1, x) - } - _ => unreachable!(), - } - } - - fn lcm(a: $Type, b: $Type) -> $Type { - if (a == 0 && b == 0) { - return 0; - } - - a * b / Self::gcd(a, b) - } - } - )* - }; -} - -impl_unsigned!(u8, u16, u32, u64, u128, usize); #[cfg(test)] #[cfg(not(tarpaulin_include))] diff --git a/src/parallel.rs b/src/parallel.rs new file mode 100644 index 0000000..a69f3dd --- /dev/null +++ b/src/parallel.rs @@ -0,0 +1,3 @@ +//! Parallel Algorithms +//! +//! These algorithms use threads to benchmark parallel operations