#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"

#include "numpy/npy_math.h"

#include "npy_config.h"

#include "npy_pycompat.h"
#include "multiarraymodule.h"

#include "common.h"
#include "ctors.h"
#include "convert_datatype.h"
#include "shape.h"
#include "buffer.h"
#include "lowlevel_strided_loops.h"
#include "methods.h"
#include "_datetime.h"
#include "datetime_strings.h"
#include "array_assign.h"
#include "mapping.h" /* for array_item_asarray */
#include "templ_common.h" /* for npy_mul_with_overflow_intp */
#include "alloc.h"
#include <assert.h>

#include "get_attr_string.h"

/*
 * Reading from a file or a string.
 *
 * As much as possible, we try to use the same code for both files and strings,
 * so the semantics for fromstring and fromfile are the same, especially with
 * regards to the handling of text representations.
 */

typedef int (*next_element)(void **, void *, PyArray_Descr *, void *);
typedef int (*skip_separator)(void **, const char *, void *);

static int
fromstr_next_element(char **s, void *dptr, PyArray_Descr *dtype,
                     const char *end)
{
    char *e = *s;
    int r = dtype->f->fromstr(*s, dptr, &e, dtype);
    /*
     * fromstr always returns 0 for basic dtypes
     * s points to the end of the parsed string
     * if an error occurs s is not changed
     */
    if (*s == e) {
        /* Nothing read */
        return -1;
    }
    *s = e;
    if (end != NULL && *s > end) {
        return -1;
    }
    return r;
}

static int
fromfile_next_element(FILE **fp, void *dptr, PyArray_Descr *dtype,
                      void *NPY_UNUSED(stream_data))
{
    /* the NULL argument is for backwards-compatibility */
    int r = dtype->f->scanfunc(*fp, dptr, NULL, dtype);
    /* r can be EOF or the number of items read (0 or 1) */
    if (r == 1) {
        return 0;
    }
    else {
        return -1;
    }
}

/*
 * Remove multiple whitespace from the separator, and add a space to the
 * beginning and end. This simplifies the separator-skipping code below.
 */
static char *
swab_separator(const char *sep)
{
    int skip_space = 0;
    char *s, *start;

    s = start = malloc(strlen(sep)+3);
    if (s == NULL) {
        return NULL;
    }
    /* add space to front if there isn't one */
    if (*sep != '\0' && !isspace(*sep)) {
        *s = ' '; s++;
    }
    while (*sep != '\0') {
        if (isspace(*sep)) {
            if (skip_space) {
                sep++;
            }
            else {
                *s = ' ';
                s++;
                sep++;
                skip_space = 1;
            }
        }
        else {
            *s = *sep;
            s++;
            sep++;
            skip_space = 0;
        }
    }
    /* add space to end if there isn't one */
    if (s != start && s[-1] == ' ') {
        *s = ' ';
        s++;
    }
    *s = '\0';
    return start;
}

/*
 * Assuming that the separator is the next bit in the string (file), skip it.
 *
 * Single spaces in the separator are matched to arbitrary-long sequences
 * of whitespace in the input. If the separator consists only of spaces,
 * it matches one or more whitespace characters.
 *
 * If we can't match the separator, return -2.
 * If we hit the end of the string (file), return -1.
 * Otherwise, return 0.
 */
static int
fromstr_skip_separator(char **s, const char *sep, const char *end)
{
    char *string = *s;
    int result = 0;
    while (1) {
        char c = *string;
        if (c == '\0' || (end != NULL && string >= end)) {
            result = -1;
            break;
        }
        else if (*sep == '\0') {
            if (string != *s) {
                /* matched separator */
                result = 0;
                break;
            }
            else {
                /* separator was whitespace wildcard that didn't match */
                result = -2;
                break;
            }
        }
        else if (*sep == ' ') {
            /* whitespace wildcard */
            if (!isspace(c)) {
                sep++;
                continue;
            }
        }
        else if (*sep != c) {
            result = -2;
            break;
        }
        else {
            sep++;
        }
        string++;
    }
    *s = string;
    return result;
}

static int
fromfile_skip_separator(FILE **fp, const char *sep, void *NPY_UNUSED(stream_data))
{
    int result = 0;
    const char *sep_start = sep;

    while (1) {
        int c = fgetc(*fp);

        if (c == EOF) {
            result = -1;
            break;
        }
        else if (*sep == '\0') {
            ungetc(c, *fp);
            if (sep != sep_start) {
                /* matched separator */
                result = 0;
                break;
            }
            else {
                /* separator was whitespace wildcard that didn't match */
                result = -2;
                break;
            }
        }
        else if (*sep == ' ') {
            /* whitespace wildcard */
            if (!isspace(c)) {
                sep++;
                sep_start++;
                ungetc(c, *fp);
            }
            else if (sep == sep_start) {
                sep_start--;
            }
        }
        else if (*sep != c) {
            ungetc(c, *fp);
            result = -2;
            break;
        }
        else {
            sep++;
        }
    }
    return result;
}

/*
 * Change a sub-array field to the base descriptor
 * and update the dimensions and strides
 * appropriately.  Dimensions and strides are added
 * to the end.
 *
 * Strides are only added if given (because data is given).
 */
static int
_update_descr_and_dimensions(PyArray_Descr **des, npy_intp *newdims,
                             npy_intp *newstrides, int oldnd)
{
    PyArray_Descr *old;
    int newnd;
    int numnew;
    npy_intp *mydim;
    int i;
    int tuple;

    old = *des;
    *des = old->subarray->base;


    mydim = newdims + oldnd;
    tuple = PyTuple_Check(old->subarray->shape);
    if (tuple) {
        numnew = PyTuple_GET_SIZE(old->subarray->shape);
    }
    else {
        numnew = 1;
    }


    newnd = oldnd + numnew;
    if (newnd > NPY_MAXDIMS) {
        goto finish;
    }
    if (tuple) {
        for (i = 0; i < numnew; i++) {
            mydim[i] = (npy_intp) PyInt_AsLong(
                    PyTuple_GET_ITEM(old->subarray->shape, i));
        }
    }
    else {
        mydim[0] = (npy_intp) PyInt_AsLong(old->subarray->shape);
    }

    if (newstrides) {
        npy_intp tempsize;
        npy_intp *mystrides;

        mystrides = newstrides + oldnd;
        /* Make new strides -- always C-contiguous */
        tempsize = (*des)->elsize;
        for (i = numnew - 1; i >= 0; i--) {
            mystrides[i] = tempsize;
            tempsize *= mydim[i] ? mydim[i] : 1;
        }
    }

 finish:
    Py_INCREF(*des);
    Py_DECREF(old);
    return newnd;
}

NPY_NO_EXPORT void
_unaligned_strided_byte_copy(char *dst, npy_intp outstrides, char *src,
                             npy_intp instrides, npy_intp N, int elsize)
{
    npy_intp i;
    char *tout = dst;
    char *tin = src;

#define _COPY_N_SIZE(size) \
    for(i=0; i<N; i++) { \
        memcpy(tout, tin, size); \
        tin += instrides; \
        tout += outstrides; \
    } \
    return

    switch(elsize) {
    case 8:
        _COPY_N_SIZE(8);
    case 4:
        _COPY_N_SIZE(4);
    case 1:
        _COPY_N_SIZE(1);
    case 2:
        _COPY_N_SIZE(2);
    case 16:
        _COPY_N_SIZE(16);
    default:
        _COPY_N_SIZE(elsize);
    }
#undef _COPY_N_SIZE

}

NPY_NO_EXPORT void
_strided_byte_swap(void *p, npy_intp stride, npy_intp n, int size)
{
    char *a, *b, c = 0;
    int j, m;

    switch(size) {
    case 1: /* no byteswap necessary */
        break;
    case 4:
        if (npy_is_aligned((void*)((npy_intp)p | stride), sizeof(npy_uint32))) {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_uint32 * a_ = (npy_uint32 *)a;
                *a_ = npy_bswap4(*a_);
            }
        }
        else {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_bswap4_unaligned(a);
            }
        }
        break;
    case 8:
        if (npy_is_aligned((void*)((npy_intp)p | stride), sizeof(npy_uint64))) {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_uint64 * a_ = (npy_uint64 *)a;
                *a_ = npy_bswap8(*a_);
            }
        }
        else {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_bswap8_unaligned(a);
            }
        }
        break;
    case 2:
        if (npy_is_aligned((void*)((npy_intp)p | stride), sizeof(npy_uint16))) {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_uint16 * a_ = (npy_uint16 *)a;
                *a_ = npy_bswap2(*a_);
            }
        }
        else {
            for (a = (char*)p; n > 0; n--, a += stride) {
                npy_bswap2_unaligned(a);
            }
        }
        break;
    default:
        m = size/2;
        for (a = (char *)p; n > 0; n--, a += stride - m) {
            b = a + (size - 1);
            for (j = 0; j < m; j++) {
                c=*a; *a++ = *b; *b-- = c;
            }
        }
        break;
    }
}

NPY_NO_EXPORT void
byte_swap_vector(void *p, npy_intp n, int size)
{
    _strided_byte_swap(p, (npy_intp) size, n, size);
    return;
}

/* If numitems > 1, then dst must be contiguous */
NPY_NO_EXPORT void
copy_and_swap(void *dst, void *src, int itemsize, npy_intp numitems,
              npy_intp srcstrides, int swap)
{
    if ((numitems == 1) || (itemsize == srcstrides)) {
        memcpy(dst, src, itemsize*numitems);
    }
    else {
        npy_intp i;
        char *s1 = (char *)src;
        char *d1 = (char *)dst;

        for (i = 0; i < numitems; i++) {
            memcpy(d1, s1, itemsize);
            d1 += itemsize;
            s1 += srcstrides;
        }
    }

    if (swap) {
        byte_swap_vector(dst, numitems, itemsize);
    }
}

/*
 * adapted from Numarray,
 * a: destination array
 * s: source object, array or sequence
 * dim: current recursion dimension, must be 0 on first call
 * dst: must be NULL on first call
 * it is a view on the destination array viewing the place where to put the
 * data of the current recursion
 */
static int
setArrayFromSequence(PyArrayObject *a, PyObject *s,
                        int dim, PyArrayObject * dst)
{
    Py_ssize_t i, slen;
    int res = -1;

    /* first recursion, view equal destination */
    if (dst == NULL)
        dst = a;

    /*
     * This code is to ensure that the sequence access below will
     * return a lower-dimensional sequence.
     */

    /* INCREF on entry DECREF on exit */
    Py_INCREF(s);

    if (PyArray_Check(s)) {
        if (!(PyArray_CheckExact(s))) {
            /*
             * make sure a base-class array is used so that the dimensionality
             * reduction assumption is correct.
             */
            /* This will DECREF(s) if replaced */
            s = PyArray_EnsureArray(s);
            if (s == NULL) {
                goto fail;
            }
        }

        /* dst points to correct array subsection */
        if (PyArray_CopyInto(dst, (PyArrayObject *)s) < 0) {
            goto fail;
        }

        Py_DECREF(s);
        return 0;
    }

    if (dim > PyArray_NDIM(a)) {
        PyErr_Format(PyExc_ValueError,
                 "setArrayFromSequence: sequence/array dimensions mismatch.");
        goto fail;
    }

    slen = PySequence_Length(s);
    if (slen < 0) {
        goto fail;
    }
    /*
     * Either the dimensions match, or the sequence has length 1 and can
     * be broadcast to the destination.
     */
    if (slen != PyArray_DIMS(a)[dim] && slen != 1) {
        PyErr_Format(PyExc_ValueError,
                 "cannot copy sequence with size %d to array axis "
                 "with dimension %d", (int)slen, (int)PyArray_DIMS(a)[dim]);
        goto fail;
    }

    /* Broadcast the one element from the sequence to all the outputs */
    if (slen == 1) {
        PyObject *o;
        npy_intp alen = PyArray_DIM(a, dim);

        o = PySequence_GetItem(s, 0);
        if (o == NULL) {
            goto fail;
        }

        for (i = 0; i < alen; i++) {
            if ((PyArray_NDIM(a) - dim) > 1) {
                PyArrayObject * tmp =
                    (PyArrayObject *)array_item_asarray(dst, i);
                if (tmp == NULL) {
                    goto fail;
                }

                res = setArrayFromSequence(a, o, dim+1, tmp);
                Py_DECREF(tmp);
            }
            else {
                char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]);
                res = PyArray_SETITEM(dst, b, o);
            }
            if (res < 0) {
                Py_DECREF(o);
                goto fail;
            }
        }
        Py_DECREF(o);
    }
    /* Copy element by element */
    else {
        PyObject * seq;
        seq = PySequence_Fast(s, "Could not convert object to sequence");
        if (seq == NULL) {
            goto fail;
        }
        for (i = 0; i < slen; i++) {
            PyObject * o = PySequence_Fast_GET_ITEM(seq, i);
            if ((PyArray_NDIM(a) - dim) > 1) {
                PyArrayObject * tmp =
                    (PyArrayObject *)array_item_asarray(dst, i);
                if (tmp == NULL) {
                    Py_DECREF(seq);
                    goto fail;
                }

                res = setArrayFromSequence(a, o, dim+1, tmp);
                Py_DECREF(tmp);
            }
            else {
                char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]);
                res = PyArray_SETITEM(dst, b, o);
            }
            if (res < 0) {
                Py_DECREF(seq);
                goto fail;
            }
        }
        Py_DECREF(seq);
    }

    Py_DECREF(s);
    return 0;

 fail:
    Py_DECREF(s);
    return res;
}

NPY_NO_EXPORT int
PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v)
{
    if (!PySequence_Check(v)) {
        PyErr_SetString(PyExc_ValueError,
                        "assignment from non-sequence");
        return -1;
    }
    if (PyArray_NDIM(self) == 0) {
        PyErr_SetString(PyExc_ValueError,
                        "assignment to 0-d array");
        return -1;
    }
    return setArrayFromSequence(self, v, 0, NULL);
}

/*
 * The rest of this code is to build the right kind of array
 * from a python object.
 */

