// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build go1.18

package kernels

import (
	
	
	

	
	
	
	
	
	
	
)

type ArithmeticOp int8

const (
	OpAdd ArithmeticOp = iota
	OpSub
	OpMul
	OpDiv
	OpAbsoluteValue
	OpNegate
	// NO SIMD for the following yet
	OpSqrt
	OpPower
	OpSin
	OpCos
	OpTan
	OpAsin
	OpAcos
	OpAtan
	OpAtan2
	OpLn
	OpLog10
	OpLog2
	OpLog1p
	OpLogb
	// End NO SIMD
	OpSign

	// Checked versions will not use SIMD except for float32/float64 impls
	OpAddChecked
	OpSubChecked
	OpMulChecked
	OpDivChecked
	OpAbsoluteValueChecked
	OpNegateChecked
	// No SIMD impls for the rest of these yet
	OpSqrtChecked
	OpPowerChecked
	OpSinChecked
	OpCosChecked
	OpTanChecked
	OpAsinChecked
	OpAcosChecked
	OpLnChecked
	OpLog10Checked
	OpLog2Checked
	OpLog1pChecked
	OpLogbChecked
)

func mulWithOverflow[ arrow.IntType | arrow.UintType](,  ) (, error) {
	,  := MinOf[](), MaxOf[]()
	switch {
	case  > 0:
		if  > 0 {
			if  > ( / ) {
				return 0, errOverflow
			}
		} else {
			if  < ( / ) {
				return 0, errOverflow
			}
		}
	case  > 0:
		if  < ( / ) {
			return 0, errOverflow
		}
	default:
		if ( != 0) && ( < ( / )) {
			return 0, errOverflow
		}
	}

	return  * , nil
}

func getGoArithmeticBinary[, ,  arrow.NumericType]( func( ,  ,  *error) ) binaryOps[, , ] {
	return binaryOps[, , ]{
		arrArr: func( *exec.KernelCtx,  [],  [],  []) error {
			var  error
			for  := range  {
				[] = ([], [], &)
			}
			return 
		},
		arrScalar: func( *exec.KernelCtx,  [],  ,  []) error {
			var  error
			for  := range  {
				[] = ([], , &)
			}
			return 
		},
		scalarArr: func( *exec.KernelCtx,  ,  [],  []) error {
			var  error
			for  := range  {
				[] = (, [], &)
			}
			return 
		},
	}
}

var (
	errOverflow      = fmt.Errorf("%w: overflow", arrow.ErrInvalid)
	errDivByZero     = fmt.Errorf("%w: divide by zero", arrow.ErrInvalid)
	errNegativeSqrt  = fmt.Errorf("%w: square root of negative number", arrow.ErrInvalid)
	errNegativePower = fmt.Errorf("%w: integers to negative integer powers are not allowed", arrow.ErrInvalid)
	errDomainErr     = fmt.Errorf("%w: domain error", arrow.ErrInvalid)
	errLogZero       = fmt.Errorf("%w: logarithm of zero", arrow.ErrInvalid)
	errLogNeg        = fmt.Errorf("%w: logarithm of negative number", arrow.ErrInvalid)
)

