#![doc = include_str!("../docs_home.md")]
pub trait NumForKnuth :
core::ops::Mul<Self, Output = Self> +
for<'a> core::ops::Mul<&'a Self, Output = Self> +
core::ops::Sub<Self, Output = Self> +
std::ops::Add<Output = Self> +
std::cmp::PartialOrd + Clone +
num_traits::sign::Unsigned +
num_traits::One +
num_traits::ToPrimitive {}
impl<Num> NumForKnuth for Num
where
Num:
core::ops::Mul<Num, Output = Num> +
for<'a> core::ops::Mul<&'a Self, Output = Self> +
core::ops::Sub<Num, Output = Num> +
std::ops::Add<Output = Num> +
std::cmp::PartialOrd + Clone +
num_traits::sign::Unsigned +
num_traits::One +
num_traits::ToPrimitive,
for<'a> &'a Num: core::ops::Mul<&'a Num, Output = Num> {}
pub fn hyperoperation<Num: NumForKnuth>(num_a: &Num, num_b: Num, arrows: u8) -> Num {
if arrows == 0 {
num_b * num_a
} else {
let mut res = num_a.clone();
let max = num_b - Num::one();
for _ in num_iter::range_inclusive(Num::one(), max) {
res = hyperoperation(num_a, res, arrows - 1);
}
res
}
}
#[derive(Clone)]
pub struct Hyperoperation<Num: NumForKnuth>
{
pub num_a: Num,
pub num_b: Num,
pub arrows: u8
}
impl<Num: NumForKnuth> Hyperoperation<Num> {
pub fn evaluate(self) -> Num {
hyperoperation(&self.num_a, self.num_b, self.arrows)
}
pub fn new(num_a: Num, num_b: Num, arrows: u8) -> Self {
Self {
num_a, num_b, arrows
}
}
}
impl<Num: core::fmt::Display + NumForKnuth> std::fmt::Display for Hyperoperation<Num> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let arrs = match self.arrows {
0 => String::from("×"),
_ => "↑".repeat((self.arrows)as usize)
};
write!(f, "{} {arrs} {}", self.num_a, self.num_b)
}
}
#[cfg(test)]
mod tests {
use num_bigint::BigUint;
use super::*;
type KN = Hyperoperation<BigUint>;
#[test]
fn small() {
let exprs: Vec<(KN, BigUint)> = vec![
(KN::new(4u8.into(), 7u8.into(), 0), 28u32.into()),
(KN::new(3u8.into(), 2u8.into(), 2), 27u8.into()),
(KN::new(2u8.into(), 4u8.into(), 2), 65536u32.into()),
(KN::new(2u8.into(), 3u8.into(), 3), 65536u32.into()),
(KN::new(3u8.into(), 3u8.into(), 2), 7625597484987u64.into()),
];
for (ex, res) in exprs {
println!("Evaluating {ex}");
assert_eq!(ex.evaluate(), res);
}
}
#[test]
fn biguint() {
let result = KN::new(5u8.into(), 3u8.into(), 2).evaluate();
println!("Result:\n{result}");
assert_eq!(
result % BigUint::from(100_000_000u32),
8203125u32.into()
)
}
}