This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 68e5750  Simplify and reduce code duplication in arithmetic kernels 
(#1161)
68e5750 is described below

commit 68e5750b6165933aa2677e7a756e251fea22d622
Author: Jörn Horstmann <[email protected]>
AuthorDate: Thu Jan 13 19:27:52 2022 +0100

    Simplify and reduce code duplication in arithmetic kernels (#1161)
    
    * Simplify and reduce code duplication in arithmetic kernels
    
    * Update comments
---
 arrow/src/compute/kernels/arithmetic.rs | 693 ++++++--------------------------
 arrow/src/datatypes/numeric.rs          |  72 +---
 2 files changed, 123 insertions(+), 642 deletions(-)

diff --git a/arrow/src/compute/kernels/arithmetic.rs 
b/arrow/src/compute/kernels/arithmetic.rs
index 81adb0b..340c113 100644
--- a/arrow/src/compute/kernels/arithmetic.rs
+++ b/arrow/src/compute/kernels/arithmetic.rs
@@ -29,6 +29,7 @@ use num::{One, Zero};
 use crate::buffer::Buffer;
 #[cfg(feature = "simd")]
 use crate::buffer::MutableBuffer;
+#[cfg(not(feature = "simd"))]
 use crate::compute::kernels::arity::unary;
 use crate::compute::util::combine_option_bitmap;
 use crate::datatypes;
@@ -41,67 +42,16 @@ use std::borrow::BorrowMut;
 #[cfg(feature = "simd")]
 use std::slice::{ChunksExact, ChunksExactMut};
 
-/// SIMD vectorized version of `unary_math_op` above specialized for signed 
numerical values.
-#[cfg(feature = "simd")]
-fn simd_signed_unary_math_op<T, SIMD_OP, SCALAR_OP>(
-    array: &PrimitiveArray<T>,
-    simd_op: SIMD_OP,
-    scalar_op: SCALAR_OP,
-) -> Result<PrimitiveArray<T>>
-where
-    T: datatypes::ArrowSignedNumericType,
-    SIMD_OP: Fn(T::SignedSimd) -> T::SignedSimd,
-    SCALAR_OP: Fn(T::Native) -> T::Native,
-{
-    let lanes = T::lanes();
-    let buffer_size = array.len() * std::mem::size_of::<T::Native>();
-    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
-
-    // safety: result is newly created above, always written as a T below
-    let mut result_chunks = unsafe { 
result.typed_data_mut().chunks_exact_mut(lanes) };
-    let mut array_chunks = array.values().chunks_exact(lanes);
-
-    result_chunks
-        .borrow_mut()
-        .zip(array_chunks.borrow_mut())
-        .for_each(|(result_slice, input_slice)| {
-            let simd_input = T::load_signed(input_slice);
-            let simd_result = T::signed_unary_op(simd_input, &simd_op);
-            T::write_signed(simd_result, result_slice);
-        });
-
-    let result_remainder = result_chunks.into_remainder();
-    let array_remainder = array_chunks.remainder();
-
-    result_remainder.into_iter().zip(array_remainder).for_each(
-        |(scalar_result, scalar_input)| {
-            *scalar_result = scalar_op(*scalar_input);
-        },
-    );
-
-    let data = unsafe {
-        ArrayData::new_unchecked(
-            T::DATA_TYPE,
-            array.len(),
-            None,
-            array
-                .data_ref()
-                .null_buffer()
-                .map(|b| b.bit_slice(array.offset(), array.len())),
-            0,
-            vec![result.into()],
-            vec![],
-        )
-    };
-    Ok(PrimitiveArray::<T>::from(data))
-}
-
+/// Creates a new PrimitiveArray by applying `simd_op` to the input `array`.
+/// If the length of the array is not multiple of the number of vector lanes
+/// then the remainder of the array will be calculated using `scalar_op`.
+/// Any operation on a `NULL` value will result in a `NULL` value in the 
output.
 #[cfg(feature = "simd")]
 fn simd_unary_math_op<T, SIMD_OP, SCALAR_OP>(
     array: &PrimitiveArray<T>,
     simd_op: SIMD_OP,
     scalar_op: SCALAR_OP,
-) -> Result<PrimitiveArray<T>>
+) -> PrimitiveArray<T>
 where
     T: ArrowNumericType,
     SIMD_OP: Fn(T::Simd) -> T::Simd,
@@ -148,7 +98,7 @@ where
             vec![],
         )
     };
-    Ok(PrimitiveArray::<T>::from(data))
+    PrimitiveArray::<T>::from(data)
 }
 
 /// Helper function to perform math lambda function on values from two arrays. 