static int
discover_itemsize(PyObject *s, int nd, int *itemsize, int string_type)
{
    int r;
    npy_intp n, i;

    if (PyArray_Check(s)) {
        *itemsize = PyArray_MAX(*itemsize, PyArray_ITEMSIZE((PyArrayObject *)s));
        return 0;
    }

    if ((nd == 0) || PyString_Check(s) ||
#if defined(NPY_PY3K)
            PyMemoryView_Check(s) ||
#else
            PyBuffer_Check(s) ||
#endif
            PyUnicode_Check(s)) {

        /* If an object has no length, leave it be */
        if (string_type && s != NULL &&
                !PyString_Check(s) && !PyUnicode_Check(s)) {
            PyObject *s_string = NULL;
            if (string_type == NPY_STRING) {
                s_string = PyObject_Str(s);
            }
            else {
#if defined(NPY_PY3K)
                s_string = PyObject_Str(s);
#else
                s_string = PyObject_Unicode(s);
#endif
            }
            if (s_string) {
                n = PyObject_Length(s_string);
                Py_DECREF(s_string);
            }
            else {
                n = -1;
            }
        }
        else {
            n = PyObject_Length(s);
        }
        if (n == -1) {
            PyErr_Clear();
        }
        else {
            *itemsize = PyArray_MAX(*itemsize, n);
        }
        return 0;
    }

    n = PySequence_Length(s);
    for (i = 0; i < n; i++) {
        PyObject *e = PySequence_GetItem(s,i);

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

        r = discover_itemsize(e, nd - 1, itemsize, string_type);
        Py_DECREF(e);
        if (r == -1) {
            return -1;
        }
    }

    return 0;
}

/*
 * Take an arbitrary object and discover how many dimensions it
 * has, filling in the dimensions as we go.
 */
static int
discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
                                    int stop_at_string, int stop_at_tuple,
                                    int *out_is_object)
{
    PyObject *e;
    int r;
    npy_intp n, i;
    Py_buffer buffer_view;
    PyObject * seq;

    if (*maxndim == 0) {
        return 0;
    }

    /* obj is an Array */
    if (PyArray_Check(obj)) {
        PyArrayObject *arr = (PyArrayObject *)obj;

        if (PyArray_NDIM(arr) < *maxndim) {
            *maxndim = PyArray_NDIM(arr);
        }

        for (i=0; i<*maxndim; i++) {
            d[i] = PyArray_DIM(arr,i);
        }
        return 0;
    }

    /* obj is a Scalar */
    if (PyArray_IsScalar(obj, Generic)) {
        *maxndim = 0;
        return 0;
    }

    /* obj is not a Sequence */
    if (!PySequence_Check(obj) ||
            PySequence_Length(obj) < 0) {
        *maxndim = 0;
        PyErr_Clear();
        return 0;
    }

    /* obj is a String */
    if (PyString_Check(obj) ||
#if defined(NPY_PY3K)
#else
            PyBuffer_Check(obj) ||
#endif
            PyUnicode_Check(obj)) {
        if (stop_at_string) {
            *maxndim = 0;
        }
        else {
            d[0] = PySequence_Length(obj);
            *maxndim = 1;
        }
        return 0;
    }

    /* obj is a Tuple, but tuples aren't expanded */
    if (stop_at_tuple && PyTuple_Check(obj)) {
        *maxndim = 0;
        return 0;
    }

    /* obj is a PEP 3118 buffer */
    /* PEP 3118 buffer interface */
    if (PyObject_CheckBuffer(obj) == 1) {
        memset(&buffer_view, 0, sizeof(Py_buffer));
        if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 ||
            PyObject_GetBuffer(obj, &buffer_view, PyBUF_ND) == 0) {
            int nd = buffer_view.ndim;
            if (nd < *maxndim) {
                *maxndim = nd;
            }
            for (i=0; i<*maxndim; i++) {
                d[i] = buffer_view.shape[i];
            }
            PyBuffer_Release(&buffer_view);
            return 0;
        }
        else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) {
            d[0] = buffer_view.len;
            *maxndim = 1;
            PyBuffer_Release(&buffer_view);
            return 0;
        }
        else {
            PyErr_Clear();
        }
    }

    /* obj has the __array_struct__ interface */
    e = PyArray_LookupSpecial_OnInstance(obj, "__array_struct__");
    if (e != NULL) {
        int nd = -1;
        if (NpyCapsule_Check(e)) {
            PyArrayInterface *inter;
            inter = (PyArrayInterface *)NpyCapsule_AsVoidPtr(e);
            if (inter->two == 2) {
                nd = inter->nd;
                if (nd >= 0) {
                    if (nd < *maxndim) {
                        *maxndim = nd;
                    }
                    for (i=0; i<*maxndim; i++) {
                        d[i] = inter->shape[i];
                    }
                }
            }
        }
        Py_DECREF(e);
        if (nd >= 0) {
            return 0;
        }
    }

    /* obj has the __array_interface__ interface */
    e = PyArray_LookupSpecial_OnInstance(obj, "__array_interface__");
    if (e != NULL) {
        int nd = -1;
        if (PyDict_Check(e)) {
            PyObject *new;
            new = PyDict_GetItemString(e, "shape");
            if (new && PyTuple_Check(new)) {
                nd = PyTuple_GET_SIZE(new);
                if (nd < *maxndim) {
                    *maxndim = nd;
                }
                for (i=0; i<*maxndim; i++) {
                    d[i] = PyInt_AsSsize_t(PyTuple_GET_ITEM(new, i));
                    if (d[i] < 0) {
                        PyErr_SetString(PyExc_RuntimeError,
                                "Invalid shape in __array_interface__");
                        Py_DECREF(e);
                        return -1;
                    }
                }
            }
        }
        Py_DECREF(e);
        if (nd >= 0) {
            return 0;
        }
    }

    seq = PySequence_Fast(obj, "Could not convert object to sequence");
    if (seq == NULL) {
        /*
         * PySequence_Check detects whether an old type object is a
         * sequence by the presence of the __getitem__ attribute, and
         * for new type objects that aren't dictionaries by the
         * presence of the __len__ attribute as well. In either case it
         * is possible to have an object that tests as a sequence but
         * doesn't behave as a sequence and consequently, the
         * PySequence_GetItem call can fail. When that happens and the
         * object looks like a dictionary, we truncate the dimensions
         * and set the object creation flag, otherwise we pass the
         * error back up the call chain.
         */
        if (PyErr_ExceptionMatches(PyExc_KeyError)) {
            PyErr_Clear();
            *maxndim = 0;
            *out_is_object = 1;
            return 0;
        }
        else {
            return -1;
        }
    }
    n = PySequence_Fast_GET_SIZE(seq);

    d[0] = n;

    /* 1-dimensional sequence */
    if (n == 0 || *maxndim == 1) {
        *maxndim = 1;
        Py_DECREF(seq);
        return 0;
    }
    else {
        npy_intp dtmp[NPY_MAXDIMS];
        int j, maxndim_m1 = *maxndim - 1;
        e = PySequence_Fast_GET_ITEM(seq, 0);

        r = discover_dimensions(e, &maxndim_m1, d + 1, check_it,
                                        stop_at_string, stop_at_tuple,
                                        out_is_object);
        if (r < 0) {
            Py_DECREF(seq);
            return r;
        }

        /* For the dimension truncation check below */
        *maxndim = maxndim_m1 + 1;
        for (i = 1; i < n; ++i) {
            e = PySequence_Fast_GET_ITEM(seq, i);
            /* Get the dimensions of the first item */
            r = discover_dimensions(e, &maxndim_m1, dtmp, check_it,
                                            stop_at_string, stop_at_tuple,
                                            out_is_object);
            if (r < 0) {
                Py_DECREF(seq);
                return r;
            }

            /* Reduce max_ndim_m1 to just items which match */
            for (j = 0; j < maxndim_m1; ++j) {
                if (dtmp[j] != d[j+1]) {
                    maxndim_m1 = j;
                    break;
                }
            }
        }
        /*
         * If the dimensions are truncated, need to produce
         * an object array.
         */
        if (maxndim_m1 + 1 < *maxndim) {
            *out_is_object = 1;
            *maxndim = maxndim_m1 + 1;
        }
    }

    Py_DECREF(seq);

    return 0;
}

/*
 * Generic new array creation routine.
 * Internal variant with calloc argument for PyArray_Zeros.
 *
 * steals a reference to descr. On failure or descr->subarray, descr will
 * be decrefed.
 */
NPY_NO_EXPORT PyObject *
PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
                         npy_intp *dims, npy_intp *strides, void *data,
                         int flags, PyObject *obj, int zeroed,
                         int allow_emptystring)
{
    PyArrayObject_fields *fa;
    int i, is_empty;
    npy_intp nbytes;

    if (descr->subarray) {
        PyObject *ret;
        npy_intp newdims[2*NPY_MAXDIMS];
        npy_intp *newstrides = NULL;
        memcpy(newdims, dims, nd*sizeof(npy_intp));
        if (strides) {
            newstrides = newdims + NPY_MAXDIMS;
            memcpy(newstrides, strides, nd*sizeof(npy_intp));
        }
        nd =_update_descr_and_dimensions(&descr, newdims,
                                         newstrides, nd);
        ret = PyArray_NewFromDescr_int(subtype, descr, nd, newdims,
                                       newstrides,
                                       data, flags, obj, zeroed,
                                       allow_emptystring);
        return ret;
    }

    if ((unsigned int)nd > (unsigned int)NPY_MAXDIMS) {
        PyErr_Format(PyExc_ValueError,
                     "number of dimensions must be within [0, %d]",
                     NPY_MAXDIMS);
        Py_DECREF(descr);
        return NULL;
    }

    /* Check datatype element size */
    nbytes = descr->elsize;
    if (PyDataType_ISUNSIZED(descr)) {
        if (!PyDataType_ISFLEXIBLE(descr)) {
            PyErr_SetString(PyExc_TypeError, "Empty data-type");
            Py_DECREF(descr);
            return NULL;
        }
        else if (PyDataType_ISSTRING(descr) && !allow_emptystring &&
                 data == NULL) {
            PyArray_DESCR_REPLACE(descr);
            if (descr == NULL) {
                return NULL;
            }
            if (descr->type_num == NPY_STRING) {
                nbytes = descr->elsize = 1;
            }
            else {
                nbytes = descr->elsize = sizeof(npy_ucs4);
            }
        }
    }

    /* Check dimensions and multiply them to nbytes */
    is_empty = 0;
    for (i = 0; i < nd; i++) {
        npy_intp dim = dims[i];

        if (dim == 0) {
            /*
             * Compare to PyArray_OverflowMultiplyList that
             * returns 0 in this case.
             */
            is_empty = 1;
            continue;
        }

        if (dim < 0) {
            PyErr_SetString(PyExc_ValueError,
                "negative dimensions are not allowed");
            Py_DECREF(descr);
            return NULL;
        }

        /*
         * Care needs to be taken to avoid integer overflow when
         * multiplying the dimensions together to get the total size of the
         * array.
         */
        if (npy_mul_with_overflow_intp(&nbytes, nbytes, dim)) {
            PyErr_SetString(PyExc_ValueError,
                "array is too big; `arr.size * arr.dtype.itemsize` "
                "is larger than the maximum possible size.");
            Py_DECREF(descr);
            return NULL;
        }
    }

    fa = (PyArrayObject_fields *) subtype->tp_alloc(subtype, 0);
    if (fa == NULL) {
        Py_DECREF(descr);
        return NULL;
    }
    fa->nd = nd;
    fa->dimensions = NULL;
    fa->data = NULL;
    if (data == NULL) {
        fa->flags = NPY_ARRAY_DEFAULT;
        if (flags) {
            fa->flags |= NPY_ARRAY_F_CONTIGUOUS;
            if (nd > 1) {
                fa->flags &= ~NPY_ARRAY_C_CONTIGUOUS;
            }
            flags = NPY_ARRAY_F_CONTIGUOUS;
        }
    }
    else {
        fa->flags = (flags & ~NPY_ARRAY_WRITEBACKIFCOPY);
        fa->flags = (fa->flags & ~NPY_ARRAY_UPDATEIFCOPY);
    }
    fa->descr = descr;
    fa->base = (PyObject *)NULL;
    fa->weakreflist = (PyObject *)NULL;

    if (nd > 0) {
        fa->dimensions = npy_alloc_cache_dim(2 * nd);
        if (fa->dimensions == NULL) {
            PyErr_NoMemory();
            goto fail;
        }
        fa->strides = fa->dimensions + nd;
        memcpy(fa->dimensions, dims, sizeof(npy_intp)*nd);
        if (strides == NULL) {  /* fill it in */
            _array_fill_strides(fa->strides, dims, nd, descr->elsize,
                                flags, &(fa->flags));
        }
        else {
            /*
             * we allow strides even when we create
             * the memory, but be careful with this...
             */
            memcpy(fa->strides, strides, sizeof(npy_intp)*nd);
        }
    }
    else {
        fa->dimensions = fa->strides = NULL;
        fa->flags |= NPY_ARRAY_F_CONTIGUOUS;
    }

    if (data == NULL) {
        /*
         * Allocate something even for zero-space arrays
         * e.g. shape=(0,) -- otherwise buffer exposure
         * (a.data) doesn't work as it should.
         * Could probably just allocate a few bytes here. -- Chuck
         */
        if (is_empty) {
            nbytes = descr->elsize;
        }
        /*
         * It is bad to have uninitialized OBJECT pointers
         * which could also be sub-fields of a VOID array
         */
        if (zeroed || PyDataType_FLAGCHK(descr, NPY_NEEDS_INIT)) {
            data = npy_alloc_cache_zero(nbytes);
        }
        else {
            data = npy_alloc_cache(nbytes);
        }
        if (data == NULL) {
            PyErr_NoMemory();
            goto fail;
        }
        fa->flags |= NPY_ARRAY_OWNDATA;

    }
    else {
        /*
         * If data is passed in, this object won't own it by default.
         * Caller must arrange for this to be reset if truly desired
         */
        fa->flags &= ~NPY_ARRAY_OWNDATA;
    }
    fa->data = data;

    /*
     * always update the flags to get the right CONTIGUOUS, ALIGN properties
     * not owned data and input strides may not be aligned and on some
     * platforms (debian sparc) malloc does not provide enough alignment for
     * long double types
     */
    PyArray_UpdateFlags((PyArrayObject *)fa, NPY_ARRAY_UPDATE_ALL);

    /*
     * call the __array_finalize__
     * method if a subtype.
     * If obj is NULL, then call method with Py_None
     */
    if ((subtype != &PyArray_Type)) {
        PyObject *res, *func, *args;

        func = PyObject_GetAttr((PyObject *)fa, npy_ma_str_array_finalize);
        if (func && func != Py_None) {
            if (NpyCapsule_Check(func)) {
                /* A C-function is stored here */
                PyArray_FinalizeFunc *cfunc;
                cfunc = NpyCapsule_AsVoidPtr(func);
                Py_DECREF(func);
                if (cfunc((PyArrayObject *)fa, obj) < 0) {
                    goto fail;
                }
            }
            else {
                args = PyTuple_New(1);
                if (obj == NULL) {
                    obj=Py_None;
                }
                Py_INCREF(obj);
                PyTuple_SET_ITEM(args, 0, obj);
                res = PyObject_Call(func, args, NULL);
                Py_DECREF(args);
                Py_DECREF(func);
                if (res == NULL) {
                    goto fail;
                }
                else {
                    Py_DECREF(res);
                }
            }
        }
        else Py_XDECREF(func);
    }
    return (PyObject *)fa;

 fail:
    Py_DECREF(fa);
    return NULL;
}


