polars_utils/
min_max.rs

1// These min/max operators don't follow our total order strictly. Instead
2// if exactly one of the two arguments is NaN the skip_nan varieties returns
3// the non-nan argument, whereas the propagate_nan varieties give the nan
4// argument. If both/neither argument is NaN these extrema follow the normal
5// total order.
6//
7// They also violate the regular total order for Option<T>: on top of the
8// above rules None's are always ignored, so only if both arguments are
9// None is the output None.
10pub trait MinMax: Sized {
11    // Comparison operators that either consider nan to be the smallest, or the
12    // largest possible value. Use tot_eq for equality. Prefer directly using
13    // min/max, they're slightly faster.
14    fn nan_min_lt(&self, other: &Self) -> bool;
15    fn nan_max_lt(&self, other: &Self) -> bool;
16
17    // Binary operators that return either the minimum or maximum.
18    #[inline(always)]
19    fn min_propagate_nan(self, other: Self) -> Self {
20        if self.nan_min_lt(&other) {
21            self
22        } else {
23            other
24        }
25    }
26
27    #[inline(always)]
28    fn max_propagate_nan(self, other: Self) -> Self {
29        if self.nan_max_lt(&other) {
30            other
31        } else {
32            self
33        }
34    }
35
36    #[inline(always)]
37    fn min_ignore_nan(self, other: Self) -> Self {
38        if self.nan_max_lt(&other) {
39            self
40        } else {
41            other
42        }
43    }
44
45    #[inline(always)]
46    fn max_ignore_nan(self, other: Self) -> Self {
47        if self.nan_min_lt(&other) {
48            other
49        } else {
50            self
51        }
52    }
53}
54
55macro_rules! impl_trivial_min_max {
56    ($T: ty) => {
57        impl MinMax for $T {
58            #[inline(always)]
59            fn nan_min_lt(&self, other: &Self) -> bool {
60                self < other
61            }
62
63            #[inline(always)]
64            fn nan_max_lt(&self, other: &Self) -> bool {
65                self < other
66            }
67        }
68    };
69}
70
71// We can't do a blanket impl because Rust complains f32 might implement
72// Ord someday.
73impl_trivial_min_max!(bool);
74impl_trivial_min_max!(u8);
75impl_trivial_min_max!(u16);
76impl_trivial_min_max!(u32);
77impl_trivial_min_max!(u64);
78impl_trivial_min_max!(u128);
79impl_trivial_min_max!(usize);
80impl_trivial_min_max!(i8);
81impl_trivial_min_max!(i16);
82impl_trivial_min_max!(i32);
83impl_trivial_min_max!(i64);
84impl_trivial_min_max!(i128);
85impl_trivial_min_max!(isize);
86impl_trivial_min_max!(char);
87impl_trivial_min_max!(&str);
88impl_trivial_min_max!(&[u8]);
89impl_trivial_min_max!(String);
90
91macro_rules! impl_float_min_max {
92    ($T: ty) => {
93        impl MinMax for $T {
94            #[inline(always)]
95            fn nan_min_lt(&self, other: &Self) -> bool {
96                !(other.is_nan() | (self >= other))
97            }
98
99            #[inline(always)]
100            fn nan_max_lt(&self, other: &Self) -> bool {
101                !(self.is_nan() | (self >= other))
102            }
103
104            #[inline(always)]
105            fn min_ignore_nan(self, other: Self) -> Self {
106                <$T>::min(self, other)
107            }
108
109            #[inline(always)]
110            fn max_ignore_nan(self, other: Self) -> Self {
111                <$T>::max(self, other)
112            }
113
114            #[inline(always)]
115            fn min_propagate_nan(self, other: Self) -> Self {
116                if (self < other) | self.is_nan() {
117                    self
118                } else {
119                    other
120                }
121            }
122
123            #[inline(always)]
124            fn max_propagate_nan(self, other: Self) -> Self {
125                if (self > other) | self.is_nan() {
126                    self
127                } else {
128                    other
129                }
130            }
131        }
132    };
133}
134
135impl_float_min_max!(f32);
136impl_float_min_max!(f64);