1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Total order on floating point types.
//! Can be used for sorting, min/max computation, and other collection algorithms.
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
/// Wraps a floating-point value to add total order and hash.
/// Possible types for `T` are `f32` and `f64`.
///
/// See also [`FloatOrd`].
#[derive(Clone, Copy)]
pub struct OrderedFloat<T>(T);
impl<T: Float + Copy> OrderedFloat<T> {
#[inline]
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: Float> Eq for OrderedFloat<T> {}
impl<T: Float> PartialEq<Self> for OrderedFloat<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
// NaNs are considered equal (equivalent) when it comes to ordering
if self.0.is_nan() {
other.0.is_nan()
} else {
self.0 == other.0
}
}
}
impl<T: Float> PartialOrd<Self> for OrderedFloat<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: Float> Ord for OrderedFloat<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
match self.0.partial_cmp(&other.0) {
Some(ord) => ord,
None => self.0.is_nan().cmp(&other.0.is_nan()),
}
}
}
impl<T: Float> Hash for OrderedFloat<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> From<T> for OrderedFloat<T> {
#[inline]
fn from(val: T) -> Self {
Self(val)
}
}
// ----------------------------------------------------------------------------
/// Extension trait to provide `ord()` method.
///
/// Example with `f64`:
/// ```
/// use epaint::util::FloatOrd;
///
/// let array = [1.0, 2.5, 2.0];
/// let max = array.iter().max_by_key(|val| val.ord());
///
/// assert_eq!(max, Some(&2.5));
/// ```
pub trait FloatOrd {
/// Type to provide total order, useful as key in sorted contexts.
fn ord(self) -> OrderedFloat<Self>
where
Self: Sized;
}
impl FloatOrd for f32 {
#[inline]
fn ord(self) -> OrderedFloat<Self> {
OrderedFloat(self)
}
}
impl FloatOrd for f64 {
#[inline]
fn ord(self) -> OrderedFloat<Self> {
OrderedFloat(self)
}
}
// ----------------------------------------------------------------------------
/// Internal abstraction over floating point types
#[doc(hidden)]
pub trait Float: PartialOrd + PartialEq + private::FloatImpl {}
impl Float for f32 {}
impl Float for f64 {}
// Keep this trait in private module, to avoid exposing its methods as extensions in user code
mod private {
use super::*;
pub trait FloatImpl {
fn is_nan(&self) -> bool;
fn hash<H: Hasher>(&self, state: &mut H);
}
impl FloatImpl for f32 {
#[inline]
fn is_nan(&self) -> bool {
Self::is_nan(*self)
}
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
crate::f32_hash(state, *self);
}
}
impl FloatImpl for f64 {
#[inline]
fn is_nan(&self) -> bool {
Self::is_nan(*self)
}
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
crate::f64_hash(state, *self);
}
}
}