If either
@@ -202,20 +152,23 @@ where
     Ok(PrimitiveArray::<T>::from(data))
 }
 
-/// Helper function to modulus two arrays.
+/// Helper function for operations where a valid `0` on the right array should
+/// result in an [ArrowError::DivideByZero], namely the division and modulo 
operations
 ///
 /// # Errors
 ///
 /// This function errors if:
 /// * the arrays have different lengths
-/// * a division by zero is found
-fn math_modulus<T>(
+/// * there is an element where both left and right values are valid and the 
right value is `0`
+fn math_checked_divide_op<T, F>(
     left: &PrimitiveArray<T>,
     right: &PrimitiveArray<T>,
+    op: F,
 ) -> Result<PrimitiveArray<T>>
 where
     T: ArrowNumericType,
-    T::Native: Rem<Output = T::Native> + Zero,
+    T::Native: One + Zero,
+    F: Fn(T::Native, T::Native) -> T::Native,
 {
     if left.len() != right.len() {
         return Err(ArrowError::ComputeError(
@@ -234,7 +187,7 @@ where
                     if right.is_zero() {
                         Err(ArrowError::DivideByZero)
                     } else {
-                        Ok(*left % *right)
+                        Ok(op(*left, *right))
                     }
                 } else {
                     Ok(T::default_value())
@@ -253,7 +206,7 @@ where
                 if right.is_zero() {
                     Err(ArrowError::DivideByZero)
                 } else {
-                    Ok(*left % *right)
+                    Ok(op(*left, *right))
                 }
             });
         // Safety: Iterator comes from a PrimitiveArray which reports its size 
correctly
@@ -274,111 +227,14 @@ where
     Ok(PrimitiveArray::<T>::from(data))
 }
 
-/// Helper function to divide two arrays.
+/// Creates a new PrimitiveArray by applying `simd_op` to the `left` and 
`right` input arrays.
+/// If the length of the arrays is not multiple of the number of vector lanes
+/// then the remainder of the array will be calculated using `scalar_op`.
+/// Any operation on a `NULL` value will result in a `NULL` value in the 
output.
 ///
 /// # Errors
 ///
-/// This function errors if:
-/// * the arrays have different lengths
-/// * a division by zero is found
-fn math_divide<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: Div<Output = T::Native> + Zero,
-{
-    if left.len() != right.len() {
-        return Err(ArrowError::ComputeError(
-            "Cannot perform math operation on arrays of different 
length".to_string(),
-        ));
-    }
-
-    let null_bit_buffer =
-        combine_option_bitmap(left.data_ref(), right.data_ref(), left.len())?;
-
-    let buffer = if let Some(b) = &null_bit_buffer {
-        let values = left.values().iter().zip(right.values()).enumerate().map(
-            |(i, (left, right))| {
-                let is_valid = unsafe { bit_util::get_bit_raw(b.as_ptr(), i) };
-                if is_valid {
-                    if right.is_zero() {
-                        Err(ArrowError::DivideByZero)
-                    } else {
-                        Ok(*left / *right)
-                    }
-                } else {
-                    Ok(T::default_value())
-                }
-            },
-        );
-        // Safety: Iterator comes from a PrimitiveArray which reports its size 
correctly
-        unsafe { Buffer::try_from_trusted_len_iter(values) }
-    } else {
-        // no value is null
-        let values = left
-            .values()
-            .iter()
-            .zip(right.values())
-            .map(|(left, right)| {
-                if right.is_zero() {
-                    Err(ArrowError::DivideByZero)
-                } else {
-                    Ok(*left / *right)
-                }
-            });
-        // Safety: Iterator comes from a PrimitiveArray which reports its size 
correctly
-        unsafe { Buffer::try_from_trusted_len_iter(values) }
-    }?;
-
-    let data = unsafe {
-        ArrayData::new_unchecked(
-            T::DATA_TYPE,
-            left.len(),
-            None,
-            null_bit_buffer,
-            0,
-            vec![buffer],
-            vec![],
-        )
-    };
-    Ok(PrimitiveArray::<T>::from(data))
-}
-
-/// Scalar-modulo version of `math_modulus`.
-fn math_modulus_scalar<T>(
-    array: &PrimitiveArray<T>,
-    modulo: T::Native,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: Rem<Output = T::Native> + Zero,
-{
-    if modulo.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    Ok(unary(array, |value| value % modulo))
-}
-
-/// Scalar-divisor version of `math_divide`.
-fn math_divide_scalar<T>(
-    array: &PrimitiveArray<T>,
-    divisor: T::Native,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: Div<Output = T::Native> + Zero,
-{
-    if divisor.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    Ok(unary(array, |value| value / divisor))
-}
-
-/// SIMD vectorized version of `math_op` above.
+/// This function errors if the arrays have different lengths
 #[cfg(feature = "simd")]
 fn simd_math_op<T, SIMD_OP, SCALAR_OP>(
     left: &PrimitiveArray<T>,
@@ -444,9 +300,12 @@ where
     Ok(PrimitiveArray::<T>::from(data))
 }
 
-/// SIMD vectorized implementation of `left % right`.
-/// If any of the lanes marked as valid in `valid_mask` are `0` then an 
`ArrowError::DivideByZero`
-/// is returned. The contents of no-valid lanes are undefined.
+/// Calculates the modulus operation `left % right` on two SIMD inputs.
+/// The lower-most bits of `valid_mask` specify which vector lanes are 
considered as valid.
+///
+/// # Errors
+///
+/// This function returns a [`ArrowError::DivideByZero`] if a valid element in 
`right` is `0`
 #[cfg(feature = "simd")]
 #[inline]
 fn simd_checked_modulus<T: ArrowNumericType>(
@@ -478,9 +337,12 @@ where
     }
 }
 
-/// SIMD vectorized implementation of `left / right`.
-/// If any of the lanes marked as valid in `valid_mask` are `0` then an 
`ArrowError::DivideByZero`
-/// is returned. The contents of no-valid lanes are undefined.
+/// Calculates the division operation `left / right` on two SIMD inputs.
+/// The lower-most bits of `valid_mask` specify which vector lanes are 
considered as valid.
+///
+/// # Errors
+///
+/// This function returns a [`ArrowError::DivideByZero`] if a valid element in 
`right` is `0`
 #[cfg(feature = "simd")]
 #[inline]
 fn simd_checked_divide<T: ArrowNumericType>(
@@ -512,52 +374,26 @@ where
     }
 }
 
-/// Scalar implementation of `left % right` for the remainder elements after 
complete chunks have been processed using SIMD.
-/// If any of the values marked as valid in `valid_mask` are `0` then an 
`ArrowError::DivideByZero` is returned.
-#[cfg(feature = "simd")]
-#[inline]
-fn simd_checked_modulus_remainder<T: ArrowNumericType>(
-    valid_mask: Option<u64>,
-    left_chunks: ChunksExact<T::Native>,
-    right_chunks: ChunksExact<T::Native>,
-    result_chunks: ChunksExactMut<T::Native>,
-) -> Result<()>
-where
-    T::Native: Zero + Rem<Output = T::Native>,
-{
-    let result_remainder = result_chunks.into_remainder();
-    let left_remainder = left_chunks.remainder();
-    let right_remainder = right_chunks.remainder();
-
-    result_remainder
-        .iter_mut()
-        .zip(left_remainder.iter().zip(right_remainder.iter()))
-        .enumerate()
-        .try_for_each(|(i, (result_scalar, (left_scalar, right_scalar)))| {
-            if valid_mask.map(|mask| mask & (1 << i) != 0).unwrap_or(true) {
-                if *right_scalar == T::Native::zero() {
-                    return Err(ArrowError::DivideByZero);
-                }
-                *result_scalar = *left_scalar % *right_scalar;
-            }
-            Ok(())
-        })?;
-
-    Ok(())
-}
-
-/// Scalar implementation of `left / right` for the remainder elements after 
complete chunks have been processed using SIMD.
-/// If any of the values marked as valid in `valid_mask` are `0` then an 
`ArrowError::DivideByZero` is returned.
+/// Applies `op` on the remainder elements of two input chunks and writes the 
result into
+/// the remainder elements of `result_chunks`.
+/// The lower-most bits of `valid_mask` specify which elements are considered 
as valid.
+///
+/// # Errors
+///
+/// This function returns a [`ArrowError::DivideByZero`] if a valid element in 
`right` is `0`
 #[cfg(feature = "simd")]
 #[inline]
-fn simd_checked_divide_remainder<T: ArrowNumericType>(
+fn simd_checked_divide_op_remainder<T, F>(
     valid_mask: Option<u64>,
     left_chunks: ChunksExact<T::Native>,
     right_chunks: ChunksExact<T::Native>,
     result_chunks: ChunksExactMut<T::Native>,
+    op: F,
 ) -> Result<()>
 where
-    T::Native: Zero + Div<Output = T::Native>,
+    T: ArrowNumericType,
+    T::Native: Zero,
+    F: Fn(T::Native, T::Native) -> T::Native,
 {
     let result_remainder = result_chunks.into_remainder();
     let left_remainder = left_chunks.remainder();
@@ -572,7 +408,9 @@ where
                 if *right_scalar == T::Native::zero() {
                     return Err(ArrowError::DivideByZero);
                 }
-                *result_scalar = *left_scalar / *right_scalar;
+                *result_scalar = op(*left_scalar, *right_scalar);
+            } else {
+                *result_scalar = T::default_value();
             }
             Ok(())
         })?;
@@ -580,201 +418,28 @@ where
     Ok(())
 }
 
