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