diff options
| author | Travis Oliphant <oliphant@enthought.com> | 2006-01-04 17:33:12 +0000 |
|---|---|---|
| committer | Travis Oliphant <oliphant@enthought.com> | 2006-01-04 17:33:12 +0000 |
| commit | 8057b2d910a5a6726a666a2c18ac495dbb9e6000 (patch) | |
| tree | e8ab5a397e9d2d1fd3885f3524821587ee2d407c /numpy/core/src | |
| parent | da9c6da4a304d240492b653f526b9607b032921c (diff) | |
| download | numpy-8057b2d910a5a6726a666a2c18ac495dbb9e6000.tar.gz | |
rename sub-packages
Diffstat (limited to 'numpy/core/src')
| -rw-r--r-- | numpy/core/src/_compiled_base.c | 453 | ||||
| -rw-r--r-- | numpy/core/src/_isnan.c | 47 | ||||
| -rw-r--r-- | numpy/core/src/_signbit.c | 32 | ||||
| -rw-r--r-- | numpy/core/src/_sortmodule.c.src | 482 | ||||
| -rw-r--r-- | numpy/core/src/arraymethods.c | 1647 | ||||
| -rw-r--r-- | numpy/core/src/arrayobject.c | 8472 | ||||
| -rw-r--r-- | numpy/core/src/arraytypes.inc.src | 1913 | ||||
| -rw-r--r-- | numpy/core/src/multiarraymodule.c | 5507 | ||||
| -rw-r--r-- | numpy/core/src/scalarmathmodule.c.src | 103 | ||||
| -rw-r--r-- | numpy/core/src/scalartypes.inc.src | 2165 | ||||
| -rw-r--r-- | numpy/core/src/ufuncobject.c | 3145 | ||||
| -rw-r--r-- | numpy/core/src/umathmodule.c.src | 1847 |
12 files changed, 25813 insertions, 0 deletions
diff --git a/numpy/core/src/_compiled_base.c b/numpy/core/src/_compiled_base.c new file mode 100644 index 000000000..3ce3743d7 --- /dev/null +++ b/numpy/core/src/_compiled_base.c @@ -0,0 +1,453 @@ +#include "Python.h" +#include "structmember.h" +#include "scipy/arrayobject.h" + +static PyObject *ErrorObject; +#define Py_Try(BOOLEAN) {if (!(BOOLEAN)) goto fail;} +#define Py_Assert(BOOLEAN,MESS) {if (!(BOOLEAN)) { \ + PyErr_SetString(ErrorObject, (MESS)); \ + goto fail;} \ + } + +static intp +incr_slot_ (double x, double *bins, intp lbins) +{ + intp i ; + for ( i = 0 ; i < lbins ; i ++ ) + if ( x < bins [i] ) + return i ; + return lbins ; +} + +static intp +decr_slot_ (double x, double * bins, intp lbins) +{ + intp i ; + for ( i = lbins - 1 ; i >= 0; i -- ) + if (x < bins [i]) + return i + 1 ; + return 0 ; +} + +static int +monotonic_ (double * a, int lena) +{ + int i; + if (a [0] <= a [1]) /* possibly monotonic increasing */ + { + for (i = 1 ; i < lena - 1; i ++) + if (a [i] > a [i + 1]) return 0 ; + return 1 ; + } + else /* possibly monotonic decreasing */ + { + for (i = 1 ; i < lena - 1; i ++) + if (a [i] < a [i + 1]) return 0 ; + return -1 ; + } +} + + + +static intp +mxx (intp *i , intp len) +{ + /* find the index of the maximum element of an integer array */ + intp mx = 0, max = i[0] ; + intp j ; + for ( j = 1 ; j < len; j ++ ) + if ( i [j] > max ) + {max = i [j] ; + mx = j ;} + return mx; +} + +static intp +mnx (intp *i , intp len) +{ + /* find the index of the minimum element of an integer array */ + intp mn = 0, min = i [0] ; + intp j ; + for ( j = 1 ; j < len; j ++ ) + if ( i [j] < min ) + {min = i [j] ; + mn = j ;} + return mn; +} + + +static PyObject * +arr_bincount(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* histogram accepts one or two arguments. The first is an array + * of non-negative integers and the second, if present, is an + * array of weights, which must be promotable to double. + * Call these arguments list and weight. Both must be one- + * dimensional. len (weight) == len(list) + * If weight is not present: + * histogram (list) [i] is the number of occurrences of i in list. + * If weight is present: + * histogram (list, weight) [i] is the sum of all weight [j] + * where list [j] == i. */ + /* self is not used */ + PyArray_Descr *type; + PyObject *list = NULL, *weight=Py_None ; + PyObject *lst=NULL, *ans=NULL, *wts=NULL; + intp *numbers, *ians, len , mxi, mni, ans_size; + int i; + double *weights , *dans; + static char *kwlist[] = {"list", "weights", NULL}; + + + Py_Try(PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, + &list, &weight)); + Py_Try(lst = PyArray_ContiguousFromAny(list, PyArray_INTP, 1, 1)); + len = PyArray_SIZE(lst); + numbers = (intp *) PyArray_DATA(lst); + mxi = mxx (numbers, len) ; + mni = mnx (numbers, len) ; + Py_Assert(numbers[mni] >= 0, + "irst argument of bincount must be non-negative"); + ans_size = numbers [mxi] + 1 ; + type = PyArray_DescrFromType(PyArray_INTP); + if (weight == Py_None) { + Py_Try(ans = PyArray_Zeros(1, &ans_size, type, 0)); + ians = (intp *)(PyArray_DATA(ans)); + for (i = 0 ; i < len ; i++) + ians [numbers [i]] += 1 ; + Py_DECREF(lst); + } + else { + Py_Try(wts = PyArray_ContiguousFromAny(weight, + PyArray_DOUBLE, 1, 1)); + weights = (double *)PyArray_DATA (wts); + Py_Assert(PyArray_SIZE(wts) == len, "bincount: length of weights " \ + "does not match that of list"); + type = PyArray_DescrFromType(PyArray_DOUBLE); + Py_Try(ans = PyArray_Zeros(1, &ans_size, type, 0)); + dans = (double *)PyArray_DATA (ans); + for (i = 0 ; i < len ; i++) { + dans[numbers[i]] += weights[i]; + } + Py_DECREF(lst); + Py_DECREF(wts); + } + return ans; + + fail: + Py_XDECREF(lst); + Py_XDECREF(wts); + Py_XDECREF(ans); + return NULL; +} + + +static PyObject * +arr_digitize(PyObject *self, PyObject *args, PyObject *kwds) +{ + /* digitize (x, bins) returns an array of python integers the same + length of x. The values i returned are such that + bins [i - 1] <= x < bins [i] if bins is monotonically increasing, + or bins [i - 1] > x >= bins [i] if bins is monotonically decreasing. + Beyond the bounds of bins, returns either i = 0 or i = len (bins) + as appropriate. */ + /* self is not used */ + PyObject *ox, *obins ; + PyObject *ax=NULL, *abins=NULL, *aret=NULL; + double *dx, *dbins ; + intp lbins, lx ; /* lengths */ + intp *iret; + int m, i ; + static char *kwlist[] = {"x", "bins", NULL}; + PyArray_Descr *type; + + Py_Try(PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &ox, &obins)); + + type = PyArray_DescrFromType(PyArray_DOUBLE); + Py_Try(ax=PyArray_FromAny(ox, type, 1, 1, CARRAY_FLAGS)); + Py_Try(abins = PyArray_FromAny(obins, type, 1, 1, CARRAY_FLAGS)); + + lx = PyArray_SIZE(ax); + dx = (double *)PyArray_DATA(ax); + lbins = PyArray_SIZE(abins); + dbins = (double *)PyArray_DATA(abins); + Py_Try(aret = PyArray_SimpleNew(1, &lx, PyArray_INTP)); + iret = (intp *)PyArray_DATA(aret); + + Py_Assert(lx > 0 && lbins > 0, + "x and bins both must have non-zero length"); + + if (lbins == 1) { + for (i=0 ; i<lx ; i++) + if (dx [i] >= dbins[0]) + iret[i] = 1; + else + iret[i] = 0; + } + else { + m = monotonic_ (dbins, lbins) ; + if ( m == -1 ) { + for ( i = 0 ; i < lx ; i ++ ) + iret [i] = decr_slot_ (dx [i], dbins, lbins) ; + } + else if ( m == 1 ) { + for ( i = 0 ; i < lx ; i ++ ) + iret [i] = incr_slot_ ((float)dx [i], dbins, lbins) ; + } + else Py_Assert(0, "bins must be montonically increasing or decreasing"); + } + + Py_DECREF(ax); + Py_DECREF(abins); + return aret; + + fail: + Py_XDECREF(ax); + Py_XDECREF(abins); + Py_XDECREF(aret); + return NULL; +} + + + +static char arr_insert__doc__[] = "Insert vals sequentially into equivalent 1-d positions indicated by mask."; + +static PyObject * +arr_insert(PyObject *self, PyObject *args, PyObject *kwdict) +{ + /* Returns input array with values inserted sequentially into places + indicated by the mask + */ + PyObject *mask=NULL, *vals=NULL; + PyArrayObject *ainput=NULL, *amask=NULL, *avals=NULL, + *tmp=NULL; + int numvals, totmask, sameshape; + char *input_data, *mptr, *vptr, *zero=NULL; + int melsize, delsize, copied, nd; + intp *instrides, *inshape; + int mindx, rem_indx, indx, i, k, objarray; + + static char *kwlist[] = {"input","mask","vals",NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O&OO", kwlist, + PyArray_Converter, &ainput, + &mask, &vals)) + goto fail; + + amask = (PyArrayObject *) PyArray_FROM_OF(mask, CARRAY_FLAGS); + if (amask == NULL) goto fail; + /* Cast an object array */ + if (amask->descr->type_num == PyArray_OBJECT) { + tmp = (PyArrayObject *)PyArray_Cast(amask, PyArray_INTP); + if (tmp == NULL) goto fail; + Py_DECREF(amask); + amask = tmp; + } + + sameshape = 1; + if (amask->nd == ainput->nd) { + for (k=0; k < amask->nd; k++) + if (amask->dimensions[k] != ainput->dimensions[k]) + sameshape = 0; + } + else { /* Test to see if amask is 1d */ + if (amask->nd != 1) sameshape = 0; + else if ((PyArray_SIZE(ainput)) != PyArray_SIZE(amask)) sameshape = 0; + } + if (!sameshape) { + PyErr_SetString(PyExc_TypeError, + "mask array must be 1-d or same shape as input array"); + goto fail; + } + + avals = (PyArrayObject *)PyArray_FromObject(vals, ainput->descr->type_num, 0, 1); + if (avals == NULL) goto fail; + + numvals = PyArray_SIZE(avals); + nd = ainput->nd; + input_data = ainput->data; + mptr = amask->data; + melsize = amask->descr->elsize; + vptr = avals->data; + delsize = avals->descr->elsize; + zero = PyArray_Zero(amask); + if (zero == NULL) + goto fail; + objarray = (ainput->descr->type_num == PyArray_OBJECT); + + /* Handle zero-dimensional case separately */ + if (nd == 0) { + if (memcmp(mptr,zero,melsize) != 0) { + /* Copy value element over to input array */ + memcpy(input_data,vptr,delsize); + if (objarray) Py_INCREF(*((PyObject **)vptr)); + } + Py_DECREF(amask); + Py_DECREF(avals); + PyDataMem_FREE(zero); + Py_INCREF(Py_None); + return Py_None; + } + + /* Walk through mask array, when non-zero is encountered + copy next value in the vals array to the input array. + If we get through the value array, repeat it as necessary. + */ + totmask = (int) PyArray_SIZE(amask); + copied = 0; + instrides = ainput->strides; + inshape = ainput->dimensions; + for (mindx = 0; mindx < totmask; mindx++) { + if (memcmp(mptr,zero,melsize) != 0) { + /* compute indx into input array + */ + rem_indx = mindx; + indx = 0; + for(i=nd-1; i > 0; --i) { + indx += (rem_indx % inshape[i]) * instrides[i]; + rem_indx /= inshape[i]; + } + indx += rem_indx * instrides[0]; + /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */ + /* Copy value element over to input array */ + memcpy(input_data+indx,vptr,delsize); + if (objarray) Py_INCREF(*((PyObject **)vptr)); + vptr += delsize; + copied += 1; + /* If we move past value data. Reset */ + if (copied >= numvals) vptr = avals->data; + } + mptr += melsize; + } + + Py_DECREF(amask); + Py_DECREF(avals); + PyDataMem_FREE(zero); + Py_DECREF(ainput); + Py_INCREF(Py_None); + return Py_None; + + fail: + PyDataMem_FREE(zero); + Py_XDECREF(ainput); + Py_XDECREF(amask); + Py_XDECREF(avals); + return NULL; +} + + +static PyTypeObject *PyMemberDescr_TypePtr=NULL; +static PyTypeObject *PyGetSetDescr_TypePtr=NULL; + +/* Can only be called if doc is currently NULL +*/ +static PyObject * +arr_add_docstring(PyObject *dummy, PyObject *args) +{ + PyObject *obj; + PyObject *str; + char *docstr; + static char *msg = "already has a docstring"; + + if (!PyArg_ParseTuple(args, "OO!", &obj, &PyString_Type, &str)) + return NULL; + + docstr = PyString_AS_STRING(str); + +#define _TESTDOC1(typebase) (obj->ob_type == &Py##typebase##_Type) +#define _TESTDOC2(typebase) (obj->ob_type == Py##typebase##_TypePtr) +#define _ADDDOC(typebase, doc, name) { \ + Py##typebase##Object *new = (Py##typebase##Object *)obj; \ + if (!(doc)) { \ + doc = docstr; \ + } \ + else { \ + PyErr_Format(PyExc_RuntimeError, \ + "%s method %s",name, msg); \ + return NULL; \ + } \ + } + + if _TESTDOC1(CFunction) + _ADDDOC(CFunction, new->m_ml->ml_doc, new->m_ml->ml_name) + else if _TESTDOC1(Type) + _ADDDOC(Type, new->tp_doc, new->tp_name) + else if _TESTDOC2(MemberDescr) + _ADDDOC(MemberDescr, new->d_member->doc, new->d_member->name) + else if _TESTDOC2(GetSetDescr) + _ADDDOC(GetSetDescr, new->d_getset->doc, new->d_getset->name) + else { + PyErr_SetString(PyExc_TypeError, + "Cannot set a docstring for that object"); + return NULL; + } + +#undef _TESTDOC1 +#undef _TESTDOC2 +#undef _ADDDOC + + Py_INCREF(str); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef methods[] = { + {"_insert", (PyCFunction)arr_insert, METH_VARARGS | METH_KEYWORDS, + arr_insert__doc__}, + {"bincount", (PyCFunction)arr_bincount, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"digitize", (PyCFunction)arr_digitize, METH_VARARGS | METH_KEYWORDS, + NULL}, + {"add_docstring", (PyCFunction)arr_add_docstring, METH_VARARGS, + NULL}, + {NULL, NULL} /* sentinel */ +}; + +static void +define_types(void) +{ + PyObject *tp_dict; + PyObject *myobj; + + tp_dict = PyArrayDescr_Type.tp_dict; + /* Get "subdescr" */ + myobj = PyDict_GetItemString(tp_dict, "fields"); + if (myobj == NULL) return; + PyGetSetDescr_TypePtr = myobj->ob_type; + myobj = PyDict_GetItemString(tp_dict, "alignment"); + if (myobj == NULL) return; + PyMemberDescr_TypePtr = myobj->ob_type; + return; +} + +/* Initialization function for the module (*must* be called initArray) */ + +DL_EXPORT(void) init_compiled_base(void) { + PyObject *m, *d, *s; + + /* Create the module and add the functions */ + m = Py_InitModule("scipy.base._compiled_base", methods); + + /* Import the array and ufunc objects */ + import_array(); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + s = PyString_FromString("0.5"); + PyDict_SetItemString(d, "__version__", s); + Py_DECREF(s); + + ErrorObject = PyString_FromString("scipy.base._compiled_base.error"); + PyDict_SetItemString(d, "error", ErrorObject); + Py_DECREF(ErrorObject); + + + /* define PyGetSetDescr_Type and PyMemberDescr_Type */ + define_types(); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module _compiled_base"); +} diff --git a/numpy/core/src/_isnan.c b/numpy/core/src/_isnan.c new file mode 100644 index 000000000..f5965fbc7 --- /dev/null +++ b/numpy/core/src/_isnan.c @@ -0,0 +1,47 @@ +/* Adapted from cephes */ + +static int +isnan(double x) +{ + union + { + double d; + unsigned short s[4]; + unsigned int i[2]; + } u; + + u.d = x; + +#if SIZEOF_INT == 4 + +#ifdef WORDS_BIGENDIAN /* defined in pyconfig.h */ + if( ((u.i[0] & 0x7ff00000) == 0x7ff00000) + && (((u.i[0] & 0x000fffff) != 0) || (u.i[1] != 0))) + return 1; +#else + if( ((u.i[1] & 0x7ff00000) == 0x7ff00000) + && (((u.i[1] & 0x000fffff) != 0) || (u.i[0] != 0))) + return 1; +#endif + +#else /* SIZEOF_INT != 4 */ + +#ifdef WORDS_BIGENDIAN + if( (u.s[0] & 0x7ff0) == 0x7ff0) + { + if( ((u.s[0] & 0x000f) | u.s[1] | u.s[2] | u.s[3]) != 0 ) + return 1; + } +#else + if( (u.s[3] & 0x7ff0) == 0x7ff0) + { + if( ((u.s[3] & 0x000f) | u.s[2] | u.s[1] | u.s[0]) != 0 ) + return 1; + } +#endif + +#endif /* SIZEOF_INT */ + + return 0; +} + diff --git a/numpy/core/src/_signbit.c b/numpy/core/src/_signbit.c new file mode 100644 index 000000000..d128cb1fb --- /dev/null +++ b/numpy/core/src/_signbit.c @@ -0,0 +1,32 @@ +/* Adapted from cephes */ + +static int +signbit(double x) +{ + union + { + double d; + short s[4]; + int i[2]; + } u; + + u.d = x; + +#if SIZEOF_INT == 4 + +#ifdef WORDS_BIGENDIAN /* defined in pyconfig.h */ + return u.i[0] < 0; +#else + return u.i[1] < 0; +#endif + +#else /* SIZEOF_INT != 4 */ + +#ifdef WORDS_BIGENDIAN + return u.s[0] < 0; +#else + return u.s[3] < 0; +#endif + +#endif /* SIZEOF_INT */ +} diff --git a/numpy/core/src/_sortmodule.c.src b/numpy/core/src/_sortmodule.c.src new file mode 100644 index 000000000..47c7520c1 --- /dev/null +++ b/numpy/core/src/_sortmodule.c.src @@ -0,0 +1,482 @@ +/* The purpose of this module is to add faster sort functions + that are type-specific. This is done by altering the + function table for the builtin descriptors. + + These sorting functions are copied almost directly from numarray + with a few modifications (complex comparisons compare the imaginary + part if the real parts are equal, for example), and the names + are changed. + + The original sorting code is due to Charles R. Harris who wrote + it for numarray. +*/ + +/* Quick sort is usually the fastest, but the worst case scenario can + be slower than the merge and heap sorts. The merge sort requires + extra memory and so for large arrays may not be useful. + + The merge sort is *stable*, meaning that equal components + are unmoved from their entry versions, so it can be used to + implement lexigraphic sorting on multiple keys. + + The heap sort is included for completeness. +*/ + + +#include "Python.h" +#include "scipy/arrayobject.h" + +#define PYA_QS_STACK 100 +#define SMALL_QUICKSORT 15 +#define SMALL_MERGESORT 20 +#define STDC_LT(a,b) ((a) < (b)) +#define STDC_LE(a,b) ((a) <= (b)) +#define STDC_EQ(a,b) ((a) == (b)) +#define SWAP(a,b) {SWAP_temp = (b); (b)=(a); (a) = SWAP_temp;} +#define NUMC_LT(p,q) ((((p).real==(q).real) ? ((p).imag < (q).imag): ((p).real < (q).real))) +#define NUMC_LE(p,q) ((((p).real==(q).real) ? ((p).imag <= (q).imag): ((p).real <= (q).real))) +#define NUMC_EQ(p,q) (((p).real==(q).real) && ((p).imag == (q).imag)) + +/**begin repeat +#TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE# +#type=Bool,byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,cfloat,cdouble,clongdouble# +#lessthan=STDC_LT*14,NUMC_LT*3# +#lessequal=STDC_LE*14,NUMC_LE*3# + **/ +static int +@TYPE@_quicksort(@type@ *start, intp num, void *unused) +{ + @type@ *pl = start; + @type@ *pr = start + num - 1; + @type@ vp, SWAP_temp; + @type@ *stack[PYA_QS_STACK], **sptr = stack, *pm, *pi, *pj, *pt; + + for(;;) { + while ((pr - pl) > SMALL_QUICKSORT) { + /* quicksort partition */ + pm = pl + ((pr - pl) >> 1); + if (@lessthan@(*pm,*pl)) SWAP(*pm,*pl); + if (@lessthan@(*pr,*pm)) SWAP(*pr,*pm); + if (@lessthan@(*pm,*pl)) SWAP(*pm,*pl); + vp = *pm; + pi = pl; + pj = pr - 1; + SWAP(*pm,*pj); + for(;;) { + do ++pi; while (@lessthan@(*pi,vp)); + do --pj; while (@lessthan@(vp,*pj)); + if (pi >= pj) break; + SWAP(*pi,*pj); + } + SWAP(*pi,*(pr-1)); + /* push largest partition on stack */ + if (pi - pl < pr - pi) { + *sptr++ = pi + 1; + *sptr++ = pr; + pr = pi - 1; + }else{ + *sptr++ = pl; + *sptr++ = pi - 1; + pl = pi + 1; + } + } + /* insertion sort */ + for(pi = pl + 1; pi <= pr; ++pi) { + vp = *pi; + for(pj = pi, pt = pi - 1; \ + pj > pl && @lessthan@(vp, *pt);) { + *pj-- = *pt--; + } + *pj = vp; + } + if (sptr == stack) break; + pr = *(--sptr); + pl = *(--sptr); + } + return 0; +} + +static int +@TYPE@_aquicksort(@type@ *v, intp* tosort, intp num, void *unused) +{ + @type@ vp; + intp *pl, *pr, SWAP_temp; + intp *stack[PYA_QS_STACK], **sptr=stack, *pm, *pi, *pj, *pt, vi; + + pl = tosort; + pr = tosort + num - 1; + + for(;;) { + while ((pr - pl) > SMALL_QUICKSORT) { + /* quicksort partition */ + pm = pl + ((pr - pl) >> 1); + if (@lessthan@(v[*pm],v[*pl])) SWAP(*pm,*pl); + if (@lessthan@(v[*pr],v[*pm])) SWAP(*pr,*pm); + if (@lessthan@(v[*pm],v[*pl])) SWAP(*pm,*pl); + vp = v[*pm]; + pi = pl; + pj = pr - 1; + SWAP(*pm,*pj); + for(;;) { + do ++pi; while (@lessthan@(v[*pi],vp)); + do --pj; while (@lessthan@(vp,v[*pj])); + if (pi >= pj) break; + SWAP(*pi,*pj); + } + SWAP(*pi,*(pr-1)); + /* push largest partition on stack */ + if (pi - pl < pr - pi) { + *sptr++ = pi + 1; + *sptr++ = pr; + pr = pi - 1; + }else{ + *sptr++ = pl; + *sptr++ = pi - 1; + pl = pi + 1; + } + } + /* insertion sort */ + for(pi = pl + 1; pi <= pr; ++pi) { + vi = *pi; + vp = v[vi]; + for(pj = pi, pt = pi - 1; \ + pj > pl && @lessthan@(vp, v[*pt]);) + { + *pj-- = *pt--; + } + *pj = vi; + } + if (sptr == stack) break; + pr = *(--sptr); + pl = *(--sptr); + } + return 0; +} + + +static int +@TYPE@_heapsort(@type@ *start, intp n, void *unused) +{ + + @type@ tmp, *a; + intp i,j,l; + + /* The array needs to be offset by one for heapsort indexing */ + a = start - 1; + + for (l = n>>1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l<<1; j <= n;) { + if (j < n && @lessthan@(a[j], a[j+1])) + j += 1; + if (@lessthan@(tmp, a[j])) { + a[i] = a[j]; + i = j; + j += j; + }else + break; + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && @lessthan@(a[j], a[j+1])) + j++; + if (@lessthan@(tmp, a[j])) { + a[i] = a[j]; + i = j; + j += j; + }else + break; + } + a[i] = tmp; + } + return 0; +} + +static int +@TYPE@_aheapsort(@type@ *v, intp *tosort, intp n, void *unused) +{ + intp *a, i,j,l, tmp; + /* The arrays need to be offset by one for heapsort indexing */ + a = tosort - 1; + + for (l = n>>1; l > 0; --l) { + tmp = a[l]; + for (i = l, j = l<<1; j <= n;) { + if (j < n && @lessthan@(v[a[j]], v[a[j+1]])) + j += 1; + if (@lessthan@(v[tmp], v[a[j]])) { + a[i] = a[j]; + i = j; + j += j; + }else + break; + } + a[i] = tmp; + } + + for (; n > 1;) { + tmp = a[n]; + a[n] = a[1]; + n -= 1; + for (i = 1, j = 2; j <= n;) { + if (j < n && @lessthan@(v[a[j]], v[a[j+1]])) + j++; + if (@lessthan@(v[tmp], v[a[j]])) { + a[i] = a[j]; + i = j; + j += j; + }else + break; + } + a[i] = tmp; + } + + return 0; +} + +static void +@TYPE@_mergesort0(@type@ *pl, @type@ *pr, @type@ *pw) +{ + @type@ vp, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl + 1)>>1); + @TYPE@_mergesort0(pl,pm-1,pw); + @TYPE@_mergesort0(pm,pr,pw); + for(pi = pw, pj = pl; pj < pm; ++pi, ++pj) { + *pi = *pj; + } + for(pk = pw, pm = pl; pk < pi && pj <= pr; ++pm) { + if (@lessequal@(*pk,*pj)) { + *pm = *pk; + ++pk; + }else{ + *pm = *pj; + ++pj; + } + } + for(; pk < pi; ++pm, ++pk) { + *pm = *pk; + } + }else{ + /* insertion sort */ + for(pi = pl + 1; pi <= pr; ++pi) { + vp = *pi; + for(pj = pi, pk = pi - 1;\ + pj > pl && @lessthan@(vp, *pk); --pj, --pk) { + *pj = *pk; + } + *pj = vp; + } + } +} + +static int +@TYPE@_mergesort(@type@ *start, intp num, void *unused) +{ + @type@ *pl, *pr, *pw; + + pl = start; pr = pl + num - 1; + pw = (@type@ *) PyDataMem_NEW(((1+num/2))*sizeof(@type@)); + + if (!pw) { + PyErr_NoMemory(); + return -1; + } + + @TYPE@_mergesort0(pl, pr, pw); + PyDataMem_FREE(pw); + return 0; +} + +static void +@TYPE@_amergesort0(intp *pl, intp *pr, @type@ *v, intp *pw) +{ + @type@ vp; + intp vi, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl + 1)>>1); + @TYPE@_amergesort0(pl,pm-1,v,pw); + @TYPE@_amergesort0(pm,pr,v,pw); + for(pi = pw, pj = pl; pj < pm; ++pi, ++pj) { + *pi = *pj; + } + for(pk = pw, pm = pl; pk < pi && pj <= pr; ++pm) { + if (@lessequal@(v[*pk],v[*pj])) { + *pm = *pk; + ++pk; + }else{ + *pm = *pj; + ++pj; + } + } + for(; pk < pi; ++pm, ++pk) { + *pm = *pk; + } + }else{ + /* insertion sort */ + for(pi = pl + 1; pi <= pr; ++pi) { + vi = *pi; + vp = v[vi]; + for(pj = pi, pk = pi - 1; \ + pj > pl && @lessthan@(vp, v[*pk]); --pj, --pk) { + *pj = *pk; + } + *pj = vi; + } + } +} + +static int +@TYPE@_amergesort(@type@ *v, intp *tosort, intp num, void *unused) +{ + intp *pl, *pr, *pw; + + pl = tosort; pr = pl + num - 1; + pw = PyDimMem_NEW((1+num/2)); + + if (!pw) { + PyErr_NoMemory(); + return -1; + } + + @TYPE@_amergesort0(pl, pr, v, pw); + PyDimMem_FREE(pw); + return 0; +} +/**end repeat**/ + +static int +unincmp(Py_UNICODE *s1, Py_UNICODE *s2, register int len) +{ + register Py_UNICODE c1, c2; + while(len-- > 0) { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + return 0; +} + +/**begin repeat +#TYPE=STRING,UNICODE# +#comp=strncmp,unincmp# +#type=char *, Py_UNICODE *# +*/ +static void +@TYPE@_amergesort0(intp *pl, intp *pr, @type@*v, intp *pw, int elsize) +{ + @type@ vp; + intp vi, *pi, *pj, *pk, *pm; + + if (pr - pl > SMALL_MERGESORT) { + /* merge sort */ + pm = pl + ((pr - pl + 1)>>1); + @TYPE@_amergesort0(pl,pm-1,v,pw,elsize); + @TYPE@_amergesort0(pm,pr,v,pw,elsize); + for(pi = pw, pj = pl; pj < pm; ++pi, ++pj) { + *pi = *pj; + } + for(pk = pw, pm = pl; pk < pi && pj <= pr; ++pm) { + if (@comp@(v[*pk],v[*pj],elsize)<=0) { + *pm = *pk; + ++pk; + }else{ + *pm = *pj; + ++pj; + } + } + for(; pk < pi; ++pm, ++pk) { + *pm = *pk; + } + }else{ + /* insertion sort */ + for(pi = pl + 1; pi <= pr; ++pi) { + vi = *pi; + vp = v[vi]; + for(pj = pi, pk = pi - 1; \ + pj > pl && (@comp@(vp, v[*pk],elsize)<=0); \ + --pj, --pk) { + *pj = *pk; + } + *pj = vi; + } + } +} + +static int +@TYPE@_amergesort(@type@*v, intp *tosort, intp num, PyArrayObject *arr) +{ + intp *pl, *pr, *pw; + int elsize; + + elsize = arr->descr->elsize; + + pl = tosort; pr = pl + num - 1; + pw = PyDimMem_NEW((1+num/2)); + + if (!pw) { + PyErr_NoMemory(); + return -1; + } + + @TYPE@_amergesort0(pl, pr, v, pw, elsize); + PyDimMem_FREE(pw); + return 0; +} +/**end repeat**/ + +static void +add_sortfuncs(void) +{ + PyArray_Descr *descr; + +/**begin repeat +#TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE# +**/ + descr = PyArray_DescrFromType(PyArray_@TYPE@); + descr->f->sort[PyArray_QUICKSORT] = \ + (PyArray_SortFunc *)@TYPE@_quicksort; + descr->f->argsort[PyArray_QUICKSORT] = \ + (PyArray_ArgSortFunc *)@TYPE@_aquicksort; + descr->f->sort[PyArray_HEAPSORT] = \ + (PyArray_SortFunc *)@TYPE@_heapsort; + descr->f->argsort[PyArray_HEAPSORT] = \ + (PyArray_ArgSortFunc *)@TYPE@_aheapsort; + descr->f->sort[PyArray_MERGESORT] = \ + (PyArray_SortFunc *)@TYPE@_mergesort; + descr->f->argsort[PyArray_MERGESORT] = \ + (PyArray_ArgSortFunc *)@TYPE@_amergesort; +/**end repeat**/ + + descr = PyArray_DescrFromType(PyArray_STRING); + descr->f->argsort[PyArray_MERGESORT] = \ + (PyArray_ArgSortFunc *)STRING_amergesort; + descr = PyArray_DescrFromType(PyArray_UNICODE); + descr->f->argsort[PyArray_MERGESORT] = \ + (PyArray_ArgSortFunc *)UNICODE_amergesort; +} + +static struct PyMethodDef methods[] = { + {NULL, NULL, 0} +}; + +PyMODINIT_FUNC +init_sort(void) { + PyObject *m; + + m = Py_InitModule("_sort", methods); + + if (import_array() < 0) return; + add_sortfuncs(); +} diff --git a/numpy/core/src/arraymethods.c b/numpy/core/src/arraymethods.c new file mode 100644 index 000000000..2b7a53042 --- /dev/null +++ b/numpy/core/src/arraymethods.c @@ -0,0 +1,1647 @@ + +/* Should only be used if x is known to be an nd-array */ +#define _ARET(x) PyArray_Return((PyArrayObject *)(x)) + +static char doc_take[] = "a.take(indices, axis=None). Selects the elements "\ + "in indices from array a along the given axis."; + +static PyObject * +array_take(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int dimension=MAX_DIMS; + PyObject *indices; + static char *kwlist[] = {"indices", "axis", NULL}; + + dimension=0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&", kwlist, + &indices, PyArray_AxisConverter, + &dimension)) + return NULL; + + return _ARET(PyArray_Take(self, indices, dimension)); +} + +static char doc_fill[] = "a.fill(value) places the scalar value at every"\ + "position in the array."; + +static PyObject * +array_fill(PyArrayObject *self, PyObject *args) +{ + PyObject *obj; + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + if (PyArray_FillWithScalar(self, obj) < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_put[] = "a.put(values, indices) sets a.flat[n] = v[n] "\ + "for each n in indices. v can be scalar or shorter than indices, "\ + "will repeat."; + +static PyObject * +array_put(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *indices, *values; + static char *kwlist[] = {"values", "indices", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &values, &indices)) + return NULL; + return PyArray_Put(self, values, indices); +} + +static char doc_putmask[] = "a.putmask(values, mask) sets a.flat[n] = v[n] "\ + "for each n where mask.flat[n] is TRUE. v can be scalar."; + +static PyObject * +array_putmask(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *mask, *values; + + static char *kwlist[] = {"values", "mask", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &values, &mask)) + return NULL; + return PyArray_PutMask(self, values, mask); +} + +/* Used to reshape a Fortran Array */ +static void +_reverse_shape(PyArray_Dims *newshape) +{ + int i, n = newshape->len; + intp *ptr = newshape->ptr; + intp *eptr; + intp tmp; + int len = n >> 1; + + eptr = ptr+n-1; + for(i=0; i<len; i++) { + tmp = *eptr; + *eptr-- = *ptr; + *ptr++ = tmp; + } +} + +static char doc_reshape[] = \ + "self.reshape(d1, d2, ..., dn) Return a new array from this one. \n" \ + "\n The new array must have the same number of elements as self. "\ + "Also\n a copy of the data only occurs if necessary."; + +static PyObject * +array_reshape(PyArrayObject *self, PyObject *args) +{ + PyArray_Dims newshape; + PyObject *ret, *tmp; + int n; + + n = PyTuple_Size(args); + if (n <= 1) { + if (!PyArg_ParseTuple(args, "O&", PyArray_IntpConverter, + &newshape)) return NULL; + } + else { + if (!PyArray_IntpConverter(args, &newshape)) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "invalid shape"); + } + goto fail; + } + } + + if (newshape.len == 1) { + PyDimMem_FREE(newshape.ptr); + return PyArray_Ravel(self, 0); + } + + if ((newshape.len == 0) || PyArray_ISCONTIGUOUS(self)) { + ret = PyArray_Newshape(self, &newshape); + } + else if PyArray_ISFORTRAN(self) { + tmp = PyArray_Transpose(self, NULL); + if (tmp == NULL) goto fail; + _reverse_shape(&newshape); + ret = PyArray_Newshape((PyArrayObject *)tmp, &newshape); + Py_DECREF(tmp); + if (ret == NULL) goto fail; + tmp = PyArray_Transpose((PyArrayObject *)ret, NULL); + Py_DECREF(ret); + if (tmp == NULL) goto fail; + ret = tmp; + } + else { + tmp = PyArray_Copy(self); + if (tmp==NULL) goto fail; + ret = PyArray_Newshape((PyArrayObject *)tmp, &newshape); + Py_DECREF(tmp); + } + PyDimMem_FREE(newshape.ptr); + return _ARET(ret); + + fail: + PyDimMem_FREE(newshape.ptr); + return NULL; +} + +static char doc_squeeze[] = "m.squeeze() eliminate all length-1 dimensions"; + +static PyObject * +array_squeeze(PyArrayObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + return _ARET(PyArray_Squeeze(self)); +} + + + +static char doc_view[] = "a.view(<dtype>) return a new view of array with same data."; + +static PyObject * +array_view(PyArrayObject *self, PyObject *args) +{ + PyArray_Descr *type=NULL; + if (!PyArg_ParseTuple(args, "|O&", + PyArray_DescrConverter, &type)) + return NULL; + + return _ARET(PyArray_View(self, type)); +} + +static char doc_argmax[] = "a.argmax(axis=None)"; + +static PyObject * +array_argmax(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return _ARET(PyArray_ArgMax(self, axis)); +} + +static char doc_argmin[] = "a.argmin(axis=None)"; + +static PyObject * +array_argmin(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return _ARET(PyArray_ArgMin(self, axis)); +} + +static char doc_max[] = "a.max(axis=None)"; + +static PyObject * +array_max(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return PyArray_Max(self, axis); +} + +static char doc_ptp[] = "a.ptp(axis=None) a.max(axis)-a.min(axis)"; + +static PyObject * +array_ptp(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return PyArray_Ptp(self, axis); +} + + +static char doc_min[] = "a.min(axis=None)"; + +static PyObject * +array_min(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return PyArray_Min(self, axis); +} + + +static char doc_swapaxes[] = "a.swapaxes(axis1, axis2) returns new view with axes swapped."; + +static PyObject * +array_swapaxes(PyArrayObject *self, PyObject *args) +{ + int axis1, axis2; + + if (!PyArg_ParseTuple(args, "ii", &axis1, &axis2)) return NULL; + + return PyArray_SwapAxes(self, axis1, axis2); +} + +static char doc_getfield[] = "m.getfield(dtype, offset) returns a field "\ + " of the given array as a certain type. A field is a view of "\ + " the array's data with each itemsize determined by the given type"\ + " and the offset into the current array."; + +/* steals typed reference */ +/*OBJECT_API + Get a subset of bytes from each element of the array +*/ +static PyObject * +PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset) +{ + PyObject *ret=NULL; + + if (offset < 0 || (offset + typed->elsize) > self->descr->elsize) { + PyErr_Format(PyExc_ValueError, + "Need 0 <= offset <= %d for requested type " \ + "but received offset = %d", + self->descr->elsize-typed->elsize, offset); + Py_DECREF(typed); + return NULL; + } + ret = PyArray_NewFromDescr(self->ob_type, + typed, + self->nd, self->dimensions, + self->strides, + self->data + offset, + self->flags, (PyObject *)self); + if (ret == NULL) return NULL; + Py_INCREF(self); + ((PyArrayObject *)ret)->base = (PyObject *)self; + + PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL_FLAGS); + return ret; +} + +static PyObject * +array_getfield(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + + PyArray_Descr *dtype; + int offset = 0; + static char *kwlist[] = {"dtype", "offset", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|i", kwlist, + PyArray_DescrConverter, + &dtype, &offset)) return NULL; + + return _ARET(PyArray_GetField(self, dtype, offset)); +} + + +static char doc_setfield[] = "m.setfield(value, dtype, offset) places val "\ + "into field of the given array defined by the data type and offset."; + +/*OBJECT_API + Set a subset of bytes from each element of the array +*/ +static int +PyArray_SetField(PyArrayObject *self, PyArray_Descr *dtype, + int offset, PyObject *val) +{ + PyObject *ret=NULL; + int retval = 0; + + if (offset < 0 || (offset + dtype->elsize) > self->descr->elsize) { + PyErr_Format(PyExc_ValueError, + "Need 0 <= offset <= %d for requested type " \ + "but received offset = %d", + self->descr->elsize-dtype->elsize, offset); + Py_DECREF(dtype); + return -1; + } + ret = PyArray_NewFromDescr(self->ob_type, + dtype, self->nd, self->dimensions, + self->strides, self->data + offset, + self->flags, (PyObject *)self); + if (ret == NULL) return -1; + Py_INCREF(self); + ((PyArrayObject *)ret)->base = (PyObject *)self; + + PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL_FLAGS); + retval = PyArray_CopyObject((PyArrayObject *)ret, val); + Py_DECREF(ret); + return retval; +} + +static PyObject * +array_setfield(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + PyArray_Descr *dtype; + int offset = 0; + PyObject *value; + static char *kwlist[] = {"value", "dtype", "offset", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|i", kwlist, + &value, PyArray_DescrConverter, + &dtype, &offset)) return NULL; + + if (PyArray_SetField(self, dtype, offset, value) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +/* This doesn't change the descriptor just the actual data... + */ + +/*OBJECT_API*/ +static PyObject * +PyArray_Byteswap(PyArrayObject *self, Bool inplace) +{ + PyArrayObject *ret; + intp size; + PyArray_CopySwapNFunc *copyswapn; + PyArray_CopySwapFunc *copyswap; + PyArrayIterObject *it; + + if (inplace) { + copyswapn = self->descr->f->copyswapn; + + size = PyArray_SIZE(self); + if (PyArray_ISONESEGMENT(self)) { + copyswapn(self->data, NULL, size, 1, + self->descr->elsize); + } + else { /* Use iterator */ + + it = (PyArrayIterObject *)\ + PyArray_IterNew((PyObject *)self); + copyswap = self->descr->f->copyswap; + while (it->index < it->size) { + copyswap(it->dataptr, NULL, 1, + self->descr->elsize); + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + } + + Py_INCREF(self); + return (PyObject *)self; + } + else { + if ((ret = (PyArrayObject *)PyArray_NewCopy(self,-1)) == NULL) + return NULL; + + size = PyArray_SIZE(self); + + /* now ret has the same dtypedescr as self (including + byteorder) + */ + + ret->descr->f->copyswapn(ret->data, NULL, size, 1, + ret->descr->elsize); + + return (PyObject *)ret; + } +} + +static char doc_byteswap[] = "m.byteswap(False) Swap the bytes in"\ + " the array. Return the byteswapped array. If the first argument"\ + " is TRUE, byteswap in-place and return a reference to self."; + +static PyObject * +array_byteswap(PyArrayObject *self, PyObject *args) +{ + Bool inplace=FALSE; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_BoolConverter, &inplace)) + return NULL; + + return PyArray_Byteswap(self, inplace); +} + +static char doc_tolist[] = "m.tolist(). Copy the data portion of the array"\ + " to a hierarchical python list and return that list."; + +static PyObject * +array_tolist(PyArrayObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + if (self->nd <= 0) { + PyErr_SetString(PyExc_ValueError, + "can't convert a 0-d array to a list"); + return NULL; + } + + return PyArray_ToList(self); +} + +static char doc_tostring[] = "m.tostring() Construct a Python string "\ + "containing the raw bytes in the array"; + +static PyObject * +array_tostring(PyArrayObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + return PyArray_ToString(self); +} + +static char doc_tofile[] = "m.tofile(fid, sep="") write the data to a file."; + +static PyObject * +array_tofile(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int ret; + PyObject *file; + FILE *fd; + char *sep=""; + char *format=""; + char *mode=""; + static char *kwlist[] = {"file", "sep", "format", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ss", kwlist, + &file, &sep, &format)) return NULL; + + if (PyString_Check(file)) { + if (sep == "") mode="wb"; + else mode="w"; + file = PyFile_FromString(PyString_AS_STRING(file), mode); + if (file==NULL) return NULL; + } + else { + Py_INCREF(file); + } + fd = PyFile_AsFile(file); + if (fd == NULL) { + PyErr_SetString(PyExc_IOError, "first argument must be a " \ + "string or open file"); + Py_DECREF(file); + return NULL; + } + ret = PyArray_ToFile(self, fd, sep, format); + Py_DECREF(file); + if (ret < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_toscalar[] = "m.item(). Copy the first data point of "\ + "the array to a standard Python scalar and return it."; + +static PyObject * +array_toscalar(PyArrayObject *self, PyObject *args) { + if (!PyArg_ParseTuple(args, "")) return NULL; + if (self->nd == 0 || PyArray_SIZE(self) == 1) + return self->descr->f->getitem(self->data, self); + else { + PyErr_SetString(PyExc_ValueError, "can only convert an" \ + " array of size 1 to Python scalar."); + return NULL; + } +} + +static char doc_cast[] = "m.astype(t). Cast array m to type t. \n\n"\ + "t can be either a string representing a typecode, or a python type"\ + " object of type int, float, or complex."; + +static PyObject * +array_cast(PyArrayObject *self, PyObject *args) +{ + PyArray_Descr *descr=NULL; + PyObject *obj; + + if (!PyArg_ParseTuple(args, "O&", PyArray_DescrConverter, + &descr)) return NULL; + + if (descr == self->descr) { + obj = _ARET(PyArray_NewCopy(self,0)); + Py_XDECREF(descr); + return obj; + } + return _ARET(PyArray_CastToType(self, descr, 0)); +} + +/* default sub-type implementation */ + +static char doc_wraparray[] = "m.__array_wrap__(obj) returns an object of "\ + "type m from the ndarray object obj"; + +static PyObject * +array_wraparray(PyArrayObject *self, PyObject *args) +{ + PyObject *arr; + PyObject *ret; + + if (PyTuple_Size(args) < 1) { + PyErr_SetString(PyExc_TypeError, + "only accepts 1 argument"); + return NULL; + } + arr = PyTuple_GET_ITEM(args, 0); + if (!PyArray_Check(arr)) { + PyErr_SetString(PyExc_TypeError, + "can only be called with ndarray object"); + return NULL; + } + + Py_INCREF(PyArray_DESCR(arr)); + ret = PyArray_NewFromDescr(self->ob_type, + PyArray_DESCR(arr), + PyArray_NDIM(arr), + PyArray_DIMS(arr), + PyArray_STRIDES(arr), PyArray_DATA(arr), + PyArray_FLAGS(arr), (PyObject *)self); + if (ret == NULL) return NULL; + Py_INCREF(arr); + PyArray_BASE(ret) = arr; + return ret; +} + +/* NO-OP --- just so all subclasses will have one by default. */ +static PyObject * +array_finalize(PyArrayObject *self, PyObject *args) +{ + Py_INCREF(Py_None); + return Py_None; +} + + +static char doc_array_getarray[] = "m.__array__(|dtype) just returns either a new reference to self if dtype is not given or a new array of provided data type if dtype is different from the current dtype of the array."; + +static PyObject * +array_getarray(PyArrayObject *self, PyObject *args) +{ + PyArray_Descr *newtype=NULL; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_DescrConverter, + &newtype)) return NULL; + + /* convert to PyArray_Type or PyBigArray_Type */ + if (!PyArray_CheckExact(self) || !PyBigArray_CheckExact(self)) { + PyObject *new; + PyTypeObject *subtype = &PyArray_Type; + + if (!PyType_IsSubtype(self->ob_type, &PyArray_Type)) { + subtype = &PyBigArray_Type; + } + + Py_INCREF(PyArray_DESCR(self)); + new = PyArray_NewFromDescr(subtype, + PyArray_DESCR(self), + PyArray_NDIM(self), + PyArray_DIMS(self), + PyArray_STRIDES(self), + PyArray_DATA(self), + PyArray_FLAGS(self), NULL); + if (new == NULL) return NULL; + Py_INCREF(self); + PyArray_BASE(new) = (PyObject *)self; + self = (PyArrayObject *)new; + } + else { + Py_INCREF(self); + } + + if ((newtype == NULL) || \ + PyArray_EquivTypes(self->descr, newtype)) { + return (PyObject *)self; + } + else { + ret = PyArray_CastToType(self, newtype, 0); + Py_DECREF(self); + return ret; + } +} + +static char doc_copy[] = "m.copy(|fortran). Return a copy of the array.\n"\ + "If fortran == 0 then the result is contiguous (default). \n"\ + "If fortran > 0 then the result has fortran data order. \n"\ + "If fortran < 0 then the result has fortran data order only if m\n" + " is already in fortran order."; + +static PyObject * +array_copy(PyArrayObject *self, PyObject *args) +{ + int fortran=0; + if (!PyArg_ParseTuple(args, "|i", &fortran)) return NULL; + + return _ARET(PyArray_NewCopy(self, fortran)); +} + +static char doc_resize[] = "self.resize(new_shape). "\ + "Change size and shape of self inplace.\n"\ + "\n Array must own its own memory and not be referenced by other " \ + "arrays\n Returns None."; + +static PyObject * +array_resize(PyArrayObject *self, PyObject *args) +{ + PyArray_Dims newshape; + PyObject *ret; + int n; + + n = PyTuple_Size(args); + if (n <= 1) { + if (!PyArg_ParseTuple(args, "O&", PyArray_IntpConverter, + &newshape)) return NULL; + } + else { + if (!PyArray_IntpConverter(args, &newshape)) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "invalid shape"); + } + return NULL; + } + } + ret = PyArray_Resize(self, &newshape); + PyDimMem_FREE(newshape.ptr); + if (ret == NULL) return NULL; + Py_DECREF(ret); + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_repeat[] = "a.repeat(repeats=, axis=None)\n"\ + "\n"\ + " Copy elements of a, repeats times. The repeats argument must\n"\ + " be a sequence of length a.shape[axis] or a scalar."; + +static PyObject * +array_repeat(PyArrayObject *self, PyObject *args, PyObject *kwds) { + PyObject *repeats; + int axis=MAX_DIMS; + static char *kwlist[] = {"repeats", "axis", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&", kwlist, + &repeats, PyArray_AxisConverter, + &axis)) return NULL; + + return _ARET(PyArray_Repeat(self, repeats, axis)); +} + +static char doc_choose[] = "a.choose(b0, b1, ..., bn)\n"\ + "\n"\ + "Return an array with elements chosen from 'a' at the positions\n"\ + "of the given arrays b_i. The array 'a' should be an integer array\n"\ + "with entries from 0 to n+1, and the b_i arrays should have the same\n"\ + "shape as 'a'."; + +static PyObject * +array_choose(PyArrayObject *self, PyObject *args) +{ + PyObject *choices; + int n; + + n = PyTuple_Size(args); + if (n <= 1) { + if (!PyArg_ParseTuple(args, "O", &choices)) + return NULL; + } + else { + choices = args; + } + + return _ARET(PyArray_Choose(self, choices)); +} + +static char doc_sort[] = "a.sort(axis=-1,kind='quicksort') sorts in place along axis. Return is None and kind can be 'quicksort', 'mergesort', or 'heapsort'"; + +static PyObject * +array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=-1; + int val; + PyArray_SORTKIND which=PyArray_QUICKSORT; + static char *kwlist[] = {"axis", "kind", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO&", kwlist, &axis, + PyArray_SortkindConverter, &which)) + return NULL; + + val = PyArray_Sort(self, axis, which); + if (val < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_argsort[] = "a.argsort(axis=-1,kind='quicksort')\n"\ + " Return the indexes into a that would sort it along the"\ + " given axis; kind can be 'quicksort', 'mergesort', or 'heapsort'"; + +static PyObject * +array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=-1; + PyArray_SORTKIND which=PyArray_QUICKSORT; + static char *kwlist[] = {"axis", "kind", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO&", kwlist, &axis, + PyArray_SortkindConverter, &which)) + return NULL; + + return _ARET(PyArray_ArgSort(self, axis, which)); +} + +static char doc_searchsorted[] = "a.searchsorted(v)\n"\ + " Assuming that a is a 1-D array, in ascending order and\n"\ + " represents bin boundaries, then a.searchsorted(values) gives an\n"\ + " array of bin numbers, giving the bin into which each value would\n"\ + " be placed. This method is helpful for histograming. \n"\ + " Note: No warning is given if the boundaries, in a, are not \n"\ + " in ascending order."; +; + +static PyObject * +array_searchsorted(PyArrayObject *self, PyObject *args) +{ + PyObject *values; + + if (!PyArg_ParseTuple(args, "O", &values)) return NULL; + + return _ARET(PyArray_SearchSorted(self, values)); +} + +static char doc_deepcopy[] = "Used if copy.deepcopy is called on an array."; + +static PyObject * +array_deepcopy(PyArrayObject *self, PyObject *args) +{ + PyObject* visit; + PyObject **optr; + PyArrayIterObject *it; + PyObject *copy, *ret, *deepcopy, *temp, *res; + + if (!PyArg_ParseTuple(args, "O", &visit)) return NULL; + ret = PyArray_Copy(self); + if (PyArray_ISOBJECT(self)) { + copy = PyImport_ImportModule("copy"); + if (copy == NULL) return NULL; + deepcopy = PyObject_GetAttrString(copy, "deepcopy"); + Py_DECREF(copy); + if (deepcopy == NULL) return NULL; + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it == NULL) {Py_DECREF(deepcopy); return NULL;} + optr = (PyObject **)PyArray_DATA(ret); + while(it->index < it->size) { + temp = *((PyObject **)it->dataptr); + Py_INCREF(temp); + /* call deepcopy on this argument */ + res = PyObject_CallFunctionObjArgs(deepcopy, + temp, visit, NULL); + Py_DECREF(temp); + Py_DECREF(*optr); + *optr++ = res; + PyArray_ITER_NEXT(it); + } + Py_DECREF(deepcopy); + Py_DECREF(it); + } + return _ARET(ret); +} + +/* Convert Object Array to flat list and pickle the flat list string */ +static PyObject * +_getobject_pkl(PyArrayObject *self) +{ + PyObject *theobject; + PyArrayIterObject *iter=NULL; + PyObject *list; + + + iter = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (iter == NULL) return NULL; + list = PyList_New(iter->size); + if (list == NULL) {Py_DECREF(iter); return NULL;} + while (iter->index < iter->size) { + theobject = *((PyObject **)iter->dataptr); + Py_INCREF(theobject); + PyList_SET_ITEM(list, (int) iter->index, theobject); + PyArray_ITER_NEXT(iter); + } + Py_DECREF(iter); + return list; +} + +static int +_setobject_pkl(PyArrayObject *self, PyObject *list) +{ + PyObject *theobject; + PyArrayIterObject *iter=NULL; + int size; + + size = self->descr->elsize; + + iter = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (iter == NULL) return -1; + while(iter->index < iter->size) { + theobject = PyList_GET_ITEM(list, (int) iter->index); + Py_INCREF(theobject); + *((PyObject **)iter->dataptr) = theobject; + PyArray_ITER_NEXT(iter); + } + Py_XDECREF(iter); + return 0; +} + + +static char doc_reduce[] = "a.__reduce__() for pickling."; + +static PyObject * +array_reduce(PyArrayObject *self, PyObject *args) +{ + PyObject *ret=NULL, *state=NULL, *obj=NULL, *mod=NULL; + PyObject *mybool, *thestr=NULL; + PyArray_Descr *descr; + + /* Return a tuple of (callable object, arguments, object's state) */ + /* We will put everything in the object's state, so that on UnPickle + it can use the string object as memory without a copy */ + + ret = PyTuple_New(3); + if (ret == NULL) return NULL; + mod = PyImport_ImportModule("scipy.base._internal"); + if (mod == NULL) {Py_DECREF(ret); return NULL;} + obj = PyObject_GetAttrString(mod, "_reconstruct"); + Py_DECREF(mod); + PyTuple_SET_ITEM(ret, 0, obj); + PyTuple_SET_ITEM(ret, 1, + Py_BuildValue("ONN", + (PyObject *)self->ob_type, + Py_BuildValue("(N)", + PyInt_FromLong(0)), + PyObject_GetAttrString((PyObject *)self, + "dtypechar"))); + + /* Now fill in object's state. This is a tuple with + 4 arguments + + 1) a Tuple giving the shape + 2) a PyArray_Descr Object (with correct bytorder set) + 3) a Bool stating if Fortran or not + 4) a binary string with the data (or a list for Object arrays) + + Notice because Python does not describe a mechanism to write + raw data to the pickle, this performs a copy to a string first + */ + + state = PyTuple_New(4); + if (state == NULL) { + Py_DECREF(ret); return NULL; + } + PyTuple_SET_ITEM(state, 0, PyObject_GetAttrString((PyObject *)self, + "shape")); + descr = self->descr; + Py_INCREF(descr); + PyTuple_SET_ITEM(state, 1, (PyObject *)descr); + mybool = (PyArray_ISFORTRAN(self) ? Py_True : Py_False); + Py_INCREF(mybool); + PyTuple_SET_ITEM(state, 2, mybool); + if (PyArray_ISOBJECT(self)) { + thestr = _getobject_pkl(self); + } + else { + thestr = PyArray_ToString(self); + } + if (thestr == NULL) { + Py_DECREF(ret); + Py_DECREF(state); + return NULL; + } + PyTuple_SET_ITEM(state, 3, thestr); + PyTuple_SET_ITEM(ret, 2, state); + return ret; +} + +static char doc_setstate[] = "a.__setstate__(tuple) for unpickling."; + +/* + 1) a Tuple giving the shape + 2) a PyArray_Descr Object + 3) a Bool stating if Fortran or not + 4) a binary string with the data (or a list if Object array) +*/ + +static intp _array_fill_strides(intp *, intp *, int, intp, int, int *); + +static int _IsAligned(PyArrayObject *); + +static PyArray_Descr * _array_typedescr_fromstr(char *); + +static PyObject * +array_setstate(PyArrayObject *self, PyObject *args) +{ + PyObject *shape; + PyArray_Descr *typecode; + long fortran; + PyObject *rawdata; + char *datastr; + int len; + intp dimensions[MAX_DIMS]; + int nd; + + /* This will free any memory associated with a and + use the string in setstate as the (writeable) memory. + */ + if (!PyArg_ParseTuple(args, "(O!O!iO)", &PyTuple_Type, + &shape, &PyArrayDescr_Type, &typecode, + &fortran, &rawdata)) + return NULL; + + Py_XDECREF(self->descr); + self->descr = typecode; + Py_INCREF(typecode); + nd = PyArray_IntpFromSequence(shape, dimensions, MAX_DIMS); + if (typecode->type_num == PyArray_OBJECT) { + if (!PyList_Check(rawdata)) { + PyErr_SetString(PyExc_TypeError, + "object pickle not returning list"); + return NULL; + } + } + else { + if (!PyString_Check(rawdata)) { + PyErr_SetString(PyExc_TypeError, + "pickle not returning string"); + return NULL; + } + + if (PyString_AsStringAndSize(rawdata, &datastr, &len)) + return NULL; + + if ((len != (self->descr->elsize * \ + (int) PyArray_MultiplyList(dimensions, nd)))) { + PyErr_SetString(PyExc_ValueError, + "buffer size does not" \ + " match array size"); + return NULL; + } + } + + if ((self->flags & OWN_DATA)) { + if (self->data != NULL) + PyDataMem_FREE(self->data); + self->flags &= ~OWN_DATA; + } + Py_XDECREF(self->base); + + self->flags &= ~UPDATEIFCOPY; + + if (self->dimensions != NULL) { + PyDimMem_FREE(self->dimensions); + self->dimensions = NULL; + } + + self->flags = DEFAULT_FLAGS; + + self->nd = nd; + + if (nd > 0) { + self->dimensions = PyDimMem_NEW(nd * 2); + self->strides = self->dimensions + nd; + memcpy(self->dimensions, dimensions, sizeof(intp)*nd); + (void) _array_fill_strides(self->strides, dimensions, nd, + self->descr->elsize, fortran, + &(self->flags)); + } + + if (typecode->type_num != PyArray_OBJECT) { + self->data = datastr; + if (!_IsAligned(self)) { + intp num = PyArray_NBYTES(self); + self->data = PyDataMem_NEW(num); + if (self->data == NULL) { + self->nd = 0; + PyDimMem_FREE(self->dimensions); + return PyErr_NoMemory(); + } + memcpy(self->data, datastr, num); + self->flags |= OWN_DATA; + self->base = NULL; + } + else { + self->base = rawdata; + Py_INCREF(self->base); + } + } + else { + self->data = PyDataMem_NEW(PyArray_NBYTES(self)); + if (self->data == NULL) { + self->nd = 0; + self->data = PyDataMem_NEW(self->descr->elsize); + if (self->dimensions) PyDimMem_FREE(self->dimensions); + return PyErr_NoMemory(); + } + self->flags |= OWN_DATA; + self->base = NULL; + if (_setobject_pkl(self, rawdata) < 0) + return NULL; + } + + PyArray_UpdateFlags(self, UPDATE_ALL_FLAGS); + + Py_INCREF(Py_None); + return Py_None; +} + +/*OBJECT_API*/ +static int +PyArray_Dump(PyObject *self, PyObject *file, int protocol) +{ + PyObject *cpick=NULL; + PyObject *ret; + if (protocol < 0) protocol = 2; + + cpick = PyImport_ImportModule("cPickle"); + if (cpick==NULL) return -1; + + if PyString_Check(file) { + file = PyFile_FromString(PyString_AS_STRING(file), "wb"); + if (file==NULL) return -1; + } + else Py_INCREF(file); + ret = PyObject_CallMethod(cpick, "dump", "OOi", self, + file, protocol); + Py_XDECREF(ret); + Py_DECREF(file); + Py_DECREF(cpick); + if (PyErr_Occurred()) return -1; + return 0; +} + +/*OBJECT_API*/ +static PyObject * +PyArray_Dumps(PyObject *self, int protocol) +{ + PyObject *cpick=NULL; + PyObject *ret; + if (protocol < 0) protocol = 2; + + cpick = PyImport_ImportModule("cPickle"); + if (cpick==NULL) return NULL; + ret = PyObject_CallMethod(cpick, "dumps", "Oi", self, protocol); + Py_DECREF(cpick); + return ret; +} + + +static char doc_dump[] = "m.dump(file)"; + +static PyObject * +array_dump(PyArrayObject *self, PyObject *args) +{ + PyObject *file=NULL; + int ret; + + if (!PyArg_ParseTuple(args, "O", &file)) + return NULL; + ret = PyArray_Dump((PyObject *)self, file, 2); + if (ret < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_dumps[] = "m.dumps()"; + +static PyObject * +array_dumps(PyArrayObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyArray_Dumps((PyObject *)self, 2); +} + + +static char doc_transpose[] = "m.transpose(<None>)"; + +static PyObject * +array_transpose(PyArrayObject *self, PyObject *args) +{ + PyObject *shape=Py_None; + int n; + PyArray_Dims permute; + PyObject *ret; + + n = PyTuple_Size(args); + if (n > 1) shape = args; + else if (n == 1) shape = PyTuple_GET_ITEM(args, 0); + + if (shape == Py_None) + ret = PyArray_Transpose(self, NULL); + else { + if (!PyArray_IntpConverter(shape, &permute)) return NULL; + ret = PyArray_Transpose(self, &permute); + PyDimMem_FREE(permute.ptr); + } + + return _ARET(ret); +} + +static char doc_mean[] = "a.mean(axis=None, dtype=None)\n\n"\ + "Average the array over the given axis. If the axis is None, average\n"\ + "over all dimensions of the array.\n"\ + "\n"\ + "If an integer axis is given, this equals:\n"\ + " a.sum(axis, dtype) * 1.0 / len(a)\n"\ + "\n"\ + "If axis is None, this equals:\n"\ + " a.sum(axis, dtype) * 1.0 / product(a.shape)\n"\ + "\n"\ + "The optional dtype argument is the data type for intermediate\n"\ + "calculations in the sum."; + +#define _CHKTYPENUM(typ) ((typ) ? (typ)->type_num : PyArray_NOTYPE) + +static PyObject * +array_mean(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_Mean(self, axis, _CHKTYPENUM(dtype)); +} + +static char doc_sum[] = "a.sum(axis=None, dtype=None)\n\n"\ + "Sum the array over the given axis. If the axis is None, sum over all\n"\ + "dimensions of the array.\n"\ + "\n"\ + "The optional dtype argument is the data type for the returned value\n"\ + "and intermediate calculations. The default is to upcast (promote)\n"\ + "smaller integer types to the platform-dependent int. For example, on\n"\ + "32-bit platforms:\n"\ + "\n"\ + " a.dtype default sum() dtype\n"\ + " ---------------------------------------------------\n"\ + " bool, int8, int16, int32 int32\n"\ + "\n"\ + "Examples:\n"\ + "\n"\ + ">>> array([0.5, 1.5]).sum()\n"\ + "2.0\n"\ + ">>> array([0.5, 1.5]).sum(dtype=int32)\n"\ + "1\n"\ + ">>> array([[0, 1], [0, 5]]).sum()\n"\ + "array([0, 6])\n"\ + ">>> array([[0, 1], [0, 5]]).sum(axis=1)\n"\ + "array([1, 5])"; + +static PyObject * +array_sum(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_Sum(self, axis, _CHKTYPENUM(dtype)); +} + + +static char doc_cumsum[] = "a.cumsum(axis=None, dtype=None)"; + +static PyObject * +array_cumsum(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_CumSum(self, axis, _CHKTYPENUM(dtype)); +} + +static char doc_prod[] = "a.prod(axis=None, dtype=None)"; + +static PyObject * +array_prod(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_Prod(self, axis, _CHKTYPENUM(dtype)); +} + + +static char doc_cumprod[] = "a.cumprod(axis=None, dtype=None)"; + +static PyObject * +array_cumprod(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_CumProd(self, axis, _CHKTYPENUM(dtype)); +} + + +static char doc_any[] = "a.any(axis=None)"; + +static PyObject * +array_any(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return PyArray_Any(self, axis); +} + +static char doc_all[] = "a.all(axis=None)"; + +static PyObject * +array_all(PyArrayObject *self, PyObject *args) +{ + int axis=MAX_DIMS; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_AxisConverter, + &axis)) return NULL; + + return PyArray_All(self, axis); +} + +static char doc_stddev[] = "a.std(axis=None, dtype=None)"; + +static PyObject * +array_stddev(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_Std(self, axis, _CHKTYPENUM(dtype), 0); +} + +static char doc_variance[] = "a.var(axis=None, dtype=None)"; + +static PyObject * +array_variance(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"axis", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, + PyArray_AxisConverter, + &axis, PyArray_DescrConverter2, + &dtype)) return NULL; + + return PyArray_Std(self, axis, _CHKTYPENUM(dtype), 1); +} + +static char doc_compress[] = "a.compress(condition=, axis=None)"; + +static PyObject * +array_compress(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis=MAX_DIMS; + PyObject *condition; + static char *kwlist[] = {"condition", "axis", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&", kwlist, + &condition, PyArray_AxisConverter, + &axis)) return NULL; + + return _ARET(PyArray_Compress(self, condition, axis)); +} + +static char doc_nonzero[] = "a.nonzero() return a tuple of indices referencing"\ + "the elements of a that are nonzero."; + +static PyObject * +array_nonzero(PyArrayObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + + return _ARET(PyArray_Nonzero(self)); +} + + +static char doc_trace[] = "a.trace(offset=0, axis1=0, axis2=1, dtype=None) \n"\ + "return the sum along the offset diagonal of the arrays indicated\n" \ + "axis1 and axis2."; + +static PyObject * +array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis1=0, axis2=1, offset=0; + PyArray_Descr *dtype=NULL; + static char *kwlist[] = {"offset", "axis1", "axis2", "dtype", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiO&", kwlist, + &offset, &axis1, &axis2, + PyArray_DescrConverter2, &dtype)) + return NULL; + + return _ARET(PyArray_Trace(self, offset, axis1, axis2, + _CHKTYPENUM(dtype))); +} + +#undef _CHKTYPENUM + + +static char doc_clip[] = "a.clip(min=, max=)"; + +static PyObject * +array_clip(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *min, *max; + static char *kwlist[] = {"min", "max", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &min, &max)) + return NULL; + + return _ARET(PyArray_Clip(self, min, max)); +} + +static char doc_conj[] = "a.conj()"; + +static char doc_conjugate[] = "a.conjugate()"; + +static PyObject * +array_conjugate(PyArrayObject *self, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, "")) return NULL; + + return PyArray_Conjugate(self); +} + + +static char doc_diagonal[] = "a.diagonal(offset=0, axis1=0, axis2=1)"; + +static PyObject * +array_diagonal(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + int axis1=0, axis2=1, offset=0; + static char *kwlist[] = {"offset", "axis1", "axis2", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, + &offset, &axis1, &axis2)) + return NULL; + + return _ARET(PyArray_Diagonal(self, offset, axis1, axis2)); +} + +static char doc_flatten[] = "a.flatten([fortran]) return a 1-d array (always copy)"; + +static PyObject * +array_flatten(PyArrayObject *self, PyObject *args) +{ + int fortran=0; + + if (!PyArg_ParseTuple(args, "|i", &fortran)) return NULL; + + return PyArray_Flatten(self, (int) fortran); +} + +static char doc_ravel[] = "a.ravel([fortran]) return a 1-d array (copy only if needed)"; + +static PyObject * +array_ravel(PyArrayObject *self, PyObject *args) +{ + int fortran=0; + + if (!PyArg_ParseTuple(args, "|i", &fortran)) return NULL; + + return PyArray_Ravel(self, fortran); +} + + + +static char doc_setflags[] = "a.setflags(write=None, align=None, uic=None)"; + +static int _IsAligned(PyArrayObject *); +static Bool _IsWriteable(PyArrayObject *); + +static PyObject * +array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"write", "align", "uic", NULL}; + PyObject *write=Py_None; + PyObject *align=Py_None; + PyObject *uic=Py_None; + int flagback = self->flags; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, + &write, &align, &uic)) + return NULL; + + if (align != Py_None) { + if (PyObject_Not(align)) self->flags &= ~ALIGNED; + else if (_IsAligned(self)) self->flags |= ALIGNED; + else { + PyErr_SetString(PyExc_ValueError, + "cannot set aligned flag of mis-"\ + "aligned array to True"); + return NULL; + } + } + + if (uic != Py_None) { + if (PyObject_IsTrue(uic)) { + self->flags = flagback; + PyErr_SetString(PyExc_ValueError, + "cannot set UPDATEIFCOPY" \ + "flag to True"); + return NULL; + } + else { + self->flags &= ~UPDATEIFCOPY; + Py_DECREF(self->base); + self->base = NULL; + } + } + + if (write != Py_None) { + if (PyObject_IsTrue(write)) + if (_IsWriteable(self)) { + self->flags |= WRITEABLE; + } + else { + self->flags = flagback; + PyErr_SetString(PyExc_ValueError, + "cannot set WRITEABLE " \ + "flag to True of this " \ + "array"); \ + return NULL; + } + else + self->flags &= ~WRITEABLE; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_newbyteorder[] = "a.newbyteorder(<byteorder>) is equivalent\n" \ + " to a.view(a.dtypedescr.newbytorder(<byteorder>))\n"; + +static PyObject * +array_newbyteorder(PyArrayObject *self, PyObject *args) +{ + char endian = PyArray_SWAP; + PyArray_Descr *new; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_ByteorderConverter, + &endian)) return NULL; + + new = PyArray_DescrNewByteorder(self->descr, endian); + if (!new) return NULL; + return _ARET(PyArray_View(self, new)); + +} + +static PyMethodDef array_methods[] = { + {"tolist", (PyCFunction)array_tolist, 1, doc_tolist}, + {"item", (PyCFunction)array_toscalar, METH_VARARGS, doc_toscalar}, + {"tofile", (PyCFunction)array_tofile, + METH_VARARGS | METH_KEYWORDS, doc_tofile}, + {"tostring", (PyCFunction)array_tostring, METH_VARARGS, doc_tostring}, + {"byteswap", (PyCFunction)array_byteswap, 1, doc_byteswap}, + {"astype", (PyCFunction)array_cast, 1, doc_cast}, + {"getfield", (PyCFunction)array_getfield, + METH_VARARGS | METH_KEYWORDS, doc_getfield}, + {"setfield", (PyCFunction)array_setfield, + METH_VARARGS | METH_KEYWORDS, doc_setfield}, + {"copy", (PyCFunction)array_copy, 1, doc_copy}, + {"resize", (PyCFunction)array_resize, 1, doc_resize}, + + /* for subtypes */ + {"__array__", (PyCFunction)array_getarray, 1, doc_array_getarray}, + {"__array_wrap__", (PyCFunction)array_wraparray, 1, doc_wraparray}, + /* default version so it is found... -- only used for subclasses */ + {"__array_finalize__", (PyCFunction)array_finalize, 1, NULL}, + + + /* for the copy module */ + {"__copy__", (PyCFunction)array_copy, 1, doc_copy}, + {"__deepcopy__", (PyCFunction)array_deepcopy, 1, doc_deepcopy}, + + /* for Pickling */ + {"__reduce__", (PyCFunction) array_reduce, 1, doc_reduce}, + {"__setstate__", (PyCFunction) array_setstate, 1, doc_setstate}, + {"dumps", (PyCFunction) array_dumps, 1, doc_dumps}, + {"dump", (PyCFunction) array_dump, 1, doc_dump}, + + /* Extended methods added 2005 */ + {"fill", (PyCFunction)array_fill, + METH_VARARGS, doc_fill}, + {"transpose", (PyCFunction)array_transpose, + METH_VARARGS, doc_transpose}, + {"take", (PyCFunction)array_take, + METH_VARARGS|METH_KEYWORDS, doc_take}, + {"put", (PyCFunction)array_put, + METH_VARARGS|METH_KEYWORDS, doc_put}, + {"putmask", (PyCFunction)array_putmask, + METH_VARARGS|METH_KEYWORDS, doc_putmask}, + {"repeat", (PyCFunction)array_repeat, + METH_VARARGS|METH_KEYWORDS, doc_repeat}, + {"choose", (PyCFunction)array_choose, + METH_VARARGS, doc_choose}, + {"sort", (PyCFunction)array_sort, + METH_VARARGS|METH_KEYWORDS, doc_sort}, + {"argsort", (PyCFunction)array_argsort, + METH_VARARGS|METH_KEYWORDS, doc_argsort}, + {"searchsorted", (PyCFunction)array_searchsorted, + METH_VARARGS, doc_searchsorted}, + {"argmax", (PyCFunction)array_argmax, + METH_VARARGS, doc_argmax}, + {"argmin", (PyCFunction)array_argmin, + METH_VARARGS, doc_argmin}, + {"reshape", (PyCFunction)array_reshape, + METH_VARARGS, doc_reshape}, + {"squeeze", (PyCFunction)array_squeeze, + METH_VARARGS, doc_squeeze}, + {"view", (PyCFunction)array_view, + METH_VARARGS, doc_view}, + {"swapaxes", (PyCFunction)array_swapaxes, + METH_VARARGS, doc_swapaxes}, + {"max", (PyCFunction)array_max, + METH_VARARGS, doc_max}, + {"min", (PyCFunction)array_min, + METH_VARARGS, doc_min}, + {"ptp", (PyCFunction)array_ptp, + METH_VARARGS, doc_ptp}, + {"mean", (PyCFunction)array_mean, + METH_VARARGS|METH_KEYWORDS, doc_mean}, + {"trace", (PyCFunction)array_trace, + METH_VARARGS|METH_KEYWORDS, doc_trace}, + {"diagonal", (PyCFunction)array_diagonal, + METH_VARARGS|METH_KEYWORDS, doc_diagonal}, + {"clip", (PyCFunction)array_clip, + METH_VARARGS|METH_KEYWORDS, doc_clip}, + {"conj", (PyCFunction)array_conjugate, + METH_VARARGS, doc_conj}, + {"conjugate", (PyCFunction)array_conjugate, + METH_VARARGS, doc_conjugate}, + {"nonzero", (PyCFunction)array_nonzero, + METH_VARARGS, doc_nonzero}, + {"std", (PyCFunction)array_stddev, + METH_VARARGS|METH_KEYWORDS, doc_stddev}, + {"var", (PyCFunction)array_variance, + METH_VARARGS|METH_KEYWORDS, doc_variance}, + {"sum", (PyCFunction)array_sum, + METH_VARARGS|METH_KEYWORDS, doc_sum}, + {"cumsum", (PyCFunction)array_cumsum, + METH_VARARGS|METH_KEYWORDS, doc_cumsum}, + {"prod", (PyCFunction)array_prod, + METH_VARARGS|METH_KEYWORDS, doc_prod}, + {"cumprod", (PyCFunction)array_cumprod, + METH_VARARGS|METH_KEYWORDS, doc_cumprod}, + {"all", (PyCFunction)array_all, + METH_VARARGS, doc_all}, + {"any", (PyCFunction)array_any, + METH_VARARGS, doc_any}, + {"compress", (PyCFunction)array_compress, + METH_VARARGS|METH_KEYWORDS, doc_compress}, + {"flatten", (PyCFunction)array_flatten, + METH_VARARGS, doc_flatten}, + {"ravel", (PyCFunction)array_ravel, + METH_VARARGS, doc_ravel}, + {"setflags", (PyCFunction)array_setflags, + METH_VARARGS|METH_KEYWORDS, doc_setflags}, + {"newbyteorder", (PyCFunction)array_newbyteorder, + METH_VARARGS, doc_newbyteorder}, + {NULL, NULL} /* sentinel */ +}; + +#undef _ARET + + diff --git a/numpy/core/src/arrayobject.c b/numpy/core/src/arrayobject.c new file mode 100644 index 000000000..72db76373 --- /dev/null +++ b/numpy/core/src/arrayobject.c @@ -0,0 +1,8472 @@ +/* + Provide multidimensional arrays as a basic object type in python. + +Based on Original Numeric implementation +Copyright (c) 1995, 1996, 1997 Jim Hugunin, hugunin@mit.edu + +with contributions from many Numeric Python developers 1995-2004 + +Heavily modified in 2005 with inspiration from Numarray + +by + +Travis Oliphant +Assistant Professor at +Brigham Young University + +maintainer email: oliphant.travis@ieee.org + +Numarray design (which provided guidance) by +Space Science Telescope Institute + (J. Todd Miller, Perry Greenfield, Rick White) + +*/ + +/* Helper functions */ + +#define error_converting(x) (((x) == -1) && PyErr_Occurred()) + +/*OBJECT_API*/ +static intp +PyArray_PyIntAsIntp(PyObject *o) +{ + longlong long_value = -1; + PyObject *obj; + static char *msg = "an integer is required"; + PyObject *arr=NULL; + PyArray_Descr *descr; + intp ret; + + if (!o) { + PyErr_SetString(PyExc_TypeError, msg); + return -1; + } + descr = PyArray_DescrFromType(PyArray_INTP); + if (PyArray_Check(o)) { + if (PyArray_SIZE(o)!=1 || !PyArray_ISINTEGER(o)) { + PyErr_SetString(PyExc_TypeError, msg); + Py_DECREF(descr); + return -1; + } + arr = PyArray_CastToType((PyArrayObject *)o, descr, 0); + } + else if (PyArray_IsScalar(o, Integer)) { + arr = PyArray_FromScalar(o, descr); + } + if (arr != NULL) { + ret = *((intp *)PyArray_DATA(arr)); + Py_DECREF(arr); + return ret; + } + if (PyInt_Check(o)) { + long_value = (longlong) PyInt_AS_LONG(o); + } else if (PyLong_Check(o)) { + long_value = (longlong) PyLong_AsLongLong(o); + } else if (o->ob_type->tp_as_number != NULL && \ + o->ob_type->tp_as_number->nb_long != NULL) { + obj = o->ob_type->tp_as_number->nb_long(o); + if (obj != NULL) { + long_value = (longlong) PyLong_AsLongLong(obj); + Py_DECREF(obj); + } + } else if (o->ob_type->tp_as_number != NULL && \ + o->ob_type->tp_as_number->nb_int != NULL) { + obj = o->ob_type->tp_as_number->nb_int(o); + if (obj != NULL) { + long_value = (longlong) PyLong_AsLongLong(obj); + Py_DECREF(obj); + } + } else { + PyErr_SetString(PyExc_NotImplementedError,""); + } + + if error_converting(long_value) { + PyErr_SetString(PyExc_TypeError, msg); + return -1; + } + +#if (SIZEOF_LONGLONG != SIZEOF_PY_INTPTR_T) + if ((long_value < MIN_INTP) || (long_value > MAX_INTP)) { + PyErr_SetString(PyExc_ValueError, + "integer won't fit into a C intp"); + return -1; + } +#endif + return (intp) long_value; +} + + +static PyObject *array_int(PyArrayObject *v); + +/*OBJECT_API*/ +static int +PyArray_PyIntAsInt(PyObject *o) +{ + long long_value = -1; + PyObject *obj; + static char *msg = "an integer is required"; + PyObject *arr=NULL; + PyArray_Descr *descr; + int ret; + + + if (!o) { + PyErr_SetString(PyExc_TypeError, msg); + return -1; + } + descr = PyArray_DescrFromType(PyArray_INT); + if (PyArray_Check(o)) { + if (PyArray_SIZE(o)!=1 || !PyArray_ISINTEGER(o)) { + PyErr_SetString(PyExc_TypeError, msg); + Py_DECREF(descr); + return -1; + } + arr = PyArray_CastToType((PyArrayObject *)o, descr, 0); + } + if (PyArray_IsScalar(o, Integer)) { + arr = PyArray_FromScalar(o, descr); + } + if (arr != NULL) { + ret = *((int *)PyArray_DATA(arr)); + Py_DECREF(arr); + return ret; + } + if (PyInt_Check(o)) { + long_value = (long) PyInt_AS_LONG(o); + } else if (PyLong_Check(o)) { + long_value = (long) PyLong_AsLong(o); + } else if (o->ob_type->tp_as_number != NULL && \ + o->ob_type->tp_as_number->nb_long != NULL) { + obj = o->ob_type->tp_as_number->nb_long(o); + if (obj == NULL) return -1; + long_value = (long) PyLong_AsLong(obj); + Py_DECREF(obj); + } else if (o->ob_type->tp_as_number != NULL && \ + o->ob_type->tp_as_number->nb_int != NULL) { + obj = o->ob_type->tp_as_number->nb_int(o); + if (obj == NULL) return -1; + long_value = (long) PyLong_AsLong(obj); + Py_DECREF(obj); + } else { + PyErr_SetString(PyExc_NotImplementedError,""); + } + if error_converting(long_value) { + PyErr_SetString(PyExc_TypeError, msg); + return -1; + } + +#if (SIZEOF_LONG != SIZEOF_INT) + if ((long_value < INT_MIN) || (long_value > INT_MAX)) { + PyErr_SetString(PyExc_ValueError, + "integer won't fit into a C int"); + return -1; + } +#endif + return (int) long_value; +} + + +/*OBJECT_API + Get Priority from object +*/ +static double +PyArray_GetPriority(PyObject *obj, double default_) +{ + PyObject *ret; + double priority=PyArray_PRIORITY; + + if (PyArray_CheckExact(obj)) + return priority; + if (PyBigArray_CheckExact(obj)) + return PyArray_BIG_PRIORITY; + + ret = PyObject_GetAttrString(obj, "__array_priority__"); + if (ret != NULL) priority = PyFloat_AsDouble(ret); + if (PyErr_Occurred()) { + PyErr_Clear(); + priority = default_; + } + Py_XDECREF(ret); + return priority; +} + +/* Backward compatibility only */ +/* In both Zero and One + + ***You must free the memory once you are done with it + using PyDataMem_FREE(ptr) or you create a memory leak*** + + If arr is an Object array you are getting a + BORROWED reference to Zero or One. + Do not DECREF. + Please INCREF if you will be hanging on to it. + + The memory for the ptr still must be freed in any case; +*/ + + +/*OBJECT_API + Get pointer to zero of correct type for array. +*/ +static char * +PyArray_Zero(PyArrayObject *arr) +{ + char *zeroval; + int ret, storeflags; + PyObject *obj; + + zeroval = PyDataMem_NEW(arr->descr->elsize); + if (zeroval == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + + obj=PyInt_FromLong((long) 0); + if (PyArray_ISOBJECT(arr)) { + memcpy(zeroval, &obj, sizeof(PyObject *)); + Py_DECREF(obj); + return zeroval; + } + storeflags = arr->flags; + arr->flags |= BEHAVED_FLAGS; + ret = arr->descr->f->setitem(obj, zeroval, arr); + arr->flags = storeflags; + Py_DECREF(obj); + if (ret < 0) { + PyDataMem_FREE(zeroval); + return NULL; + } + return zeroval; +} + +/*OBJECT_API + Get pointer to one of correct type for array +*/ +static char * +PyArray_One(PyArrayObject *arr) +{ + char *oneval; + int ret, storeflags; + PyObject *obj; + + oneval = PyDataMem_NEW(arr->descr->elsize); + if (oneval == NULL) { + PyErr_SetNone(PyExc_MemoryError); + return NULL; + } + + obj = PyInt_FromLong((long) 1); + if (PyArray_ISOBJECT(arr)) { + memcpy(oneval, &obj, sizeof(PyObject *)); + Py_DECREF(obj); + return oneval; + } + + storeflags = arr->flags; + arr->flags |= BEHAVED_FLAGS; + ret = arr->descr->f->setitem(obj, oneval, arr); + arr->flags = storeflags; + Py_DECREF(obj); + if (ret < 0) { + PyDataMem_FREE(oneval); + return NULL; + } + return oneval; +} + +/* End deprecated */ + + +static int +do_sliced_copy(char *dest, intp *dest_strides, intp *dest_dimensions, + int dest_nd, char *src, intp *src_strides, + intp *src_dimensions, int src_nd, int elsize, + int copies) { + intp i, j; + + if (src_nd == 0 && dest_nd == 0) { + for(j=0; j<copies; j++) { + memmove(dest, src, elsize); + dest += elsize; + } + return 0; + } + + if (dest_nd > src_nd) { + for(i=0; i<*dest_dimensions; i++, dest += *dest_strides) { + if (do_sliced_copy(dest, dest_strides+1, + dest_dimensions+1, dest_nd-1, + src, src_strides, + src_dimensions, src_nd, + elsize, copies) == -1) + return -1; + } + return 0; + } + + if (dest_nd == 1) { + if (*dest_dimensions != *src_dimensions) { + PyErr_SetString(PyExc_ValueError, + "matrices are not aligned for copy"); + return -1; + } + for(i=0; i<*dest_dimensions; i++, src += *src_strides) { + for(j=0; j<copies; j++) { + memmove(dest, src, elsize); + dest += *dest_strides; + } + } + } else { + for(i=0; i<*dest_dimensions; i++, dest += *dest_strides, + src += *src_strides) { + if (do_sliced_copy(dest, dest_strides+1, + dest_dimensions+1, dest_nd-1, + src, src_strides+1, + src_dimensions+1, src_nd-1, + elsize, copies) == -1) + return -1; + } + } + return 0; +} + +/* This function reduces a source and destination array until a + discontiguous segment is found in either the source or + destination. Thus, an N dimensional array where the last dimension + is contiguous and has size n while the items are of size elsize, + will be reduced to an N-1 dimensional array with items of size n * + elsize. + + This process is repeated until a discontiguous section is found. + Thus, a contiguous array will be reduced to a 0-dimensional array + with items of size elsize * sizeof(N-dimensional array). + + Finally, if a source array has been reduced to a 0-dimensional + array with large element sizes, the contiguous destination array is + reduced as well. + + The only thing this function changes is the element size, the + number of copies, and the source and destination number of + dimensions. The strides and dimensions are not changed. +*/ + +static int +optimize_slices(intp **dest_strides, intp **dest_dimensions, + int *dest_nd, intp **src_strides, + intp **src_dimensions, int *src_nd, + int *elsize, int *copies) +{ + while (*src_nd > 0) { + if (((*dest_strides)[*dest_nd-1] == *elsize) && + ((*src_strides)[*src_nd-1] == *elsize)) { + if ((*dest_dimensions)[*dest_nd-1] != + (*src_dimensions)[*src_nd-1]) { + PyErr_SetString(PyExc_ValueError, + "matrices are not aligned"); + return -1; + } + *elsize *= (*dest_dimensions)[*dest_nd-1]; + *dest_nd-=1; *src_nd-=1; + } else { + break; + } + } + if (*src_nd == 0) { + while (*dest_nd > 0) { + if (((*dest_strides)[*dest_nd-1] == *elsize)) { + *copies *= (*dest_dimensions)[*dest_nd-1]; + *dest_nd-=1; + } else { + break; + } + } + } + return 0; +} + +static char * +contiguous_data(PyArrayObject *src) +{ + intp dest_strides[MAX_DIMS], *dest_strides_ptr; + intp *dest_dimensions=src->dimensions; + int dest_nd=src->nd; + intp *src_strides = src->strides; + intp *src_dimensions=src->dimensions; + int src_nd=src->nd; + int elsize=src->descr->elsize; + int copies=1; + int ret, i; + intp stride=elsize; + char *new_data; + + for(i=dest_nd-1; i>=0; i--) { + dest_strides[i] = stride; + stride *= dest_dimensions[i]; + } + + dest_strides_ptr = dest_strides; + + if (optimize_slices(&dest_strides_ptr, &dest_dimensions, &dest_nd, + &src_strides, &src_dimensions, &src_nd, + &elsize, &copies) == -1) + return NULL; + + new_data = (char *)_pya_malloc(stride); + + ret = do_sliced_copy(new_data, dest_strides_ptr, dest_dimensions, + dest_nd, src->data, src_strides, + src_dimensions, src_nd, elsize, copies); + + if (ret != -1) { return new_data; } + else { _pya_free(new_data); return NULL; } +} + +/* end Helper functions */ + + +static PyObject *PyArray_New(PyTypeObject *, int nd, intp *, + int, intp *, void *, int, int, PyObject *); + +/* C-API functions */ + +/* Used for arrays of python objects to increment the reference count of */ +/* every python object in the array. */ +/*OBJECT_API + For object arrays, increment all internal references. +*/ +static int +PyArray_INCREF(PyArrayObject *mp) +{ + intp i, n; + + PyObject **data, **data2; + + if (mp->descr->type_num != PyArray_OBJECT) return 0; + + if (PyArray_ISONESEGMENT(mp)) { + data = (PyObject **)mp->data; + } else { + if ((data = (PyObject **)contiguous_data(mp)) == NULL) + return -1; + } + + n = PyArray_SIZE(mp); + data2 = data; + for(i=0; i<n; i++, data++) Py_XINCREF(*data); + + if (!PyArray_ISONESEGMENT(mp)) _pya_free(data2); + + return 0; +} + +/*OBJECT_API + Decrement all internal references for object arrays. +*/ +static int +PyArray_XDECREF(PyArrayObject *mp) +{ + intp i, n; + PyObject **data, **data2; + + if (mp->descr->type_num != PyArray_OBJECT) return 0; + + if (PyArray_ISONESEGMENT(mp)) { + data = (PyObject **)mp->data; + } else { + if ((data = (PyObject **)contiguous_data(mp)) == NULL) + return -1; + } + + n = PyArray_SIZE(mp); + data2 = data; + for(i=0; i<n; i++, data++) Py_XDECREF(*data); + + if (!PyArray_ISONESEGMENT(mp)) _pya_free(data2); + + return 0; +} + +/* byte-swap inplace (unrolled loops for special cases) */ +static void +byte_swap_vector(void *p, int n, int size) { + char *a, *b, c=0; + int j,m; + + switch(size) { + case 1: /* no byteswap necessary */ + break; + case 2: + for (a = (char*)p ; n > 0; n--, a += 1) { + b = a + 1; + c = *a; *a++ = *b; *b = c; + } + break; + case 4: + for (a = (char*)p ; n > 0; n--, a += 2) { + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + } + break; + case 8: + for (a = (char*)p ; n > 0; n--, a += 4) { + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + } + break; + default: + m = size / 2; + for (a = (char *)p ; n > 0; n--, a += m) { + b = a + (size-1); + for (j=0; j<m; j++) + c=*a; *a++ = *b; *b-- = c; + } + break; + } +} + + +/* If numitems > 1, then dst must be contiguous */ +static void +copy_and_swap(void *dst, void *src, int itemsize, intp numitems, + intp srcstrides, int swap) +{ + int i; + char *s1 = (char *)src; + char *d1 = (char *)dst; + + + if ((numitems == 1) || (itemsize == srcstrides)) + memcpy(d1, s1, itemsize*numitems); + else { + for (i = 0; i < numitems; i++) { + memcpy(d1, s1, itemsize); + d1 += itemsize; + s1 += srcstrides; + } + } + + if (swap) + byte_swap_vector(d1, numitems, itemsize); +} + +static PyArray_Descr **userdescrs=NULL; +/* Computer-generated arraytype and scalartype code */ +#include "scalartypes.inc" +#include "arraytypes.inc" + +static char * +index2ptr(PyArrayObject *mp, intp i) +{ + if(mp->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed"); + return NULL; + } + if (i==0 && mp->dimensions[0] > 0) + return mp->data; + + if (mp->nd>0 && i>0 && i < mp->dimensions[0]) { + return mp->data+i*mp->strides[0]; + } + PyErr_SetString(PyExc_IndexError,"index out of bounds"); + return NULL; +} + +/*OBJECT_API + Compute the size of an array (in number of items) +*/ +static intp +PyArray_Size(PyObject *op) +{ + if (PyArray_Check(op)) { + return PyArray_SIZE((PyArrayObject *)op); + } + else { + return 0; + } +} + +/* If destination is not the right type, then src + will be cast to destination. +*/ + +/* Does a flat iterator-based copy. + + The arrays are assumed to have the same number of elements + They can be different sizes and have different types however. +*/ + +/*OBJECT_API + Copy an Array into another array. +*/ +static int +PyArray_CopyInto(PyArrayObject *dest, PyArrayObject *src) +{ + intp dsize, ssize, sbytes, ncopies; + int elsize, index; + PyArrayIterObject *dit=NULL; + PyArrayIterObject *sit=NULL; + char *dptr; + int swap; + PyArray_CopySwapFunc *copyswap; + PyArray_CopySwapNFunc *copyswapn; + + if (!PyArray_ISWRITEABLE(dest)) { + PyErr_SetString(PyExc_RuntimeError, + "cannot write to array"); + return -1; + } + + if (!PyArray_EquivArrTypes(dest, src)) { + return PyArray_CastTo(dest, src); + } + + dsize = PyArray_SIZE(dest); + ssize = PyArray_SIZE(src); + if (ssize == 0) return 0; + if (dsize % ssize != 0) { + PyErr_SetString(PyExc_ValueError, + "number of elements in destination must be "\ + "integer multiple of number of "\ + "elements in source"); + return -1; + } + ncopies = (dsize / ssize); + + swap = PyArray_ISNOTSWAPPED(dest) != PyArray_ISNOTSWAPPED(src); + copyswap = dest->descr->f->copyswap; + copyswapn = dest->descr->f->copyswapn; + + elsize = dest->descr->elsize; + + if ((PyArray_ISCONTIGUOUS(dest) && PyArray_ISCONTIGUOUS(src)) \ + || (PyArray_ISFORTRAN(dest) && PyArray_ISFORTRAN(src))) { + + PyArray_XDECREF(dest); + dptr = dest->data; + sbytes = ssize * src->descr->elsize; + while(ncopies--) { + memmove(dptr, src->data, sbytes); + dptr += sbytes; + } + if (swap) + copyswapn(dest->data, NULL, dsize, 1, elsize); + PyArray_INCREF(dest); + return 0; + } + + dit = (PyArrayIterObject *)PyArray_IterNew((PyObject *)dest); + sit = (PyArrayIterObject *)PyArray_IterNew((PyObject *)src); + + if ((dit == NULL) || (sit == NULL)) { + Py_XDECREF(dit); + Py_XDECREF(sit); + return -1; + } + + PyArray_XDECREF(dest); + while(ncopies--) { + index = ssize; + while(index--) { + memmove(dit->dataptr, sit->dataptr, elsize); + if (swap) + copyswap(dit->dataptr, NULL, 1, elsize); + PyArray_ITER_NEXT(dit); + PyArray_ITER_NEXT(sit); + } + PyArray_ITER_RESET(sit); + } + PyArray_INCREF(dest); + Py_DECREF(dit); + Py_DECREF(sit); + return 0; +} + + +static int +PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object) +{ + PyArrayObject *src; + int ret; + + Py_INCREF(dest->descr); + src = (PyArrayObject *)PyArray_FromAny(src_object, + dest->descr, 0, + dest->nd, FORTRAN_IF(dest)); + if (src == NULL) return -1; + + ret = PyArray_CopyInto(dest, src); + Py_DECREF(src); + return ret; +} + + +/* These are also old calls (should use PyArray_New) */ + +/* They all zero-out the memory as previously done */ + +/* steals reference to descr -- and enforces native byteorder on it.*/ +/*OBJECT_API + Like FromDimsAndData but uses the Descr structure instead of typecode + as input. +*/ +static PyObject * +PyArray_FromDimsAndDataAndDescr(int nd, int *d, + PyArray_Descr *descr, + char *data) +{ + PyObject *ret; +#if SIZEOF_INTP != SIZEOF_INT + int i; + intp newd[MAX_DIMS]; +#endif + + if (!PyArray_ISNBO(descr->byteorder)) + descr->byteorder = '='; + +#if SIZEOF_INTP != SIZEOF_INT + for (i=0; i<nd; i++) newd[i] = (intp) d[i]; + ret = PyArray_NewFromDescr(&PyArray_Type, descr, + nd, newd, + NULL, data, + (data ? CARRAY_FLAGS : 0), NULL); +#else + ret = PyArray_NewFromDescr(&PyArray_Type, descr, + nd, (intp *)d, + NULL, data, + (data ? CARRAY_FLAGS : 0), NULL); +#endif + return ret; +} + +/*OBJECT_API + Construct an empty array from dimensions and typenum +*/ +static PyObject * +PyArray_FromDims(int nd, int *d, int type) +{ + PyObject *ret; + ret = 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 != PyArray_OBJECT)) { + memset(PyArray_DATA(ret), 0, PyArray_NBYTES(ret)); + } + return ret; +} + +/* end old calls */ + +/*OBJECT_API + Copy an array. +*/ +static PyObject * +PyArray_NewCopy(PyArrayObject *m1, int fortran) +{ + PyArrayObject *ret; + if (fortran < 0) fortran = PyArray_ISFORTRAN(m1); + + Py_INCREF(m1->descr); + ret = (PyArrayObject *)PyArray_NewFromDescr(m1->ob_type, + m1->descr, + m1->nd, + m1->dimensions, + NULL, NULL, + fortran, + (PyObject *)m1); + if (ret == NULL) return NULL; + if (PyArray_CopyInto(ret, m1) == -1) { + Py_DECREF(ret); + return NULL; + } + + return (PyObject *)ret; +} + +static PyObject *array_big_item(PyArrayObject *, intp); + +/* Does nothing with descr (cannot be NULL) */ +/*OBJECT_API + Get scalar-equivalent to a region of memory described by a descriptor. +*/ +static PyObject * +PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base) +{ + PyTypeObject *type; + PyObject *obj; + void *destptr; + PyArray_CopySwapFunc *copyswap; + int type_num; + int itemsize; + int swap; + + type_num = descr->type_num; + itemsize = descr->elsize; + type = descr->typeobj; + copyswap = descr->f->copyswap; + swap = !PyArray_ISNBO(descr->byteorder); + if (type->tp_itemsize != 0) /* String type */ + obj = type->tp_alloc(type, itemsize); + else + obj = type->tp_alloc(type, 0); + if (obj == NULL) return NULL; + if PyTypeNum_ISEXTENDED(type_num) { + if (type_num == PyArray_STRING) { + destptr = PyString_AS_STRING(obj); + ((PyStringObject *)obj)->ob_shash = -1; + ((PyStringObject *)obj)->ob_sstate = \ + SSTATE_NOT_INTERNED; + } + else if (type_num == PyArray_UNICODE) { + PyUnicodeObject *uni = (PyUnicodeObject*)obj; + int length = itemsize / sizeof(Py_UNICODE); + /* Need an extra slot and need to use + Python memory manager */ + uni->str = NULL; + destptr = PyMem_NEW(Py_UNICODE, length+1); + if (destptr == NULL) { + Py_DECREF(obj); + return PyErr_NoMemory(); + } + uni->str = (Py_UNICODE *)destptr; + uni->str[0] = 0; + uni->str[length] = 0; + uni->length = length; + uni->hash = -1; + uni->defenc = NULL; + } + else { + PyVoidScalarObject *vobj = (PyVoidScalarObject *)obj; + vobj->base = NULL; + vobj->descr = descr; + Py_INCREF(descr); + vobj->obval = NULL; + vobj->ob_size = itemsize; + vobj->flags = BEHAVED_FLAGS | OWNDATA; + swap = 0; + if (descr->fields) { + if (base) { + Py_INCREF(base); + vobj->base = base; + vobj->flags = PyArray_FLAGS(base); + vobj->flags &= ~OWNDATA; + vobj->obval = data; + return obj; + } + } + destptr = PyDataMem_NEW(itemsize); + if (destptr == NULL) { + Py_DECREF(obj); + return PyErr_NoMemory(); + } + vobj->obval = destptr; + } + } + else { + destptr = _SOFFSET_(obj, type_num); + } + /* copyswap for OBJECT increments the reference count */ + copyswap(destptr, data, swap, itemsize); + return obj; +} + +/* returns an Array-Scalar Object of the type of arr + from the given pointer to memory -- main Scalar creation function + default new method calls this. +*/ + +/* Ideally, here the descriptor would contain all the information needed. + So, that we simply need the data and the descriptor, and perhaps + a flag +*/ + +/*OBJECT_API + Get scalar-equivalent to 0-d array +*/ +static PyObject * +PyArray_ToScalar(void *data, PyArrayObject *arr) +{ + return PyArray_Scalar(data, arr->descr, (PyObject *)arr); +} + + +/* Return Python scalar if 0-d array object is encountered */ + +/*OBJECT_API + Return either an array or the appropriate Python object if the array + is 0d and matches a Python type. +*/ +static PyObject * +PyArray_Return(PyArrayObject *mp) +{ + + + if (mp == NULL) return NULL; + + if (PyErr_Occurred()) { + Py_XDECREF(mp); + return NULL; + } + + if (mp->nd == 0) { + PyObject *ret; + ret = PyArray_ToScalar(mp->data, mp); + Py_DECREF(mp); + return ret; + } + else { + return (PyObject *)mp; + } +} + +/* + returns typenum to associate with this type >=PyArray_USERDEF. + Also creates a copy of the VOID_DESCR table inserting it's typeobject in + and it's typenum in the appropriate place. + + needs the userdecrs table and PyArray_NUMUSER variables + defined in arratypes.inc +*/ +/*OBJECT_API + Register Data type +*/ +static int +PyArray_RegisterDataType(PyTypeObject *type) +{ + PyArray_Descr *descr; + PyObject *obj; + int typenum; + int i; + + if ((type == &PyVoidArrType_Type) || \ + !PyType_IsSubtype(type, &PyVoidArrType_Type)) { + PyErr_SetString(PyExc_ValueError, + "can only register void subtypes"); + return -1; + } + /* See if this type is already registered */ + for (i=0; i<PyArray_NUMUSERTYPES; i++) { + descr = userdescrs[i]; + if (descr->typeobj == type) + return descr->type_num; + } + descr = PyArray_DescrNewFromType(PyArray_VOID); + typenum = PyArray_USERDEF + PyArray_NUMUSERTYPES; + descr->type_num = typenum; + descr->typeobj = type; + obj = PyObject_GetAttrString((PyObject *)type,"itemsize"); + if (obj) { + i = PyInt_AsLong(obj); + if ((i < 0) && (PyErr_Occurred())) PyErr_Clear(); + else descr->elsize = i; + Py_DECREF(obj); + } + Py_INCREF(type); + userdescrs = realloc(userdescrs, + (PyArray_NUMUSERTYPES+1)*sizeof(void *)); + if (userdescrs == NULL) { + PyErr_SetString(PyExc_MemoryError, "RegisterDataType"); + Py_DECREF(descr); + return -1; + } + userdescrs[PyArray_NUMUSERTYPES++] = descr; + return typenum; +} + + +/* + copyies over from the old descr table for anything + NULL or zero in what is given. + DECREF's the Descr already there. + places a pointer to the new one into the slot. +*/ + +/* steals a reference to descr */ +/*OBJECT_API + Insert Descr Table +*/ +static int +PyArray_RegisterDescrForType(int typenum, PyArray_Descr *descr) +{ + PyArray_Descr *old; + + if (!PyTypeNum_ISUSERDEF(typenum)) { + PyErr_SetString(PyExc_TypeError, + "data type not registered"); + Py_DECREF(descr); + return -1; + } + old = userdescrs[typenum-PyArray_USERDEF]; + descr->typeobj = old->typeobj; + descr->type_num = typenum; + + if (descr->f == NULL) descr->f = old->f; + if (descr->fields == NULL) { + descr->fields = old->fields; + Py_XINCREF(descr->fields); + } + if (descr->subarray == NULL && old->subarray) { + descr->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr)); + memcpy(descr->subarray, old->subarray, + sizeof(PyArray_ArrayDescr)); + Py_INCREF(descr->subarray->shape); + Py_INCREF(descr->subarray->base); + } + Py_XINCREF(descr->typeobj); + +#define _ZERO_CHECK(member) \ + if (descr->member == 0) descr->member = old->member + + _ZERO_CHECK(kind); + _ZERO_CHECK(type); + _ZERO_CHECK(byteorder); + _ZERO_CHECK(elsize); + _ZERO_CHECK(alignment); +#undef _ZERO_CHECK + + Py_DECREF(old); + userdescrs[typenum-PyArray_USERDEF] = descr; + return 0; +} + + +/*OBJECT_API + To File +*/ +static int +PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) +{ + intp size; + intp n, n2; + int n3, n4; + PyArrayIterObject *it; + PyObject *obj, *strobj, *tupobj; + + n3 = (sep ? strlen((const char *)sep) : 0); + if (n3 == 0) { /* binary data */ + if (PyArray_ISOBJECT(self)) { + PyErr_SetString(PyExc_ValueError, "cannot write "\ + "object arrays to a file in " \ + "binary mode"); + return -1; + } + + if (PyArray_ISCONTIGUOUS(self)) { + size = PyArray_SIZE(self); + if ((n=fwrite((const void *)self->data, + (size_t) self->descr->elsize, + (size_t) size, fp)) < size) { + PyErr_Format(PyExc_ValueError, + "%ld requested and %ld written", + (long) size, (long) n); + return -1; + } + } + else { + it=(PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)self); + while(it->index < it->size) { + if (fwrite((const void *)it->dataptr, + (size_t) self->descr->elsize, + 1, fp) < 1) { + PyErr_Format(PyExc_IOError, + "problem writing element"\ + " %d to file", + (int)it->index); + Py_DECREF(it); + return -1; + } + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + } + } + else { /* text data */ + it=(PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)self); + n4 = (format ? strlen((const char *)format) : 0); + while(it->index < it->size) { + obj = self->descr->f->getitem(it->dataptr, self); + if (obj == NULL) {Py_DECREF(it); return -1;} + if (n4 == 0) { /* standard writing */ + strobj = PyObject_Str(obj); + Py_DECREF(obj); + if (strobj == NULL) {Py_DECREF(it); return -1;} + } + else { /* use format string */ + tupobj = PyTuple_New(1); + if (tupobj == NULL) {Py_DECREF(it); return -1;} + PyTuple_SET_ITEM(tupobj,0,obj); + obj = PyString_FromString((const char *)format); + if (obj == NULL) {Py_DECREF(tupobj); + Py_DECREF(it); return -1;} + strobj = PyString_Format(obj, tupobj); + Py_DECREF(obj); + Py_DECREF(tupobj); + if (strobj == NULL) {Py_DECREF(it); return -1;} + } + if ((n=fwrite(PyString_AS_STRING(strobj), + 1, n2=PyString_GET_SIZE(strobj), + fp)) < n2) { + PyErr_Format(PyExc_IOError, + "problem writing element %d"\ + " to file", + (int) it->index); + Py_DECREF(strobj); + Py_DECREF(it); + return -1; + } + /* write separator for all but last one */ + if (it->index != it->size-1) + fwrite(sep, 1, n3, fp); + Py_DECREF(strobj); + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + } + return 0; +} + +/*OBJECT_API + To List +*/ +static PyObject * +PyArray_ToList(PyArrayObject *self) +{ + PyObject *lp; + PyArrayObject *v; + intp sz, i; + + if (!PyArray_Check(self)) return (PyObject *)self; + + if (self->nd == 0) + return self->descr->f->getitem(self->data,self); + + sz = self->dimensions[0]; + lp = PyList_New(sz); + + for (i=0; i<sz; i++) { + v=(PyArrayObject *)array_big_item(self, i); + if (v->nd >= self->nd) { + PyErr_SetString(PyExc_RuntimeError, + "array_item not returning smaller-" \ + "dimensional array"); + Py_DECREF(v); + Py_DECREF(lp); + return NULL; + } + PyList_SetItem(lp, i, PyArray_ToList(v)); + Py_DECREF(v); + } + + return lp; +} + +static PyObject * +PyArray_ToString(PyArrayObject *self) +{ + intp numbytes; + intp index; + char *dptr; + int elsize; + PyObject *ret; + PyArrayIterObject *it; + + /* if (PyArray_TYPE(self) == PyArray_OBJECT) { + PyErr_SetString(PyExc_ValueError, "a string for the data" \ + "in an object array is not appropriate"); + return NULL; + } + */ + + numbytes = PyArray_NBYTES(self); + if (PyArray_ISONESEGMENT(self)) { + ret = PyString_FromStringAndSize(self->data, (int) numbytes); + } + else { + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it==NULL) return NULL; + ret = PyString_FromStringAndSize(NULL, (int) numbytes); + if (ret == NULL) {Py_DECREF(it); return NULL;} + dptr = PyString_AS_STRING(ret); + index = it->size; + elsize = self->descr->elsize; + while(index--) { + memcpy(dptr, it->dataptr, elsize); + dptr += elsize; + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + } + return ret; +} + + +/*********************** end C-API functions **********************/ + + +/* array object functions */ + +static void +array_dealloc(PyArrayObject *self) { + + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)self); + + if(self->base) { + /* UPDATEIFCOPY means that base points to an + array that should be updated with the contents + of this array upon destruction. + self->base->flags must have been WRITEABLE + (checked previously) and it was locked here + thus, unlock it. + */ + if (self->flags & UPDATEIFCOPY) { + ((PyArrayObject *)self->base)->flags |= WRITEABLE; + Py_INCREF(self); /* hold on to self in next call */ + PyArray_CopyInto((PyArrayObject *)self->base, self); + /* Don't need to DECREF -- because we are deleting + self already... */ + } + /* In any case base is pointing to something that we need + to DECREF -- either a view or a buffer object */ + Py_DECREF(self->base); + } + + if ((self->flags & OWN_DATA) && self->data) { + /* Free internal references if an Object array */ + if (PyArray_ISOBJECT(self)) + PyArray_XDECREF(self); + PyDataMem_FREE(self->data); + } + + PyDimMem_FREE(self->dimensions); + + Py_DECREF(self->descr); + + self->ob_type->tp_free((PyObject *)self); +} + +/************************************************************************* + **************** Implement Mapping Protocol *************************** + *************************************************************************/ + +static int +array_length(PyArrayObject *self) +{ + if (self->nd != 0) { + return self->dimensions[0]; + } else { + PyErr_SetString(PyExc_TypeError, "len() of unsized object"); + return -1; + } +} + +static PyObject * +array_big_item(PyArrayObject *self, intp i) +{ + char *item; + PyArrayObject *r; + + if(self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed"); + return NULL; + } + if ((item = index2ptr(self, i)) == NULL) return NULL; + + Py_INCREF(self->descr); + r = (PyArrayObject *)PyArray_NewFromDescr(self->ob_type, + self->descr, + self->nd-1, + self->dimensions+1, + self->strides+1, item, + self->flags, + (PyObject *)self); + if (r == NULL) return NULL; + Py_INCREF(self); + r->base = (PyObject *)self; + PyArray_UpdateFlags(r, CONTIGUOUS | FORTRAN); + return (PyObject *)r; +} + +static PyObject * +array_item_nice(PyArrayObject *self, int i) +{ + return PyArray_Return((PyArrayObject *)array_big_item(self, (intp) i)); +} + + +static int +array_ass_big_item(PyArrayObject *self, intp i, PyObject *v) +{ + PyArrayObject *tmp; + char *item; + int ret; + + if (v == NULL) { + PyErr_SetString(PyExc_ValueError, + "can't delete array elements"); + return -1; + } + if (!PyArray_ISWRITEABLE(self)) { + PyErr_SetString(PyExc_RuntimeError, + "array is not writeable"); + return -1; + } + if (self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed."); + return -1; + } + + if (i < 0) i = i+self->dimensions[0]; + + if (self->nd > 1) { + if((tmp = (PyArrayObject *)array_big_item(self, i)) == NULL) + return -1; + ret = PyArray_CopyObject(tmp, v); + Py_DECREF(tmp); + return ret; + } + + if ((item = index2ptr(self, i)) == NULL) return -1; + if (self->descr->f->setitem(v, item, self) == -1) return -1; + return 0; +} + +#if SIZEOF_INT == SIZEOF_INTP +#define array_ass_item array_ass_big_item +#else +static int +array_ass_item(PyArrayObject *self, int i, PyObject *v) +{ + return array_ass_big_item(self, (intp) i, v); +} +#endif + + +/* -------------------------------------------------------------- */ +static int +slice_coerce_index(PyObject *o, intp *v) +{ + *v = PyArray_PyIntAsIntp(o); + if (error_converting(*v)) { + PyErr_Clear(); + return 0; + } + return 1; +} + + +/* This is basically PySlice_GetIndicesEx, but with our coercion + * of indices to integers (plus, that function is new in Python 2.3) */ +static int +slice_GetIndices(PySliceObject *r, intp length, + intp *start, intp *stop, intp *step, + intp *slicelength) +{ + intp defstart, defstop; + + if (r->step == Py_None) { + *step = 1; + } else { + if (!slice_coerce_index(r->step, step)) return -1; + if (*step == 0) { + PyErr_SetString(PyExc_ValueError, + "slice step cannot be zero"); + return -1; + } + } + + defstart = *step < 0 ? length - 1 : 0; + defstop = *step < 0 ? -1 : length; + + if (r->start == Py_None) { + *start = *step < 0 ? length-1 : 0; + } else { + if (!slice_coerce_index(r->start, start)) return -1; + if (*start < 0) *start += length; + if (*start < 0) *start = (*step < 0) ? -1 : 0; + if (*start >= length) { + *start = (*step < 0) ? length - 1 : length; + } + } + + if (r->stop == Py_None) { + *stop = defstop; + } else { + if (!slice_coerce_index(r->stop, stop)) return -1; + if (*stop < 0) *stop += length; + if (*stop < 0) *stop = -1; + if (*stop > length) *stop = length; + } + + if ((*step < 0 && *stop >= *start) || \ + (*step > 0 && *start >= *stop)) { + *slicelength = 0; + } else if (*step < 0) { + *slicelength = (*stop - *start + 1) / (*step) + 1; + } else { + *slicelength = (*stop - *start - 1) / (*step) + 1; + } + + return 0; +} + +#define PseudoIndex -1 +#define RubberIndex -2 +#define SingleIndex -3 + +static intp +parse_subindex(PyObject *op, intp *step_size, intp *n_steps, intp max) +{ + intp index; + + if (op == Py_None) { + *n_steps = PseudoIndex; + index = 0; + } else if (op == Py_Ellipsis) { + *n_steps = RubberIndex; + index = 0; + } else if (PySlice_Check(op)) { + intp stop; + if (slice_GetIndices((PySliceObject *)op, max, + &index, &stop, step_size, n_steps) < 0) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_IndexError, + "invalid slice"); + } + goto fail; + } + if (*n_steps <= 0) { + *n_steps = 0; + *step_size = 1; + index = 0; + } + } else { + index = PyArray_PyIntAsIntp(op); + if (error_converting(index)) { + PyErr_SetString(PyExc_IndexError, + "each subindex must be either a "\ + "slice, an integer, Ellipsis, or "\ + "newaxis"); + goto fail; + } + *n_steps = SingleIndex; + *step_size = 0; + if (index < 0) index += max; + if (index >= max || index < 0) { + PyErr_SetString(PyExc_IndexError, "invalid index"); + goto fail; + } + } + return index; + fail: + return -1; +} + + +static int +parse_index(PyArrayObject *self, PyObject *op, + intp *dimensions, intp *strides, intp *offset_ptr) +{ + int i, j, n; + int nd_old, nd_new, n_add, n_pseudo; + intp n_steps, start, offset, step_size; + PyObject *op1=NULL; + int is_slice; + + + if (PySlice_Check(op) || op == Py_Ellipsis || op == Py_None) { + n = 1; + op1 = op; + Py_INCREF(op); + /* this relies on the fact that n==1 for loop below */ + is_slice = 1; + } + else { + if (!PySequence_Check(op)) { + PyErr_SetString(PyExc_IndexError, + "index must be either an int "\ + "or a sequence"); + return -1; + } + n = PySequence_Length(op); + is_slice = 0; + } + + nd_old = nd_new = 0; + + offset = 0; + for(i=0; i<n; i++) { + if (!is_slice) { + if (!(op1=PySequence_GetItem(op, i))) { + PyErr_SetString(PyExc_IndexError, + "invalid index"); + return -1; + } + } + + start = parse_subindex(op1, &step_size, &n_steps, + nd_old < self->nd ? \ + self->dimensions[nd_old] : 0); + Py_DECREF(op1); + if (start == -1) break; + + if (n_steps == PseudoIndex) { + dimensions[nd_new] = 1; strides[nd_new] = 0; nd_new++; + } else { + if (n_steps == RubberIndex) { + for(j=i+1, n_pseudo=0; j<n; j++) { + op1 = PySequence_GetItem(op, j); + if (op1 == Py_None) n_pseudo++; + Py_DECREF(op1); + } + n_add = self->nd-(n-i-n_pseudo-1+nd_old); + if (n_add < 0) { + PyErr_SetString(PyExc_IndexError, + "too many indices"); + return -1; + } + for(j=0; j<n_add; j++) { + dimensions[nd_new] = \ + self->dimensions[nd_old]; + strides[nd_new] = \ + self->strides[nd_old]; + nd_new++; nd_old++; + } + } else { + if (nd_old >= self->nd) { + PyErr_SetString(PyExc_IndexError, + "too many indices"); + return -1; + } + offset += self->strides[nd_old]*start; + nd_old++; + if (n_steps != SingleIndex) { + dimensions[nd_new] = n_steps; + strides[nd_new] = step_size * \ + self->strides[nd_old-1]; + nd_new++; + } + } + } + } + if (i < n) return -1; + n_add = self->nd-nd_old; + for(j=0; j<n_add; j++) { + dimensions[nd_new] = self->dimensions[nd_old]; + strides[nd_new] = self->strides[nd_old]; + nd_new++; nd_old++; + } + *offset_ptr = offset; + return nd_new; +} + +static void +_swap_axes(PyArrayMapIterObject *mit, PyArrayObject **ret) +{ + PyObject *new; + int n1, n2, n3, val; + int i; + PyArray_Dims permute; + intp d[MAX_DIMS]; + + permute.ptr = d; + permute.len = mit->nd; + + /* tuple for transpose is + (n1,..,n1+n2-1,0,..,n1-1,n1+n2,...,n3-1) + n1 is the number of dimensions of + the broadcasted index array + n2 is the number of dimensions skipped at the + start + n3 is the number of dimensions of the + result + */ + n1 = mit->iters[0]->nd_m1 + 1; + n2 = mit->iteraxes[0]; + n3 = mit->nd; + val = n1; + i = 0; + while(val < n1+n2) + permute.ptr[i++] = val++; + val = 0; + while(val < n1) + permute.ptr[i++] = val++; + val = n1+n2; + while(val < n3) + permute.ptr[i++] = val++; + + new = PyArray_Transpose(*ret, &permute); + Py_DECREF(*ret); + *ret = (PyArrayObject *)new; +} + +/* Prototypes for Mapping calls --- not part of the C-API + because only useful as part of a getitem call. +*/ + +static void PyArray_MapIterReset(PyArrayMapIterObject *); +static void PyArray_MapIterNext(PyArrayMapIterObject *); +static void PyArray_MapIterBind(PyArrayMapIterObject *, PyArrayObject *); +static PyObject* PyArray_MapIterNew(PyObject *, int); + +static PyObject * +PyArray_GetMap(PyArrayMapIterObject *mit) +{ + + PyArrayObject *ret, *temp; + PyArrayIterObject *it; + int index; + int swap; + PyArray_CopySwapFunc *copyswap; + + /* Unbound map iterator --- Bind should have been called */ + if (mit->ait == NULL) return NULL; + + /* This relies on the map iterator object telling us the shape + of the new array in nd and dimensions. + */ + temp = mit->ait->ao; + Py_INCREF(temp->descr); + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(temp->ob_type, + temp->descr, + mit->nd, mit->dimensions, + NULL, NULL, + PyArray_ISFORTRAN(temp), + (PyObject *)temp); + if (ret == NULL) return NULL; + + /* Now just iterate through the new array filling it in + with the next object from the original array as + defined by the mapping iterator */ + + if ((it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ret)) + == NULL) { + Py_DECREF(ret); + return NULL; + } + index = it->size; + swap = (PyArray_ISNOTSWAPPED(temp) != PyArray_ISNOTSWAPPED(ret)); + copyswap = ret->descr->f->copyswap; + PyArray_MapIterReset(mit); + while (index--) { + copyswap(it->dataptr, mit->dataptr, swap, ret->descr->elsize); + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + + /* check for consecutive axes */ + if ((mit->subspace != NULL) && (mit->consec)) { + if (mit->iteraxes[0] > 0) { /* then we need to swap */ + _swap_axes(mit, &ret); + } + } + return (PyObject *)ret; +} + +static int +PyArray_SetMap(PyArrayMapIterObject *mit, PyObject *op) +{ + PyObject *arr=NULL; + PyArrayIterObject *it; + int index; + int swap; + PyArray_CopySwapFunc *copyswap; + PyArray_Descr *descr; + + /* Unbound Map Iterator */ + if (mit->ait == NULL) return -1; + + descr = mit->ait->ao->descr; + Py_INCREF(descr); + arr = PyArray_FromAny(op, descr, 0, 0, FORCECAST); + if (arr == NULL) return -1; + + if ((mit->subspace != NULL) && (mit->consec)) { + if (mit->iteraxes[0] > 0) { /* then we need to swap */ + _swap_axes(mit, (PyArrayObject **)&arr); + } + } + + if ((it = (PyArrayIterObject *)PyArray_IterNew(arr))==NULL) { + Py_DECREF(arr); + return -1; + } + + index = mit->size; + swap = (PyArray_ISNOTSWAPPED(mit->ait->ao) != \ + (PyArray_ISNOTSWAPPED(arr))); + + copyswap = PyArray_DESCR(arr)->f->copyswap; + PyArray_MapIterReset(mit); + /* Need to decref OBJECT arrays */ + if (PyTypeNum_ISOBJECT(descr->type_num)) { + while (index--) { + Py_XDECREF(*((PyObject **)mit->dataptr)); + Py_INCREF(*((PyObject **)it->dataptr)); + memmove(mit->dataptr, it->dataptr, sizeof(PyObject *)); + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + if (it->index == it->size) + PyArray_ITER_RESET(it); + } + Py_DECREF(arr); + Py_DECREF(it); + return 0; + } + while(index--) { + memmove(mit->dataptr, it->dataptr, PyArray_ITEMSIZE(arr)); + copyswap(mit->dataptr, NULL, swap, PyArray_ITEMSIZE(arr)); + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + if (it->index == it->size) + PyArray_ITER_RESET(it); + } + Py_DECREF(arr); + Py_DECREF(it); + return 0; +} + +/* Called when treating array object like a mapping -- called first from + Python when using a[object] unless object is a standard slice object + (not an extended one). + +*/ + +/* There are two situations: + + 1 - the subscript is a standard view and a reference to the + array can be returned + + 2 - the subscript uses Boolean masks or integer indexing and + therefore a new array is created and returned. + +*/ + +/* Always returns arrays */ + +static PyObject *iter_subscript(PyArrayIterObject *, PyObject *); + +static PyObject * +array_subscript(PyArrayObject *self, PyObject *op) +{ + intp dimensions[MAX_DIMS], strides[MAX_DIMS]; + intp offset; + int nd, oned; + intp i; + PyArrayObject *other; + PyArrayMapIterObject *mit; + + if (PyString_Check(op) || PyUnicode_Check(op)) { + if (self->descr->fields) { + PyObject *obj; + obj = PyDict_GetItem(self->descr->fields, op); + if (obj != NULL) { + PyArray_Descr *descr; + int offset; + PyObject *title; + + if (PyArg_ParseTuple(obj, "Oi|O", + &descr, &offset, &title)) { + Py_INCREF(descr); + return PyArray_GetField(self, descr, + offset); + } + } + } + + PyErr_Format(PyExc_ValueError, + "field named %s not found.", + PyString_AsString(op)); + return NULL; + } + if (self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed."); + return NULL; + } + if (PyArray_IsScalar(op, Integer) || PyInt_Check(op) || \ + PyLong_Check(op)) { + intp value; + value = PyArray_PyIntAsIntp(op); + if (PyErr_Occurred()) + PyErr_Clear(); + else if (value >= 0) { + return array_big_item(self, value); + } + else /* (value < 0) */ { + value += self->dimensions[0]; + return array_big_item(self, value); + } + } + + oned = ((self->nd == 1) && !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); + + /* wrap arguments into a mapiter object */ + mit = (PyArrayMapIterObject *)PyArray_MapIterNew(op, oned); + if (mit == NULL) return NULL; + if (!mit->view) { /* fancy indexing */ + if (oned) { + PyArrayIterObject *it; + PyObject *rval; + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it == NULL) {Py_DECREF(mit); return NULL;} + rval = iter_subscript(it, mit->indexobj); + Py_DECREF(it); + Py_DECREF(mit); + return rval; + } + PyArray_MapIterBind(mit, self); + other = (PyArrayObject *)PyArray_GetMap(mit); + Py_DECREF(mit); + return (PyObject *)other; + } + Py_DECREF(mit); + + i = PyArray_PyIntAsIntp(op); + if (!error_converting(i)) { + if (i < 0 && self->nd > 0) i = i+self->dimensions[0]; + return array_big_item(self, i); + } + PyErr_Clear(); + + /* Standard (view-based) Indexing */ + if ((nd = parse_index(self, op, dimensions, strides, &offset)) + == -1) + return NULL; + + /* This will only work if new array will be a view */ + Py_INCREF(self->descr); + if ((other = (PyArrayObject *) \ + PyArray_NewFromDescr(self->ob_type, self->descr, + nd, dimensions, + strides, self->data+offset, + self->flags, + (PyObject *)self)) == NULL) + return NULL; + + + other->base = (PyObject *)self; + Py_INCREF(self); + + PyArray_UpdateFlags(other, UPDATE_ALL_FLAGS); + + return (PyObject *)other; +} + + +/* Another assignment hacked by using CopyObject. */ + +/* This only works if subscript returns a standard view. */ + +/* Again there are two cases. In the first case, PyArray_CopyObject + can be used. In the second case, a new indexing function has to be + used. +*/ + +static int iter_ass_subscript(PyArrayIterObject *, PyObject *, PyObject *); + +static int +array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op) +{ + int ret, oned; + intp i; + PyArrayObject *tmp; + PyArrayMapIterObject *mit; + + if (op == NULL) { + PyErr_SetString(PyExc_ValueError, + "cannot delete array elements"); + return -1; + } + if (!PyArray_ISWRITEABLE(self)) { + PyErr_SetString(PyExc_RuntimeError, + "array is not writeable"); + return -1; + } + + if (PyArray_IsScalar(index, Integer) || PyInt_Check(index) || \ + PyLong_Check(index)) { + intp value; + value = PyArray_PyIntAsIntp(index); + if (PyErr_Occurred()) + PyErr_Clear(); + else + return array_ass_big_item(self, value, op); + } + + if (PyString_Check(index) || PyUnicode_Check(index)) { + if (self->descr->fields) { + PyObject *obj; + obj = PyDict_GetItem(self->descr->fields, index); + if (obj != NULL) { + PyArray_Descr *descr; + int offset; + PyObject *title; + + if (PyArg_ParseTuple(obj, "Oi|O", + &descr, &offset, &title)) { + Py_INCREF(descr); + return PyArray_SetField(self, descr, + offset, op); + } + } + } + + PyErr_Format(PyExc_ValueError, + "field named %s not found.", + PyString_AsString(index)); + return -1; + } + + if (self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed."); + return -1; + } + + oned = ((self->nd == 1) && !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); + + mit = (PyArrayMapIterObject *)PyArray_MapIterNew(index, oned); + if (mit == NULL) return -1; + if (!mit->view) { + if (oned) { + PyArrayIterObject *it; + int rval; + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it == NULL) {Py_DECREF(mit); return -1;} + rval = iter_ass_subscript(it, mit->indexobj, op); + Py_DECREF(it); + Py_DECREF(mit); + return rval; + } + PyArray_MapIterBind(mit, self); + ret = PyArray_SetMap(mit, op); + Py_DECREF(mit); + return ret; + } + Py_DECREF(mit); + + i = PyArray_PyIntAsIntp(index); + if (!error_converting(i)) { + return array_ass_big_item(self, i, op); + } + PyErr_Clear(); + + /* Rest of standard (view-based) indexing */ + + if ((tmp = (PyArrayObject *)array_subscript(self, index)) == NULL) + return -1; + if (PyArray_ISOBJECT(self) && (tmp->nd == 0)) { + ret = tmp->descr->f->setitem(op, tmp->data, tmp); + } + else { + ret = PyArray_CopyObject(tmp, op); + } + Py_DECREF(tmp); + return ret; +} + +/* There are places that require that array_subscript return a PyArrayObject + and not possibly a scalar. Thus, this is the function exposed to + Python so that 0-dim arrays are passed as scalars +*/ + +static PyObject * +array_subscript_nice(PyArrayObject *self, PyObject *op) +{ + return PyArray_Return((PyArrayObject *)array_subscript(self, op)); +} + + +static PyMappingMethods array_as_mapping = { + (inquiry)array_length, /*mp_length*/ + (binaryfunc)array_subscript_nice, /*mp_subscript*/ + (objobjargproc)array_ass_sub, /*mp_ass_subscript*/ +}; + +/****************** End of Mapping Protocol ******************************/ + + +/************************************************************************* + **************** Implement Buffer Protocol **************************** + *************************************************************************/ + +/* removed multiple segment interface */ + +static int +array_getsegcount(PyArrayObject *self, int *lenp) +{ + if (lenp) + *lenp = PyArray_NBYTES(self); + + if (PyArray_ISONESEGMENT(self)) { + return 1; + } + + if (lenp) + *lenp = 0; + return 0; +} + +static int +array_getreadbuf(PyArrayObject *self, int segment, void **ptrptr) +{ + if (segment != 0) { + PyErr_SetString(PyExc_ValueError, + "accessing non-existing array segment"); + return -1; + } + + if (PyArray_ISONESEGMENT(self)) { + *ptrptr = self->data; + return PyArray_NBYTES(self); + } + PyErr_SetString(PyExc_ValueError, "array is not a single segment"); + *ptrptr = NULL; + return -1; +} + + +static int +array_getwritebuf(PyArrayObject *self, int segment, void **ptrptr) +{ + if (PyArray_CHKFLAGS(self, WRITEABLE)) + return array_getreadbuf(self, segment, (void **) ptrptr); + else { + PyErr_SetString(PyExc_ValueError, "array cannot be "\ + "accessed as a writeable buffer"); + return -1; + } +} + +static int +array_getcharbuf(PyArrayObject *self, int segment, const char **ptrptr) +{ + if (self->descr->type_num == PyArray_STRING || \ + self->descr->type_num == PyArray_UNICODE) + return array_getreadbuf(self, segment, (void **) ptrptr); + else { + PyErr_SetString(PyExc_TypeError, + "non-character array cannot be interpreted "\ + "as character buffer"); + return -1; + } +} + +static PyBufferProcs array_as_buffer = { + (getreadbufferproc)array_getreadbuf, /*bf_getreadbuffer*/ + (getwritebufferproc)array_getwritebuf, /*bf_getwritebuffer*/ + (getsegcountproc)array_getsegcount, /*bf_getsegcount*/ + (getcharbufferproc)array_getcharbuf, /*bf_getcharbuffer*/ +}; + +/****************** End of Buffer Protocol *******************************/ + + +/************************************************************************* + **************** Implement Number Protocol **************************** + *************************************************************************/ + + +typedef struct { + PyObject *add, + *subtract, + *multiply, + *divide, + *remainder, + *power, + *sqrt, + *negative, + *absolute, + *invert, + *left_shift, + *right_shift, + *bitwise_and, + *bitwise_xor, + *bitwise_or, + *less, + *less_equal, + *equal, + *not_equal, + *greater, + *greater_equal, + *floor_divide, + *true_divide, + *logical_or, + *logical_and, + *floor, + *ceil, + *maximum, + *minimum; + +} NumericOps; + +static NumericOps n_ops = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL}; + +/* Dictionary can contain any of the numeric operations, by name. + Those not present will not be changed + */ + +#define SET(op) temp=PyDict_GetItemString(dict, #op); \ + if (temp != NULL) { \ + if (!(PyCallable_Check(temp))) return -1; \ + Py_XDECREF(n_ops.op); \ + n_ops.op = temp; \ + } + + +/*OBJECT_API + Set internal structure with number functions that all arrays will use +*/ +int +PyArray_SetNumericOps(PyObject *dict) +{ + PyObject *temp = NULL; + SET(add); + SET(subtract); + SET(multiply); + SET(divide); + SET(remainder); + SET(power); + SET(sqrt); + SET(negative); + SET(absolute); + SET(invert); + SET(left_shift); + SET(right_shift); + SET(bitwise_and); + SET(bitwise_or); + SET(bitwise_xor); + SET(less); + SET(less_equal); + SET(equal); + SET(not_equal); + SET(greater); + SET(greater_equal); + SET(floor_divide); + SET(true_divide); + SET(logical_or); + SET(logical_and); + SET(floor); + SET(ceil); + SET(maximum); + SET(minimum); + return 0; +} + +#define GET(op) if (n_ops.op && \ + (PyDict_SetItemString(dict, #op, n_ops.op)==-1)) \ + goto fail; + +/*OBJECT_API + Get dictionary showing number functions that all arrays will use +*/ +static PyObject * +PyArray_GetNumericOps(void) +{ + PyObject *dict; + if ((dict = PyDict_New())==NULL) + return NULL; + GET(add); + GET(subtract); + GET(multiply); + GET(divide); + GET(remainder); + GET(power); + GET(sqrt); + GET(negative); + GET(absolute); + GET(invert); + GET(left_shift); + GET(right_shift); + GET(bitwise_and); + GET(bitwise_or); + GET(bitwise_xor); + GET(less); + GET(less_equal); + GET(equal); + GET(not_equal); + GET(greater); + GET(greater_equal); + GET(floor_divide); + GET(true_divide); + GET(logical_or); + GET(logical_and); + GET(floor); + GET(ceil); + GET(maximum); + GET(minimum); + return dict; + + fail: + Py_DECREF(dict); + return NULL; +} + +static PyObject * +PyArray_GenericReduceFunction(PyArrayObject *m1, PyObject *op, int axis, + int rtype) +{ + PyObject *args, *ret=NULL, *meth; + if (op == NULL) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (rtype == PyArray_NOTYPE) + args = Py_BuildValue("(Oi)", m1, axis); + else { + PyArray_Descr *descr; + descr = PyArray_DescrFromType(rtype); + args = Py_BuildValue("(Oic)", m1, axis, descr->type); + Py_DECREF(descr); + } + meth = PyObject_GetAttrString(op, "reduce"); + if (meth && PyCallable_Check(meth)) { + ret = PyObject_Call(meth, args, NULL); + } + Py_DECREF(args); + Py_DECREF(meth); + return ret; +} + + +static PyObject * +PyArray_GenericAccumulateFunction(PyArrayObject *m1, PyObject *op, int axis, + int rtype) +{ + PyObject *args, *ret=NULL, *meth; + if (op == NULL) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (rtype == PyArray_NOTYPE) + args = Py_BuildValue("(Oi)", m1, axis); + else { + PyArray_Descr *descr; + descr = PyArray_DescrFromType(rtype); + args = Py_BuildValue("(Oic)", m1, axis, descr->type); + Py_DECREF(descr); + } + meth = PyObject_GetAttrString(op, "accumulate"); + if (meth && PyCallable_Check(meth)) { + ret = PyObject_Call(meth, args, NULL); + } + Py_DECREF(args); + Py_DECREF(meth); + return ret; +} + + +static PyObject * +PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op) +{ + if (op == NULL) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyObject_CallFunction(op, "OO", m1, m2); +} + +static PyObject * +PyArray_GenericUnaryFunction(PyArrayObject *m1, PyObject *op) +{ + if (op == NULL) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyObject_CallFunction(op, "(O)", m1); +} + +static PyObject * +PyArray_GenericInplaceBinaryFunction(PyArrayObject *m1, + PyObject *m2, PyObject *op) +{ + if (op == NULL) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyObject_CallFunction(op, "OOO", m1, m2, m1); +} + +static PyObject * +array_add(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.add); +} + +static PyObject * +array_subtract(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.subtract); +} + +static PyObject * +array_multiply(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.multiply); +} + +static PyObject * +array_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.divide); +} + +static PyObject * +array_remainder(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.remainder); +} + +static PyObject * +array_power(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.power); +} + +static PyObject * +array_negative(PyArrayObject *m1) +{ + return PyArray_GenericUnaryFunction(m1, n_ops.negative); +} + +static PyObject * +array_absolute(PyArrayObject *m1) +{ + return PyArray_GenericUnaryFunction(m1, n_ops.absolute); +} + +static PyObject * +array_invert(PyArrayObject *m1) +{ + return PyArray_GenericUnaryFunction(m1, n_ops.invert); +} + +static PyObject * +array_left_shift(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.left_shift); +} + +static PyObject * +array_right_shift(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.right_shift); +} + +static PyObject * +array_bitwise_and(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_and); +} + +static PyObject * +array_bitwise_or(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_or); +} + +static PyObject * +array_bitwise_xor(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.bitwise_xor); +} + +static PyObject * +array_inplace_add(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.add); +} + +static PyObject * +array_inplace_subtract(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.subtract); +} + +static PyObject * +array_inplace_multiply(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.multiply); +} + +static PyObject * +array_inplace_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.divide); +} + +static PyObject * +array_inplace_remainder(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.remainder); +} + +static PyObject * +array_inplace_power(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.power); +} + +static PyObject * +array_inplace_left_shift(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.left_shift); +} + +static PyObject * +array_inplace_right_shift(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.right_shift); +} + +static PyObject * +array_inplace_bitwise_and(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_and); +} + +static PyObject * +array_inplace_bitwise_or(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_or); +} + +static PyObject * +array_inplace_bitwise_xor(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.bitwise_xor); +} + +static PyObject * +array_floor_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.floor_divide); +} + +static PyObject * +array_true_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericBinaryFunction(m1, m2, n_ops.true_divide); +} + +static PyObject * +array_inplace_floor_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, + n_ops.floor_divide); +} + +static PyObject * +array_inplace_true_divide(PyArrayObject *m1, PyObject *m2) +{ + return PyArray_GenericInplaceBinaryFunction(m1, m2, + n_ops.true_divide); +} + +/* Array evaluates as "TRUE" if any of the elements are non-zero*/ +static int +array_any_nonzero(PyArrayObject *mp) +{ + intp index; + PyArrayIterObject *it; + Bool anyTRUE = FALSE; + + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp); + if (it==NULL) return anyTRUE; + index = it->size; + while(index--) { + if (mp->descr->f->nonzero(it->dataptr, mp)) { + anyTRUE = TRUE; + break; + } + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + return anyTRUE; +} + +static int +_array_nonzero(PyArrayObject *mp) +{ + intp n; + n = PyArray_SIZE(mp); + if (n == 1) { + return mp->descr->f->nonzero(mp->data, mp); + } + else if (n == 0) { + return 0; + } + else { + PyErr_SetString(PyExc_ValueError, + "The truth value of an array " \ + "with more than one element is ambiguous. " \ + "Use a.any() or a.all()"); + return -1; + } +} + + + +static PyObject * +array_divmod(PyArrayObject *op1, PyObject *op2) +{ + PyObject *divp, *modp, *result; + + divp = array_floor_divide(op1, op2); + if (divp == NULL) return NULL; + modp = array_remainder(op1, op2); + if (modp == NULL) { + Py_DECREF(divp); + return NULL; + } + result = Py_BuildValue("OO", divp, modp); + Py_DECREF(divp); + Py_DECREF(modp); + return result; +} + + +static PyObject * +array_int(PyArrayObject *v) +{ + PyObject *pv, *pv2; + if (PyArray_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, "only length-1 arrays can be"\ + " converted to Python scalars"); + return NULL; + } + pv = v->descr->f->getitem(v->data, v); + if (pv == NULL) return NULL; + if (pv->ob_type->tp_as_number == 0) { + PyErr_SetString(PyExc_TypeError, "cannot convert to an int; "\ + "scalar object is not a number"); + Py_DECREF(pv); + return NULL; + } + if (pv->ob_type->tp_as_number->nb_int == 0) { + PyErr_SetString(PyExc_TypeError, "don't know how to convert "\ + "scalar number to int"); + Py_DECREF(pv); + return NULL; + } + + pv2 = pv->ob_type->tp_as_number->nb_int(pv); + Py_DECREF(pv); + return pv2; +} + +static PyObject * +array_float(PyArrayObject *v) +{ + PyObject *pv, *pv2; + if (PyArray_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, "only length-1 arrays can "\ + "be converted to Python scalars"); + return NULL; + } + pv = v->descr->f->getitem(v->data, v); + if (pv == NULL) return NULL; + if (pv->ob_type->tp_as_number == 0) { + PyErr_SetString(PyExc_TypeError, "cannot convert to an "\ + "int; scalar object is not a number"); + Py_DECREF(pv); + return NULL; + } + if (pv->ob_type->tp_as_number->nb_float == 0) { + PyErr_SetString(PyExc_TypeError, "don't know how to convert "\ + "scalar number to float"); + Py_DECREF(pv); + return NULL; + } + pv2 = pv->ob_type->tp_as_number->nb_float(pv); + Py_DECREF(pv); + return pv2; +} + +static PyObject * +array_long(PyArrayObject *v) +{ + PyObject *pv, *pv2; + if (PyArray_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, "only length-1 arrays can "\ + "be converted to Python scalars"); + return NULL; + } + pv = v->descr->f->getitem(v->data, v); + if (pv->ob_type->tp_as_number == 0) { + PyErr_SetString(PyExc_TypeError, "cannot convert to an int; "\ + "scalar object is not a number"); + return NULL; + } + if (pv->ob_type->tp_as_number->nb_long == 0) { + PyErr_SetString(PyExc_TypeError, "don't know how to convert "\ + "scalar number to long"); + return NULL; + } + pv2 = pv->ob_type->tp_as_number->nb_long(pv); + Py_DECREF(pv); + return pv2; +} + +static PyObject * +array_oct(PyArrayObject *v) +{ + PyObject *pv, *pv2; + if (PyArray_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, "only length-1 arrays can "\ + "be converted to Python scalars"); + return NULL; + } + pv = v->descr->f->getitem(v->data, v); + if (pv->ob_type->tp_as_number == 0) { + PyErr_SetString(PyExc_TypeError, "cannot convert to an int; "\ + "scalar object is not a number"); + return NULL; + } + if (pv->ob_type->tp_as_number->nb_oct == 0) { + PyErr_SetString(PyExc_TypeError, "don't know how to convert "\ + "scalar number to oct"); + return NULL; + } + pv2 = pv->ob_type->tp_as_number->nb_oct(pv); + Py_DECREF(pv); + return pv2; +} + +static PyObject * +array_hex(PyArrayObject *v) +{ + PyObject *pv, *pv2; + if (PyArray_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, "only length-1 arrays can "\ + "be converted to Python scalars"); + return NULL; + } + pv = v->descr->f->getitem(v->data, v); + if (pv->ob_type->tp_as_number == 0) { + PyErr_SetString(PyExc_TypeError, "cannot convert to an int; "\ + "scalar object is not a number"); + return NULL; + } + if (pv->ob_type->tp_as_number->nb_hex == 0) { + PyErr_SetString(PyExc_TypeError, "don't know how to convert "\ + "scalar number to hex"); + return NULL; + } + pv2 = pv->ob_type->tp_as_number->nb_hex(pv); + Py_DECREF(pv); + return pv2; +} + +static PyObject * +_array_copy_nice(PyArrayObject *self) +{ + return PyArray_Return((PyArrayObject *) \ + PyArray_Copy(self)); +} + +static PyNumberMethods array_as_number = { + (binaryfunc)array_add, /*nb_add*/ + (binaryfunc)array_subtract, /*nb_subtract*/ + (binaryfunc)array_multiply, /*nb_multiply*/ + (binaryfunc)array_divide, /*nb_divide*/ + (binaryfunc)array_remainder, /*nb_remainder*/ + (binaryfunc)array_divmod, /*nb_divmod*/ + (ternaryfunc)array_power, /*nb_power*/ + (unaryfunc)array_negative, /*nb_neg*/ + (unaryfunc)_array_copy_nice, /*nb_pos*/ + (unaryfunc)array_absolute, /*(unaryfunc)array_abs,*/ + (inquiry)_array_nonzero, /*nb_nonzero*/ + (unaryfunc)array_invert, /*nb_invert*/ + (binaryfunc)array_left_shift, /*nb_lshift*/ + (binaryfunc)array_right_shift, /*nb_rshift*/ + (binaryfunc)array_bitwise_and, /*nb_and*/ + (binaryfunc)array_bitwise_xor, /*nb_xor*/ + (binaryfunc)array_bitwise_or, /*nb_or*/ + 0, /*nb_coerce*/ + (unaryfunc)array_int, /*nb_int*/ + (unaryfunc)array_long, /*nb_long*/ + (unaryfunc)array_float, /*nb_float*/ + (unaryfunc)array_oct, /*nb_oct*/ + (unaryfunc)array_hex, /*nb_hex*/ + + /*This code adds augmented assignment functionality*/ + /*that was made available in Python 2.0*/ + (binaryfunc)array_inplace_add, /*inplace_add*/ + (binaryfunc)array_inplace_subtract, /*inplace_subtract*/ + (binaryfunc)array_inplace_multiply, /*inplace_multiply*/ + (binaryfunc)array_inplace_divide, /*inplace_divide*/ + (binaryfunc)array_inplace_remainder, /*inplace_remainder*/ + (ternaryfunc)array_inplace_power, /*inplace_power*/ + (binaryfunc)array_inplace_left_shift, /*inplace_lshift*/ + (binaryfunc)array_inplace_right_shift, /*inplace_rshift*/ + (binaryfunc)array_inplace_bitwise_and, /*inplace_and*/ + (binaryfunc)array_inplace_bitwise_xor, /*inplace_xor*/ + (binaryfunc)array_inplace_bitwise_or, /*inplace_or*/ + + (binaryfunc)array_floor_divide, /*nb_floor_divide*/ + (binaryfunc)array_true_divide, /*nb_true_divide*/ + (binaryfunc)array_inplace_floor_divide, /*nb_inplace_floor_divide*/ + (binaryfunc)array_inplace_true_divide, /*nb_inplace_true_divide*/ + +}; + +/****************** End of Buffer Protocol *******************************/ + + +/************************************************************************* + **************** Implement Sequence Protocol ************************** + *************************************************************************/ + +/* Some of this is repeated in the array_as_mapping protocol. But + we fill it in here so that PySequence_XXXX calls work as expected +*/ + + +static PyObject * +array_slice(PyArrayObject *self, int ilow, int ihigh) +{ + PyArrayObject *r; + int l; + char *data; + + if (self->nd == 0) { + PyErr_SetString(PyExc_ValueError, "cannot slice a scalar"); + return NULL; + } + + l=self->dimensions[0]; + if (ihigh < 0) ihigh += l; + if (ilow < 0) ilow += l; + if (ilow < 0) ilow = 0; + else if (ilow > l) ilow = l; + if (ihigh < 0) ihigh = 0; + else if (ihigh > l) ihigh = l; + if (ihigh < ilow) ihigh = ilow; + + if (ihigh != ilow) { + data = index2ptr(self, ilow); + if (data == NULL) return NULL; + } else { + data = self->data; + } + + self->dimensions[0] = ihigh-ilow; + Py_INCREF(self->descr); + r = (PyArrayObject *) \ + PyArray_NewFromDescr(self->ob_type, self->descr, + self->nd, self->dimensions, + self->strides, data, + self->flags, (PyObject *)self); + + self->dimensions[0] = l; + r->base = (PyObject *)self; + Py_INCREF(self); + PyArray_UpdateFlags(r, UPDATE_ALL_FLAGS); + return (PyObject *)r; +} + + +static int +array_ass_slice(PyArrayObject *self, int ilow, int ihigh, PyObject *v) { + int ret; + PyArrayObject *tmp; + + if (v == NULL) { + PyErr_SetString(PyExc_ValueError, + "cannot delete array elements"); + return -1; + } + if (!PyArray_ISWRITEABLE(self)) { + PyErr_SetString(PyExc_RuntimeError, + "array is not writeable"); + return -1; + } + if ((tmp = (PyArrayObject *)array_slice(self, ilow, ihigh)) \ + == NULL) + return -1; + ret = PyArray_CopyObject(tmp, v); + Py_DECREF(tmp); + + return ret; +} + +static int +array_contains(PyArrayObject *self, PyObject *el) +{ + /* equivalent to (self == el).any() */ + + PyObject *res; + int ret; + + res = PyArray_EnsureArray(PyObject_RichCompare((PyObject *)self, el, Py_EQ)); + if (res == NULL) return -1; + ret = array_any_nonzero((PyArrayObject *)res); + Py_DECREF(res); + return ret; +} + + +static PySequenceMethods array_as_sequence = { + (inquiry)array_length, /*sq_length*/ + (binaryfunc)NULL, /* sq_concat is handled by nb_add*/ + (intargfunc)NULL, /* sq_repeat is handled nb_multiply*/ + (intargfunc)array_item_nice, /*sq_item*/ + (intintargfunc)array_slice, /*sq_slice*/ + (intobjargproc)array_ass_item, /*sq_ass_item*/ + (intintobjargproc)array_ass_slice, /*sq_ass_slice*/ + (objobjproc) array_contains, /* sq_contains */ + (binaryfunc) NULL, /* sg_inplace_concat */ + (intargfunc) NULL /* sg_inplace_repeat */ +}; + + +/****************** End of Sequence Protocol ****************************/ + + +static int +dump_data(char **string, int *n, int *max_n, char *data, int nd, + intp *dimensions, intp *strides, PyArrayObject* self) +{ + PyArray_Descr *descr=self->descr; + PyObject *op, *sp; + char *ostring; + int i, N; + +#define CHECK_MEMORY if (*n >= *max_n-16) { *max_n *= 2; \ + *string = (char *)_pya_realloc(*string, *max_n); } + + if (nd == 0) { + + if ((op = descr->f->getitem(data, self)) == NULL) return -1; + sp = PyObject_Repr(op); + if (sp == NULL) {Py_DECREF(op); return -1;} + ostring = PyString_AsString(sp); + N = PyString_Size(sp)*sizeof(char); + *n += N; + CHECK_MEMORY + memmove(*string+(*n-N), ostring, N); + Py_DECREF(sp); + Py_DECREF(op); + return 0; + } else { + CHECK_MEMORY + (*string)[*n] = '['; + *n += 1; + for(i=0; i<dimensions[0]; i++) { + if (dump_data(string, n, max_n, + data+(*strides)*i, + nd-1, dimensions+1, + strides+1, self) < 0) + return -1; + CHECK_MEMORY + if (i<dimensions[0]-1) { + (*string)[*n] = ','; + (*string)[*n+1] = ' '; + *n += 2; + } + } + CHECK_MEMORY + (*string)[*n] = ']'; *n += 1; + return 0; + } + +#undef CHECK_MEMORY +} + +static PyObject * +array_repr_builtin(PyArrayObject *self) +{ + PyObject *ret; + char *string; + int n, max_n; + + max_n = PyArray_NBYTES(self)*4*sizeof(char) + 7; + + if ((string = (char *)_pya_malloc(max_n)) == NULL) { + PyErr_SetString(PyExc_MemoryError, "out of memory"); + return NULL; + } + + n = 6; + sprintf(string, "array("); + + if (dump_data(&string, &n, &max_n, self->data, + self->nd, self->dimensions, + self->strides, self) < 0) { + _pya_free(string); return NULL; + } + + if (PyArray_ISEXTENDED(self)) { + char buf[100]; + snprintf(buf, sizeof(buf), "%d", self->descr->elsize); + sprintf(string+n, ", '%c%s')", self->descr->type, buf); + ret = PyString_FromStringAndSize(string, n+6+strlen(buf)); + } + else { + sprintf(string+n, ", '%c')", self->descr->type); + ret = PyString_FromStringAndSize(string, n+6); + } + + + _pya_free(string); + return ret; +} + +static PyObject *PyArray_StrFunction=NULL; +static PyObject *PyArray_ReprFunction=NULL; + +/*OBJECT_API + Set the array print function to be a Python function. +*/ +static void +PyArray_SetStringFunction(PyObject *op, int repr) +{ + if (repr) { + /* Dispose of previous callback */ + Py_XDECREF(PyArray_ReprFunction); + /* Add a reference to new callback */ + Py_XINCREF(op); + /* Remember new callback */ + PyArray_ReprFunction = op; + } else { + /* Dispose of previous callback */ + Py_XDECREF(PyArray_StrFunction); + /* Add a reference to new callback */ + Py_XINCREF(op); + /* Remember new callback */ + PyArray_StrFunction = op; + } +} + +static PyObject * +array_repr(PyArrayObject *self) +{ + PyObject *s, *arglist; + + if (PyArray_ReprFunction == NULL) { + s = array_repr_builtin(self); + } else { + arglist = Py_BuildValue("(O)", self); + s = PyEval_CallObject(PyArray_ReprFunction, arglist); + Py_DECREF(arglist); + } + return s; +} + +static PyObject * +array_str(PyArrayObject *self) +{ + PyObject *s, *arglist; + + if (PyArray_StrFunction == NULL) { + s = array_repr(self); + } else { + arglist = Py_BuildValue("(O)", self); + s = PyEval_CallObject(PyArray_StrFunction, arglist); + Py_DECREF(arglist); + } + return s; +} + +static PyObject * +array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) +{ + PyObject *array_other, *result; + + switch (cmp_op) + { + case Py_LT: + return PyArray_GenericBinaryFunction(self, other, + n_ops.less); + case Py_LE: + return PyArray_GenericBinaryFunction(self, other, + n_ops.less_equal); + case Py_EQ: + /* Try to convert other to an array */ + array_other = PyArray_FromObject(other, + PyArray_NOTYPE, 0, 0); + /* If not successful, then return the integer + object 0. This fixes code that used to + allow equality comparisons between arrays + and other objects which would give a result + of 0 + */ + if ((array_other == NULL) || \ + (array_other == Py_None)) { + Py_XDECREF(array_other); + PyErr_Clear(); + Py_INCREF(Py_False); + return Py_False; + } + result = PyArray_GenericBinaryFunction(self, + array_other, + n_ops.equal); + /* If the comparison results in NULL, then the + two array objects can not be compared together so + return zero + */ + Py_DECREF(array_other); + if (result == NULL) { + PyErr_Clear(); + Py_INCREF(Py_False); + return Py_False; + } + return result; + case Py_NE: + /* Try to convert other to an array */ + array_other = PyArray_FromObject(other, + PyArray_NOTYPE, 0, 0); + /* If not successful, then objects cannot be + compared and cannot be equal, therefore, + return True; + */ + if ((array_other == NULL) || \ + (array_other == Py_None)) { + Py_XDECREF(array_other); + PyErr_Clear(); + Py_INCREF(Py_True); + return Py_True; + } + result = PyArray_GenericBinaryFunction(self, + array_other, + n_ops.not_equal); + Py_DECREF(array_other); + if (result == NULL) { + PyErr_Clear(); + Py_INCREF(Py_True); + return Py_True; + } + return result; + case Py_GT: + return PyArray_GenericBinaryFunction(self, other, + n_ops.greater); + case Py_GE: + return PyArray_GenericBinaryFunction(self, + other, + n_ops.greater_equal); + } + return NULL; +} + +static PyObject * +_check_axis(PyArrayObject *arr, int *axis, int flags) +{ + PyObject *temp; + int n = arr->nd; + + if ((*axis >= MAX_DIMS) || (n==0)) { + temp = PyArray_Ravel(arr,0); + *axis = 0; + return temp; + } + else { + if (flags) { + temp = PyArray_FromAny((PyObject *)arr, NULL, + 0, 0, flags); + if (temp == NULL) return NULL; + } + else { + Py_INCREF(arr); + temp = (PyObject *)arr; + } + } + if (*axis < 0) *axis += n; + if ((*axis < 0) || (*axis >= n)) { + PyErr_Format(PyExc_ValueError, + "axis(=%d) out of bounds", *axis); + Py_DECREF(temp); + return NULL; + } + return temp; +} + +#include "arraymethods.c" + +/* Lifted from numarray */ +static PyObject * +PyArray_IntTupleFromIntp(int len, intp *vals) +{ + int i; + PyObject *intTuple = PyTuple_New(len); + if (!intTuple) goto fail; + for(i=0; i<len; i++) { +#if SIZEOF_INTP <= SIZEOF_LONG + PyObject *o = PyInt_FromLong((long) vals[i]); +#else + PyObject *o = PyLong_FromLongLong((longlong) vals[i]); +#endif + if (!o) { + Py_DECREF(intTuple); + intTuple = NULL; + goto fail; + } + PyTuple_SET_ITEM(intTuple, i, o); + } + fail: + return intTuple; +} + +/* Returns the number of dimensions or -1 if an error occurred */ +/* vals must be large enough to hold maxvals */ +/*MULTIARRAY_API + PyArray_IntpFromSequence +*/ +static int +PyArray_IntpFromSequence(PyObject *seq, intp *vals, int maxvals) +{ + int nd, i; + PyObject *op; + + /* Check to see if sequence is a single integer first. + or, can be made into one */ + if ((nd=PySequence_Length(seq)) == -1) { + if (PyErr_Occurred()) PyErr_Clear(); + if (!(op = PyNumber_Int(seq))) return -1; + nd = 1; + vals[0] = (intp ) PyInt_AsLong(op); + Py_DECREF(op); + } else { + for(i=0; i < MIN(nd,maxvals); i++) { + op = PySequence_GetItem(seq, i); + if (op == NULL) return -1; + vals[i]=(intp )PyInt_AsLong(op); + Py_DECREF(op); + if(PyErr_Occurred()) return -1; + } + } + return nd; +} + + +/* Check whether the given array is stored contiguously (row-wise) in + memory. */ +static int +_IsContiguous(PyArrayObject *ap) +{ + intp sd; + int i; + + if (ap->nd == 0) return 1; + sd = ap->descr->elsize; + if (ap->nd == 1) return sd == ap->strides[0]; + for (i = ap->nd-1; i >= 0; --i) { + /* contiguous by definition */ + if (ap->dimensions[i] == 0) return 1; + + if (ap->strides[i] != sd) return 0; + sd *= ap->dimensions[i]; + } + return 1; +} + + +static int +_IsFortranContiguous(PyArrayObject *ap) +{ + intp sd; + int i; + + if (ap->nd == 0) return 1; + sd = ap->descr->elsize; + if (ap->nd == 1) return sd == ap->strides[0]; + for (i=0; i< ap->nd; ++i) { + /* contiguous by definition */ + if (ap->dimensions[i] == 0) return 1; + + if (ap->strides[i] != sd) return 0; + sd *= ap->dimensions[i]; + } + return 1; +} + +static int +_IsAligned(PyArrayObject *ap) +{ + int i, alignment, aligned=1; + intp ptr; + int type = ap->descr->type_num; + + if ((type == PyArray_STRING) || (type == PyArray_VOID)) + return 1; + + alignment = ap->descr->alignment; + if (alignment == 1) return 1; + + ptr = (intp) ap->data; + aligned = (ptr % alignment) == 0; + for (i=0; i <ap->nd; i++) + aligned &= ((ap->strides[i] % alignment) == 0); + return aligned != 0; +} + +static Bool +_IsWriteable(PyArrayObject *ap) +{ + PyObject *base=ap->base; + void *dummy; + int n; + + /* If we own our own data, then no-problem */ + if ((base == NULL) || (ap->flags & OWN_DATA)) return TRUE; + + /* Get to the final base object + If it is a writeable array, then return TRUE + If we can find an array object + or a writeable buffer object as the final base object + or a string object (for pickling support memory savings). + - this last could be removed if a proper pickleable + buffer was added to Python. + */ + + while(PyArray_Check(base)) { + if (PyArray_CHKFLAGS(base, OWN_DATA)) + return (Bool) (PyArray_ISWRITEABLE(base)); + base = PyArray_BASE(base); + } + + /* here so pickle support works seamlessly + and unpickled array can be set and reset writeable + -- could be abused -- */ + if PyString_Check(base) return TRUE; + + if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) + return FALSE; + + return TRUE; +} + + +/*OBJECT_API + Update Several Flags at once. +*/ +static void +PyArray_UpdateFlags(PyArrayObject *ret, int flagmask) +{ + + if (flagmask & FORTRAN) { + if (_IsFortranContiguous(ret)) { + ret->flags |= FORTRAN; + if (ret->nd > 1) ret->flags &= ~CONTIGUOUS; + } + else ret->flags &= ~FORTRAN; + } + if (flagmask & CONTIGUOUS) { + if (_IsContiguous(ret)) { + ret->flags |= CONTIGUOUS; + if (ret->nd > 1) ret->flags &= ~FORTRAN; + } + else ret->flags &= ~CONTIGUOUS; + } + if (flagmask & ALIGNED) { + if (_IsAligned(ret)) ret->flags |= ALIGNED; + else ret->flags &= ~ALIGNED; + } + /* This is not checked by default WRITEABLE is not part of UPDATE_ALL_FLAGS */ + if (flagmask & WRITEABLE) { + if (_IsWriteable(ret)) ret->flags |= WRITEABLE; + else ret->flags &= ~WRITEABLE; + } + return; +} + +/* This routine checks to see if newstrides (of length nd) will not + walk outside of the memory implied by a single segment array of the provided + dimensions and element size. If numbytes is 0 it will be calculated from + the provided shape and element size. +*/ +/*OBJECT_API*/ +static Bool +PyArray_CheckStrides(int elsize, int nd, intp numbytes, + intp *dims, intp *newstrides) +{ + int i; + + if (numbytes == 0) + numbytes = PyArray_MultiplyList(dims, nd) * elsize; + + for (i=0; i<nd; i++) { + if (newstrides[i]*(dims[i]-1)+elsize > numbytes) { + return FALSE; + } + } + return TRUE; + +} + + +/* 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 CONTIGUOUS bit will be set. If the flags argument + has the FORTRAN bit set, then a FORTRAN-style strides array will be + created (and of course the FORTRAN flag bit will be set). + + If data is not given but created here, then flags will be DEFAULT_FLAGS + and a non-zero flags argument can be used to indicate a FORTRAN style + array is desired. +*/ + +static intp +_array_fill_strides(intp *strides, intp *dims, int nd, intp itemsize, + int inflag, int *objflags) +{ + int i; + /* Only make Fortran strides if not contiguous as well */ + if ((inflag & FORTRAN) && !(inflag & CONTIGUOUS)) { + for (i=0; i<nd; i++) { + strides[i] = itemsize; + itemsize *= dims[i] ? dims[i] : 1; + } + *objflags |= FORTRAN; + if (nd > 1) *objflags &= ~CONTIGUOUS; + else *objflags |= CONTIGUOUS; + } + else { + for (i=nd-1;i>=0;i--) { + strides[i] = itemsize; + itemsize *= dims[i] ? dims[i] : 1; + } + *objflags |= CONTIGUOUS; + if (nd > 1) *objflags &= ~FORTRAN; + else *objflags |= FORTRAN; + } + return itemsize; +} + +/*OBJECT_API + Generic new array creation routine. +*/ +static PyObject * +PyArray_New(PyTypeObject *subtype, int nd, intp *dims, int type_num, + 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 (descr->elsize == 0) { + 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; +} + +/* Change a sub-array field to the base descriptor */ +static int +_update_descr_and_dimensions(PyArray_Descr **des, intp *newdims, + intp *newstrides, int oldnd) +{ + PyArray_Descr *old; + int newnd; + int numnew; + intp *mydim; + int i; + + old = *des; + *des = old->subarray->base; + + mydim = newdims + oldnd; + if (PyTuple_Check(old->subarray->shape)) { + numnew = PyTuple_GET_SIZE(old->subarray->shape); + + for (i=0; i<numnew; i++) { + mydim[i] = (intp) PyInt_AsLong \ + (PyTuple_GET_ITEM(old->subarray->shape, i)); + } + } + else { + numnew = 1; + mydim[0] = (intp) PyInt_AsLong(old->subarray->shape); + } + + newnd = oldnd + numnew; + + if (newstrides) { + intp tempsize; + intp *mystrides; + mystrides = newstrides + oldnd; + /* Make new strides */ + tempsize = (*des)->elsize; + for (i=numnew-1; i>=0; i--) { + mystrides[i] = tempsize; + tempsize *= mydim[i] ? mydim[i] : 1; + } + } + Py_INCREF(*des); + Py_DECREF(old); + return newnd; +} + + +/* steals a reference to descr (even on failure) */ +/*OBJECT_API + Generic new array creation routine. +*/ +static PyObject * +PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd, + intp *dims, intp *strides, void *data, + int flags, PyObject *obj) +{ + PyArrayObject *self; + register int i; + intp sd; + + if (descr->subarray) { + PyObject *ret; + intp newdims[2*MAX_DIMS]; + intp *newstrides=NULL; + memcpy(newdims, dims, nd*sizeof(intp)); + if (strides) { + newstrides = newdims + MAX_DIMS; + memcpy(newstrides, strides, nd*sizeof(intp)); + } + nd =_update_descr_and_dimensions(&descr, newdims, + newstrides, nd); + ret = PyArray_NewFromDescr(subtype, descr, nd, newdims, + newstrides, + data, flags, obj); + return ret; + } + + if (nd < 0) { + PyErr_SetString(PyExc_ValueError, + "number of dimensions must be >=0"); + Py_DECREF(descr); + return NULL; + } + if (nd > MAX_DIMS) { + PyErr_Format(PyExc_ValueError, + "maximum number of dimensions is %d", MAX_DIMS); + Py_DECREF(descr); + return NULL; + } + + /* Check dimensions */ + for (i=nd-1;i>=0;i--) { + if (dims[i] < 0) { + PyErr_SetString(PyExc_ValueError, + "negative dimensions " \ + "are not allowed"); + Py_DECREF(descr); + return NULL; + } + } + + self = (PyArrayObject *) subtype->tp_alloc(subtype, 0); + if (self == NULL) { + Py_DECREF(descr); + return NULL; + } + self->dimensions = NULL; + if (data == NULL) { /* strides is NULL too */ + self->flags = DEFAULT_FLAGS; + if (flags) { + self->flags |= FORTRAN; + if (nd > 1) self->flags &= ~CONTIGUOUS; + flags = FORTRAN; + } + } + else self->flags = (flags & ~UPDATEIFCOPY); + + sd = descr->elsize; + + if (nd > 0) { + self->dimensions = PyDimMem_NEW(2*nd); + if (self->dimensions == NULL) { + PyErr_NoMemory(); + goto fail; + } + self->strides = self->dimensions + nd; + memcpy(self->dimensions, dims, sizeof(intp)*nd); + if (strides == NULL) { /* fill it in */ + sd = _array_fill_strides(self->strides, dims, nd, sd, + flags, &(self->flags)); + } + else { + if (data == NULL) { + PyErr_SetString(PyExc_ValueError, + "if 'strides' is given in " \ + "array creation, data must " \ + "be given too"); + PyDimMem_FREE(self->dimensions); + self->ob_type->tp_free((PyObject *)self); + return NULL; + } + memcpy(self->strides, strides, sizeof(intp)*nd); + } + } + + self->descr = descr; + + + 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. */ + + if (sd==0) sd = sizeof(intp); + + if ((data = PyDataMem_NEW(sd))==NULL) { + PyErr_NoMemory(); + goto fail; + } + self->flags |= OWN_DATA; + + /* It is bad to have unitialized OBJECT pointers */ + if (descr == &OBJECT_Descr) { + memset(data, 0, sd); + } + } + else { + self->flags &= ~OWN_DATA; /* If data is passed in, + this object won't own it + by default. + Caller must arrange for + this to be reset if truly + desired */ + } + self->data = data; + self->nd = nd; + self->base = (PyObject *)NULL; + self->weakreflist = (PyObject *)NULL; + + /* call the __array_finalize__ + method if a subtype and some object passed in */ + if ((obj != NULL) && (subtype != &PyArray_Type) && + (subtype != &PyBigArray_Type)) { + PyObject *res; + if (!(self->flags & OWNDATA)) { /* did not allocate own data */ + /* update flags before calling back into + Python */ + PyArray_UpdateFlags(self, UPDATE_ALL_FLAGS); + } + res = PyObject_CallMethod((PyObject *)self, + "__array_finalize__", + "O", obj); + if (res == NULL) { + if (self->flags & OWNDATA) PyDataMem_FREE(self); + PyDimMem_FREE(self->dimensions); + /* theoretically should free self + but this causes segmentation faults... + Not sure why */ + return NULL; + } + else Py_DECREF(res); + } + + return (PyObject *)self; + + fail: + Py_DECREF(descr); + PyDimMem_FREE(self->dimensions); + subtype->tp_free((PyObject *)self); + return NULL; + +} + + + +/*OBJECT_API + Resize (reallocate data). Only works if nothing else is referencing + this array and it is contiguous. +*/ +static PyObject * +PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape) +{ + intp oldsize, newsize; + int new_nd=newshape->len, k, n, elsize; + int refcnt; + intp* new_dimensions=newshape->ptr; + intp new_strides[MAX_DIMS]; + intp sd; + intp *dimptr; + char *new_data; + + if (!PyArray_ISCONTIGUOUS(self)) { + PyErr_SetString(PyExc_ValueError, + "resize only works on contiguous arrays"); + return NULL; + } + + + newsize = PyArray_MultiplyList(new_dimensions, new_nd); + + if (newsize == 0) { + PyErr_SetString(PyExc_ValueError, + "newsize is zero; cannot delete an array "\ + "in this way"); + return NULL; + } + oldsize = PyArray_SIZE(self); + + if (oldsize != newsize) { + if (!(self->flags & OWN_DATA)) { + PyErr_SetString(PyExc_ValueError, + "cannot resize this array: " \ + "it does not own its data"); + return NULL; + } + + refcnt = REFCOUNT(self); + if ((refcnt > 2) || (self->base != NULL) || \ + (self->weakreflist != NULL)) { + PyErr_SetString(PyExc_ValueError, + "cannot resize an array that has "\ + "been referenced or is referencing\n"\ + "another array in this way. Use the "\ + "resize function"); + return NULL; + } + + /* Reallocate space if needed */ + new_data = PyDataMem_RENEW(self->data, + newsize*(self->descr->elsize)); + if (new_data == NULL) { + PyErr_SetString(PyExc_MemoryError, + "cannot allocate memory for array"); + return NULL; + } + self->data = new_data; + } + + if ((newsize > oldsize) && PyArray_ISWRITEABLE(self)) { + /* Fill new memory with zeros */ + elsize = self->descr->elsize; + if ((PyArray_TYPE(self) == PyArray_OBJECT)) { + PyObject *zero = PyInt_FromLong(0); + PyObject **optr; + optr = ((PyObject **)self->data) + oldsize; + n = newsize - oldsize; + for (k=0; k<n; k++) { + Py_INCREF(zero); + *optr++ = zero; + } + Py_DECREF(zero); + } + else{ + memset(self->data+oldsize*elsize, 0, + (newsize-oldsize)*elsize); + } + } + + if (self->nd != new_nd) { /* Different number of dimensions. */ + self->nd = new_nd; + + /* Need new dimensions and strides arrays */ + dimptr = PyDimMem_RENEW(self->dimensions, 2*new_nd); + if (dimptr == NULL) { + PyErr_SetString(PyExc_MemoryError, + "cannot allocate memory for array " \ + "(array may be corrupted)"); + return NULL; + } + self->dimensions = dimptr; + self->strides = dimptr + new_nd; + } + + /* make new_strides variable */ + sd = (intp) self->descr->elsize; + sd = _array_fill_strides(new_strides, new_dimensions, new_nd, sd, + 0, &(self->flags)); + + + memmove(self->dimensions, new_dimensions, new_nd*sizeof(intp)); + memmove(self->strides, new_strides, new_nd*sizeof(intp)); + + Py_INCREF(Py_None); + return Py_None; + +} + + +/* Assumes contiguous */ +/*OBJECT_API*/ +static void +PyArray_FillObjectArray(PyArrayObject *arr, PyObject *obj) +{ + PyObject **optr; + intp i,n; + optr = (PyObject **)(arr->data); + n = PyArray_SIZE(arr); + if (obj == NULL) { + for (i=0; i<n; i++) { + *optr++ = NULL; + } + } + else { + for (i=0; i<n; i++) { + Py_INCREF(obj); + *optr++ = obj; + } + } +} + +/*OBJECT_API*/ +static int +PyArray_FillWithScalar(PyArrayObject *arr, PyObject *obj) +{ + PyObject *newarr; + int itemsize, swap; + void *fromptr; + PyArray_Descr *descr; + intp size; + PyArray_CopySwapFunc *copyswap; + + descr = PyArray_DESCR(arr); + itemsize = descr->elsize; + Py_INCREF(descr); + newarr = PyArray_FromAny(obj, descr, 0,0, ALIGNED); + if (newarr == NULL) return -1; + fromptr = PyArray_DATA(newarr); + size=PyArray_SIZE(arr); + swap=!PyArray_ISNOTSWAPPED(arr); + copyswap = arr->descr->f->copyswap; + if (PyArray_ISONESEGMENT(arr)) { + char *toptr=PyArray_DATA(arr); + while (size--) { + copyswap(toptr, fromptr, swap, itemsize); + toptr += itemsize; + } + } + else { + PyArrayIterObject *iter; + + iter = (PyArrayIterObject *)\ + PyArray_IterNew((PyObject *)arr); + if (iter == NULL) { + Py_DECREF(newarr); + return -1; + } + while(size--) { + copyswap(iter->dataptr, fromptr, swap, itemsize); + PyArray_ITER_NEXT(iter); + } + Py_DECREF(iter); + } + Py_DECREF(newarr); + return 0; +} + +static PyObject * +array_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"shape", "dtype", "buffer", + "offset", "strides", + "fortran", NULL}; + PyArray_Descr *descr=NULL; + int type_num; + int itemsize; + PyArray_Dims dims = {NULL, 0}; + PyArray_Dims strides = {NULL, 0}; + PyArray_Chunk buffer; + longlong offset=0; + int fortran = 0; + PyArrayObject *ret; + + buffer.ptr = NULL; + /* Usually called with shape and type + but can also be called with buffer, strides, and swapped info + */ + + /* For now, let's just use this to create an empty, contiguous + array of a specific type and shape. + */ + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&LO&i", + kwlist, PyArray_IntpConverter, + &dims, + PyArray_DescrConverter, + &descr, + PyArray_BufferConverter, + &buffer, + &offset, + &PyArray_IntpConverter, + &strides, + &fortran)) + goto fail; + + type_num = descr->type_num; + itemsize = descr->elsize; + + if (dims.ptr == NULL) { + PyErr_SetString(PyExc_ValueError, "need to give a "\ + "valid shape as the first argument"); + goto fail; + } + if (buffer.ptr == NULL) { + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(subtype, descr, + (int)dims.len, + dims.ptr, + NULL, NULL, fortran, NULL); + if (ret == NULL) {descr=NULL;goto fail;} + if (type_num == PyArray_OBJECT) { /* place Py_None */ + PyArray_FillObjectArray(ret, Py_None); + } + } + else { /* buffer given -- use it */ + buffer.len -= offset; + buffer.ptr += offset; + if (dims.len == 1 && dims.ptr[0] == -1) { + dims.ptr[0] = buffer.len / itemsize; + } + else if (buffer.len < itemsize* \ + PyArray_MultiplyList(dims.ptr, dims.len)) { + PyErr_SetString(PyExc_TypeError, + "buffer is too small for " \ + "requested array"); + goto fail; + } + if (strides.ptr != NULL) { + if (strides.len != dims.len) { + PyErr_SetString(PyExc_ValueError, + "strides, if given, must be "\ + "the same length as shape"); + goto fail; + } + if (!PyArray_CheckStrides(itemsize, strides.len, + buffer.len, + dims.ptr, strides.ptr)) { + PyErr_SetString(PyExc_ValueError, + "strides is incompatible "\ + "with shape of requested"\ + "array and size of buffer"); + goto fail; + } + } + if (type_num == PyArray_OBJECT) { + PyErr_SetString(PyExc_TypeError, "cannot construct "\ + "an object array from buffer data"); + goto fail; + } + /* get writeable and aligned */ + if (fortran) buffer.flags |= FORTRAN; + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(subtype, descr, + dims.len, dims.ptr, + strides.ptr, + (char *)buffer.ptr, + buffer.flags, NULL); + if (ret == NULL) {descr=NULL; goto fail;} + PyArray_UpdateFlags(ret, UPDATE_ALL_FLAGS); + ret->base = buffer.base; + Py_INCREF(buffer.base); + } + + PyDimMem_FREE(dims.ptr); + if (strides.ptr) PyDimMem_FREE(strides.ptr); + return (PyObject *)ret; + + fail: + Py_XDECREF(descr); + if (dims.ptr) PyDimMem_FREE(dims.ptr); + if (strides.ptr) PyDimMem_FREE(strides.ptr); + return NULL; +} + + +static PyObject * +array_iter(PyArrayObject *arr) +{ + if (arr->nd == 0) { + PyErr_SetString(PyExc_TypeError, + "iteration over a scalar (0-dim array)"); + return NULL; + } + return PySeqIter_New((PyObject *)arr); +} + + +/******************* array attribute get and set routines ******************/ + +static PyObject * +array_ndim_get(PyArrayObject *self) +{ + return PyInt_FromLong(self->nd); +} + +static PyObject * +array_flags_get(PyArrayObject *self) +{ + return PyObject_CallMethod(_scipy_internal, "flagsobj", "Oii", + self, self->flags, 0); +} + +static PyObject * +array_shape_get(PyArrayObject *self) +{ + return PyArray_IntTupleFromIntp(self->nd, self->dimensions); +} + + +static int +array_shape_set(PyArrayObject *self, PyObject *val) +{ + int nd; + PyObject *ret; + + ret = PyArray_Reshape(self, val); + if (ret == NULL) return -1; + + /* Free old dimensions and strides */ + PyDimMem_FREE(self->dimensions); + nd = PyArray_NDIM(ret); + self->nd = nd; + if (nd > 0) { /* create new dimensions and strides */ + self->dimensions = PyDimMem_NEW(2*nd); + if (self->dimensions == NULL) { + Py_DECREF(ret); + PyErr_SetString(PyExc_MemoryError,""); + return -1; + } + self->strides = self->dimensions + nd; + memcpy(self->dimensions, PyArray_DIMS(ret), + nd*sizeof(intp)); + memcpy(self->strides, PyArray_STRIDES(ret), + nd*sizeof(intp)); + } + else {self->dimensions=NULL; self->strides=NULL;} + Py_DECREF(ret); + PyArray_UpdateFlags(self, CONTIGUOUS | FORTRAN); + return 0; +} + + +static PyObject * +array_strides_get(PyArrayObject *self) +{ + return PyArray_IntTupleFromIntp(self->nd, self->strides); +} + +static int +array_strides_set(PyArrayObject *self, PyObject *obj) +{ + PyArray_Dims newstrides = {NULL, 0}; + PyArrayObject *new; + intp numbytes; + + if (!PyArray_IntpConverter(obj, &newstrides) || \ + newstrides.ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "invalid strides"); + return -1; + } + if (newstrides.len != self->nd) { + PyErr_Format(PyExc_ValueError, "strides must be " \ + " same length as shape (%d)", self->nd); + goto fail; + } + new = self; + while(new->base != NULL) { + if (PyArray_Check(new->base)) + new = (PyArrayObject *)new->base; + } + numbytes = PyArray_MultiplyList(new->dimensions, + new->nd)*new->descr->elsize; + + if (!PyArray_CheckStrides(self->descr->elsize, self->nd, numbytes, + self->dimensions, newstrides.ptr)) { + PyErr_SetString(PyExc_ValueError, "strides is not "\ + "compatible with available memory"); + goto fail; + } + memcpy(self->strides, newstrides.ptr, sizeof(intp)*newstrides.len); + PyArray_UpdateFlags(self, CONTIGUOUS | FORTRAN); + PyDimMem_FREE(newstrides.ptr); + return 0; + + fail: + PyDimMem_FREE(newstrides.ptr); + return -1; +} + + +static PyObject * +array_protocol_strides_get(PyArrayObject *self) +{ + if PyArray_ISCONTIGUOUS(self) { + Py_INCREF(Py_None); + return Py_None; + } + return PyArray_IntTupleFromIntp(self->nd, self->strides); +} + +static PyObject * +array_priority_get(PyArrayObject *self) +{ + if (PyArray_CheckExact(self)) + return PyFloat_FromDouble(PyArray_PRIORITY); + else if (PyBigArray_CheckExact(self)) + return PyFloat_FromDouble(PyArray_BIG_PRIORITY); + else + return PyFloat_FromDouble(PyArray_SUBTYPE_PRIORITY); +} + + +static PyObject * +array_dataptr_get(PyArrayObject *self) +{ + return Py_BuildValue("NO", + PyString_FromFormat("%p", self->data), + (self->flags & WRITEABLE ? Py_False : + Py_True)); +} + +static PyObject * +array_data_get(PyArrayObject *self) +{ + intp nbytes; + if (!(PyArray_ISONESEGMENT(self))) { + PyErr_SetString(PyExc_AttributeError, "cannot get single-"\ + "segment buffer for discontiguous array"); + return NULL; + } + nbytes = PyArray_NBYTES(self); + if PyArray_ISWRITEABLE(self) + return PyBuffer_FromReadWriteObject((PyObject *)self, 0, + (int) nbytes); + else + return PyBuffer_FromObject((PyObject *)self, 0, (int) nbytes); +} + +static int +array_data_set(PyArrayObject *self, PyObject *op) +{ + void *buf; + int buf_len; + int writeable=1; + + if (PyObject_AsWriteBuffer(op, &buf, &buf_len) < 0) { + writeable = 0; + if (PyObject_AsReadBuffer(op, (const void **)&buf, + &buf_len) < 0) { + PyErr_SetString(PyExc_AttributeError, + "object does not have single-segment " \ + "buffer interface"); + return -1; + } + } + if (!PyArray_ISONESEGMENT(self)) { + PyErr_SetString(PyExc_AttributeError, "cannot set single-" \ + "segment buffer for discontiguous array"); + return -1; + } + if (PyArray_NBYTES(self) > buf_len) { + PyErr_SetString(PyExc_AttributeError, + "not enough data for array"); + return -1; + } + if (self->flags & OWN_DATA) { + PyArray_XDECREF(self); + PyDataMem_FREE(self->data); + } + if (self->base) { + if (self->flags & UPDATEIFCOPY) { + ((PyArrayObject *)self->base)->flags |= WRITEABLE; + self->flags &= ~UPDATEIFCOPY; + } + Py_DECREF(self->base); + } + Py_INCREF(op); + self->base = op; + self->data = buf; + self->flags = CARRAY_FLAGS; + if (!writeable) + self->flags &= ~WRITEABLE; + return 0; +} + + +static PyObject * +array_itemsize_get(PyArrayObject *self) +{ + return PyInt_FromLong((long) self->descr->elsize); +} + +static PyObject * +array_size_get(PyArrayObject *self) +{ + intp size=PyArray_SIZE(self); +#if SIZEOF_INTP <= SIZEOF_LONG + return PyInt_FromLong((long) size); +#else + if (size > MAX_LONG || size < MIN_LONG) + return PyLong_FromLongLong(size); + else + return PyInt_FromLong((long) size); +#endif +} + +static PyObject * +array_nbytes_get(PyArrayObject *self) +{ + intp nbytes = PyArray_NBYTES(self); +#if SIZEOF_INTP <= SIZEOF_LONG + return PyInt_FromLong((long) nbytes); +#else + if (nbytes > MAX_LONG || nbytes < MIN_LONG) + return PyLong_FromLongLong(nbytes); + else + return PyInt_FromLong((long) nbytes); +#endif +} + + +static PyObject * +array_typechar_get(PyArrayObject *self) +{ + if PyArray_ISEXTENDED(self) + return PyString_FromFormat("%c%d", (self->descr->type), + self->descr->elsize); + else + return PyString_FromStringAndSize(&(self->descr->type), 1); +} + +static PyObject *arraydescr_protocol_typestr_get(PyArray_Descr *); + +static PyObject * +array_typestr_get(PyArrayObject *self) +{ + return arraydescr_protocol_typestr_get(self->descr); +} + +static PyObject * +array_descr_get(PyArrayObject *self) +{ + Py_INCREF(self->descr); + return (PyObject *)self->descr; +} + + +/* If the type is changed. + Also needing change: strides, itemsize + + Either itemsize is exactly the same + or the array is single-segment (contiguous or fortran) with + compatibile dimensions + + The shape and strides will be adjusted in that case as well. +*/ + +static int +array_descr_set(PyArrayObject *self, PyObject *arg) +{ + PyArray_Descr *newtype=NULL; + intp newdim; + int index; + char *msg = "new type not compatible with array."; + + if (!(PyArray_DescrConverter(arg, &newtype)) || + newtype == NULL) { + PyErr_SetString(PyExc_TypeError, "invalid type for array"); + return -1; + } + if (newtype->type_num == PyArray_OBJECT || \ + self->descr->type_num == PyArray_OBJECT) { + PyErr_SetString(PyExc_TypeError, \ + "Cannot change descriptor for object"\ + "array."); + Py_DECREF(newtype); + return -1; + } + + if ((newtype->elsize != self->descr->elsize) && \ + (self->nd == 0 || !PyArray_ISONESEGMENT(self) || \ + newtype->subarray)) goto fail; + + if (PyArray_ISCONTIGUOUS(self)) index = self->nd - 1; + else index = 0; + + if (newtype->elsize < self->descr->elsize) { + /* if it is compatible increase the size of the + dimension at end (or at the front for FORTRAN) + */ + if (self->descr->elsize % newtype->elsize != 0) + goto fail; + newdim = self->descr->elsize / newtype->elsize; + self->dimensions[index] *= newdim; + self->strides[index] = newtype->elsize; + } + + else if (newtype->elsize > self->descr->elsize) { + + /* Determine if last (or first if FORTRAN) dimension + is compatible */ + + newdim = self->dimensions[index] * self->descr->elsize; + if ((newdim % newtype->elsize) != 0) goto fail; + + self->dimensions[index] = newdim / newtype->elsize; + self->strides[index] = newtype->elsize; + } + + /* fall through -- adjust type*/ + + Py_DECREF(self->descr); + if (newtype->subarray) { + /* create new array object from data and update + dimensions, strides and descr from it */ + PyArrayObject *temp; + + temp = (PyArrayObject *)\ + PyArray_NewFromDescr(&PyArray_Type, newtype, self->nd, + self->dimensions, self->strides, + self->data, self->flags, NULL); + PyDimMem_FREE(self->dimensions); + self->dimensions = temp->dimensions; + self->nd = temp->nd; + self->strides = temp->strides; + Py_DECREF(newtype); + newtype = temp->descr; + /* Fool deallocator */ + temp->nd = 0; + temp->dimensions = NULL; + temp->descr = NULL; + Py_DECREF(temp); + } + + self->descr = newtype; + PyArray_UpdateFlags(self, UPDATE_ALL_FLAGS); + + return 0; + + fail: + PyErr_SetString(PyExc_ValueError, msg); + Py_DECREF(newtype); + return -1; +} + +static PyObject * +array_protocol_descr_get(PyArrayObject *self) +{ + PyObject *res; + PyObject *dobj; + + res = PyObject_GetAttrString((PyObject *)self->descr, "arrdescr"); + if (res) return res; + PyErr_Clear(); + + /* get default */ + dobj = PyTuple_New(2); + if (dobj == NULL) return NULL; + PyTuple_SET_ITEM(dobj, 0, PyString_FromString("")); + PyTuple_SET_ITEM(dobj, 1, array_typestr_get(self)); + res = PyList_New(1); + if (res == NULL) {Py_DECREF(dobj); return NULL;} + PyList_SET_ITEM(res, 0, dobj); + return res; +} + +static PyObject * +array_struct_get(PyArrayObject *self) +{ + PyArrayInterface *inter; + + inter = (PyArrayInterface *)_pya_malloc(sizeof(PyArrayInterface)); + inter->version = 2; + inter->nd = self->nd; + inter->typekind = self->descr->kind; + inter->itemsize = self->descr->elsize; + inter->flags = self->flags; + /* reset unused flags */ + inter->flags &= ~(UPDATEIFCOPY | OWNDATA); + if (PyArray_ISNOTSWAPPED(self)) inter->flags |= NOTSWAPPED; + inter->strides = self->strides; + inter->shape = self->dimensions; + inter->data = self->data; + Py_INCREF(self); + return PyCObject_FromVoidPtrAndDesc(inter, self, gentype_struct_free); +} + +static PyObject * +array_type_get(PyArrayObject *self) +{ + Py_INCREF(self->descr->typeobj); + return (PyObject *)self->descr->typeobj; +} + + + +static PyObject * +array_base_get(PyArrayObject *self) +{ + if (self->base == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else { + Py_INCREF(self->base); + return self->base; + } +} + + +static PyObject * +array_real_get(PyArrayObject *self) +{ + PyArrayObject *ret; + + if (PyArray_ISCOMPLEX(self)) { + ret = (PyArrayObject *)PyArray_New(self->ob_type, + self->nd, + self->dimensions, + self->descr->type_num - \ + PyArray_NUM_FLOATTYPE, + self->strides, + self->data, + 0, + self->flags, (PyObject *)self); + if (ret == NULL) return NULL; + ret->flags &= ~CONTIGUOUS; + ret->flags &= ~FORTRAN; + Py_INCREF(self); + ret->base = (PyObject *)self; + return (PyObject *)ret; + } + else { + Py_INCREF(self); + return (PyObject *)self; + } +} + + +static int +array_real_set(PyArrayObject *self, PyObject *val) +{ + PyArrayObject *ret; + PyArrayObject *new; + int rint; + + new = (PyArrayObject *)PyArray_FromAny(val, NULL, 0, 0, 0); + if (new == NULL) return -1; + + if (PyArray_ISCOMPLEX(self)) { + ret = (PyArrayObject *)PyArray_New(self->ob_type, + self->nd, + self->dimensions, + self->descr->type_num - \ + PyArray_NUM_FLOATTYPE, + self->strides, + self->data, + 0, + self->flags, (PyObject *)self); + if (ret == NULL) {Py_DECREF(new); return -1;} + ret->flags &= ~CONTIGUOUS; + ret->flags &= ~FORTRAN; + Py_INCREF(self); + ret->base = (PyObject *)self; + } + else { + Py_INCREF(self); + ret = self; + } + rint = PyArray_CopyInto(ret, new); + Py_DECREF(ret); + Py_DECREF(new); + return rint; +} + +static PyObject * +array_imag_get(PyArrayObject *self) +{ + PyArrayObject *ret; + PyArray_Descr *type; + + if (PyArray_ISCOMPLEX(self)) { + type = PyArray_DescrFromType(self->descr->type_num - + PyArray_NUM_FLOATTYPE); + ret = (PyArrayObject *) \ + PyArray_NewFromDescr(self->ob_type, + type, + self->nd, + self->dimensions, + self->strides, + self->data + type->elsize, + self->flags, (PyObject *)self); + if (ret == NULL) return NULL; + ret->flags &= ~CONTIGUOUS; + ret->flags &= ~FORTRAN; + Py_INCREF(self); + ret->base = (PyObject *)self; + return (PyObject *) ret; + } + else { + type = self->descr; + Py_INCREF(type); + ret = (PyArrayObject *)PyArray_Zeros(self->nd, + self->dimensions, + type, + PyArray_ISFORTRAN(self)); + ret->flags &= ~WRITEABLE; + return (PyObject *)ret; + } +} + +static int +array_imag_set(PyArrayObject *self, PyObject *val) +{ + if (PyArray_ISCOMPLEX(self)) { + PyArrayObject *ret; + PyArrayObject *new; + int rint; + + new = (PyArrayObject *)PyArray_FromAny(val, NULL, 0, 0, 0); + if (new == NULL) return -1; + ret = (PyArrayObject *)PyArray_New(self->ob_type, + self->nd, + self->dimensions, + self->descr->type_num - \ + PyArray_NUM_FLOATTYPE, + self->strides, + self->data + \ + (self->descr->elsize >> 1), + 0, + self->flags, (PyObject *)self); + if (ret == NULL) { + Py_DECREF(new); + return -1; + } + ret->flags &= ~CONTIGUOUS; + ret->flags &= ~FORTRAN; + Py_INCREF(self); + ret->base = (PyObject *)self; + rint = PyArray_CopyInto(ret, new); + Py_DECREF(ret); + Py_DECREF(new); + return rint; + } + else { + PyErr_SetString(PyExc_TypeError, "does not have imaginary " \ + "part to set"); + return -1; + } +} + +static PyObject * +array_flat_get(PyArrayObject *self) +{ + return PyArray_IterNew((PyObject *)self); +} + +static int +array_flat_set(PyArrayObject *self, PyObject *val) +{ + PyObject *arr=NULL; + int retval = -1; + PyArrayIterObject *selfit=NULL, *arrit=NULL; + PyArray_Descr *typecode; + int swap; + PyArray_CopySwapFunc *copyswap; + + typecode = self->descr; + Py_INCREF(typecode); + arr = PyArray_FromAny(val, typecode, + 0, 0, FORCECAST | FORTRAN_IF(self)); + if (arr == NULL) return -1; + arrit = (PyArrayIterObject *)PyArray_IterNew(arr); + if (arrit == NULL) goto exit; + selfit = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (selfit == NULL) goto exit; + + swap = PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(arr); + copyswap = self->descr->f->copyswap; + if (PyArray_ISOBJECT(self)) { + while(selfit->index < selfit->size) { + Py_XDECREF(*((PyObject **)selfit->dataptr)); + Py_INCREF(*((PyObject **)arrit->dataptr)); + memmove(selfit->dataptr, arrit->dataptr, + sizeof(PyObject *)); + PyArray_ITER_NEXT(selfit); + PyArray_ITER_NEXT(arrit); + if (arrit->index == arrit->size) + PyArray_ITER_RESET(arrit); + } + retval = 0; + goto exit; + } + + while(selfit->index < selfit->size) { + memmove(selfit->dataptr, arrit->dataptr, self->descr->elsize); + copyswap(selfit->dataptr, NULL, swap, self->descr->elsize); + PyArray_ITER_NEXT(selfit); + PyArray_ITER_NEXT(arrit); + if (arrit->index == arrit->size) + PyArray_ITER_RESET(arrit); + } + retval = 0; + exit: + Py_XDECREF(selfit); + Py_XDECREF(arrit); + Py_XDECREF(arr); + return retval; +} + +static PyGetSetDef array_getsetlist[] = { + {"ndim", + (getter)array_ndim_get, + NULL, + "number of array dimensions"}, + {"flags", + (getter)array_flags_get, + NULL, + "special dictionary of flags"}, + {"shape", + (getter)array_shape_get, + (setter)array_shape_set, + "tuple of array dimensions"}, + {"strides", + (getter)array_strides_get, + (setter)array_strides_set, + "tuple of bytes steps in each dimension"}, + {"data", + (getter)array_data_get, + (setter)array_data_set, + "pointer to start of data"}, + {"itemsize", + (getter)array_itemsize_get, + NULL, + "length of one element in bytes"}, + {"size", + (getter)array_size_get, + NULL, + "number of elements in the array"}, + {"nbytes", + (getter)array_nbytes_get, + NULL, + "number of bytes in the array"}, + {"base", + (getter)array_base_get, + NULL, + "base object"}, + {"dtype", + (getter)array_type_get, + NULL, + "get array type class"}, + {"dtypechar", + (getter)array_typechar_get, + NULL, + "get array type character code"}, + {"dtypestr", + (getter)array_typestr_get, + NULL, + "get array type string"}, + {"dtypedescr", + (getter)array_descr_get, + (setter)array_descr_set, + "get(set) data-type-descriptor for array"}, + {"real", + (getter)array_real_get, + (setter)array_real_set, + "real part of array"}, + {"imag", + (getter)array_imag_get, + (setter)array_imag_set, + "imaginary part of array"}, + {"flat", + (getter)array_flat_get, + (setter)array_flat_set, + "a 1-d view of a contiguous array"}, + {"__array_data__", + (getter)array_dataptr_get, + NULL, + "Array protocol: data"}, + {"__array_typestr__", + (getter)array_typestr_get, + NULL, + "Array protocol: typestr"}, + {"__array_descr__", + (getter)array_protocol_descr_get, + NULL, + "Array protocol: descr"}, + {"__array_shape__", + (getter)array_shape_get, + NULL, + "Array protocol: shape"}, + {"__array_strides__", + (getter)array_protocol_strides_get, + NULL, + "Array protocol: strides"}, + {"__array_struct__", + (getter)array_struct_get, + NULL, + "Array protocol: struct"}, + {"__array_priority__", + (getter)array_priority_get, + NULL, + "Array priority"}, + {NULL, NULL, NULL, NULL}, /* Sentinel */ +}; + +/****************** end of attribute get and set routines *******************/ + + +static PyObject * +array_alloc(PyTypeObject *type, int nitems) +{ + PyObject *obj; + /* nitems will always be 0 */ + obj = (PyObject *)_pya_malloc(sizeof(PyArrayObject)); + PyObject_Init(obj, type); + return obj; +} + + +static char Arraytype__doc__[] = + "A array object represents a multidimensional, homogeneous array\n" + " of fixed-size items. An associated data-type-descriptor object\n" + " details the data-type in an array (including byteorder and any\n" + " fields). An array can be constructed using the scipy.array\n" + " command. Arrays are sequence, mapping and numeric objects.\n" + " More information is available in the scipy module and by looking\n" + " at the methods and attributes of an array.\n\n" + " ndarray.__new__(subtype, shape=, dtype=int_, buffer=None, \n" + " offset=0, strides=None, fortran=False)\n\n" + " There are two modes of creating an array using __new__:\n" + " 1) If buffer is None, then only shape, dtype, and fortran \n" + " are used\n" + " 2) If buffer is an object exporting the buffer interface, then\n" + " all keywords are interpreted.\n" + " The dtype parameter can be any object that can be interpreted \n" + " as a scipy.dtypedescr object.\n\n" + " No __init__ method is needed because the array is fully \n" + " initialized after the __new__ method."; + +static PyTypeObject PyBigArray_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "scipy.bigndarray", /*tp_name*/ + sizeof(PyArrayObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)array_dealloc, /*tp_dealloc */ + (printfunc)NULL, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)array_repr, /*tp_repr*/ + &array_as_number, /*tp_as_number*/ + NULL, /*tp_as_sequence*/ + &array_as_mapping, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)array_str, /*tp_str*/ + + (getattrofunc)0, /*tp_getattro*/ + (setattrofunc)0, /*tp_setattro*/ + NULL, /*tp_as_buffer*/ + (Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_CHECKTYPES), /*tp_flags*/ + /*Documentation string */ + Arraytype__doc__, /*tp_doc*/ + + (traverseproc)0, /*tp_traverse */ + (inquiry)0, /*tp_clear */ + (richcmpfunc)array_richcompare, + offsetof(PyArrayObject, weakreflist), /*tp_weaklistoffset */ + + /* Iterator support (use standard) */ + + (getiterfunc)array_iter, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + + /* Sub-classing (new-style object) support */ + + array_methods, /* tp_methods */ + 0, /* tp_members */ + array_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + array_alloc, /* tp_alloc */ + (newfunc)array_new, /* tp_new */ + _pya_free, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0 /* tp_weaklist */ +}; + +/* A standard array will subclass from the Big Array and + add the array_as_sequence table + and the array_as_buffer table + */ + +static PyTypeObject PyArray_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "scipy.ndarray", /*tp_name*/ + sizeof(PyArrayObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ +}; + + +/* The rest of this code is to build the right kind of array from a python */ +/* object. */ + +static int +discover_depth(PyObject *s, int max, int stop_at_string, int stop_at_tuple) +{ + int d=0; + PyObject *e; + + if(max < 1) return -1; + + if(! PySequence_Check(s) || PyInstance_Check(s) || \ + PySequence_Length(s) < 0) { + PyErr_Clear(); return 0; + } + if (PyArray_Check(s)) + return PyArray_NDIM(s); + if(PyString_Check(s) || PyBuffer_Check(s) || PyUnicode_Check(s)) + return stop_at_string ? 0:1; + if (stop_at_tuple && PyTuple_Check(s)) return 0; + if ((e=PyObject_GetAttrString(s, "__array_shape__")) != NULL) { + if (PyTuple_Check(e)) d=PyTuple_GET_SIZE(e); + else d=-1; + Py_DECREF(e); + if (d>-1) return d; + } + else PyErr_Clear(); + + if (PySequence_Length(s) == 0) + return 1; + if ((e=PySequence_GetItem(s,0)) == NULL) return -1; + if(e!=s) { + d=discover_depth(e, max-1, stop_at_string, stop_at_tuple); + if(d >= 0) d++; + } + Py_DECREF(e); + return d; +} + +static int +discover_itemsize(PyObject *s, int nd, int *itemsize) +{ + int n, r, i; + PyObject *e; + + n = PyObject_Length(s); + + if ((nd == 0) || PyString_Check(s) || \ + PyUnicode_Check(s) || PyBuffer_Check(s)) { + if PyUnicode_Check(s) + *itemsize = MAX(*itemsize, sizeof(Py_UNICODE)*n); + else + *itemsize = MAX(*itemsize, n); + return 0; + } + for (i=0; i<n; i++) { + if ((e=PySequence_GetItem(s,i))==NULL) return -1; + r=discover_itemsize(e,nd-1,itemsize); + Py_DECREF(e); + if (r == -1) return -1; + } + return 0; +} + +/* Take an arbitrary object known to represent + an array of ndim nd, and determine the size in each dimension +*/ + +static int +discover_dimensions(PyObject *s, int nd, intp *d, int check_it) +{ + PyObject *e; + int r, n, i, n_lower; + + n=PyObject_Length(s); + *d = n; + if(*d < 0) return -1; + if(nd <= 1) return 0; + n_lower = 0; + for(i=0; i<n; i++) { + if ((e=PySequence_GetItem(s,i)) == NULL) return -1; + r=discover_dimensions(e,nd-1,d+1,check_it); + Py_DECREF(e); + + if (r == -1) return -1; + if (check_it && n_lower != 0 && n_lower != d[1]) { + PyErr_SetString(PyExc_ValueError, + "inconsistent shape in sequence"); + return -1; + } + if (d[1] > n_lower) n_lower = d[1]; + } + d[1] = n_lower; + + return 0; +} + +/* new reference */ +/* doesn't alter refcount of chktype or mintype --- + unless one of them is returned */ +static PyArray_Descr * +_array_small_type(PyArray_Descr *chktype, PyArray_Descr* mintype) +{ + PyArray_Descr *outtype; + + if (chktype->type_num > mintype->type_num) outtype = chktype; + else outtype = mintype; + + Py_INCREF(outtype); + if (PyTypeNum_ISEXTENDED(outtype->type_num) && \ + (PyTypeNum_ISEXTENDED(mintype->type_num) || \ + mintype->type_num==0)) { + int testsize = outtype->elsize; + register int chksize, minsize; + chksize = chktype->elsize; + minsize = mintype->elsize; + /* Handle string->unicode case separately + because string itemsize is twice as large */ + if (outtype->type_num == PyArray_UNICODE && + mintype->type_num == PyArray_STRING) { + testsize = MAX(chksize, 2*minsize); + } + else { + testsize = MAX(chksize, minsize); + } + if (testsize != outtype->elsize) { + PyArray_DESCR_REPLACE(outtype); + outtype->elsize = testsize; + Py_XDECREF(outtype->fields); + outtype->fields = NULL; + } + } + return outtype; +} + +/* op is an object to be converted to an ndarray. + + minitype is the minimum type-descriptor needed. + + max is the maximum number of dimensions -- used for recursive call + to avoid infinite recursion... + +*/ + +static PyArray_Descr * +_array_find_type(PyObject *op, PyArray_Descr *minitype, int max) +{ + int l; + PyObject *ip; + PyArray_Descr *chktype=NULL; + PyArray_Descr *outtype; + + if (minitype == NULL) + minitype = PyArray_DescrFromType(PyArray_BOOL); + else Py_INCREF(minitype); + + if (max < 0) goto deflt; + + if (PyArray_Check(op)) { + chktype = PyArray_DESCR(op); + Py_INCREF(chktype); + goto finish; + } + + if (PyArray_IsScalar(op, Generic)) { + chktype = PyArray_DescrFromScalar(op); + goto finish; + } + + if ((ip=PyObject_GetAttrString(op, "__array_typestr__"))!=NULL) { + if (PyString_Check(ip)) { + chktype =_array_typedescr_fromstr(PyString_AS_STRING(ip)); + } + Py_DECREF(ip); + if (chktype) goto finish; + } + else PyErr_Clear(); + + if ((ip=PyObject_GetAttrString(op, "__array_struct__")) != NULL) { + PyArrayInterface *inter; + char buf[40]; + if (PyCObject_Check(ip)) { + inter=(PyArrayInterface *)PyCObject_AsVoidPtr(ip); + if (inter->version == 2) { + snprintf(buf, 40, "|%c%d", inter->typekind, + inter->itemsize); + chktype = _array_typedescr_fromstr(buf); + } + } + Py_DECREF(ip); + if (chktype) goto finish; + } + else PyErr_Clear(); + + if (PyString_Check(op)) { + chktype = PyArray_DescrNewFromType(PyArray_STRING); + chktype->elsize = PyString_GET_SIZE(op); + goto finish; + } + + if (PyUnicode_Check(op)) { + chktype = PyArray_DescrNewFromType(PyArray_UNICODE); + chktype->elsize = PyUnicode_GET_DATA_SIZE(op); + goto finish; + } + + if (PyBuffer_Check(op)) { + chktype = PyArray_DescrNewFromType(PyArray_VOID); + chktype->elsize = op->ob_type->tp_as_sequence->sq_length(op); + PyErr_Clear(); + goto finish; + } + + if (PyObject_HasAttrString(op, "__array__")) { + ip = PyObject_CallMethod(op, "__array__", NULL); + if(ip && PyArray_Check(ip)) { + chktype = PyArray_DESCR(ip); + Py_INCREF(chktype); + Py_DECREF(ip); + goto finish; + } + Py_XDECREF(ip); + if (PyErr_Occurred()) PyErr_Clear(); + } + + if (PyInstance_Check(op)) goto deflt; + + if (PySequence_Check(op)) { + + l = PyObject_Length(op); + if (l < 0 && PyErr_Occurred()) { + PyErr_Clear(); + goto deflt; + } + if (l == 0 && minitype->type_num == PyArray_BOOL) { + Py_DECREF(minitype); + minitype = PyArray_DescrFromType(PyArray_INTP); + } + while (--l >= 0) { + PyArray_Descr *newtype; + ip = PySequence_GetItem(op, l); + if (ip==NULL) { + PyErr_Clear(); + goto deflt; + } + chktype = _array_find_type(ip, minitype, max-1); + newtype = _array_small_type(chktype, minitype); + Py_DECREF(minitype); + minitype = newtype; + Py_DECREF(chktype); + Py_DECREF(ip); + } + chktype = minitype; + Py_INCREF(minitype); + goto finish; + } + + if (PyBool_Check(op)) { + chktype = PyArray_DescrFromType(PyArray_BOOL); + goto finish; + } + else if (PyInt_Check(op)) { + chktype = PyArray_DescrFromType(PyArray_LONG); + goto finish; + } else if (PyFloat_Check(op)) { + chktype = PyArray_DescrFromType(PyArray_DOUBLE); + goto finish; + } else if (PyComplex_Check(op)) { + chktype = PyArray_DescrFromType(PyArray_CDOUBLE); + goto finish; + } + + deflt: + chktype = PyArray_DescrFromType(PyArray_OBJECT); + + finish: + + outtype = _array_small_type(chktype, minitype); + Py_DECREF(chktype); + Py_DECREF(minitype); + return outtype; +} + +static int +Assign_Array(PyArrayObject *self, PyObject *v) +{ + PyObject *e; + int l, r; + + if (!PySequence_Check(v)) { + PyErr_SetString(PyExc_ValueError, + "assignment from non-sequence"); + return -1; + } + + l=PyObject_Length(v); + if(l < 0) return -1; + + while(--l >= 0) + { + e=PySequence_GetItem(v,l); + if (e == NULL) return -1; + r = PySequence_SetItem((PyObject*)self,l,e); + Py_DECREF(e); + if(r == -1) return -1; + } + return 0; +} + +/* "Array Scalars don't call this code" */ +/* steals reference to typecode -- no NULL*/ +static PyObject * +Array_FromScalar(PyObject *op, PyArray_Descr *typecode) +{ + PyArrayObject *ret; + int itemsize; + int type; + + itemsize = typecode->elsize; + type = typecode->type_num; + + if (itemsize == 0 && PyTypeNum_ISEXTENDED(type)) { + itemsize = PyObject_Length(op); + if (type == PyArray_UNICODE) itemsize *= sizeof(Py_UNICODE); + } + + ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, typecode, + 0, NULL, + NULL, NULL, 0, NULL); + + if (ret == NULL) return NULL; + + ret->descr->f->setitem(op, ret->data, ret); + + if (PyErr_Occurred()) { + Py_DECREF(ret); + return NULL; + } else { + return (PyObject *)ret; + } +} + + +/* steals reference to typecode unless return value is NULL*/ +static PyObject * +Array_FromSequence(PyObject *s, PyArray_Descr *typecode, int fortran, + int min_depth, int max_depth) +{ + PyArrayObject *r; + int nd; + intp d[MAX_DIMS]; + int stop_at_string; + int stop_at_tuple; + int type = typecode->type_num; + int itemsize = typecode->elsize; + PyArray_Descr *savetype=typecode; + + stop_at_string = ((type == PyArray_OBJECT) || \ + (type == PyArray_STRING) || \ + (type == PyArray_UNICODE) || \ + (type == PyArray_VOID)); + + stop_at_tuple = (type == PyArray_VOID && ((typecode->fields && \ + typecode->fields!=Py_None) \ + || (typecode->subarray))); + + if (!((nd=discover_depth(s, MAX_DIMS+1, stop_at_string, + stop_at_tuple)) > 0)) { + if (nd==0) + return Array_FromScalar(s, typecode); + PyErr_SetString(PyExc_ValueError, + "invalid input sequence"); + return NULL; + } + + if ((max_depth && nd > max_depth) || \ + (min_depth && nd < min_depth)) { + PyErr_SetString(PyExc_ValueError, + "invalid number of dimensions"); + return NULL; + } + + if(discover_dimensions(s,nd,d, !stop_at_string) == -1) { + return NULL; + } + if (itemsize == 0 && PyTypeNum_ISEXTENDED(type)) { + if (discover_itemsize(s, nd, &itemsize) == -1) { + return NULL; + } + if (type == PyArray_UNICODE) itemsize*=sizeof(Py_UNICODE); + } + + if (itemsize != typecode->elsize) { + PyArray_DESCR_REPLACE(typecode); + typecode->elsize = itemsize; + } + + r=(PyArrayObject*)PyArray_NewFromDescr(&PyArray_Type, typecode, + nd, d, + NULL, NULL, + fortran, NULL); + + if(!r) {Py_XINCREF(savetype); return NULL;} + if(Assign_Array(r,s) == -1) { + Py_XINCREF(savetype); + Py_DECREF(r); + return NULL; + } + return (PyObject*)r; +} + + +/*OBJECT_API + Is the typenum valid? +*/ +static int +PyArray_ValidType(int type) +{ + PyArray_Descr *descr; + int res=TRUE; + + descr = PyArray_DescrFromType(type); + if (descr==NULL) res = FALSE; + Py_DECREF(descr); + return res; +} + + +/* If the output is not a CARRAY, then it is buffered also */ + +static int +_bufferedcast(PyArrayObject *out, PyArrayObject *in) +{ + char *inbuffer, *bptr, *optr; + char *outbuffer=NULL; + PyArrayIterObject *it_in=NULL, *it_out=NULL; + register intp i, index; + intp ncopies = PyArray_SIZE(out) / PyArray_SIZE(in); + int elsize=in->descr->elsize; + int nels = PyArray_BUFSIZE; + int el; + int inswap, outswap=0; + int obuf=!PyArray_ISCARRAY(out); + int oelsize = out->descr->elsize; + PyArray_VectorUnaryFunc *castfunc; + PyArray_CopySwapFunc *in_csn; + PyArray_CopySwapFunc *out_csn; + int retval = -1; + + castfunc = in->descr->f->cast[out->descr->type_num]; + in_csn = in->descr->f->copyswap; + out_csn = out->descr->f->copyswap; + + /* If the input or output is STRING, UNICODE, or VOID */ + /* then getitem and setitem are used for the cast */ + /* and byteswapping is handled by those methods */ + + inswap = !(PyArray_ISFLEXIBLE(in) || PyArray_ISNOTSWAPPED(in)); + + inbuffer = PyDataMem_NEW(PyArray_BUFSIZE*elsize); + if (inbuffer == NULL) return -1; + if (PyArray_ISOBJECT(in)) + memset(inbuffer, 0, PyArray_BUFSIZE*elsize); + it_in = (PyArrayIterObject *)PyArray_IterNew((PyObject *)in); + if (it_in == NULL) goto exit; + + if (obuf) { + outswap = !(PyArray_ISFLEXIBLE(out) || \ + PyArray_ISNOTSWAPPED(out)); + outbuffer = PyDataMem_NEW(PyArray_BUFSIZE*oelsize); + if (outbuffer == NULL) goto exit; + if (PyArray_ISOBJECT(out)) + memset(outbuffer, 0, PyArray_BUFSIZE*oelsize); + + it_out = (PyArrayIterObject *)PyArray_IterNew((PyObject *)out); + if (it_out == NULL) goto exit; + + nels = MIN(nels, PyArray_BUFSIZE); + } + + optr = (obuf) ? outbuffer: out->data; + bptr = inbuffer; + el = 0; + while(ncopies--) { + index = it_in->size; + PyArray_ITER_RESET(it_in); + while(index--) { + in_csn(bptr, it_in->dataptr, inswap, elsize); + bptr += elsize; + PyArray_ITER_NEXT(it_in); + el += 1; + if ((el == nels) || (index == 0)) { + /* buffer filled, do cast */ + + castfunc(inbuffer, optr, el, in, out); + + if (obuf) { + /* Copy from outbuffer to array */ + for(i=0; i<el; i++) { + out_csn(it_out->dataptr, + optr, outswap, + oelsize); + optr += oelsize; + PyArray_ITER_NEXT(it_out); + } + optr = outbuffer; + } + else { + optr += out->descr->elsize * nels; + } + el = 0; + bptr = inbuffer; + } + } + } + retval = 0; + exit: + Py_XDECREF(it_in); + PyDataMem_FREE(inbuffer); + PyDataMem_FREE(outbuffer); + if (obuf) { + Py_XDECREF(it_out); + } + return retval; +} + + +/* For backward compatibility */ + +/* steals reference to at --- cannot be NULL*/ +/*OBJECT_API + Cast an array using typecode structure. +*/ +static PyObject * +PyArray_CastToType(PyArrayObject *mp, PyArray_Descr *at, int fortran) +{ + PyObject *out; + int ret; + PyArray_Descr *mpd; + + mpd = mp->descr; + + if (((mpd == at) || ((mpd->type_num == at->type_num) && \ + PyArray_EquivByteorders(mpd->byteorder,\ + at->byteorder) && \ + ((mpd->elsize == at->elsize) || \ + (at->elsize==0)))) && \ + PyArray_ISBEHAVED_RO(mp)) { + Py_DECREF(at); + Py_INCREF(mp); + return (PyObject *)mp; + } + + if (at->elsize == 0) { + PyArray_DESCR_REPLACE(at); + if (at == NULL) return NULL; + if (mpd->type_num == PyArray_STRING && \ + at->type_num == PyArray_UNICODE) + at->elsize = mpd->elsize*sizeof(Py_UNICODE); + if (mpd->type_num == PyArray_UNICODE && + at->type_num == PyArray_STRING) + at->elsize = mpd->elsize/sizeof(Py_UNICODE); + if (at->type_num == PyArray_VOID) + at->elsize = mpd->elsize; + } + + out = PyArray_NewFromDescr(mp->ob_type, at, + mp->nd, + mp->dimensions, + NULL, NULL, + fortran, + (PyObject *)mp); + + if (out == NULL) return NULL; + ret = PyArray_CastTo((PyArrayObject *)out, mp); + if (ret != -1) return out; + + Py_DECREF(out); + return NULL; + +} + +/* The number of elements in out must be an integer multiple + of the number of elements in mp. +*/ + +/*OBJECT_API + Cast to an already created array. +*/ +static int +PyArray_CastTo(PyArrayObject *out, PyArrayObject *mp) +{ + + int simple; + intp mpsize = PyArray_SIZE(mp); + intp outsize = PyArray_SIZE(out); + + if (mpsize == 0) return 0; + if (!PyArray_ISWRITEABLE(out)) { + PyErr_SetString(PyExc_ValueError, + "output array is not writeable"); + return -1; + } + if (outsize % mpsize != 0) { + PyErr_SetString(PyExc_ValueError, + "output array must have an integer-multiple"\ + " of the number of elements in the input "\ + "array"); + return -1; + } + + if (out->descr->type_num >= PyArray_NTYPES) { + PyErr_SetString(PyExc_ValueError, + "Can only cast to builtin types."); + return -1; + + } + + simple = ((PyArray_ISCARRAY_RO(mp) && PyArray_ISCARRAY(out)) || \ + (PyArray_ISFARRAY_RO(mp) && PyArray_ISFARRAY(out))); + + if (simple) { + char *inptr; + char *optr = out->data; + intp obytes = out->descr->elsize * outsize; + intp ncopies = outsize / mpsize; + + while(ncopies--) { + inptr = mp->data; + mp->descr->f->cast[out->descr->type_num](inptr, + optr, + mpsize, + mp, out); + optr += obytes; + } + return 0; + } + + /* If not a well-behaved cast, then use buffers */ + if (_bufferedcast(out, mp) == -1) { + return -1; + } + return 0; +} + +/* steals reference to newtype --- acc. NULL */ +static PyObject * +array_fromarray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) +{ + + PyArrayObject *ret=NULL; + int type, itemsize; + int copy = 0; + int arrflags; + PyArray_Descr *oldtype; + char *msg = "cannot copy back to a read-only array"; + PyTypeObject *subtype; + + oldtype = PyArray_DESCR(arr); + + subtype = arr->ob_type; + + if (newtype == NULL) {newtype = oldtype; Py_INCREF(oldtype);} + type = newtype->type_num; + itemsize = newtype->elsize; + + /* Don't copy if sizes are compatible */ + if (PyArray_EquivTypes(oldtype, newtype)) { + arrflags = arr->flags; + + copy = (flags & ENSURECOPY) || \ + ((flags & CONTIGUOUS) && (!(arrflags & CONTIGUOUS))) \ + || ((flags & ALIGNED) && (!(arrflags & ALIGNED))) \ + || (arr->nd > 1 && \ + ((flags & FORTRAN) != (arrflags & FORTRAN))) || \ + ((flags & WRITEABLE) && (!(arrflags & WRITEABLE))); + + if (copy) { + if ((flags & UPDATEIFCOPY) && \ + (!PyArray_ISWRITEABLE(arr))) { + Py_DECREF(newtype); + PyErr_SetString(PyExc_ValueError, msg); + return NULL; + } + if ((flags & ENSUREARRAY) && \ + (subtype != &PyBigArray_Type)) { + subtype = &PyArray_Type; + } + ret = (PyArrayObject *) \ + PyArray_NewFromDescr(subtype, newtype, + arr->nd, + arr->dimensions, + NULL, NULL, + flags & FORTRAN, + (PyObject *)arr); + if (ret == NULL) return NULL; + if (PyArray_CopyInto(ret, arr) == -1) + {Py_DECREF(ret); return NULL;} + if (flags & UPDATEIFCOPY) { + ret->flags |= UPDATEIFCOPY; + ret->base = (PyObject *)arr; + PyArray_FLAGS(ret->base) &= ~WRITEABLE; + Py_INCREF(arr); + } + } + /* If no copy then just increase the reference + count and return the input */ + else { + if ((flags & ENSUREARRAY) && \ + (subtype != &PyBigArray_Type)) { + Py_DECREF(newtype); + Py_INCREF(arr->descr); + ret = (PyArrayObject *) \ + PyArray_NewFromDescr(&PyArray_Type, + arr->descr, + arr->nd, + arr->dimensions, + arr->strides, + arr->data, + arr->flags,NULL); + if (ret == NULL) return NULL; + ret->base = (PyObject *)arr; + } + else { + ret = arr; + } + Py_INCREF(arr); + } + } + + /* The desired output type is different than the input + array type */ + else { + /* Cast to the desired type if we can do it safely + Also cast if source is a ndim-0 array to mimic + behavior with Python scalars */ + if (flags & FORCECAST || PyArray_NDIM(arr)==0 || + PyArray_CanCastTo(oldtype, newtype)) { + if ((flags & UPDATEIFCOPY) && \ + (!PyArray_ISWRITEABLE(arr))) { + Py_DECREF(newtype); + PyErr_SetString(PyExc_ValueError, msg); + return NULL; + } + if ((flags & ENSUREARRAY) && \ + (subtype != &PyBigArray_Type)) { + subtype = &PyArray_Type; + } + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(subtype, + newtype, + arr->nd, + arr->dimensions, + NULL, NULL, + flags & FORTRAN, + (PyObject *)arr); + if (ret == NULL) return NULL; + if (PyArray_CastTo(ret, arr) < 0) { + Py_DECREF(ret); + return NULL; + } + if (flags & UPDATEIFCOPY) { + ret->flags |= UPDATEIFCOPY; + ret->base = (PyObject *)arr; + PyArray_FLAGS(ret->base) &= ~WRITEABLE; + Py_INCREF(arr); + } + } + else { + PyErr_SetString(PyExc_TypeError, + "array cannot be safely cast " \ + "to required type"); + ret = NULL; + } + } + return (PyObject *)ret; +} + +/* new reference */ +static PyArray_Descr * +_array_typedescr_fromstr(char *str) +{ + PyArray_Descr *descr; + int type_num; + char typechar; + int size; + char msg[] = "unsupported typestring"; + int swap; + char swapchar; + + swapchar = str[0]; + str += 1; + +#define _MY_FAIL { \ + PyErr_SetString(PyExc_ValueError, msg); \ + return NULL; \ + } + + typechar = str[0]; + size = atoi(str + 1); + switch (typechar) { + case 'b': + if (size == sizeof(Bool)) + type_num = PyArray_BOOL; + else _MY_FAIL + break; + case 'u': + if (size == sizeof(uintp)) + type_num = PyArray_UINTP; + else if (size == sizeof(char)) + type_num = PyArray_UBYTE; + else if (size == sizeof(short)) + type_num = PyArray_USHORT; + else if (size == sizeof(ulong)) + type_num = PyArray_ULONG; + else if (size == sizeof(int)) + type_num = PyArray_UINT; + else if (size == sizeof(ulonglong)) + type_num = PyArray_ULONGLONG; + else _MY_FAIL + break; + case 'i': + if (size == sizeof(intp)) + type_num = PyArray_INTP; + else if (size == sizeof(char)) + type_num = PyArray_BYTE; + else if (size == sizeof(short)) + type_num = PyArray_SHORT; + else if (size == sizeof(long)) + type_num = PyArray_LONG; + else if (size == sizeof(int)) + type_num = PyArray_INT; + else if (size == sizeof(longlong)) + type_num = PyArray_LONGLONG; + else _MY_FAIL + break; + case 'f': + if (size == sizeof(float)) + type_num = PyArray_FLOAT; + else if (size == sizeof(double)) + type_num = PyArray_DOUBLE; + else if (size == sizeof(longdouble)) + type_num = PyArray_LONGDOUBLE; + else _MY_FAIL + break; + case 'c': + if (size == sizeof(float)*2) + type_num = PyArray_CFLOAT; + else if (size == sizeof(double)*2) + type_num = PyArray_CDOUBLE; + else if (size == sizeof(longdouble)*2) + type_num = PyArray_CLONGDOUBLE; + else _MY_FAIL + break; + case 'O': + if (size == sizeof(PyObject *)) + type_num = PyArray_OBJECT; + else _MY_FAIL + break; + case 'S': + type_num = PyArray_STRING; + break; + case 'U': + type_num = PyArray_UNICODE; + size *= sizeof(Py_UNICODE); + break; + case 'V': + type_num = PyArray_VOID; + break; + default: + _MY_FAIL + } + +#undef _MY_FAIL + + descr = PyArray_DescrFromType(type_num); + if (descr == NULL) return NULL; + swap = !PyArray_ISNBO(swapchar); + if (descr->elsize == 0 || swap) { + /* Need to make a new PyArray_Descr */ + PyArray_DESCR_REPLACE(descr); + if (descr==NULL) return NULL; + if (descr->elsize == 0) + descr->elsize = size; + if (swap) + descr->byteorder = swapchar; + } + return descr; +} + +/* steals a reference to intype unless NotImplemented */ +static PyObject * +array_fromstructinterface(PyObject *input, PyArray_Descr *intype, int flags) +{ + PyArray_Descr *thetype; + char buf[40]; + PyArrayInterface *inter; + PyObject *attr, *r, *ret; + char endian = PyArray_NATBYTE; + + attr = PyObject_GetAttrString(input, "__array_struct__"); + if (attr == NULL) { + PyErr_Clear(); + return Py_NotImplemented; + } + if (!PyCObject_Check(attr) || \ + ((inter=((PyArrayInterface *)\ + PyCObject_AsVoidPtr(attr)))->version != 2)) { + PyErr_SetString(PyExc_ValueError, "invalid __array_struct__"); + Py_XDECREF(intype); + Py_DECREF(attr); + return NULL; + } + if ((inter->flags & NOTSWAPPED) != NOTSWAPPED) { + endian = PyArray_OPPBYTE; + inter->flags &= ~NOTSWAPPED; + } + + snprintf(buf, 40, "%c%c%d", endian, inter->typekind, inter->itemsize); + if (!(thetype=_array_typedescr_fromstr(buf))) { + Py_XDECREF(intype); + Py_DECREF(attr); + return NULL; + } + + r = PyArray_NewFromDescr(&PyArray_Type, thetype, + inter->nd, inter->shape, + inter->strides, inter->data, + inter->flags, NULL); + Py_INCREF(input); + PyArray_BASE(r) = input; + Py_DECREF(attr); + PyArray_UpdateFlags((PyArrayObject *)r, UPDATE_ALL_FLAGS); + ret = array_fromarray((PyArrayObject*)r, intype, flags); + Py_DECREF(r); + return ret; +} + +/* steals a reference to intype unless NotImplemented */ +static PyObject * +array_frominterface(PyObject *input, PyArray_Descr *intype, int flags) +{ + PyObject *attr=NULL, *item=NULL, *r; + PyObject *tstr=NULL, *shape=NULL; + PyArrayObject *ret=NULL; + PyArray_Descr *type=NULL; + char *data; + int buffer_len; + int res, i, n; + intp dims[MAX_DIMS], strides[MAX_DIMS]; + int dataflags = BEHAVED_FLAGS; + + /* Get the memory from __array_data__ and __array_offset__ */ + /* Get the shape */ + /* Get the typestring -- ignore array_descr */ + /* Get the strides */ + + shape = PyObject_GetAttrString(input, "__array_shape__"); + if (shape == NULL) {PyErr_Clear(); return Py_NotImplemented;} + tstr = PyObject_GetAttrString(input, "__array_typestr__"); + if (tstr == NULL) {Py_DECREF(shape); PyErr_Clear(); return Py_NotImplemented;} + + attr = PyObject_GetAttrString(input, "__array_data__"); + if ((attr == NULL) || (attr==Py_None) || (!PyTuple_Check(attr))) { + if (attr && (attr != Py_None)) item=attr; + else item=input; + res = PyObject_AsWriteBuffer(item, (void **)&data, + &buffer_len); + if (res < 0) { + PyErr_Clear(); + res = PyObject_AsReadBuffer(item, (const void **)&data, + &buffer_len); + if (res < 0) goto fail; + dataflags &= ~WRITEABLE; + } + Py_XDECREF(attr); + attr = PyObject_GetAttrString(input, "__array_offset__"); + if (attr) { + long num = PyInt_AsLong(attr); + if (error_converting(num)) { + PyErr_SetString(PyExc_TypeError, + "__array_offset__ "\ + "must be an integer"); + goto fail; + } + data += num; + } + else PyErr_Clear(); + } + else { + if (PyTuple_GET_SIZE(attr) != 2) { + PyErr_SetString(PyExc_TypeError, + "__array_data__ must return " \ + "a 2-tuple with ('data pointer "\ + "string', read-only flag)"); + goto fail; + } + res = sscanf(PyString_AsString(PyTuple_GET_ITEM(attr,0)), + "%p", (void **)&data); + if (res < 1) { + PyErr_SetString(PyExc_TypeError, + "__array_data__ string cannot be " \ + "converted"); + goto fail; + } + if (PyObject_IsTrue(PyTuple_GET_ITEM(attr,1))) { + dataflags &= ~WRITEABLE; + } + } + Py_XDECREF(attr); + attr = tstr; + if (!PyString_Check(attr)) { + PyErr_SetString(PyExc_TypeError, "__array_typestr__ must be a string"); + Py_INCREF(attr); /* decref'd twice below */ + goto fail; + } + type = _array_typedescr_fromstr(PyString_AS_STRING(attr)); + Py_DECREF(attr); attr=NULL; tstr=NULL; + if (type==NULL) goto fail; + attr = shape; + if (!PyTuple_Check(attr)) { + PyErr_SetString(PyExc_TypeError, "__array_shape__ must be a tuple"); + Py_INCREF(attr); /* decref'd twice below */ + Py_DECREF(type); + goto fail; + } + n = PyTuple_GET_SIZE(attr); + for (i=0; i<n; i++) { + item = PyTuple_GET_ITEM(attr, i); + dims[i] = PyArray_PyIntAsIntp(item); + if (error_converting(dims[i])) break; + } + Py_DECREF(attr); shape=NULL; + + ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, type, + n, dims, + NULL, data, + dataflags, NULL); + if (ret == NULL) {Py_XDECREF(intype); return NULL;} + Py_INCREF(input); + ret->base = input; + + attr = PyObject_GetAttrString(input, "__array_strides__"); + if (attr != NULL && attr != Py_None) { + if (!PyTuple_Check(attr)) { + PyErr_SetString(PyExc_TypeError, + "__array_strides__ must be a tuple"); + Py_DECREF(attr); + Py_DECREF(ret); + Py_XDECREF(intype); + return NULL; + } + if (n != PyTuple_GET_SIZE(attr)) { + PyErr_SetString(PyExc_ValueError, + "mismatch in length of "\ + "__array_strides__ and "\ + "__array_shape__"); + Py_DECREF(attr); + Py_DECREF(ret); + Py_XDECREF(intype); + return NULL; + } + for (i=0; i<n; i++) { + item = PyTuple_GET_ITEM(attr, i); + strides[i] = PyArray_PyIntAsIntp(item); + if (error_converting(strides[i])) break; + } + Py_DECREF(attr); + if (PyErr_Occurred()) PyErr_Clear(); + memcpy(ret->strides, strides, n*sizeof(intp)); + } + else PyErr_Clear(); + PyArray_UpdateFlags(ret, UPDATE_ALL_FLAGS); + r = array_fromarray(ret, intype, flags); + Py_DECREF(ret); + return r; + + fail: + Py_XDECREF(intype); + Py_XDECREF(attr); + Py_XDECREF(shape); + Py_XDECREF(tstr); + return NULL; +} + +/* steals a reference to typecode */ +static PyObject * +array_fromattr(PyObject *op, PyArray_Descr *typecode, int flags) +{ + PyObject *new, *r; + + if (typecode == NULL) { + new = PyObject_CallMethod(op, "__array__", NULL); + } else { + PyObject *obj; + + if (PyTypeNum_ISEXTENDED(typecode->type_num)) { + obj = PyString_FromFormat("%c%d", typecode->type, + typecode->elsize); + } + else { + obj = (PyObject *)(typecode->typeobj); Py_INCREF(obj); + } + new = PyObject_CallMethod(op, "__array__", "N", obj); + } + if (new == NULL) {Py_XDECREF(typecode); return NULL;} + if (!PyArray_Check(new)) { + PyErr_SetString(PyExc_ValueError, + "object __array__ method not " \ + "producing an array"); + Py_DECREF(new); + Py_DECREF(typecode); + return NULL; + } + r = array_fromarray((PyArrayObject *)new, typecode, flags); + Py_DECREF(new); + return r; +} + +/* Steals a reference to newtype --- which can be NULL */ +static PyObject * +array_fromobject(PyObject *op, PyArray_Descr *newtype, int min_depth, + int max_depth, int flags) +{ + /* This is the main code to make a SciPy array from a Python + Object. It is called from lot's of different places which + is why there are so many checks. The comments try to + explain some of the checks. */ + + PyObject *r=NULL; + int seq = FALSE; + + /* Is input object already an array? */ + /* This is where the flags are used */ + if (PyArray_Check(op)) + r = array_fromarray((PyArrayObject *)op, newtype, flags); + else if (PyArray_IsScalar(op, Generic)) { + r = PyArray_FromScalar(op, newtype); + } + else if ((r = array_fromstructinterface(op, newtype, flags)) != \ + Py_NotImplemented) { + } + else if ((r = array_frominterface(op, newtype, flags)) != \ + Py_NotImplemented) { + } + else if (PyObject_HasAttrString(op, "__array__")) { + /* Code that returns the object to convert for a non + multiarray input object from the __array__ attribute of the + object. */ + r = array_fromattr(op, newtype, flags); + } + else { + if (newtype == NULL) { + newtype = _array_find_type(op, NULL, MAX_DIMS); + } + if (PySequence_Check(op)) { + /* necessary but not sufficient */ + + r = Array_FromSequence(op, newtype, flags & FORTRAN, + min_depth, max_depth); + if (PyErr_Occurred() && r == NULL) + /* It wasn't really a sequence after all. + * Try interpreting it as a scalar */ + PyErr_Clear(); + else + seq = TRUE; + } + if (!seq) + r = Array_FromScalar(op, newtype); + } + + /* If we didn't succeed return NULL */ + if (r == NULL) return NULL; + + /* Be sure we succeed here */ + + if(!PyArray_Check(r)) { + PyErr_SetString(PyExc_RuntimeError, + "internal error: array_fromobject "\ + "not producing an array"); + Py_DECREF(r); + return NULL; + } + + if (min_depth != 0 && ((PyArrayObject *)r)->nd < min_depth) { + PyErr_SetString(PyExc_ValueError, + "object of too small depth for desired array"); + Py_DECREF(r); + return NULL; + } + if (max_depth != 0 && ((PyArrayObject *)r)->nd > max_depth) { + PyErr_SetString(PyExc_ValueError, + "object too deep for desired array"); + Py_DECREF(r); + return NULL; + } + return r; +} + +/* new reference -- accepts NULL for mintype*/ +/*OBJECT_API*/ +static PyArray_Descr * +PyArray_DescrFromObject(PyObject *op, PyArray_Descr *mintype) +{ + return _array_find_type(op, mintype, MAX_DIMS); +} + +/*OBJECT_API + Return the typecode of the array a Python object would be converted + to +*/ +static int +PyArray_ObjectType(PyObject *op, int minimum_type) +{ + PyArray_Descr *intype; + PyArray_Descr *outtype; + int ret; + + intype = PyArray_DescrFromType(minimum_type); + if (intype == NULL) PyErr_Clear(); + outtype = _array_find_type(op, intype, MAX_DIMS); + ret = outtype->type_num; + Py_DECREF(outtype); + Py_DECREF(intype); + return ret; +} + + +/* flags is any of + CONTIGUOUS, + FORTRAN, + ALIGNED, + WRITEABLE, + NOTSWAPPED, + ENSURECOPY, + UPDATEIFCOPY, + FORCECAST, + ENSUREARRAY + + 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 ENSURECOPY is enough + to guarantee CONTIGUOUS, ALIGNED and WRITEABLE + and therefore it is redundant to include those as well. + + BEHAVED_FLAGS == ALIGNED | WRITEABLE + CARRAY_FLAGS = CONTIGUOUS | BEHAVED_FLAGS + FARRAY_FLAGS = FORTRAN | BEHAVED_FLAGS + + FORTRAN 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). + + UPDATEIFCOPY flag sets this flag in the returned array if a copy is + made and the base argument points to the (possibly) misbehaved array. + When the new array is deallocated, the original array held in base + is updated with the contents of the new array. + + FORCECAST will cause a cast to occur regardless of whether or not + it is safe. +*/ + + +/* steals a reference to descr -- accepts NULL */ +/*OBJECT_API*/ +static PyObject * +PyArray_FromAny(PyObject *op, PyArray_Descr *descr, int min_depth, + int max_depth, int requires) +{ + if (requires & ENSURECOPY) { + requires |= DEFAULT_FLAGS; + } + if (requires & NOTSWAPPED) { + if (!descr && PyArray_Check(op) && \ + !PyArray_ISNBO(PyArray_DESCR(op)->byteorder)) { + descr = PyArray_DescrNew(PyArray_DESCR(op)); + } + else if ((descr && !PyArray_ISNBO(descr->byteorder))) { + PyArray_DESCR_REPLACE(descr); + } + descr->byteorder = PyArray_NATIVE; + } + + return array_fromobject(op, descr, min_depth, max_depth, + requires); +} + +/* This is a quick wrapper around PyArray_FromAny(op, NULL, 0, 0, + ENSUREARRAY) */ +/* 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 or PyBigArray_Type */ + +/* Because it decrefs op if any conversion needs to take place + so it can be used like PyArray_EnsureArray(some_function(...)) */ + +/*OBJECT_API*/ +static PyObject * +PyArray_EnsureArray(PyObject *op) +{ + PyObject *new; + + if (op == NULL) return NULL; + + if (PyArray_CheckExact(op) || PyBigArray_CheckExact(op)) return op; + + if (PyArray_IsScalar(op, Generic)) { + new = PyArray_FromScalar(op, NULL); + Py_DECREF(op); + return new; + } + new = PyArray_FROM_OF(op, ENSUREARRAY); + Py_DECREF(op); + return new; +} + + + +/*OBJECT_API + Check the type coercion rules. +*/ +static int +PyArray_CanCastSafely(int fromtype, int totype) +{ + PyArray_Descr *from, *to; + register int felsize, telsize; + + if (fromtype == totype) return 1; + if (fromtype == PyArray_BOOL) return 1; + if (totype == PyArray_BOOL) return 0; + if (totype == PyArray_OBJECT || totype == PyArray_VOID) return 1; + if (fromtype == PyArray_OBJECT || fromtype == PyArray_VOID) return 0; + + from = PyArray_DescrFromType(fromtype); + to = PyArray_DescrFromType(totype); + telsize = to->elsize; + felsize = from->elsize; + Py_DECREF(from); + Py_DECREF(to); + + switch(fromtype) { + case PyArray_BYTE: + case PyArray_SHORT: + case PyArray_INT: + case PyArray_LONG: + case PyArray_LONGLONG: + if (PyTypeNum_ISINTEGER(totype)) { + if (PyTypeNum_ISUNSIGNED(totype)) { + return (telsize > felsize); + } + else { + return (telsize >= felsize); + } + } + else if (PyTypeNum_ISFLOAT(totype)) { + if (felsize < 8) + return (telsize > felsize); + else + return (telsize >= felsize); + } + else if (PyTypeNum_ISCOMPLEX(totype)) { + if (felsize < 8) + return ((telsize >> 1) > felsize); + else + return ((telsize >> 1) >= felsize); + } + else return totype > fromtype; + case PyArray_UBYTE: + case PyArray_USHORT: + case PyArray_UINT: + case PyArray_ULONG: + case PyArray_ULONGLONG: + if (PyTypeNum_ISINTEGER(totype)) { + if (PyTypeNum_ISSIGNED(totype)) { + return (telsize > felsize); + } + else { + return (telsize >= felsize); + } + } + else if (PyTypeNum_ISFLOAT(totype)) { + if (felsize < 8) + return (telsize > felsize); + else + return (telsize >= felsize); + } + else if (PyTypeNum_ISCOMPLEX(totype)) { + if (felsize < 8) + return ((telsize >> 1) > felsize); + else + return ((telsize >> 1) >= felsize); + } + else return totype > fromtype; + case PyArray_FLOAT: + case PyArray_DOUBLE: + case PyArray_LONGDOUBLE: + if (PyTypeNum_ISCOMPLEX(totype)) + return ((telsize >> 1) >= felsize); + else + return (totype > fromtype); + case PyArray_CFLOAT: + case PyArray_CDOUBLE: + case PyArray_CLONGDOUBLE: + return (totype > fromtype); + case PyArray_STRING: + case PyArray_UNICODE: + return (totype > fromtype); + default: + return 0; + } +} + +/* leaves reference count alone --- cannot be NULL*/ +/*OBJECT_API*/ +static Bool +PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to) +{ + int fromtype=from->type_num; + int totype=to->type_num; + Bool ret; + + ret = (Bool) PyArray_CanCastSafely(fromtype, totype); + if (ret) { /* Check String and Unicode more closely */ + if (fromtype == PyArray_STRING) { + if (totype == PyArray_STRING) { + ret = (from->elsize <= to->elsize); + } + else if (totype == PyArray_UNICODE) { + ret = (from->elsize * sizeof(Py_UNICODE)\ + <= to->elsize); + } + } + else if (fromtype == PyArray_UNICODE) { + if (totype == PyArray_UNICODE) { + ret = (from->elsize <= to->elsize); + } + } + /* TODO: If totype is STRING or unicode + see if the length is long enough to hold the + stringified value of the object. + */ + } + return ret; +} + + + +/*********************** Element-wise Array Iterator ***********************/ +/* Aided by Peter J. Verveer's nd_image package and scipy's arraymap ****/ +/* and Python's array iterator ***/ + + +/*OBJECT_API + Get Iterator. +*/ +static PyObject * +PyArray_IterNew(PyObject *obj) +{ + PyArrayIterObject *it; + int i, nd; + PyArrayObject *ao = (PyArrayObject *)obj; + + if (!PyArray_Check(ao)) { + PyErr_BadInternalCall(); + return NULL; + } + + it = (PyArrayIterObject *)_pya_malloc(sizeof(PyArrayIterObject)); + PyObject_Init((PyObject *)it, &PyArrayIter_Type); + /* it = PyObject_New(PyArrayIterObject, &PyArrayIter_Type);*/ + if (it == NULL) + return NULL; + + nd = ao->nd; + PyArray_UpdateFlags(ao, CONTIGUOUS); + it->contiguous = 0; + if PyArray_ISCONTIGUOUS(ao) it->contiguous = 1; + Py_INCREF(ao); + it->ao = ao; + it->size = PyArray_SIZE(ao); + it->nd_m1 = nd - 1; + it->factors[nd-1] = 1; + for (i=0; i < nd; i++) { + it->dims_m1[i] = it->ao->dimensions[i] - 1; + it->strides[i] = it->ao->strides[i]; + it->backstrides[i] = it->strides[i] * \ + it->dims_m1[i]; + if (i > 0) + it->factors[nd-i-1] = it->factors[nd-i] * \ + it->ao->dimensions[nd-i]; + } + PyArray_ITER_RESET(it); + + return (PyObject *)it; +} + + +/*OBJECT_API + Get Iterator that iterates over all but one axis (don't use this with + PyArray_ITER_GOTO1D) +*/ +static PyObject * +PyArray_IterAllButAxis(PyObject *obj, int axis) +{ + PyArrayIterObject *it; + it = (PyArrayIterObject *)PyArray_IterNew(obj); + if (it == NULL) return NULL; + + /* adjust so that will not iterate over axis */ + it->contiguous = 0; + if (it->size != 0) { + it->size /= PyArray_DIM(obj,axis); + } + it->dims_m1[axis] = 0; + it->backstrides[axis] = 0; + + /* (won't fix factors so don't use + PyArray_ITER_GOTO1D with this iterator) */ + return (PyObject *)it; +} + +/* Returns an array scalar holding the element desired */ + +static PyObject * +arrayiter_next(PyArrayIterObject *it) +{ + PyObject *ret; + + if (it->index < it->size) { + ret = PyArray_ToScalar(it->dataptr, it->ao); + PyArray_ITER_NEXT(it); + return ret; + } + return NULL; +} + +static void +arrayiter_dealloc(PyArrayIterObject *it) +{ + Py_XDECREF(it->ao); + _pya_free(it); +} + +static int +iter_length(PyArrayIterObject *self) +{ + return (int) self->size; +} + + +static PyObject * +iter_subscript_Bool(PyArrayIterObject *self, PyArrayObject *ind) +{ + int index, strides, itemsize; + intp count=0; + char *dptr, *optr; + PyObject *r; + int swap; + PyArray_CopySwapFunc *copyswap; + + + if (ind->nd != 1) { + PyErr_SetString(PyExc_ValueError, + "boolean index array should have 1 dimension"); + return NULL; + } + index = (ind->dimensions[0]); + strides = ind->strides[0]; + dptr = ind->data; + /* Get size of return array */ + while(index--) { + if (*((Bool *)dptr) != 0) + count++; + dptr += strides; + } + itemsize = self->ao->descr->elsize; + Py_INCREF(self->ao->descr); + r = PyArray_NewFromDescr(self->ao->ob_type, + self->ao->descr, 1, &count, + NULL, NULL, + 0, (PyObject *)self->ao); + if (r==NULL) return NULL; + + /* Set up loop */ + optr = PyArray_DATA(r); + index = ind->dimensions[0]; + dptr = ind->data; + + copyswap = self->ao->descr->f->copyswap; + /* Loop over Boolean array */ + swap = !(PyArray_ISNOTSWAPPED(self->ao)); + while(index--) { + if (*((Bool *)dptr) != 0) { + copyswap(optr, self->dataptr, swap, itemsize); + optr += itemsize; + } + dptr += strides; + PyArray_ITER_NEXT(self); + } + PyArray_ITER_RESET(self); + return r; +} + +static PyObject * +iter_subscript_int(PyArrayIterObject *self, PyArrayObject *ind) +{ + intp num; + PyObject *r; + PyArrayIterObject *ind_it; + int itemsize; + int swap; + char *optr; + int index; + PyArray_CopySwapFunc *copyswap; + + itemsize = self->ao->descr->elsize; + if (ind->nd == 0) { + num = *((intp *)ind->data); + PyArray_ITER_GOTO1D(self, num); + r = PyArray_ToScalar(self->dataptr, self->ao); + PyArray_ITER_RESET(self); + return r; + } + + Py_INCREF(self->ao->descr); + r = PyArray_NewFromDescr(self->ao->ob_type, self->ao->descr, + ind->nd, ind->dimensions, + NULL, NULL, + 0, (PyObject *)self->ao); + if (r==NULL) return NULL; + + optr = PyArray_DATA(r); + ind_it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ind); + if (ind_it == NULL) {Py_DECREF(r); return NULL;} + index = ind_it->size; + copyswap = PyArray_DESCR(r)->f->copyswap; + swap = !PyArray_ISNOTSWAPPED(self->ao); + while(index--) { + num = *((intp *)(ind_it->dataptr)); + if (num < 0) num += self->size; + if (num < 0 || num >= self->size) { + PyErr_Format(PyExc_IndexError, + "index %d out of bounds" \ + " 0<=index<%d", (int) num, + (int) self->size); + Py_DECREF(ind_it); + Py_DECREF(r); + PyArray_ITER_RESET(self); + return NULL; + } + PyArray_ITER_GOTO1D(self, num); + copyswap(optr, self->dataptr, swap, itemsize); + optr += itemsize; + PyArray_ITER_NEXT(ind_it); + } + Py_DECREF(ind_it); + PyArray_ITER_RESET(self); + return r; +} + + +static PyObject * +iter_subscript(PyArrayIterObject *self, PyObject *ind) +{ + PyArray_Descr *indtype=NULL; + intp start, step_size; + intp n_steps; + PyObject *r; + char *dptr; + int size; + PyObject *obj = NULL; + int swap; + PyArray_CopySwapFunc *copyswap; + + if (ind == Py_Ellipsis) { + ind = PySlice_New(NULL, NULL, NULL); + obj = iter_subscript(self, ind); + Py_DECREF(ind); + return obj; + } + if (PyTuple_Check(ind)) { + int len; + len = PyTuple_GET_SIZE(ind); + if (len > 1) goto fail; + ind = PyTuple_GET_ITEM(ind, 0); + } + + /* Tuples >1d not accepted --- i.e. no NewAxis */ + /* Could implement this with adjusted strides + and dimensions in iterator */ + + /* Check for Boolean -- this is first becasue + Bool is a subclass of Int */ + PyArray_ITER_RESET(self); + + if (PyBool_Check(ind)) { + if (PyObject_IsTrue(ind)) { + return PyArray_ToScalar(self->dataptr, self->ao); + } + else { /* empty array */ + intp ii = 0; + Py_INCREF(self->ao->descr); + r = PyArray_NewFromDescr(self->ao->ob_type, + self->ao->descr, + 1, &ii, + NULL, NULL, 0, + (PyObject *)self->ao); + return r; + } + } + + /* Check for Integer or Slice */ + + if (PyLong_Check(ind) || PyInt_Check(ind) || PySlice_Check(ind)) { + start = parse_subindex(ind, &step_size, &n_steps, + self->size); + if (start == -1) + goto fail; + if (n_steps == RubberIndex || n_steps == PseudoIndex) { + PyErr_SetString(PyExc_IndexError, + "cannot use Ellipsis or NewAxes here"); + goto fail; + } + PyArray_ITER_GOTO1D(self, start) + if (n_steps == SingleIndex) { /* Integer */ + r = PyArray_ToScalar(self->dataptr, self->ao); + PyArray_ITER_RESET(self); + return r; + } + size = self->ao->descr->elsize; + Py_INCREF(self->ao->descr); + r = PyArray_NewFromDescr(self->ao->ob_type, + self->ao->descr, + 1, &n_steps, + NULL, NULL, + 0, (PyObject *)self->ao); + if (r==NULL) goto fail; + dptr = PyArray_DATA(r); + swap = !PyArray_ISNOTSWAPPED(self->ao); + copyswap = PyArray_DESCR(r)->f->copyswap; + while(n_steps--) { + copyswap(dptr, self->dataptr, swap, size); + start += step_size; + PyArray_ITER_GOTO1D(self, start) + dptr += size; + } + PyArray_ITER_RESET(self); + return r; + } + + /* convert to INTP array if Integer array scalar or List */ + + indtype = PyArray_DescrFromType(PyArray_INTP); + if (PyArray_IsScalar(ind, Integer) || PyList_Check(ind)) { + Py_INCREF(indtype); + obj = PyArray_FromAny(ind, indtype, 0, 0, FORCECAST); + if (obj == NULL) goto fail; + } + else { + Py_INCREF(ind); + obj = ind; + } + + if (PyArray_Check(obj)) { + /* Check for Boolean object */ + if (PyArray_TYPE(obj)==PyArray_BOOL) { + r = iter_subscript_Bool(self, (PyArrayObject *)obj); + Py_DECREF(indtype); + } + /* Check for integer array */ + else if (PyArray_ISINTEGER(obj)) { + PyObject *new; + new = PyArray_FromAny(obj, indtype, 0, 0, + FORCECAST | ALIGNED); + if (new==NULL) goto fail; + Py_DECREF(obj); + obj = new; + r = iter_subscript_int(self, (PyArrayObject *)obj); + } + else { + goto fail; + } + Py_DECREF(obj); + return r; + } + else Py_DECREF(indtype); + + + fail: + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_IndexError, "unsupported iterator index"); + Py_XDECREF(indtype); + Py_XDECREF(obj); + return NULL; + +} + + +static int +iter_ass_sub_Bool(PyArrayIterObject *self, PyArrayObject *ind, + PyArrayIterObject *val, int swap) +{ + int index, strides, itemsize; + char *dptr; + PyArray_CopySwapFunc *copyswap; + + if (ind->nd != 1) { + PyErr_SetString(PyExc_ValueError, + "boolean index array should have 1 dimension"); + return -1; + } + itemsize = self->ao->descr->elsize; + index = ind->dimensions[0]; + strides = ind->strides[0]; + dptr = ind->data; + PyArray_ITER_RESET(self); + /* Loop over Boolean array */ + copyswap = self->ao->descr->f->copyswap; + while(index--) { + if (*((Bool *)dptr) != 0) { + copyswap(self->dataptr, val->dataptr, swap, + itemsize); + PyArray_ITER_NEXT(val); + if (val->index==val->size) + PyArray_ITER_RESET(val); + } + dptr += strides; + PyArray_ITER_NEXT(self); + } + PyArray_ITER_RESET(self); + return 0; +} + +static int +iter_ass_sub_int(PyArrayIterObject *self, PyArrayObject *ind, + PyArrayIterObject *val, int swap) +{ + PyArray_Descr *typecode; + intp num; + PyArrayIterObject *ind_it; + int itemsize; + int index; + PyArray_CopySwapFunc *copyswap; + + typecode = self->ao->descr; + itemsize = typecode->elsize; + copyswap = self->ao->descr->f->copyswap; + if (ind->nd == 0) { + num = *((intp *)ind->data); + PyArray_ITER_GOTO1D(self, num); + copyswap(self->dataptr, val->dataptr, swap, itemsize); + return 0; + } + ind_it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ind); + if (ind_it == NULL) return -1; + index = ind_it->size; + while(index--) { + num = *((intp *)(ind_it->dataptr)); + if (num < 0) num += self->size; + if ((num < 0) || (num >= self->size)) { + PyErr_Format(PyExc_IndexError, + "index %d out of bounds" \ + " 0<=index<%d", (int) num, + (int) self->size); + Py_DECREF(ind_it); + return -1; + } + PyArray_ITER_GOTO1D(self, num); + copyswap(self->dataptr, val->dataptr, swap, itemsize); + PyArray_ITER_NEXT(ind_it); + PyArray_ITER_NEXT(val); + if (val->index == val->size) + PyArray_ITER_RESET(val); + } + Py_DECREF(ind_it); + return 0; +} + +static int +iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val) +{ + PyObject *arrval=NULL; + PyArrayIterObject *val_it=NULL; + PyArray_Descr *type; + PyArray_Descr *indtype=NULL; + int swap, retval=-1; + int itemsize; + intp start, step_size; + intp n_steps; + PyObject *obj=NULL; + PyArray_CopySwapFunc *copyswap; + + + if (ind == Py_Ellipsis) { + ind = PySlice_New(NULL, NULL, NULL); + retval = iter_ass_subscript(self, ind, val); + Py_DECREF(ind); + return retval; + } + + if (PyTuple_Check(ind)) { + int len; + len = PyTuple_GET_SIZE(ind); + if (len > 1) goto finish; + ind = PyTuple_GET_ITEM(ind, 0); + } + + type = self->ao->descr; + itemsize = type->elsize; + + Py_INCREF(type); + arrval = PyArray_FromAny(val, type, 0, 0, 0); + if (arrval==NULL) return -1; + val_it = (PyArrayIterObject *)PyArray_IterNew(arrval); + if (val_it==NULL) goto finish; + + /* Check for Boolean -- this is first becasue + Bool is a subclass of Int */ + + copyswap = PyArray_DESCR(arrval)->f->copyswap; + swap = (PyArray_ISNOTSWAPPED(self->ao)!=PyArray_ISNOTSWAPPED(arrval)); + if (PyBool_Check(ind)) { + if (PyObject_IsTrue(ind)) { + copyswap(self->dataptr, PyArray_DATA(arrval), + swap, itemsize); + } + retval=0; + goto finish; + } + + /* Check for Integer or Slice */ + + if (PyLong_Check(ind) || PyInt_Check(ind) || PySlice_Check(ind)) { + start = parse_subindex(ind, &step_size, &n_steps, + self->size); + if (start == -1) goto finish; + if (n_steps == RubberIndex || n_steps == PseudoIndex) { + PyErr_SetString(PyExc_IndexError, + "cannot use Ellipsis or NewAxes here"); + goto finish; + } + PyArray_ITER_GOTO1D(self, start); + if (n_steps == SingleIndex) { /* Integer */ + copyswap(self->dataptr, PyArray_DATA(arrval), + swap, itemsize); + PyArray_ITER_RESET(self); + retval=0; + goto finish; + } + while(n_steps--) { + copyswap(self->dataptr, val_it->dataptr, + swap, itemsize); + start += step_size; + PyArray_ITER_GOTO1D(self, start) + PyArray_ITER_NEXT(val_it); + if (val_it->index == val_it->size) + PyArray_ITER_RESET(val_it); + } + PyArray_ITER_RESET(self); + retval = 0; + goto finish; + } + + /* convert to INTP array if Integer array scalar or List */ + + indtype = PyArray_DescrFromType(PyArray_INTP); + if (PyArray_IsScalar(ind, Integer)) { + Py_INCREF(indtype); + obj = PyArray_FromScalar(ind, indtype); + } + else if (PyList_Check(ind)) { + Py_INCREF(indtype); + obj = PyArray_FromAny(ind, indtype, 0, 0, FORCECAST); + } + else { + Py_INCREF(ind); + obj = ind; + } + + if (PyArray_Check(obj)) { + /* Check for Boolean object */ + if (PyArray_TYPE(obj)==PyArray_BOOL) { + if (iter_ass_sub_Bool(self, (PyArrayObject *)obj, + val_it, swap) < 0) + goto finish; + retval=0; + } + /* Check for integer array */ + else if (PyArray_ISINTEGER(obj)) { + PyObject *new; + Py_INCREF(indtype); + new = PyArray_FromAny(obj, indtype, 0, 0, + FORCECAST | BEHAVED_FLAGS); + Py_DECREF(obj); + obj = new; + if (new==NULL) goto finish; + if (iter_ass_sub_int(self, (PyArrayObject *)obj, + val_it, swap) < 0) + goto finish; + retval=0; + } + } + + finish: + if (!PyErr_Occurred() && retval < 0) + PyErr_SetString(PyExc_IndexError, + "unsupported iterator index"); + Py_XDECREF(indtype); + Py_XDECREF(obj); + Py_XDECREF(val_it); + Py_XDECREF(arrval); + return retval; + +} + + +static PyMappingMethods iter_as_mapping = { + (inquiry)iter_length, /*mp_length*/ + (binaryfunc)iter_subscript, /*mp_subscript*/ + (objobjargproc)iter_ass_subscript, /*mp_ass_subscript*/ +}; + +static char doc_iter_array[] = "__array__(type=None)\n Get array "\ + "from iterator"; + +static PyObject * +iter_array(PyArrayIterObject *it, PyObject *op) +{ + + PyObject *r; + intp size; + + /* Any argument ignored */ + + /* Two options: + 1) underlying array is contiguous + -- return 1-d wrapper around it + 2) underlying array is not contiguous + -- make new 1-d contiguous array with updateifcopy flag set + to copy back to the old array + */ + + size = PyArray_SIZE(it->ao); + Py_INCREF(it->ao->descr); + if (PyArray_ISCONTIGUOUS(it->ao)) { + r = PyArray_NewFromDescr(it->ao->ob_type, + it->ao->descr, + 1, &size, + NULL, it->ao->data, + it->ao->flags, + (PyObject *)it->ao); + if (r==NULL) return NULL; + } + else { + r = PyArray_NewFromDescr(it->ao->ob_type, + it->ao->descr, + 1, &size, + NULL, NULL, + 0, (PyObject *)it->ao); + if (r==NULL) return NULL; + if (PyArray_CopyInto((PyArrayObject *)r, it->ao) < 0) { + Py_DECREF(r); + return NULL; + } + PyArray_FLAGS(r) |= UPDATEIFCOPY; + it->ao->flags &= ~WRITEABLE; + } + Py_INCREF(it->ao); + PyArray_BASE(r) = (PyObject *)it->ao; + return r; + +} + +static char doc_iter_copy[] = "copy()\n Get a copy of 1-d array"; + +static PyObject * +iter_copy(PyArrayIterObject *it, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + return PyArray_Flatten(it->ao, 0); +} + +static PyMethodDef iter_methods[] = { + /* to get array */ + {"__array__", (PyCFunction)iter_array, 1, doc_iter_array}, + {"copy", (PyCFunction)iter_copy, 1, doc_iter_copy}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef iter_members[] = { + {"base", T_OBJECT, offsetof(PyArrayIterObject, ao), RO, NULL}, + {NULL}, +}; + +static PyTypeObject PyArrayIter_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "scipy.flatiter", /* tp_name */ + sizeof(PyArrayIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)arrayiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + &iter_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + (iternextfunc)arrayiter_next, /* tp_iternext */ + iter_methods, /* tp_methods */ + iter_members, /* tp_members */ + 0, /* tp_getset */ + +}; + +/** END of Array Iterator **/ + + + +/*********************** Subscript Array Iterator ************************* + * * + * This object handles subscript behavior for array objects. * + * It is an iterator object with a next method * + * It abstracts the n-dimensional mapping behavior to make the looping * + * code more understandable (maybe) * + * and so that indexing can be set up ahead of time * + */ + +/* This checks the args for any fancy indexing objects */ + +#define SOBJ_NOTFANCY 0 +#define SOBJ_ISFANCY 1 +#define SOBJ_BADARRAY 2 +#define SOBJ_TOOMANY 3 +#define SOBJ_LISTTUP 4 + +static int +fancy_indexing_check(PyObject *args) +{ + int i, n; + PyObject *obj; + int retval = SOBJ_NOTFANCY; + + if (PyTuple_Check(args)) { + n = PyTuple_GET_SIZE(args); + if (n >= MAX_DIMS) return SOBJ_TOOMANY; + for (i=0; i<n; i++) { + obj = PyTuple_GET_ITEM(args,i); + if (PyArray_Check(obj)) { + if (PyArray_ISINTEGER(obj)) + retval = SOBJ_ISFANCY; + else { + retval = SOBJ_BADARRAY; + break; + } + } + else if (PySequence_Check(obj)) { + retval = SOBJ_ISFANCY; + } + } + } + else if (PyArray_Check(args)) { + if ((PyArray_TYPE(args)==PyArray_BOOL) || + (PyArray_ISINTEGER(args))) + return SOBJ_ISFANCY; + else + return SOBJ_BADARRAY; + } + else if (PySequence_Check(args)) { + /* Sequences < MAX_DIMS with any slice objects + or NewAxis, or Ellipsis is considered standard + as long as there are also no Arrays and or additional + sequences embedded. + */ + retval = SOBJ_ISFANCY; + n = PySequence_Size(args); + if (n<0 || n>=MAX_DIMS) return SOBJ_ISFANCY; + for (i=0; i<n; i++) { + obj = PySequence_GetItem(args, i); + if (obj == NULL) return SOBJ_ISFANCY; + if (PyArray_Check(obj)) { + if (PyArray_ISINTEGER(obj)) + retval = SOBJ_LISTTUP; + else + retval = SOBJ_BADARRAY; + } + else if (PySequence_Check(obj)) { + retval = SOBJ_LISTTUP; + } + else if (PySlice_Check(obj) || obj == Py_Ellipsis || \ + obj == Py_None) { + retval = SOBJ_NOTFANCY; + } + Py_DECREF(obj); + if (retval > SOBJ_ISFANCY) return retval; + } + } + + return retval; +} + +/* convert an indexing object to an INTP indexing array iterator + if possible -- otherwise, it is a Slice or Ellipsis object + and has to be interpreted on bind to a particular + array so leave it NULL for now. + */ +static int +_convert_obj(PyObject *obj, PyArrayIterObject **iter) +{ + PyArray_Descr *indtype; + PyObject *arr; + + if (PySlice_Check(obj) || (obj == Py_Ellipsis)) + *iter = NULL; + else { + indtype = PyArray_DescrFromType(PyArray_INTP); + arr = PyArray_FromAny(obj, indtype, 0, 0, FORCECAST); + if (arr == NULL) return -1; + *iter = (PyArrayIterObject *)PyArray_IterNew(arr); + Py_DECREF(arr); + if (*iter == NULL) return -1; + } + return 0; +} + +/* Adjust dimensionality and strides for index object iterators + --- i.e. broadcast + */ +/*OBJECT_API*/ +static int +PyArray_Broadcast(PyArrayMultiIterObject *mit) +{ + int i, nd, k, j; + intp tmp; + PyArrayIterObject *it; + + /* Discover the broadcast number of dimensions */ + for (i=0, nd=0; i<mit->numiter; i++) + nd = MAX(nd, mit->iters[i]->ao->nd); + mit->nd = nd; + + /* Discover the broadcast shape in each dimension */ + for (i=0; i<nd; i++) { + mit->dimensions[i] = 1; + for (j=0; j<mit->numiter; j++) { + it = mit->iters[j]; + /* This prepends 1 to shapes not already + equal to nd */ + k = i + it->ao->nd - nd; + if (k>=0) { + tmp = it->ao->dimensions[k]; + if (tmp == 1) continue; + if (mit->dimensions[i] == 1) + mit->dimensions[i] = tmp; + else if (mit->dimensions[i] != tmp) { + PyErr_SetString(PyExc_ValueError, + "index objects are " \ + "not broadcastable " \ + "to a single shape"); + return -1; + } + } + } + } + + /* Reset the iterator dimensions and strides of each iterator + object -- using 0 valued strides for broadcasting */ + + tmp = PyArray_MultiplyList(mit->dimensions, mit->nd); + mit->size = tmp; + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + it->nd_m1 = mit->nd - 1; + it->size = tmp; + nd = it->ao->nd; + it->factors[mit->nd-1] = 1; + for (j=0; j < mit->nd; j++) { + it->dims_m1[j] = mit->dimensions[j] - 1; + k = j + nd - mit->nd; + /* If this dimension was added or shape + of underlying array was 1 */ + if ((k < 0) || \ + it->ao->dimensions[k] != mit->dimensions[j]) { + it->contiguous = 0; + it->strides[j] = 0; + } + else { + it->strides[j] = it->ao->strides[k]; + } + it->backstrides[j] = it->strides[j] * \ + it->dims_m1[j]; + if (j > 0) + it->factors[mit->nd-j-1] = \ + it->factors[mit->nd-j] * \ + mit->dimensions[mit->nd-j]; + } + PyArray_ITER_RESET(it); + } + return 0; +} + +/* Reset the map iterator to the beginning */ +static void +PyArray_MapIterReset(PyArrayMapIterObject *mit) +{ + int i,j; intp coord[MAX_DIMS]; + PyArrayIterObject *it; + PyArray_CopySwapFunc *copyswap; + + mit->index = 0; + + copyswap = mit->iters[0]->ao->descr->f->copyswap; + + if (mit->subspace != NULL) { + memcpy(coord, mit->bscoord, sizeof(intp)*mit->ait->ao->nd); + PyArray_ITER_RESET(mit->subspace); + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + PyArray_ITER_RESET(it); + j = mit->iteraxes[i]; + copyswap(coord+j,it->dataptr, + !PyArray_ISNOTSWAPPED(it->ao), + sizeof(intp)); + } + PyArray_ITER_GOTO(mit->ait, coord); + mit->subspace->dataptr = mit->ait->dataptr; + mit->dataptr = mit->subspace->dataptr; + } + else { + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + PyArray_ITER_RESET(it); + copyswap(coord+i,it->dataptr, + !PyArray_ISNOTSWAPPED(it->ao), + sizeof(intp)); + } + PyArray_ITER_GOTO(mit->ait, coord); + mit->dataptr = mit->ait->dataptr; + } + return; +} + +/* This function needs to update the state of the map iterator + and point mit->dataptr to the memory-location of the next object +*/ +static void +PyArray_MapIterNext(PyArrayMapIterObject *mit) +{ + int i, j; + intp coord[MAX_DIMS]; + PyArrayIterObject *it; + PyArray_CopySwapFunc *copyswap; + + mit->index += 1; + if (mit->index >= mit->size) return; + copyswap = mit->iters[0]->ao->descr->f->copyswap; + /* Sub-space iteration */ + if (mit->subspace != NULL) { + PyArray_ITER_NEXT(mit->subspace); + if (mit->subspace->index == mit->subspace->size) { + /* reset coord to coordinates of + beginning of the subspace */ + memcpy(coord, mit->bscoord, + sizeof(intp)*mit->ait->ao->nd); + PyArray_ITER_RESET(mit->subspace); + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + PyArray_ITER_NEXT(it); + j = mit->iteraxes[i]; + copyswap(coord+j,it->dataptr, + !PyArray_ISNOTSWAPPED(it->ao), + sizeof(intp)); + } + PyArray_ITER_GOTO(mit->ait, coord); + mit->subspace->dataptr = mit->ait->dataptr; + } + mit->dataptr = mit->subspace->dataptr; + } + else { + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + PyArray_ITER_NEXT(it); + copyswap(coord+i,it->dataptr, + !PyArray_ISNOTSWAPPED(it->ao), + sizeof(intp)); + } + PyArray_ITER_GOTO(mit->ait, coord); + mit->dataptr = mit->ait->dataptr; + } + return; +} + +/* Bind a mapiteration to a particular array */ + +/* Determine if subspace iteration is necessary. If so, + 1) Fill in mit->iteraxes + 2) Create subspace iterator + 3) Update nd, dimensions, and size. + + Subspace iteration is necessary if: arr->nd > mit->numiter +*/ + +/* Need to check for index-errors somewhere. + + Let's do it at bind time and also convert all <0 values to >0 here + as well. +*/ +static void +PyArray_MapIterBind(PyArrayMapIterObject *mit, PyArrayObject *arr) +{ + int subnd; + PyObject *sub, *obj=NULL; + int i, j, n, curraxis, ellipexp, noellip; + PyArrayIterObject *it; + intp dimsize; + intp *indptr; + + subnd = arr->nd - mit->numiter; + if (subnd < 0) { + PyErr_SetString(PyExc_ValueError, + "too many indices for array"); + return; + } + + mit->ait = (PyArrayIterObject *)PyArray_IterNew((PyObject *)arr); + if (mit->ait == NULL) return; + + /* If this is just a view, then do nothing more */ + /* views are handled by just adjusting the strides + and dimensions of the object. + */ + + if (mit->view) return; + + /* no subspace iteration needed. Finish up and Return */ + if (subnd == 0) { + n = arr->nd; + for (i=0; i<n; i++) { + mit->iteraxes[i] = i; + } + goto finish; + } + + /* all indexing arrays have been converted to 0 + therefore we can extract the subspace with a simple + getitem call which will use view semantics + */ + + sub = PyObject_GetItem((PyObject *)arr, mit->indexobj); + if (sub == NULL) goto fail; + mit->subspace = (PyArrayIterObject *)PyArray_IterNew(sub); + Py_DECREF(sub); + if (mit->subspace == NULL) goto fail; + + /* Expand dimensions of result */ + n = mit->subspace->ao->nd; + for (i=0; i<n; i++) + mit->dimensions[mit->nd+i] = mit->subspace->ao->dimensions[i]; + mit->nd += n; + + /* Now, we still need to interpret the ellipsis and slice objects + to determine which axes the indexing arrays are referring to + */ + n = PyTuple_GET_SIZE(mit->indexobj); + + /* The number of dimensions an ellipsis takes up */ + ellipexp = arr->nd - n + 1; + /* Now fill in iteraxes -- remember indexing arrays have been + converted to 0's in mit->indexobj */ + curraxis = 0; + j = 0; + noellip = 1; /* Only expand the first ellipsis */ + memset(mit->bscoord, 0, sizeof(intp)*arr->nd); + for (i=0; i<n; i++) { + /* We need to fill in the starting coordinates for + the subspace */ + obj = PyTuple_GET_ITEM(mit->indexobj, i); + if (PyInt_Check(obj) || PyLong_Check(obj)) + mit->iteraxes[j++] = curraxis++; + else if (noellip && obj == Py_Ellipsis) { + curraxis += ellipexp; + noellip = 0; + } + else { + intp start=0; + intp stop, step; + /* Should be slice object or + another Ellipsis */ + if (obj == Py_Ellipsis) { + mit->bscoord[curraxis] = 0; + } + else if (!PySlice_Check(obj) || \ + (slice_GetIndices((PySliceObject *)obj, + arr->dimensions[curraxis], + &start, &stop, &step, + &dimsize) < 0)) { + PyErr_Format(PyExc_ValueError, + "unexpected object " \ + "(%s) in selection position %d", + obj->ob_type->tp_name, i); + goto fail; + } + else { + mit->bscoord[curraxis] = start; + } + curraxis += 1; + } + } + finish: + /* Here check the indexes (now that we have iteraxes) */ + mit->size = PyArray_MultiplyList(mit->dimensions, mit->nd); + for (i=0; i<mit->numiter; i++) { + it = mit->iters[i]; + PyArray_ITER_RESET(it); + dimsize = arr->dimensions[mit->iteraxes[i]]; + while(it->index < it->size) { + indptr = ((intp *)it->dataptr); + if (*indptr < 0) *indptr += dimsize; + if (*indptr < 0 || *indptr >= dimsize) { + PyErr_Format(PyExc_IndexError, + "index (%d) out of range "\ + "(0<=index<=%d) in dimension %d", + (int) *indptr, (int) (dimsize-1), + mit->iteraxes[i]); + goto fail; + } + PyArray_ITER_NEXT(it); + } + PyArray_ITER_RESET(it); + } + return; + + fail: + Py_XDECREF(mit->subspace); + Py_XDECREF(mit->ait); + mit->subspace = NULL; + mit->ait = NULL; + return; +} + +/* This function takes a Boolean array and constructs index objects and + iterators as if nonzero(Bool) had been called +*/ +static int +_nonzero_indices(PyObject *myBool, PyArrayIterObject **iters) +{ + PyArray_Descr *typecode; + PyArrayObject *ba =NULL, *new=NULL; + int nd, j; + intp size, i, count; + Bool *ptr; + intp coords[MAX_DIMS], dims_m1[MAX_DIMS]; + intp *dptr[MAX_DIMS]; + + typecode=PyArray_DescrFromType(PyArray_BOOL); + ba = (PyArrayObject *)PyArray_FromAny(myBool, typecode, 0, 0, + CARRAY_FLAGS); + if (ba == NULL) return -1; + nd = ba->nd; + for (j=0; j<nd; j++) iters[j] = NULL; + size = PyArray_SIZE(ba); + ptr = (Bool *)ba->data; + count = 0; + + /* pre-determine how many nonzero entries there are */ + for (i=0; i<size; i++) + if (*(ptr++)) count++; + + /* create count-sized index arrays for each dimension */ + for (j=0; j<nd; j++) { + new = (PyArrayObject *)PyArray_New(&PyArray_Type, 1, &count, + PyArray_INTP, NULL, NULL, + 0, 0, NULL); + if (new == NULL) goto fail; + iters[j] = (PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)new); + Py_DECREF(new); + if (iters[j] == NULL) goto fail; + dptr[j] = (intp *)iters[j]->ao->data; + coords[j] = 0; + dims_m1[j] = ba->dimensions[j]-1; + } + + ptr = (Bool *)ba->data; + + if (count == 0) goto finish; + + /* Loop through the Boolean array and copy coordinates + for non-zero entries */ + for (i=0; i<size; i++) { + if (*(ptr++)) { + for (j=0; j<nd; j++) + *(dptr[j]++) = coords[j]; + } + /* Borrowed from ITER_NEXT macro */ + for (j=nd-1; j>=0; j--) { + if (coords[j] < dims_m1[j]) { + coords[j]++; + break; + } + else { + coords[j] = 0; + } + } + } + + finish: + Py_DECREF(ba); + return nd; + + fail: + for (j=0; j<nd; j++) { + Py_XDECREF(iters[j]); + } + Py_XDECREF(ba); + return -1; + +} + +static PyObject * +PyArray_MapIterNew(PyObject *indexobj, int oned) +{ + PyArrayMapIterObject *mit; + int fancy=0; + PyArray_Descr *indtype; + PyObject *arr = NULL; + int i, n, started, nonindex; + + + mit = (PyArrayMapIterObject *)_pya_malloc(sizeof(PyArrayMapIterObject)); + PyObject_Init((PyObject *)mit, &PyArrayMapIter_Type); + if (mit == NULL) + return NULL; + for (i=0; i<MAX_DIMS; i++) + mit->iters[i] = NULL; + mit->view = 0; + mit->index = 0; + mit->ait = NULL; + mit->subspace = NULL; + mit->numiter = 0; + mit->consec = 1; + fancy = fancy_indexing_check(indexobj); + Py_INCREF(indexobj); + mit->indexobj = indexobj; + if (fancy == SOBJ_NOTFANCY) { /* bail out */ + mit->view = 1; + goto ret; + } + + if (fancy == SOBJ_BADARRAY) { + PyErr_SetString(PyExc_IndexError, \ + "arrays used as indices must be of " \ + "integer type"); + goto fail; + } + if (fancy == SOBJ_TOOMANY) { + PyErr_SetString(PyExc_IndexError, "too many indices"); + goto fail; + } + + if (fancy == SOBJ_LISTTUP) { + PyObject *newobj; + newobj = PySequence_Tuple(indexobj); + if (newobj == NULL) goto fail; + Py_DECREF(indexobj); + indexobj = newobj; + mit->indexobj = indexobj; + } + +#undef SOBJ_NOTFANCY +#undef SOBJ_ISFANCY +#undef SOBJ_BADARRAY +#undef SOBJ_TOOMANY +#undef SOBJ_LISTTUP + + if (oned) return (PyObject *)mit; + + /* Must have some kind of fancy indexing if we are here */ + /* indexobj is either a list, an arrayobject, or a tuple + (with at least 1 list or arrayobject or Bool object), */ + + /* convert all inputs to iterators */ + if (PyArray_Check(indexobj) && \ + (PyArray_TYPE(indexobj) == PyArray_BOOL)) { + mit->numiter = _nonzero_indices(indexobj, mit->iters); + if (mit->numiter < 0) goto fail; + mit->nd = 1; + mit->dimensions[0] = mit->iters[0]->dims_m1[0]+1; + Py_DECREF(mit->indexobj); + mit->indexobj = PyTuple_New(mit->numiter); + if (mit->indexobj == NULL) goto fail; + for (i=0; i<mit->numiter; i++) { + PyTuple_SET_ITEM(mit->indexobj, i, + PyInt_FromLong(0)); + } + } + + else if (PyArray_Check(indexobj) || !PyTuple_Check(indexobj)) { + mit->numiter = 1; + indtype = PyArray_DescrFromType(PyArray_INTP); + arr = PyArray_FromAny(indexobj, indtype, 0, 0, FORCECAST); + if (arr == NULL) goto fail; + mit->iters[0] = (PyArrayIterObject *)PyArray_IterNew(arr); + if (mit->iters[0] == NULL) {Py_DECREF(arr); goto fail;} + mit->nd = PyArray_NDIM(arr); + memcpy(mit->dimensions,PyArray_DIMS(arr),mit->nd*sizeof(intp)); + mit->size = PyArray_SIZE(arr); + Py_DECREF(arr); + Py_DECREF(mit->indexobj); + mit->indexobj = Py_BuildValue("(N)", PyInt_FromLong(0)); + } + else { /* must be a tuple */ + PyObject *obj; + PyArrayIterObject *iter; + PyObject *new; + /* Make a copy of the tuple -- we will be replacing + index objects with 0's */ + n = PyTuple_GET_SIZE(indexobj); + new = PyTuple_New(n); + if (new == NULL) goto fail; + started = 0; + nonindex = 0; + for (i=0; i<n; i++) { + obj = PyTuple_GET_ITEM(indexobj,i); + if (_convert_obj(obj, &iter) < 0) { + Py_DECREF(new); + goto fail; + } + if (iter!= NULL) { + started = 1; + if (nonindex) mit->consec = 0; + mit->iters[(mit->numiter)++] = iter; + PyTuple_SET_ITEM(new,i, + PyInt_FromLong(0)); + } + else { + if (started) nonindex = 1; + Py_INCREF(obj); + PyTuple_SET_ITEM(new,i,obj); + } + } + Py_DECREF(mit->indexobj); + mit->indexobj = new; + /* Store the number of iterators actually converted */ + /* These will be mapped to actual axes at bind time */ + if (PyArray_Broadcast((PyArrayMultiIterObject *)mit) < 0) + goto fail; + } + + ret: + return (PyObject *)mit; + + fail: + Py_DECREF(mit); + return NULL; +} + + +static void +arraymapiter_dealloc(PyArrayMapIterObject *mit) +{ + int i; + Py_XDECREF(mit->indexobj); + Py_XDECREF(mit->ait); + Py_XDECREF(mit->subspace); + for (i=0; i<mit->numiter; i++) + Py_XDECREF(mit->iters[i]); + _pya_free(mit); +} + +/* The mapiter object must be created new each time. It does not work + to bind to a new array, and continue. + + This was the orginal intention, but currently that does not work. + Do not expose the MapIter_Type to Python. + + It's not very useful anyway, since mapiter(indexobj); mapiter.bind(a); + mapiter is equivalent to a[indexobj].flat but the latter gets to use + slice syntax. +*/ + +static PyTypeObject PyArrayMapIter_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "scipy.mapiter", /* tp_name */ + sizeof(PyArrayIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)arraymapiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + (iternextfunc)0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0 /* tp_weaklist */ + +}; + +/** END of Subscript Iterator **/ + + +/*OBJECT_API + Get MultiIterator, +*/ +static PyObject * +PyArray_MultiIterNew(int n, ...) +{ + va_list va; + PyArrayMultiIterObject *multi; + PyObject *current; + PyObject *arr; + + int i, err=0; + + if (n < 2 || n > MAX_DIMS) { + PyErr_Format(PyExc_ValueError, + "Need between 2 and (%d) " \ + "array objects (inclusive).", MAX_DIMS); + } + + /* fprintf(stderr, "multi new...");*/ + multi = PyObject_New(PyArrayMultiIterObject, &PyArrayMultiIter_Type); + if (multi == NULL) + return NULL; + + for (i=0; i<n; i++) multi->iters[i] = NULL; + multi->numiter = n; + multi->index = 0; + + va_start(va, n); + for (i=0; i<n; i++) { + current = va_arg(va, PyObject *); + arr = PyArray_FROM_O(current); + if (arr==NULL) { + err=1; break; + } + else { + multi->iters[i] = (PyArrayIterObject *)PyArray_IterNew(arr); + Py_DECREF(arr); + } + } + + va_end(va); + + if (!err && PyArray_Broadcast(multi) < 0) err=1; + + if (err) { + Py_DECREF(multi); + return NULL; + } + + PyArray_MultiIter_RESET(multi); + + return (PyObject *)multi; +} + +static PyObject * +arraymultiter_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) +{ + + int n, i; + PyArrayMultiIterObject *multi; + PyObject *arr; + + if (kwds != NULL) { + PyErr_SetString(PyExc_ValueError, + "keyword arguments not accepted."); + return NULL; + } + + n = PyTuple_Size(args); + if (n < 2 || n > MAX_DIMS) { + if (PyErr_Occurred()) return NULL; + PyErr_Format(PyExc_ValueError, + "Need at least two and fewer than (%d) " \ + "array objects.", MAX_DIMS); + return NULL; + } + + multi = _pya_malloc(sizeof(PyArrayMultiIterObject)); + if (multi == NULL) return PyErr_NoMemory(); + PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type); + + multi->numiter = n; + multi->index = 0; + for (i=0; i<n; i++) multi->iters[i] = NULL; + for (i=0; i<n; i++) { + arr = PyArray_FromAny(PyTuple_GET_ITEM(args, i), NULL, 0, 0, 0); + if (arr == NULL) goto fail; + if ((multi->iters[i] = \ + (PyArrayIterObject *)PyArray_IterNew(arr))==NULL) + goto fail; + Py_DECREF(arr); + } + if (PyArray_Broadcast(multi) < 0) goto fail; + PyArray_MultiIter_RESET(multi); + + return (PyObject *)multi; + + fail: + Py_DECREF(multi); + return NULL; +} + +static PyObject * +arraymultiter_next(PyArrayMultiIterObject *multi) +{ + PyObject *ret; + int i, n; + + n = multi->numiter; + ret = PyTuple_New(n); + if (ret == NULL) return NULL; + if (multi->index < multi->size) { + for (i=0; i < n; i++) { + PyArrayIterObject *it=multi->iters[i]; + PyTuple_SET_ITEM(ret, i, + PyArray_ToScalar(it->dataptr, it->ao)); + PyArray_ITER_NEXT(it); + } + multi->index++; + return ret; + } + return NULL; +} + +static void +arraymultiter_dealloc(PyArrayMultiIterObject *multi) +{ + int i; + + for (i=0; i<multi->numiter; i++) + Py_XDECREF(multi->iters[i]); + _pya_free(multi); +} + +static PyObject * +arraymultiter_size_get(PyArrayMultiIterObject *self) +{ +#if SIZEOF_INTP <= SIZEOF_LONG + return PyInt_FromLong((long) self->size); +#else + if (self->size < MAX_LONG) + return PyInt_FromLong((long) self->size); + else + return PyLong_FromLongLong((longlong) self->size); +#endif +} + +static PyObject * +arraymultiter_index_get(PyArrayMultiIterObject *self) +{ +#if SIZEOF_INTP <= SIZEOF_LONG + return PyInt_FromLong((long) self->index); +#else + if (self->size < MAX_LONG) + return PyInt_FromLong((long) self->index); + else + return PyLong_FromLongLong((longlong) self->index); +#endif +} + +static PyObject * +arraymultiter_shape_get(PyArrayMultiIterObject *self) +{ + return PyArray_IntTupleFromIntp(self->nd, self->dimensions); +} + +static PyObject * +arraymultiter_iters_get(PyArrayMultiIterObject *self) +{ + PyObject *res; + int i, n; + n = self->numiter; + res = PyTuple_New(n); + if (res == NULL) return res; + for (i=0; i<n; i++) { + Py_INCREF(self->iters[i]); + PyTuple_SET_ITEM(res, i, (PyObject *)self->iters[i]); + } + return res; +} + +static PyGetSetDef arraymultiter_getsetlist[] = { + {"size", + (getter)arraymultiter_size_get, + NULL, + "total size of broadcasted result"}, + {"index", + (getter)arraymultiter_index_get, + NULL, + "current index in broadcasted result"}, + {"shape", + (getter)arraymultiter_shape_get, + NULL, + "shape of broadcasted result"}, + {"iters", + (getter)arraymultiter_iters_get, + NULL, + "tuple of individual iterators"}, + {NULL, NULL, NULL, NULL}, +}; + +static PyMemberDef arraymultiter_members[] = { + {"numiter", T_INT, offsetof(PyArrayMultiIterObject, numiter), + RO, NULL}, + {"nd", T_INT, offsetof(PyArrayMultiIterObject, nd), RO, NULL}, + {NULL}, +}; + +static PyObject * +arraymultiter_reset(PyArrayMultiIterObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + + PyArray_MultiIter_RESET(self); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef arraymultiter_methods[] = { + {"reset", (PyCFunction) arraymultiter_reset, METH_VARARGS, NULL}, + {NULL, NULL}, +}; + +static PyTypeObject PyArrayMultiIter_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "scipy.broadcast", /* tp_name */ + sizeof(PyArrayMultiIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)arraymultiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + (iternextfunc)arraymultiter_next, /* tp_iternext */ + arraymultiter_methods, /* tp_methods */ + arraymultiter_members, /* tp_members */ + arraymultiter_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)0, /* tp_init */ + 0, /* tp_alloc */ + arraymultiter_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0 /* tp_weaklist */ +}; + +/*OBJECT_API*/ +static PyArray_Descr * +PyArray_DescrNewFromType(int type_num) +{ + PyArray_Descr *old; + PyArray_Descr *new; + + old = PyArray_DescrFromType(type_num); + new = PyArray_DescrNew(old); + Py_DECREF(old); + return new; +} + +/*** Array Descr Objects for dynamic types **/ + +/** There are some statically-defined PyArray_Descr objects corresponding + to the basic built-in types. + These can and should be DECREF'd and INCREF'd as appropriate, anyway. + If a mistake is made in reference counting, deallocation on these + builtins will be attempted leading to problems. + + This let's us deal with all PyArray_Descr objects using reference + counting (regardless of whether they are statically or dynamically + allocated). +**/ + +/* base cannot be NULL */ +/*OBJECT_API*/ +static PyArray_Descr * +PyArray_DescrNew(PyArray_Descr *base) +{ + PyArray_Descr *new; + + new = PyObject_New(PyArray_Descr, &PyArrayDescr_Type); + if (new == NULL) return NULL; + /* Don't copy PyObject_HEAD part */ + memcpy((char *)new+sizeof(PyObject), + (char *)base+sizeof(PyObject), + sizeof(PyArray_Descr)-sizeof(PyObject)); + + if (new->fields == Py_None) new->fields = NULL; + Py_XINCREF(new->fields); + if (new->subarray) { + new->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr)); + memcpy(new->subarray, base->subarray, + sizeof(PyArray_ArrayDescr)); + Py_INCREF(new->subarray->shape); + Py_INCREF(new->subarray->base); + } + Py_INCREF(new->typeobj); + return new; +} + +/* should never be called for builtin-types unless + there is a reference-count problem +*/ +static void +arraydescr_dealloc(PyArray_Descr *self) +{ + Py_XDECREF(self->typeobj); + Py_XDECREF(self->fields); + if (self->subarray) { + Py_DECREF(self->subarray->shape); + Py_DECREF(self->subarray->base); + _pya_free(self->subarray); + } + self->ob_type->tp_free(self); +} + +/* we need to be careful about setting attributes because these + objects are pointed to by arrays that depend on them for interpreting + data. Currently no attributes of dtypedescr objects can be set. +*/ +static PyMemberDef arraydescr_members[] = { + {"dtype", T_OBJECT, offsetof(PyArray_Descr, typeobj), RO, NULL}, + {"kind", T_CHAR, offsetof(PyArray_Descr, kind), RO, NULL}, + {"char", T_CHAR, offsetof(PyArray_Descr, type), RO, NULL}, + {"num", T_INT, offsetof(PyArray_Descr, type_num), RO, NULL}, + {"byteorder", T_CHAR, offsetof(PyArray_Descr, byteorder), RO, NULL}, + {"itemsize", T_INT, offsetof(PyArray_Descr, elsize), RO, NULL}, + {"alignment", T_INT, offsetof(PyArray_Descr, alignment), RO, NULL}, + {NULL}, +}; + +static PyObject * +arraydescr_subdescr_get(PyArray_Descr *self) +{ + if (self->subarray == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return Py_BuildValue("OO", (PyObject *)self->subarray->base, + self->subarray->shape); +} + +static PyObject * +arraydescr_protocol_typestr_get(PyArray_Descr *self) +{ + char basic_=self->kind; + char endian = self->byteorder; + + if (endian == '=') { + endian = '<'; + if (!PyArray_IsNativeByteOrder(endian)) endian = '>'; + } + + return PyString_FromFormat("%c%c%d", endian, basic_, + self->elsize); +} + +static PyObject * +arraydescr_protocol_descr_get(PyArray_Descr *self) +{ + PyObject *dobj, *res; + + if (self->fields == NULL || self->fields == Py_None) { + /* get default */ + dobj = PyTuple_New(2); + if (dobj == NULL) return NULL; + PyTuple_SET_ITEM(dobj, 0, PyString_FromString("")); + PyTuple_SET_ITEM(dobj, 1, \ + arraydescr_protocol_typestr_get(self)); + res = PyList_New(1); + if (res == NULL) {Py_DECREF(dobj); return NULL;} + PyList_SET_ITEM(res, 0, dobj); + return res; + } + + return PyObject_CallMethod(_scipy_internal, "_array_descr", + "O", self); +} + +/* returns 1 for a builtin type + and 2 for a user-defined data-type descriptor + return 0 if neither (i.e. it's a copy of one) +*/ +static PyObject * +arraydescr_isbuiltin_get(PyArray_Descr *self) +{ + long val; + val = 0; + if (self->fields == Py_None) val = 1; + if (PyTypeNum_ISUSERDEF(self->type_num)) val = 2; + return PyInt_FromLong(val); +} + +static PyObject * +arraydescr_isnative_get(PyArray_Descr *self) +{ + PyObject *ret; + + ret = (PyArray_ISNBO(self->byteorder) ? Py_True : Py_False); + Py_INCREF(ret); + return ret; +} + +static PyObject * +arraydescr_fields_get(PyArray_Descr *self) +{ + if (self->fields == NULL || self->fields == Py_None) { + Py_INCREF(Py_None); + return Py_None; + } + return PyDictProxy_New(self->fields); +} + +static PyGetSetDef arraydescr_getsets[] = { + {"subdescr", + (getter)arraydescr_subdescr_get, + NULL, + "A tuple of (descr, shape) or None."}, + {"arrdescr", + (getter)arraydescr_protocol_descr_get, + NULL, + "The array_protocol type descriptor."}, + {"dtypestr", + (getter)arraydescr_protocol_typestr_get, + NULL, + "The array_protocol typestring."}, + {"isbuiltin", + (getter)arraydescr_isbuiltin_get, + NULL, + "Is this a buillt-in data-type descriptor?"}, + {"isnative", + (getter)arraydescr_isnative_get, + NULL, + "Is the byte-order of this descriptor native?"}, + {"fields", + (getter)arraydescr_fields_get, + NULL, + NULL}, + {NULL, NULL, NULL, NULL}, +}; + +static PyArray_Descr *_convert_from_list(PyObject *obj, int align, int try_descr); +static PyArray_Descr *_convert_from_dict(PyObject *obj, int align); +static PyArray_Descr *_convert_from_commastring(PyObject *obj, int align); +static PyArray_Descr *_convert_from_array_descr(PyObject *obj); + +static PyObject * +arraydescr_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) +{ + PyObject *odescr; + PyArray_Descr *descr, *conv; + int align=0; + Bool copy=FALSE; + + if (!PyArg_ParseTuple(args, "O|iO&", &odescr, &align, + PyArray_BoolConverter, ©)) + return NULL; + + if (align) { + conv = NULL; + if PyDict_Check(odescr) + conv = _convert_from_dict(odescr, 1); + else if PyList_Check(odescr) + conv = _convert_from_list(odescr, 1, 0); + else if PyString_Check(odescr) + conv = _convert_from_commastring(odescr, + 1); + else { + PyErr_SetString(PyExc_ValueError, + "align can only be non-zero for" \ + "dictionary, list, and string objects."); + } + if (conv) return (PyObject *)conv; + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "data-type-descriptor not understood"); + } + return NULL; + } + + if PyList_Check(odescr) { + conv = _convert_from_array_descr(odescr); + if (!conv) { + PyErr_Clear(); + conv = _convert_from_list(odescr, 0, 0); + } + return (PyObject *)conv; + } + + if (!PyArray_DescrConverter(odescr, &conv)) + return NULL; + /* Get a new copy of it unless it's already a copy */ + if (copy && conv->fields == Py_None) { + descr = PyArray_DescrNew(conv); + Py_DECREF(conv); + conv = descr; + } + return (PyObject *)conv; +} + +static char doc_arraydescr_reduce[] = "self.__reduce__() for pickling."; + +/* return a tuple of (callable object, args, state) */ +static PyObject * +arraydescr_reduce(PyArray_Descr *self, PyObject *args) +{ + PyObject *ret, *mod, *obj; + PyObject *state; + char endian; + int elsize, alignment; + + ret = PyTuple_New(3); + if (ret == NULL) return NULL; + mod = PyImport_ImportModule("scipy.base.multiarray"); + if (mod == NULL) {Py_DECREF(ret); return NULL;} + obj = PyObject_GetAttrString(mod, "dtypedescr"); + Py_DECREF(mod); + if (obj == NULL) {Py_DECREF(ret); return NULL;} + PyTuple_SET_ITEM(ret, 0, obj); + if (PyTypeNum_ISUSERDEF(self->type_num) || \ + ((self->type_num == PyArray_VOID && \ + self->typeobj != &PyVoidArrType_Type))) { + obj = (PyObject *)self->typeobj; + Py_INCREF(obj); + } + else { + obj = PyString_FromFormat("%c%d",self->kind, self->elsize); + } + PyTuple_SET_ITEM(ret, 1, Py_BuildValue("(Nii)", obj, 0, 1)); + + /* Now return the state which is at least + byteorder, subarray, and fields */ + endian = self->byteorder; + if (endian == '=') { + endian = '<'; + if (!PyArray_IsNativeByteOrder(endian)) endian = '>'; + } + state = PyTuple_New(5); + PyTuple_SET_ITEM(state, 0, PyString_FromFormat("%c", endian)); + PyTuple_SET_ITEM(state, 1, arraydescr_subdescr_get(self)); + if (self->fields && self->fields != Py_None) { + Py_INCREF(self->fields); + PyTuple_SET_ITEM(state, 2, self->fields); + } + else { + PyTuple_SET_ITEM(state, 2, Py_None); + Py_INCREF(Py_None); + } + + /* for extended types it also includes elsize and alignment */ + if (PyTypeNum_ISEXTENDED(self->type_num)) { + elsize = self->elsize; + alignment = self->alignment; + } + else {elsize = -1; alignment = -1;} + + PyTuple_SET_ITEM(state, 3, PyInt_FromLong(elsize)); + PyTuple_SET_ITEM(state, 4, PyInt_FromLong(alignment)); + + PyTuple_SET_ITEM(ret, 2, state); + return ret; +} + +/* state is at least byteorder, subarray, and fields but could include elsize + and alignment for EXTENDED arrays +*/ +static char doc_arraydescr_setstate[] = "self.__setstate__() for pickling."; + +static PyObject * +arraydescr_setstate(PyArray_Descr *self, PyObject *args) +{ + int elsize = -1, alignment = -1; + char endian; + PyObject *subarray, *fields; + + if (self->fields == Py_None) {Py_INCREF(Py_None); return Py_None;} + + if (!PyArg_ParseTuple(args, "(cOOii)", &endian, &subarray, &fields, + &elsize, &alignment)) return NULL; + + if (PyArray_IsNativeByteOrder(endian)) endian = '='; + + self->byteorder = endian; + if (self->subarray) { + Py_XDECREF(self->subarray->base); + Py_XDECREF(self->subarray->shape); + _pya_free(self->subarray); + } + self->subarray = NULL; + + if (subarray != Py_None) { + self->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr)); + self->subarray->base = (PyArray_Descr *)PyTuple_GET_ITEM(subarray, 0); + Py_INCREF(self->subarray->base); + self->subarray->shape = PyTuple_GET_ITEM(subarray, 1); + Py_INCREF(self->subarray->shape); + } + + if (fields != Py_None) { + Py_XDECREF(self->fields); + self->fields = fields; + Py_INCREF(fields); + } + + if (PyTypeNum_ISEXTENDED(self->type_num)) { + self->elsize = elsize; + self->alignment = alignment; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +/* returns a copy of the PyArray_Descr structure with the byteorder + altered: + no arguments: The byteorder is swapped (in all subfields as well) + single argument: The byteorder is forced to the given state + (in all subfields as well) + + Valid states: ('big', '>') or ('little' or '<') + ('native', or '=') + + If a descr structure with | is encountered it's own + byte-order is not changed but any fields are: +*/ + +/*OBJECT_API + Deep bytorder change of a data-type descriptor +*/ +static PyArray_Descr * +PyArray_DescrNewByteorder(PyArray_Descr *self, char newendian) +{ + PyArray_Descr *new; + char endian; + + new = PyArray_DescrNew(self); + endian = new->byteorder; + if (endian != PyArray_IGNORE) { + if (newendian == PyArray_SWAP) { /* swap byteorder */ + if PyArray_ISNBO(endian) endian = PyArray_OPPBYTE; + else endian = PyArray_NATBYTE; + new->byteorder = endian; + } + else if (newendian != PyArray_IGNORE) { + new->byteorder = newendian; + } + } + if (new->fields) { + PyObject *newfields; + PyObject *key, *value; + PyObject *newvalue; + PyObject *old; + PyArray_Descr *newdescr; + int pos = 0, len, i; + newfields = PyDict_New(); + /* make new dictionary with replaced */ + /* PyArray_Descr Objects */ + while(PyDict_Next(self->fields, &pos, &key, &value)) { + if (PyInt_Check(key) && \ + PyInt_AsLong(key) == -1) { + PyDict_SetItem(newfields, key, value); + continue; + } + if (!PyString_Check(key) || \ + !PyTuple_Check(value) || \ + ((len=PyTuple_GET_SIZE(value)) < 2)) + continue; + + old = PyTuple_GET_ITEM(value, 0); + if (!PyArray_DescrCheck(old)) continue; + newdescr = PyArray_DescrNewByteorder \ + ((PyArray_Descr *)old, newendian); + if (newdescr == NULL) { + Py_DECREF(newfields); Py_DECREF(new); + return NULL; + } + newvalue = PyTuple_New(len); + PyTuple_SET_ITEM(newvalue, 0, \ + (PyObject *)newdescr); + for(i=1; i<len; i++) { + old = PyTuple_GET_ITEM(value, i); + Py_INCREF(old); + PyTuple_SET_ITEM(newvalue, i, old); + } + PyDict_SetItem(newfields, key, newvalue); + Py_DECREF(newvalue); + } + Py_DECREF(new->fields); + new->fields = newfields; + } + if (new->subarray) { + Py_DECREF(new->subarray->base); + new->subarray->base = PyArray_DescrNewByteorder \ + (self->subarray->base, newendian); + } + return new; +} + + +static char doc_arraydescr_newbyteorder[] = "self.newbyteorder(<endian>)" + " returns a copy of the dtypedescr object\n" + " with altered byteorders. If <endian> is not given all byteorders\n" + " are swapped. Otherwise endian can be '>', '<', or '=' to force\n" + " a byteorder. Descriptors in all fields are also updated in the\n" + " new dtypedescr object."; + +static PyObject * +arraydescr_newbyteorder(PyArray_Descr *self, PyObject *args) +{ + char endian=PyArray_SWAP; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_ByteorderConverter, + &endian)) return NULL; + + return (PyObject *)PyArray_DescrNewByteorder(self, endian); +} + +static PyMethodDef arraydescr_methods[] = { + /* for pickling */ + {"__reduce__", (PyCFunction)arraydescr_reduce, METH_VARARGS, + doc_arraydescr_reduce}, + {"__setstate__", (PyCFunction)arraydescr_setstate, METH_VARARGS, + doc_arraydescr_setstate}, + + {"newbyteorder", (PyCFunction)arraydescr_newbyteorder, METH_VARARGS, + doc_arraydescr_newbyteorder}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +arraydescr_str(PyArray_Descr *self) +{ + PyObject *sub; + + if (self->fields && self->fields != Py_None) { + PyObject *lst; + lst = arraydescr_protocol_descr_get(self); + if (!lst) sub = PyString_FromString("<err>"); + else sub = PyObject_Str(lst); + Py_XDECREF(lst); + if (self->type_num != PyArray_VOID) { + PyObject *p; + PyObject *t=PyString_FromString("'"); + p = arraydescr_protocol_typestr_get(self); + PyString_Concat(&p, t); + PyString_ConcatAndDel(&t, p); + p = PyString_FromString("("); + PyString_ConcatAndDel(&p, t); + PyString_ConcatAndDel(&p, PyString_FromString(", ")); + PyString_ConcatAndDel(&p, sub); + PyString_ConcatAndDel(&p, PyString_FromString(")")); + sub = p; + } + } + else if (self->subarray) { + PyObject *p; + PyObject *t = PyString_FromString("("); + p = arraydescr_str(self->subarray->base); + PyString_ConcatAndDel(&t, p); + PyString_ConcatAndDel(&t, PyString_FromString(",")); + PyString_ConcatAndDel(&t, PyObject_Str(self->subarray->shape)); + PyString_ConcatAndDel(&t, PyString_FromString(")")); + sub = t; + } + else { + PyObject *t=PyString_FromString("'"); + sub = arraydescr_protocol_typestr_get(self); + PyString_Concat(&sub, t); + PyString_ConcatAndDel(&t, sub); + sub = t; + } + return sub; +} + +static PyObject * +arraydescr_repr(PyArray_Descr *self) +{ + PyObject *sub, *s; + s = PyString_FromString("dtypedescr("); + sub = arraydescr_str(self); + PyString_ConcatAndDel(&s, sub); + sub = PyString_FromString(")"); + PyString_ConcatAndDel(&s, sub); + return s; +} + +static int +arraydescr_compare(PyArray_Descr *self, PyObject *other) +{ + if (!PyArray_DescrCheck(other)) { + PyErr_SetString(PyExc_TypeError, + "not a dtypedescr object."); + return -1; + } + if (PyArray_EquivTypes(self, (PyArray_Descr *)other)) return 0; + if (PyArray_CanCastTo(self, (PyArray_Descr *)other)) return -1; + return 1; +} + +static PyTypeObject PyArrayDescr_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "scipy.dtypedescr", /* tp_name */ + sizeof(PyArray_Descr), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)arraydescr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)arraydescr_compare, /* tp_compare */ + (reprfunc)arraydescr_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)arraydescr_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + arraydescr_methods, /* tp_methods */ + arraydescr_members, /* tp_members */ + arraydescr_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + arraydescr_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0 /* tp_weaklist */ +}; diff --git a/numpy/core/src/arraytypes.inc.src b/numpy/core/src/arraytypes.inc.src new file mode 100644 index 000000000..322eafef8 --- /dev/null +++ b/numpy/core/src/arraytypes.inc.src @@ -0,0 +1,1913 @@ +/* -*- c -*- */ + +static ulong +MyPyLong_AsUnsignedLong(PyObject *vv) +{ + if ((vv != NULL) && PyInt_Check(vv)) { + long val = PyInt_AsLong(vv); + if (val < 0) { + PyErr_SetString(PyExc_OverflowError, + "can't convert negative value to unsigned long"); + return (ulong) -1; + } + return val; + } + return PyLong_AsUnsignedLong(vv); +} + +static ulonglong +MyPyLong_AsUnsignedLongLong(PyObject *vv) +{ + if ((vv != NULL) && PyInt_Check(vv)) { + longlong val = PyInt_AsLong(vv); + if (val < 0) { + PyErr_SetString(PyExc_OverflowError, + "can't convert negative value to unsigned long"); + return (ulonglong) -1; + } + return val; + } + return PyLong_AsUnsignedLongLong(vv); +} + +/****************** getitem and setitem **********************/ + +/**begin repeat + +#TYP=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,LONG,UINT,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE# +#func1=PyBool_FromLong, PyInt_FromLong*6, PyLong_FromUnsignedLong*2, PyLong_FromLongLong, PyLong_FromUnsignedLongLong, PyFloat_FromDouble*2# +#func2=PyObject_IsTrue, PyInt_AsLong*6, MyPyLong_AsUnsignedLong*2, PyLong_AsLongLong, MyPyLong_AsUnsignedLongLong, PyFloat_AsDouble*2# +#typ=Bool, byte, ubyte, short, ushort, int, long, uint, ulong, longlong, ulonglong, float, double# +#typ1=long*7, ulong*2, longlong, ulonglong, float, double# +#kind=Bool, Byte, UByte, Short, UShort, Int, Long, UInt, ULong, LongLong, ULongLong, Float, Double# +*/ + +static PyObject * +@TYP@_getitem(char *ip, PyArrayObject *ap) { + @typ@ t1; + + if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) { + t1 = *((@typ@ *)ip); + return @func1@((@typ1@)t1); + } + else { + ap->descr->f->copyswap(&t1, ip, !PyArray_ISNOTSWAPPED(ap), + ap->descr->elsize); + return @func1@((@typ1@)t1); + } +} + +static int +@TYP@_setitem(PyObject *op, char *ov, PyArrayObject *ap) { + @typ@ temp; /* ensures alignment */ + + if (PyArray_IsScalar(op, @kind@)) { + temp = ((Py@kind@ScalarObject *)op)->obval; + } + else { + temp = (@typ@)@func2@(op); + } + if (PyErr_Occurred()) return -1; + if (ap == NULL || PyArray_ISBEHAVED(ap)) + *((@typ@ *)ov)=temp; + else { + ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap), + ap->descr->elsize); + } + + return 0; +} + +/**end repeat**/ + + +/**begin repeat + +#TYP=CFLOAT,CDOUBLE# +#typ=float, double# +*/ + +static PyObject * +@TYP@_getitem(char *ip, PyArrayObject *ap) { + @typ@ t1, t2; + + if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) { + return PyComplex_FromDoubles((double)((@typ@ *)ip)[0], + (double)((@typ@ *)ip)[1]); + } + else { + int size = sizeof(@typ@); + Bool swap = !PyArray_ISNOTSWAPPED(ap); + copy_and_swap(&t1, ip, size, 1, 0, swap); + copy_and_swap(&t2, ip+size, size, 1, 0, swap); + return PyComplex_FromDoubles((double)t1, (double)t2); + } +} +/**end repeat**/ + +/**begin repeat + +#TYP=CFLOAT, CDOUBLE, CLONGDOUBLE# +#typ=float, double, longdouble# +#kind=CFloat, CDouble, CLongDouble# +*/ +static int +@TYP@_setitem(PyObject *op, char *ov, PyArrayObject *ap) +{ + Py_complex oop; + PyObject *op2; + c@typ@ temp; + int rsize; + + if (!(PyArray_IsScalar(op, @kind@))) { + if (PyArray_Check(op) && (PyArray_NDIM(op)==0)) { + op2 = ((PyArrayObject *)op)->descr->f->getitem \ + (((PyArrayObject *)op)->data, + (PyArrayObject *)op); + } + else { + op2 = op; Py_INCREF(op); + } + oop = PyComplex_AsCComplex (op2); + Py_DECREF(op2); + if (PyErr_Occurred()) return -1; + temp.real = (@typ@) oop.real; + temp.imag = (@typ@) oop.imag; + } + else { + temp = ((Py@kind@ScalarObject *)op)->obval; + } + + memcpy(ov, &temp, ap->descr->elsize); + if (!PyArray_ISNOTSWAPPED(ap)) + byte_swap_vector(ov, 2, sizeof(@typ@)); + + rsize = sizeof(@typ@); + copy_and_swap(ov, &temp, rsize, 2, rsize, !PyArray_ISNOTSWAPPED(ap)); + return 0; +} +/**end repeat**/ + +static PyObject * +LONGDOUBLE_getitem(char *ip, PyArrayObject *ap) +{ + return PyArray_Scalar(ip, ap->descr, NULL); +} + +static int +LONGDOUBLE_setitem(PyObject *op, char *ov, PyArrayObject *ap) { + longdouble temp; /* ensures alignment */ + + if (PyArray_IsScalar(op, LongDouble)) { + temp = ((PyLongDoubleScalarObject *)op)->obval; + } + else { + temp = (longdouble)PyFloat_AsDouble(op); + } + if (PyErr_Occurred()) return -1; + if (ap == NULL || PyArray_ISBEHAVED(ap)) + *((longdouble *)ov)=temp; + else { + copy_and_swap(ov, &temp, ap->descr->elsize, 1, 0, + !PyArray_ISNOTSWAPPED(ap)); + } + + return 0; +} + +static PyObject * +CLONGDOUBLE_getitem(char *ip, PyArrayObject *ap) +{ + return PyArray_Scalar(ip, ap->descr, NULL); +} + + + +/* UNICODE */ +static PyObject * +UNICODE_getitem(char *ip, PyArrayObject *ap) +{ + PyObject *obj; + size_t size = sizeof(Py_UNICODE); + + obj = PyUnicode_FromUnicode((const Py_UNICODE *)ip, + ap->descr->elsize / size); + if (!PyArray_ISNOTSWAPPED(ap) && (obj != NULL)) { + byte_swap_vector(PyUnicode_AS_UNICODE(obj), + ap->descr->elsize / size, size); + } + return obj; +} + +static int +UNICODE_setitem(PyObject *op, char *ov, PyArrayObject *ap) +{ + PyObject *temp; + Py_UNICODE *ptr; + int datalen; + size_t size = sizeof(Py_UNICODE); + + if ((temp=PyObject_Unicode(op)) == NULL) return -1; + + ptr = PyUnicode_AS_UNICODE(temp); + if ((ptr == NULL) || (PyErr_Occurred())) { + Py_DECREF(temp); + return -1; + } + datalen = PyUnicode_GET_DATA_SIZE(op); + + memcpy(ov, ptr, MIN(ap->descr->elsize, datalen)); + /* Fill in the rest of the space with 0 */ + if (ap->descr->elsize > datalen) { + memset(ov + datalen, 0, (ap->descr->elsize - datalen)); + } + + if (!PyArray_ISNOTSWAPPED(ap)) + byte_swap_vector(ov, ap->descr->elsize / size, size); + + Py_DECREF(temp); + return 0; +} + +/* STRING -- can handle both NULL-terminated and not NULL-terminated cases */ +static PyObject * +STRING_getitem(char *ip, PyArrayObject *ap) +{ + if (ip[ap->descr->elsize-1]) + return PyString_FromStringAndSize(ip,ap->descr->elsize); + else + return PyString_FromString(ip); +} + +static int +STRING_setitem(PyObject *op, char *ov, PyArrayObject *ap) +{ + char *ptr; + int len; + PyObject *temp=PyObject_Str(op); + + if (temp == NULL) return -1; + + if (PyString_AsStringAndSize(temp, &ptr, &len) == -1) { + Py_DECREF(temp); + return -1; + } + memcpy(ov, ptr, MIN(ap->descr->elsize,len)); + if (ap->descr->elsize > len) { + memset(ov + len, 0, (ap->descr->elsize - len)); + } + Py_DECREF(temp); + return 0; +} + +/* OBJECT */ + +static PyObject * +OBJECT_getitem(char *ip, PyArrayObject *ap) +{ + Py_INCREF(*(PyObject **)ip); + return *(PyObject **)ip; +} + +static int +OBJECT_setitem(PyObject *op, char *ov, PyArrayObject *ap) +{ + Py_XDECREF(*(PyObject **)ov); + Py_INCREF(op); + *(PyObject **)ov = op; + return PyErr_Occurred() ? -1:0; +} + +/* VOID */ + +static PyObject * +VOID_getitem(char *ip, PyArrayObject *ap) +{ + PyObject *u=NULL; + PyArray_Descr* descr; + int itemsize; + + descr = ap->descr; + if (descr->fields && descr->fields != Py_None) { + PyObject *key; + PyObject *names; + int i, n; + PyObject *ret; + PyObject *tup, *title; + PyArray_Descr *new; + int offset; + int savedflags; + + /* get the names from the fields dictionary*/ + key = PyInt_FromLong(-1); + names = PyDict_GetItem(descr->fields, key); + Py_DECREF(key); + if (!names) goto finish; + n = PyList_GET_SIZE(names); + ret = PyTuple_New(n); + savedflags = ap->flags; + for (i=0; i<n; i++) { + key = PyList_GET_ITEM(names, i); + tup = PyDict_GetItem(descr->fields, key); + if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset, + &title)) { + Py_DECREF(ret); + ap->descr = descr; + return NULL; + } + ap->descr = new; + /* update alignment based on offset */ + if ((new->alignment > 1) && \ + ((((intp)(ip+offset)) % new->alignment) != 0)) + ap->flags &= ~ALIGNED; + else + ap->flags |= ALIGNED; + + PyTuple_SET_ITEM(ret, i, \ + new->f->getitem(ip+offset, ap)); + ap->flags = savedflags; + } + ap->descr = descr; + return ret; + } + + if (descr->subarray) { + /* return an array of the basic type */ + PyArray_Dims shape={NULL,-1}; + PyObject *ret; + if (!(PyArray_IntpConverter(descr->subarray->shape, + &shape))) { + PyDimMem_FREE(shape.ptr); + PyErr_SetString(PyExc_ValueError, + "invalid shape in fixed-type tuple."); + return NULL; + } + ret = PyArray_NewFromDescr(&PyArray_Type, + descr->subarray->base, + shape.len, shape.ptr, + NULL, ip, ap->flags, NULL); + PyDimMem_FREE(shape.ptr); + if (!ret) return NULL; + PyArray_BASE(ret) = (PyObject *)ap; + Py_INCREF(ap); + PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL_FLAGS); + return ret; + } + + finish: + itemsize=ap->descr->elsize; + if (PyArray_ISWRITEABLE(ap)) + u = PyBuffer_FromReadWriteMemory(ip, itemsize); + else + u = PyBuffer_FromMemory(ip, itemsize); + if (u==NULL) goto fail; + + /* default is to return buffer object pointing to current item */ + /* a view of it */ + return u; + + fail: + return NULL; +} + + + +static int PyArray_CopyObject(PyArrayObject *, PyObject *); + +static int +VOID_setitem(PyObject *op, char *ip, PyArrayObject *ap) +{ + PyArray_Descr* descr; + int itemsize=ap->descr->elsize; + int res; + + descr = ap->descr; + if (descr->fields && (descr->fields != Py_None) && \ + PyTuple_Check(op)) { + PyObject *key; + PyObject *names; + int i, n; + PyObject *tup, *title; + PyArray_Descr *new; + int offset; + int savedflags; + res = -1; + /* get the names from the fields dictionary*/ + key = PyInt_FromLong(-1); + names = PyDict_GetItem(descr->fields, key); + Py_DECREF(key); + if (!names) goto finish; + n = PyList_GET_SIZE(names); + if (PyTuple_GET_SIZE(op) != n) { + PyErr_SetString(PyExc_ValueError, + "size of tuple must match"\ + "number of fields."); + return -1; + } + savedflags = ap->flags; + for (i=0; i<n; i++) { + key = PyList_GET_ITEM(names, i); + tup = PyDict_GetItem(descr->fields, key); + if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset, + &title)) { + ap->descr = descr; + return -1; + } + ap->descr = new; + /* remember to update alignment flags */ + if ((new->alignment > 1) && \ + ((((intp)(ip+offset)) % new->alignment) != 0)) + ap->flags &= ~ALIGNED; + else + ap->flags |= ALIGNED; + + res = new->f->setitem(PyTuple_GET_ITEM(op, i), + ip+offset, ap); + ap->flags = savedflags; + if (res < 0) break; + } + ap->descr = descr; + return res; + } + + if (descr->subarray) { + /* copy into an array of the same basic type */ + PyArray_Dims shape={NULL,-1}; + PyObject *ret; + if (!(PyArray_IntpConverter(descr->subarray->shape, + &shape))) { + PyDimMem_FREE(shape.ptr); + PyErr_SetString(PyExc_ValueError, + "invalid shape in fixed-type tuple."); + return -1; + } + ret = PyArray_NewFromDescr(&PyArray_Type, + descr->subarray->base, + shape.len, shape.ptr, + NULL, ip, ap->flags, NULL); + PyDimMem_FREE(shape.ptr); + if (!ret) return -1; + PyArray_BASE(ret) = (PyObject *)ap; + Py_INCREF(ap); + PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL_FLAGS); + res = PyArray_CopyObject((PyArrayObject *)ret, op); + Py_DECREF(ret); + return res; + } + + finish: + /* Default is to use buffer interface to set item */ + { + const void *buffer; + int buflen; + res = PyObject_AsReadBuffer(op, &buffer, &buflen); + if (res == -1) goto fail; + memcpy(ip, buffer, MIN(buflen, itemsize)); + } + return 0; + + fail: + return -1; +} + + +/****************** XXX_to_YYY *******************************/ + +/* Assumes contiguous, and aligned, from and to */ + + +/**begin repeat +#to=(BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE)*16# +#from=BYTE*13,UBYTE*13,SHORT*13,USHORT*13,INT*13,UINT*13,LONG*13,ULONG*13,LONGLONG*13,ULONGLONG*13,FLOAT*13,DOUBLE*13,LONGDOUBLE*13,CFLOAT*13,CDOUBLE*13,CLONGDOUBLE*13# +#totyp=(byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble)*16# +#fromtyp=byte*13, ubyte*13, short*13, ushort*13, int*13, uint*13, long*13, ulong*13, longlong*13, ulonglong*13, float*13, double*13, longdouble*13, float*13, double*13, longdouble*13# +#incr= ip++*169,ip+=2*39# +*/ +static void +@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<n;i++,op++) { + *op = (@totyp@)*ip; + @incr@; + } +} +/**end repeat**/ + +/**begin repeat +#from=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#fromtyp=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +@from@_to_BOOL(@fromtyp@ *ip, Bool *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<n;i++,op++,ip++) { + *op = (Bool)(*ip != FALSE); + } +} +/**end repeat**/ + +/**begin repeat +#from=CFLOAT, CDOUBLE, CLONGDOUBLE# +#fromtyp=cfloat, cdouble, clongdouble# +*/ +static void +@from@_to_BOOL(@fromtyp@ *ip, Bool *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<n;i++,op++,ip++) { + *op = (Bool)(((*ip).real != FALSE) || ((*ip).imag != FALSE)); + } +} +/**end repeat**/ + +/**begin repeat +#to=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#totyp=byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +BOOL_to_@to@(Bool *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<n;i++,op++,ip++) { + *op = (@totyp@)(*ip != FALSE); + } +} +/**end repeat**/ + +/**begin repeat + +#to=(CFLOAT,CDOUBLE,CLONGDOUBLE)*14# +#from=BOOL*3,BYTE*3,UBYTE*3,SHORT*3,USHORT*3,INT*3,UINT*3,LONG*3,ULONG*3,LONGLONG*3,ULONGLONG*3,FLOAT*3,DOUBLE*3,LONGDOUBLE*3# +#fromtyp=Bool*3,byte*3, ubyte*3, short*3, ushort*3, int*3, uint*3, long*3, ulong*3, longlong*3, ulonglong*3, float*3, double*3, longdouble*3# +#totyp= (float, double, longdouble)*14# +*/ +static void +@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<n;i++,ip++) { + *op++ = (@totyp@)*ip; + *op++ = 0.0; + } +} +/**end repeat**/ + +/**begin repeat + +#to=(CFLOAT,CDOUBLE,CLONGDOUBLE)*3# +#from=CFLOAT*3,CDOUBLE*3,CLONGDOUBLE*3# +#totyp=(float, double, longdouble)*3# +#fromtyp=float*3, double*3, longdouble*3# +*/ +static void +@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) { + register intp i; + for(i=0;i<2*n;i++,ip++,op++) { + *op = (@totyp@)*ip; + } +} + +/**end repeat**/ + +/**begin repeat + +#from=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE, STRING, UNICODE, VOID, OBJECT# +#fromtyp=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, char, PyObject *# +#skip= 1*17, aip->descr->elsize*3, 1# +*/ +static void +@from@_to_OBJECT(@fromtyp@ *ip, PyObject **op, intp n, PyArrayObject *aip, + PyArrayObject *aop) +{ + register intp i; + int skip=@skip@; + for(i=0;i<n;i++,ip+=skip,op++) { + Py_XDECREF(*op); + *op = @from@_getitem((char *)ip, aip); + } +} +/**end repeat**/ + +/**begin repeat + +#to=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE, STRING, UNICODE, VOID# +#totyp=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, char# +#skip= 1*17, aip->descr->elsize*3# +*/ +static void +OBJECT_to_@to@(PyObject **ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) +{ + register intp i; + int skip=@skip@; + for(i=0;i<n;i++,ip++,op+=skip) { + @to@_setitem(*ip, (char *)op, aop); + } +} +/**end repeat**/ + + +/**begin repeat + +#from=STRING*20, UNICODE*20, VOID*20# +#fromtyp=char*60# +#to=(BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,STRING,UNICODE,VOID)*3# +#totyp=(Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, void)*3# +#oskip=(1*17,aop->descr->elsize*3)*3# +#convert=1*17,0*3,1*17,0*3,0*20# +#convstr=(Int*9,Long*2,Float*3,Complex*3,Tuple*3)*3# +*/ +static void +@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) +{ + register intp i; + PyObject *temp=NULL; + int skip=aip->descr->elsize; + int oskip=@oskip@; + for(i=0; i<n; i++, ip+=skip, op+=oskip) { + temp = @from@_getitem((char *)ip, aip); + if (temp==NULL) return; + /* convert from Python object to needed one */ + if (@convert@) { + PyObject *new, *args; + /* call out to the Python builtin given by convstr */ + args = Py_BuildValue("(N)", temp); + new = Py@convstr@_Type.tp_new(&Py@convstr@_Type, args, NULL); + Py_DECREF(args); + temp = new; + if (temp==NULL) return; + } + + @to@_setitem(temp,(char *)op, aop); + Py_DECREF(temp); + } +} + +/**end repeat**/ + +/**begin repeat + +#to=STRING*17, UNICODE*17, VOID*17# +#totyp=char*17, char*17, char*17# +#from=(BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE)*3# +#fromtyp=(Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble)*3# +*/ + +static void +@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip, + PyArrayObject *aop) +{ + register intp i; + PyObject *temp=NULL; + int skip=1; + int oskip=aop->descr->elsize; + for(i=0; i<n; i++, ip+=skip, op+=oskip) { + temp = @from@_getitem((char *)ip, aip); + if (temp==NULL) { + Py_INCREF(Py_False); + temp = Py_False; + } + @to@_setitem(temp,(char *)op, aop); + Py_DECREF(temp); + } +} + +/**end repeat**/ + + +/****************** scan *************************************/ + +/**begin repeat + +#fname=SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#type=short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble# +#format="hd","hu","d","u","ld","lu",LONGLONG_FMT,ULONGLONG_FMT,"f","lf","Lf"# +*/ +static int +@fname@_scan (FILE *fp, @type@ *ip, char *sep, void *ignore) +{ + int num; + num = fscanf(fp, "%"@format@, ip); + if (num != 1) { + if (num == 0) return -3; + if (num == EOF) return -4; + return -5; + } + if (sep != NULL) { + num = fscanf(fp, sep); + if (num == 0) return 0; + if (num == EOF) return -1; + } + return 0; +} + +/**end repeat**/ + +/**begin repeat +#fname=BOOL,BYTE,UBYTE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID# +*/ +#define @fname@_scan NULL +/**end repeat**/ + + + +/****************** copyswapn *************************************/ + +/**begin repeat + +#fname=SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#fsize=SHORT,SHORT,INT,INT,LONG,LONG,LONGLONG,LONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#type=short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble# +*/ +static void +@fname@_copyswapn (void *dst, void *src, intp n, int swap, int itemsize) +{ + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, n*sizeof(@type@)); + + if (swap) { + register char *a, *b, c; + for (a = (char *)dst; n>0; n--) { +#if SIZEOF_@fsize@ == 2 + b = a + 1; + c = *a; *a++ = *b; *b = c; + a += 1; +#elif SIZEOF_@fsize@ == 4 + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 2; +#elif SIZEOF_@fsize@ == 8 + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 4; +#elif SIZEOF_@fsize@ == 10 + b = a + 9; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 5; +#elif SIZEOF_@fsize@ == 12 + b = a + 11; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 6; +#elif SIZEOF_@fsize@ == 16 + b = a + 15; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 8; +#else + register int i, nn; + b = a + (SIZEOF_@fsize@-1); + nn = SIZEOF_@fsize@ / 2; + for (i=0; i<nn; i++) { + c=*a; *a++ = *b; *b-- = c; + } + a += nn / 2; +#endif + } + } +} + +static void +@fname@_copyswap (void *dst, void *src, int swap, int itemsize) +{ + + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, sizeof(@type@)); + + if (swap) { + register char *a, *b, c; + a = (char *)dst; +#if SIZEOF_@fsize@ == 2 + b = a + 1; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 4 + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 8 + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 10 + b = a + 9; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 12 + b = a + 11; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 16 + b = a + 15; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#else + { + register int i, nn; + b = a + (SIZEOF_@fsize@-1); + nn = SIZEOF_@fsize@ / 2; + for (i=0; i<nn; i++) { + c=*a; *a++ = *b; *b-- = c; + } + } +#endif + } +} + + +/**end repeat**/ + +/**begin repeat + +#fname=BOOL, BYTE, UBYTE# +#type=Bool, byte, ubyte# +*/ +static void +@fname@_copyswapn (void *dst, void *src, intp n, int swap, int itemsize) +{ + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, n*sizeof(@type@)); + /* ignore swap */ +} + +static void +@fname@_copyswap (void *dst, void *src, int swap, int itemsize) +{ + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, sizeof(@type@)); + /* ignore swap */ +} + +/**end repeat**/ + + + +/**begin repeat + +#fname=CFLOAT,CDOUBLE,CLONGDOUBLE# +#type=cfloat, cdouble, clongdouble# +#fsize=FLOAT,DOUBLE,LONGDOUBLE# +*/ +static void +@fname@_copyswapn (void *dst, void *src, intp n, int swap, int itemsize) +{ + + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, n*sizeof(@type@)); + + if (swap) { + register char *a, *b, c; + /* complex type -- swap twice as many */ + register intp nn = 2*n; + for (a = (char *)dst; nn>0; nn--) { +#if SIZEOF_@fsize@ == 4 + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 2; +#elif SIZEOF_@fsize@ == 8 + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 4; +#elif SIZEOF_@fsize@ == 10 + b = a + 9; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 5; +#elif SIZEOF_@fsize@ == 12 + b = a + 11; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 6; +#elif SIZEOF_@fsize@ == 16 + b = a + 15; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 8; +#else + register int i, kn; + + b = a + (SIZEOF_@fsize@-1); + kn = SIZEOF_@fsize@ / 2; + for (i=0; i<kn; i++) { + c=*a; *a++ = *b; *b-- = c; + } + a += kn / 2; +#endif + } + } +} + +static void +@fname@_copyswap (void *dst, void *src, int swap, int itemsize) +{ + if (src != NULL) /* copy first if needed */ + memcpy(dst, src, sizeof(@type@)); + + if (swap) { + register char *a, *b, c; + a = (char *)dst; +#if SIZEOF_@fsize@ == 4 + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 2; + b = a + 3; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 8 + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 4; + b = a + 7; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 10 + b = a + 9; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 5; + b = a + 9; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 12 + b = a + 11; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 6; + b = a + 11; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#elif SIZEOF_@fsize@ == 16 + b = a + 15; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; + a += 8; + b = a + 15; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b-- = c; + c = *a; *a++ = *b; *b = c; +#else + { + register int i, nn; + b = a + (SIZEOF_@fsize@-1); + nn = SIZEOF_@fsize@ / 2; + for (i=0; i<nn; i++) { + c=*a; *a++ = *b; *b-- = c; + } + a += nn / 2; + b = a + (SIZEOF_@fsize@-1); + nn = SIZEOF_@fsize@ / 2; + for (i=0; i<nn; i++) { + c=*a; *a++ = *b; *b-- = c; + } + } +#endif + } +} + + +/**end repeat**/ + +static void +OBJECT_copyswapn (PyObject **dst, PyObject **src, intp n, int swap, int itemsize) +{ + register int i, nn=n; + PyObject **dp=dst, **sp=src; + if (src != NULL) { + for (i=0; i<nn; i++) { + Py_XDECREF(*dp); + Py_INCREF(*sp); + *dp++ = *sp++; + } + } + /* ignore swap */ + return; +} + +/* ignore swap */ +static void +STRING_copyswapn (char *dst, char *src, intp n, int swap, int itemsize) +{ + if (src != NULL) + memcpy(dst, src, itemsize * n); + + return; +} + +/* ignore swap */ +static void +VOID_copyswapn (char *dst, char *src, intp n, int swap, int itemsize) +{ + if (src != NULL) + memcpy(dst, src, itemsize * n); + return; +} + +static void +UNICODE_copyswapn (char *dst, char *src, intp n, int swap, int itemsize) +{ + int size = sizeof(Py_UNICODE); + + if (src != NULL) + memcpy(dst, src, itemsize * n); + + if (swap) { + register char *a, *b, c; + int j, i = size / 2; + for (a = (char *)dst; n>0; n--) { + b = a + (size-1); + for (j=0; j<i; j++) { + c=*a; *a++ = *b; *b-- = c; + } + a += i / 2; + } + } +} + + +static void +OBJECT_copyswap (PyObject **dst, PyObject **src, int swap, int itemsize) +{ + OBJECT_copyswapn(dst, src, 1, swap, itemsize); +} + +static void +STRING_copyswap (char *dst, char *src, int swap, int itemsize) +{ + if (src != NULL) + memcpy(dst, src, itemsize); + +} + +/* ignore swap */ +static void +VOID_copyswap (char *dst, char *src, int swap, int itemsize) +{ + if (src != NULL) + memcpy(dst, src, itemsize); + return; +} + +static void +UNICODE_copyswap (char *dst, char *src, int swap, int itemsize) +{ + int size = sizeof(Py_UNICODE); + + if (src != NULL) + memcpy(dst, src, itemsize); + + if (swap) { + register char *a, *b, c; + int j, i = size / 2; + a = (char *)dst; + b = a + (size-1); + for (j=0; j<i; j++) { + c=*a; *a++ = *b; *b-- = c; + } + } +} + + +/****************** nonzero **********************************/ + +/**begin repeat +#fname=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#type=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static Bool +@fname@_nonzero (@type@ *ip, PyArrayObject *ap) +{ + @type@ t1; + if (ap==NULL || PyArray_ISBEHAVED_RO(ap)) + return (Bool) (*ip != 0); + else { + /* don't worry about swap, since we are just testing + whether or not equal to 0 */ + memcpy(&t1, ip, sizeof(@type@)); + return (Bool) (t1 != 0); + } +} +/**end repeat**/ + +/**begin repeat +#fname=CFLOAT,CDOUBLE,CLONGDOUBLE# +#type=cfloat, cdouble, clongdouble# +*/ +static Bool +@fname@_nonzero (@type@ *ip, PyArrayObject *ap) +{ + @type@ t1; + if (ap==NULL || PyArray_ISBEHAVED_RO(ap)) + return (Bool) ((ip->real != 0) || (ip->imag != 0)); + else { + /* don't worry about swap, since we are just testing + whether or not equal to 0 */ + memcpy(&t1, ip, sizeof(@type@)); + return (Bool) ((t1.real != 0) || (t1.imag != 0)); + } +} +/**end repeat**/ + + + +#define WHITESPACE " \t\n\r\v\f" +#define WHITELEN 6 + +static Bool +Py_STRING_ISSPACE(char ch) +{ + char white[] = WHITESPACE; + int j; + Bool space=FALSE; + for (j=0; j<WHITELEN; j++) { + if (ch == white[j]) { + space=TRUE; + break; + } + } + return space; +} + +static Bool +STRING_nonzero (char *ip, PyArrayObject *ap) +{ + int len = ap->descr->elsize; + int i; + Bool nonz = FALSE; + + for (i=0; i<len; i++) { + if (!Py_STRING_ISSPACE(*ip)) { + nonz = TRUE; + break; + } + ip++; + } + return nonz; +} + +static Bool +UNICODE_nonzero (Py_UNICODE *ip, PyArrayObject *ap) +{ + int len = ap->descr->elsize >> 1; + int i; + Bool nonz = FALSE; + + for (i=0; i<len; i++) { + if (!Py_UNICODE_ISSPACE(*ip)) { + nonz = TRUE; + break; + } + ip++; + } + return nonz; +} + +static Bool +OBJECT_nonzero (PyObject **ip, PyArrayObject *ap) +{ + return (Bool) PyObject_IsTrue(*ip); +} + +/* If subclass has _nonzero method call it with buffer + object wrapping current item. Otherwise, just compare with '\0'. +*/ +static Bool +VOID_nonzero (char *ip, PyArrayObject *ap) +{ + int i; + int len = ap->descr->elsize; + Bool nonz = FALSE; + + for (i=0; i<len; i++) { + if (*ip != '\0') { + nonz = TRUE; + break; + } + ip++; + } + return nonz; +} + + +/****************** compare **********************************/ + +static int +BOOL_compare(Bool *ip1, Bool *ip2, PyArrayObject *ap) +{ + return (*ip1 ? (*ip2 ? 0 : 1) : (*ip2 ? -1 : 0)); +} + +/**begin repeat +#fname=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#type=byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ + +static int +@fname@_compare (@type@ *ip1, @type@ *ip2, PyArrayObject *ap) +{ + return *ip1 < *ip2 ? -1 : *ip1 == *ip2 ? 0 : 1; +} + +/**end repeat**/ + +/* compare imaginary part first, then complex if equal imaginary */ +/**begin repeat +#fname=CFLOAT, CDOUBLE, CLONGDOUBLE# +#type= float, double, longdouble# +*/ + +static int +@fname@_compare (@type@ *ip1, @type@ *ip2, PyArrayObject *ap) +{ + if (*ip1 == *ip2) { + return ip1[1]<ip2[1] ? -1 : (ip1[1] == ip2[1] ? 0 : 1); + } + else { + return *ip1 < *ip2 ? -1 : 1; + } +} + /**end repeat**/ + +static int +OBJECT_compare(PyObject **ip1, PyObject **ip2, PyArrayObject *ap) +{ + return PyObject_Compare(*ip1, *ip2); +} + +static int +STRING_compare(char *ip1, char *ip2, PyArrayObject *ap) +{ + return strncmp(ip1, ip2, ap->descr->elsize); +} + +/* taken from Python */ +static int +UNICODE_compare(register Py_UNICODE *ip1, register Py_UNICODE *ip2, + PyArrayObject *ap) +{ + register int itemsize=ap->descr->elsize; + register Py_UNICODE c1, c2; + + if (itemsize < 0) return 0; + + while(itemsize-- > 0) { + c1 = *ip1++; + c2 = *ip2++; + + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + return 0; +} + +/* possibly redefine compare in terms of fields and subarrays if any */ + +/* as it is, it compares raw-bytes as it they were strings */ +#define VOID_compare STRING_compare + +/****************** argfunc **********************************/ + +/**begin repeat + +#fname= BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE# +#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble# +#incr= ip++*14, ip+=2*3# +*/ + +static int +@fname@_argmax(@type@ *ip, intp n, intp *max_ind, PyArrayObject *aip) +{ + register intp i; + @type@ mp=*ip; + *max_ind=0; + for (i=1; i<n; i++) { + @incr@; + if (*ip > mp) { + mp = *ip; + *max_ind = i; + } + } + return 0; +} + +/**end repeat**/ + +static int +OBJECT_argmax(PyObject **ip, intp n, intp *max_ind, PyArrayObject *aip) +{ + register intp i; + PyObject *mp=ip[0]; *max_ind=0; + for(i=1; i<n; i++) { + ip++; + if (PyObject_Compare(*ip,mp) > 0) { + mp = *ip; + *max_ind=i; + } + } + return 0; +} + +/**begin repeat + +#fname= STRING, UNICODE# +#type= char, Py_UNICODE# + +*/ +static int +@fname@_argmax(@type@ *ip, intp n, intp *max_ind, PyArrayObject *aip) +{ + register intp i; + int elsize = aip->descr->elsize; + @type@ *mp = (@type@ *)_pya_malloc(elsize); + + if (mp==NULL) return 0; + memcpy(mp, ip, elsize); + *max_ind = 0; + for(i=1; i<n; i++) { + ip += elsize; + if (@fname@_compare(ip,mp,aip) > 0) { + memcpy(mp, ip, elsize); + *max_ind=i; + } + } + _pya_free(mp); + return 0; +} + +/**end repeat**/ + +#define VOID_argmax NULL + +static void +BOOL_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n, + void *ignore) +{ + register Bool tmp=FALSE; + register intp i; + for(i=0;i<n;i++,ip1+=is1,ip2+=is2) { + if ((*((Bool *)ip1) != 0) && (*((Bool *)ip2) != 0)) { + tmp = TRUE; + break; + } + } + *((Bool *)op) = tmp; +} + +/**begin repeat +#name=BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE# +#type= byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +#out= long, ulong, long, ulong, long, ulong, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +@name@_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n, + void *ignore) +{ + register @out@ tmp=(@out@)0; + register intp i; + for(i=0;i<n;i++,ip1+=is1,ip2+=is2) { + tmp += (@out@)(*((@type@ *)ip1)) * \ + (@out@)(*((@type@ *)ip2)); + } + *((@type@ *)op) = (@type@) tmp; +} +/**end repeat**/ + + +/**begin repeat +#name=CFLOAT, CDOUBLE, CLONGDOUBLE# +#type= float, double, longdouble# +*/ +static void @name@_dot(char *ip1, intp is1, char *ip2, intp is2, + char *op, intp n, void *ignore) +{ + @type@ tmpr=(@type@)0.0, tmpi=(@type@)0.0; + intp i; + for(i=0;i<n;i++,ip1+=is1,ip2+=is2) { + tmpr += ((@type@ *)ip1)[0] * ((@type@ *)ip2)[0] + - ((@type@ *)ip1)[1] * ((@type@ *)ip2)[1]; + tmpi += ((@type@ *)ip1)[1] * ((@type@ *)ip2)[0] + + ((@type@ *)ip1)[0] * ((@type@ *)ip2)[1]; + } + ((@type@ *)op)[0] = tmpr; ((@type@ *)op)[1] = tmpi; +} + +/**end repeat**/ + +static void +OBJECT_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n, + void *ignore) +{ + intp i; + PyObject *tmp1, *tmp2, *tmp=NULL; + PyObject **tmp3; + for(i=0;i<n;i++,ip1+=is1,ip2+=is2) { + tmp1 = PyNumber_Multiply(*((PyObject **)ip1), + *((PyObject **)ip2)); + if (!tmp1) { Py_XDECREF(tmp); return;} + if (i == 0) { + tmp = tmp1; + } else { + tmp2 = PyNumber_Add(tmp, tmp1); + Py_XDECREF(tmp); + Py_XDECREF(tmp1); + if (!tmp2) return; + tmp = tmp2; + } + } + tmp3 = (PyObject**) op; + tmp2 = *tmp3; + *((PyObject **)op) = tmp; + Py_XDECREF(tmp2); +} + +#define BOOL_fill NULL + +/* this requires buffer to be filled with objects or NULL */ +static void +OBJECT_fill(PyObject **buffer, intp length, void *ignored) +{ + intp i; + PyObject *start = buffer[0]; + PyObject *delta = buffer[1]; + delta = PyNumber_Subtract(delta, start); + if (!delta) return; + start = PyNumber_Add(start, delta); + if (!start) goto finish; + buffer += 2; + + for (i=2; i<length; i++, buffer++) { + start = PyNumber_Add(start, delta); + if (!start) goto finish; + Py_XDECREF(*buffer); + *buffer = start; + } + + finish: + Py_DECREF(delta); + return; +} + +/**begin repeat +#NAME=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#typ=byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble# +*/ +static void +@NAME@_fill(@typ@ *buffer, intp length, void *ignored) +{ + intp i; + @typ@ start = buffer[0]; + @typ@ delta = buffer[1]; + delta -= start; + start += (delta + delta); + buffer += 2; + for (i=2; i<length; i++, buffer++) { + *buffer = start; + start += delta; + } +} +/**end repeat**/ + +/**begin repeat +#NAME=CFLOAT,CDOUBLE,CLONGDOUBLE# +#typ=cfloat,cdouble,clongdouble# +*/ +static void +@NAME@_fill(@typ@ *buffer, intp length, void *ignored) +{ + intp i; + @typ@ start; + @typ@ delta; + + start.real = buffer->real; + start.imag = buffer->imag; + delta.real = buffer[1].real; + delta.imag = buffer[1].imag; + delta.real -= start.real; + delta.imag -= start.imag; + start.real += (delta.real + delta.real); + start.imag += (delta.imag + delta.imag); + buffer += 2; + for (i=2; i<length; i++, buffer++) { + buffer->real = start.real; + buffer->imag = start.imag; + start.real += delta.real; + start.imag += delta.imag; + } +} +/**end repeat**/ + + +#define _ALIGN(type) offsetof(struct {char c; type v;},v) + +/**begin repeat + +#from= VOID, STRING, UNICODE# +#align= char, char, Py_UNICODE# +#NAME= Void, String, Unicode# +#endian= |, |, =# +*/ + +static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { + { + (PyArray_VectorUnaryFunc*)@from@_to_BOOL, + (PyArray_VectorUnaryFunc*)@from@_to_BYTE, + (PyArray_VectorUnaryFunc*)@from@_to_UBYTE, + (PyArray_VectorUnaryFunc*)@from@_to_SHORT, + (PyArray_VectorUnaryFunc*)@from@_to_USHORT, + (PyArray_VectorUnaryFunc*)@from@_to_INT, + (PyArray_VectorUnaryFunc*)@from@_to_UINT, + (PyArray_VectorUnaryFunc*)@from@_to_LONG, + (PyArray_VectorUnaryFunc*)@from@_to_ULONG, + (PyArray_VectorUnaryFunc*)@from@_to_LONGLONG, + (PyArray_VectorUnaryFunc*)@from@_to_ULONGLONG, + (PyArray_VectorUnaryFunc*)@from@_to_FLOAT, + (PyArray_VectorUnaryFunc*)@from@_to_DOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_LONGDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_CFLOAT, + (PyArray_VectorUnaryFunc*)@from@_to_CDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_CLONGDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_OBJECT, + (PyArray_VectorUnaryFunc*)@from@_to_STRING, + (PyArray_VectorUnaryFunc*)@from@_to_UNICODE, + (PyArray_VectorUnaryFunc*)@from@_to_VOID + }, + (PyArray_GetItemFunc*)@from@_getitem, + (PyArray_SetItemFunc*)@from@_setitem, + (PyArray_CompareFunc*)@from@_compare, + (PyArray_ArgFunc*)@from@_argmax, + (PyArray_DotFunc*)NULL, + (PyArray_ScanFunc*)@from@_scan, + (PyArray_CopySwapNFunc*)@from@_copyswapn, + (PyArray_CopySwapFunc*)@from@_copyswap, + (PyArray_NonzeroFunc*)@from@_nonzero, + (PyArray_FillFunc*)NULL, + { + NULL, NULL, NULL, NULL + }, + { + NULL, NULL, NULL, NULL + } +}; + +static PyArray_Descr @from@_Descr = { + PyObject_HEAD_INIT(&PyArrayDescr_Type) + &Py@NAME@ArrType_Type, + PyArray_@from@LTR, + PyArray_@from@LTR, + '@endian@', + PyArray_@from@, 0, + _ALIGN(@align@), + NULL, + NULL, + &_Py@NAME@_ArrFuncs, +}; + +/**end repeat**/ + + +/**begin repeat + +#from= BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT# +#num= 1*14,2*3,1# +#fromtyp= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble, PyObject *# +#NAME= Bool, Byte, UByte, Short, UShort, Int, UInt, Long, ULong, LongLong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object# +#kind= GENBOOL, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, FLOATING, FLOATING, FLOATING, COMPLEX, COMPLEX, COMPLEX, OBJECT# +#endian= |*3, =*14, |# +*/ + +static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { + { + (PyArray_VectorUnaryFunc*)@from@_to_BOOL, + (PyArray_VectorUnaryFunc*)@from@_to_BYTE, + (PyArray_VectorUnaryFunc*)@from@_to_UBYTE, + (PyArray_VectorUnaryFunc*)@from@_to_SHORT, + (PyArray_VectorUnaryFunc*)@from@_to_USHORT, + (PyArray_VectorUnaryFunc*)@from@_to_INT, + (PyArray_VectorUnaryFunc*)@from@_to_UINT, + (PyArray_VectorUnaryFunc*)@from@_to_LONG, + (PyArray_VectorUnaryFunc*)@from@_to_ULONG, + (PyArray_VectorUnaryFunc*)@from@_to_LONGLONG, + (PyArray_VectorUnaryFunc*)@from@_to_ULONGLONG, + (PyArray_VectorUnaryFunc*)@from@_to_FLOAT, + (PyArray_VectorUnaryFunc*)@from@_to_DOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_LONGDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_CFLOAT, + (PyArray_VectorUnaryFunc*)@from@_to_CDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_CLONGDOUBLE, + (PyArray_VectorUnaryFunc*)@from@_to_OBJECT, + (PyArray_VectorUnaryFunc*)@from@_to_STRING, + (PyArray_VectorUnaryFunc*)@from@_to_UNICODE, + (PyArray_VectorUnaryFunc*)@from@_to_VOID + }, + (PyArray_GetItemFunc*)@from@_getitem, + (PyArray_SetItemFunc*)@from@_setitem, + (PyArray_CompareFunc*)@from@_compare, + (PyArray_ArgFunc*)@from@_argmax, + (PyArray_DotFunc*)@from@_dot, + (PyArray_ScanFunc*)@from@_scan, + (PyArray_CopySwapNFunc*)@from@_copyswapn, + (PyArray_CopySwapFunc*)@from@_copyswap, + (PyArray_NonzeroFunc*)@from@_nonzero, + (PyArray_FillFunc*)@from@_fill, + { + NULL, NULL, NULL, NULL + }, + { + NULL, NULL, NULL, NULL + } +}; + +static PyArray_Descr @from@_Descr = { + PyObject_HEAD_INIT(&PyArrayDescr_Type) + &Py@NAME@ArrType_Type, + PyArray_@kind@LTR, + PyArray_@from@LTR, + '@endian@', + PyArray_@from@, + @num@*sizeof(@fromtyp@), + _ALIGN(@fromtyp@), + NULL, + NULL, + &_Py@NAME@_ArrFuncs, +}; + +/**end repeat**/ + +#define _MAX_LETTER 128 +static char _letter_to_num[_MAX_LETTER]; + +static PyArray_Descr *_builtin_descrs[] = { + &BOOL_Descr, + &BYTE_Descr, + &UBYTE_Descr, + &SHORT_Descr, + &USHORT_Descr, + &INT_Descr, + &UINT_Descr, + &LONG_Descr, + &ULONG_Descr, + &LONGLONG_Descr, + &ULONGLONG_Descr, + &FLOAT_Descr, + &DOUBLE_Descr, + &LONGDOUBLE_Descr, + &CFLOAT_Descr, + &CDOUBLE_Descr, + &CLONGDOUBLE_Descr, + &OBJECT_Descr, + &STRING_Descr, + &UNICODE_Descr, + &VOID_Descr, +}; + +/*OBJECT_API + Get the PyArray_Descr structure for a type. +*/ +static PyArray_Descr * +PyArray_DescrFromType(int type) +{ + PyArray_Descr *ret=NULL; + + if (type < PyArray_NTYPES) { + ret = _builtin_descrs[type]; + } + else if (type == PyArray_NOTYPE) { + /* This needs to not raise an error so + that PyArray_DescrFromType(PyArray_NOTYPE) + works for backwards-compatible C-API + */ + return NULL; + } + else if PyTypeNum_ISUSERDEF(type) { + ret = userdescrs[type-PyArray_USERDEF]; + } + else { + int num=PyArray_NTYPES; + if (type < _MAX_LETTER) + num = (int) _letter_to_num[type]; + if (num >= PyArray_NTYPES) + ret = NULL; + else + ret = _builtin_descrs[num]; + } + if (ret==NULL) { + PyErr_SetString(PyExc_ValueError, + "Invalid type for array"); + } + else Py_INCREF(ret); + return ret; +} + + +static int +set_typeinfo(PyObject *dict) +{ + PyObject *infodict, *s; + int i; + + for (i=0; i<_MAX_LETTER; i++) { + _letter_to_num[i] = PyArray_NTYPES; + } + +/**begin repeat +#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID# +*/ + _letter_to_num[PyArray_@name@LTR] = PyArray_@name@; +/**end repeat**/ + _letter_to_num[PyArray_STRINGLTR2] = PyArray_STRING; + + +/**begin repeat +#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID# +*/ + @name@_Descr.fields = Py_None; +/**end repeat**/ + + + + /* Set a dictionary with type information */ + infodict = PyDict_New(); + if (infodict == NULL) return -1; + +#define BITSOF_INTP CHAR_BIT*SIZEOF_PY_INTPTR_T +#define BITSOF_BYTE CHAR_BIT + +/**begin repeat + +#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG# +#uname=BOOL,BYTE*2,SHORT*2,INT*2,INTP*2,LONG*2,LONGLONG*2# +#Name=Bool,Byte,UByte,Short,UShort,Int,UInt,Intp,UIntp,Long,ULong,LongLong,ULongLong# +#type=Bool,byte,ubyte,short,ushort,int,uint,intp,uintp,long,ulong,longlong,ulonglong# +#max=1,MAX_BYTE,MAX_UBYTE,MAX_SHORT,MAX_USHORT,MAX_INT,PyLong_FromUnsignedLong(MAX_UINT),PyLong_FromLongLong((longlong) MAX_INTP),PyLong_FromUnsignedLongLong((ulonglong) MAX_UINTP),MAX_LONG,PyLong_FromUnsignedLong((unsigned long) MAX_ULONG),PyLong_FromLongLong((longlong) MAX_LONGLONG), PyLong_FromUnsignedLongLong((ulonglong) MAX_ULONGLONG)# +#min=0,MIN_BYTE,0,MIN_SHORT,0,MIN_INT,0,PyLong_FromLongLong((longlong) MIN_INTP),0,MIN_LONG,0,PyLong_FromLongLong((longlong) MIN_LONGLONG),0# +#cx=i*6,N,N,N,l,N,N,N# +#cn=i*7,N,i,l,i,N,i# +*/ + PyDict_SetItemString(infodict, "@name@", + s=Py_BuildValue("ciii@cx@@cn@O", + PyArray_@name@LTR, + PyArray_@name@, + BITSOF_@uname@, + _ALIGN(@type@), + @max@, @min@, + (PyObject *)&Py@Name@ArrType_Type)); + Py_DECREF(s); +/**end repeat**/ + +#define BITSOF_CFLOAT 2*BITSOF_FLOAT +#define BITSOF_CDOUBLE 2*BITSOF_DOUBLE +#define BITSOF_CLONGDOUBLE 2*BITSOF_LONGDOUBLE + +/**begin repeat + +#type=float,double,longdouble,cfloat,cdouble,clongdouble# +#name=FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE# +#Name=Float,Double,LongDouble,CFloat,CDouble,CLongDouble# +*/ + PyDict_SetItemString(infodict, "@name@", + s=Py_BuildValue("ciiiO", PyArray_@name@LTR, + PyArray_@name@, BITSOF_@name@, + _ALIGN(@type@), + (PyObject *)\ + &Py@Name@ArrType_Type)); + Py_DECREF(s); +/**end repeat**/ + + PyDict_SetItemString(infodict, "OBJECT", + s=Py_BuildValue("ciiiO", PyArray_OBJECTLTR, + PyArray_OBJECT, + sizeof(PyObject *)*CHAR_BIT, + _ALIGN(PyObject *), + (PyObject *)\ + &PyObjectArrType_Type)); + Py_DECREF(s); + PyDict_SetItemString(infodict, "STRING", + s=Py_BuildValue("ciiiO", PyArray_STRINGLTR, + PyArray_STRING, 0, + _ALIGN(char), + (PyObject *)\ + &PyStringArrType_Type)); + Py_DECREF(s); + PyDict_SetItemString(infodict, "UNICODE", + s=Py_BuildValue("ciiiO", PyArray_UNICODELTR, + PyArray_UNICODE, 0, + _ALIGN(Py_UNICODE), + (PyObject *)\ + &PyUnicodeArrType_Type)); + Py_DECREF(s); + PyDict_SetItemString(infodict, "VOID", + s=Py_BuildValue("ciiiO", PyArray_VOIDLTR, + PyArray_VOID, 0, + _ALIGN(char), + (PyObject *)\ + &PyVoidArrType_Type)); + Py_DECREF(s); + +#define SETTYPE(name) \ + Py_INCREF(&Py##name##ArrType_Type); \ + PyDict_SetItemString(infodict, #name, \ + (PyObject *)&Py##name##ArrType_Type); + + SETTYPE(Generic) + SETTYPE(Numeric) + SETTYPE(Integer) + SETTYPE(Inexact) + SETTYPE(SignedInteger) + SETTYPE(UnsignedInteger) + SETTYPE(Floating) + SETTYPE(ComplexFloating) + SETTYPE(Flexible) + SETTYPE(Character) + +#undef SETTYPE + + PyDict_SetItemString(dict, "typeinfo", infodict); + Py_DECREF(infodict); + return 0; +} + +#undef _MAX_LETTER + diff --git a/numpy/core/src/multiarraymodule.c b/numpy/core/src/multiarraymodule.c new file mode 100644 index 000000000..2cce9ad03 --- /dev/null +++ b/numpy/core/src/multiarraymodule.c @@ -0,0 +1,5507 @@ +/* + Python Multiarray Module -- A useful collection of functions for creating and + using ndarrays + + Original file + Copyright (c) 1995, 1996, 1997 Jim Hugunin, hugunin@mit.edu + + Modified for scipy_core in 2005 + + Travis E. Oliphant + Assistant Professor at + Brigham Young University + +*/ + +/* $Id: multiarraymodule.c,v 1.36 2005/09/14 00:14:00 teoliphant Exp $ */ + +#include "Python.h" +#include "structmember.h" +/*#include <string.h> +#include <math.h> +*/ + +#define _MULTIARRAYMODULE +#include "scipy/arrayobject.h" + +#define PyAO PyArrayObject + +static PyObject *typeDict=NULL; /* Must be explicitly loaded */ +static PyObject *_scipy_internal=NULL; /* A Python module for callbacks */ + + +static PyArray_Descr * +_arraydescr_fromobj(PyObject *obj) +{ + PyObject *dtypedescr; + PyArray_Descr *new; + int ret; + + dtypedescr = PyObject_GetAttrString(obj, "dtypedescr"); + PyErr_Clear(); + if (dtypedescr) { + ret = PyArray_DescrConverter(dtypedescr, &new); + Py_DECREF(dtypedescr); + if (ret) return new; + PyErr_Clear(); + } + return NULL; +} + + +/* Including this file is the only way I know how to declare functions + static in each file, and store the pointers from functions in both + arrayobject.c and multiarraymodule.c for the C-API + + Declarying an external pointer-containing variable in arrayobject.c + and trying to copy it to PyArray_API, did not work. + + Think about two modules with a common api that import each other... + + This file would just be the module calls. +*/ + +#include "arrayobject.c" + + +/* An Error object -- rarely used? */ +static PyObject *MultiArrayError; + +/*MULTIARRAY_API + Multiply a List of ints +*/ +static int +PyArray_MultiplyIntList(register int *l1, register int n) +{ + register int s=1; + while (n--) s *= (*l1++); + return s; +} + +/*MULTIARRAY_API + Multiply a List +*/ +static intp +PyArray_MultiplyList(register intp *l1, register int n) +{ + register intp s=1; + while (n--) s *= (*l1++); + return s; +} + +/*MULTIARRAY_API + Produce a pointer into array +*/ +static char * +PyArray_GetPtr(PyArrayObject *obj, register intp* ind) +{ + register int n = obj->nd; + register intp *strides = obj->strides; + register char *dptr = obj->data; + + while (n--) dptr += (*strides++) * (*ind++); + return dptr; +} + +/*MULTIARRAY_API + Get axis from an object (possibly None) -- a converter function, +*/ +static int +PyArray_AxisConverter(PyObject *obj, int *axis) +{ + if (obj == Py_None) { + *axis = MAX_DIMS; + } + else { + *axis = (int) PyInt_AsLong(obj); + if (PyErr_Occurred()) { + return PY_FAIL; + } + } + return PY_SUCCEED; +} + +/*MULTIARRAY_API + Compare Lists +*/ +static int +PyArray_CompareLists(intp *l1, intp *l2, int n) +{ + int i; + for(i=0;i<n;i++) { + if (l1[i] != l2[i]) return 0; + } + return 1; +} + +/* steals a reference to type -- accepts NULL */ +/*MULTIARRAY_API + View +*/ +static PyObject * +PyArray_View(PyArrayObject *self, PyArray_Descr *type) +{ + PyObject *new=NULL; + + Py_INCREF(self->descr); + new = PyArray_NewFromDescr(self->ob_type, + self->descr, + self->nd, self->dimensions, + self->strides, + self->data, + self->flags, (PyObject *)self); + + if (new==NULL) return NULL; + Py_INCREF(self); + PyArray_BASE(new) = (PyObject *)self; + + if (type != NULL) { + if (PyObject_SetAttrString(new, "dtypedescr", + (PyObject *)type) < 0) { + Py_DECREF(new); + Py_DECREF(type); + return NULL; + } + Py_DECREF(type); + } + return new; +} + +/*MULTIARRAY_API + Ravel +*/ +static PyObject * +PyArray_Ravel(PyArrayObject *a, int fortran) +{ + PyArray_Dims newdim = {NULL,1}; + intp val[1] = {-1}; + + if (fortran < 0) fortran = PyArray_ISFORTRAN(a); + + newdim.ptr = val; + if (!fortran && PyArray_ISCONTIGUOUS(a)) { + if (a->nd == 1) { + Py_INCREF(a); + return (PyObject *)a; + } + return PyArray_Newshape(a, &newdim); + } + else + return PyArray_Flatten(a, fortran); +} + +/*MULTIARRAY_API + Flatten +*/ +static PyObject * +PyArray_Flatten(PyArrayObject *a, int fortran) +{ + PyObject *ret, *new; + intp size; + + if (fortran < 0) fortran = PyArray_ISFORTRAN(a); + + size = PyArray_SIZE(a); + Py_INCREF(a->descr); + ret = PyArray_NewFromDescr(a->ob_type, + a->descr, + 1, &size, + NULL, + NULL, + 0, (PyObject *)a); + + if (ret== NULL) return NULL; + if (fortran) { + new = PyArray_Transpose(a, NULL); + if (new == NULL) { + Py_DECREF(ret); + return NULL; + } + } + else { + Py_INCREF(a); + new = (PyObject *)a; + } + if (PyArray_CopyInto((PyArrayObject *)ret, (PyArrayObject *)new) < 0) { + Py_DECREF(ret); + Py_DECREF(new); + return NULL; + } + Py_DECREF(new); + return ret; +} + + +/* For back-ward compatability * + +/ * Not recommended */ + +/*MULTIARRAY_API + Reshape an array +*/ +static PyObject * +PyArray_Reshape(PyArrayObject *self, PyObject *shape) +{ + PyObject *ret; + PyArray_Dims newdims; + + if (!PyArray_IntpConverter(shape, &newdims)) return NULL; + ret = PyArray_Newshape(self, &newdims); + PyDimMem_FREE(newdims.ptr); + return ret; +} + +static int +_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides) +{ + int nd; + intp *dims; + Bool done=FALSE; + int j, k; + + nd = self->nd; + dims = self->dimensions; + + for (k=0, j=0; !done && (j<nd || k<newnd);) { + if ((j<nd) && (k<newnd) && (newdims[k]==dims[j])) { + strides[k] = self->strides[j]; + j++; k++; + } + else if ((k<newnd) && (newdims[k]==1)) { + strides[k] = 0; + k++; + } + else if ((j<nd) && (dims[j]==1)) { + j++; + } + else done=TRUE; + } + if (done) return -1; + return 0; +} + +/* Returns a new array + with the new shape from the data + in the old array +*/ + +/*MULTIARRAY_API + New shape for an array +*/ +static PyObject * +PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims) +{ + intp i, s_original, i_unknown, s_known; + intp *dimensions = newdims->ptr; + PyArrayObject *ret; + char msg[] = "total size of new array must be unchanged"; + int n = newdims->len; + Bool same; + intp *strides = NULL; + intp newstrides[MAX_DIMS]; + + /* Quick check to make sure anything needs to be done */ + if (n == self->nd) { + same = TRUE; + i=0; + while(same && i<n) { + if (PyArray_DIM(self,i) != dimensions[i]) + same=FALSE; + i++; + } + if (same) return PyArray_View(self, NULL); + } + + /* Returns a pointer to an appropriate strides array + if all we are doing is inserting ones into the shape, + or removing ones from the shape + or doing a combination of the two*/ + i=_check_ones(self, n, dimensions, newstrides); + if (i==0) strides=newstrides; + + if (strides==NULL) { + if (!PyArray_ISCONTIGUOUS(self)) { + PyErr_SetString(PyExc_ValueError, + "changing shape that way "\ + "only works on contiguous arrays"); + return NULL; + } + + s_known = 1; + i_unknown = -1; + + for(i=0; i<n; i++) { + if (dimensions[i] < 0) { + if (i_unknown == -1) { + i_unknown = i; + } else { + PyErr_SetString(PyExc_ValueError, + "can only specify one" \ + " unknown dimension"); + return NULL; + } + } else { + s_known *= dimensions[i]; + } + } + + s_original = PyArray_SIZE(self); + + if (i_unknown >= 0) { + if ((s_known == 0) || (s_original % s_known != 0)) { + PyErr_SetString(PyExc_ValueError, msg); + return NULL; + } + dimensions[i_unknown] = s_original/s_known; + } else { + if (s_original != s_known) { + PyErr_SetString(PyExc_ValueError, msg); + return NULL; + } + } + } + + Py_INCREF(self->descr); + ret = (PyAO *)PyArray_NewFromDescr(self->ob_type, + self->descr, + n, dimensions, + strides, + self->data, + self->flags, (PyObject *)self); + + if (ret== NULL) return NULL; + + Py_INCREF(self); + ret->base = (PyObject *)self; + PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); + + return (PyObject *)ret; +} + +/* return a new view of the array object with all of its unit-length + dimensions squeezed out if needed, otherwise + return the same array. + */ + +/*MULTIARRAY_API*/ +static PyObject * +PyArray_Squeeze(PyArrayObject *self) +{ + int nd = self->nd; + int newnd = nd; + intp dimensions[MAX_DIMS]; + intp strides[MAX_DIMS]; + int i,j; + PyObject *ret; + + if (nd == 0) { + Py_INCREF(self); + return (PyObject *)self; + } + for (j=0, i=0; i<nd; i++) { + if (self->dimensions[i] == 1) { + newnd -= 1; + } + else { + dimensions[j] = self->dimensions[i]; + strides[j++] = self->strides[i]; + } + } + + Py_INCREF(self->descr); + ret = PyArray_NewFromDescr(self->ob_type, + self->descr, + newnd, dimensions, + strides, self->data, + self->flags, + (PyObject *)self); + if (ret == NULL) return NULL; + PyArray_FLAGS(ret) &= ~OWN_DATA; + PyArray_BASE(ret) = (PyObject *)self; + Py_INCREF(self); + return (PyObject *)ret; +} + + +/*MULTIARRAY_API + Mean +*/ +static PyObject * +PyArray_Mean(PyArrayObject *self, int axis, int rtype) +{ + PyObject *obj1=NULL, *obj2=NULL; + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + obj1 = PyArray_GenericReduceFunction((PyAO *)new, n_ops.add, axis, + rtype); + obj2 = PyFloat_FromDouble((double) PyArray_DIM(new,axis)); + Py_DECREF(new); + if (obj1 == NULL || obj2 == NULL) { + Py_XDECREF(obj1); + Py_XDECREF(obj2); + return NULL; + } + + ret = PyNumber_Divide(obj1, obj2); + Py_DECREF(obj1); + Py_DECREF(obj2); + return ret; +} + +/* Set variance to 1 to by-pass square-root calculation and return variance */ +/*MULTIARRAY_API + Std +*/ +static PyObject * +PyArray_Std(PyArrayObject *self, int axis, int rtype, int variance) +{ + PyObject *obj1=NULL, *obj2=NULL, *new=NULL; + PyObject *ret=NULL, *newshape=NULL; + int i, n; + intp val; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + /* Compute and reshape mean */ + obj1 = PyArray_EnsureArray(PyArray_Mean((PyAO *)new, axis, rtype)); + if (obj1 == NULL) {Py_DECREF(new); return NULL;} + n = PyArray_NDIM(new); + newshape = PyTuple_New(n); + if (newshape == NULL) {Py_DECREF(obj1); Py_DECREF(new); return NULL;} + for (i=0; i<n; i++) { + if (i==axis) val = 1; + else val = PyArray_DIM(new,i); + PyTuple_SET_ITEM(newshape, i, PyInt_FromLong((long)val)); + } + obj2 = PyArray_Reshape((PyAO *)obj1, newshape); + Py_DECREF(obj1); + Py_DECREF(newshape); + if (obj2 == NULL) {Py_DECREF(new); return NULL;} + + /* Compute x = x - mx */ + obj1 = PyNumber_Subtract((PyObject *)new, obj2); + Py_DECREF(obj2); + if (obj1 == NULL) {Py_DECREF(new); return NULL;} + + /* Compute x * x */ + obj2 = PyArray_EnsureArray(PyNumber_Multiply(obj1, obj1)); + Py_DECREF(obj1); + if (obj2 == NULL) {Py_DECREF(new); return NULL;} + + /* Compute add.reduce(x*x,axis) */ + obj1 = PyArray_GenericReduceFunction((PyAO *)obj2, n_ops.add, + axis, rtype); + Py_DECREF(obj2); + if (obj1 == NULL) {Py_DECREF(new); return NULL;} + + n = PyArray_DIM(new,axis)-1; + Py_DECREF(new); + if (n<=0) n=1; + obj2 = PyFloat_FromDouble(1.0/((double )n)); + if (obj2 == NULL) {Py_DECREF(obj1); return NULL;} + ret = PyNumber_Multiply(obj1, obj2); + Py_DECREF(obj1); + Py_DECREF(obj2); + + if (variance) return ret; + + ret = PyArray_EnsureArray(ret); + + /* sqrt() */ + obj1 = PyArray_GenericUnaryFunction((PyAO *)ret, n_ops.sqrt); + Py_DECREF(ret); + + return obj1; +} + + +/*MULTIARRAY_API + Sum +*/ +static PyObject * +PyArray_Sum(PyArrayObject *self, int axis, int rtype) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericReduceFunction((PyAO *)new, n_ops.add, axis, + rtype); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + Prod +*/ +static PyObject * +PyArray_Prod(PyArrayObject *self, int axis, int rtype) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericReduceFunction((PyAO *)new, n_ops.multiply, axis, + rtype); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + CumSum +*/ +static PyObject * +PyArray_CumSum(PyArrayObject *self, int axis, int rtype) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericAccumulateFunction((PyAO *)new, n_ops.add, axis, + rtype); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + CumProd +*/ +static PyObject * +PyArray_CumProd(PyArrayObject *self, int axis, int rtype) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericAccumulateFunction((PyAO *)new, + n_ops.multiply, axis, + rtype); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + Any +*/ +static PyObject * +PyArray_Any(PyArrayObject *self, int axis) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericReduceFunction((PyAO *)new, + n_ops.logical_or, axis, + PyArray_BOOL); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + All +*/ +static PyObject * +PyArray_All(PyArrayObject *self, int axis) +{ + PyObject *new, *ret; + + if ((new = _check_axis(self, &axis, 0))==NULL) return NULL; + + ret = PyArray_GenericReduceFunction((PyAO *)new, + n_ops.logical_and, axis, + PyArray_BOOL); + Py_DECREF(new); + return ret; +} + + +/*MULTIARRAY_API + Compress +*/ +static PyObject * +PyArray_Compress(PyArrayObject *self, PyObject *condition, int axis) +{ + PyArrayObject *cond; + PyObject *res, *ret; + + cond = (PyAO *)PyArray_FromAny(condition, NULL, 0, 0, 0); + if (cond == NULL) return NULL; + + if (cond->nd != 1) { + Py_DECREF(cond); + PyErr_SetString(PyExc_ValueError, + "condition must be 1-d array"); + return NULL; + } + + res = PyArray_Nonzero(cond); + Py_DECREF(cond); + ret = PyArray_Take(self, res, axis); + Py_DECREF(res); + return ret; +} + +/*MULTIARRAY_API + Nonzero +*/ +static PyObject * +PyArray_Nonzero(PyArrayObject *self) +{ + int n=self->nd, j; + intp count=0, i, size; + PyArrayIterObject *it=NULL; + PyObject *ret=NULL, *item; + intp *dptr[MAX_DIMS]; + + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it==NULL) return NULL; + + size = it->size; + for (i=0; i<size; i++) { + if (self->descr->f->nonzero(it->dataptr, self)) count++; + PyArray_ITER_NEXT(it); + } + + PyArray_ITER_RESET(it); + if (n==1) { + ret = PyArray_New(self->ob_type, 1, &count, PyArray_INTP, + NULL, NULL, 0, 0, (PyObject *)self); + if (ret == NULL) goto fail; + dptr[0] = (intp *)PyArray_DATA(ret); + + for (i=0; i<size; i++) { + if (self->descr->f->nonzero(it->dataptr, self)) + *(dptr[0])++ = i; + PyArray_ITER_NEXT(it); + } + } + else { + ret = PyTuple_New(n); + for (j=0; j<n; j++) { + item = PyArray_New(self->ob_type, 1, &count, + PyArray_INTP, NULL, NULL, 0, 0, + (PyObject *)self); + if (item == NULL) goto fail; + PyTuple_SET_ITEM(ret, j, item); + dptr[j] = (intp *)PyArray_DATA(item); + } + + /* reset contiguous so that coordinates gets updated */ + it->contiguous = 0; + for (i=0; i<size; i++) { + if (self->descr->f->nonzero(it->dataptr, self)) + for (j=0; j<n; j++) + *(dptr[j])++ = it->coordinates[j]; + PyArray_ITER_NEXT(it); + } + } + + Py_DECREF(it); + return ret; + + fail: + Py_XDECREF(ret); + Py_XDECREF(it); + return NULL; + +} + +/*MULTIARRAY_API + Clip +*/ +static PyObject * +PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max) +{ + PyObject *selector=NULL, *newtup=NULL, *ret=NULL; + PyObject *res1=NULL, *res2=NULL, *res3=NULL; + PyObject *two; + + two = PyInt_FromLong((long)2); + res1 = PyArray_GenericBinaryFunction(self, max, n_ops.greater); + res2 = PyArray_GenericBinaryFunction(self, min, n_ops.less); + if ((res1 == NULL) || (res2 == NULL)) { + Py_DECREF(two); + Py_XDECREF(res1); + Py_XDECREF(res2); + } + res3 = PyNumber_Multiply(two, res1); + Py_DECREF(two); + Py_DECREF(res1); + if (res3 == NULL) return NULL; + + selector = PyArray_EnsureArray(PyNumber_Add(res2, res3)); + Py_DECREF(res2); + Py_DECREF(res3); + if (selector == NULL) return NULL; + + newtup = Py_BuildValue("(OOO)", (PyObject *)self, min, max); + if (newtup == NULL) {Py_DECREF(selector); return NULL;} + ret = PyArray_Choose((PyAO *)selector, newtup); + Py_DECREF(selector); + Py_DECREF(newtup); + return ret; +} + +/*MULTIARRAY_API + Conjugate +*/ +static PyObject * +PyArray_Conjugate(PyArrayObject *self) +{ + if (PyArray_ISCOMPLEX(self)) { + PyObject *new; + intp size, i; + /* Make a copy */ + new = PyArray_NewCopy(self, -1); + if (new==NULL) return NULL; + size = PyArray_SIZE(new); + if (self->descr->type_num == PyArray_CFLOAT) { + cfloat *dptr = (cfloat *) PyArray_DATA(new); + for (i=0; i<size; i++) { + dptr->imag = -dptr->imag; + dptr++; + } + } + else if (self->descr->type_num == PyArray_CDOUBLE) { + cdouble *dptr = (cdouble *)PyArray_DATA(new); + for (i=0; i<size; i++) { + dptr->imag = -dptr->imag; + dptr++; + } + } + else if (self->descr->type_num == PyArray_CLONGDOUBLE) { + clongdouble *dptr = (clongdouble *)PyArray_DATA(new); + for (i=0; i<size; i++) { + dptr->imag = -dptr->imag; + dptr++; + } + } + return new; + } + else { + Py_INCREF(self); + return (PyObject *) self; + } +} + +/*MULTIARRAY_API + Trace +*/ +static PyObject * +PyArray_Trace(PyArrayObject *self, int offset, int axis1, int axis2, +int rtype) +{ + PyObject *diag=NULL, *ret=NULL; + + diag = PyArray_Diagonal(self, offset, axis1, axis2); + if (diag == NULL) return NULL; + ret = PyArray_GenericReduceFunction((PyAO *)diag, n_ops.add, -1, rtype); + Py_DECREF(diag); + return ret; +} + +/*MULTIARRAY_API + Diagonal +*/ +static PyObject * +PyArray_Diagonal(PyArrayObject *self, int offset, int axis1, int axis2) +{ + int n = self->nd; + PyObject *new; + PyArray_Dims newaxes; + intp dims[MAX_DIMS]; + int i, pos; + + newaxes.ptr = dims; + if (n < 2) { + PyErr_SetString(PyExc_ValueError, + "array.ndim must be >= 2"); + return NULL; + } + if (axis1 < 0) axis1 += n; + if (axis2 < 0) axis2 += n; + if ((axis1 == axis2) || (axis1 < 0) || (axis1 >= n) || \ + (axis2 < 0) || (axis2 >= n)) { + PyErr_Format(PyExc_ValueError, "axis1(=%d) and axis2(=%d) "\ + "must be different and within range (nd=%d)", + axis1, axis2, n); + return NULL; + } + + newaxes.len = n; + /* insert at the end */ + newaxes.ptr[n-2] = axis1; + newaxes.ptr[n-1] = axis2; + pos = 0; + for (i=0; i<n; i++) { + if ((i==axis1) || (i==axis2)) continue; + newaxes.ptr[pos++] = i; + } + new = PyArray_Transpose(self, &newaxes); + if (new == NULL) return NULL; + self = (PyAO *)new; + + if (n == 2) { + PyObject *a=NULL, *indices=NULL, *ret=NULL; + intp n1, n2, start, stop, step, count; + intp *dptr; + n1 = self->dimensions[0]; + n2 = self->dimensions[1]; + step = n2+1; + if (offset < 0) { + start = -n2 * offset; + stop = MIN(n2, n1+offset)*(n2+1) - n2*offset; + } + else { + start = offset; + stop = MIN(n1, n2-offset)*(n2+1) + offset; + } + + /* count = ceil((stop-start)/step) */ + count = ((stop-start) / step) + (((stop-start) % step) != 0); + + indices = PyArray_New(&PyArray_Type, 1, &count, + PyArray_INTP, NULL, NULL, 0, 0, NULL); + if (indices == NULL) { + Py_DECREF(self); return NULL; + } + dptr = (intp *)PyArray_DATA(indices); + for (n1=start; n1<stop; n1+=step) *dptr++ = n1; + a = PyArray_IterNew((PyObject *)self); + Py_DECREF(self); + if (a == NULL) {Py_DECREF(indices); return NULL;} + ret = PyObject_GetItem(a, indices); + Py_DECREF(a); + Py_DECREF(indices); + return ret; + } + + else { + /* + my_diagonal = [] + for i in range (s [0]) : + my_diagonal.append (diagonal (a [i], offset)) + return array (my_diagonal) + */ + PyObject *mydiagonal=NULL, *new=NULL, *ret=NULL, *sel=NULL; + intp i, n1; + int res; + PyArray_Descr *typecode; + + typecode = self->descr; + + mydiagonal = PyList_New(0); + if (mydiagonal == NULL) {Py_DECREF(self); return NULL;} + n1 = self->dimensions[0]; + for (i=0; i<n1; i++) { + new = PyInt_FromLong((long) i); + sel = PyArray_EnsureArray(PyObject_GetItem((PyObject *)self, new)); + Py_DECREF(new); + if (sel == NULL) { + Py_DECREF(self); + Py_DECREF(mydiagonal); + return NULL; + } + new = PyArray_Diagonal((PyAO *)sel, offset, n-3, n-2); + Py_DECREF(sel); + if (new == NULL) { + Py_DECREF(self); + Py_DECREF(mydiagonal); + return NULL; + } + res = PyList_Append(mydiagonal, new); + Py_DECREF(new); + if (res < 0) { + Py_DECREF(self); + Py_DECREF(mydiagonal); + return NULL; + } + } + Py_DECREF(self); + Py_INCREF(typecode); + ret = PyArray_FromAny(mydiagonal, typecode, 0, 0, 0); + Py_DECREF(mydiagonal); + return ret; + } +} + +/* simulates a C-style 1-3 dimensional array which can be accesed using + ptr[i] or ptr[i][j] or ptr[i][j][k] -- requires pointer allocation + for 2-d and 3-d. + + For 2-d and up, ptr is NOT equivalent to a statically defined + 2-d or 3-d array. In particular, it cannot be passed into a + function that requires a true pointer to a fixed-size array. +*/ + +/* steals a reference to typedescr -- can be NULL*/ +/*MULTIARRAY_API + Simulat a C-array +*/ +static int +PyArray_AsCArray(PyObject **op, void *ptr, intp *dims, int nd, + PyArray_Descr* typedescr) +{ + PyArrayObject *ap; + intp n, m, i, j; + char **ptr2; + char ***ptr3; + + if ((nd < 1) || (nd > 3)) { + PyErr_SetString(PyExc_ValueError, + "C arrays of only 1-3 dimensions available"); + Py_XDECREF(typedescr); + return -1; + } + if ((ap = (PyArrayObject*)PyArray_FromAny(*op, typedescr, nd, nd, + CARRAY_FLAGS)) == NULL) + return -1; + switch(nd) { + case 1: + *((char **)ptr) = ap->data; + break; + case 2: + n = ap->dimensions[0]; + ptr2 = (char **)_pya_malloc(n * sizeof(char *)); + if (!ptr2) goto fail; + for (i=0; i<n; i++) { + ptr2[i] = ap->data + i*ap->strides[0]; + } + *((char ***)ptr) = ptr2; + break; + case 3: + n = ap->dimensions[0]; + m = ap->dimensions[1]; + ptr3 = (char ***)_pya_malloc(n*(m+1) * sizeof(char *)); + if (!ptr3) goto fail; + for (i=0; i<n; i++) { + ptr3[i] = ptr3[n + (m-1)*i]; + for (j=0; j<m; j++) { + ptr3[i][j] = ap->data + i*ap->strides[0] + \ + j*ap->strides[1]; + } + } + *((char ****)ptr) = ptr3; + } + memcpy(dims, ap->dimensions, nd*sizeof(intp)); + *op = (PyObject *)ap; + return 0; + + fail: + PyErr_SetString(PyExc_MemoryError, "no memory"); + return -1; +} + +/* Deprecated --- Use PyArray_AsCArray instead */ + +/*MULTIARRAY_API + Convert to a 1D C-array +*/ +static int +PyArray_As1D(PyObject **op, char **ptr, int *d1, int typecode) +{ + intp newd1; + PyArray_Descr *descr; + + descr = PyArray_DescrFromType(typecode); + if (PyArray_AsCArray(op, (void *)ptr, &newd1, 1, descr) == -1) + return -1; + *d1 = (int) newd1; + return 0; +} + +/*MULTIARRAY_API + Convert to a 2D C-array +*/ +static int +PyArray_As2D(PyObject **op, char ***ptr, int *d1, int *d2, int typecode) +{ + intp newdims[2]; + PyArray_Descr *descr; + + descr = PyArray_DescrFromType(typecode); + if (PyArray_AsCArray(op, (void *)ptr, newdims, 2, descr) == -1) + return -1; + + *d1 = (int ) newdims[0]; + *d2 = (int ) newdims[1]; + return 0; +} + +/* End Deprecated */ + +/*MULTIARRAY_API + Free pointers created if As2D is called +*/ +static int +PyArray_Free(PyObject *op, void *ptr) +{ + PyArrayObject *ap = (PyArrayObject *)op; + + if ((ap->nd < 1) || (ap->nd > 3)) + return -1; + if (ap->nd >= 2) { + _pya_free(ptr); + } + Py_DECREF(ap); + return 0; +} + + +static PyObject * +_swap_and_concat(PyObject *op, int axis, int n) +{ + PyObject *newtup=NULL; + PyObject *otmp, *arr; + int i; + + newtup = PyTuple_New(n); + if (newtup==NULL) return NULL; + for (i=0; i<n; i++) { + otmp = PySequence_GetItem(op, i); + arr = PyArray_FROM_O(otmp); + Py_DECREF(otmp); + if (arr==NULL) goto fail; + otmp = PyArray_SwapAxes((PyArrayObject *)arr, axis, 0); + Py_DECREF(arr); + if (otmp == NULL) goto fail; + PyTuple_SET_ITEM(newtup, i, otmp); + } + otmp = PyArray_Concatenate(newtup, 0); + Py_DECREF(newtup); + if (otmp == NULL) return NULL; + arr = PyArray_SwapAxes((PyArrayObject *)otmp, axis, 0); + Py_DECREF(otmp); + return arr; + + fail: + Py_DECREF(newtup); + return NULL; +} + +/*op is a python object supporting the sequence interface. + Its elements will be concatenated together to form a single + multidimensional array.*/ +/* If axis is MAX_DIMS or bigger, then each sequence object will + be flattened before concatenation +*/ +/*MULTIARRAY_API + Concatenate an arbitrary Python sequence into an array. +*/ +static PyObject * +PyArray_Concatenate(PyObject *op, int axis) +{ + PyArrayObject *ret, **mps; + PyObject *otmp; + int i, n, tmp, nd=0, new_dim; + char *data; + PyTypeObject *subtype; + double prior1, prior2; + intp numbytes; + + n = PySequence_Length(op); + if (n == -1) { + return NULL; + } + if (n == 0) { + PyErr_SetString(PyExc_ValueError, + "concatenation of zero-length sequences is "\ + "impossible"); + return NULL; + } + + if ((axis < 0) || ((0 < axis) && (axis < MAX_DIMS))) + return _swap_and_concat(op, axis, n); + + mps = PyArray_ConvertToCommonType(op, &n); + if (mps == NULL) return NULL; + + /* Make sure these arrays are legal to concatenate. */ + /* Must have same dimensions except d0 */ + + prior1 = 0.0; + subtype = &PyArray_Type; + ret = NULL; + for(i=0; i<n; i++) { + if (axis >= MAX_DIMS) { + otmp = PyArray_Ravel(mps[i],0); + Py_DECREF(mps[i]); + mps[i] = (PyArrayObject *)otmp; + } + prior2 = PyArray_GetPriority((PyObject *)(mps[i]), 0.0); + if (prior2 > prior1) { + prior1 = prior2; + subtype = mps[i]->ob_type; + ret = mps[i]; + } + } + + new_dim = 0; + for(i=0; i<n; i++) { + if (mps[i] == NULL) goto fail; + if (i == 0) nd = mps[i]->nd; + else { + if (nd != mps[i]->nd) { + PyErr_SetString(PyExc_ValueError, + "arrays must have same "\ + "number of dimensions"); + goto fail; + } + if (!PyArray_CompareLists(mps[0]->dimensions+1, + mps[i]->dimensions+1, + nd-1)) { + PyErr_SetString(PyExc_ValueError, + "array dimensions must "\ + "agree except for d_0"); + goto fail; + } + } + if (nd == 0) { + PyErr_SetString(PyExc_ValueError, + "0-d arrays can't be concatenated"); + goto fail; + } + new_dim += mps[i]->dimensions[0]; + } + + tmp = mps[0]->dimensions[0]; + mps[0]->dimensions[0] = new_dim; + Py_INCREF(mps[0]->descr); + ret = (PyArrayObject *)PyArray_NewFromDescr(subtype, + mps[0]->descr, nd, + mps[0]->dimensions, + NULL, NULL, 0, + (PyObject *)ret); + mps[0]->dimensions[0] = tmp; + + if (ret == NULL) goto fail; + + data = ret->data; + for(i=0; i<n; i++) { + numbytes = PyArray_NBYTES(mps[i]); + memcpy(data, mps[i]->data, numbytes); + data += numbytes; + } + + PyArray_INCREF(ret); + for(i=0; i<n; i++) Py_XDECREF(mps[i]); + PyDataMem_FREE(mps); + return (PyObject *)ret; + + fail: + Py_XDECREF(ret); + for(i=0; i<n; i++) Py_XDECREF(mps[i]); + PyDataMem_FREE(mps); + return NULL; +} + +/*MULTIARRAY_API + SwapAxes +*/ +static PyObject * +PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2) +{ + PyArray_Dims new_axes; + intp dims[MAX_DIMS]; + int n, i, val; + PyObject *ret; + + if (a1 == a2) { + Py_INCREF(ap); + return (PyObject *)ap; + } + + n = ap->nd; + if (n <= 1) { + Py_INCREF(ap); + return (PyObject *)ap; + } + + if (a1 < 0) a1 += n; + if (a2 < 0) a2 += n; + if ((a1 < 0) || (a1 >= n)) { + PyErr_SetString(PyExc_ValueError, + "bad axis1 argument to swapaxes"); + return NULL; + } + if ((a2 < 0) || (a2 >= n)) { + PyErr_SetString(PyExc_ValueError, + "bad axis2 argument to swapaxes"); + return NULL; + } + new_axes.ptr = dims; + new_axes.len = n; + + for (i=0; i<n; i++) { + if (i == a1) val = a2; + else if (i == a2) val = a1; + else val = i; + new_axes.ptr[i] = val; + } + ret = PyArray_Transpose(ap, &new_axes); + return ret; +} + +/*MULTIARRAY_API + Return Transpose. +*/ +static PyObject * +PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) +{ + intp *axes, axis; + intp i, n; + intp permutation[MAX_DIMS]; + PyArrayObject *ret = NULL; + + if (permute == NULL) { + n = ap->nd; + for(i=0; i<n; i++) + permutation[i] = n-1-i; + } else { + n = permute->len; + axes = permute->ptr; + if (n > ap->nd) { + PyErr_SetString(PyExc_ValueError, + "too many axes for this array"); + return NULL; + } + for(i=0; i<n; i++) { + axis = axes[i]; + if (axis < 0) axis = ap->nd+axis; + if (axis < 0 || axis >= ap->nd) { + PyErr_SetString(PyExc_ValueError, + "invalid axis for this array"); + return NULL; + } + permutation[i] = axis; + } + } + + /* this allocates memory for dimensions and strides (but fills them + incorrectly), sets up descr, and points data at ap->data. */ + Py_INCREF(ap->descr); + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(ap->ob_type, + ap->descr, + n, permutation, + NULL, ap->data, ap->flags, + (PyObject *)ap); + if (ret == NULL) return NULL; + + /* point at true owner of memory: */ + ret->base = (PyObject *)ap; + Py_INCREF(ap); + + for(i=0; i<n; i++) { + ret->dimensions[i] = ap->dimensions[permutation[i]]; + ret->strides[i] = ap->strides[permutation[i]]; + } + PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); + + return (PyObject *)ret; +} + +/*MULTIARRAY_API + Repeat the array. +*/ +static PyObject * +PyArray_Repeat(PyArrayObject *aop, PyObject *op, int axis) +{ + intp *counts; + intp n, n_outer, i, j, k, chunk, total; + intp tmp; + int nd; + PyArrayObject *repeats=NULL; + PyObject *ap=NULL; + PyArrayObject *ret=NULL; + char *new_data, *old_data; + + repeats = (PyAO *)PyArray_ContiguousFromAny(op, PyArray_INTP, 0, 1); + if (repeats == NULL) return NULL; + nd = repeats->nd; + counts = (intp *)repeats->data; + + if ((ap=_check_axis(aop, &axis, CARRAY_FLAGS))==NULL) { + Py_DECREF(repeats); + return NULL; + } + + aop = (PyAO *)ap; + + if (nd == 1) + n = repeats->dimensions[0]; + else /* nd == 0 */ + n = aop->dimensions[axis]; + + if (aop->dimensions[axis] != n) { + PyErr_SetString(PyExc_ValueError, + "a.shape[axis] != len(repeats)"); + goto fail; + } + + + if (nd == 0) + total = counts[0]*n; + else { + + total = 0; + for(j=0; j<n; j++) { + if (counts[j] < 0) { + PyErr_SetString(PyExc_ValueError, "count < 0"); + goto fail; + } + total += counts[j]; + } + } + + + /* Construct new array */ + aop->dimensions[axis] = total; + Py_INCREF(aop->descr); + ret = (PyArrayObject *)PyArray_NewFromDescr(aop->ob_type, + aop->descr, + aop->nd, + aop->dimensions, + NULL, NULL, 0, + (PyObject *)aop); + aop->dimensions[axis] = n; + + if (ret == NULL) goto fail; + + new_data = ret->data; + old_data = aop->data; + + chunk = aop->descr->elsize; + for(i=axis+1; i<aop->nd; i++) { + chunk *= aop->dimensions[i]; + } + + n_outer = 1; + for(i=0; i<axis; i++) n_outer *= aop->dimensions[i]; + + for(i=0; i<n_outer; i++) { + for(j=0; j<n; j++) { + tmp = (nd ? counts[j] : counts[0]); + for(k=0; k<tmp; k++) { + memcpy(new_data, old_data, chunk); + new_data += chunk; + } + old_data += chunk; + } + } + + Py_DECREF(repeats); + PyArray_INCREF(ret); + Py_XDECREF(aop); + return (PyObject *)ret; + + fail: + Py_DECREF(repeats); + Py_XDECREF(aop); + Py_XDECREF(ret); + return NULL; +} + +/*OBJECT_API*/ +static PyArrayObject ** +PyArray_ConvertToCommonType(PyObject *op, int *retn) +{ + int i, n, allscalars=0; + PyArrayObject **mps=NULL; + PyObject *otmp; + PyArray_Descr *intype=NULL, *stype=NULL; + PyArray_Descr *newtype=NULL; + + + *retn = n = PySequence_Length(op); + if (PyErr_Occurred()) {*retn = 0; return NULL;} + + mps = (PyArrayObject **)PyDataMem_NEW(n*sizeof(PyArrayObject *)); + if (mps == NULL) { + *retn = 0; + return (void*)PyErr_NoMemory(); + } + + for(i=0; i<n; i++) { + otmp = PySequence_GetItem(op, i); + if (!PyArray_CheckAnyScalar(otmp)) { + newtype = PyArray_DescrFromObject(otmp, intype); + Py_XDECREF(intype); + intype = newtype; + mps[i] = NULL; + } + else { + newtype = PyArray_DescrFromObject(otmp, stype); + Py_XDECREF(stype); + stype = newtype; + mps[i] = (PyArrayObject *)Py_None; + Py_INCREF(Py_None); + } + Py_XDECREF(otmp); + } + if (intype==NULL) { /* all scalars */ + allscalars = 1; + intype = stype; + Py_INCREF(intype); + for (i=0; i<n; i++) { + Py_XDECREF(mps[i]); + mps[i] = NULL; + } + } + /* Make sure all arrays are actual array objects. */ + for(i=0; i<n; i++) { + int flags = CARRAY_FLAGS; + if ((otmp = PySequence_GetItem(op, i)) == NULL) + goto fail; + if (!allscalars && ((PyObject *)(mps[i]) == Py_None)) { + /* forcecast scalars */ + flags |= FORCECAST; + Py_DECREF(Py_None); + } + mps[i] = (PyArrayObject*) + PyArray_FromAny(otmp, intype, 0, 0, flags); + Py_DECREF(otmp); + Py_XDECREF(stype); + } + return mps; + + fail: + Py_XDECREF(intype); + Py_XDECREF(stype); + *retn = 0; + for (i=0; i<n; i++) Py_XDECREF(mps[i]); + PyDataMem_FREE(mps); + return NULL; +} + + +/*MULTIARRAY_API + Numeric.choose() +*/ +static PyObject * +PyArray_Choose(PyArrayObject *ip, PyObject *op) +{ + intp *sizes, offset; + int i,n,m,elsize; + char *ret_data; + PyArrayObject **mps, *ap, *ret; + intp *self_data, mi; + ap = NULL; + ret = NULL; + + /* Convert all inputs to arrays of a common type */ + mps = PyArray_ConvertToCommonType(op, &n); + if (mps == NULL) return NULL; + + sizes = (intp *)_pya_malloc(n*sizeof(intp)); + if (sizes == NULL) goto fail; + + ap = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)ip, + PyArray_INTP, + 0, 0); + if (ap == NULL) goto fail; + + /* Check the dimensions of the arrays */ + for(i=0; i<n; i++) { + if (mps[i] == NULL) goto fail; + if (ap->nd < mps[i]->nd) { + PyErr_SetString(PyExc_ValueError, + "too many dimensions"); + goto fail; + } + if (!PyArray_CompareLists(ap->dimensions+(ap->nd-mps[i]->nd), + mps[i]->dimensions, mps[i]->nd)) { + PyErr_SetString(PyExc_ValueError, + "array dimensions must agree"); + goto fail; + } + sizes[i] = PyArray_NBYTES(mps[i]); + } + + Py_INCREF(mps[0]->descr); + ret = (PyArrayObject *)PyArray_NewFromDescr(ap->ob_type, + mps[0]->descr, + ap->nd, + ap->dimensions, + NULL, NULL, 0, + (PyObject *)ap); + if (ret == NULL) goto fail; + + elsize = ret->descr->elsize; + m = PyArray_SIZE(ret); + self_data = (intp *)ap->data; + ret_data = ret->data; + + for (i=0; i<m; i++) { + mi = *self_data; + if (mi < 0 || mi >= n) { + PyErr_SetString(PyExc_ValueError, + "invalid entry in choice array"); + goto fail; + } + offset = i*elsize; + if (offset >= sizes[mi]) {offset = offset % sizes[mi]; } + memmove(ret_data, mps[mi]->data+offset, elsize); + ret_data += elsize; self_data++; + } + + PyArray_INCREF(ret); + for(i=0; i<n; i++) Py_XDECREF(mps[i]); + Py_DECREF(ap); + PyDataMem_FREE(mps); + _pya_free(sizes); + + return (PyObject *)ret; + + fail: + for(i=0; i<n; i++) Py_XDECREF(mps[i]); + Py_XDECREF(ap); + PyDataMem_FREE(mps); + _pya_free(sizes); + Py_XDECREF(ret); + return NULL; +} + +static void +_strided_copy(char *dst, intp dststride, char *src, intp srcstride, intp num, int elsize) +{ + while(num--) { + memcpy(dst, src, elsize); + dst += dststride; + src += srcstride; + } +} + +/* These algorithms use special sorting. They are not called unless the + underlying sort function for the type is available. Note that axis is already + valid. The sort functions require 1-d contiguous and well-behaved data. + Therefore, a copy will be made of the data if needed before handing it to the + sorting routine. + An iterator is constructed and adjusted to walk over all but the desired sorting + axis. +*/ +static int +_new_sort(PyArrayObject *op, int axis, PyArray_SORTKIND which) +{ + PyArrayIterObject *it; + int needcopy=0; + intp N, size; + int elsize; + intp astride; + PyArray_SortFunc *sort; + BEGIN_THREADS_DEF + + it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)op, axis); + if (it == NULL) return -1; + + BEGIN_THREADS + sort = op->descr->f->sort[which]; + size = it->size; + N = op->dimensions[axis]; + elsize = op->descr->elsize; + astride = op->strides[axis]; + + needcopy = !(op->flags & ALIGNED) || (astride != (intp) elsize); + + if (needcopy) { + char *buffer; + buffer = PyDataMem_NEW(N*elsize); + while (size--) { + _strided_copy(buffer, (intp) elsize, it->dataptr, + astride, N, elsize); + if (sort(buffer, N, op) < 0) { + PyDataMem_FREE(buffer); goto fail; + } + _strided_copy(it->dataptr, astride, buffer, + (intp) elsize, N, elsize); + PyArray_ITER_NEXT(it); + } + PyDataMem_FREE(buffer); + } + else { + while (size--) { + if (sort(it->dataptr, N, op) < 0) goto fail; + PyArray_ITER_NEXT(it); + } + } + + END_THREADS + + Py_DECREF(it); + return 0; + + fail: + END_THREADS + + Py_DECREF(it); + return 0; +} + +static PyObject* +_new_argsort(PyArrayObject *op, int axis, PyArray_SORTKIND which) +{ + + PyArrayIterObject *it=NULL; + PyArrayIterObject *rit=NULL; + PyObject *ret; + int needcopy=0, i; + intp N, size; + int elsize; + intp astride, rstride, *iptr; + PyArray_ArgSortFunc *argsort; + BEGIN_THREADS_DEF + + ret = PyArray_New(op->ob_type, op->nd, + op->dimensions, PyArray_INTP, + NULL, NULL, 0, 0, (PyObject *)op); + if (ret == NULL) return NULL; + + it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)op, axis); + rit = (PyArrayIterObject *)PyArray_IterAllButAxis(ret, axis); + if (rit == NULL || it == NULL) goto fail; + + BEGIN_THREADS + + argsort = op->descr->f->argsort[which]; + size = it->size; + N = op->dimensions[axis]; + elsize = op->descr->elsize; + astride = op->strides[axis]; + rstride = PyArray_STRIDE(ret,axis); + + needcopy = !(op->flags & ALIGNED) || (astride != (intp) elsize) || \ + (rstride != sizeof(intp)); + + if (needcopy) { + char *valbuffer, *indbuffer; + valbuffer = PyDataMem_NEW(N*(elsize+sizeof(intp))); + indbuffer = valbuffer + (N*elsize); + while (size--) { + _strided_copy(valbuffer, (intp) elsize, it->dataptr, + astride, N, elsize); + iptr = (intp *)indbuffer; + for (i=0; i<N; i++) *iptr++ = i; + if (argsort(valbuffer, (intp *)indbuffer, N, op) < 0) { + PyDataMem_FREE(valbuffer); goto fail; + } + _strided_copy(rit->dataptr, rstride, indbuffer, + sizeof(intp), N, sizeof(intp)); + PyArray_ITER_NEXT(it); + PyArray_ITER_NEXT(rit); + } + PyDataMem_FREE(valbuffer); + } + else { + while (size--) { + iptr = (intp *)rit->dataptr; + for (i=0; i<N; i++) *iptr++ = i; + if (argsort(it->dataptr, (intp *)rit->dataptr, + N, op) < 0) goto fail; + PyArray_ITER_NEXT(it); + PyArray_ITER_NEXT(rit); + } + } + + END_THREADS + + Py_DECREF(it); + Py_DECREF(rit); + return ret; + + fail: + + END_THREADS + + Py_DECREF(ret); + Py_XDECREF(it); + Py_XDECREF(rit); + return NULL; +} + + +/* Be sure to save this global_compare when necessary */ + +static PyArrayObject *global_obj; + +static int +qsortCompare (const void *a, const void *b) +{ + return global_obj->descr->f->compare(a,b,global_obj); +} + +/* Consumes reference to ap (op gets it) + op contains a version of the array with axes swapped if + local variable axis is not the last dimension. + orign must be defined locally. +*/ + +#define SWAPAXES(op, ap) { \ + orign = (ap)->nd-1; \ + if (axis != orign) { \ + (op) = (PyAO *)PyArray_SwapAxes((ap), axis, orign); \ + Py_DECREF((ap)); \ + if ((op) == NULL) return NULL; \ + } \ + else (op) = (ap); \ + } + +/* Consumes reference to ap (op gets it) + origin must be previously defined locally. + SWAPAXES must have been called previously. + op contains the swapped version of the array. +*/ +#define SWAPBACK(op, ap) { \ + if (axis != orign) { \ + (op) = (PyAO *)PyArray_SwapAxes((ap), axis, orign); \ + Py_DECREF((ap)); \ + if ((op) == NULL) return NULL; \ + } \ + else (op) = (ap); \ + } + +/* These swap axes in-place if necessary */ +#define SWAPINTP(a,b) {intp c; c=(a); (a) = (b); (b) = c;} +#define SWAPAXES2(ap) { \ + orign = (ap)->nd-1; \ + if (axis != orign) { \ + SWAPINTP(ap->dimensions[axis], ap->dimensions[orign]); \ + SWAPINTP(ap->strides[axis], ap->strides[orign]); \ + PyArray_UpdateFlags(ap, CONTIGUOUS | FORTRAN); \ + } \ + } + +#define SWAPBACK2(ap) { \ + if (axis != orign) { \ + SWAPINTP(ap->dimensions[axis], ap->dimensions[orign]); \ + SWAPINTP(ap->strides[axis], ap->strides[orign]); \ + PyArray_UpdateFlags(ap, CONTIGUOUS | FORTRAN); \ + } \ + } + +/*MULTIARRAY_API + Sort an array in-place +*/ +static int +PyArray_Sort(PyArrayObject *op, int axis, PyArray_SORTKIND which) +{ + PyArrayObject *ap=NULL, *store_arr=NULL; + char *ip; + int i, n, m, elsize, orign; + + n = op->nd; + if ((n==0) || (PyArray_SIZE(op)==1)) return 0; + + if (axis < 0) axis += n; + if ((axis < 0) || (axis >= n)) { + PyErr_Format(PyExc_ValueError, + "axis(=%d) out of bounds", axis); + return -1; + } + if (!PyArray_ISWRITEABLE(op)) { + PyErr_SetString(PyExc_RuntimeError, + "attempted sort on unwriteable array."); + return -1; + } + + /* Determine if we should use type-specific algorithm or not */ + if (op->descr->f->sort[which] != NULL) { + return _new_sort(op, axis, which); + } + + if ((which != PyArray_QUICKSORT) || \ + op->descr->f->compare == NULL) { + PyErr_SetString(PyExc_TypeError, + "desired sort not supported for this type"); + return -1; + } + + SWAPAXES2(op); + + ap = (PyArrayObject *)PyArray_FromAny((PyObject *)op, + NULL, 1, 0, + DEFAULT_FLAGS | UPDATEIFCOPY); + if (ap == NULL) goto fail; + + elsize = ap->descr->elsize; + m = ap->dimensions[ap->nd-1]; + if (m == 0) goto finish; + + n = PyArray_SIZE(ap)/m; + + /* Store global -- allows re-entry -- restore before leaving*/ + store_arr = global_obj; + global_obj = ap; + + for (ip=ap->data, i=0; i<n; i++, ip+=elsize*m) { + qsort(ip, m, elsize, qsortCompare); + } + + global_obj = store_arr; + + if (PyErr_Occurred()) goto fail; + + finish: + Py_DECREF(ap); /* Should update op if needed */ + SWAPBACK2(op); + return 0; + fail: + Py_XDECREF(ap); + SWAPBACK2(op); + return -1; +} + + +static char *global_data; + +static int +argsort_static_compare(const void *ip1, const void *ip2) +{ + int isize = global_obj->descr->elsize; + const intp *ipa = ip1; + const intp *ipb = ip2; + return global_obj->descr->f->compare(global_data + (isize * *ipa), + global_data + (isize * *ipb), + global_obj); +} + +/*MULTIARRAY_API + ArgSort an array +*/ +static PyObject * +PyArray_ArgSort(PyArrayObject *op, int axis, PyArray_SORTKIND which) +{ + PyArrayObject *ap=NULL, *ret, *store; + intp *ip; + intp i, j, n, m, orign; + int argsort_elsize; + char *store_ptr; + + n = op->nd; + if ((n==0) || (PyArray_SIZE(op)==1)) { + ret = (PyArrayObject *)PyArray_New(op->ob_type, op->nd, + op->dimensions, + PyArray_INTP, + NULL, NULL, 0, 0, + (PyObject *)op); + if (ret == NULL) return NULL; + *((intp *)ret->data) = 0; + return (PyObject *)ret; + } + if (axis < 0) axis += n; + if ((axis < 0) || (axis >= n)) { + PyErr_Format(PyExc_ValueError, + "axis(=%d) out of bounds", axis); + return NULL; + } + + /* Determine if we should use new algorithm or not */ + if (op->descr->f->argsort[which] != NULL) { + return _new_argsort(op, axis, which); + } + + if ((which != PyArray_QUICKSORT) || op->descr->f->compare == NULL) { + PyErr_SetString(PyExc_TypeError, + "requested sort not available for type"); + goto fail; + } + + SWAPAXES(ap, op); + + op = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)ap, + PyArray_NOTYPE, + 1, 0); + + if (op == NULL) return NULL; + + ret = (PyArrayObject *)PyArray_New(op->ob_type, op->nd, + op->dimensions, PyArray_INTP, + NULL, NULL, 0, 0, (PyObject *)op); + if (ret == NULL) goto fail; + + + ip = (intp *)ret->data; + argsort_elsize = op->descr->elsize; + m = op->dimensions[op->nd-1]; + if (m == 0) goto finish; + + n = PyArray_SIZE(op)/m; + store_ptr = global_data; + global_data = op->data; + store = global_obj; + global_obj = op; + for (i=0; i<n; i++, ip+=m, global_data += m*argsort_elsize) { + for(j=0; j<m; j++) ip[j] = j; + qsort((char *)ip, m, sizeof(intp), + argsort_static_compare); + } + global_data = store_ptr; + global_obj = store; + + + finish: + Py_DECREF(op); + SWAPBACK(op, ret); + return (PyObject *)op; + + fail: + Py_XDECREF(ap); + Py_XDECREF(ret); + return NULL; + +} + + +/*MULTIARRAY_API + LexSort an array providing indices that will sort a collection of arrays + lexicographically. The first key is sorted on first, followed by the second key + -- requires that arg"merge"sort is available for each sort_key + + Returns an index array that shows the indexes for the lexicographic sort along + the given axis. +*/ +static PyObject * +PyArray_LexSort(PyObject *sort_keys, int axis) +{ + PyArrayObject **mps; + PyArrayIterObject **its; + PyArrayObject *ret=NULL; + PyArrayIterObject *rit=NULL; + int n; + int nd; + int needcopy=0, i,j; + intp N, size; + int elsize; + intp astride, rstride, *iptr; + PyArray_ArgSortFunc *argsort; + + if (!PyTuple_Check(sort_keys) || \ + ((n=PyTuple_GET_SIZE(sort_keys)) <= 0)) { + PyErr_SetString(PyExc_TypeError, + "need tuple of keys with len > 0 in lexsort"); + return NULL; + } + mps = (PyArrayObject **) _pya_malloc(n*sizeof(PyArrayObject)); + if (mps==NULL) return PyErr_NoMemory(); + its = (PyArrayIterObject **) _pya_malloc(n*sizeof(PyArrayIterObject)); + if (its == NULL) {_pya_free(mps); return PyErr_NoMemory();} + for (i=0; i<n; i++) {mps[i] = NULL; its[i] = NULL;} + for (i=0; i<n; i++) { + mps[i] = (PyArrayObject *)PyArray_FROM_O\ + (PyTuple_GET_ITEM(sort_keys, i)); + if (mps[i] == NULL) goto fail; + if (i>0) { + if ((mps[i]->nd != mps[0]->nd) || \ + (!PyArray_CompareLists(mps[i]->dimensions, + mps[0]->dimensions, + mps[0]->nd))) { + PyErr_SetString(PyExc_ValueError, + "all keys need to be the same shape"); + goto fail; + } + } + if (!mps[i]->descr->f->argsort[PyArray_MERGESORT]) { + PyErr_Format(PyExc_TypeError, + "merge sort not available for item %d", i); + goto fail; + } + its[i] = (PyArrayIterObject *)PyArray_IterAllButAxis \ + ((PyObject *)mps[i], axis); + if (its[i]==NULL) goto fail; + } + + /* Now we can check the axis */ + nd = mps[0]->nd; + if ((nd==0) || (PyArray_SIZE(mps[0])==1)) { + ret = (PyArrayObject *)PyArray_New(&PyArray_Type, mps[0]->nd, + mps[0]->dimensions, + PyArray_INTP, + NULL, NULL, 0, 0, NULL); + if (ret == NULL) return NULL; + *((intp *)(ret->data)) = 0; + return (PyObject *)ret; + } + if (axis < 0) axis += nd; + if ((axis < 0) || (axis >= nd)) { + PyErr_Format(PyExc_ValueError, + "axis(=%d) out of bounds", axis); + goto fail; + } + + /* Now do the sorting */ + + ret = (PyArrayObject *)PyArray_New(&PyArray_Type, mps[0]->nd, + mps[0]->dimensions, PyArray_INTP, + NULL, NULL, 0, 0, NULL); + if (ret == NULL) goto fail; + + rit = (PyArrayIterObject *)\ + PyArray_IterAllButAxis((PyObject *)ret, axis); + if (rit == NULL) goto fail; + + size = rit->size; + N = mps[0]->dimensions[axis]; + rstride = PyArray_STRIDE(ret,axis); + + needcopy = (rstride != sizeof(intp)); + for (j=0; j<n && !needcopy; j++) { + needcopy = !(mps[j]->flags & ALIGNED) || \ + (mps[j]->strides[axis] != (intp)mps[j]->descr->elsize); + } + + if (needcopy) { + char *valbuffer, *indbuffer; + valbuffer = PyDataMem_NEW(N*(elsize+sizeof(intp))); + indbuffer = valbuffer + (N*elsize); + while (size--) { + iptr = (intp *)indbuffer; + for (i=0; i<N; i++) *iptr++ = i; + for (j=0; j<n; j++) { + elsize = mps[j]->descr->elsize; + astride = mps[j]->strides[axis]; + argsort = mps[j]->descr->f->argsort[PyArray_MERGESORT]; + _strided_copy(valbuffer, (intp) elsize, its[j]->dataptr, + astride, N, elsize); + if (argsort(valbuffer, (intp *)indbuffer, N, mps[j]) < 0) { + PyDataMem_FREE(valbuffer); goto fail; + } + PyArray_ITER_NEXT(its[j]); + } + _strided_copy(rit->dataptr, rstride, indbuffer, + sizeof(intp), N, sizeof(intp)); + PyArray_ITER_NEXT(rit); + } + PyDataMem_FREE(valbuffer); + } + else { + while (size--) { + iptr = (intp *)rit->dataptr; + for (i=0; i<N; i++) *iptr++ = i; + for (j=0; j<n; j++) { + argsort = mps[j]->descr->f->argsort[PyArray_MERGESORT]; + if (argsort(its[j]->dataptr, (intp *)rit->dataptr, + N, mps[j]) < 0) goto fail; + PyArray_ITER_NEXT(its[j]); + } + PyArray_ITER_NEXT(rit); + } + } + + for (i=0; i<n; i++) {Py_XDECREF(mps[i]); Py_XDECREF(its[i]);} + Py_DECREF(rit); + _pya_free(mps); + _pya_free(its); + return (PyObject *)ret; + + fail: + Py_XDECREF(rit); + Py_XDECREF(ret); + for (i=0; i<n; i++) {Py_XDECREF(mps[i]); Py_XDECREF(its[i]);} + _pya_free(mps); + _pya_free(its); + return NULL; + +} + + +static void +local_where(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject *ret) +{ + PyArray_CompareFunc *compare = ap2->descr->f->compare; + intp min_i, max_i, i, j; + int location, elsize = ap1->descr->elsize; + intp elements = ap1->dimensions[ap1->nd-1]; + intp n = PyArray_SIZE(ap2); + intp *rp = (intp *)ret->data; + char *ip = ap2->data; + char *vp = ap1->data; + + for (j=0; j<n; j++, ip+=elsize, rp++) { + min_i = 0; + max_i = elements; + while (min_i != max_i) { + i = (max_i-min_i)/2 + min_i; + location = compare(ip, vp+elsize*i, ap2); + if (location == 0) { + while (i > 0) { + if (compare(ip, vp+elsize*(--i), ap2) \ + != 0) { + i = i+1; break; + } + } + min_i = i; + break; + } + else if (location < 0) { + max_i = i; + } else { + min_i = i+1; + } + } + *rp = min_i; + } +} + +/*MULTIARRAY_API + Numeric.searchsorted(a,v) +*/ +static PyObject * +PyArray_SearchSorted(PyArrayObject *op1, PyObject *op2) +{ + PyArrayObject *ap1=NULL, *ap2=NULL, *ret=NULL; + int typenum = 0; + + /* + PyObject *args; + args = Py_BuildValue("O",op2); + Py_DELEGATE_ARGS(((PyObject *)op1), searchsorted, args); + Py_XDECREF(args); + */ + + typenum = PyArray_ObjectType((PyObject *)op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + ret = NULL; + ap1 = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)op1, + typenum, + 1, 1); + if (ap1 == NULL) return NULL; + ap2 = (PyArrayObject *)PyArray_ContiguousFromAny(op2, typenum, + 0, 0); + if (ap2 == NULL) goto fail; + + ret = (PyArrayObject *)PyArray_New(ap2->ob_type, ap2->nd, + ap2->dimensions, PyArray_INTP, + NULL, NULL, 0, 0, (PyObject *)ap2); + if (ret == NULL) goto fail; + + if (ap2->descr->f->compare == NULL) { + PyErr_SetString(PyExc_TypeError, + "compare not supported for type"); + goto fail; + } + + local_where(ap1, ap2, ret); + + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + + + +/* Could perhaps be redone to not make contiguous arrays + */ + +/*MULTIARRAY_API + Numeric.innerproduct(a,v) +*/ +static PyObject * +PyArray_InnerProduct(PyObject *op1, PyObject *op2) +{ + PyArrayObject *ap1, *ap2, *ret=NULL; + intp i, j, l, i1, i2, n1, n2; + int typenum; + intp is1, is2, os; + char *ip1, *ip2, *op; + intp dimensions[MAX_DIMS], nd; + PyArray_DotFunc *dot; + PyTypeObject *subtype; + double prior1, prior2; + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + ap1 = (PyArrayObject *)PyArray_ContiguousFromAny(op1, typenum, + 0, 0); + if (ap1 == NULL) return NULL; + ap2 = (PyArrayObject *)PyArray_ContiguousFromAny(op2, typenum, + 0, 0); + if (ap2 == NULL) goto fail; + + if (ap1->nd == 0 || ap2->nd == 0) { + ret = (ap1->nd == 0 ? ap1 : ap2); + ret = (PyArrayObject *)ret->ob_type->tp_as_number->\ + nb_multiply((PyObject *)ap1, (PyObject *)ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + } + + l = ap1->dimensions[ap1->nd-1]; + + if (ap2->dimensions[ap2->nd-1] != l) { + PyErr_SetString(PyExc_ValueError, "matrices are not aligned"); + goto fail; + } + + if (l == 0) n1 = n2 = 0; + else { + n1 = PyArray_SIZE(ap1)/l; + n2 = PyArray_SIZE(ap2)/l; + } + + nd = ap1->nd+ap2->nd-2; + j = 0; + for(i=0; i<ap1->nd-1; i++) { + dimensions[j++] = ap1->dimensions[i]; + } + for(i=0; i<ap2->nd-1; i++) { + dimensions[j++] = ap2->dimensions[i]; + } + + + /* Need to choose an output array that can hold a sum + -- use priority to determine which subtype. + */ + prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); + prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); + subtype = (prior2 > prior1 ? ap2->ob_type : ap1->ob_type); + + ret = (PyArrayObject *)PyArray_New(subtype, nd, dimensions, + typenum, NULL, NULL, 0, 0, + (PyObject *) + (prior2 > prior1 ? ap2 : ap1)); + if (ret == NULL) goto fail; + + dot = (ret->descr->f->dotfunc); + + if (dot == NULL) { + PyErr_SetString(PyExc_ValueError, + "dot not available for this type"); + goto fail; + } + + + is1 = ap1->strides[ap1->nd-1]; + is2 = ap2->strides[ap2->nd-1]; + op = ret->data; os = ret->descr->elsize; + + ip1 = ap1->data; + for(i1=0; i1<n1; i1++) { + ip2 = ap2->data; + for(i2=0; i2<n2; i2++) { + dot(ip1, is1, ip2, is2, op, l, ret); + ip2 += is2*l; + op += os; + } + ip1 += is1*l; + } + if (PyErr_Occurred()) goto fail; + + + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + + +/* just like inner product but does the swapaxes stuff on the fly */ +/*MULTIARRAY_API + Numeric.matrixproduct(a,v) +*/ +static PyObject * +PyArray_MatrixProduct(PyObject *op1, PyObject *op2) +{ + PyArrayObject *ap1, *ap2, *ret=NULL; + intp i, j, l, i1, i2, n1, n2; + int typenum; + intp is1, is2, os; + char *ip1, *ip2, *op; + intp dimensions[MAX_DIMS], nd; + PyArray_DotFunc *dot; + intp matchDim, otherDim, is2r, is1r; + PyTypeObject *subtype; + double prior1, prior2; + PyArray_Descr *typec; + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + typec = PyArray_DescrFromType(typenum); + Py_INCREF(typec); + ap1 = (PyArrayObject *)PyArray_FromAny(op1, typec, 0, 0, + DEFAULT_FLAGS); + if (ap1 == NULL) {Py_DECREF(typec); return NULL;} + ap2 = (PyArrayObject *)PyArray_FromAny(op2, typec, 0, 0, + DEFAULT_FLAGS); + if (ap2 == NULL) goto fail; + + if (ap1->nd == 0 || ap2->nd == 0) { + ret = (ap1->nd == 0 ? ap1 : ap2); + ret = (PyArrayObject *)ret->ob_type->tp_as_number->\ + nb_multiply((PyObject *)ap1, (PyObject *)ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + } + + l = ap1->dimensions[ap1->nd-1]; + if (ap2->nd > 1) { + matchDim = ap2->nd - 2; + otherDim = ap2->nd - 1; + } + else { + matchDim = 0; + otherDim = 0; + } + + if (ap2->dimensions[matchDim] != l) { + PyErr_SetString(PyExc_ValueError, "objects are not aligned"); + goto fail; + } + + if (l == 0) n1 = n2 = 0; + else { + n1 = PyArray_SIZE(ap1)/l; + n2 = PyArray_SIZE(ap2)/l; + } + + nd = ap1->nd+ap2->nd-2; + j = 0; + for(i=0; i<ap1->nd-1; i++) { + dimensions[j++] = ap1->dimensions[i]; + } + for(i=0; i<ap2->nd-2; i++) { + dimensions[j++] = ap2->dimensions[i]; + } + if(ap2->nd > 1) { + dimensions[j++] = ap2->dimensions[ap2->nd-1]; + } + /* + fprintf(stderr, "nd=%d dimensions=", nd); + for(i=0; i<j; i++) + fprintf(stderr, "%d ", dimensions[i]); + fprintf(stderr, "\n"); + */ + + /* Choose which subtype to return */ + prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); + prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); + subtype = (prior2 > prior1 ? ap2->ob_type : ap1->ob_type); + + ret = (PyArrayObject *)PyArray_New(subtype, nd, dimensions, + typenum, NULL, NULL, 0, 0, + (PyObject *) + (prior2 > prior1 ? ap2 : ap1)); + if (ret == NULL) goto fail; + + dot = ret->descr->f->dotfunc; + if (dot == NULL) { + PyErr_SetString(PyExc_ValueError, + "dot not available for this type"); + goto fail; + } + + is1 = ap1->strides[ap1->nd-1]; is2 = ap2->strides[matchDim]; + if(ap1->nd > 1) + is1r = ap1->strides[ap1->nd-2]; + else + is1r = ap1->strides[ap1->nd-1]; + is2r = ap2->strides[otherDim]; + + op = ret->data; os = ret->descr->elsize; + + ip1 = ap1->data; + for(i1=0; i1<n1; i1++) { + ip2 = ap2->data; + for(i2=0; i2<n2; i2++) { + dot(ip1, is1, ip2, is2, op, l, ret); + ip2 += is2r; + op += os; + } + ip1 += is1r; + } + if (PyErr_Occurred()) goto fail; + + + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + +/*MULTIARRAY_API + Fast Copy and Transpose +*/ +static PyObject * +PyArray_CopyAndTranspose(PyObject *op) +{ + PyObject *ret, *arr; + int nd; + intp dims[2]; + intp i,j; + int elsize, str2; + char *iptr; + char *optr; + + /* make sure it is well-behaved */ + arr = PyArray_FromAny(op, NULL, 0, 0, CARRAY_FLAGS); + nd = PyArray_NDIM(arr); + if (nd == 1) { /* we will give in to old behavior */ + ret = PyArray_Copy((PyArrayObject *)arr); + Py_DECREF(arr); + return ret; + } + else if (nd != 2) { + Py_DECREF(arr); + PyErr_SetString(PyExc_ValueError, + "only 2-d arrays are allowed"); + return NULL; + } + + /* Now construct output array */ + dims[0] = PyArray_DIM(arr,1); + dims[1] = PyArray_DIM(arr,0); + elsize = PyArray_ITEMSIZE(arr); + + Py_INCREF(PyArray_DESCR(arr)); + ret = PyArray_NewFromDescr(arr->ob_type, + PyArray_DESCR(arr), + 2, dims, + NULL, NULL, 0, arr); + + if (ret == NULL) { + Py_DECREF(arr); + return NULL; + } + /* do 2-d loop */ + optr = PyArray_DATA(ret); + str2 = elsize*dims[0]; + for (i=0; i<dims[0]; i++) { + iptr = PyArray_DATA(arr) + i*elsize; + for (j=0; j<dims[1]; j++) { + /* optr[i,j] = iptr[j,i] */ + memcpy(optr, iptr, elsize); + optr += elsize; + iptr += str2; + } + } + Py_DECREF(arr); + return ret; +} + +/*MULTIARRAY_API + Numeric.correlate(a1,a2,mode) +*/ +static PyObject * +PyArray_Correlate(PyObject *op1, PyObject *op2, int mode) +{ + PyArrayObject *ap1, *ap2, *ret=NULL; + intp length; + intp i, n1, n2, n, n_left, n_right; + int typenum; + intp is1, is2, os; + char *ip1, *ip2, *op; + PyArray_DotFunc *dot; + PyArray_Descr *typec; + double prior1, prior2; + PyTypeObject *subtype=NULL; + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + typec = PyArray_DescrFromType(typenum); + Py_INCREF(typec); + ap1 = (PyArrayObject *)PyArray_FromAny(op1, typec, 1, 1, + DEFAULT_FLAGS); + if (ap1 == NULL) {Py_DECREF(typec); return NULL;} + ap2 = (PyArrayObject *)PyArray_FromAny(op2, typec, 1, 1, + DEFAULT_FLAGS); + if (ap2 == NULL) goto fail; + + n1 = ap1->dimensions[0]; + n2 = ap2->dimensions[0]; + + if (n1 < n2) { + ret = ap1; ap1 = ap2; ap2 = ret; + ret = NULL; i = n1;n1=n2;n2=i; + } + length = n1; + n = n2; + switch(mode) { + case 0: + length = length-n+1; + n_left = n_right = 0; + break; + case 1: + n_left = (intp)(n/2); + n_right = n-n_left-1; + break; + case 2: + n_right = n-1; + n_left = n-1; + length = length+n-1; + break; + default: + PyErr_SetString(PyExc_ValueError, + "mode must be 0, 1, or 2"); + goto fail; + } + + /* Need to choose an output array that can hold a sum + -- use priority to determine which subtype. + */ + prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); + prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); + subtype = (prior2 > prior1 ? ap2->ob_type : ap1->ob_type); + + ret = (PyArrayObject *)PyArray_New(subtype, 1, + &length, typenum, + NULL, NULL, 0, 0, + (PyObject *) + (prior2 > prior1 ? ap2 : ap1)); + if (ret == NULL) goto fail; + + dot = ret->descr->f->dotfunc; + if (dot == NULL) { + PyErr_SetString(PyExc_ValueError, + "function not available for this data type"); + goto fail; + } + + is1 = ap1->strides[0]; is2 = ap2->strides[0]; + op = ret->data; os = ret->descr->elsize; + + ip1 = ap1->data; ip2 = ap2->data+n_left*is2; + n = n-n_left; + for(i=0; i<n_left; i++) { + dot(ip1, is1, ip2, is2, op, n, ret); + n++; + ip2 -= is2; + op += os; + } + for(i=0; i<(n1-n2+1); i++) { + dot(ip1, is1, ip2, is2, op, n, ret); + ip1 += is1; + op += os; + } + for(i=0; i<n_right; i++) { + n--; + dot(ip1, is1, ip2, is2, op, n, ret); + ip1 += is1; + op += os; + } + if (PyErr_Occurred()) goto fail; + Py_DECREF(ap1); + Py_DECREF(ap2); + return (PyObject *)ret; + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + + +/*MULTIARRAY_API + ArgMin +*/ +static PyObject * +PyArray_ArgMin(PyArrayObject *ap, int axis) +{ + PyObject *obj, *new, *ret; + + if (PyArray_ISFLEXIBLE(ap)) { + PyErr_SetString(PyExc_TypeError, + "argmax is unsupported for this type"); + return NULL; + } + else if (PyArray_ISUNSIGNED(ap)) + obj = PyInt_FromLong((long) -1); + + else if (PyArray_TYPE(ap)==PyArray_BOOL) + obj = PyInt_FromLong((long) 1); + + else + obj = PyInt_FromLong((long) 0); + + new = PyArray_EnsureArray(PyNumber_Subtract(obj, (PyObject *)ap)); + Py_DECREF(obj); + if (new == NULL) return NULL; + ret = PyArray_ArgMax((PyArrayObject *)new, axis); + Py_DECREF(new); + return ret; +} + +/*MULTIARRAY_API + Max +*/ +static PyObject * +PyArray_Max(PyArrayObject *ap, int axis) +{ + PyArrayObject *arr; + PyObject *ret; + + if ((arr=(PyArrayObject *)_check_axis(ap, &axis, 0))==NULL) + return NULL; + ret = PyArray_GenericReduceFunction(arr, n_ops.maximum, axis, + arr->descr->type_num); + Py_DECREF(arr); + return ret; +} + +/*MULTIARRAY_API + Min +*/ +static PyObject * +PyArray_Min(PyArrayObject *ap, int axis) +{ + PyArrayObject *arr; + PyObject *ret; + + if ((arr=(PyArrayObject *)_check_axis(ap, &axis, 0))==NULL) + return NULL; + ret = PyArray_GenericReduceFunction(arr, n_ops.minimum, axis, + arr->descr->type_num); + Py_DECREF(arr); + return ret; +} + +/*MULTIARRAY_API + Ptp +*/ +static PyObject * +PyArray_Ptp(PyArrayObject *ap, int axis) +{ + PyArrayObject *arr; + PyObject *ret; + PyObject *obj1=NULL, *obj2=NULL; + + if ((arr=(PyArrayObject *)_check_axis(ap, &axis, 0))==NULL) + return NULL; + obj1 = PyArray_Max(arr, axis); + if (obj1 == NULL) goto fail; + obj2 = PyArray_Min(arr, axis); + if (obj2 == NULL) goto fail; + Py_DECREF(arr); + ret = PyNumber_Subtract(obj1, obj2); + Py_DECREF(obj1); + Py_DECREF(obj2); + return ret; + + fail: + Py_XDECREF(arr); + Py_XDECREF(obj1); + Py_XDECREF(obj2); + return NULL; +} + + +/*MULTIARRAY_API + ArgMax +*/ +static PyObject * +PyArray_ArgMax(PyArrayObject *op, int axis) +{ + PyArrayObject *ap=NULL, *rp=NULL; + PyArray_ArgFunc* arg_func; + char *ip; + intp *rptr; + intp i, n, orign, m; + int elsize; + + if ((ap=(PyAO *)_check_axis(op, &axis, 0))==NULL) return NULL; + + SWAPAXES(op, ap); + + ap = (PyArrayObject *)\ + PyArray_ContiguousFromAny((PyObject *)op, + PyArray_NOTYPE, 1, 0); + + Py_DECREF(op); + if (ap == NULL) return NULL; + + arg_func = ap->descr->f->argmax; + if (arg_func == NULL) { + PyErr_SetString(PyExc_TypeError, "data type not ordered"); + goto fail; + } + + rp = (PyArrayObject *)PyArray_New(ap->ob_type, ap->nd-1, + ap->dimensions, PyArray_INTP, + NULL, NULL, 0, 0, + (PyObject *)ap); + if (rp == NULL) goto fail; + + + elsize = ap->descr->elsize; + m = ap->dimensions[ap->nd-1]; + if (m == 0) { + PyErr_SetString(MultiArrayError, + "attempt to get argmax/argmin "\ + "of an empty sequence??"); + goto fail; + } + n = PyArray_SIZE(ap)/m; + rptr = (intp *)rp->data; + for (ip = ap->data, i=0; i<n; i++, ip+=elsize*m) { + arg_func(ip, m, rptr, ap); + rptr += 1; + } + Py_DECREF(ap); + + SWAPBACK(op, rp); /* op now contains the return */ + + return (PyObject *)op; + + fail: + Py_DECREF(ap); + Py_XDECREF(rp); + return NULL; +} + + +/*MULTIARRAY_API + Take +*/ +static PyObject * +PyArray_Take(PyArrayObject *self0, PyObject *indices0, int axis) +{ + PyArrayObject *self, *indices, *ret; + intp nd, i, j, n, m, max_item, tmp, chunk; + intp shape[MAX_DIMS]; + char *src, *dest; + + indices = ret = NULL; + self = (PyAO *)_check_axis(self0, &axis, CARRAY_FLAGS); + if (self == NULL) return NULL; + + indices = (PyArrayObject *)PyArray_ContiguousFromAny(indices0, + PyArray_INTP, + 1, 0); + if (indices == NULL) goto fail; + + n = m = chunk = 1; + nd = self->nd + indices->nd - 1; + for (i=0; i< nd; i++) { + if (i < axis) { + shape[i] = self->dimensions[i]; + n *= shape[i]; + } else { + if (i < axis+indices->nd) { + shape[i] = indices->dimensions[i-axis]; + m *= shape[i]; + } else { + shape[i] = self->dimensions[i-indices->nd+1]; + chunk *= shape[i]; + } + } + } + Py_INCREF(self->descr); + ret = (PyArrayObject *)PyArray_NewFromDescr(self->ob_type, + self->descr, + nd, shape, + NULL, NULL, 0, + (PyObject *)self); + + if (ret == NULL) goto fail; + + max_item = self->dimensions[axis]; + chunk = chunk * ret->descr->elsize; + src = self->data; + dest = ret->data; + + for(i=0; i<n; i++) { + for(j=0; j<m; j++) { + tmp = ((intp *)(indices->data))[j]; + if (tmp < 0) tmp = tmp+max_item; + if ((tmp < 0) || (tmp >= max_item)) { + PyErr_SetString(PyExc_IndexError, + "index out of range for "\ + "array"); + goto fail; + } + memmove(dest, src+tmp*chunk, chunk); + dest += chunk; + } + src += chunk*max_item; + } + + PyArray_INCREF(ret); + + Py_XDECREF(indices); + Py_XDECREF(self); + + return (PyObject *)ret; + + + fail: + Py_XDECREF(ret); + Py_XDECREF(indices); + Py_XDECREF(self); + return NULL; +} + +/*MULTIARRAY_API + Put values into an array +*/ +static PyObject * +PyArray_Put(PyArrayObject *self, PyObject* values0, PyObject *indices0) +{ + PyArrayObject *indices, *values; + int i, chunk, ni, max_item, nv, tmp, thistype; + char *src, *dest; + + indices = NULL; + values = NULL; + + if (!PyArray_Check(self)) { + PyErr_SetString(PyExc_TypeError, "put: first argument must be an array"); + return NULL; + } + if (!PyArray_ISCONTIGUOUS(self)) { + PyErr_SetString(PyExc_ValueError, "put: first argument must be contiguous"); + return NULL; + } + max_item = PyArray_SIZE(self); + dest = self->data; + chunk = self->descr->elsize; + + indices = (PyArrayObject *)PyArray_ContiguousFromAny(indices0, PyArray_INTP, 0, 0); + if (indices == NULL) goto fail; + ni = PyArray_SIZE(indices); + + thistype = self->descr->type_num; + values = (PyArrayObject *)\ + PyArray_ContiguousFromAny(values0, thistype, 0, 0); + if (values == NULL) goto fail; + nv = PyArray_SIZE(values); + if (nv > 0) { /* nv == 0 for a null array */ + if (thistype == PyArray_OBJECT) { + for(i=0; i<ni; i++) { + src = values->data + chunk * (i % nv); + tmp = ((intp *)(indices->data))[i]; + if (tmp < 0) tmp = tmp+max_item; + if ((tmp < 0) || (tmp >= max_item)) { + PyErr_SetString(PyExc_IndexError, "index out of range for array"); + goto fail; + } + Py_INCREF(*((PyObject **)src)); + Py_XDECREF(*((PyObject **)(dest+tmp*chunk))); + memmove(dest + tmp * chunk, src, chunk); + } + } + else { + for(i=0; i<ni; i++) { + src = values->data + chunk * (i % nv); + tmp = ((intp *)(indices->data))[i]; + if (tmp < 0) tmp = tmp+max_item; + if ((tmp < 0) || (tmp >= max_item)) { + PyErr_SetString(PyExc_IndexError, "index out of range for array"); + goto fail; + } + memmove(dest + tmp * chunk, src, chunk); + } + } + + } + + Py_XDECREF(values); + Py_XDECREF(indices); + Py_INCREF(Py_None); + return Py_None; + + fail: + Py_XDECREF(indices); + Py_XDECREF(values); + return NULL; +} + +/*MULTIARRAY_API + Put values into an array according to a mask. +*/ +static PyObject * +PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) +{ + PyArrayObject *mask, *values; + int i, chunk, ni, max_item, nv, tmp, thistype; + char *src, *dest; + + mask = NULL; + values = NULL; + + if (!PyArray_Check(self)) { + PyErr_SetString(PyExc_TypeError, + "putmask: first argument must "\ + "be an array"); + return NULL; + } + if (!PyArray_ISCONTIGUOUS(self)) { + PyErr_SetString(PyExc_ValueError, + "putmask: first argument must be contiguous"); + return NULL; + } + + max_item = PyArray_SIZE(self); + dest = self->data; + chunk = self->descr->elsize; + + mask = (PyArrayObject *)\ + PyArray_FROM_OTF(mask0, PyArray_BOOL, CARRAY_FLAGS | FORCECAST); + if (mask == NULL) goto fail; + ni = PyArray_SIZE(mask); + if (ni != max_item) { + PyErr_SetString(PyExc_ValueError, + "putmask: mask and data must be "\ + "the same size"); + goto fail; + } + + thistype = self->descr->type_num; + values = (PyArrayObject *)\ + PyArray_ContiguousFromAny(values0, thistype, 0, 0); + if (values == NULL) goto fail; + nv = PyArray_SIZE(values); /* zero if null array */ + if (nv > 0) { + if (thistype == PyArray_OBJECT) { + for(i=0; i<ni; i++) { + src = values->data + chunk * (i % nv); + tmp = ((Bool *)(mask->data))[i]; + if (tmp) { + Py_INCREF(*((PyObject **)src)); + Py_XDECREF(*((PyObject **)(dest+i*chunk))); + memmove(dest + i * chunk, src, chunk); + } + } + } + else { + for(i=0; i<ni; i++) { + src = values->data + chunk * (i % nv); + tmp = ((Bool *)(mask->data))[i]; + if (tmp) memmove(dest + i * chunk, src, chunk); + } + } + } + + Py_XDECREF(values); + Py_XDECREF(mask); + Py_INCREF(Py_None); + return Py_None; + + fail: + Py_XDECREF(mask); + Py_XDECREF(values); + return NULL; +} + + +/* This conversion function can be used with the "O&" argument for + PyArg_ParseTuple. It will immediately return an object of array type + or will convert to a CARRAY any other object. + + If you use PyArray_Converter, you must DECREF the array when finished + as you get a new reference to it. +*/ + +/*MULTIARRAY_API + Useful to pass as converter function for O& processing in + PyArgs_ParseTuple. +*/ +static int +PyArray_Converter(PyObject *object, PyObject **address) +{ + if (PyArray_Check(object)) { + *address = object; + Py_INCREF(object); + return PY_SUCCEED; + } + else { + *address = PyArray_FromAny(object, NULL, 0, 0, CARRAY_FLAGS); + if (*address == NULL) return PY_FAIL; + return PY_SUCCEED; + } +} + +/*MULTIARRAY_API + Convert an object to true / false +*/ +static int +PyArray_BoolConverter(PyObject *object, Bool *val) +{ + if (PyObject_IsTrue(object)) + *val=TRUE; + else *val=FALSE; + if (PyErr_Occurred()) + return PY_FAIL; + return PY_SUCCEED; +} + + +/*MULTIARRAY_API + Typestr converter +*/ +static int +PyArray_TypestrConvert(int itemsize, int gentype) +{ + register int newtype = gentype; + + if (gentype == PyArray_GENBOOLLTR) { + if (itemsize == 1) + newtype = PyArray_BOOL; + else + newtype = PyArray_NOTYPE; + } + else if (gentype == PyArray_SIGNEDLTR) { + switch(itemsize) { + case 1: + newtype = PyArray_INT8; + break; + case 2: + newtype = PyArray_INT16; + break; + case 4: + newtype = PyArray_INT32; + break; + case 8: + newtype = PyArray_INT64; + break; +#ifdef PyArray_INT128 + case 16: + newtype = PyArray_INT128; + break; +#endif + default: + newtype = PyArray_NOTYPE; + } + } + + else if (gentype == PyArray_UNSIGNEDLTR) { + switch(itemsize) { + case 1: + newtype = PyArray_UINT8; + break; + case 2: + newtype = PyArray_UINT16; + break; + case 4: + newtype = PyArray_UINT32; + break; + case 8: + newtype = PyArray_UINT64; + break; +#ifdef PyArray_INT128 + case 16: + newtype = PyArray_UINT128; + break; +#endif + default: + newtype = PyArray_NOTYPE; + break; + } + } + else if (gentype == PyArray_FLOATINGLTR) { + switch(itemsize) { + case 4: + newtype = PyArray_FLOAT32; + break; + case 8: + newtype = PyArray_FLOAT64; + break; +#ifdef PyArray_FLOAT80 + case 10: + newtype = PyArray_FLOAT80; + break; +#endif +#ifdef PyArray_FLOAT96 + case 12: + newtype = PyArray_FLOAT96; + break; +#endif +#ifdef PyArray_FLOAT128 + case 16: + newtype = PyArray_FLOAT128; + break; +#endif + default: + newtype = PyArray_NOTYPE; + } + } + + else if (gentype == PyArray_COMPLEXLTR) { + switch(itemsize) { + case 8: + newtype = PyArray_COMPLEX64; + break; + case 16: + newtype = PyArray_COMPLEX128; + break; +#ifdef PyArray_FLOAT80 + case 20: + newtype = PyArray_COMPLEX160; + break; +#endif +#ifdef PyArray_FLOAT96 + case 24: + newtype = PyArray_COMPLEX192; + break; +#endif +#ifdef PyArray_FLOAT128 + case 32: + newtype = PyArray_COMPLEX256; + break; +#endif + default: + newtype = PyArray_NOTYPE; + } + } + + return newtype; +} + + +/* this function takes a Python object which exposes the (single-segment) + buffer interface and returns a pointer to the data segment + + You should increment the reference count by one of buf->base + if you will hang on to a reference + + You only get a borrowed reference to the object. Do not free the + memory... +*/ + + +/*MULTIARRAY_API + Get buffer chunk from object +*/ +static int +PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) +{ + int buflen; + + buf->ptr = NULL; + buf->flags = BEHAVED_FLAGS; + buf->base = NULL; + + if (obj == Py_None) + return PY_SUCCEED; + + if (PyObject_AsWriteBuffer(obj, &(buf->ptr), &buflen) < 0) { + PyErr_Clear(); + buf->flags &= ~WRITEABLE; + if (PyObject_AsReadBuffer(obj, (const void **)&(buf->ptr), + &buflen) < 0) + return PY_FAIL; + } + buf->len = (intp) buflen; + + /* Point to the base of the buffer object if present */ + if (PyBuffer_Check(obj)) buf->base = ((PyArray_Chunk *)obj)->base; + if (buf->base == NULL) buf->base = obj; + + return PY_SUCCEED; +} + + + +/* This function takes a Python sequence object and allocates and + fills in an intp array with the converted values. + + **Remember to free the pointer seq.ptr when done using + PyDimMem_FREE(seq.ptr)** +*/ + +/*MULTIARRAY_API + Get intp chunk from sequence +*/ +static int +PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) +{ + int len; + int nd; + + seq->ptr = NULL; + if (obj == Py_None) return PY_SUCCEED; + len = PySequence_Size(obj); + if (len == -1) { /* Check to see if it is a number */ + if (PyNumber_Check(obj)) len = 1; + } + if (len < 0) { + PyErr_SetString(PyExc_TypeError, + "expected sequence object with len >= 0"); + return PY_FAIL; + } + if (len > MAX_DIMS) { + PyErr_Format(PyExc_ValueError, "sequence too large; " \ + "must be smaller than %d", MAX_DIMS); + return PY_FAIL; + } + if (len > 0) { + seq->ptr = PyDimMem_NEW(len); + if (seq->ptr == NULL) { + PyErr_NoMemory(); + return PY_FAIL; + } + } + seq->len = len; + nd = PyArray_IntpFromSequence(obj, (intp *)seq->ptr, len); + if (nd == -1 || nd != len) { + PyDimMem_FREE(seq->ptr); + seq->ptr=NULL; + return PY_FAIL; + } + return PY_SUCCEED; +} + + +/* A tuple type would be either (generic typeobject, typesize) + or (fixed-length data-type, shape) + + or (inheriting data-type, new-data-type) + The new data-type must have the same itemsize as the inheriting data-type + unless the latter is 0 + + Thus (int32, {'real':(int16,0),'imag',(int16,2)}) + + is one way to specify a descriptor that will give + a['real'] and a['imag'] to an int32 array. +*/ + +/* leave type reference alone */ +static PyArray_Descr * +_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag) +{ + PyArray_Descr *new; + PyArray_Descr *conv; + + *errflag = 0; + if (!PyArray_DescrConverter(newobj, &conv)) { + return NULL; + } + *errflag = 1; + if (type == &OBJECT_Descr) { + PyErr_SetString(PyExc_ValueError, + "cannot base a new descriptor on an"\ + " OBJECT descriptor."); + return NULL; + } + new = PyArray_DescrNew(type); + if (new == NULL) return NULL; + + if (new->elsize && new->elsize != conv->elsize) { + PyErr_SetString(PyExc_ValueError, + "mismatch in size of old"\ + "and new data-descriptor"); + return NULL; + } + new->elsize = conv->elsize; + if (conv->fields != Py_None) { + new->fields = conv->fields; + Py_XINCREF(new->fields); + } + Py_DECREF(conv); + *errflag = 0; + return new; +} + +static PyArray_Descr * +_convert_from_tuple(PyObject *obj) +{ + PyArray_Descr *type, *res; + PyObject *val; + int errflag; + + if (PyTuple_GET_SIZE(obj) != 2) return NULL; + + if (!PyArray_DescrConverter(PyTuple_GET_ITEM(obj,0), &type)) + return NULL; + val = PyTuple_GET_ITEM(obj,1); + /* try to interpret next item as a type */ + res = _use_inherit(type, val, &errflag); + if (res || errflag) { + Py_DECREF(type); + if (res) return res; + else return NULL; + } + PyErr_Clear(); + /* We get here if res was NULL but errflag wasn't set + --- i.e. the conversion to a data-descr failed in _use_inherit + */ + + if (type->elsize == 0) { /* interpret next item as a typesize */ + int itemsize; + itemsize = PyArray_PyIntAsInt(PyTuple_GET_ITEM(obj,1)); + if (error_converting(itemsize)) { + PyErr_SetString(PyExc_ValueError, + "invalid itemsize in generic type "\ + "tuple"); + goto fail; + } + PyArray_DESCR_REPLACE(type); + type->elsize = itemsize; + } + else { + /* interpret next item as shape (if it's a tuple) + and reset the type to PyArray_VOID with + anew fields attribute. + */ + PyArray_Dims shape={NULL,-1}; + PyArray_Descr *newdescr; + if (!(PyArray_IntpConverter(val, &shape)) || + (shape.len > MAX_DIMS)) { + PyDimMem_FREE(shape.ptr); + PyErr_SetString(PyExc_ValueError, + "invalid shape in fixed-type tuple."); + goto fail; + } + newdescr = PyArray_DescrNewFromType(PyArray_VOID); + if (newdescr == NULL) {PyDimMem_FREE(shape.ptr); goto fail;} + newdescr->elsize = type->elsize; + newdescr->elsize *= PyArray_MultiplyList(shape.ptr, + shape.len); + PyDimMem_FREE(shape.ptr); + newdescr->subarray = _pya_malloc(sizeof(PyArray_ArrayDescr)); + newdescr->subarray->base = type; + Py_INCREF(val); + newdescr->subarray->shape = val; + Py_XDECREF(newdescr->fields); + newdescr->fields = NULL; + type = newdescr; + } + return type; + + fail: + Py_XDECREF(type); + return NULL; +} + +/* obj is a list. Each item is a tuple with + +(field-name, data-type (either a list or a string), and an optional + shape parameter). +*/ +static PyArray_Descr * +_convert_from_array_descr(PyObject *obj) +{ + int n, i, totalsize; + int ret; + PyObject *fields, *item, *newobj; + PyObject *name, *key, *tup; + PyObject *nameslist; + PyArray_Descr *new; + PyArray_Descr *conv; + + n = PyList_GET_SIZE(obj); + nameslist = PyList_New(n); + if (!nameslist) return NULL; + totalsize = 0; + fields = PyDict_New(); + for (i=0; i<n; i++) { + item = PyList_GET_ITEM(obj, i); + if (!PyTuple_Check(item) || (PyTuple_GET_SIZE(item) < 2) || \ + !PyString_Check((name = PyTuple_GET_ITEM(item,0)))) + goto fail; + if (PyString_GET_SIZE(name)==0) { + name = PyString_FromFormat("f%d", i); + } + else { + Py_INCREF(name); + } + PyList_SET_ITEM(nameslist, i, name); + if (PyTuple_GET_SIZE(item) == 2) { + ret = PyArray_DescrConverter(PyTuple_GET_ITEM(item, 1), + &conv); + if (ret == PY_FAIL) + PyObject_Print(PyTuple_GET_ITEM(item,1), + stderr, 0); + } + else if (PyTuple_GET_SIZE(item) == 3) { + newobj = PyTuple_GetSlice(item, 1, 3); + ret = PyArray_DescrConverter(newobj, &conv); + Py_DECREF(newobj); + } + else goto fail; + if (ret == PY_FAIL) goto fail; + tup = PyTuple_New(2); + PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); + PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); + totalsize += conv->elsize; + PyDict_SetItem(fields, name, tup); + Py_DECREF(tup); + } + key = PyInt_FromLong(-1); + PyDict_SetItem(fields, key, nameslist); + Py_DECREF(key); + Py_DECREF(nameslist); + new = PyArray_DescrNewFromType(PyArray_VOID); + new->fields = fields; + new->elsize = totalsize; + return new; + + fail: + Py_DECREF(fields); + Py_DECREF(nameslist); + return NULL; + +} + +/* a list specifying a data-type can just be + a list of formats. The names for the fields + will default to f1, f2, f3, and so forth. + + or it can be an array_descr format string -- in which case + align must be 0. +*/ + +static PyArray_Descr * +_convert_from_list(PyObject *obj, int align, int try_descr) +{ + int n, i; + int totalsize; + PyObject *fields; + PyArray_Descr *conv=NULL; + PyArray_Descr *new; + PyObject *key, *tup; + PyObject *nameslist=NULL; + int ret; + int maxalign=0; + + n = PyList_GET_SIZE(obj); + totalsize = 0; + if (n==0) return NULL; + nameslist = PyList_New(n); + if (!nameslist) return NULL; + fields = PyDict_New(); + for (i=0; i<n; i++) { + tup = PyTuple_New(2); + key = PyString_FromFormat("f%d", i+1); + ret = PyArray_DescrConverter(PyList_GET_ITEM(obj, i), &conv); + PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); + PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); + PyDict_SetItem(fields, key, tup); + Py_DECREF(tup); + PyList_SET_ITEM(nameslist, i, key); + if (ret == PY_FAIL) goto fail; + totalsize += conv->elsize; + if (align) { + int _align; + _align = conv->alignment; + if (_align > 1) totalsize = \ + ((totalsize + _align - 1)/_align)*_align; + maxalign = MAX(maxalign, _align); + } + } + key = PyInt_FromLong(-1); + PyDict_SetItem(fields, key, nameslist); + Py_DECREF(key); + Py_DECREF(nameslist); + new = PyArray_DescrNewFromType(PyArray_VOID); + new->fields = fields; + if (maxalign > 1) { + totalsize = ((totalsize+maxalign-1)/maxalign)*maxalign; + } + if (align) new->alignment = maxalign; + new->elsize = totalsize; + return new; + + fail: + Py_DECREF(nameslist); + Py_DECREF(fields); + if (!try_descr) return NULL; + if (align) { + PyErr_SetString(PyExc_ValueError, + "failed to convert from list of formats "\ + "and align cannot be 1 for conversion from "\ + "array_descr structure"); + return NULL; + } + PyErr_Clear(); + return _convert_from_array_descr(obj); +} + + +/* comma-separated string */ +/* this is the format developed by the numarray records module */ +/* and implemented by the format parser in that module */ +/* this is an alternative implementation found in the _internal.py + file patterned after that one -- the approach is to try to convert + to a list (with tuples if any repeat information is present) + and then call the _convert_from_list) +*/ + +static PyArray_Descr * +_convert_from_commastring(PyObject *obj, int align) +{ + PyObject *listobj; + PyArray_Descr *res; + + if (!PyString_Check(obj)) return NULL; + listobj = PyObject_CallMethod(_scipy_internal, "_commastring", + "O", obj); + if (!listobj) return NULL; + res = _convert_from_list(listobj, align, 0); + Py_DECREF(listobj); + if (!res && !PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "invalid data-type"); + return NULL; + } + return res; +} + + + +/* a dictionary specifying a data-type + must have at least two and up to four + keys These must all be sequences of the same length. + + "names" --- field names + "formats" --- the data-type descriptors for the field. + + Optional: + + "offsets" --- integers indicating the offset into the + record of the start of the field. + if not given, then "consecutive offsets" + will be assumed and placed in the dictionary. + + "titles" --- Allows the use of an additional key + for the fields dictionary. + +Attribute-lookup-based field names merely has to query the fields +dictionary of the data-descriptor. Any result present can be used +to return the correct field. + +So, the notion of what is a name and what is a title is really quite +arbitrary. + +What does distinguish a title, however, is that if it is not None, +it will be placed at the end of the tuple inserted into the +fields dictionary. + +If the dictionary does not have "names" and "formats" entries, +then it will be checked for conformity and used directly. +*/ + +static PyArray_Descr * +_use_fields_dict(PyObject *obj, int align) +{ + return (PyArray_Descr *)PyObject_CallMethod(_scipy_internal, + "_usefields", + "Oi", obj, align); +} + +static PyArray_Descr * +_convert_from_dict(PyObject *obj, int align) +{ + PyArray_Descr *new; + PyObject *fields=NULL; + PyObject *names, *offsets, *descrs, *titles, *key; + int n, i; + int totalsize; + int maxalign=0; + + fields = PyDict_New(); + if (fields == NULL) return (PyArray_Descr *)PyErr_NoMemory(); + + names = PyDict_GetItemString(obj, "names"); + descrs = PyDict_GetItemString(obj, "formats"); + + if (!names || !descrs) { + Py_DECREF(fields); + return _use_fields_dict(obj, align); + } + n = PyObject_Length(names); + offsets = PyDict_GetItemString(obj, "offsets"); + titles = PyDict_GetItemString(obj, "titles"); + if ((n > PyObject_Length(descrs)) || \ + (offsets && (n > PyObject_Length(offsets))) || \ + (titles && (n > PyObject_Length(titles)))) { + PyErr_SetString(PyExc_ValueError, + "all items in the dictionary must have" \ + " the same length."); + goto fail; + } + + totalsize = 0; + for(i=0; i<n; i++) { + PyObject *tup, *descr, *index, *item, *name, *off; + int len, ret; + PyArray_Descr *newdescr; + + /* Build item to insert (descr, offset, [title])*/ + len = 2; + item = NULL; + index = PyInt_FromLong(i); + if (titles) { + item=PyObject_GetItem(titles, index); + if (item && item != Py_None) len = 3; + else Py_XDECREF(item); + PyErr_Clear(); + } + tup = PyTuple_New(len); + descr = PyObject_GetItem(descrs, index); + ret = PyArray_DescrConverter(descr, &newdescr); + Py_DECREF(descr); + PyTuple_SET_ITEM(tup, 0, (PyObject *)newdescr); + if (offsets) { + long offset; + off = PyObject_GetItem(offsets, index); + offset = PyInt_AsLong(off); + PyTuple_SET_ITEM(tup, 1, off); + if (offset < totalsize) { + PyErr_SetString(PyExc_ValueError, + "invalid offset (must be "\ + "ordered)"); + ret = PY_FAIL; + } + if (offset > totalsize) totalsize = offset; + } + else + PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(totalsize)); + if (len == 3) PyTuple_SET_ITEM(tup, 2, item); + name = PyObject_GetItem(names, index); + Py_DECREF(index); + + /* Insert into dictionary */ + PyDict_SetItem(fields, name, tup); + Py_DECREF(name); + if (len == 3) PyDict_SetItem(fields, item, tup); + Py_DECREF(tup); + if ((ret == PY_FAIL) || (newdescr->elsize == 0)) goto fail; + totalsize += newdescr->elsize; + if (align) { + int _align = newdescr->alignment; + if (_align > 1) totalsize = \ + ((totalsize + _align - 1)/_align)*_align; + maxalign = MAX(maxalign,_align); + } + } + + new = PyArray_DescrNewFromType(PyArray_VOID); + if (new == NULL) goto fail; + if (maxalign > 1) + totalsize = ((totalsize + maxalign - 1)/maxalign)*maxalign; + if (align) new->alignment = maxalign; + new->elsize = totalsize; + key = PyInt_FromLong(-1); + PyDict_SetItem(fields, key, names); + Py_DECREF(key); + new->fields = fields; + return new; + + fail: + Py_XDECREF(fields); + return NULL; +} + +/* + any object with + the .fields attribute and/or .itemsize attribute + (if the .fields attribute does not give + the total size -- i.e. a partial record naming). + If itemsize is given it must be >= size computed from fields + + The .fields attribute must return a convertible dictionary if + present. Result inherits from PyArray_VOID. +*/ + + +/*MULTIARRAY_API + Get typenum from an object -- None goes to NULL +*/ +static int +PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at) +{ + if (obj == Py_None) { + *at = NULL; + return PY_SUCCEED; + } + else return PyArray_DescrConverter(obj, at); +} + +/* This function takes a Python object representing a type and converts it + to a the correct PyArray_Descr * structure to describe the type. + + Many objects can be used to represent a data-type which in SciPy is + quite a flexible concept. + + This is the central code that converts Python objects to + Type-descriptor objects that are used throughout scipy. + */ + +/* new reference in *at */ +/*MULTIARRAY_API + Get typenum from an object -- None goes to &LONG_descr +*/ +static int +PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at) +{ + char *type; + int check_num=PyArray_NOTYPE+10; + int len; + PyObject *item; + int elsize = 0; + char endian = '='; + + *at=NULL; + + /* default */ + if (obj == Py_None) { + *at = PyArray_DescrFromType(PyArray_LONG); + return PY_SUCCEED; + } + + if (PyArray_DescrCheck(obj)) { + *at = (PyArray_Descr *)obj; + Py_INCREF(*at); + return PY_SUCCEED; + } + + if (PyType_Check(obj)) { + if (PyType_IsSubtype((PyTypeObject *)obj, + &PyGenericArrType_Type)) { + *at = PyArray_DescrFromTypeObject(obj); + if (*at) return PY_SUCCEED; + else return PY_FAIL; + } + check_num = PyArray_OBJECT; + if (obj == (PyObject *)(&PyInt_Type)) + check_num = PyArray_LONG; + else if (obj == (PyObject *)(&PyLong_Type)) + check_num = PyArray_LONGLONG; + else if (obj == (PyObject *)(&PyFloat_Type)) + check_num = PyArray_DOUBLE; + else if (obj == (PyObject *)(&PyComplex_Type)) + check_num = PyArray_CDOUBLE; + else if (obj == (PyObject *)(&PyBool_Type)) + check_num = PyArray_BOOL; + else if (obj == (PyObject *)(&PyString_Type)) + check_num = PyArray_STRING; + else if (obj == (PyObject *)(&PyUnicode_Type)) + check_num = PyArray_UNICODE; + else if (obj == (PyObject *)(&PyBuffer_Type)) + check_num = PyArray_VOID; + else { + *at = _arraydescr_fromobj(obj); + if (*at) return PY_SUCCEED; + } + goto finish; + } + + /* or a typecode string */ + + if (PyString_Check(obj)) { + /* Check for a string typecode. */ + type = PyString_AS_STRING(obj); + len = PyString_GET_SIZE(obj); + if (len <= 0) goto fail; + check_num = (int) type[0]; + if ((char) check_num == '>' || (char) check_num == '<' || \ + (char) check_num == '|') { + if (len <= 1) goto fail; + endian = (char) check_num; + type++; len--; + check_num = (int) type[0]; + if (endian == '|') endian = '='; + } + if (len > 1) { + elsize = atoi(type+1); + if (len > 2 && elsize < 10) { + /* perhaps commas present */ + int i; + for (i=1;i<len && type[i]!=',';i++); + if (i < len) { + /* see if it can be converted from + a comma-separated string */ + *at = _convert_from_commastring(obj, + 0); + if (*at) return PY_SUCCEED; + else return PY_FAIL; + } + } + if (elsize == 0) { + check_num = PyArray_NOTYPE+10; + } + /* When specifying length of UNICODE + the number of characters is given to match + the STRING interface. Each character can be + more than one byte and itemsize must be + the number of bytes. + */ + else if (check_num == PyArray_UNICODELTR) { + elsize *= sizeof(Py_UNICODE); + } + /* Support for generic processing + c4, i4, f8, etc... + */ + else if ((check_num != PyArray_STRINGLTR) && + (check_num != PyArray_VOIDLTR) && \ + (check_num != PyArray_STRINGLTR2)) { + check_num = \ + PyArray_TypestrConvert(elsize, + check_num); + if (check_num == PyArray_NOTYPE) + check_num += 10; + elsize = 0; + } + } + } + /* or a tuple */ + else if (PyTuple_Check(obj)) { + *at = _convert_from_tuple(obj); + if (*at == NULL){ + if (PyErr_Occurred()) return PY_FAIL; + goto fail; + } + return PY_SUCCEED; + } + /* or a list */ + else if (PyList_Check(obj)) { + *at = _convert_from_list(obj,0,1); + if (*at == NULL) { + if (PyErr_Occurred()) return PY_FAIL; + goto fail; + } + return PY_SUCCEED; + } + /* or a dictionary */ + else if (PyDict_Check(obj)) { + *at = _convert_from_dict(obj,0); + if (*at == NULL) { + if (PyErr_Occurred()) return PY_FAIL; + goto fail; + } + return PY_SUCCEED; + } + else { + *at = _arraydescr_fromobj(obj); + if (*at) return PY_SUCCEED; + if (PyErr_Occurred()) return PY_FAIL; + goto fail; + } + if (PyErr_Occurred()) goto fail; + + /* + if (check_num == PyArray_NOTYPE) return PY_FAIL; + */ + + finish: + if ((check_num == PyArray_NOTYPE+10) || \ + (*at = PyArray_DescrFromType(check_num))==NULL) { + /* Now check to see if the object is registered + in typeDict */ + if (typeDict != NULL) { + item = PyDict_GetItem(typeDict, obj); + if (item) return PyArray_DescrConverter(item, at); + } + goto fail; + } + + if (((*at)->elsize == 0) && (elsize != 0)) { + PyArray_DESCR_REPLACE(*at); + (*at)->elsize = elsize; + } + if (endian != '=' && PyArray_ISNBO(endian)) endian = '='; + + if (endian != '=' && (*at)->byteorder != '|' && \ + (*at)->byteorder != endian) { + PyArray_DESCR_REPLACE(*at); + (*at)->byteorder = endian; + } + + return PY_SUCCEED; + + fail: + PyErr_SetString(PyExc_TypeError, + "data type not understood"); + *at=NULL; + return PY_FAIL; +} + +/*MULTIARRAY_API + Convert object to endian +*/ +static int +PyArray_ByteorderConverter(PyObject *obj, char *endian) +{ + char *str; + *endian = PyArray_SWAP; + str = PyString_AsString(obj); + if (!str) return PY_FAIL; + if (strlen(str) < 1) { + PyErr_SetString(PyExc_ValueError, + "Byteorder string must be at least length 1"); + return PY_FAIL; + } + *endian = str[0]; + if (str[0] != PyArray_BIG && str[0] != PyArray_LITTLE && \ + str[0] != PyArray_NATIVE) { + if (str[0] == 'b' || str[0] == 'B') + *endian = PyArray_BIG; + else if (str[0] == 'l' || str[0] == 'L') + *endian = PyArray_LITTLE; + else if (str[0] == 'n' || str[0] == 'N') + *endian = PyArray_NATIVE; + else if (str[0] == 'i' || str[0] == 'I') + *endian = PyArray_IGNORE; + else if (str[0] == 's' || str[0] == 'S') + *endian = PyArray_SWAP; + else { + PyErr_Format(PyExc_ValueError, + "%s is an unrecognized byteorder", + str); + return PY_FAIL; + } + } + return PY_SUCCEED; +} + +/*MULTIARRAY_API + Convert object to sort kind +*/ +static int +PyArray_SortkindConverter(PyObject *obj, PyArray_SORTKIND *sortkind) +{ + char *str; + *sortkind = PyArray_QUICKSORT; + str = PyString_AsString(obj); + if (!str) return PY_FAIL; + if (strlen(str) < 1) { + PyErr_SetString(PyExc_ValueError, + "Sort kind string must be at least length 1"); + return PY_FAIL; + } + if (str[0] == 'q' || str[0] == 'Q') + *sortkind = PyArray_QUICKSORT; + else if (str[0] == 'h' || str[0] == 'H') + *sortkind = PyArray_HEAPSORT; + else if (str[0] == 'm' || str[0] == 'M') + *sortkind = PyArray_MERGESORT; + else if (str[0] == 't' || str[0] == 'T') + *sortkind = PyArray_TIMSORT; + else { + PyErr_Format(PyExc_ValueError, + "%s is an unrecognized kind of sort", + str); + return PY_FAIL; + } + return PY_SUCCEED; +} + + +/* This function returns true if the two typecodes are + equivalent (same basic kind and same itemsize). +*/ + +/*MULTIARRAY_API*/ +static Bool +PyArray_EquivTypes(PyArray_Descr *typ1, PyArray_Descr *typ2) +{ + register int typenum1=typ1->type_num; + register int typenum2=typ2->type_num; + register int size1=typ1->elsize; + register int size2=typ2->elsize; + + if (size1 != size2) return FALSE; + if (typ1->fields != typ2->fields) return FALSE; + if (PyArray_ISNBO(typ1->byteorder) != PyArray_ISNBO(typ2->byteorder)) + return FALSE; + + if (typenum1 == PyArray_VOID || \ + typenum2 == PyArray_VOID) { + return ((typenum1 == typenum2) && + (typ1->typeobj == typ2->typeobj) && + (typ1->fields == typ2->fields)); + } + return (typ1->kind == typ2->kind); +} + +/*** END C-API FUNCTIONS **/ + + +#define _ARET(x) PyArray_Return((PyArrayObject *)(x)) + +static char doc_fromobject[] = "array(object, dtype=None, copy=1, fortran=0, "\ + "subok=0)\n"\ + "will return a new array formed from the given object type given.\n"\ + "Object can anything with an __array__ method, or any object\n"\ + "exposing the array interface, or any (nested) sequence.\n"\ + "If no type is given, then the type will be determined as the\n"\ + "minimum type required to hold the objects in the sequence.\n"\ + "If copy is zero and sequence is already an array with the right \n"\ + "type, a reference will be returned. If the sequence is an array,\n"\ + "type can be used only to upcast the array. For downcasting \n"\ + "use .astype(t) method. If subok is true, then subclasses of the\n"\ + "array may be returned. Otherwise, a base-class ndarray is returned"; + +static PyObject * +_array_fromobject(PyObject *ignored, PyObject *args, PyObject *kws) +{ + PyObject *op, *ret=NULL; + static char *kwd[]= {"object", "dtype", "copy", "fortran", "subok", + NULL}; + Bool subok=FALSE; + Bool copy=TRUE; + PyArray_Descr *type=NULL; + PyArray_Descr *oldtype=NULL; + Bool fortran=FALSE; + int flags=0; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&", kwd, &op, + PyArray_DescrConverter2, + &type, + PyArray_BoolConverter, ©, + PyArray_BoolConverter, &fortran, + PyArray_BoolConverter, &subok)) + return NULL; + + /* fast exit if simple call */ + if ((PyArray_CheckExact(op) || PyBigArray_CheckExact(op))) { + if (type==NULL) { + if (!copy && fortran==PyArray_ISFORTRAN(op)) { + Py_INCREF(op); + return op; + } + else { + return PyArray_NewCopy((PyArrayObject*)op, + fortran); + } + } + /* One more chance */ + oldtype = PyArray_DESCR(op); + if (PyArray_EquivTypes(oldtype, type)) { + if (!copy && fortran==PyArray_ISFORTRAN(op)) { + Py_INCREF(op); + return op; + } + else { + ret = PyArray_NewCopy((PyArrayObject*)op, + fortran); + if (oldtype == type) return ret; + Py_INCREF(oldtype); + Py_DECREF(PyArray_DESCR(ret)); + PyArray_DESCR(ret) = oldtype; + return ret; + } + } + } + + if (copy) { + flags = ENSURECOPY; + } + if (fortran) { + flags |= FORTRAN; + } + if (!subok) { + flags |= ENSUREARRAY; + } + + if ((ret = PyArray_FromAny(op, type, 0, 0, flags)) == NULL) + return NULL; + + return ret; +} + +/* accepts NULL type */ +/* steals referenct to type */ +/*MULTIARRAY_API + Empty +*/ +static PyObject * +PyArray_Empty(int nd, intp *dims, PyArray_Descr *type, int fortran) +{ + PyArrayObject *ret; + + if (!type) type = PyArray_DescrFromType(PyArray_LONG); + ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, + type, nd, dims, + NULL, NULL, + fortran, NULL); + if (ret == NULL) return NULL; + + if ((PyArray_TYPE(ret) == PyArray_OBJECT)) { + PyArray_FillObjectArray(ret, Py_None); + } + return (PyObject *)ret; +} + + +static char doc_empty[] = "empty((d1,...,dn),dtype=int,fortran=0) will return a new array\n of shape (d1,...,dn) and given type with all its entries uninitialized. This can be faster than zeros."; + +static PyObject * +array_empty(PyObject *ignored, PyObject *args, PyObject *kwds) +{ + + static char *kwlist[] = {"shape","dtype","fortran",NULL}; + PyArray_Descr *typecode=NULL; + PyArray_Dims shape = {NULL, 0}; + Bool fortran = FALSE; + PyObject *ret=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&", + kwlist, PyArray_IntpConverter, + &shape, + PyArray_DescrConverter, + &typecode, + PyArray_BoolConverter, &fortran)) + goto fail; + + ret = PyArray_Empty(shape.len, shape.ptr, typecode, fortran); + PyDimMem_FREE(shape.ptr); + return ret; + + fail: + PyDimMem_FREE(shape.ptr); + return ret; +} + +static char doc_scalar[] = "scalar(dtypedescr,obj) will return a new scalar array of the given type initialized with obj. Mainly for pickle support. The dtypedescr must be a valid data-type descriptor. If dtypedescr corresponds to an OBJECT descriptor, then obj can be any object, otherwise obj must be a string. If obj is not given it will be interpreted as None for object type and zeros for all other types."; + +static PyObject * +array_scalar(PyObject *ignored, PyObject *args, PyObject *kwds) +{ + + static char *kwlist[] = {"dtypedescr","obj", NULL}; + PyArray_Descr *typecode; + PyObject *obj=NULL; + int alloc=0; + void *dptr; + PyObject *ret; + + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", + kwlist, &PyArrayDescr_Type, + &typecode, + &obj)) + return NULL; + + if (typecode->elsize == 0) { + PyErr_SetString(PyExc_ValueError, \ + "itemsize cannot be zero"); + return NULL; + } + + if (typecode->type_num == PyArray_OBJECT) { + if (obj == NULL) obj = Py_None; + dptr = &obj; + } + else { + if (obj == NULL) { + dptr = _pya_malloc(typecode->elsize); + if (dptr == NULL) { + return PyErr_NoMemory(); + } + memset(dptr, '\0', typecode->elsize); + alloc = 1; + } + else { + if (!PyString_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "initializing object must "\ + "be a string"); + return NULL; + } + if (PyString_GET_SIZE(obj) < typecode->elsize) { + PyErr_SetString(PyExc_ValueError, + "initialization string is too"\ + " small"); + return NULL; + } + dptr = PyString_AS_STRING(obj); + } + } + + ret = PyArray_Scalar(dptr, typecode, NULL); + + /* free dptr which contains zeros */ + if (alloc) _pya_free(dptr); + return ret; +} + + +/* steal a reference */ +/* accepts NULL type */ +/*MULTIARRAY_API + Zeros +*/ +static PyObject * +PyArray_Zeros(int nd, intp *dims, PyArray_Descr *type, int fortran) +{ + PyArrayObject *ret; + intp n; + + if (!type) type = PyArray_DescrFromType(PyArray_LONG); + ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, + type, + nd, dims, + NULL, NULL, + fortran, NULL); + if (ret == NULL) return NULL; + + if ((PyArray_TYPE(ret) == PyArray_OBJECT)) { + PyObject *zero = PyInt_FromLong(0); + PyArray_FillObjectArray(ret, zero); + Py_DECREF(zero); + } + else { + n = PyArray_NBYTES(ret); + memset(ret->data, 0, n); + } + return (PyObject *)ret; + +} + +static char doc_zeros[] = "zeros((d1,...,dn),dtype=int,fortran=0) will return a new array of shape (d1,...,dn) and type typecode with all it's entries initialized to zero."; + + +static PyObject * +array_zeros(PyObject *ignored, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"shape","dtype","fortran",NULL}; + PyArray_Descr *typecode=NULL; + PyArray_Dims shape = {NULL, 0}; + Bool fortran = FALSE; + PyObject *ret=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&", + kwlist, PyArray_IntpConverter, + &shape, + PyArray_DescrConverter, + &typecode, + PyArray_BoolConverter, + &fortran)) + goto fail; + + ret = PyArray_Zeros(shape.len, shape.ptr, typecode, (int) fortran); + PyDimMem_FREE(shape.ptr); + return ret; + + fail: + PyDimMem_FREE(shape.ptr); + return ret; +} + +static char doc_set_typeDict[] = "set_typeDict(dict) set the internal "\ + "dictionary that can look up an array type using a registered "\ + "code"; + +static PyObject * +array_set_typeDict(PyObject *ignored, PyObject *args) +{ + PyObject *dict; + if (!PyArg_ParseTuple(args, "O", &dict)) return NULL; + Py_XDECREF(typeDict); /* Decrement old reference (if any)*/ + typeDict = dict; + Py_INCREF(dict); /* Create an internal reference to it */ + Py_INCREF(Py_None); + return Py_None; +} + +/* steals a reference to dtype -- accepts NULL */ +/*OBJECT_API*/ +static PyObject * +PyArray_FromString(char *data, intp slen, PyArray_Descr *dtype, intp n) +{ + int itemsize; + PyArrayObject *ret; + + if (dtype == NULL) + dtype=PyArray_DescrFromType(PyArray_LONG); + + if (dtype == &OBJECT_Descr) { + 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; + } + + if (n < 0 ) { + if (slen % itemsize != 0) { + PyErr_SetString(PyExc_ValueError, + "string size must be a multiple"\ + " of element size"); + Py_DECREF(dtype); + return NULL; + } + n = slen/itemsize; + } else { + if (slen < n*itemsize) { + PyErr_SetString(PyExc_ValueError, + "string is smaller than requested"\ + " size"); + Py_DECREF(dtype); + return NULL; + } + } + + if ((ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, + dtype, + 1, &n, + NULL, NULL, + 0, NULL)) == NULL) + return NULL; + + memcpy(ret->data, data, n*dtype->elsize); + return (PyObject *)ret; +} + +static char doc_fromString[] = "fromstring(string, dtype=int, count=-1) returns a new 1d array initialized from the raw binary data in string. If count is positive, the new array will have count elements, otherwise it's size is determined by the size of string."; + +static PyObject * +array_fromString(PyObject *ignored, PyObject *args, PyObject *keywds) +{ + char *data; + longlong nin=-1; + int s; + static char *kwlist[] = {"string", "dtype", "count", NULL}; + PyArray_Descr *descr=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#|O&L", kwlist, + &data, &s, + PyArray_DescrConverter, &descr, + &nin)) { + return NULL; + } + + return PyArray_FromString(data, (intp)s, descr, (intp)nin); +} + +/* This needs an open file object and reads it in directly. + memory-mapped files handled differently through buffer interface. + +file pointer number in resulting 1d array +(can easily reshape later, -1 for to end of file) +type of array +sep is a separator string for character-based data (or NULL for binary) + " " means whitespace +*/ + +/*OBJECT_API*/ +static PyObject * +PyArray_FromFile(FILE *fp, PyArray_Descr *typecode, intp num, char *sep) +{ + PyArrayObject *r; + size_t nread = 0; + PyArray_ScanFunc *scan; + Bool binary; + + if (typecode->elsize == 0) { + PyErr_SetString(PyExc_ValueError, "0-sized elements."); + return NULL; + } + + binary = ((sep == NULL) || (strlen(sep) == 0)); + if (num == -1 && binary) { /* Get size for binary file*/ + intp start, numbytes; + start = (intp )ftell(fp); + fseek(fp, 0, SEEK_END); + numbytes = (intp )ftell(fp) - start; + rewind(fp); + if (numbytes == -1) { + PyErr_SetString(PyExc_IOError, + "could not seek in file"); + return NULL; + } + num = numbytes / typecode->elsize; + } + + if (binary) { /* binary data */ + r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, + typecode, + 1, &num, + NULL, NULL, + 0, NULL); + if (r==NULL) return NULL; + nread = fread(r->data, typecode->elsize, num, fp); + } + else { /* character reading */ + intp i; + char *dptr; + int done=0; + + scan = typecode->f->scanfunc; + if (scan == NULL) { + PyErr_SetString(PyExc_ValueError, + "don't know how to read " \ + "character files with that " \ + "array type"); + return NULL; + } + + if (num != -1) { /* number to read is known */ + r = (PyArrayObject *)\ + PyArray_NewFromDescr(&PyArray_Type, + typecode, + 1, &num, + NULL, NULL, + 0, NULL); + if (r==NULL) return NULL; + dptr = r->data; + for (i=0; i < num; i++) { + if (done) break; + done = scan(fp, dptr, sep, NULL); + if (done < -2) break; + nread += 1; + dptr += r->descr->elsize; + } + if (PyErr_Occurred()) { + Py_DECREF(r); + return NULL; + } + } + else { /* we have to watch for the end of the file and + reallocate at the end */ +#define _FILEBUFNUM 4096 + intp thisbuf=0; + intp size = _FILEBUFNUM; + intp bytes; + intp totalbytes; + + r = (PyArrayObject *)\ + PyArray_NewFromDescr(&PyArray_Type, + typecode, + 1, &size, + NULL, NULL, + 0, NULL); + if (r==NULL) return NULL; + totalbytes = bytes = size * typecode->elsize; + dptr = r->data; + while (!done) { + done = scan(fp, dptr, sep, NULL); + + /* end of file reached trying to + scan value. done is 1 or 2 + if end of file reached trying to + scan separator. Still good value. + */ + if (done < -2) break; + thisbuf += 1; + nread += 1; + dptr += r->descr->elsize; + if (!done && thisbuf == size) { + totalbytes += bytes; + r->data = PyDataMem_RENEW(r->data, + totalbytes); + dptr = r->data + (totalbytes - bytes); + thisbuf = 0; + } + } + if (PyErr_Occurred()) { + Py_DECREF(r); + return NULL; + } + r->data = PyDataMem_RENEW(r->data, nread*r->descr->elsize); + PyArray_DIM(r,0) = nread; + num = nread; +#undef _FILEBUFNUM + } + } + if (nread < num) { + fprintf(stderr, "%ld items requested but only %ld read\n", + (long) num, (long) nread); + r->data = PyDataMem_RENEW(r->data, nread * r->descr->elsize); + PyArray_DIM(r,0) = nread; + } + return (PyObject *)r; +} + +static char doc_fromfile[] = \ + "fromfile(file=, dtype=int, count=-1, sep='')\n" \ + "\n"\ + " Return an array of the given data type from a \n"\ + " (text or binary) file. The file argument can be an open file\n"\ + " or a string with the name of a file to read from. If\n"\ + " count==-1, then the entire file is read, otherwise count is\n"\ + " the number of items of the given type read in. If sep is ''\n"\ + " then read a binary file, otherwise it gives the separator\n"\ + " between elements in a text file.\n"\ + "\n"\ + " WARNING: This function should be used sparingly, as it is not\n"\ + " a robust method of persistence. But it can be useful to\n"\ + " read in simply-formatted or binary data quickly."; + +static PyObject * +array_fromfile(PyObject *ignored, PyObject *args, PyObject *keywds) +{ + PyObject *file=NULL, *ret; + FILE *fp; + char *sep=""; + char *mode=NULL; + longlong nin=-1; + static char *kwlist[] = {"file", "dtype", "count", "sep", NULL}; + PyArray_Descr *type=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O&Ls", kwlist, + &file, + PyArray_DescrConverter, &type, + &nin, &sep)) { + return NULL; + } + + if (type == NULL) type = PyArray_DescrFromType(PyArray_LONG); + + if (PyString_Check(file)) { + if (sep == "") mode="rb"; + else mode="r"; + file = PyFile_FromString(PyString_AS_STRING(file), mode); + if (file==NULL) return NULL; + } + else { + Py_INCREF(file); + } + fp = PyFile_AsFile(file); + if (fp == NULL) { + PyErr_SetString(PyExc_IOError, + "first argument must be an open file"); + Py_DECREF(file); + return NULL; + } + ret = PyArray_FromFile(fp, type, (intp) nin, sep); + Py_DECREF(file); + return ret; +} + +/*OBJECT_API*/ +static PyObject * +PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, + intp count, intp offset) +{ + PyArrayObject *ret; + char *data; + int ts; + intp s, n; + int itemsize; + int write=1; + + + if (type->type_num == PyArray_OBJECT) { + PyErr_SetString(PyExc_ValueError, + "cannot create an OBJECT array from memory"\ + " buffer"); + Py_DECREF(type); + return NULL; + } + if (type->elsize == 0) { + PyErr_SetString(PyExc_ValueError, + "itemsize cannot be zero in type"); + Py_DECREF(type); + return NULL; + } + + if (buf->ob_type->tp_as_buffer == NULL || \ + (buf->ob_type->tp_as_buffer->bf_getwritebuffer == NULL && \ + buf->ob_type->tp_as_buffer->bf_getreadbuffer == NULL)) { + PyObject *newbuf; + newbuf = PyObject_GetAttrString(buf, "__buffer__"); + if (newbuf == NULL) {Py_DECREF(type); return NULL;} + buf = newbuf; + } + else {Py_INCREF(buf);} + + if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts)==-1) { + write = 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 positive and smaller than %" + INTP_FMT, (intp)ts); + } + + data += offset; + s = (intp)ts - offset; + n = (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, + DEFAULT_FLAGS, + NULL)) == NULL) { + Py_DECREF(buf); + return NULL; + } + + if (!write) ret->flags &= ~WRITEABLE; + + /* Store a reference for decref on deallocation */ + ret->base = buf; + PyArray_UpdateFlags(ret, ALIGNED); + return (PyObject *)ret; +} + +static char doc_frombuffer[] = \ + "frombuffer(buffer=, dtype=int, count=-1, offset=0)\n"\ + "\n" \ + " Returns a 1-d array of data type dtype from buffer. The buffer\n"\ + " argument must be an object that exposes the buffer interface.\n"\ + " If count is -1 then the entire buffer is used, otherwise, count\n"\ + " is the size of the output. If offset is given then jump that\n"\ + " far into the buffer. If the buffer has data that is out\n" \ + " not in machine byte-order, than use a propert data type\n"\ + " descriptor. The data will not\n" \ + " be byteswapped, but the array will manage it in future\n"\ + " operations.\n"; + +static PyObject * +array_frombuffer(PyObject *ignored, PyObject *args, PyObject *keywds) +{ + PyObject *obj=NULL; + longlong nin=-1, offset=0; + static char *kwlist[] = {"buffer", "dtype", "count", NULL}; + PyArray_Descr *type=NULL; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O&LL", kwlist, + &obj, + PyArray_DescrConverter, &type, + &nin, &offset)) { + return NULL; + } + if (type==NULL) + type = PyArray_DescrFromType(PyArray_LONG); + + return PyArray_FromBuffer(obj, type, (intp)nin, (intp)offset); +} + + +static char doc_concatenate[] = "concatenate((a1,a2,...),axis=None)."; + +static PyObject * +array_concatenate(PyObject *dummy, PyObject *args, PyObject *kwds) +{ + PyObject *a0; + int axis=0; + static char *kwlist[] = {"seq", "axis", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&", kwlist, + &a0, + PyArray_AxisConverter, &axis)) + return NULL; + return PyArray_Concatenate(a0, axis); +} + +static char doc_innerproduct[] = \ + "inner(a,b) returns the dot product of two arrays, which has\n"\ + "shape a.shape[:-1] + b.shape[:-1] with elements computed by\n" \ + "the product of the elements from the last dimensions of a and b."; + +static PyObject *array_innerproduct(PyObject *dummy, PyObject *args) { + PyObject *b0, *a0; + + if (!PyArg_ParseTuple(args, "OO", &a0, &b0)) return NULL; + + return _ARET(PyArray_InnerProduct(a0, b0)); +} + +static char doc_matrixproduct[] = \ + "dot(a,v) returns matrix-multiplication between a and b. \n"\ + "The product-sum is over the last dimension of a and the \n"\ + "second-to-last dimension of b."; + +static PyObject *array_matrixproduct(PyObject *dummy, PyObject *args) { + PyObject *v, *a; + + if (!PyArg_ParseTuple(args, "OO", &a, &v)) return NULL; + + return _ARET(PyArray_MatrixProduct(a, v)); +} + +static char doc_fastCopyAndTranspose[] = "_fastCopyAndTranspose(a)"; + +static PyObject *array_fastCopyAndTranspose(PyObject *dummy, PyObject *args) { + PyObject *a0; + + if (!PyArg_ParseTuple(args, "O", &a0)) return NULL; + + return _ARET(PyArray_CopyAndTranspose(a0)); +} + +static char doc_correlate[] = "cross_correlate(a,v, mode=0)"; + +static PyObject *array_correlate(PyObject *dummy, PyObject *args, PyObject *kwds) { + PyObject *shape, *a0; + int mode=0; + static char *kwlist[] = {"a", "v", "mode", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i", kwlist, + &a0, &shape, &mode)) return NULL; + + return PyArray_Correlate(a0, shape, mode); +} + + +/*MULTIARRAY_API + Arange, +*/ +static PyObject * +PyArray_Arange(double start, double stop, double step, int type_num) +{ + intp length; + PyObject *range; + PyArray_ArrFuncs *funcs; + PyObject *obj; + int ret; + + length = (intp ) ceil((stop - start)/step); + + if (length <= 0) { + length = 0; + return PyArray_New(&PyArray_Type, 1, &length, type_num, + NULL, NULL, 0, 0, NULL); + } + + range = 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), (PyArrayObject *)range); + Py_DECREF(obj); + if (ret < 0) goto fail; + if (length == 1) return range; + + obj = PyFloat_FromDouble(start + step); + ret = funcs->setitem(obj, PyArray_DATA(range)+PyArray_ITEMSIZE(range), + (PyArrayObject *)range); + Py_DECREF(obj); + if (ret < 0) goto fail; + if (length == 2) return range; + + if (!funcs->fill) { + PyErr_SetString(PyExc_ValueError, "no fill-function for data-type."); + Py_DECREF(range); + return NULL; + } + funcs->fill(PyArray_DATA(range), length, (PyArrayObject *)range); + if (PyErr_Occurred()) goto fail; + + return range; + + fail: + Py_DECREF(range); + return NULL; +} + +/* the formula is + len = (intp) ceil((start - stop) / step); +*/ +static intp +_calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, int cmplx) +{ + intp len; + PyObject *val; + double value; + + *next = PyNumber_Subtract(stop, start); + if (!(*next)) 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 = (intp) ceil(value); + value = PyComplex_ImagAsDouble(val); + Py_DECREF(val); + if (error_converting(value)) return -1; + len = MIN(len, (intp) ceil(value)); + } + else { + value = PyFloat_AsDouble(val); + Py_DECREF(val); + if (error_converting(value)) return -1; + len = (intp) ceil(value); + } + + if (len > 0) { + *next = PyNumber_Add(start, step); + if (!next) return -1; + } + return len; +} + +/* this doesn't change the references */ +/*MULTIARRAY_API + ArangeObj, +*/ +static PyObject * +PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr *dtype) +{ + PyObject *range; + PyArray_ArrFuncs *funcs; + PyObject *next; + intp length; + + if (!dtype) { + PyArray_Descr *deftype; + PyArray_Descr *newtype; + deftype = PyArray_DescrFromType(PyArray_LONG); + newtype = PyArray_DescrFromObject(start, deftype); + Py_DECREF(deftype); + deftype = newtype; + if (stop && stop != Py_None) { + newtype = PyArray_DescrFromObject(stop, deftype); + Py_DECREF(deftype); + deftype = newtype; + } + if (step && step != Py_None) { + newtype = PyArray_DescrFromObject(step, deftype); + Py_DECREF(deftype); + 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)); + + if (PyErr_Occurred()) {Py_DECREF(dtype); goto fail;} + if (length <= 0) { + length = 0; + range = PyArray_SimpleNewFromDescr(1, &length, dtype); + Py_DECREF(step); Py_DECREF(start); return range; + } + + range = PyArray_SimpleNewFromDescr(1, &length, dtype); + if (range == NULL) goto fail; + + 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 */ + + if (funcs->setitem(start, PyArray_DATA(range), (PyArrayObject *)range) < 0) + goto fail; + if (length == 1) goto finish; + if (funcs->setitem(next, PyArray_DATA(range)+PyArray_ITEMSIZE(range), + (PyArrayObject *)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; + } + funcs->fill(PyArray_DATA(range), length, (PyArrayObject *)range); + if (PyErr_Occurred()) goto fail; + + finish: + Py_DECREF(start); + Py_DECREF(step); + Py_DECREF(next); + return range; + + fail: + Py_DECREF(start); + Py_DECREF(step); + Py_XDECREF(next); + return NULL; +} + + +static char doc_arange[] = "arange(start, stop=None, step=1, dtype=int)\n\n Just like range() except it returns an array whose type can be\n specified by the keyword argument typecode."; + +static PyObject * +array_arange(PyObject *ignored, PyObject *args, PyObject *kws) { + PyObject *o_start=NULL, *o_stop=NULL, *o_step=NULL; + static char *kwd[]= {"start", "stop", "step", "dtype", NULL}; + PyArray_Descr *typecode=NULL; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "O|OOO&", kwd, &o_start, + &o_stop, &o_step, + PyArray_DescrConverter, + &typecode)) + return NULL; + + return PyArray_ArangeObj(o_start, o_stop, o_step, typecode); +} + + +static char +doc_set_string_function[] = "set_string_function(f, repr=1) sets the python function f to be the function used to obtain a pretty printable string version of a array whenever a array is printed. f(M) should expect a array argument M, and should return a string consisting of the desired representation of M for printing."; + +static PyObject * +array_set_string_function(PyObject *dummy, PyObject *args, PyObject *kwds) +{ + PyObject *op; + int repr=1; + static char *kwlist[] = {"f", "repr", NULL}; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, + &op, &repr)) return NULL; + if (!PyCallable_Check(op)) { + PyErr_SetString(PyExc_TypeError, + "Argument must be callable."); + return NULL; + } + PyArray_SetStringFunction(op, repr); + Py_INCREF(Py_None); + return Py_None; +} + +static char +doc_set_ops_function[] = "set_numeric_ops(op=func, ...) sets some or all of the number methods for all array objects. Don't forget **dict can be used as the argument list. Returns the functions that were replaced -- can be stored and set later."; + +static PyObject * +array_set_ops_function(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *oldops=NULL; + + if ((oldops = PyArray_GetNumericOps())==NULL) return NULL; + + /* Should probably ensure that objects are at least callable */ + /* Leave this to the caller for now --- error will be raised + later when use is attempted + */ + if (kwds && PyArray_SetNumericOps(kwds) == -1) { + Py_DECREF(oldops); + PyErr_SetString(PyExc_ValueError, + "one or more objects not callable"); + return NULL; + } + return oldops; +} + + +/*MULTIARRAY_API + Where +*/ +static PyObject * +PyArray_Where(PyObject *condition, PyObject *x, PyObject *y) +{ + PyArrayObject *arr; + PyObject *tup=NULL, *obj=NULL; + PyObject *ret=NULL, *zero=NULL; + + + arr = (PyArrayObject *)PyArray_FromAny(condition, NULL, 0, 0, 0); + if (arr == NULL) return NULL; + + if ((x==NULL) && (y==NULL)) { + ret = PyArray_Nonzero(arr); + Py_DECREF(arr); + return ret; + } + + if ((x==NULL) || (y==NULL)) { + Py_DECREF(arr); + PyErr_SetString(PyExc_ValueError, "either both or neither " + "of x and y should be given"); + return NULL; + } + + + zero = PyInt_FromLong((long) 0); + + obj = PyArray_EnsureArray(PyArray_GenericBinaryFunction(arr, zero, + n_ops.not_equal)); + Py_DECREF(zero); + Py_DECREF(arr); + if (obj == NULL) return NULL; + + tup = Py_BuildValue("(OO)", y, x); + if (tup == NULL) {Py_DECREF(obj); return NULL;} + + ret = PyArray_Choose((PyAO *)obj, tup); + + Py_DECREF(obj); + Py_DECREF(tup); + return ret; +} + +static char doc_where[] = "where(condition, | x, y) is shaped like condition"\ + " and has elements of x and y where condition is respectively true or"\ + " false. If x or y are not given, then it is equivalent to"\ + " nonzero(condition)."; + +static PyObject * +array_where(PyObject *ignored, PyObject *args) +{ + PyObject *obj=NULL, *x=NULL, *y=NULL; + + if (!PyArg_ParseTuple(args, "O|OO", &obj, &x, &y)) return NULL; + + return PyArray_Where(obj, x, y); + +} + +static char doc_lexsort[] = "lexsort(keys=, axis=-1) returns an array of indexes"\ + " similar to argsort except the sorting is done using the provided sorting"\ + " keys. First the sort is done using key[0], then the resulting list of"\ + " indexes is further manipulated by sorting on key[0]. And so forth"\ + " The result is a sort on multiple keys. If the keys represented columns" \ + " of a spread-sheet, for example, this would sort using multiple columns."\ + " The keys argument must be a tuple of things that can be converted to "\ + " arrays of the same shape."; + +static PyObject * +array_lexsort(PyObject *ignored, PyObject *args, PyObject *kwds) +{ + int axis=-1; + PyObject *obj; + static char *kwlist[] = {"keys", "axis", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|i", kwlist, + &PyTuple_Type, &obj, &axis)) return NULL; + + return _ARET(PyArray_LexSort(obj, axis)); +} + +#undef _ARET + + +static char doc_register_dtype[] = \ + "register_dtype(a) registers a new type object -- gives it a typenum"; + +static PyObject * +array_register_dtype(PyObject *dummy, PyObject *args) +{ + PyObject *dtype; + int ret; + + if (!PyArg_ParseTuple(args, "O", &dtype)) return NULL; + + ret = PyArray_RegisterDataType((PyTypeObject *)dtype); + if (ret < 0) + return NULL; + return PyInt_FromLong((long) ret); +} + +static char doc_can_cast_safely[] = \ + "can_cast_safely(from=d1, to=d2) returns True if data type d1 "\ + "can be cast to data type d2 without losing precision."; + +static PyObject * +array_can_cast_safely(PyObject *dummy, PyObject *args, PyObject *kwds) +{ + PyArray_Descr *d1=NULL; + PyArray_Descr *d2=NULL; + Bool ret; + PyObject *retobj; + static char *kwlist[] = {"from", "to", NULL}; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&", kwlist, + PyArray_DescrConverter, &d1, + PyArray_DescrConverter, &d2)) + return NULL; + if (d1 == NULL || d2 == NULL) { + PyErr_SetString(PyExc_TypeError, + "did not understand one of the types; " \ + "'None' not accepted"); + return NULL; + } + + ret = PyArray_CanCastTo(d1, d2); + retobj = (ret ? Py_True : Py_False); + Py_INCREF(retobj); + return retobj; +} + +static char doc_new_buffer[] = \ + "newbuffer(size) return a new uninitialized buffer object of size " + "bytes"; + +static PyObject * +new_buffer(PyObject *dummy, PyObject *args) +{ + int size; + + if(!PyArg_ParseTuple(args, "i", &size)) + return NULL; + + return PyBuffer_New(size); +} + +static char doc_buffer_buffer[] = \ + "getbuffer(obj [,offset[, size]]) create a buffer object from the "\ + "given object\n referencing a slice of length size starting at "\ + "offset. Default\n is the entire buffer. A read-write buffer is "\ + "attempted followed by a read-only buffer."; + +static PyObject * +buffer_buffer(PyObject *dummy, PyObject *args, PyObject *kwds) +{ + PyObject *obj; + int offset=0, size=Py_END_OF_BUFFER, n; + void *unused; + static char *kwlist[] = {"object", "offset", "size", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ii", kwlist, + &obj, &offset, &size)) + return NULL; + + + if (PyObject_AsWriteBuffer(obj, &unused, &n) < 0) { + PyErr_Clear(); + return PyBuffer_FromObject(obj, offset, size); + } + else + return PyBuffer_FromReadWriteObject(obj, offset, size); +} + + +static struct PyMethodDef array_module_methods[] = { + {"set_string_function", (PyCFunction)array_set_string_function, + METH_VARARGS|METH_KEYWORDS, doc_set_string_function}, + {"set_numeric_ops", (PyCFunction)array_set_ops_function, + METH_VARARGS|METH_KEYWORDS, doc_set_ops_function}, + {"set_typeDict", (PyCFunction)array_set_typeDict, + METH_VARARGS, doc_set_typeDict}, + + {"array", (PyCFunction)_array_fromobject, + METH_VARARGS|METH_KEYWORDS, doc_fromobject}, + {"arange", (PyCFunction)array_arange, + METH_VARARGS|METH_KEYWORDS, doc_arange}, + {"zeros", (PyCFunction)array_zeros, + METH_VARARGS|METH_KEYWORDS, doc_zeros}, + {"empty", (PyCFunction)array_empty, + METH_VARARGS|METH_KEYWORDS, doc_empty}, + {"scalar", (PyCFunction)array_scalar, + METH_VARARGS|METH_KEYWORDS, doc_scalar}, + {"where", (PyCFunction)array_where, + METH_VARARGS, doc_where}, + {"lexsort", (PyCFunction)array_lexsort, + METH_VARARGS | METH_KEYWORDS, doc_lexsort}, + {"fromstring",(PyCFunction)array_fromString, + METH_VARARGS|METH_KEYWORDS, doc_fromString}, + {"concatenate", (PyCFunction)array_concatenate, + METH_VARARGS|METH_KEYWORDS, doc_concatenate}, + {"inner", (PyCFunction)array_innerproduct, + METH_VARARGS, doc_innerproduct}, + {"dot", (PyCFunction)array_matrixproduct, + METH_VARARGS, doc_matrixproduct}, + {"_fastCopyAndTranspose", (PyCFunction)array_fastCopyAndTranspose, + METH_VARARGS, doc_fastCopyAndTranspose}, + {"correlate", (PyCFunction)array_correlate, + METH_VARARGS | METH_KEYWORDS, doc_correlate}, + {"frombuffer", (PyCFunction)array_frombuffer, + METH_VARARGS | METH_KEYWORDS, doc_frombuffer}, + {"fromfile", (PyCFunction)array_fromfile, + METH_VARARGS | METH_KEYWORDS, doc_fromfile}, + {"register_dtype", (PyCFunction)array_register_dtype, + METH_VARARGS, doc_register_dtype}, + {"can_cast", (PyCFunction)array_can_cast_safely, + METH_VARARGS | METH_KEYWORDS, doc_can_cast_safely}, + {"newbuffer", (PyCFunction)new_buffer, + METH_VARARGS, doc_new_buffer}, + {"getbuffer", (PyCFunction)buffer_buffer, + METH_VARARGS | METH_KEYWORDS, doc_buffer_buffer}, + {NULL, NULL, 0} /* sentinel */ +}; + +#include "__multiarray_api.c" + +/* Establish scalar-type hierarchy */ + +/* For dual inheritance we need to make sure that the objects being + inherited from have the tp->mro object initialized. This is + not necessarily true for the basic type objects of Python (it is + checked for single inheritance but not dual in PyType_Ready). + + Thus, we call PyType_Ready on the standard Python Types, here. +*/ +static int +setup_scalartypes(PyObject *dict) +{ + + initialize_numeric_types(); + + if (PyType_Ready(&PyBool_Type) < 0) return -1; + if (PyType_Ready(&PyInt_Type) < 0) return -1; + if (PyType_Ready(&PyFloat_Type) < 0) return -1; + if (PyType_Ready(&PyComplex_Type) < 0) return -1; + if (PyType_Ready(&PyString_Type) < 0) return -1; + if (PyType_Ready(&PyUnicode_Type) < 0) return -1; + +#define SINGLE_INHERIT(child, parent) \ + Py##child##ArrType_Type.tp_base = &Py##parent##ArrType_Type; \ + if (PyType_Ready(&Py##child##ArrType_Type) < 0) { \ + PyErr_Print(); \ + PyErr_Format(PyExc_SystemError, \ + "could not initialize Py%sArrType_Type", \ + #child); \ + return -1; \ + } + + if (PyType_Ready(&PyGenericArrType_Type) < 0) + return -1; + + SINGLE_INHERIT(Numeric, Generic); + SINGLE_INHERIT(Integer, Numeric); + SINGLE_INHERIT(Inexact, Numeric); + SINGLE_INHERIT(SignedInteger, Integer); + SINGLE_INHERIT(UnsignedInteger, Integer); + SINGLE_INHERIT(Floating, Inexact); + SINGLE_INHERIT(ComplexFloating, Inexact); + SINGLE_INHERIT(Flexible, Generic); + SINGLE_INHERIT(Character, Flexible); + +#define DUAL_INHERIT(child, parent1, parent2) \ + Py##child##ArrType_Type.tp_base = &Py##parent2##ArrType_Type; \ + Py##child##ArrType_Type.tp_bases = \ + Py_BuildValue("(OO)", &Py##parent2##ArrType_Type, \ + &Py##parent1##_Type); \ + if (PyType_Ready(&Py##child##ArrType_Type) < 0) { \ + PyErr_Print(); \ + PyErr_Format(PyExc_SystemError, \ + "could not initialize Py%sArrType_Type", \ + #child); \ + return -1; \ + }\ + Py##child##ArrType_Type.tp_hash = Py##parent1##_Type.tp_hash; + +#define DUAL_INHERIT2(child, parent1, parent2) \ + Py##child##ArrType_Type.tp_base = &Py##parent1##_Type; \ + Py##child##ArrType_Type.tp_bases = \ + Py_BuildValue("(OO)", &Py##parent1##_Type, \ + &Py##parent2##ArrType_Type); \ + Py##child##ArrType_Type.tp_richcompare = \ + Py##parent1##_Type.tp_richcompare; \ + Py##child##ArrType_Type.tp_compare = \ + Py##parent1##_Type.tp_compare; \ + Py##child##ArrType_Type.tp_hash = Py##parent1##_Type.tp_hash; \ + if (PyType_Ready(&Py##child##ArrType_Type) < 0) { \ + PyErr_Print(); \ + PyErr_Format(PyExc_SystemError, \ + "could not initialize Py%sArrType_Type", \ + #child); \ + return -1; \ + } + + SINGLE_INHERIT(Bool, Generic); + SINGLE_INHERIT(Byte, SignedInteger); + SINGLE_INHERIT(Short, SignedInteger); +#if SIZEOF_INT == SIZEOF_LONG + DUAL_INHERIT(Int, Int, SignedInteger); +#else + SINGLE_INHERIT(Int, SignedInteger); +#endif + DUAL_INHERIT(Long, Int, SignedInteger); +#if SIZEOF_LONGLONG == SIZEOF_LONG + DUAL_INHERIT(LongLong, Int, SignedInteger); +#else + SINGLE_INHERIT(LongLong, SignedInteger); +#endif + + /* fprintf(stderr, "tp_free = %p, PyObject_Del = %p, int_tp_free = %p, base.tp_free = %p\n", PyIntArrType_Type.tp_free, PyObject_Del, PyInt_Type.tp_free, PySignedIntegerArrType_Type.tp_free); + */ + SINGLE_INHERIT(UByte, UnsignedInteger); + SINGLE_INHERIT(UShort, UnsignedInteger); + SINGLE_INHERIT(UInt, UnsignedInteger); + SINGLE_INHERIT(ULong, UnsignedInteger); + SINGLE_INHERIT(ULongLong, UnsignedInteger); + + SINGLE_INHERIT(Float, Floating); + DUAL_INHERIT(Double, Float, Floating); + SINGLE_INHERIT(LongDouble, Floating); + + SINGLE_INHERIT(CFloat, ComplexFloating); + DUAL_INHERIT(CDouble, Complex, ComplexFloating); + SINGLE_INHERIT(CLongDouble, ComplexFloating); + + DUAL_INHERIT2(String, String, Character); + DUAL_INHERIT2(Unicode, Unicode, Character); + + SINGLE_INHERIT(Void, Flexible); + + SINGLE_INHERIT(Object, Generic); + + return 0; + +#undef SINGLE_INHERIT +#undef DUAL_INHERIT + + /* Clean up string and unicode array types so they act more like + strings -- get their tables from the standard types. + */ +} + +/* place a flag dictionary in d */ + +static void +set_flaginfo(PyObject *d) +{ + PyObject *s; + PyObject *newd; + + newd = PyDict_New(); + + PyDict_SetItemString(newd, "OWNDATA", s=PyInt_FromLong(OWNDATA)); + Py_DECREF(s); + PyDict_SetItemString(newd, "FORTRAN", s=PyInt_FromLong(FORTRAN)); + Py_DECREF(s); + PyDict_SetItemString(newd, "CONTIGUOUS", s=PyInt_FromLong(CONTIGUOUS)); + Py_DECREF(s); + PyDict_SetItemString(newd, "ALIGNED", s=PyInt_FromLong(ALIGNED)); + Py_DECREF(s); + + PyDict_SetItemString(newd, "UPDATEIFCOPY", s=PyInt_FromLong(UPDATEIFCOPY)); + Py_DECREF(s); + PyDict_SetItemString(newd, "WRITEABLE", s=PyInt_FromLong(WRITEABLE)); + Py_DECREF(s); + + PyDict_SetItemString(d, "_flagdict", newd); + Py_DECREF(newd); + return; +} + + +/* Initialization function for the module */ + +DL_EXPORT(void) initmultiarray(void) { + PyObject *m, *d, *s; + PyObject *c_api; + + /* Create the module and add the functions */ + m = Py_InitModule("multiarray", array_module_methods); + if (!m) goto err; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + if (!d) goto err; + + /* Create the module and add the functions */ + if (PyType_Ready(&PyBigArray_Type) < 0) + return; + + PyArray_Type.tp_base = &PyBigArray_Type; + + PyArray_Type.tp_as_mapping = &array_as_mapping; + /* Even though, this would be inherited, it needs to be set now + so that the __getitem__ will map to the as_mapping descriptor + */ + PyArray_Type.tp_as_number = &array_as_number; + /* For good measure */ + PyArray_Type.tp_as_sequence = &array_as_sequence; + PyArray_Type.tp_as_buffer = &array_as_buffer; + PyArray_Type.tp_flags = (Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_CHECKTYPES); + PyArray_Type.tp_doc = Arraytype__doc__; + + if (PyType_Ready(&PyArray_Type) < 0) + return; + + if (setup_scalartypes(d) < 0) goto err; + + PyArrayIter_Type.tp_iter = PyObject_SelfIter; + PyArrayMultiIter_Type.tp_iter = PyObject_SelfIter; + if (PyType_Ready(&PyArrayIter_Type) < 0) + return; + + if (PyType_Ready(&PyArrayMapIter_Type) < 0) + return; + + if (PyType_Ready(&PyArrayMultiIter_Type) < 0) + return; + + if (PyType_Ready(&PyArrayDescr_Type) < 0) + return; + + c_api = PyCObject_FromVoidPtr((void *)PyArray_API, NULL); + if (PyErr_Occurred()) goto err; + PyDict_SetItemString(d, "_ARRAY_API", c_api); + Py_DECREF(c_api); + if (PyErr_Occurred()) goto err; + + MultiArrayError = PyString_FromString ("multiarray.error"); + PyDict_SetItemString (d, "error", MultiArrayError); + + s = PyString_FromString("3.0"); + PyDict_SetItemString(d, "__version__", s); + Py_DECREF(s); + Py_INCREF(&PyBigArray_Type); + PyDict_SetItemString(d, "bigndarray", (PyObject *)&PyBigArray_Type); + Py_INCREF(&PyArray_Type); + PyDict_SetItemString(d, "ndarray", (PyObject *)&PyArray_Type); + Py_INCREF(&PyArrayIter_Type); + PyDict_SetItemString(d, "flatiter", (PyObject *)&PyArrayIter_Type); + Py_INCREF(&PyArrayMultiIter_Type); + PyDict_SetItemString(d, "broadcast", + (PyObject *)&PyArrayMultiIter_Type); + Py_INCREF(&PyArrayDescr_Type); + PyDict_SetItemString(d, "dtypedescr", (PyObject *)&PyArrayDescr_Type); + + /* Doesn't need to be exposed to Python + Py_INCREF(&PyArrayMapIter_Type); + PyDict_SetItemString(d, "mapiter", (PyObject *)&PyArrayMapIter_Type); + */ + set_flaginfo(d); + + if (set_typeinfo(d) != 0) goto err; + + _scipy_internal = \ + PyImport_ImportModule("scipy.base._internal"); + if (_scipy_internal != NULL) return; + + err: + /* Check for errors */ + if (PyErr_Occurred()) + PyErr_Print(); + Py_FatalError("can't initialize module multiarray"); + + return; +} + diff --git a/numpy/core/src/scalarmathmodule.c.src b/numpy/core/src/scalarmathmodule.c.src new file mode 100644 index 000000000..dc2c3c198 --- /dev/null +++ b/numpy/core/src/scalarmathmodule.c.src @@ -0,0 +1,103 @@ +/* The purpose of this module is to add faster math for array scalars + that does not go through the ufunc machinery + + NOT FINISHED + */ + +#include "scipy/arrayobject.h" +#include "scipy/ufuncobject.h" + + +/**begin repeat +name=bool, + +**/ +static PyNumberMethods @name@_as_number = { + (binaryfunc)@name@_add, /*nb_add*/ + (binaryfunc)@name@_subtract, /*nb_subtract*/ + (binaryfunc)@name@_multiply, /*nb_multiply*/ + (binaryfunc)@name@_divide, /*nb_divide*/ + (binaryfunc)@name@_remainder, /*nb_remainder*/ + (binaryfunc)@name@_divmod, /*nb_divmod*/ + (ternaryfunc)@name@_power, /*nb_power*/ + (unaryfunc)@name@_negative, + (unaryfunc)@name@_copy, /*nb_pos*/ + (unaryfunc)@name@_absolute, /*nb_abs*/ + (inquiry)@name@_nonzero_number, /*nb_nonzero*/ + (unaryfunc)@name@_invert, /*nb_invert*/ + (binaryfunc)@name@_lshift, /*nb_lshift*/ + (binaryfunc)@name@_rshift, /*nb_rshift*/ + (binaryfunc)@name@_and, /*nb_and*/ + (binaryfunc)@name@_xor, /*nb_xor*/ + (binaryfunc)@name@_or, /*nb_or*/ + 0, /*nb_coerce*/ + (unaryfunc)@name@_int, /*nb_int*/ + (unaryfunc)@name@_long, /*nb_long*/ + (unaryfunc)@name@_float, /*nb_float*/ + (unaryfunc)@name@_oct, /*nb_oct*/ + (unaryfunc)@name@_hex, /*nb_hex*/ + 0, /*inplace_add*/ + 0, /*inplace_subtract*/ + 0, /*inplace_multiply*/ + 0, /*inplace_divide*/ + 0, /*inplace_remainder*/ + 0, /*inplace_power*/ + 0, /*inplace_lshift*/ + 0, /*inplace_rshift*/ + 0, /*inplace_and*/ + 0, /*inplace_xor*/ + 0, /*inplace_or*/ + (binaryfunc)@name@_floor_divide, /*nb_floor_divide*/ + (binaryfunc)@name@_true_divide, /*nb_true_divide*/ + 0, /*nb_inplace_floor_divide*/ + 0, /*nb_inplace_true_divide*/ + +}; + +/**end repeat**/ + + +/**begin repeat + +**/ + +static PyObject* +@name@_richcompare(PyObject *self, PyObject *other, int cmp_op) +{ +} +/**end repeat**/ + + + +static void +add_scalarmath(void) +{ +/**begin repeat +name=bool, +NAME=Bool +**/ + PyArr@NAME@Type_Type.tp_as_number = @name@_as_number; + PyArr@NAME@Type_Type.tp_richcompare = @name@_richcompare; +/**end repeat**/ +} + + + +static struct PyMethodDef methods[] = { + {"alter_pyscalars", (PyCFunction) alter_pyscalars, + METH_VARARGS , doc_alterpyscalars}, + {NULL, NULL, 0} +}; + +DL_EXPORT(void) initscalarmath(void) { + PyObject *m; + + m = Py_initModule("scalarmath", methods); + + if (import_array() < 0) return; + if (import_umath() < 0) return; + + add_scalarmath(); + + return; +} diff --git a/numpy/core/src/scalartypes.inc.src b/numpy/core/src/scalartypes.inc.src new file mode 100644 index 000000000..629adbcf0 --- /dev/null +++ b/numpy/core/src/scalartypes.inc.src @@ -0,0 +1,2165 @@ +/* -*- c -*- */ + +static int PyArrayScalar_Offset[PyArray_NTYPES+1]; + +#define _SOFFSET_(obj, type_num) ((char *)(obj) + PyArrayScalar_Offset[(type_num)]) + +/**begin repeat +#name=Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object,# +#type=Bool, signed char, short, int, long, longlong, unsigned char, unsigned short, unsigned int, unsigned long, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, PyObject *,char# +*/ +typedef struct { + PyObject_HEAD; + @type@ obval; +} Py@name@ScalarObject; +/**end repeat**/ + +/* Inheritance established later when tp_bases is set (or tp_base for + single inheritance) */ + +/**begin repeat + +#name=numeric, integer, signedinteger, unsignedinteger, inexact, floating, complexfloating, flexible, +character# +#NAME=Numeric, Integer, SignedInteger, UnsignedInteger, Inexact, Floating, ComplexFloating, Flexible, Character# +*/ + +static PyTypeObject Py@NAME@ArrType_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@name@_arrtype", /*tp_name*/ + sizeof(PyObject), /*tp_basicsize*/ +}; +/**end repeat**/ + + +#define PyStringScalarObject PyStringObject +#define PyUnicodeScalarObject PyUnicodeObject + +typedef struct { + PyObject_VAR_HEAD; + char *obval; + PyArray_Descr *descr; + int flags; + PyObject *base; +} PyVoidScalarObject; + +/* no error checking is performed -- ctypeptr must be same type as scalar */ +/* in case of flexible type, the data is not copied + into ctypeptr which is expected to be a pointer to pointer */ +/*OBJECT_API + Convert to c-type +*/ +static void +PyArray_ScalarAsCtype(PyObject *scalar, void *ctypeptr) +{ + PyArray_Descr *typecode; + typecode = PyArray_DescrFromScalar(scalar); + + if (PyTypeNum_ISEXTENDED(typecode->type_num)) { + void **newptr = (void **)ctypeptr; + switch(typecode->type_num) { + case PyArray_STRING: + *newptr = (void *)PyString_AS_STRING(scalar); + case PyArray_UNICODE: + *newptr = (void *)PyUnicode_AS_DATA(scalar); + default: + *newptr = ((PyVoidScalarObject *)scalar)->obval; + } + return; + } + memcpy(ctypeptr, _SOFFSET_(scalar, typecode->type_num), + typecode->elsize); + Py_DECREF(typecode); + return; +} + +/* The output buffer must be large-enough to receive the value */ +/* Even for flexible types which is different from ScalarAsCtype + where only a reference for flexible types is returned +*/ + +/*OBJECT_API + Cast Scalar to c-type +*/ +static int +PyArray_CastScalarToCtype(PyObject *scalar, void *ctypeptr, + PyArray_Descr *outcode) +{ + PyArray_Descr* descr; + + descr = PyArray_DescrFromScalar(scalar); + if (PyTypeNum_ISEXTENDED(descr->type_num) || + PyTypeNum_ISEXTENDED(outcode->type_num)) { + PyArrayObject *ain, *aout; + + ain = (PyArrayObject *)PyArray_FromScalar(scalar, NULL); + if (ain == NULL) {Py_DECREF(descr); return -1;} + aout = (PyArrayObject *)\ + PyArray_NewFromDescr(&PyArray_Type, + outcode, + 0, NULL, + NULL, ctypeptr, + CARRAY_FLAGS, NULL); + if (aout == NULL) {Py_DECREF(ain); return -1;} + descr->f->cast[outcode->type_num](ain->data, + aout->data, 1, ain, aout); + Py_DECREF(ain); + Py_DECREF(aout); + } + else { + descr->f->cast[outcode->type_num](_SOFFSET_(scalar, + descr->type_num), + ctypeptr, 1, NULL, NULL); + } + Py_DECREF(descr); + return 0; +} + +/* 0-dim array from array-scalar object */ +/* always contains a copy of the data + unless outcode is NULL, it is of void type and the referrer does + not own it either. +*/ + +/* steals reference to outcode */ +/*OBJECT_API + Get 0-dim array from scalar +*/ +static PyObject * +PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode) +{ + PyArray_Descr *typecode; + PyObject *r; + char *memptr; + PyObject *ret; + + /* convert to 0-dim array of scalar typecode */ + typecode = PyArray_DescrFromScalar(scalar); + if ((typecode->type_num == PyArray_VOID) && \ + !(((PyVoidScalarObject *)scalar)->flags & OWNDATA) && \ + outcode == NULL) { + r = PyArray_NewFromDescr(&PyArray_Type, + typecode, + 0, NULL, NULL, + ((PyVoidScalarObject *)scalar)->obval, + ((PyVoidScalarObject *)scalar)->flags, + NULL); + PyArray_BASE(r) = (PyObject *)scalar; + Py_INCREF(scalar); + return r; + } + r = PyArray_NewFromDescr(&PyArray_Type, + typecode, + 0, NULL, + NULL, NULL, 0, NULL); + if (r==NULL) {Py_XDECREF(outcode); return NULL;} + + switch(typecode->type_num) { + case PyArray_STRING: + memptr = PyString_AS_STRING(scalar); + break; + case PyArray_UNICODE: + memptr = (char *)PyUnicode_AS_DATA(scalar); + break; + default: + if (PyTypeNum_ISEXTENDED(typecode->type_num)) { + memptr = (((PyVoidScalarObject *)scalar)->obval); + } + else { + memptr = _SOFFSET_(scalar, typecode->type_num); + } + break; + } + + memcpy(PyArray_DATA(r), memptr, PyArray_ITEMSIZE(r)); + if (PyArray_ISOBJECT(r)) { + Py_INCREF(*((PyObject **)memptr)); + } + + if (outcode == NULL) return r; + + if (outcode->type_num == typecode->type_num) { + if (!PyTypeNum_ISEXTENDED(typecode->type_num)) + return r; + if (outcode->elsize == typecode->elsize); + return r; + } + + /* cast if necessary to desired output typecode */ + ret = PyArray_CastToType((PyArrayObject *)r, outcode, 0); + Py_DECREF(r); + return ret; +} + +static PyObject * +gentype_alloc(PyTypeObject *type, int nitems) +{ + PyObject *obj; + const size_t size = _PyObject_VAR_SIZE(type, nitems+1); + + obj = (PyObject *)_pya_malloc(size); + memset(obj, 0, size); + if (type->tp_itemsize == 0) + PyObject_INIT(obj, type); + else + (void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems); + return obj; +} + +static void +gentype_dealloc(PyObject *v) +{ + v->ob_type->tp_free(v); +} + + +static PyObject * +gentype_power(PyObject *m1, PyObject *m2, PyObject *m3) +{ + PyObject *arr, *ret, *arg2; + char *msg="unsupported operand type(s) for ** or pow()"; + + if (!PyArray_IsScalar(m1,Generic)) { + if (PyArray_Check(m1)) { + ret = m1->ob_type->tp_as_number->nb_power(m1,m2, + Py_None); + } + else { + if (!PyArray_IsScalar(m2,Generic)) { + PyErr_SetString(PyExc_TypeError, msg); + return NULL; + } + arr = PyArray_FromScalar(m2, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_as_number->nb_power(m1, arr, + Py_None); + Py_DECREF(arr); + } + return ret; + } + if (!PyArray_IsScalar(m2, Generic)) { + if (PyArray_Check(m2)) { + ret = m2->ob_type->tp_as_number->nb_power(m1,m2, + Py_None); + } + else { + if (!PyArray_IsScalar(m1, Generic)) { + PyErr_SetString(PyExc_TypeError, msg); + return NULL; + } + arr = PyArray_FromScalar(m1, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_as_number->nb_power(arr, m2, + Py_None); + Py_DECREF(arr); + } + return ret; + } + arr=arg2=NULL; + arr = PyArray_FromScalar(m1, NULL); + arg2 = PyArray_FromScalar(m2, NULL); + if (arr == NULL || arg2 == NULL) { + Py_XDECREF(arr); Py_XDECREF(arg2); return NULL; + } + ret = arr->ob_type->tp_as_number->nb_power(arr, arg2, Py_None); + Py_DECREF(arr); + Py_DECREF(arg2); + return ret; +} + +static PyObject * +gentype_generic_method(PyObject *self, PyObject *args, PyObject *kwds, + char *str) +{ + PyObject *arr, *meth, *ret; + + arr = PyArray_FromScalar(self, NULL); + if (arr == NULL) return NULL; + meth = PyObject_GetAttrString(arr, str); + if (meth == NULL) {Py_DECREF(arr); return NULL;} + if (kwds == NULL) + ret = PyObject_CallObject(meth, args); + else + ret = PyObject_Call(meth, args, kwds); + Py_DECREF(meth); + Py_DECREF(arr); + if (ret && PyArray_Check(ret)) + return PyArray_Return((PyArrayObject *)ret); + else + return ret; +} + +/**begin repeat + +#name=add, subtract, divide, remainder, divmod, lshift, rshift, and, xor, or, floor_divide, true_divide# +#PYNAME=Add, Subtract, Divide, Remainder, Divmod, Lshift, Rshift, And, Xor, Or, FloorDivide, TrueDivide# +*/ + +static PyObject * +gentype_@name@(PyObject *m1, PyObject *m2) +{ + PyObject *arr, *ret=NULL, *tup; + + if (!PyArray_IsScalar(m1, Generic)) { + if (PyArray_Check(m1)) { + ret = m1->ob_type->tp_as_number->nb_@name@(m1,m2); + } + else { + PyObject *newarr; + /* Convert object to Array scalar and try again */ + newarr = PyArray_FromAny(m1, NULL, 0, 0, 0); + if (newarr!=NULL) { + ret = newarr->ob_type->tp_as_number->nb_@name@(newarr, m2); + Py_DECREF(newarr); + } + else ret=NULL; + } + return ret; + } + if (!PyArray_IsScalar(m2, Generic)) { + if (PyArray_Check(m2)) { + ret = m2->ob_type->tp_as_number->nb_@name@(m1,m2); + } + else { + PyObject *newarr; + /* Convert object to Array and try again */ + newarr = PyArray_FromAny(m2, NULL, 0, 0, 0); + if (newarr!=NULL) { + ret = newarr->ob_type->tp_as_number->nb_@name@(m1, newarr); + Py_DECREF(newarr); + } + else ret=NULL; + } + return ret; + } + arr=tup=NULL; + arr = PyArray_FromScalar(m1, NULL); + tup = PyArray_FromScalar(m2, NULL); + if (arr == NULL || tup == NULL) { + Py_XDECREF(tup); Py_XDECREF(arr); return NULL; + } + ret = arr->ob_type->tp_as_number->nb_@name@(arr, tup); + Py_DECREF(arr); + Py_DECREF(tup); + return ret; +} +/**end repeat**/ + + +static PyObject * +gentype_multiply(PyObject *m1, PyObject *m2) +{ + PyObject *arr, *ret=NULL, *tup; + long repeat; + + if (!PyArray_IsScalar(m1, Generic)) { + if (PyArray_Check(m1)) { + ret = m1->ob_type->tp_as_number->nb_multiply(m1,m2); + } + else if ((m1->ob_type->tp_as_number == NULL) || + (m1->ob_type->tp_as_number->nb_multiply == NULL)) { + /* Convert m2 to an int and assume sequence + repeat */ + repeat = PyInt_AsLong(m2); + if (repeat == -1 && PyErr_Occurred()) return NULL; + ret = PySequence_Repeat(m1, (int) repeat); + if (ret == NULL) { + PyErr_Clear(); + arr = PyArray_FromScalar(m2, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_as_number->\ + nb_multiply(m1, arr); + Py_DECREF(arr); + } + } + else { + PyObject *newarr; + /* Convert object to Array scalar and try again */ + newarr = PyArray_FromAny(m1, NULL, 0, 0, 0); + if (newarr!=NULL) { + ret = newarr->ob_type->tp_as_number->nb_multiply(newarr, m2); + Py_DECREF(newarr); + } + else ret=NULL; + } + return ret; + } + if (!PyArray_IsScalar(m2, Generic)) { + if (PyArray_Check(m2)) { + ret = m2->ob_type->tp_as_number->nb_multiply(m1,m2); + } + else if ((m2->ob_type->tp_as_number == NULL) || + (m2->ob_type->tp_as_number->nb_multiply == NULL)) { + /* Convert m1 to an int and assume sequence + repeat */ + repeat = PyInt_AsLong(m1); + if (repeat == -1 && PyErr_Occurred()) return NULL; + ret = PySequence_Repeat(m2, (int) repeat); + if (ret == NULL) { + PyErr_Clear(); + arr = PyArray_FromScalar(m1, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_as_number-> \ + nb_multiply(arr, m2); + Py_DECREF(arr); + } + } + else { + PyObject *newarr; + /* Convert object to Array scalar and try again */ + newarr = PyArray_FromAny(m2, NULL, 0, 0, 0); + if (newarr!=NULL) { + ret = newarr->ob_type->tp_as_number->nb_multiply(m1, newarr); + Py_DECREF(newarr); + } + else ret =NULL; + } + return ret; + } + /* Both are array scalar objects */ + arr=tup=NULL; + arr = PyArray_FromScalar(m1, NULL); + tup = PyArray_FromScalar(m2, NULL); + if (arr == NULL || tup == NULL) { + Py_XDECREF(tup); Py_XDECREF(arr); return NULL; + } + ret = arr->ob_type->tp_as_number->nb_multiply(arr, tup); + Py_DECREF(arr); + Py_DECREF(tup); + return ret; + +} + + + +/**begin repeat + +#name=negative, absolute, invert, int, long, float, oct, hex# +*/ + +static PyObject * +gentype_@name@(PyObject *m1) +{ + PyObject *arr, *ret; + + arr = PyArray_FromScalar(m1, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_as_number->nb_@name@(arr); + Py_DECREF(arr); + return ret; +} +/**end repeat**/ + +static int +gentype_nonzero_number(PyObject *m1) +{ + PyObject *arr; + int ret; + + arr = PyArray_FromScalar(m1, NULL); + if (arr == NULL) return -1; + ret = arr->ob_type->tp_as_number->nb_nonzero(arr); + Py_DECREF(arr); + return ret; +} + +static PyObject * +gentype_str(PyObject *self) +{ + PyArrayObject *arr; + PyObject *ret, *tmp; + + arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + if (arr==NULL) return NULL; + ret = PyObject_Str((tmp=arr->descr->f->getitem(arr->data, arr))); + Py_DECREF(arr); + Py_XDECREF(tmp); + return ret; +} + +static PyObject * +gentype_repr(PyObject *self) +{ + PyArrayObject *arr; + PyObject *ret, *tmp ; + + arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + if (arr==NULL) return NULL; + ret = PyObject_Repr((tmp=arr->descr->f->getitem(arr->data, arr))); + Py_DECREF(arr); + Py_XDECREF(tmp); + return ret; +} + +static void +format_longdouble(char *buf, size_t buflen, longdouble val, int precision) +{ + register char *cp; + + PyOS_snprintf(buf, buflen, "%.*" LONGDOUBLE_FMT, precision, val); + cp = buf; + if (*cp == '-') + cp++; + for (; *cp != '\0'; cp++) { + if (!isdigit(Py_CHARMASK(*cp))) + break; + } + if (*cp == '\0') { + *cp++ = '.'; + *cp++ = '0'; + *cp++ = '\0'; + } +} + +#if SIZEOF_LONGDOUBLE == SIZEOF_DOUBLE +#define PREC_REPR 15 +#define PREC_STR 15 +#else +#define PREC_REPR 21 +#define PREC_STR 21 +#endif + +static PyObject * +longdoubletype_repr(PyObject *self) +{ + static char buf[100]; + format_longdouble(buf, sizeof(buf), ((PyLongDoubleScalarObject *)self)->obval, PREC_REPR); + return PyString_FromString(buf); +} + +static PyObject * +clongdoubletype_repr(PyObject *self) +{ + static char buf1[100]; + static char buf2[100]; + static char buf3[202]; + clongdouble x; + x = ((PyCLongDoubleScalarObject *)self)->obval; + format_longdouble(buf1, sizeof(buf1), x.real, PREC_REPR); + format_longdouble(buf2, sizeof(buf2), x.imag, PREC_REPR); + + snprintf(buf3, sizeof(buf3), "(%s+%sj)", buf1, buf2); + return PyString_FromString(buf3); +} + +#define longdoubletype_str longdoubletype_repr +#define clongdoubletype_str clongdoubletype_repr + +/** Could improve this with a PyLong_FromLongDouble(longdouble ldval) + but this would need some more work... +**/ + +/**begin repeat + +#name=(int, long, hex, oct, float)*2# +#KIND=(Long*4, Float)*2# +#char=,,,,,c*5# +#CHAR=,,,,,C*5# +#POST=,,,,,.real*5# +*/ +static PyObject * +@char@longdoubletype_@name@(PyObject *self) +{ + double dval; + PyObject *obj, *ret; + + dval = (double)(((Py@CHAR@LongDoubleScalarObject *)self)->obval)@POST@; + obj = Py@KIND@_FromDouble(dval); + ret = obj->ob_type->tp_as_number->nb_@name@(obj); + Py_DECREF(obj); + return ret; +} +/**end repeat**/ + + +static PyObject *gentype_copy(PyObject *, PyObject *); + +static PyNumberMethods gentype_as_number = { + (binaryfunc)gentype_add, /*nb_add*/ + (binaryfunc)gentype_subtract, /*nb_subtract*/ + (binaryfunc)gentype_multiply, /*nb_multiply*/ + (binaryfunc)gentype_divide, /*nb_divide*/ + (binaryfunc)gentype_remainder, /*nb_remainder*/ + (binaryfunc)gentype_divmod, /*nb_divmod*/ + (ternaryfunc)gentype_power, /*nb_power*/ + (unaryfunc)gentype_negative, + (unaryfunc)gentype_copy, /*nb_pos*/ + (unaryfunc)gentype_absolute, /*(unaryfunc)gentype_abs,*/ + (inquiry)gentype_nonzero_number, /*nb_nonzero*/ + (unaryfunc)gentype_invert, /*nb_invert*/ + (binaryfunc)gentype_lshift, /*nb_lshift*/ + (binaryfunc)gentype_rshift, /*nb_rshift*/ + (binaryfunc)gentype_and, /*nb_and*/ + (binaryfunc)gentype_xor, /*nb_xor*/ + (binaryfunc)gentype_or, /*nb_or*/ + 0, /*nb_coerce*/ + (unaryfunc)gentype_int, /*nb_int*/ + (unaryfunc)gentype_long, /*nb_long*/ + (unaryfunc)gentype_float, /*nb_float*/ + (unaryfunc)gentype_oct, /*nb_oct*/ + (unaryfunc)gentype_hex, /*nb_hex*/ + 0, /*inplace_add*/ + 0, /*inplace_subtract*/ + 0, /*inplace_multiply*/ + 0, /*inplace_divide*/ + 0, /*inplace_remainder*/ + 0, /*inplace_power*/ + 0, /*inplace_lshift*/ + 0, /*inplace_rshift*/ + 0, /*inplace_and*/ + 0, /*inplace_xor*/ + 0, /*inplace_or*/ + (binaryfunc)gentype_floor_divide, /*nb_floor_divide*/ + (binaryfunc)gentype_true_divide, /*nb_true_divide*/ + 0, /*nb_inplace_floor_divide*/ + 0, /*nb_inplace_true_divide*/ + +}; + +static PyObject * +gentype_richcompare(PyObject *self, PyObject *other, int cmp_op) +{ + + PyObject *arr, *ret; + + arr = PyArray_FromScalar(self, NULL); + if (arr == NULL) return NULL; + ret = arr->ob_type->tp_richcompare(arr, other, cmp_op); + Py_DECREF(arr); + return ret; +} + +static PyObject * +gentype_ndim_get(PyObject *self) +{ + return PyInt_FromLong(0); +} + +static PyObject * +gentype_flags_get(PyObject *self) +{ + static int flags=CONTIGUOUS | OWNDATA | FORTRAN | ALIGNED; + + return PyObject_CallMethod(_scipy_internal, "flagsobj", "Oii", + self, flags, 1); +} + +static PyObject * +voidtype_flags_get(PyVoidScalarObject *self) +{ + return PyObject_CallMethod(_scipy_internal, "flagsobj", "Oii", + self, self->flags, 1); +} + +static PyObject * +voidtype_dtypedescr_get(PyVoidScalarObject *self) +{ + Py_INCREF(self->descr); + return (PyObject *)self->descr; +} + + + +static PyObject * +gentype_shape_get(PyObject *self) +{ + return PyTuple_New(0); +} + +/* +static int +gentype_shape_set(PyObject *self, PyObject *val) +{ + if (!PyTuple_Check(val) || PyTuple_GET_SIZE(val) > 0) { + PyErr_SetString(PyExc_ValueError, \ + "invalid shape for scalar"); + return -1; + } + return 0; +} +*/ + +static PyObject * +gentype_dataptr_get(PyObject *self) +{ + return Py_BuildValue("NO",PyString_FromString(""),Py_True); +} + + +static PyObject * +gentype_data_get(PyObject *self) +{ + PyArray_Descr *typecode; + PyObject *ret; + + typecode = PyArray_DescrFromScalar(self); + ret = PyBuffer_FromObject(self, 0, typecode->elsize); + Py_DECREF(typecode); + return ret; +} + + +static PyObject * +gentype_itemsize_get(PyObject *self) +{ + PyArray_Descr *typecode; + PyObject *ret; + + typecode = PyArray_DescrFromScalar(self); + ret = PyInt_FromLong((long) typecode->elsize); + Py_DECREF(typecode); + return ret; +} + +static PyObject * +gentype_size_get(PyObject *self) +{ + return PyInt_FromLong(1); +} + + +static PyObject * +gentype_typechar_get(PyObject *self) +{ + PyArray_Descr *descr; + char type; + int elsize; + + descr = PyArray_DescrFromScalar(self); + type = descr->type; + elsize = descr->elsize; + Py_DECREF(descr); + if (PyArray_IsScalar(self, Flexible)) + return PyString_FromFormat("%c%d", (int)type, elsize); + else + return PyString_FromStringAndSize(&type, 1); +} + +static void +gentype_struct_free(void *ptr, void *arr) +{ + Py_DECREF((PyObject *)arr); + _pya_free(ptr); +} + +static PyObject * +gentype_struct_get(PyObject *self) +{ + PyArrayObject *arr; + PyArrayInterface *inter; + + arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + inter = (PyArrayInterface *)_pya_malloc(sizeof(PyArrayInterface)); + inter->version = 2; + inter->nd = 0; + inter->flags = arr->flags; + inter->typekind = arr->descr->kind; + inter->itemsize = arr->descr->elsize; + inter->strides = NULL; + inter->shape = NULL; + inter->data = arr->data; + return PyCObject_FromVoidPtrAndDesc(inter, arr, gentype_struct_free); +} + +static PyObject * +gentype_typestr_get(PyObject *self) +{ + PyArrayObject *arr; + PyObject *ret; + + arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + ret = PyObject_GetAttrString((PyObject *)arr, "dtypestr"); + Py_DECREF(arr); + return ret; +} + +static PyObject * +gentype_descr_get(PyObject *self) +{ + PyArrayObject *arr; + PyObject *ret; + + arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + ret = PyObject_GetAttrString((PyObject *)arr, "__array_descr__"); + Py_DECREF(arr); + return ret; +} + + +static PyObject * +gentype_type_get(PyObject *self) +{ + Py_INCREF(self->ob_type); + return (PyObject *)self->ob_type; +} + +static PyObject * +gentype_typedescr_get(PyObject *self) +{ + return (PyObject *)PyArray_DescrFromScalar(self); +} + + +static PyObject * +gentype_base_get(PyObject *self) +{ + Py_INCREF(Py_None); + return Py_None; +} + + +static PyArray_Descr * +_realdescr_fromcomplexscalar(PyObject *self, int *typenum) +{ + if PyArray_IsScalar(self, CDouble) { + *typenum = PyArray_CDOUBLE; + return PyArray_DescrFromType(PyArray_DOUBLE); + } + if PyArray_IsScalar(self, CFloat) { + *typenum = PyArray_CFLOAT; + return PyArray_DescrFromType(PyArray_FLOAT); + } + if PyArray_IsScalar(self, CLongDouble) { + *typenum = PyArray_CLONGDOUBLE; + return PyArray_DescrFromType(PyArray_LONGDOUBLE); + } + return NULL; +} + +static PyObject * +gentype_real_get(PyObject *self) +{ + PyArray_Descr *typecode; + PyObject *ret; + int typenum; + + if (PyArray_IsScalar(self, ComplexFloating)) { + typecode = _realdescr_fromcomplexscalar(self, &typenum); + ret = PyArray_Scalar(_SOFFSET_(self, typenum), typecode, + NULL); + Py_DECREF(typecode); + return ret; + } + else if PyArray_IsScalar(self, Object) { + PyObject *obj = ((PyObjectScalarObject *)self)->obval; + ret = PyObject_GetAttrString(obj, "real"); + if (ret != NULL) return ret; + PyErr_Clear(); + } + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * +gentype_imag_get(PyObject *self) +{ + PyArray_Descr *typecode; + PyObject *ret; + int typenum; + + typecode = _realdescr_fromcomplexscalar(self, &typenum); + if (PyArray_IsScalar(self, ComplexFloating)) { + ret = PyArray_Scalar(_SOFFSET_(self, typenum) \ + + typecode->elsize, typecode, NULL); + } + else if PyArray_IsScalar(self, Object) { + PyObject *obj = ((PyObjectScalarObject *)self)->obval; + PyArray_Descr *newtype; + ret = PyObject_GetAttrString(obj, "imag"); + if (ret == NULL) { + PyErr_Clear(); + obj = PyInt_FromLong(0); + newtype = PyArray_DescrFromType(PyArray_OBJECT); + ret = PyArray_Scalar((char *)&obj, newtype, NULL); + Py_DECREF(newtype); + Py_DECREF(obj); + } + } + else { + char *temp; + temp = PyDataMem_NEW(typecode->elsize); + memset(temp, '\0', typecode->elsize); + ret = PyArray_Scalar(temp, typecode, NULL); + PyDataMem_FREE(temp); + } + + Py_DECREF(typecode); + return ret; +} + +static PyObject * +gentype_flat_get(PyObject *self) +{ + PyObject *ret, *arr; + + arr = PyArray_FromScalar(self, NULL); + if (arr == NULL) return NULL; + ret = PyArray_IterNew(arr); + Py_DECREF(arr); + return ret; +} + +static PyGetSetDef gentype_getsets[] = { + {"ndim", + (getter)gentype_ndim_get, + (setter) 0, + "number of array dimensions"}, + {"flags", + (getter)gentype_flags_get, + (setter)0, + "integer value of flags"}, + {"shape", + (getter)gentype_shape_get, + (setter)0, + "tuple of array dimensions"}, + {"strides", + (getter)gentype_shape_get, + (setter) 0, + "tuple of bytes steps in each dimension"}, + {"data", + (getter)gentype_data_get, + (setter) 0, + "pointer to start of data"}, + {"itemsize", + (getter)gentype_itemsize_get, + (setter)0, + "length of one element in bytes"}, + {"size", + (getter)gentype_size_get, + (setter)0, + "number of elements in the gentype"}, + {"nbytes", + (getter)gentype_itemsize_get, + (setter)0, + "length of item in bytes"}, + {"base", + (getter)gentype_base_get, + (setter)0, + "base object"}, + {"dtype", + (getter)gentype_type_get, + (setter)0, + "get gentype type class"}, + {"dtypechar", + (getter)gentype_typechar_get, + (setter)0, + "get gentype type character code"}, + {"dtypestr", + (getter)gentype_typestr_get, + NULL, + "get array type string"}, + {"dtypedescr", + (getter)gentype_typedescr_get, + NULL, + "get array data-descriptor"}, + {"real", + (getter)gentype_real_get, + (setter)0, + "real part of scalar"}, + {"imag", + (getter)gentype_imag_get, + (setter)0, + "imaginary part of scalar"}, + {"flat", + (getter)gentype_flat_get, + (setter)0, + "a 1-d view of scalar"}, + {"__array_data__", + (getter)gentype_dataptr_get, + NULL, + "Array protocol: data"}, + {"__array_typestr__", + (getter)gentype_typestr_get, + NULL, + "Array protocol: typestr"}, + {"__array_descr__", + (getter)gentype_descr_get, + NULL, + "Array protocol: descr"}, + {"__array_shape__", + (getter)gentype_shape_get, + NULL, + "Array protocol: shape"}, + {"__array_strides__", + (getter)gentype_shape_get, + NULL, + "Array protocol: strides"}, + {"__array_struct__", + (getter)gentype_struct_get, + NULL, + "Array protocol: struct"}, + /* Does not have __array_priority__ because it is not a subtype. + */ + {NULL, NULL, NULL, NULL} /* Sentinel */ +}; + + +/* 0-dim array from scalar object */ + +static char doc_getarray[] = "sc.__array__(|type) return 0-dim array"; + +static PyObject * +gentype_getarray(PyObject *scalar, PyObject *args) +{ + PyArray_Descr *outcode=NULL; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "|O&", &PyArray_DescrConverter, + &outcode)) return NULL; + ret = PyArray_FromScalar(scalar, outcode); + return ret; +} + +static char doc_sc_wraparray[] = "sc.__array_wrap__(obj) return scalar from array"; + +static PyObject * +gentype_wraparray(PyObject *scalar, PyObject *args) +{ + PyObject *arr; + + if (PyTuple_Size(args) < 1) { + PyErr_SetString(PyExc_TypeError, + "only accepts 1 argument."); + return NULL; + } + arr = PyTuple_GET_ITEM(args, 0); + if (!PyArray_Check(arr)) { + PyErr_SetString(PyExc_TypeError, + "can only be called with ndarray object"); + return NULL; + } + + return PyArray_Scalar(PyArray_DATA(arr), PyArray_DESCR(arr), arr); +} + + +/**begin repeat + +#name=tolist, item, tostring, astype, copy, resize, __deepcopy__, choose, searchsorted, argmax, argmin, reshape, view, swapaxes, max, min, ptp, conj, conjugate, nonzero, all, any, flatten, ravel, fill, transpose, newbyteorder# +*/ + +static PyObject * +gentype_@name@(PyObject *self, PyObject *args) +{ + return gentype_generic_method(self, args, NULL, "@name@"); +} +/**end repeat**/ + +static PyObject * +gentype_squeeze(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) return NULL; + Py_INCREF(self); + return self; +} + +static int +gentype_getreadbuf(PyObject *, int, void **); + +static PyObject * +gentype_byteswap(PyObject *self, PyObject *args) +{ + Bool inplace=FALSE; + + if (!PyArg_ParseTuple(args, "|O&", PyArray_BoolConverter, &inplace)) + return NULL; + + if (inplace) { + PyErr_SetString(PyExc_ValueError, + "cannot byteswap a scalar in-place"); + return NULL; + } + else { + /* get the data, copyswap it and pass it to a new Array scalar + */ + char *data; + int numbytes; + PyArray_Descr *descr; + PyObject *new; + char *newmem; + + numbytes = gentype_getreadbuf(self, 0, (void **)&data); + descr = PyArray_DescrFromScalar(self); + newmem = _pya_malloc(descr->elsize); + if (newmem == NULL) {Py_DECREF(descr); return PyErr_NoMemory();} + else memcpy(newmem, data, descr->elsize); + descr->f->copyswap(newmem, NULL, 1, descr->elsize); + new = PyArray_Scalar(newmem, descr, NULL); + _pya_free(newmem); + Py_DECREF(descr); + return new; + } +} + + +/**begin repeat + +#name=take, getfield, put, putmask, repeat, tofile, mean, trace, diagonal, clip, std, var, sum, cumsum, prod, cumprod, compress, sort, argsort# +*/ + +static PyObject * +gentype_@name@(PyObject *self, PyObject *args, PyObject *kwds) +{ + return gentype_generic_method(self, args, kwds, "@name@"); +} +/**end repeat**/ + +static PyObject * +voidtype_getfield(PyVoidScalarObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *ret; + + ret = gentype_generic_method((PyObject *)self, args, kwds, "getfield"); + if (!ret) return ret; + if (PyArray_IsScalar(ret, Generic) && \ + (!PyArray_IsScalar(ret, Void))) { + PyArray_Descr *new; + if (!PyArray_ISNBO(self->descr->byteorder)) { + new = PyArray_DescrFromScalar(ret); + new->f->copyswap(_SOFFSET_(ret, + new->type_num), + NULL, 1, new->elsize); + Py_DECREF(new); + } + } + return ret; +} + +static PyObject * +gentype_setfield(PyObject *self, PyObject *args, PyObject *kwds) +{ + + PyErr_SetString(PyExc_TypeError, + "Can't set fields in a non-void array scalar."); + return NULL; +} + +static PyObject * +voidtype_setfield(PyVoidScalarObject *self, PyObject *args, PyObject *kwds) +{ + PyArray_Descr *typecode; + int offset = 0; + PyObject *value, *src; + int mysize; + char *dptr; + static char *kwlist[] = {"value", "dtype", "offset", 0}; + + if ((self->flags & WRITEABLE) != WRITEABLE) { + PyErr_SetString(PyExc_RuntimeError, + "Can't write to memory"); + return NULL; + } + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|i", kwlist, + &value, + PyArray_DescrConverter, + &typecode, &offset)) return NULL; + + mysize = self->ob_size; + + if (offset < 0 || (offset + typecode->elsize) > mysize) { + PyErr_Format(PyExc_ValueError, + "Need 0 <= offset <= %d for requested type " \ + "but received offset = %d", + mysize-typecode->elsize, offset); + Py_DECREF(typecode); + return NULL; + } + + dptr = self->obval + offset; + + /* Copy data from value to correct place in dptr */ + src = PyArray_FromAny(value, typecode, 0, 0, CARRAY_FLAGS); + if (src == NULL) return NULL; + typecode->f->copyswap(dptr, PyArray_DATA(src), + !PyArray_ISNBO(self->descr->byteorder), + PyArray_ITEMSIZE(src)); + Py_DECREF(src); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gentype_reduce(PyObject *self, PyObject *args) +{ + PyObject *ret=NULL, *obj=NULL, *mod=NULL; + const char *buffer; + int buflen; + + /* Return a tuple of (callable object, arguments) */ + + ret = PyTuple_New(2); + if (ret == NULL) return NULL; + if (PyObject_AsReadBuffer(self, (const void **)&buffer, &buflen)<0) { + Py_DECREF(ret); return NULL; + } + mod = PyImport_ImportModule("scipy.base.multiarray"); + if (mod == NULL) return NULL; + obj = PyObject_GetAttrString(mod, "scalar"); + Py_DECREF(mod); + if (obj == NULL) return NULL; + PyTuple_SET_ITEM(ret, 0, obj); + obj = PyObject_GetAttrString((PyObject *)self, "dtypedescr"); + if PyArray_IsScalar(self, Object) { + mod = ((PyObjectScalarObject *)self)->obval; + PyTuple_SET_ITEM(ret, 1, + Py_BuildValue("NO", obj, mod)); + } + else { + mod = PyString_FromStringAndSize(buffer, buflen); + PyTuple_SET_ITEM(ret, 1, + Py_BuildValue("NN", obj, mod)); + } + return ret; +} + +/* ignores everything */ +static PyObject * +gentype_setstate(PyObject *self, PyObject *args) +{ + Py_INCREF(Py_None); + return (Py_None); +} + +static PyObject * +gentype_dump(PyObject *self, PyObject *args) +{ + PyObject *file=NULL; + int ret; + + if (!PyArg_ParseTuple(args, "O", &file)) + return NULL; + ret = PyArray_Dump(self, file, 2); + if (ret < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +gentype_dumps(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyArray_Dumps(self, 2); +} + + +/* setting flags cannot be done for scalars */ +static PyObject * +gentype_setflags(PyObject *self, PyObject *args, PyObject *kwds) +{ + Py_INCREF(Py_None); + return Py_None; +} + + +/* need to fill in doc-strings for these methods on import -- copy from + array docstrings +*/ +static PyMethodDef gentype_methods[] = { + {"tolist", (PyCFunction)gentype_tolist, 1, NULL}, + {"item", (PyCFunction)gentype_item, METH_VARARGS, NULL}, + {"tofile", (PyCFunction)gentype_tofile, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"tostring", (PyCFunction)gentype_tostring, METH_VARARGS, NULL}, + {"byteswap", (PyCFunction)gentype_byteswap,1, NULL}, + {"astype", (PyCFunction)gentype_astype, 1, NULL}, + {"getfield", (PyCFunction)gentype_getfield, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"setfield", (PyCFunction)gentype_setfield, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"copy", (PyCFunction)gentype_copy, 1, NULL}, + {"resize", (PyCFunction)gentype_resize, 1, NULL}, + + {"__array__", (PyCFunction)gentype_getarray, 1, doc_getarray}, + {"__array_wrap__", (PyCFunction)gentype_wraparray, 1, doc_sc_wraparray}, + + /* for the copy module */ + {"__copy__", (PyCFunction)gentype_copy, 1, NULL}, + {"__deepcopy__", (PyCFunction)gentype___deepcopy__, 1, NULL}, + + + {"__reduce__", (PyCFunction) gentype_reduce, 1, NULL}, + /* For consistency does nothing */ + {"__setstate__", (PyCFunction) gentype_setstate, 1, NULL}, + + {"dumps", (PyCFunction) gentype_dumps, 1, NULL}, + {"dump", (PyCFunction) gentype_dump, 1, NULL}, + + /* Methods for array */ + {"fill", (PyCFunction)gentype_fill, + METH_VARARGS, NULL}, + {"transpose", (PyCFunction)gentype_transpose, + METH_VARARGS, NULL}, + {"take", (PyCFunction)gentype_take, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"put", (PyCFunction)gentype_put, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"putmask", (PyCFunction)gentype_putmask, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"repeat", (PyCFunction)gentype_repeat, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"choose", (PyCFunction)gentype_choose, + METH_VARARGS, NULL}, + {"sort", (PyCFunction)gentype_sort, + METH_VARARGS, NULL}, + {"argsort", (PyCFunction)gentype_argsort, + METH_VARARGS, NULL}, + {"searchsorted", (PyCFunction)gentype_searchsorted, + METH_VARARGS, NULL}, + {"argmax", (PyCFunction)gentype_argmax, + METH_VARARGS, NULL}, + {"argmin", (PyCFunction)gentype_argmin, + METH_VARARGS, NULL}, + {"reshape", (PyCFunction)gentype_reshape, + METH_VARARGS, NULL}, + {"squeeze", (PyCFunction)gentype_squeeze, + METH_VARARGS, NULL}, + {"view", (PyCFunction)gentype_view, + METH_VARARGS, NULL}, + {"swapaxes", (PyCFunction)gentype_swapaxes, + METH_VARARGS, NULL}, + {"max", (PyCFunction)gentype_max, + METH_VARARGS, NULL}, + {"min", (PyCFunction)gentype_min, + METH_VARARGS, NULL}, + {"ptp", (PyCFunction)gentype_ptp, + METH_VARARGS, NULL}, + {"mean", (PyCFunction)gentype_mean, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"trace", (PyCFunction)gentype_trace, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"diagonal", (PyCFunction)gentype_diagonal, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"clip", (PyCFunction)gentype_clip, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"conj", (PyCFunction)gentype_conj, + METH_VARARGS, NULL}, + {"conjugate", (PyCFunction)gentype_conjugate, + METH_VARARGS, NULL}, + {"nonzero", (PyCFunction)gentype_nonzero, + METH_VARARGS, NULL}, + {"std", (PyCFunction)gentype_std, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"var", (PyCFunction)gentype_var, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"sum", (PyCFunction)gentype_sum, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"cumsum", (PyCFunction)gentype_cumsum, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"prod", (PyCFunction)gentype_prod, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"cumprod", (PyCFunction)gentype_cumprod, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"all", (PyCFunction)gentype_all, + METH_VARARGS, NULL}, + {"any", (PyCFunction)gentype_any, + METH_VARARGS, NULL}, + {"compress", (PyCFunction)gentype_compress, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"flatten", (PyCFunction)gentype_flatten, + METH_VARARGS, NULL}, + {"ravel", (PyCFunction)gentype_ravel, + METH_VARARGS, NULL}, + {"setflags", (PyCFunction)gentype_setflags, + METH_VARARGS|METH_KEYWORDS, NULL}, + {"newbyteorder", (PyCFunction)gentype_newbyteorder, + METH_VARARGS, NULL}, + {NULL, NULL} /* sentinel */ +}; + + +static PyGetSetDef voidtype_getsets[] = { + {"flags", + (getter)voidtype_flags_get, + (setter)0, + "integer value of flags"}, + {"dtypedescr", + (getter)voidtype_dtypedescr_get, + (setter)0, + "dtypedescr object"}, + {NULL, NULL} +}; + +static PyMethodDef voidtype_methods[] = { + {"getfield", (PyCFunction)voidtype_getfield, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"setfield", (PyCFunction)voidtype_setfield, + METH_VARARGS | METH_KEYWORDS, NULL}, + {NULL, NULL} +}; + +/************* As_mapping functions for void array scalar ************/ + + +static int +voidtype_length(PyVoidScalarObject *self) +{ + if (!self->descr->fields || self->descr->fields == Py_None) { + return 0; + } + else { /* return the number of fields */ + PyObject *key; + PyObject *flist; + key = PyInt_FromLong(-1); + flist = PyDict_GetItem(self->descr->fields, key); + Py_DECREF(key); + if (!flist) return 0; + return PyList_GET_SIZE(flist); + } +} + +/* get field by name or number */ +static PyObject * +voidtype_subscript(PyVoidScalarObject *self, PyObject *ind) +{ + int n, m; + char *msg = "invalid index"; + PyObject *flist=NULL, *key, *fieldinfo; + + if (!self->descr->fields || self->descr->fields == Py_None) { + PyErr_SetString(PyExc_IndexError, + "can't index void scalar without fields"); + return NULL; + } + + if (PyString_Check(ind) || PyUnicode_Check(ind)) { + /* look up in fields */ + fieldinfo = PyDict_GetItem(self->descr->fields, ind); + if (!fieldinfo) { + PyErr_SetString(PyExc_IndexError, msg); + return NULL; + } + return voidtype_getfield(self, fieldinfo, NULL); + } + + /* try to convert it to a number */ + n = PyArray_PyIntAsIntp(ind); + if (error_converting(n)) { + PyErr_Clear(); + goto fail; + } + key = PyInt_FromLong(-1); + flist = PyDict_GetItem(self->descr->fields, key); + Py_DECREF(key); + if (!flist) m = 0; + m = PyList_GET_SIZE(flist); + if (n < 0) n += m; + if (n < 0 || n >= m) goto fail; + fieldinfo = PyDict_GetItem(self->descr->fields, + PyList_GET_ITEM(flist, n)); + return voidtype_getfield(self, fieldinfo, NULL); + + fail: + PyErr_SetString(PyExc_IndexError, msg); + return NULL; +} + +static int +voidtype_ass_subscript(PyVoidScalarObject *self, PyObject *ind, PyObject *val) +{ + int n, m; + char *msg = "invalid index"; + PyObject *flist=NULL, *key, *fieldinfo, *newtup; + PyObject *res; + + if (!self->descr->fields || self->descr->fields == Py_None) { + PyErr_SetString(PyExc_IndexError, + "can't index void scalar without fields"); + return -1; + } + + if (PyString_Check(ind) || PyUnicode_Check(ind)) { + /* look up in fields */ + fieldinfo = PyDict_GetItem(self->descr->fields, ind); + if (!fieldinfo) { + PyErr_SetString(PyExc_IndexError, msg); + return -1; + } + newtup = Py_BuildValue("(OOO)", val, + PyTuple_GET_ITEM(fieldinfo, 0), + PyTuple_GET_ITEM(fieldinfo, 1)); + res = voidtype_setfield(self, newtup, NULL); + Py_DECREF(newtup); + if (!res) return -1; + Py_DECREF(res); + return 0; + } + + /* try to convert it to a number */ + n = PyArray_PyIntAsIntp(ind); + if (error_converting(n)) { + PyErr_Clear(); + goto fail; + } + key = PyInt_FromLong(-1); + flist = PyDict_GetItem(self->descr->fields, key); + Py_DECREF(key); + if (!flist) m = 0; + m = PyList_GET_SIZE(flist); + if (n < 0) n += m; + if (n < 0 || n >= m) goto fail; + fieldinfo = PyDict_GetItem(self->descr->fields, + PyList_GET_ITEM(flist, n)); + newtup = Py_BuildValue("(OOO)", val, + PyTuple_GET_ITEM(fieldinfo, 0), + PyTuple_GET_ITEM(fieldinfo, 1)); + res = voidtype_setfield(self, fieldinfo, NULL); + Py_DECREF(newtup); + if (!res) return -1; + Py_DECREF(res); + return 0; + + fail: + PyErr_SetString(PyExc_IndexError, msg); + return -1; +} + +static PyMappingMethods voidtype_as_mapping = { + (inquiry)voidtype_length, /*mp_length*/ + (binaryfunc)voidtype_subscript, /*mp_subscript*/ + (objobjargproc)voidtype_ass_subscript, /*mp_ass_subscript*/ +}; + + +static int +gentype_getreadbuf(PyObject *self, int segment, void **ptrptr) +{ + int numbytes; + PyArray_Descr *outcode; + + if (segment != 0) { + PyErr_SetString(PyExc_SystemError, + "Accessing non-existent array segment"); + return -1; + } + + outcode = PyArray_DescrFromScalar(self); + numbytes = outcode->elsize; + if PyArray_IsScalar(self, Flexible) { + if PyArray_IsScalar(self, String) + *ptrptr = PyString_AS_STRING(self); + else if PyArray_IsScalar(self, Unicode) + *ptrptr = (char *)PyUnicode_AS_DATA(self); + else if PyArray_IsScalar(self, Void) + *ptrptr = ((PyVoidScalarObject *)self)->obval; + } + else + *ptrptr = (void *)_SOFFSET_(self, outcode->type_num); + + Py_DECREF(outcode); + return numbytes; +} + +static int +gentype_getsegcount(PyObject *self, int *lenp) +{ + PyArray_Descr *outcode; + + outcode = PyArray_DescrFromScalar(self); + if (lenp) + *lenp = outcode->elsize; + Py_DECREF(outcode); + return 1; +} + +static int +gentype_getcharbuf(PyObject *self, int segment, const char **ptrptr) +{ + if (PyArray_IsScalar(self, String) || \ + PyArray_IsScalar(self, Unicode)) + return gentype_getreadbuf(self, segment, (void **)ptrptr); + else { + PyErr_SetString(PyExc_TypeError, + "Non-character array cannot be interpreted "\ + "as character buffer."); + return -1; + } +} + + +static PyBufferProcs gentype_as_buffer = { + (getreadbufferproc)gentype_getreadbuf, /*bf_getreadbuffer*/ + (getwritebufferproc)0, /*bf_getwritebuffer*/ + (getsegcountproc)gentype_getsegcount, /*bf_getsegcount*/ + (getcharbufferproc)gentype_getcharbuf, /*bf_getcharbuffer*/ +}; + + +#define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES +#define LEAFFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES + +static PyTypeObject PyGenericArrType_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "generic_arrtype", /*tp_name*/ + sizeof(PyObject), /*tp_basicsize*/ +}; + +static void +unicode_dealloc(PyObject *v) +{ + PyDataMem_FREE(((PyVoidScalarObject *)v)->obval); + v->ob_type->tp_free(v); +} + +static void +void_dealloc(PyVoidScalarObject *v) +{ + if (v->flags & OWNDATA) + PyDataMem_FREE(v->obval); + Py_XDECREF(v->descr); + Py_XDECREF(v->base); + v->ob_type->tp_free(v); +} + +static void +object_arrtype_dealloc(PyObject *v) +{ + Py_XDECREF(((PyObjectScalarObject *)v)->obval); + v->ob_type->tp_free(v); +} + +/* string and unicode inherit from Python Type first and so GET_ITEM is different to + get to the Python Type. + */ + +/**begin repeat +#name=byte, short, int, long, longlong, ubyte, ushort, uint, ulong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, string, unicode, object# +#TYPE=BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, OBJECT# +#num=1*16,0,0,1# +*/ +static PyObject * +@name@_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj=NULL; + PyObject *arr; + PyArray_Descr *typecode; + + if (type->tp_bases && (PyTuple_GET_SIZE(type->tp_bases)==2)) { + PyTypeObject *sup; + PyObject *ret; + /* We are inheriting from a Python type as well so + give it first dibs on conversion */ + sup = (PyTypeObject *)PyTuple_GET_ITEM(type->tp_bases, @num@); + ret = sup->tp_new(type, args, kwds); + if (ret) return ret; + PyErr_Clear(); + /* now do default conversion */ + } + + if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; + + typecode = PyArray_DescrFromType(PyArray_@TYPE@); + arr = PyArray_FromAny(obj, typecode, 0, 0, FORCECAST); + return PyArray_Return((PyArrayObject *)arr); +} +/**end repeat**/ + +/* bool->tp_new only returns Py_True or Py_False */ +static PyObject * +bool_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj=NULL; + PyObject *arr; + + if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; + + arr = PyArray_FROM_OTF(obj, PyArray_BOOL, FORCECAST); + return PyArray_Return((PyArrayObject *)arr); +} + +static PyObject * +void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj, *arr; + ulonglong memu=1; + PyObject *new=NULL; + char *destptr; + + if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; + /* For a VOID scalar first see if obj is an integer or long + and create new memory of that size (filled with 0) for the scalar + */ + + if (PyLong_Check(obj) || PyInt_Check(obj) || \ + PyArray_IsScalar(obj, Integer) || + (PyArray_Check(obj) && PyArray_NDIM(obj)==0 && \ + PyArray_ISINTEGER(obj))) { + new = obj->ob_type->tp_as_number->nb_long(obj); + } + if (new && PyLong_Check(new)) { + PyObject *ret; + memu = PyLong_AsUnsignedLongLong(new); + Py_DECREF(new); + if (PyErr_Occurred() || (memu > MAX_INT)) { + PyErr_Clear(); + PyErr_Format(PyExc_OverflowError, + "size must be smaller than %d", + (int) MAX_INT); + return NULL; + } + destptr = PyDataMem_NEW((int) memu); + if (destptr == NULL) return PyErr_NoMemory(); + ret = type->tp_alloc(type, 0); + if (ret == NULL) { + PyDataMem_FREE(destptr); + return PyErr_NoMemory(); + } + ((PyVoidScalarObject *)ret)->obval = destptr; + ((PyVoidScalarObject *)ret)->ob_size = (int) memu; + ((PyVoidScalarObject *)ret)->descr = \ + PyArray_DescrNewFromType(PyArray_VOID); + ((PyVoidScalarObject *)ret)->descr->elsize = (int) memu; + ((PyVoidScalarObject *)ret)->flags = BEHAVED_FLAGS | OWNDATA; + ((PyVoidScalarObject *)ret)->base = NULL; + memset(destptr, '\0', (size_t) memu); + return ret; + } + + arr = PyArray_FROM_OTF(obj, PyArray_VOID, FORCECAST); + return PyArray_Return((PyArrayObject *)arr); +} + + +/**************** Define Hash functions ********************/ + +/**begin repeat +#lname=bool,ubyte,ushort# +#name=Bool,UByte, UShort# + */ +static long +@lname@_arrtype_hash(PyObject *obj) +{ + return (long)(((Py@name@ScalarObject *)obj)->obval); +} +/**end repeat**/ + +/**begin repeat +#lname=byte,short,uint,ulong# +#name=Byte,Short,UInt,ULong# + */ +static long +@lname@_arrtype_hash(PyObject *obj) +{ + long x = (long)(((Py@name@ScalarObject *)obj)->obval); + if (x == -1) x=-2; + return x; +} +/**end repeat**/ + +#if SIZEOF_INT != SIZEOF_LONG +static long +int_arrtype_hash(PyObject *obj) +{ + long x = (long)(((PyIntScalarObject *)obj)->obval); + if (x == -1) x=-2; + return x; +} +#endif + +/**begin repeat +#char=,u# +#Char=,U# +#ext=&& (x >= LONG_MIN),# +*/ +#if SIZEOF_LONG != SIZEOF_LONGLONG +/* we assume SIZEOF_LONGLONG=2*SIZEOF_LONG */ +static long +@char@longlong_arrtype_hash(PyObject *obj) +{ + long y; + @char@longlong x = (((Py@Char@LongLongScalarObject *)obj)->obval); + + if ((x <= LONG_MAX)@ext@) { + y = (long) x; + } + else { + union Mask { + long hashvals[2]; + @char@longlong v; + } both; + + both.v = x; + y = both.hashvals[0] + (1000003)*both.hashvals[1]; + } + if (y == -1) y = -2; + return y; +} +#endif +/**end repeat**/ + +#if SIZEOF_LONG==SIZEOF_LONGLONG +static long +ulonglong_arrtype_hash(PyObject *obj) +{ + long x = (long)(((PyULongLongScalarObject *)obj)->obval); + if (x == -1) x=-2; + return x; +} +#endif + + + +/* Wrong thing to do for longdouble, but....*/ +/**begin repeat +#lname=float, longdouble# +#name=Float, LongDouble# + */ +static long +@lname@_arrtype_hash(PyObject *obj) +{ + return _Py_HashDouble((double) ((Py@name@ScalarObject *)obj)->obval); +} + +/* borrowed from complex_hash */ +static long +c@lname@_arrtype_hash(PyObject *obj) +{ + long hashreal, hashimag, combined; + hashreal = _Py_HashDouble((double) \ + (((PyC@name@ScalarObject *)obj)->obval).real); + + if (hashreal == -1) return -1; + hashimag = _Py_HashDouble((double) \ + (((PyC@name@ScalarObject *)obj)->obval).imag); + if (hashimag == -1) return -1; + + combined = hashreal + 1000003 * hashimag; + if (combined == -1) combined = -2; + return combined; +} +/**end repeat**/ + +static long +object_arrtype_hash(PyObject *obj) +{ + return PyObject_Hash(((PyObjectScalarObject *)obj)->obval); +} + +/* just hash the pointer */ +static long +void_arrtype_hash(PyObject *obj) +{ + return _Py_HashPointer((void *)(((PyVoidScalarObject *)obj)->obval)); +} + + +/*object arrtype getattro and setattro */ +static PyObject * +object_getattro(PyObjectScalarObject *obj, PyObject *attr) { + PyObject *res; + /* first look in generic type and then hand off to actual object */ + + res = PyObject_GenericGetAttr((PyObject *)obj, attr); + if (res) return res; + PyErr_Clear(); + return PyObject_GenericGetAttr(obj->obval, attr); +} + +static int +object_setattro(PyObjectScalarObject *obj, PyObject *attr, PyObject *val) { + int res; + /* first look in generic type and then hand off to actual object */ + + res = PyObject_GenericSetAttr((PyObject *)obj, attr, val); + if (res >= 0) return res; + PyErr_Clear(); + return PyObject_GenericSetAttr(obj->obval, attr, val); +} + + +/**begin repeat +#name=bool, string, unicode, void, object# +#NAME=Bool, String, Unicode, Void, Object# +*/ +static PyTypeObject Py@NAME@ArrType_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@name@_arrtype", /*tp_name*/ + sizeof(Py@NAME@ScalarObject), /*tp_basicsize*/ +}; +/**end repeat**/ + +/**begin repeat +#NAME=Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble# +#name=int*5, uint*5, float*3, complex*3# +#CNAME=(CHAR, SHORT, INT, LONG, LONGLONG)*2, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE# +*/ +static PyTypeObject Py@NAME@ArrType_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "@name@" STRBITSOF_@CNAME@ "_arrtype", /*tp_name*/ + sizeof(Py@NAME@ScalarObject), /*tp_basicsize*/ +}; + +/**end repeat**/ + + +static PyNumberMethods longdoubletype_as_number; +static PyNumberMethods clongdoubletype_as_number; + + +static void +initialize_numeric_types(void) +{ + PyGenericArrType_Type.tp_dealloc = (destructor)gentype_dealloc; + PyGenericArrType_Type.tp_as_number = &gentype_as_number; + PyGenericArrType_Type.tp_as_buffer = &gentype_as_buffer; + PyGenericArrType_Type.tp_flags = BASEFLAGS; + PyGenericArrType_Type.tp_methods = gentype_methods; + PyGenericArrType_Type.tp_getset = gentype_getsets; + PyGenericArrType_Type.tp_new = NULL; + PyGenericArrType_Type.tp_alloc = gentype_alloc; + PyGenericArrType_Type.tp_free = _pya_free; + PyGenericArrType_Type.tp_repr = gentype_repr; + PyGenericArrType_Type.tp_str = gentype_str; + PyGenericArrType_Type.tp_richcompare = gentype_richcompare; + + PyStringArrType_Type.tp_alloc = NULL; + PyStringArrType_Type.tp_free = NULL; + + PyVoidArrType_Type.tp_methods = voidtype_methods; + PyVoidArrType_Type.tp_getset = voidtype_getsets; + PyVoidArrType_Type.tp_as_mapping = &voidtype_as_mapping; + + /**begin repeat +#NAME=Numeric, Integer, SignedInteger, UnsignedInteger, Inexact, Floating, +ComplexFloating, Flexible, Character# + */ + Py@NAME@ArrType_Type.tp_flags = BASEFLAGS; + /**end repeat**/ + + /**begin repeat +#name=bool, byte, short, int, long, longlong, ubyte, ushort, uint, ulong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, string, unicode, void, object# +#NAME=Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, String, Unicode, Void, Object# + */ + Py@NAME@ArrType_Type.tp_flags = LEAFFLAGS; + Py@NAME@ArrType_Type.tp_new = @name@_arrtype_new; + Py@NAME@ArrType_Type.tp_richcompare = gentype_richcompare; + /**end repeat**/ + /* Allow the Void type to be subclassed -- for adding new types */ + PyVoidArrType_Type.tp_flags = BASEFLAGS; + + /**begin repeat +#name=bool, byte, short, ubyte, ushort, uint, ulong, ulonglong, float, longdouble, cfloat, clongdouble, void, object# +#NAME=Bool, Byte, Short, UByte, UShort, UInt, ULong, ULongLong, Float, LongDouble, CFloat, CLongDouble, Void, Object# + */ + Py@NAME@ArrType_Type.tp_hash = @name@_arrtype_hash; + /**end repeat**/ + +#if SIZEOF_INT != SIZEOF_LONG + /* We won't be inheriting from Python Int type. */ + PyIntArrType_Type.tp_hash = int_arrtype_hash; +#endif + +#if SIZEOF_LONG != SIZEOF_LONGLONG + /* We won't be inheriting from Python Int type. */ + PyLongLongArrType_Type.tp_hash = longlong_arrtype_hash; +#endif + + /* These need to be coded specially because getitem does not + return a normal Python type + */ + PyLongDoubleArrType_Type.tp_as_number = &longdoubletype_as_number; + PyCLongDoubleArrType_Type.tp_as_number = &clongdoubletype_as_number; + + /**begin repeat +#name=int, long, hex, oct, float, repr, str# +#kind=tp_as_number->nb*5, tp*2# + */ + PyLongDoubleArrType_Type.@kind@_@name@ = longdoubletype_@name@; + PyCLongDoubleArrType_Type.@kind@_@name@ = clongdoubletype_@name@; + /**end repeat**/ + + PyStringArrType_Type.tp_itemsize = sizeof(char); + PyVoidArrType_Type.tp_dealloc = (destructor) void_dealloc; + PyUnicodeArrType_Type.tp_dealloc = unicode_dealloc; + PyObjectArrType_Type.tp_dealloc = object_arrtype_dealloc; + PyObjectArrType_Type.tp_getattro = (getattrofunc) object_getattro; + PyObjectArrType_Type.tp_setattro = (setattrofunc) object_setattro; + + + + PyArrayIter_Type.tp_iter = PyObject_SelfIter; + PyArrayMapIter_Type.tp_iter = PyObject_SelfIter; + +/**begin repeat +#name=Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object,# +#num=BOOL, BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, OBJECT, NTYPES# +**/ + PyArrayScalar_Offset[PyArray_@num@] = (int) offsetof(Py@name@ScalarObject, obval); +/**end repeat**/ +} + + +/* the order of this table is important */ +static PyTypeObject *typeobjects[] = { + &PyBoolArrType_Type, + &PyByteArrType_Type, + &PyUByteArrType_Type, + &PyShortArrType_Type, + &PyUShortArrType_Type, + &PyIntArrType_Type, + &PyUIntArrType_Type, + &PyLongArrType_Type, + &PyULongArrType_Type, + &PyLongLongArrType_Type, + &PyULongLongArrType_Type, + &PyFloatArrType_Type, + &PyDoubleArrType_Type, + &PyLongDoubleArrType_Type, + &PyCFloatArrType_Type, + &PyCDoubleArrType_Type, + &PyCLongDoubleArrType_Type, + &PyObjectArrType_Type, + &PyStringArrType_Type, + &PyUnicodeArrType_Type, + &PyVoidArrType_Type +}; + +static int +_typenum_fromtypeobj(PyObject *type, int user) +{ + int typenum, i; + + typenum = PyArray_NOTYPE; + i = 0; + while(i < PyArray_NTYPES) { + if (type == (PyObject *)typeobjects[i]) { + typenum = i; + break; + } + i++; + } + + if (!user) return typenum; + + /* Search any registered types */ + i = 0; + while (i < PyArray_NUMUSERTYPES) { + if (type == (PyObject *)(userdescrs[i]->typeobj)) { + typenum = i + PyArray_USERDEF; + break; + } + i++; + } + return typenum; +} + +/* new reference */ +static PyArray_Descr * +PyArray_DescrFromTypeObject(PyObject *type) +{ + int typenum; + PyArray_Descr *new, *conv=NULL; + + /* if it's a builtin type, then use the typenumber */ + typenum = _typenum_fromtypeobj(type,1); + if (typenum != PyArray_NOTYPE) { + new = PyArray_DescrFromType(typenum); + if (PyTypeNum_ISUSERDEF(typenum)) goto finish; + return new; + } + + /* Check the generic types */ + if ((type == (PyObject *) &PyNumericArrType_Type) || \ + (type == (PyObject *) &PyInexactArrType_Type) || \ + (type == (PyObject *) &PyFloatingArrType_Type)) + typenum = PyArray_DOUBLE; + else if (type == (PyObject *)&PyComplexFloatingArrType_Type) + typenum = PyArray_CDOUBLE; + else if ((type == (PyObject *)&PyIntegerArrType_Type) || \ + (type == (PyObject *)&PySignedIntegerArrType_Type)) + typenum = PyArray_LONG; + else if (type == (PyObject *) &PyUnsignedIntegerArrType_Type) + typenum = PyArray_ULONG; + else if (type == (PyObject *) &PyCharacterArrType_Type) + typenum = PyArray_STRING; + else if ((type == (PyObject *) &PyGenericArrType_Type) || \ + (type == (PyObject *) &PyFlexibleArrType_Type)) + typenum = PyArray_VOID; + + if (typenum != PyArray_NOTYPE) { + return PyArray_DescrFromType(typenum); + } + + /* Otherwise --- type is a sub-type of an array scalar + currently only VOID allows it -- use it as the type-object. + */ + /* look for a dtypedescr attribute */ + new = PyArray_DescrNewFromType(PyArray_VOID); + + finish: + conv = _arraydescr_fromobj(type); + if (conv) { + new->fields = conv->fields; + Py_INCREF(new->fields); + new->elsize = conv->elsize; + new->subarray = conv->subarray; + conv->subarray = NULL; + Py_DECREF(conv); + } + Py_DECREF(new->typeobj); + new->typeobj = (PyTypeObject *)type; + Py_INCREF(type); + return new; +} + +/* New reference */ +/*OBJECT_API + Return descr object from array scalar. +*/ +static PyArray_Descr * +PyArray_DescrFromScalar(PyObject *sc) +{ + int type_num; + PyArray_Descr *descr; + + if PyArray_IsScalar(sc, Void) { + descr = ((PyVoidScalarObject *)sc)->descr; + Py_INCREF(descr); + return descr; + } + descr = PyArray_DescrFromTypeObject((PyObject *)sc->ob_type); + if (descr->elsize == 0) { + PyArray_DESCR_REPLACE(descr); + type_num = descr->type_num; + if (type_num == PyArray_STRING) + descr->elsize = PyString_GET_SIZE(sc); + else if (type_num == PyArray_UNICODE) + descr->elsize = PyUnicode_GET_DATA_SIZE(sc); + else { + descr->elsize = \ + ((PyVoidScalarObject *)sc)->ob_size; + descr->fields = PyObject_GetAttrString(sc, "fields"); + if (!descr->fields || !PyDict_Check(descr->fields) || \ + (descr->fields == Py_None)) { + Py_XDECREF(descr->fields); + descr->fields = NULL; + } + PyErr_Clear(); + } + } + return descr; +} + +/* New reference */ +/*OBJECT_API + Get a typeobject from a type-number +*/ +static PyObject * +PyArray_TypeObjectFromType(int type) +{ + PyArray_Descr *descr; + PyObject *obj; + + descr = PyArray_DescrFromType(type); + if (descr == NULL) return NULL; + Py_INCREF((PyObject *)descr->typeobj); + obj = (PyObject *)descr->typeobj; + Py_DECREF(descr); + return obj; +} + diff --git a/numpy/core/src/ufuncobject.c b/numpy/core/src/ufuncobject.c new file mode 100644 index 000000000..47aad4828 --- /dev/null +++ b/numpy/core/src/ufuncobject.c @@ -0,0 +1,3145 @@ + +/* + Python Universal Functions Object -- Math for all types, plus fast + arrays math + + Full description + + This supports mathematical (and Boolean) functions on arrays and other python + objects. Math on large arrays of basic C types is rather efficient. + + Travis E. Oliphant (2005) + Assistant Professor + Brigham Young University + + based on the + + Original Implementation: + Copyright (c) 1995, 1996, 1997 Jim Hugunin, hugunin@mit.edu + + with inspiration and code from + Numarray + Space Science Telescope Institute + J. Todd Miller + Perry Greenfield + Rick White + +*/ + + +typedef double (DoubleBinaryFunc)(double x, double y); +typedef float (FloatBinaryFunc)(float x, float y); +typedef longdouble (LongdoubleBinaryFunc)(longdouble x, longdouble y); + +typedef void (CdoubleBinaryFunc)(cdouble *x, cdouble *y, cdouble *res); +typedef void (CfloatBinaryFunc)(cfloat *x, cfloat *y, cfloat *res); +typedef void (ClongdoubleBinaryFunc)(clongdouble *x, clongdouble *y, \ + clongdouble *res); + +/*UFUNC_API*/ +static void +PyUFunc_ff_f_As_dd_d(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n=dimensions[0]; + register intp is1=steps[0],is2=steps[1],os=steps[2]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + *(float *)op = (float)((DoubleBinaryFunc *)func) \ + ((double)*(float *)ip1, (double)*(float *)ip2); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_ff_f(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n=dimensions[0]; + register intp is1=steps[0],is2=steps[1],os=steps[2]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + *(float *)op = ((FloatBinaryFunc *)func)(*(float *)ip1, + *(float *)ip2); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_dd_d(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n=dimensions[0]; + register intp is1=steps[0],is2=steps[1],os=steps[2]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + *(double *)op = ((DoubleBinaryFunc *)func)\ + (*(double *)ip1, *(double *)ip2); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_gg_g(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n=dimensions[0]; + register intp is1=steps[0],is2=steps[1],os=steps[2]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + *(longdouble *)op = \ + ((LongdoubleBinaryFunc *)func)(*(longdouble *)ip1, + *(longdouble *)ip2); + } +} + + +/*UFUNC_API*/ +static void +PyUFunc_FF_F_As_DD_D(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i,n=dimensions[0],is1=steps[0],is2=steps[1],os=steps[2]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + cdouble x, y, r; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + x.real = ((float *)ip1)[0]; x.imag = ((float *)ip1)[1]; + y.real = ((float *)ip2)[0]; y.imag = ((float *)ip2)[1]; + ((CdoubleBinaryFunc *)func)(&x, &y, &r); + ((float *)op)[0] = (float)r.real; + ((float *)op)[1] = (float)r.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_DD_D(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + cdouble x,y,r; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + x.real = ((double *)ip1)[0]; x.imag = ((double *)ip1)[1]; + y.real = ((double *)ip2)[0]; y.imag = ((double *)ip2)[1]; + ((CdoubleBinaryFunc *)func)(&x, &y, &r); + ((double *)op)[0] = r.real; + ((double *)op)[1] = r.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_FF_F(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + cfloat x,y,r; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + x.real = ((float *)ip1)[0]; x.imag = ((float *)ip1)[1]; + y.real = ((float *)ip2)[0]; y.imag = ((float *)ip2)[1]; + ((CfloatBinaryFunc *)func)(&x, &y, &r); + ((float *)op)[0] = r.real; + ((float *)op)[1] = r.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_GG_G(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + clongdouble x,y,r; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + x.real = ((longdouble *)ip1)[0]; + x.imag = ((longdouble *)ip1)[1]; + y.real = ((longdouble *)ip2)[0]; + y.imag = ((longdouble *)ip2)[1]; + ((ClongdoubleBinaryFunc *)func)(&x, &y, &r); + ((longdouble *)op)[0] = r.real; + ((longdouble *)op)[1] = r.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_OO_O(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2], \ + n=dimensions[0]; + char *ip1=args[0], *ip2=args[1], *op=args[2]; + PyObject *tmp; + PyObject *x1, *x2; + + for(i=0; i<n; i++, ip1+=is1, ip2+=is2, op+=os) { + x1 = *((PyObject **)ip1); + x2 = *((PyObject **)ip2); + if ((x1 == NULL) || (x2 == NULL)) goto done; + if ( (void *) func == (void *) PyNumber_Power) + tmp = ((ternaryfunc)func)(x1, x2, Py_None); + else + tmp = ((binaryfunc)func)(x1, x2); + if (PyErr_Occurred()) goto done; + Py_XDECREF(*((PyObject **)op)); + *((PyObject **)op) = tmp; + } + done: + return; +} + + +typedef double DoubleUnaryFunc(double x); +typedef float FloatUnaryFunc(float x); +typedef longdouble LongdoubleUnaryFunc(longdouble x); +typedef void CdoubleUnaryFunc(cdouble *x, cdouble *res); +typedef void CfloatUnaryFunc(cfloat *x, cfloat *res); +typedef void ClongdoubleUnaryFunc(clongdouble *x, clongdouble *res); + +/*UFUNC_API*/ +static void +PyUFunc_f_f_As_d_d(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n=dimensions[0]; + char *ip1=args[0], *op=args[1]; + for(i=0; i<n; i++, ip1+=steps[0], op+=steps[1]) { + *(float *)op = (float)((DoubleUnaryFunc *)func)((double)*(float *)ip1); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_d_d(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; + char *ip1=args[0], *op=args[1]; + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + *(double *)op = ((DoubleUnaryFunc *)func)(*(double *)ip1); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_f_f(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp n=dimensions[0]; + char *ip1=args[0], *op=args[1]; + for(i=0; i<n; i++, ip1+=steps[0], op+=steps[1]) { + *(float *)op = ((FloatUnaryFunc *)func)(*(float *)ip1); + } +} + +/*UFUNC_API*/ +static void +PyUFunc_g_g(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp n=dimensions[0]; + char *ip1=args[0], *op=args[1]; + for(i=0; i<n; i++, ip1+=steps[0], op+=steps[1]) { + *(longdouble *)op = ((LongdoubleUnaryFunc *)func)\ + (*(longdouble *)ip1); + } +} + + +/*UFUNC_API*/ +static void +PyUFunc_F_F_As_D_D(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; cdouble x, res; + intp n=dimensions[0]; + char *ip1=args[0], *op=args[1]; + for(i=0; i<n; i++, ip1+=steps[0], op+=steps[1]) { + x.real = ((float *)ip1)[0]; x.imag = ((float *)ip1)[1]; + ((CdoubleUnaryFunc *)func)(&x, &res); + ((float *)op)[0] = (float)res.real; + ((float *)op)[1] = (float)res.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_F_F(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; cfloat x, res; + char *ip1=args[0], *op=args[1]; + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + x.real = ((float *)ip1)[0]; + x.imag = ((float *)ip1)[1]; + ((CfloatUnaryFunc *)func)(&x, &res); + ((float *)op)[0] = res.real; + ((float *)op)[1] = res.imag; + } +} + + +/*UFUNC_API*/ +static void +PyUFunc_D_D(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; cdouble x, res; + char *ip1=args[0], *op=args[1]; + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + x.real = ((double *)ip1)[0]; + x.imag = ((double *)ip1)[1]; + ((CdoubleUnaryFunc *)func)(&x, &res); + ((double *)op)[0] = res.real; + ((double *)op)[1] = res.imag; + } +} + + +/*UFUNC_API*/ +static void +PyUFunc_G_G(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; clongdouble x, res; + char *ip1=args[0], *op=args[1]; + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + x.real = ((longdouble *)ip1)[0]; + x.imag = ((longdouble *)ip1)[1]; + ((ClongdoubleUnaryFunc *)func)(&x, &res); + ((double *)op)[0] = res.real; + ((double *)op)[1] = res.imag; + } +} + +/*UFUNC_API*/ +static void +PyUFunc_O_O(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; PyObject *tmp, *x1; + char *ip1=args[0], *op=args[1]; + + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + x1 = *(PyObject **)ip1; + if (x1 == NULL) goto done; + tmp = ((unaryfunc)func)(x1); + if ((tmp==NULL) || PyErr_Occurred()) goto done; + Py_XDECREF(*((PyObject **)op)); + *((PyObject **)op) = tmp; + } + done: + return; +} + +/*UFUNC_API*/ +static void +PyUFunc_O_O_method(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i; PyObject *tmp, *meth, *arglist, *x1; + char *ip1=args[0], *op=args[1]; + + for(i=0; i<*dimensions; i++, ip1+=steps[0], op+=steps[1]) { + x1 = *(PyObject **)ip1; + if (x1 == NULL) goto done; + meth = PyObject_GetAttrString(x1, (char *)func); + if (meth != NULL) { + arglist = PyTuple_New(0); + tmp = PyEval_CallObject(meth, arglist); + Py_DECREF(arglist); + Py_DECREF(meth); + if ((tmp==NULL) || PyErr_Occurred()) goto done; + Py_XDECREF(*((PyObject **)op)); + *((PyObject **)op) = tmp; + } + } + done: + return; + +} + + + +/* a general-purpose ufunc that deals with general-purpose Python callable. + func is a structure with nin, nout, and a Python callable function +*/ + +/*UFUNC_API*/ +static void +PyUFunc_On_Om(char **args, intp *dimensions, intp *steps, void *func) +{ + intp i, j; + intp n=dimensions[0]; + PyUFunc_PyFuncData *data = (PyUFunc_PyFuncData *)func; + int nin = data->nin, nout=data->nout; + int ntot; + PyObject *tocall = data->callable; + char *ptrs[MAX_ARGS]; + PyObject *arglist, *result; + PyObject *in, **op; + + ntot = nin+nout; + + for (j=0; j < ntot; j++) ptrs[j] = args[j]; + for(i=0; i<n; i++) { + arglist = PyTuple_New(nin); + if (arglist == NULL) return; + for (j=0; j < nin; j++) { + in = *((PyObject **)ptrs[j]); + if (in == NULL) {Py_DECREF(arglist); return;} + PyTuple_SET_ITEM(arglist, j, in); + Py_INCREF(in); + } + result = PyEval_CallObject(tocall, arglist); + Py_DECREF(arglist); + if (result == NULL) return; + if PyTuple_Check(result) { + if (nout != PyTuple_Size(result)) { + Py_DECREF(result); + return; + } + for (j=0; j < nout; j++) { + op = (PyObject **)ptrs[j+nin]; + Py_XDECREF(*op); + *op = PyTuple_GET_ITEM(result, j); + Py_INCREF(*op); + } + Py_DECREF(result); + } + else { + op = (PyObject **)ptrs[nin]; + Py_XDECREF(*op); + *op = result; + } + for (j=0; j < ntot; j++) ptrs[j] += steps[j]; + } + return; +} + + + + +/* ---------------------------------------------------------------- */ + + +/* fpstatus is the ufunc_formatted hardware status + errmask is the handling mask specified by the user. + errobj is a Python object with (string, callable object or None) + or NULL +*/ + +/* + 2. for each of the flags + determine whether to ignore, warn, raise error, or call Python function. + If ignore, do nothing + If warn, print a warning and continue + If raise return an error + If call, call a user-defined function with string +*/ + +static int +_error_handler(int method, PyObject *errobj, char *errtype, int retstatus) +{ + PyObject *pyfunc, *ret, *args; + char *name=PyString_AS_STRING(PyTuple_GET_ITEM(errobj,0)); + char msg[100]; + + ALLOW_C_API_DEF + + ALLOW_C_API + + switch(method) { + case UFUNC_ERR_WARN: + snprintf(msg, 100, "%s encountered in %s", errtype, name); + if (PyErr_Warn(PyExc_RuntimeWarning, msg) < 0) goto fail; + break; + case UFUNC_ERR_RAISE: + PyErr_Format(PyExc_FloatingPointError, + "%s encountered in %s", + errtype, name); + goto fail; + case UFUNC_ERR_CALL: + pyfunc = PyTuple_GET_ITEM(errobj, 1); + + if (pyfunc == Py_None) { + PyErr_Format(PyExc_NameError, + "python callback specified for %s (in " \ + " %s) but no function found.", + errtype, name); + goto fail; + } + args = Py_BuildValue("NN", PyString_FromString(errtype), + PyInt_FromLong((long) retstatus)); + if (args == NULL) goto fail; + ret = PyObject_CallObject(pyfunc, args); + Py_DECREF(args); + if (ret == NULL) goto fail; + Py_DECREF(ret); + + break; + } + DISABLE_C_API + return 0; + + fail: + DISABLE_C_API + return -1; +} + + +/*UFUNC_API*/ +static int +PyUFunc_checkfperr(int errmask, PyObject *errobj) +{ + int retstatus; + int handle; + + /* 1. check hardware flag --- this is platform dependent code */ + + UFUNC_CHECK_STATUS(retstatus) /* no semicolon */ + + /* End platform dependent code */ + +#define HANDLEIT(NAME, str) {if (retstatus & UFUNC_FPE_##NAME) { \ + handle = errmask & UFUNC_MASK_##NAME;\ + if (handle && \ + _error_handler(handle >> UFUNC_SHIFT_##NAME, \ + errobj, str, retstatus) < 0) \ + return -1; \ + }} + + if (errmask && retstatus) { + HANDLEIT(DIVIDEBYZERO, "divide by zero"); + HANDLEIT(OVERFLOW, "overflow"); + HANDLEIT(UNDERFLOW, "underflow"); + HANDLEIT(INVALID, "invalid"); + } + +#undef HANDLEIT + + return 0; +} + + +/* Checking the status flag clears it */ +/*UFUNC_API*/ +static void +PyUFunc_clearfperr() +{ + int retstatus; + + UFUNC_CHECK_STATUS(retstatus) +} + + +#define UFUNC_NOSCALAR 0 +#define UFUNC_BOOL_SCALAR 1 +#define UFUNC_INTPOS_SCALAR 2 +#define UFUNC_INTNEG_SCALAR 3 +#define UFUNC_FLOAT_SCALAR 4 +#define UFUNC_COMPLEX_SCALAR 5 +#define UFUNC_OBJECT_SCALAR 6 + +#define NO_UFUNCLOOP 0 +#define ZERODIM_REDUCELOOP 0 +#define ONE_UFUNCLOOP 1 +#define ONEDIM_REDUCELOOP 1 +#define NOBUFFER_UFUNCLOOP 2 +#define NOBUFFER_REDUCELOOP 2 +#define BUFFER_UFUNCLOOP 3 +#define BUFFER_REDUCELOOP 3 + + +static char +_lowest_type(char intype) +{ + switch(intype) { + /* case PyArray_BYTE */ + case PyArray_SHORT: + case PyArray_INT: + case PyArray_LONG: + case PyArray_LONGLONG: + return PyArray_BYTE; + /* case PyArray_UBYTE */ + case PyArray_USHORT: + case PyArray_UINT: + case PyArray_ULONG: + case PyArray_ULONGLONG: + return PyArray_UBYTE; + /* case PyArray_FLOAT:*/ + case PyArray_DOUBLE: + case PyArray_LONGDOUBLE: + return PyArray_FLOAT; + /* case PyArray_CFLOAT:*/ + case PyArray_CDOUBLE: + case PyArray_CLONGDOUBLE: + return PyArray_CFLOAT; + default: + return intype; + } +} + +/* Called to determine coercion + */ + +static int +_cancoerce(char thistype, char neededtype, char scalar) +{ + + switch(scalar) { + case UFUNC_NOSCALAR: + case UFUNC_BOOL_SCALAR: + case UFUNC_OBJECT_SCALAR: + return PyArray_CanCastSafely(thistype, neededtype); + case UFUNC_INTPOS_SCALAR: + return (neededtype >= PyArray_UBYTE); + case UFUNC_INTNEG_SCALAR: + return (neededtype >= PyArray_BYTE) && \ + !(PyTypeNum_ISUNSIGNED(neededtype)); + case UFUNC_FLOAT_SCALAR: + return (neededtype >= PyArray_FLOAT); + case UFUNC_COMPLEX_SCALAR: + return (neededtype >= PyArray_CFLOAT); + } + fprintf(stderr, "\n**Error** coerce fall through: %d %d %d\n\n", + thistype, neededtype, scalar); + return 1; /* should never get here... */ +} + + +static int +select_types(PyUFuncObject *self, int *arg_types, + PyUFuncGenericFunction *function, void **data, + char *scalars) +{ + + int i=0, j; + char start_type; + + if (PyTypeNum_ISUSERDEF((arg_types[0]))) { + PyObject *key, *obj; + for (i=0; i<self->nin; i++) { + if (arg_types[i] != arg_types[0]) { + PyErr_SetString(PyExc_TypeError, + "ufuncs on user defined" \ + " types don't support "\ + "coercion"); + return -1; + } + } + for (i=self->nin; i<self->nargs; i++) { + arg_types[i] = arg_types[0]; + } + + obj = NULL; + if (self->userloops) { + key = PyInt_FromLong((long) arg_types[0]); + if (key == NULL) return -1; + obj = PyDict_GetItem(self->userloops, key); + Py_DECREF(key); + } + if (obj == NULL) { + PyErr_SetString(PyExc_TypeError, + "no registered loop for this " \ + "user-defined type"); + return -1; + } + if PyTuple_Check(obj) { + *function = (PyUFuncGenericFunction) \ + PyCObject_AsVoidPtr(PyTuple_GET_ITEM(obj, 0)); + *data = PyCObject_AsVoidPtr(PyTuple_GET_ITEM(obj, 1)); + } + else { + *function = (PyUFuncGenericFunction) \ + PyCObject_AsVoidPtr(obj); + *data = NULL; + } + Py_DECREF(obj); + return 0; + } + + + start_type = arg_types[0]; + /* If the first argument is a scalar we need to place + the start type as the lowest type in the class + */ + if (scalars[0] != UFUNC_NOSCALAR) { + start_type = _lowest_type(start_type); + } + + while (i<self->ntypes && start_type > self->types[i*self->nargs]) + i++; + + for(;i<self->ntypes; i++) { + for(j=0; j<self->nin; j++) { + if (!_cancoerce(arg_types[j], + self->types[i*self->nargs+j], + scalars[j])) + break; + } + if (j == self->nin) break; + } + if(i>=self->ntypes) { + PyErr_SetString(PyExc_TypeError, + "function not supported for these types, "\ + "and can't coerce safely to supported types"); + return -1; + } + for(j=0; j<self->nargs; j++) + arg_types[j] = self->types[i*self->nargs+j]; + + if (self->data) + *data = self->data[i]; + else + *data = NULL; + *function = self->functions[i]; + + return 0; +} + +static int PyUFunc_USEDEFAULTS=0; + +/*UFUNC_API*/ +static int +PyUFunc_GetPyValues(char *name, int *bufsize, int *errmask, PyObject **errobj) +{ + PyObject *thedict; + PyObject *ref=NULL; + PyObject *retval; + static PyObject *thestring=NULL; + + if (!PyUFunc_USEDEFAULTS) { + if (thestring == NULL) { + thestring = PyString_InternFromString(UFUNC_PYVALS_NAME); + } + thedict = PyEval_GetLocals(); + ref = PyDict_GetItem(thedict, thestring); + if (ref == NULL) { + thedict = PyEval_GetGlobals(); + ref = PyDict_GetItem(thedict, thestring); + } + if (ref == NULL) { + thedict = PyEval_GetBuiltins(); + ref = PyDict_GetItem(thedict, thestring); + } + } + if (ref == NULL) { + *errmask = UFUNC_ERR_DEFAULT; + *errobj = Py_BuildValue("NO", + PyString_FromString(name), + Py_None); + *bufsize = PyArray_BUFSIZE; + return 0; + } + *errobj = NULL; + if (!PyList_Check(ref) || (PyList_GET_SIZE(ref)!=3)) { + PyErr_Format(PyExc_TypeError, "%s must be a length 3 list.", + UFUNC_PYVALS_NAME); + return -1; + } + + *bufsize = PyInt_AsLong(PyList_GET_ITEM(ref, 0)); + if ((*bufsize == -1) && PyErr_Occurred()) return -1; + if ((*bufsize < PyArray_MIN_BUFSIZE) || \ + (*bufsize > PyArray_MAX_BUFSIZE) || \ + (*bufsize % 16 != 0)) { + PyErr_Format(PyExc_ValueError, + "buffer size (%d) is not " \ + "in range (%d - %d) or not a multiple of 16", + *bufsize, PyArray_MIN_BUFSIZE, + PyArray_MAX_BUFSIZE); + return -1; + } + + *errmask = PyInt_AsLong(PyList_GET_ITEM(ref, 1)); + if (*errmask < 0) { + if (PyErr_Occurred()) return -1; + PyErr_Format(PyExc_ValueError, \ + "invalid error mask (%d)", + *errmask); + return -1; + } + + retval = PyList_GET_ITEM(ref, 2); + if (retval != Py_None && !PyCallable_Check(retval)) { + PyErr_SetString(PyExc_TypeError, + "callback function must be callable"); + return -1; + } + + *errobj = Py_BuildValue("NO", + PyString_FromString(name), + retval); + if (*errobj == NULL) return -1; + + return 0; +} + + +static char +_scalar_kind(int typenum, PyArrayObject **arr) +{ + if (PyTypeNum_ISSIGNED(typenum)) return UFUNC_INTNEG_SCALAR; + if (PyTypeNum_ISFLOAT(typenum)) return UFUNC_FLOAT_SCALAR; + if (PyTypeNum_ISCOMPLEX(typenum)) return UFUNC_COMPLEX_SCALAR; + if (PyTypeNum_ISUNSIGNED(typenum)) return UFUNC_INTPOS_SCALAR; + if (PyTypeNum_ISBOOL(typenum)) return UFUNC_BOOL_SCALAR; + return UFUNC_OBJECT_SCALAR; +} + + +/* Create copies for any arrays that are less than loop->bufsize + in total size and are mis-behaved or in need + of casting. +*/ + +static int +_create_copies(PyUFuncLoopObject *loop, int *arg_types, PyArrayObject **mps) +{ + int nin = loop->ufunc->nin; + int i; + intp size; + PyObject *new; + PyArray_Descr *ntype; + PyArray_Descr *atype; + + for (i=0; i<nin; i++) { + size = PyArray_SIZE(mps[i]); + /* if the type of mps[i] is equivalent to arg_types[i] */ + /* then set arg_types[i] equal to type of + mps[i] for later checking.... + */ + if (PyArray_TYPE(mps[i]) != arg_types[i]) { + ntype = mps[i]->descr; + atype = PyArray_DescrFromType(arg_types[i]); + if (PyArray_EquivTypes(atype, ntype)) { + arg_types[i] = ntype->type_num; + } + Py_DECREF(atype); + } + if (size < loop->bufsize) { + if (!(PyArray_ISBEHAVED_RO(mps[i])) || \ + PyArray_TYPE(mps[i]) != arg_types[i]) { + ntype = PyArray_DescrFromType(arg_types[i]); + new = PyArray_FromAny((PyObject *)mps[i], + ntype, 0, 0, + FORCECAST | ALIGNED); + if (new == NULL) return -1; + Py_DECREF(mps[i]); + mps[i] = (PyArrayObject *)new; + } + } + } + + return 0; +} + +#define _GETATTR_(str, rstr) if (strcmp(name, #str) == 0) { \ + return PyObject_HasAttrString(op, "__" #rstr "__");} + +static int +_has_reflected_op(PyObject *op, char *name) +{ + _GETATTR_(add, radd) + _GETATTR_(subtract, rsub) + _GETATTR_(multiply, rmul) + _GETATTR_(divide, rdiv) + _GETATTR_(true_divide, rtruediv) + _GETATTR_(floor_divide, rfloordiv) + _GETATTR_(remainder, rmod) + _GETATTR_(power, rpow) + _GETATTR_(left_shift, rrlshift) + _GETATTR_(right_shift, rrshift) + _GETATTR_(bitwise_and, rand) + _GETATTR_(bitwise_xor, rxor) + _GETATTR_(bitwise_or, ror) + return 0; +} + +#undef _GETATTR_ + + +static int +construct_matrices(PyUFuncLoopObject *loop, PyObject *args, PyArrayObject **mps) +{ + int nargs, i, maxsize; + int arg_types[MAX_ARGS]; + char scalars[MAX_ARGS]; + PyUFuncObject *self=loop->ufunc; + Bool allscalars=TRUE; + PyTypeObject *subtype=&PyArray_Type; + + /* Check number of arguments */ + nargs = PyTuple_Size(args); + if ((nargs != self->nin) && (nargs != self->nargs)) { + PyErr_SetString(PyExc_ValueError, + "invalid number of arguments"); + return -1; + } + + + /* Get each input argument */ + for (i=0; i<self->nin; i++) { + mps[i] = (PyArrayObject *)\ + PyArray_FromAny(PyTuple_GET_ITEM(args,i), + NULL, 0, 0, 0); + if (mps[i] == NULL) return -1; + arg_types[i] = PyArray_TYPE(mps[i]); + if (PyTypeNum_ISFLEXIBLE(arg_types[i])) { + loop->notimplemented = 1; + return nargs; + } + /* + fprintf(stderr, "array %d has reference %d\n", i, + (mps[i])->ob_refcnt); + */ + + /* Scalars are 0-dimensional arrays + at this point + */ + if (mps[i]->nd > 0) { + scalars[i] = UFUNC_NOSCALAR; + allscalars=FALSE; + } + else scalars[i] = _scalar_kind(arg_types[i], &(mps[i])); + + /* If any input is a big-array */ + if (!PyType_IsSubtype(mps[i]->ob_type, &PyArray_Type)) { + subtype = &PyBigArray_Type; + } + } + + /* If everything is a scalar, then use normal coercion rules */ + if (allscalars) { + for (i=0; i<self->nin; i++) { + scalars[i] = UFUNC_NOSCALAR; + } + } + + /* Select an appropriate function for these argument types. */ + if (select_types(loop->ufunc, arg_types, &(loop->function), + &(loop->funcdata), scalars) == -1) + return -1; + + /* FAIL with NotImplemented if the other object has + the __r<op>__ method and has __array_priority__ as + an attribute (signalling it can handle ndarray's) + and is not already an ndarray or bigndarray + */ + if ((arg_types[1] == PyArray_OBJECT) && \ + (loop->ufunc->nin==2) && (loop->ufunc->nout == 1)) { + PyObject *_obj = PyTuple_GET_ITEM(args, 1); + if (!PyArray_CheckExact(_obj) && \ + !PyBigArray_CheckExact(_obj) && \ + PyObject_HasAttrString(_obj, "__array_priority__") && \ + _has_reflected_op(_obj, loop->ufunc->name)) { + loop->notimplemented = 1; + return nargs; + } + } + loop->notimplemented=0; + + /* Create copies for some of the arrays if appropriate */ + if (_create_copies(loop, arg_types, mps) < 0) return -1; + + /* Create Iterators for the Inputs */ + for (i=0; i<self->nin; i++) { + loop->iters[i] = (PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)mps[i]); + if (loop->iters[i] == NULL) return -1; + } + + /* Broadcast the result */ + loop->numiter = self->nin; + if (PyArray_Broadcast((PyArrayMultiIterObject *)loop) < 0) + return -1; + + /* Get any return arguments */ + for (i=self->nin; i<nargs; i++) { + mps[i] = (PyArrayObject *)PyTuple_GET_ITEM(args, i); + if (((PyObject *)mps[i])==Py_None) { + mps[i] = NULL; + continue; + } + Py_INCREF(mps[i]); + if (!PyArray_Check((PyObject *)mps[i])) { + PyObject *new; + if (PyArrayIter_Check(mps[i])) { + new = PyObject_CallMethod((PyObject *)mps[i], + "__array__", NULL); + Py_DECREF(mps[i]); + mps[i] = (PyArrayObject *)new; + } + else { + PyErr_SetString(PyExc_TypeError, + "return arrays must be "\ + "of ArrayType"); + Py_DECREF(mps[i]); + mps[i] = NULL; + return -1; + } + } + if (!PyArray_CompareLists(mps[i]->dimensions, + loop->dimensions, loop->nd)) { + PyErr_SetString(PyExc_ValueError, + "invalid return array shape"); + Py_DECREF(mps[i]); + mps[i] = NULL; + return -1; + } + if (!PyArray_ISWRITEABLE(mps[i])) { + PyErr_SetString(PyExc_ValueError, + "return array is not writeable"); + Py_DECREF(mps[i]); + mps[i] = NULL; + return -1; + } + } + + /* construct any missing return arrays and make output iterators */ + + for (i=self->nin; i<self->nargs; i++) { + PyArray_Descr *ntype; + + if (mps[i] == NULL) { + mps[i] = (PyArrayObject *)PyArray_New(subtype, + loop->nd, + loop->dimensions, + arg_types[i], + NULL, NULL, + 0, 0, NULL); + if (mps[i] == NULL) return -1; + } + + /* reset types for outputs that are equivalent + -- no sense casting uselessly + */ + else { + if (mps[i]->descr->type_num != arg_types[i]) { + PyArray_Descr *atype; + ntype = mps[i]->descr; + atype = PyArray_DescrFromType(arg_types[i]); + if (PyArray_EquivTypes(atype, ntype)) { + arg_types[i] = ntype->type_num; + } + Py_DECREF(atype); + } + + /* still not the same -- or will we have to use buffers?*/ + if (mps[i]->descr->type_num != arg_types[i] || + !PyArray_ISBEHAVED_RO(mps[i])) { + if (loop->size < loop->bufsize) { + PyObject *new; + /* Copy the array to a temporary copy + and set the UPDATEIFCOPY flag + */ + ntype = PyArray_DescrFromType(arg_types[i]); + new = PyArray_FromAny((PyObject *)mps[i], + ntype, 0, 0, + FORCECAST | ALIGNED | + UPDATEIFCOPY); + if (new == NULL) return -1; + Py_DECREF(mps[i]); + mps[i] = (PyArrayObject *)new; + } + } + } + + loop->iters[i] = (PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)mps[i]); + if (loop->iters[i] == NULL) return -1; + } + + + /* If any of different type, or misaligned or swapped + then must use buffers */ + + loop->bufcnt = 0; + + loop->obj = 0; + + /* Determine looping method needed */ + loop->meth = NO_UFUNCLOOP; + + maxsize = 0; + for (i=0; i<self->nargs; i++) { + loop->needbuffer[i] = 0; + if (arg_types[i] != mps[i]->descr->type_num || + !PyArray_ISBEHAVED_RO(mps[i])) { + loop->meth = BUFFER_UFUNCLOOP; + loop->needbuffer[i] = 1; + } + if (!loop->obj && mps[i]->descr->type_num == PyArray_OBJECT) { + loop->obj = 1; + } + } + + if (loop->meth == NO_UFUNCLOOP) { + + loop->meth = ONE_UFUNCLOOP; + + /* All correct type and BEHAVED */ + /* Check for non-uniform stridedness */ + + for (i=0; i<self->nargs; i++) { + if (!(loop->iters[i]->contiguous)) { + /* may still have uniform stride + if (broadcated result) <= 1-d */ + if (mps[i]->nd != 0 && \ + (loop->iters[i]->nd_m1 > 0)) { + loop->meth = NOBUFFER_UFUNCLOOP; + break; + } + } + } + if (loop->meth == ONE_UFUNCLOOP) { + for (i=0; i<self->nargs; i++) { + loop->bufptr[i] = mps[i]->data; + } + } + } + + loop->numiter = self->nargs; + + /* Fill in steps */ + if (loop->meth != ONE_UFUNCLOOP) { + int ldim = 0; + intp maxdim=-1; + PyArrayIterObject *it; + + /* Fix iterators */ + + /* Find the **largest** dimension */ + + maxdim = -1; + for (i=loop->nd - 1; i>=0; i--) { + if (loop->dimensions[i] > maxdim) { + ldim = i; + maxdim = loop->dimensions[i]; + } + } + + loop->size /= maxdim; + loop->bufcnt = maxdim; + loop->lastdim = ldim; + + /* Fix the iterators so the inner loop occurs over the + largest dimensions -- This can be done by + setting the size to 1 in that dimension + (just in the iterators) + */ + + for (i=0; i<loop->numiter; i++) { + it = loop->iters[i]; + it->contiguous = 0; + it->size /= (it->dims_m1[ldim]+1); + it->dims_m1[ldim] = 0; + it->backstrides[ldim] = 0; + + /* (won't fix factors because we + don't use PyArray_ITER_GOTO1D + so don't change them) */ + + /* Set the steps to the strides in that dimension */ + loop->steps[i] = it->strides[ldim]; + } + + /* fix up steps where we will be copying data to + buffers and calculate the ninnerloops and leftover + values -- if step size is already zero that is not changed... + */ + if (loop->meth == BUFFER_UFUNCLOOP) { + loop->leftover = maxdim % loop->bufsize; + loop->ninnerloops = (maxdim / loop->bufsize) + 1; + for (i=0; i<self->nargs; i++) { + if (loop->needbuffer[i] && loop->steps[i]) { + loop->steps[i] = mps[i]->descr->elsize; + } + /* These are changed later if casting is needed */ + } + } + } + else { /* uniformly-strided case ONE_UFUNCLOOP */ + for (i=0; i<self->nargs; i++) { + if (PyArray_SIZE(mps[i]) == 1) + loop->steps[i] = 0; + else + loop->steps[i] = mps[i]->strides[mps[i]->nd-1]; + } + } + + + /* Finally, create memory for buffers if we need them */ + + /* buffers for scalars are specially made small -- scalars are + not copied multiple times */ + if (loop->meth == BUFFER_UFUNCLOOP) { + int cnt = 0, cntcast = 0; /* keeps track of bytes to allocate */ + int scnt = 0, scntcast = 0; + char *castptr; + char *bufptr; + int last_was_scalar=0; + int last_cast_was_scalar=0; + int oldbufsize=0; + int oldsize=0; + int scbufsize = 4*sizeof(double); + int memsize; + PyArray_Descr *descr; + + /* compute the element size */ + for (i=0; i<self->nargs;i++) { + if (!loop->needbuffer) continue; + if (arg_types[i] != mps[i]->descr->type_num) { + descr = PyArray_DescrFromType(arg_types[i]); + if (loop->steps[i]) + cntcast += descr->elsize; + else + scntcast += descr->elsize; + if (i < self->nin) { + loop->cast[i] = \ + mps[i]->descr->f->cast[arg_types[i]]; + } + else { + loop->cast[i] = descr->f-> \ + cast[mps[i]->descr->type_num]; + } + Py_DECREF(descr); + } + loop->swap[i] = !(PyArray_ISNOTSWAPPED(mps[i])); + if (loop->steps[i]) + cnt += mps[i]->descr->elsize; + else + scnt += mps[i]->descr->elsize; + } + memsize = loop->bufsize*(cnt+cntcast) + scbufsize*(scnt+scntcast); + loop->buffer[0] = PyDataMem_NEW(memsize); + + /* fprintf(stderr, "Allocated buffer at %p of size %d, cnt=%d, cntcast=%d\n", loop->buffer[0], loop->bufsize * (cnt + cntcast), cnt, cntcast); */ + + if (loop->buffer[0] == NULL) {PyErr_NoMemory(); return -1;} + castptr = loop->buffer[0] + loop->bufsize*cnt + scbufsize*scnt; + bufptr = loop->buffer[0]; + for (i=0; i<self->nargs; i++) { + if (!loop->needbuffer[i]) continue; + loop->buffer[i] = bufptr + (last_was_scalar ? scbufsize : \ + loop->bufsize)*oldbufsize; + last_was_scalar = (loop->steps[i] == 0); + bufptr = loop->buffer[i]; + oldbufsize = mps[i]->descr->elsize; + /* fprintf(stderr, "buffer[%d] = %p\n", i, loop->buffer[i]); */ + if (loop->cast[i]) { + PyArray_Descr *descr; + loop->castbuf[i] = castptr + (last_cast_was_scalar ? scbufsize : \ + loop->bufsize)*oldsize; + last_cast_was_scalar = last_was_scalar; + /* fprintf(stderr, "castbuf[%d] = %p\n", i, loop->castbuf[i]); */ + descr = PyArray_DescrFromType(arg_types[i]); + oldsize = descr->elsize; + Py_DECREF(descr); + loop->bufptr[i] = loop->castbuf[i]; + castptr = loop->castbuf[i]; + if (loop->steps[i]) + loop->steps[i] = oldsize; + } + else { + loop->bufptr[i] = loop->buffer[i]; + } + } + } + return nargs; +} + +static void +ufuncreduce_dealloc(PyUFuncReduceObject *self) +{ + if (self->ufunc) { + Py_XDECREF(self->it); + Py_XDECREF(self->rit); + Py_XDECREF(self->ret); + Py_XDECREF(self->errobj); + Py_XDECREF(self->decref); + if (self->buffer) PyDataMem_FREE(self->buffer); + Py_DECREF(self->ufunc); + } + _pya_free(self); +} + +static void +ufuncloop_dealloc(PyUFuncLoopObject *self) +{ + int i; + + if (self->ufunc != NULL) { + for (i=0; i<self->ufunc->nargs; i++) + Py_XDECREF(self->iters[i]); + if (self->buffer[0]) PyDataMem_FREE(self->buffer[0]); + Py_XDECREF(self->errobj); + Py_DECREF(self->ufunc); + } + _pya_free(self); +} + +static PyUFuncLoopObject * +construct_loop(PyUFuncObject *self, PyObject *args, PyArrayObject **mps) +{ + PyUFuncLoopObject *loop; + int i; + + if (self == NULL) { + PyErr_SetString(PyExc_ValueError, "function not supported"); + return NULL; + } + if ((loop = _pya_malloc(sizeof(PyUFuncLoopObject)))==NULL) { + PyErr_NoMemory(); return loop; + } + + loop->index = 0; + loop->ufunc = self; + Py_INCREF(self); + loop->buffer[0] = NULL; + for (i=0; i<self->nargs; i++) { + loop->iters[i] = NULL; + loop->cast[i] = NULL; + } + loop->errobj = NULL; + + if (PyUFunc_GetPyValues((self->name ? self->name : ""), + &(loop->bufsize), &(loop->errormask), + &(loop->errobj)) < 0) goto fail; + + /* Setup the matrices */ + if (construct_matrices(loop, args, mps) < 0) goto fail; + + PyUFunc_clearfperr(); + + return loop; + + fail: + ufuncloop_dealloc(loop); + return NULL; +} + + +/* +static void +_printbytebuf(PyUFuncLoopObject *loop, int bufnum) +{ + int i; + + fprintf(stderr, "Printing byte buffer %d\n", bufnum); + for (i=0; i<loop->bufcnt; i++) { + fprintf(stderr, " %d\n", *(((byte *)(loop->buffer[bufnum]))+i)); + } +} + +static void +_printlongbuf(PyUFuncLoopObject *loop, int bufnum) +{ + int i; + + fprintf(stderr, "Printing long buffer %d\n", bufnum); + for (i=0; i<loop->bufcnt; i++) { + fprintf(stderr, " %ld\n", *(((long *)(loop->buffer[bufnum]))+i)); + } +} + +static void +_printlongbufptr(PyUFuncLoopObject *loop, int bufnum) +{ + int i; + + fprintf(stderr, "Printing long buffer %d\n", bufnum); + for (i=0; i<loop->bufcnt; i++) { + fprintf(stderr, " %ld\n", *(((long *)(loop->bufptr[bufnum]))+i)); + } +} + + + +static void +_printcastbuf(PyUFuncLoopObject *loop, int bufnum) +{ + int i; + + fprintf(stderr, "Printing long buffer %d\n", bufnum); + for (i=0; i<loop->bufcnt; i++) { + fprintf(stderr, " %ld\n", *(((long *)(loop->castbuf[bufnum]))+i)); + } +} + +*/ + + + + +/* currently generic ufuncs cannot be built for use on flexible arrays. + + The cast functions in the generic loop would need to be fixed to pass + in something besides NULL, NULL. + + Also the underlying ufunc loops would not know the element-size unless + that was passed in as data (which could be arranged). + +*/ + +/* This generic function is called with the ufunc object, the arguments to it, + and an array of (pointers to) PyArrayObjects which are NULL. The + arguments are parsed and placed in mps in construct_loop (construct_matrices) +*/ + +/*UFUNC_API*/ +static int +PyUFunc_GenericFunction(PyUFuncObject *self, PyObject *args, + PyArrayObject **mps) +{ + PyUFuncLoopObject *loop; + int i; + BEGIN_THREADS_DEF + + if (!(loop = construct_loop(self, args, mps))) return -1; + if (loop->notimplemented) {ufuncloop_dealloc(loop); return -2;} + + LOOP_BEGIN_THREADS + + switch(loop->meth) { + case ONE_UFUNCLOOP: + /* Everything is contiguous, notswapped, aligned, + and of the right type. -- Fastest. + Or if not contiguous, then a single-stride + increment moves through the entire array. + */ + /*fprintf(stderr, "ONE...%d\n", loop->size);*/ + loop->function((char **)loop->bufptr, &(loop->size), + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + break; + case NOBUFFER_UFUNCLOOP: + /* Everything is notswapped, aligned and of the + right type but not contiguous. -- Almost as fast. + */ + /*fprintf(stderr, "NOBUFFER...%d\n", loop->size);*/ + while (loop->index < loop->size) { + for (i=0; i<self->nargs; i++) + loop->bufptr[i] = loop->iters[i]->dataptr; + + loop->function((char **)loop->bufptr, &(loop->bufcnt), + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + + for (i=0; i<self->nargs; i++) { + PyArray_ITER_NEXT(loop->iters[i]); + } + loop->index++; + } + break; + case BUFFER_UFUNCLOOP: { + PyArray_CopySwapNFunc *copyswapn[MAX_ARGS]; + PyArrayIterObject **iters=loop->iters; + int *swap=loop->swap; + void **dptr=loop->dptr; + int mpselsize[MAX_ARGS]; + intp laststrides[MAX_ARGS]; + int fastmemcpy[MAX_ARGS]; + int *needbuffer=loop->needbuffer; + intp index=loop->index, size=loop->size; + int bufsize; + intp bufcnt; + int copysizes[MAX_ARGS]; + void **bufptr = loop->bufptr; + void **buffer = loop->buffer; + void **castbuf = loop->castbuf; + intp *steps = loop->steps; + char *tptr[MAX_ARGS]; + int ninnerloops = loop->ninnerloops; + Bool pyobject[MAX_ARGS]; + int datasize[MAX_ARGS]; + int i, j, k, stopcondition; + char *myptr1, *myptr2; + + + for (i=0; i<self->nargs; i++) { + copyswapn[i] = mps[i]->descr->f->copyswapn; + mpselsize[i] = mps[i]->descr->elsize; + pyobject[i] = (loop->obj && \ + (mps[i]->descr->type_num == PyArray_OBJECT)); + laststrides[i] = iters[i]->strides[loop->lastdim]; + if (steps[i] && laststrides[i] != mpselsize[i]) fastmemcpy[i] = 0; + else fastmemcpy[i] = 1; + } + /* Do generic buffered looping here (works for any kind of + arrays -- some need buffers, some don't. + */ + + /* New algorithm: N is the largest dimension. B is the buffer-size. + quotient is loop->ninnerloops-1 + remainder is loop->leftover + + Compute N = quotient * B + remainder. + quotient = N / B # integer math + (store quotient + 1) as the number of innerloops + remainder = N % B # integer remainder + + On the inner-dimension we will have (quotient + 1) loops where + the size of the inner function is B for all but the last when the niter size is + remainder. + + So, the code looks very similar to NOBUFFER_LOOP except the inner-most loop is + replaced with... + + for(i=0; i<quotient+1; i++) { + if (i==quotient+1) make itersize remainder size + copy only needed items to buffer. + swap input buffers if needed + cast input buffers if needed + call loop_function() + cast outputs in buffers if needed + swap outputs in buffers if needed + copy only needed items back to output arrays. + update all data-pointers by strides*niter + } + */ + + + /* fprintf(stderr, "BUFFER...%d,%d,%d\n", loop->size, + loop->ninnerloops, loop->leftover); + */ + /* + for (i=0; i<self->nargs; i++) { + fprintf(stderr, "iters[%d]->dataptr = %p, %p of size %d\n", i, + iters[i], iters[i]->ao->data, PyArray_NBYTES(iters[i]->ao)); + } + */ + + stopcondition = ninnerloops; + if (loop->leftover == 0) stopcondition--; + while (index < size) { + bufsize=loop->bufsize; + for (i=0; i<self->nargs; i++) { + tptr[i] = loop->iters[i]->dataptr; + if (needbuffer[i]) { + dptr[i] = bufptr[i]; + datasize[i] = (steps[i] ? bufsize : 1); + copysizes[i] = datasize[i] * mpselsize[i]; + } + else { + dptr[i] = tptr[i]; + } + } + + /* This is the inner function over the last dimension */ + for (k=1; k<=stopcondition; k++) { + if (k==ninnerloops) { + bufsize = loop->leftover; + for (i=0; i<self->nargs;i++) { + if (!needbuffer[i]) continue; + datasize[i] = (steps[i] ? bufsize : 1); + copysizes[i] = datasize[i] * mpselsize[i]; + } + } + + for (i=0; i<self->nin; i++) { + if (!needbuffer[i]) continue; + if (fastmemcpy[i]) + memcpy(buffer[i], tptr[i], + copysizes[i]); + else { + myptr1 = buffer[i]; + myptr2 = tptr[i]; + for (j=0; j<bufsize; j++) { + memcpy(myptr1, myptr2, mpselsize[i]); + myptr1 += mpselsize[i]; + myptr2 += laststrides[i]; + } + } + + /* swap the buffer if necessary */ + if (swap[i]) { + /* fprintf(stderr, "swapping...\n");*/ + copyswapn[i](buffer[i], NULL, + (intp) datasize[i], 1, + mpselsize[i]); + } + /* cast to the other buffer if necessary */ + if (loop->cast[i]) { + loop->cast[i](buffer[i], + castbuf[i], + (intp) datasize[i], + NULL, NULL); + } + } + + bufcnt = (intp) bufsize; + loop->function((char **)dptr, &bufcnt, steps, loop->funcdata); + + for (i=self->nin; i<self->nargs; i++) { + if (!needbuffer[i]) continue; + if (loop->cast[i]) { + loop->cast[i](castbuf[i], + buffer[i], + (intp) datasize[i], + NULL, NULL); + } + if (swap[i]) { + copyswapn[i](buffer[i], NULL, + (intp) datasize[i], 1, + mpselsize[i]); + } + /* copy back to output arrays */ + /* decref what's already there for object arrays */ + if (pyobject[i]) { + myptr1 = tptr[i]; + for (j=0; j<datasize[i]; j++) { + Py_XDECREF(*((PyObject **)myptr1)); + myptr1 += laststrides[i]; + } + } + if (fastmemcpy[i]) + memcpy(tptr[i], buffer[i], copysizes[i]); + else { + myptr2 = buffer[i]; + myptr1 = tptr[i]; + for (j=0; j<bufsize; j++) { + memcpy(myptr1, myptr2, + mpselsize[i]); + myptr1 += laststrides[i]; + myptr2 += mpselsize[i]; + } + } + } + if (k == stopcondition) continue; + for (i=0; i<self->nargs; i++) { + tptr[i] += bufsize * laststrides[i]; + if (!needbuffer[i]) dptr[i] = tptr[i]; + } + } + + if (loop->obj) { /* DECREF castbuf for object arrays */ + for (i=0; i<self->nargs; i++) { + if (pyobject[i]) { + if (steps[i] == 0) { + Py_XDECREF(*((PyObject **)castbuf[i])); + } + else { + int size = loop->bufsize; + PyObject **objptr = castbuf[i]; + /* size is loop->bufsize unless there + was only one loop */ + if (ninnerloops == 1) \ + size = loop->leftover; + + for (j=0; j<size; j++) { + Py_XDECREF(*objptr); + objptr += 1; + } + } + } + } + + } + + UFUNC_CHECK_ERROR(loop); + + for (i=0; i<self->nargs; i++) { + PyArray_ITER_NEXT(loop->iters[i]); + } + index++; + } + } + } + + LOOP_END_THREADS + + ufuncloop_dealloc(loop); + return 0; + + fail: + LOOP_END_THREADS + + if (loop) ufuncloop_dealloc(loop); + return -1; +} + +static PyArrayObject * +_getidentity(PyUFuncObject *self, int otype, char *str) +{ + PyObject *obj, *arr; + PyArray_Descr *typecode; + + if (self->identity == PyUFunc_None) { + PyErr_Format(PyExc_ValueError, + "zero-size array to ufunc.%s " \ + "without identity", str); + return NULL; + } + if (self->identity == PyUFunc_One) { + obj = PyInt_FromLong((long) 1); + } else { + obj = PyInt_FromLong((long) 0); + } + + typecode = PyArray_DescrFromType(otype); + arr = PyArray_FromAny(obj, typecode, 0, 0, CARRAY_FLAGS); + Py_DECREF(obj); + return (PyArrayObject *)arr; +} + +static int +_create_reduce_copy(PyUFuncReduceObject *loop, PyArrayObject **arr, int rtype) +{ + intp maxsize; + PyObject *new; + PyArray_Descr *ntype; + + maxsize = PyArray_SIZE(*arr); + + if (maxsize < loop->bufsize) { + if (!(PyArray_ISBEHAVED_RO(*arr)) || \ + PyArray_TYPE(*arr) != rtype) { + ntype = PyArray_DescrFromType(rtype); + new = PyArray_FromAny((PyObject *)(*arr), + ntype, 0, 0, + FORCECAST | ALIGNED); + if (new == NULL) return -1; + *arr = (PyArrayObject *)new; + loop->decref = new; + } + } + + /* Don't decref *arr before re-assigning + because it was not going to be DECREF'd anyway. + + If a copy is made, then the copy will be removed + on deallocation of the loop structure by setting + loop->decref. + */ + + return 0; +} + +static PyUFuncReduceObject * +construct_reduce(PyUFuncObject *self, PyArrayObject **arr, int axis, + int otype, int operation, intp ind_size, char *str) +{ + PyUFuncReduceObject *loop; + PyArrayObject *idarr; + PyArrayObject *aar; + intp loop_i[MAX_DIMS]; + int arg_types[3] = {otype, otype, otype}; + char scalars[3] = {UFUNC_NOSCALAR, UFUNC_NOSCALAR, UFUNC_NOSCALAR}; + int i, j; + int nd = (*arr)->nd; + /* Reduce type is the type requested of the input + during reduction */ + + if ((loop = _pya_malloc(sizeof(PyUFuncReduceObject)))==NULL) { + PyErr_NoMemory(); return loop; + } + + loop->swap = 0; + loop->index = 0; + loop->ufunc = self; + Py_INCREF(self); + loop->cast = NULL; + loop->buffer = NULL; + loop->ret = NULL; + loop->it = NULL; + loop->rit = NULL; + loop->errobj = NULL; + loop->decref=NULL; + loop->N = (*arr)->dimensions[axis]; + loop->instrides = (*arr)->strides[axis]; + + if (select_types(loop->ufunc, arg_types, &(loop->function), + &(loop->funcdata), scalars) == -1) goto fail; + + /* output type may change -- if it does + reduction is forced into that type + and we need to select the reduction function again + */ + if (otype != arg_types[2]) { + otype = arg_types[2]; + arg_types[0] = otype; + arg_types[1] = otype; + if (select_types(loop->ufunc, arg_types, &(loop->function), + &(loop->funcdata), scalars) == -1) + goto fail; + } + + /* get looping parameters from Python */ + if (PyUFunc_GetPyValues(str, &(loop->bufsize), &(loop->errormask), + &(loop->errobj)) < 0) goto fail; + + /* Make copy if misbehaved or not otype for small arrays */ + if (_create_reduce_copy(loop, arr, otype) < 0) goto fail; + aar = *arr; + + if (loop->N == 0) { + loop->meth = ZERODIM_REDUCELOOP; + } + else if (PyArray_ISBEHAVED_RO(aar) && \ + otype == (aar)->descr->type_num) { + if (loop->N == 1) { + loop->meth = ONEDIM_REDUCELOOP; + } + else { + loop->meth = NOBUFFER_UFUNCLOOP; + loop->steps[0] = (aar)->strides[axis]; + loop->N -= 1; + } + } + else { + loop->meth = BUFFER_UFUNCLOOP; + loop->swap = !(PyArray_ISNOTSWAPPED(aar)); + } + + /* Determine if object arrays are involved */ + if (otype == PyArray_OBJECT || aar->descr->type_num == PyArray_OBJECT) + loop->obj = 1; + else + loop->obj = 0; + + if (loop->meth == ZERODIM_REDUCELOOP) { + idarr = _getidentity(self, otype, str); + if (idarr == NULL) goto fail; + if (idarr->descr->elsize > UFUNC_MAXIDENTITY) { + PyErr_Format(PyExc_RuntimeError, + "UFUNC_MAXIDENTITY (%d)" \ + " is too small (needs to be at least %d)", + UFUNC_MAXIDENTITY, idarr->descr->elsize); + Py_DECREF(idarr); + goto fail; + } + memcpy(loop->idptr, idarr->data, idarr->descr->elsize); + Py_DECREF(idarr); + } + + /* Construct return array */ + switch(operation) { + case UFUNC_REDUCE: + for (j=0, i=0; i<nd; i++) { + if (i != axis) + loop_i[j++] = (aar)->dimensions[i]; + + } + loop->ret = (PyArrayObject *) \ + PyArray_New(aar->ob_type, aar->nd-1, loop_i, otype, + NULL, NULL, 0, 0, (PyObject *)aar); + break; + case UFUNC_ACCUMULATE: + loop->ret = (PyArrayObject *) \ + PyArray_New(aar->ob_type, aar->nd, aar->dimensions, + otype, NULL, NULL, 0, 0, (PyObject *)aar); + break; + case UFUNC_REDUCEAT: + memcpy(loop_i, aar->dimensions, nd*sizeof(intp)); + /* Index is 1-d array */ + loop_i[axis] = ind_size; + loop->ret = (PyArrayObject *)\ + PyArray_New(aar->ob_type, aar->nd, loop_i, otype, + NULL, NULL, 0, 0, (PyObject *)aar); + if (loop->ret == NULL) goto fail; + if (ind_size == 0) { + loop->meth = ZERODIM_REDUCELOOP; + return loop; + } + if (loop->meth == ONEDIM_REDUCELOOP) + loop->meth = NOBUFFER_REDUCELOOP; + break; + } + if (loop->ret == NULL) goto fail; + loop->insize = aar->descr->elsize; + loop->outsize = loop->ret->descr->elsize; + loop->bufptr[1] = loop->ret->data; + + if (loop->meth == ZERODIM_REDUCELOOP) { + loop->size = PyArray_SIZE(loop->ret); + return loop; + } + + loop->it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)aar); + if (loop->it == NULL) return NULL; + + if (loop->meth == ONEDIM_REDUCELOOP) { + loop->size = loop->it->size; + return loop; + } + + /* Fix iterator to loop over correct dimension */ + /* Set size in axis dimension to 1 */ + + loop->it->contiguous = 0; + loop->it->size /= (loop->it->dims_m1[axis]+1); + loop->it->dims_m1[axis] = 0; + loop->it->backstrides[axis] = 0; + + + loop->size = loop->it->size; + + if (operation == UFUNC_REDUCE) { + loop->steps[1] = 0; + } + else { + loop->rit = (PyArrayIterObject *) \ + PyArray_IterNew((PyObject *)(loop->ret)); + if (loop->rit == NULL) return NULL; + + /* Fix iterator to loop over correct dimension */ + /* Set size in axis dimension to 1 */ + + loop->rit->contiguous = 0; + loop->rit->size /= (loop->rit->dims_m1[axis]+1); + loop->rit->dims_m1[axis] = 0; + loop->rit->backstrides[axis] = 0; + + if (operation == UFUNC_ACCUMULATE) + loop->steps[1] = loop->ret->strides[axis]; + else + loop->steps[1] = 0; + } + loop->steps[2] = loop->steps[1]; + loop->bufptr[2] = loop->bufptr[1] + loop->steps[2]; + + + if (loop->meth == BUFFER_UFUNCLOOP) { + int _size; + loop->steps[0] = loop->outsize; + if (otype != aar->descr->type_num) { + _size=loop->bufsize*(loop->outsize + \ + aar->descr->elsize); + loop->buffer = PyDataMem_NEW(_size); + if (loop->buffer == NULL) goto fail; + if (loop->obj) memset(loop->buffer, 0, _size); + loop->castbuf = loop->buffer + \ + loop->bufsize*aar->descr->elsize; + loop->bufptr[0] = loop->castbuf; + loop->cast = aar->descr->f->cast[otype]; + } + else { + _size = loop->bufsize * loop->outsize; + loop->buffer = PyDataMem_NEW(_size); + if (loop->buffer == NULL) goto fail; + if (loop->obj) memset(loop->buffer, 0, _size); + loop->bufptr[0] = loop->buffer; + } + } + + + PyUFunc_clearfperr(); + return loop; + + fail: + ufuncreduce_dealloc(loop); + return NULL; +} + + +/* We have two basic kinds of loops */ +/* One is used when arr is not-swapped and aligned and output type + is the same as input type. + and another using buffers when one of these is not satisfied. + + Zero-length and one-length axes-to-be-reduced are handled separately. +*/ + +static PyObject * +PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, int axis, int otype) +{ + PyArrayObject *ret=NULL; + PyUFuncReduceObject *loop; + intp i, n; + char *dptr; + BEGIN_THREADS_DEF + + /* Construct loop object */ + loop = construct_reduce(self, &arr, axis, otype, UFUNC_REDUCE, 0, + "reduce"); + if (!loop) return NULL; + + LOOP_BEGIN_THREADS + switch(loop->meth) { + case ZERODIM_REDUCELOOP: + /* fprintf(stderr, "ZERO..%d\n", loop->size); */ + for(i=0; i<loop->size; i++) { + if (loop->obj) Py_INCREF(*((PyObject **)loop->idptr)); + memmove(loop->bufptr[1], loop->idptr, loop->outsize); + loop->bufptr[1] += loop->outsize; + } + break; + case ONEDIM_REDUCELOOP: + /*fprintf(stderr, "ONEDIM..%d\n", loop->size); */ + while(loop->index < loop->size) { + if (loop->obj) Py_INCREF(*((PyObject **)loop->it->dataptr)); + memmove(loop->bufptr[1], loop->it->dataptr, + loop->outsize); + PyArray_ITER_NEXT(loop->it); + loop->bufptr[1] += loop->outsize; + loop->index++; + } + break; + case NOBUFFER_UFUNCLOOP: + /*fprintf(stderr, "NOBUFFER..%d\n", loop->size); */ + while(loop->index < loop->size) { + /* Copy first element to output */ + if (loop->obj) + Py_INCREF(*((PyObject **)loop->it->dataptr)); + memmove(loop->bufptr[1], loop->it->dataptr, + loop->outsize); + /* Adjust input pointer */ + loop->bufptr[0] = loop->it->dataptr+loop->steps[0]; + loop->function((char **)loop->bufptr, + &(loop->N), + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + + PyArray_ITER_NEXT(loop->it) + loop->bufptr[1] += loop->outsize; + loop->bufptr[2] = loop->bufptr[1]; + loop->index++; + } + break; + case BUFFER_UFUNCLOOP: + /* use buffer for arr */ + /* + For each row to reduce + 1. copy first item over to output (casting if necessary) + 2. Fill inner buffer + 3. When buffer is filled or end of row + a. Cast input buffers if needed + b. Call inner function. + 4. Repeat 2 until row is done. + */ + /* fprintf(stderr, "BUFFERED..%d %d\n", loop->size, + loop->swap); */ + while(loop->index < loop->size) { + loop->inptr = loop->it->dataptr; + /* Copy (cast) First term over to output */ + if (loop->cast) { + /* A little tricky because we need to + cast it first */ + arr->descr->f->copyswap(loop->buffer, + loop->inptr, + loop->swap, + loop->insize); + loop->cast(loop->buffer, loop->castbuf, + 1, NULL, NULL); + if (loop->obj) + Py_INCREF(*((PyObject **)loop->castbuf)); + memcpy(loop->bufptr[1], loop->castbuf, + loop->outsize); + } + else { /* Simple copy */ + arr->descr->f->copyswap(loop->bufptr[1], + loop->inptr, + loop->swap, + loop->insize); + } + loop->inptr += loop->instrides; + n = 1; + while(n < loop->N) { + /* Copy up to loop->bufsize elements to + buffer */ + dptr = loop->buffer; + for (i=0; i<loop->bufsize; i++, n++) { + if (n == loop->N) break; + arr->descr->f->copyswap(dptr, + loop->inptr, + loop->swap, + loop->insize); + loop->inptr += loop->instrides; + dptr += loop->insize; + } + if (loop->cast) + loop->cast(loop->buffer, + loop->castbuf, + i, NULL, NULL); + loop->function((char **)loop->bufptr, + &i, + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + } + PyArray_ITER_NEXT(loop->it); + loop->bufptr[1] += loop->outsize; + loop->bufptr[2] = loop->bufptr[1]; + loop->index++; + } + } + + LOOP_END_THREADS + + ret = loop->ret; + /* Hang on to this reference -- will be decref'd with loop */ + Py_INCREF(ret); + ufuncreduce_dealloc(loop); + return (PyObject *)ret; + + fail: + LOOP_END_THREADS + + if (loop) ufuncreduce_dealloc(loop); + return NULL; +} + + +static PyObject * +PyUFunc_Accumulate(PyUFuncObject *self, PyArrayObject *arr, int axis, + int otype) +{ + PyArrayObject *ret=NULL; + PyUFuncReduceObject *loop; + intp i, n; + char *dptr; + + /* Construct loop object */ + loop = construct_reduce(self, &arr, axis, otype, UFUNC_ACCUMULATE, 0, + "accumulate"); + if (!loop) return NULL; + + LOOP_BEGIN_THREADS + switch(loop->meth) { + case ZERODIM_REDUCELOOP: /* Accumulate */ + /* fprintf(stderr, "ZERO..%d\n", loop->size); */ + for(i=0; i<loop->size; i++) { + if (loop->obj) + Py_INCREF(*((PyObject **)loop->idptr)); + memcpy(loop->bufptr[1], loop->idptr, loop->outsize); + loop->bufptr[1] += loop->outsize; + } + break; + case ONEDIM_REDUCELOOP: /* Accumulate */ + /* fprintf(stderr, "ONEDIM..%d\n", loop->size); */ + while(loop->index < loop->size) { + if (loop->obj) + Py_INCREF(*((PyObject **)loop->it->dataptr)); + memcpy(loop->bufptr[1], loop->it->dataptr, + loop->outsize); + PyArray_ITER_NEXT(loop->it); + loop->bufptr[1] += loop->outsize; + loop->index++; + } + break; + case NOBUFFER_UFUNCLOOP: /* Accumulate */ + /* fprintf(stderr, "NOBUFFER..%d\n", loop->size); */ + while(loop->index < loop->size) { + /* Copy first element to output */ + if (loop->obj) + Py_INCREF(*((PyObject **)loop->it->dataptr)); + memcpy(loop->bufptr[1], loop->it->dataptr, + loop->outsize); + /* Adjust input pointer */ + loop->bufptr[0] = loop->it->dataptr+loop->steps[0]; + loop->function((char **)loop->bufptr, + &(loop->N), + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + + PyArray_ITER_NEXT(loop->it); + PyArray_ITER_NEXT(loop->rit); + loop->bufptr[1] = loop->rit->dataptr; + loop->bufptr[2] = loop->bufptr[1] + loop->steps[1]; + loop->index++; + } + break; + case BUFFER_UFUNCLOOP: /* Accumulate */ + /* use buffer for arr */ + /* + For each row to reduce + 1. copy identity over to output (casting if necessary) + 2. Fill inner buffer + 3. When buffer is filled or end of row + a. Cast input buffers if needed + b. Call inner function. + 4. Repeat 2 until row is done. + */ + /* fprintf(stderr, "BUFFERED..%d %p\n", loop->size, + loop->cast); */ + while(loop->index < loop->size) { + loop->inptr = loop->it->dataptr; + /* Copy (cast) First term over to output */ + if (loop->cast) { + /* A little tricky because we need to + cast it first */ + arr->descr->f->copyswap(loop->buffer, + loop->inptr, + loop->swap, + loop->insize); + loop->cast(loop->buffer, loop->castbuf, + 1, NULL, NULL); + if (loop->obj) + Py_INCREF(*((PyObject **)loop->castbuf)); + memcpy(loop->bufptr[1], loop->castbuf, + loop->outsize); + } + else { /* Simple copy */ + arr->descr->f->copyswap(loop->bufptr[1], + loop->inptr, + loop->swap, + loop->insize); + } + loop->inptr += loop->instrides; + n = 1; + while(n < loop->N) { + /* Copy up to loop->bufsize elements to + buffer */ + dptr = loop->buffer; + for (i=0; i<loop->bufsize; i++, n++) { + if (n == loop->N) break; + arr->descr->f->copyswap(dptr, + loop->inptr, + loop->swap, + loop->insize); + loop->inptr += loop->instrides; + dptr += loop->insize; + } + if (loop->cast) + loop->cast(loop->buffer, + loop->castbuf, + i, NULL, NULL); + loop->function((char **)loop->bufptr, + &i, + loop->steps, loop->funcdata); + UFUNC_CHECK_ERROR(loop); + } + PyArray_ITER_NEXT(loop->it); + PyArray_ITER_NEXT(loop->rit); + loop->bufptr[1] = loop->rit->dataptr; + loop->bufptr[2] = loop->bufptr[1] + loop->steps[1]; + loop->index++; + } + } + + LOOP_END_THREADS + ret = loop->ret; + /* Hang on to this reference -- will be decref'd with loop */ + Py_INCREF(ret); + ufuncreduce_dealloc(loop); + return (PyObject *)ret; + + fail: + LOOP_END_THREADS + + if (loop) ufuncreduce_dealloc(loop); + return NULL; +} + +/* Reduceat performs a reduce over an axis using the indices as a guide + +op.reduceat(array,indices) computes +op.reduce(array[indices[i]:indices[i+1]] + for i=0..end with an implicit indices[i+1]=len(array) + assumed when i=end-1 + +if indices[i+1] <= indices[i]+1 + then the result is array[indices[i]] for that value + +op.accumulate(array) is the same as +op.reduceat(array,indices)[::2] +where indices is range(len(array)-1) with a zero placed in every other sample + indices = zeros(len(array)*2-1) + indices[1::2] = range(1,len(array)) + +output shape is based on the size of indices + */ + +static PyObject * +PyUFunc_Reduceat(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *ind, + int axis, int otype) +{ + PyArrayObject *ret; + PyUFuncReduceObject *loop; + intp *ptr=(intp *)ind->data; + intp nn=ind->dimensions[0]; + intp mm=arr->dimensions[axis]-1; + intp n, i, j; + char *dptr; + + /* Check for out-of-bounds values in indices array */ + for (i=0; i<nn; i++) { + if ((*ptr < 0) || (*ptr > mm)) { + PyErr_Format(PyExc_IndexError, + "index out-of-bounds (0, %d)", (int) mm); + return NULL; + } + ptr++; + } + + ptr = (intp *)ind->data; + /* Construct loop object */ + loop = construct_reduce(self, &arr, axis, otype, UFUNC_REDUCEAT, nn, + "reduceat"); + if (!loop) return NULL; + + LOOP_BEGIN_THREADS + switch(loop->meth) { + /* zero-length index -- return array immediately */ + case ZERODIM_REDUCELOOP: + /* fprintf(stderr, "ZERO..\n"); */ + break; + /* NOBUFFER -- behaved array and same type */ + case NOBUFFER_UFUNCLOOP: /* Reduceat */ + /* fprintf(stderr, "NOBUFFER..%d\n", loop->size); */ + while(loop->index < loop->size) { + ptr = (intp *)ind->data; + for (i=0; i<nn; i++) { + loop->bufptr[0] = loop->it->dataptr + \ + (*ptr)*loop->instrides; + if (loop->obj) + Py_INCREF(*((PyObject **)loop->bufptr[0])); + memcpy(loop->bufptr[1], loop->bufptr[0], + loop->outsize); + mm = (i==nn-1 ? arr->dimensions[axis]-*ptr : \ + *(ptr+1) - *ptr) - 1; + if (mm > 0) { + loop->bufptr[0] += loop->instrides; + loop->bufptr[2] = loop->bufptr[1]; + loop->function((char **)loop->bufptr, + &mm, loop->steps, + loop->funcdata); + UFUNC_CHECK_ERROR(loop); + } + loop->bufptr[1] += loop->ret->strides[axis]; + ptr++; + } + PyArray_ITER_NEXT(loop->it); + PyArray_ITER_NEXT(loop->rit); + loop->bufptr[1] = loop->rit->dataptr; + loop->index++; + } + break; + + /* BUFFER -- misbehaved array or different types */ + case BUFFER_UFUNCLOOP: /* Reduceat */ + /* fprintf(stderr, "BUFFERED..%d\n", loop->size); */ + while(loop->index < loop->size) { + ptr = (intp *)ind->data; + for (i=0; i<nn; i++) { + if (loop->obj) + Py_INCREF(*((PyObject **)loop->idptr)); + memcpy(loop->bufptr[1], loop->idptr, + loop->outsize); + n = 0; + mm = (i==nn-1 ? arr->dimensions[axis] - *ptr :\ + *(ptr+1) - *ptr); + if (mm < 1) mm = 1; + loop->inptr = loop->it->dataptr + \ + (*ptr)*loop->instrides; + while (n < mm) { + /* Copy up to loop->bufsize elements + to buffer */ + dptr = loop->buffer; + for (j=0; j<loop->bufsize; j++, n++) { + if (n == mm) break; + arr->descr->f->copyswap\ + (dptr, + loop->inptr, + loop->swap, + loop->insize); + loop->inptr += loop->instrides; + dptr += loop->insize; + } + if (loop->cast) + loop->cast(loop->buffer, + loop->castbuf, + j, NULL, NULL); + loop->bufptr[2] = loop->bufptr[1]; + loop->function((char **)loop->bufptr, + &j, loop->steps, + loop->funcdata); + UFUNC_CHECK_ERROR(loop); + } + loop->bufptr[1] += loop->ret->strides[axis]; + ptr++; + } + PyArray_ITER_NEXT(loop->it); + PyArray_ITER_NEXT(loop->rit); + loop->bufptr[1] = loop->rit->dataptr; + loop->index++; + } + break; + } + + LOOP_END_THREADS + + ret = loop->ret; + /* Hang on to this reference -- will be decref'd with loop */ + Py_INCREF(ret); + ufuncreduce_dealloc(loop); + return (PyObject *)ret; + + fail: + LOOP_END_THREADS + + if (loop) ufuncreduce_dealloc(loop); + return NULL; +} + + +/* This code handles reduce, reduceat, and accumulate + (accumulate and reduce are special cases of the more general reduceat + but they are handled separately for speed) +*/ + +static PyObject * +PyUFunc_GenericReduction(PyUFuncObject *self, PyObject *args, + PyObject *kwds, int operation) +{ + int axis=0; + PyArrayObject *mp, *ret = NULL; + PyObject *op, *res=NULL; + PyObject *obj_ind; + PyArrayObject *indices = NULL; + PyArray_Descr *otype=NULL; + static char *kwlist1[] = {"array", "axis", "dtype", NULL}; + static char *kwlist2[] = {"array", "indices", "axis", "dtype", NULL}; + static char *_reduce_type[] = {"reduce", "accumulate", \ + "reduceat", NULL}; + if (self == NULL) { + PyErr_SetString(PyExc_ValueError, "function not supported"); + return NULL; + } + + if (self->nin != 2) { + PyErr_Format(PyExc_ValueError, + "%s only supported for binary functions", + _reduce_type[operation]); + return NULL; + } + if (self->nout != 1) { + PyErr_Format(PyExc_ValueError, + "%s only supported for functions " \ + "returning a single value", + _reduce_type[operation]); + return NULL; + } + + if (operation == UFUNC_REDUCEAT) { + PyArray_Descr *indtype; + indtype = PyArray_DescrFromType(PyArray_INTP); + if(!PyArg_ParseTupleAndKeywords(args, kwds, "OO|iO&", kwlist2, + &op, &obj_ind, &axis, + PyArray_DescrConverter, + &otype)) return NULL; + indices = (PyArrayObject *)PyArray_FromAny(obj_ind, indtype, + 1, 1, CARRAY_FLAGS); + if (indices == NULL) return NULL; + Py_DECREF(indtype); + } + else { + if(!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO&", kwlist1, + &op, &axis, + PyArray_DescrConverter, + &otype)) return NULL; + } + + /* Ensure input is an array */ + mp = (PyArrayObject *)PyArray_FromAny(op, NULL, 0, 0, 0); + if (mp == NULL) return NULL; + + /* Check to see if input is zero-dimensional */ + if (mp->nd == 0) { + PyErr_Format(PyExc_TypeError, "cannot %s on a scalar", + _reduce_type[operation]); + Py_DECREF(mp); + return NULL; + } + + /* Check to see that type (and otype) is not FLEXIBLE */ + if (PyArray_ISFLEXIBLE(mp) || (otype && PyTypeNum_ISFLEXIBLE(otype->type_num))) { + PyErr_Format(PyExc_TypeError, + "cannot perform %s with flexible type", + _reduce_type[operation]); + Py_DECREF(mp); + return NULL; + } + + if (axis < 0) axis += mp->nd; + if (axis < 0 || axis >= mp->nd) { + PyErr_SetString(PyExc_ValueError, "axis not in array"); + Py_DECREF(mp); + return NULL; + } + + /* Get default type to reduce over if not given */ + if (otype == NULL) { + /* For integer types --- makes sure at + least a long is used */ + int typenum = PyArray_TYPE(mp); + if (PyTypeNum_ISINTEGER(typenum) && \ + (mp->descr->elsize < sizeof(long))) { + if (PyTypeNum_ISUNSIGNED(typenum)) + typenum = PyArray_ULONG; + else + typenum = PyArray_LONG; + } + else if (PyTypeNum_ISBOOL(typenum) && \ + ((strcmp(self->name,"add")==0) || \ + (strcmp(self->name,"multiply")==0))) { + typenum = PyArray_LONG; + } + otype = PyArray_DescrFromType(typenum); + } + + switch(operation) { + case UFUNC_REDUCE: + ret = (PyArrayObject *)PyUFunc_Reduce(self, mp, axis, + otype->type_num); + break; + case UFUNC_ACCUMULATE: + ret = (PyArrayObject *)PyUFunc_Accumulate(self, mp, axis, + otype->type_num); + break; + case UFUNC_REDUCEAT: + ret = (PyArrayObject *)PyUFunc_Reduceat(self, mp, indices, + axis, otype->type_num); + Py_DECREF(indices); + break; + } + Py_DECREF(mp); + Py_DECREF(otype); + if (ret==NULL) return NULL; + if (op->ob_type != ret->ob_type) { + res = PyObject_CallMethod(op, "__array_wrap__", "O", ret); + if (res == NULL) PyErr_Clear(); + else if (res == Py_None) Py_DECREF(res); + else { + Py_DECREF(ret); + return res; + } + } + return PyArray_Return(ret); + +} + + + +/* ---------- */ + +static PyObject * +_find_array_wrap(PyObject *args) +{ + int nargs, i; + int np = 0; + int argmax = 0; + int val; + double priority[MAX_ARGS]; + double maxpriority = PyArray_SUBTYPE_PRIORITY; + PyObject *with_wrap[MAX_ARGS]; + PyObject *attr; + PyObject *obj; + + nargs = PyTuple_Size(args); + for (i=0; i<nargs; i++) { + obj = PyTuple_GET_ITEM(args, i); + if (PyArray_CheckExact(obj) || PyBigArray_CheckExact(obj) || \ + PyArray_IsAnyScalar(obj)) + continue; + attr = PyObject_GetAttrString(obj, "__array_wrap__"); + if (attr != NULL) { + val = PyCallable_Check(attr); + Py_DECREF(attr); + if (val) { + attr = PyObject_GetAttrString(obj, + "__array_priority__"); + if (attr == NULL) + priority[np] = \ + PyArray_SUBTYPE_PRIORITY; + else { + priority[np] = PyFloat_AsDouble(attr); + if (PyErr_Occurred()) { + PyErr_Clear(); + priority[np] = PyArray_SUBTYPE_PRIORITY; + } + Py_DECREF(attr); + } + with_wrap[np] = obj; + np += 1; + } + } + PyErr_Clear(); + } + + if (np == 0) return NULL; + + for (i=0; i<np; i++) { + if (priority[i] > maxpriority) { + maxpriority = priority[i]; + argmax = i; + } + } + + return with_wrap[argmax]; +} + +static PyObject * +ufunc_generic_call(PyUFuncObject *self, PyObject *args) +{ + int i; + PyTupleObject *ret; + PyArrayObject *mps[MAX_ARGS]; + PyObject *retobj[MAX_ARGS]; + PyObject *res; + PyObject *obj; + int errval; + + /* Initialize all array objects to NULL to make cleanup easier + if something goes wrong. */ + for(i=0; i<self->nargs; i++) mps[i] = NULL; + + errval = PyUFunc_GenericFunction(self, args, mps); + if (errval < 0) { + for(i=0; i<self->nargs; i++) Py_XDECREF(mps[i]); + if (errval == -1) + return NULL; + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + } + + for(i=0; i<self->nin; i++) Py_DECREF(mps[i]); + + /* Use __array_wrap__ on all outputs + if present on one of the input arguments. + If present for multiple inputs: + use __array_wrap__ of input object with largest + __array_priority__ (default = 0.0) + */ + obj = _find_array_wrap(args); + + /* wrap outputs */ + for (i=0; i<self->nout; i++) { + int j=self->nin+i; + /* check to see if any UPDATEIFCOPY flags are set + which meant that a temporary output was generated + */ + if (mps[j]->flags & UPDATEIFCOPY) { + PyObject *old = mps[j]->base; + Py_INCREF(old); /* we want to hang on to this */ + Py_DECREF(mps[j]); /* should trigger the copy + back into old */ + mps[j] = (PyArrayObject *)old; + } + if (obj != NULL) { + res = PyObject_CallMethod(obj, "__array_wrap__", + "O", mps[j]); + if (res == NULL) PyErr_Clear(); + else if (res == Py_None) Py_DECREF(res); + else { + Py_DECREF(mps[j]); + retobj[i] = res; + continue; + } + } + retobj[i] = PyArray_Return(mps[j]); + } + + if (self->nout == 1) { + return retobj[0]; + } else { + ret = (PyTupleObject *)PyTuple_New(self->nout); + for(i=0; i<self->nout; i++) { + PyTuple_SET_ITEM(ret, i, retobj[i]); + } + return (PyObject *)ret; + } + +} + +static PyObject * +ufunc_update_use_defaults(PyObject *dummy, PyObject *args) +{ + PyObject *errobj; + int errmask, bufsize; + + if (!PyArg_ParseTuple(args, "")) return NULL; + + PyUFunc_USEDEFAULTS = 0; + if (PyUFunc_GetPyValues("test", &bufsize, &errmask, &errobj) < 0) return NULL; + + if ((errmask == UFUNC_ERR_DEFAULT) && \ + (bufsize == PyArray_BUFSIZE) && \ + (PyTuple_GET_ITEM(errobj, 1) == Py_None)) { + PyUFunc_USEDEFAULTS = 1; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om}; + +static char +doc_frompyfunc[] = "frompyfunc(func, nin, nout) take an arbitrary python function that takes nin objects as input and returns nout objects and return a universal function (ufunc). This ufunc always returns PyObject arrays"; + +static PyObject * +ufunc_frompyfunc(PyObject *dummy, PyObject *args, PyObject *kwds) { + /* Keywords are ignored for now */ + + PyObject *function, *pyname=NULL; + int nin, nout, i; + PyUFunc_PyFuncData *fdata; + PyUFuncObject *self; + char *fname, *str; + int fname_len=-1; + int offset[2]; + + if (!PyArg_ParseTuple(args, "Oii", &function, &nin, &nout)) return NULL; + + if (!PyCallable_Check(function)) { + PyErr_SetString(PyExc_TypeError, "function must be callable"); + return NULL; + } + + self = _pya_malloc(sizeof(PyUFuncObject)); + if (self == NULL) return NULL; + PyObject_Init((PyObject *)self, &PyUFunc_Type); + + self->userloops = NULL; + self->nin = nin; + self->nout = nout; + self->nargs = nin+nout; + self->identity = PyUFunc_None; + self->functions = pyfunc_functions; + + self->ntypes = 1; + self->check_return = 0; + + pyname = PyObject_GetAttrString(function, "__name__"); + if (pyname) + (void) PyString_AsStringAndSize(pyname, &fname, &fname_len); + + if (PyErr_Occurred()) { + fname = "?"; + fname_len = 1; + PyErr_Clear(); + } + Py_XDECREF(pyname); + + + + /* self->ptr holds a pointer for enough memory for + self->data[0] (fdata) + self->data + self->name + self->types + + To be safest, all of these need their memory aligned on void * pointers + Therefore, we may need to allocate extra space. + */ + offset[0] = sizeof(PyUFunc_PyFuncData); + i = (sizeof(PyUFunc_PyFuncData) % sizeof(void *)); + if (i) offset[0] += (sizeof(void *) - i); + offset[1] = self->nargs; + i = (self->nargs % sizeof(void *)); + if (i) offset[1] += (sizeof(void *)-i); + + self->ptr = _pya_malloc(offset[0] + offset[1] + sizeof(void *) + \ + (fname_len+14)); + + if (self->ptr == NULL) return PyErr_NoMemory(); + Py_INCREF(function); + self->obj = function; + fdata = (PyUFunc_PyFuncData *)(self->ptr); + fdata->nin = nin; + fdata->nout = nout; + fdata->callable = function; + + self->data = (void **)(self->ptr + offset[0]); + self->data[0] = (void *)fdata; + + self->types = (char *)self->data + sizeof(void *); + for (i=0; i<self->nargs; i++) self->types[i] = PyArray_OBJECT; + + str = self->types + offset[1]; + memcpy(str, fname, fname_len); + memcpy(str+fname_len, " (vectorized)", 14); + + self->name = str; + + /* Do a better job someday */ + self->doc = "dynamic ufunc based on a python function"; + + + return (PyObject *)self; +} + + +/*UFUNC_API*/ +static PyObject * +PyUFunc_FromFuncAndData(PyUFuncGenericFunction *func, void **data, + char *types, int ntypes, + int nin, int nout, int identity, + char *name, char *doc, int check_return) +{ + PyUFuncObject *self; + + self = _pya_malloc(sizeof(PyUFuncObject)); + if (self == NULL) return NULL; + PyObject_Init((PyObject *)self, &PyUFunc_Type); + + self->nin = nin; + self->nout = nout; + self->nargs = nin+nout; + self->identity = identity; + + self->functions = func; + self->data = data; + self->types = types; + self->ntypes = ntypes; + self->check_return = check_return; + self->ptr = NULL; + self->obj = NULL; + self->userloops=NULL; + + if (name == NULL) self->name = "?"; + else self->name = name; + + if (doc == NULL) self->doc = "NULL"; + else self->doc = doc; + + return (PyObject *)self; +} + +/*UFUNC_API*/ +static int +PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, + int usertype, + PyUFuncGenericFunction function, + void *data) +{ + PyArray_Descr *descr; + PyObject *key, *cobj; + int ret; + + descr=PyArray_DescrFromType(usertype); + if ((usertype < PyArray_USERDEF) || (descr==NULL)) { + PyErr_SetString(PyExc_TypeError, + "unknown type"); + return -1; + } + Py_DECREF(descr); + + if (ufunc->userloops == NULL) { + ufunc->userloops = PyDict_New(); + } + key = PyInt_FromLong((long) usertype); + if (key == NULL) return -1; + cobj = PyCObject_FromVoidPtr((void *)function, NULL); + if (cobj == NULL) {Py_DECREF(key); return -1;} + if (data == NULL) { + ret = PyDict_SetItem(ufunc->userloops, key, cobj); + Py_DECREF(cobj); + Py_DECREF(key); + return ret; + } + else { + PyObject *cobj2, *tmp; + cobj2 = PyCObject_FromVoidPtr(data, NULL); + if (cobj2 == NULL) { + Py_DECREF(cobj); + Py_DECREF(key); + return -1; + } + tmp=Py_BuildValue("NN", cobj, cobj2); + ret = PyDict_SetItem(ufunc->userloops, key, tmp); + Py_DECREF(tmp); + Py_DECREF(key); + return ret; + } +} + +static void +ufunc_dealloc(PyUFuncObject *self) +{ + if (self->ptr) _pya_free(self->ptr); + Py_XDECREF(self->userloops); + Py_XDECREF(self->obj); + _pya_free(self); +} + +static PyObject * +ufunc_repr(PyUFuncObject *self) +{ + char buf[100]; + + sprintf(buf, "<ufunc '%.50s'>", self->name); + + return PyString_FromString(buf); +} + + +/* -------------------------------------------------------- */ + +/* op.outer(a,b) is equivalent to op(a[:,NewAxis,NewAxis,etc.],b) + where a has b.ndim NewAxis terms appended. + + The result has dimensions a.ndim + b.ndim + */ + +static PyObject * +ufunc_outer(PyUFuncObject *self, PyObject *args) +{ + int i; + PyObject *ret; + PyArrayObject *ap1=NULL, *ap2=NULL, *ap_new=NULL; + PyObject *new_args, *tmp; + PyObject *shape1, *shape2, *newshape; + + if(self->nin != 2) { + PyErr_SetString(PyExc_ValueError, + "outer product only supported "\ + "for binary functions"); + return NULL; + } + + if (PySequence_Length(args) != 2) { + PyErr_SetString(PyExc_TypeError, + "exactly two arguments expected"); + return NULL; + } + + tmp = PySequence_GetItem(args, 0); + if (tmp == NULL) return NULL; + ap1 = (PyArrayObject *) \ + PyArray_FromObject(tmp, PyArray_NOTYPE, 0, 0); + Py_DECREF(tmp); + if (ap1 == NULL) return NULL; + + tmp = PySequence_GetItem(args, 1); + if (tmp == NULL) return NULL; + ap2 = (PyArrayObject *)PyArray_FromObject(tmp, PyArray_NOTYPE, 0, 0); + Py_DECREF(tmp); + if (ap2 == NULL) {Py_DECREF(ap1); return NULL;} + + /* Construct new shape tuple */ + shape1 = PyTuple_New(ap1->nd); + if (shape1 == NULL) goto fail; + for (i=0; i<ap1->nd; i++) + PyTuple_SET_ITEM(shape1, i, + PyLong_FromLongLong((longlong)ap1-> \ + dimensions[i])); + + shape2 = PyTuple_New(ap2->nd); + for (i=0; i<ap2->nd; i++) + PyTuple_SET_ITEM(shape2, i, PyInt_FromLong((long) 1)); + if (shape2 == NULL) {Py_DECREF(shape1); goto fail;} + newshape = PyNumber_Add(shape1, shape2); + Py_DECREF(shape1); + Py_DECREF(shape2); + if (newshape == NULL) goto fail; + + ap_new = (PyArrayObject *)PyArray_Reshape(ap1, newshape); + Py_DECREF(newshape); + if (ap_new == NULL) goto fail; + + new_args = Py_BuildValue("(OO)", ap_new, ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + Py_DECREF(ap_new); + ret = ufunc_generic_call(self, new_args); + Py_DECREF(new_args); + return ret; + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ap_new); + return NULL; + +} + + +static PyObject * +ufunc_reduce(PyUFuncObject *self, PyObject *args, PyObject *kwds) +{ + + return PyUFunc_GenericReduction(self, args, kwds, UFUNC_REDUCE); +} + +static PyObject * +ufunc_accumulate(PyUFuncObject *self, PyObject *args, PyObject *kwds) +{ + + return PyUFunc_GenericReduction(self, args, kwds, UFUNC_ACCUMULATE); +} + +static PyObject * +ufunc_reduceat(PyUFuncObject *self, PyObject *args, PyObject *kwds) +{ + return PyUFunc_GenericReduction(self, args, kwds, UFUNC_REDUCEAT); +} + + +static struct PyMethodDef ufunc_methods[] = { + {"reduce", (PyCFunction)ufunc_reduce, METH_VARARGS | METH_KEYWORDS}, + {"accumulate", (PyCFunction)ufunc_accumulate, + METH_VARARGS | METH_KEYWORDS}, + {"reduceat", (PyCFunction)ufunc_reduceat, + METH_VARARGS | METH_KEYWORDS}, + {"outer", (PyCFunction)ufunc_outer, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + + +/* construct the string + y1,y2,...,yn +*/ + +static void +_makeargs(int num, char ltr, char *str) +{ + int ind=0; + int k; + static char *digits="123456789ABCDE"; + + if (num == 1) { + str[0] = ltr; + ind = 1; + } + else { + for (k=0; k<num; k++) { + str[3*k] = ltr; + str[3*k+1] = digits[k]; + str[3*k+2] = ','; + } + /* overwrite last comma */ + ind = 3*k-1; + } + + str[ind] = '\0'; + return; +} + +static char +_typecharfromnum(int num) { + PyArray_Descr *descr; + char ret; + + descr = PyArray_DescrFromType(num); + ret = descr->type; + Py_DECREF(descr); + return ret; +} + +static PyObject * +ufunc_getattr(PyUFuncObject *self, char *name) +{ + PyObject *obj; + /* Put docstring first or FindMethod finds it...*/ + /* could so some introspection on name and nin + nout */ + /* to automate the first part of it */ + /* the doc string shouldn't need the calling convention */ + if (strcmp(name, "__doc__") == 0) { + static char doc[256]; + static char tmp1[3*MAX_ARGS+2]; + static char tmp2[3*MAX_ARGS+2]; + /* construct + y1,y2,,... = name(x1,x2,...) __doc__ + */ + _makeargs(self->nout, 'y', tmp1); + _makeargs(self->nin, 'x', tmp2); + snprintf(doc, 256, "%s = %s(%s) %s", tmp1, self->name, + tmp2, self->doc); + return PyString_FromString(doc); + } + obj = Py_FindMethod(ufunc_methods, (PyObject *)self, name); + if (obj != NULL) return obj; + PyErr_Clear(); + if (strcmp(name, "nin") == 0) { + return PyInt_FromLong(self->nin); + } + else if (strcmp(name, "nout") == 0) { + return PyInt_FromLong(self->nout); + } + else if (strcmp(name, "nargs") == 0) { + return PyInt_FromLong(self->nargs); + } + else if (strcmp(name, "ntypes") == 0) { + return PyInt_FromLong(self->ntypes); + } + else if (strcmp(name, "types") == 0) { + /* return a list with types grouped + input->output */ + PyObject *list; + PyObject *str; + int k, j, n, nt=self->ntypes; + int ni = self->nin; + int no = self->nout; + char *t; + list = PyList_New(nt); + if (list == NULL) return NULL; + t = _pya_malloc(no+ni+2); + n = 0; + for (k=0; k<nt; k++) { + for (j=0; j<ni; j++) { + t[j] = _typecharfromnum(self->types[n]); + n++; + } + t[ni] = '-'; + t[ni+1] = '>'; + for (j=0; j<no; j++) { + t[ni+2+j] = \ + _typecharfromnum(self->types[n]); + n++; + } + str = PyString_FromStringAndSize(t, no+ni+2); + PyList_SET_ITEM(list, k, str); + } + _pya_free(t); + return list; + + } + else if (strcmp(name, "__name__") == 0) { + return PyString_FromString(self->name); + } + else if (strcmp(name, "identity") == 0) { + switch(self->identity) { + case PyUFunc_One: + return PyInt_FromLong(1); + case PyUFunc_Zero: + return PyInt_FromLong(0); + default: + Py_INCREF(Py_None); + return Py_None; + } + } + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +#undef _typecharfromnum + +static int +ufunc_setattr(PyUFuncObject *self, char *name, PyObject *v) +{ + return -1; +} + +static char Ufunctype__doc__[] = + "Optimized functions make it possible to implement arithmetic "\ + "with arrays efficiently"; + +static PyTypeObject PyUFunc_Type = { + PyObject_HEAD_INIT(0) + 0, /*ob_size*/ + "scipy.ufunc", /*tp_name*/ + sizeof(PyUFuncObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)ufunc_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)ufunc_getattr, /*tp_getattr*/ + (setattrfunc)ufunc_setattr, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)ufunc_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)ufunc_generic_call, /*tp_call*/ + (reprfunc)ufunc_repr, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + Ufunctype__doc__ /* Documentation string */ +}; + +/* End of code for ufunc objects */ +/* -------------------------------------------------------- */ diff --git a/numpy/core/src/umathmodule.c.src b/numpy/core/src/umathmodule.c.src new file mode 100644 index 000000000..5096f3361 --- /dev/null +++ b/numpy/core/src/umathmodule.c.src @@ -0,0 +1,1847 @@ +/* -*- c -*- */ + +#include "Python.h" +#include "scipy/arrayobject.h" +#define _UMATHMODULE +#include "scipy/ufuncobject.h" +#include "abstract.h" +#include <math.h> + + +/* A whole slew of basic math functions are provided originally by Konrad Hinsen. */ + +#if !defined(__STDC__) && !defined(_MSC_VER) +extern double fmod (double, double); +extern double frexp (double, int *); +extern double ldexp (double, int); +extern double modf (double, double *); +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846264338328 +#endif + +#ifndef HAVE_INVERSE_HYPERBOLIC +static double acosh(double x) +{ + return log(x + sqrt((x-1.0)*(x+1.0))); +} + +static double asinh(double xx) +{ + double x; + int sign; + if (xx < 0.0) { + sign = -1; + x = -xx; + } + else { + sign = 1; + x = xx; + } + return sign*log(x + sqrt(x*x+1.0)); +} + +static double atanh(double x) +{ + return 0.5*log((1.0+x)/(1.0-x)); +} +#endif + +#ifdef HAVE_HYPOT +#if !defined(NeXT) && !defined(_MSC_VER) +extern double hypot(double, double); +#endif +#else +double hypot(double x, double y) +{ + double yx; + + x = fabs(x); + y = fabs(y); + if (x < y) { + double temp = x; + x = y; + y = temp; + } + if (x == 0.) + return 0.; + else { + yx = y/x; + return x*sqrt(1.+yx*yx); + } +} +#endif + + + +/* Define isnan, isinf, isfinite, signbit if needed */ +/* Use fpclassify if possible */ +/* isnan, isinf -- + these will use macros and then fpclassify if available before + defaulting to a dumb convert-to-double version... + + isfinite -- define a macro if not already available + signbit -- if macro available use it, otherwise define a function + and a dumb convert-to-double version for other types. +*/ + +#if defined(fpclassify) + +#if !defined(isnan) +#define isnan(x) (fpclassify(x) == FP_NAN) +#endif +#if !defined(isinf) +#define isinf(x) (fpclassify(x) == FP_INFINITE) +#endif + +#else /* check to see if already have a function like this */ + +#if !defined(HAVE_ISNAN) + +#if !defined(isnan) +#include "_isnan.c" +#endif +#endif /* HAVE_ISNAN */ + +#if !defined(HAVE_ISINF) +#if !defined(isinf) +#define isinf(x) (!isnan((x)) && isnan((x)-(x))) +#endif +#endif /* HAVE_ISINF */ + +#endif /* defined(fpclassify) */ + + +/* Define signbit if needed */ +#if !defined(signbit) +#include "_signbit.c" +#endif + + +/* Now defined the extended type macros */ + +#if !defined(isnan) + +#if !defined(HAVE_LONGDOUBLE_FUNCS) || !defined(HAVE_ISNAN) +#define isnanl(x) isnan((double)(x)) +#endif + +#if !defined(HAVE_FLOAT_FUNCS) || !defined(HAVE_ISNAN) +#define isnanf(x) isnan((double)(x)) +#endif + +#else /* !defined(isnan) */ + +#define isnanl(x) isnan((x)) +#define isnanf(x) isnan((x)) + +#endif /* !defined(isnan) */ + + +#if !defined(isinf) + +#if !defined(HAVE_LONGDOUBLE_FUNCS) || !defined(HAVE_ISINF) +#define isinfl(x) (!isnanl((x)) && isnanl((x)-(x))) +#endif + +#if !defined(HAVE_FLOAT_FUNCS) || !defined(HAVE_ISINF) +#define isinff(x) (!isnanf((x)) && isnanf((x)-(x))) +#endif + +#else /* !defined(isinf) */ + +#define isinfl(x) isinf((x)) +#define isinff(x) isinf((x)) + +#endif /* !defined(isinf) */ + + +#if !defined(signbit) +#define signbitl(x) ((longdouble) signbit((double)(x))) +#define signbitf(x) ((float) signbit((double) (x))) +#else +#define signbitl(x) signbit((x)) +#define signbitf(x) signbit((x)) +#endif + +#if !defined(isfinite) +#define isfinite(x) (!(isinf((x)) || isnan((x)))) +#endif +#define isfinitef(x) (!(isinff((x)) || isnanf((x)))) +#define isfinitel(x) (!(isinfl((x)) || isnanl((x)))) + + +/* First, the C functions that do the real work */ + +/* if C99 extensions not availble + +then define dummy functions that use the double versions for + +sin, cos, tan +sinh, cosh, tanh, +fabs, floor, ceil, fmod, sqrt, log10, log, exp, fabs +asin, acos, atan, +asinh, acosh, atanh + +hypot, atan2, pow + +*/ + +/**begin repeat + +#kind=(sin,cos,tan,sinh,cosh,tanh,fabs,floor,ceil,sqrt,log10,log,exp,asin,acos,atan)*2# +#typ=longdouble*16, float*16# +#c=l*16,f*16# +#TYPE=LONGDOUBLE*16, FLOAT*16# +*/ +#ifndef HAVE_@TYPE@_FUNCS +@typ@ @kind@@c@(@typ@ x) { + return (@typ@) @kind@((double)x); +} +#endif +/**end repeat**/ + +/**begin repeat + +#kind=(atan2,hypot,pow,fmod)*2# +#typ=longdouble*4, float*4# +#c=l*4,f*4# +#TYPE=LONGDOUBLE*4,FLOAT*4# +*/ +#ifndef HAVE_@TYPE@_FUNCS +@typ@ @kind@@c@(@typ@ x, @typ@ y) { + return (@typ@) @kind@((double)x, (double) y); +} +#endif +/**end repeat**/ + +/**begin repeat +#kind=modf*2# +#typ=longdouble, float# +#c=l,f# +#TYPE=LONGDOUBLE, FLOAT# +*/ +#ifndef HAVE_@TYPE@_FUNCS +@typ@ modf@c@(@typ@ x, @typ@ *iptr) { + double nx, niptr, y; + nx = (double) x; + y = modf(nx, &niptr); + *iptr = (@typ@) niptr; + return (@typ@) y; +} +#endif +/**end repeat**/ + + +#if !defined(HAVE_INVERSE_HYPERBOLIC_FLOAT) +#ifdef HAVE_FLOAT_FUNCS +static float acoshf(float x) +{ + return logf(x + sqrtf((x-1.0)*(x+1.0))); +} + +static float asinhf(float xx) +{ + float x; + int sign; + if (xx < 0.0) { + sign = -1; + x = -xx; + } + else { + sign = 1; + x = xx; + } + return sign*logf(x + sqrtf(x*x+1.0)); +} + +static float atanhf(float x) +{ + return 0.5*logf((1.0+x)/(1.0-x)); +} +#else +static float acoshf(float x) +{ + return (float)acosh((double)(x)); +} + +static float asinhf(float x) +{ + return (float)asinh((double)(x)); +} + +static float atanhf(float x) +{ + return (float)atanh((double)(x)); +} +#endif +#endif + + +#if !defined(HAVE_INVERSE_HYPERBOLIC_LONGDOUBLE) +#ifdef HAVE_LONGDOUBLE_FUNCS +static longdouble acoshl(longdouble x) +{ + return logl(x + sqrtl((x-1.0)*(x+1.0))); +} + +static longdouble asinhl(longdouble xx) +{ + longdouble x; + int sign; + if (xx < 0.0) { + sign = -1; + x = -xx; + } + else { + sign = 1; + x = xx; + } + return sign*logl(x + sqrtl(x*x+1.0)); +} + +static longdouble atanhl(longdouble x) +{ + return 0.5*logl((1.0+x)/(1.0-x)); +} +#else +static longdouble acoshl(longdouble x) +{ + return (longdouble)acosh((double)(x)); +} + +static longdouble asinhl(longdouble x) +{ + return (longdouble)asinh((double)(x)); +} + +static longdouble atanhl(longdouble x) +{ + return (longdouble)atanh((double)(x)); +} +#endif +#endif + + + + +/* Don't pass structures between functions (only pointers) because how + structures are passed is compiler dependent and could cause + segfaults if ufuncobject.c is compiled with a different compiler + than an extension that makes use of the UFUNC API +*/ + +/**begin repeat + +#typ=float, double, longdouble# +#c=f,,l# +*/ + +/* constants */ +static c@typ@ nc_1@c@ = {1., 0.}; +static c@typ@ nc_half@c@ = {0.5, 0.}; +static c@typ@ nc_i@c@ = {0., 1.}; +static c@typ@ nc_i2@c@ = {0., 0.5}; +/* +static c@typ@ nc_mi@c@ = {0., -1.}; +static c@typ@ nc_pi2@c@ = {M_PI/2., 0.}; +*/ + +static void +nc_sum@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + r->real = a->real + b->real; + r->imag = a->imag + b->imag; + return; +} + +static void +nc_diff@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + r->real = a->real - b->real; + r->imag = a->imag - b->imag; + return; +} + +static void +nc_neg@c@(c@typ@ *a, c@typ@ *r) +{ + r->real = -a->real; + r->imag = -a->imag; + return; +} + +static void +nc_prod@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + @typ@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; + r->real = ar*br - ai*bi; + r->imag = ar*bi + ai*br; + return; +} + +static void +nc_quot@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + + @typ@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; + @typ@ d = br*br + bi*bi; + r->real = (ar*br + ai*bi)/d; + r->imag = (ai*br - ar*bi)/d; + return; +} + +static void +nc_floor_quot@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + @typ@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; + @typ@ d = br*br + bi*bi; + r->real = floor@c@((ar*br + ai*bi)/d); + r->imag = 0; + return; +} + +static void +nc_sqrt@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ s,d; + if (x->real == 0. && x->imag == 0.) + *r = *x; + else { + s = sqrt@c@(0.5*(fabs@c@(x->real) + hypot@c@(x->real,x->imag))); + d = 0.5*x->imag/s; + if (x->real > 0.) { + r->real = s; + r->imag = d; + } + else if (x->imag >= 0.) { + r->real = d; + r->imag = s; + } + else { + r->real = -d; + r->imag = -s; + } + } + return; +} + +static void +nc_log@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ l = hypot@c@(x->real,x->imag); + r->imag = atan2@c@(x->imag, x->real); + r->real = log@c@(l); + return; +} + +static void +nc_exp@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ a = exp@c@(x->real); + r->real = a*cos@c@(x->imag); + r->imag = a*sin@c@(x->imag); + return; +} + +static void +nc_pow@c@(c@typ@ *a, c@typ@ *b, c@typ@ *r) +{ + @typ@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; + + if (br == 0. && bi == 0.) { + r->real = 1.; + r->imag = 0.; + } + else if (ar == 0. && ai == 0.) { + r->real = 0.; + r->imag = 0.; + } + else { + nc_log@c@(a, r); + nc_prod@c@(r, b, r); + nc_exp@c@(r, r); + } + return; +} + + +static void +nc_prodi@c@(c@typ@ *x, c@typ@ *r) +{ + r->real = -x->imag; + r->imag = x->real; + return; +} + + +static void +nc_acos@c@(c@typ@ *x, c@typ@ *r) +{ + nc_prod@c@(x,x,r); + nc_diff@c@(&nc_1@c@, r, r); + nc_sqrt@c@(r, r); + nc_prodi@c@(r, r); + nc_sum@c@(x, r, r); + nc_log@c@(r, r); + nc_prodi@c@(r, r); + nc_neg@c@(r, r); + return; + /* return nc_neg(nc_prodi(nc_log(nc_sum(x,nc_prod(nc_i, + nc_sqrt(nc_diff(nc_1,nc_prod(x,x)))))))); + */ +} + +static void +nc_acosh@c@(c@typ@ *x, c@typ@ *r) +{ + nc_prod@c@(x, x, r); + nc_diff@c@(&nc_1@c@, r, r); + nc_sqrt@c@(r, r); + nc_prodi@c@(r, r); + nc_sum@c@(x, r, r); + nc_log@c@(r, r); + return; + /* + return nc_log(nc_sum(x,nc_prod(nc_i, + nc_sqrt(nc_diff(nc_1,nc_prod(x,x)))))); + */ +} + +static void +nc_asin@c@(c@typ@ *x, c@typ@ *r) +{ + c@typ@ a, *pa=&a; + nc_prod@c@(x, x, r); + nc_diff@c@(&nc_1@c@, r, r); + nc_sqrt@c@(r, r); + nc_prodi@c@(x, pa); + nc_sum@c@(pa, r, r); + nc_log@c@(r, r); + nc_prodi@c@(r, r); + nc_neg@c@(r, r); + return; + /* + return nc_neg(nc_prodi(nc_log(nc_sum(nc_prod(nc_i,x), + nc_sqrt(nc_diff(nc_1,nc_prod(x,x))))))); + */ +} + + +static void +nc_asinh@c@(c@typ@ *x, c@typ@ *r) +{ + nc_prod@c@(x, x, r); + nc_sum@c@(&nc_1@c@, r, r); + nc_sqrt@c@(r, r); + nc_diff@c@(r, x, r); + nc_log@c@(r, r); + nc_neg@c@(r, r); + return; + /* + return nc_neg(nc_log(nc_diff(nc_sqrt(nc_sum(nc_1,nc_prod(x,x))),x))); + */ +} + +static void +nc_atan@c@(c@typ@ *x, c@typ@ *r) +{ + c@typ@ a, *pa=&a; + nc_diff@c@(&nc_i@c@, x, pa); + nc_sum@c@(&nc_i@c@, x, r); + nc_quot@c@(r, pa, r); + nc_log@c@(r,r); + nc_prod@c@(&nc_i2@c@, r, r); + return; + /* + return nc_prod(nc_i2,nc_log(nc_quot(nc_sum(nc_i,x),nc_diff(nc_i,x)))); + */ +} + +static void +nc_atanh@c@(c@typ@ *x, c@typ@ *r) +{ + c@typ@ a, *pa=&a; + nc_diff@c@(&nc_1@c@, x, r); + nc_sum@c@(&nc_1@c@, x, pa); + nc_quot@c@(pa, r, r); + nc_log@c@(r, r); + nc_prod@c@(&nc_half@c@, r, r); + return; + /* + return nc_prod(nc_half,nc_log(nc_quot(nc_sum(nc_1,x),nc_diff(nc_1,x)))); + */ +} + +static void +nc_cos@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ xr=x->real, xi=x->imag; + r->real = cos@c@(xr)*cosh@c@(xi); + r->imag = -sin@c@(xr)*sinh@c@(xi); + return; +} + +static void +nc_cosh@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ xr=x->real, xi=x->imag; + r->real = cos(xi)*cosh(xr); + r->imag = sin(xi)*sinh(xr); + return; +} + + +#define M_LOG10_E 0.434294481903251827651128918916605082294397 + +static void +nc_log10@c@(c@typ@ *x, c@typ@ *r) +{ + nc_log@c@(x, r); + r->real *= M_LOG10_E; + r->imag *= M_LOG10_E; + return; +} + +static void +nc_sin@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ xr=x->real, xi=x->imag; + r->real = sin@c@(xr)*cosh@c@(xi); + r->imag = cos@c@(xr)*sinh@c@(xi); + return; +} + +static void +nc_sinh@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ xr=x->real, xi=x->imag; + r->real = cos@c@(xi)*sinh@c@(xr); + r->imag = sin@c@(xi)*cosh@c@(xr); + return; +} + +static void +nc_tan@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ sr,cr,shi,chi; + @typ@ rs,is,rc,ic; + @typ@ d; + @typ@ xr=x->real, xi=x->imag; + sr = sin@c@(xr); + cr = cos@c@(xr); + shi = sinh(xi); + chi = cosh(xi); + rs = sr*chi; + is = cr*shi; + rc = cr*chi; + ic = -sr*shi; + d = rc*rc + ic*ic; + r->real = (rs*rc+is*ic)/d; + r->imag = (is*rc-rs*ic)/d; + return; +} + +static void +nc_tanh@c@(c@typ@ *x, c@typ@ *r) +{ + @typ@ si,ci,shr,chr; + @typ@ rs,is,rc,ic; + @typ@ d; + @typ@ xr=x->real, xi=x->imag; + si = sin@c@(xi); + ci = cos@c@(xi); + shr = sinh@c@(xr); + chr = cosh@c@(xr); + rs = ci*shr; + is = si*chr; + rc = ci*chr; + ic = si*shr; + d = rc*rc + ic*ic; + r->real = (rs*rc+is*ic)/d; + r->imag = (is*rc-rs*ic)/d; + return; +} + +/**end repeat**/ + + +/**begin repeat + +#TYPE=(BOOL, BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE)*2# +#OP=||, +*13, ^, -*13# +#kind=add*14, subtract*14# +#typ=(Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble)*2# +*/ + +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1) @OP@ *((@typ@ *)i2); + } +} + +/**end repeat**/ + +/**begin repeat + +#TYPE=(CFLOAT, CDOUBLE, CLONGDOUBLE)*2# +#OP=+*3,-*3# +#kind=add*3,subtract*3# +#typ=(float, double, longdouble)*2# + +*/ + +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + ((@typ@ *)op)[0]=((@typ@ *)i1)[0] @OP@ ((@typ@ *)i2)[0]; + ((@typ@ *)op)[1]=((@typ@ *)i1)[1] @OP@ ((@typ@ *)i2)[1]; + } +} + +/**end repeat**/ + + +/** Routines borrowed from numarray **/ + +/* The following routine is used in the event of a detected integer * +** divide by zero so that a floating divide by zero is generated. * +** This is done since Numeric uses the floating point exception * +** sticky bits to detect errors. The last bit is an attempt to * +** prevent optimization of the divide by zero away, the output value * +** should always be 0 * +*/ + +/* These should really be altered to just set the corresponding bit + in the floating point status flag. Need to figure out how to do that + on all the platforms... +*/ + +static int numeric_zero = 0.0; + +#if !defined(generate_divbyzero_error) +static void generate_divbyzero_error(void) { + double dummy; + dummy = 1./numeric_zero; + return; +} +#endif + +#if !defined(generate_overflow_error) +static double numeric_two = 2.0; +static void generate_overflow_error(void) { + double dummy; + dummy = pow(numeric_two,1000); + return; +} +#endif + + +static int ulonglong_overflow(ulonglong a, ulonglong b) +{ + ulonglong ah, al, bh, bl, w, x, y, z; + +#if SIZEOF_LONGLONG == 64 + ah = (a >> 32); + al = (a & 0xFFFFFFFFL); + bh = (b >> 32); + bl = (b & 0xFFFFFFFFL); +#elif SIZEOF_LONGLONG == 128 + ah = (a >> 64); + al = (a & 0xFFFFFFFFFFFFFFFFL); + bh = (b >> 64); + bl = (b & 0xFFFFFFFFFFFFFFFFL); +#else + ah = al = bh = bl = 0; +#endif + + /* 128-bit product: z*2**64 + (x+y)*2**32 + w */ + w = al*bl; + x = bh*al; + y = ah*bl; + z = ah*bh; + + /* *c = ((x + y)<<32) + w; */ +#if SIZEOF_LONGLONG == 64 + return z || (x>>32) || (y>>32) || + (((x & 0xFFFFFFFFL) + (y & 0xFFFFFFFFL) + (w >> 32)) >> 32); +#elif SIZEOF_LONGLONG == 128 + return z || (x>>64) || (y>>64) || + (((x & 0xFFFFFFFFFFFFFFFFL) + (y & 0xFFFFFFFFFFFFFFFFL) + (w >> 64)) >> 64); +#else + return 0; +#endif + +} + +static int slonglong_overflow(longlong a0, longlong b0) +{ + ulonglong a, b; + ulonglong ah, al, bh, bl, w, x, y, z; + + /* Convert to non-negative quantities */ + if (a0 < 0) { a = -a0; } else { a = a0; } + if (b0 < 0) { b = -b0; } else { b = b0; } + + +#if SIZEOF_LONGLONG == 64 + ah = (a >> 32); + al = (a & 0xFFFFFFFFL); + bh = (b >> 32); + bl = (b & 0xFFFFFFFFL); +#elif SIZEOF_LONGLONG == 128 + ah = (a >> 64); + al = (a & 0xFFFFFFFFFFFFFFFFL); + bh = (b >> 64); + bl = (b & 0xFFFFFFFFFFFFFFFFL); +#else + ah = al = bh = bl = 0; +#endif + + w = al*bl; + x = bh*al; + y = ah*bl; + z = ah*bh; + + /* + ulonglong c = ((x + y)<<32) + w; + if ((a0 < 0) ^ (b0 < 0)) + *c = -c; + else + *c = c + */ + +#if SIZEOF_LONGLONG == 64 + return z || (x>>31) || (y>>31) || + (((x & 0xFFFFFFFFL) + (y & 0xFFFFFFFFL) + (w >> 32)) >> 31); +#elif SIZEOF_LONGLONG == 128 + return z || (x>>63) || (y>>63) || + (((x & 0xFFFFFFFFFFFFFFFFL) + (y & 0xFFFFFFFFFFFFFFFFL) + (w >> 64)) >> 63); +#else + return 0; +#endif +} + +/** end direct numarray code **/ + +static void +BOOL_multiply(char **args, intp *dimensions, intp *steps, void *func) { + register intp i; + intp is1=steps[0], is2=steps[1], os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op) = *((Bool *)i1) && *((Bool *)i2); + } +} + +/**begin repeat + +#TYP= UBYTE,USHORT,UINT, ULONG# +#typ= ubyte, ushort, uint, ulong# +#bigtyp= int, int, double, double# +*/ + +static void +@TYP@_multiply(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], is2=steps[1], os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @bigtyp@ temp; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + temp = (@bigtyp@)(*((@typ@ *)i1)) * (@bigtyp@)(*((@typ@ *)i2)); + if (temp > MAX_@TYP@) + generate_overflow_error(); + *((@typ@ *)op) = temp; + } +} + +/**end repeat**/ + +static void +ULONGLONG_multiply(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], is2=steps[1], os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + ulonglong temp; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + temp = *((ulonglong *)i1) * *((ulonglong *)i2); + if (ulonglong_overflow(*((ulonglong *)i1), *((ulonglong *)i2))) + generate_overflow_error(); + *((ulonglong *)op) = temp; + } +} + +/**begin repeat + +#TYP= BYTE,SHORT,INT, LONG# +#typ= byte, short, int, long# +#bigtyp= int, int, double, double# +*/ + +static void +@TYP@_multiply(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], is2=steps[1], os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @bigtyp@ temp; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + temp = (@bigtyp@)*((@typ@ *)i1) * (@bigtyp@)*((@typ@ *)i2); + if (temp > MAX_@TYP@) + generate_overflow_error(); + else if (temp < MIN_@TYP@) + generate_overflow_error(); + *((@typ@ *)op) = temp; + } +} + +/**end repeat**/ + +static void +LONGLONG_multiply(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], is2=steps[1], os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + longlong temp; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + temp = *((longlong *)i1) * *((longlong *)i2); + if (slonglong_overflow(*((longlong *)i1), *((longlong *)i2))) + generate_overflow_error(); + *((longlong *)op) = temp; + } +} + + +/**begin repeat + +#TYP=FLOAT,DOUBLE,LONGDOUBLE# +#typ=float,double,longdouble# +*/ +static void +@TYP@_multiply(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + /* fprintf(stderr, "Multiplying %d elements of type @typ@\n", n); + fprintf(stderr, "args= %p, %p, %p\n", i1, i2, op); + fprintf(stderr, "steps=%d, %d, %d\n", is1, is2, os); */ + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1) * *((@typ@ *)i2); + } +} +/**end repeat**/ + + +/**begin repeat + +#TYP=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG# +#typ=char, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong# +#otyp=float*4, double*6# +*/ +static void +@TYP@_divide(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + if (*((@typ@ *)i2)==0) { + generate_divbyzero_error(); + *((@typ@ *)op)=0; + } + else { + *((@typ@ *)op)= *((@typ@ *)i1) / *((@typ@ *)i2); + } + } +} +static void +@TYP@_true_divide(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + if (*((@typ@ *)i2)==0) { + generate_divbyzero_error(); + *((@otyp@ *)op)=0; + } + else { + *((@otyp@ *)op)= \ + *((@typ@ *)i1) / (double)*((@typ@ *)i2); + } + } +} +#define @TYP@_floor_divide @TYP@_divide +/**end repeat**/ + +/**begin repeat + +#TYP=(FLOAT,DOUBLE,LONGDOUBLE)*2# +#typ=(float,double,longdouble)*2# +#kind=divide*3, true_divide*3# +*/ +static void +@TYP@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1) / *((@typ@ *)i2); + } +} +/**end repeat**/ + +/**begin repeat + +#TYP=FLOAT,DOUBLE,LONGDOUBLE# +#typ=float,double,longdouble# +#c=f,,l# +*/ +static void +@TYP@_floor_divide(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=floor@c@(*((@typ@ *)i1) / *((@typ@ *)i2)); + } +} +/**end repeat**/ + + +/**begin repeat + +#TYP=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG# +#typ=char, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong# +#btyp=float*4, double*6# +*/ +static void +@TYP@_power(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0],is2=steps[1]; + register intp os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @btyp@ x, y, v; + @typ@ z; + + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + x = *((@typ@ *)i1); + y = *((@typ@ *)i2); + z = (@typ@) y; + if ((x < 0.0) && (y != z)) v = 1.0/numeric_zero; + else v = pow(x,y); + *((@typ@ *)op) = (@typ@) v; + } +} +/**end repeat**/ + +/**begin repeat + +#TYP=UBYTE, BYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE# +#typ=ubyte, char, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +@TYP@_conjugate(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, is1=steps[0], os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1); + } +} +/**end repeat**/ + +/**begin repeat + +#TYP=CFLOAT, CDOUBLE, CLONGDOUBLE# +#typ=float, double, longdouble# +*/ +static void +@TYP@_conjugate(char **args, intp *dimensions, intp *steps, void *func) { + register intp i, is1=steps[0], os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + + for(i=0; i<n; i++, i1+=is1, op+=os) { + ((@typ@ *)op)[0]=((@typ@ *)i1)[0]; + ((@typ@ *)op)[1]=-(((@typ@ *)i1)[1]); + } +} +/**end repeat**/ + + +/**begin repeat + +#TYPE=BOOL,UBYTE,USHORT,UINT,ULONG,ULONGLONG# +#typ=Bool, ubyte, ushort, uint, ulong, ulonglong# +*/ +static void +@TYPE@_absolute(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n; + intp is1=steps[0], os=steps[1]; + char *i1=args[0], *op=args[1]; + + n=dimensions[0]; + + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op) = *((@typ@*)i1); + } +} +/**end repeat**/ + +/**begin repeat + +#TYPE=BYTE,SHORT,INT,LONG,LONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#typ=byte, short, int, long, longlong, float, double, longdouble# +*/ +static void +@TYPE@_absolute(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n; + intp is1=steps[0], os=steps[1]; + char *i1=args[0], *op=args[1]; + + n=dimensions[0]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op) = *((@typ@ *)i1) < 0 ? -*((@typ@ *)i1) : *((@typ@ *)i1); + } +} +/**end repeat**/ + +/**begin repeat + #TYPE=CFLOAT,CDOUBLE,CLONGDOUBLE# + #typ= float, double, longdouble# + #c= f,,l# +*/ +static void +@TYPE@_absolute(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i, n; + register intp is1=steps[0], os=steps[1]; + char *i1=args[0], *op=args[1]; + n=dimensions[0]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op) = (@typ@)sqrt@c@(((@typ@ *)i1)[0]*((@typ@ *)i1)[0] + ((@typ@ *)i1)[1]*((@typ@ *)i1)[1]); + } +} +/**end repeat**/ + +/**begin repeat + +#kind=greater, greater_equal, less, less_equal, equal, not_equal, logical_and, logical_or, bitwise_and, bitwise_or, bitwise_xor# +#OP=>, >=, <, <=, ==, !=, &&, ||, &, |, ^# +**/ +static void +BOOL_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + Bool in1, in2; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + in1 = (*((Bool *)i1) != 0); + in2 = (*((Bool *)i2) != 0); + *((Bool *)op)= in1 @OP@ in2; + } +} +/**end repeat**/ + +/**begin repeat + +#TYPE=(BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE)*4# +#OP= >*13, >=*13, <*13, <=*13# +#typ=(byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble)*4# +#kind= greater*13, greater_equal*13, less*13, less_equal*13# +*/ + +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op)=*((@typ@ *)i1) @OP@ *((@typ@ *)i2); + } +} +/**end repeat**/ + + +/**begin repeat +#TYPE=(CFLOAT,CDOUBLE,CLONGDOUBLE)*4# +#OP= >*3, >=*3, <*3, <=*3# +#typ=(cfloat, cdouble, clongdouble)*4# +#kind= greater*3, greater_equal*3, less*3, less_equal*3# +*/ + +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + if (((@typ@ *)i1)->real == ((@typ@ *)i2)->real) + *((Bool *)op)=((@typ@ *)i1)->imag @OP@ \ + ((@typ@ *)i2)->imag; + else + *((Bool *)op)=((@typ@ *)i1)->real @OP@ \ + ((@typ@ *)i2)->real; + } +} +/**end repeat**/ + + +/**begin repeat +#TYPE=(BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE)*4# +#typ=(byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble)*4# +#OP= ==*13, !=*13, &&*13, ||*13# +#kind=equal*13, not_equal*13, logical_and*13, logical_or*13# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op) = *((@typ@ *)i1) @OP@ *((@typ@ *)i2); + } +} +/**end repeat**/ + + +/**begin repeat + +#TYPE=(CFLOAT, CDOUBLE, CLONGDOUBLE)*4# +#typ=(float, double, longdouble)*4# +#OP= ==*3, !=*3, &&*3, ||*3# +#OP2= &&*3, ||*3, &&*3, ||*3# +#kind=equal*3, not_equal*3, logical_and*3, logical_or*3# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op) = (*((@typ@ *)i1) @OP@ *((@typ@ *)i2)) @OP2@ (*((@typ@ *)i1+1) @OP@ *((@typ@ *)i2+1)); + } +} +/**end repeat**/ + + +/** OBJECT comparison for OBJECT arrays **/ + +/**begin repeat + +#kind=greater, greater_equal, less, less_equal, equal, not_equal# +#op=GT, GE, LT, LE, EQ, NE# +*/ +static void +OBJECT_@kind@(char **args, intp *dimensions, intp *steps, void *func) { + register intp i, is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op)=PyObject_RichCompareBool(*((PyObject **)i1), + *((PyObject **)i2), + Py_@op@); + } +} +/**end repeat**/ + +/**begin repeat + +#TYPE=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#typ=byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +@TYPE@_negative(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op) = - *((@typ@ *)i1); + } +} +/**end repeat**/ + +#define BOOL_negative BOOL_logical_not + + +/**begin repeat +#TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#typ=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ +static void +@TYPE@_logical_not(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((Bool *)op) = ! *((@typ@ *)i1); + } +} +/**end repeat**/ + +/**begin repeat +#TYPE=CFLOAT,CDOUBLE,CLONGDOUBLE# +#typ=cfloat, cdouble, clongdouble# +*/ +static void +@TYPE@_logical_not(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((Bool *)op) = ! (((@typ@ *)i1)->real || \ + ((@typ@ *)i1)->imag); + } +} +/**end repeat**/ + + + + +/**begin repeat +#TYPE=BYTE,SHORT,INT,LONG,LONGLONG# +#typ=byte, short, int, long, longlong# +#ftyp=float*2,double*2,longdouble*1# +#c=f*2,,,l*1# +*/ +static void +@TYPE@_remainder(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + register @typ@ ix,iy, tmp; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + ix = *((@typ@ *)i1); + iy = *((@typ@ *)i2); + if (iy == 0 || ix == 0) { + if (iy == 0) generate_divbyzero_error(); + *((@typ@ *)op) = 0; + } + else if ((ix > 0) == (iy > 0)) { + *((@typ@ *)op) = ix % iy; + } + else { /* handle mixed case the way Python does */ + tmp = ix % iy; + if (tmp) tmp += iy; + *((@typ@ *)op)= tmp; + } + } +} +/**end repeat**/ + +/**begin repeat +#TYPE=UBYTE,USHORT,UINT,ULONG,ULONGLONG# +#typ=ubyte, ushort, uint, ulong, ulonglong# +*/ +static void +@TYPE@_remainder(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + register @typ@ ix,iy; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + ix = *((@typ@ *)i1); + iy = *((@typ@ *)i2); + if (iy == 0) { + generate_divbyzero_error(); + *((@typ@ *)op) = 0; + } + *((@typ@ *)op) = ix % iy; + } +} +/**end repeat**/ + +/**begin repeat +#TYPE=FLOAT,DOUBLE,LONGDOUBLE# +#typ=float,double,longdouble# +#c=f,,l# +*/ +static void +@TYPE@_remainder(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @typ@ x, y, res; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + x = *((@typ@ *)i1); + y = *((@typ@ *)i2); + res = x - floor@c@(x/y)*y; + *((@typ@ *)op)= res; + } +} +/**end repeat**/ + + +/**begin repeat + +#TYPE=(BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG)*6# +#typ=(byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong)*6# +#OP= %*10, &*10, |*10, ^*10, <<*10, >>*10# +#kind=fmod*10, bitwise_and*10, bitwise_or*10, bitwise_xor*10, left_shift*10, right_shift*10# + +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1) @OP@ *((@typ@ *)i2); + } +} +/**end repeat**/ + + +/**begin repeat + #TYPE=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG# + #typ=byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong# +*/ +static void +@TYPE@_invert(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((@typ@ *)op) = ~ *((@typ@*)i1); + } +} +/**end repeat**/ + +static void +BOOL_invert(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0], os=steps[1], n=dimensions[0]; + char *i1=args[0], *op=args[1]; + for(i=0; i<n; i++, i1+=is1, op+=os) { + *((Bool *)op) = (*((Bool *)i1) ? FALSE : TRUE); + } +} + + +/**begin repeat +#TYPE=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE# +#typ=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# + +*/ +static void +@TYPE@_logical_xor(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((Bool *)op)=(*((@typ@ *)i1) || *((@typ@ *)i2)) && !(*((@typ@ *)i1) && *((@typ@ *)i2)); + } +} +/**end repeat**/ + + +/**begin repeat +#TYPE=CFLOAT,CDOUBLE,CLONGDOUBLE# +#typ=cfloat, cdouble, clongdouble# +*/ +static void +@TYPE@_logical_xor(char **args, intp *dimensions, intp *steps, void *func) +{ + Bool p1, p2; + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + p1 = ((@typ@ *)i1)->real || ((@typ@ *)i1)->imag; + p2 = ((@typ@ *)i2)->real || ((@typ@ *)i2)->imag; + *((Bool *)op)= (p1 || p2) && !(p1 && p2); + } +} +/**end repeat**/ + + + +/**begin repeat + +#TYPE=(BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE)*2# +#OP= >*14, <*14# +#typ=(Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble)*2# +#kind= maximum*14, minimum*14# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + *((@typ@ *)op)=*((@typ@ *)i1) @OP@ *((@typ@ *)i2) ? *((@typ@ *)i1) : *((@typ@ *)i2); + } +} +/**end repeat**/ + +/**begin repeat + +#TYPE=(CFLOAT,CDOUBLE,CLONGDOUBLE)*2# +#OP= >*3, <*3# +#typ=(cfloat, cdouble, clongdouble)*2# +#kind= maximum*3, minimum*3# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2], n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @typ@ *i1c, *i2c; + for(i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + i1c = (@typ@ *)i1; + i2c = (@typ@ *)i2; + if ((i1c->real @OP@ i2c->real) || \ + ((i1c->real==i2c->real) && (i1c->imag @OP@ i2c->imag))) + memcpy(op, i1, sizeof(@typ@)); + else + memcpy(op, i2, sizeof(@typ@)); + } +} +/**end repeat**/ + + + +/*** isinf, isinf, isfinite, signbit ***/ +/**begin repeat +#kind=isnan*3, isinf*3, isfinite*3, signbit*3# +#TYPE=(FLOAT, DOUBLE, LONGDOUBLE)*4# +#typ=(float, double, longdouble)*4# +#c=(f,,l)*4# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is=steps[0], os=steps[1], n=dimensions[0]; + char *ip=args[0], *op=args[1]; + for(i=0; i<n; i++, ip+=is, op+=os) { + *((Bool *)op) = (Bool) (@kind@@c@(*((@typ@ *)ip)) != 0); + } +} +/**end repeat**/ + + +/**begin repeat +#kind=isnan*3, isinf*3, isfinite*3# +#TYPE=(CFLOAT, CDOUBLE, CLONGDOUBLE)*3# +#typ=(float, double, longdouble)*3# +#c=(f,,l)*3# +#OP=||*6,&&*3# +*/ +static void +@TYPE@_@kind@(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is=steps[0], os=steps[1], n=dimensions[0]; + char *ip=args[0], *op=args[1]; + for(i=0; i<n; i++, ip+=is, op+=os) { + *((Bool *)op) = @kind@@c@(((@typ@ *)ip)[0]) @OP@ \ + @kind@@c@(((@typ@ *)ip)[1]); + } +} +/**end repeat**/ + + + + +/****** modf ****/ + +/**begin repeat +#TYPE=FLOAT, DOUBLE, LONGDOUBLE# +#typ=float, double, longdouble# +#c=f,,l# +*/ +static void +@TYPE@_modf(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],os1=steps[1],os2=steps[2],n=dimensions[0]; + char *i1=args[0], *op1=args[1], *op2=args[2]; + @typ@ x1, y1, y2; + for (i=0; i<n; i++, i1+=is1, op1+=os1, op2+=os2) { + x1 = *((@typ@ *)i1); + y1 = modf@c@(x1, &y2); + *((@typ@ *)op1) = y1; + *((@typ@ *)op2) = y2; + } +} +/**end repeat**/ + +#define HAVE_DOUBLE_FUNCS +/**begin repeat +#TYPE=FLOAT, DOUBLE, LONGDOUBLE# +#typ=float, double, longdouble# +#c=f,,l# +*/ +#ifdef HAVE_@TYPE@_FUNCS +static void +@TYPE@_frexp(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],os1=steps[1],os2=steps[2],n=dimensions[0]; + char *i1=args[0], *op1=args[1], *op2=args[2]; + @typ@ x1, y1; + int y2; + for (i=0; i<n; i++, i1+=is1, op1+=os1, op2+=os2) { + x1 = *((@typ@ *)i1); + y1 = frexp@c@(x1, &y2); + *((@typ@ *)op1) = y1; + *((int *) op2) = y2; + } +} + +static void +@TYPE@_ldexp(char **args, intp *dimensions, intp *steps, void *func) +{ + register intp i; + intp is1=steps[0],is2=steps[1],os=steps[2],n=dimensions[0]; + char *i1=args[0], *i2=args[1], *op=args[2]; + @typ@ x1, y1; + int x2; + for (i=0; i<n; i++, i1+=is1, i2+=is2, op+=os) { + x1 = *((@typ@ *)i1); + x2 = *((int *)i2); + y1 = ldexp@c@(x1, x2); + *((@typ@ *)op) = y1; + } +} +#endif +/**end repeat**/ +#undef HAVE_DOUBLE_FUNCS + + +static PyUFuncGenericFunction frexp_functions[] = { +#ifdef HAVE_FLOAT_FUNCS + FLOAT_frexp, +#endif + DOUBLE_frexp +#ifdef HAVE_LONGDOUBLE_FUNCS + ,LONGDOUBLE_frexp +#endif +}; + +static void * blank3_data[] = { (void *)NULL, (void *)NULL, (void *)NULL}; +static char frexp_signatures[] = { +#ifdef HAVE_FLOAT_FUNCS + PyArray_FLOAT, PyArray_FLOAT, PyArray_INT, +#endif + PyArray_DOUBLE, PyArray_DOUBLE, PyArray_INT +#ifdef HAVE_LONGDOUBLE_FUNCS + ,PyArray_LONGDOUBLE, PyArray_LONGDOUBLE, PyArray_INT +#endif +}; + + +static PyUFuncGenericFunction ldexp_functions[] = { +#ifdef HAVE_FLOAT_FUNCS + FLOAT_ldexp, +#endif + DOUBLE_ldexp +#ifdef HAVE_LONGDOUBLE_FUNCS + ,LONGDOUBLE_ldexp +#endif +}; + +static char ldexp_signatures[] = { +#ifdef HAVE_FLOAT_FUNCS + PyArray_FLOAT, PyArray_INT, PyArray_FLOAT, +#endif + PyArray_DOUBLE, PyArray_INT, PyArray_DOUBLE +#ifdef HAVE_LONGDOUBLE_FUNCS + ,PyArray_LONGDOUBLE, PyArray_INT, PyArray_LONGDOUBLE +#endif +}; + + + +#include "__umath_generated.c" + + +#include "ufuncobject.c" + +#include "__ufunc_api.c" + +static double +pinf_init(void) +{ + double mul = 1e10; + double tmp = 0.0; + double pinf; + + pinf = mul; + for (;;) { + pinf *= mul; + if (pinf == tmp) break; + tmp = pinf; + } + return pinf; +} + +static double +pzero_init(void) +{ + double div = 1e10; + double tmp = 0.0; + double pinf; + + pinf = div; + for (;;) { + pinf /= div; + if (pinf == tmp) break; + tmp = pinf; + } + return pinf; +} + +/* Less automated additions to the ufuncs */ + +static void +InitOtherOperators(PyObject *dictionary) { + PyObject *f; + int num=1; + +#ifdef HAVE_LONGDOUBLE_FUNCS + num += 1; +#endif +#ifdef HAVE_FLOAT_FUNCS + num += 1; +#endif + f = PyUFunc_FromFuncAndData(frexp_functions, blank3_data, + frexp_signatures, num, + 1, 2, PyUFunc_None, "frexp", + "Split the number, x, into a normalized"\ + " fraction (y1) and exponent (y2)",0); + PyDict_SetItemString(dictionary, "frexp", f); + Py_DECREF(f); + + f = PyUFunc_FromFuncAndData(ldexp_functions, blank3_data, ldexp_signatures, num, + 2, 1, PyUFunc_None, "ldexp", + "Compute y = x1 * 2**x2.",0); + PyDict_SetItemString(dictionary, "ldexp", f); + Py_DECREF(f); + return; +} + +static struct PyMethodDef methods[] = { + {"frompyfunc", (PyCFunction) ufunc_frompyfunc, + METH_VARARGS | METH_KEYWORDS, doc_frompyfunc}, + {"update_use_defaults", (PyCFunction) ufunc_update_use_defaults, + METH_VARARGS , NULL}, + {NULL, NULL, 0} /* sentinel */ +}; + +DL_EXPORT(void) initumath(void) { + PyObject *m, *d, *s, *s2, *c_api; + double pinf, pzero, mynan; + + /* Create the module and add the functions */ + m = Py_InitModule("umath", methods); + + /* Import the array */ + if (import_array() < 0) return; + + /* Initialize the types */ + if (PyType_Ready(&PyUFunc_Type) < 0) + return; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + c_api = PyCObject_FromVoidPtr((void *)PyUFunc_API, NULL); + if (PyErr_Occurred()) goto err; + PyDict_SetItemString(d, "_UFUNC_API", c_api); + Py_DECREF(c_api); + if (PyErr_Occurred()) goto err; + + s = PyString_FromString("0.4.0"); + PyDict_SetItemString(d, "__version__", s); + Py_DECREF(s); + + /* Load the ufunc operators into the array module's namespace */ + InitOperators(d); + + InitOtherOperators(d); + + PyDict_SetItemString(d, "pi", s = PyFloat_FromDouble(M_PI)); + Py_DECREF(s); + PyDict_SetItemString(d, "e", s = PyFloat_FromDouble(exp(1.0))); + Py_DECREF(s); + +#define ADDCONST(str) PyModule_AddIntConstant(m, #str, UFUNC_##str) +#define ADDSCONST(str) PyModule_AddStringConstant(m, "UFUNC_" #str, UFUNC_##str) + + ADDCONST(ERR_IGNORE); + ADDCONST(ERR_WARN); + ADDCONST(ERR_CALL); + ADDCONST(ERR_RAISE); + ADDCONST(ERR_DEFAULT); + + ADDCONST(SHIFT_DIVIDEBYZERO); + ADDCONST(SHIFT_OVERFLOW); + ADDCONST(SHIFT_UNDERFLOW); + ADDCONST(SHIFT_INVALID); + + ADDCONST(FPE_DIVIDEBYZERO); + ADDCONST(FPE_OVERFLOW); + ADDCONST(FPE_UNDERFLOW); + ADDCONST(FPE_INVALID); + + ADDSCONST(PYVALS_NAME); + +#undef ADDCONST +#undef ADDSCONST + PyModule_AddIntConstant(m, "UFUNC_BUFSIZE_DEFAULT", (long)PyArray_BUFSIZE); + + pinf = pinf_init(); + pzero = pzero_init(); + mynan = pinf / pinf; + + PyModule_AddObject(m, "PINF", PyFloat_FromDouble(pinf)); + PyModule_AddObject(m, "NINF", PyFloat_FromDouble(-pinf)); + PyModule_AddObject(m, "PZERO", PyFloat_FromDouble(pzero)); + PyModule_AddObject(m, "NZERO", PyFloat_FromDouble(-pzero)); + PyModule_AddObject(m, "NAN", PyFloat_FromDouble(mynan)); + + s = PyDict_GetItemString(d, "conjugate"); + s2 = PyDict_GetItemString(d, "remainder"); + /* Setup the array object's numerical structures with appropriate + ufuncs in d*/ + PyArray_SetNumericOps(d); + + PyDict_SetItemString(d, "conj", s); + PyDict_SetItemString(d, "mod", s2); + + err: + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module umath"); +} |
