/* -*- c -*- */
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "structmember.h"

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#ifndef _MULTIARRAYMODULE
#define _MULTIARRAYMODULE
#endif

#include "numpy/arrayobject.h"
#include "numpy/npy_math.h"
#include "numpy/halffloat.h"
#include "numpy/arrayscalars.h"

#include "npy_pycompat.h"

#include "npy_config.h"
#include "mapping.h"
#include "ctors.h"
#include "usertypes.h"
#include "numpyos.h"
#include "common.h"
#include "scalartypes.h"
#include "_datetime.h"
#include "datetime_strings.h"

#include <stdlib.h>

NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = {
    {PyObject_HEAD_INIT(&PyBoolArrType_Type) 0},
    {PyObject_HEAD_INIT(&PyBoolArrType_Type) 1},
};

/* TimeInteger is deleted, but still here to fill the API slot */
NPY_NO_EXPORT PyTypeObject PyTimeIntegerArrType_Type;

/*
 * Inheritance is established later when tp_bases is set (or tp_base for
 * single inheritance)
 */

/**begin repeat
 * #name = number, integer, signedinteger, unsignedinteger, inexact,
 *         floating, complexfloating, flexible, character#
 * #NAME = Number, Integer, SignedInteger, UnsignedInteger, Inexact,
 *         Floating, ComplexFloating, Flexible, Character#
 */
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
#endif
    "numpy.@name@",                             /* tp_name*/
    sizeof(PyObject),                           /* tp_basicsize*/
    0,                                          /* tp_itemsize */
    /* methods */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    0,                                          /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};
/**end repeat**/

static PyObject *
gentype_alloc(PyTypeObject *type, Py_ssize_t nitems)
{
    PyObject *obj;
    const size_t size = _PyObject_VAR_SIZE(type, nitems + 1);

    obj = (PyObject *)PyObject_Malloc(size);
    /*
     * Fixme. Need to check for no memory.
     * If we don't need to zero memory, we could use
     * PyObject_{New, NewVar} for this whole function.
     */
    memset(obj, 0, size);
    if (type->tp_itemsize == 0) {
        PyObject_Init(obj, type);
    }
    else {
        (void) PyObject_InitVar((PyVarObject *)obj, type, nitems);
    }
    return obj;
}

static void
gentype_dealloc(PyObject *v)
{
    Py_TYPE(v)->tp_free(v);
}

static void
gentype_free(PyObject *v)
{
    /*
     * have an explicit tp_free to enforce inheritance from it.
     * PyObject_Free is also the tp_free of PyBaseObject so python does not
     * COPYSLOT it, instead it takes the next parent PyInt which has a
     * different allocator
     */
    PyObject_Free(v);
}


static PyObject *
gentype_power(PyObject *m1, PyObject *m2, PyObject *NPY_UNUSED(m3))
{
    PyObject *arr, *ret, *arg2;
    char *msg="unsupported operand type(s) for ** or pow()";

    if (!PyArray_IsScalar(m1, Generic)) {
        if (PyArray_Check(m1)) {
            ret = Py_TYPE(m1)->tp_as_number->nb_power(m1,m2, Py_None);
        }
        else {
            if (!PyArray_IsScalar(m2, Generic)) {
                PyErr_SetString(PyExc_TypeError, msg);
                return NULL;
            }
            arr = PyArray_FromScalar(m2, NULL);
            if (arr == NULL) {
                return NULL;
            }
            ret = Py_TYPE(arr)->tp_as_number->nb_power(m1, arr, Py_None);
            Py_DECREF(arr);
        }
        return ret;
    }
    if (!PyArray_IsScalar(m2, Generic)) {
        if (PyArray_Check(m2)) {
            ret = Py_TYPE(m2)->tp_as_number->nb_power(m1,m2, Py_None);
        }
        else {
            if (!PyArray_IsScalar(m1, Generic)) {
                PyErr_SetString(PyExc_TypeError, msg);
                return NULL;
            }
            arr = PyArray_FromScalar(m1, NULL);
            if (arr == NULL) {
                return NULL;
            }
            ret = Py_TYPE(arr)->tp_as_number->nb_power(arr, m2, Py_None);
            Py_DECREF(arr);
        }
        return ret;
    }
    arr = arg2 = NULL;
    arr = PyArray_FromScalar(m1, NULL);
    arg2 = PyArray_FromScalar(m2, NULL);
    if (arr == NULL || arg2 == NULL) {
        Py_XDECREF(arr);
        Py_XDECREF(arg2);
        return NULL;
    }
    ret = Py_TYPE(arr)->tp_as_number->nb_power(arr, arg2, Py_None);
    Py_DECREF(arr);
    Py_DECREF(arg2);
    return ret;
}

static PyObject *
gentype_generic_method(PyObject *self, PyObject *args, PyObject *kwds,
        char *str)
{
    PyObject *arr, *meth, *ret;

    arr = PyArray_FromScalar(self, NULL);
    if (arr == NULL) {
        return NULL;
    }
    meth = PyObject_GetAttrString(arr, str);
    if (meth == NULL) {
        Py_DECREF(arr);
        return NULL;
    }
    if (kwds == NULL) {
        ret = PyObject_CallObject(meth, args);
    }
    else {
        ret = PyObject_Call(meth, args, kwds);
    }
    Py_DECREF(meth);
    Py_DECREF(arr);
    if (ret && PyArray_Check(ret)) {
        return PyArray_Return((PyArrayObject *)ret);
    }
    else {
        return ret;
    }
}

/**begin repeat
 *
 * #name = add, subtract, remainder, divmod, lshift, rshift,
 *         and, xor, or, floor_divide, true_divide#
 */
static PyObject *
gentype_@name@(PyObject *m1, PyObject *m2)
{
    return PyArray_Type.tp_as_number->nb_@name@(m1, m2);
}

/**end repeat**/

#if !defined(NPY_PY3K)
/**begin repeat
 *
 * #name = divide#
 */
static PyObject *
gentype_@name@(PyObject *m1, PyObject *m2)
{
    return PyArray_Type.tp_as_number->nb_@name@(m1, m2);
}
/**end repeat**/
#endif

static PyObject *
gentype_multiply(PyObject *m1, PyObject *m2)
{
    PyObject *ret = NULL;
    npy_intp repeat;

    if (!PyArray_IsScalar(m1, Generic) &&
            ((Py_TYPE(m1)->tp_as_number == NULL) ||
             (Py_TYPE(m1)->tp_as_number->nb_multiply == NULL))) {
        /* Try to convert m2 to an int and try sequence repeat */
        repeat = PyArray_PyIntAsIntp(m2);
        if (repeat == -1 && PyErr_Occurred()) {
            return NULL;
        }
        /* Note that npy_intp is compatible to Py_Ssize_t */
        ret = PySequence_Repeat(m1, repeat);
    }
    else if (!PyArray_IsScalar(m2, Generic) &&
            ((Py_TYPE(m2)->tp_as_number == NULL) ||
             (Py_TYPE(m2)->tp_as_number->nb_multiply == NULL))) {
        /* Try to convert m1 to an int and try sequence repeat */
        repeat = PyArray_PyIntAsIntp(m1);
        if (repeat == -1 && PyErr_Occurred()) {
            return NULL;
        }
        ret = PySequence_Repeat(m2, repeat);
    }
    if (ret == NULL) {
        PyErr_Clear(); /* no effect if not set */
        ret = PyArray_Type.tp_as_number->nb_multiply(m1, m2);
    }
    return ret;
}

/**begin repeat
 *
 * #name = positive, negative, absolute, invert, int, float#
 */
static PyObject *
gentype_@name@(PyObject *m1)
{
    PyObject *arr, *ret;

    arr = PyArray_FromScalar(m1, NULL);
    if (arr == NULL) {
        return NULL;
    }
    ret = Py_TYPE(arr)->tp_as_number->nb_@name@(arr);
    Py_DECREF(arr);
    return ret;
}
/**end repeat**/

#if !defined(NPY_PY3K)
/**begin repeat
 *
 * #name = long, oct, hex#
 */
static PyObject *
gentype_@name@(PyObject *m1)
{
    PyObject *arr, *ret;

    arr = PyArray_FromScalar(m1, NULL);
    if (arr == NULL) {
        return NULL;
    }
    ret = Py_TYPE(arr)->tp_as_number->nb_@name@(arr);
    Py_DECREF(arr);
    return ret;
}
/**end repeat**/
#endif

static int
gentype_nonzero_number(PyObject *m1)
{
    PyObject *arr;
    int ret;

    arr = PyArray_FromScalar(m1, NULL);
    if (arr == NULL) {
        return -1;
    }
#if defined(NPY_PY3K)
    ret = Py_TYPE(arr)->tp_as_number->nb_bool(arr);
#else
    ret = Py_TYPE(arr)->tp_as_number->nb_nonzero(arr);
#endif
    Py_DECREF(arr);
    return ret;
}

static PyObject *
gentype_str(PyObject *self)
{
    PyObject *arr, *ret = NULL;

    arr = PyArray_FromScalar(self, NULL);
    if (arr != NULL) {
        ret = PyObject_Str((PyObject *)arr);
        Py_DECREF(arr);
    }
    return ret;
}


static PyObject *
gentype_repr(PyObject *self)
{
    PyObject *arr, *ret = NULL;

    arr = PyArray_FromScalar(self, NULL);
    if (arr != NULL) {
        /* XXX: Why are we using str here? */
        ret = PyObject_Str((PyObject *)arr);
        Py_DECREF(arr);
    }
    return ret;
}

/*
 * The __format__ method for PEP 3101.
 */
static PyObject *
gentype_format(PyObject *self, PyObject *args)
{
    PyObject *format_spec;
    PyObject *obj, *ret;

#if defined(NPY_PY3K)
    if (!PyArg_ParseTuple(args, "U:__format__", &format_spec)) {
        return NULL;
    }
#else
    if (!PyArg_ParseTuple(args, "O:__format__", &format_spec)) {
        return NULL;
    }

    if (!PyUnicode_Check(format_spec) && !PyString_Check(format_spec)) {
        PyErr_SetString(PyExc_TypeError,
                "format must be a string");
        return NULL;
    }
#endif

    /*
     * Convert to an appropriate Python type and call its format.
     * TODO: For some types, like long double, this isn't right,
     *       because it throws away precision.
     */
    if (Py_TYPE(self) == &PyBoolArrType_Type) {
        obj = PyBool_FromLong(((PyBoolScalarObject *)self)->obval);
    }
    else if (PyArray_IsScalar(self, Integer)) {
#if defined(NPY_PY3K)
        obj = Py_TYPE(self)->tp_as_number->nb_int(self);
#else
        obj = Py_TYPE(self)->tp_as_number->nb_long(self);
#endif
    }
    else if (PyArray_IsScalar(self, Floating)) {
        obj = Py_TYPE(self)->tp_as_number->nb_float(self);
    }
    else if (PyArray_IsScalar(self, ComplexFloating)) {
        double val[2];
        PyArray_Descr *dtype = PyArray_DescrFromScalar(self);

        if (dtype == NULL) {
            return NULL;
        }
        if (PyArray_CastScalarDirect(self, dtype, &val[0], NPY_CDOUBLE) < 0) {
            Py_DECREF(dtype);
            return NULL;
        }
        obj = PyComplex_FromDoubles(val[0], val[1]);
        Py_DECREF(dtype);
    }
    else {
        obj = PyObject_Str(self);
    }

    if (obj == NULL) {
        return NULL;
    }

    ret = PyObject_Format(obj, format_spec);
    Py_DECREF(obj);
    return ret;
}

#ifdef FORCE_NO_LONG_DOUBLE_FORMATTING
#undef NPY_LONGDOUBLE_FMT
#define NPY_LONGDOUBLE_FMT NPY_DOUBLE_FMT
#endif

/**begin repeat
 * #name = float, double, longdouble#
 * #NAME = FLOAT, DOUBLE, LONGDOUBLE#
 * #type = npy_float, npy_double, npy_longdouble#
 * #suff = f, d, l#
 */

#define _FMT1 "%%.%i" NPY_@NAME@_FMT
#define _FMT2 "%%+.%i" NPY_@NAME@_FMT

NPY_NO_EXPORT void
format_@name@(char *buf, size_t buflen, @type@ val, unsigned int prec)
{
    /* XXX: Find a correct size here for format string */
    char format[64], *res;
    size_t i, cnt;

    PyOS_snprintf(format, sizeof(format), _FMT1, prec);
    res = NumPyOS_ascii_format@suff@(buf, buflen, format, val, 0);
    if (res == NULL) {
        fprintf(stderr, "Error while formatting\n");
        return;
    }

    /* If nothing but digits after sign, append ".0" */
    cnt = strlen(buf);
    for (i = (buf[0] == '-') ? 1 : 0; i < cnt; ++i) {
        if (!isdigit(Py_CHARMASK(buf[i]))) {
            break;
        }
    }
    if (i == cnt && buflen >= cnt + 3) {
        strcpy(&buf[cnt],".0");
    }
}

#undef _FMT1
#undef _FMT2

/**end repeat**/

/**begin repeat
 * #name = cfloat, cdouble, clongdouble#
 * #NAME = FLOAT, DOUBLE, LONGDOUBLE#
 * #type = npy_cfloat, npy_cdouble, npy_clongdouble#
 * #suff = f, d, l#
 */

#define _FMT1 "%%.%i" NPY_@NAME@_FMT
#define _FMT2 "%%+.%i" NPY_@NAME@_FMT