func getGoArithmeticOpIntegral[,  arrow.UintType | arrow.IntType]( ArithmeticOp) exec.ArrayKernelExec {
	switch  {
	case OpAdd:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( + ) }))
	case OpSub:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( - ) }))
	case OpMul:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( * ) }))
	case OpDiv:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error)  {
			if  == 0 {
				* = errDivByZero
				return 0
			}
			return ( / )
		})
	case OpAbsoluteValue:
		if  := ^(0);  < 0 {
			 := (SizeOf[]() * 8) - 1
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				// get abs without branching
				for ,  := range  {
					// right shift (sign check)
					 :=  >> 
					// add the mask '+' and '-' balance
					 =  + 
					// invert and return
					[] = ( ^ )
				}
				return nil
			})
		}

		if SizeOf[]() == SizeOf[]() {
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				,  := arrow.GetBytes(), arrow.GetBytes()
				copy(, )
				return nil
			})
		} else {
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				DoStaticCast(, )
				return nil
			})
		}
	case OpNegate:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (-)
			}
			return nil
		})
	case OpSign:
		if ^(0) < 0 {
			var  int8 = -1
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				 := ()
				for ,  := range  {
					switch {
					case  > 0:
						[] = 1
					case  < 0:
						[] = 
					default:
						[] = 0
					}
				}
				return nil
			})
		}
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				if  > 0 {
					[] = 1
				} else {
					[] = 0
				}
			}
			return nil
		})
	case OpPower:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  {
			if  < 0 {
				* = errNegativePower
				return 0
			}
			// integer power
			var (
				        = uint64()
				         = uint64()
				  uint64 = 1
			)

			// right to left 0(logn) power
			for  != 0 {
				if &1 != 0 {
					 *= 
				}
				 *= 
				 >>= 1
			}
			return ()
		}))
	case OpAddChecked:
		 := (SizeOf[]() * 8) - 1
		// ie: uint32 does a >> 31 at the end, int32 does >> 30
		if ^(0) < 0 {
			--
		}
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			 = ( + )
			// see math/bits/bits.go Add64 for explanation of logic
			 := ((&) | ((|) &^ )) >> 
			if  > 0 {
				* = errOverflow
			}
			return
		})
	case OpSubChecked:
		 := (SizeOf[]() * 8) - 1
		// ie: uint32 does a >> 31 at the end, int32 does >> 30
		if ^(0) < 0 {
			--
		}
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			 = ( - )
			// see math/bits/bits.go Sub64 for explanation of bit logic
			 := ((^&) | (^(^) & )) >> 
			if  > 0 {
				* = errOverflow
			}
			return
		})
	case OpMulChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error) ( ) {
			,  := mulWithOverflow(, )
			if  != nil {
				* = 
			}
			return ()
		}))
	case OpDivChecked:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			if  == 0 {
				* = errDivByZero
				return
			}
			return ( / )
		})
	case OpAbsoluteValueChecked:
		if  := ^(0);  < 0 {
			 := (SizeOf[]() * 8) - 1
			 := MinOf[]()
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				for ,  := range  {
					if  ==  {
						return errOverflow
					}

					// right shift (sign check)
					 :=  >> 
					// add the mask '+' and '-' balance
					 =  + 
					// invert and return
					[] = ( ^ )
				}
				return nil
			})
		}
		if SizeOf[]() == SizeOf[]() {
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				,  := arrow.GetBytes(), arrow.GetBytes()
				copy(, )
				return nil
			})
		} else {
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				DoStaticCast(, )
				return nil
			})
		}
	case OpNegateChecked:
		if  := ^(0);  < 0 {
			 := MinOf[]()
			// signed
			return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
				for ,  := range  {
					if  !=  {
						[] = (-)
					} else {
						return errOverflow
					}
				}
				return nil
			})
		}
	case OpPowerChecked:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error)  {
			if  < 0 {
				* = errNegativePower
				return 0
			} else if  == 0 {
				return 1
			}

			// left to right 0(logn) power with overflow checks
			var (
				 bool
				      = uint64(1) << (63 - bits.LeadingZeros64(uint64()))
				       = 1
				      error
			)

			for  != 0 {
				,  = mulWithOverflow(, )
				 =  || ( != nil)
				if uint64()& != 0 {
					,  = mulWithOverflow(, )
					 =  || ( != nil)
				}
				 >>= 1
			}
			if  {
				* = errOverflow
			}
			return ()
		})
	}
	debug.Assert(false, "invalid arithmetic op")
	return nil
}

