use crate::num::arithmetic::traits::Abs;
use crate::num::basic::floats::PrimitiveFloat;
use crate::num::comparison::traits::{EqAbs, OrdAbs, PartialOrdAbs};
use core::cmp::Ordering::{self, *};
use core::fmt::{self, Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
use core::str::FromStr;
#[derive(Clone, Copy, Default)]
pub struct NiceFloat<T: PrimitiveFloat>(pub T);
#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum FloatType {
NegativeInfinity,
NegativeFinite,
NegativeZero,
NaN,
PositiveZero,
PositiveFinite,
PositiveInfinity,
}
impl<T: PrimitiveFloat> NiceFloat<T> {
fn float_type(self) -> FloatType {
let f = self.0;
if f.is_nan() {
FloatType::NaN
} else if f.sign() == Greater {
if f == T::ZERO {
FloatType::PositiveZero
} else if f.is_finite() {
FloatType::PositiveFinite
} else {
FloatType::PositiveInfinity
}
} else if f == T::ZERO {
FloatType::NegativeZero
} else if f.is_finite() {
FloatType::NegativeFinite
} else {
FloatType::NegativeInfinity
}
}
}
impl Abs for FloatType {
type Output = Self;
fn abs(self) -> Self::Output {
match self {
Self::NegativeInfinity => Self::PositiveInfinity,
Self::NegativeFinite => Self::PositiveFinite,
Self::NegativeZero => Self::PositiveZero,
t => t,
}
}
}
impl<T: PrimitiveFloat> PartialEq<Self> for NiceFloat<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
let f = self.0;
let g = other.0;
f.to_bits() == g.to_bits() || f.is_nan() && g.is_nan()
}
}
impl<T: PrimitiveFloat> Eq for NiceFloat<T> {}
impl<T: PrimitiveFloat> EqAbs for NiceFloat<T> {
fn eq_abs(&self, other: &Self) -> bool {
let f = self.0;
let g = other.0;
f.abs().to_bits() == g.abs().to_bits() || f.is_nan() && g.is_nan()
}
}
impl<T: PrimitiveFloat> Hash for NiceFloat<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let f = self.0;
if f.is_nan() {
"NaN".hash(state);
} else {
f.to_bits().hash(state);
}
}
}
impl<T: PrimitiveFloat> Ord for NiceFloat<T> {
fn cmp(&self, other: &Self) -> Ordering {
let self_type = self.float_type();
let other_type = other.float_type();
self_type.cmp(&other_type).then_with(|| {
if self_type == FloatType::PositiveFinite || self_type == FloatType::NegativeFinite {
self.0.partial_cmp(&other.0).unwrap()
} else {
Equal
}
})
}
}
impl<T: PrimitiveFloat> PartialOrd<Self> for NiceFloat<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: PrimitiveFloat> OrdAbs for NiceFloat<T> {
fn cmp_abs(&self, other: &Self) -> Ordering {
let self_type = self.float_type().abs();
let other_type = other.float_type().abs();
self_type.cmp(&other_type).then_with(|| {
if self_type == FloatType::PositiveFinite {
self.0.abs().partial_cmp(&other.0.abs()).unwrap()
} else {
Equal
}
})
}
}
impl<T: PrimitiveFloat> PartialOrdAbs<Self> for NiceFloat<T> {
#[inline]
fn partial_cmp_abs(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp_abs(other))
}
}
#[doc(hidden)]
pub trait FmtRyuString: Copy {
fn fmt_ryu_string(self, f: &mut Formatter<'_>) -> fmt::Result;
}
macro_rules! impl_fmt_ryu_string {
($f: ident) => {
impl FmtRyuString for $f {
#[inline]
fn fmt_ryu_string(self, f: &mut Formatter<'_>) -> fmt::Result {
let mut buffer = ryu::Buffer::new();
let printed = buffer.format_finite(self);
let mut e_index = None;
let mut found_dot = false;
for (i, &b) in printed.as_bytes().iter().enumerate() {
match b {
b'.' => {
found_dot = true;
break; }
b'e' => {
e_index = Some(i);
break; }
_ => {}
}
}
if found_dot {
f.write_str(printed)
} else {
if let Some(e_index) = e_index {
let mut out_bytes = ::alloc::vec![0; printed.len() + 2];
let (in_bytes_lo, in_bytes_hi) = printed.as_bytes().split_at(e_index);
let (out_bytes_lo, out_bytes_hi) = out_bytes.split_at_mut(e_index);
out_bytes_lo.copy_from_slice(in_bytes_lo);
out_bytes_hi[0] = b'.';
out_bytes_hi[1] = b'0';
out_bytes_hi[2..].copy_from_slice(in_bytes_hi);
f.write_str(core::str::from_utf8(&out_bytes).unwrap())
} else {
panic!("Unexpected Ryu string: {}", printed);
}
}
}
}
};
}
impl_fmt_ryu_string!(f32);
impl_fmt_ryu_string!(f64);
impl<T: PrimitiveFloat> Display for NiceFloat<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.0.is_nan() {
f.write_str("NaN")
} else if self.0.is_infinite() {
if self.0.sign() == Greater {
f.write_str("Infinity")
} else {
f.write_str("-Infinity")
}
} else {
self.0.fmt_ryu_string(f)
}
}
}
impl<T: PrimitiveFloat> Debug for NiceFloat<T> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<T: PrimitiveFloat> FromStr for NiceFloat<T> {
type Err = <T as FromStr>::Err;
#[inline]
fn from_str(src: &str) -> Result<Self, <T as FromStr>::Err> {
match src {
"NaN" => Ok(T::NAN),
"Infinity" => Ok(T::INFINITY),
"-Infinity" => Ok(T::NEGATIVE_INFINITY),
"inf" | "-inf" => T::from_str("invalid"),
src => T::from_str(src),
}
.map(NiceFloat)
}
}