use crate::comparisons_integral::CompareWith;
use crate::{Arbi, Digit};
use core::cmp::Ordering;
use core::cmp::{PartialEq, PartialOrd};
#[allow(clippy::unnecessary_cast)]
const BASE_DBL: f64 = 2.0 * ((1 as Digit) << (Digit::BITS - 1)) as f64;
const BASE_DBL_RECIPROCAL: f64 = 1.0 / BASE_DBL;
const CMP_DBL_SIZE_UPPER: usize = 33;
impl Arbi {
#[allow(dead_code)]
fn cmp_abs_double(z: &Self, mut dbl: f64) -> Ordering {
if dbl.is_infinite() {
if dbl < 0.0 {
return Ordering::Greater;
} else {
return Ordering::Less;
}
}
if z.size() == 0 {
return if dbl > 0.0 {
Ordering::Less
} else {
Ordering::Equal
};
}
if z.size() >= CMP_DBL_SIZE_UPPER {
return Ordering::Greater;
}
for _ in 1..z.size() {
dbl *= BASE_DBL_RECIPROCAL;
}
if BASE_DBL <= dbl {
return Ordering::Less;
}
for digit in z.vec.iter().rev() {
let cur: Digit = dbl as Digit;
match digit {
x if *x < cur => {
return Ordering::Less;
}
x if *x > cur => {
return Ordering::Greater;
}
_ => {
dbl = (dbl - cur as f64) * BASE_DBL;
}
}
}
if dbl > 0.0 {
Ordering::Less
} else {
Ordering::Equal
}
}
}
impl CompareWith<f64> for Arbi {
fn cmp_with(&self, dbl: f64) -> Ordering {
if self.is_negative() {
if dbl < 0.0 {
match Self::cmp_abs_double(self, -dbl) {
Ordering::Less => Ordering::Greater,
Ordering::Equal => Ordering::Equal,
Ordering::Greater => Ordering::Less,
}
} else {
Ordering::Less
}
} else if dbl < 0.0 {
Ordering::Greater
} else {
Self::cmp_abs_double(self, dbl)
}
}
}
impl PartialEq<f64> for Arbi {
fn eq(&self, other: &f64) -> bool {
!other.is_nan() && (self.cmp_with(*other) == Ordering::Equal)
}
}
impl PartialOrd<f64> for Arbi {
fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
if !other.is_nan() {
Some(self.cmp_with(*other))
} else {
None
}
}
}
impl PartialEq<f64> for &Arbi {
fn eq(&self, other: &f64) -> bool {
!other.is_nan() && ((*self).cmp_with(*other) == Ordering::Equal)
}
}
impl PartialOrd<f64> for &Arbi {
fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
if !other.is_nan() {
Some(self.cmp_with(*other))
} else {
None
}
}
}
impl PartialEq<Arbi> for f64 {
fn eq(&self, other: &Arbi) -> bool {
!self.is_nan() && other.cmp_with(*self) == Ordering::Equal
}
}
impl PartialOrd<Arbi> for f64 {
fn partial_cmp(&self, other: &Arbi) -> Option<Ordering> {
if !self.is_nan() {
Some(other.cmp_with(*self).reverse())
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::test::float::*;
use crate::util::test::{
get_seedable_rng, get_uniform_die, ldexp, Distribution,
};
use crate::{DDigit, SDDigit, SDigit};
#[test]
fn compare_to_f64() {
let zero = Arbi::zero();
assert_eq!(&zero, ZERO);
assert!(&zero < MIN_DOUBLE);
assert!(&zero < SUBNORMAL_DOUBLE);
assert!(&zero > LOWEST_DOUBLE);
let max_double = Arbi::from(MAX_DOUBLE);
assert!(&max_double < INF);
assert!(zero < INF);
assert!(-(&max_double) > MINF);
assert!(zero > MINF);
let mut max_double_mut = max_double.clone();
assert_eq!(&max_double_mut, MAX_DOUBLE);
max_double_mut += 1;
assert!(&max_double_mut > MAX_DOUBLE);
max_double_mut -= 2;
assert!(&max_double_mut < MAX_DOUBLE);
assert_eq!(Arbi::from(-MAX_DOUBLE), -MAX_DOUBLE);
assert!(Arbi::from(-&max_double) + 1 > -MAX_DOUBLE);
assert!(Arbi::from(-&max_double) - 1 < -MAX_DOUBLE);
let one = Arbi::one();
assert!(&one != NAN);
assert!(!(&one == NAN));
assert!(!(&one < NAN));
assert!(!(&one <= NAN));
assert!(!(&one >= NAN));
assert!(!(&one > NAN));
assert!(Arbi::from(DDigit::MAX) <= DDigit::MAX as f64);
assert!(Arbi::from(SDDigit::MIN) >= SDDigit::MIN as f64);
assert!(Arbi::from(SDigit::MIN) >= SDigit::MIN as f64);
assert!(Arbi::from(Digit::MAX) <= Digit::MAX as f64);
assert!(
(Arbi::one() << Digit::BITS as usize)
< ldexp(1.0, Digit::BITS as i32 + 1)
);
assert!(
(Arbi::one() << (Digit::BITS * 2) as usize)
< ldexp(1.0, (Digit::BITS as i32) * 2 + 1)
);
assert!(
(Arbi::one() << (Digit::BITS + 2) as usize)
> ldexp(1.0, Digit::BITS as i32 + 1)
);
assert!(
(Arbi::one() << (Digit::BITS * 2 + 2) as usize)
> ldexp(1.0, Digit::BITS as i32 * 2 + 1)
);
let shift = Digit::BITS as usize * (CMP_DBL_SIZE_UPPER - 1);
let large = Arbi::one() << shift; if Digit::BITS == 32 {
assert_eq!(large.size(), 33);
}
assert!(&large > f64::MAX);
assert!((&large + &Arbi::one()) > f64::MAX);
assert!((&large - &Arbi::one()) > f64::MAX); }
#[test]
fn misc() {
assert!(Arbi::from(5) > 4.9);
assert!(4.9 < Arbi::from(5));
assert!(Arbi::one() > -1.0);
assert!(Arbi::neg_one() < 1.0);
}
#[test]
fn smoke() {
let (mut rng, _) = get_seedable_rng();
let die = get_uniform_die(MAX_INT_NEG, MAX_INT);
for _ in 0..i16::MAX {
let rand_double: f64 = die.sample(&mut rng);
let int64: i64 = rand_double as i64;
let rand_arbi = Arbi::from(rand_double);
assert_eq!(rand_arbi, int64);
if rand_double < 0.0 {
assert!(rand_arbi >= rand_double);
} else {
assert!(rand_arbi <= rand_double);
}
}
}
}