use crate::cast::cast;
use crate::Float;
use gmp_mpfr_sys::gmp;
use gmp_mpfr_sys::mpfr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::slice;
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct OrdFloat {
inner: Float,
}
fn _static_assertions() {
static_assert_size!(OrdFloat, Float);
}
impl OrdFloat {
#[inline]
pub fn as_float(&self) -> &Float {
&self.inner
}
#[inline]
pub fn as_float_mut(&mut self) -> &mut Float {
&mut self.inner
}
}
impl Hash for OrdFloat {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
let s = &self.inner;
s.get_exp().hash(state);
s.is_sign_negative().hash(state);
if s.is_nan() || s.is_infinite() {
return;
}
let prec: usize = cast(s.prec());
let mut limbs = prec / cast::<_, usize>(gmp::LIMB_BITS);
if prec % cast::<_, usize>(gmp::LIMB_BITS) > 0 {
limbs += 1;
};
let slice = unsafe { slice::from_raw_parts(s.inner().d, limbs) };
slice.hash(state);
}
}
impl Eq for OrdFloat {}
impl Ord for OrdFloat {
#[inline]
fn cmp(&self, other: &OrdFloat) -> Ordering {
let s = &self.inner;
let o = &other.inner;
if s.is_zero() && o.is_zero() {
s.is_sign_positive().cmp(&o.is_sign_positive())
} else {
match (s.is_nan(), o.is_nan()) {
(false, true) => {
if o.is_sign_negative() {
Ordering::Greater
} else {
Ordering::Less
}
}
(true, false) => {
if o.is_sign_negative() {
Ordering::Less
} else {
Ordering::Greater
}
}
(true, true) => s.is_sign_positive().cmp(&o.is_sign_positive()),
(false, false) => unsafe {
mpfr::cmp(s.as_raw(), o.as_raw()).cmp(&0)
},
}
}
}
}
impl PartialEq for OrdFloat {
#[inline]
fn eq(&self, other: &OrdFloat) -> bool {
let s = &self.inner;
let o = &other.inner;
if s.is_nan() {
o.is_nan() && s.is_sign_negative() == o.is_sign_negative()
} else if s.is_zero() {
o.is_zero() && s.is_sign_negative() == o.is_sign_negative()
} else {
s.eq(o)
}
}
}
impl PartialOrd for OrdFloat {
#[inline]
fn partial_cmp(&self, other: &OrdFloat) -> Option<Ordering> {
Some(<OrdFloat as Ord>::cmp(self, other))
}
}
impl From<Float> for OrdFloat {
#[inline]
fn from(src: Float) -> Self {
OrdFloat { inner: src }
}
}
impl From<OrdFloat> for Float {
#[inline]
fn from(src: OrdFloat) -> Self {
src.inner
}
}
#[cfg(test)]
mod tests {
use crate::float::Special;
use crate::Float;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn calculate_hash<T>(t: &T) -> u64
where
T: Hash,
{
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
#[test]
fn check_zero() {
let p = Float::with_val(53, Special::Zero);
let n = Float::with_val(53, Special::NegZero);
assert_eq!(p, n);
let ord_p = p.as_ord();
let ord_n = n.as_ord();
assert_eq!(ord_p, ord_p);
assert_eq!(ord_n, ord_n);
assert_eq!(calculate_hash(ord_p), calculate_hash(ord_p));
assert_eq!(calculate_hash(ord_n), calculate_hash(ord_n));
assert_ne!(ord_p, ord_n);
assert_ne!(calculate_hash(ord_p), calculate_hash(ord_n));
}
}