-/// Scalar-modulo version of `simd_checked_modulus_remainder`.
-#[cfg(feature = "simd")]
-#[inline]
-fn simd_checked_modulus_scalar_remainder<T: ArrowNumericType>(
-    array_chunks: ChunksExact<T::Native>,
-    modulo: T::Native,
-    result_chunks: ChunksExactMut<T::Native>,
-) -> Result<()>
-where
-    T::Native: Zero + Rem<Output = T::Native>,
-{
-    if modulo.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    let result_remainder = result_chunks.into_remainder();
-    let array_remainder = array_chunks.remainder();
-
-    result_remainder
-        .iter_mut()
-        .zip(array_remainder.iter())
-        .for_each(|(result_scalar, array_scalar)| {
-            *result_scalar = *array_scalar % modulo;
-        });
-
-    Ok(())
-}
-
-/// Scalar-divisor version of `simd_checked_divide_remainder`.
-#[cfg(feature = "simd")]
-#[inline]
-fn simd_checked_divide_scalar_remainder<T: ArrowNumericType>(
-    array_chunks: ChunksExact<T::Native>,
-    divisor: T::Native,
-    result_chunks: ChunksExactMut<T::Native>,
-) -> Result<()>
-where
-    T::Native: Zero + Div<Output = T::Native>,
-{
-    if divisor.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    let result_remainder = result_chunks.into_remainder();
-    let array_remainder = array_chunks.remainder();
-
-    result_remainder
-        .iter_mut()
-        .zip(array_remainder.iter())
-        .for_each(|(result_scalar, array_scalar)| {
-            *result_scalar = *array_scalar / divisor;
-        });
-
-    Ok(())
-}
-
-/// SIMD vectorized version of `modulus`.
+/// Creates a new PrimitiveArray by applying `simd_op` to the `left` and 
`right` input array.
+/// If the length of the arrays is not multiple of the number of vector lanes
+/// then the remainder of the array will be calculated using `scalar_op`.
+/// Any operation on a `NULL` value will result in a `NULL` value in the 
output.
 ///