/*NUMPY_API
 * Generic new array creation routine.
 *
 * steals a reference to descr. On failure or when dtype->subarray is
 * true, dtype will be decrefed.
 */
NPY_NO_EXPORT PyObject *
PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
                     npy_intp *dims, npy_intp *strides, void *data,
                     int flags, PyObject *obj)
{
    return PyArray_NewFromDescr_int(subtype, descr, nd,
                                    dims, strides, data,
                                    flags, obj, 0, 0);
}

/*NUMPY_API
 * Creates a new array with the same shape as the provided one,
 * with possible memory layout order and data type changes.
 *
 * prototype - The array the new one should be like.
 * order     - NPY_CORDER - C-contiguous result.
 *             NPY_FORTRANORDER - Fortran-contiguous result.
 *             NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise.
 *             NPY_KEEPORDER - Keeps the axis ordering of prototype.
 * dtype     - If not NULL, overrides the data type of the result.
 * subok     - If 1, use the prototype's array subtype, otherwise
 *             always create a base-class array.
 *
 * NOTE: If dtype is not NULL, steals the dtype reference.  On failure or when
 * dtype->subarray is true, dtype will be decrefed.
 */
NPY_NO_EXPORT PyObject *
PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
                     PyArray_Descr *dtype, int subok)
{
    PyObject *ret = NULL;
    int ndim = PyArray_NDIM(prototype);

    /* If no override data type, use the one from the prototype */
    if (dtype == NULL) {
        dtype = PyArray_DESCR(prototype);
        Py_INCREF(dtype);
    }

    /* Handle ANYORDER and simple KEEPORDER cases */
    switch (order) {
        case NPY_ANYORDER:
            order = PyArray_ISFORTRAN(prototype) ?
                                    NPY_FORTRANORDER : NPY_CORDER;
            break;
        case NPY_KEEPORDER:
            if (PyArray_IS_C_CONTIGUOUS(prototype) || ndim <= 1) {
                order = NPY_CORDER;
                break;
            }
            else if (PyArray_IS_F_CONTIGUOUS(prototype)) {
                order = NPY_FORTRANORDER;
                break;
            }
            break;
        default:
            break;
    }

    /* If it's not KEEPORDER, this is simple */
    if (order != NPY_KEEPORDER) {
        ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
                                        dtype,
                                        ndim,
                                        PyArray_DIMS(prototype),
                                        NULL,
                                        NULL,
                                        order,
                                        subok ? (PyObject *)prototype : NULL);
    }
    /* KEEPORDER needs some analysis of the strides */
    else {
        npy_intp strides[NPY_MAXDIMS], stride;
        npy_intp *shape = PyArray_DIMS(prototype);
        npy_stride_sort_item strideperm[NPY_MAXDIMS];
        int idim;

        PyArray_CreateSortedStridePerm(PyArray_NDIM(prototype),
                                        PyArray_STRIDES(prototype),
                                        strideperm);

        /* Build the new strides */
        stride = dtype->elsize;
        for (idim = ndim-1; idim >= 0; --idim) {
            npy_intp i_perm = strideperm[idim].perm;
            strides[i_perm] = stride;
            stride *= shape[i_perm];
        }

        /* Finally, allocate the array */
        ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type,
                                        dtype,
                                        ndim,
                                        shape,
                                        strides,
                                        NULL,
                                        0,
                                        subok ? (PyObject *)prototype : NULL);
    }

    return ret;
}

/*NUMPY_API
 * Generic new array creation routine.
 */
NPY_NO_EXPORT PyObject *
PyArray_New(PyTypeObject *subtype, int nd, npy_intp *dims, int type_num,
            npy_intp *strides, void *data, int itemsize, int flags,
            PyObject *obj)
{
    PyArray_Descr *descr;
    PyObject *new;

    descr = PyArray_DescrFromType(type_num);
    if (descr == NULL) {
        return NULL;
    }
    if (PyDataType_ISUNSIZED(descr)) {
        if (itemsize < 1) {
            PyErr_SetString(PyExc_ValueError,
                            "data type must provide an itemsize");
            Py_DECREF(descr);
            return NULL;
        }
        PyArray_DESCR_REPLACE(descr);
        descr->elsize = itemsize;
    }
    new = PyArray_NewFromDescr(subtype, descr, nd, dims, strides,
                               data, flags, obj);
    return new;
}


NPY_NO_EXPORT int
_array_from_buffer_3118(PyObject *obj, PyObject **out)
{
    /* PEP 3118 */
    PyObject *memoryview;
    Py_buffer *view;
    PyArray_Descr *descr = NULL;
    PyObject *r;
    int nd, flags, k;
    Py_ssize_t d;
    npy_intp shape[NPY_MAXDIMS], strides[NPY_MAXDIMS];

    memoryview = PyMemoryView_FromObject(obj);
    if (memoryview == NULL) {
        PyErr_Clear();
        return -1;
    }

    view = PyMemoryView_GET_BUFFER(memoryview);
    if (view->format != NULL) {
        descr = _descriptor_from_pep3118_format(view->format);
        if (descr == NULL) {
            PyObject *msg;
            msg = PyBytes_FromFormat("Invalid PEP 3118 format string: '%s'",
                                     view->format);
            PyErr_WarnEx(PyExc_RuntimeWarning, PyBytes_AS_STRING(msg), 0);
            Py_DECREF(msg);
            goto fail;
        }

        /* Sanity check */
        if (descr->elsize != view->itemsize) {
            PyErr_WarnEx(PyExc_RuntimeWarning,
                         "Item size computed from the PEP 3118 buffer format "
                         "string does not match the actual item size.",
                         0);
            goto fail;
        }
    }
    else {
        descr = PyArray_DescrNewFromType(NPY_STRING);
        descr->elsize = view->itemsize;
    }

    nd = view->ndim;
    if (view->shape != NULL) {
        if (nd >= NPY_MAXDIMS || nd < 0) {
            goto fail;
        }
        for (k = 0; k < nd; ++k) {
            if (k >= NPY_MAXDIMS) {
                goto fail;
            }
            shape[k] = view->shape[k];
        }
        if (view->strides != NULL) {
            for (k = 0; k < nd; ++k) {
                strides[k] = view->strides[k];
            }
        }
        else {
            d = view->len;
            for (k = 0; k < nd; ++k) {
                if (view->shape[k] != 0) {
                    d /= view->shape[k];
                }
                strides[k] = d;
            }
        }
    }
    else {
        if (nd == 1) {
            shape[0] = view->len / view->itemsize;
            strides[0] = view->itemsize;
        }
        else if (nd > 1) {
            PyErr_WarnEx(PyExc_RuntimeWarning,
                         "ndim computed from the PEP 3118 buffer format "
                         "is greater than 1, but shape is NULL.",
                         0);
            goto fail;
        }
    }

    flags = NPY_ARRAY_BEHAVED & (view->readonly ? ~NPY_ARRAY_WRITEABLE : ~0);
    r = PyArray_NewFromDescr(&PyArray_Type, descr,
                             nd, shape, strides, view->buf,
                             flags, NULL);
    if (r == NULL ||
            PyArray_SetBaseObject((PyArrayObject *)r, memoryview) < 0) {
        Py_XDECREF(r);
        Py_DECREF(memoryview);
        return -1;
    }
    PyArray_UpdateFlags((PyArrayObject *)r, NPY_ARRAY_UPDATE_ALL);

    *out = r;
    return 0;

fail:
    Py_XDECREF(descr);
    Py_DECREF(memoryview);
    return -1;

}

/*NUMPY_API
 * Retrieves the array parameters for viewing/converting an arbitrary
 * PyObject* to a NumPy array. This allows the "innate type and shape"
 * of Python list-of-lists to be discovered without
 * actually converting to an array.
 *
 * In some cases, such as structured arrays and the __array__ interface,
 * a data type needs to be used to make sense of the object.  When
 * this is needed, provide a Descr for 'requested_dtype', otherwise
 * provide NULL. This reference is not stolen. Also, if the requested
 * dtype doesn't modify the interpretation of the input, out_dtype will
 * still get the "innate" dtype of the object, not the dtype passed
 * in 'requested_dtype'.
 *
 * If writing to the value in 'op' is desired, set the boolean
 * 'writeable' to 1.  This raises an error when 'op' is a scalar, list
 * of lists, or other non-writeable 'op'.
 *
 * Result: When success (0 return value) is returned, either out_arr
 *         is filled with a non-NULL PyArrayObject and
 *         the rest of the parameters are untouched, or out_arr is
 *         filled with NULL, and the rest of the parameters are
 *         filled.
 *
 * Typical usage:
 *
 *      PyArrayObject *arr = NULL;
 *      PyArray_Descr *dtype = NULL;
 *      int ndim = 0;
 *      npy_intp dims[NPY_MAXDIMS];
 *
 *      if (PyArray_GetArrayParamsFromObject(op, NULL, 1, &dtype,
 *                                          &ndim, dims, &arr, NULL) < 0) {
 *          return NULL;
 *      }
 *      if (arr == NULL) {
 *          ... validate/change dtype, validate flags, ndim, etc ...
 *          // Could make custom strides here too
 *          arr = PyArray_NewFromDescr(&PyArray_Type, dtype, ndim,
 *                                      dims, NULL,
 *                                      is_f_order ? NPY_ARRAY_F_CONTIGUOUS : 0,
 *                                      NULL);
 *          if (arr == NULL) {
 *              return NULL;
 *          }
 *          if (PyArray_CopyObject(arr, op) < 0) {
 *              Py_DECREF(arr);
 *              return NULL;
 *          }
 *      }
 *      else {
 *          ... in this case the other parameters weren't filled, just
 *              validate and possibly copy arr itself ...
 *      }
 *      ... use arr ...
 */
NPY_NO_EXPORT int
PyArray_GetArrayParamsFromObject(PyObject *op,
                        PyArray_Descr *requested_dtype,
                        npy_bool writeable,
                        PyArray_Descr **out_dtype,
                        int *out_ndim, npy_intp *out_dims,
                        PyArrayObject **out_arr, PyObject *context)
{
    PyObject *tmp;

    /* If op is an array */
    if (PyArray_Check(op)) {
        if (writeable
            && PyArray_FailUnlessWriteable((PyArrayObject *)op, "array") < 0) {
            return -1;
        }
        Py_INCREF(op);
        *out_arr = (PyArrayObject *)op;
        return 0;
    }

    /* If op is a NumPy scalar */
    if (PyArray_IsScalar(op, Generic)) {
        if (writeable) {
            PyErr_SetString(PyExc_RuntimeError,
                                "cannot write to scalar");
            return -1;
        }
        *out_dtype = PyArray_DescrFromScalar(op);
        if (*out_dtype == NULL) {
            return -1;
        }
        *out_ndim = 0;
        *out_arr = NULL;
        return 0;
    }

    /* If op is a Python scalar */
    *out_dtype = _array_find_python_scalar_type(op);
    if (*out_dtype != NULL) {
        if (writeable) {
            PyErr_SetString(PyExc_RuntimeError,
                                "cannot write to scalar");
            Py_DECREF(*out_dtype);
            return -1;
        }
        *out_ndim = 0;
        *out_arr = NULL;
        return 0;
    }

    /* If op supports the PEP 3118 buffer interface */
    if (!PyBytes_Check(op) && !PyUnicode_Check(op) &&
             _array_from_buffer_3118(op, (PyObject **)out_arr) == 0) {
        if (writeable
            && PyArray_FailUnlessWriteable(*out_arr, "PEP 3118 buffer") < 0) {
            Py_DECREF(*out_arr);
            return -1;
        }
        return (*out_arr) == NULL ? -1 : 0;
    }

    /* If op supports the __array_struct__ or __array_interface__ interface */
    tmp = PyArray_FromStructInterface(op);
    if (tmp == NULL) {
        return -1;
    }
    if (tmp == Py_NotImplemented) {
        tmp = PyArray_FromInterface(op);
        if (tmp == NULL) {
            return -1;
        }
    }
    if (tmp != Py_NotImplemented) {
        if (writeable
            && PyArray_FailUnlessWriteable((PyArrayObject *)tmp,
                                           "array interface object") < 0) {
            Py_DECREF(tmp);
            return -1;
        }
        *out_arr = (PyArrayObject *)tmp;
        return (*out_arr) == NULL ? -1 : 0;
    }

    /*
     * If op supplies the __array__ function.
     * The documentation says this should produce a copy, so
     * we skip this method if writeable is true, because the intent
     * of writeable is to modify the operand.
     * XXX: If the implementation is wrong, and/or if actual
     *      usage requires this behave differently,
     *      this should be changed!
     */
    if (!writeable) {
        tmp = PyArray_FromArrayAttr(op, requested_dtype, context);
        if (tmp != Py_NotImplemented) {
            *out_arr = (PyArrayObject *)tmp;
            return (*out_arr) == NULL ? -1 : 0;
        }
    }

    /* Try to treat op as a list of lists */
    if (!writeable && PySequence_Check(op)) {
        int check_it, stop_at_string, stop_at_tuple, is_object;
        int type_num, type;

        /*
         * Determine the type, using the requested data type if
         * it will affect how the array is retrieved
         */
        if (requested_dtype != NULL && (
                requested_dtype->type_num == NPY_STRING ||
                requested_dtype->type_num == NPY_UNICODE ||
                (requested_dtype->type_num == NPY_VOID &&
                    (requested_dtype->names || requested_dtype->subarray)) ||
                requested_dtype->type == NPY_CHARLTR ||
                requested_dtype->type_num == NPY_OBJECT)) {
            Py_INCREF(requested_dtype);
            *out_dtype = requested_dtype;
        }
        else {
            *out_dtype = NULL;
            if (PyArray_DTypeFromObject(op, NPY_MAXDIMS, out_dtype) < 0) {
                if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
                    return -1;
                }
                /* Return NPY_OBJECT for most exceptions */
                else {
                    PyErr_Clear();
                    *out_dtype = PyArray_DescrFromType(NPY_OBJECT);
                    if (*out_dtype == NULL) {
                        return -1;
                    }
                }
            }
            if (*out_dtype == NULL) {
                *out_dtype = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
                if (*out_dtype == NULL) {
                    return -1;
                }
            }
        }

        type_num = (*out_dtype)->type_num;
        type = (*out_dtype)->type;

        check_it = (type != NPY_CHARLTR);
        stop_at_string = (type_num != NPY_STRING) ||
                         (type == NPY_STRINGLTR);
        stop_at_tuple = (type_num == NPY_VOID &&
                         ((*out_dtype)->names || (*out_dtype)->subarray));

        *out_ndim = NPY_MAXDIMS;
        is_object = 0;
        if (discover_dimensions(op, out_ndim, out_dims, check_it,
                                    stop_at_string, stop_at_tuple,
                                    &is_object) < 0) {
            Py_DECREF(*out_dtype);
            if (PyErr_Occurred()) {
                return -1;
            }
            *out_dtype = PyArray_DescrFromType(NPY_OBJECT);
            if (*out_dtype == NULL) {
                return -1;
            }
            *out_ndim = 0;
            *out_arr = NULL;
            return 0;
        }
        /* If object arrays are forced */
        if (is_object) {
            Py_DECREF(*out_dtype);
            *out_dtype = PyArray_DescrFromType(NPY_OBJECT);
            if (*out_dtype == NULL) {
                return -1;
            }
        }

        if ((*out_dtype)->type == NPY_CHARLTR && (*out_ndim) > 0 &&
                                        out_dims[(*out_ndim) - 1] == 1) {
            (*out_ndim) -= 1;
        }

        /* If the type is flexible, determine its size */
        if (PyDataType_ISUNSIZED(*out_dtype) &&
                            PyTypeNum_ISEXTENDED((*out_dtype)->type_num)) {
            int itemsize = 0;
            int string_type = 0;
            if ((*out_dtype)->type_num == NPY_STRING ||
                    (*out_dtype)->type_num == NPY_UNICODE) {
                string_type = (*out_dtype)->type_num;
            }
            if (discover_itemsize(op, *out_ndim, &itemsize, string_type) < 0) {
                Py_DECREF(*out_dtype);
                if (PyErr_Occurred() &&
                        PyErr_GivenExceptionMatches(PyErr_Occurred(),
                                                PyExc_MemoryError)) {
                    return -1;
                }
                /* Say it's an OBJECT scalar if there's an error */
                PyErr_Clear();
                *out_dtype = PyArray_DescrFromType(NPY_OBJECT);
                *out_ndim = 0;
                *out_arr = NULL;
                return 0;
            }
            if ((*out_dtype)->type_num == NPY_UNICODE) {
                itemsize *= 4;
            }

            if (itemsize != (*out_dtype)->elsize) {
                PyArray_DESCR_REPLACE(*out_dtype);
                (*out_dtype)->elsize = itemsize;
            }
        }

        *out_arr = NULL;
        return 0;
    }

    /* Anything can be viewed as an object, unless it needs to be writeable */
    if (!writeable) {
        *out_dtype = PyArray_DescrFromType(NPY_OBJECT);
        if (*out_dtype == NULL) {
            return -1;
        }
        *out_ndim = 0;
        *out_arr = NULL;
        return 0;
    }

    PyErr_SetString(PyExc_RuntimeError,
                    "object cannot be viewed as a writeable numpy array");
    return -1;
}

