use std::time::Instant;
use puremp::{Float, Int, Nat, Rational, RoundingMode};
fn bench<F, R>(label: &str, iters: u32, f: F) -> R
where
F: Fn() -> R,
{
let mut out = f();
let start = Instant::now();
for _ in 0..iters {
out = f();
}
let per = start.elapsed() / iters;
println!("{label:<34} {per:?}/iter");
out
}
fn factorial(n: u64) -> Int {
(2..=n).fold(Int::one(), |acc, k| acc.mul(&Int::from_i64(k as i64)))
}
fn main() {
let big = factorial(20000);
let big2 = factorial(20001);
println!(
"operands: factorial(20000) has {} bits",
big.magnitude().bit_len()
);
let a1000 = Int::from_i64(7).pow(1000);
let b1000 = Int::from_i64(3).pow(1010);
bench("mul ~1k-bit", 200, || a1000.mul(&b1000));
bench("mul factorial(20000)^2", 5, || big.mul(&big2));
let prod = big.mul(&big2);
bench("div large/large (BZ)", 5, || prod.div_rem(&big));
bench("gcd ~large", 20, || big.gcd(&big2));
bench("to_string factorial(20000)", 10, || big.to_string().len());
let r = Rational::new(Int::from_i64(355), Int::from_i64(113));
bench("rational add+reduce", 5000, || r.add(&r).mul(&r));
bench("isqrt of factorial(20000)", 20, || big.magnitude().isqrt());
let n = RoundingMode::Nearest;
bench("pi @ 1000 bits", 20, || Float::pi(1000, n));
let two = Float::from_int(&Int::from_i64(2), 1000, n);
bench("sqrt(2) @ 1000 bits", 200, || two.sqrt(1000, n));
bench("exp(1) @ 1000 bits", 20, || Float::e(1000, n));
let check = Nat::from_u64(2).pow(64);
println!("2^64 = {check}");
}