# Source code for arrayfire.arith

    #######################################################
    # Copyright (c) 2015, ArrayFire
    # All rights reserved.
    #
    # This file is distributed under 3-clause BSD license.
    # The complete license agreement can be obtained at:
    # http://arrayfire.com/licenses/BSD-3-Clause
    ########################################################

    """
    Math functions (sin, sqrt, exp, etc).
    """

    from .library import *
    from .array import *
    from .bcast import _bcast_var
    from .util import _is_number

    def _arith_binary_func(lhs, rhs, c_func):
        out = Array()

        is_left_array = isinstance(lhs, Array)
        is_right_array = isinstance(rhs, Array)

        if not (is_left_array or is_right_array):
            raise TypeError("Atleast one input needs to be of type arrayfire.array")

        elif (is_left_array and is_right_array):
            safe_call(c_func(ct.pointer(out.arr), lhs.arr, rhs.arr, _bcast_var.get()))

        elif (_is_number(rhs)):
            ldims = dim4_to_tuple(lhs.dims())
            rty = implicit_dtype(rhs, lhs.type())
            other = Array()
            other.arr = constant_array(rhs, ldims[0], ldims[1], ldims[2], ldims[3], rty)
            safe_call(c_func(ct.pointer(out.arr), lhs.arr, other.arr, _bcast_var.get()))

        else:
            rdims = dim4_to_tuple(rhs.dims())
            lty = implicit_dtype(lhs, rhs.type())
            other = Array()
            other.arr = constant_array(lhs, rdims[0], rdims[1], rdims[2], rdims[3], lty)
            safe_call(c_func(ct.pointer(out.arr), other.arr, rhs.arr, _bcast_var.get()))

        return out

    def _arith_unary_func(a, c_func):
        out = Array()
        safe_call(c_func(ct.pointer(out.arr), a.arr))
        return out

    def cast(a, dtype):
        """
        Cast an array to a specified type

        Parameters
        ----------
        a    : af.Array
               Multi dimensional arrayfire array.
        dtype: af.Dtype
               Must be one of the following:
                   - Dtype.f32 for float
                   - Dtype.f64 for double
                   - Dtype.b8  for bool
                   - Dtype.u8  for unsigned char
                   - Dtype.s32 for signed 32 bit integer
                   - Dtype.u32 for unsigned 32 bit integer
                   - Dtype.s64 for signed 64 bit integer
                   - Dtype.u64 for unsigned 64 bit integer
                   - Dtype.c32 for 32 bit complex number
                   - Dtype.c64 for 64 bit complex number
        Returns
        --------
        out  : af.Array
               array containing the values from `a` after converting to `dtype`.
        """
        out=Array()
        safe_call(backend.get().af_cast(ct.pointer(out.arr), a.arr, dtype.value))
        return out

    def minof(lhs, rhs):
        """
        Find the minimum value of two inputs at each location.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             array containing the minimum value at each location of the inputs.

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_minof)

    def maxof(lhs, rhs):
        """
        Find the maximum value of two inputs at each location.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             array containing the maximum value at each location of the inputs.

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_maxof)

    def rem(lhs, rhs):
        """
        Find the remainder.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             Contains the remainders after dividing each value of lhs` with those in `rhs`.

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_rem)

    def abs(a):
        """
        Find the absolute values.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             Contains the absolute values of the inputs.
        """
        return _arith_unary_func(a, backend.get().af_abs)

    def arg(a):
        """
        Find the theta value of the inputs in polar co-ordinates.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             Contains the theta values.
        """
        return _arith_unary_func(a, backend.get().af_arg)

    def sign(a):
        """
        Find the sign of the inputs.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing -1 for negative values, 1 otherwise.
        """
        return _arith_unary_func(a, backend.get().af_sign)

    def round(a):
        """
        Round the values to nearest integer.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the values rounded to nearest integer.
        """
        return _arith_unary_func(a, backend.get().af_round)

    def trunc(a):
        """
        Round the values towards zero.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the truncated values.
        """
        return _arith_unary_func(a, backend.get().af_trunc)

    def floor(a):
        """
        Round the values towards a smaller integer.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the floored values.
        """
        return _arith_unary_func(a, backend.get().af_floor)

    def ceil(a):
        """
        Round the values towards a bigger integer.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the ceiled values.
        """
        return _arith_unary_func(a, backend.get().af_ceil)

    def hypot(lhs, rhs):
        """
        Find the value of the hypotunese.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             Contains the value of `sqrt(lhs**2, rhs**2)`.

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_hypot)

    def sin(a):
        """
        Sine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the sine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_sin)

    def cos(a):
        """
        Cosine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the cosine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_cos)

    def tan(a):
        """
        Tangent of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the tangent of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_tan)

    def asin(a):
        """
        Arc Sine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc sine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_asin)

    def acos(a):
        """
        Arc Cosine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc cosine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_acos)

    def atan(a):
        """
        Arc Tangent of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc tangent of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_atan)

    def atan2(lhs, rhs):
        """
        Find the arc tan using two values.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             Contains the value arc tan values where:
             - `lhs` contains the sine values.
             - `rhs` contains the cosine values.

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_atan2)

    def cplx(lhs, rhs=None):
        """
        Create a complex array from real inputs.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : optional: af.Array or scalar. default: None.
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             Contains complex values whose
             - real values contain values from `lhs`
             - imaginary values contain values from `rhs` (0 if `rhs` is None)

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        if rhs is None:
            return _arith_unary_func(lhs, backend.get().af_cplx)
        else:
            return _arith_binary_func(lhs, rhs, backend.get().af_cplx2)

    def real(a):
        """
        Find the real values of the input.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the real values from `a`.

        """
        return _arith_unary_func(a, backend.get().af_real)

    def imag(a):
        """
        Find the imaginary values of the input.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the imaginary values from `a`.
        """
        return _arith_unary_func(a, backend.get().af_imag)

    def conjg(a):
        """
        Find the complex conjugate values of the input.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing copmplex conjugate values from `a`.
        """
        return _arith_unary_func(a, backend.get().af_conjg)

    def sinh(a):
        """
        Hyperbolic Sine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the hyperbolic sine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_sinh)

    def cosh(a):
        """
        Hyperbolic Cosine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the hyperbolic cosine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_cosh)

    def tanh(a):
        """
        Hyperbolic Tangent of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the hyperbolic tangent of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_tanh)

    def asinh(a):
        """
        Arc Hyperbolic Sine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc hyperbolic sine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_asinh)

    def acosh(a):
        """
        Arc Hyperbolic Cosine of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc hyperbolic cosine of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_acosh)

    def atanh(a):
        """
        Arc Hyperbolic Tangent of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the arc hyperbolic tangent of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_atanh)

    def root(lhs, rhs):
        """
        Find the root values of two inputs at each location.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             array containing the value of `lhs ** (1/rhs)`

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_root)

    def pow(lhs, rhs):
        """
        Find the power of two inputs at each location.

        Parameters
        ----------
        lhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        rhs : af.Array or scalar
              Multi dimensional arrayfire array or a scalar number.

        Returns
        --------
        out : af.Array
             array containing the value of `lhs ** (rhs)`

        Note
        -------
        - Atleast one of `lhs` and `rhs` needs to be af.Array.
        - If `lhs` and `rhs` are both af.Array, they must be of same size.
        """
        return _arith_binary_func(lhs, rhs, backend.get().af_pow)

    def pow2(a):
        """
        Raise 2 to the power of each element in input.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array where each element is 2 raised to power of the corresponding value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_pow2)

    def sigmoid(a):
        """
        Raise 2 to the power of each element in input.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array where each element is outout of a sigmoid function for the corresponding value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_sigmoid)

    def exp(a):
        """
        Exponential of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the exponential of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_exp)

    def expm1(a):
        """
        Exponential of each element in the array minus 1.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the exponential of each value from `a`.

        Note
        -------
        - `a` must not be complex.
        - This function provides a more stable result for small values of `a`.
        """
        return _arith_unary_func(a, backend.get().af_expm1)

    def erf(a):
        """
        Error function of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the error function of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_erf)

    def erfc(a):
        """
        Complementary error function of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the complementary error function of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_erfc)

    def log(a):
        """
        Natural logarithm of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the natural logarithm of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_log)

    def log1p(a):
        """
        Logarithm of each element in the array plus 1.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the the values of `log(a) + 1`

        Note
        -------
        - `a` must not be complex.
        - This function provides a more stable result for small values of `a`.
        """
        return _arith_unary_func(a, backend.get().af_log1p)

    def log10(a):
        """
        Logarithm base 10 of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the logarithm base 10 of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_log10)

    def log2(a):
        """
        Logarithm base 2 of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the logarithm base 2 of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_log2)

    def sqrt(a):
        """
        Square root of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the square root of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_sqrt)

    def cbrt(a):
        """
        Cube root of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the cube root of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_cbrt)

    def factorial(a):
        """
        factorial of each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the factorial of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_factorial)

    def tgamma(a):
        """
        Performs the gamma function for each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the output of gamma function of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_tgamma)

    def lgamma(a):
        """
        Performs the logarithm of gamma function for each element in the array.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the output of logarithm of gamma function of each value from `a`.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_lgamma)

    def iszero(a):
        """
        Check if each element of the input is zero.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the output after checking if each value of `a` is 0.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_iszero)

    def isinf(a):
        """
        Check if each element of the input is infinity.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the output after checking if each value of `a` is inifnite.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_isinf)

    def isnan(a):
        """
        Check if each element of the input is NaN.

        Parameters
        ----------
        a : af.Array
            Multi dimensional arrayfire array.

        Returns
        --------
        out : af.Array
             array containing the output after checking if each value of `a` is NaN.

        Note
        -------
        `a` must not be complex.
        """
        return _arith_unary_func(a, backend.get().af_isnan)