Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-utils/src/min_max.rs
8422 views
1
use crate::float16::pf16;
2
3
// These min/max operators don't follow our total order strictly. Instead
4
// if exactly one of the two arguments is NaN the skip_nan varieties returns
5
// the non-nan argument, whereas the propagate_nan varieties give the nan
6
// argument. If both/neither argument is NaN these extrema follow the normal
7
// total order.
8
//
9
// They also violate the regular total order for Option<T>: on top of the
10
// above rules None's are always ignored, so only if both arguments are
11
// None is the output None.
12
pub trait MinMax: Sized {
13
// Comparison operators that either consider nan to be the smallest, or the
14
// largest possible value. Use tot_eq for equality. Prefer directly using
15
// min/max, they're slightly faster.
16
fn nan_min_lt(&self, other: &Self) -> bool;
17
fn nan_max_lt(&self, other: &Self) -> bool;
18
19
#[inline(always)]
20
fn nan_min_gt(&self, other: &Self) -> bool {
21
other.nan_min_lt(self)
22
}
23
24
#[inline(always)]
25
fn nan_max_gt(&self, other: &Self) -> bool {
26
other.nan_max_lt(self)
27
}
28
29
// Binary operators that return either the minimum or maximum.
30
#[inline(always)]
31
fn min_propagate_nan(self, other: Self) -> Self {
32
if self.nan_min_lt(&other) { self } else { other }
33
}
34
35
#[inline(always)]
36
fn max_propagate_nan(self, other: Self) -> Self {
37
if self.nan_max_lt(&other) { other } else { self }
38
}
39
40
#[inline(always)]
41
fn min_ignore_nan(self, other: Self) -> Self {
42
if self.nan_max_lt(&other) { self } else { other }
43
}
44
45
#[inline(always)]
46
fn max_ignore_nan(self, other: Self) -> Self {
47
if self.nan_min_lt(&other) { other } else { self }
48
}
49
}
50
51
macro_rules! impl_trivial_min_max {
52
($T: ty) => {
53
impl MinMax for $T {
54
#[inline(always)]
55
fn nan_min_lt(&self, other: &Self) -> bool {
56
self < other
57
}
58
59
#[inline(always)]
60
fn nan_max_lt(&self, other: &Self) -> bool {
61
self < other
62
}
63
}
64
};
65
}
66
67
// We can't do a blanket impl because Rust complains f32 might implement
68
// Ord someday.
69
impl_trivial_min_max!(bool);
70
impl_trivial_min_max!(u8);
71
impl_trivial_min_max!(u16);
72
impl_trivial_min_max!(u32);
73
impl_trivial_min_max!(u64);
74
impl_trivial_min_max!(u128);
75
impl_trivial_min_max!(usize);
76
impl_trivial_min_max!(i8);
77
impl_trivial_min_max!(i16);
78
impl_trivial_min_max!(i32);
79
impl_trivial_min_max!(i64);
80
impl_trivial_min_max!(i128);
81
impl_trivial_min_max!(isize);
82
impl_trivial_min_max!(char);
83
impl_trivial_min_max!(&str);
84
impl_trivial_min_max!(&[u8]);
85
impl_trivial_min_max!(String);
86
87
macro_rules! impl_float_min_max {
88
($T: ty) => {
89
impl MinMax for $T {
90
#[inline(always)]
91
fn nan_min_lt(&self, other: &Self) -> bool {
92
!(other.is_nan() | (self >= other))
93
}
94
95
#[inline(always)]
96
fn nan_max_lt(&self, other: &Self) -> bool {
97
!(self.is_nan() | (self >= other))
98
}
99
100
#[inline(always)]
101
fn min_ignore_nan(self, other: Self) -> Self {
102
<$T>::min(self, other)
103
}
104
105
#[inline(always)]
106
fn max_ignore_nan(self, other: Self) -> Self {
107
<$T>::max(self, other)
108
}
109
110
#[inline(always)]
111
fn min_propagate_nan(self, other: Self) -> Self {
112
if (self < other) | self.is_nan() {
113
self
114
} else {
115
other
116
}
117
}
118
119
#[inline(always)]
120
fn max_propagate_nan(self, other: Self) -> Self {
121
if (self > other) | self.is_nan() {
122
self
123
} else {
124
other
125
}
126
}
127
}
128
};
129
}
130
131
impl_float_min_max!(pf16);
132
impl_float_min_max!(f32);
133
impl_float_min_max!(f64);
134
135
pub trait MinMaxPolicy {
136
// Is the first argument strictly better than the second, per the policy?
137
fn is_better<T: MinMax>(a: &T, b: &T) -> bool;
138
fn best<T: MinMax>(a: T, b: T) -> T;
139
}
140
141
#[derive(Copy, Clone, Debug)]
142
pub struct MinIgnoreNan;
143
impl MinMaxPolicy for MinIgnoreNan {
144
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
145
T::nan_max_lt(a, b)
146
}
147
148
fn best<T: MinMax>(a: T, b: T) -> T {
149
T::min_ignore_nan(a, b)
150
}
151
}
152
153
#[derive(Copy, Clone, Debug)]
154
pub struct MinPropagateNan;
155
impl MinMaxPolicy for MinPropagateNan {
156
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
157
T::nan_min_lt(a, b)
158
}
159
160
fn best<T: MinMax>(a: T, b: T) -> T {
161
T::min_propagate_nan(a, b)
162
}
163
}
164
165
#[derive(Copy, Clone, Debug)]
166
pub struct MaxIgnoreNan;
167
impl MinMaxPolicy for MaxIgnoreNan {
168
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
169
T::nan_min_lt(b, a)
170
}
171
172
fn best<T: MinMax>(a: T, b: T) -> T {
173
T::max_ignore_nan(a, b)
174
}
175
}
176
177
#[derive(Copy, Clone, Debug)]
178
pub struct MaxPropagateNan;
179
impl MinMaxPolicy for MaxPropagateNan {
180
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
181
T::nan_max_lt(b, a)
182
}
183
184
fn best<T: MinMax>(a: T, b: T) -> T {
185
T::max_propagate_nan(a, b)
186
}
187
}
188
189