use super::Ordinal;
use std::cmp::Ordering;
impl Ordinal {
pub fn ord_add(&self, other: &Ordinal) -> Ordinal {
if other.is_zero() {
return self.clone();
}
if self.is_zero() {
return other.clone();
}
let beta0 = &other.terms[0].0; let b0 = other.terms[0].1; let mut terms: Vec<(Ordinal, u128)> = Vec::new();
let mut self_coeff_at_beta0 = 0u128;
for (e, c) in &self.terms {
match e.cmp(beta0) {
Ordering::Greater => terms.push((e.clone(), *c)),
Ordering::Equal => self_coeff_at_beta0 = *c,
Ordering::Less => break, }
}
let coeff = self_coeff_at_beta0
.checked_add(b0)
.expect("ordinary ordinal addition coefficient exceeds u128");
terms.push((beta0.clone(), coeff));
terms.extend(other.terms[1..].iter().cloned());
Ordinal { terms }
}
pub fn ord_mul(&self, other: &Ordinal) -> Ordinal {
if self.is_zero() || other.is_zero() {
return Ordinal::zero();
}
let alpha0 = self.terms[0].0.clone(); let a0 = self.terms[0].1; let mut result = Ordinal::zero();
for (beta, b) in &other.terms {
let contribution = if beta.is_zero() {
let mut terms = Vec::with_capacity(self.terms.len());
terms.push((
alpha0.clone(),
a0.checked_mul(*b)
.expect("ordinary ordinal multiplication coefficient exceeds u128"),
));
terms.extend(self.terms[1..].iter().cloned());
Ordinal { terms }
} else {
Ordinal::monomial(alpha0.ord_add(beta), *b)
};
result = result.ord_add(&contribution);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fin(n: u128) -> Ordinal {
Ordinal::from_u128(n)
}
#[test]
fn ordinary_ordinal_addition_is_not_nim() {
let omega = Ordinal::omega();
let one = fin(1);
assert_eq!(one.ord_add(&omega), omega);
assert_ne!(omega.ord_add(&one), omega);
assert_eq!(omega.ord_add(&one), omega.nim_add(&one));
assert_eq!(omega.ord_add(&omega), Ordinal::monomial(fin(1), 2));
assert!(omega.nim_add(&omega).is_zero());
let left = Ordinal::monomial(fin(1), 2).ord_add(&one); let right = omega.ord_add(&fin(5)); assert_eq!(
left.ord_add(&right),
Ordinal::monomial(fin(1), 3).ord_add(&fin(5))
);
let a = omega.ord_add(&fin(2));
let b = Ordinal::omega_pow(fin(2)).ord_add(&one);
let c = Ordinal::monomial(fin(1), 5);
assert_eq!(a.ord_add(&b).ord_add(&c), a.ord_add(&b.ord_add(&c)));
}
#[test]
fn ordinary_ordinal_multiplication() {
let omega = Ordinal::omega();
assert_eq!(omega.ord_mul(&fin(2)), Ordinal::monomial(fin(1), 2));
assert_eq!(fin(2).ord_mul(&omega), omega);
assert_eq!(omega.ord_mul(&omega), Ordinal::omega_pow(fin(2)));
let w_plus_1 = omega.ord_add(&fin(1));
assert_eq!(
w_plus_1.ord_mul(&fin(2)),
Ordinal::monomial(fin(1), 2).ord_add(&fin(1))
);
assert_eq!(w_plus_1.ord_mul(&omega), Ordinal::omega_pow(fin(2)));
let lhs = omega.ord_mul(&omega).ord_mul(&omega);
let rhs = omega.ord_mul(&omega.ord_mul(&omega));
assert_eq!(lhs, rhs);
assert_eq!(lhs, Ordinal::omega_pow(fin(3)));
}
#[test]
fn ordinary_ordinal_coefficients_do_not_wrap() {
let half = 1u128 << 127;
let a = Ordinal::monomial(fin(1), half);
let b = Ordinal::monomial(fin(1), half);
assert!(std::panic::catch_unwind(|| a.ord_add(&b)).is_err());
assert!(
std::panic::catch_unwind(|| { Ordinal::monomial(fin(1), half).ord_mul(&fin(4)) })
.is_err()
);
}
}