/*NUMPY_API
 * Does not check for NPY_ARRAY_ENSURECOPY and NPY_ARRAY_NOTSWAPPED in flags
 * Steals a reference to newtype --- which can be NULL
 */
NPY_NO_EXPORT PyObject *
PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
                int max_depth, int flags, PyObject *context)
{
    /*
     * This is the main code to make a NumPy array from a Python
     * Object.  It is called from many different places.
     */
    PyArrayObject *arr = NULL, *ret;
    PyArray_Descr *dtype = NULL;
    int ndim = 0;
    npy_intp dims[NPY_MAXDIMS];

    /* Get either the array or its parameters if it isn't an array */
    if (PyArray_GetArrayParamsFromObject(op, newtype,
                        0, &dtype,
                        &ndim, dims, &arr, context) < 0) {
        Py_XDECREF(newtype);
        return NULL;
    }

    /* If the requested dtype is flexible, adapt it */
    if (newtype != NULL) {
        PyArray_AdaptFlexibleDType(op,
                    (dtype == NULL) ? PyArray_DESCR(arr) : dtype,
                    &newtype);
    }

    /* If we got dimensions and dtype instead of an array */
    if (arr == NULL) {
        if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) ||
            (flags & NPY_ARRAY_UPDATEIFCOPY)) {
            Py_XDECREF(newtype);
            PyErr_SetString(PyExc_TypeError,
                            "WRITEBACKIFCOPY used for non-array input.");
            return NULL;
        }
        else if (min_depth != 0 && ndim < min_depth) {
            Py_DECREF(dtype);
            Py_XDECREF(newtype);
            PyErr_SetString(PyExc_ValueError,
                            "object of too small depth for desired array");
            ret = NULL;
        }
        else if (max_depth != 0 && ndim > max_depth) {
            Py_DECREF(dtype);
            Py_XDECREF(newtype);
            PyErr_SetString(PyExc_ValueError,
                            "object too deep for desired array");
            ret = NULL;
        }
        else if (ndim == 0 && PyArray_IsScalar(op, Generic)) {
            ret = (PyArrayObject *)PyArray_FromScalar(op, newtype);
            Py_DECREF(dtype);
        }
        else {
            if (newtype == NULL) {
                newtype = dtype;
            }
            else {
                /*
                 * TODO: would be nice to do this too, but it's
                 *       a behavior change.  It's also a bit tricky
                 *       for downcasting to small integer and float
                 *       types, and might be better to modify
                 *       PyArray_AssignFromSequence and descr->f->setitem
                 *       to have a 'casting' parameter and
                 *       to check each value with scalar rules like
                 *       in PyArray_MinScalarType.
                 */
                /*
                if (!(flags&NPY_ARRAY_FORCECAST) && ndim > 0 &&
                        !PyArray_CanCastTo(dtype, newtype)) {
                    Py_DECREF(dtype);
                    Py_XDECREF(newtype);
                    PyErr_SetString(PyExc_TypeError,
                                "object cannot be safely cast to array "
                                "of required type");
                    return NULL;
                }
                */
                Py_DECREF(dtype);
            }

            /* Create an array and copy the data */
            ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, newtype,
                                         ndim, dims,
                                         NULL, NULL,
                                         flags&NPY_ARRAY_F_CONTIGUOUS, NULL);
            if (ret == NULL) {
                return NULL;
            }

            if (ndim > 0) {
                if (PyArray_AssignFromSequence(ret, op) < 0) {
                    Py_DECREF(ret);
                    ret = NULL;
                }
            }
            else {
                if (PyArray_SETITEM(ret, PyArray_DATA(ret), op) < 0) {
                    Py_DECREF(ret);
                    ret = NULL;
                }
            }
        }
    }
    else {
        if (min_depth != 0 && PyArray_NDIM(arr) < min_depth) {
            PyErr_SetString(PyExc_ValueError,
                            "object of too small depth for desired array");
            Py_DECREF(arr);
            ret = NULL;
        }
        else if (max_depth != 0 && PyArray_NDIM(arr) > max_depth) {
            PyErr_SetString(PyExc_ValueError,
                            "object too deep for desired array");
            Py_DECREF(arr);
            ret = NULL;
        }
        else {
            ret = (PyArrayObject *)PyArray_FromArray(arr, newtype, flags);
            Py_DECREF(arr);
        }
    }

    return (PyObject *)ret;
}

/*
 * flags is any of
 * NPY_ARRAY_C_CONTIGUOUS (formerly CONTIGUOUS),
 * NPY_ARRAY_F_CONTIGUOUS (formerly FORTRAN),
 * NPY_ARRAY_ALIGNED,
 * NPY_ARRAY_WRITEABLE,
 * NPY_ARRAY_NOTSWAPPED,
 * NPY_ARRAY_ENSURECOPY,
 * NPY_ARRAY_UPDATEIFCOPY,
 * NPY_ARRAY_WRITEBACKIFCOPY,
 * NPY_ARRAY_FORCECAST,
 * NPY_ARRAY_ENSUREARRAY,
 * NPY_ARRAY_ELEMENTSTRIDES
 *
 * or'd (|) together
 *
 * Any of these flags present means that the returned array should
 * guarantee that aspect of the array.  Otherwise the returned array
 * won't guarantee it -- it will depend on the object as to whether or
 * not it has such features.
 *
 * Note that NPY_ARRAY_ENSURECOPY is enough
 * to guarantee NPY_ARRAY_C_CONTIGUOUS, NPY_ARRAY_ALIGNED and
 * NPY_ARRAY_WRITEABLE and therefore it is redundant to include
 * those as well.
 *
 * NPY_ARRAY_BEHAVED == NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE
 * NPY_ARRAY_CARRAY = NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_BEHAVED
 * NPY_ARRAY_FARRAY = NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_BEHAVED
 *
 * NPY_ARRAY_F_CONTIGUOUS can be set in the FLAGS to request a FORTRAN array.
 * Fortran arrays are always behaved (aligned,
 * notswapped, and writeable) and not (C) CONTIGUOUS (if > 1d).
 *
 * NPY_ARRAY_UPDATEIFCOPY is deprecated in favor of
 * NPY_ARRAY_WRITEBACKIFCOPY in 1.14

 * NPY_ARRAY_WRITEBACKIFCOPY flag sets this flag in the returned
 * array if a copy is made and the base argument points to the (possibly)
 * misbehaved array. Before returning to python, PyArray_ResolveWritebackIfCopy
 * must be called to update the contents of the orignal array from the copy.
 *
 * NPY_ARRAY_FORCECAST will cause a cast to occur regardless of whether or not
 * it is safe.
 */

/*NUMPY_API
 * steals a reference to descr -- accepts NULL
 */
NPY_NO_EXPORT PyObject *
PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth,
                     int max_depth, int requires, PyObject *context)
{
    PyObject *obj;
    if (requires & NPY_ARRAY_NOTSWAPPED) {
        if (!descr && PyArray_Check(op) &&
                PyArray_ISBYTESWAPPED((PyArrayObject* )op)) {
            descr = PyArray_DescrNew(PyArray_DESCR((PyArrayObject *)op));
        }
        else if (descr && !PyArray_ISNBO(descr->byteorder)) {
            PyArray_DESCR_REPLACE(descr);
        }
        if (descr && descr->byteorder != NPY_IGNORE) {
            descr->byteorder = NPY_NATIVE;
        }
    }

    obj = PyArray_FromAny(op, descr, min_depth, max_depth, requires, context);
    if (obj == NULL) {
        return NULL;
    }
    if ((requires & NPY_ARRAY_ELEMENTSTRIDES) &&
        !PyArray_ElementStrides(obj)) {
        PyObject *ret;
        ret = PyArray_NewCopy((PyArrayObject *)obj, NPY_ANYORDER);
        Py_DECREF(obj);
        obj = ret;
    }
    return obj;
}

/*NUMPY_API
 * steals reference to newtype --- acc. NULL
 */
NPY_NO_EXPORT PyObject *
PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
{

    PyArrayObject *ret = NULL;
    int copy = 0;
    int arrflags;
    PyArray_Descr *oldtype;
    NPY_CASTING casting = NPY_SAFE_CASTING;

    oldtype = PyArray_DESCR(arr);
    if (newtype == NULL) {
        /*
         * Check if object is of array with Null newtype.
         * If so return it directly instead of checking for casting.
         */
        if (flags == 0) {
            Py_INCREF(arr);
            return (PyObject *)arr;
        }
        newtype = oldtype;
        Py_INCREF(oldtype);
    }
    if (PyDataType_ISUNSIZED(newtype)) {
        PyArray_DESCR_REPLACE(newtype);
        if (newtype == NULL) {
            return NULL;
        }
        newtype->elsize = oldtype->elsize;
    }

    /* If the casting if forced, use the 'unsafe' casting rule */
    if (flags & NPY_ARRAY_FORCECAST) {
        casting = NPY_UNSAFE_CASTING;
    }

    /* Raise an error if the casting rule isn't followed */
    if (!PyArray_CanCastArrayTo(arr, newtype, casting)) {
        PyObject *errmsg;
        PyArray_Descr *arr_descr = NULL;
        PyObject *arr_descr_repr = NULL;
        PyObject *newtype_repr = NULL;

        PyErr_Clear();
        errmsg = PyUString_FromString("Cannot cast array data from ");
        arr_descr = PyArray_DESCR(arr);
        if (arr_descr == NULL) {
            Py_DECREF(newtype);
            Py_DECREF(errmsg);
            return NULL;
        }
        arr_descr_repr = PyObject_Repr((PyObject *)arr_descr);
        if (arr_descr_repr == NULL) {
            Py_DECREF(newtype);
            Py_DECREF(errmsg);
            return NULL;
        }
        PyUString_ConcatAndDel(&errmsg, arr_descr_repr);
        PyUString_ConcatAndDel(&errmsg,
                PyUString_FromString(" to "));
        newtype_repr = PyObject_Repr((PyObject *)newtype);
        if (newtype_repr == NULL) {
            Py_DECREF(newtype);
            Py_DECREF(errmsg);
            return NULL;
        }
        PyUString_ConcatAndDel(&errmsg, newtype_repr);
        PyUString_ConcatAndDel(&errmsg,
                PyUString_FromFormat(" according to the rule %s",
                        npy_casting_to_string(casting)));
        PyErr_SetObject(PyExc_TypeError, errmsg);
        Py_DECREF(errmsg);

        Py_DECREF(newtype);
        return NULL;
    }

    arrflags = PyArray_FLAGS(arr);
           /* If a guaranteed copy was requested */
    copy = (flags & NPY_ARRAY_ENSURECOPY) ||
           /* If C contiguous was requested, and arr is not */
           ((flags & NPY_ARRAY_C_CONTIGUOUS) &&
                   (!(arrflags & NPY_ARRAY_C_CONTIGUOUS))) ||
           /* If an aligned array was requested, and arr is not */
           ((flags & NPY_ARRAY_ALIGNED) &&
                   (!(arrflags & NPY_ARRAY_ALIGNED))) ||
           /* If a Fortran contiguous array was requested, and arr is not */
           ((flags & NPY_ARRAY_F_CONTIGUOUS) &&
                   (!(arrflags & NPY_ARRAY_F_CONTIGUOUS))) ||
           /* If a writeable array was requested, and arr is not */
           ((flags & NPY_ARRAY_WRITEABLE) &&
                   (!(arrflags & NPY_ARRAY_WRITEABLE))) ||
           !PyArray_EquivTypes(oldtype, newtype);

    if (copy) {
        NPY_ORDER order = NPY_KEEPORDER;
        int subok = 1;

        /* Set the order for the copy being made based on the flags */
        if (flags & NPY_ARRAY_F_CONTIGUOUS) {
            order = NPY_FORTRANORDER;
        }
        else if (flags & NPY_ARRAY_C_CONTIGUOUS) {
            order = NPY_CORDER;
        }

        if ((flags & NPY_ARRAY_ENSUREARRAY)) {
            subok = 0;
        }
        ret = (PyArrayObject *)PyArray_NewLikeArray(arr, order,
                                                    newtype, subok);
        if (ret == NULL) {
            return NULL;
        }

        if (PyArray_CopyInto(ret, arr) < 0) {
            Py_DECREF(ret);
            return NULL;
        }

        if (flags & NPY_ARRAY_UPDATEIFCOPY) {
            /* This is the ONLY place the NPY_ARRAY_UPDATEIFCOPY flag
             * is still used.
             * Can be deleted once the flag itself is removed
             */

            /* 2017-Nov-10 1.14 */
            if (DEPRECATE("NPY_ARRAY_UPDATEIFCOPY, NPY_ARRAY_INOUT_ARRAY, and "
                "NPY_ARRAY_INOUT_FARRAY are deprecated, use NPY_WRITEBACKIFCOPY, "
                "NPY_ARRAY_INOUT_ARRAY2, or NPY_ARRAY_INOUT_FARRAY2 respectively "
                "instead, and call PyArray_ResolveWritebackIfCopy before the "
                "array is deallocated, i.e. before the last call to Py_DECREF.") < 0)
                return NULL;
            Py_INCREF(arr);
            if (PyArray_SetWritebackIfCopyBase(ret, arr) < 0) {
                Py_DECREF(ret);
                return NULL;
            }
            PyArray_ENABLEFLAGS(ret, NPY_ARRAY_UPDATEIFCOPY);
            PyArray_CLEARFLAGS(ret, NPY_ARRAY_WRITEBACKIFCOPY);
        }
        else if (flags & NPY_ARRAY_WRITEBACKIFCOPY) {
            Py_INCREF(arr);
            if (PyArray_SetWritebackIfCopyBase(ret, arr) < 0) {
                Py_DECREF(ret);
                return NULL;
            }
        }
    }
    /*
     * If no copy then take an appropriate view if necessary, or
     * just return a reference to ret itself.
     */
    else {
        int needview = ((flags & NPY_ARRAY_ENSUREARRAY) &&
                        !PyArray_CheckExact(arr));

        Py_DECREF(newtype);
        if (needview) {
            PyArray_Descr *dtype = PyArray_DESCR(arr);
            PyTypeObject *subtype = NULL;

            if (flags & NPY_ARRAY_ENSUREARRAY) {
                subtype = &PyArray_Type;
            }

            Py_INCREF(dtype);
            ret = (PyArrayObject *)PyArray_View(arr, NULL, subtype);
            if (ret == NULL) {
                return NULL;
            }
        }
        else {
            Py_INCREF(arr);
            ret = arr;
        }
    }

    return (PyObject *)ret;
}

