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
6939 views
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.
10
pub 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) { self } else { other }
21
}
22
23
#[inline(always)]
24
fn max_propagate_nan(self, other: Self) -> Self {
25
if self.nan_max_lt(&other) { other } else { self }
26
}
27
28
#[inline(always)]
29
fn min_ignore_nan(self, other: Self) -> Self {
30
if self.nan_max_lt(&other) { self } else { other }
31
}
32
33
#[inline(always)]
34
fn max_ignore_nan(self, other: Self) -> Self {
35
if self.nan_min_lt(&other) { other } else { self }
36
}
37
}
38
39
macro_rules! impl_trivial_min_max {
40
($T: ty) => {
41
impl MinMax for $T {
42
#[inline(always)]
43
fn nan_min_lt(&self, other: &Self) -> bool {
44
self < other
45
}
46
47
#[inline(always)]
48
fn nan_max_lt(&self, other: &Self) -> bool {
49
self < other
50
}
51
}
52
};
53
}
54
55
// We can't do a blanket impl because Rust complains f32 might implement
56
// Ord someday.
57
impl_trivial_min_max!(bool);
58
impl_trivial_min_max!(u8);
59
impl_trivial_min_max!(u16);
60
impl_trivial_min_max!(u32);
61
impl_trivial_min_max!(u64);
62
impl_trivial_min_max!(u128);
63
impl_trivial_min_max!(usize);
64
impl_trivial_min_max!(i8);
65
impl_trivial_min_max!(i16);
66
impl_trivial_min_max!(i32);
67
impl_trivial_min_max!(i64);
68
impl_trivial_min_max!(i128);
69
impl_trivial_min_max!(isize);
70
impl_trivial_min_max!(char);
71
impl_trivial_min_max!(&str);
72
impl_trivial_min_max!(&[u8]);
73
impl_trivial_min_max!(String);
74
75
macro_rules! impl_float_min_max {
76
($T: ty) => {
77
impl MinMax for $T {
78
#[inline(always)]
79
fn nan_min_lt(&self, other: &Self) -> bool {
80
!(other.is_nan() | (self >= other))
81
}
82
83
#[inline(always)]
84
fn nan_max_lt(&self, other: &Self) -> bool {
85
!(self.is_nan() | (self >= other))
86
}
87
88
#[inline(always)]
89
fn min_ignore_nan(self, other: Self) -> Self {
90
<$T>::min(self, other)
91
}
92
93
#[inline(always)]
94
fn max_ignore_nan(self, other: Self) -> Self {
95
<$T>::max(self, other)
96
}
97
98
#[inline(always)]
99
fn min_propagate_nan(self, other: Self) -> Self {
100
if (self < other) | self.is_nan() {
101
self
102
} else {
103
other
104
}
105
}
106
107
#[inline(always)]
108
fn max_propagate_nan(self, other: Self) -> Self {
109
if (self > other) | self.is_nan() {
110
self
111
} else {
112
other
113
}
114
}
115
}
116
};
117
}
118
119
impl_float_min_max!(f32);
120
impl_float_min_max!(f64);
121
122
pub trait MinMaxPolicy {
123
// Is the first argument strictly better than the second, per the policy?
124
fn is_better<T: MinMax>(a: &T, b: &T) -> bool;
125
fn best<T: MinMax>(a: T, b: T) -> T;
126
}
127
128
#[derive(Copy, Clone, Debug)]
129
pub struct MinIgnoreNan;
130
impl MinMaxPolicy for MinIgnoreNan {
131
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
132
T::nan_max_lt(a, b)
133
}
134
135
fn best<T: MinMax>(a: T, b: T) -> T {
136
T::min_ignore_nan(a, b)
137
}
138
}
139
140
#[derive(Copy, Clone, Debug)]
141
pub struct MinPropagateNan;
142
impl MinMaxPolicy for MinPropagateNan {
143
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
144
T::nan_min_lt(a, b)
145
}
146
147
fn best<T: MinMax>(a: T, b: T) -> T {
148
T::min_propagate_nan(a, b)
149
}
150
}
151
152
#[derive(Copy, Clone, Debug)]
153
pub struct MaxIgnoreNan;
154
impl MinMaxPolicy for MaxIgnoreNan {
155
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
156
T::nan_min_lt(b, a)
157
}
158
159
fn best<T: MinMax>(a: T, b: T) -> T {
160
T::max_ignore_nan(a, b)
161
}
162
}
163
164
#[derive(Copy, Clone, Debug)]
165
pub struct MaxPropagateNan;
166
impl MinMaxPolicy for MaxPropagateNan {
167
fn is_better<T: MinMax>(a: &T, b: &T) -> bool {
168
T::nan_max_lt(b, a)
169
}
170
171
fn best<T: MinMax>(a: T, b: T) -> T {
172
T::max_propagate_nan(a, b)
173
}
174
}
175
176