Skip to main content

arrow_arith/
aggregate.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Defines aggregations over Arrow arrays.
19
20use arrow_array::cast::*;
21use arrow_array::iterator::ArrayIter;
22use arrow_array::*;
23use arrow_buffer::NullBuffer;
24use arrow_data::bit_iterator::try_for_each_valid_idx;
25use arrow_schema::*;
26use std::borrow::BorrowMut;
27use std::cmp::{self, Ordering};
28use std::ops::{BitAnd, BitOr, BitXor};
29use types::ByteViewType;
30
31/// An accumulator for primitive numeric values.
32trait NumericAccumulator<T: ArrowNativeTypeOp>: Copy + Default {
33    /// Accumulate a non-null value.
34    fn accumulate(&mut self, value: T);
35    /// Accumulate a nullable values.
36    /// If `valid` is false the `value` should not affect the accumulator state.
37    fn accumulate_nullable(&mut self, value: T, valid: bool);
38    /// Merge another accumulator into this accumulator
39    fn merge(&mut self, other: Self);
40    /// Return the aggregated value.
41    fn finish(&mut self) -> T;
42}
43
44/// Helper for branchlessly selecting either `a` or `b` based on the boolean `m`.
45/// After verifying the generated assembly this can be a simple `if`.
46#[inline(always)]
47fn select<T: Copy>(m: bool, a: T, b: T) -> T {
48    if m { a } else { b }
49}
50
51#[derive(Clone, Copy)]
52struct SumAccumulator<T: ArrowNativeTypeOp> {
53    sum: T,
54}
55
56impl<T: ArrowNativeTypeOp> Default for SumAccumulator<T> {
57    fn default() -> Self {
58        Self { sum: T::ZERO }
59    }
60}
61
62impl<T: ArrowNativeTypeOp> NumericAccumulator<T> for SumAccumulator<T> {
63    fn accumulate(&mut self, value: T) {
64        self.sum = self.sum.add_wrapping(value);
65    }
66
67    fn accumulate_nullable(&mut self, value: T, valid: bool) {
68        let sum = self.sum;
69        self.sum = select(valid, sum.add_wrapping(value), sum)
70    }
71
72    fn merge(&mut self, other: Self) {
73        self.sum = self.sum.add_wrapping(other.sum);
74    }
75
76    fn finish(&mut self) -> T {
77        self.sum
78    }
79}
80
81#[derive(Clone, Copy)]
82struct ProductAccumulator<T: ArrowNativeTypeOp> {
83    product: T,
84}
85
86impl<T: ArrowNativeTypeOp> Default for ProductAccumulator<T> {
87    fn default() -> Self {
88        Self { product: T::ONE }
89    }
90}
91
92impl<T: ArrowNativeTypeOp> NumericAccumulator<T> for ProductAccumulator<T> {
93    fn accumulate(&mut self, value: T) {
94        self.product = self.product.mul_wrapping(value);
95    }
96
97    fn accumulate_nullable(&mut self, value: T, valid: bool) {
98        let product = self.product;
99        self.product = select(valid, product.mul_wrapping(value), product)
100    }
101
102    fn merge(&mut self, other: Self) {
103        self.product = self.product.mul_wrapping(other.product);
104    }
105
106    fn finish(&mut self) -> T {
107        self.product
108    }
109}
110
111#[derive(Clone, Copy)]
112struct MinAccumulator<T: ArrowNativeTypeOp> {
113    min: T,
114}
115
116impl<T: ArrowNativeTypeOp> Default for MinAccumulator<T> {
117    fn default() -> Self {
118        Self {
119            min: T::MAX_TOTAL_ORDER,
120        }
121    }
122}
123
124impl<T: ArrowNativeTypeOp> NumericAccumulator<T> for MinAccumulator<T> {
125    fn accumulate(&mut self, value: T) {
126        let min = self.min;
127        self.min = select(value.is_lt(min), value, min);
128    }
129
130    fn accumulate_nullable(&mut self, value: T, valid: bool) {
131        let min = self.min;
132        let is_lt = valid & value.is_lt(min);
133        self.min = select(is_lt, value, min);
134    }
135
136    fn merge(&mut self, other: Self) {
137        self.accumulate(other.min)
138    }
139
140    fn finish(&mut self) -> T {
141        self.min
142    }
143}
144
145#[derive(Clone, Copy)]
146struct MaxAccumulator<T: ArrowNativeTypeOp> {
147    max: T,
148}
149
150impl<T: ArrowNativeTypeOp> Default for MaxAccumulator<T> {
151    fn default() -> Self {
152        Self {
153            max: T::MIN_TOTAL_ORDER,
154        }
155    }
156}
157
158impl<T: ArrowNativeTypeOp> NumericAccumulator<T> for MaxAccumulator<T> {
159    fn accumulate(&mut self, value: T) {
160        let max = self.max;
161        self.max = select(value.is_gt(max), value, max);
162    }
163
164    fn accumulate_nullable(&mut self, value: T, valid: bool) {
165        let max = self.max;
166        let is_gt = value.is_gt(max) & valid;
167        self.max = select(is_gt, value, max);
168    }
169
170    fn merge(&mut self, other: Self) {
171        self.accumulate(other.max)
172    }
173
174    fn finish(&mut self) -> T {
175        self.max
176    }
177}
178
179fn reduce_accumulators<T: ArrowNativeTypeOp, A: NumericAccumulator<T>, const LANES: usize>(
180    mut acc: [A; LANES],
181) -> A {
182    assert!(LANES > 0 && LANES.is_power_of_two());
183    let mut len = LANES;
184
185    // attempt at tree reduction, unfortunately llvm does not fully recognize this pattern,
186    // but the generated code is still a little faster than purely sequential reduction for floats.
187    while len >= 2 {
188        let mid = len / 2;
189        let (h, t) = acc[..len].split_at_mut(mid);
190
191        for i in 0..mid {
192            h[i].merge(t[i]);
193        }
194        len /= 2;
195    }
196    acc[0]
197}
198
199#[inline(always)]
200fn aggregate_nonnull_chunk<T: ArrowNativeTypeOp, A: NumericAccumulator<T>, const LANES: usize>(
201    acc: &mut [A; LANES],
202    values: &[T; LANES],
203) {
204    for i in 0..LANES {
205        acc[i].accumulate(values[i]);
206    }
207}
208
209#[inline(always)]
210fn aggregate_nullable_chunk<T: ArrowNativeTypeOp, A: NumericAccumulator<T>, const LANES: usize>(
211    acc: &mut [A; LANES],
212    values: &[T; LANES],
213    validity: u64,
214) {
215    let mut bit = 1;
216    for i in 0..LANES {
217        acc[i].accumulate_nullable(values[i], (validity & bit) != 0);
218        bit <<= 1;
219    }
220}
221
222fn aggregate_nonnull_simple<T: ArrowNativeTypeOp, A: NumericAccumulator<T>>(values: &[T]) -> T {
223    values
224        .iter()
225        .copied()
226        .fold(A::default(), |mut a, b| {
227            a.accumulate(b);
228            a
229        })
230        .finish()
231}
232
233#[inline(never)]
234fn aggregate_nonnull_lanes<T: ArrowNativeTypeOp, A: NumericAccumulator<T>, const LANES: usize>(
235    values: &[T],
236) -> T {
237    // aggregating into multiple independent accumulators allows the compiler to use vector registers
238    // with a single accumulator the compiler would not be allowed to reorder floating point addition
239    let mut acc = [A::default(); LANES];
240    let mut chunks = values.chunks_exact(LANES);
241    chunks.borrow_mut().for_each(|chunk| {
242        aggregate_nonnull_chunk(&mut acc, chunk[..LANES].try_into().unwrap());
243    });
244
245    let remainder = chunks.remainder();
246    for i in 0..remainder.len() {
247        acc[i].accumulate(remainder[i]);
248    }
249
250    reduce_accumulators(acc).finish()
251}
252
253#[inline(never)]
254fn aggregate_nullable_lanes<T: ArrowNativeTypeOp, A: NumericAccumulator<T>, const LANES: usize>(
255    values: &[T],
256    validity: &NullBuffer,
257) -> T {
258    assert!(LANES > 0 && 64 % LANES == 0);
259    assert_eq!(values.len(), validity.len());
260
261    // aggregating into multiple independent accumulators allows the compiler to use vector registers
262    let mut acc = [A::default(); LANES];
263    // we process 64 bits of validity at a time
264    let mut values_chunks = values.chunks_exact(64);
265    let validity_chunks = validity.inner().bit_chunks();
266    let mut validity_chunks_iter = validity_chunks.iter();
267
268    values_chunks.borrow_mut().for_each(|chunk| {
269        // Safety: we asserted that values and validity have the same length and trust the iterator impl
270        let mut validity = unsafe { validity_chunks_iter.next().unwrap_unchecked() };
271        // chunk further based on the number of vector lanes
272        chunk.chunks_exact(LANES).for_each(|chunk| {
273            aggregate_nullable_chunk(&mut acc, chunk[..LANES].try_into().unwrap(), validity);
274            validity >>= LANES;
275        });
276    });
277
278    let remainder = values_chunks.remainder();
279    if !remainder.is_empty() {
280        let mut validity = validity_chunks.remainder_bits();
281
282        let mut remainder_chunks = remainder.chunks_exact(LANES);
283        remainder_chunks.borrow_mut().for_each(|chunk| {
284            aggregate_nullable_chunk(&mut acc, chunk[..LANES].try_into().unwrap(), validity);
285            validity >>= LANES;
286        });
287
288        let remainder = remainder_chunks.remainder();
289        if !remainder.is_empty() {
290            let mut bit = 1;
291            for i in 0..remainder.len() {
292                acc[i].accumulate_nullable(remainder[i], (validity & bit) != 0);
293                bit <<= 1;
294            }
295        }
296    }
297
298    reduce_accumulators(acc).finish()
299}
300
301/// The preferred vector size in bytes for the target platform.
302/// Note that the avx512 target feature is still unstable and this also means it is not detected on stable rust.
303const PREFERRED_VECTOR_SIZE: usize =
304    if cfg!(all(target_arch = "x86_64", target_feature = "avx512f")) {
305        64
306    } else if cfg!(all(target_arch = "x86_64", target_feature = "avx")) {
307        32
308    } else {
309        16
310    };
311
312/// non-nullable aggregation requires fewer temporary registers so we can use more of them for accumulators
313const PREFERRED_VECTOR_SIZE_NON_NULL: usize = PREFERRED_VECTOR_SIZE * 2;
314
315/// Generic aggregation for any primitive type.
316/// Returns None if there are no non-null values in `array`.
317fn aggregate<T: ArrowNativeTypeOp, P: ArrowPrimitiveType<Native = T>, A: NumericAccumulator<T>>(
318    array: &PrimitiveArray<P>,
319) -> Option<T> {
320    let null_count = array.null_count();
321    if null_count == array.len() {
322        return None;
323    }
324    let values = array.values().as_ref();
325    match array.nulls() {
326        Some(nulls) if null_count > 0 => {
327            // const generics depending on a generic type parameter are not supported
328            // so we have to match and call aggregate with the corresponding constant
329            match PREFERRED_VECTOR_SIZE / std::mem::size_of::<T>() {
330                64 => Some(aggregate_nullable_lanes::<T, A, 64>(values, nulls)),
331                32 => Some(aggregate_nullable_lanes::<T, A, 32>(values, nulls)),
332                16 => Some(aggregate_nullable_lanes::<T, A, 16>(values, nulls)),
333                8 => Some(aggregate_nullable_lanes::<T, A, 8>(values, nulls)),
334                4 => Some(aggregate_nullable_lanes::<T, A, 4>(values, nulls)),
335                2 => Some(aggregate_nullable_lanes::<T, A, 2>(values, nulls)),
336                _ => Some(aggregate_nullable_lanes::<T, A, 1>(values, nulls)),
337            }
338        }
339        _ => {
340            let is_float = matches!(
341                array.data_type(),
342                DataType::Float16 | DataType::Float32 | DataType::Float64
343            );
344            if is_float {
345                match PREFERRED_VECTOR_SIZE_NON_NULL / std::mem::size_of::<T>() {
346                    64 => Some(aggregate_nonnull_lanes::<T, A, 64>(values)),
347                    32 => Some(aggregate_nonnull_lanes::<T, A, 32>(values)),
348                    16 => Some(aggregate_nonnull_lanes::<T, A, 16>(values)),
349                    8 => Some(aggregate_nonnull_lanes::<T, A, 8>(values)),
350                    4 => Some(aggregate_nonnull_lanes::<T, A, 4>(values)),
351                    2 => Some(aggregate_nonnull_lanes::<T, A, 2>(values)),
352                    _ => Some(aggregate_nonnull_simple::<T, A>(values)),
353                }
354            } else {
355                // for non-null integers its better to not chunk ourselves and instead
356                // let llvm fully handle loop unrolling and vectorization
357                Some(aggregate_nonnull_simple::<T, A>(values))
358            }
359        }
360    }
361}
362
363/// Returns the minimum value in the boolean array.
364///
365/// # Example
366/// ```
367/// # use arrow_array::BooleanArray;
368/// # use arrow_arith::aggregate::min_boolean;
369/// let a = BooleanArray::from(vec![Some(true), None, Some(false)]);
370/// assert_eq!(min_boolean(&a), Some(false))
371/// ```
372pub fn min_boolean(array: &BooleanArray) -> Option<bool> {
373    // short circuit if all nulls / zero length array
374    if array.null_count() == array.len() {
375        return None;
376    }
377
378    // Note the min bool is false (0), so short circuit as soon as we see it
379    match array.nulls() {
380        None => {
381            let bit_chunks = array.values().bit_chunks();
382            if bit_chunks.iter().any(|x| {
383                // u64::MAX has all bits set, so if the value is not that, then there is a false
384                x != u64::MAX
385            }) {
386                return Some(false);
387            }
388            // If the remainder bits are not all set, then there is a false
389            if bit_chunks.remainder_bits().count_ones() as usize != bit_chunks.remainder_len() {
390                Some(false)
391            } else {
392                Some(true)
393            }
394        }
395        Some(nulls) => {
396            let validity_chunks = nulls.inner().bit_chunks();
397            let value_chunks = array.values().bit_chunks();
398
399            if value_chunks
400                .iter()
401                .zip(validity_chunks.iter())
402                .any(|(value, validity)| {
403                    // We are looking for a false value, but because applying the validity mask
404                    // can create a false for a true value (e.g. value: true, validity: false), we instead invert the value, so that we have to look for a true.
405                    (!value & validity) != 0
406                })
407            {
408                return Some(false);
409            }
410
411            // Same trick as above: Instead of looking for a false, we invert the value bits and look for a true
412            if (!value_chunks.remainder_bits() & validity_chunks.remainder_bits()) != 0 {
413                Some(false)
414            } else {
415                Some(true)
416            }
417        }
418    }
419}
420
421/// Returns the maximum value in the boolean array
422///
423/// # Example
424/// ```
425/// # use arrow_array::BooleanArray;
426/// # use arrow_arith::aggregate::max_boolean;
427/// let a = BooleanArray::from(vec![Some(true), None, Some(false)]);
428/// assert_eq!(max_boolean(&a), Some(true))
429/// ```
430pub fn max_boolean(array: &BooleanArray) -> Option<bool> {
431    // short circuit if all nulls / zero length array
432    if array.null_count() == array.len() {
433        return None;
434    }
435
436    // Note the max bool is true (1), so short circuit as soon as we see it
437    match array.nulls() {
438        None => array
439            .values()
440            .bit_chunks()
441            .iter_padded()
442            // We found a true if any bit is set
443            .map(|x| x != 0)
444            .find(|b| *b)
445            .or(Some(false)),
446        Some(nulls) => {
447            let validity_chunks = nulls.inner().bit_chunks().iter_padded();
448            let value_chunks = array.values().bit_chunks().iter_padded();
449            value_chunks
450                .zip(validity_chunks)
451                // We found a true if the value bit is 1, AND the validity bit is 1 for any bits in the chunk
452                .map(|(value_bits, validity_bits)| (value_bits & validity_bits) != 0)
453                .find(|b| *b)
454                .or(Some(false))
455        }
456    }
457}
458
459/// Helper to compute min/max of [`ArrayAccessor`].
460fn min_max_helper<T, A: ArrayAccessor<Item = T>, F>(array: A, cmp: F) -> Option<T>
461where
462    F: Fn(&T, &T) -> bool,
463{
464    let null_count = array.null_count();
465    if null_count == array.len() {
466        None
467    } else if null_count == 0 {
468        // JUSTIFICATION
469        //  Benefit:  ~8% speedup
470        //  Soundness: `i` is always within the array bounds
471        (0..array.len())
472            .map(|i| unsafe { array.value_unchecked(i) })
473            .reduce(|acc, item| if cmp(&acc, &item) { item } else { acc })
474    } else {
475        let nulls = array.nulls().unwrap();
476        unsafe {
477            let idx = nulls.valid_indices().reduce(|acc_idx, idx| {
478                let acc = array.value_unchecked(acc_idx);
479                let item = array.value_unchecked(idx);
480                if cmp(&acc, &item) { idx } else { acc_idx }
481            });
482            idx.map(|idx| array.value_unchecked(idx))
483        }
484    }
485}
486
487/// Helper to compute min/max of [`GenericByteViewArray<T>`].
488/// The specialized min/max leverages the inlined values to compare the byte views.
489/// `swap_cond` is the condition to swap current min/max with the new value.
490/// For example, `Ordering::Greater` for max and `Ordering::Less` for min.
491fn min_max_view_helper<T: ByteViewType>(
492    array: &GenericByteViewArray<T>,
493    swap_cond: cmp::Ordering,
494) -> Option<&T::Native> {
495    let null_count = array.null_count();
496    if null_count == array.len() {
497        None
498    } else if null_count == 0 {
499        let target_idx = (0..array.len()).reduce(|acc, item| {
500            // SAFETY:  array's length is correct so item is within bounds
501            let cmp = unsafe { GenericByteViewArray::compare_unchecked(array, item, array, acc) };
502            if cmp == swap_cond { item } else { acc }
503        });
504        // SAFETY: idx came from valid range `0..array.len()`
505        unsafe { target_idx.map(|idx| array.value_unchecked(idx)) }
506    } else {
507        let nulls = array.nulls().unwrap();
508
509        let target_idx = nulls.valid_indices().reduce(|acc_idx, idx| {
510            let cmp =
511                unsafe { GenericByteViewArray::compare_unchecked(array, idx, array, acc_idx) };
512            if cmp == swap_cond { idx } else { acc_idx }
513        });
514
515        // SAFETY: idx came from valid range `0..array.len()`
516        unsafe { target_idx.map(|idx| array.value_unchecked(idx)) }
517    }
518}
519
520/// Returns the maximum value in the binary array, according to the natural order.
521pub fn max_binary<T: OffsetSizeTrait>(array: &GenericBinaryArray<T>) -> Option<&[u8]> {
522    min_max_helper::<&[u8], _, _>(array, |a, b| *a < *b)
523}
524
525/// Returns the maximum value in the binary view array, according to the natural order.
526pub fn max_binary_view(array: &BinaryViewArray) -> Option<&[u8]> {
527    min_max_view_helper(array, Ordering::Greater)
528}
529
530/// Returns the maximum value in the fixed size binary array, according to the natural order.
531pub fn max_fixed_size_binary(array: &FixedSizeBinaryArray) -> Option<&[u8]> {
532    min_max_helper::<&[u8], _, _>(array, |a, b| *a < *b)
533}
534
535/// Returns the minimum value in the binary array, according to the natural order.
536pub fn min_binary<T: OffsetSizeTrait>(array: &GenericBinaryArray<T>) -> Option<&[u8]> {
537    min_max_helper::<&[u8], _, _>(array, |a, b| *a > *b)
538}
539
540/// Returns the minimum value in the binary view array, according to the natural order.
541pub fn min_binary_view(array: &BinaryViewArray) -> Option<&[u8]> {
542    min_max_view_helper(array, Ordering::Less)
543}
544
545/// Returns the minimum value in the fixed size binary array, according to the natural order.
546pub fn min_fixed_size_binary(array: &FixedSizeBinaryArray) -> Option<&[u8]> {
547    min_max_helper::<&[u8], _, _>(array, |a, b| *a > *b)
548}
549
550/// Returns the maximum value in the string array, according to the natural order.
551pub fn max_string<T: OffsetSizeTrait>(array: &GenericStringArray<T>) -> Option<&str> {
552    min_max_helper::<&str, _, _>(array, |a, b| *a < *b)
553}
554
555/// Returns the maximum value in the string view array, according to the natural order.
556pub fn max_string_view(array: &StringViewArray) -> Option<&str> {
557    min_max_view_helper(array, Ordering::Greater)
558}
559
560/// Returns the minimum value in the string array, according to the natural order.
561pub fn min_string<T: OffsetSizeTrait>(array: &GenericStringArray<T>) -> Option<&str> {
562    min_max_helper::<&str, _, _>(array, |a, b| *a > *b)
563}
564
565/// Returns the minimum value in the string view array, according to the natural order.
566pub fn min_string_view(array: &StringViewArray) -> Option<&str> {
567    min_max_view_helper(array, Ordering::Less)
568}
569
570/// Returns the sum of values in the array.
571///
572/// This doesn't detect overflow. Once overflowing, the result will wrap around.
573/// For an overflow-checking variant, use [`sum_array_checked`] instead.
574pub fn sum_array<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
575    array: A,
576) -> Option<T::Native> {
577    match array.data_type() {
578        DataType::Dictionary(_, _) => {
579            let null_count = array.null_count();
580
581            if null_count == array.len() {
582                return None;
583            }
584
585            let iter = ArrayIter::new(array);
586            let sum = iter
587                .into_iter()
588                .fold(T::default_value(), |accumulator, value| {
589                    if let Some(value) = value {
590                        accumulator.add_wrapping(value)
591                    } else {
592                        accumulator
593                    }
594                });
595
596            Some(sum)
597        }
598        DataType::RunEndEncoded(run_ends, _) => match run_ends.data_type() {
599            DataType::Int16 => ree::sum_wrapping::<types::Int16Type, T>(&array),
600            DataType::Int32 => ree::sum_wrapping::<types::Int32Type, T>(&array),
601            DataType::Int64 => ree::sum_wrapping::<types::Int64Type, T>(&array),
602            _ => unreachable!(),
603        },
604        _ => sum::<T>(as_primitive_array(&array)),
605    }
606}
607
608/// Returns the sum of values in the array.
609///
610/// This detects overflow and returns an `Err` for that. For an non-overflow-checking variant,
611/// use [`sum_array`] instead.
612/// Additionally returns an `Err` on run-end-encoded arrays with a provided
613/// values type parameter that is incorrect.
614pub fn sum_array_checked<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
615    array: A,
616) -> Result<Option<T::Native>, ArrowError> {
617    match array.data_type() {
618        DataType::Dictionary(_, _) => {
619            let null_count = array.null_count();
620
621            if null_count == array.len() {
622                return Ok(None);
623            }
624
625            let iter = ArrayIter::new(array);
626            let sum = iter
627                .into_iter()
628                .try_fold(T::default_value(), |accumulator, value| {
629                    if let Some(value) = value {
630                        accumulator.add_checked(value)
631                    } else {
632                        Ok(accumulator)
633                    }
634                })?;
635
636            Ok(Some(sum))
637        }
638        DataType::RunEndEncoded(run_ends, _) => match run_ends.data_type() {
639            DataType::Int16 => ree::sum_checked::<types::Int16Type, T>(&array),
640            DataType::Int32 => ree::sum_checked::<types::Int32Type, T>(&array),
641            DataType::Int64 => ree::sum_checked::<types::Int64Type, T>(&array),
642            _ => unreachable!(),
643        },
644        _ => sum_checked::<T>(as_primitive_array(&array)),
645    }
646}
647
648// Logic for summing run-end-encoded arrays.
649mod ree {
650    use std::convert::Infallible;
651
652    use arrow_array::cast::AsArray;
653    use arrow_array::types::RunEndIndexType;
654    use arrow_array::{Array, ArrowNativeTypeOp, ArrowNumericType, PrimitiveArray, TypedRunArray};
655    use arrow_buffer::ArrowNativeType;
656    use arrow_schema::ArrowError;
657
658    /// Downcasts an array to a TypedRunArray.
659    fn downcast<'a, I: RunEndIndexType, V: ArrowNumericType>(
660        array: &'a dyn Array,
661    ) -> Option<TypedRunArray<'a, I, PrimitiveArray<V>>> {
662        let array = array.as_run_opt::<I>()?;
663        // We only support RunArray wrapping primitive types.
664        array.downcast::<PrimitiveArray<V>>()
665    }
666
667    /// Computes the sum (wrapping) of the array values.
668    pub(super) fn sum_wrapping<I: RunEndIndexType, V: ArrowNumericType>(
669        array: &dyn Array,
670    ) -> Option<V::Native> {
671        let ree = downcast::<I, V>(array)?;
672        let Ok(sum) = fold(ree, |acc, val, len| -> Result<V::Native, Infallible> {
673            Ok(acc.add_wrapping(val.mul_wrapping(V::Native::usize_as(len))))
674        });
675        sum
676    }
677
678    /// Computes the sum (erroring on overflow) of the array values.
679    pub(super) fn sum_checked<I: RunEndIndexType, V: ArrowNumericType>(
680        array: &dyn Array,
681    ) -> Result<Option<V::Native>, ArrowError> {
682        let Some(ree) = downcast::<I, V>(array) else {
683            return Err(ArrowError::InvalidArgumentError(
684                "Input run array values are not a PrimitiveArray".to_string(),
685            ));
686        };
687        fold(ree, |acc, val, len| -> Result<V::Native, ArrowError> {
688            let Some(len) = V::Native::from_usize(len) else {
689                return Err(ArrowError::ArithmeticOverflow(format!(
690                    "Cannot convert a run-end index ({:?}) to the value type ({})",
691                    len,
692                    std::any::type_name::<V::Native>()
693                )));
694            };
695            acc.add_checked(val.mul_checked(len)?)
696        })
697    }
698
699    /// Folds over the values in a run-end-encoded array.
700    fn fold<'a, I: RunEndIndexType, V: ArrowNumericType, F, E>(
701        array: TypedRunArray<'a, I, PrimitiveArray<V>>,
702        mut f: F,
703    ) -> Result<Option<V::Native>, E>
704    where
705        F: FnMut(V::Native, V::Native, usize) -> Result<V::Native, E>,
706    {
707        let run_ends = array.run_ends();
708        let logical_start = run_ends.offset();
709        let logical_end = run_ends.offset() + run_ends.len();
710        let run_ends = run_ends.sliced_values();
711
712        let values_slice = array.run_array().values_slice();
713        let values = values_slice
714            .as_any()
715            .downcast_ref::<PrimitiveArray<V>>()
716            // Safety: we know the values array is PrimitiveArray<V>.
717            .unwrap();
718
719        let mut prev_end = 0;
720        let mut acc = V::Native::ZERO;
721        let mut has_non_null_value = false;
722
723        for (run_end, value) in run_ends.zip(values) {
724            let current_run_end = run_end.as_usize().clamp(logical_start, logical_end);
725            let run_length = current_run_end - prev_end;
726
727            if let Some(value) = value {
728                has_non_null_value = true;
729                acc = f(acc, value, run_length)?;
730            }
731
732            prev_end = current_run_end;
733            if current_run_end == logical_end {
734                break;
735            }
736        }
737
738        Ok(if has_non_null_value { Some(acc) } else { None })
739    }
740}
741
742/// Returns the min of values in the array of `ArrowNumericType` type, or dictionary
743/// array with value of `ArrowNumericType` type.
744pub fn min_array<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
745    array: A,
746) -> Option<T::Native> {
747    min_max_array_helper::<T, A, _, _>(array, |a, b| a.is_gt(*b), min)
748}
749
750/// Returns the max of values in the array of `ArrowNumericType` type, or dictionary
751/// array with value of `ArrowNumericType` type.
752pub fn max_array<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
753    array: A,
754) -> Option<T::Native> {
755    min_max_array_helper::<T, A, _, _>(array, |a, b| a.is_lt(*b), max)
756}
757
758fn min_max_array_helper<T, A: ArrayAccessor<Item = T::Native>, F, M>(
759    array: A,
760    cmp: F,
761    m: M,
762) -> Option<T::Native>
763where
764    T: ArrowNumericType,
765    F: Fn(&T::Native, &T::Native) -> bool,
766    M: Fn(&PrimitiveArray<T>) -> Option<T::Native>,
767{
768    match array.data_type() {
769        DataType::Dictionary(_, _) => min_max_helper::<T::Native, _, _>(array, cmp),
770        DataType::RunEndEncoded(run_ends, _) => {
771            // We can directly perform min/max on the values child array, as any
772            // run must have non-zero length.
773            let array: &dyn Array = &array;
774            let values = match run_ends.data_type() {
775                DataType::Int16 => array.as_run_opt::<types::Int16Type>()?.values_slice(),
776                DataType::Int32 => array.as_run_opt::<types::Int32Type>()?.values_slice(),
777                DataType::Int64 => array.as_run_opt::<types::Int64Type>()?.values_slice(),
778                _ => return None,
779            };
780            // We only support RunArray wrapping primitive types.
781            let values = values.as_any().downcast_ref::<PrimitiveArray<T>>()?;
782            m(values)
783        }
784        _ => m(as_primitive_array(&array)),
785    }
786}
787
788macro_rules! bit_operation {
789    ($NAME:ident, $OP:ident, $NATIVE:ident, $DEFAULT:expr, $DOC:expr) => {
790        #[doc = $DOC]
791        ///
792        /// Returns `None` if the array is empty or only contains null values.
793        pub fn $NAME<T>(array: &PrimitiveArray<T>) -> Option<T::Native>
794        where
795            T: ArrowNumericType,
796            T::Native: $NATIVE<Output = T::Native> + ArrowNativeTypeOp,
797        {
798            let default;
799            if $DEFAULT == -1 {
800                default = T::Native::ONE.neg_wrapping();
801            } else {
802                default = T::default_value();
803            }
804
805            let null_count = array.null_count();
806
807            if null_count == array.len() {
808                return None;
809            }
810
811            let data: &[T::Native] = array.values();
812
813            match array.nulls() {
814                None => {
815                    let result = data
816                        .iter()
817                        .fold(default, |accumulator, value| accumulator.$OP(*value));
818
819                    Some(result)
820                }
821                Some(nulls) => {
822                    let mut result = default;
823                    let data_chunks = data.chunks_exact(64);
824                    let remainder = data_chunks.remainder();
825
826                    let bit_chunks = nulls.inner().bit_chunks();
827                    data_chunks
828                        .zip(bit_chunks.iter())
829                        .for_each(|(chunk, mask)| {
830                            // index_mask has value 1 << i in the loop
831                            let mut index_mask = 1;
832                            chunk.iter().for_each(|value| {
833                                if (mask & index_mask) != 0 {
834                                    result = result.$OP(*value);
835                                }
836                                index_mask <<= 1;
837                            });
838                        });
839
840                    let remainder_bits = bit_chunks.remainder_bits();
841
842                    remainder.iter().enumerate().for_each(|(i, value)| {
843                        if remainder_bits & (1 << i) != 0 {
844                            result = result.$OP(*value);
845                        }
846                    });
847
848                    Some(result)
849                }
850            }
851        }
852    };
853}
854
855bit_operation!(
856    bit_and,
857    bitand,
858    BitAnd,
859    -1,
860    "Returns the bitwise and of all non-null input values."
861);
862bit_operation!(
863    bit_or,
864    bitor,
865    BitOr,
866    0,
867    "Returns the bitwise or of all non-null input values."
868);
869bit_operation!(
870    bit_xor,
871    bitxor,
872    BitXor,
873    0,
874    "Returns the bitwise xor of all non-null input values."
875);
876
877/// Returns true if all non-null input values are true, otherwise false.
878///
879/// Returns `None` if the array is empty or only contains null values.
880pub fn bool_and(array: &BooleanArray) -> Option<bool> {
881    min_boolean(array)
882}
883
884/// Returns true if any non-null input value is true, otherwise false.
885///
886/// Returns `None` if the array is empty or only contains null values.
887pub fn bool_or(array: &BooleanArray) -> Option<bool> {
888    max_boolean(array)
889}
890
891/// Returns the sum of values in the primitive array.
892///
893/// Returns `Ok(None)` if the array is empty or only contains null values.
894///
895/// This detects overflow and returns an `Err` for that. For an non-overflow-checking variant,
896/// use [`sum`] instead.
897pub fn sum_checked<T: ArrowNumericType>(
898    array: &PrimitiveArray<T>,
899) -> Result<Option<T::Native>, ArrowError> {
900    let null_count = array.null_count();
901
902    if null_count == array.len() {
903        return Ok(None);
904    }
905
906    let data: &[T::Native] = array.values();
907
908    match array.nulls() {
909        None => {
910            let sum = data
911                .iter()
912                .try_fold(T::default_value(), |accumulator, value| {
913                    accumulator.add_checked(*value)
914                })?;
915
916            Ok(Some(sum))
917        }
918        Some(nulls) => {
919            let mut sum = T::default_value();
920
921            try_for_each_valid_idx(
922                nulls.len(),
923                nulls.offset(),
924                nulls.null_count(),
925                Some(nulls.validity()),
926                |idx| {
927                    unsafe { sum = sum.add_checked(array.value_unchecked(idx))? };
928                    Ok::<_, ArrowError>(())
929                },
930            )?;
931
932            Ok(Some(sum))
933        }
934    }
935}
936
937/// Returns the sum of values in the primitive array.
938///
939/// Returns `None` if the array is empty or only contains null values.
940///
941/// This doesn't detect overflow in release mode by default. Once overflowing, the result will
942/// wrap around. For an overflow-checking variant, use [`sum_checked`] instead.
943pub fn sum<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
944    aggregate::<T::Native, T, SumAccumulator<T::Native>>(array)
945}
946
947/// Returns the product of values in the primitive array.
948///
949/// Returns `None` if the array is empty or only contains null values.
950///
951/// This doesn't detect overflow in release mode by default. Once overflowing, the result will
952/// wrap around. For an overflow-checking variant, use [`product_checked`] instead.
953pub fn product<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
954    aggregate::<T::Native, T, ProductAccumulator<T::Native>>(array)
955}
956
957/// Returns the product of values in the primitive array.
958///
959/// Returns `Ok(None)` if the array is empty or only contains null values.
960///
961/// This detects overflow and returns an `Err` for that. For an non-overflow-checking variant,
962/// use [`product`] instead.
963pub fn product_checked<T: ArrowNumericType>(
964    array: &PrimitiveArray<T>,
965) -> Result<Option<T::Native>, ArrowError> {
966    let null_count = array.null_count();
967
968    if null_count == array.len() {
969        return Ok(None);
970    }
971
972    let data: &[T::Native] = array.values();
973
974    match array.nulls() {
975        None => {
976            let product = data.iter().try_fold(T::Native::ONE, |accumulator, value| {
977                accumulator.mul_checked(*value)
978            })?;
979
980            Ok(Some(product))
981        }
982        Some(nulls) => {
983            let mut product = T::Native::ONE;
984
985            try_for_each_valid_idx(
986                nulls.len(),
987                nulls.offset(),
988                nulls.null_count(),
989                Some(nulls.validity()),
990                |idx| {
991                    unsafe { product = product.mul_checked(array.value_unchecked(idx))? };
992                    Ok::<_, ArrowError>(())
993                },
994            )?;
995
996            Ok(Some(product))
997        }
998    }
999}
1000
1001/// Returns the minimum value in the array, according to the natural order.
1002/// For floating point arrays any NaN values are considered to be greater than any other non-null value
1003///
1004/// # Example
1005/// ```rust
1006/// # use arrow_array::Int32Array;
1007/// # use arrow_arith::aggregate::min;
1008/// let array = Int32Array::from(vec![8, 2, 4]);
1009/// let result = min(&array);
1010/// assert_eq!(result, Some(2));
1011/// ```
1012pub fn min<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
1013    aggregate::<T::Native, T, MinAccumulator<T::Native>>(array)
1014}
1015
1016/// Returns the maximum value in the array, according to the natural order.
1017/// For floating point arrays any NaN values are considered to be greater than any other non-null value
1018///
1019/// # Example
1020/// ```rust
1021/// # use arrow_array::Int32Array;
1022/// # use arrow_arith::aggregate::max;
1023/// let array = Int32Array::from(vec![4, 8, 2]);
1024/// let result = max(&array);
1025/// assert_eq!(result, Some(8));
1026/// ```
1027pub fn max<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
1028    aggregate::<T::Native, T, MaxAccumulator<T::Native>>(array)
1029}
1030
1031#[cfg(test)]
1032mod tests {
1033    use super::*;
1034    use arrow_array::types::*;
1035    use builder::BooleanBuilder;
1036    use std::sync::Arc;
1037
1038    #[test]
1039    fn test_primitive_array_sum() {
1040        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1041        assert_eq!(15, sum(&a).unwrap());
1042    }
1043
1044    #[test]
1045    fn test_primitive_array_float_sum() {
1046        let a = Float64Array::from(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
1047        assert_eq!(16.5, sum(&a).unwrap());
1048    }
1049
1050    #[test]
1051    fn test_primitive_array_product() {
1052        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1053        assert_eq!(120, product(&a).unwrap());
1054    }
1055
1056    #[test]
1057    fn test_primitive_array_float_product() {
1058        let a = Float64Array::from(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1059        assert_eq!(120.0, product(&a).unwrap());
1060    }
1061
1062    #[test]
1063    fn test_primitive_array_product_with_nulls() {
1064        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1065        assert_eq!(30, product(&a).unwrap());
1066    }
1067
1068    #[test]
1069    fn test_primitive_array_product_all_nulls() {
1070        let a = Int32Array::from(vec![None, None, None]);
1071        assert_eq!(None, product(&a));
1072    }
1073
1074    #[test]
1075    fn test_primitive_array_product_empty() {
1076        let a = Int32Array::from(Vec::<i32>::new());
1077        assert_eq!(None, product(&a));
1078    }
1079
1080    #[test]
1081    fn test_primitive_array_product_checked() {
1082        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1083        assert_eq!(120, product_checked(&a).unwrap().unwrap());
1084    }
1085
1086    #[test]
1087    fn test_primitive_array_product_checked_with_nulls() {
1088        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1089        assert_eq!(30, product_checked(&a).unwrap().unwrap());
1090    }
1091
1092    #[test]
1093    fn test_primitive_array_product_checked_all_nulls() {
1094        let a = Int32Array::from(vec![None, None, None]);
1095        assert_eq!(None, product_checked(&a).unwrap());
1096    }
1097
1098    #[test]
1099    fn test_product_overflow() {
1100        let a = Int32Array::from(vec![i32::MAX, 2]);
1101        // wrapping variant silently overflows
1102        assert_eq!(product(&a).unwrap(), -2);
1103    }
1104
1105    #[test]
1106    fn test_product_checked_overflow() {
1107        let a = Int32Array::from(vec![i32::MAX, 2]);
1108        product_checked(&a).expect_err("overflow should be detected");
1109    }
1110
1111    #[test]
1112    fn test_primitive_array_sum_with_nulls() {
1113        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1114        assert_eq!(10, sum(&a).unwrap());
1115    }
1116
1117    #[test]
1118    fn test_primitive_array_sum_all_nulls() {
1119        let a = Int32Array::from(vec![None, None, None]);
1120        assert_eq!(None, sum(&a));
1121    }
1122
1123    #[test]
1124    fn test_primitive_array_sum_large_float_64() {
1125        let c = Float64Array::new((1..=100).map(|x| x as f64).collect(), None);
1126        assert_eq!(Some((1..=100).sum::<i64>() as f64), sum(&c));
1127
1128        // create an array that actually has non-zero values at the invalid indices
1129        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1130        let c = Float64Array::new((1..=100).map(|x| x as f64).collect(), Some(validity));
1131
1132        assert_eq!(
1133            Some((1..=100).filter(|i| i % 3 == 0).sum::<i64>() as f64),
1134            sum(&c)
1135        );
1136    }
1137
1138    #[test]
1139    fn test_primitive_array_sum_large_float_32() {
1140        let c = Float32Array::new((1..=100).map(|x| x as f32).collect(), None);
1141        assert_eq!(Some((1..=100).sum::<i64>() as f32), sum(&c));
1142
1143        // create an array that actually has non-zero values at the invalid indices
1144        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1145        let c = Float32Array::new((1..=100).map(|x| x as f32).collect(), Some(validity));
1146
1147        assert_eq!(
1148            Some((1..=100).filter(|i| i % 3 == 0).sum::<i64>() as f32),
1149            sum(&c)
1150        );
1151    }
1152
1153    #[test]
1154    fn test_primitive_array_sum_large_64() {
1155        let c = Int64Array::new((1..=100).collect(), None);
1156        assert_eq!(Some((1..=100).sum()), sum(&c));
1157
1158        // create an array that actually has non-zero values at the invalid indices
1159        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1160        let c = Int64Array::new((1..=100).collect(), Some(validity));
1161
1162        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1163    }
1164
1165    #[test]
1166    fn test_primitive_array_sum_large_32() {
1167        let c = Int32Array::new((1..=100).collect(), None);
1168        assert_eq!(Some((1..=100).sum()), sum(&c));
1169
1170        // create an array that actually has non-zero values at the invalid indices
1171        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1172        let c = Int32Array::new((1..=100).collect(), Some(validity));
1173        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1174    }
1175
1176    #[test]
1177    fn test_primitive_array_sum_large_16() {
1178        let c = Int16Array::new((1..=100).collect(), None);
1179        assert_eq!(Some((1..=100).sum()), sum(&c));
1180
1181        // create an array that actually has non-zero values at the invalid indices
1182        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1183        let c = Int16Array::new((1..=100).collect(), Some(validity));
1184        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1185    }
1186
1187    #[test]
1188    fn test_primitive_array_sum_large_8() {
1189        let c = UInt8Array::new((1..=100).collect(), None);
1190        assert_eq!(
1191            Some((1..=100).fold(0_u8, |a, x| a.wrapping_add(x))),
1192            sum(&c)
1193        );
1194
1195        // create an array that actually has non-zero values at the invalid indices
1196        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1197        let c = UInt8Array::new((1..=100).collect(), Some(validity));
1198        assert_eq!(
1199            Some(
1200                (1..=100)
1201                    .filter(|i| i % 3 == 0)
1202                    .fold(0_u8, |a, x| a.wrapping_add(x))
1203            ),
1204            sum(&c)
1205        );
1206    }
1207
1208    #[test]
1209    fn test_primitive_array_bit_and() {
1210        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1211        assert_eq!(0, bit_and(&a).unwrap());
1212    }
1213
1214    #[test]
1215    fn test_primitive_array_bit_and_with_nulls() {
1216        let a = Int32Array::from(vec![None, Some(2), Some(3), None, None]);
1217        assert_eq!(2, bit_and(&a).unwrap());
1218    }
1219
1220    #[test]
1221    fn test_primitive_array_bit_and_all_nulls() {
1222        let a = Int32Array::from(vec![None, None, None]);
1223        assert_eq!(None, bit_and(&a));
1224    }
1225
1226    #[test]
1227    fn test_primitive_array_bit_or() {
1228        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1229        assert_eq!(7, bit_or(&a).unwrap());
1230    }
1231
1232    #[test]
1233    fn test_primitive_array_bit_or_with_nulls() {
1234        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1235        assert_eq!(7, bit_or(&a).unwrap());
1236    }
1237
1238    #[test]
1239    fn test_primitive_array_bit_or_all_nulls() {
1240        let a = Int32Array::from(vec![None, None, None]);
1241        assert_eq!(None, bit_or(&a));
1242    }
1243
1244    #[test]
1245    fn test_primitive_array_bit_xor() {
1246        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1247        assert_eq!(1, bit_xor(&a).unwrap());
1248    }
1249
1250    #[test]
1251    fn test_primitive_array_bit_xor_with_nulls() {
1252        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1253        assert_eq!(4, bit_xor(&a).unwrap());
1254    }
1255
1256    #[test]
1257    fn test_primitive_array_bit_xor_all_nulls() {
1258        let a = Int32Array::from(vec![None, None, None]);
1259        assert_eq!(None, bit_xor(&a));
1260    }
1261
1262    #[test]
1263    fn test_primitive_array_bool_and() {
1264        let a = BooleanArray::from(vec![true, false, true, false, true]);
1265        assert!(!bool_and(&a).unwrap());
1266    }
1267
1268    #[test]
1269    fn test_primitive_array_bool_and_with_nulls() {
1270        let a = BooleanArray::from(vec![None, Some(true), Some(true), None, Some(true)]);
1271        assert!(bool_and(&a).unwrap());
1272    }
1273
1274    #[test]
1275    fn test_primitive_array_bool_and_all_nulls() {
1276        let a = BooleanArray::from(vec![None, None, None]);
1277        assert_eq!(None, bool_and(&a));
1278    }
1279
1280    #[test]
1281    fn test_primitive_array_bool_or() {
1282        let a = BooleanArray::from(vec![true, false, true, false, true]);
1283        assert!(bool_or(&a).unwrap());
1284    }
1285
1286    #[test]
1287    fn test_primitive_array_bool_or_with_nulls() {
1288        let a = BooleanArray::from(vec![None, Some(false), Some(false), None, Some(false)]);
1289        assert!(!bool_or(&a).unwrap());
1290    }
1291
1292    #[test]
1293    fn test_primitive_array_bool_or_all_nulls() {
1294        let a = BooleanArray::from(vec![None, None, None]);
1295        assert_eq!(None, bool_or(&a));
1296    }
1297
1298    #[test]
1299    fn test_primitive_array_min_max() {
1300        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
1301        assert_eq!(5, min(&a).unwrap());
1302        assert_eq!(9, max(&a).unwrap());
1303    }
1304
1305    #[test]
1306    fn test_primitive_array_min_max_with_nulls() {
1307        let a = Int32Array::from(vec![Some(5), None, None, Some(8), Some(9)]);
1308        assert_eq!(5, min(&a).unwrap());
1309        assert_eq!(9, max(&a).unwrap());
1310    }
1311
1312    #[test]
1313    fn test_primitive_min_max_1() {
1314        let a = Int32Array::from(vec![None, None, Some(5), Some(2)]);
1315        assert_eq!(Some(2), min(&a));
1316        assert_eq!(Some(5), max(&a));
1317    }
1318
1319    #[test]
1320    fn test_primitive_min_max_float_large_nonnull_array() {
1321        let a: Float64Array = (0..256).map(|i| Some((i + 1) as f64)).collect();
1322        // min/max are on boundaries of chunked data
1323        assert_eq!(Some(1.0), min(&a));
1324        assert_eq!(Some(256.0), max(&a));
1325
1326        // max is last value in remainder after chunking
1327        let a: Float64Array = (0..255).map(|i| Some((i + 1) as f64)).collect();
1328        assert_eq!(Some(255.0), max(&a));
1329
1330        // max is first value in remainder after chunking
1331        let a: Float64Array = (0..257).map(|i| Some((i + 1) as f64)).collect();
1332        assert_eq!(Some(257.0), max(&a));
1333    }
1334
1335    #[test]
1336    fn test_primitive_min_max_float_large_nullable_array() {
1337        let a: Float64Array = (0..256)
1338            .map(|i| {
1339                if (i + 1) % 3 == 0 {
1340                    None
1341                } else {
1342                    Some((i + 1) as f64)
1343                }
1344            })
1345            .collect();
1346        // min/max are on boundaries of chunked data
1347        assert_eq!(Some(1.0), min(&a));
1348        assert_eq!(Some(256.0), max(&a));
1349
1350        let a: Float64Array = (0..256)
1351            .map(|i| {
1352                if i == 0 || i == 255 {
1353                    None
1354                } else {
1355                    Some((i + 1) as f64)
1356                }
1357            })
1358            .collect();
1359        // boundaries of chunked data are null
1360        assert_eq!(Some(2.0), min(&a));
1361        assert_eq!(Some(255.0), max(&a));
1362
1363        let a: Float64Array = (0..256)
1364            .map(|i| if i != 100 { None } else { Some((i) as f64) })
1365            .collect();
1366        // a single non-null value somewhere in the middle
1367        assert_eq!(Some(100.0), min(&a));
1368        assert_eq!(Some(100.0), max(&a));
1369
1370        // max is last value in remainder after chunking
1371        let a: Float64Array = (0..255).map(|i| Some((i + 1) as f64)).collect();
1372        assert_eq!(Some(255.0), max(&a));
1373
1374        // max is first value in remainder after chunking
1375        let a: Float64Array = (0..257).map(|i| Some((i + 1) as f64)).collect();
1376        assert_eq!(Some(257.0), max(&a));
1377    }
1378
1379    #[test]
1380    fn test_primitive_min_max_float_edge_cases() {
1381        let a: Float64Array = (0..100).map(|_| Some(f64::NEG_INFINITY)).collect();
1382        assert_eq!(Some(f64::NEG_INFINITY), min(&a));
1383        assert_eq!(Some(f64::NEG_INFINITY), max(&a));
1384
1385        let a: Float64Array = (0..100).map(|_| Some(f64::MIN)).collect();
1386        assert_eq!(Some(f64::MIN), min(&a));
1387        assert_eq!(Some(f64::MIN), max(&a));
1388
1389        let a: Float64Array = (0..100).map(|_| Some(f64::MAX)).collect();
1390        assert_eq!(Some(f64::MAX), min(&a));
1391        assert_eq!(Some(f64::MAX), max(&a));
1392
1393        let a: Float64Array = (0..100).map(|_| Some(f64::INFINITY)).collect();
1394        assert_eq!(Some(f64::INFINITY), min(&a));
1395        assert_eq!(Some(f64::INFINITY), max(&a));
1396    }
1397
1398    #[test]
1399    fn test_primitive_min_max_float_all_nans_non_null() {
1400        let a: Float64Array = (0..100).map(|_| Some(f64::NAN)).collect();
1401        assert!(max(&a).unwrap().is_nan());
1402        assert!(min(&a).unwrap().is_nan());
1403    }
1404
1405    #[test]
1406    fn test_primitive_min_max_float_negative_nan() {
1407        let a: Float64Array =
1408            Float64Array::from(vec![f64::NEG_INFINITY, f64::NAN, f64::INFINITY, -f64::NAN]);
1409        let max = max(&a).unwrap();
1410        let min = min(&a).unwrap();
1411        assert!(max.is_nan());
1412        assert!(max.is_sign_positive());
1413
1414        assert!(min.is_nan());
1415        assert!(min.is_sign_negative());
1416    }
1417
1418    #[test]
1419    fn test_primitive_min_max_float_first_nan_nonnull() {
1420        let a: Float64Array = (0..100)
1421            .map(|i| {
1422                if i == 0 {
1423                    Some(f64::NAN)
1424                } else {
1425                    Some(i as f64)
1426                }
1427            })
1428            .collect();
1429        assert_eq!(Some(1.0), min(&a));
1430        assert!(max(&a).unwrap().is_nan());
1431    }
1432
1433    #[test]
1434    fn test_primitive_min_max_float_last_nan_nonnull() {
1435        let a: Float64Array = (0..100)
1436            .map(|i| {
1437                if i == 99 {
1438                    Some(f64::NAN)
1439                } else {
1440                    Some((i + 1) as f64)
1441                }
1442            })
1443            .collect();
1444        assert_eq!(Some(1.0), min(&a));
1445        assert!(max(&a).unwrap().is_nan());
1446    }
1447
1448    #[test]
1449    fn test_primitive_min_max_float_first_nan_nullable() {
1450        let a: Float64Array = (0..100)
1451            .map(|i| {
1452                if i == 0 {
1453                    Some(f64::NAN)
1454                } else if i % 2 == 0 {
1455                    None
1456                } else {
1457                    Some(i as f64)
1458                }
1459            })
1460            .collect();
1461        assert_eq!(Some(1.0), min(&a));
1462        assert!(max(&a).unwrap().is_nan());
1463    }
1464
1465    #[test]
1466    fn test_primitive_min_max_float_last_nan_nullable() {
1467        let a: Float64Array = (0..100)
1468            .map(|i| {
1469                if i == 99 {
1470                    Some(f64::NAN)
1471                } else if i % 2 == 0 {
1472                    None
1473                } else {
1474                    Some(i as f64)
1475                }
1476            })
1477            .collect();
1478        assert_eq!(Some(1.0), min(&a));
1479        assert!(max(&a).unwrap().is_nan());
1480    }
1481
1482    #[test]
1483    fn test_primitive_min_max_float_inf_and_nans() {
1484        let a: Float64Array = (0..100)
1485            .map(|i| {
1486                let x = match i % 10 {
1487                    0 => f64::NEG_INFINITY,
1488                    1 => f64::MIN,
1489                    2 => f64::MAX,
1490                    4 => f64::INFINITY,
1491                    5 => f64::NAN,
1492                    _ => i as f64,
1493                };
1494                Some(x)
1495            })
1496            .collect();
1497        assert_eq!(Some(f64::NEG_INFINITY), min(&a));
1498        assert!(max(&a).unwrap().is_nan());
1499    }
1500
1501    fn pad_inputs_and_test_fixed_size_binary(
1502        input: Vec<Option<&[u8]>>,
1503        expected_min: Option<&[u8]>,
1504        expected_max: Option<&[u8]>,
1505    ) {
1506        fn pad_slice(slice: &[u8], len: usize) -> Vec<u8> {
1507            let mut padded = vec![0; len];
1508            padded[..slice.len()].copy_from_slice(slice);
1509            padded
1510        }
1511
1512        let max_len = input
1513            .iter()
1514            .filter_map(|x| x.as_ref().map(|b| b.len()))
1515            .max()
1516            .unwrap_or(0);
1517        let padded_input = input
1518            .iter()
1519            .map(|x| x.as_ref().map(|b| pad_slice(b, max_len)));
1520        let input_arr =
1521            FixedSizeBinaryArray::try_from_sparse_iter_with_size(padded_input, max_len as i32)
1522                .unwrap();
1523        let padded_expected_min = expected_min.map(|b| pad_slice(b, max_len));
1524        let padded_expected_max = expected_max.map(|b| pad_slice(b, max_len));
1525
1526        assert_eq!(
1527            padded_expected_min.as_deref(),
1528            min_fixed_size_binary(&input_arr)
1529        );
1530        assert_eq!(
1531            padded_expected_max.as_deref(),
1532            max_fixed_size_binary(&input_arr)
1533        );
1534    }
1535
1536    macro_rules! test_binary {
1537        ($NAME:ident, $ARRAY:expr, $EXPECTED_MIN:expr, $EXPECTED_MAX: expr) => {
1538            #[test]
1539            fn $NAME() {
1540                let binary = BinaryArray::from($ARRAY);
1541                assert_eq!($EXPECTED_MIN, min_binary(&binary));
1542                assert_eq!($EXPECTED_MAX, max_binary(&binary));
1543
1544                let large_binary = LargeBinaryArray::from($ARRAY);
1545                assert_eq!($EXPECTED_MIN, min_binary(&large_binary));
1546                assert_eq!($EXPECTED_MAX, max_binary(&large_binary));
1547
1548                let binary_view = BinaryViewArray::from($ARRAY);
1549                assert_eq!($EXPECTED_MIN, min_binary_view(&binary_view));
1550                assert_eq!($EXPECTED_MAX, max_binary_view(&binary_view));
1551
1552                pad_inputs_and_test_fixed_size_binary($ARRAY, $EXPECTED_MIN, $EXPECTED_MAX);
1553            }
1554        };
1555    }
1556
1557    test_binary!(
1558        test_binary_min_max_with_nulls,
1559        vec![
1560            Some("b01234567890123".as_bytes()), // long bytes
1561            None,
1562            None,
1563            Some(b"a"),
1564            Some(b"c"),
1565            Some(b"abcdedfg0123456"),
1566        ],
1567        Some("a".as_bytes()),
1568        Some("c".as_bytes())
1569    );
1570
1571    test_binary!(
1572        test_binary_min_max_no_null,
1573        vec![
1574            Some("b".as_bytes()),
1575            Some(b"abcdefghijklmnopqrst"), // long bytes
1576            Some(b"c"),
1577            Some(b"b01234567890123"), // long bytes for view types
1578        ],
1579        Some("abcdefghijklmnopqrst".as_bytes()),
1580        Some("c".as_bytes())
1581    );
1582
1583    test_binary!(test_binary_min_max_all_nulls, vec![None, None], None, None);
1584
1585    test_binary!(
1586        test_binary_min_max_1,
1587        vec![
1588            None,
1589            Some("b01234567890123435".as_bytes()), // long bytes for view types
1590            None,
1591            Some(b"b0123xxxxxxxxxxx"),
1592            Some(b"a")
1593        ],
1594        Some("a".as_bytes()),
1595        Some("b0123xxxxxxxxxxx".as_bytes())
1596    );
1597
1598    macro_rules! test_string {
1599        ($NAME:ident, $ARRAY:expr, $EXPECTED_MIN:expr, $EXPECTED_MAX: expr) => {
1600            #[test]
1601            fn $NAME() {
1602                let string = StringArray::from($ARRAY);
1603                assert_eq!($EXPECTED_MIN, min_string(&string));
1604                assert_eq!($EXPECTED_MAX, max_string(&string));
1605
1606                let large_string = LargeStringArray::from($ARRAY);
1607                assert_eq!($EXPECTED_MIN, min_string(&large_string));
1608                assert_eq!($EXPECTED_MAX, max_string(&large_string));
1609
1610                let string_view = StringViewArray::from($ARRAY);
1611                assert_eq!($EXPECTED_MIN, min_string_view(&string_view));
1612                assert_eq!($EXPECTED_MAX, max_string_view(&string_view));
1613            }
1614        };
1615    }
1616
1617    test_string!(
1618        test_string_min_max_with_nulls,
1619        vec![
1620            Some("b012345678901234"), // long bytes for view types
1621            None,
1622            None,
1623            Some("a"),
1624            Some("c"),
1625            Some("b0123xxxxxxxxxxx")
1626        ],
1627        Some("a"),
1628        Some("c")
1629    );
1630
1631    test_string!(
1632        test_string_min_max_no_null,
1633        vec![
1634            Some("b"),
1635            Some("b012345678901234"), // long bytes for view types
1636            Some("a"),
1637            Some("b012xxxxxxxxxxxx")
1638        ],
1639        Some("a"),
1640        Some("b012xxxxxxxxxxxx")
1641    );
1642
1643    test_string!(
1644        test_string_min_max_all_nulls,
1645        Vec::<Option<&str>>::from_iter([None, None]),
1646        None,
1647        None
1648    );
1649
1650    test_string!(
1651        test_string_min_max_1,
1652        vec![
1653            None,
1654            Some("c12345678901234"), // long bytes for view types
1655            None,
1656            Some("b"),
1657            Some("c1234xxxxxxxxxx")
1658        ],
1659        Some("b"),
1660        Some("c1234xxxxxxxxxx")
1661    );
1662
1663    test_string!(
1664        test_string_min_max_empty,
1665        Vec::<Option<&str>>::new(),
1666        None,
1667        None
1668    );
1669
1670    #[test]
1671    fn test_boolean_min_max_empty() {
1672        let a = BooleanArray::from(vec![] as Vec<Option<bool>>);
1673        assert_eq!(None, min_boolean(&a));
1674        assert_eq!(None, max_boolean(&a));
1675    }
1676
1677    #[test]
1678    fn test_boolean_min_max_all_null() {
1679        let a = BooleanArray::from(vec![None, None]);
1680        assert_eq!(None, min_boolean(&a));
1681        assert_eq!(None, max_boolean(&a));
1682    }
1683
1684    #[test]
1685    fn test_boolean_min_max_no_null() {
1686        let a = BooleanArray::from(vec![Some(true), Some(false), Some(true)]);
1687        assert_eq!(Some(false), min_boolean(&a));
1688        assert_eq!(Some(true), max_boolean(&a));
1689    }
1690
1691    #[test]
1692    fn test_boolean_min_max() {
1693        let a = BooleanArray::from(vec![Some(true), Some(true), None, Some(false), None]);
1694        assert_eq!(Some(false), min_boolean(&a));
1695        assert_eq!(Some(true), max_boolean(&a));
1696
1697        let a = BooleanArray::from(vec![None, Some(true), None, Some(false), None]);
1698        assert_eq!(Some(false), min_boolean(&a));
1699        assert_eq!(Some(true), max_boolean(&a));
1700
1701        let a = BooleanArray::from(vec![Some(false), Some(true), None, Some(false), None]);
1702        assert_eq!(Some(false), min_boolean(&a));
1703        assert_eq!(Some(true), max_boolean(&a));
1704
1705        let a = BooleanArray::from(vec![Some(true), None]);
1706        assert_eq!(Some(true), min_boolean(&a));
1707        assert_eq!(Some(true), max_boolean(&a));
1708
1709        let a = BooleanArray::from(vec![Some(false), None]);
1710        assert_eq!(Some(false), min_boolean(&a));
1711        assert_eq!(Some(false), max_boolean(&a));
1712
1713        let a = BooleanArray::from(vec![Some(true)]);
1714        assert_eq!(Some(true), min_boolean(&a));
1715        assert_eq!(Some(true), max_boolean(&a));
1716
1717        let a = BooleanArray::from(vec![Some(false)]);
1718        assert_eq!(Some(false), min_boolean(&a));
1719        assert_eq!(Some(false), max_boolean(&a));
1720    }
1721
1722    #[test]
1723    fn test_boolean_min_max_smaller() {
1724        let a = BooleanArray::from(vec![Some(false)]);
1725        assert_eq!(Some(false), min_boolean(&a));
1726        assert_eq!(Some(false), max_boolean(&a));
1727
1728        let a = BooleanArray::from(vec![None, Some(false)]);
1729        assert_eq!(Some(false), min_boolean(&a));
1730        assert_eq!(Some(false), max_boolean(&a));
1731
1732        let a = BooleanArray::from(vec![None, Some(true)]);
1733        assert_eq!(Some(true), min_boolean(&a));
1734        assert_eq!(Some(true), max_boolean(&a));
1735
1736        let a = BooleanArray::from(vec![Some(true)]);
1737        assert_eq!(Some(true), min_boolean(&a));
1738        assert_eq!(Some(true), max_boolean(&a));
1739    }
1740
1741    #[test]
1742    fn test_boolean_min_max_64_true_64_false() {
1743        let mut no_nulls = BooleanBuilder::new();
1744        no_nulls.append_slice(&[true; 64]);
1745        no_nulls.append_slice(&[false; 64]);
1746        let no_nulls = no_nulls.finish();
1747
1748        assert_eq!(Some(false), min_boolean(&no_nulls));
1749        assert_eq!(Some(true), max_boolean(&no_nulls));
1750
1751        let mut with_nulls = BooleanBuilder::new();
1752        with_nulls.append_slice(&[true; 31]);
1753        with_nulls.append_null();
1754        with_nulls.append_slice(&[true; 32]);
1755        with_nulls.append_slice(&[false; 1]);
1756        with_nulls.append_nulls(63);
1757        let with_nulls = with_nulls.finish();
1758
1759        assert_eq!(Some(false), min_boolean(&with_nulls));
1760        assert_eq!(Some(true), max_boolean(&with_nulls));
1761    }
1762
1763    #[test]
1764    fn test_boolean_min_max_64_false_64_true() {
1765        let mut no_nulls = BooleanBuilder::new();
1766        no_nulls.append_slice(&[false; 64]);
1767        no_nulls.append_slice(&[true; 64]);
1768        let no_nulls = no_nulls.finish();
1769
1770        assert_eq!(Some(false), min_boolean(&no_nulls));
1771        assert_eq!(Some(true), max_boolean(&no_nulls));
1772
1773        let mut with_nulls = BooleanBuilder::new();
1774        with_nulls.append_slice(&[false; 31]);
1775        with_nulls.append_null();
1776        with_nulls.append_slice(&[false; 32]);
1777        with_nulls.append_slice(&[true; 1]);
1778        with_nulls.append_nulls(63);
1779        let with_nulls = with_nulls.finish();
1780
1781        assert_eq!(Some(false), min_boolean(&with_nulls));
1782        assert_eq!(Some(true), max_boolean(&with_nulls));
1783    }
1784
1785    #[test]
1786    fn test_boolean_min_max_96_true() {
1787        let mut no_nulls = BooleanBuilder::new();
1788        no_nulls.append_slice(&[true; 96]);
1789        let no_nulls = no_nulls.finish();
1790
1791        assert_eq!(Some(true), min_boolean(&no_nulls));
1792        assert_eq!(Some(true), max_boolean(&no_nulls));
1793
1794        let mut with_nulls = BooleanBuilder::new();
1795        with_nulls.append_slice(&[true; 31]);
1796        with_nulls.append_null();
1797        with_nulls.append_slice(&[true; 32]);
1798        with_nulls.append_slice(&[true; 31]);
1799        with_nulls.append_null();
1800        let with_nulls = with_nulls.finish();
1801
1802        assert_eq!(Some(true), min_boolean(&with_nulls));
1803        assert_eq!(Some(true), max_boolean(&with_nulls));
1804    }
1805
1806    #[test]
1807    fn test_boolean_min_max_96_false() {
1808        let mut no_nulls = BooleanBuilder::new();
1809        no_nulls.append_slice(&[false; 96]);
1810        let no_nulls = no_nulls.finish();
1811
1812        assert_eq!(Some(false), min_boolean(&no_nulls));
1813        assert_eq!(Some(false), max_boolean(&no_nulls));
1814
1815        let mut with_nulls = BooleanBuilder::new();
1816        with_nulls.append_slice(&[false; 31]);
1817        with_nulls.append_null();
1818        with_nulls.append_slice(&[false; 32]);
1819        with_nulls.append_slice(&[false; 31]);
1820        with_nulls.append_null();
1821        let with_nulls = with_nulls.finish();
1822
1823        assert_eq!(Some(false), min_boolean(&with_nulls));
1824        assert_eq!(Some(false), max_boolean(&with_nulls));
1825    }
1826
1827    #[test]
1828    fn test_sum_dyn() {
1829        let values = Int8Array::from_iter_values([10_i8, 11, 12, 13, 14, 15, 16, 17]);
1830        let values = Arc::new(values) as ArrayRef;
1831        let keys = Int8Array::from_iter_values([2_i8, 3, 4]);
1832
1833        let dict_array = DictionaryArray::new(keys, values.clone());
1834        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1835        assert_eq!(39, sum_array::<Int8Type, _>(array).unwrap());
1836
1837        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1838        assert_eq!(15, sum_array::<Int32Type, _>(&a).unwrap());
1839
1840        let keys = Int8Array::from(vec![Some(2_i8), None, Some(4)]);
1841        let dict_array = DictionaryArray::new(keys, values.clone());
1842        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1843        assert_eq!(26, sum_array::<Int8Type, _>(array).unwrap());
1844
1845        let keys = Int8Array::from(vec![None, None, None]);
1846        let dict_array = DictionaryArray::new(keys, values.clone());
1847        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1848        assert!(sum_array::<Int8Type, _>(array).is_none());
1849    }
1850
1851    #[test]
1852    fn test_max_min_dyn() {
1853        let values = Int8Array::from_iter_values([10_i8, 11, 12, 13, 14, 15, 16, 17]);
1854        let keys = Int8Array::from_iter_values([2_i8, 3, 4]);
1855        let values = Arc::new(values) as ArrayRef;
1856
1857        let dict_array = DictionaryArray::new(keys, values.clone());
1858        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1859        assert_eq!(14, max_array::<Int8Type, _>(array).unwrap());
1860
1861        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1862        assert_eq!(12, min_array::<Int8Type, _>(array).unwrap());
1863
1864        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1865        assert_eq!(5, max_array::<Int32Type, _>(&a).unwrap());
1866        assert_eq!(1, min_array::<Int32Type, _>(&a).unwrap());
1867
1868        let keys = Int8Array::from(vec![Some(2_i8), None, Some(7)]);
1869        let dict_array = DictionaryArray::new(keys, values.clone());
1870        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1871        assert_eq!(17, max_array::<Int8Type, _>(array).unwrap());
1872        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1873        assert_eq!(12, min_array::<Int8Type, _>(array).unwrap());
1874
1875        let keys = Int8Array::from(vec![None, None, None]);
1876        let dict_array = DictionaryArray::new(keys, values.clone());
1877        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1878        assert!(max_array::<Int8Type, _>(array).is_none());
1879        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1880        assert!(min_array::<Int8Type, _>(array).is_none());
1881    }
1882
1883    #[test]
1884    fn test_max_min_dyn_nan() {
1885        let values = Float32Array::from(vec![5.0_f32, 2.0_f32, f32::NAN]);
1886        let keys = Int8Array::from_iter_values([0_i8, 1, 2]);
1887
1888        let dict_array = DictionaryArray::new(keys, Arc::new(values));
1889        let array = dict_array.downcast_dict::<Float32Array>().unwrap();
1890        assert!(max_array::<Float32Type, _>(array).unwrap().is_nan());
1891
1892        let array = dict_array.downcast_dict::<Float32Array>().unwrap();
1893        assert_eq!(2.0_f32, min_array::<Float32Type, _>(array).unwrap());
1894    }
1895
1896    #[test]
1897    fn test_min_max_sliced_primitive() {
1898        let expected = Some(4.0);
1899        let input: Float64Array = vec![None, Some(4.0)].into_iter().collect();
1900        let actual = min(&input);
1901        assert_eq!(actual, expected);
1902        let actual = max(&input);
1903        assert_eq!(actual, expected);
1904
1905        let sliced_input: Float64Array = vec![None, None, None, None, None, Some(4.0)]
1906            .into_iter()
1907            .collect();
1908        let sliced_input = sliced_input.slice(4, 2);
1909
1910        assert_eq!(&sliced_input, &input);
1911
1912        let actual = min(&sliced_input);
1913        assert_eq!(actual, expected);
1914        let actual = max(&sliced_input);
1915        assert_eq!(actual, expected);
1916    }
1917
1918    #[test]
1919    fn test_min_max_sliced_boolean() {
1920        let expected = Some(true);
1921        let input: BooleanArray = vec![None, Some(true)].into_iter().collect();
1922        let actual = min_boolean(&input);
1923        assert_eq!(actual, expected);
1924        let actual = max_boolean(&input);
1925        assert_eq!(actual, expected);
1926
1927        let sliced_input: BooleanArray = vec![None, None, None, None, None, Some(true)]
1928            .into_iter()
1929            .collect();
1930        let sliced_input = sliced_input.slice(4, 2);
1931
1932        assert_eq!(sliced_input, input);
1933
1934        let actual = min_boolean(&sliced_input);
1935        assert_eq!(actual, expected);
1936        let actual = max_boolean(&sliced_input);
1937        assert_eq!(actual, expected);
1938    }
1939
1940    #[test]
1941    fn test_min_max_sliced_string() {
1942        let expected = Some("foo");
1943        let input: StringArray = vec![None, Some("foo")].into_iter().collect();
1944        let actual = min_string(&input);
1945        assert_eq!(actual, expected);
1946        let actual = max_string(&input);
1947        assert_eq!(actual, expected);
1948
1949        let sliced_input: StringArray = vec![None, None, None, None, None, Some("foo")]
1950            .into_iter()
1951            .collect();
1952        let sliced_input = sliced_input.slice(4, 2);
1953
1954        assert_eq!(&sliced_input, &input);
1955
1956        let actual = min_string(&sliced_input);
1957        assert_eq!(actual, expected);
1958        let actual = max_string(&sliced_input);
1959        assert_eq!(actual, expected);
1960    }
1961
1962    #[test]
1963    fn test_min_max_sliced_binary() {
1964        let expected: Option<&[u8]> = Some(&[5]);
1965        let input: BinaryArray = vec![None, Some(&[5])].into_iter().collect();
1966        let actual = min_binary(&input);
1967        assert_eq!(actual, expected);
1968        let actual = max_binary(&input);
1969        assert_eq!(actual, expected);
1970
1971        let sliced_input: BinaryArray = vec![None, None, None, None, None, Some(&[5])]
1972            .into_iter()
1973            .collect();
1974        let sliced_input = sliced_input.slice(4, 2);
1975
1976        assert_eq!(&sliced_input, &input);
1977
1978        let actual = min_binary(&sliced_input);
1979        assert_eq!(actual, expected);
1980        let actual = max_binary(&sliced_input);
1981        assert_eq!(actual, expected);
1982    }
1983
1984    #[test]
1985    fn test_sum_overflow() {
1986        let a = Int32Array::from(vec![i32::MAX, 1]);
1987
1988        assert_eq!(sum(&a).unwrap(), -2147483648);
1989        assert_eq!(sum_array::<Int32Type, _>(&a).unwrap(), -2147483648);
1990    }
1991
1992    #[test]
1993    fn test_sum_checked_overflow() {
1994        let a = Int32Array::from(vec![i32::MAX, 1]);
1995
1996        sum_checked(&a).expect_err("overflow should be detected");
1997        sum_array_checked::<Int32Type, _>(&a).expect_err("overflow should be detected");
1998    }
1999
2000    /// Helper for building a RunArray.
2001    fn make_run_array<'a, I: RunEndIndexType, V: ArrowNumericType, ItemType>(
2002        values: impl IntoIterator<Item = &'a ItemType>,
2003    ) -> RunArray<I>
2004    where
2005        ItemType: Clone + Into<Option<V::Native>> + 'static,
2006    {
2007        let mut builder = arrow_array::builder::PrimitiveRunBuilder::<I, V>::new();
2008        for v in values.into_iter() {
2009            builder.append_option((*v).clone().into());
2010        }
2011        builder.finish()
2012    }
2013
2014    #[test]
2015    fn test_ree_sum_array_basic() {
2016        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[10, 10, 20, 30, 30, 30]);
2017        let typed_array = run_array.downcast::<Int32Array>().unwrap();
2018
2019        let result = sum_array::<Int32Type, _>(typed_array);
2020        assert_eq!(result, Some(130));
2021
2022        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
2023        assert_eq!(result, Some(130));
2024    }
2025
2026    #[test]
2027    fn test_ree_sum_array_empty() {
2028        let run_array = make_run_array::<Int16Type, Int32Type, i32>(&[]);
2029        let typed_array = run_array.downcast::<Int32Array>().unwrap();
2030
2031        let result = sum_array::<Int32Type, _>(typed_array);
2032        assert_eq!(result, None);
2033
2034        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
2035        assert_eq!(result, None);
2036    }
2037
2038    #[test]
2039    fn test_ree_sum_array_with_nulls() {
2040        let run_array =
2041            make_run_array::<Int16Type, Int32Type, _>(&[Some(10), None, Some(20), None, Some(30)]);
2042        let typed_array = run_array.downcast::<Int32Array>().unwrap();
2043
2044        let result = sum_array::<Int32Type, _>(typed_array);
2045        assert_eq!(result, Some(60));
2046
2047        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
2048        assert_eq!(result, Some(60));
2049    }
2050
2051    #[test]
2052    fn test_ree_sum_array_with_only_nulls() {
2053        let run_array = make_run_array::<Int16Type, Int16Type, _>(&[None, None, None, None, None]);
2054        let typed_array = run_array.downcast::<Int16Array>().unwrap();
2055
2056        let result = sum_array::<Int16Type, _>(typed_array);
2057        assert_eq!(result, None);
2058
2059        let result = sum_array_checked::<Int16Type, _>(typed_array).unwrap();
2060        assert_eq!(result, None);
2061    }
2062
2063    #[test]
2064    fn test_ree_sum_array_overflow() {
2065        let run_array = make_run_array::<Int16Type, Int8Type, _>(&[126, 2]);
2066        let typed_array = run_array.downcast::<Int8Array>().unwrap();
2067
2068        // i8 range is -128..=127. 126+2 overflows to -128.
2069        let result = sum_array::<Int8Type, _>(typed_array);
2070        assert_eq!(result, Some(-128));
2071
2072        let result = sum_array_checked::<Int8Type, _>(typed_array);
2073        assert!(result.is_err());
2074    }
2075
2076    #[test]
2077    fn test_ree_sum_array_sliced() {
2078        let run_array = make_run_array::<Int16Type, UInt8Type, _>(&[0, 10, 10, 10, 20, 30, 30, 30]);
2079        // Skip 2 values at the start and 1 at the end.
2080        let sliced = run_array.slice(2, 5);
2081        let typed_array = sliced.downcast::<UInt8Array>().unwrap();
2082
2083        let result = sum_array::<UInt8Type, _>(typed_array);
2084        assert_eq!(result, Some(100));
2085
2086        let result = sum_array_checked::<UInt8Type, _>(typed_array).unwrap();
2087        assert_eq!(result, Some(100));
2088    }
2089
2090    #[test]
2091    fn test_ree_min_max_array_basic() {
2092        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[30, 30, 10, 20, 20]);
2093        let typed_array = run_array.downcast::<Int32Array>().unwrap();
2094
2095        let result = min_array::<Int32Type, _>(typed_array);
2096        assert_eq!(result, Some(10));
2097
2098        let result = max_array::<Int32Type, _>(typed_array);
2099        assert_eq!(result, Some(30));
2100    }
2101
2102    #[test]
2103    fn test_ree_min_max_array_empty() {
2104        let run_array = make_run_array::<Int16Type, Int32Type, i32>(&[]);
2105        let typed_array = run_array.downcast::<Int32Array>().unwrap();
2106
2107        let result = min_array::<Int32Type, _>(typed_array);
2108        assert_eq!(result, None);
2109
2110        let result = max_array::<Int32Type, _>(typed_array);
2111        assert_eq!(result, None);
2112    }
2113
2114    #[test]
2115    fn test_ree_min_max_array_float() {
2116        let run_array = make_run_array::<Int16Type, Float64Type, _>(&[5.5, 5.5, 2.1, 8.9, 8.9]);
2117        let typed_array = run_array.downcast::<Float64Array>().unwrap();
2118
2119        let result = min_array::<Float64Type, _>(typed_array);
2120        assert_eq!(result, Some(2.1));
2121
2122        let result = max_array::<Float64Type, _>(typed_array);
2123        assert_eq!(result, Some(8.9));
2124    }
2125
2126    #[test]
2127    fn test_ree_min_max_array_with_nulls() {
2128        let run_array = make_run_array::<Int16Type, UInt8Type, _>(&[None, Some(10)]);
2129        let typed_array = run_array.downcast::<UInt8Array>().unwrap();
2130
2131        let result = min_array::<UInt8Type, _>(typed_array);
2132        assert_eq!(result, Some(10));
2133
2134        let result = max_array::<UInt8Type, _>(typed_array);
2135        assert_eq!(result, Some(10));
2136    }
2137
2138    #[test]
2139    fn test_ree_min_max_array_sliced() {
2140        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[0, 30, 30, 10, 20, 20, 100]);
2141        // Skip 1 value at the start and 1 at the end.
2142        let sliced = run_array.slice(1, 5);
2143        let typed_array = sliced.downcast::<Int32Array>().unwrap();
2144
2145        let result = min_array::<Int32Type, _>(typed_array);
2146        assert_eq!(result, Some(10));
2147
2148        let result = max_array::<Int32Type, _>(typed_array);
2149        assert_eq!(result, Some(30));
2150    }
2151
2152    #[test]
2153    fn test_ree_min_max_array_sliced_mid_run() {
2154        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[0, 0, 30, 10, 20, 100, 100]);
2155        // Skip 1 value at the start and 1 at the end.
2156        let sliced = run_array.slice(1, 5);
2157        let typed_array = sliced.downcast::<Int32Array>().unwrap();
2158
2159        let result = min_array::<Int32Type, _>(typed_array);
2160        assert_eq!(result, Some(0));
2161
2162        let result = max_array::<Int32Type, _>(typed_array);
2163        assert_eq!(result, Some(100));
2164    }
2165}