/*NUMPY_API */
NPY_NO_EXPORT PyObject *
PyArray_FromStructInterface(PyObject *input)
{
    PyArray_Descr *thetype = NULL;
    char buf[40];
    PyArrayInterface *inter;
    PyObject *attr;
    PyArrayObject *ret;
    char endian = NPY_NATBYTE;

    attr = PyArray_LookupSpecial_OnInstance(input, "__array_struct__");
    if (attr == NULL) {
        return Py_NotImplemented;
    }
    if (!NpyCapsule_Check(attr)) {
        goto fail;
    }
    inter = NpyCapsule_AsVoidPtr(attr);
    if (inter->two != 2) {
        goto fail;
    }
    if ((inter->flags & NPY_ARRAY_NOTSWAPPED) != NPY_ARRAY_NOTSWAPPED) {
        endian = NPY_OPPBYTE;
        inter->flags &= ~NPY_ARRAY_NOTSWAPPED;
    }

    if (inter->flags & NPY_ARR_HAS_DESCR) {
        if (PyArray_DescrConverter(inter->descr, &thetype) == NPY_FAIL) {
            thetype = NULL;
            PyErr_Clear();
        }
    }

    if (thetype == NULL) {
        PyOS_snprintf(buf, sizeof(buf),
                "%c%c%d", endian, inter->typekind, inter->itemsize);
        if (!(thetype=_array_typedescr_fromstr(buf))) {
            Py_DECREF(attr);
            return NULL;
        }
    }

    ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, thetype,
                             inter->nd, inter->shape,
                             inter->strides, inter->data,
                             inter->flags, NULL);
    Py_INCREF(input);
    if (PyArray_SetBaseObject(ret, input) < 0) {
        Py_DECREF(ret);
        return NULL;
    }
    Py_DECREF(attr);
    PyArray_UpdateFlags(ret, NPY_ARRAY_UPDATE_ALL);
    return (PyObject *)ret;

 fail:
    PyErr_SetString(PyExc_ValueError, "invalid __array_struct__");
    Py_DECREF(attr);
    return NULL;
}

/*
 * Checks if the object in descr is the default 'descr' member for the
 * __array_interface__ dictionary with 'typestr' member typestr.
 */
NPY_NO_EXPORT int
_is_default_descr(PyObject *descr, PyObject *typestr) {
    PyObject *tuple, *name, *typestr2;
#if defined(NPY_PY3K)
    PyObject *tmp = NULL;
#endif
    int ret = 0;

    if (!PyList_Check(descr) || PyList_GET_SIZE(descr) != 1) {
        return 0;
    }
    tuple = PyList_GET_ITEM(descr, 0);
    if (!(PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2)) {
        return 0;
    }
    name = PyTuple_GET_ITEM(tuple, 0);
    if (!(PyUString_Check(name) && PyUString_GET_SIZE(name) == 0)) {
        return 0;
    }
    typestr2 = PyTuple_GET_ITEM(tuple, 1);
#if defined(NPY_PY3K)
    /* Allow unicode type strings */
    if (PyUnicode_Check(typestr2)) {
        tmp = PyUnicode_AsASCIIString(typestr2);
        if (tmp == NULL) {
            return 0;
        }
        typestr2 = tmp;
    }
#endif
    if (PyBytes_Check(typestr2) &&
            PyObject_RichCompareBool(typestr, typestr2, Py_EQ)) {
        ret = 1;
    }
#if defined(NPY_PY3K)
    Py_XDECREF(tmp);
#endif

    return ret;
}

#define PyIntOrLong_Check(obj) (PyInt_Check(obj) || PyLong_Check(obj))

/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_FromInterface(PyObject *origin)
{
    PyObject *tmp = NULL;
    PyObject *iface = NULL;
    PyObject *attr = NULL;
    PyObject *base = NULL;
    PyArrayObject *ret;
    PyArray_Descr *dtype = NULL;
    char *data = NULL;
    Py_ssize_t buffer_len;
    int res, i, n;
    npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS];
    int dataflags = NPY_ARRAY_BEHAVED;

    iface = PyArray_LookupSpecial_OnInstance(origin,
                                                    "__array_interface__");
    if (iface == NULL) {
        return Py_NotImplemented;
    }
    if (!PyDict_Check(iface)) {
        Py_DECREF(iface);
        PyErr_SetString(PyExc_ValueError,
                "Invalid __array_interface__ value, must be a dict");
        return NULL;
    }

    /* Get type string from interface specification */
    attr = PyDict_GetItemString(iface, "typestr");
    if (attr == NULL) {
        Py_DECREF(iface);
        PyErr_SetString(PyExc_ValueError,
                "Missing __array_interface__ typestr");
        return NULL;
    }
#if defined(NPY_PY3K)
    /* Allow unicode type strings */
    if (PyUnicode_Check(attr)) {
        tmp = PyUnicode_AsASCIIString(attr);
        attr = tmp;
    }
#endif
    if (!PyBytes_Check(attr)) {
        PyErr_SetString(PyExc_TypeError,
                    "__array_interface__ typestr must be a string");
        goto fail;
    }
    /* Get dtype from type string */
    dtype = _array_typedescr_fromstr(PyString_AS_STRING(attr));
#if defined(NPY_PY3K)
    if (tmp == attr) {
        Py_DECREF(tmp);
    }
#endif
    if (dtype == NULL) {
        goto fail;
    }

    /*
     * If the dtype is NPY_VOID, see if there is extra information in
     * the 'descr' attribute.
     */
    if (dtype->type_num == NPY_VOID) {
        PyObject *descr = PyDict_GetItemString(iface, "descr");
        PyArray_Descr *new_dtype = NULL;

        if (descr != NULL && !_is_default_descr(descr, attr) &&
                PyArray_DescrConverter2(descr, &new_dtype) == NPY_SUCCEED &&
                new_dtype != NULL) {
            Py_DECREF(dtype);
            dtype = new_dtype;
        }
    }

    /* Get shape tuple from interface specification */
    attr = PyDict_GetItemString(iface, "shape");
    if (attr == NULL) {
        /* Shape must be specified when 'data' is specified */
        if (PyDict_GetItemString(iface, "data") != NULL) {
            Py_DECREF(iface);
            PyErr_SetString(PyExc_ValueError,
                    "Missing __array_interface__ shape");
            return NULL;
        }
        /* Assume shape as scalar otherwise */
        else {
            /* NOTE: pointers to data and base should be NULL */
            n = dims[0] = 0;
        }
    }
    /* Make sure 'shape' is a tuple */
    else if (!PyTuple_Check(attr)) {
        PyErr_SetString(PyExc_TypeError,
                "shape must be a tuple");
        goto fail;
    }
    /* Get dimensions from shape tuple */
    else {
        n = PyTuple_GET_SIZE(attr);
        for (i = 0; i < n; i++) {
            tmp = PyTuple_GET_ITEM(attr, i);
            dims[i] = PyArray_PyIntAsIntp(tmp);
            if (error_converting(dims[i])) {
                goto fail;
            }
        }
    }

    /* Get data buffer from interface specification */
    attr = PyDict_GetItemString(iface, "data");

    /* Case for data access through pointer */
    if (attr && PyTuple_Check(attr)) {
        PyObject *dataptr;
        if (PyTuple_GET_SIZE(attr) != 2) {
            PyErr_SetString(PyExc_TypeError,
                    "__array_interface__ data must be a 2-tuple with "
                    "(data pointer integer, read-only flag)");
            goto fail;
        }
        dataptr = PyTuple_GET_ITEM(attr, 0);
        if (PyString_Check(dataptr)) {
            res = sscanf(PyString_AsString(dataptr),
                         "%p", (void **)&data);
            if (res < 1) {
                PyErr_SetString(PyExc_TypeError,
                        "__array_interface__ data string cannot be converted");
                goto fail;
            }
        }
        else if (PyIntOrLong_Check(dataptr)) {
            data = PyLong_AsVoidPtr(dataptr);
        }
        else {
            PyErr_SetString(PyExc_TypeError,
                    "first element of __array_interface__ data tuple "
                    "must be integer or string.");
            goto fail;
        }
        if (PyObject_IsTrue(PyTuple_GET_ITEM(attr,1))) {
            dataflags &= ~NPY_ARRAY_WRITEABLE;
        }
        base = origin;
    }

    /* Case for data access through buffer */
    else if (attr) {
        if (attr != Py_None) {
            base = attr;
        }
        else {
            base = origin;
        }
        res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len);
        if (res < 0) {
            PyErr_Clear();
            res = PyObject_AsReadBuffer(
                        base, (const void **)&data, &buffer_len);
            if (res < 0) {
                goto fail;
            }
            dataflags &= ~NPY_ARRAY_WRITEABLE;
        }
        /* Get offset number from interface specification */
        attr = PyDict_GetItemString(origin, "offset");
        if (attr) {
            npy_longlong num = PyLong_AsLongLong(attr);
            if (error_converting(num)) {
                PyErr_SetString(PyExc_TypeError,
                        "__array_interface__ offset must be an integer");
                goto fail;
            }
            data += num;
        }
    }

    ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype,
                                                n, dims,
                                                NULL, data,
                                                dataflags, NULL);
    if (ret == NULL) {
        goto fail;
    }
    if (data == NULL) {
        if (PyArray_SIZE(ret) > 1) {
            PyErr_SetString(PyExc_ValueError,
                    "cannot coerce scalar to array with size > 1");
            Py_DECREF(ret);
            goto fail;
        }
        if (PyArray_SETITEM(ret, PyArray_DATA(ret), origin) < 0) {
            Py_DECREF(ret);
            goto fail;
        }
    }
    if (base) {
        Py_INCREF(base);
        if (PyArray_SetBaseObject(ret, base) < 0) {
            Py_DECREF(ret);
            goto fail;
        }
    }
    attr = PyDict_GetItemString(iface, "strides");
    if (attr != NULL && attr != Py_None) {
        if (!PyTuple_Check(attr)) {
            PyErr_SetString(PyExc_TypeError,
                    "strides must be a tuple");
            Py_DECREF(ret);
            goto fail;
        }
        if (n != PyTuple_GET_SIZE(attr)) {
            PyErr_SetString(PyExc_ValueError,
                    "mismatch in length of strides and shape");
            Py_DECREF(ret);
            goto fail;
        }
        for (i = 0; i < n; i++) {
            tmp = PyTuple_GET_ITEM(attr, i);
            strides[i] = PyArray_PyIntAsIntp(tmp);
            if (error_converting(strides[i])) {
                Py_DECREF(ret);
                goto fail;
            }
        }
        memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp));
    }
    PyArray_UpdateFlags(ret, NPY_ARRAY_UPDATE_ALL);
    Py_DECREF(iface);
    return (PyObject *)ret;

 fail:
    Py_XDECREF(dtype);
    Py_XDECREF(iface);
    return NULL;
}

/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
{
    PyObject *new;
    PyObject *array_meth;

    array_meth = PyArray_LookupSpecial_OnInstance(op, "__array__");
    if (array_meth == NULL) {
        return Py_NotImplemented;
    }
    if (context == NULL) {
        if (typecode == NULL) {
            new = PyObject_CallFunction(array_meth, NULL);
        }
        else {
            new = PyObject_CallFunction(array_meth, "O", typecode);
        }
    }
    else {
        if (typecode == NULL) {
            new = PyObject_CallFunction(array_meth, "OO", Py_None, context);
            if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
                PyErr_Clear();
                new = PyObject_CallFunction(array_meth, "");
            }
        }
        else {
            new = PyObject_CallFunction(array_meth, "OO", typecode, context);
            if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
                PyErr_Clear();
                new = PyObject_CallFunction(array_meth, "O", typecode);
            }
        }
    }
    Py_DECREF(array_meth);
    if (new == NULL) {
        return NULL;
    }
    if (!PyArray_Check(new)) {
        PyErr_SetString(PyExc_ValueError,
                        "object __array__ method not "  \
                        "producing an array");
        Py_DECREF(new);
        return NULL;
    }
    return new;
}

