Split benchmark functions into modules
This commit is contained in:
parent
8f43327b54
commit
adbdc3b8eb
85
src/factorial.rs
Normal file
85
src/factorial.rs
Normal file
@ -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<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)?;
|
||||
}
|
||||
|
||||
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<u128> {
|
||||
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<f64> {
|
||||
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
|
||||
}
|
||||
}
|
103
src/fibonacci.rs
Normal file
103
src/fibonacci.rs
Normal file
@ -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<u128> {
|
||||
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<u128> {
|
||||
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<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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<u128> {
|
||||
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)
|
||||
}
|
||||
}
|
94
src/gcd.rs
Normal file
94
src/gcd.rs
Normal file
@ -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);
|
286
src/lib.rs
286
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<u128> {
|
||||
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<u128> {
|
||||
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<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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<u128> {
|
||||
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<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)?;
|
||||
}
|
||||
|
||||
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<u128> {
|
||||
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<f64> {
|
||||
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))]
|
||||
|
3
src/parallel.rs
Normal file
3
src/parallel.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Parallel Algorithms
|
||||
//!
|
||||
//! These algorithms use threads to benchmark parallel operations
|
Loading…
Reference in New Issue
Block a user