use std::{
cmp::Ordering,
fmt,
fmt::{Display, Formatter},
hash::{Hash, Hasher},
ops::Deref,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de, de::Visitor};
use crate::{
error::{Error, TypeError},
util::float_format::format_f32,
};
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default)]
pub struct OrderedF32(f32);
impl Serialize for OrderedF32 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f32(self.0)
}
}
impl<'de> Deserialize<'de> for OrderedF32 {
fn deserialize<D>(deserializer: D) -> Result<OrderedF32, D::Error>
where
D: Deserializer<'de>,
{
struct F32Visitor;
impl Visitor<'_> for F32Visitor {
type Value = OrderedF32;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a 32-bit floating point number")
}
fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E> {
Ok(OrderedF32(value))
}
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(OrderedF32(value as f32))
}
}
deserializer.deserialize_f32(F32Visitor)
}
}
impl OrderedF32 {
pub fn value(&self) -> f32 {
self.0
}
pub fn zero() -> OrderedF32 {
OrderedF32(0.0f32)
}
}
impl Deref for OrderedF32 {
type Target = f32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for OrderedF32 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&format_f32(self.0))
}
}
impl PartialEq for OrderedF32 {
fn eq(&self, other: &Self) -> bool {
self.0.to_bits() == other.0.to_bits()
}
}
impl Eq for OrderedF32 {}
impl PartialOrd for OrderedF32 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedF32 {
fn cmp(&self, other: &Self) -> Ordering {
let l = float_to_ordered_u32(self.0);
let r = float_to_ordered_u32(other.0);
l.cmp(&r)
}
}
#[inline]
fn float_to_ordered_u32(f: f32) -> u32 {
let bits = f.to_bits();
if bits & 0x80000000 == 0 {
bits ^ 0x80000000
} else {
!bits
}
}
impl Hash for OrderedF32 {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state);
}
}
impl From<OrderedF32> for f32 {
fn from(v: OrderedF32) -> Self {
v.0
}
}
impl TryFrom<f32> for OrderedF32 {
type Error = Error;
fn try_from(f: f32) -> Result<Self, Self::Error> {
let normalized = if f == 0.0 {
0.0
} else {
f
};
if f.is_nan() {
Err(TypeError::NanNotAllowed.into())
} else {
Ok(OrderedF32(normalized))
}
}
}
#[cfg(test)]
pub mod tests {
use std::{collections::HashSet, convert::TryFrom};
use super::*;
#[test]
fn test_sorting() {
let mut values = vec![
OrderedF32::try_from(10.0).unwrap(),
OrderedF32::try_from(2.0).unwrap(),
OrderedF32::try_from(5.0).unwrap(),
];
values.sort();
let sorted: Vec<f32> = values.into_iter().map(|v| v.0).collect();
assert_eq!(sorted, vec![2.0, 5.0, 10.0]);
}
#[test]
fn test_hash_eq() {
let a = OrderedF32::try_from(1.0).unwrap();
let b = OrderedF32::try_from(1.0).unwrap();
let mut set = HashSet::new();
set.insert(a);
assert!(set.contains(&b));
}
#[test]
fn test_normalizes_zero() {
let pos_zero = OrderedF32::try_from(0.0).unwrap();
let neg_zero = OrderedF32::try_from(-0.0).unwrap();
assert_eq!(pos_zero, neg_zero);
let mut set = HashSet::new();
set.insert(pos_zero);
assert!(set.contains(&neg_zero));
}
#[test]
fn test_nan_fails() {
assert!(OrderedF32::try_from(f32::NAN).is_err());
}
#[test]
fn test_negative_less_than_positive() {
let neg = OrderedF32::try_from(-1.5).unwrap();
let pos = OrderedF32::try_from(1.5).unwrap();
assert!(neg < pos);
}
#[test]
fn test_negative_less_than_zero() {
let neg = OrderedF32::try_from(-0.0001).unwrap();
let zero = OrderedF32::try_from(0.0).unwrap();
assert!(neg < zero);
}
#[test]
fn test_sorting_with_negatives() {
let mut values = vec![
OrderedF32::try_from(3.14).unwrap(),
OrderedF32::try_from(-1.5).unwrap(),
OrderedF32::try_from(0.0).unwrap(),
OrderedF32::try_from(99999.0).unwrap(),
OrderedF32::try_from(-100.0).unwrap(),
];
values.sort();
let sorted: Vec<f32> = values.into_iter().map(|v| v.0).collect();
assert_eq!(sorted, vec![-100.0, -1.5, 0.0, 3.14, 99999.0]);
}
}