static void
format_@name@(char *buf, size_t buflen, @type@ val, unsigned int prec)
{
    /* XXX: Find a correct size here for format string */
    char format[64];
    char *res;

    /*
     * Ideally, we should handle this nan/inf stuff in NumpyOS_ascii_format*
     */
#if PY_VERSION_HEX >= 0x02070000
    if (val.real == 0.0 && npy_signbit(val.real) == 0) {
#else
    if (val.real == 0.0) {
#endif
        PyOS_snprintf(format, sizeof(format), _FMT1, prec);
        res = NumPyOS_ascii_format@suff@(buf, buflen - 1, format, val.imag, 0);
        if (res == NULL) {
            /* FIXME
             * We need a better way to handle the error message
             */
            fprintf(stderr, "Error while formatting\n");
            return;
        }
        if (!npy_isfinite(val.imag)) {
            strncat(buf, "*", 1);
        }
        strncat(buf, "j", 1);
    }
    else {
        char re[64], im[64];
        if (npy_isfinite(val.real)) {
                PyOS_snprintf(format, sizeof(format), _FMT1, prec);
                res = NumPyOS_ascii_format@suff@(re, sizeof(re), format,
                        val.real, 0);
                if (res == NULL) {
                    /* FIXME
                     * We need a better way to handle the error message
                     */
                    fprintf(stderr, "Error while formatting\n");
                    return;
                }
        }
        else {
                if (npy_isnan(val.real)) {
                        strcpy(re, "nan");
                }
                else if (val.real > 0){
                        strcpy(re, "inf");
                }
                else {
                        strcpy(re, "-inf");
                }
        }


        if (npy_isfinite(val.imag)) {
                PyOS_snprintf(format, sizeof(format), _FMT2, prec);
                res = NumPyOS_ascii_format@suff@(im, sizeof(im), format,
                        val.imag, 0);
                if (res == NULL) {
                    fprintf(stderr, "Error while formatting\n");
                    return;
                }
        }
        else {
                if (npy_isnan(val.imag)) {
                        strcpy(im, "+nan");
                }
                else if (val.imag > 0){
                        strcpy(im, "+inf");
                }
                else {
                        strcpy(im, "-inf");
                }
                if (!npy_isfinite(val.imag)) {
                        strncat(im, "*", 1);
                }
        }
        PyOS_snprintf(buf, buflen, "(%s%sj)", re, im);
    }
}

#undef _FMT1
#undef _FMT2

/**end repeat**/

NPY_NO_EXPORT void
format_half(char *buf, size_t buflen, npy_half val, unsigned int prec)
{
    format_float(buf, buflen, npy_half_to_float(val), prec);
}

/*
 * over-ride repr and str of array-scalar strings and unicode to
 * remove NULL bytes and then call the corresponding functions
 * of string and unicode.
 */

/**begin repeat
 * #name = string*2,unicode*2#
 * #form = (repr,str)*2#
 * #Name = String*2,Unicode*2#
 * #NAME = STRING*2,UNICODE*2#
 * #extra = AndSize*2,,#
 * #type = npy_char*2, Py_UNICODE*2#
 */
static PyObject *
@name@type_@form@(PyObject *self)
{
    const @type@ *dptr, *ip;
    int len;
    PyObject *new;
    PyObject *ret;

    ip = dptr = Py@Name@_AS_@NAME@(self);
    len = Py@Name@_GET_SIZE(self);
    dptr += len-1;
    while(len > 0 && *dptr-- == 0) {
        len--;
    }
    new = Py@Name@_From@Name@@extra@(ip, len);
    if (new == NULL) {
        return PyUString_FromString("");
    }
    ret = Py@Name@_Type.tp_@form@(new);
    Py_DECREF(new);
    return ret;
}
/**end repeat**/

static PyObject *
datetimetype_repr(PyObject *self)
{
    PyDatetimeScalarObject *scal;
    npy_datetimestruct dts;
    PyObject *ret;
    char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
    NPY_DATETIMEUNIT unit;

    if (!PyArray_IsScalar(self, Datetime)) {
        PyErr_SetString(PyExc_RuntimeError,
                "Called NumPy datetime repr on a non-datetime type");
        return NULL;
    }

    scal = (PyDatetimeScalarObject *)self;

    if (convert_datetime_to_datetimestruct(&scal->obmeta,
                scal->obval, &dts) < 0) {
        return NULL;
    }

    unit = scal->obmeta.base;
    if (make_iso_8601_datetime(&dts, iso, sizeof(iso), 0, 0,
                            unit, -1, NPY_SAFE_CASTING) < 0) {
        return NULL;
    }

    /*
     * For straight units or generic units, the unit will be deduced
     * from the string, so it's not necessary to specify it.
     */
    if ((scal->obmeta.num == 1 && scal->obmeta.base != NPY_FR_h) ||
            scal->obmeta.base == NPY_FR_GENERIC) {
        ret = PyUString_FromString("numpy.datetime64('");
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString(iso));
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString("')"));
    }
    else {
        ret = PyUString_FromString("numpy.datetime64('");
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString(iso));
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString("','"));
        ret = append_metastr_to_string(&scal->obmeta, 1, ret);
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString("')"));
    }

    return ret;
}

static PyObject *
timedeltatype_repr(PyObject *self)
{
    PyTimedeltaScalarObject *scal;
    PyObject *ret;

    if (!PyArray_IsScalar(self, Timedelta)) {
        PyErr_SetString(PyExc_RuntimeError,
                "Called NumPy timedelta repr on a non-datetime type");
        return NULL;
    }

    scal = (PyTimedeltaScalarObject *)self;

    /* The value */
    if (scal->obval == NPY_DATETIME_NAT) {
        ret = PyUString_FromString("numpy.timedelta64('NaT'");
    }
    else {
        /*
         * Can't use "%lld" in Python < 2.7, Python3 < 3.2,
         * or if HAVE_LONG_LONG is not defined
         */
#if defined(HAVE_LONG_LONG) && \
           ((PY_VERSION_HEX >= 0x02070000 && PY_VERSION_HEX < 0x03000000) || \
            (PY_VERSION_HEX >= 0x03020000))
        ret = PyUString_FromFormat("numpy.timedelta64(%lld",
                                            (long long)scal->obval);
#else
        ret = PyUString_FromFormat("numpy.timedelta64(%ld",
                                            (long)scal->obval);
#endif
    }
    /* The metadata unit */
    if (scal->obmeta.base == NPY_FR_GENERIC) {
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString(")"));
    }
    else {
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString(",'"));
        ret = append_metastr_to_string(&scal->obmeta, 1, ret);
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString("')"));
    }

    return ret;
}

static PyObject *
datetimetype_str(PyObject *self)
{
    PyDatetimeScalarObject *scal;
    npy_datetimestruct dts;
    char iso[NPY_DATETIME_MAX_ISO8601_STRLEN];
    NPY_DATETIMEUNIT unit;

    if (!PyArray_IsScalar(self, Datetime)) {
        PyErr_SetString(PyExc_RuntimeError,
                "Called NumPy datetime str on a non-datetime type");
        return NULL;
    }

    scal = (PyDatetimeScalarObject *)self;

    if (convert_datetime_to_datetimestruct(&scal->obmeta, scal->obval,
                                                            &dts) < 0) {
        return NULL;
    }

    unit = scal->obmeta.base;
    if (make_iso_8601_datetime(&dts, iso, sizeof(iso), 0, 0,
                            unit, -1, NPY_SAFE_CASTING) < 0) {
        return NULL;
    }

    return PyUString_FromString(iso);
}

static char *_datetime_verbose_strings[NPY_DATETIME_NUMUNITS] = {
    "years",
    "months",
    "weeks",
    "<invalid>",
    "days",
    "hours",
    "minutes",
    "seconds",
    "milliseconds",
    "microseconds",
    "nanoseconds",
    "picoseconds",
    "femtoseconds",
    "attoseconds",
    "generic time units"
};

static PyObject *
timedeltatype_str(PyObject *self)
{
    PyTimedeltaScalarObject *scal;
    PyObject *ret;
    char *basestr = "invalid";

    if (!PyArray_IsScalar(self, Timedelta)) {
        PyErr_SetString(PyExc_RuntimeError,
                "Called NumPy timedelta str on a non-datetime type");
        return NULL;
    }

    scal = (PyTimedeltaScalarObject *)self;

    if (scal->obmeta.base >= 0 && scal->obmeta.base < NPY_DATETIME_NUMUNITS) {
        basestr = _datetime_verbose_strings[scal->obmeta.base];
    }
    else {
        PyErr_SetString(PyExc_RuntimeError,
                "NumPy datetime metadata is corrupted");
        return NULL;
    }

    if (scal->obval == NPY_DATETIME_NAT) {
        ret = PyUString_FromString("NaT");
    }
    else {
        /*
         * Can't use "%lld" in Python < 2.7, Python3 < 3.2,
         * or if HAVE_LONG_LONG is not defined
         */
#if defined(HAVE_LONG_LONG) && \
           ((PY_VERSION_HEX >= 0x02070000 && PY_VERSION_HEX < 0x03000000) || \
            (PY_VERSION_HEX >= 0x03020000))
        ret = PyUString_FromFormat("%lld ",
                                (long long)(scal->obval * scal->obmeta.num));
#else
        ret = PyUString_FromFormat("%ld ",
                                (long)(scal->obval * scal->obmeta.num));
#endif
        PyUString_ConcatAndDel(&ret,
                PyUString_FromString(basestr));
    }

    return ret;
}

/* The REPR values are finfo.precision + 2 */
#define HALFPREC_REPR 5
#define HALFPREC_STR 5
#define FLOATPREC_REPR 8
#define FLOATPREC_STR 6
#define DOUBLEPREC_REPR 17
#define DOUBLEPREC_STR 12
#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE
#define LONGDOUBLEPREC_REPR DOUBLEPREC_REPR
#define LONGDOUBLEPREC_STR DOUBLEPREC_STR
#else /* More than probably needed on Intel FP */
#define LONGDOUBLEPREC_REPR 20
#define LONGDOUBLEPREC_STR 12
#endif

/*
 * float type str and repr
 *
 * These functions will return NULL if PyString creation fails.
 */

/**begin repeat
 * #name = half, float, double, longdouble#
 * #Name = Half, Float, Double, LongDouble#
 * #NAME = HALF, FLOAT, DOUBLE, LONGDOUBLE#
 * #hascomplex = 0, 1, 1, 1#
 */
/**begin repeat1
 * #kind = str, repr#
 * #KIND = STR, REPR#
 */

#define PREC @NAME@PREC_@KIND@

static PyObject *
@name@type_@kind@(PyObject *self)
{
    char buf[100];
    npy_@name@ val = ((Py@Name@ScalarObject *)self)->obval;

    format_@name@(buf, sizeof(buf), val, PREC);
    return PyUString_FromString(buf);
}

#if @hascomplex@
static PyObject *
c@name@type_@kind@(PyObject *self)
{
    char buf[202];
    npy_c@name@ val = ((PyC@Name@ScalarObject *)self)->obval;

    format_c@name@(buf, sizeof(buf), val, PREC);
    return PyUString_FromString(buf);
}
#endif

#undef PREC

/**end repeat1**/
/**end repeat**/

/*
 * float type print (control print a, where a is a float type instance)
 */
/**begin repeat
 * #name = half, float, double, longdouble#
 * #Name = Half, Float, Double, LongDouble#
 * #NAME = HALF, FLOAT, DOUBLE, LONGDOUBLE#
 * #hascomplex = 0, 1, 1, 1#
 */

static int
@name@type_print(PyObject *v, FILE *fp, int flags)
{
        char buf[100];
        npy_@name@ val = ((Py@Name@ScalarObject *)v)->obval;

        format_@name@(buf, sizeof(buf), val,
                      (flags & Py_PRINT_RAW) ? @NAME@PREC_STR : @NAME@PREC_REPR);
        Py_BEGIN_ALLOW_THREADS
        fputs(buf, fp);
        Py_END_ALLOW_THREADS
        return 0;
}

#if @hascomplex@
static int
c@name@type_print(PyObject *v, FILE *fp, int flags)
{
        /* Size of buf: twice sizeof(real) + 2 (for the parenthesis) */
        char buf[202];
        npy_c@name@ val = ((PyC@Name@ScalarObject *)v)->obval;

        format_c@name@(buf, sizeof(buf), val,
                       (flags & Py_PRINT_RAW) ? @NAME@PREC_STR : @NAME@PREC_REPR);
        Py_BEGIN_ALLOW_THREADS
        fputs(buf, fp);
        Py_END_ALLOW_THREADS
        return 0;
}
#endif

/**end repeat**/


/*
 * Could improve this with a PyLong_FromLongDouble(longdouble ldval)
 * but this would need some more work...
 */

/**begin repeat
 *
 * #name = (int, float)*2#
 * #KIND = (Long, Float)*2#
 * #char = ,,c*2#
 * #CHAR = ,,C*2#
 * #POST = ,,.real*2#
 */
static PyObject *
@char@longdoubletype_@name@(PyObject *self)
{
    double dval;
    PyObject *obj, *ret;

    dval = (double)(((Py@CHAR@LongDoubleScalarObject *)self)->obval)@POST@;
    obj = Py@KIND@_FromDouble(dval);
    if (obj == NULL) {
        return NULL;
    }
    ret = Py_TYPE(obj)->tp_as_number->nb_@name@(obj);
    Py_DECREF(obj);
    return ret;
}
/**end repeat**/

#if !defined(NPY_PY3K)

/**begin repeat
 *
 * #name = (long, hex, oct)*2#
 * #KIND = (Long*3)*2#
 * #char = ,,,c*3#
 * #CHAR = ,,,C*3#
 * #POST = ,,,.real*3#
 */
static PyObject *
@char@longdoubletype_@name@(PyObject *self)
{
    double dval;
    PyObject *obj, *ret;

    dval = (double)(((Py@CHAR@LongDoubleScalarObject *)self)->obval)@POST@;
    obj = Py@KIND@_FromDouble(dval);
    if (obj == NULL) {
        return NULL;
    }
    ret = Py_TYPE(obj)->tp_as_number->nb_@name@(obj);
    Py_DECREF(obj);
    return ret;
}
/**end repeat**/

#endif /* !defined(NPY_PY3K) */

static PyNumberMethods gentype_as_number = {
    (binaryfunc)gentype_add,                     /*nb_add*/
    (binaryfunc)gentype_subtract,                /*nb_subtract*/
    (binaryfunc)gentype_multiply,                /*nb_multiply*/
#if defined(NPY_PY3K)
#else
    (binaryfunc)gentype_divide,                  /*nb_divide*/
#endif
    (binaryfunc)gentype_remainder,               /*nb_remainder*/
    (binaryfunc)gentype_divmod,                  /*nb_divmod*/
    (ternaryfunc)gentype_power,                  /*nb_power*/
    (unaryfunc)gentype_negative,
    (unaryfunc)gentype_positive,                 /*nb_pos*/
    (unaryfunc)gentype_absolute,                 /*(unaryfunc)gentype_abs,*/
    (inquiry)gentype_nonzero_number,             /*nb_nonzero*/
    (unaryfunc)gentype_invert,                   /*nb_invert*/
    (binaryfunc)gentype_lshift,                  /*nb_lshift*/
    (binaryfunc)gentype_rshift,                  /*nb_rshift*/
    (binaryfunc)gentype_and,                     /*nb_and*/
    (binaryfunc)gentype_xor,                     /*nb_xor*/
    (binaryfunc)gentype_or,                      /*nb_or*/
#if defined(NPY_PY3K)
#else
    0,                                           /*nb_coerce*/
#endif
    (unaryfunc)gentype_int,                      /*nb_int*/
#if defined(NPY_PY3K)
    0,                                           /*nb_reserved*/
#else
    (unaryfunc)gentype_long,                     /*nb_long*/
#endif
    (unaryfunc)gentype_float,                    /*nb_float*/
#if defined(NPY_PY3K)
#else
    (unaryfunc)gentype_oct,                      /*nb_oct*/
    (unaryfunc)gentype_hex,                      /*nb_hex*/
#endif
    0,                                           /*inplace_add*/
    0,                                           /*inplace_subtract*/
    0,                                           /*inplace_multiply*/
#if defined(NPY_PY3K)
#else
    0,                                           /*inplace_divide*/
#endif
    0,                                           /*inplace_remainder*/
    0,                                           /*inplace_power*/
    0,                                           /*inplace_lshift*/
    0,                                           /*inplace_rshift*/
    0,                                           /*inplace_and*/
    0,                                           /*inplace_xor*/
    0,                                           /*inplace_or*/
    (binaryfunc)gentype_floor_divide,            /*nb_floor_divide*/
    (binaryfunc)gentype_true_divide,             /*nb_true_divide*/
    0,                                           /*nb_inplace_floor_divide*/
    0,                                           /*nb_inplace_true_divide*/
    (unaryfunc)NULL,                             /*nb_index*/
};


static PyObject *
gentype_richcompare(PyObject *self, PyObject *other, int cmp_op)
{
    PyObject *arr, *ret;

    /*
     * If the other object is None, False is always right. This avoids
     * the array None comparison, at least until deprecation it is fixed.
     * After that, this may be removed and numpy false would be returned.
     *
     * NOTE: np.equal(NaT, None) evaluates to TRUE! This is an
     *       an inconsistency, which may has to be considered
     *       when the deprecation is finished.
     */
    if (other == Py_None) {
        if (cmp_op == Py_EQ) {
            Py_RETURN_FALSE;
        }
        if (cmp_op == Py_NE) {
            Py_RETURN_TRUE;
        }
    }

    arr = PyArray_FromScalar(self, NULL);
    if (arr == NULL) {
        return NULL;
    }
    /*
     * Call via PyObject_RichCompare to ensure that other.__eq__
     * has a chance to run when necessary
     */
    ret = PyObject_RichCompare(arr, other, cmp_op);
    Py_DECREF(arr);
    return ret;
}

static PyObject *
gentype_ndim_get(PyObject *NPY_UNUSED(self))
{
    return PyInt_FromLong(0);
}

static PyObject *
gentype_flags_get(PyObject *NPY_UNUSED(self))
{
    return PyArray_NewFlagsObject(NULL);
}

