use crate::scalar::Scalar;
use crate::scalar::Surreal;
use std::cmp::Ordering;
#[derive(Clone, PartialEq)]
pub struct Omnific(Surreal);
impl std::fmt::Display for Omnific {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::fmt::Debug for Omnific {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl From<i128> for Omnific {
fn from(n: i128) -> Self {
Omnific::from_int(n)
}
}
pub fn is_omnific_integer(s: &Surreal) -> bool {
for (exp, coeff) in s.terms() {
use std::cmp::Ordering::*;
match exp.sign() {
Less => return false, Equal => {
if !coeff.is_integer() {
return false;
}
}
Greater => {} }
}
true
}
impl Omnific {
pub fn from_int(n: i128) -> Self {
Omnific(Surreal::from_int(n))
}
pub fn omega() -> Self {
Omnific(Surreal::omega())
}
pub fn from_surreal(s: Surreal) -> Option<Self> {
if is_omnific_integer(&s) {
Some(Omnific(s))
} else {
None
}
}
pub fn inner(&self) -> &Surreal {
&self.0
}
#[allow(clippy::should_implement_trait)]
pub fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
pub fn rem(&self, modulus: &Self) -> Option<Self> {
let r = self.0.rem(&modulus.0)?;
Some(Omnific::from_surreal(r).expect("an omnific CNF tail is omnific"))
}
pub fn from_floor(s: &Surreal) -> Omnific {
Omnific::from_surreal(s.floor()).expect("a surreal floor is always omnific")
}
}
impl Eq for Omnific {}
impl PartialOrd for Omnific {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(std::cmp::Ord::cmp(self, other))
}
}
impl Ord for Omnific {
fn cmp(&self, other: &Self) -> Ordering {
Omnific::cmp(self, other)
}
}
impl Scalar for Omnific {
fn zero() -> Self {
Omnific(Surreal::zero())
}
fn one() -> Self {
Omnific(Surreal::one())
}
fn from_int(n: i128) -> Self {
Omnific::from_int(n)
}
fn add(&self, rhs: &Self) -> Self {
Omnific(self.0.add(&rhs.0))
}
fn neg(&self) -> Self {
Omnific(self.0.neg())
}
fn mul(&self, rhs: &Self) -> Self {
Omnific(self.0.mul(&rhs.0))
}
fn characteristic() -> u128 {
0
}
fn inv(&self) -> Option<Self> {
if self.0 == Surreal::one() || self.0 == Surreal::one().neg() {
Some(self.clone())
} else {
None
}
}
fn is_zero(&self) -> bool {
self.0.is_zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clifford::{CliffordAlgebra, Metric};
use crate::scalar::{Integer, Rational};
fn surr_int(n: i128) -> Surreal {
Surreal::from_int(n)
}
#[test]
fn validator_accepts_omnific_integers() {
assert!(is_omnific_integer(&Surreal::omega())); assert!(is_omnific_integer(
&Surreal::omega_pow(surr_int(2)).add(&surr_int(3))
)); assert!(is_omnific_integer(&Surreal::monomial(
surr_int(1),
Rational::new(1, 2)
)));
assert!(is_omnific_integer(&surr_int(7)));
assert!(is_omnific_integer(&Surreal::zero()));
}
#[test]
fn validator_rejects_non_integers() {
assert!(!is_omnific_integer(&Surreal::epsilon())); assert!(!is_omnific_integer(
&Surreal::omega().add(&Surreal::from_rational(Rational::new(1, 2)))
));
assert!(!is_omnific_integer(&Surreal::from_rational(Rational::new(
5, 3
))));
assert!(Omnific::from_surreal(Surreal::epsilon()).is_none());
}
#[test]
fn ring_axioms_on_a_sample() {
let a = Omnific::omega(); let b = Omnific::from_int(3);
let c = Omnific::from_surreal(Surreal::omega_pow(surr_int(2))).unwrap(); assert_eq!(a.add(&b).add(&c), a.add(&b.add(&c)));
assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)));
assert_eq!(a.mul(&b.add(&c)), a.mul(&b).add(&a.mul(&c)));
assert_eq!(a.add(&b), b.add(&a));
assert_eq!(a.mul(&b), b.mul(&a));
assert_eq!(a.add(&Omnific::zero()), a);
assert_eq!(a.mul(&Omnific::one()), a);
assert_eq!(a.sub(&a), Omnific::zero());
assert!(is_omnific_integer(a.mul(&b).add(&c).inner()));
}
#[test]
fn standard_order_is_surreal_value_order() {
assert!(Omnific::omega() > Omnific::from_int(1_000_000));
assert_eq!(
std::cmp::Ord::cmp(&Omnific::from_int(4), &Omnific::from_int(4)),
Ordering::Equal
);
}
#[test]
fn only_plus_minus_one_are_units() {
assert!(Omnific::one().inv().is_some());
assert!(Omnific::one().neg().inv().is_some());
assert!(Omnific::from_int(2).inv().is_none());
assert!(Omnific::omega().inv().is_none()); assert!(Omnific::zero().inv().is_none());
}
#[test]
fn remainder_by_monic_omega_power_preserves_omnific_integrality() {
let x = Omnific::from_surreal(
Surreal::omega_pow(surr_int(2))
.mul(&surr_int(3))
.sub(&Surreal::omega())
.add(&surr_int(5)),
)
.unwrap();
let w2 = Omnific::from_surreal(Surreal::omega_pow(surr_int(2))).unwrap();
assert_eq!(
x.rem(&w2).unwrap(),
Omnific::from_surreal(Surreal::omega().neg().add(&surr_int(5))).unwrap()
);
assert_eq!(x.rem(&Omnific::omega()).unwrap(), Omnific::from_int(5));
assert_eq!(x.rem(&Omnific::from_int(1)).unwrap(), Omnific::zero());
}
#[test]
fn remainder_rejects_non_monic_omnific_moduli() {
let x = Omnific::omega().add(&Omnific::from_int(7));
assert!(x.rem(&Omnific::zero()).is_none());
assert!(x
.rem(&Omnific::omega().add(&Omnific::from_int(1)))
.is_none());
assert!(x
.rem(&Omnific::omega().mul(&Omnific::from_int(2)))
.is_none());
}
#[test]
fn exterior_algebra_over_oz_with_transfinite_coefficients() {
let alg = CliffordAlgebra::new(3, Metric::<Omnific>::grassmann(3));
let (e0, e1, e2) = (alg.e(0), alg.e(1), alg.e(2));
assert!(alg.mul(&e0, &e0).is_zero());
assert_eq!(alg.add(&alg.mul(&e0, &e1), &alg.mul(&e1, &e0)), alg.zero());
let we0 = alg.scalar_mul(&Omnific::omega(), &e0);
let prod = alg.wedge(&we0, &e1);
assert_eq!(
prod,
alg.scalar_mul(&Omnific::omega(), &alg.wedge(&e0, &e1))
);
let triple = alg.wedge(&alg.wedge(&e0, &e1), &e2);
assert_eq!(triple, alg.blade(&[0, 1, 2]));
}
#[test]
fn matches_integer_backend_on_integer_inputs() {
let oz = CliffordAlgebra::new(2, Metric::<Omnific>::grassmann(2));
let oz_prod = oz.wedge(
&oz.scalar_mul(&Omnific::from_int(2), &oz.e(0)),
&oz.scalar_mul(&Omnific::from_int(3), &oz.e(1)),
);
assert_eq!(
oz_prod,
oz.scalar_mul(&Omnific::from_int(6), &oz.wedge(&oz.e(0), &oz.e(1)))
);
let zz = CliffordAlgebra::new(2, Metric::<Integer>::grassmann(2));
let zz_prod = zz.wedge(
&zz.scalar_mul(&Integer(2), &zz.e(0)),
&zz.scalar_mul(&Integer(3), &zz.e(1)),
);
assert_eq!(*zz_prod.terms.get(&0b11).unwrap(), Integer(6));
assert_eq!(*oz_prod.terms.get(&0b11).unwrap(), Omnific::from_int(6));
}
}