/*NUMPY_API
* new reference -- accepts NULL for mintype
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_DescrFromObject(PyObject *op, PyArray_Descr *mintype)
{
    PyArray_Descr *dtype;

    dtype = mintype;
    Py_XINCREF(dtype);

    if (PyArray_DTypeFromObject(op, NPY_MAXDIMS, &dtype) < 0) {
        return NULL;
    }

    if (dtype == NULL) {
        return PyArray_DescrFromType(NPY_DEFAULT_TYPE);
    }
    else {
        return dtype;
    }
}

/* These are also old calls (should use PyArray_NewFromDescr) */

/* They all zero-out the memory as previously done */

/* steals reference to descr -- and enforces native byteorder on it.*/
/*NUMPY_API
  Like FromDimsAndData but uses the Descr structure instead of typecode
  as input.
*/
NPY_NO_EXPORT PyObject *
PyArray_FromDimsAndDataAndDescr(int nd, int *d,
                                PyArray_Descr *descr,
                                char *data)
{
    PyObject *ret;
    int i;
    npy_intp newd[NPY_MAXDIMS];
    char msg[] = "PyArray_FromDimsAndDataAndDescr: use PyArray_NewFromDescr.";

    if (DEPRECATE(msg) < 0) {
        /* 2009-04-30, 1.5 */
        return NULL;
    }
    if (!PyArray_ISNBO(descr->byteorder))
        descr->byteorder = '=';
    for (i = 0; i < nd; i++) {
        newd[i] = (npy_intp) d[i];
    }
    ret = PyArray_NewFromDescr(&PyArray_Type, descr,
                               nd, newd,
                               NULL, data,
                               (data ? NPY_ARRAY_CARRAY : 0), NULL);
    return ret;
}

/*NUMPY_API
  Construct an empty array from dimensions and typenum
*/
NPY_NO_EXPORT PyObject *
PyArray_FromDims(int nd, int *d, int type)
{
    PyArrayObject *ret;
    char msg[] = "PyArray_FromDims: use PyArray_SimpleNew.";

    if (DEPRECATE(msg) < 0) {
        /* 2009-04-30, 1.5 */
        return NULL;
    }
    ret = (PyArrayObject *)PyArray_FromDimsAndDataAndDescr(nd, d,
                                          PyArray_DescrFromType(type),
                                          NULL);
    /*
     * Old FromDims set memory to zero --- some algorithms
     * relied on that.  Better keep it the same. If
     * Object type, then it's already been set to zero, though.
     */
    if (ret && (PyArray_DESCR(ret)->type_num != NPY_OBJECT)) {
        memset(PyArray_DATA(ret), 0, PyArray_NBYTES(ret));
    }
    return (PyObject *)ret;
}

/* end old calls */

/*NUMPY_API
 * This is a quick wrapper around
 * PyArray_FromAny(op, NULL, 0, 0, NPY_ARRAY_ENSUREARRAY, NULL)
 * that special cases Arrays and PyArray_Scalars up front
 * It *steals a reference* to the object
 * It also guarantees that the result is PyArray_Type
 * Because it decrefs op if any conversion needs to take place
 * so it can be used like PyArray_EnsureArray(some_function(...))
 */
NPY_NO_EXPORT PyObject *
PyArray_EnsureArray(PyObject *op)
{
    PyObject *new;

    if ((op == NULL) || (PyArray_CheckExact(op))) {
        new = op;
        Py_XINCREF(new);
    }
    else if (PyArray_Check(op)) {
        new = PyArray_View((PyArrayObject *)op, NULL, &PyArray_Type);
    }
    else if (PyArray_IsScalar(op, Generic)) {
        new = PyArray_FromScalar(op, NULL);
    }
    else {
        new = PyArray_FROM_OF(op, NPY_ARRAY_ENSUREARRAY);
    }
    Py_XDECREF(op);
    return new;
}

/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_EnsureAnyArray(PyObject *op)
{
    if (op && PyArray_Check(op)) {
        return op;
    }
    return PyArray_EnsureArray(op);
}

/* TODO: Put the order parameter in PyArray_CopyAnyInto and remove this */
NPY_NO_EXPORT int
PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order)
{
    PyArray_StridedUnaryOp *stransfer = NULL;
    NpyAuxData *transferdata = NULL;
    NpyIter *dst_iter, *src_iter;

    NpyIter_IterNextFunc *dst_iternext, *src_iternext;
    char **dst_dataptr, **src_dataptr;
    npy_intp dst_stride, src_stride;
    npy_intp *dst_countptr, *src_countptr;
    npy_uint32 baseflags;

    char *dst_data, *src_data;
    npy_intp dst_count, src_count, count;
    npy_intp src_itemsize;
    npy_intp dst_size, src_size;
    int needs_api;

    NPY_BEGIN_THREADS_DEF;

    if (PyArray_FailUnlessWriteable(dst, "destination array") < 0) {
        return -1;
    }

    /*
     * If the shapes match and a particular order is forced
     * for both, use the more efficient CopyInto
     */
    if (order != NPY_ANYORDER && order != NPY_KEEPORDER &&
            PyArray_NDIM(dst) == PyArray_NDIM(src) &&
            PyArray_CompareLists(PyArray_DIMS(dst), PyArray_DIMS(src),
                                PyArray_NDIM(dst))) {
        return PyArray_CopyInto(dst, src);
    }

    dst_size = PyArray_SIZE(dst);
    src_size = PyArray_SIZE(src);
    if (dst_size != src_size) {
        PyErr_Format(PyExc_ValueError,
                "cannot copy from array of size %d into an array "
                "of size %d", (int)src_size, (int)dst_size);
        return -1;
    }

    /* Zero-sized arrays require nothing be done */
    if (dst_size == 0) {
        return 0;
    }

    baseflags = NPY_ITER_EXTERNAL_LOOP |
                NPY_ITER_DONT_NEGATE_STRIDES |
                NPY_ITER_REFS_OK;

    /*
     * This copy is based on matching C-order traversals of src and dst.
     * By using two iterators, we can find maximal sub-chunks that
     * can be processed at once.
     */
    dst_iter = NpyIter_New(dst, NPY_ITER_WRITEONLY | baseflags,
                                order,
                                NPY_NO_CASTING,
                                NULL);
    if (dst_iter == NULL) {
        return -1;
    }
    src_iter = NpyIter_New(src, NPY_ITER_READONLY | baseflags,
                                order,
                                NPY_NO_CASTING,
                                NULL);
    if (src_iter == NULL) {
        NpyIter_Deallocate(dst_iter);
        return -1;
    }

    /* Get all the values needed for the inner loop */
    dst_iternext = NpyIter_GetIterNext(dst_iter, NULL);
    dst_dataptr = NpyIter_GetDataPtrArray(dst_iter);
    /* Since buffering is disabled, we can cache the stride */
    dst_stride = NpyIter_GetInnerStrideArray(dst_iter)[0];
    dst_countptr = NpyIter_GetInnerLoopSizePtr(dst_iter);

    src_iternext = NpyIter_GetIterNext(src_iter, NULL);
    src_dataptr = NpyIter_GetDataPtrArray(src_iter);
    /* Since buffering is disabled, we can cache the stride */
    src_stride = NpyIter_GetInnerStrideArray(src_iter)[0];
    src_countptr = NpyIter_GetInnerLoopSizePtr(src_iter);
    src_itemsize = PyArray_DESCR(src)->elsize;

    if (dst_iternext == NULL || src_iternext == NULL) {
        NpyIter_Deallocate(dst_iter);
        NpyIter_Deallocate(src_iter);
        return -1;
    }

    needs_api = NpyIter_IterationNeedsAPI(dst_iter) ||
                NpyIter_IterationNeedsAPI(src_iter);

    /*
     * Because buffering is disabled in the iterator, the inner loop
     * strides will be the same throughout the iteration loop.  Thus,
     * we can pass them to this function to take advantage of
     * contiguous strides, etc.
     */
    if (PyArray_GetDTypeTransferFunction(
                    PyArray_ISALIGNED(src) && PyArray_ISALIGNED(dst),
                    src_stride, dst_stride,
                    PyArray_DESCR(src), PyArray_DESCR(dst),
                    0,
                    &stransfer, &transferdata,
                    &needs_api) != NPY_SUCCEED) {
        NpyIter_Deallocate(dst_iter);
        NpyIter_Deallocate(src_iter);
        return -1;
    }

    if (!needs_api) {
        NPY_BEGIN_THREADS;
    }

    dst_count = *dst_countptr;
    src_count = *src_countptr;
    dst_data = dst_dataptr[0];
    src_data = src_dataptr[0];
    for(;;) {
        /* Transfer the biggest amount that fits both */
        count = (src_count < dst_count) ? src_count : dst_count;
        stransfer(dst_data, dst_stride,
                    src_data, src_stride,
                    count, src_itemsize, transferdata);

        /* If we exhausted the dst block, refresh it */
        if (dst_count == count) {
            if (!dst_iternext(dst_iter)) {
                break;
            }
            dst_count = *dst_countptr;
            dst_data = dst_dataptr[0];
        }
        else {
            dst_count -= count;
            dst_data += count*dst_stride;
        }

        /* If we exhausted the src block, refresh it */
        if (src_count == count) {
            if (!src_iternext(src_iter)) {
                break;
            }
            src_count = *src_countptr;
            src_data = src_dataptr[0];
        }
        else {
            src_count -= count;
            src_data += count*src_stride;
        }
    }

    NPY_END_THREADS;

    NPY_AUXDATA_FREE(transferdata);
    NpyIter_Deallocate(dst_iter);
    NpyIter_Deallocate(src_iter);

    return PyErr_Occurred() ? -1 : 0;
}

/*NUMPY_API
 * Copy an Array into another array -- memory must not overlap
 * Does not require src and dest to have "broadcastable" shapes
 * (only the same number of elements).
 *
 * TODO: For NumPy 2.0, this could accept an order parameter which
 *       only allows NPY_CORDER and NPY_FORDER.  Could also rename
 *       this to CopyAsFlat to make the name more intuitive.
 *
 * Returns 0 on success, -1 on error.
 */
NPY_NO_EXPORT int
PyArray_CopyAnyInto(PyArrayObject *dst, PyArrayObject *src)
{
    return PyArray_CopyAsFlat(dst, src, NPY_CORDER);
}

/*NUMPY_API
 * Copy an Array into another array.
 * Broadcast to the destination shape if necessary.
 *
 * Returns 0 on success, -1 on failure.
 */
NPY_NO_EXPORT int
PyArray_CopyInto(PyArrayObject *dst, PyArrayObject *src)
{
    return PyArray_AssignArray(dst, src, NULL, NPY_UNSAFE_CASTING);
}

/*NUMPY_API
 * Move the memory of one array into another, allowing for overlapping data.
 *
 * Returns 0 on success, negative on failure.
 */
NPY_NO_EXPORT int
PyArray_MoveInto(PyArrayObject *dst, PyArrayObject *src)
{
    return PyArray_AssignArray(dst, src, NULL, NPY_UNSAFE_CASTING);
}

/*NUMPY_API
 * PyArray_CheckAxis
 *
 * check that axis is valid
 * convert 0-d arrays to 1-d arrays
 */
NPY_NO_EXPORT PyObject *
PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags)
{
    PyObject *temp1, *temp2;
    int n = PyArray_NDIM(arr);

    if (*axis == NPY_MAXDIMS || n == 0) {
        if (n != 1) {
            temp1 = PyArray_Ravel(arr,0);
            if (temp1 == NULL) {
                *axis = 0;
                return NULL;
            }
            if (*axis == NPY_MAXDIMS) {
                *axis = PyArray_NDIM((PyArrayObject *)temp1)-1;
            }
        }
        else {
            temp1 = (PyObject *)arr;
            Py_INCREF(temp1);
            *axis = 0;
        }
        if (!flags && *axis == 0) {
            return temp1;
        }
    }
    else {
        temp1 = (PyObject *)arr;
        Py_INCREF(temp1);
    }
    if (flags) {
        temp2 = PyArray_CheckFromAny((PyObject *)temp1, NULL,
                                     0, 0, flags, NULL);
        Py_DECREF(temp1);
        if (temp2 == NULL) {
            return NULL;
        }
    }
    else {
        temp2 = (PyObject *)temp1;
    }
    n = PyArray_NDIM((PyArrayObject *)temp2);
    if (check_and_adjust_axis(axis, n) < 0) {
        Py_DECREF(temp2);
        return NULL;
    }
    return temp2;
}

/*NUMPY_API
 * Zeros
 *
 * steals a reference to type. On failure or when dtype->subarray is
 * true, dtype will be decrefed.
 * accepts NULL type
 */
NPY_NO_EXPORT PyObject *
PyArray_Zeros(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order)
{
    PyArrayObject *ret;

    if (!type) {
        type = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
    }

    ret = (PyArrayObject *)PyArray_NewFromDescr_int(&PyArray_Type,
                                                    type,
                                                    nd, dims,
                                                    NULL, NULL,
                                                    is_f_order, NULL, 1, 0);

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

    /* handle objects */
    if (PyDataType_REFCHK(PyArray_DESCR(ret))) {
        if (_zerofill(ret) < 0) {
            Py_DECREF(ret);
            return NULL;
        }
    }


    return (PyObject *)ret;

}

/*NUMPY_API
 * Empty
 *
 * accepts NULL type
 * steals referenct to type
 */
NPY_NO_EXPORT PyObject *
PyArray_Empty(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order)
{
    PyArrayObject *ret;

    if (!type) type = PyArray_DescrFromType(NPY_DEFAULT_TYPE);

    /*
     * PyArray_NewFromDescr steals a ref,
     * but we need to look at type later.
     * */
    Py_INCREF(type);

    ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
                                                type, nd, dims,
                                                NULL, NULL,
                                                is_f_order, NULL);
    if (ret != NULL && PyDataType_REFCHK(type)) {
        PyArray_FillObjectArray(ret, Py_None);
        if (PyErr_Occurred()) {
            Py_DECREF(ret);
            Py_DECREF(type);
            return NULL;
        }
    }

    Py_DECREF(type);
    return (PyObject *)ret;
}

/*
 * Like ceil(value), but check for overflow.
 *
 * Return 0 on success, -1 on failure. In case of failure, set a PyExc_Overflow
 * exception
 */
static npy_intp
_arange_safe_ceil_to_intp(double value)
{
    double ivalue;

    ivalue = npy_ceil(value);
    /* condition inverted to handle NaN */
    if (npy_isnan(ivalue)) {
        PyErr_SetString(PyExc_ValueError,
            "arange: cannot compute length");
        return -1;
    }
    if (!(NPY_MIN_INTP <= ivalue && ivalue <= NPY_MAX_INTP)) {
        PyErr_SetString(PyExc_OverflowError,
                "arange: overflow while computing length");
        return -1;
    }

    return (npy_intp)ivalue;
}