static PyObject *
voidtype_flags_get(PyVoidScalarObject *self)
{
    PyObject *flagobj;
    flagobj = PyArrayFlags_Type.tp_alloc(&PyArrayFlags_Type, 0);
    if (flagobj == NULL) {
        return NULL;
    }
    ((PyArrayFlagsObject *)flagobj)->arr = NULL;
    ((PyArrayFlagsObject *)flagobj)->flags = self->flags;
    return flagobj;
}

static PyObject *
voidtype_dtypedescr_get(PyVoidScalarObject *self)
{
    Py_INCREF(self->descr);
    return (PyObject *)self->descr;
}


static PyObject *
inttype_numerator_get(PyObject *self)
{
    Py_INCREF(self);
    return self;
}


static PyObject *
inttype_denominator_get(PyObject *self)
{
    return PyInt_FromLong(1);
}


static PyObject *
gentype_data_get(PyObject *self)
{
#if defined(NPY_PY3K)
    return PyMemoryView_FromObject(self);
#else
    return PyBuffer_FromObject(self, 0, Py_END_OF_BUFFER);
#endif
}


static PyObject *
gentype_itemsize_get(PyObject *self)
{
    PyArray_Descr *typecode;
    PyObject *ret;
    int elsize;

    typecode = PyArray_DescrFromScalar(self);
    elsize = typecode->elsize;
#ifndef Py_UNICODE_WIDE
    if (typecode->type_num == NPY_UNICODE) {
        elsize >>= 1;
    }
#endif
    ret = PyInt_FromLong((long) elsize);
    Py_DECREF(typecode);
    return ret;
}

static PyObject *
gentype_size_get(PyObject *NPY_UNUSED(self))
{
    return PyInt_FromLong(1);
}

static PyObject *
gentype_sizeof(PyObject *self)
{
    Py_ssize_t nbytes;
    PyObject * isz = gentype_itemsize_get(self);
    if (isz == NULL) {
        return NULL;
    }
    nbytes = PyLong_AsLong(isz) + Py_TYPE(self)->tp_basicsize +
        Py_SIZE(self) * Py_TYPE(self)->tp_itemsize;
    Py_DECREF(isz);
    return PyLong_FromSsize_t(nbytes);
}

#if PY_VERSION_HEX >= 0x03000000
NPY_NO_EXPORT void
gentype_struct_free(PyObject *ptr)
{
    PyArrayInterface *arrif;
    PyObject *context;

    arrif = (PyArrayInterface*)PyCapsule_GetPointer(ptr, NULL);
    context = (PyObject *)PyCapsule_GetContext(ptr);
    Py_DECREF(context);
    Py_XDECREF(arrif->descr);
    PyArray_free(arrif->shape);
    PyArray_free(arrif);
}
#else
NPY_NO_EXPORT void
gentype_struct_free(void *ptr, void *arg)
{
    PyArrayInterface *arrif = (PyArrayInterface *)ptr;
    Py_DECREF((PyObject *)arg);
    Py_XDECREF(arrif->descr);
    PyArray_free(arrif->shape);
    PyArray_free(arrif);
}
#endif

static PyObject *
gentype_struct_get(PyObject *self)
{
    PyArrayObject *arr;
    PyArrayInterface *inter;
    PyObject *ret;

    arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
    inter = (PyArrayInterface *)PyArray_malloc(sizeof(PyArrayInterface));
    inter->two = 2;
    inter->nd = 0;
    inter->flags = PyArray_FLAGS(arr);
    inter->flags &= ~(NPY_ARRAY_UPDATEIFCOPY | NPY_ARRAY_OWNDATA);
    inter->flags |= NPY_ARRAY_NOTSWAPPED;
    inter->typekind = PyArray_DESCR(arr)->kind;
    inter->itemsize = PyArray_DESCR(arr)->elsize;
    inter->strides = NULL;
    inter->shape = NULL;
    inter->data = PyArray_DATA(arr);
    inter->descr = NULL;

    ret = NpyCapsule_FromVoidPtrAndDesc(inter, arr, gentype_struct_free);
    return ret;
}

static PyObject *
gentype_priority_get(PyObject *NPY_UNUSED(self))
{
    return PyFloat_FromDouble(NPY_SCALAR_PRIORITY);
}

static PyObject *
gentype_shape_get(PyObject *NPY_UNUSED(self))
{
    return PyTuple_New(0);
}


static PyObject *
gentype_interface_get(PyObject *self)
{
    PyArrayObject *arr;
    PyObject *inter;

    arr = (PyArrayObject *)PyArray_FromScalar(self, NULL);
    if (arr == NULL) {
        return NULL;
    }
    inter = PyObject_GetAttrString((PyObject *)arr, "__array_interface__");
    if (inter != NULL) {
        PyDict_SetItemString(inter, "__ref", (PyObject *)arr);
    }
    Py_DECREF(arr);
    return inter;
}



static PyObject *
gentype_typedescr_get(PyObject *self)
{
    return (PyObject *)PyArray_DescrFromScalar(self);
}


static PyObject *
gentype_base_get(PyObject *NPY_UNUSED(self))
{
    Py_RETURN_NONE;
}


static PyArray_Descr *
_realdescr_fromcomplexscalar(PyObject *self, int *typenum)
{
    if (PyArray_IsScalar(self, CDouble)) {
        *typenum = NPY_CDOUBLE;
        return PyArray_DescrFromType(NPY_DOUBLE);
    }
    if (PyArray_IsScalar(self, CFloat)) {
        *typenum = NPY_CFLOAT;
        return PyArray_DescrFromType(NPY_FLOAT);
    }
    if (PyArray_IsScalar(self, CLongDouble)) {
        *typenum = NPY_CLONGDOUBLE;
        return PyArray_DescrFromType(NPY_LONGDOUBLE);
    }
    return NULL;
}

static PyObject *
gentype_real_get(PyObject *self)
{
    PyArray_Descr *typecode;
    PyObject *ret;
    int typenum;

    if (PyArray_IsScalar(self, ComplexFloating)) {
        void *ptr;
        typecode = _realdescr_fromcomplexscalar(self, &typenum);
        ptr = scalar_value(self, NULL);
        ret = PyArray_Scalar(ptr, typecode, NULL);
        Py_DECREF(typecode);
        return ret;
    }
    else if (PyArray_IsScalar(self, Object)) {
        PyObject *obj = ((PyObjectScalarObject *)self)->obval;
        ret = PyObject_GetAttrString(obj, "real");
        if (ret != NULL) {
            return ret;
        }
        PyErr_Clear();
    }
    Py_INCREF(self);
    return (PyObject *)self;
}

static PyObject *
gentype_imag_get(PyObject *self)
{
    PyArray_Descr *typecode=NULL;
    PyObject *ret;
    int typenum;

    if (PyArray_IsScalar(self, ComplexFloating)) {
        char *ptr;
        typecode = _realdescr_fromcomplexscalar(self, &typenum);
        ptr = (char *)scalar_value(self, NULL);
        ret = PyArray_Scalar(ptr + typecode->elsize, typecode, NULL);
    }
    else if (PyArray_IsScalar(self, Object)) {
        PyObject *obj = ((PyObjectScalarObject *)self)->obval;
        PyArray_Descr *newtype;
        ret = PyObject_GetAttrString(obj, "imag");
        if (ret == NULL) {
            PyErr_Clear();
            obj = PyInt_FromLong(0);
            newtype = PyArray_DescrFromType(NPY_OBJECT);
            ret = PyArray_Scalar((char *)&obj, newtype, NULL);
            Py_DECREF(newtype);
            Py_DECREF(obj);
        }
    }
    else {
        char *temp;
        int elsize;
        typecode = PyArray_DescrFromScalar(self);
        elsize = typecode->elsize;
        temp = PyDataMem_NEW(elsize);
        memset(temp, '\0', elsize);
        ret = PyArray_Scalar(temp, typecode, NULL);
        PyDataMem_FREE(temp);
    }

    Py_XDECREF(typecode);
    return ret;
}

static PyObject *
gentype_flat_get(PyObject *self)
{
    PyObject *ret, *arr;

    arr = PyArray_FromScalar(self, NULL);
    if (arr == NULL) {
        return NULL;
    }
    ret = PyArray_IterNew(arr);
    Py_DECREF(arr);
    return ret;
}


static PyObject *
gentype_transpose_get(PyObject *self)
{
    Py_INCREF(self);
    return self;
}


static PyGetSetDef gentype_getsets[] = {
    {"ndim",
        (getter)gentype_ndim_get,
        (setter) 0,
        "number of array dimensions",
        NULL},
    {"flags",
        (getter)gentype_flags_get,
        (setter)0,
        "integer value of flags",
        NULL},
    {"shape",
        (getter)gentype_shape_get,
        (setter)0,
        "tuple of array dimensions",
        NULL},
    {"strides",
        (getter)gentype_shape_get,
        (setter) 0,
        "tuple of bytes steps in each dimension",
        NULL},
    {"data",
        (getter)gentype_data_get,
        (setter) 0,
        "pointer to start of data",
        NULL},
    {"itemsize",
        (getter)gentype_itemsize_get,
        (setter)0,
        "length of one element in bytes",
        NULL},
    {"size",
        (getter)gentype_size_get,
        (setter)0,
        "number of elements in the gentype",
        NULL},
    {"nbytes",
        (getter)gentype_itemsize_get,
        (setter)0,
        "length of item in bytes",
        NULL},
    {"base",
        (getter)gentype_base_get,
        (setter)0,
        "base object",
        NULL},
    {"dtype",
        (getter)gentype_typedescr_get,
        NULL,
        "get array data-descriptor",
        NULL},
    {"real",
        (getter)gentype_real_get,
        (setter)0,
        "real part of scalar",
        NULL},
    {"imag",
        (getter)gentype_imag_get,
        (setter)0,
        "imaginary part of scalar",
        NULL},
    {"flat",
        (getter)gentype_flat_get,
        (setter)0,
        "a 1-d view of scalar",
        NULL},
    {"T",
        (getter)gentype_transpose_get,
        (setter)0,
        "transpose",
        NULL},
    {"__array_interface__",
        (getter)gentype_interface_get,
        NULL,
        "Array protocol: Python side",
        NULL},
    {"__array_struct__",
        (getter)gentype_struct_get,
        NULL,
        "Array protocol: struct",
        NULL},
    {"__array_priority__",
        (getter)gentype_priority_get,
        NULL,
        "Array priority.",
        NULL},
    {NULL, NULL, NULL, NULL, NULL}  /* Sentinel */
};


/* 0-dim array from scalar object */

static char doc_getarray[] = "sc.__array__(|type) return 0-dim array";

static PyObject *
gentype_getarray(PyObject *scalar, PyObject *args)
{
    PyArray_Descr *outcode=NULL;
    PyObject *ret;

    if (!PyArg_ParseTuple(args, "|O&", &PyArray_DescrConverter,
                &outcode)) {
        Py_XDECREF(outcode);
        return NULL;
    }
    ret = PyArray_FromScalar(scalar, outcode);
    return ret;
}

static char doc_sc_wraparray[] = "sc.__array_wrap__(obj) return scalar from array";

static PyObject *
gentype_wraparray(PyObject *NPY_UNUSED(scalar), PyObject *args)
{
    PyObject *obj;
    PyArrayObject *arr;

    if (PyTuple_Size(args) < 1) {
        PyErr_SetString(PyExc_TypeError,
                "only accepts 1 argument.");
        return NULL;
    }
    obj = PyTuple_GET_ITEM(args, 0);
    if (!PyArray_Check(obj)) {
        PyErr_SetString(PyExc_TypeError,
                "can only be called with ndarray object");
        return NULL;
    }
    arr = (PyArrayObject *)obj;

    return PyArray_Scalar(PyArray_DATA(arr),
                    PyArray_DESCR(arr), (PyObject *)arr);
}

/*
 * These gentype_* functions do not take keyword arguments.
 * The proper flag is METH_VARARGS.
 */
/**begin repeat
 *
 * #name = tolist, item, tostring, tobytes, astype, copy, __deepcopy__,
 *         searchsorted, view, swapaxes, conj, conjugate, nonzero, flatten,
 *         ravel, fill, transpose, newbyteorder#
 */
static PyObject *
gentype_@name@(PyObject *self, PyObject *args)
{
    return gentype_generic_method(self, args, NULL, "@name@");
}
/**end repeat**/

static PyObject *
gentype_itemset(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args))
{
    PyErr_SetString(PyExc_ValueError, "array-scalars are immutable");
    return NULL;
}

static PyObject *
gentype_squeeze(PyObject *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, "")) {
        return NULL;
    }
    Py_INCREF(self);
    return self;
}

static Py_ssize_t
gentype_getreadbuf(PyObject *, Py_ssize_t, void **);

static PyObject *
gentype_byteswap(PyObject *self, PyObject *args)
{
    npy_bool inplace = NPY_FALSE;

    if (!PyArg_ParseTuple(args, "|O&", PyArray_BoolConverter, &inplace)) {
        return NULL;
    }
    if (inplace) {
        PyErr_SetString(PyExc_ValueError,
                "cannot byteswap a scalar in-place");
        return NULL;
    }
    else {
        /* get the data, copyswap it and pass it to a new Array scalar */
        char *data;
        PyArray_Descr *descr;
        PyObject *new;
        char *newmem;

        gentype_getreadbuf(self, 0, (void **)&data);
        descr = PyArray_DescrFromScalar(self);
        newmem = PyObject_Malloc(descr->elsize);
        if (newmem == NULL) {
            Py_DECREF(descr);
            return PyErr_NoMemory();
        }
        else {
            descr->f->copyswap(newmem, data, 1, NULL);
        }
        new = PyArray_Scalar(newmem, descr, NULL);
        PyObject_Free(newmem);
        Py_DECREF(descr);
        return new;
    }
}


/*
 * These gentype_* functions take keyword arguments.
 * The proper flag is METH_VARARGS | METH_KEYWORDS.
 */
/**begin repeat
 *
 * #name = take, getfield, put, repeat, tofile, mean, trace, diagonal, clip,
 *         std, var, sum, cumsum, prod, cumprod, compress, sort, argsort,
 *         round, argmax, argmin, max, min, ptp, any, all, resize, reshape,
 *         choose#
 */
static PyObject *
gentype_@name@(PyObject *self, PyObject *args, PyObject *kwds)
{
    return gentype_generic_method(self, args, kwds, "@name@");
}
/**end repeat**/

static PyObject *
voidtype_getfield(PyVoidScalarObject *self, PyObject *args, PyObject *kwds)
{
    /* Use ndarray's getfield to obtain the field safely */
    return gentype_generic_method((PyObject *)self, args, kwds, "getfield");
}

static PyObject *
gentype_setfield(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args),
                 PyObject *NPY_UNUSED(kwds))
{
    PyErr_SetString(PyExc_TypeError,
            "Can't set fields in a non-void array scalar.");
    return NULL;
}

