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}