-/// The modulus kernels need their own implementation as there is a need to 
handle situations
-/// where a modulus by `0` occurs.  This is complicated by `NULL` slots and 
padding.
-#[cfg(feature = "simd")]
-fn simd_modulus<T>(
-    left: &PrimitiveArray<T>,
-    right: &PrimitiveArray<T>,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: One + Zero + Rem<Output = T::Native>,
-{
-    if left.len() != right.len() {
-        return Err(ArrowError::ComputeError(
-            "Cannot perform math operation on arrays of different 
length".to_string(),
-        ));
-    }
-
-    // Create the combined `Bitmap`
-    let null_bit_buffer =
-        combine_option_bitmap(left.data_ref(), right.data_ref(), left.len())?;
-
-    let lanes = T::lanes();
-    let buffer_size = left.len() * std::mem::size_of::<T::Native>();
-    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
-
-    match &null_bit_buffer {
-        Some(b) => {
-            // combine_option_bitmap returns a slice or new buffer starting at 0
-            let valid_chunks = b.bit_chunks(0, left.len());
-
-            // process data in chunks of 64 elements since we also get 64 bits 
of validity information at a time
-
-            // safety: result is newly created above, always written as a T 
below
-            let mut result_chunks =
-                unsafe { result.typed_data_mut().chunks_exact_mut(64) };
-            let mut left_chunks = left.values().chunks_exact(64);
-            let mut right_chunks = right.values().chunks_exact(64);
-
-            valid_chunks
-                .iter()
-                .zip(
-                    result_chunks
-                        .borrow_mut()
-                        
.zip(left_chunks.borrow_mut().zip(right_chunks.borrow_mut())),
-                )
-                .try_for_each(
-                    |(mut mask, (result_slice, (left_slice, right_slice)))| {
-                        // split chunks further into slices corresponding to 
the vector length
-                        // the compiler is able to unroll this inner loop and 
remove bounds checks
-                        // since the outer chunk size (64) is always a 
multiple of the number of lanes
-                        result_slice
-                            .chunks_exact_mut(lanes)
-                            
.zip(left_slice.chunks_exact(lanes).zip(right_slice.chunks_exact(lanes)))
-                            .try_for_each(|(result_slice, (left_slice, 
right_slice))| -> Result<()> {
-                                let simd_left = T::load(left_slice);
-                                let simd_right = T::load(right_slice);
-
-                                let simd_result = 
simd_checked_modulus::<T>(Some(mask), simd_left, simd_right)?;
-
-                                T::write(simd_result, result_slice);
-
-                                // skip the shift and avoid overflow for u8 
type, which uses 64 lanes.
-                                mask >>= T::lanes() % 64;
-
-                                Ok(())
-                            })
-                    },
-                )?;
-
-            let valid_remainder = valid_chunks.remainder_bits();
-
-            simd_checked_modulus_remainder::<T>(
-                Some(valid_remainder),
-                left_chunks,
-                right_chunks,
-                result_chunks,
-            )?;
-        }
-        None => {
-            // safety: result is newly created above, always written as a T 
below
-            let mut result_chunks =
-                unsafe { result.typed_data_mut().chunks_exact_mut(lanes) };
-            let mut left_chunks = left.values().chunks_exact(lanes);
-            let mut right_chunks = right.values().chunks_exact(lanes);
-
-            result_chunks
-                .borrow_mut()
-                .zip(left_chunks.borrow_mut().zip(right_chunks.borrow_mut()))
-                .try_for_each(
-                    |(result_slice, (left_slice, right_slice))| -> Result<()> {
-                        let simd_left = T::load(left_slice);
-                        let simd_right = T::load(right_slice);
-
-                        let simd_result =
-                            simd_checked_modulus::<T>(None, simd_left, 
simd_right)?;
-
-                        T::write(simd_result, result_slice);
-
-                        Ok(())
-                    },
-                )?;
-
-            simd_checked_modulus_remainder::<T>(
-                None,
-                left_chunks,
-                right_chunks,
-                result_chunks,
-            )?;
-        }
-    }
-
-    let data = unsafe {
-        ArrayData::new_unchecked(
-            T::DATA_TYPE,
-            left.len(),
-            None,
-            null_bit_buffer,
-            0,
-            vec![result.into()],
-            vec![],
-        )
-    };
-    Ok(PrimitiveArray::<T>::from(data))
-}
-
-/// SIMD vectorized version of `divide`.
+/// # Errors
 ///
-/// The divide kernels need their own implementation as there is a need to 
handle situations
-/// where a divide by `0` occurs.  This is complicated by `NULL` slots and 
padding.
+/// This function errors if:
+/// * the arrays have different lengths
+/// * there is an element where both left and right values are valid and the 
right value is `0`
 #[cfg(feature = "simd")]
-fn simd_divide<T>(
+fn simd_checked_divide_op<T, SIMD_OP, SCALAR_OP>(
     left: &PrimitiveArray<T>,
     right: &PrimitiveArray<T>,
+    simd_op: SIMD_OP,
+    scalar_op: SCALAR_OP,
 ) -> Result<PrimitiveArray<T>>
 where
     T: ArrowNumericType,
-    T::Native: One + Zero + Div<Output = T::Native>,
+    T::Native: One + Zero,
+    SIMD_OP: Fn(Option<u64>, T::Simd, T::Simd) -> Result<T::Simd>,
+    SCALAR_OP: Fn(T::Native, T::Native) -> T::Native,
 {
     if left.len() != right.len() {
         return Err(ArrowError::ComputeError(
@@ -822,7 +487,7 @@ where
                                 let simd_left = T::load(left_slice);
                                 let simd_right = T::load(right_slice);
 
-                                let simd_result = 
simd_checked_divide::<T>(Some(mask), simd_left, simd_right)?;
+                                let simd_result = simd_op(Some(mask), 
simd_left, simd_right)?;
 
                                 T::write(simd_result, result_slice);
 
@@ -836,11 +501,12 @@ where
 
             let valid_remainder = valid_chunks.remainder_bits();
 
-            simd_checked_divide_remainder::<T>(
+            simd_checked_divide_op_remainder::<T, _>(
                 Some(valid_remainder),
                 left_chunks,
                 right_chunks,
                 result_chunks,
+                scalar_op,
             )?;
         }
         None => {
@@ -858,8 +524,7 @@ where
                         let simd_left = T::load(left_slice);
                         let simd_right = T::load(right_slice);
 
-                        let simd_result =
-                            simd_checked_divide::<T>(None, simd_left, 
simd_right)?;
+                        let simd_result = simd_op(None, simd_left, 
simd_right)?;
 
                         T::write(simd_result, result_slice);
 
@@ -867,11 +532,12 @@ where
                     },
                 )?;
 
-            simd_checked_divide_remainder::<T>(
+            simd_checked_divide_op_remainder::<T, _>(
                 None,
                 left_chunks,
                 right_chunks,
                 result_chunks,
+                scalar_op,
             )?;
         }
     }
@@ -890,112 +556,6 @@ where
     Ok(PrimitiveArray::<T>::from(data))
 }
 
-/// SIMD vectorized version of `modulus_scalar`.
-#[cfg(feature = "simd")]
-fn simd_modulus_scalar<T>(
-    array: &PrimitiveArray<T>,
-    modulo: T::Native,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: One + Zero + Rem<Output = T::Native>,
-{
-    if modulo.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    let lanes = T::lanes();
-    let buffer_size = array.len() * std::mem::size_of::<T::Native>();
-    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
-
-    // safety: result is newly created above, always written as a T below
-    let mut result_chunks = unsafe { 
result.typed_data_mut().chunks_exact_mut(lanes) };
-    let mut array_chunks = array.values().chunks_exact(lanes);
-
-    let simd_right = T::init(modulo);
-
-    result_chunks
-        .borrow_mut()
-        .zip(array_chunks.borrow_mut())
-        .for_each(|(result_slice, array_slice)| {
-            let simd_left = T::load(array_slice);
-
-            let simd_result = T::bin_op(simd_left, simd_right, |a, b| a % b);
-            T::write(simd_result, result_slice);
-        });
-
-    simd_checked_modulus_scalar_remainder::<T>(array_chunks, modulo, 
result_chunks)?;
-
-    let data = unsafe {
-        ArrayData::new_unchecked(
-            T::DATA_TYPE,
-            array.len(),
-            None,
-            array
-                .data_ref()
-                .null_buffer()
-                .map(|b| b.bit_slice(array.offset(), array.len())),
-            0,
-            vec![result.into()],
-            vec![],
-        )
-    };
-    Ok(PrimitiveArray::<T>::from(data))
-}
-
-/// SIMD vectorized version of `divide_scalar`.
-#[cfg(feature = "simd")]
-fn simd_divide_scalar<T>(
-    array: &PrimitiveArray<T>,
-    divisor: T::Native,
-) -> Result<PrimitiveArray<T>>
-where
-    T: ArrowNumericType,
-    T::Native: One + Zero + Div<Output = T::Native>,
-{
-    if divisor.is_zero() {
-        return Err(ArrowError::DivideByZero);
-    }
-
-    let lanes = T::lanes();
-    let buffer_size = array.len() * std::mem::size_of::<T::Native>();
-    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
-
-    // safety: result is newly created above, always written as a T below
-    let mut result_chunks = unsafe { 
result.typed_data_mut().chunks_exact_mut(lanes) };
-    let mut array_chunks = array.values().chunks_exact(lanes);
-
-    let simd_right = T::init(divisor);
-
-    result_chunks
-        .borrow_mut()
-        .zip(array_chunks.borrow_mut())
-        .for_each(|(result_slice, array_slice)| {
-            let simd_left = T::load(array_slice);
-
-            let simd_result = T::bin_op(simd_left, simd_right, |a, b| a / b);
-            T::write(simd_result, result_slice);
-        });
-
-    simd_checked_divide_scalar_remainder::<T>(array_chunks, divisor, 
result_chunks)?;
-
-    let data = unsafe {
-        ArrayData::new_unchecked(
-            T::DATA_TYPE,
-            array.len(),
-            None,
-            array
-                .data_ref()
-                .null_buffer()
-                .map(|b| b.bit_slice(array.offset(), array.len())),
-            0,
-            vec![result.into()],
-            vec![],
-        )
-    };
-    Ok(PrimitiveArray::<T>::from(data))
-}
-
 /// Perform `left + right` operation on two arrays. If either left or right 
value is null
 /// then the result is also null.
 pub fn add<T>(
@@ -1004,11 +564,7 @@ pub fn add<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
+    T::Native: Add<Output = T::Native>,
 {
     #[cfg(feature = "simd")]
     return simd_math_op(&left, &right, |a, b| a + b, |a, b| a + b);
@@ -1024,18 +580,16 @@ pub fn add_scalar<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero
-        + One,
+    T::Native: Add<Output = T::Native>,
 {
     #[cfg(feature = "simd")]
     {
         let scalar_vector = T::init(scalar);
-        return simd_unary_math_op(array, |x| x + scalar_vector, |x| x + 
scalar);
+        return Ok(simd_unary_math_op(
+            array,
+            |x| x + scalar_vector,
+            |x| x + scalar,
+        ));
     }
     #[cfg(not(feature = "simd"))]
     return Ok(unary(array, |value| value + scalar));
@@ -1049,11 +603,7 @@ pub fn subtract<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Zero,
+    T::Native: Sub<Output = T::Native>,
 {
     #[cfg(feature = "simd")]
     return simd_math_op(&left, &right, |a, b| a - b, |a, b| a - b);
@@ -1087,11 +637,14 @@ where
 /// Perform `-` operation on an array. If value is null then the result is 
also null.
 pub fn negate<T>(array: &PrimitiveArray<T>) -> Result<PrimitiveArray<T>>
 where
-    T: datatypes::ArrowSignedNumericType,
+    T: datatypes::ArrowNumericType,
     T::Native: Neg<Output = T::Native>,
 {
     #[cfg(feature = "simd")]
-    return simd_signed_unary_math_op(array, |x| -x, |x| -x);
+    {
+        let zero_vector = T::init(T::default_value());
+        return Ok(simd_unary_math_op(array, |x| zero_vector - x, |x| -x));
+    }
     #[cfg(not(feature = "simd"))]
     return Ok(unary(array, |x| -x));
 }
@@ -1108,7 +661,11 @@ where
     #[cfg(feature = "simd")]
     {
         let raise_vector = T::init(raise);
-        return simd_unary_math_op(array, |x| T::pow(x, raise_vector), |x| 
x.pow(raise));
+        return Ok(simd_unary_math_op(
+            array,
+            |x| T::pow(x, raise_vector),
+            |x| x.pow(raise),
+        ));
     }
     #[cfg(not(feature = "simd"))]
     return Ok(unary(array, |x| x.pow(raise)));
@@ -1122,12 +679,7 @@ pub fn multiply<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero,
+    T::Native: Mul<Output = T::Native>,
 {
     #[cfg(feature = "simd")]
     return simd_math_op(&left, &right, |a, b| a * b, |a, b| a * b);
@@ -1169,18 +721,14 @@ pub fn modulus<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero
-        + One,
+    T::Native: Rem<Output = T::Native> + Zero + One,
 {
     #[cfg(feature = "simd")]
-    return simd_modulus(&left, &right);
+    return simd_checked_divide_op(&left, &right, simd_checked_modulus::<T>, 
|a, b| {
+        a % b
+    });
     #[cfg(not(feature = "simd"))]
-    return math_modulus(left, right);
+    return math_checked_divide_op(left, right, |a, b| a % b);
 }
 
 /// Perform `left / right` operation on two arrays. If either left or right 
value is null
@@ -1192,18 +740,12 @@ pub fn divide<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero
-        + One,
+    T::Native: Div<Output = T::Native> + Zero + One,
 {
     #[cfg(feature = "simd")]
-    return simd_divide(&left, &right);
+    return simd_checked_divide_op(&left, &right, simd_checked_divide::<T>, |a, 
b| a / b);
     #[cfg(not(feature = "simd"))]
-    return math_divide(left, right);
+    return math_checked_divide_op(left, right, |a, b| a / b);
 }
 
 /// Modulus every value in an array by a scalar. If any value in the array is 
null then the
@@ -1215,18 +757,23 @@ pub fn modulus_scalar<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero
-        + One,
+    T::Native: Rem<Output = T::Native> + Zero,
 {
+    if modulo.is_zero() {
+        return Err(ArrowError::DivideByZero);
+    }
+
     #[cfg(feature = "simd")]
-    return simd_modulus_scalar(&array, modulo);
+    {
+        let modulo_vector = T::init(modulo);
+        return Ok(simd_unary_math_op(
+            &array,
+            |a| a % modulo_vector,
+            |a| a % modulo,
+        ));
+    }
     #[cfg(not(feature = "simd"))]
-    return math_modulus_scalar(array, modulo);
+    return Ok(unary(array, |a| a % modulo));
 }
 
 /// Divide every value in an array by a scalar. If any value in the array is 
null then the
@@ -1238,18 +785,22 @@ pub fn divide_scalar<T>(
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowNumericType,
-    T::Native: Add<Output = T::Native>
-        + Sub<Output = T::Native>
-        + Mul<Output = T::Native>
-        + Div<Output = T::Native>
-        + Rem<Output = T::Native>
-        + Zero
-        + One,
+    T::Native: Div<Output = T::Native> + Zero,
 {
+    if divisor.is_zero() {
+        return Err(ArrowError::DivideByZero);
+    }
     #[cfg(feature = "simd")]
-    return simd_divide_scalar(&array, divisor);
+    {
+        let divisor_vector = T::init(divisor);
+        return Ok(simd_unary_math_op(
+            &array,
+            |a| a / divisor_vector,
+            |a| a / divisor,
+        ));
+    }
     #[cfg(not(feature = "simd"))]
-    return math_divide_scalar(array, divisor);
+    return Ok(unary(array, |a| a / divisor));
 }
 
 #[cfg(test)]
diff --git a/arrow/src/datatypes/numeric.rs b/arrow/src/datatypes/numeric.rs
index 2ff53c9..b8fa871 100644
--- a/arrow/src/datatypes/numeric.rs
+++ b/arrow/src/datatypes/numeric.rs
@@ -19,9 +19,7 @@ use super::*;
 #[cfg(feature = "simd")]
 use packed_simd::*;
 #[cfg(feature = "simd")]
-use std::ops::{
-    Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, Div, Mul, Neg, Not, Rem, 
Sub,
-};
+use std::ops::{Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, Div, Mul, Not, 
Rem, Sub};
 
 /// A subtype of primitive type that represents numeric values.
 ///
@@ -368,74 +366,6 @@ make_numeric_type!(DurationMillisecondType, i64, i64x8, 
m64x8);
 make_numeric_type!(DurationMicrosecondType, i64, i64x8, m64x8);
 make_numeric_type!(DurationNanosecondType, i64, i64x8, m64x8);
 
-/// A subtype of primitive type that represents signed numeric values.
-///
-/// SIMD operations are defined in this trait if available on the target 
system.
-#[cfg(feature = "simd")]
-pub trait ArrowSignedNumericType: ArrowNumericType
-where
-    Self::SignedSimd: Neg<Output = Self::SignedSimd>,
-{
-    /// Defines the SIMD type that should be used for this numeric type
-    type SignedSimd;
-
-    /// Loads a slice of signed numeric type into a SIMD register
-    fn load_signed(slice: &[Self::Native]) -> Self::SignedSimd;
-
-    /// Performs a SIMD unary operation on signed numeric type
-    fn signed_unary_op<F: Fn(Self::SignedSimd) -> Self::SignedSimd>(
-        a: Self::SignedSimd,
-        op: F,
-    ) -> Self::SignedSimd;
-
-    /// Writes a signed SIMD result back to a slice
-    fn write_signed(simd_result: Self::SignedSimd, slice: &mut [Self::Native]);
-}
-
-#[cfg(not(feature = "simd"))]
-pub trait ArrowSignedNumericType: ArrowNumericType
-where
-    Self::Native: std::ops::Neg<Output = Self::Native>,
-{
-}
-
-macro_rules! make_signed_numeric_type {
-    ($impl_ty:ty, $simd_ty:ident) => {
-        #[cfg(feature = "simd")]
-        impl ArrowSignedNumericType for $impl_ty {
-            type SignedSimd = $simd_ty;
-
-            #[inline]
-            fn load_signed(slice: &[Self::Native]) -> Self::SignedSimd {
-                unsafe { 
Self::SignedSimd::from_slice_unaligned_unchecked(slice) }
-            }
-
-            #[inline]
-            fn signed_unary_op<F: Fn(Self::SignedSimd) -> Self::SignedSimd>(
-                a: Self::SignedSimd,
-                op: F,
-            ) -> Self::SignedSimd {
-                op(a)
-            }
-
-            #[inline]
-            fn write_signed(simd_result: Self::SignedSimd, slice: &mut 
[Self::Native]) {
-                unsafe { simd_result.write_to_slice_unaligned_unchecked(slice) 
};
-            }
-        }
-
-        #[cfg(not(feature = "simd"))]
-        impl ArrowSignedNumericType for $impl_ty {}
-    };
-}
-
-make_signed_numeric_type!(Int8Type, i8x64);
-make_signed_numeric_type!(Int16Type, i16x32);
-make_signed_numeric_type!(Int32Type, i32x16);
-make_signed_numeric_type!(Int64Type, i64x8);
-make_signed_numeric_type!(Float32Type, f32x16);
-make_signed_numeric_type!(Float64Type, f64x8);
-
 #[cfg(feature = "simd")]
 pub trait ArrowFloatNumericType: ArrowNumericType {
     fn pow(base: Self::Simd, raise: Self::Simd) -> Self::Simd;

Reply via email to