rust-book/__non-book-expirements__/factorial/src/main.rs

70 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) {
// 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
}
}
}
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.");
}
}
}
}