use crate::{ext::xmpfr, Float};
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
};
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct OrdFloat {
inner: Float,
}
static_assert_same_layout!(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: Hasher>(&self, state: &mut H) {
let float = &self.inner;
float.inner().sign.hash(state);
float.inner().exp.hash(state);
let slice = float.inner_data();
if let Some(first) = slice.iter().position(|&limb| limb != 0) {
slice[first..].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) => xmpfr::cmp(s, o),
}
}
}
}
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(self.cmp(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
}
}
impl AsRef<Float> for OrdFloat {
#[inline]
fn as_ref(&self) -> &Float {
self.as_float()
}
}
impl AsMut<Float> for OrdFloat {
#[inline]
fn as_mut(&mut self) -> &mut Float {
self.as_float_mut()
}
}
#[cfg(test)]
mod tests {
use crate::{
float::{self, FreeCache, OrdFloat, Special},
ops::NegAssign,
Assign, Float,
};
use core::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
fn calculate_hash<T: Hash>(t: &T) -> u64 {
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));
float::free_cache(FreeCache::All);
}
fn hash(f: &OrdFloat) -> u64 {
let mut hasher = DefaultHasher::new();
f.hash(&mut hasher);
hasher.finish()
}
#[test]
fn check_hash_different_prec() {
let mut f = Float::new(53);
let mut g = Float::new(5301);
assert_eq!(f, g);
assert_eq!(f.as_ord(), g.as_ord());
assert_eq!(hash(f.as_ord()), hash(g.as_ord()));
g.neg_assign();
assert_eq!(f, g);
assert_ne!(f.as_ord(), g.as_ord());
assert_ne!(hash(f.as_ord()), hash(g.as_ord()));
f.assign(23.5);
g.assign(23.5);
assert_eq!(f, g);
assert_eq!(f.as_ord(), g.as_ord());
assert_eq!(hash(f.as_ord()), hash(g.as_ord()));
g.neg_assign();
assert_ne!(f, g);
assert_ne!(f.as_ord(), g.as_ord());
assert_ne!(hash(f.as_ord()), hash(g.as_ord()));
f.assign(Special::Nan);
g.assign(Special::Nan);
assert_ne!(f, g);
assert_eq!(f.as_ord(), g.as_ord());
assert_eq!(hash(f.as_ord()), hash(g.as_ord()));
g.neg_assign();
assert_ne!(f, g);
assert_ne!(f.as_ord(), g.as_ord());
assert_ne!(hash(f.as_ord()), hash(g.as_ord()));
}
#[test]
fn check_refs() {
let f = Float::with_val(53, 23.5);
assert_eq!(
&f as *const Float as *const OrdFloat,
f.as_ord() as *const OrdFloat
);
assert_eq!(
&f as *const Float as *const OrdFloat,
AsRef::<OrdFloat>::as_ref(&f) as *const OrdFloat
);
let mut o = OrdFloat::from(f);
assert_eq!(
&o as *const OrdFloat as *const Float,
o.as_float() as *const Float
);
assert_eq!(
&o as *const OrdFloat as *const Float,
AsRef::<Float>::as_ref(&o) as *const Float
);
assert_eq!(
&mut o as *mut OrdFloat as *mut Float,
o.as_float_mut() as *mut Float
);
assert_eq!(
&mut o as *mut OrdFloat as *mut Float,
AsMut::<Float>::as_mut(&mut o) as *mut Float
);
}
}