hyperoperation/
lib.rs

1#![doc = include_str!("../docs_home.md")]
2
3/// Number that can be used either as the first or second number in hyperoperation.
4/// Automatically implemented for all fitting numbers.
5///
6/// # Notable implementors
7///
8///  - All unsigned primitive numeric types
9///  - [BigUint](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html), which is useful to handle big results without overflowing
10pub trait NumForKnuth :
11    core::ops::Mul<Self, Output = Self> +
12    for<'a> core::ops::Mul<&'a Self, Output = Self> +
13    core::ops::Sub<Self, Output = Self> +
14    std::ops::Add<Output = Self> +
15    std::cmp::PartialOrd + Clone +
16    num_traits::sign::Unsigned +
17    num_traits::One +
18    num_traits::ToPrimitive {}
19
20
21impl<Num> NumForKnuth for Num
22where
23    Num:
24        core::ops::Mul<Num, Output = Num> +
25        for<'a> core::ops::Mul<&'a Self, Output = Self> +
26        core::ops::Sub<Num, Output = Num> +
27        std::ops::Add<Output = Num> +
28        std::cmp::PartialOrd + Clone +
29        num_traits::sign::Unsigned +
30        num_traits::One +
31        num_traits::ToPrimitive,
32
33    for<'a> &'a Num: core::ops::Mul<&'a Num, Output = Num> {}
34
35/// Calculate result of hyperoperation
36///
37/// First argument is the first number, second argument is the second number. Third argument is number of arrows in Knuth's up-arrow notation. \
38/// **Example:** `hyperoperation(4, 3, 2)` corresponds to 4 ↑↑ 3
39///
40/// This function is equivalent to `KnuthNotation::new(num_a, num_b, arrows).evaluate()`. [More about Hyperoperation struct...](Hyperoperation)
41///
42/// ```
43/// # use hyperoperation::hyperoperation;
44/// assert_eq!(
45///     hyperoperation::<u64>(&3, 3, 2), // 3 ↑↑ 3
46///     7625597484987
47/// );
48/// ```
49pub fn hyperoperation<Num: NumForKnuth>(num_a: &Num, num_b: Num, arrows: u8) -> Num {
50    // TODO: Use power
51    if arrows == 0 {
52        num_b * num_a
53    } else {
54        let mut res = num_a.clone();
55        let max = num_b - Num::one();
56        for _ in num_iter::range_inclusive(Num::one(), max) {
57            res = hyperoperation(num_a, res, arrows - 1);
58        }
59        res
60    }
61}
62
63/// Representation of Hyperoperation
64///
65/// # Features
66///
67///  - Evaluate the operation with [evaluate](Self::evaluate)
68///  - Format it with the Knuth's up-arrow notation
69///
70/// # Example
71///
72/// Evaluating hyperoperation and formatting it with [Knuth's up-arrow notation](https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation):
73/// ```
74/// # use hyperoperation::Hyperoperation;
75/// let expr = Hyperoperation::<u64>::new(3, 3, 2); // Represents 3 ↑↑ 3
76/// let result = expr.clone().evaluate(); // Calculate the value of 3 ↑↑ 3
77///
78/// println!("{expr} = {result}");
79/// assert_eq!(result, 7625597484987);
80/// assert_eq!(format!("{expr}"), "3 ↑↑ 3");
81/// ```
82#[derive(Clone)]
83pub struct Hyperoperation<Num: NumForKnuth>
84{
85    /// The first number, _before_ the arrows in Knuth's up-arrow notation
86    pub num_a: Num,
87
88    /// The second numer, _after_ the arrows in Knuth's up-arrow notation
89    pub num_b: Num,
90
91    /// Number of arrows in Knuth's up-arrow notation
92    pub arrows: u8
93}
94
95impl<Num: NumForKnuth> Hyperoperation<Num> {
96    /// Calculates the value of the operation.
97    ///
98    /// Please keep in mind, that for some expressions (like 3 ↑↑↑ 3), this could take a lot of time and/or overflow the value. \
99    /// To correctly handle large results, it's recommended to use [BigUint](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) as `Num`.
100    ///
101    /// # Panics
102    ///
103    /// In debug mode, the result might overflow `Num`'s capacity. In release mode, **it might silently overflow**!
104    ///
105    /// # Example
106    ///
107    /// ```
108    /// # use hyperoperation::Hyperoperation;
109    /// let expr = Hyperoperation::<u64>::new(3, 3, 2); // Represents 3 ↑↑ 3
110    /// assert_eq!(expr.evaluate(), 7625597484987);
111    /// ```
112    pub fn evaluate(self) -> Num {
113        hyperoperation(&self.num_a, self.num_b, self.arrows)
114    }
115
116    /// Shorthand for initializing the struct
117    pub fn new(num_a: Num, num_b: Num, arrows: u8) -> Self {
118        Self {
119            num_a, num_b, arrows
120        }
121    }
122}
123
124/*
125/// A more readable way of initializing the [KnuthNotation] struct. \
126/// It should be the preferred way to initialize it. Evaluate it with the [`evaluate`](KnuthNotation::evaluate) function
127///
128/// It's not possible to use expressions in this macro. If you need to, initialize [KnuthNotation] directly
129/// # Examples
130///
131/// | Syntax | Knuth's up-arrow notation |
132/// | ------ | ------- |
133/// | `knuth!(3 ^2^ 2)` | 3 ↑↑ 2 |
134/// | `knuth!(3 ^2^ 4)` | 3 ↑↑ 4 |
135/// | `knuth!(3 ^3^ 2)` | 3 ↑↑↑ 2 |
136///
137/// Example of usage:
138/// ```
139/// use knuth::knuth;
140/// // Evaluating the notation
141/// assert_eq!(
142///     knuth!(u8, 3 ^2^ 2).evaluate(),
143///     27
144/// );
145/// ```
146#[macro_export]
147macro_rules! knuth {
148    ($type:ty, $a:literal ^$arrs:literal^ $b:literal) => {
149    {
150        $crate::KnuthNotation {
151            num_a: <$type>::from($a as u128),
152            num_b: <$type>::from($b as u128),
153            arrows: $arrs
154        }
155    }};
156}*/
157
158impl<Num: core::fmt::Display + NumForKnuth> std::fmt::Display for Hyperoperation<Num> {
159    /// Format the expression as Knuth's notation
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// # use hyperoperation::Hyperoperation;
165    /// assert_eq!(
166    ///     format!("{}", Hyperoperation::<u32> {num_a: 3, num_b: 4, arrows: 2}),
167    ///     String::from("3 ↑↑ 4")
168    /// )
169    /// ```
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        let arrs = match self.arrows {
172            0 => String::from("×"),
173            _ => "↑".repeat((self.arrows)as usize)
174        };
175        write!(f, "{} {arrs} {}", self.num_a, self.num_b)
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use num_bigint::BigUint;
182    use super::*;
183
184    type KN = Hyperoperation<BigUint>;
185    #[test]
186    fn small() {
187        let exprs: Vec<(KN, BigUint)> = vec![
188            (KN::new(4u8.into(), 7u8.into(), 0), 28u32.into()),
189            (KN::new(3u8.into(), 2u8.into(), 2), 27u8.into()),
190            (KN::new(2u8.into(), 4u8.into(), 2), 65536u32.into()),
191            (KN::new(2u8.into(), 3u8.into(), 3), 65536u32.into()),
192            (KN::new(3u8.into(), 3u8.into(), 2), 7625597484987u64.into()),
193        ];
194
195        for (ex, res) in exprs {
196            println!("Evaluating {ex}");
197            assert_eq!(ex.evaluate(), res);
198        }
199    }
200
201    #[test]
202    fn biguint() {
203        let result = KN::new(5u8.into(), 3u8.into(), 2).evaluate();
204        println!("Result:\n{result}");
205        assert_eq!(
206            result % BigUint::from(100_000_000u32),
207            8203125u32.into()
208        )
209    }
210}