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 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: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
545    array: A,
546) -> Option<T::Native> {
547    match array.data_type() {
548        DataType::Dictionary(_, _) => {
549            let null_count = array.null_count();
550
551            if null_count == array.len() {
552                return None;
553            }
554
555            let iter = ArrayIter::new(array);
556            let sum = iter
557                .into_iter()
558                .fold(T::default_value(), |accumulator, value| {
559                    if let Some(value) = value {
560                        accumulator.add_wrapping(value)
561                    } else {
562                        accumulator
563                    }
564                });
565
566            Some(sum)
567        }
568        DataType::RunEndEncoded(run_ends, _) => match run_ends.data_type() {
569            DataType::Int16 => ree::sum_wrapping::<types::Int16Type, T>(&array),
570            DataType::Int32 => ree::sum_wrapping::<types::Int32Type, T>(&array),
571            DataType::Int64 => ree::sum_wrapping::<types::Int64Type, T>(&array),
572            _ => unreachable!(),
573        },
574        _ => sum::<T>(as_primitive_array(&array)),
575    }
576}
577
578/// Returns the sum of values in the array.
579///
580/// This detects overflow and returns an `Err` for that. For an non-overflow-checking variant,
581/// use [`sum_array`] instead.
582/// Additionally returns an `Err` on run-end-encoded arrays with a provided
583/// values type parameter that is incorrect.
584pub fn sum_array_checked<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
585    array: A,
586) -> Result<Option<T::Native>, ArrowError> {
587    match array.data_type() {
588        DataType::Dictionary(_, _) => {
589            let null_count = array.null_count();
590
591            if null_count == array.len() {
592                return Ok(None);
593            }
594
595            let iter = ArrayIter::new(array);
596            let sum = iter
597                .into_iter()
598                .try_fold(T::default_value(), |accumulator, value| {
599                    if let Some(value) = value {
600                        accumulator.add_checked(value)
601                    } else {
602                        Ok(accumulator)
603                    }
604                })?;
605
606            Ok(Some(sum))
607        }
608        DataType::RunEndEncoded(run_ends, _) => match run_ends.data_type() {
609            DataType::Int16 => ree::sum_checked::<types::Int16Type, T>(&array),
610            DataType::Int32 => ree::sum_checked::<types::Int32Type, T>(&array),
611            DataType::Int64 => ree::sum_checked::<types::Int64Type, T>(&array),
612            _ => unreachable!(),
613        },
614        _ => sum_checked::<T>(as_primitive_array(&array)),
615    }
616}
617
618// Logic for summing run-end-encoded arrays.
619mod ree {
620    use std::convert::Infallible;
621
622    use arrow_array::cast::AsArray;
623    use arrow_array::types::RunEndIndexType;
624    use arrow_array::{Array, ArrowNativeTypeOp, ArrowNumericType, PrimitiveArray, TypedRunArray};
625    use arrow_buffer::ArrowNativeType;
626    use arrow_schema::ArrowError;
627
628    /// Downcasts an array to a TypedRunArray.
629    fn downcast<'a, I: RunEndIndexType, V: ArrowNumericType>(
630        array: &'a dyn Array,
631    ) -> Option<TypedRunArray<'a, I, PrimitiveArray<V>>> {
632        let array = array.as_run_opt::<I>()?;
633        // We only support RunArray wrapping primitive types.
634        array.downcast::<PrimitiveArray<V>>()
635    }
636
637    /// Computes the sum (wrapping) of the array values.
638    pub(super) fn sum_wrapping<I: RunEndIndexType, V: ArrowNumericType>(
639        array: &dyn Array,
640    ) -> Option<V::Native> {
641        let ree = downcast::<I, V>(array)?;
642        let Ok(sum) = fold(ree, |acc, val, len| -> Result<V::Native, Infallible> {
643            Ok(acc.add_wrapping(val.mul_wrapping(V::Native::usize_as(len))))
644        });
645        sum
646    }
647
648    /// Computes the sum (erroring on overflow) of the array values.
649    pub(super) fn sum_checked<I: RunEndIndexType, V: ArrowNumericType>(
650        array: &dyn Array,
651    ) -> Result<Option<V::Native>, ArrowError> {
652        let Some(ree) = downcast::<I, V>(array) else {
653            return Err(ArrowError::InvalidArgumentError(
654                "Input run array values are not a PrimitiveArray".to_string(),
655            ));
656        };
657        fold(ree, |acc, val, len| -> Result<V::Native, ArrowError> {
658            let Some(len) = V::Native::from_usize(len) else {
659                return Err(ArrowError::ArithmeticOverflow(format!(
660                    "Cannot convert a run-end index ({:?}) to the value type ({})",
661                    len,
662                    std::any::type_name::<V::Native>()
663                )));
664            };
665            acc.add_checked(val.mul_checked(len)?)
666        })
667    }
668
669    /// Folds over the values in a run-end-encoded array.
670    fn fold<'a, I: RunEndIndexType, V: ArrowNumericType, F, E>(
671        array: TypedRunArray<'a, I, PrimitiveArray<V>>,
672        mut f: F,
673    ) -> Result<Option<V::Native>, E>
674    where
675        F: FnMut(V::Native, V::Native, usize) -> Result<V::Native, E>,
676    {
677        let run_ends = array.run_ends();
678        let logical_start = run_ends.offset();
679        let logical_end = run_ends.offset() + run_ends.len();
680        let run_ends = run_ends.sliced_values();
681
682        let values_slice = array.run_array().values_slice();
683        let values = values_slice
684            .as_any()
685            .downcast_ref::<PrimitiveArray<V>>()
686            // Safety: we know the values array is PrimitiveArray<V>.
687            .unwrap();
688
689        let mut prev_end = 0;
690        let mut acc = V::Native::ZERO;
691        let mut has_non_null_value = false;
692
693        for (run_end, value) in run_ends.zip(values) {
694            let current_run_end = run_end.as_usize().clamp(logical_start, logical_end);
695            let run_length = current_run_end - prev_end;
696
697            if let Some(value) = value {
698                has_non_null_value = true;
699                acc = f(acc, value, run_length)?;
700            }
701
702            prev_end = current_run_end;
703            if current_run_end == logical_end {
704                break;
705            }
706        }
707
708        Ok(if has_non_null_value { Some(acc) } else { None })
709    }
710}
711
712/// Returns the min of values in the array of `ArrowNumericType` type, or dictionary
713/// array with value of `ArrowNumericType` type.
714pub fn min_array<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
715    array: A,
716) -> Option<T::Native> {
717    min_max_array_helper::<T, A, _, _>(array, |a, b| a.is_gt(*b), min)
718}
719
720/// Returns the max of values in the array of `ArrowNumericType` type, or dictionary
721/// array with value of `ArrowNumericType` type.
722pub fn max_array<T: ArrowNumericType, A: ArrayAccessor<Item = T::Native>>(
723    array: A,
724) -> Option<T::Native> {
725    min_max_array_helper::<T, A, _, _>(array, |a, b| a.is_lt(*b), max)
726}
727
728fn min_max_array_helper<T, A: ArrayAccessor<Item = T::Native>, F, M>(
729    array: A,
730    cmp: F,
731    m: M,
732) -> Option<T::Native>
733where
734    T: ArrowNumericType,
735    F: Fn(&T::Native, &T::Native) -> bool,
736    M: Fn(&PrimitiveArray<T>) -> Option<T::Native>,
737{
738    match array.data_type() {
739        DataType::Dictionary(_, _) => min_max_helper::<T::Native, _, _>(array, cmp),
740        DataType::RunEndEncoded(run_ends, _) => {
741            // We can directly perform min/max on the values child array, as any
742            // run must have non-zero length.
743            let array: &dyn Array = &array;
744            let values = match run_ends.data_type() {
745                DataType::Int16 => array.as_run_opt::<types::Int16Type>()?.values_slice(),
746                DataType::Int32 => array.as_run_opt::<types::Int32Type>()?.values_slice(),
747                DataType::Int64 => array.as_run_opt::<types::Int64Type>()?.values_slice(),
748                _ => return None,
749            };
750            // We only support RunArray wrapping primitive types.
751            let values = values.as_any().downcast_ref::<PrimitiveArray<T>>()?;
752            m(values)
753        }
754        _ => m(as_primitive_array(&array)),
755    }
756}
757
758macro_rules! bit_operation {
759    ($NAME:ident, $OP:ident, $NATIVE:ident, $DEFAULT:expr, $DOC:expr) => {
760        #[doc = $DOC]
761        ///
762        /// Returns `None` if the array is empty or only contains null values.
763        pub fn $NAME<T>(array: &PrimitiveArray<T>) -> Option<T::Native>
764        where
765            T: ArrowNumericType,
766            T::Native: $NATIVE<Output = T::Native> + ArrowNativeTypeOp,
767        {
768            let default;
769            if $DEFAULT == -1 {
770                default = T::Native::ONE.neg_wrapping();
771            } else {
772                default = T::default_value();
773            }
774
775            let null_count = array.null_count();
776
777            if null_count == array.len() {
778                return None;
779            }
780
781            let data: &[T::Native] = array.values();
782
783            match array.nulls() {
784                None => {
785                    let result = data
786                        .iter()
787                        .fold(default, |accumulator, value| accumulator.$OP(*value));
788
789                    Some(result)
790                }
791                Some(nulls) => {
792                    let mut result = default;
793                    let data_chunks = data.chunks_exact(64);
794                    let remainder = data_chunks.remainder();
795
796                    let bit_chunks = nulls.inner().bit_chunks();
797                    data_chunks
798                        .zip(bit_chunks.iter())
799                        .for_each(|(chunk, mask)| {
800                            // index_mask has value 1 << i in the loop
801                            let mut index_mask = 1;
802                            chunk.iter().for_each(|value| {
803                                if (mask & index_mask) != 0 {
804                                    result = result.$OP(*value);
805                                }
806                                index_mask <<= 1;
807                            });
808                        });
809
810                    let remainder_bits = bit_chunks.remainder_bits();
811
812                    remainder.iter().enumerate().for_each(|(i, value)| {
813                        if remainder_bits & (1 << i) != 0 {
814                            result = result.$OP(*value);
815                        }
816                    });
817
818                    Some(result)
819                }
820            }
821        }
822    };
823}
824
825bit_operation!(
826    bit_and,
827    bitand,
828    BitAnd,
829    -1,
830    "Returns the bitwise and of all non-null input values."
831);
832bit_operation!(
833    bit_or,
834    bitor,
835    BitOr,
836    0,
837    "Returns the bitwise or of all non-null input values."
838);
839bit_operation!(
840    bit_xor,
841    bitxor,
842    BitXor,
843    0,
844    "Returns the bitwise xor of all non-null input values."
845);
846
847/// Returns true if all non-null input values are true, otherwise false.
848///
849/// Returns `None` if the array is empty or only contains null values.
850pub fn bool_and(array: &BooleanArray) -> Option<bool> {
851    min_boolean(array)
852}
853
854/// Returns true if any non-null input value is true, otherwise false.
855///
856/// Returns `None` if the array is empty or only contains null values.
857pub fn bool_or(array: &BooleanArray) -> Option<bool> {
858    max_boolean(array)
859}
860
861/// Returns the sum of values in the primitive array.
862///
863/// Returns `Ok(None)` if the array is empty or only contains null values.
864///
865/// This detects overflow and returns an `Err` for that. For an non-overflow-checking variant,
866/// use [`sum`] instead.
867pub fn sum_checked<T: ArrowNumericType>(
868    array: &PrimitiveArray<T>,
869) -> Result<Option<T::Native>, ArrowError> {
870    let null_count = array.null_count();
871
872    if null_count == array.len() {
873        return Ok(None);
874    }
875
876    let data: &[T::Native] = array.values();
877
878    match array.nulls() {
879        None => {
880            let sum = data
881                .iter()
882                .try_fold(T::default_value(), |accumulator, value| {
883                    accumulator.add_checked(*value)
884                })?;
885
886            Ok(Some(sum))
887        }
888        Some(nulls) => {
889            let mut sum = T::default_value();
890
891            try_for_each_valid_idx(
892                nulls.len(),
893                nulls.offset(),
894                nulls.null_count(),
895                Some(nulls.validity()),
896                |idx| {
897                    unsafe { sum = sum.add_checked(array.value_unchecked(idx))? };
898                    Ok::<_, ArrowError>(())
899                },
900            )?;
901
902            Ok(Some(sum))
903        }
904    }
905}
906
907/// Returns the sum of values in the primitive array.
908///
909/// Returns `None` if the array is empty or only contains null values.
910///
911/// This doesn't detect overflow in release mode by default. Once overflowing, the result will
912/// wrap around. For an overflow-checking variant, use [`sum_checked`] instead.
913pub fn sum<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
914    aggregate::<T::Native, T, SumAccumulator<T::Native>>(array)
915}
916
917/// Returns the minimum value in the array, according to the natural order.
918/// For floating point arrays any NaN values are considered to be greater than any other non-null value
919///
920/// # Example
921/// ```rust
922/// # use arrow_array::Int32Array;
923/// # use arrow_arith::aggregate::min;
924/// let array = Int32Array::from(vec![8, 2, 4]);
925/// let result = min(&array);
926/// assert_eq!(result, Some(2));
927/// ```
928pub fn min<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
929    aggregate::<T::Native, T, MinAccumulator<T::Native>>(array)
930}
931
932/// Returns the maximum 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::max;
939/// let array = Int32Array::from(vec![4, 8, 2]);
940/// let result = max(&array);
941/// assert_eq!(result, Some(8));
942/// ```
943pub fn max<T: ArrowNumericType>(array: &PrimitiveArray<T>) -> Option<T::Native> {
944    aggregate::<T::Native, T, MaxAccumulator<T::Native>>(array)
945}
946
947#[cfg(test)]
948mod tests {
949    use super::*;
950    use arrow_array::types::*;
951    use builder::BooleanBuilder;
952    use std::sync::Arc;
953
954    #[test]
955    fn test_primitive_array_sum() {
956        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
957        assert_eq!(15, sum(&a).unwrap());
958    }
959
960    #[test]
961    fn test_primitive_array_float_sum() {
962        let a = Float64Array::from(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
963        assert_eq!(16.5, sum(&a).unwrap());
964    }
965
966    #[test]
967    fn test_primitive_array_sum_with_nulls() {
968        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
969        assert_eq!(10, sum(&a).unwrap());
970    }
971
972    #[test]
973    fn test_primitive_array_sum_all_nulls() {
974        let a = Int32Array::from(vec![None, None, None]);
975        assert_eq!(None, sum(&a));
976    }
977
978    #[test]
979    fn test_primitive_array_sum_large_float_64() {
980        let c = Float64Array::new((1..=100).map(|x| x as f64).collect(), None);
981        assert_eq!(Some((1..=100).sum::<i64>() as f64), sum(&c));
982
983        // create an array that actually has non-zero values at the invalid indices
984        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
985        let c = Float64Array::new((1..=100).map(|x| x as f64).collect(), Some(validity));
986
987        assert_eq!(
988            Some((1..=100).filter(|i| i % 3 == 0).sum::<i64>() as f64),
989            sum(&c)
990        );
991    }
992
993    #[test]
994    fn test_primitive_array_sum_large_float_32() {
995        let c = Float32Array::new((1..=100).map(|x| x as f32).collect(), None);
996        assert_eq!(Some((1..=100).sum::<i64>() as f32), sum(&c));
997
998        // create an array that actually has non-zero values at the invalid indices
999        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1000        let c = Float32Array::new((1..=100).map(|x| x as f32).collect(), Some(validity));
1001
1002        assert_eq!(
1003            Some((1..=100).filter(|i| i % 3 == 0).sum::<i64>() as f32),
1004            sum(&c)
1005        );
1006    }
1007
1008    #[test]
1009    fn test_primitive_array_sum_large_64() {
1010        let c = Int64Array::new((1..=100).collect(), None);
1011        assert_eq!(Some((1..=100).sum()), sum(&c));
1012
1013        // create an array that actually has non-zero values at the invalid indices
1014        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1015        let c = Int64Array::new((1..=100).collect(), Some(validity));
1016
1017        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1018    }
1019
1020    #[test]
1021    fn test_primitive_array_sum_large_32() {
1022        let c = Int32Array::new((1..=100).collect(), None);
1023        assert_eq!(Some((1..=100).sum()), sum(&c));
1024
1025        // create an array that actually has non-zero values at the invalid indices
1026        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1027        let c = Int32Array::new((1..=100).collect(), Some(validity));
1028        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1029    }
1030
1031    #[test]
1032    fn test_primitive_array_sum_large_16() {
1033        let c = Int16Array::new((1..=100).collect(), None);
1034        assert_eq!(Some((1..=100).sum()), sum(&c));
1035
1036        // create an array that actually has non-zero values at the invalid indices
1037        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1038        let c = Int16Array::new((1..=100).collect(), Some(validity));
1039        assert_eq!(Some((1..=100).filter(|i| i % 3 == 0).sum()), sum(&c));
1040    }
1041
1042    #[test]
1043    fn test_primitive_array_sum_large_8() {
1044        let c = UInt8Array::new((1..=100).collect(), None);
1045        assert_eq!(
1046            Some((1..=100).fold(0_u8, |a, x| a.wrapping_add(x))),
1047            sum(&c)
1048        );
1049
1050        // create an array that actually has non-zero values at the invalid indices
1051        let validity = NullBuffer::new((1..=100).map(|x| x % 3 == 0).collect());
1052        let c = UInt8Array::new((1..=100).collect(), Some(validity));
1053        assert_eq!(
1054            Some(
1055                (1..=100)
1056                    .filter(|i| i % 3 == 0)
1057                    .fold(0_u8, |a, x| a.wrapping_add(x))
1058            ),
1059            sum(&c)
1060        );
1061    }
1062
1063    #[test]
1064    fn test_primitive_array_bit_and() {
1065        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1066        assert_eq!(0, bit_and(&a).unwrap());
1067    }
1068
1069    #[test]
1070    fn test_primitive_array_bit_and_with_nulls() {
1071        let a = Int32Array::from(vec![None, Some(2), Some(3), None, None]);
1072        assert_eq!(2, bit_and(&a).unwrap());
1073    }
1074
1075    #[test]
1076    fn test_primitive_array_bit_and_all_nulls() {
1077        let a = Int32Array::from(vec![None, None, None]);
1078        assert_eq!(None, bit_and(&a));
1079    }
1080
1081    #[test]
1082    fn test_primitive_array_bit_or() {
1083        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1084        assert_eq!(7, bit_or(&a).unwrap());
1085    }
1086
1087    #[test]
1088    fn test_primitive_array_bit_or_with_nulls() {
1089        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1090        assert_eq!(7, bit_or(&a).unwrap());
1091    }
1092
1093    #[test]
1094    fn test_primitive_array_bit_or_all_nulls() {
1095        let a = Int32Array::from(vec![None, None, None]);
1096        assert_eq!(None, bit_or(&a));
1097    }
1098
1099    #[test]
1100    fn test_primitive_array_bit_xor() {
1101        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1102        assert_eq!(1, bit_xor(&a).unwrap());
1103    }
1104
1105    #[test]
1106    fn test_primitive_array_bit_xor_with_nulls() {
1107        let a = Int32Array::from(vec![None, Some(2), Some(3), None, Some(5)]);
1108        assert_eq!(4, bit_xor(&a).unwrap());
1109    }
1110
1111    #[test]
1112    fn test_primitive_array_bit_xor_all_nulls() {
1113        let a = Int32Array::from(vec![None, None, None]);
1114        assert_eq!(None, bit_xor(&a));
1115    }
1116
1117    #[test]
1118    fn test_primitive_array_bool_and() {
1119        let a = BooleanArray::from(vec![true, false, true, false, true]);
1120        assert!(!bool_and(&a).unwrap());
1121    }
1122
1123    #[test]
1124    fn test_primitive_array_bool_and_with_nulls() {
1125        let a = BooleanArray::from(vec![None, Some(true), Some(true), None, Some(true)]);
1126        assert!(bool_and(&a).unwrap());
1127    }
1128
1129    #[test]
1130    fn test_primitive_array_bool_and_all_nulls() {
1131        let a = BooleanArray::from(vec![None, None, None]);
1132        assert_eq!(None, bool_and(&a));
1133    }
1134
1135    #[test]
1136    fn test_primitive_array_bool_or() {
1137        let a = BooleanArray::from(vec![true, false, true, false, true]);
1138        assert!(bool_or(&a).unwrap());
1139    }
1140
1141    #[test]
1142    fn test_primitive_array_bool_or_with_nulls() {
1143        let a = BooleanArray::from(vec![None, Some(false), Some(false), None, Some(false)]);
1144        assert!(!bool_or(&a).unwrap());
1145    }
1146
1147    #[test]
1148    fn test_primitive_array_bool_or_all_nulls() {
1149        let a = BooleanArray::from(vec![None, None, None]);
1150        assert_eq!(None, bool_or(&a));
1151    }
1152
1153    #[test]
1154    fn test_primitive_array_min_max() {
1155        let a = Int32Array::from(vec![5, 6, 7, 8, 9]);
1156        assert_eq!(5, min(&a).unwrap());
1157        assert_eq!(9, max(&a).unwrap());
1158    }
1159
1160    #[test]
1161    fn test_primitive_array_min_max_with_nulls() {
1162        let a = Int32Array::from(vec![Some(5), None, None, Some(8), Some(9)]);
1163        assert_eq!(5, min(&a).unwrap());
1164        assert_eq!(9, max(&a).unwrap());
1165    }
1166
1167    #[test]
1168    fn test_primitive_min_max_1() {
1169        let a = Int32Array::from(vec![None, None, Some(5), Some(2)]);
1170        assert_eq!(Some(2), min(&a));
1171        assert_eq!(Some(5), max(&a));
1172    }
1173
1174    #[test]
1175    fn test_primitive_min_max_float_large_nonnull_array() {
1176        let a: Float64Array = (0..256).map(|i| Some((i + 1) as f64)).collect();
1177        // min/max are on boundaries of chunked data
1178        assert_eq!(Some(1.0), min(&a));
1179        assert_eq!(Some(256.0), max(&a));
1180
1181        // max is last value in remainder after chunking
1182        let a: Float64Array = (0..255).map(|i| Some((i + 1) as f64)).collect();
1183        assert_eq!(Some(255.0), max(&a));
1184
1185        // max is first value in remainder after chunking
1186        let a: Float64Array = (0..257).map(|i| Some((i + 1) as f64)).collect();
1187        assert_eq!(Some(257.0), max(&a));
1188    }
1189
1190    #[test]
1191    fn test_primitive_min_max_float_large_nullable_array() {
1192        let a: Float64Array = (0..256)
1193            .map(|i| {
1194                if (i + 1) % 3 == 0 {
1195                    None
1196                } else {
1197                    Some((i + 1) as f64)
1198                }
1199            })
1200            .collect();
1201        // min/max are on boundaries of chunked data
1202        assert_eq!(Some(1.0), min(&a));
1203        assert_eq!(Some(256.0), max(&a));
1204
1205        let a: Float64Array = (0..256)
1206            .map(|i| {
1207                if i == 0 || i == 255 {
1208                    None
1209                } else {
1210                    Some((i + 1) as f64)
1211                }
1212            })
1213            .collect();
1214        // boundaries of chunked data are null
1215        assert_eq!(Some(2.0), min(&a));
1216        assert_eq!(Some(255.0), max(&a));
1217
1218        let a: Float64Array = (0..256)
1219            .map(|i| if i != 100 { None } else { Some((i) as f64) })
1220            .collect();
1221        // a single non-null value somewhere in the middle
1222        assert_eq!(Some(100.0), min(&a));
1223        assert_eq!(Some(100.0), max(&a));
1224
1225        // max is last value in remainder after chunking
1226        let a: Float64Array = (0..255).map(|i| Some((i + 1) as f64)).collect();
1227        assert_eq!(Some(255.0), max(&a));
1228
1229        // max is first value in remainder after chunking
1230        let a: Float64Array = (0..257).map(|i| Some((i + 1) as f64)).collect();
1231        assert_eq!(Some(257.0), max(&a));
1232    }
1233
1234    #[test]
1235    fn test_primitive_min_max_float_edge_cases() {
1236        let a: Float64Array = (0..100).map(|_| Some(f64::NEG_INFINITY)).collect();
1237        assert_eq!(Some(f64::NEG_INFINITY), min(&a));
1238        assert_eq!(Some(f64::NEG_INFINITY), max(&a));
1239
1240        let a: Float64Array = (0..100).map(|_| Some(f64::MIN)).collect();
1241        assert_eq!(Some(f64::MIN), min(&a));
1242        assert_eq!(Some(f64::MIN), max(&a));
1243
1244        let a: Float64Array = (0..100).map(|_| Some(f64::MAX)).collect();
1245        assert_eq!(Some(f64::MAX), min(&a));
1246        assert_eq!(Some(f64::MAX), max(&a));
1247
1248        let a: Float64Array = (0..100).map(|_| Some(f64::INFINITY)).collect();
1249        assert_eq!(Some(f64::INFINITY), min(&a));
1250        assert_eq!(Some(f64::INFINITY), max(&a));
1251    }
1252
1253    #[test]
1254    fn test_primitive_min_max_float_all_nans_non_null() {
1255        let a: Float64Array = (0..100).map(|_| Some(f64::NAN)).collect();
1256        assert!(max(&a).unwrap().is_nan());
1257        assert!(min(&a).unwrap().is_nan());
1258    }
1259
1260    #[test]
1261    fn test_primitive_min_max_float_negative_nan() {
1262        let a: Float64Array =
1263            Float64Array::from(vec![f64::NEG_INFINITY, f64::NAN, f64::INFINITY, -f64::NAN]);
1264        let max = max(&a).unwrap();
1265        let min = min(&a).unwrap();
1266        assert!(max.is_nan());
1267        assert!(max.is_sign_positive());
1268
1269        assert!(min.is_nan());
1270        assert!(min.is_sign_negative());
1271    }
1272
1273    #[test]
1274    fn test_primitive_min_max_float_first_nan_nonnull() {
1275        let a: Float64Array = (0..100)
1276            .map(|i| {
1277                if i == 0 {
1278                    Some(f64::NAN)
1279                } else {
1280                    Some(i as f64)
1281                }
1282            })
1283            .collect();
1284        assert_eq!(Some(1.0), min(&a));
1285        assert!(max(&a).unwrap().is_nan());
1286    }
1287
1288    #[test]
1289    fn test_primitive_min_max_float_last_nan_nonnull() {
1290        let a: Float64Array = (0..100)
1291            .map(|i| {
1292                if i == 99 {
1293                    Some(f64::NAN)
1294                } else {
1295                    Some((i + 1) as f64)
1296                }
1297            })
1298            .collect();
1299        assert_eq!(Some(1.0), min(&a));
1300        assert!(max(&a).unwrap().is_nan());
1301    }
1302
1303    #[test]
1304    fn test_primitive_min_max_float_first_nan_nullable() {
1305        let a: Float64Array = (0..100)
1306            .map(|i| {
1307                if i == 0 {
1308                    Some(f64::NAN)
1309                } else if i % 2 == 0 {
1310                    None
1311                } else {
1312                    Some(i as f64)
1313                }
1314            })
1315            .collect();
1316        assert_eq!(Some(1.0), min(&a));
1317        assert!(max(&a).unwrap().is_nan());
1318    }
1319
1320    #[test]
1321    fn test_primitive_min_max_float_last_nan_nullable() {
1322        let a: Float64Array = (0..100)
1323            .map(|i| {
1324                if i == 99 {
1325                    Some(f64::NAN)
1326                } else if i % 2 == 0 {
1327                    None
1328                } else {
1329                    Some(i as f64)
1330                }
1331            })
1332            .collect();
1333        assert_eq!(Some(1.0), min(&a));
1334        assert!(max(&a).unwrap().is_nan());
1335    }
1336
1337    #[test]
1338    fn test_primitive_min_max_float_inf_and_nans() {
1339        let a: Float64Array = (0..100)
1340            .map(|i| {
1341                let x = match i % 10 {
1342                    0 => f64::NEG_INFINITY,
1343                    1 => f64::MIN,
1344                    2 => f64::MAX,
1345                    4 => f64::INFINITY,
1346                    5 => f64::NAN,
1347                    _ => i as f64,
1348                };
1349                Some(x)
1350            })
1351            .collect();
1352        assert_eq!(Some(f64::NEG_INFINITY), min(&a));
1353        assert!(max(&a).unwrap().is_nan());
1354    }
1355
1356    fn pad_inputs_and_test_fixed_size_binary(
1357        input: Vec<Option<&[u8]>>,
1358        expected_min: Option<&[u8]>,
1359        expected_max: Option<&[u8]>,
1360    ) {
1361        fn pad_slice(slice: &[u8], len: usize) -> Vec<u8> {
1362            let mut padded = vec![0; len];
1363            padded[..slice.len()].copy_from_slice(slice);
1364            padded
1365        }
1366
1367        let max_len = input
1368            .iter()
1369            .filter_map(|x| x.as_ref().map(|b| b.len()))
1370            .max()
1371            .unwrap_or(0);
1372        let padded_input = input
1373            .iter()
1374            .map(|x| x.as_ref().map(|b| pad_slice(b, max_len)));
1375        let input_arr =
1376            FixedSizeBinaryArray::try_from_sparse_iter_with_size(padded_input, max_len as i32)
1377                .unwrap();
1378        let padded_expected_min = expected_min.map(|b| pad_slice(b, max_len));
1379        let padded_expected_max = expected_max.map(|b| pad_slice(b, max_len));
1380
1381        assert_eq!(
1382            padded_expected_min.as_deref(),
1383            min_fixed_size_binary(&input_arr)
1384        );
1385        assert_eq!(
1386            padded_expected_max.as_deref(),
1387            max_fixed_size_binary(&input_arr)
1388        );
1389    }
1390
1391    macro_rules! test_binary {
1392        ($NAME:ident, $ARRAY:expr, $EXPECTED_MIN:expr, $EXPECTED_MAX: expr) => {
1393            #[test]
1394            fn $NAME() {
1395                let binary = BinaryArray::from($ARRAY);
1396                assert_eq!($EXPECTED_MIN, min_binary(&binary));
1397                assert_eq!($EXPECTED_MAX, max_binary(&binary));
1398
1399                let large_binary = LargeBinaryArray::from($ARRAY);
1400                assert_eq!($EXPECTED_MIN, min_binary(&large_binary));
1401                assert_eq!($EXPECTED_MAX, max_binary(&large_binary));
1402
1403                let binary_view = BinaryViewArray::from($ARRAY);
1404                assert_eq!($EXPECTED_MIN, min_binary_view(&binary_view));
1405                assert_eq!($EXPECTED_MAX, max_binary_view(&binary_view));
1406
1407                pad_inputs_and_test_fixed_size_binary($ARRAY, $EXPECTED_MIN, $EXPECTED_MAX);
1408            }
1409        };
1410    }
1411
1412    test_binary!(
1413        test_binary_min_max_with_nulls,
1414        vec![
1415            Some("b01234567890123".as_bytes()), // long bytes
1416            None,
1417            None,
1418            Some(b"a"),
1419            Some(b"c"),
1420            Some(b"abcdedfg0123456"),
1421        ],
1422        Some("a".as_bytes()),
1423        Some("c".as_bytes())
1424    );
1425
1426    test_binary!(
1427        test_binary_min_max_no_null,
1428        vec![
1429            Some("b".as_bytes()),
1430            Some(b"abcdefghijklmnopqrst"), // long bytes
1431            Some(b"c"),
1432            Some(b"b01234567890123"), // long bytes for view types
1433        ],
1434        Some("abcdefghijklmnopqrst".as_bytes()),
1435        Some("c".as_bytes())
1436    );
1437
1438    test_binary!(test_binary_min_max_all_nulls, vec![None, None], None, None);
1439
1440    test_binary!(
1441        test_binary_min_max_1,
1442        vec![
1443            None,
1444            Some("b01234567890123435".as_bytes()), // long bytes for view types
1445            None,
1446            Some(b"b0123xxxxxxxxxxx"),
1447            Some(b"a")
1448        ],
1449        Some("a".as_bytes()),
1450        Some("b0123xxxxxxxxxxx".as_bytes())
1451    );
1452
1453    macro_rules! test_string {
1454        ($NAME:ident, $ARRAY:expr, $EXPECTED_MIN:expr, $EXPECTED_MAX: expr) => {
1455            #[test]
1456            fn $NAME() {
1457                let string = StringArray::from($ARRAY);
1458                assert_eq!($EXPECTED_MIN, min_string(&string));
1459                assert_eq!($EXPECTED_MAX, max_string(&string));
1460
1461                let large_string = LargeStringArray::from($ARRAY);
1462                assert_eq!($EXPECTED_MIN, min_string(&large_string));
1463                assert_eq!($EXPECTED_MAX, max_string(&large_string));
1464
1465                let string_view = StringViewArray::from($ARRAY);
1466                assert_eq!($EXPECTED_MIN, min_string_view(&string_view));
1467                assert_eq!($EXPECTED_MAX, max_string_view(&string_view));
1468            }
1469        };
1470    }
1471
1472    test_string!(
1473        test_string_min_max_with_nulls,
1474        vec![
1475            Some("b012345678901234"), // long bytes for view types
1476            None,
1477            None,
1478            Some("a"),
1479            Some("c"),
1480            Some("b0123xxxxxxxxxxx")
1481        ],
1482        Some("a"),
1483        Some("c")
1484    );
1485
1486    test_string!(
1487        test_string_min_max_no_null,
1488        vec![
1489            Some("b"),
1490            Some("b012345678901234"), // long bytes for view types
1491            Some("a"),
1492            Some("b012xxxxxxxxxxxx")
1493        ],
1494        Some("a"),
1495        Some("b012xxxxxxxxxxxx")
1496    );
1497
1498    test_string!(
1499        test_string_min_max_all_nulls,
1500        Vec::<Option<&str>>::from_iter([None, None]),
1501        None,
1502        None
1503    );
1504
1505    test_string!(
1506        test_string_min_max_1,
1507        vec![
1508            None,
1509            Some("c12345678901234"), // long bytes for view types
1510            None,
1511            Some("b"),
1512            Some("c1234xxxxxxxxxx")
1513        ],
1514        Some("b"),
1515        Some("c1234xxxxxxxxxx")
1516    );
1517
1518    test_string!(
1519        test_string_min_max_empty,
1520        Vec::<Option<&str>>::new(),
1521        None,
1522        None
1523    );
1524
1525    #[test]
1526    fn test_boolean_min_max_empty() {
1527        let a = BooleanArray::from(vec![] as Vec<Option<bool>>);
1528        assert_eq!(None, min_boolean(&a));
1529        assert_eq!(None, max_boolean(&a));
1530    }
1531
1532    #[test]
1533    fn test_boolean_min_max_all_null() {
1534        let a = BooleanArray::from(vec![None, None]);
1535        assert_eq!(None, min_boolean(&a));
1536        assert_eq!(None, max_boolean(&a));
1537    }
1538
1539    #[test]
1540    fn test_boolean_min_max_no_null() {
1541        let a = BooleanArray::from(vec![Some(true), Some(false), Some(true)]);
1542        assert_eq!(Some(false), min_boolean(&a));
1543        assert_eq!(Some(true), max_boolean(&a));
1544    }
1545
1546    #[test]
1547    fn test_boolean_min_max() {
1548        let a = BooleanArray::from(vec![Some(true), Some(true), None, Some(false), None]);
1549        assert_eq!(Some(false), min_boolean(&a));
1550        assert_eq!(Some(true), max_boolean(&a));
1551
1552        let a = BooleanArray::from(vec![None, Some(true), None, Some(false), None]);
1553        assert_eq!(Some(false), min_boolean(&a));
1554        assert_eq!(Some(true), max_boolean(&a));
1555
1556        let a = BooleanArray::from(vec![Some(false), Some(true), None, Some(false), None]);
1557        assert_eq!(Some(false), min_boolean(&a));
1558        assert_eq!(Some(true), max_boolean(&a));
1559
1560        let a = BooleanArray::from(vec![Some(true), None]);
1561        assert_eq!(Some(true), min_boolean(&a));
1562        assert_eq!(Some(true), max_boolean(&a));
1563
1564        let a = BooleanArray::from(vec![Some(false), None]);
1565        assert_eq!(Some(false), min_boolean(&a));
1566        assert_eq!(Some(false), max_boolean(&a));
1567
1568        let a = BooleanArray::from(vec![Some(true)]);
1569        assert_eq!(Some(true), min_boolean(&a));
1570        assert_eq!(Some(true), max_boolean(&a));
1571
1572        let a = BooleanArray::from(vec![Some(false)]);
1573        assert_eq!(Some(false), min_boolean(&a));
1574        assert_eq!(Some(false), max_boolean(&a));
1575    }
1576
1577    #[test]
1578    fn test_boolean_min_max_smaller() {
1579        let a = BooleanArray::from(vec![Some(false)]);
1580        assert_eq!(Some(false), min_boolean(&a));
1581        assert_eq!(Some(false), max_boolean(&a));
1582
1583        let a = BooleanArray::from(vec![None, Some(false)]);
1584        assert_eq!(Some(false), min_boolean(&a));
1585        assert_eq!(Some(false), max_boolean(&a));
1586
1587        let a = BooleanArray::from(vec![None, Some(true)]);
1588        assert_eq!(Some(true), min_boolean(&a));
1589        assert_eq!(Some(true), max_boolean(&a));
1590
1591        let a = BooleanArray::from(vec![Some(true)]);
1592        assert_eq!(Some(true), min_boolean(&a));
1593        assert_eq!(Some(true), max_boolean(&a));
1594    }
1595
1596    #[test]
1597    fn test_boolean_min_max_64_true_64_false() {
1598        let mut no_nulls = BooleanBuilder::new();
1599        no_nulls.append_slice(&[true; 64]);
1600        no_nulls.append_slice(&[false; 64]);
1601        let no_nulls = no_nulls.finish();
1602
1603        assert_eq!(Some(false), min_boolean(&no_nulls));
1604        assert_eq!(Some(true), max_boolean(&no_nulls));
1605
1606        let mut with_nulls = BooleanBuilder::new();
1607        with_nulls.append_slice(&[true; 31]);
1608        with_nulls.append_null();
1609        with_nulls.append_slice(&[true; 32]);
1610        with_nulls.append_slice(&[false; 1]);
1611        with_nulls.append_nulls(63);
1612        let with_nulls = with_nulls.finish();
1613
1614        assert_eq!(Some(false), min_boolean(&with_nulls));
1615        assert_eq!(Some(true), max_boolean(&with_nulls));
1616    }
1617
1618    #[test]
1619    fn test_boolean_min_max_64_false_64_true() {
1620        let mut no_nulls = BooleanBuilder::new();
1621        no_nulls.append_slice(&[false; 64]);
1622        no_nulls.append_slice(&[true; 64]);
1623        let no_nulls = no_nulls.finish();
1624
1625        assert_eq!(Some(false), min_boolean(&no_nulls));
1626        assert_eq!(Some(true), max_boolean(&no_nulls));
1627
1628        let mut with_nulls = BooleanBuilder::new();
1629        with_nulls.append_slice(&[false; 31]);
1630        with_nulls.append_null();
1631        with_nulls.append_slice(&[false; 32]);
1632        with_nulls.append_slice(&[true; 1]);
1633        with_nulls.append_nulls(63);
1634        let with_nulls = with_nulls.finish();
1635
1636        assert_eq!(Some(false), min_boolean(&with_nulls));
1637        assert_eq!(Some(true), max_boolean(&with_nulls));
1638    }
1639
1640    #[test]
1641    fn test_boolean_min_max_96_true() {
1642        let mut no_nulls = BooleanBuilder::new();
1643        no_nulls.append_slice(&[true; 96]);
1644        let no_nulls = no_nulls.finish();
1645
1646        assert_eq!(Some(true), 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(&[true; 31]);
1651        with_nulls.append_null();
1652        with_nulls.append_slice(&[true; 32]);
1653        with_nulls.append_slice(&[true; 31]);
1654        with_nulls.append_null();
1655        let with_nulls = with_nulls.finish();
1656
1657        assert_eq!(Some(true), min_boolean(&with_nulls));
1658        assert_eq!(Some(true), max_boolean(&with_nulls));
1659    }
1660
1661    #[test]
1662    fn test_boolean_min_max_96_false() {
1663        let mut no_nulls = BooleanBuilder::new();
1664        no_nulls.append_slice(&[false; 96]);
1665        let no_nulls = no_nulls.finish();
1666
1667        assert_eq!(Some(false), min_boolean(&no_nulls));
1668        assert_eq!(Some(false), max_boolean(&no_nulls));
1669
1670        let mut with_nulls = BooleanBuilder::new();
1671        with_nulls.append_slice(&[false; 31]);
1672        with_nulls.append_null();
1673        with_nulls.append_slice(&[false; 32]);
1674        with_nulls.append_slice(&[false; 31]);
1675        with_nulls.append_null();
1676        let with_nulls = with_nulls.finish();
1677
1678        assert_eq!(Some(false), min_boolean(&with_nulls));
1679        assert_eq!(Some(false), max_boolean(&with_nulls));
1680    }
1681
1682    #[test]
1683    fn test_sum_dyn() {
1684        let values = Int8Array::from_iter_values([10_i8, 11, 12, 13, 14, 15, 16, 17]);
1685        let values = Arc::new(values) as ArrayRef;
1686        let keys = Int8Array::from_iter_values([2_i8, 3, 4]);
1687
1688        let dict_array = DictionaryArray::new(keys, values.clone());
1689        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1690        assert_eq!(39, sum_array::<Int8Type, _>(array).unwrap());
1691
1692        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1693        assert_eq!(15, sum_array::<Int32Type, _>(&a).unwrap());
1694
1695        let keys = Int8Array::from(vec![Some(2_i8), None, Some(4)]);
1696        let dict_array = DictionaryArray::new(keys, values.clone());
1697        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1698        assert_eq!(26, sum_array::<Int8Type, _>(array).unwrap());
1699
1700        let keys = Int8Array::from(vec![None, None, None]);
1701        let dict_array = DictionaryArray::new(keys, values.clone());
1702        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1703        assert!(sum_array::<Int8Type, _>(array).is_none());
1704    }
1705
1706    #[test]
1707    fn test_max_min_dyn() {
1708        let values = Int8Array::from_iter_values([10_i8, 11, 12, 13, 14, 15, 16, 17]);
1709        let keys = Int8Array::from_iter_values([2_i8, 3, 4]);
1710        let values = Arc::new(values) as ArrayRef;
1711
1712        let dict_array = DictionaryArray::new(keys, values.clone());
1713        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1714        assert_eq!(14, max_array::<Int8Type, _>(array).unwrap());
1715
1716        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1717        assert_eq!(12, min_array::<Int8Type, _>(array).unwrap());
1718
1719        let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
1720        assert_eq!(5, max_array::<Int32Type, _>(&a).unwrap());
1721        assert_eq!(1, min_array::<Int32Type, _>(&a).unwrap());
1722
1723        let keys = Int8Array::from(vec![Some(2_i8), None, Some(7)]);
1724        let dict_array = DictionaryArray::new(keys, values.clone());
1725        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1726        assert_eq!(17, max_array::<Int8Type, _>(array).unwrap());
1727        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1728        assert_eq!(12, min_array::<Int8Type, _>(array).unwrap());
1729
1730        let keys = Int8Array::from(vec![None, None, None]);
1731        let dict_array = DictionaryArray::new(keys, values.clone());
1732        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1733        assert!(max_array::<Int8Type, _>(array).is_none());
1734        let array = dict_array.downcast_dict::<Int8Array>().unwrap();
1735        assert!(min_array::<Int8Type, _>(array).is_none());
1736    }
1737
1738    #[test]
1739    fn test_max_min_dyn_nan() {
1740        let values = Float32Array::from(vec![5.0_f32, 2.0_f32, f32::NAN]);
1741        let keys = Int8Array::from_iter_values([0_i8, 1, 2]);
1742
1743        let dict_array = DictionaryArray::new(keys, Arc::new(values));
1744        let array = dict_array.downcast_dict::<Float32Array>().unwrap();
1745        assert!(max_array::<Float32Type, _>(array).unwrap().is_nan());
1746
1747        let array = dict_array.downcast_dict::<Float32Array>().unwrap();
1748        assert_eq!(2.0_f32, min_array::<Float32Type, _>(array).unwrap());
1749    }
1750
1751    #[test]
1752    fn test_min_max_sliced_primitive() {
1753        let expected = Some(4.0);
1754        let input: Float64Array = vec![None, Some(4.0)].into_iter().collect();
1755        let actual = min(&input);
1756        assert_eq!(actual, expected);
1757        let actual = max(&input);
1758        assert_eq!(actual, expected);
1759
1760        let sliced_input: Float64Array = vec![None, None, None, None, None, Some(4.0)]
1761            .into_iter()
1762            .collect();
1763        let sliced_input = sliced_input.slice(4, 2);
1764
1765        assert_eq!(&sliced_input, &input);
1766
1767        let actual = min(&sliced_input);
1768        assert_eq!(actual, expected);
1769        let actual = max(&sliced_input);
1770        assert_eq!(actual, expected);
1771    }
1772
1773    #[test]
1774    fn test_min_max_sliced_boolean() {
1775        let expected = Some(true);
1776        let input: BooleanArray = vec![None, Some(true)].into_iter().collect();
1777        let actual = min_boolean(&input);
1778        assert_eq!(actual, expected);
1779        let actual = max_boolean(&input);
1780        assert_eq!(actual, expected);
1781
1782        let sliced_input: BooleanArray = vec![None, None, None, None, None, Some(true)]
1783            .into_iter()
1784            .collect();
1785        let sliced_input = sliced_input.slice(4, 2);
1786
1787        assert_eq!(sliced_input, input);
1788
1789        let actual = min_boolean(&sliced_input);
1790        assert_eq!(actual, expected);
1791        let actual = max_boolean(&sliced_input);
1792        assert_eq!(actual, expected);
1793    }
1794
1795    #[test]
1796    fn test_min_max_sliced_string() {
1797        let expected = Some("foo");
1798        let input: StringArray = vec![None, Some("foo")].into_iter().collect();
1799        let actual = min_string(&input);
1800        assert_eq!(actual, expected);
1801        let actual = max_string(&input);
1802        assert_eq!(actual, expected);
1803
1804        let sliced_input: StringArray = vec![None, None, None, None, None, Some("foo")]
1805            .into_iter()
1806            .collect();
1807        let sliced_input = sliced_input.slice(4, 2);
1808
1809        assert_eq!(&sliced_input, &input);
1810
1811        let actual = min_string(&sliced_input);
1812        assert_eq!(actual, expected);
1813        let actual = max_string(&sliced_input);
1814        assert_eq!(actual, expected);
1815    }
1816
1817    #[test]
1818    fn test_min_max_sliced_binary() {
1819        let expected: Option<&[u8]> = Some(&[5]);
1820        let input: BinaryArray = vec![None, Some(&[5])].into_iter().collect();
1821        let actual = min_binary(&input);
1822        assert_eq!(actual, expected);
1823        let actual = max_binary(&input);
1824        assert_eq!(actual, expected);
1825
1826        let sliced_input: BinaryArray = vec![None, None, None, None, None, Some(&[5])]
1827            .into_iter()
1828            .collect();
1829        let sliced_input = sliced_input.slice(4, 2);
1830
1831        assert_eq!(&sliced_input, &input);
1832
1833        let actual = min_binary(&sliced_input);
1834        assert_eq!(actual, expected);
1835        let actual = max_binary(&sliced_input);
1836        assert_eq!(actual, expected);
1837    }
1838
1839    #[test]
1840    fn test_sum_overflow() {
1841        let a = Int32Array::from(vec![i32::MAX, 1]);
1842
1843        assert_eq!(sum(&a).unwrap(), -2147483648);
1844        assert_eq!(sum_array::<Int32Type, _>(&a).unwrap(), -2147483648);
1845    }
1846
1847    #[test]
1848    fn test_sum_checked_overflow() {
1849        let a = Int32Array::from(vec![i32::MAX, 1]);
1850
1851        sum_checked(&a).expect_err("overflow should be detected");
1852        sum_array_checked::<Int32Type, _>(&a).expect_err("overflow should be detected");
1853    }
1854
1855    /// Helper for building a RunArray.
1856    fn make_run_array<'a, I: RunEndIndexType, V: ArrowNumericType, ItemType>(
1857        values: impl IntoIterator<Item = &'a ItemType>,
1858    ) -> RunArray<I>
1859    where
1860        ItemType: Clone + Into<Option<V::Native>> + 'static,
1861    {
1862        let mut builder = arrow_array::builder::PrimitiveRunBuilder::<I, V>::new();
1863        for v in values.into_iter() {
1864            builder.append_option((*v).clone().into());
1865        }
1866        builder.finish()
1867    }
1868
1869    #[test]
1870    fn test_ree_sum_array_basic() {
1871        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[10, 10, 20, 30, 30, 30]);
1872        let typed_array = run_array.downcast::<Int32Array>().unwrap();
1873
1874        let result = sum_array::<Int32Type, _>(typed_array);
1875        assert_eq!(result, Some(130));
1876
1877        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
1878        assert_eq!(result, Some(130));
1879    }
1880
1881    #[test]
1882    fn test_ree_sum_array_empty() {
1883        let run_array = make_run_array::<Int16Type, Int32Type, i32>(&[]);
1884        let typed_array = run_array.downcast::<Int32Array>().unwrap();
1885
1886        let result = sum_array::<Int32Type, _>(typed_array);
1887        assert_eq!(result, None);
1888
1889        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
1890        assert_eq!(result, None);
1891    }
1892
1893    #[test]
1894    fn test_ree_sum_array_with_nulls() {
1895        let run_array =
1896            make_run_array::<Int16Type, Int32Type, _>(&[Some(10), None, Some(20), None, Some(30)]);
1897        let typed_array = run_array.downcast::<Int32Array>().unwrap();
1898
1899        let result = sum_array::<Int32Type, _>(typed_array);
1900        assert_eq!(result, Some(60));
1901
1902        let result = sum_array_checked::<Int32Type, _>(typed_array).unwrap();
1903        assert_eq!(result, Some(60));
1904    }
1905
1906    #[test]
1907    fn test_ree_sum_array_with_only_nulls() {
1908        let run_array = make_run_array::<Int16Type, Int16Type, _>(&[None, None, None, None, None]);
1909        let typed_array = run_array.downcast::<Int16Array>().unwrap();
1910
1911        let result = sum_array::<Int16Type, _>(typed_array);
1912        assert_eq!(result, None);
1913
1914        let result = sum_array_checked::<Int16Type, _>(typed_array).unwrap();
1915        assert_eq!(result, None);
1916    }
1917
1918    #[test]
1919    fn test_ree_sum_array_overflow() {
1920        let run_array = make_run_array::<Int16Type, Int8Type, _>(&[126, 2]);
1921        let typed_array = run_array.downcast::<Int8Array>().unwrap();
1922
1923        // i8 range is -128..=127. 126+2 overflows to -128.
1924        let result = sum_array::<Int8Type, _>(typed_array);
1925        assert_eq!(result, Some(-128));
1926
1927        let result = sum_array_checked::<Int8Type, _>(typed_array);
1928        assert!(result.is_err());
1929    }
1930
1931    #[test]
1932    fn test_ree_sum_array_sliced() {
1933        let run_array = make_run_array::<Int16Type, UInt8Type, _>(&[0, 10, 10, 10, 20, 30, 30, 30]);
1934        // Skip 2 values at the start and 1 at the end.
1935        let sliced = run_array.slice(2, 5);
1936        let typed_array = sliced.downcast::<UInt8Array>().unwrap();
1937
1938        let result = sum_array::<UInt8Type, _>(typed_array);
1939        assert_eq!(result, Some(100));
1940
1941        let result = sum_array_checked::<UInt8Type, _>(typed_array).unwrap();
1942        assert_eq!(result, Some(100));
1943    }
1944
1945    #[test]
1946    fn test_ree_min_max_array_basic() {
1947        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[30, 30, 10, 20, 20]);
1948        let typed_array = run_array.downcast::<Int32Array>().unwrap();
1949
1950        let result = min_array::<Int32Type, _>(typed_array);
1951        assert_eq!(result, Some(10));
1952
1953        let result = max_array::<Int32Type, _>(typed_array);
1954        assert_eq!(result, Some(30));
1955    }
1956
1957    #[test]
1958    fn test_ree_min_max_array_empty() {
1959        let run_array = make_run_array::<Int16Type, Int32Type, i32>(&[]);
1960        let typed_array = run_array.downcast::<Int32Array>().unwrap();
1961
1962        let result = min_array::<Int32Type, _>(typed_array);
1963        assert_eq!(result, None);
1964
1965        let result = max_array::<Int32Type, _>(typed_array);
1966        assert_eq!(result, None);
1967    }
1968
1969    #[test]
1970    fn test_ree_min_max_array_float() {
1971        let run_array = make_run_array::<Int16Type, Float64Type, _>(&[5.5, 5.5, 2.1, 8.9, 8.9]);
1972        let typed_array = run_array.downcast::<Float64Array>().unwrap();
1973
1974        let result = min_array::<Float64Type, _>(typed_array);
1975        assert_eq!(result, Some(2.1));
1976
1977        let result = max_array::<Float64Type, _>(typed_array);
1978        assert_eq!(result, Some(8.9));
1979    }
1980
1981    #[test]
1982    fn test_ree_min_max_array_with_nulls() {
1983        let run_array = make_run_array::<Int16Type, UInt8Type, _>(&[None, Some(10)]);
1984        let typed_array = run_array.downcast::<UInt8Array>().unwrap();
1985
1986        let result = min_array::<UInt8Type, _>(typed_array);
1987        assert_eq!(result, Some(10));
1988
1989        let result = max_array::<UInt8Type, _>(typed_array);
1990        assert_eq!(result, Some(10));
1991    }
1992
1993    #[test]
1994    fn test_ree_min_max_array_sliced() {
1995        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[0, 30, 30, 10, 20, 20, 100]);
1996        // Skip 1 value at the start and 1 at the end.
1997        let sliced = run_array.slice(1, 5);
1998        let typed_array = sliced.downcast::<Int32Array>().unwrap();
1999
2000        let result = min_array::<Int32Type, _>(typed_array);
2001        assert_eq!(result, Some(10));
2002
2003        let result = max_array::<Int32Type, _>(typed_array);
2004        assert_eq!(result, Some(30));
2005    }
2006
2007    #[test]
2008    fn test_ree_min_max_array_sliced_mid_run() {
2009        let run_array = make_run_array::<Int16Type, Int32Type, _>(&[0, 0, 30, 10, 20, 100, 100]);
2010        // Skip 1 value at the start and 1 at the end.
2011        let sliced = run_array.slice(1, 5);
2012        let typed_array = sliced.downcast::<Int32Array>().unwrap();
2013
2014        let result = min_array::<Int32Type, _>(typed_array);
2015        assert_eq!(result, Some(0));
2016
2017        let result = max_array::<Int32Type, _>(typed_array);
2018        assert_eq!(result, Some(100));
2019    }
2020}