static PyObject *
voidtype_setfield(PyVoidScalarObject *self, PyObject *args, PyObject *kwds)
{
    /*
     * We would like to use ndarray's setfield because it performs safety
     * checks on the field datatypes and because it broadcasts properly.
     * However, as a special case, void-scalar assignment broadcasts
     * differently from ndarrays when assigning to an object field: Assignment
     * to an ndarray object field broadcasts, but assignment to a void-scalar
     * object-field should not, in order to allow nested ndarrays. 
     * These lines should then behave identically:
     *
     *     b = np.zeros(1, dtype=[('x', 'O')])
     *     b[0]['x'] = arange(3)  # uses voidtype_setfield
     *     b['x'][0] = arange(3)  # uses ndarray setitem
     *
     * Ndarray's setfield would try to broadcast the lhs. Instead we use
     * ndarray getfield to get the field safely, then setitem with an empty
     * tuple to set the value without broadcast. Note we also want subarrays to
     * be set properly, ie
     *
     *     a = np.zeros(1, dtype=[('x', 'i', 5)])
     *     a[0]['x'] = 1
     *
     * sets all values to 1. "getfield + setitem with empty tuple" takes
     * care of both object arrays and subarrays.
     */
    PyObject *getfield_args, *value, *arr, *meth, *arr_field, *emptytuple;

    value = PyTuple_GetItem(args, 0);
    if (value == NULL) {
        return NULL;
    }
    getfield_args = PyTuple_GetSlice(args, 1, 3);
    if (getfield_args == NULL) {
        return NULL;
    }

    /* 1. Convert to 0-d array and use getfield */
    arr = PyArray_FromScalar((PyObject*)self, NULL);
    if (arr == NULL) {
        Py_DECREF(getfield_args);
        return NULL;
    }
    meth = PyObject_GetAttrString(arr, "getfield");
    if (meth == NULL) {
        Py_DECREF(getfield_args);
        Py_DECREF(arr);
        return NULL;
    }
    if (kwds == NULL) {
        arr_field = PyObject_CallObject(meth, getfield_args);
    }
    else {
        arr_field = PyObject_Call(meth, getfield_args, kwds);
    }
    Py_DECREF(getfield_args);
    Py_DECREF(meth);
    Py_DECREF(arr);

    if(arr_field == NULL){
        return NULL;
    }

    /* 2. Assign the value using setitem with empty tuple. */
    emptytuple = PyTuple_New(0);
    if (PyObject_SetItem(arr_field, emptytuple, value) < 0) {
        Py_DECREF(arr_field);
        Py_DECREF(emptytuple);
        return NULL;
    }
    Py_DECREF(emptytuple);
    Py_DECREF(arr_field);

    Py_RETURN_NONE;
}


static PyObject *
gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
{
    PyObject *ret = NULL, *obj = NULL, *mod = NULL;
    const char *buffer;
    Py_ssize_t buflen;

    /* Return a tuple of (callable object, arguments) */
    ret = PyTuple_New(2);
    if (ret == NULL) {
        return NULL;
    }
#if defined(NPY_PY3K)
    if (PyArray_IsScalar(self, Unicode)) {
        /* Unicode on Python 3 does not expose the buffer interface */
        buffer = PyUnicode_AS_DATA(self);
        buflen = PyUnicode_GET_DATA_SIZE(self);
    }
    else
#endif
    if (PyObject_AsReadBuffer(self, (const void **)&buffer, &buflen)<0) {
        Py_DECREF(ret);
        return NULL;
    }
    mod = PyImport_ImportModule("numpy.core.multiarray");
    if (mod == NULL) {
        return NULL;
    }
    obj = PyObject_GetAttrString(mod, "scalar");
    Py_DECREF(mod);
    if (obj == NULL) {
        return NULL;
    }
    PyTuple_SET_ITEM(ret, 0, obj);
    obj = PyObject_GetAttrString((PyObject *)self, "dtype");
    if (PyArray_IsScalar(self, Object)) {
        mod = ((PyObjectScalarObject *)self)->obval;
        PyTuple_SET_ITEM(ret, 1, Py_BuildValue("NO", obj, mod));
    }
    else {
#ifndef Py_UNICODE_WIDE
        /*
         * We need to expand the buffer so that we always write
         * UCS4 to disk for pickle of unicode scalars.
         *
         * This could be in a unicode_reduce function, but
         * that would require re-factoring.
         */
        int alloc = 0;
        char *tmp;
        int newlen;

        if (PyArray_IsScalar(self, Unicode)) {
            tmp = PyArray_malloc(buflen*2);
            if (tmp == NULL) {
                Py_DECREF(ret);
                return PyErr_NoMemory();
            }
            alloc = 1;
            newlen = PyUCS2Buffer_AsUCS4((Py_UNICODE *)buffer,
                    (npy_ucs4 *)tmp,
                    buflen / 2, buflen / 2);
            buflen = newlen*4;
            buffer = tmp;
        }
#endif
        mod = PyBytes_FromStringAndSize(buffer, buflen);
        if (mod == NULL) {
            Py_DECREF(ret);
#ifndef Py_UNICODE_WIDE
            ret = NULL;
            goto fail;
#else
            return NULL;
#endif
        }
        PyTuple_SET_ITEM(ret, 1,
                Py_BuildValue("NN", obj, mod));
#ifndef Py_UNICODE_WIDE
fail:
        if (alloc) PyArray_free((char *)buffer);
#endif
    }
    return ret;
}

/* ignores everything */
static PyObject *
gentype_setstate(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args))
{
    Py_RETURN_NONE;
}

static PyObject *
gentype_dump(PyObject *self, PyObject *args)
{
    PyObject *file = NULL;
    int ret;

    if (!PyArg_ParseTuple(args, "O", &file)) {
        return NULL;
    }
    ret = PyArray_Dump(self, file, 2);
    if (ret < 0) {
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject *
gentype_dumps(PyObject *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, "")) {
        return NULL;
    }
    return PyArray_Dumps(self, 2);
}


/* setting flags cannot be done for scalars */
static PyObject *
gentype_setflags(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args),
        PyObject *NPY_UNUSED(kwds))
{
    Py_RETURN_NONE;
}

/*
 * casting complex numbers (that don't inherit from Python complex)
 * to Python complex
 */

/**begin repeat
 * #name = cfloat, clongdouble#
 * #Name = CFloat, CLongDouble#
 */
static PyObject *
@name@_complex(PyObject *self, PyObject *NPY_UNUSED(args),
               PyObject *NPY_UNUSED(kwds))
{
    return PyComplex_FromDoubles(PyArrayScalar_VAL(self, @Name@).real,
                                 PyArrayScalar_VAL(self, @Name@).imag);
}
/**end repeat**/

/*
 * need to fill in doc-strings for these methods on import -- copy from
 * array docstrings
 */
static PyMethodDef gentype_methods[] = {
    {"tolist",
        (PyCFunction)gentype_tolist,
        METH_VARARGS, NULL},
    {"item",
        (PyCFunction)gentype_item,
        METH_VARARGS, NULL},
    {"itemset",
        (PyCFunction)gentype_itemset,
        METH_VARARGS, NULL},
    {"tobytes",
        (PyCFunction)gentype_tobytes,
        METH_VARARGS, NULL},
    {"tofile",
        (PyCFunction)gentype_tofile,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"tostring",
        (PyCFunction)gentype_tostring,
        METH_VARARGS, NULL},
    {"byteswap",
        (PyCFunction)gentype_byteswap,
        METH_VARARGS, NULL},
    {"astype",
        (PyCFunction)gentype_astype,
        METH_VARARGS, NULL},
    {"getfield",
        (PyCFunction)gentype_getfield,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"setfield",
        (PyCFunction)gentype_setfield,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"copy",
        (PyCFunction)gentype_copy,
        METH_VARARGS, NULL},
    {"resize",
        (PyCFunction)gentype_resize,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"__array__",
        (PyCFunction)gentype_getarray,
        METH_VARARGS, doc_getarray},
    {"__array_wrap__",
        (PyCFunction)gentype_wraparray,
        METH_VARARGS, doc_sc_wraparray},

    /* for the sys module */
    {"__sizeof__",
        (PyCFunction)gentype_sizeof,
        METH_NOARGS, NULL},

    /* for the copy module */
    {"__copy__",
        (PyCFunction)gentype_copy,
        METH_VARARGS, NULL},
    {"__deepcopy__",
        (PyCFunction)gentype___deepcopy__,
        METH_VARARGS, NULL},

    {"__reduce__",
        (PyCFunction) gentype_reduce,
        METH_VARARGS, NULL},
    /* For consistency does nothing */
    {"__setstate__",
        (PyCFunction) gentype_setstate,
        METH_VARARGS, NULL},

    {"dumps",
        (PyCFunction) gentype_dumps,
        METH_VARARGS, NULL},
    {"dump",
        (PyCFunction) gentype_dump,
        METH_VARARGS, NULL},

    /* Methods for array */
    {"fill",
        (PyCFunction)gentype_fill,
        METH_VARARGS, NULL},
    {"transpose",
        (PyCFunction)gentype_transpose,
        METH_VARARGS, NULL},
    {"take",
        (PyCFunction)gentype_take,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"put",
        (PyCFunction)gentype_put,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"repeat",
        (PyCFunction)gentype_repeat,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"choose",
        (PyCFunction)gentype_choose,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"sort",
        (PyCFunction)gentype_sort,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"argsort",
        (PyCFunction)gentype_argsort,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"searchsorted",
        (PyCFunction)gentype_searchsorted,
        METH_VARARGS, NULL},
    {"argmax",
        (PyCFunction)gentype_argmax,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"argmin",
        (PyCFunction)gentype_argmin,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"reshape",
        (PyCFunction)gentype_reshape,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"squeeze",
        (PyCFunction)gentype_squeeze,
        METH_VARARGS, NULL},
    {"view",
        (PyCFunction)gentype_view,
        METH_VARARGS, NULL},
    {"swapaxes",
        (PyCFunction)gentype_swapaxes,
        METH_VARARGS, NULL},
    {"max",
        (PyCFunction)gentype_max,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"min",
        (PyCFunction)gentype_min,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"ptp",
        (PyCFunction)gentype_ptp,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"mean",
        (PyCFunction)gentype_mean,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"trace",
        (PyCFunction)gentype_trace,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"diagonal",
        (PyCFunction)gentype_diagonal,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"clip",
        (PyCFunction)gentype_clip,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"conj",
        (PyCFunction)gentype_conj,
        METH_VARARGS, NULL},
    {"conjugate",
        (PyCFunction)gentype_conjugate,
        METH_VARARGS, NULL},
    {"nonzero",
        (PyCFunction)gentype_nonzero,
        METH_VARARGS, NULL},
    {"std",
        (PyCFunction)gentype_std,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"var",
        (PyCFunction)gentype_var,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"sum",
        (PyCFunction)gentype_sum,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"cumsum",
        (PyCFunction)gentype_cumsum,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"prod",
        (PyCFunction)gentype_prod,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"cumprod",
        (PyCFunction)gentype_cumprod,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"all",
        (PyCFunction)gentype_all,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"any",
        (PyCFunction)gentype_any,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"compress",
        (PyCFunction)gentype_compress,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"flatten",
        (PyCFunction)gentype_flatten,
        METH_VARARGS, NULL},
    {"ravel",
        (PyCFunction)gentype_ravel,
        METH_VARARGS, NULL},
    {"round",
        (PyCFunction)gentype_round,
        METH_VARARGS | METH_KEYWORDS, NULL},
#if defined(NPY_PY3K)
    /* Hook for the round() builtin */
    {"__round__",
        (PyCFunction)gentype_round,
        METH_VARARGS | METH_KEYWORDS, NULL},
#endif
    /* For the format function */
    {"__format__",
        gentype_format,
        METH_VARARGS,
        "NumPy array scalar formatter"},
    {"setflags",
        (PyCFunction)gentype_setflags,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"newbyteorder",
        (PyCFunction)gentype_newbyteorder,
        METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL} /* sentinel */
};


static PyGetSetDef voidtype_getsets[] = {
    {"flags",
        (getter)voidtype_flags_get,
        (setter)0,
        "integer value of flags",
        NULL},
    {"dtype",
        (getter)voidtype_dtypedescr_get,
        (setter)0,
        "dtype object",
        NULL},
    {NULL, NULL, NULL, NULL, NULL}
};

