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