1use crate::basic::SortOrder;
19use crate::column::writer::{compare_greater, is_nan};
20use crate::data_type::private::ParquetValueType;
21use crate::schema::types::BasicTypeInfo;
22
23const LANES_32: usize = 16;
26const LANES_64: usize = 8;
27
28#[inline(always)]
31fn min_max_by_key<T: Copy, K: Copy + Ord, const LANES: usize>(
32 values: &[T],
33 key: impl Fn(T) -> K,
34 key_min: K,
35 key_max: K,
36) -> Option<(K, K)> {
37 if values.is_empty() {
38 return None;
39 }
40
41 let mut mins = [key_max; LANES];
42 let mut maxs = [key_min; LANES];
43
44 let (chunks, remainder) = values.as_chunks::<LANES>();
45 for chunk in chunks {
46 for i in 0..LANES {
47 let k = key(chunk[i]);
48 mins[i] = mins[i].min(k);
49 maxs[i] = maxs[i].max(k);
50 }
51 }
52 for (i, v) in remainder.iter().enumerate() {
53 let k = key(*v);
54 mins[i] = mins[i].min(k);
55 maxs[i] = maxs[i].max(k);
56 }
57
58 let min = mins.into_iter().min().unwrap();
59 let max = maxs.into_iter().max().unwrap();
60 Some((min, max))
61}
62
63#[inline]
64fn is_unsigned(basic_type_info: &BasicTypeInfo) -> bool {
65 matches!(basic_type_info.sort_order(), SortOrder::UNSIGNED)
66}
67
68pub(crate) fn min_max_i32(basic_type_info: &BasicTypeInfo, values: &[i32]) -> Option<(i32, i32)> {
70 if is_unsigned(basic_type_info) {
71 min_max_by_key::<_, _, LANES_32>(values, |v| v as u32, u32::MIN, u32::MAX)
72 .map(|(min, max)| (min as i32, max as i32))
73 } else {
74 min_max_by_key::<_, _, LANES_32>(values, |v| v, i32::MIN, i32::MAX)
75 }
76}
77
78pub(crate) fn min_max_i64(basic_type_info: &BasicTypeInfo, values: &[i64]) -> Option<(i64, i64)> {
80 if is_unsigned(basic_type_info) {
81 min_max_by_key::<_, _, LANES_64>(values, |v| v as u64, u64::MIN, u64::MAX)
82 .map(|(min, max)| (min as i64, max as i64))
83 } else {
84 min_max_by_key::<_, _, LANES_64>(values, |v| v, i64::MIN, i64::MAX)
85 }
86}
87
88macro_rules! float_min_max {
89 ($name:ident, $float:ty, $int:ty, $uint:ty, $lanes:expr) => {
90 pub(crate) fn $name(values: &[$float]) -> Option<($float, $float, u64)> {
97 const SIGN_SHIFT: u32 = <$int>::BITS - 1;
98
99 #[inline(always)]
112 fn total_order_key(bits: $int) -> $int {
113 bits ^ ((((bits >> SIGN_SHIFT) as $uint) >> 1) as $int)
114 }
115
116 const BLOCK: usize = 1 << 30;
119
120 let mut min = <$int>::MAX;
121 let mut max = <$int>::MIN;
122 let mut nan_count = 0u64;
123
124 for block in values.chunks(BLOCK) {
125 let mut mins = [<$int>::MAX; $lanes];
126 let mut maxs = [<$int>::MIN; $lanes];
127 let mut nans = [<$uint>::MIN; $lanes];
128
129 let mut accumulate = |i: usize, v: $float| {
132 let bits = v.to_bits();
133 let is_nan = v.is_nan();
134 let key = total_order_key(bits as $int);
135 mins[i] = mins[i].min(if is_nan { <$int>::MAX } else { key });
136 maxs[i] = maxs[i].max(if is_nan { <$int>::MIN } else { key });
137 nans[i] += is_nan as $uint;
138 };
139
140 let (chunks, remainder) = block.as_chunks::<$lanes>();
141 for chunk in chunks {
142 for i in 0..$lanes {
143 accumulate(i, chunk[i]);
144 }
145 }
146 for (i, v) in remainder.iter().enumerate() {
147 accumulate(i, *v);
148 }
149
150 min = min.min(mins.into_iter().min().unwrap());
151 max = max.max(maxs.into_iter().max().unwrap());
152 nan_count += nans.into_iter().map(u64::from).sum::<u64>();
153 }
154
155 if nan_count as usize == values.len() {
156 return None;
157 }
158
159 let min = <$float>::from_bits(total_order_key(min) as $uint);
160 let max = <$float>::from_bits(total_order_key(max) as $uint);
161 Some((min, max, nan_count))
162 }
163 };
164}
165
166float_min_max!(min_max_f32, f32, i32, u32, LANES_32);
167float_min_max!(min_max_f64, f64, i64, u64, LANES_64);
168
169pub(crate) fn fallback_min_max<'a, T, I>(
176 basic_type_info: &BasicTypeInfo,
177 mut iter: I,
178) -> Option<(T, T, u64)>
179where
180 T: ParquetValueType + 'a,
181 I: Iterator<Item = &'a T>,
182{
183 let first = iter.next()?;
184 let mut min_max_nan = is_nan(basic_type_info, first);
185 let mut nan_count = min_max_nan as u64;
186
187 let mut min = first;
188 let mut max = first;
189 for val in iter {
190 match (min_max_nan, is_nan(basic_type_info, val)) {
191 (false, true) => {
193 nan_count += 1;
194 }
195 (true, false) => {
197 min = val;
198 max = val;
199 min_max_nan = false;
200 }
201 (_, val_is_nan) => {
203 nan_count += val_is_nan as u64;
204 if compare_greater(basic_type_info, min, val) {
207 min = val;
208 } else if compare_greater(basic_type_info, val, max) {
209 max = val;
210 }
211 }
212 }
213 }
214
215 Some((min.clone(), max.clone(), nan_count))
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::basic::{ConvertedType, IntType, LogicalType, Type as PhysicalType};
222 use crate::schema::types::Type;
223 use rand::prelude::*;
224
225 fn type_info(physical: PhysicalType, logical: Option<LogicalType>) -> BasicTypeInfo {
226 Type::primitive_type_builder("col", physical)
227 .with_logical_type(logical)
228 .build()
229 .unwrap()
230 .get_basic_info()
231 .clone()
232 }
233
234 fn signed_int(bits: u8) -> Option<LogicalType> {
235 Some(LogicalType::Integer(IntType {
236 bit_width: bits as i8,
237 is_signed: true,
238 }))
239 }
240
241 fn unsigned_int(bits: u8) -> Option<LogicalType> {
242 Some(LogicalType::Integer(IntType {
243 bit_width: bits as i8,
244 is_signed: false,
245 }))
246 }
247
248 fn lengths() -> impl Iterator<Item = usize> {
250 (0..=70).chain([127, 128, 129, 1000, 4097])
251 }
252
253 fn check_ints<T, F>(
254 rng: &mut StdRng,
255 info: &BasicTypeInfo,
256 kernel: F,
257 gen_value: impl Fn(&mut StdRng) -> T,
258 ) where
259 T: crate::data_type::private::ParquetValueType + Copy,
260 F: Fn(&BasicTypeInfo, &[T]) -> Option<(T, T)>,
261 {
262 for len in lengths() {
263 let values: Vec<T> = (0..len).map(|_| gen_value(rng)).collect();
264 let expected = fallback_min_max(info, values.iter());
265 let actual = kernel(info, &values).map(|(min, max)| (min, max, 0));
266 assert_eq!(actual, expected, "len {len}: {values:?}");
267 }
268 }
269
270 #[test]
271 fn test_min_max_i32() {
272 let mut rng = StdRng::seed_from_u64(42);
273 let edges = [i32::MIN, i32::MIN + 1, -1, 0, 1, i32::MAX - 1, i32::MAX];
274 for info in [
275 type_info(PhysicalType::INT32, None),
276 type_info(PhysicalType::INT32, signed_int(32)),
277 type_info(PhysicalType::INT32, unsigned_int(32)),
278 type_info(PhysicalType::INT32, unsigned_int(8)),
279 ] {
280 check_ints(&mut rng, &info, min_max_i32, |r| r.random());
281 check_ints(&mut rng, &info, min_max_i32, |r| *edges.choose(r).unwrap());
282 check_ints(&mut rng, &info, min_max_i32, |r| r.random_range(-3..3));
283 }
284 }
285
286 #[test]
287 fn test_min_max_i64() {
288 let mut rng = StdRng::seed_from_u64(42);
289 let edges = [i64::MIN, i64::MIN + 1, -1, 0, 1, i64::MAX - 1, i64::MAX];
290 for info in [
291 type_info(PhysicalType::INT64, None),
292 type_info(PhysicalType::INT64, signed_int(64)),
293 type_info(PhysicalType::INT64, unsigned_int(64)),
294 ] {
295 check_ints(&mut rng, &info, min_max_i64, |r| r.random());
296 check_ints(&mut rng, &info, min_max_i64, |r| *edges.choose(r).unwrap());
297 check_ints(&mut rng, &info, min_max_i64, |r| r.random_range(-3..3));
298 }
299 }
300
301 #[test]
302 fn test_unsigned_int32_converted_type() {
303 let info = Type::primitive_type_builder("col", PhysicalType::INT32)
304 .with_converted_type(ConvertedType::UINT_32)
305 .build()
306 .unwrap()
307 .get_basic_info()
308 .clone();
309 assert_eq!(min_max_i32(&info, &[-1, 0, 5]), Some((0, -1)));
310 }
311
312 macro_rules! float_tests {
313 ($test:ident, $float:ty, $uint:ty, $kernel:ident, $physical:expr) => {
314 #[test]
315 fn $test() {
316 let info = type_info($physical, None);
317 let mut rng = StdRng::seed_from_u64(42);
318
319 let quiet_nan = <$float>::NAN;
320 let neg_nan = -<$float>::NAN;
321 let payload_nan = <$float>::from_bits(<$float>::NAN.to_bits() | 1);
322 let max_nan = <$float>::from_bits(<$uint>::MAX >> 1);
323 let edges = [
324 <$float>::NEG_INFINITY,
325 <$float>::MIN,
326 -1.0,
327 -<$float>::MIN_POSITIVE,
328 -0.0,
329 0.0,
330 <$float>::MIN_POSITIVE,
331 <$float>::from_bits(1),
332 1.0,
333 <$float>::MAX,
334 <$float>::INFINITY,
335 quiet_nan,
336 neg_nan,
337 payload_nan,
338 max_nan,
339 ];
340 let nans = [quiet_nan, neg_nan, payload_nan, max_nan];
341
342 let generators: [&dyn Fn(&mut StdRng) -> $float; 5] = [
343 &|r| r.random::<$float>() * 2.0 - 1.0,
344 &|r| <$float>::from_bits(r.random()),
345 &|r| *edges.choose(r).unwrap(),
346 &|r| *nans.choose(r).unwrap(),
347 &|r| {
348 if r.random_bool(0.95) {
349 quiet_nan
350 } else {
351 r.random()
352 }
353 },
354 ];
355
356 for generator in generators {
357 for len in lengths() {
358 let values: Vec<$float> = (0..len).map(|_| generator(&mut rng)).collect();
359 let expected = fallback_min_max(&info, values.iter());
360 match $kernel(&values) {
361 Some(actual) => {
362 let to_bits = |(min, max, nans): ($float, $float, u64)| {
364 (min.to_bits(), max.to_bits(), nans)
365 };
366 assert_eq!(
367 to_bits(actual),
368 to_bits(expected.unwrap()),
369 "len {len}: {values:?}"
370 );
371 }
372 None => assert!(values.iter().all(|v| v.is_nan()), "{values:?}"),
374 }
375 }
376 }
377 }
378 };
379 }
380
381 float_tests!(test_min_max_f32, f32, u32, min_max_f32, PhysicalType::FLOAT);
382 float_tests!(
383 test_min_max_f64,
384 f64,
385 u64,
386 min_max_f64,
387 PhysicalType::DOUBLE
388 );
389}