func getGoArithmeticOpFloating[,  constraints.Float]( ArithmeticOp) exec.ArrayKernelExec {
	switch  {
	case OpAdd, OpAddChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( + ) }))
	case OpSub, OpSubChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( - ) }))
	case OpMul, OpMulChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  { return ( * ) }))
	case OpDiv:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			return ( / )
		})
	case OpDivChecked:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			if  == 0 {
				* = errDivByZero
				return
			}
			return ( / )
		})
	case OpAbsoluteValue, OpAbsoluteValueChecked:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Abs(float64()))
			}
			return nil
		})
	case OpNegate, OpNegateChecked:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (-)
			}
			return nil
		})
	case OpSqrt:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Sqrt(float64()))
			}
			return nil
		})
	case OpSqrtChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if  < 0 {
				* = errNegativeSqrt
				return (math.NaN())
			}
			return (math.Sqrt(float64()))
		})
	case OpSign:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				switch {
				case math.IsNaN(float64()):
					[] = ()
				case  == 0:
					[] = 0
				case math.Signbit(float64()):
					[] = -1
				default:
					[] = 1
				}
			}
			return nil
		})
	case OpPower, OpPowerChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  {
			return (math.Pow(float64(), float64()))
		}))
	case OpSin:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Sin(float64()))
			}
			return nil
		})
	case OpSinChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if math.IsInf(float64(), 0) {
				* = errDomainErr
				return ()
			}
			return (math.Sin(float64()))
		})
	case OpCos:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Cos(float64()))
			}
			return nil
		})
	case OpCosChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if math.IsInf(float64(), 0) {
				* = errDomainErr
				return ()
			}
			return (math.Cos(float64()))
		})
	case OpTan:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Tan(float64()))
			}
			return nil
		})
	case OpTanChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if math.IsInf(float64(), 0) {
				* = errDomainErr
				return ()
			}
			return (math.Tan(float64()))
		})
	case OpAsin:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Asin(float64()))
			}
			return nil
		})
	case OpAsinChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if  < -1 ||  > 1 {
				* = errDomainErr
				return ()
			}
			return (math.Asin(float64()))
		})
	case OpAcos:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Acos(float64()))
			}
			return nil
		})
	case OpAcosChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			if  < -1 ||  > 1 {
				* = errDomainErr
				return ()
			}
			return (math.Acos(float64()))
		})
	case OpAtan:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Atan(float64()))
			}
			return nil
		})
	case OpAtan2:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  {
			return (math.Atan2(float64(), float64()))
		}))
	case OpLn:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Log(float64()))
			}
			return nil
		})
	case OpLnChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			switch {
			case  == 0:
				* = errLogZero
				return ()
			case  < 0:
				* = errLogNeg
				return ()
			}

			return (math.Log(float64()))
		})
	case OpLog10:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Log10(float64()))
			}
			return nil
		})
	case OpLog10Checked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			switch {
			case  == 0:
				* = errLogZero
				return ()
			case  < 0:
				* = errLogNeg
				return ()
			}

			return (math.Log10(float64()))
		})
	case OpLog2:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Log2(float64()))
			}
			return nil
		})
	case OpLog2Checked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			switch {
			case  == 0:
				* = errLogZero
				return ()
			case  < 0:
				* = errLogNeg
				return ()
			}

			return (math.Log2(float64()))
		})
	case OpLog1p:
		return ScalarUnary(func( *exec.KernelCtx,  [],  []) error {
			for ,  := range  {
				[] = (math.Log1p(float64()))
			}
			return nil
		})
	case OpLog1pChecked:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			switch {
			case  == -1:
				* = errLogZero
				return ()
			case  < -1:
				* = errLogNeg
				return ()
			}

			return (math.Log1p(float64()))
		})
	case OpLogb:
		return ScalarBinary(getGoArithmeticBinary(func(,  ,  *error)  {
			if  == 0 {
				if  == 0 ||  < 0 {
					return (math.NaN())
				} else {
					return (math.Inf(-1))
				}
			} else if  < 0 {
				return (math.NaN())
			}
			return (math.Log(float64()) / math.Log(float64()))
		}))
	case OpLogbChecked:
		return ScalarBinaryNotNull((func( *exec.KernelCtx, ,  ,  *error)  {
			if  == 0 ||  == 0 {
				* = errLogZero
				return ()
			} else if  < 0 ||  < 0 {
				* = errLogNeg
				return ()
			}
			return (math.Log(float64()) / math.Log(float64()))
		}))
	}
	debug.Assert(false, "invalid arithmetic op")
	return nil
}