static PyMethodDef voidtype_methods[] = {
    {"getfield",
        (PyCFunction)voidtype_getfield,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"setfield",
        (PyCFunction)voidtype_setfield,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {NULL, NULL, 0, NULL}
};

static PyGetSetDef inttype_getsets[] = {
    {"numerator",
        (getter)inttype_numerator_get,
        (setter)0,
        "numerator of value (the value itself)",
        NULL},
    {"denominator",
        (getter)inttype_denominator_get,
        (setter)0,
        "denominator of value (1)",
        NULL},
    {NULL, NULL, NULL, NULL, NULL}
};

/**begin repeat
 * #name = cfloat,clongdouble#
 */
static PyMethodDef @name@type_methods[] = {
    {"__complex__",
        (PyCFunction)@name@_complex,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {NULL, NULL, 0, NULL}
};
/**end repeat**/

/************* As_mapping functions for void array scalar ************/

static Py_ssize_t
voidtype_length(PyVoidScalarObject *self)
{
    if (!PyDataType_HASFIELDS(self->descr)) {
        return 0;
    }
    else {
        /* return the number of fields */
        return (Py_ssize_t) PyTuple_GET_SIZE(self->descr->names);
    }
}

static PyObject *
voidtype_subscript(PyVoidScalarObject *self, PyObject *ind);

static PyObject *
voidtype_item(PyVoidScalarObject *self, Py_ssize_t n)
{
    npy_intp m;
    PyObject *flist=NULL;

    if (!(PyDataType_HASFIELDS(self->descr))) {
        PyErr_SetString(PyExc_IndexError,
                "can't index void scalar without fields");
        return NULL;
    }
    flist = self->descr->names;
    m = PyTuple_GET_SIZE(flist);
    if (n < 0) {
        n += m;
    }
    if (n < 0 || n >= m) {
        PyErr_Format(PyExc_IndexError, "invalid index (%d)", (int) n);
        return NULL;
    }

    return voidtype_subscript(self, PyTuple_GetItem(flist, n));
}

/* get field by name or number */
static PyObject *
voidtype_subscript(PyVoidScalarObject *self, PyObject *ind)
{
    npy_intp n;
    PyObject *ret, *args;

    if (!(PyDataType_HASFIELDS(self->descr))) {
        PyErr_SetString(PyExc_IndexError,
                "can't index void scalar without fields");
        return NULL;
    }

#if defined(NPY_PY3K)
    if (PyUString_Check(ind)) {
#else
    if (PyBytes_Check(ind) || PyUnicode_Check(ind)) {
#endif
        args = Py_BuildValue("(O)", ind);
        ret = gentype_generic_method((PyObject *)self, args, NULL, "__getitem__");
        Py_DECREF(args);
        return ret;
    }

    /* try to convert it to a number */
    n = PyArray_PyIntAsIntp(ind);
    if (error_converting(n)) {
        goto fail;
    }
    return voidtype_item(self, (Py_ssize_t)n);

fail:
    PyErr_SetString(PyExc_IndexError, "invalid index");
    return NULL;
}

static int
voidtype_ass_subscript(PyVoidScalarObject *self, PyObject *ind, PyObject *val);

static int
voidtype_ass_item(PyVoidScalarObject *self, Py_ssize_t n, PyObject *val)
{
    npy_intp m;
    PyObject *flist=NULL;

    if (!(PyDataType_HASFIELDS(self->descr))) {
        PyErr_SetString(PyExc_IndexError,
                "can't index void scalar without fields");
        return -1;
    }

    flist = self->descr->names;
    m = PyTuple_GET_SIZE(flist);
    if (n < 0) {
        n += m;
    }
    if (n < 0 || n >= m) {
        PyErr_Format(PyExc_IndexError, "invalid index (%d)", (int) n);
        return -1;
    }

    return voidtype_ass_subscript(self, PyTuple_GetItem(flist, n), val);
}

static int
voidtype_ass_subscript(PyVoidScalarObject *self, PyObject *ind, PyObject *val)
{
    npy_intp n;
    char *msg = "invalid index";
    PyObject *args;

    if (!PyDataType_HASFIELDS(self->descr)) {
        PyErr_SetString(PyExc_IndexError,
                "can't index void scalar without fields");
        return -1;
    }

    if (!val) {
        PyErr_SetString(PyExc_ValueError,
                "cannot delete scalar field");
        return -1;
    }

#if defined(NPY_PY3K)
    if (PyUString_Check(ind)) {
#else
    if (PyBytes_Check(ind) || PyUnicode_Check(ind)) {
#endif
        /*
         * Much like in voidtype_setfield, we cannot simply use ndarray's
         * __setitem__ since assignment to void scalars should not broadcast
         * the lhs. Instead we get a view through __getitem__ and then assign
         * the value using setitem with an empty tuple (which treats both
         * object arrays and subarrays properly).
         *
         * Also we do not want to use voidtype_setfield here, since we do
         * not need to do the (slow) view safety checks, since we already
         * know the dtype/offset are safe.
         */

        PyObject *arr, *arr_field, *meth, *emptytuple;

        /* 1. Convert to 0-d array and use getitem */
        arr = PyArray_FromScalar((PyObject*)self, NULL);
        if (arr == NULL) {
            return -1;
        }
        meth = PyObject_GetAttrString(arr, "__getitem__");
        if (meth == NULL) {
            Py_DECREF(arr);
            return -1;
        }
        args = Py_BuildValue("(O)", ind);
        arr_field = PyObject_CallObject(meth, args);
        Py_DECREF(meth);
        Py_DECREF(arr);
        Py_DECREF(args);

        if(arr_field == NULL){
            return -1;
        }

        /* 2. Assign the value using setitem with empty tuple. */
        emptytuple = PyTuple_New(0);
        if (PyObject_SetItem(arr_field, emptytuple, val) < 0) {
            Py_DECREF(arr_field);
            Py_DECREF(emptytuple);
            return -1;
        }
        Py_DECREF(emptytuple);
        Py_DECREF(arr_field);
        return 0;
    }

    /* try to convert it to a number */
    n = PyArray_PyIntAsIntp(ind);
    if (error_converting(n)) {
        goto fail;
    }
    return voidtype_ass_item(self, (Py_ssize_t)n, val);

fail:
    PyErr_SetString(PyExc_IndexError, msg);
    return -1;
}

static PyMappingMethods voidtype_as_mapping = {
    (lenfunc)voidtype_length,                    /*mp_length*/
    (binaryfunc)voidtype_subscript,              /*mp_subscript*/
    (objobjargproc)voidtype_ass_subscript,       /*mp_ass_subscript*/
};


static PySequenceMethods voidtype_as_sequence = {
    (lenfunc)voidtype_length,                    /*sq_length*/
    0,                                           /*sq_concat*/
    0,                                           /*sq_repeat*/
    (ssizeargfunc)voidtype_item,                 /*sq_item*/
    0,                                           /*sq_slice*/
    (ssizeobjargproc)voidtype_ass_item,          /*sq_ass_item*/
    0,                                           /* ssq_ass_slice */
    0,                                           /* sq_contains */
    0,                                           /* sq_inplace_concat */
    0,                                           /* sq_inplace_repeat */
};


static Py_ssize_t
gentype_getreadbuf(PyObject *self, Py_ssize_t segment, void **ptrptr)
{
    int numbytes;
    PyArray_Descr *outcode;

    if (segment != 0) {
        PyErr_SetString(PyExc_SystemError,
                "Accessing non-existent array segment");
        return -1;
    }

    outcode = PyArray_DescrFromScalar(self);
    numbytes = outcode->elsize;
    *ptrptr = (void *)scalar_value(self, outcode);

#ifndef Py_UNICODE_WIDE
    if (outcode->type_num == NPY_UNICODE) {
        numbytes >>= 1;
    }
#endif
    Py_DECREF(outcode);
    return numbytes;
}

#if !defined(NPY_PY3K)
static Py_ssize_t
gentype_getsegcount(PyObject *self, Py_ssize_t *lenp)
{
    PyArray_Descr *outcode;

    outcode = PyArray_DescrFromScalar(self);
    if (lenp) {
        *lenp = outcode->elsize;
#ifndef Py_UNICODE_WIDE
        if (outcode->type_num == NPY_UNICODE) {
            *lenp >>= 1;
        }
#endif
    }
    Py_DECREF(outcode);
    return 1;
}

static Py_ssize_t
gentype_getcharbuf(PyObject *self, Py_ssize_t segment, constchar **ptrptr)
{
    if (PyArray_IsScalar(self, String) ||
            PyArray_IsScalar(self, Unicode)) {
        return gentype_getreadbuf(self, segment, (void **)ptrptr);
    }
    else {
        PyErr_SetString(PyExc_TypeError,
                "Non-character array cannot be interpreted "\
                "as character buffer.");
        return -1;
    }
}
#endif /* !defined(NPY_PY3K) */


static int
gentype_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
    Py_ssize_t len;
    void *buf;

    /* FIXME: XXX: the format is not implemented! -- this needs more work */

    len = gentype_getreadbuf(self, 0, &buf);
    return PyBuffer_FillInfo(view, self, buf, len, 1, flags);
}

/* releasebuffer is not needed */


static PyBufferProcs gentype_as_buffer = {
#if !defined(NPY_PY3K)
    gentype_getreadbuf,                          /* bf_getreadbuffer*/
    NULL,                                        /* bf_getwritebuffer*/
    gentype_getsegcount,                         /* bf_getsegcount*/
    gentype_getcharbuf,                          /* bf_getcharbuffer*/
#endif
    gentype_getbuffer,                           /* bf_getbuffer */
    NULL,                                        /* bf_releasebuffer */
};


#if defined(NPY_PY3K)
#define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
#define LEAFFLAGS  Py_TPFLAGS_DEFAULT
#else
#define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES
#define LEAFFLAGS  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES
#endif

NPY_NO_EXPORT PyTypeObject PyGenericArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
#endif
    "numpy.generic",                            /* tp_name*/
    sizeof(PyObject),                           /* tp_basicsize*/
    0,                                          /* tp_itemsize */
    /* methods */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    0,                                          /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};

static void
void_dealloc(PyVoidScalarObject *v)
{
    if (v->flags & NPY_ARRAY_OWNDATA) {
        PyDataMem_FREE(v->obval);
    }
    Py_XDECREF(v->descr);
    Py_XDECREF(v->base);
    Py_TYPE(v)->tp_free(v);
}

static void
object_arrtype_dealloc(PyObject *v)
{
    Py_XDECREF(((PyObjectScalarObject *)v)->obval);
    Py_TYPE(v)->tp_free(v);
}

/*
 * string and unicode inherit from Python Type first and so GET_ITEM
 * is different to get to the Python Type.
 *
 * ok is a work-around for a bug in complex_new that doesn't allocate
 *  memory from the sub-types memory allocator.
 */

#define _WORK(num)  \
    if (type->tp_bases && (PyTuple_GET_SIZE(type->tp_bases)==2)) { \
        PyTypeObject *sup; \
        /* We are inheriting from a Python type as well so \
           give it first dibs on conversion */ \
        sup = (PyTypeObject *)PyTuple_GET_ITEM(type->tp_bases, num); \
        /* Prevent recursion */ \
        if (thisfunc != sup->tp_new) { \
            robj = sup->tp_new(type, args, kwds); \
            if (robj != NULL) goto finish;        \
            if (PyTuple_GET_SIZE(args)!=1) return NULL; \
            PyErr_Clear(); \
        } \
        /* now do default conversion */ \
    }

#define _WORK1 _WORK(1)
#define _WORKz _WORK(0)
#define _WORK0

/**begin repeat
 * #name = byte, short, int, long, longlong, ubyte, ushort, uint, ulong,
 *         ulonglong, half, float, double, longdouble, cfloat, cdouble,
 *         clongdouble, string, unicode, object#
 * #Name = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong,
 *         ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble,
 *         CLongDouble, String, Unicode, Object#
 * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG,
 *         ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE,
 *         CLONGDOUBLE, STRING, UNICODE, OBJECT#
 * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0#
 * #default = 0*17,1*2,2#
 */

#define _NPY_UNUSED2_1
#define _NPY_UNUSED2_z
#define _NPY_UNUSED2_0 NPY_UNUSED
#define _NPY_UNUSED1_0
#define _NPY_UNUSED1_1
#define _NPY_UNUSED1_2 NPY_UNUSED

static PyObject *
@name@_arrtype_new(PyTypeObject *_NPY_UNUSED1_@default@(type), PyObject *args, PyObject *_NPY_UNUSED2_@work@(kwds))
{
    PyObject *obj = NULL;
    PyObject *robj;
    PyArrayObject *arr;
    PyArray_Descr *typecode = NULL;
#if (@work@ != 0) || (@default@ == 1)
    void *thisfunc = (void *)@name@_arrtype_new;
#endif
#if !(@default@ == 2)
    int itemsize;
    void *dest, *src;
#endif

    /*
     * allow base-class (if any) to do conversion
     * If successful, this will jump to finish:
     */
    _WORK@work@

    if (!PyArg_ParseTuple(args, "|O", &obj)) {
        return NULL;
    }
    typecode = PyArray_DescrFromType(NPY_@TYPE@);
    if (typecode == NULL) {
        return NULL;
    }
    /*
     * typecode is new reference and stolen by
     * PyArray_FromAny but not PyArray_Scalar
     */
    if (obj == NULL) {
#if @default@ == 0
        robj = PyArray_Scalar(NULL, typecode, NULL);
        if (robj == NULL) {
            Py_DECREF(typecode);
            return NULL;
        }
        memset(&((Py@Name@ScalarObject *)robj)->obval, 0, sizeof(npy_@name@));
#elif @default@ == 1
        robj = PyArray_Scalar(NULL, typecode, NULL);
#elif @default@ == 2
        Py_INCREF(Py_None);
        robj = Py_None;
#endif
        Py_DECREF(typecode);
        goto finish;
    }

    /*
     * It is expected at this point that robj is a PyArrayScalar
     * (even for Object Data Type)
     */
    arr = (PyArrayObject *)PyArray_FromAny(obj, typecode,
                                    0, 0, NPY_ARRAY_FORCECAST, NULL);
    if ((arr == NULL) || (PyArray_NDIM(arr) > 0)) {
        return (PyObject *)arr;
    }
    /* 0-d array */
    robj = PyArray_ToScalar(PyArray_DATA(arr), arr);
    Py_DECREF(arr);

finish:
    /*
     * In OBJECT case, robj is no longer a
     * PyArrayScalar at this point but the
     * remaining code assumes it is
     */
#if @default@ == 2
    return robj;
#else
    /* Normal return */
    if ((robj == NULL) || (Py_TYPE(robj) == type)) {
        return robj;
    }

    /*
     * This return path occurs when the requested type is not created
     * but another scalar object is created instead (i.e. when
     * the base-class does the conversion in _WORK macro)
     */

    /* Need to allocate new type and copy data-area over */
    if (type->tp_itemsize) {
        itemsize = PyBytes_GET_SIZE(robj);
    }
    else {
        itemsize = 0;
    }
    obj = type->tp_alloc(type, itemsize);
    if (obj == NULL) {
        Py_DECREF(robj);
        return NULL;
    }
    /* typecode will be NULL */
    typecode = PyArray_DescrFromType(NPY_@TYPE@);
    dest = scalar_value(obj, typecode);
    src = scalar_value(robj, typecode);
    Py_DECREF(typecode);
#if @default@ == 0
    *((npy_@name@ *)dest) = *((npy_@name@ *)src);
#elif @default@ == 1 /* unicode and strings */
    if (itemsize == 0) { /* unicode */
#if PY_VERSION_HEX >= 0x03030000
        itemsize = PyUnicode_GetLength(robj) * PyUnicode_KIND(robj);
#else
        itemsize = ((PyUnicodeObject *)robj)->length * sizeof(Py_UNICODE);
#endif
    }
    memcpy(dest, src, itemsize);
    /* @default@ == 2 won't get here */
#endif
    Py_DECREF(robj);
    return obj;
#endif
}
/**end repeat**/

#undef _WORK1
#undef _WORKz
#undef _WORK0
#undef _WORK

/**begin repeat
 * #name = datetime, timedelta#
 * #Name = Datetime, Timedelta#
 * #NAME = DATETIME, TIMEDELTA#
 * #is_datetime = 1, 0#
 */

static PyObject *
@name@_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyObject *obj = NULL, *meta_obj = NULL;
    Py@Name@ScalarObject *ret;

    if (!PyArg_ParseTuple(args, "|OO", &obj, &meta_obj)) {
        return NULL;
    }

    /* Allocate the return scalar */
    ret = (Py@Name@ScalarObject *)Py@Name@ArrType_Type.tp_alloc(
                                            &Py@Name@ArrType_Type, 0);
    if (ret == NULL) {
        return NULL;
    }

    /* Incorporate the metadata if its provided */
    if (meta_obj != NULL) {
        /* Parse the provided metadata input */
        if (convert_pyobject_to_datetime_metadata(meta_obj, &ret->obmeta)
                                                                    < 0) {
            Py_DECREF(ret);
            return NULL;
        }
    }
    else {
        /*
         * A unit of -1 signals that convert_pyobject_to_datetime
         * should populate.
         */
        ret->obmeta.base = -1;
    }

    if (obj == NULL) {
        if (ret->obmeta.base == -1) {
            ret->obmeta.base = NPY_DATETIME_DEFAULTUNIT;
            ret->obmeta.num = 1;
        }

        /* Make datetime default to NaT, timedelta default to zero */
#if @is_datetime@
        ret->obval = NPY_DATETIME_NAT;
#else
        ret->obval = 0;
#endif
    }
    else if (convert_pyobject_to_@name@(&ret->obmeta, obj,
                            NPY_SAME_KIND_CASTING, &ret->obval) < 0) {
        Py_DECREF(ret);
        return NULL;
    }

    return (PyObject *)ret;
}
/**end repeat**/

/* bool->tp_new only returns Py_True or Py_False */
static PyObject *
bool_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *NPY_UNUSED(kwds))
{
    PyObject *obj = NULL;
    PyArrayObject *arr;

    if (!PyArg_ParseTuple(args, "|O", &obj)) {
        return NULL;
    }
    if (obj == NULL) {
        PyArrayScalar_RETURN_FALSE;
    }
    if (obj == Py_False) {
        PyArrayScalar_RETURN_FALSE;
    }
    if (obj == Py_True) {
        PyArrayScalar_RETURN_TRUE;
    }
    arr = (PyArrayObject *)PyArray_FROM_OTF(obj,
                                NPY_BOOL, NPY_ARRAY_FORCECAST);
    if (arr && 0 == PyArray_NDIM(arr)) {
        npy_bool val = *((npy_bool *)PyArray_DATA(arr));
        Py_DECREF(arr);
        PyArrayScalar_RETURN_BOOL_FROM_LONG(val);
    }
    return PyArray_Return((PyArrayObject *)arr);
}

static PyObject *
bool_arrtype_and(PyObject *a, PyObject *b)
{
    if (PyArray_IsScalar(a, Bool) && PyArray_IsScalar(b, Bool)) {
        PyArrayScalar_RETURN_BOOL_FROM_LONG
            ((a == PyArrayScalar_True) & (b == PyArrayScalar_True));
    }
    return PyGenericArrType_Type.tp_as_number->nb_and(a, b);
}

static PyObject *
bool_arrtype_or(PyObject *a, PyObject *b)
{
    if (PyArray_IsScalar(a, Bool) && PyArray_IsScalar(b, Bool)) {
        PyArrayScalar_RETURN_BOOL_FROM_LONG
            ((a == PyArrayScalar_True)|(b == PyArrayScalar_True));
    }
    return PyGenericArrType_Type.tp_as_number->nb_or(a, b);
}

static PyObject *
bool_arrtype_xor(PyObject *a, PyObject *b)
{
    if (PyArray_IsScalar(a, Bool) && PyArray_IsScalar(b, Bool)) {
        PyArrayScalar_RETURN_BOOL_FROM_LONG
            ((a == PyArrayScalar_True)^(b == PyArrayScalar_True));
    }
    return PyGenericArrType_Type.tp_as_number->nb_xor(a, b);
}

static int
bool_arrtype_nonzero(PyObject *a)
{
    return a == PyArrayScalar_True;
}

