74 lines
2.1 KiB
Rust
74 lines
2.1 KiB
Rust
extern crate separator;
|
|
|
|
use separator::Separatable;
|
|
|
|
use std::io;
|
|
use std::u128;
|
|
|
|
///! 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.
|
|
/// If the result overflows, an error message will be displayed.
|
|
fn factorial (n: usize, table: &mut Vec<u128>) -> Option<u128> {
|
|
match table.get(n) {
|
|
Some(x) => {
|
|
// Vec<T>.get returns a Option with a reference to the value, so deref
|
|
// Wrap in Some() for proper return type
|
|
Some(*x)
|
|
},
|
|
None => {
|
|
// If a previous base overflowed, just
|
|
// pass on the overflow. The overflow will
|
|
// be caught later. Using the max value of u128
|
|
// for convenience.
|
|
let prev = factorial(n - 1, table)
|
|
.unwrap_or(u128::MAX);
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
// The lookup table for previously calculated values
|
|
let mut table: Vec<u128> = vec![1, 1, 2];
|
|
|
|
println!("Factorial calculator.");
|
|
println!("Any non-number will exit.");
|
|
|
|
loop {
|
|
println!("\nWhich factorial to calculate?");
|
|
|
|
let mut index = String::new();
|
|
|
|
io::stdin().read_line(&mut index)
|
|
.expect("Failed to read line");
|
|
|
|
let index = match index.trim().parse() {
|
|
Ok(num) => num,
|
|
Err(_) => break, // Exit on non-number
|
|
};
|
|
|
|
println!("Calculating factorial of: {}", index);
|
|
|
|
match factorial(index, &mut table) {
|
|
Some(calculated) => {
|
|
println!("{}! is {}", index, calculated.separated_string())
|
|
},
|
|
None => {
|
|
println!("Calculation overflow. The factorial base was too large.");
|
|
}
|
|
}
|
|
}
|
|
}
|