func timeDurationOp[, ,  ~int32 | ~int64]( int64,  ArithmeticOp) exec.ArrayKernelExec {
	switch  {
	case OpAdd:
		return ScalarBinary(getGoArithmeticBinary(func( ,  ,  *error)  {
			 := () + ()
			if  < 0 ||  <= int64() {
				* = fmt.Errorf("%w: %d is not within acceptable range of [0, %d) s", arrow.ErrInvalid, , )
			}
			return 
		}))
	case OpSub:
		return ScalarBinary(getGoArithmeticBinary(func( ,  ,  *error)  {
			 := () - ()
			if  < 0 ||  <= int64() {
				* = fmt.Errorf("%w: %d is not within acceptable range of [0, %d) s", arrow.ErrInvalid, , )
			}
			return 
		}))
	case OpAddChecked:
		 := (SizeOf[]() * 8) - 1
		// ie: uint32 does a >> 31 at the end, int32 does >> 30
		if ^(0) < 0 {
			--
		}
		return ScalarBinary(getGoArithmeticBinary(func( ,  ,  *error) ( ) {
			,  := (), ()
			 =  + 
			 := (( & ) | (( | ) &^ )) >> 
			if  > 0 {
				* = errOverflow
				return
			}
			if  < 0 ||  <= int64() {
				* = fmt.Errorf("%w: %d is not within acceptable range of [0, %d) s", arrow.ErrInvalid, , )
			}
			return
		}))
	case OpSubChecked:
		 := (SizeOf[]() * 8) - 1
		// ie: uint32 does a >> 31 at the end, int32 does >> 30
		if ^(0) < 0 {
			--
		}
		return ScalarBinary(getGoArithmeticBinary(func( ,  ,  *error) ( ) {
			,  := (), ()
			 =  - 
			 := ((^ & ) | (^( ^ ) & )) >> 
			if  > 0 {
				* = errOverflow
				return
			}
			if  < 0 ||  <= int64() {
				* = fmt.Errorf("%w: %d is not within acceptable range of [0, %d) s", arrow.ErrInvalid, , )
			}
			return
		}))
	}
	return nil
}

func ( ArithmeticOp) exec.ArrayKernelExec {
	const  = 86400
	switch  {
	case OpSub:
		return ScalarBinary(getGoArithmeticBinary(func(,  arrow.Time32,  *error) ( arrow.Duration) {
			return arrow.Duration(( - ) * )
		}))
	case OpSubChecked:
		return ScalarBinary(getGoArithmeticBinary(func(,  arrow.Time32,  *error) ( arrow.Duration) {
			 = arrow.Duration() - arrow.Duration()
			,  := utils.Mul64(int64(), )
			if ! {
				* = errOverflow
			}
			return arrow.Duration()
		}))
	}
	panic("invalid op for subtractDate32")
}

type decOps[ decimal128.Num | decimal256.Num] struct {
	Add  func(, ) 
	Sub  func(, ) 
	Div  func(, ) 
	Mul  func(, ) 
	Abs  func() 
	Neg  func() 
	Sign func() int
}

var dec128Ops = decOps[decimal128.Num]{
	Add: func(,  decimal128.Num) decimal128.Num { return .Add() },
	Sub: func(,  decimal128.Num) decimal128.Num { return .Sub() },
	Mul: func(,  decimal128.Num) decimal128.Num { return .Mul() },
	Div: func(,  decimal128.Num) decimal128.Num {
		, _ = .Div()
		return 
	},
	Abs:  func( decimal128.Num) decimal128.Num { return .Abs() },
	Neg:  func( decimal128.Num) decimal128.Num { return .Negate() },
	Sign: func( decimal128.Num) int { return .Sign() },
}

var dec256Ops = decOps[decimal256.Num]{
	Add: func(,  decimal256.Num) decimal256.Num { return .Add() },
	Sub: func(,  decimal256.Num) decimal256.Num { return .Sub() },
	Mul: func(,  decimal256.Num) decimal256.Num { return .Mul() },
	Div: func(,  decimal256.Num) decimal256.Num {
		, _ = .Div()
		return 
	},
	Abs:  func( decimal256.Num) decimal256.Num { return .Abs() },
	Neg:  func( decimal256.Num) decimal256.Num { return .Negate() },
	Sign: func( decimal256.Num) int { return .Sign() },
}

func getArithmeticOpDecimalImpl[ decimal128.Num | decimal256.Num]( ArithmeticOp,  decOps[]) exec.ArrayKernelExec {
	if  >= OpAddChecked {
		 -= OpAddChecked // decimal128/256 checked is the same as unchecked
	}

	switch  {
	case OpAdd:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error)  {
			return .Add(, )
		})
	case OpSub:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error)  {
			return .Sub(, )
		})
	case OpMul:
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error)  {
			return .Mul(, )
		})
	case OpDiv:
		var  
		return ScalarBinaryNotNull(func( *exec.KernelCtx, ,  ,  *error) ( ) {
			if  ==  {
				* = errDivByZero
				return
			}
			return .Div(, )
		})
	case OpAbsoluteValue:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			return .Abs()
		})
	case OpNegate:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error)  {
			return .Neg()
		})
	case OpSign:
		return ScalarUnaryNotNull(func( *exec.KernelCtx,  ,  *error) int64 {
			return int64(.Sign())
		})
	}
	debug.Assert(false, "unimplemented arithmetic op")
	return nil
}