/*NUMPY_API
  Arange,
*/
NPY_NO_EXPORT PyObject *
PyArray_Arange(double start, double stop, double step, int type_num)
{
    npy_intp length;
    PyArrayObject *range;
    PyArray_ArrFuncs *funcs;
    PyObject *obj;
    int ret;
    NPY_BEGIN_THREADS_DEF;

    length = _arange_safe_ceil_to_intp((stop - start)/step);
    if (error_converting(length)) {
        return NULL;
    }

    if (length <= 0) {
        length = 0;
        return PyArray_New(&PyArray_Type, 1, &length, type_num,
                           NULL, NULL, 0, 0, NULL);
    }
    range = (PyArrayObject *)PyArray_New(&PyArray_Type, 1, &length, type_num,
                        NULL, NULL, 0, 0, NULL);
    if (range == NULL) {
        return NULL;
    }
    funcs = PyArray_DESCR(range)->f;

    /*
     * place start in the buffer and the next value in the second position
     * if length > 2, then call the inner loop, otherwise stop
     */
    obj = PyFloat_FromDouble(start);
    ret = funcs->setitem(obj, PyArray_DATA(range), range);
    Py_DECREF(obj);
    if (ret < 0) {
        goto fail;
    }
    if (length == 1) {
        return (PyObject *)range;
    }
    obj = PyFloat_FromDouble(start + step);
    ret = funcs->setitem(obj, PyArray_BYTES(range)+PyArray_ITEMSIZE(range),
                         range);
    Py_DECREF(obj);
    if (ret < 0) {
        goto fail;
    }
    if (length == 2) {
        return (PyObject *)range;
    }
    if (!funcs->fill) {
        PyErr_SetString(PyExc_ValueError,
                "no fill-function for data-type.");
        Py_DECREF(range);
        return NULL;
    }
    NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(range));
    funcs->fill(PyArray_DATA(range), length, range);
    NPY_END_THREADS;
    if (PyErr_Occurred()) {
        goto fail;
    }
    return (PyObject *)range;

 fail:
    Py_DECREF(range);
    return NULL;
}

/*
 * the formula is len = (intp) ceil((stop - start) / step);
 */
static npy_intp
_calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, int cmplx)
{
    npy_intp len, tmp;
    PyObject *val;
    double value;

    *next = PyNumber_Subtract(stop, start);
    if (!(*next)) {
        if (PyTuple_Check(stop)) {
            PyErr_Clear();
            PyErr_SetString(PyExc_TypeError,
                            "arange: scalar arguments expected "\
                            "instead of a tuple.");
        }
        return -1;
    }
    val = PyNumber_TrueDivide(*next, step);
    Py_DECREF(*next);
    *next = NULL;
    if (!val) {
        return -1;
    }
    if (cmplx && PyComplex_Check(val)) {
        value = PyComplex_RealAsDouble(val);
        if (error_converting(value)) {
            Py_DECREF(val);
            return -1;
        }
        len = _arange_safe_ceil_to_intp(value);
        if (error_converting(len)) {
            Py_DECREF(val);
            return -1;
        }
        value = PyComplex_ImagAsDouble(val);
        Py_DECREF(val);
        if (error_converting(value)) {
            return -1;
        }
        tmp = _arange_safe_ceil_to_intp(value);
        if (error_converting(tmp)) {
            return -1;
        }
        len = PyArray_MIN(len, tmp);
    }
    else {
        value = PyFloat_AsDouble(val);
        Py_DECREF(val);
        if (error_converting(value)) {
            return -1;
        }
        len = _arange_safe_ceil_to_intp(value);
        if (error_converting(len)) {
            return -1;
        }
    }
    if (len > 0) {
        *next = PyNumber_Add(start, step);
        if (!*next) {
            return -1;
        }
    }
    return len;
}

/*NUMPY_API
 *
 * ArangeObj,
 *
 * this doesn't change the references
 */
NPY_NO_EXPORT PyObject *
PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr *dtype)
{
    PyArrayObject *range;
    PyArray_ArrFuncs *funcs;
    PyObject *next, *err;
    npy_intp length;
    PyArray_Descr *native = NULL;
    int swap;
    NPY_BEGIN_THREADS_DEF;

    /* Datetime arange is handled specially */
    if ((dtype != NULL && (dtype->type_num == NPY_DATETIME ||
                           dtype->type_num == NPY_TIMEDELTA)) ||
            (dtype == NULL && (is_any_numpy_datetime_or_timedelta(start) ||
                              is_any_numpy_datetime_or_timedelta(stop) ||
                              is_any_numpy_datetime_or_timedelta(step)))) {
        return (PyObject *)datetime_arange(start, stop, step, dtype);
    }

    if (!dtype) {
        PyArray_Descr *deftype;
        PyArray_Descr *newtype;

        /* intentionally made to be at least NPY_LONG */
        deftype = PyArray_DescrFromType(NPY_LONG);
        newtype = PyArray_DescrFromObject(start, deftype);
        Py_DECREF(deftype);
        if (newtype == NULL) {
            return NULL;
        }
        deftype = newtype;
        if (stop && stop != Py_None) {
            newtype = PyArray_DescrFromObject(stop, deftype);
            Py_DECREF(deftype);
            if (newtype == NULL) {
                return NULL;
            }
            deftype = newtype;
        }
        if (step && step != Py_None) {
            newtype = PyArray_DescrFromObject(step, deftype);
            Py_DECREF(deftype);
            if (newtype == NULL) {
                return NULL;
            }
            deftype = newtype;
        }
        dtype = deftype;
    }
    else {
        Py_INCREF(dtype);
    }
    if (!step || step == Py_None) {
        step = PyInt_FromLong(1);
    }
    else {
        Py_XINCREF(step);
    }
    if (!stop || stop == Py_None) {
        stop = start;
        start = PyInt_FromLong(0);
    }
    else {
        Py_INCREF(start);
    }
    /* calculate the length and next = start + step*/
    length = _calc_length(start, stop, step, &next,
                          PyTypeNum_ISCOMPLEX(dtype->type_num));
    err = PyErr_Occurred();
    if (err) {
        Py_DECREF(dtype);
        if (err && PyErr_GivenExceptionMatches(err, PyExc_OverflowError)) {
            PyErr_SetString(PyExc_ValueError, "Maximum allowed size exceeded");
        }
        goto fail;
    }
    if (length <= 0) {
        length = 0;
        range = (PyArrayObject *)PyArray_SimpleNewFromDescr(1, &length, dtype);
        Py_DECREF(step);
        Py_DECREF(start);
        return (PyObject *)range;
    }

    /*
     * If dtype is not in native byte-order then get native-byte
     * order version.  And then swap on the way out.
     */
    if (!PyArray_ISNBO(dtype->byteorder)) {
        native = PyArray_DescrNewByteorder(dtype, NPY_NATBYTE);
        swap = 1;
    }
    else {
        native = dtype;
        swap = 0;
    }

    range = (PyArrayObject *)PyArray_SimpleNewFromDescr(1, &length, native);
    if (range == NULL) {
        goto fail;
    }

    /*
     * place start in the buffer and the next value in the second position
     * if length > 2, then call the inner loop, otherwise stop
     */
    funcs = PyArray_DESCR(range)->f;
    if (funcs->setitem(start, PyArray_DATA(range), range) < 0) {
        goto fail;
    }
    if (length == 1) {
        goto finish;
    }
    if (funcs->setitem(next, PyArray_BYTES(range)+PyArray_ITEMSIZE(range),
                       range) < 0) {
        goto fail;
    }
    if (length == 2) {
        goto finish;
    }
    if (!funcs->fill) {
        PyErr_SetString(PyExc_ValueError, "no fill-function for data-type.");
        Py_DECREF(range);
        goto fail;
    }
    NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(range));
    funcs->fill(PyArray_DATA(range), length, range);
    NPY_END_THREADS;
    if (PyErr_Occurred()) {
        goto fail;
    }
 finish:
    /* TODO: This swapping could be handled on the fly by the nditer */
    if (swap) {
        PyObject *new;
        new = PyArray_Byteswap(range, 1);
        Py_DECREF(new);
        Py_DECREF(PyArray_DESCR(range));
        /* steals the reference */
        ((PyArrayObject_fields *)range)->descr = dtype;
    }
    Py_DECREF(start);
    Py_DECREF(step);
    Py_DECREF(next);
    return (PyObject *)range;

 fail:
    Py_DECREF(start);
    Py_DECREF(step);
    Py_XDECREF(next);
    return NULL;
}

static PyArrayObject *
array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nread)
{
    PyArrayObject *r;
    npy_off_t start, numbytes;

    if (num < 0) {
        int fail = 0;
        start = npy_ftell(fp);
        if (start < 0) {
            fail = 1;
        }
        if (npy_fseek(fp, 0, SEEK_END) < 0) {
            fail = 1;
        }
        numbytes = npy_ftell(fp);
        if (numbytes < 0) {
            fail = 1;
        }
        numbytes -= start;
        if (npy_fseek(fp, start, SEEK_SET) < 0) {
            fail = 1;
        }
        if (fail) {
            PyErr_SetString(PyExc_IOError,
                            "could not seek in file");
            Py_DECREF(dtype);
            return NULL;
        }
        num = numbytes / dtype->elsize;
    }
    /*
     * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype
     * even on success, so make sure it stays around until exit.
     */
    Py_INCREF(dtype);
    r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &num,
                                              NULL, NULL, 0, NULL);
    if (r == NULL) {
        Py_DECREF(dtype);
        return NULL;
    }
    NPY_BEGIN_ALLOW_THREADS;
    *nread = fread(PyArray_DATA(r), dtype->elsize, num, fp);
    NPY_END_ALLOW_THREADS;
    Py_DECREF(dtype);
    return r;
}

/*
 * Create an array by reading from the given stream, using the passed
 * next_element and skip_separator functions.
 */
#define FROM_BUFFER_SIZE 4096
static PyArrayObject *
array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread,
                void *stream, next_element next, skip_separator skip_sep,
                void *stream_data)
{
    PyArrayObject *r;
    npy_intp i;
    char *dptr, *clean_sep, *tmp;
    int err = 0;
    npy_intp thisbuf = 0;
    npy_intp size;
    npy_intp bytes, totalbytes;

    size = (num >= 0) ? num : FROM_BUFFER_SIZE;

    /*
     * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype
     * even on success, so make sure it stays around until exit.
     */
    Py_INCREF(dtype);
    r = (PyArrayObject *)
        PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &size,
                             NULL, NULL, 0, NULL);
    if (r == NULL) {
        Py_DECREF(dtype);
        return NULL;
    }
    clean_sep = swab_separator(sep);
    if (clean_sep == NULL) {
        err = 1;
        goto fail;
    }

    NPY_BEGIN_ALLOW_THREADS;
    totalbytes = bytes = size * dtype->elsize;
    dptr = PyArray_DATA(r);
    for (i= 0; num < 0 || i < num; i++) {
        if (next(&stream, dptr, dtype, stream_data) < 0) {
            /* EOF */
            break;
        }
        *nread += 1;
        thisbuf += 1;
        dptr += dtype->elsize;
        if (num < 0 && thisbuf == size) {
            totalbytes += bytes;
            tmp = PyDataMem_RENEW(PyArray_DATA(r), totalbytes);
            if (tmp == NULL) {
                err = 1;
                break;
            }
            ((PyArrayObject_fields *)r)->data = tmp;
            dptr = tmp + (totalbytes - bytes);
            thisbuf = 0;
        }
        if (skip_sep(&stream, clean_sep, stream_data) < 0) {
            break;
        }
    }
    if (num < 0) {
        tmp = PyDataMem_RENEW(PyArray_DATA(r), PyArray_MAX(*nread,1)*dtype->elsize);
        if (tmp == NULL) {
            err = 1;
        }
        else {
            PyArray_DIMS(r)[0] = *nread;
            ((PyArrayObject_fields *)r)->data = tmp;
        }
    }
    NPY_END_ALLOW_THREADS;
    free(clean_sep);

fail:
    Py_DECREF(dtype);
    if (err == 1) {
        PyErr_NoMemory();
    }
    if (PyErr_Occurred()) {
        Py_DECREF(r);
        return NULL;
    }
    return r;
}
#undef FROM_BUFFER_SIZE

/*NUMPY_API
 *
 * Given a ``FILE *`` pointer ``fp``, and a ``PyArray_Descr``, return an
 * array corresponding to the data encoded in that file.
 *
 * If the dtype is NULL, the default array type is used (double).
 * If non-null, the reference is stolen and if dtype->subarray is true dtype
 * will be decrefed even on success.
 *
 * The number of elements to read is given as ``num``; if it is < 0, then
 * then as many as possible are read.
 *
 * If ``sep`` is NULL or empty, then binary data is assumed, else
 * text data, with ``sep`` as the separator between elements. Whitespace in
 * the separator matches any length of whitespace in the text, and a match
 * for whitespace around the separator is added.
 *
 * For memory-mapped files, use the buffer interface. No more data than
 * necessary is read by this routine.
 */
NPY_NO_EXPORT PyObject *
PyArray_FromFile(FILE *fp, PyArray_Descr *dtype, npy_intp num, char *sep)
{
    PyArrayObject *ret;
    size_t nread = 0;

    if (PyDataType_REFCHK(dtype)) {
        PyErr_SetString(PyExc_ValueError,
                "Cannot read into object array");
        Py_DECREF(dtype);
        return NULL;
    }
    if (dtype->elsize == 0) {
        /* Nothing to read, just create an empty array of the requested type */
        return PyArray_NewFromDescr_int(&PyArray_Type,
                                        dtype,
                                        1, &num,
                                        NULL, NULL,
                                        0, NULL, 0, 1);
    }
    if ((sep == NULL) || (strlen(sep) == 0)) {
        ret = array_fromfile_binary(fp, dtype, num, &nread);
    }
    else {
        if (dtype->f->scanfunc == NULL) {
            PyErr_SetString(PyExc_ValueError,
                    "Unable to read character files of that array type");
            Py_DECREF(dtype);
            return NULL;
        }
        ret = array_from_text(dtype, num, sep, &nread, fp,
                (next_element) fromfile_next_element,
                (skip_separator) fromfile_skip_separator, NULL);
    }
    if (ret == NULL) {
        Py_DECREF(dtype);
        return NULL;
    }
    if (((npy_intp) nread) < num) {
        /* Realloc memory for smaller number of elements */
        const size_t nsize = PyArray_MAX(nread,1)*PyArray_DESCR(ret)->elsize;
        char *tmp;

        if((tmp = PyDataMem_RENEW(PyArray_DATA(ret), nsize)) == NULL) {
            Py_DECREF(ret);
            return PyErr_NoMemory();
        }
        ((PyArrayObject_fields *)ret)->data = tmp;
        PyArray_DIMS(ret)[0] = nread;
    }
    return (PyObject *)ret;
}