/**begin repeat
 * #name = byte, short, int, long, ubyte, ushort, longlong, uint,
 *         ulong, ulonglong#
 * #Name = Byte, Short, Int, Long, UByte, UShort, LongLong, UInt,
 *         ULong, ULongLong#
 * #type = PyInt_FromLong*6, PyLong_FromLongLong*1,
 *         PyLong_FromUnsignedLong*2, PyLong_FromUnsignedLongLong#
 */
static PyNumberMethods @name@_arrtype_as_number;
static PyObject *
@name@_index(PyObject *self)
{
    return @type@(PyArrayScalar_VAL(self, @Name@));
}
/**end repeat**/

static PyObject *
bool_index(PyObject *a)
{
    return PyInt_FromLong(PyArrayScalar_VAL(a, Bool));
}

/* Arithmetic methods -- only so we can override &, |, ^. */
NPY_NO_EXPORT PyNumberMethods bool_arrtype_as_number = {
    0,                                           /* nb_add */
    0,                                           /* nb_subtract */
    0,                                           /* nb_multiply */
#if defined(NPY_PY3K)
#else
    0,                                           /* nb_divide */
#endif
    0,                                           /* nb_remainder */
    0,                                           /* nb_divmod */
    0,                                           /* nb_power */
    0,                                           /* nb_negative */
    0,                                           /* nb_positive */
    0,                                           /* nb_absolute */
    (inquiry)bool_arrtype_nonzero,               /* nb_nonzero / nb_bool */
    0,                                           /* nb_invert */
    0,                                           /* nb_lshift */
    0,                                           /* nb_rshift */
    (binaryfunc)bool_arrtype_and,                /* nb_and */
    (binaryfunc)bool_arrtype_xor,                /* nb_xor */
    (binaryfunc)bool_arrtype_or,                 /* nb_or */
#if defined(NPY_PY3K)
#else
    0,                                           /* nb_coerce */
#endif
    0,                                           /* nb_int */
#if defined(NPY_PY3K)
    0,                                           /* nb_reserved */
#else
    0,                                           /* nb_long */
#endif
    0,                                           /* nb_float */
#if defined(NPY_PY3K)
#else
    0,                                           /* nb_oct */
    0,                                           /* nb_hex */
#endif
    /* Added in release 2.0 */
    0,                                           /* nb_inplace_add */
    0,                                           /* nb_inplace_subtract */
    0,                                           /* nb_inplace_multiply */
#if defined(NPY_PY3K)
#else
    0,                                           /* nb_inplace_divide */
#endif
    0,                                           /* nb_inplace_remainder */
    0,                                           /* nb_inplace_power */
    0,                                           /* nb_inplace_lshift */
    0,                                           /* nb_inplace_rshift */
    0,                                           /* nb_inplace_and */
    0,                                           /* nb_inplace_xor */
    0,                                           /* nb_inplace_or */
    /* Added in release 2.2 */
    /* The following require the Py_TPFLAGS_HAVE_CLASS flag */
    0,                                           /* nb_floor_divide */
    0,                                           /* nb_true_divide */
    0,                                           /* nb_inplace_floor_divide */
    0,                                           /* nb_inplace_true_divide */
    /* Added in release 2.5 */
    0,                                           /* nb_index */
};

static PyObject *
void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds))
{
    PyObject *obj, *arr;
    npy_ulonglong memu = 1;
    PyObject *new = NULL;
    char *destptr;

    if (!PyArg_ParseTuple(args, "O", &obj)) {
        return NULL;
    }
    /*
     * For a VOID scalar first see if obj is an integer or long
     * and create new memory of that size (filled with 0) for the scalar
     */
    if (PyLong_Check(obj) || PyInt_Check(obj) ||
            PyArray_IsScalar(obj, Integer) ||
            (PyArray_Check(obj) &&
                     PyArray_NDIM((PyArrayObject *)obj)==0 &&
                     PyArray_ISINTEGER((PyArrayObject *)obj))) {
#if defined(NPY_PY3K)
        new = Py_TYPE(obj)->tp_as_number->nb_int(obj);
#else
        new = Py_TYPE(obj)->tp_as_number->nb_long(obj);
#endif
    }
    if (new && PyLong_Check(new)) {
        PyObject *ret;
        memu = PyLong_AsUnsignedLongLong(new);
        Py_DECREF(new);
        if (PyErr_Occurred() || (memu > NPY_MAX_INT)) {
            PyErr_Clear();
            PyErr_Format(PyExc_OverflowError,
                    "size cannot be greater than %d",
                    (int) NPY_MAX_INT);
            return NULL;
        }
        destptr = PyDataMem_NEW((int) memu);
        if (destptr == NULL) {
            return PyErr_NoMemory();
        }
        ret = type->tp_alloc(type, 0);
        if (ret == NULL) {
            PyDataMem_FREE(destptr);
            return PyErr_NoMemory();
        }
        ((PyVoidScalarObject *)ret)->obval = destptr;
        Py_SIZE((PyVoidScalarObject *)ret) = (int) memu;
        ((PyVoidScalarObject *)ret)->descr =
            PyArray_DescrNewFromType(NPY_VOID);
        ((PyVoidScalarObject *)ret)->descr->elsize = (int) memu;
        ((PyVoidScalarObject *)ret)->flags = NPY_ARRAY_BEHAVED |
                                             NPY_ARRAY_OWNDATA;
        ((PyVoidScalarObject *)ret)->base = NULL;
        memset(destptr, '\0', (size_t) memu);
        return ret;
    }

    arr = PyArray_FROM_OTF(obj, NPY_VOID, NPY_ARRAY_FORCECAST);
    return PyArray_Return((PyArrayObject *)arr);
}


/****************  Define Hash functions ********************/

/**begin repeat
 * #lname = bool, ubyte, ushort#
 * #name = Bool,UByte, UShort#
 */
static npy_hash_t
@lname@_arrtype_hash(PyObject *obj)
{
    return (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval);
}
/**end repeat**/

/**begin repeat
 * #lname = byte, short, uint#
 * #name = Byte, Short, UInt#
 */
static npy_hash_t
@lname@_arrtype_hash(PyObject *obj)
{
    npy_hash_t x = (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval);
    if (x == -1) {
        x = -2;
    }
    return x;
}
/**end repeat**/

static npy_hash_t
ulong_arrtype_hash(PyObject *obj)
{
    PyObject * l = PyLong_FromUnsignedLong(((PyULongScalarObject*)obj)->obval);
    npy_hash_t x = PyObject_Hash(l);
    Py_DECREF(l);
    return x;
}

#if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K)
static npy_hash_t
int_arrtype_hash(PyObject *obj)
{
    npy_hash_t x = (npy_hash_t)(((PyIntScalarObject *)obj)->obval);
    if (x == -1) {
        x = -2;
    }
    return x;
}
#endif

#if defined(NPY_PY3K)
static npy_hash_t
long_arrtype_hash(PyObject *obj)
{
    PyObject * l = PyLong_FromLong(((PyLongScalarObject*)obj)->obval);
    npy_hash_t x = PyObject_Hash(l);
    Py_DECREF(l);
    return x;
}
#endif

/**begin repeat
 * #char = ,u#
 * #Char = ,U#
 * #Word = ,Unsigned#
 */
static NPY_INLINE npy_hash_t
@char@longlong_arrtype_hash(PyObject *obj)
{
    PyObject * l = PyLong_From@Word@LongLong(
                                 ((Py@Char@LongLongScalarObject*)obj)->obval);
    npy_hash_t x = PyObject_Hash(l);
    Py_DECREF(l);
    return x;
}
/**end repeat**/


/**begin repeat
 * #lname = datetime, timedelta#
 * #name = Datetime, Timedelta#
 */
#if NPY_SIZEOF_HASH_T==NPY_SIZEOF_DATETIME
static npy_hash_t
@lname@_arrtype_hash(PyObject *obj)
{
    npy_hash_t x = (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval);
    if (x == -1) {
        x = -2;
    }
    return x;
}
#elif NPY_SIZEOF_LONGLONG==NPY_SIZEOF_DATETIME
static npy_hash_t
@lname@_arrtype_hash(PyObject *obj)
{
    npy_hash_t y;
    npy_longlong x = (((Py@name@ScalarObject *)obj)->obval);

    if ((x <= LONG_MAX)) {
        y = (npy_hash_t) x;
    }
    else {
        union Mask {
            long hashvals[2];
            npy_longlong v;
        } both;

        both.v = x;
        y = both.hashvals[0] + (1000003)*both.hashvals[1];
    }
    if (y == -1) {
        y = -2;
    }
    return y;
}
#endif
/**end repeat**/



/* Wrong thing to do for longdouble, but....*/

/**begin repeat
 * #lname = float, longdouble#
 * #name = Float, LongDouble#
 */
static npy_hash_t
@lname@_arrtype_hash(PyObject *obj)
{
    return _Py_HashDouble((double) ((Py@name@ScalarObject *)obj)->obval);
}

/* borrowed from complex_hash */
static npy_hash_t
c@lname@_arrtype_hash(PyObject *obj)
{
    npy_hash_t hashreal, hashimag, combined;
    hashreal = _Py_HashDouble((double)
            (((PyC@name@ScalarObject *)obj)->obval).real);

    if (hashreal == -1) {
        return -1;
    }
    hashimag = _Py_HashDouble((double)
            (((PyC@name@ScalarObject *)obj)->obval).imag);
    if (hashimag == -1) {
        return -1;
    }
    combined = hashreal + 1000003 * hashimag;
    if (combined == -1) {
        combined = -2;
    }
    return combined;
}
/**end repeat**/

static npy_hash_t
half_arrtype_hash(PyObject *obj)
{
    return _Py_HashDouble(npy_half_to_double(((PyHalfScalarObject *)obj)->obval));
}

static npy_hash_t
object_arrtype_hash(PyObject *obj)
{
    return PyObject_Hash(((PyObjectScalarObject *)obj)->obval);
}

/* we used to just hash the pointer */
/* now use tuplehash algorithm using voidtype_item to get the object
*/
static npy_hash_t
void_arrtype_hash(PyObject *obj)
{
    npy_hash_t x, y;
    Py_ssize_t len, n;
    PyVoidScalarObject *p;
    PyObject *element;
    npy_hash_t mult = 1000003L;
    x = 0x345678L;
    p = (PyVoidScalarObject *)obj;
    /* Cannot hash mutable void scalars */
    if (p->flags & NPY_ARRAY_WRITEABLE) {
       PyErr_SetString(PyExc_TypeError, "unhashable type: 'writeable void-scalar'");
       return -1;
    }
    len = voidtype_length(p);
    for (n=0; n < len; n++) {
        element = voidtype_item(p, n);
        y = PyObject_Hash(element);
        Py_DECREF(element);
        if (y == -1)
           return -1;
        x = (x ^ y) * mult;
        mult += (npy_hash_t)(82520L + len + len);
    }
    x += 97531L;
    if (x == -1)
        x = -2;
    return x;
}

/*object arrtype getattro and setattro */
static PyObject *
object_arrtype_getattro(PyObjectScalarObject *obj, PyObject *attr) {
    PyObject *res;

    /* first look in object and then hand off to generic type */

    res = PyObject_GenericGetAttr(obj->obval, attr);
    if (res) {
        return res;
    }
    PyErr_Clear();
    return  PyObject_GenericGetAttr((PyObject *)obj, attr);
}

static int
object_arrtype_setattro(PyObjectScalarObject *obj, PyObject *attr, PyObject *val) {
    int res;
    /* first look in object and then hand off to generic type */

    res = PyObject_GenericSetAttr(obj->obval, attr, val);
    if (res >= 0) {
        return res;
    }
    PyErr_Clear();
    return PyObject_GenericSetAttr((PyObject *)obj, attr, val);
}

static PyObject *
object_arrtype_concat(PyObjectScalarObject *self, PyObject *other)
{
    return PySequence_Concat(self->obval, other);
}

static Py_ssize_t
object_arrtype_length(PyObjectScalarObject *self)
{
    return PyObject_Length(self->obval);
}

static PyObject *
object_arrtype_repeat(PyObjectScalarObject *self, Py_ssize_t count)
{
    return PySequence_Repeat(self->obval, count);
}

static PyObject *
object_arrtype_subscript(PyObjectScalarObject *self, PyObject *key)
{
    return PyObject_GetItem(self->obval, key);
}

static int
object_arrtype_ass_subscript(PyObjectScalarObject *self, PyObject *key,
                             PyObject *value)
{
    return PyObject_SetItem(self->obval, key, value);
}

static int
object_arrtype_contains(PyObjectScalarObject *self, PyObject *ob)
{
    return PySequence_Contains(self->obval, ob);
}

static PyObject *
object_arrtype_inplace_concat(PyObjectScalarObject *self, PyObject *o)
{
    return PySequence_InPlaceConcat(self->obval, o);
}

static PyObject *
object_arrtype_inplace_repeat(PyObjectScalarObject *self, Py_ssize_t count)
{
    return PySequence_InPlaceRepeat(self->obval, count);
}

static PySequenceMethods object_arrtype_as_sequence = {
    (lenfunc)object_arrtype_length,              /*sq_length*/
    (binaryfunc)object_arrtype_concat,           /*sq_concat*/
    (ssizeargfunc)object_arrtype_repeat,         /*sq_repeat*/
    0,                                           /*sq_item*/
    0,                                           /*sq_slice*/
    0,                                           /* sq_ass_item */
    0,                                           /* sq_ass_slice */
    (objobjproc)object_arrtype_contains,         /* sq_contains */
    (binaryfunc)object_arrtype_inplace_concat,   /* sq_inplace_concat */
    (ssizeargfunc)object_arrtype_inplace_repeat, /* sq_inplace_repeat */
};

static PyMappingMethods object_arrtype_as_mapping = {
    (lenfunc)object_arrtype_length,
    (binaryfunc)object_arrtype_subscript,
    (objobjargproc)object_arrtype_ass_subscript,
};

#if !defined(NPY_PY3K)
static Py_ssize_t
object_arrtype_getsegcount(PyObjectScalarObject *self, Py_ssize_t *lenp)
{
    Py_ssize_t newlen;
    int cnt;
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;

    if (pb == NULL ||
            pb->bf_getsegcount == NULL ||
            (cnt = (*pb->bf_getsegcount)(self->obval, &newlen)) != 1) {
        return 0;
    }
    if (lenp) {
        *lenp = newlen;
    }
    return cnt;
}

static Py_ssize_t
object_arrtype_getreadbuf(PyObjectScalarObject *self, Py_ssize_t segment, void **ptrptr)
{
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;

    if (pb == NULL ||
            pb->bf_getreadbuffer == NULL ||
            pb->bf_getsegcount == NULL) {
        PyErr_SetString(PyExc_TypeError,
                "expected a readable buffer object");
        return -1;
    }
    return (*pb->bf_getreadbuffer)(self->obval, segment, ptrptr);
}

static Py_ssize_t
object_arrtype_getwritebuf(PyObjectScalarObject *self, Py_ssize_t segment, void **ptrptr)
{
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;

    if (pb == NULL ||
            pb->bf_getwritebuffer == NULL ||
            pb->bf_getsegcount == NULL) {
        PyErr_SetString(PyExc_TypeError,
                "expected a writeable buffer object");
        return -1;
    }
    return (*pb->bf_getwritebuffer)(self->obval, segment, ptrptr);
}

static Py_ssize_t
object_arrtype_getcharbuf(PyObjectScalarObject *self, Py_ssize_t segment,
                          constchar **ptrptr)
{
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;

    if (pb == NULL ||
            pb->bf_getcharbuffer == NULL ||
            pb->bf_getsegcount == NULL) {
        PyErr_SetString(PyExc_TypeError,
                "expected a character buffer object");
        return -1;
    }
    return (*pb->bf_getcharbuffer)(self->obval, segment, ptrptr);
}
#endif