func getArithmeticDecimal[ decimal128.Num | decimal256.Num]( ArithmeticOp) exec.ArrayKernelExec {
	var  
	switch any().(type) {
	case decimal128.Num:
		return getArithmeticOpDecimalImpl(, dec128Ops)
	case decimal256.Num:
		return getArithmeticOpDecimalImpl(, dec256Ops)
	}
	panic("should never get here")
}

func ( arrow.Type,  ArithmeticOp) exec.ArrayKernelExec {
	switch  {
	case arrow.INT8:
		return getArithmeticOpIntegral[int8, int8]()
	case arrow.UINT8:
		return getArithmeticOpIntegral[uint8, uint8]()
	case arrow.INT16:
		return getArithmeticOpIntegral[int16, int16]()
	case arrow.UINT16:
		return getArithmeticOpIntegral[uint16, uint16]()
	case arrow.INT32, arrow.TIME32:
		return getArithmeticOpIntegral[int32, int32]()
	case arrow.UINT32:
		return getArithmeticOpIntegral[uint32, uint32]()
	case arrow.INT64, arrow.TIME64, arrow.DATE64, arrow.TIMESTAMP, arrow.DURATION:
		return getArithmeticOpIntegral[int64, int64]()
	case arrow.UINT64:
		return getArithmeticOpIntegral[uint64, uint64]()
	case arrow.FLOAT32:
		return getArithmeticOpFloating[float32, float32]()
	case arrow.FLOAT64:
		return getArithmeticOpFloating[float64, float64]()
	}
	debug.Assert(false, "invalid arithmetic type")
	return nil
}

func arithmeticExec[ arrow.IntType | arrow.UintType]( arrow.Type,  ArithmeticOp) exec.ArrayKernelExec {
	switch  {
	case arrow.INT8:
		return getArithmeticOpIntegral[, int8]()
	case arrow.UINT8:
		return getArithmeticOpIntegral[, uint8]()
	case arrow.INT16:
		return getArithmeticOpIntegral[, int16]()
	case arrow.UINT16:
		return getArithmeticOpIntegral[, uint16]()
	case arrow.INT32, arrow.TIME32:
		return getArithmeticOpIntegral[, int32]()
	case arrow.UINT32:
		return getArithmeticOpIntegral[, uint32]()
	case arrow.INT64, arrow.TIME64, arrow.DATE64, arrow.TIMESTAMP, arrow.DURATION:
		return getArithmeticOpIntegral[, int64]()
	case arrow.UINT64:
		return getArithmeticOpIntegral[, uint64]()
	}
	debug.Assert(false, "arithmetic integral to floating not implemented")
	return nil
}

func (,  arrow.Type,  ArithmeticOp) exec.ArrayKernelExec {
	if  ==  {
		return ArithmeticExecSameType(, )
	}

	switch  {
	case arrow.INT8:
		return arithmeticExec[int8](, )
	case arrow.UINT8:
		return arithmeticExec[uint8](, )
	case arrow.INT16:
		return arithmeticExec[int16](, )
	case arrow.UINT16:
		return arithmeticExec[uint16](, )
	case arrow.INT32, arrow.TIME32:
		return arithmeticExec[int32](, )
	case arrow.UINT32:
		return arithmeticExec[uint32](, )
	case arrow.INT64, arrow.TIME64, arrow.DATE64, arrow.TIMESTAMP, arrow.DURATION:
		return arithmeticExec[int64](, )
	case arrow.UINT64:
		return arithmeticExec[uint64](, )
	case arrow.FLOAT32:
		if  == arrow.FLOAT32 {
			return getArithmeticOpFloating[float32, float32]()
		}
		return getArithmeticOpFloating[float32, float64]()
	case arrow.FLOAT64:
		if  == arrow.FLOAT32 {
			return getArithmeticOpFloating[float64, float32]()
		}
		return getArithmeticOpFloating[float64, float64]()
	}
	return nil
}