/*NUMPY_API*/
NPY_NO_EXPORT PyObject *
PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
                   npy_intp count, npy_intp offset)
{
    PyArrayObject *ret;
    char *data;
    Py_ssize_t ts;
    npy_intp s, n;
    int itemsize;
    int writeable = 1;


    if (PyDataType_REFCHK(type)) {
        PyErr_SetString(PyExc_ValueError,
                        "cannot create an OBJECT array from memory"\
                        " buffer");
        Py_DECREF(type);
        return NULL;
    }
    if (PyDataType_ISUNSIZED(type)) {
        PyErr_SetString(PyExc_ValueError,
                        "itemsize cannot be zero in type");
        Py_DECREF(type);
        return NULL;
    }
    if (Py_TYPE(buf)->tp_as_buffer == NULL
#if defined(NPY_PY3K)
        || Py_TYPE(buf)->tp_as_buffer->bf_getbuffer == NULL
#else
        || (Py_TYPE(buf)->tp_as_buffer->bf_getwritebuffer == NULL
            && Py_TYPE(buf)->tp_as_buffer->bf_getreadbuffer == NULL)
#endif
        ) {
        PyObject *newbuf;
        newbuf = PyObject_GetAttr(buf, npy_ma_str_buffer);
        if (newbuf == NULL) {
            Py_DECREF(type);
            return NULL;
        }
        buf = newbuf;
    }
    else {
        Py_INCREF(buf);
    }

    if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) {
        writeable = 0;
        PyErr_Clear();
        if (PyObject_AsReadBuffer(buf, (void *)&data, &ts) == -1) {
            Py_DECREF(buf);
            Py_DECREF(type);
            return NULL;
        }
    }

    if ((offset < 0) || (offset > ts)) {
        PyErr_Format(PyExc_ValueError,
                     "offset must be non-negative and no greater than buffer "\
                     "length (%" NPY_INTP_FMT ")", (npy_intp)ts);
        Py_DECREF(buf);
        Py_DECREF(type);
        return NULL;
    }

    data += offset;
    s = (npy_intp)ts - offset;
    n = (npy_intp)count;
    itemsize = type->elsize;
    if (n < 0 ) {
        if (s % itemsize != 0) {
            PyErr_SetString(PyExc_ValueError,
                            "buffer size must be a multiple"\
                            " of element size");
            Py_DECREF(buf);
            Py_DECREF(type);
            return NULL;
        }
        n = s/itemsize;
    }
    else {
        if (s < n*itemsize) {
            PyErr_SetString(PyExc_ValueError,
                            "buffer is smaller than requested"\
                            " size");
            Py_DECREF(buf);
            Py_DECREF(type);
            return NULL;
        }
    }

    if ((ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
                                                     type,
                                                     1, &n,
                                                     NULL, data,
                                                     NPY_ARRAY_DEFAULT,
                                                     NULL)) == NULL) {
        Py_DECREF(buf);
        return NULL;
    }

    if (!writeable) {
        PyArray_CLEARFLAGS(ret, NPY_ARRAY_WRITEABLE);
    }
    /* Store a reference for decref on deallocation */
    if (PyArray_SetBaseObject(ret, buf) < 0) {
        Py_DECREF(ret);
        return NULL;
    }
    PyArray_UpdateFlags(ret, NPY_ARRAY_ALIGNED);
    return (PyObject *)ret;
}

/*NUMPY_API
 *
 * Given a pointer to a string ``data``, a string length ``slen``, and
 * a ``PyArray_Descr``, return an array corresponding to the data
 * encoded in that string.
 *
 * If the dtype is NULL, the default array type is used (double).
 * If non-null, the reference is stolen.
 *
 * If ``slen`` is < 0, then the end of string is used for text data.
 * It is an error for ``slen`` to be < 0 for binary data (since embedded NULLs
 * would be the norm).
 *
 * The number of elements to read is given as ``num``; if it is < 0, then
 * then as many as possible are read.
 *
 * If ``sep`` is NULL or empty, then binary data is assumed, else
 * text data, with ``sep`` as the separator between elements. Whitespace in
 * the separator matches any length of whitespace in the text, and a match
 * for whitespace around the separator is added.
 */
NPY_NO_EXPORT PyObject *
PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype,
                   npy_intp num, char *sep)
{
    int itemsize;
    PyArrayObject *ret;
    npy_bool binary;

    if (dtype == NULL) {
        dtype=PyArray_DescrFromType(NPY_DEFAULT_TYPE);
        if (dtype == NULL) {
            return NULL;
        }
    }
    if (PyDataType_FLAGCHK(dtype, NPY_ITEM_IS_POINTER) ||
                    PyDataType_REFCHK(dtype)) {
        PyErr_SetString(PyExc_ValueError,
                        "Cannot create an object array from"    \
                        " a string");
        Py_DECREF(dtype);
        return NULL;
    }
    itemsize = dtype->elsize;
    if (itemsize == 0) {
        PyErr_SetString(PyExc_ValueError, "zero-valued itemsize");
        Py_DECREF(dtype);
        return NULL;
    }

    binary = ((sep == NULL) || (strlen(sep) == 0));
    if (binary) {
        if (num < 0 ) {
            if (slen % itemsize != 0) {
                PyErr_SetString(PyExc_ValueError,
                                "string size must be a "\
                                "multiple of element size");
                Py_DECREF(dtype);
                return NULL;
            }
            num = slen/itemsize;
        }
        else {
            if (slen < num*itemsize) {
                PyErr_SetString(PyExc_ValueError,
                                "string is smaller than " \
                                "requested size");
                Py_DECREF(dtype);
                return NULL;
            }
        }
        ret = (PyArrayObject *)
            PyArray_NewFromDescr(&PyArray_Type, dtype,
                                 1, &num, NULL, NULL,
                                 0, NULL);
        if (ret == NULL) {
            return NULL;
        }
        memcpy(PyArray_DATA(ret), data, num*dtype->elsize);
    }
    else {
        /* read from character-based string */
        size_t nread = 0;
        char *end;

        if (dtype->f->scanfunc == NULL) {
            PyErr_SetString(PyExc_ValueError,
                            "don't know how to read "       \
                            "character strings with that "  \
                            "array type");
            Py_DECREF(dtype);
            return NULL;
        }
        if (slen < 0) {
            end = NULL;
        }
        else {
            end = data + slen;
        }
        ret = array_from_text(dtype, num, sep, &nread,
                              data,
                              (next_element) fromstr_next_element,
                              (skip_separator) fromstr_skip_separator,
                              end);
    }
    return (PyObject *)ret;
}

/*NUMPY_API
 *
 * steals a reference to dtype (which cannot be NULL)
 */
NPY_NO_EXPORT PyObject *
PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
{
    PyObject *value;
    PyObject *iter = PyObject_GetIter(obj);
    PyArrayObject *ret = NULL;
    npy_intp i, elsize, elcount;
    char *item, *new_data;

    if (iter == NULL) {
        goto done;
    }
    if (PyDataType_ISUNSIZED(dtype)) {
        PyErr_SetString(PyExc_ValueError,
                "Must specify length when using variable-size data-type.");
        goto done;
    }
    elcount = (count < 0) ? 0 : count;
    elsize = dtype->elsize;

    /*
     * We would need to alter the memory RENEW code to decrement any
     * reference counts before throwing away any memory.
     */
    if (PyDataType_REFCHK(dtype)) {
        PyErr_SetString(PyExc_ValueError,
                "cannot create object arrays from iterator");
        goto done;
    }

    ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1,
                                                &elcount, NULL,NULL, 0, NULL);
    dtype = NULL;
    if (ret == NULL) {
        goto done;
    }
    for (i = 0; (i < count || count == -1) &&
             (value = PyIter_Next(iter)); i++) {
        if (i >= elcount) {
            npy_intp nbytes;
            /*
              Grow PyArray_DATA(ret):
              this is similar for the strategy for PyListObject, but we use
              50% overallocation => 0, 4, 8, 14, 23, 36, 56, 86 ...
            */
            elcount = (i >> 1) + (i < 4 ? 4 : 2) + i;
            if (!npy_mul_with_overflow_intp(&nbytes, elcount, elsize)) {
                new_data = PyDataMem_RENEW(PyArray_DATA(ret), nbytes);
            }
            else {
                new_data = NULL;
            }
            if (new_data == NULL) {
                PyErr_SetString(PyExc_MemoryError,
                        "cannot allocate array memory");
                Py_DECREF(value);
                goto done;
            }
            ((PyArrayObject_fields *)ret)->data = new_data;
        }
        PyArray_DIMS(ret)[0] = i + 1;

        if (((item = index2ptr(ret, i)) == NULL) ||
                PyArray_SETITEM(ret, item, value) == -1) {
            Py_DECREF(value);
            goto done;
        }
        Py_DECREF(value);
    }


    if (PyErr_Occurred()) {
        goto done;
    }
    if (i < count) {
        PyErr_SetString(PyExc_ValueError,
                "iterator too short");
        goto done;
    }

    /*
     * Realloc the data so that don't keep extra memory tied up
     * (assuming realloc is reasonably good about reusing space...)
     */
    if (i == 0) {
        /* The size cannot be zero for PyDataMem_RENEW. */
        i = 1;
    }
    new_data = PyDataMem_RENEW(PyArray_DATA(ret), i * elsize);
    if (new_data == NULL) {
        PyErr_SetString(PyExc_MemoryError,
                "cannot allocate array memory");
        goto done;
    }
    ((PyArrayObject_fields *)ret)->data = new_data;

 done:
    Py_XDECREF(iter);
    Py_XDECREF(dtype);
    if (PyErr_Occurred()) {
        Py_XDECREF(ret);
        return NULL;
    }
    return (PyObject *)ret;
}

/*
 * This is the main array creation routine.
 *
 * Flags argument has multiple related meanings
 * depending on data and strides:
 *
 * If data is given, then flags is flags associated with data.
 * If strides is not given, then a contiguous strides array will be created
 * and the NPY_ARRAY_C_CONTIGUOUS bit will be set.  If the flags argument
 * has the NPY_ARRAY_F_CONTIGUOUS bit set, then a FORTRAN-style strides array will be
 * created (and of course the NPY_ARRAY_F_CONTIGUOUS flag bit will be set).
 *
 * If data is not given but created here, then flags will be NPY_ARRAY_DEFAULT
 * and a non-zero flags argument can be used to indicate a FORTRAN style
 * array is desired.
 *
 * Dimensions and itemsize must have been checked for validity.
 */

NPY_NO_EXPORT void
_array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize,
                    int inflag, int *objflags)
{
    int i;
#if NPY_RELAXED_STRIDES_CHECKING
    npy_bool not_cf_contig = 0;
    npy_bool nod = 0; /* A dim != 1 was found */

    /* Check if new array is both F- and C-contiguous */
    for (i = 0; i < nd; i++) {
        if (dims[i] != 1) {
            if (nod) {
                not_cf_contig = 1;
                break;
            }
            nod = 1;
        }
    }
#endif /* NPY_RELAXED_STRIDES_CHECKING */

    /* Only make Fortran strides if not contiguous as well */
    if ((inflag & (NPY_ARRAY_F_CONTIGUOUS|NPY_ARRAY_C_CONTIGUOUS)) ==
                                            NPY_ARRAY_F_CONTIGUOUS) {
        for (i = 0; i < nd; i++) {
            strides[i] = itemsize;
            if (dims[i]) {
                itemsize *= dims[i];
            }
#if NPY_RELAXED_STRIDES_CHECKING
            else {
                not_cf_contig = 0;
            }
#if NPY_RELAXED_STRIDES_DEBUG
            /* For testing purpose only */
            if (dims[i] == 1) {
                strides[i] = NPY_MAX_INTP;
            }
#endif /* NPY_RELAXED_STRIDES_DEBUG */
#endif /* NPY_RELAXED_STRIDES_CHECKING */
        }
#if NPY_RELAXED_STRIDES_CHECKING
        if (not_cf_contig) {
#else /* not NPY_RELAXED_STRIDES_CHECKING */
        if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[nd-1] > 1))) {
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
            *objflags = ((*objflags)|NPY_ARRAY_F_CONTIGUOUS) &
                                            ~NPY_ARRAY_C_CONTIGUOUS;
        }
        else {
            *objflags |= (NPY_ARRAY_F_CONTIGUOUS|NPY_ARRAY_C_CONTIGUOUS);
        }
    }
    else {
        for (i = nd - 1; i >= 0; i--) {
            strides[i] = itemsize;
            if (dims[i]) {
                itemsize *= dims[i];
            }
#if NPY_RELAXED_STRIDES_CHECKING
            else {
                not_cf_contig = 0;
            }
#if NPY_RELAXED_STRIDES_DEBUG
            /* For testing purpose only */
            if (dims[i] == 1) {
                strides[i] = NPY_MAX_INTP;
            }
#endif /* NPY_RELAXED_STRIDES_DEBUG */
#endif /* NPY_RELAXED_STRIDES_CHECKING */
        }
#if NPY_RELAXED_STRIDES_CHECKING
        if (not_cf_contig) {
#else /* not NPY_RELAXED_STRIDES_CHECKING */
        if ((nd > 1) && ((strides[0] != strides[nd-1]) || (dims[0] > 1))) {
#endif /* not NPY_RELAXED_STRIDES_CHECKING */
            *objflags = ((*objflags)|NPY_ARRAY_C_CONTIGUOUS) &
                                            ~NPY_ARRAY_F_CONTIGUOUS;
        }
        else {
            *objflags |= (NPY_ARRAY_C_CONTIGUOUS|NPY_ARRAY_F_CONTIGUOUS);
        }
    }
    return;
}

/*
 * Calls arr_of_subclass.__array_wrap__(towrap), in order to make 'towrap'
 * have the same ndarray subclass as 'arr_of_subclass'.
 */
NPY_NO_EXPORT PyArrayObject *
PyArray_SubclassWrap(PyArrayObject *arr_of_subclass, PyArrayObject *towrap)
{
    PyObject *wrapped = PyObject_CallMethod((PyObject *)arr_of_subclass,
                                        "__array_wrap__", "O", towrap);
    if (wrapped == NULL) {
        return NULL;
    }
    if (!PyArray_Check(wrapped)) {
        PyErr_SetString(PyExc_RuntimeError,
                "ndarray subclass __array_wrap__ method returned an "
                "object which was not an instance of an ndarray subclass");
        Py_DECREF(wrapped);
        return NULL;
    }

    return (PyArrayObject *)wrapped;
}