static int
object_arrtype_getbuffer(PyObjectScalarObject *self, Py_buffer *view, int flags)
{
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;
    if (pb == NULL || pb->bf_getbuffer == NULL) {
        PyErr_SetString(PyExc_TypeError,
                        "expected a readable buffer object");
        return -1;
    }
    return (*pb->bf_getbuffer)(self->obval, view, flags);
}

static void
object_arrtype_releasebuffer(PyObjectScalarObject *self, Py_buffer *view)
{
    PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer;
    if (pb == NULL) {
        PyErr_SetString(PyExc_TypeError,
                        "expected a readable buffer object");
        return;
    }
    if (pb->bf_releasebuffer != NULL) {
        (*pb->bf_releasebuffer)(self->obval, view);
    }
}

static PyBufferProcs object_arrtype_as_buffer = {
#if !defined(NPY_PY3K)
    (readbufferproc)object_arrtype_getreadbuf,
    (writebufferproc)object_arrtype_getwritebuf,
    (segcountproc)object_arrtype_getsegcount,
    (charbufferproc)object_arrtype_getcharbuf,
#endif
    (getbufferproc)object_arrtype_getbuffer,
    (releasebufferproc)object_arrtype_releasebuffer,
};

static PyObject *
object_arrtype_call(PyObjectScalarObject *obj, PyObject *args, PyObject *kwds)
{
    return PyObject_Call(obj->obval, args, kwds);
}

NPY_NO_EXPORT PyTypeObject PyObjectArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
#endif
    "numpy.object_",                            /* tp_name*/
    sizeof(PyObjectScalarObject),               /* tp_basicsize*/
    0,                                          /* tp_itemsize */
    (destructor)object_arrtype_dealloc,         /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    &object_arrtype_as_sequence,                /* tp_as_sequence */
    &object_arrtype_as_mapping,                 /* tp_as_mapping */
    0,                                          /* tp_hash */
    (ternaryfunc)object_arrtype_call,           /* tp_call */
    0,                                          /* tp_str */
    (getattrofunc)object_arrtype_getattro,      /* tp_getattro */
    (setattrofunc)object_arrtype_setattro,      /* tp_setattro */
    &object_arrtype_as_buffer,                  /* tp_as_buffer */
    0,                                          /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};

static PyObject *
gen_arrtype_subscript(PyObject *self, PyObject *key)
{
    /*
     * Only [...], [...,<???>], [<???>, ...],
     * is allowed for indexing a scalar
     *
     * These return a new N-d array with a copy of
     * the data where N is the number of None's in <???>.
     */
    PyObject *res, *ret;

    res = PyArray_FromScalar(self, NULL);

    ret = array_subscript((PyArrayObject *)res, key);
    Py_DECREF(res);
    if (ret == NULL) {
        PyErr_SetString(PyExc_IndexError,
                        "invalid index to scalar variable.");
    }
    return ret;
}


#define NAME_bool "bool"
#define NAME_void "void"
#if defined(NPY_PY3K)
#define NAME_string "bytes"
#define NAME_unicode "str"
#else
#define NAME_string "string"
#define NAME_unicode "unicode"
#endif

/**begin repeat
 * #name = bool, string, unicode, void#
 * #NAME = Bool, String, Unicode, Void#
 * #ex = _,_,_,#
 */
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
#endif
    "numpy." NAME_@name@ "@ex@",                /* tp_name*/
    sizeof(Py@NAME@ScalarObject),               /* tp_basicsize*/
    0,                                          /* tp_itemsize */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    0,                                          /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};
/**end repeat**/

#undef NAME_bool
#undef NAME_void
#undef NAME_string
#undef NAME_unicode

/**begin repeat
 * #NAME = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong,
 *         ULongLong, Half, Float, Double, LongDouble, Datetime, Timedelta#
 * #name = int*5, uint*5, float*4, datetime, timedelta#
 * #CNAME = (CHAR, SHORT, INT, LONG, LONGLONG)*2, HALF, FLOAT, DOUBLE,
 *          LONGDOUBLE, DATETIME, TIMEDELTA#
 */
#if NPY_BITSOF_@CNAME@ == 8
#define _THIS_SIZE "8"
#elif NPY_BITSOF_@CNAME@ == 16
#define _THIS_SIZE "16"
#elif NPY_BITSOF_@CNAME@ == 32
#define _THIS_SIZE "32"
#elif NPY_BITSOF_@CNAME@ == 64
#define _THIS_SIZE "64"
#elif NPY_BITSOF_@CNAME@ == 80
#define _THIS_SIZE "80"
#elif NPY_BITSOF_@CNAME@ == 96
#define _THIS_SIZE "96"
#elif NPY_BITSOF_@CNAME@ == 128
#define _THIS_SIZE "128"
#elif NPY_BITSOF_@CNAME@ == 256
#define _THIS_SIZE "256"
#endif
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(NULL, 0)
#else
    PyObject_HEAD_INIT(NULL)
    0,                                          /* ob_size */
#endif
    "numpy.@name@" _THIS_SIZE,                  /* tp_name*/
    sizeof(Py@NAME@ScalarObject),               /* tp_basicsize*/
    0,                                          /* tp_itemsize */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    0,                                          /* tp_flags */
    0,                                          /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};

#undef _THIS_SIZE
/**end repeat**/


static PyMappingMethods gentype_as_mapping = {
    NULL,
    (binaryfunc)gen_arrtype_subscript,
    NULL
};


/**begin repeat
 * #NAME = CFloat, CDouble, CLongDouble#
 * #name = complex*3#
 * #CNAME = FLOAT, DOUBLE, LONGDOUBLE#
 */
#if NPY_BITSOF_@CNAME@ == 16
#define _THIS_SIZE2 "16"
#define _THIS_SIZE1 "32"
#elif NPY_BITSOF_@CNAME@ == 32
#define _THIS_SIZE2 "32"
#define _THIS_SIZE1 "64"
#elif NPY_BITSOF_@CNAME@ == 64
#define _THIS_SIZE2 "64"
#define _THIS_SIZE1 "128"
#elif NPY_BITSOF_@CNAME@ == 80
#define _THIS_SIZE2 "80"
#define _THIS_SIZE1 "160"
#elif NPY_BITSOF_@CNAME@ == 96
#define _THIS_SIZE2 "96"
#define _THIS_SIZE1 "192"
#elif NPY_BITSOF_@CNAME@ == 128
#define _THIS_SIZE2 "128"
#define _THIS_SIZE1 "256"
#elif NPY_BITSOF_@CNAME@ == 256
#define _THIS_SIZE2 "256"
#define _THIS_SIZE1 "512"
#endif

#define _THIS_DOC "Composed of two " _THIS_SIZE2 " bit floats"

NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
#if defined(NPY_PY3K)
    PyVarObject_HEAD_INIT(0, 0)
#else
    PyObject_HEAD_INIT(0)
    0,                                          /* ob_size */
#endif
    "numpy.@name@" _THIS_SIZE1,                 /* tp_name*/
    sizeof(Py@NAME@ScalarObject),               /* tp_basicsize*/
    0,                                          /* tp_itemsize*/
    0,                                          /* tp_dealloc*/
    0,                                          /* tp_print*/
    0,                                          /* tp_getattr*/
    0,                                          /* tp_setattr*/
#if defined(NPY_PY3K)
    0,                                          /* tp_reserved */
#else
    0,                                          /* tp_compare */
#endif
    0,                                          /* tp_repr*/
    0,                                          /* tp_as_number*/
    0,                                          /* tp_as_sequence*/
    0,                                          /* tp_as_mapping*/
    0,                                          /* tp_hash */
    0,                                          /* tp_call*/
    0,                                          /* tp_str*/
    0,                                          /* tp_getattro*/
    0,                                          /* tp_setattro*/
    0,                                          /* tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                         /* tp_flags*/
    _THIS_DOC,                                  /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    0,                                          /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    0,                                          /* tp_new */
    0,                                          /* tp_free */
    0,                                          /* tp_is_gc */
    0,                                          /* tp_bases */
    0,                                          /* tp_mro */
    0,                                          /* tp_cache */
    0,                                          /* tp_subclasses */
    0,                                          /* tp_weaklist */
    0,                                          /* tp_del */
    0,                                          /* tp_version_tag */
};
#undef _THIS_SIZE1
#undef _THIS_SIZE2
#undef _THIS_DOC

/**end repeat**/

/*
 * This table maps the built-in type numbers to their scalar
 * type numbers.  Note that signed integers are mapped to INTNEG_SCALAR,
 * which is different than what PyArray_ScalarKind returns.
 */
NPY_NO_EXPORT signed char
_npy_scalar_kinds_table[NPY_NTYPES];

/*
 * This table maps a scalar kind (excluding NPY_NOSCALAR)
 * to the smallest type number of that kind.
 */
NPY_NO_EXPORT signed char
_npy_smallest_type_of_kind_table[NPY_NSCALARKINDS];

/*
 * This table gives the type of the same kind, but next in the sequence
 * of sizes.
 */
NPY_NO_EXPORT signed char
_npy_next_larger_type_table[NPY_NTYPES];

/*
 * This table describes safe casting for small type numbers,
 * and is used by PyArray_CanCastSafely.
 */
NPY_NO_EXPORT unsigned char
_npy_can_cast_safely_table[NPY_NTYPES][NPY_NTYPES];

/*
 * This table gives the smallest-size and smallest-kind type to which
 * the input types may be safely cast, according to _npy_can_cast_safely.
 */
NPY_NO_EXPORT signed char
_npy_type_promotion_table[NPY_NTYPES][NPY_NTYPES];

NPY_NO_EXPORT void
initialize_casting_tables(void)
{
    int i, j;

    _npy_smallest_type_of_kind_table[NPY_BOOL_SCALAR] = NPY_BOOL;
    _npy_smallest_type_of_kind_table[NPY_INTPOS_SCALAR] = NPY_UBYTE;
    _npy_smallest_type_of_kind_table[NPY_INTNEG_SCALAR] = NPY_BYTE;
    _npy_smallest_type_of_kind_table[NPY_FLOAT_SCALAR] = NPY_HALF;
    _npy_smallest_type_of_kind_table[NPY_COMPLEX_SCALAR] = NPY_CFLOAT;
    _npy_smallest_type_of_kind_table[NPY_OBJECT_SCALAR] = NPY_OBJECT;

    /* Default for built-in types is object scalar */
    memset(_npy_scalar_kinds_table, NPY_OBJECT_SCALAR,
                                        sizeof(_npy_scalar_kinds_table));
    /* Default for next largest type is -1, signalling no bigger */
    memset(_npy_next_larger_type_table, -1,
                                        sizeof(_npy_next_larger_type_table));

    /* Compile-time loop of scalar kinds */

    /**begin repeat
     * #NAME = BOOL,
     *         BYTE, UBYTE, SHORT, USHORT, INT, UINT,
     *         LONG, ULONG, LONGLONG, ULONGLONG,
     *         HALF, FLOAT, DOUBLE, LONGDOUBLE,
     *         CFLOAT, CDOUBLE, CLONGDOUBLE#
     * #BIGGERTYPE = -1,
     *         NPY_SHORT, NPY_USHORT, NPY_INT, NPY_UINT, NPY_LONG, NPY_ULONG,
     *         NPY_LONGLONG, NPY_ULONGLONG, -1, -1,
     *         NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, -1,
     *         NPY_CDOUBLE, NPY_CLONGDOUBLE, -1#
     * #SCKIND = BOOL,
     *           (INTNEG, INTPOS)*5,
     *           FLOAT*4,
     *           COMPLEX*3#
     */

    _npy_scalar_kinds_table[NPY_@NAME@] = NPY_@SCKIND@_SCALAR;
    _npy_next_larger_type_table[NPY_@NAME@] = @BIGGERTYPE@;

    /**end repeat**/

    memset(_npy_can_cast_safely_table, 0, sizeof(_npy_can_cast_safely_table));

    for (i = 0; i < NPY_NTYPES; ++i) {
        /* Identity */
        _npy_can_cast_safely_table[i][i] = 1;
        if (i != NPY_DATETIME) {
            /*
             * Bool -> <Anything> except datetime (since
             *                    it conceptually has no zero)
             */
            _npy_can_cast_safely_table[NPY_BOOL][i] = 1;
        }
        /* <Anything> -> Object */
        _npy_can_cast_safely_table[i][NPY_OBJECT] = 1;
        /* <Anything> -> Void */
        _npy_can_cast_safely_table[i][NPY_VOID] = 1;
    }

    _npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1;
    _npy_can_cast_safely_table[NPY_BOOL][NPY_TIMEDELTA] = 1;

#ifndef NPY_SIZEOF_BYTE
#define NPY_SIZEOF_BYTE 1
#endif

    /* Compile-time loop of casting rules */

    /**begin repeat
     * #FROM_NAME = BYTE, UBYTE, SHORT, USHORT, INT, UINT,
     *              LONG, ULONG, LONGLONG, ULONGLONG,
     *              HALF, FLOAT, DOUBLE, LONGDOUBLE,
     *              CFLOAT, CDOUBLE, CLONGDOUBLE#
     * #FROM_BASENAME = BYTE, BYTE, SHORT, SHORT, INT, INT,
     *                  LONG, LONG, LONGLONG, LONGLONG,
     *                  HALF, FLOAT, DOUBLE, LONGDOUBLE,
     *                  FLOAT, DOUBLE, LONGDOUBLE#
     * #from_isint = 1, 0, 1, 0, 1, 0, 1, 0,
     *               1, 0, 0, 0, 0, 0,
     *               0, 0, 0#
     * #from_isuint = 0, 1, 0, 1, 0, 1, 0, 1,
     *                0, 1, 0, 0, 0, 0,
     *                0, 0, 0#
     * #from_isfloat = 0, 0, 0, 0, 0, 0, 0, 0,
     *                 0, 0, 1, 1, 1, 1,
     *                 0, 0, 0#
     * #from_iscomplex = 0, 0, 0, 0, 0, 0, 0, 0,
     *                   0, 0, 0, 0, 0, 0,
     *                   1, 1, 1#
     */

#define _FROM_BSIZE NPY_SIZEOF_@FROM_BASENAME@
#define _FROM_NUM   (NPY_@FROM_NAME@)

    _npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 1;
    _npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 1;

    /* Allow casts from any integer to the TIMEDELTA type */
#if @from_isint@ || @from_isuint@
    _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1;
#endif

    /**begin repeat1
     * #TO_NAME = BYTE, UBYTE, SHORT, USHORT, INT, UINT,
     *            LONG, ULONG, LONGLONG, ULONGLONG,
     *            HALF, FLOAT, DOUBLE, LONGDOUBLE,
     *            CFLOAT, CDOUBLE, CLONGDOUBLE#
     * #TO_BASENAME = BYTE, BYTE, SHORT, SHORT, INT, INT,
     *                LONG, LONG, LONGLONG, LONGLONG,
     *                HALF, FLOAT, DOUBLE, LONGDOUBLE,
     *                FLOAT, DOUBLE, LONGDOUBLE#
     * #to_isint = 1, 0, 1, 0, 1, 0, 1, 0,
     *             1, 0, 0, 0, 0, 0,
     *             0, 0, 0#
     * #to_isuint = 0, 1, 0, 1, 0, 1, 0, 1,
     *              0, 1, 0, 0, 0, 0,
     *              0, 0, 0#
     * #to_isfloat = 0, 0, 0, 0, 0, 0, 0, 0,
     *               0, 0, 1, 1, 1, 1,
     *               0, 0, 0#
     * #to_iscomplex = 0, 0, 0, 0, 0, 0, 0, 0,
     *                 0, 0, 0, 0, 0, 0,
     *                 1, 1, 1#
     */
#define _TO_BSIZE NPY_SIZEOF_@TO_BASENAME@
#define _TO_NUM   (NPY_@TO_NAME@)

    /*
     * NOTE: _FROM_BSIZE and _TO_BSIZE are the sizes of the "base type"
     *       which is the same as the size of the type except for
     *       complex, where it is the size of the real type.
     */

#if @from_isint@

#  if @to_isint@ && (_TO_BSIZE >= _FROM_BSIZE)
    /* int -> int */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_isfloat@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE)
    /* int -> float */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_isfloat@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE)
    /* int -> float */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_iscomplex@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE)
    /* int -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_iscomplex@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE)
    /* int -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  endif

#elif @from_isuint@

#  if @to_isint@ && (_TO_BSIZE > _FROM_BSIZE)
    /* uint -> int */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_isuint@ && (_TO_BSIZE >= _FROM_BSIZE)
    /* uint -> uint */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_isfloat@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE)
    /* uint -> float */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_isfloat@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE)
    /* uint -> float */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_iscomplex@ && (_FROM_BSIZE < 8) && (_TO_BSIZE > _FROM_BSIZE)
    /* uint -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_iscomplex@ && (_FROM_BSIZE >= 8) && (_TO_BSIZE >= _FROM_BSIZE)
    /* uint -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  endif


#elif @from_isfloat@

#  if @to_isfloat@ && (_TO_BSIZE >= _FROM_BSIZE)
    /* float -> float */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  elif @to_iscomplex@ && (_TO_BSIZE >= _FROM_BSIZE)
    /* float -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  endif

#elif @from_iscomplex@

#  if @to_iscomplex@ && (_TO_BSIZE >= _FROM_BSIZE)
    /* complex -> complex */
    _npy_can_cast_safely_table[_FROM_NUM][_TO_NUM] = 1;
