/* -*- c -*- */

/*
 * This file is for the definitions of the non-c99 functions used in ufuncs.
 * All the complex ufuncs are defined here along with a smattering of real and
 * object functions.
 */

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include "npy_pycompat.h"


/*
 *****************************************************************************
 **                        PYTHON OBJECT FUNCTIONS                          **
 *****************************************************************************
 */

static PyObject *
Py_square(PyObject *o)
{
    return PyNumber_Multiply(o, o);
}

static PyObject *
Py_get_one(PyObject *NPY_UNUSED(o))
{
    return PyInt_FromLong(1);
}

static PyObject *
Py_reciprocal(PyObject *o)
{
    PyObject *one = PyInt_FromLong(1);
    PyObject *result;

    if (!one) {
        return NULL;
    }
#if defined(NPY_PY3K)
    result = PyNumber_TrueDivide(one, o);
#else
    result = PyNumber_Divide(one, o);
#endif
    Py_DECREF(one);
    return result;
}

/*
 * Define numpy version of PyNumber_Power as binary function.
 */
static PyObject *
npy_ObjectPower(PyObject *x, PyObject *y)
{
    return PyNumber_Power(x, y, Py_None);
}

/**begin repeat
 * #Kind = Max, Min#
 * #OP = Py_GE, Py_LE#
 */
static PyObject *
npy_Object@Kind@(PyObject *i1, PyObject *i2)
{
    PyObject *result;
    int cmp;

    cmp = PyObject_RichCompareBool(i1, i2, @OP@);
    if (cmp < 0) {
        return NULL;
    }
    if (cmp == 1) {
        result = i1;
    }
    else {
        result = i2;
    }
    Py_INCREF(result);
    return result;
}
/**end repeat**/

/* Emulates Python's 'a or b' behavior */
static PyObject *
npy_ObjectLogicalOr(PyObject *i1, PyObject *i2)
{
    if (i1 == NULL) {
        Py_XINCREF(i2);
        return i2;
    }
    else if (i2 == NULL) {
        Py_INCREF(i1);
        return i1;
    }
    else {
        int retcode = PyObject_IsTrue(i1);
        if (retcode == -1) {
            return NULL;
        }
        else if (retcode) {
            Py_INCREF(i1);
            return i1;
        }
        else {
            Py_INCREF(i2);
            return i2;
        }
    }
}

/* Emulates Python's 'a and b' behavior */
static PyObject *
npy_ObjectLogicalAnd(PyObject *i1, PyObject *i2)
{
    if (i1 == NULL) {
        return NULL;
    }
    else if (i2 == NULL) {
        return NULL;
    }
    else {
        int retcode = PyObject_IsTrue(i1);
        if (retcode == -1) {
            return NULL;
        }
        else if (!retcode) {
            Py_INCREF(i1);
            return i1;
        }
        else {
            Py_INCREF(i2);
            return i2;
        }
    }
}


/* Emulates Python's 'not b' behavior */
static PyObject *
npy_ObjectLogicalNot(PyObject *i1)
{
    if (i1 == NULL) {
        return NULL;
    }
    else {
        int retcode = PyObject_Not(i1);
        if (retcode == -1) {
            return NULL;
        }
        else if (retcode) {
            Py_INCREF(Py_True);
            return Py_True;
        }
        else {
            Py_INCREF(Py_False);
            return Py_False;
        }
    }
}

/*
 *****************************************************************************
 **                           COMPLEX FUNCTIONS                             **
 *****************************************************************************
 */


/*
 * Don't pass structures between functions (only pointers) because how
 * structures are passed is compiler dependent and could cause segfaults if
 * umath_ufunc_object.inc is compiled with a different compiler than an
 * extension that makes use of the UFUNC API
 */

/**begin repeat
 *
 * #ctype = npy_cfloat, npy_cdouble, npy_clongdouble#
 * #ftype = npy_float, npy_double, npy_longdouble#
 * #c = f, ,l#
 */

static void
nc_neg@c@(@ctype@ *a, @ctype@ *r)
{
    r->real = -a->real;
    r->imag = -a->imag;
    return;
}

static void
nc_pos@c@(@ctype@ *a, @ctype@ *r)
{
    r->real = +a->real;
    r->imag = +a->imag;
    return;
}

static void
nc_sqrt@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_csqrt@c@(*x);
    return;
}

static void
nc_rint@c@(@ctype@ *x, @ctype@ *r)
{
    r->real = npy_rint@c@(x->real);
    r->imag = npy_rint@c@(x->imag);
}

static void
nc_log@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_clog@c@(*x);
    return;
}

static void
nc_log1p@c@(@ctype@ *x, @ctype@ *r)
{
    @ftype@ l = npy_hypot@c@(x->real + 1,x->imag);
    r->imag = npy_atan2@c@(x->imag, x->real + 1);
    r->real = npy_log@c@(l);
    return;
}

static void
nc_exp@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_cexp@c@(*x);
    return;
}

static void
nc_exp2@c@(@ctype@ *x, @ctype@ *r)
{
    @ctype@ a;
    a.real = x->real*NPY_LOGE2@c@;
    a.imag = x->imag*NPY_LOGE2@c@;
    nc_exp@c@(&a, r);
    return;
}

static void
nc_expm1@c@(@ctype@ *x, @ctype@ *r)
{
    @ftype@ a = npy_exp@c@(x->real);
    r->real = a*npy_cos@c@(x->imag) - 1.0@c@;
    r->imag = a*npy_sin@c@(x->imag);
    return;
}

static void
nc_pow@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r)
{
   *r = npy_cpow@c@(*a, *b);
    return;
}

static void
nc_acos@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_cacos@c@(*x);
    return;
}

static void
nc_acosh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_cacosh@c@(*x);
    return;
}

static void
nc_asin@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_casin@c@(*x);
    return;
}


static void
nc_asinh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_casinh@c@(*x);
    return;
}

static void
nc_atan@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_catan@c@(*x);
    return;
}

static void
nc_atanh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_catanh@c@(*x);
    return;
}

static void
nc_cos@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_ccos@c@(*x);
    return;
}

static void
nc_cosh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_ccosh@c@(*x);
    return;
}

static void
nc_log10@c@(@ctype@ *x, @ctype@ *r)
{
    nc_log@c@(x, r);
    r->real *= NPY_LOG10E@c@;
    r->imag *= NPY_LOG10E@c@;
    return;
}

static void
nc_log2@c@(@ctype@ *x, @ctype@ *r)
{
    nc_log@c@(x, r);
    r->real *= NPY_LOG2E@c@;
    r->imag *= NPY_LOG2E@c@;
    return;
}

static void
nc_sin@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_csin@c@(*x);
    return;
}

static void
nc_sinh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_csinh@c@(*x);
    return;
}

static void
nc_tan@c@(@ctype@ *x, @ctype@ *r)
{
   *r = npy_ctan@c@(*x);
   return;
}

static void
nc_tanh@c@(@ctype@ *x, @ctype@ *r)
{
    *r = npy_ctanh@c@(*x);
    return;
}

/**end repeat**/
