use super::Surreal;
use crate::scalar::{Rational, Scalar};
use std::cmp::Ordering;
impl Surreal {
pub fn as_rational(&self) -> Option<Rational> {
match self.terms.as_slice() {
[] => Some(Rational::zero()),
[(e, c)] if e.is_zero() => Some(c.clone()),
_ => None,
}
}
pub fn as_dyadic(&self) -> Option<(i128, u128)> {
let q = self.as_rational()?;
let den = q.denom();
if den & (den - 1) != 0 {
return None; }
Some((q.numer(), u128::from(den.trailing_zeros())))
}
pub fn is_dyadic(&self) -> bool {
self.as_dyadic().is_some()
}
pub fn dyadic_birthday(&self) -> Option<u128> {
let (num, k) = self.as_dyadic()?;
Some(birthday_dyadic(num, k))
}
pub fn simplest_above(&self) -> Option<Surreal> {
let q = self.as_rational()?;
let v = if q.sign() == Ordering::Less {
Rational::zero() } else {
Rational::from_int(q.floor() + 1) };
Some(Surreal::from_rational(v))
}
pub fn simplest_below(&self) -> Option<Surreal> {
Some(self.neg().simplest_above()?.neg())
}
pub fn simplest_between(a: &Surreal, b: &Surreal) -> Option<Surreal> {
let (qa, qb) = (a.as_rational()?, b.as_rational()?);
if qa.cmp(&qb) != Ordering::Less {
return None;
}
Some(Surreal::from_rational(simplest_rational_between(qa, qb)))
}
pub fn floor(&self) -> Surreal {
let mut terms: Vec<(Surreal, Rational)> = Vec::new();
let mut constant = Rational::zero();
let mut saw_constant = false;
let mut infinitesimal_sign = Ordering::Equal;
for (e, c) in &self.terms {
match e.sign() {
Ordering::Greater => terms.push((e.clone(), c.clone())), Ordering::Equal => {
constant = c.clone();
saw_constant = true;
}
Ordering::Less if infinitesimal_sign == Ordering::Equal => {
infinitesimal_sign = c.sign();
}
Ordering::Less => {} }
}
let mut f = constant.floor();
if (!saw_constant || constant.is_integer()) && infinitesimal_sign == Ordering::Less {
f -= 1;
}
if f != 0 {
terms.push((Surreal::zero(), Rational::from_int(f)));
}
Surreal { terms }
}
pub fn frac(&self) -> Surreal {
self.sub(&self.floor())
}
}
fn simplest_below_rat(h: &Rational) -> Rational {
if h.sign() == Ordering::Greater {
Rational::zero() } else {
let f = h.floor();
if Rational::from_int(f).cmp(h) == Ordering::Less {
Rational::from_int(f) } else {
Rational::from_int(f - 1) }
}
}
fn simplest_above_rat(l: &Rational) -> Rational {
simplest_below_rat(&l.neg()).neg()
}
pub(super) fn simplest_in_cut(lo: &Option<Rational>, hi: &Option<Rational>) -> Rational {
match (lo, hi) {
(None, None) => Rational::zero(),
(None, Some(h)) => simplest_below_rat(h),
(Some(l), None) => simplest_above_rat(l),
(Some(l), Some(h)) => simplest_rational_between(l.clone(), h.clone()),
}
}
fn reduce_dyadic(mut num: i128, mut k: u128) -> (i128, u128) {
while k > 0 && num % 2 == 0 {
num /= 2;
k -= 1;
}
(num, k)
}
fn birthday_dyadic(num: i128, k: u128) -> u128 {
if k == 0 {
return num.unsigned_abs();
}
let (ln, lk) = reduce_dyadic(num - 1, k);
let (rn, rk) = reduce_dyadic(num + 1, k);
1 + birthday_dyadic(ln, lk).max(birthday_dyadic(rn, rk))
}
fn simplest_rational_between(a: Rational, b: Rational) -> Rational {
if b.sign() != Ordering::Greater {
return simplest_rational_between(b.neg(), a.neg()).neg();
}
if a.sign() == Ordering::Less {
return Rational::zero(); }
let c = a.floor() + 1;
if Rational::from_int(c).cmp(&b) == Ordering::Less {
return Rational::from_int(c); }
let m = a.floor();
let off = Rational::from_int(m);
off.add(&simplest_in_unit(a.sub(&off), b.sub(&off)))
}
fn simplest_in_unit(a: Rational, b: Rational) -> Rational {
let half = Rational::new(1, 2);
let mut lo = Rational::zero();
let mut hi = Rational::one();
loop {
let mid = lo.add(&hi).mul(&half);
let below_b = mid.cmp(&b) == Ordering::Less;
let above_a = a.cmp(&mid) == Ordering::Less;
if above_a && below_b {
return mid;
} else if !above_a {
lo = mid; } else {
hi = mid; }
}
}