#  endif

#endif

#undef _TO_NUM
#undef _TO_BSIZE

/**end repeat1**/

#undef _FROM_NUM
#undef _FROM_BSIZE

/**end repeat**/

    /*
     * Now that the _can_cast_safely table is finished, we can
     * use it to build the _type_promotion table
     */
    for (i = 0; i < NPY_NTYPES; ++i) {
        _npy_type_promotion_table[i][i] = i;
        /* Don't let number promote to string/unicode/void/datetime/timedelta */
        if (i == NPY_STRING || i == NPY_UNICODE || i == NPY_VOID ||
                i == NPY_DATETIME || i == NPY_TIMEDELTA) {
            /* Promoting these types requires examining their contents */
            _npy_type_promotion_table[i][i] = -1;
            for (j = i + 1; j < NPY_NTYPES; ++j) {
                _npy_type_promotion_table[i][j] = -1;
                _npy_type_promotion_table[j][i] = -1;
            }
            /* Except they can convert to OBJECT */
            _npy_type_promotion_table[i][NPY_OBJECT] = NPY_OBJECT;
            _npy_type_promotion_table[NPY_OBJECT][i] = NPY_OBJECT;
        }
        else {
            for (j = i + 1; j < NPY_NTYPES; ++j) {
                /* Don't let number promote to string/unicode/void */
                if (j == NPY_STRING || j == NPY_UNICODE || j == NPY_VOID) {
                    _npy_type_promotion_table[i][j] = -1;
                    _npy_type_promotion_table[j][i] = -1;
                }
                else if (_npy_can_cast_safely_table[i][j]) {
                    _npy_type_promotion_table[i][j] = j;
                    _npy_type_promotion_table[j][i] = j;
                }
                else if (_npy_can_cast_safely_table[j][i]) {
                    _npy_type_promotion_table[i][j] = i;
                    _npy_type_promotion_table[j][i] = i;
                }
                else {
                    int k, iskind, jskind, skind;
                    iskind = _npy_scalar_kinds_table[i];
                    jskind = _npy_scalar_kinds_table[j];
                    /* If there's no kind (void/string/etc) */
                    if (iskind == NPY_NOSCALAR || jskind == NPY_NOSCALAR) {
                        k = -1;
                    }
                    else {
                        /* Start with the type of larger kind */
                        if (iskind > jskind) {
                            skind = iskind;
                            k = i;
                        }
                        else {
                            skind = jskind;
                            k = j;
                        }
                        for (;;) {
                            /* Try the next larger type of this kind */
                            k = _npy_next_larger_type_table[k];

                            /* If there is no larger, try a larger kind */
                            if (k < 0) {
                                ++skind;
                                /* Use -1 to signal no promoted type found */
                                if (skind < NPY_NSCALARKINDS) {
                                    k = _npy_smallest_type_of_kind_table[skind];
                                }
                                else {
                                    k = -1;
                                    break;
                                }
                            }

                            if (_npy_can_cast_safely_table[i][k] &&
                                            _npy_can_cast_safely_table[j][k]) {
                                break;
                            }
                        }
                    }
                    _npy_type_promotion_table[i][j] = k;
                    _npy_type_promotion_table[j][i] = k;
                }
            }
        }
    }
}


static PyNumberMethods longdoubletype_as_number;
static PyNumberMethods clongdoubletype_as_number;
static void init_basetypes(void);


NPY_NO_EXPORT void
initialize_numeric_types(void)
{
    init_basetypes();
    PyGenericArrType_Type.tp_dealloc = (destructor)gentype_dealloc;
    PyGenericArrType_Type.tp_as_number = &gentype_as_number;
    PyGenericArrType_Type.tp_as_buffer = &gentype_as_buffer;
    PyGenericArrType_Type.tp_as_mapping = &gentype_as_mapping;
    PyGenericArrType_Type.tp_flags = BASEFLAGS;
    PyGenericArrType_Type.tp_methods = gentype_methods;
    PyGenericArrType_Type.tp_getset = gentype_getsets;
    PyGenericArrType_Type.tp_new = NULL;
    PyGenericArrType_Type.tp_alloc = gentype_alloc;
    PyGenericArrType_Type.tp_free = (freefunc)gentype_free;
    PyGenericArrType_Type.tp_repr = gentype_repr;
    PyGenericArrType_Type.tp_str = gentype_str;
    PyGenericArrType_Type.tp_richcompare = gentype_richcompare;

    PyBoolArrType_Type.tp_as_number = &bool_arrtype_as_number;
    /*
     * need to add dummy versions with filled-in nb_index
     * in-order for PyType_Ready to fill in .__index__() method
     */

    /**begin repeat
     * #name = byte, short, int, long, longlong, ubyte, ushort,
     *         uint, ulong, ulonglong#
     * #NAME = Byte, Short, Int, Long, LongLong, UByte, UShort,
     *         UInt, ULong, ULongLong#
     */
    Py@NAME@ArrType_Type.tp_as_number = &@name@_arrtype_as_number;
    Py@NAME@ArrType_Type.tp_as_number->nb_index = (unaryfunc)@name@_index;

    /**end repeat**/
    PyBoolArrType_Type.tp_as_number->nb_index = (unaryfunc)bool_index;

    PyStringArrType_Type.tp_alloc = NULL;
    PyStringArrType_Type.tp_free = NULL;

    PyStringArrType_Type.tp_repr = stringtype_repr;
    PyStringArrType_Type.tp_str = stringtype_str;

    PyUnicodeArrType_Type.tp_repr = unicodetype_repr;
    PyUnicodeArrType_Type.tp_str = unicodetype_str;

    PyVoidArrType_Type.tp_methods = voidtype_methods;
    PyVoidArrType_Type.tp_getset = voidtype_getsets;
    PyVoidArrType_Type.tp_as_mapping = &voidtype_as_mapping;
    PyVoidArrType_Type.tp_as_sequence = &voidtype_as_sequence;

    PyIntegerArrType_Type.tp_getset = inttype_getsets;

    /**begin repeat
     * #NAME= Number, Integer, SignedInteger, UnsignedInteger, Inexact,
     *        Floating, ComplexFloating, Flexible, Character#
     */

    Py@NAME@ArrType_Type.tp_flags = BASEFLAGS;

    /**end repeat**/

    /**begin repeat
     * #name = bool, byte, short, int, long, longlong, ubyte, ushort, uint,
     *         ulong, ulonglong, half, float, double, longdouble, cfloat,
     *         cdouble, clongdouble, string, unicode, void, object, datetime,
     *         timedelta#
     * #NAME = Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt,
     *         ULong, ULongLong, Half, Float, Double, LongDouble, CFloat,
     *         CDouble, CLongDouble, String, Unicode, Void, Object, Datetime,
     *         Timedelta#
     */

    Py@NAME@ArrType_Type.tp_flags = BASEFLAGS;
    Py@NAME@ArrType_Type.tp_new = @name@_arrtype_new;
    Py@NAME@ArrType_Type.tp_richcompare = gentype_richcompare;

    /**end repeat**/

    /**begin repeat
     * #name = bool, byte, short, ubyte, ushort, uint, ulong, ulonglong,
     *         half, float, longdouble, cfloat, clongdouble, void, object,
     *         datetime, timedelta#
     * #NAME = Bool, Byte, Short, UByte, UShort, UInt, ULong, ULongLong,
     *         Half, Float, LongDouble, CFloat, CLongDouble, Void, Object,
     *         Datetime, Timedelta#
     */

    Py@NAME@ArrType_Type.tp_hash = @name@_arrtype_hash;

    /**end repeat**/

    /**begin repeat
     * #name = cfloat, clongdouble#
     * #NAME = CFloat, CLongDouble#
     */

    Py@NAME@ArrType_Type.tp_methods = @name@type_methods;

    /**end repeat**/

#if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K)
    /* We won't be inheriting from Python Int type. */
    PyIntArrType_Type.tp_hash = int_arrtype_hash;
#endif

#if defined(NPY_PY3K)
    /* We won't be inheriting from Python Int type. */
    PyLongArrType_Type.tp_hash = long_arrtype_hash;
#endif

#if (NPY_SIZEOF_LONG != NPY_SIZEOF_LONGLONG) || defined(NPY_PY3K)
    /* We won't be inheriting from Python Int type. */
    PyLongLongArrType_Type.tp_hash = longlong_arrtype_hash;
#endif

    /**begin repeat
     * #name = repr, str#
     */

    PyHalfArrType_Type.tp_@name@ = halftype_@name@;

    PyFloatArrType_Type.tp_@name@ = floattype_@name@;
    PyCFloatArrType_Type.tp_@name@ = cfloattype_@name@;

    PyDoubleArrType_Type.tp_@name@ = doubletype_@name@;
    PyCDoubleArrType_Type.tp_@name@  = cdoubletype_@name@;

    PyDatetimeArrType_Type.tp_@name@ = datetimetype_@name@;
    PyTimedeltaArrType_Type.tp_@name@ = timedeltatype_@name@;

    /**end repeat**/

    PyHalfArrType_Type.tp_print = halftype_print;
    PyFloatArrType_Type.tp_print = floattype_print;
    PyDoubleArrType_Type.tp_print = doubletype_print;
    PyLongDoubleArrType_Type.tp_print = longdoubletype_print;

    PyCFloatArrType_Type.tp_print = cfloattype_print;
    PyCDoubleArrType_Type.tp_print = cdoubletype_print;
    PyCLongDoubleArrType_Type.tp_print = clongdoubletype_print;

    /*
     * These need to be coded specially because getitem does not
     * return a normal Python type
     */
    PyLongDoubleArrType_Type.tp_as_number = &longdoubletype_as_number;
    PyCLongDoubleArrType_Type.tp_as_number = &clongdoubletype_as_number;

    /**begin repeat
     * #name = int, float, repr, str#
     * #kind = tp_as_number->nb*2, tp*2#
     */

    PyLongDoubleArrType_Type.@kind@_@name@ = longdoubletype_@name@;
    PyCLongDoubleArrType_Type.@kind@_@name@ = clongdoubletype_@name@;

    /**end repeat**/


#if !defined(NPY_PY3K)
    /**begin repeat
     * #name = long, hex, oct#
     * #kind = tp_as_number->nb*3#
     */

    PyLongDoubleArrType_Type.@kind@_@name@ = longdoubletype_@name@;
    PyCLongDoubleArrType_Type.@kind@_@name@ = clongdoubletype_@name@;

    /**end repeat**/

#endif

    PyStringArrType_Type.tp_itemsize = sizeof(char);
    PyVoidArrType_Type.tp_dealloc = (destructor) void_dealloc;

    PyArrayIter_Type.tp_iter = PyObject_SelfIter;
    PyArrayMapIter_Type.tp_iter = PyObject_SelfIter;
}

typedef struct {
    PyTypeObject * type;
    int typenum;
} scalar_type;

static scalar_type typeobjects[] = {
    {&PyBoolArrType_Type, NPY_BOOL},
    {&PyByteArrType_Type, NPY_BYTE},
    {&PyUByteArrType_Type, NPY_UBYTE},
    {&PyShortArrType_Type, NPY_SHORT},
    {&PyUShortArrType_Type, NPY_USHORT},
    {&PyIntArrType_Type, NPY_INT},
    {&PyUIntArrType_Type, NPY_UINT},
    {&PyLongArrType_Type, NPY_LONG},
    {&PyULongArrType_Type, NPY_ULONG},
    {&PyLongLongArrType_Type, NPY_LONGLONG},
    {&PyULongLongArrType_Type, NPY_ULONGLONG},
    {&PyFloatArrType_Type, NPY_FLOAT},
    {&PyDoubleArrType_Type, NPY_DOUBLE},
    {&PyLongDoubleArrType_Type, NPY_LONGDOUBLE},
    {&PyCFloatArrType_Type, NPY_CFLOAT},
    {&PyCDoubleArrType_Type, NPY_CDOUBLE},
    {&PyCLongDoubleArrType_Type, NPY_CLONGDOUBLE},
    {&PyObjectArrType_Type, NPY_OBJECT},
    {&PyStringArrType_Type, NPY_STRING},
    {&PyUnicodeArrType_Type, NPY_UNICODE},
    {&PyVoidArrType_Type, NPY_VOID},
    {&PyDatetimeArrType_Type, NPY_DATETIME},
    {&PyTimedeltaArrType_Type, NPY_TIMEDELTA},
    {&PyHalfArrType_Type, NPY_HALF}
};

static int compare_types(const void * a_, const void * b_)
{
    const PyTypeObject * a = ((const scalar_type *)a_)->type;
    const PyTypeObject * b = ((const scalar_type *)b_)->type;
    if (a < b) {
        return -1;
    }
    else if (a > b) {
        return 1;
    }
    return 0;
}

static void init_basetypes(void)
{
    qsort(typeobjects, sizeof(typeobjects) / sizeof(typeobjects[0]),
          sizeof(typeobjects[0]),
          compare_types);
}


NPY_NO_EXPORT int
get_typeobj_idx(PyTypeObject * obj)
{
    npy_intp imin = 0, imax = sizeof(typeobjects) / sizeof(typeobjects[0]) - 1;
    while (imax >= imin)
    {
        npy_intp imid = ((imax - imin) / 2) + imin;
        if(typeobjects[imid].type == obj) {
            return imid;
        }
        else if (typeobjects[imid].type < obj) {
            imin = imid + 1;
        }
        else {
            imax = imid - 1;
        }
    }

    return -1;
}

NPY_NO_EXPORT int
is_anyscalar_exact(PyObject *obj)
{
    return get_typeobj_idx(Py_TYPE(obj)) >= 0;
}

NPY_NO_EXPORT int
_typenum_fromtypeobj(PyObject *type, int user)
{
    int typenum, i;

    typenum = NPY_NOTYPE;
    i = get_typeobj_idx((PyTypeObject*)type);
    if (i >= 0) {
        typenum = typeobjects[i].typenum;
    }

    if (!user) {
        return typenum;
    }
    /* Search any registered types */
    i = 0;
    while (i < NPY_NUMUSERTYPES) {
        if (type == (PyObject *)(userdescrs[i]->typeobj)) {
            typenum = i + NPY_USERDEF;
            break;
        }
        i++;
    }
    return typenum;
}
