Skip to main content

logicaffeine_base/
ordinal.rs

1//! Ordinals below ε₀ in Cantor normal form — the stage counter of transfinite execution.
2//!
3//! Every ordinal `α < ε₀` is uniquely `ω^{β₁}·c₁ + ω^{β₂}·c₂ + … + ω^{βₖ}·cₖ` with exponents
4//! `β₁ > β₂ > … > βₖ` (themselves ordinals below ε₀, in the same form) and positive natural
5//! coefficients. An [`Ordinal`] stores exactly that list, so equality is structural and the order
6//! is lexicographic on (exponent, coefficient) pairs, a longer list winning a common prefix.
7//!
8//! The arithmetic is the ordinal arithmetic, not the arithmetic of naturals, and its asymmetries
9//! are the point:
10//!
11//! - **addition** absorbs every term of the left summand below the right summand's leading
12//!   exponent: `1 + ω = ω`, yet `ω + 1 > ω`;
13//! - **multiplication** distributes on the left only: `α·(β + γ) = α·β + α·γ`, while
14//!   `(1 + 1)·ω = ω ≠ ω + ω`; a term `ω^β·c` of the right factor contributes `ω^{α₁ + β}·c` when
15//!   `β > 0` and `α·c` (the leading coefficient scaled, the rest kept) when `β = 0`;
16//! - **exponentiation** `α^β = ∏ (α^{ω^{βᵢ}})^{cᵢ}`, where for `β ≥ 1` a finite base `n ≥ 2` gives
17//!   `n^{ω^β} = ω^{ω^{β'}}` (`β = 1 + β'`) and an infinite base gives `α^{ω^β} = ω^{α₁·ω^β}` —
18//!   so `2^ω = ω` and `(ω + 1)^ω = ω^ω`.
19
20use crate::numeric::BigInt;
21use std::cmp::Ordering;
22use std::fmt;
23
24/// An ordinal below ε₀. See the module documentation.
25#[derive(Clone, PartialEq, Eq, Hash, Debug)]
26pub struct Ordinal {
27    /// `(exponent, coefficient)` with strictly decreasing exponents and positive coefficients.
28    terms: Vec<(Ordinal, BigInt)>,
29}
30
31impl Ordinal {
32    pub fn zero() -> Ordinal {
33        Ordinal { terms: Vec::new() }
34    }
35
36    pub fn one() -> Ordinal {
37        Self::from_u64(1)
38    }
39
40    pub fn from_u64(n: u64) -> Ordinal {
41        Self::from_natural(BigInt::from_u64(n))
42    }
43
44    /// The finite ordinal `n`. Panics on a negative `n`.
45    pub fn from_natural(n: BigInt) -> Ordinal {
46        assert!(!n.is_negative(), "an ordinal is never negative");
47        if n.is_zero() {
48            return Self::zero();
49        }
50        Ordinal {
51            terms: vec![(Self::zero(), n)],
52        }
53    }
54
55    pub fn omega() -> Ordinal {
56        Self::omega_pow(Self::one())
57    }
58
59    /// `ω^e`.
60    pub fn omega_pow(e: Ordinal) -> Ordinal {
61        Ordinal {
62            terms: vec![(e, BigInt::from_i64(1))],
63        }
64    }
65
66    pub fn is_zero(&self) -> bool {
67        self.terms.is_empty()
68    }
69
70    pub fn is_finite(&self) -> bool {
71        self.terms.iter().all(|(e, _)| e.is_zero())
72    }
73
74    pub fn to_natural(&self) -> Option<BigInt> {
75        match self.terms.as_slice() {
76            [] => Some(BigInt::zero()),
77            [(e, c)] if e.is_zero() => Some(c.clone()),
78            _ => None,
79        }
80    }
81
82    /// `α + 1`.
83    pub fn successor(&self) -> Ordinal {
84        self.add(&Self::one())
85    }
86
87    /// Ends in a finite term: `α = β + 1` for some `β`.
88    pub fn is_successor(&self) -> bool {
89        self.terms.last().is_some_and(|(e, _)| e.is_zero())
90    }
91
92    /// Nonzero and not a successor: the supremum of everything below it.
93    pub fn is_limit(&self) -> bool {
94        self.terms.last().is_some_and(|(e, _)| !e.is_zero())
95    }
96
97    /// `β` with `β + 1 = self`, for a successor.
98    pub fn predecessor(&self) -> Option<Ordinal> {
99        if !self.is_successor() {
100            return None;
101        }
102        let mut terms = self.terms.clone();
103        let (_, c) = terms.last_mut().expect("a successor has a last term");
104        *c = c.sub(&BigInt::from_i64(1));
105        if c.is_zero() {
106            terms.pop();
107        }
108        Some(Ordinal { terms })
109    }
110
111    /// The exponent of the leading term; `None` for zero.
112    fn leading_exponent(&self) -> Option<&Ordinal> {
113        self.terms.first().map(|(e, _)| e)
114    }
115
116    pub fn add(&self, o: &Ordinal) -> Ordinal {
117        let Some(lead) = o.leading_exponent() else {
118            return self.clone();
119        };
120        let mut terms: Vec<(Ordinal, BigInt)> = self
121            .terms
122            .iter()
123            .take_while(|(e, _)| e >= lead)
124            .cloned()
125            .collect();
126        let mut rest = o.terms.iter();
127        if let Some((e, c)) = terms.last_mut() {
128            if e == lead {
129                let (_, oc) = rest.next().expect("o has a leading term");
130                *c = c.add(oc);
131            }
132        }
133        terms.extend(rest.cloned());
134        Ordinal { terms }
135    }
136
137    pub fn mul(&self, o: &Ordinal) -> Ordinal {
138        let Some(lead) = self.leading_exponent() else {
139            return Self::zero();
140        };
141        let mut acc = Self::zero();
142        for (beta, c) in &o.terms {
143            let part = if beta.is_zero() {
144                let mut terms = self.terms.clone();
145                terms[0].1 = terms[0].1.mul(c);
146                Ordinal { terms }
147            } else {
148                Ordinal {
149                    terms: vec![(lead.add(beta), c.clone())],
150                }
151            };
152            acc = acc.add(&part);
153        }
154        acc
155    }
156
157    /// `self^e` (with `0^0 = 1`).
158    pub fn pow(&self, e: &Ordinal) -> Ordinal {
159        if e.is_zero() {
160            return Self::one();
161        }
162        if self.is_zero() {
163            return Self::zero();
164        }
165        if *self == Self::one() {
166            return Self::one();
167        }
168        let mut acc = Self::one();
169        for (beta, c) in &e.terms {
170            let base = if beta.is_zero() {
171                self.clone()
172            } else {
173                self.pow_omega_power(beta)
174            };
175            acc = acc.mul(&base.pow_natural(c));
176        }
177        acc
178    }
179
180    /// `self^{ω^β}` for `β ≥ 1` and `self ≥ 2`.
181    fn pow_omega_power(&self, beta: &Ordinal) -> Ordinal {
182        if self.is_finite() {
183            let rest = match beta.to_natural() {
184                Some(n) => Self::from_natural(n.sub(&BigInt::from_i64(1))),
185                None => beta.clone(),
186            };
187            Self::omega_pow(Self::omega_pow(rest))
188        } else {
189            let lead = self.leading_exponent().expect("an infinite ordinal is nonzero");
190            Self::omega_pow(lead.mul(&Self::omega_pow(beta.clone())))
191        }
192    }
193
194    fn pow_natural(&self, n: &BigInt) -> Ordinal {
195        let mut acc = Self::one();
196        let mut base = self.clone();
197        let mut k = n.clone();
198        let two = BigInt::from_i64(2);
199        while !k.is_zero() {
200            let (q, r) = k.div_rem(&two).expect("2 is nonzero");
201            if !r.is_zero() {
202                acc = acc.mul(&base);
203            }
204            k = q;
205            if !k.is_zero() {
206                base = base.mul(&base);
207            }
208        }
209        acc
210    }
211}
212
213impl Ord for Ordinal {
214    fn cmp(&self, o: &Self) -> Ordering {
215        for (a, b) in self.terms.iter().zip(o.terms.iter()) {
216            match a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1)) {
217                Ordering::Equal => continue,
218                other => return other,
219            }
220        }
221        self.terms.len().cmp(&o.terms.len())
222    }
223}
224
225impl PartialOrd for Ordinal {
226    fn partial_cmp(&self, o: &Self) -> Option<Ordering> {
227        Some(self.cmp(o))
228    }
229}
230
231impl fmt::Display for Ordinal {
232    /// `ω^2·3 + ω + 5`, `ω^(ω + 1)`, `ω^ω^ω`: an exponent is parenthesised unless it is a
233    /// natural or a single bare power of ω.
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        if self.is_zero() {
236            return write!(f, "0");
237        }
238        let one = BigInt::from_i64(1);
239        for (i, (e, c)) in self.terms.iter().enumerate() {
240            if i > 0 {
241                write!(f, " + ")?;
242            }
243            if e.is_zero() {
244                write!(f, "{c}")?;
245                continue;
246            }
247            if *e == Ordinal::one() {
248                write!(f, "ω")?;
249            } else if e.is_finite() || (e.terms.len() == 1 && e.terms[0].1 == one) {
250                write!(f, "ω^{e}")?;
251            } else {
252                write!(f, "ω^({e})")?;
253            }
254            if *c != one {
255                write!(f, "·{c}")?;
256            }
257        }
258        Ok(())
259    }
260}
261
262impl crate::exact_codec::ExactCodec for Ordinal {
263    /// The Cantor normal form: the number of terms, then each exponent (recursively) and its
264    /// coefficient.
265    fn write(&self, out: &mut Vec<u8>) {
266        crate::exact_codec::write_len(self.terms.len(), out);
267        for (e, c) in &self.terms {
268            e.write(out);
269            c.write(out);
270        }
271    }
272
273    fn read(buf: &[u8], pos: &mut usize) -> Option<Self> {
274        read_ordinal(buf, pos, 0)
275    }
276}
277
278/// An ordinal at nesting `depth`, refused unless its exponents strictly decrease and its
279/// coefficients are positive.
280fn read_ordinal(buf: &[u8], pos: &mut usize, depth: usize) -> Option<Ordinal> {
281    use crate::exact_codec::ExactCodec;
282    if depth > crate::exact_codec::MAX_WIRE_DEPTH {
283        return None;
284    }
285    let n = crate::exact_codec::read_len(buf, pos, crate::exact_codec::MAX_WIRE_LEN)?;
286    let mut terms: Vec<(Ordinal, BigInt)> = Vec::with_capacity(n.min(64));
287    for _ in 0..n {
288        let e = read_ordinal(buf, pos, depth + 1)?;
289        let c = BigInt::read(buf, pos)?;
290        if !c.is_positive() || terms.last().is_some_and(|(prev, _)| *prev <= e) {
291            return None;
292        }
293        terms.push((e, c));
294    }
295    Some(Ordinal { terms })
296}
297
298#[cfg(test)]
299#[path = "../../logicaffeine_tests/unit/logicaffeine_base/ordinal/tests.rs"]
300mod tests;