logicaffeine_base/
ordinal.rs1use crate::numeric::BigInt;
21use std::cmp::Ordering;
22use std::fmt;
23
24#[derive(Clone, PartialEq, Eq, Hash, Debug)]
26pub struct Ordinal {
27 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 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 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 pub fn successor(&self) -> Ordinal {
84 self.add(&Self::one())
85 }
86
87 pub fn is_successor(&self) -> bool {
89 self.terms.last().is_some_and(|(e, _)| e.is_zero())
90 }
91
92 pub fn is_limit(&self) -> bool {
94 self.terms.last().is_some_and(|(e, _)| !e.is_zero())
95 }
96
97 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 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 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 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 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 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
278fn 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;