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 | |
| parent | da9c6da4a304d240492b653f526b9607b032921c (diff) | |
| download | numpy-8057b2d910a5a6726a666a2c18ac495dbb9e6000.tar.gz | |
rename sub-packages
Diffstat (limited to 'numpy/core')
62 files changed, 41041 insertions, 0 deletions
diff --git a/numpy/core/__init__.py b/numpy/core/__init__.py new file mode 100644 index 000000000..0c0c158df --- /dev/null +++ b/numpy/core/__init__.py @@ -0,0 +1,37 @@ + +from info import __doc__ +from scipy.core_version import version as __version__ + +import multiarray +import umath +import numerictypes as nt +multiarray.set_typeDict(nt.typeDict) +import _sort +from numeric import * +from oldnumeric import * +from matrix import * +from type_check import * +from index_tricks import * +from function_base import * +from shape_base import * +from twodim_base import * +from ufunclike import * + +import scimath as math +from polynomial import * +from machar import * +from getlimits import * +import ma +import chararray as char +import records as rec +from records import * +from memmap import * +import convertcode +del nt + +from utils import * + +__all__ = filter(lambda s:not s.startswith('_'),dir()) + +from scipy.testing import ScipyTest +test = ScipyTest().test diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py new file mode 100644 index 000000000..260381e85 --- /dev/null +++ b/numpy/core/_internal.py @@ -0,0 +1,327 @@ + +#A place for code to be called from C-code +# that implements more complicated stuff. + +import re +from multiarray import _flagdict, dtypedescr, ndarray + +_defflags = _flagdict.keys() + +_setable = ['WRITEABLE','UPDATEIFCOPY', 'ALIGNED', + 'W','U','A'] +_setable2 = ['write','uic','align']*2 +_firstltr = {'W':'WRITEABLE', + 'A':'ALIGNED', + 'C':'CONTIGUOUS', + 'F':'FORTRAN', + 'O':'OWNDATA', + 'U':'UPDATEIFCOPY'} + +_anum = _flagdict['ALIGNED'] +_wnum = _flagdict['WRITEABLE'] +_cnum = _flagdict['CONTIGUOUS'] +_fnum = _flagdict['FORTRAN'] +_unum = _flagdict['UPDATEIFCOPY'] +_onum = _flagdict['OWNDATA'] + +class flagsobj(dict): + def __init__(self, arr, flags, scalar): + self._arr = arr + self._flagnum = flags + for k in _defflags: + num = _flagdict[k] + dict.__setitem__(self, k, flags & num == num) + self.scalar = scalar + + def __getitem__(self, key): + if not isinstance(key, str): + raise KeyError, "Unknown flag %s" % key + if len(key) == 1: + try: + return dict.__getitem__(self, _firstltr[key]) + except: + if (key == 'B'): + num = _anum + _wnum + return self._flagnum & num == num + else: + try: + return dict.__getitem__(self, key) + except: # special cases + if (key == 'FNC'): + return (self._flagnum & _fnum == _fnum) and not \ + (self._flagnum & _cnum == _cnum) + if (key == 'FORC'): + return (self._flagnum & _fnum == _fnum) or \ + (self._flagnum & _cnum == _cnum) + if (key == 'BEHAVED'): + num = _anum + _wnum + return self._flagnum & num == num + if (key in ['CARRAY','CA']): + num = _anum + _wnum + _cnum + return self._flagnum & num == num + if (key in ['FARRAY','FA']): + num = _anum + _wnum + _fnum + return (self._flagnum & num == num) and not \ + (self._flagnum & _cnum == _cnum) + raise KeyError, "Unknown flag: %s" % key + + def __setitem__(self, item, val): + if self.scalar: + raise ValueError, "Cannot set flags on array scalars." + val = not not val # convert to boolean + if item not in _setable: + raise KeyError, "Cannot set flag", item + dict.__setitem__(self, item, val) # Does this matter? + + kwds = {} + for k, name in enumerate(_setable): + if item == name: + kwds[_setable2[k]] = val + + # now actually update array flags + self._arr.setflags(**kwds) + + + def get_fnc(self): + fl = self._flagnum + return (fl & _fnum == _fnum) and \ + not (fl & _cnum == _cnum) + + def get_forc(self): + fl = self._flagnum + return (fl & _cnum == _cnum) or \ + (fl & _fnum == _fnum) + + def get_behaved(self): + fl = self._flagnum + return (fl & _anum == _anum) and \ + (fl & _wnum == _wnum) + + def get_carray(self): + fl = self._flagnum + return (fl & _anum == _anum) and \ + (fl & _wnum == _wnum) and \ + (fl & _cnum == _cnum) + + def get_farray(self): + fl = self._flagnum + return (fl & _anum == _anum) and \ + (fl & _wnum == _wnum) and \ + (fl & _fnum == _fnum) and \ + not (fl & _cnum == _cnum) + + def get_contiguous(self): + return (self._flagnum & _cnum == _cnum) + + def get_fortran(self): + return (self._flagnum & _fnum == _fnum) + + def get_updateifcopy(self): + return (self._flagnum & _unum == _unum) + + def get_owndata(self): + return (self._flagnum & _onum == _onum) + + def get_aligned(self): + return (self._flagnum & _anum == _anum) + + def get_writeable(self): + return (self._flagnum & _wnum == _wnum) + + def set_writeable(self, val): + val = not not val + self._arr.setflags(write=val) + + def set_aligned(self, val): + val = not not val + self._arr.setflags(align=val) + + def set_updateifcopy(self, val): + val = not not val + self._arr.setflags(uic=val) + + contiguous = property(get_contiguous, None, "") + fortran = property(get_fortran, None, "") + updateifcopy = property(get_updateifcopy, set_updateifcopy, "") + owndata = property(get_owndata, None, "") + aligned = property(get_aligned, set_aligned, "") + writeable = property(get_writeable, set_writeable, "") + + fnc = property(get_fnc, None, "") + forc = property(get_forc, None, "") + behaved = property(get_behaved, None, "") + carray = property(get_carray, None, "") + farray = property(get_farray, None, "") + + + +# make sure the tuple entries are PyArray_Descr +# or convert them +# +# make sure offsets are all interpretable +# as positive integers and +# convert them to positive integers if so +# +# +# return totalsize from last offset and size + +# Called in PyArray_DescrConverter function when +# a dictionary without "names" and "formats" +# fields is used as a data-type descriptor. +def _usefields(adict, align): + try: + names = adict[-1] + except KeyError: + names = None + if names is None: + allfields = [] + fnames = adict.keys() + for fname in fnames: + obj = adict[fname] + n = len(obj) + if not isinstance(obj, tuple) or n not in [2,3]: + raise ValueError, "entry not a 2- or 3- tuple" + if (n > 2) and (obj[2] == fname): + continue + num = int(obj[1]) + if (num < 0): + raise ValueError, "invalid offset." + format = dtypedescr(obj[0]) + if (format.itemsize == 0): + raise ValueError, "all itemsizes must be fixed." + if (n > 2): + title = obj[2] + else: + title = None + allfields.append((fname, format, num, title)) + # sort by offsets + allfields.sort(lambda x,y: cmp(x[2],y[2])) + names = [x[0] for x in allfields] + formats = [x[1] for x in allfields] + offsets = [x[2] for x in allfields] + titles = [x[3] for x in allfields] + else: + formats = [] + offsets = [] + titles = [] + for name in names: + res = adict[name] + formats.append(res[0]) + offsets.append(res[1]) + if (len(res) > 2): + titles.append(res[2]) + else: + titles.append(None) + + return dtypedescr({"names" : names, + "formats" : formats, + "offsets" : offsets, + "titles" : titles}, align) + + +# construct an array_protocol descriptor list +# from the fields attribute of a descriptor +# This calls itself recursively but should eventually hit +# a descriptor that has no fields and then return +# a simple typestring + +def _array_descr(descriptor): + fields = descriptor.fields + if fields is None: + return descriptor.dtypestr + + #get ordered list of fields with names + ordered_fields = fields.items() + # remove duplicates + new = {} + for item in ordered_fields: + # We don't want to include redundant or non-string + # entries + if not isinstance(item[0],str) or (len(item[1]) > 2 \ + and item[0] == item[1][2]): + continue + new[item[1]] = item[0] + ordered_fields = [x[0] + (x[1],) for x in new.items()] + #sort the list on the offset + ordered_fields.sort(lambda x,y : cmp(x[1],y[1])) + + result = [] + offset = 0 + for field in ordered_fields: + if field[1] > offset: + result.append(('','|V%d' % (field[1]-offset))) + if len(field) > 3: + name = (field[2],field[3]) + else: + name = field[2] + if field[0].subdescr: + tup = (name, _array_descr(field[0].subdescr[0]), + field[0].subdescr[1]) + else: + tup = (name, _array_descr(field[0])) + offset += field[0].itemsize + result.append(tup) + + return result + +def _reconstruct(subtype, shape, dtype): + return ndarray.__new__(subtype, shape, dtype) + + +# format_re and _split were taken from numarray by J. Todd Miller +format_re = re.compile(r'(?P<repeat> *[(]?[ ,0-9]*[)]? *)(?P<dtype>[><|A-Za-z0-9.]*)') + +def _split(input): + """Split the input formats string into field formats without splitting + the tuple used to specify multi-dimensional arrays.""" + + newlist = [] + hold = '' + + for element in input.split(','): + if hold != '': + item = hold + ',' + element + else: + item = element + left = item.count('(') + right = item.count(')') + + # if the parenthesis is not balanced, hold the string + if left > right : + hold = item + + # when balanced, append to the output list and reset the hold + elif left == right: + newlist.append(item.strip()) + hold = '' + + # too many close parenthesis is unacceptable + else: + raise SyntaxError, item + + # if there is string left over in hold + if hold != '': + raise SyntaxError, hold + + return newlist + +# str is a string (perhaps comma separated) +def _commastring(astr): + res = _split(astr) + if (len(res)) == 1: + raise ValueError, "no commas present" + result = [] + for k,item in enumerate(res): + # convert item + try: + (repeats, dtype) = format_re.match(item).groups() + except (TypeError, AttributeError): + raise ValueError('format %s is not recognized' % item) + + if (repeats == ''): + newitem = dtype + else: + newitem = (dtype, eval(repeats)) + result.append(newitem) + + return result diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py new file mode 100644 index 000000000..6a5e4b23b --- /dev/null +++ b/numpy/core/arrayprint.py @@ -0,0 +1,359 @@ +"""Array printing function + +$Id: arrayprint.py,v 1.9 2005/09/13 13:58:44 teoliphant Exp $ +""" +__all__ = ["set_summary", "summary_off", "set_precision", "set_line_width", + "array2string"] + +# +# Written by Konrad Hinsen <hinsenk@ere.umontreal.ca> +# last revision: 1996-3-13 +# modified by Jim Hugunin 1997-3-3 for repr's and str's (and other details) +# and by Perry Greenfield 2000-4-1 for numarray +# and by Travis Oliphant 2005-8-22 for scipy.base + +import sys +import numeric as _gen +import numerictypes as _nt +import umath as _uf +_nc = _gen + +# The following functions are emergency substitutes for numeric functions +# which sometimes get broken during development. + +def product(x, y): return x*y + +def _maximum_reduce(arr): + maximum = arr[0] + for i in xrange(1, arr.nelements()): + if arr[i] > maximum: maximum = arr[i] + return maximum + +def _minimum_reduce(arr): + minimum = arr[0] + for i in xrange(1, arr.nelements()): + if arr[i] < minimum: minimum = arr[i] + return minimum + +def _numeric_compress(arr): + nonzero = 0 + for i in xrange(arr.nelements()): + if arr[i] != 0: nonzero += 1 + retarr = _nc.zeros((nonzero,)) + nonzero = 0 + for i in xrange(arr.nelements()): + if arr[i] != 0: + retarr[nonzero] = abs(arr[i]) + nonzero += 1 + return retarr + +_failsafe = 0 +if _failsafe: + max_reduce = _maximum_reduce + min_reduce = _minimum_reduce +else: + max_reduce = _uf.maximum.reduce + min_reduce = _uf.minimum.reduce + +_summaryEdgeItems = 3 # repr N leading and trailing items of each dimension +_summaryThreshhold = 1000 # total items > triggers array summarization + +_float_output_precision = 8 +_float_output_suppress_small = False +_line_width = 75 + + +def set_printoptions(precision=None, threshold=None, edgeitems=None, + linewidth=None, suppress=None): + """Set options associated with printing. + + precision the default number of digits of precision for floating + point output + (default 8) + threshold total number of array elements which trigger summarization + rather than full repr. + (default 1000) + edgeitems number of array items in summary at beginning and end of + each dimension. + (default 3) + linewidth the number of characters per line for the purpose of inserting + line breaks. + (default 75) + supress Boolean value indicating whether or not suppress printing + of small floating point values using scientific notation + (default False) + """ + + global _summaryThreshhold, _summaryEdgeItems, _float_output_precision, \ + _line_width, _float_output_suppress_small + if (linewidth is not None): + _line_width = linewidth + if (threshold is not None): + _summaryThreshhold = threshold + if (edgeitems is not None): + _summaryEdgeItems = edgeitems + if (precision is not None): + _float_output_precision = precision + if (suppress is not None): + _float_output_supress_small = not not suppress + return + +def get_printoptions(): + return _float_output_precision, _summaryThreshhold, _summaryEdgeItems, \ + _line_width, _float_output_suppress_small + + +def _leading_trailing(a): + if a.ndim == 1: + if len(a) > 2*_summaryEdgeItems: + b = _gen.concatenate((a[:_summaryEdgeItems], + a[-_summaryEdgeItems:])) + else: + b = a + else: + if len(a) > 2*_summaryEdgeItems: + l = [_leading_trailing(a[i]) for i in range( + min(len(a), _summaryEdgeItems))] + l.extend([_leading_trailing(a[-i]) for i in range( + min(len(a), _summaryEdgeItems),0,-1)]) + else: + l = [_leading_trailing(a[i]) for i in range(0, len(a))] + b = _gen.concatenate(tuple(l)) + return b + +def _array2string(a, max_line_width, precision, suppress_small, separator=' ', + prefix=""): + + if max_line_width is None: + max_line_width = _line_width + + if precision is None: + precision = _float_output_precision + + if suppress_small is None: + suppress_small = _float_output_suppress_small + + if a.size > _summaryThreshhold: + summary_insert = "..., " + data = _leading_trailing(a) + else: + summary_insert = "" + data = a.ravel() + + items_per_line = a.shape[-1] + + try: + format_function = a._format + except AttributeError: + dtype = a.dtype + if issubclass(dtype, _nt.bool): + format = "%s" + format_function = lambda x, f = format: format % x + if issubclass(dtype, _nt.integer): + max_str_len = max(len(str(max_reduce(data))), + len(str(min_reduce(data)))) + format = '%' + str(max_str_len) + 'd' + format_function = lambda x, f = format: _formatInteger(x, f) + elif issubclass(dtype, _nt.floating): + format = _floatFormat(data, precision, suppress_small) + format_function = lambda x, f = format: _formatFloat(x, f) + elif issubclass(dtype, _nt.complexfloating): + real_format = _floatFormat( + data.real, precision, suppress_small, sign=0) + imag_format = _floatFormat( + data.imag, precision, suppress_small, sign=1) + format_function = lambda x, f1 = real_format, f2 = imag_format: \ + _formatComplex(x, f1, f2) + else: + format = '%s' + format_function = lambda x, f = format: format % str(x) + + next_line_prefix = " " # skip over "[" + next_line_prefix += " "*len(prefix) # skip over array( + + + lst = _formatArray(a, format_function, len(a.shape), max_line_width, + next_line_prefix, separator, + _summaryEdgeItems, summary_insert)[:-1] + + return lst + +def array2string(a, max_line_width = None, precision = None, + suppress_small = None, separator=' ', prefix="", + style=repr): + + if a.shape == (): + x = a.item() + try: + lst = a._format(x) + except AttributeError: + lst = style(x) + elif reduce(product, a.shape) == 0: + # treat as a null array if any of shape elements == 0 + lst = "[]" + else: + lst = _array2string(a, max_line_width, precision, suppress_small, + separator, prefix) + return lst + +def _extendLine(s, line, word, max_line_len, next_line_prefix): + if len(line.rstrip()) + len(word.rstrip()) >= max_line_len: + s += line.rstrip() + "\n" + line = next_line_prefix + line += word + return s, line + +def _formatArray(a, format_function, rank, max_line_len, + next_line_prefix, separator, edge_items, summary_insert): + """formatArray is designed for two modes of operation: + + 1. Full output + + 2. Summarized output + + """ + if rank == 0: + return str(a.item()) + + if summary_insert and 2*edge_items < len(a): + leading_items, trailing_items, summary_insert1 = \ + edge_items, edge_items, summary_insert + else: + leading_items, trailing_items, summary_insert1 = 0, len(a), "" + + if rank == 1: + + s = "" + line = next_line_prefix + for i in xrange(leading_items): + word = format_function(a[i]) + separator + s, line = _extendLine(s, line, word, max_line_len, next_line_prefix) + + if summary_insert1: + s, line = _extendLine(s, line, summary_insert1, max_line_len, next_line_prefix) + + for i in xrange(trailing_items, 1, -1): + word = format_function(a[-i]) + separator + s, line = _extendLine(s, line, word, max_line_len, next_line_prefix) + + word = format_function(a[-1]) + s, line = _extendLine(s, line, word, max_line_len, next_line_prefix) + s += line + "]\n" + s = '[' + s[len(next_line_prefix):] + else: + s = '[' + sep = separator.rstrip() + for i in xrange(leading_items): + if i > 0: + s += next_line_prefix + s += _formatArray(a[i], format_function, rank-1, max_line_len, + " " + next_line_prefix, separator, edge_items, + summary_insert) + s = s.rstrip()+ sep.rstrip() + '\n'*max(rank-1,1) + + if summary_insert1: + s += next_line_prefix + summary_insert1 + "\n" + + for i in xrange(trailing_items, 1, -1): + if leading_items or i != trailing_items: + s += next_line_prefix + s += _formatArray(a[-i], format_function, rank-1, max_line_len, + " " + next_line_prefix, separator, edge_items, + summary_insert) + s = s.rstrip() + sep.rstrip() + '\n'*max(rank-1,1) + if leading_items or trailing_items > 1: + s += next_line_prefix + s += _formatArray(a[-1], format_function, rank-1, max_line_len, + " " + next_line_prefix, separator, edge_items, + summary_insert).rstrip()+']\n' + return s + +def _floatFormat(data, precision, suppress_small, sign = 0): + exp_format = 0 + non_zero = _uf.absolute(data.compress(_uf.not_equal(data, 0))) + ##non_zero = _numeric_compress(data) ## + if len(non_zero) == 0: + max_val = 0. + min_val = 0. + else: + max_val = max_reduce(non_zero) + min_val = min_reduce(non_zero) + if max_val >= 1.e8: + exp_format = 1 + if not suppress_small and (min_val < 0.0001 + or max_val/min_val > 1000.): + exp_format = 1 + if exp_format: + large_exponent = 0 < min_val < 1e-99 or max_val >= 1e100 + max_str_len = 8 + precision + large_exponent + if sign: format = '%+' + else: format = '%' + format = format + str(max_str_len) + '.' + str(precision) + 'e' + if large_exponent: format = format + '3' + else: + format = '%.' + str(precision) + 'f' + precision = min(precision, max(tuple(map(lambda x, p=precision, + f=format: _digits(x,p,f), + data)))) + max_str_len = len(str(int(max_val))) + precision + 2 + if sign: format = '%#+' + else: format = '%#' + format = format + str(max_str_len) + '.' + str(precision) + 'f' + return format + +def _digits(x, precision, format): + s = format % x + zeros = len(s) + while s[zeros-1] == '0': zeros = zeros-1 + return precision-len(s)+zeros + + +_MAXINT = sys.maxint +_MININT = -sys.maxint-1 +def _formatInteger(x, format): + if (x < _MAXINT) and (x > _MININT): + return format % x + else: + return "%s" % x + +def _formatFloat(x, format, strip_zeros = 1): + if format[-1] == '3': + # 3-digit exponent + format = format[:-1] + s = format % x + third = s[-3] + if third == '+' or third == '-': + s = s[1:-2] + '0' + s[-2:] + elif format[-1] == 'e': + # 2-digit exponent + s = format % x + if s[-3] == '0': + s = ' ' + s[:-3] + s[-2:] + elif format[-1] == 'f': + s = format % x + if strip_zeros: + zeros = len(s) + while s[zeros-1] == '0': zeros = zeros-1 + s = s[:zeros] + (len(s)-zeros)*' ' + else: + s = format % x + return s + +def _formatComplex(x, real_format, imag_format): + r = _formatFloat(x.real, real_format) + i = _formatFloat(x.imag, imag_format, 0) + if imag_format[-1] == 'f': + zeros = len(i) + while zeros > 2 and i[zeros-1] == '0': zeros = zeros-1 + i = i[:zeros] + 'j' + (len(i)-zeros)*' ' + else: + i = i + 'j' + return r + i + +def _formatGeneral(x): + return str(x) + ' ' + +if __name__ == '__main__': + a = _nc.arange(10) + print array2string(a) + print array2string(_nc.array([[],[]])) diff --git a/numpy/core/blasdot/_dotblas.c b/numpy/core/blasdot/_dotblas.c new file mode 100644 index 000000000..648ea397f --- /dev/null +++ b/numpy/core/blasdot/_dotblas.c @@ -0,0 +1,761 @@ +static char module_doc[] = +"This module provides a BLAS optimized\nmatrix multiply, inner product and dot for scipy arrays"; + +#include "Python.h" +#include "scipy/arrayobject.h" +#ifndef CBLAS_HEADER +#define CBLAS_HEADER "cblas.h" +#endif +#include CBLAS_HEADER + +#include <stdio.h> + +static void +FLOAT_dot(void *a, intp stridea, void *b, intp strideb, void *res, + intp n, void *tmp) +{ + register int na = stridea / sizeof(float); + register int nb = strideb / sizeof(float); + + *((float *)res) = cblas_sdot((int)n, (float *)a, na, (float *)b, nb); +} + +static void +DOUBLE_dot(void *a, intp stridea, void *b, intp strideb, void *res, + intp n, void *tmp) +{ + register int na = stridea / sizeof(double); + register int nb = strideb / sizeof(double); + + *((double *)res) = cblas_ddot((int)n, (double *)a, na, (double *)b, nb); +} + +static void +CFLOAT_dot(void *a, intp stridea, void *b, intp strideb, void *res, + intp n, void *tmp) +{ + + register int na = stridea / sizeof(cfloat); + register int nb = strideb / sizeof(cfloat); + + cblas_cdotu_sub((int)n, (float *)a, na, (float *)b, nb, (float *)res); +} + +static void +CDOUBLE_dot(void *a, intp stridea, void *b, intp strideb, void *res, + intp n, void *tmp) +{ + register int na = stridea / sizeof(cdouble); + register int nb = strideb / sizeof(cdouble); + + cblas_zdotu_sub((int)n, (double *)a, na, (double *)b, nb, (double *)res); +} + + +static PyArray_DotFunc *oldFunctions[PyArray_NTYPES]; +static Bool altered=FALSE; + +static char doc_alterdot[] = "alterdot() changes all dot functions to use blas."; + +static PyObject * +dotblas_alterdot(PyObject *dummy, PyObject *args) +{ + PyArray_Descr *descr; + + if (!PyArg_ParseTuple(args, "")) return NULL; + + /* Replace the dot functions to the ones using blas */ + + if (!altered) { + descr = PyArray_DescrFromType(PyArray_FLOAT); + oldFunctions[PyArray_FLOAT] = descr->f->dotfunc; + descr->f->dotfunc = (PyArray_DotFunc *)FLOAT_dot; + + descr = PyArray_DescrFromType(PyArray_DOUBLE); + oldFunctions[PyArray_DOUBLE] = descr->f->dotfunc; + descr->f->dotfunc = (PyArray_DotFunc *)DOUBLE_dot; + + descr = PyArray_DescrFromType(PyArray_CFLOAT); + oldFunctions[PyArray_CFLOAT] = descr->f->dotfunc; + descr->f->dotfunc = (PyArray_DotFunc *)CFLOAT_dot; + + descr = PyArray_DescrFromType(PyArray_CDOUBLE); + oldFunctions[PyArray_CDOUBLE] = descr->f->dotfunc; + descr->f->dotfunc = (PyArray_DotFunc *)CDOUBLE_dot; + + altered = TRUE; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static char doc_restoredot[] = "restoredot() restores dots to defaults."; + +static PyObject * +dotblas_restoredot(PyObject *dummy, PyObject *args) +{ + PyArray_Descr *descr; + + if (!PyArg_ParseTuple(args, "")) return NULL; + + if (altered) { + descr = PyArray_DescrFromType(PyArray_FLOAT); + descr->f->dotfunc = oldFunctions[PyArray_FLOAT]; + oldFunctions[PyArray_FLOAT] = NULL; + Py_XDECREF(descr); + + descr = PyArray_DescrFromType(PyArray_DOUBLE); + descr->f->dotfunc = oldFunctions[PyArray_DOUBLE]; + oldFunctions[PyArray_DOUBLE] = NULL; + Py_XDECREF(descr); + + descr = PyArray_DescrFromType(PyArray_CFLOAT); + descr->f->dotfunc = oldFunctions[PyArray_CFLOAT]; + oldFunctions[PyArray_CFLOAT] = NULL; + Py_XDECREF(descr); + + descr = PyArray_DescrFromType(PyArray_CDOUBLE); + descr->f->dotfunc = oldFunctions[PyArray_CDOUBLE]; + oldFunctions[PyArray_CDOUBLE] = NULL; + Py_XDECREF(descr); + + altered = FALSE; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +static char doc_matrixproduct[] = "matrixproduct(a,b)\nReturns the dot product of a and b for arrays of floating point types.\nLike the generic scipy equivalent the product sum is over\nthe last dimension of a and the second-to-last dimension of b.\nNB: The first argument is not conjugated."; + +static PyObject * +dotblas_matrixproduct(PyObject *dummy, PyObject *args) +{ + PyObject *op1, *op2; + PyArrayObject *ap1, *ap2, *ret; + int j, l, lda, ldb, ldc; + int typenum, nd; + intp dimensions[MAX_DIMS]; + static const float oneF[2] = {1.0, 0.0}; + static const float zeroF[2] = {0.0, 0.0}; + static const double oneD[2] = {1.0, 0.0}; + static const double zeroD[2] = {0.0, 0.0}; + double prior1, prior2; + PyTypeObject *subtype; + PyArray_Descr *dtype; + + + if (!PyArg_ParseTuple(args, "OO", &op1, &op2)) return NULL; + + /* + * "Matrix product" using the BLAS. + * Only works for float double and complex types. + */ + + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + /* This function doesn't handle other types */ + if ((typenum != PyArray_DOUBLE && typenum != PyArray_CDOUBLE && + typenum != PyArray_FLOAT && typenum != PyArray_CFLOAT)) { + return PyArray_Return((PyArrayObject *)PyArray_MatrixProduct(op1, op2)); + } + + ret = NULL; + dtype = PyArray_DescrFromType(typenum); + ap1 = (PyArrayObject *)PyArray_FromAny(op1, dtype, 0, 0, CARRAY_FLAGS); + if (ap1 == NULL) return NULL; + ap2 = (PyArrayObject *)PyArray_FromAny(op2, dtype, 0, 0, CARRAY_FLAGS); + if (ap2 == NULL) goto fail; + + if ((ap1->nd > 2) || (ap2->nd > 2)) { + /* This function doesn't handle dimensions greater than 2 -- other + than to ensure the dot function is altered + */ + if (!altered) { + /* need to alter dot product */ + PyObject *tmp1, *tmp2; + tmp1 = PyTuple_New(0); + tmp2 = dotblas_alterdot(NULL, tmp1); + Py_DECREF(tmp1); + Py_DECREF(tmp2); + } + ret = (PyArrayObject *)PyArray_MatrixProduct((PyObject *)ap1, + (PyObject *)ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + } + + if (ap1->nd == 0 || ap2->nd == 0) { + /* One of ap1 or ap2 is a scalar */ + if (ap1->nd == 0) { /* Make ap2 the scalar */ + PyArrayObject *t = ap1; + ap1 = ap2; + ap2 = t; + } + for (l = 1, j = 0; j < ap1->nd; j++) { + dimensions[j] = ap1->dimensions[j]; + l *= dimensions[j]; + } + nd = ap1->nd; + } + else { /* (ap1->nd <= 2 && ap2->nd <= 2) */ + /* Both ap1 and ap2 are vectors or matrices */ + l = ap1->dimensions[ap1->nd-1]; + + if (ap2->dimensions[0] != l) { + PyErr_SetString(PyExc_ValueError, "matrices are not aligned"); + goto fail; + } + nd = ap1->nd+ap2->nd-2; + + if (nd == 1) + dimensions[0] = (ap1->nd == 2) ? ap1->dimensions[0] : ap2->dimensions[1]; + else if (nd == 2) { + dimensions[0] = ap1->dimensions[0]; + dimensions[1] = ap2->dimensions[1]; + } + } + + /* 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; + memset(ret->data, 0, PyArray_NBYTES(ret)); + + if (ap2->nd == 0) { + /* Multiplication by a scalar -- Level 1 BLAS */ + if (typenum == PyArray_DOUBLE) { + cblas_daxpy(l, *((double *)ap2->data), (double *)ap1->data, 1, + (double *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zaxpy(l, (double *)ap2->data, (double *)ap1->data, 1, + (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_saxpy(l, *((float *)ap2->data), (float *)ap1->data, 1, + (float *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_caxpy(l, (float *)ap2->data, (float *)ap1->data, 1, + (float *)ret->data, 1); + } + } + else if (ap1->nd == 1 && ap2->nd == 1) { + /* Dot product between two vectors -- Level 1 BLAS */ + if (typenum == PyArray_DOUBLE) { + double result = cblas_ddot(l, (double *)ap1->data, 1, + (double *)ap2->data, 1); + *((double *)ret->data) = result; + } + else if (typenum == PyArray_FLOAT) { + float result = cblas_sdot(l, (float *)ap1->data, 1, + (float *)ap2->data, 1); + *((float *)ret->data) = result; + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zdotu_sub(l, (double *)ap1->data, 1, + (double *)ap2->data, 1, (double *)ret->data); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cdotu_sub(l, (float *)ap1->data, 1, + (float *)ap2->data, 1, (float *)ret->data); + fprintf(stderr, "Here...\n"); + } + } + else if (ap1->nd == 2 && ap2->nd == 1) { + /* Matrix vector multiplication -- Level 2 BLAS */ + /* lda must be MAX(M,1) */ + lda = (ap1->dimensions[1] > 1 ? ap1->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + 1.0, (double *)ap1->data, lda, + (double *)ap2->data, 1, 0.0, (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + 1.0, (float *)ap1->data, lda, + (float *)ap2->data, 1, 0.0, (float *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + oneD, (double *)ap1->data, lda, + (double *)ap2->data, 1, zeroD, (double *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + oneF, (float *)ap1->data, lda, + (float *)ap2->data, 1, zeroF, (float *)ret->data, 1); + } + } + else if (ap1->nd == 1 && ap2->nd == 2) { + /* Vector matrix multiplication -- Level 2 BLAS */ + lda = (ap2->dimensions[1] > 1 ? ap2->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemv(CblasRowMajor, + CblasTrans, ap2->dimensions[0], ap2->dimensions[1], + 1.0, (double *)ap2->data, lda, + (double *)ap1->data, 1, 0.0, (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemv(CblasRowMajor, + CblasTrans, ap2->dimensions[0], ap2->dimensions[1], + 1.0, (float *)ap2->data, lda, + (float *)ap1->data, 1, 0.0, (float *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemv(CblasRowMajor, + CblasTrans, ap2->dimensions[0], ap2->dimensions[1], + oneD, (double *)ap2->data, lda, + (double *)ap1->data, 1, zeroD, (double *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemv(CblasRowMajor, + CblasTrans, ap2->dimensions[0], ap2->dimensions[1], + oneF, (float *)ap2->data, lda, + (float *)ap1->data, 1, zeroF, (float *)ret->data, 1); + } + } + else { /* (ap1->nd == 2 && ap2->nd == 2) */ + /* Matrix matrix multiplication -- Level 3 BLAS */ + lda = (ap1->dimensions[1] > 1 ? ap1->dimensions[1] : 1); + ldb = (ap2->dimensions[1] > 1 ? ap2->dimensions[1] : 1); + ldc = (ret->dimensions[1] > 1 ? ret->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, + ap1->dimensions[0], ap2->dimensions[1], ap2->dimensions[0], + 1.0, (double *)ap1->data, lda, + (double *)ap2->data, ldb, + 0.0, (double *)ret->data, ldc); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, + ap1->dimensions[0], ap2->dimensions[1], ap2->dimensions[0], + 1.0, (float *)ap1->data, lda, + (float *)ap2->data, ldb, + 0.0, (float *)ret->data, ldc); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, + ap1->dimensions[0], ap2->dimensions[1], ap2->dimensions[0], + oneD, (double *)ap1->data, lda, + (double *)ap2->data, ldb, + zeroD, (double *)ret->data, ldc); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, + ap1->dimensions[0], ap2->dimensions[1], ap2->dimensions[0], + oneF, (float *)ap1->data, lda, + (float *)ap2->data, ldb, + zeroF, (float *)ret->data, ldc); + } + } + + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + + +static char doc_innerproduct[] = "innerproduct(a,b)\nReturns the inner product of a and b for arrays of floating point types.\nLike the generic Numeric equivalent the product sum is over\nthe last dimension of a and b.\nNB: The first argument is not conjugated."; + +static PyObject * +dotblas_innerproduct(PyObject *dummy, PyObject *args) +{ + PyObject *op1, *op2; + PyArrayObject *ap1, *ap2, *ret; + int j, l, lda, ldb, ldc; + int typenum, nd; + intp dimensions[MAX_DIMS]; + static const float oneF[2] = {1.0, 0.0}; + static const float zeroF[2] = {0.0, 0.0}; + static const double oneD[2] = {1.0, 0.0}; + static const double zeroD[2] = {0.0, 0.0}; + PyTypeObject *subtype; + double prior1, prior2; + + if (!PyArg_ParseTuple(args, "OO", &op1, &op2)) return NULL; + + /* + * Inner product using the BLAS. The product sum is taken along the last + * dimensions of the two arrays. + * Only speeds things up for float double and complex types. + */ + + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + /* This function doesn't handle other types */ + if ((typenum != PyArray_DOUBLE && typenum != PyArray_CDOUBLE && + typenum != PyArray_FLOAT && typenum != PyArray_CFLOAT)) { + return PyArray_Return((PyArrayObject *)PyArray_InnerProduct(op1, op2)); + } + + ret = NULL; + ap1 = (PyArrayObject *)PyArray_ContiguousFromObject(op1, typenum, 0, 0); + if (ap1 == NULL) return NULL; + ap2 = (PyArrayObject *)PyArray_ContiguousFromObject(op2, typenum, 0, 0); + if (ap2 == NULL) goto fail; + + + if ((ap1->nd > 2) || (ap2->nd > 2)) { + /* This function doesn't handle dimensions greater than 2 -- other + than to ensure the dot function is altered + */ + if (!altered) { + /* need to alter dot product */ + PyObject *tmp1, *tmp2; + tmp1 = PyTuple_New(0); + tmp2 = dotblas_alterdot(NULL, tmp1); + Py_DECREF(tmp1); + Py_DECREF(tmp2); + } + ret = (PyArrayObject *)PyArray_InnerProduct((PyObject *)ap1, + (PyObject *)ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + } + + if (ap1->nd == 0 || ap2->nd == 0) { + /* One of ap1 or ap2 is a scalar */ + if (ap1->nd == 0) { /* Make ap2 the scalar */ + PyArrayObject *t = ap1; + ap1 = ap2; + ap2 = t; + } + for (l = 1, j = 0; j < ap1->nd; j++) { + dimensions[j] = ap1->dimensions[j]; + l *= dimensions[j]; + } + nd = ap1->nd; + } + else { /* (ap1->nd <= 2 && ap2->nd <= 2) */ + /* Both ap1 and ap2 are vectors or matrices */ + l = ap1->dimensions[ap1->nd-1]; + + if (ap2->dimensions[ap2->nd-1] != l) { + PyErr_SetString(PyExc_ValueError, "matrices are not aligned"); + goto fail; + } + nd = ap1->nd+ap2->nd-2; + + if (nd == 1) + dimensions[0] = (ap1->nd == 2) ? ap1->dimensions[0] : ap2->dimensions[0]; + else if (nd == 2) { + dimensions[0] = ap1->dimensions[0]; + dimensions[1] = ap2->dimensions[0]; + } + } + + /* 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; + memset(ret->data, 0, PyArray_NBYTES(ret)); + + if (ap2->nd == 0) { + /* Multiplication by a scalar -- Level 1 BLAS */ + if (typenum == PyArray_DOUBLE) { + cblas_daxpy(l, *((double *)ap2->data), (double *)ap1->data, 1, + (double *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zaxpy(l, (double *)ap2->data, (double *)ap1->data, 1, + (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_saxpy(l, *((float *)ap2->data), (float *)ap1->data, 1, + (float *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_caxpy(l, (float *)ap2->data, (float *)ap1->data, 1, + (float *)ret->data, 1); + } + } + else if (ap1->nd == 1 && ap2->nd == 1) { + /* Dot product between two vectors -- Level 1 BLAS */ + if (typenum == PyArray_DOUBLE) { + double result = cblas_ddot(l, (double *)ap1->data, 1, + (double *)ap2->data, 1); + *((double *)ret->data) = result; + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zdotu_sub(l, (double *)ap1->data, 1, + (double *)ap2->data, 1, (double *)ret->data); + } + else if (typenum == PyArray_FLOAT) { + float result = cblas_sdot(l, (float *)ap1->data, 1, + (float *)ap2->data, 1); + *((float *)ret->data) = result; + } + else if (typenum == PyArray_CFLOAT) { + cblas_cdotu_sub(l, (float *)ap1->data, 1, + (float *)ap2->data, 1, (float *)ret->data); + } + } + else if (ap1->nd == 2 && ap2->nd == 1) { + /* Matrix-vector multiplication -- Level 2 BLAS */ + lda = (ap1->dimensions[1] > 1 ? ap1->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + 1.0, (double *)ap1->data, lda, + (double *)ap2->data, 1, 0.0, (double *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + oneD, (double *)ap1->data, lda, + (double *)ap2->data, 1, zeroD, (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + 1.0, (float *)ap1->data, lda, + (float *)ap2->data, 1, 0.0, (float *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemv(CblasRowMajor, + CblasNoTrans, ap1->dimensions[0], ap1->dimensions[1], + oneF, (float *)ap1->data, lda, + (float *)ap2->data, 1, zeroF, (float *)ret->data, 1); + } + } + else if (ap1->nd == 1 && ap2->nd == 2) { + /* Vector matrix multiplication -- Level 2 BLAS */ + lda = (ap2->dimensions[1] > 1 ? ap2->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemv(CblasRowMajor, + CblasNoTrans, ap2->dimensions[0], ap2->dimensions[1], + 1.0, (double *)ap2->data, lda, + (double *)ap1->data, 1, 0.0, (double *)ret->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemv(CblasRowMajor, + CblasNoTrans, ap2->dimensions[0], ap2->dimensions[1], + oneD, (double *)ap2->data, lda, + (double *)ap1->data, 1, zeroD, (double *)ret->data, 1); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemv(CblasRowMajor, + CblasNoTrans, ap2->dimensions[0], ap2->dimensions[1], + 1.0, (float *)ap2->data, lda, + (float *)ap1->data, 1, 0.0, (float *)ret->data, 1); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemv(CblasRowMajor, + CblasNoTrans, ap2->dimensions[0], ap2->dimensions[1], + oneF, (float *)ap2->data, lda, + (float *)ap1->data, 1, zeroF, (float *)ret->data, 1); + } + } + else { /* (ap1->nd == 2 && ap2->nd == 2) */ + /* Matrix matrix multiplication -- Level 3 BLAS */ + lda = (ap1->dimensions[1] > 1 ? ap1->dimensions[1] : 1); + ldb = (ap2->dimensions[1] > 1 ? ap2->dimensions[1] : 1); + ldc = (ret->dimensions[1] > 1 ? ret->dimensions[1] : 1); + if (typenum == PyArray_DOUBLE) { + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasTrans, + ap1->dimensions[0], ap2->dimensions[0], ap1->dimensions[1], + 1.0, (double *)ap1->data, lda, + (double *)ap2->data, ldb, + 0.0, (double *)ret->data, ldc); + } + else if (typenum == PyArray_FLOAT) { + cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, + ap1->dimensions[0], ap2->dimensions[0], ap1->dimensions[1], + 1.0, (float *)ap1->data, lda, + (float *)ap2->data, ldb, + 0.0, (float *)ret->data, ldc); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasTrans, + ap1->dimensions[0], ap2->dimensions[0], ap1->dimensions[1], + oneD, (double *)ap1->data, lda, + (double *)ap2->data, ldb, + zeroD, (double *)ret->data, ldc); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasTrans, + ap1->dimensions[0], ap2->dimensions[0], ap1->dimensions[1], + oneF, (float *)ap1->data, lda, + (float *)ap2->data, ldb, + zeroF, (float *)ret->data, ldc); + } + } + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + + +static char doc_vdot[] = "vdot(a,b)\nReturns the dot product of a and b for scalars and vectors\nof floating point and complex types. The first argument, a, is conjugated."; + + +static PyObject *dotblas_vdot(PyObject *dummy, PyObject *args) { + PyObject *op1, *op2; + PyArrayObject *ap1=NULL, *ap2=NULL, *ret=NULL; + int l; + int typenum; + intp dimensions[MAX_DIMS]; + PyArray_Descr *type; + + if (!PyArg_ParseTuple(args, "OO", &op1, &op2)) return NULL; + + /* + * Conjugating dot product using the BLAS for vectors. + * Multiplies op1 and op2, each of which must be vector. + */ + + typenum = PyArray_ObjectType(op1, 0); + typenum = PyArray_ObjectType(op2, typenum); + + type = PyArray_DescrFromType(typenum); + + ap1 = (PyArrayObject *)PyArray_FromAny(op1, type, 0, 0, 0); + if (ap1==NULL) goto fail; + op1 = PyArray_Flatten(ap1, 0); + if (op1==NULL) goto fail; + Py_DECREF(ap1); + ap1 = (PyArrayObject *)op1; + + ap2 = (PyArrayObject *)PyArray_FromAny(op2, type, 0, 0, 0); + if (ap2==NULL) goto fail; + op2 = PyArray_Flatten(ap2, 0); + if (op2 == NULL) goto fail; + Py_DECREF(ap2); + ap2 = (PyArrayObject *)op2; + + if (typenum != PyArray_FLOAT && typenum != PyArray_DOUBLE && + typenum != PyArray_CFLOAT && typenum != PyArray_CDOUBLE) { + if (!altered) { + /* need to alter dot product */ + PyObject *tmp1, *tmp2; + tmp1 = PyTuple_New(0); + tmp2 = dotblas_alterdot(NULL, tmp1); + Py_DECREF(tmp1); + Py_DECREF(tmp2); + } + if (PyTypeNum_ISCOMPLEX(typenum)) { + op1 = PyArray_Conjugate(ap1); + if (op1==NULL) goto fail; + Py_DECREF(ap1); + ap1 = (PyArrayObject *)op1; + } + ret = (PyArrayObject *)PyArray_InnerProduct((PyObject *)ap1, + (PyObject *)ap2); + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + } + + if (ap2->dimensions[0] != ap1->dimensions[ap1->nd-1]) { + PyErr_SetString(PyExc_ValueError, "vectors have different lengths"); + goto fail; + } + l = ap1->dimensions[ap1->nd-1]; + + ret = (PyArrayObject *)PyArray_SimpleNew(0, dimensions, typenum); + if (ret == NULL) goto fail; + + + /* Dot product between two vectors -- Level 1 BLAS */ + if (typenum == PyArray_DOUBLE) { + *((double *)ret->data) = cblas_ddot(l, (double *)ap1->data, 1, + (double *)ap2->data, 1); + } + else if (typenum == PyArray_FLOAT) { + *((float *)ret->data) = cblas_sdot(l, (float *)ap1->data, 1, + (float *)ap2->data, 1); + } + else if (typenum == PyArray_CDOUBLE) { + cblas_zdotc_sub(l, (double *)ap1->data, 1, + (double *)ap2->data, 1, (double *)ret->data); + } + else if (typenum == PyArray_CFLOAT) { + cblas_cdotc_sub(l, (float *)ap1->data, 1, + (float *)ap2->data, 1, (float *)ret->data); + } + + Py_DECREF(ap1); + Py_DECREF(ap2); + return PyArray_Return(ret); + + fail: + Py_XDECREF(ap1); + Py_XDECREF(ap2); + Py_XDECREF(ret); + return NULL; +} + +static struct PyMethodDef dotblas_module_methods[] = { + {"dot", (PyCFunction)dotblas_matrixproduct, 1, doc_matrixproduct}, + {"inner", (PyCFunction)dotblas_innerproduct, 1, doc_innerproduct}, + {"vdot", (PyCFunction)dotblas_vdot, 1, doc_vdot}, + {"alterdot", (PyCFunction)dotblas_alterdot, 1, doc_alterdot}, + {"restoredot", (PyCFunction)dotblas_restoredot, 1, doc_restoredot}, + {NULL, NULL, 0} /* sentinel */ +}; + +/* Initialization function for the module */ +DL_EXPORT(void) init_dotblas(void) { + int i; + PyObject *m, *d, *s; + + /* Create the module and add the functions */ + m = Py_InitModule3("_dotblas", dotblas_module_methods, module_doc); + + /* Import the array object */ + import_array(); + + /* Initialise the array of dot functions */ + for (i = 0; i < PyArray_NTYPES; i++) + oldFunctions[i] = NULL; + + /* alterdot at load */ + d = PyTuple_New(0); + s = dotblas_alterdot(NULL, d); + Py_DECREF(d); + Py_DECREF(s); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module _dotblas"); +} diff --git a/numpy/core/blasdot/cblas.h b/numpy/core/blasdot/cblas.h new file mode 100644 index 000000000..3e0faebbe --- /dev/null +++ b/numpy/core/blasdot/cblas.h @@ -0,0 +1,578 @@ +#ifndef CBLAS_H +#define CBLAS_H +#include <stddef.h> + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * Enumerated and derived types + */ +#define CBLAS_INDEX size_t /* this may vary between platforms */ + +enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; +enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; +enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; +enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132}; +enum CBLAS_SIDE {CblasLeft=141, CblasRight=142}; + +/* + * =========================================================================== + * Prototypes for level 1 BLAS functions (complex are recast as routines) + * =========================================================================== + */ +float cblas_sdsdot(const int N, const float alpha, const float *X, + const int incX, const float *Y, const int incY); +double cblas_dsdot(const int N, const float *X, const int incX, const float *Y, + const int incY); +float cblas_sdot(const int N, const float *X, const int incX, + const float *Y, const int incY); +double cblas_ddot(const int N, const double *X, const int incX, + const double *Y, const int incY); + +/* + * Functions having prefixes Z and C only + */ +void cblas_cdotu_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotu); +void cblas_cdotc_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotc); + +void cblas_zdotu_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotu); +void cblas_zdotc_sub(const int N, const void *X, const int incX, + const void *Y, const int incY, void *dotc); + + +/* + * Functions having prefixes S D SC DZ + */ +float cblas_snrm2(const int N, const float *X, const int incX); +float cblas_sasum(const int N, const float *X, const int incX); + +double cblas_dnrm2(const int N, const double *X, const int incX); +double cblas_dasum(const int N, const double *X, const int incX); + +float cblas_scnrm2(const int N, const void *X, const int incX); +float cblas_scasum(const int N, const void *X, const int incX); + +double cblas_dznrm2(const int N, const void *X, const int incX); +double cblas_dzasum(const int N, const void *X, const int incX); + + +/* + * Functions having standard 4 prefixes (S D C Z) + */ +CBLAS_INDEX cblas_isamax(const int N, const float *X, const int incX); +CBLAS_INDEX cblas_idamax(const int N, const double *X, const int incX); +CBLAS_INDEX cblas_icamax(const int N, const void *X, const int incX); +CBLAS_INDEX cblas_izamax(const int N, const void *X, const int incX); + +/* + * =========================================================================== + * Prototypes for level 1 BLAS routines + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (s, d, c, z) + */ +void cblas_sswap(const int N, float *X, const int incX, + float *Y, const int incY); +void cblas_scopy(const int N, const float *X, const int incX, + float *Y, const int incY); +void cblas_saxpy(const int N, const float alpha, const float *X, + const int incX, float *Y, const int incY); + +void cblas_dswap(const int N, double *X, const int incX, + double *Y, const int incY); +void cblas_dcopy(const int N, const double *X, const int incX, + double *Y, const int incY); +void cblas_daxpy(const int N, const double alpha, const double *X, + const int incX, double *Y, const int incY); + +void cblas_cswap(const int N, void *X, const int incX, + void *Y, const int incY); +void cblas_ccopy(const int N, const void *X, const int incX, + void *Y, const int incY); +void cblas_caxpy(const int N, const void *alpha, const void *X, + const int incX, void *Y, const int incY); + +void cblas_zswap(const int N, void *X, const int incX, + void *Y, const int incY); +void cblas_zcopy(const int N, const void *X, const int incX, + void *Y, const int incY); +void cblas_zaxpy(const int N, const void *alpha, const void *X, + const int incX, void *Y, const int incY); + + +/* + * Routines with S and D prefix only + */ +void cblas_srotg(float *a, float *b, float *c, float *s); +void cblas_srotmg(float *d1, float *d2, float *b1, const float b2, float *P); +void cblas_srot(const int N, float *X, const int incX, + float *Y, const int incY, const float c, const float s); +void cblas_srotm(const int N, float *X, const int incX, + float *Y, const int incY, const float *P); + +void cblas_drotg(double *a, double *b, double *c, double *s); +void cblas_drotmg(double *d1, double *d2, double *b1, const double b2, double *P); +void cblas_drot(const int N, double *X, const int incX, + double *Y, const int incY, const double c, const double s); +void cblas_drotm(const int N, double *X, const int incX, + double *Y, const int incY, const double *P); + + +/* + * Routines with S D C Z CS and ZD prefixes + */ +void cblas_sscal(const int N, const float alpha, float *X, const int incX); +void cblas_dscal(const int N, const double alpha, double *X, const int incX); +void cblas_cscal(const int N, const void *alpha, void *X, const int incX); +void cblas_zscal(const int N, const void *alpha, void *X, const int incX); +void cblas_csscal(const int N, const float alpha, void *X, const int incX); +void cblas_zdscal(const int N, const double alpha, void *X, const int incX); + +/* + * =========================================================================== + * Prototypes for level 2 BLAS + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (S, D, C, Z) + */ +void cblas_sgemv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *X, const int incX, const float beta, + float *Y, const int incY); +void cblas_sgbmv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const int KL, const int KU, const float alpha, + const float *A, const int lda, const float *X, + const int incX, const float beta, float *Y, const int incY); +void cblas_strmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const float *A, const int lda, + float *X, const int incX); +void cblas_stbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const float *A, const int lda, + float *X, const int incX); +void cblas_stpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const float *Ap, float *X, const int incX); +void cblas_strsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const float *A, const int lda, float *X, + const int incX); +void cblas_stbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const float *A, const int lda, + float *X, const int incX); +void cblas_stpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const float *Ap, float *X, const int incX); + +void cblas_dgemv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const double alpha, const double *A, const int lda, + const double *X, const int incX, const double beta, + double *Y, const int incY); +void cblas_dgbmv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const int KL, const int KU, const double alpha, + const double *A, const int lda, const double *X, + const int incX, const double beta, double *Y, const int incY); +void cblas_dtrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const double *A, const int lda, + double *X, const int incX); +void cblas_dtbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const double *A, const int lda, + double *X, const int incX); +void cblas_dtpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const double *Ap, double *X, const int incX); +void cblas_dtrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const double *A, const int lda, double *X, + const int incX); +void cblas_dtbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const double *A, const int lda, + double *X, const int incX); +void cblas_dtpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const double *Ap, double *X, const int incX); + +void cblas_cgemv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *X, const int incX, const void *beta, + void *Y, const int incY); +void cblas_cgbmv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const int KL, const int KU, const void *alpha, + const void *A, const int lda, const void *X, + const int incX, const void *beta, void *Y, const int incY); +void cblas_ctrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *A, const int lda, + void *X, const int incX); +void cblas_ctbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const void *A, const int lda, + void *X, const int incX); +void cblas_ctpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *Ap, void *X, const int incX); +void cblas_ctrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *A, const int lda, void *X, + const int incX); +void cblas_ctbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const void *A, const int lda, + void *X, const int incX); +void cblas_ctpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *Ap, void *X, const int incX); + +void cblas_zgemv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *X, const int incX, const void *beta, + void *Y, const int incY); +void cblas_zgbmv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const int KL, const int KU, const void *alpha, + const void *A, const int lda, const void *X, + const int incX, const void *beta, void *Y, const int incY); +void cblas_ztrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *A, const int lda, + void *X, const int incX); +void cblas_ztbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const void *A, const int lda, + void *X, const int incX); +void cblas_ztpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *Ap, void *X, const int incX); +void cblas_ztrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *A, const int lda, void *X, + const int incX); +void cblas_ztbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const int K, const void *A, const int lda, + void *X, const int incX); +void cblas_ztpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const int N, const void *Ap, void *X, const int incX); + + +/* + * Routines with S and D prefixes only + */ +void cblas_ssymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *A, + const int lda, const float *X, const int incX, + const float beta, float *Y, const int incY); +void cblas_ssbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const int K, const float alpha, const float *A, + const int lda, const float *X, const int incX, + const float beta, float *Y, const int incY); +void cblas_sspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *Ap, + const float *X, const int incX, + const float beta, float *Y, const int incY); +void cblas_sger(const enum CBLAS_ORDER order, const int M, const int N, + const float alpha, const float *X, const int incX, + const float *Y, const int incY, float *A, const int lda); +void cblas_ssyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *X, + const int incX, float *A, const int lda); +void cblas_sspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *X, + const int incX, float *Ap); +void cblas_ssyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *X, + const int incX, const float *Y, const int incY, float *A, + const int lda); +void cblas_sspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const float *X, + const int incX, const float *Y, const int incY, float *A); + +void cblas_dsymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *A, + const int lda, const double *X, const int incX, + const double beta, double *Y, const int incY); +void cblas_dsbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const int K, const double alpha, const double *A, + const int lda, const double *X, const int incX, + const double beta, double *Y, const int incY); +void cblas_dspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *Ap, + const double *X, const int incX, + const double beta, double *Y, const int incY); +void cblas_dger(const enum CBLAS_ORDER order, const int M, const int N, + const double alpha, const double *X, const int incX, + const double *Y, const int incY, double *A, const int lda); +void cblas_dsyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *X, + const int incX, double *A, const int lda); +void cblas_dspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *X, + const int incX, double *Ap); +void cblas_dsyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *X, + const int incX, const double *Y, const int incY, double *A, + const int lda); +void cblas_dspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const double *X, + const int incX, const double *Y, const int incY, double *A); + + +/* + * Routines with C and Z prefixes only + */ +void cblas_chemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const void *alpha, const void *A, + const int lda, const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_chbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const int K, const void *alpha, const void *A, + const int lda, const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_chpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const void *alpha, const void *Ap, + const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_cgeru(const enum CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_cgerc(const enum CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_cher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const void *X, const int incX, + void *A, const int lda); +void cblas_chpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const float alpha, const void *X, + const int incX, void *A); +void cblas_cher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_chpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *Ap); + +void cblas_zhemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const void *alpha, const void *A, + const int lda, const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_zhbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const int K, const void *alpha, const void *A, + const int lda, const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_zhpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const void *alpha, const void *Ap, + const void *X, const int incX, + const void *beta, void *Y, const int incY); +void cblas_zgeru(const enum CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_zgerc(const enum CBLAS_ORDER order, const int M, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_zher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const void *X, const int incX, + void *A, const int lda); +void cblas_zhpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const int N, const double alpha, const void *X, + const int incX, void *A); +void cblas_zher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *A, const int lda); +void cblas_zhpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, + const void *alpha, const void *X, const int incX, + const void *Y, const int incY, void *Ap); + +/* + * =========================================================================== + * Prototypes for level 3 BLAS + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (S, D, C, Z) + */ +void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const float alpha, const float *A, + const int lda, const float *B, const int ldb, + const float beta, float *C, const int ldc); +void cblas_ssymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *B, const int ldb, const float beta, + float *C, const int ldc); +void cblas_ssyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const float alpha, const float *A, const int lda, + const float beta, float *C, const int ldc); +void cblas_ssyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const float alpha, const float *A, const int lda, + const float *B, const int ldb, const float beta, + float *C, const int ldc); +void cblas_strmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const float alpha, const float *A, const int lda, + float *B, const int ldb); +void cblas_strsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const float alpha, const float *A, const int lda, + float *B, const int ldb); + +void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const double alpha, const double *A, + const int lda, const double *B, const int ldb, + const double beta, double *C, const int ldc); +void cblas_dsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const double alpha, const double *A, const int lda, + const double *B, const int ldb, const double beta, + double *C, const int ldc); +void cblas_dsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const double alpha, const double *A, const int lda, + const double beta, double *C, const int ldc); +void cblas_dsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const double alpha, const double *A, const int lda, + const double *B, const int ldb, const double beta, + double *C, const int ldc); +void cblas_dtrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const double alpha, const double *A, const int lda, + double *B, const int ldb); +void cblas_dtrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const double alpha, const double *A, const int lda, + double *B, const int ldb); + +void cblas_cgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const void *alpha, const void *A, + const int lda, const void *B, const int ldb, + const void *beta, void *C, const int ldc); +void cblas_csymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_csyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *beta, void *C, const int ldc); +void cblas_csyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_ctrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const void *alpha, const void *A, const int lda, + void *B, const int ldb); +void cblas_ctrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const void *alpha, const void *A, const int lda, + void *B, const int ldb); + +void cblas_zgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const int M, const int N, + const int K, const void *alpha, const void *A, + const int lda, const void *B, const int ldb, + const void *beta, void *C, const int ldc); +void cblas_zsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_zsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *beta, void *C, const int ldc); +void cblas_zsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_ztrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const void *alpha, const void *A, const int lda, + void *B, const int ldb); +void cblas_ztrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const int M, const int N, + const void *alpha, const void *A, const int lda, + void *B, const int ldb); + + +/* + * Routines with prefixes C and Z only + */ +void cblas_chemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_cherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const float alpha, const void *A, const int lda, + const float beta, void *C, const int ldc); +void cblas_cher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const float beta, + void *C, const int ldc); + +void cblas_zhemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const int M, const int N, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const void *beta, + void *C, const int ldc); +void cblas_zherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const double alpha, const void *A, const int lda, + const double beta, void *C, const int ldc); +void cblas_zher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const int N, const int K, + const void *alpha, const void *A, const int lda, + const void *B, const int ldb, const double beta, + void *C, const int ldc); + +void cblas_xerbla(int p, const char *rout, const char *form, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/numpy/core/chararray.py b/numpy/core/chararray.py new file mode 100644 index 000000000..130a40cc4 --- /dev/null +++ b/numpy/core/chararray.py @@ -0,0 +1,341 @@ +from numerictypes import character, string, unicode_, \ + obj2dtype, integer, object_ +from numeric import ndarray, broadcast, empty +from numeric import array as narray +import sys + +__all__ = ['chararray'] + +# special sub-class for character arrays (string and unicode_) +# This adds equality testing and methods of str and unicode types +# which operate on an element-by-element basis + + +class chararray(ndarray): + def __new__(subtype, shape, itemsize=1, unicode=False, buffer=None, + offset=0, strides=None, fortran=0): + + if unicode: + dtype = unicode_ + else: + dtype = string + + if buffer is None: + self = ndarray.__new__(subtype, shape, (dtype, itemsize), + fortran=fortran) + else: + self = ndarray.__new__(subtype, shape, (dtype, itemsize), + buffer=buffer, + offset=offset, strides=strides, + fortran=fortran) + return self + + def _richcmpfunc(self, other, op): + b = broadcast(self, other) + result = empty(b.shape, dtype=bool) + res = result.flat + for k, val in enumerate(b): + r1 = val[0].rstrip('\x00') + r2 = val[1] + res[k] = eval("r1 %s r2" % op, {'r1':r1,'r2':r2}) + return result + + # these should probably be moved to C + def __eq__(self, other): + return self._richcmpfunc(other, '==') + + def __ne__(self, other): + return self._richcmpfunc(other, '!=') + + def __ge__(self, other): + return self._richcmpfunc(other, '>=') + + def __le__(self, other): + return self._richcmpfunc(other, '<=') + + def __gt__(self, other): + return self._richcmpfunc(other, '>') + + def __lt__(self, other): + return self._richcmpfunc(other, '<') + + def __add__(self, other): + b = broadcast(self, other) + arr = b.iters[1].base + outitem = self.itemsize + arr.itemsize + result = chararray(b.shape, outitem, self.dtype is unicode_) + res = result.flat + for k, val in enumerate(b): + res[k] = (val[0] + val[1]) + return result + + def __radd__(self, other): + b = broadcast(other, self) + outitem = b.iters[0].base.itemsize + \ + b.iters[1].base.itemsize + result = chararray(b.shape, outitem, self.dtype is unicode_) + res = result.flat + for k, val in enumerate(b): + res[k] = (val[0] + val[1]) + return result + + def __mul__(self, other): + b = broadcast(self, other) + arr = b.iters[1].base + if not issubclass(arr.dtype, integer): + raise ValueError, "Can only multiply by integers" + outitem = b.iters[0].base.itemsize * arr.max() + result = chararray(b.shape, outitem, self.dtype is unicode_) + res = result.flat + for k, val in enumerate(b): + res[k] = val[0]*val[1] + return result + + def __rmul__(self, other): + b = broadcast(self, other) + arr = b.iters[1].base + if not issubclass(arr.dtype, integer): + raise ValueError, "Can only multiply by integers" + outitem = b.iters[0].base.itemsize * arr.max() + result = chararray(b.shape, outitem, self.dtype is unicode_) + res = result.flat + for k, val in enumerate(b): + res[k] = val[0]*val[1] + return result + + def __mod__(self, other): + b = broadcast(self, other) + res = [None]*b.size + maxsize = -1 + for k,val in enumerate(b): + newval = val[0] % val[1] + maxsize = max(len(newval), maxsize) + res[k] = newval + newarr = chararray(b.shape, maxsize, self.dtype is unicode_) + newarr[:] = res + return newarr + + def __rmod__(self, other): + return NotImplemented + + def _generalmethod(self, name, myiter): + res = [None]*myiter.size + maxsize = -1 + for k, val in enumerate(myiter): + newval = [] + for chk in val[1:]: + if chk.dtype is object_ and chk.item() is None: + break + newval.append(chk) + newitem = getattr(val[0],name)(*newval) + maxsize = max(len(newitem), maxsize) + res[k] = newitem + newarr = chararray(myiter.shape, maxsize, self.dtype is unicode_) + print res, maxsize + newarr[:] = res + return newarr + + def _typedmethod(self, name, myiter, dtype): + result = empty(myiter.shape, dtype=dtype) + res = result.flat + for k, val in enumerate(myiter): + newval = [] + for chk in val[1:]: + if chk.dtype is object_ and chk.item() is None: + break + newval.append(chk) + this_str = val[0].rstrip('\x00') + newitem = getattr(this_str,name)(*newval) + res[k] = newitem + return result + + def _samemethod(self, name): + result = self.copy() + res = result.flat + for k, val in enumerate(self.flat): + res[k] = getattr(val, name)() + return result + + def capitalize(self): + return self._samemethod('capitalize') + + if sys.version[:3] >= '2.4': + def center(self, width, fillchar=' '): + return self._generalmethod('center', + broadcast(self, width, fillchar)) + def ljust(self, width, fillchar=' '): + return self._generalmethod('ljust', + broadcast(self, width, fillchar)) + def rjust(self, width, fillchar=' '): + return self._generalmethod('rjust', + broadcast(self, width, fillchar)) + def rsplit(self, sep=None, maxsplit=None): + return self._typedmethod('rsplit', broadcast(self, sep, maxsplit), + object) + else: + def ljust(self, width): + return self._generalmethod('ljust', broadcast(self, width)) + def rjust(self, width): + return self._generalmethod('rjust', broadcast(self, width)) + def center(self, width): + return self._generalmethod('center', broadcast(self, width)) + + def count(self, sub, start=None, end=None): + return self._typedmethod('count', broadcast(self, sub, start, end), int) + + def decode(self,encoding=None,errors=None): + return self._generalmethod('decode', broadcast(self, encoding, errors)) + + def encode(self,encoding=None,errors=None): + return self._generalmethod('encode', broadcast(self, encoding, errors)) + + def endswith(self, suffix, start=None, end=None): + return self._typedmethod('endswith', broadcast(self, suffix, start, end), bool) + + def expandtabs(self, tabsize=None): + return self._generalmethod('endswith', broadcast(self, tabsize)) + + def find(self, sub, start=None, end=None): + return self._typedmethod('find', broadcast(self, sub, start, end), int) + + def index(self, sub, start=None, end=None): + return self._typedmethod('index', broadcast(self, sub, start, end), int) + + def _ismethod(self, name): + result = empty(self.shape, dtype=bool) + res = result.flat + for k, val in enumerate(self.flat): + item = val.rstrip('\x00') + res[k] = getattr(item, name)() + return result + + def isalnum(self): + return self._ismethod('isalnum') + + def isalpha(self): + return self._ismethod('isalpha') + + def isdigit(self): + return self._ismethod('isdigit') + + def islower(self): + return self._ismethod('islower') + + def isspace(self): + return self._ismethod('isspace') + + def istitle(self): + return self._ismethod('istitle') + + def isupper(self): + return self._ismethod('isupper') + + def join(self, seq): + return self._generalmethod('join', broadcast(self, seq)) + + def lower(self): + return self._samemethod('lower') + + def lstrip(self, chars): + return self._generalmethod('lstrip', broadcast(self, chars)) + + def replace(self, old, new, count=None): + return self._generalmethod('replace', broadcast(self, old, new, count)) + + def rfind(self, sub, start=None, end=None): + return self._typedmethod('rfind', broadcast(self, sub, start, end), int) + + def rindex(self, sub, start=None, end=None): + return self._typedmethod('rindex', broadcast(self, sub, start, end), int) + + def rstrip(self, chars=None): + return self._generalmethod('rstrip', broadcast(self, chars)) + + def split(self, sep=None, maxsplit=None): + return self._typedmethod('split', broadcast(self, sep, maxsplit), object) + + def splitlines(self, keepends=None): + return self._typedmethod('splitlines', broadcast(self, keepends), object) + + def startswith(self, prefix, start=None, end=None): + return self._typedmethod('startswith', broadcast(self, prefix, start, end), bool) + + def strip(self, chars=None): + return self._generalmethod('strip', broadcast(self, chars)) + + def swapcase(self): + return self._samemethod('swapcase') + + def title(self): + return self._samemethod('title') + + def translate(self, table, deletechars=None): + if self.dtype is unicode_: + return self._generalmethod('translate', broadcast(self, table)) + else: + return self._generalmethod('translate', broadcast(self, table, deletechars)) + + def upper(self): + return self._samemethod('upper') + + def zfill(self, width): + return self._generalmethod('zfill', broadcast(self, width)) + + +def array(obj, itemsize=None, copy=True, unicode=False, fortran=False): + + if isinstance(obj, chararray): + if itemsize is None: + itemsize = obj.itemsize + if copy or (itemsize != obj.itemsize) \ + or (not unicode and obj.dtype == unicode_) \ + or (unicode and obj.dtype == string): + return obj.astype("%s%d" % (obj.dtypechar, itemsize)) + else: + return obj + + if isinstance(obj, ndarray) and (obj.dtype in [unicode_, string]): + if itemsize is None: + itemsize = obj.itemsize + copied = 0 + if unicode: + dtype = (unicode_, obj.itemsize) + if obj.dtype == string: + obj = obj.astype(dtype) + copied = 1 + else: + dtype = (string, obj.itemsize) + if obj.dtype == unicode_: + obj = obj.astype(dtype) + copied = 1 + + if copy and not copied: + obj = obj.copy() + + return chararray(obj.shape, itemsize=itemsize, unicode=unicode, + buffer=obj, offset=0, + fortran=obj.flags['FNC']) + + if unicode: dtype = "U" + else: dtype = "S" + + if itemsize is not None: + dtype += str(itemsize) + + if isinstance(obj, str) or isinstance(obj, unicode): + if itemsize is None: + itemsize = len(obj) + shape = len(obj) / itemsize + return chararray(shape, itemsize=itemsize, unicode=unicode, + buffer=obj) + + # default + val = narray(obj, dtype=dtype, fortran=fortran, subok=1) + + return chararray(val.shape, itemsize, unicode, buffer=val, + strides=val.strides, + fortran=fortran) + +def asarray(obj, itemsize=None, unicode=False, fortran=False): + return array(obj, itemsize, copy=False, + unicode=unicode, fortran=fortran) diff --git a/numpy/core/code_generators/array_api_order.txt b/numpy/core/code_generators/array_api_order.txt new file mode 100644 index 000000000..154373592 --- /dev/null +++ b/numpy/core/code_generators/array_api_order.txt @@ -0,0 +1,62 @@ +# The functions in the scipy_core C API +# They are defined here so that the order is set. +PyArray_SetNumericOps +PyArray_GetNumericOps +PyArray_INCREF +PyArray_XDECREF +PyArray_SetStringFunction +PyArray_DescrFromType +PyArray_TypeObjectFromType +PyArray_Zero +PyArray_One +PyArray_CastToType +PyArray_CastTo +PyArray_CanCastSafely +PyArray_CanCastTo +PyArray_ObjectType +PyArray_DescrFromObject +PyArray_ConvertToCommonType +PyArray_DescrFromScalar +PyArray_Size +PyArray_Scalar +PyArray_ToScalar +PyArray_FromScalar +PyArray_ScalarAsCtype +PyArray_CastScalarToCtype +PyArray_RegisterDataType +PyArray_RegisterDescrForType +PyArray_FromDims +PyArray_FromDimsAndDataAndDescr +PyArray_FromAny +PyArray_EnsureArray +PyArray_FromFile +PyArray_FromString +PyArray_FromBuffer +PyArray_Return +PyArray_GetField +PyArray_SetField +PyArray_Byteswap +PyArray_Resize +PyArray_NewCopy +PyArray_CopyInto +PyArray_ToList +PyArray_ToFile +PyArray_Dump +PyArray_Dumps +PyArray_ValidType +PyArray_UpdateFlags +PyArray_New +PyArray_NewFromDescr +PyArray_DescrNew +PyArray_DescrNewFromType +PyArray_GetPriority +PyArray_IterNew +PyArray_MultiIterNew +PyArray_PyIntAsInt +PyArray_PyIntAsIntp +PyArray_Broadcast +PyArray_FillObjectArray +PyArray_FillWithScalar +PyArray_CheckStrides +PyArray_DescrNewByteorder +PyArray_IterAllButAxis diff --git a/numpy/core/code_generators/genapi.py b/numpy/core/code_generators/genapi.py new file mode 100644 index 000000000..b70aa7a14 --- /dev/null +++ b/numpy/core/code_generators/genapi.py @@ -0,0 +1,224 @@ +import sys, os, re +import md5 + +API_FILES = ['arraymethods.c', + 'arrayobject.c', + 'arraytypes.inc.src', + 'multiarraymodule.c', + 'scalartypes.inc.src', + 'ufuncobject.c', + ] +THIS_DIR = os.path.dirname(__file__) +API_FILES = [os.path.join(THIS_DIR, '..', 'src', a) for a in API_FILES] + +def remove_whitespace(s): + return ''.join(s.split()) + +class Function(object): + def __init__(self, name, return_type, args, doc=''): + self.name = name + self.return_type = return_type + self.args = args + self.doc = doc + + def _format_arg(self, (typename, name)): + if typename.endswith('*'): + return typename + name + else: + return typename + ' ' + name + + def argtypes_string(self): + if not self.args: + return 'void' + argstr = ', '.join([a[0] for a in self.args]) + return argstr + + def __str__(self): + argstr = ', '.join([self._format_arg(a) for a in self.args]) + if self.doc: + doccomment = '/* %s */\n' % self.doc + else: + doccomment = '' + return '%s%s %s(%s)' % (doccomment, self.return_type, self.name, argstr) + + def api_hash(self): + m = md5.new() + m.update(remove_whitespace(self.return_type)) + m.update('\000') + m.update(self.name) + m.update('\000') + for typename, name in self.args: + m.update(remove_whitespace(typename)) + m.update('\000') + return m.hexdigest()[:8] + +class ParseError(Exception): + def __init__(self, filename, lineno, msg): + self.filename = filename + self.lineno = lineno + self.msg = msg + + def __str__(self): + return '%s:%s:%s' % (self.filename, self.lineno, self.msg) + +def skip_brackets(s, lbrac, rbrac): + count = 0 + for i, c in enumerate(s): + if c == lbrac: + count += 1 + elif c == rbrac: + count -= 1 + if count == 0: + return i + raise ValueError("no match '%s' for '%s' (%r)" % (lbrac, rbrac, s)) + +def split_arguments(argstr): + arguments = [] + bracket_counts = {'(': 0, '[': 0} + current_argument = [] + state = 0 + i = 0 + def finish_arg(): + if current_argument: + argstr = ''.join(current_argument).strip() + m = re.match(r'(.*(\s+|[*]))(\w+)$', argstr) + if m: + typename = m.group(1).strip() + name = m.group(3) + else: + typename = argstr + name = '' + arguments.append((typename, name)) + del current_argument[:] + while i < len(argstr): + c = argstr[i] + if c == ',': + finish_arg() + elif c == '(': + p = skip_brackets(argstr[i:], '(', ')') + current_argument += argstr[i:i+p] + i += p-1 + else: + current_argument += c + i += 1 + finish_arg() + return arguments + + +def find_functions(filename, tag='API'): + fo = open(filename, 'r') + functions = [] + return_type = None + function_name = None + function_args = [] + doclist = [] + SCANNING, STATE_DOC, STATE_RETTYPE, STATE_NAME, STATE_ARGS = range(5) + state = SCANNING + tagcomment = '/*' + tag + for lineno, line in enumerate(fo): + try: + line = line.strip() + if state == SCANNING: + if line.startswith(tagcomment): + if line.endswith('*/'): + state = STATE_RETTYPE + else: + state = STATE_DOC + elif state == STATE_DOC: + if line.startswith('*/'): + state = STATE_RETTYPE + else: + line = line.lstrip(' *') + doclist.append(line) + elif state == STATE_RETTYPE: #first line of declaration with return type + m = re.match(r'static\s+(.*)$', line) + if m: + line = m.group(1) + return_type = line + state = STATE_NAME + elif state == STATE_NAME: # second line, with function name + m = re.match(r'(\w+)\s*\(', line) + if m: + function_name = m.group(1) + else: + raise ParseError(filename, lineno+1, 'could not find function name') + function_args.append(line[m.end():]) + state = STATE_ARGS + elif state == STATE_ARGS: + if line.startswith('{'): # finished + fargs_str = ' '.join(function_args).rstrip(' )') + fargs = split_arguments(fargs_str) + f = Function(function_name, return_type, fargs, + ' '.join(doclist)) + functions.append(f) + return_type = None + function_name = None + function_args = [] + doclist = [] + state = 0 + else: + function_args.append(line) + except: + print filename, lineno+1 + raise + fo.close() + return functions + +def read_order(order_file): + fo = open(order_file, 'r') + order = {} + i = 0 + for line in fo: + line = line.strip() + if not line.startswith('#'): + order[line] = i + i += 1 + fo.close() + return order + +def get_api_functions(tagname, order_file): + if not os.path.exists(order_file): + order_file = os.path.join(THIS_DIR, order_file) + order = read_order(order_file) + functions = [] + for f in API_FILES: + functions.extend(find_functions(f, tagname)) + dfunctions = [] + for func in functions: + o = order[func.name] + dfunctions.append( (o, func) ) + dfunctions.sort() + return [a[1] for a in dfunctions] + +def add_api_list(offset, APIname, api_list, + module_list, extension_list, init_list): + """Add the API function declerations to the appropiate lists for use in + the headers. + """ + for k, func in enumerate(api_list): + num = offset + k + astr = "static %s %s \\\n (%s);" % \ + (func.return_type, func.name, func.argtypes_string()) + module_list.append(astr) + astr = "#define %s \\\n (*(%s (*)(%s)) \\\n"\ + " %s[%d])" % (func.name,func.return_type, + func.argtypes_string(), APIname, num) + extension_list.append(astr) + astr = " (void *) %s," % func.name + init_list.append(astr) + + +def main(): + tagname = sys.argv[1] + order_file = sys.argv[2] + functions = get_api_functions(tagname, order_file) + m = md5.new(tagname) + for func in functions: + print func + ah = func.api_hash() + m.update(ah) + print hex(int(ah,16)) + print hex(int(m.hexdigest()[:8],16)) + +if __name__ == '__main__': + main() diff --git a/numpy/core/code_generators/generate_array_api.py b/numpy/core/code_generators/generate_array_api.py new file mode 100644 index 000000000..e4ec8b2e1 --- /dev/null +++ b/numpy/core/code_generators/generate_array_api.py @@ -0,0 +1,136 @@ +import os +import genapi + +types = ['Generic','Numeric','Integer','SignedInteger','UnsignedInteger', + 'Inexact', + 'Floating', 'ComplexFloating', 'Flexible', 'Character', + 'Bool','Byte','Short','Int', 'Long', 'LongLong', 'UByte', 'UShort', + 'UInt', 'ULong', 'ULongLong', 'Float', 'Double', 'LongDouble', + 'CFloat', 'CDouble', 'CLongDouble', 'Object', 'String', 'Unicode', + 'Void'] + +h_template = r""" +#ifdef _MULTIARRAYMODULE + +static PyTypeObject PyBigArray_Type; +static PyTypeObject PyArray_Type; +static PyTypeObject PyArrayDescr_Type; +static PyTypeObject PyArrayIter_Type; +static PyTypeObject PyArrayMapIter_Type; +static PyTypeObject PyArrayMultiIter_Type; +static int PyArray_NUMUSERTYPES=0; + +%s + +#else + +#if defined(PY_ARRAY_UNIQUE_SYMBOL) +#define PyArray_API PY_ARRAY_UNIQUE_SYMBOL +#endif + +#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY) +extern void **PyArray_API; +#else +#if defined(PY_ARRAY_UNIQUE_SYMBOL) +void **PyArray_API; +#else +static void **PyArray_API=NULL; +#endif +#endif + +#define PyBigArray_Type (*(PyTypeObject *)PyArray_API[0]) +#define PyArray_Type (*(PyTypeObject *)PyArray_API[1]) +#define PyArrayDescr_Type (*(PyTypeObject *)PyArray_API[2]) +#define PyArrayIter_Type (*(PyTypeObject *)PyArray_API[3]) +#define PyArrayMultiIter_Type (*(PyTypeObject *)PyArray_API[4]) +#define PyArray_NUMUSERTYPES (*(int *)PyArray_API[5]) + +%s + +#if !defined(NO_IMPORT_ARRAY) && !defined(NO_IMPORT) +static int +import_array(void) +{ + PyObject *numpy = PyImport_ImportModule("scipy.base.multiarray"); + PyObject *c_api = NULL; + if (numpy == NULL) return -1; + c_api = PyObject_GetAttrString(numpy, "_ARRAY_API"); + if (c_api == NULL) {Py_DECREF(numpy); return -1;} + if (PyCObject_Check(c_api)) { + PyArray_API = (void **)PyCObject_AsVoidPtr(c_api); + } + Py_DECREF(c_api); + Py_DECREF(numpy); + if (PyArray_API == NULL) return -1; + return 0; +} +#endif + +#endif +""" + + +c_template = r""" +/* These pointers will be stored in the C-object for use in other + extension modules +*/ + +void *PyArray_API[] = { + (void *) &PyBigArray_Type, + (void *) &PyArray_Type, + (void *) &PyArrayDescr_Type, + (void *) &PyArrayIter_Type, + (void *) &PyArrayMultiIter_Type, + (int *) &PyArray_NUMUSERTYPES, +%s +}; +""" + +def generate_api(output_dir): + objectapi_list = genapi.get_api_functions('OBJECT_API', + 'array_api_order.txt') + multiapi_list = genapi.get_api_functions('MULTIARRAY_API', + 'multiarray_api_order.txt') + # API fixes for __arrayobject_api.h + + fixed = 6 + numtypes = len(types) + fixed + numobject = len(objectapi_list) + numtypes + nummulti = len(multiapi_list) + numtotal = numobject + nummulti + + module_list = [] + extension_list = [] + init_list = [] + + # setup types + for k, atype in enumerate(types): + num = fixed + k + astr = " (void *) &Py%sArrType_Type," % types[k] + init_list.append(astr) + astr = "static PyTypeObject Py%sArrType_Type;" % types[k] + module_list.append(astr) + astr = "#define Py%sArrType_Type (*(PyTypeObject *)PyArray_API[%d])" % \ + (types[k], num) + extension_list.append(astr) + + #setup object API + genapi.add_api_list(numtypes, 'PyArray_API', objectapi_list, + module_list, extension_list, init_list) + + # setup multiarray module API + genapi.add_api_list(numobject, 'PyArray_API', multiapi_list, + module_list, extension_list, init_list) + + + # Write to header + fid = open(os.path.join(output_dir, '__multiarray_api.h'),'w') + s = h_template % ('\n'.join(module_list), '\n'.join(extension_list)) + fid.write(s) + fid.close() + + # Write to c-code + fid = open(os.path.join(output_dir,'__multiarray_api.c'),'w') + s = c_template % '\n'.join(init_list) + fid.write(s) + fid.close() diff --git a/numpy/core/code_generators/generate_ufunc_api.py b/numpy/core/code_generators/generate_ufunc_api.py new file mode 100644 index 000000000..59c808e36 --- /dev/null +++ b/numpy/core/code_generators/generate_ufunc_api.py @@ -0,0 +1,91 @@ +import os +import genapi + +h_template = r""" +#ifdef _UMATHMODULE + +static PyTypeObject PyUFunc_Type; + +%s + +#else + +#if defined(PY_UFUNC_UNIQUE_SYMBOL) +#define PyUFunc_API PY_UFUNC_UNIQUE_SYMBOL +#endif + +#if defined(NO_IMPORT) || defined(NO_IMPORT_UFUNC) +extern void **PyUFunc_API; +#else +#if defined(PY_UFUNC_UNIQUE_SYMBOL) +void **PyUFunc_API; +#else +static void **PyUFunc_API=NULL; +#endif +#endif + +#define PyUFunc_Type (*(PyTypeObject *)PyUFunc_API[0]) + +%s + +static int +import_ufunc(void) +{ + PyObject *numpy = PyImport_ImportModule("scipy.base.umath"); + PyObject *c_api = NULL; + + if (numpy == NULL) return -1; + c_api = PyObject_GetAttrString(numpy, "_UFUNC_API"); + if (c_api == NULL) {Py_DECREF(numpy); return -1;} + if (PyCObject_Check(c_api)) { + PyUFunc_API = (void **)PyCObject_AsVoidPtr(c_api); + } + Py_DECREF(c_api); + Py_DECREF(numpy); + if (PyUFunc_API == NULL) return -1; + return 0; +} + +#endif +""" + +c_template = r""" +/* These pointers will be stored in the C-object for use in other + extension modules +*/ + +void *PyUFunc_API[] = { + (void *) &PyUFunc_Type, +%s +}; +""" + +def generate_api(output_dir): + ufunc_api_list = genapi.get_api_functions('UFUNC_API', + 'ufunc_api_order.txt') + + # API fixes for __arrayobject_api.h + + fixed = 1 + nummulti = len(ufunc_api_list) + numtotal = fixed + nummulti + + module_list = [] + extension_list = [] + init_list = [] + + #setup object API + genapi.add_api_list(fixed, 'PyUFunc_API', ufunc_api_list, + module_list, extension_list, init_list) + + # Write to header + fid = open(os.path.join(output_dir, '__ufunc_api.h'),'w') + s = h_template % ('\n'.join(module_list), '\n'.join(extension_list)) + fid.write(s) + fid.close() + + # Write to c-code + fid = open(os.path.join(output_dir, '__ufunc_api.c'),'w') + s = c_template % '\n'.join(init_list) + fid.write(s) + fid.close() diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py new file mode 100644 index 000000000..16945d256 --- /dev/null +++ b/numpy/core/code_generators/generate_umath.py @@ -0,0 +1,500 @@ + +import string +import re + +Zero = "PyUFunc_Zero" +One = "PyUFunc_One" +None_ = "PyUFunc_None" +#each entry in defdict is + +#name: [string of chars for which it is defined, +# string of characters using func interface, +# tuple of strings giving funcs for data, +# (in, out), or (instr, outstr) giving the signature as character codes, +# identity, +# docstring, +# output specification (optional) +# ] + +all = '?bBhHiIlLqQfdgFDGO' +ints = 'bBhHiIlLqQ' +intsO = ints + 'O' +bintsO = '?'+ints+'O' +flts = 'fdg' +fltsO = flts+'O' +fltsM = flts+'M' +cmplx = 'FDG' +cmplxO = cmplx+'O' +cmplxM = cmplx+'M' +noint = flts+cmplx+'O' +nointM = flts+cmplx+'M' +allM = '?'+ints+flts+cmplxM +nobool = all[1:] +nobool_or_obj = all[1:-1] +intflt = ints+flts +nocmplx = '?'+ints+flts +nocmplxO = nocmplx+'O' +nocmplxM = nocmplx+'M' +noobj = all[:-1] + +defdict = { +'add': [all,'O',("PyNumber_Add",), + (2,1), Zero, + "adds the arguments elementwise." + ], +'subtract' : [all,'O',("PyNumber_Subtract",), + (2,1), Zero, + "subtracts the arguments elementwise." + ], +'multiply' : [all,cmplxO, + ("prod,"*3,"PyNumber_Multiply",), + (2,1), One, + "multiplies the arguments elementwise." + ], +'divide' : [nobool,cmplxO, + ("quot,"*3,"PyNumber_Divide",), + (2,1), One, + "divides the arguments elementwise." + ], +'floor_divide' : [nobool, cmplxO, + ("floor_quot,"*3, + "PyNumber_FloorDivide"), + (2,1), One, + "floor divides the arguments elementwise." + ], +'true_divide' : [nobool, cmplxO, + ("quot,"*3,"PyNumber_TrueDivide"), + (2,1), One, + "true divides the arguments elementwise.", + 'f'*4+'d'*6+flts+cmplxO + ], +'conjugate' : [nobool_or_obj, 'M', + ('"conjugate"',), + (1,1), None, + "takes the conjugate of x elementwise." + ], + +'fmod' : [intflt,fltsM, + ("fmod,"*3, "fmod"), + (2,1), Zero, + "computes (C-like) x1 % x2 elementwise." + ], +'power' : [nobool,noint, + ("pow,"*6, + "PyNumber_Power"), + (2,1), One, + "computes x1**x2 elementwise." + ], +'absolute' : [all,'O', + ("PyNumber_Absolute",), + (1,1), None, + "takes |x| elementwise.", + nocmplx+fltsO + ], +'negative' : [all,cmplxO, + ("neg,"*3,"PyNumber_Negative"), + (1,1), None, + "determines -x elementwise", + ], +'greater' : [all,'',(),(2,1), None, + "returns elementwise x1 > x2 in a bool array.", + '?'*len(all) + ], +'greater_equal' : [all,'',(),(2,1), None, + "returns elementwise x1 >= x2 in a bool array.", + '?'*len(all) + ], +'less' : [all,'',(),(2,1), None, + "returns elementwise x1 < x2 in a bool array.", + '?'*len(all) + ], +'less_equal' : [all,'',(),(2,1), None, + "returns elementwise x1 <= x2 in a bool array", + '?'*len(all) + ], +'equal' : [all, '', (), (2,1), None, + "returns elementwise x1 == x2 in a bool array", + '?'*len(all) + ], +'not_equal' : [all, '', (), (2,1), None, + "returns elementwise x1 |= x2", + '?'*len(all) + ], +'logical_and': [allM,'M',('"logical_and"',), + (2,1), One, + "returns x1 and x2 elementwise.", + '?'*len(nocmplxM+cmplx) + ], +'logical_or': [allM,'M',('"logical_or"',), + (2,1), Zero, + "returns x1 or x2 elementwise.", + '?'*len(nocmplxM+cmplx) + ], +'logical_xor': [allM, 'M', ('"logical_xor"',), + (2,1), None, + "returns x1 xor x2 elementwise.", + '?'*len(nocmplxM+cmplx) + ], +'logical_not' : [allM, 'M', ('"logical_not"',), + (1,1), None, + "returns not x elementwise.", + '?'*len(nocmplxM+cmplx) + ], +'maximum' : [noobj,'',(), + (2,1), None, + "returns maximum (if x1 > x2: x1; else: x2) elementwise."], +'minimum' : [noobj,'',(), + (2,1), None, + "returns minimum (if x1 < x2: x1; else: x2) elementwise"], +'bitwise_and' : [bintsO,'O',("PyNumber_And",), + (2,1), One, + "computes x1 & x2 elementwise."], +'bitwise_or' : [bintsO, 'O', ("PyNumber_Or",), + (2,1), Zero, + "computes x1 | x2 elementwise."], +'bitwise_xor' : [bintsO, 'O', ("PyNumber_Xor",), + (2,1), None, + "computes x1 ^ x2 elementwise."], +'invert' : [bintsO,'O', ("PyNumber_Invert",), + (1,1), None, + "computes ~x (bit inversion) elementwise." + ], +'left_shift' : [intsO, 'O', ("PyNumber_Lshift",), + (2,1), None, + "computes x1 << x2 (x1 shifted to left by x2 bits) elementwise." + ], +'right_shift' : [intsO, 'O', ("PyNumber_Rshift",), + (2,1), None, + "computes x1 >> x2 (x1 shifted to right by x2 bits) elementwise." + ], +'arccos' : [nointM, nointM, + ("acos,"*6, '"arccos"'), + (1, 1), None, + "inverse cosine elementwise." + ], +'arcsin': [nointM, nointM, + ("asin,"*6, '"arcsin"'), + (1, 1), None, + "inverse sine elementwise." + ], +'arctan': [nointM, nointM, + ("atan,"*6, '"arctan"'), + (1, 1), None, + "inverse tangent elementwise." + ], +'arccosh' : [nointM, nointM, + ("acosh,"*6, '"arccosh"'), + (1, 1), None, + "inverse hyperbolic cosine elementwise." + ], +'arcsinh': [nointM, nointM, + ("asinh,"*6, '"arcsinh"'), + (1, 1), None, + "inverse hyperbolic sine elementwise." + ], +'arctanh': [nointM, nointM, + ("atanh,"*6, '"arctanh"'), + (1, 1), None, + "inverse hyperbolic tangent elementwise." + ], +'cos': [nointM, nointM, + ("cos,"*6, '"cos"'), + (1, 1), None, + "cosine elementwise." + ], +'sin': [nointM, nointM, + ("sin,"*6, '"sin"'), + (1, 1), None, + "sine elementwise." + ], +'tan': [nointM, nointM, + ("tan,"*6, '"tan"'), + (1, 1), None, + "tangent elementwise." + ], +'cosh': [nointM, nointM, + ("cosh,"*6, '"cosh"'), + (1, 1), None, + "hyperbolic cosine elementwise." + ], +'sinh': [nointM, nointM, + ("sinh,"*6, '"sinh"'), + (1, 1), None, + "hyperbolic sine elementwise." + ], +'tanh': [nointM, nointM, + ("tanh,"*6, '"tanh"'), + (1, 1), None, + "hyperbolic tangent elementwise." + ], +'exp' : [nointM, nointM, + ("exp,"*6, '"exp"'), + (1, 1), None, + "e**x elementwise." + ], +'log' : [nointM, nointM, + ("log,"*6, '"log"'), + (1, 1), None, + "logarithm base e elementwise." + ], +'log10' : [nointM, nointM, + ("log10,"*6, '"log10"'), + (1, 1), None, + "logarithm base 10 elementwise." + ], +'sqrt' : [nointM, nointM, + ("sqrt,"*6, '"sqrt"'), + (1,1), None, + "square-root elementwise." + ], +'ceil' : [fltsM, fltsM, + ("ceil,"*3, '"ceil"'), + (1,1), None, + "elementwise smallest integer >= x." + ], +'fabs' : [fltsM, fltsM, + ("fabs,"*3, '"fabs"'), + (1,1), None, + "absolute values." + ], +'floor' : [fltsM, fltsM, + ("floor,"*3, '"floor"'), + (1,1), None, + "elementwise largest integer <= x" + ], +'arctan2' : [fltsM, fltsM, + ("atan2,"*3, '"arctan2"'), + (2,1), None, + "a safe and correct arctan(x1/x2)" + ], + +'remainder' : [intflt, 'O', + ("PyObject_Remainder"), + (2,1), None, + "computes x1-n*x2 where n is floor(x1 / x2)"], + +'hypot' : [fltsM, fltsM, + ("hypot,"*3, '"hypot"'), + (2,1), None, + "sqrt(x1**2 + x2**2) elementwise" + ], + +'isnan' : [flts+cmplx, '', + (), (1,1), None, + "returns True where x is Not-A-Number", + '?'*len(flts+cmplx) + ], + +'isinf' : [flts+cmplx, '', + (), (1,1), None, + "returns True where x is +inf or -inf", + '?'*len(flts+cmplx) + ], + +'isfinite' : [flts+cmplx, '', + (), (1,1), None, + "returns True where x is finite", + '?'*len(flts+cmplx) + ], + +'signbit' : [flts,'', + (),(1,1),None, + "returns True where signbit of x is set (x<0).", + '?'*len(flts) + ], + +'modf' : [flts,'', + (),(1,2),None, + "breaks x into fractional (y1) and integral (y2) parts.\\n\\n Each output has the same sign as the input." + ] +} + + +def indent(st,spaces): + indention = ' '*spaces + indented = indention + string.replace(st,'\n','\n'+indention) + # trim off any trailing spaces + indented = re.sub(r' +$',r'',indented) + return indented + +chartoname = {'?': 'bool', + 'b': 'byte', + 'B': 'ubyte', + 'h': 'short', + 'H': 'ushort', + 'i': 'int', + 'I': 'uint', + 'l': 'long', + 'L': 'ulong', + 'q': 'longlong', + 'Q': 'ulonglong', + 'f': 'float', + 'd': 'double', + 'g': 'longdouble', + 'F': 'cfloat', + 'D': 'cdouble', + 'G': 'clongdouble', + 'O': 'OBJECT', + 'M': 'OBJECT', + } + +chartotype1 = {'f': 'f_f', + 'd': 'd_d', + 'g': 'g_g', + 'F': 'F_F', + 'D': 'D_D', + 'G': 'G_G', + 'O': 'O_O', + 'M': 'O_O_method'} + +chartotype2 = {'f': 'ff_f', + 'd': 'dd_d', + 'g': 'gg_g', + 'F': 'FF_F', + 'D': 'DD_D', + 'G': 'GG_G', + 'O': 'OO_O', + 'M': 'O_O_method'} +#for each name +# 1) create functions, data, and signature +# 2) fill in functions and data in InitOperators +# 3) add function. + +def make_arrays(funcdict): + # functions array contains an entry for every type implemented + # NULL should be placed where PyUfunc_ style function will be filled in later + # + code1list = [] + code2list = [] + for name, vals in funcdict.iteritems(): + funclist = [] + datalist = [] + siglist = [] + k=0; + sub=0; + numin, numout = vals[3] + + if numin > 1: + thedict = chartotype2 # two inputs and one output + else: + thedict = chartotype1 # one input and one output + + instr = ''.join([x*numin for x in list(vals[0])]) + if len(vals) > 6: + if isinstance(vals[6],type('')): + outstr = vals[6] + else: # a tuple specifying input signature, output signature + instr, outstr = vals[6] + else: + outstr = ''.join([x*numout for x in list(vals[0])]) + + _valslen = len(vals[0]) + assert _valslen*numout == len(outstr), "input/output signature doesn't match" + assert len(instr) == _valslen*numin, "input/output signature doesn't match" + + for char in vals[0]: + if char in vals[1]: # use generic function-based interface + funclist.append('NULL') + astr = '%s_functions[%d] = PyUFunc_%s;' % \ + (name, k, thedict[char]) + code2list.append(astr) + thisfunc = vals[2][sub] + if len(thisfunc) > 8 and thisfunc[:8] == "PyNumber": + astr = '%s_data[%d] = (void *) %s;' % \ + (name, k, thisfunc) + code2list.append(astr) + datalist.append('(void *)NULL'); + else: + datalist.append('(void *)%s' % thisfunc) + sub += 1 + else: # individual wrapper interface + datalist.append('(void *)NULL'); + funclist.append('%s_%s' % (chartoname[char].upper(), name)) + + insubstr = instr[numin*k:numin*(k+1)] + outsubstr = outstr[numout*k:numout*(k+1)] + siglist.extend(['PyArray_%s' % chartoname[x].upper() for x in insubstr]) + siglist.extend(['PyArray_%s' % chartoname[x].upper() for x in outsubstr]) + k += 1 + funcnames = ', '.join(funclist) + signames = ', '.join(siglist) + datanames = ', '.join(datalist) + code1list.append("static PyUFuncGenericFunction %s_functions[] = { %s };" \ + % (name, funcnames)) + code1list.append("static void * %s_data[] = { %s };" \ + % (name, datanames)) + code1list.append("static char %s_signatures[] = { %s };" \ + % (name, signames)) + return "\n".join(code1list),"\n".join(code2list) + +def make_ufuncs(funcdict): + code3list = [] + for name, vals in funcdict.items(): + mlist = [] + mlist.append(\ +r"""f = PyUFunc_FromFuncAndData(%s_functions, %s_data, %s_signatures, %d, + %d, %d, %s, "%s", + "%s", 0);""" % (name,name,name,len(vals[0]), + vals[3][0], vals[3][1], vals[4], + name, vals[5])) + mlist.append(r"""PyDict_SetItemString(dictionary, "%s", f);"""%name) + mlist.append(r"""Py_DECREF(f);""") + code3list.append('\n'.join(mlist)) + return '\n'.join(code3list) + + +def convert_vals(funcdict): + for name, vals in funcdict.iteritems(): + if vals[4] is None: + vals[4] = None_ + vals2 = vals[2] + if len(vals2) > 0: + alist = vals2[0].split(',') + if len(alist) == 4: + a = alist[0] + if 'f' in vals[1]: + newlist = [ a+'f', a, a+'l'] + else: + newlist = ['nc_'+a+'f', 'nc_'+a, 'nc_'+a+'l'] + elif len(alist) == 7: + a = alist[0] + newlist = [a+'f', a, a+'l','nc_'+a+'f', 'nc_'+a, 'nc_'+a+'l'] + else: + newlist = alist + newlist = newlist + list(vals2[1:]) + vals[2] = tuple(newlist) + funcdict[name] = vals + + +def make_code(funcdict,filename): + convert_vals(funcdict) + code1, code2 = make_arrays(funcdict) + code3 = make_ufuncs(funcdict) + code2 = indent(code2,4) + code3 = indent(code3,4) + code = r""" + +/** Warning this file is autogenerated!!! + + Please make changes to the code generator program (%s) +**/ + +%s + +static void +InitOperators(PyObject *dictionary) { + PyObject *f; + +%s +%s +} +""" % (filename, code1, code2, code3) + return code; + + +if __name__ == "__main__": + filename = __file__ + fid = open('__umath_generated.c','w') + code = make_code(defdict, filename) + fid.write(code) + fid.close() diff --git a/numpy/core/code_generators/multiarray_api_order.txt b/numpy/core/code_generators/multiarray_api_order.txt new file mode 100644 index 000000000..8dbb86882 --- /dev/null +++ b/numpy/core/code_generators/multiarray_api_order.txt @@ -0,0 +1,66 @@ +PyArray_Transpose +PyArray_Take +PyArray_Put +PyArray_PutMask +PyArray_Repeat +PyArray_Choose +PyArray_Sort +PyArray_ArgSort +PyArray_SearchSorted +PyArray_ArgMax +PyArray_ArgMin +PyArray_Reshape +PyArray_Newshape +PyArray_Squeeze +PyArray_View +PyArray_SwapAxes +PyArray_Max +PyArray_Min +PyArray_Ptp +PyArray_Mean +PyArray_Trace +PyArray_Diagonal +PyArray_Clip +PyArray_Conjugate +PyArray_Nonzero +PyArray_Std +PyArray_Sum +PyArray_CumSum +PyArray_Prod +PyArray_CumProd +PyArray_All +PyArray_Any +PyArray_Compress +PyArray_Flatten +PyArray_Ravel +PyArray_MultiplyList +PyArray_MultiplyIntList +PyArray_GetPtr +PyArray_CompareLists +PyArray_AsCArray +PyArray_As1D +PyArray_As2D +PyArray_Free +PyArray_Converter +PyArray_IntpFromSequence +PyArray_Concatenate +PyArray_InnerProduct +PyArray_MatrixProduct +PyArray_CopyAndTranspose +PyArray_Correlate +PyArray_TypestrConvert +PyArray_DescrConverter +PyArray_DescrConverter2 +PyArray_IntpConverter +PyArray_BufferConverter +PyArray_AxisConverter +PyArray_BoolConverter +PyArray_ByteorderConverter +PyArray_EquivTypes +PyArray_Zeros +PyArray_Empty +PyArray_Where +PyArray_Arange +PyArray_ArangeObj +PyArray_SortkindConverter +PyArray_LexSort diff --git a/numpy/core/code_generators/ufunc_api_order.txt b/numpy/core/code_generators/ufunc_api_order.txt new file mode 100644 index 000000000..805765313 --- /dev/null +++ b/numpy/core/code_generators/ufunc_api_order.txt @@ -0,0 +1,26 @@ +PyUFunc_FromFuncAndData +PyUFunc_RegisterLoopForType +PyUFunc_GenericFunction +PyUFunc_f_f_As_d_d +PyUFunc_d_d +PyUFunc_f_f +PyUFunc_g_g +PyUFunc_F_F_As_D_D +PyUFunc_F_F +PyUFunc_D_D +PyUFunc_G_G +PyUFunc_O_O +PyUFunc_ff_f_As_dd_d +PyUFunc_ff_f +PyUFunc_dd_d +PyUFunc_gg_g +PyUFunc_FF_F_As_DD_D +PyUFunc_DD_D +PyUFunc_FF_F +PyUFunc_GG_G +PyUFunc_OO_O +PyUFunc_O_O_method +PyUFunc_On_Om +PyUFunc_GetPyValues +PyUFunc_checkfperr +PyUFunc_clearfperr diff --git a/numpy/core/convertcode.py b/numpy/core/convertcode.py new file mode 100644 index 000000000..5c532b394 --- /dev/null +++ b/numpy/core/convertcode.py @@ -0,0 +1,147 @@ + +# This module converts code written for Numeric to run with scipy.base + +# Makes the following changes: +# * Converts typecharacters +# * Changes import statements (warns of use of from Numeric import *) +# * Changes import statements (using numerix) ... +# * Makes search and replace changes to: +# - .typecode() +# - .iscontiguous() +# - .byteswapped() +# - .itemsize() +# * Converts .flat to .ravel() except for .flat = xxx or .flat[xxx] +# * Change typecode= to dtype= +# * Eliminates savespace=xxx +# * Replace xxx.spacesaver() with True +# * Convert xx.savespace(?) to pass + ## xx.savespace(?) +# #### -- not * Convert a.shape = ? to a.reshape(?) +# * Prints warning for use of bool, int, float, copmlex, object, and unicode +# + +__all__ = ['fromfile', 'fromstr'] + +import sys +import os +import re +import glob + +flatindex_re = re.compile('([.]flat(\s*?[[=]))') + +def replacetypechars(astr): +# astr = astr.replace("'s'","'h'") +# astr = astr.replace("'c'","'S1'") + astr = astr.replace("'b'","'B'") + astr = astr.replace("'1'","'b'") +# astr = astr.replace("'w'","'H'") + astr = astr.replace("'u'","'I'") + return astr + +def changeimports(fstr, name, newname): + importstr = 'import %s' % name + importasstr = 'import %s as ' % name + fromstr = 'from %s import ' % name + fromall=0 + + fstr = fstr.replace(importasstr, 'import %s as ' % newname) + fstr = fstr.replace(importstr, 'import %s as %s' % (newname,name)) + + ind = 0 + Nlen = len(fromstr) + Nlen2 = len("from %s import " % newname) + while 1: + found = fstr.find(fromstr,ind) + if (found < 0): + break + ind = found + Nlen + if fstr[ind] == '*': + continue + fstr = "%sfrom %s import %s" % (fstr[:found], newname, fstr[ind:]) + ind += Nlen2 - Nlen + return fstr, fromall + +def replaceattr(astr): + astr = astr.replace(".typecode()",".dtypechar") + astr = astr.replace(".iscontiguous()",".flags.contiguous") + astr = astr.replace(".byteswapped()",".byteswap()") + astr = astr.replace(".toscalar()", ".item()") + astr = astr.replace(".itemsize()",".itemsize") + # preserve uses of flat that should be o.k. + tmpstr = flatindex_re.sub("@@@@\\2",astr) + # replace other uses of flat + tmpstr = tmpstr.replace(".flat",".ravel()") + # put back .flat where it was valid + astr = tmpstr.replace("@@@@", ".flat") + return astr + +svspc = re.compile(r'(\S+\s*[(].+),\s*savespace\s*=.+\s*[)]') +svspc2 = re.compile(r'([^,(\s]+[.]spacesaver[(][)])') +svspc3 = re.compile(r'(\S+[.]savespace[(].*[)])') +#shpe = re.compile(r'(\S+\s*)[.]shape\s*=[^=]\s*(.+)') +def replaceother(astr): + astr = astr.replace("typecode=","dtype=") + astr = astr.replace("UserArray","ndarray") + astr = svspc.sub('\\1)',astr) + astr = svspc2.sub('True',astr) + astr = svspc3.sub('pass ## \\1', astr) + #astr = shpe.sub('\\1=\\1.reshape(\\2)', astr) + return astr + +import datetime +def fromstr(filestr): + filestr = replacetypechars(filestr) + filestr, fromall1 = changeimports(filestr, 'Numeric', 'scipy') + filestr, fromall1 = changeimports(filestr, 'multiarray', + 'scipy.base.multiarray') + filestr, fromall1 = changeimports(filestr, 'umath', + 'scipy.base.umath') + filestr, fromall1 = changeimports(filestr, 'Precision', 'scipy.base') + filestr, fromall2 = changeimports(filestr, 'numerix', 'scipy.base') + filestr, fromall3 = changeimports(filestr, 'scipy_base', 'scipy.base') + filestr, fromall3 = changeimports(filestr, 'MLab', 'scipy.base.mlab') + filestr, fromall3 = changeimports(filestr, 'LinearAlgebra', 'scipy.corelinalg') + filestr, fromall3 = changeimports(filestr, 'RNG', 'scipy.random') + filestr, fromall3 = changeimports(filestr, 'RandomArray', 'scipy.random') + filestr, fromall3 = changeimports(filestr, 'FFT', 'scipy.corefft') + filestr, fromall3 = changeimports(filestr, 'MA', 'scipy.base.ma') + fromall = fromall1 or fromall2 or fromall3 + filestr = replaceattr(filestr) + filestr = replaceother(filestr) + today = datetime.date.today().strftime('%b %d, %Y') + name = os.path.split(sys.argv[0])[-1] + filestr = '## Automatically adapted for '\ + 'scipy %s by %s\n\n%s' % (today, name, filestr) + return filestr + +def makenewfile(name, filestr): + fid = file(name, 'w') + fid.write(filestr) + fid.close() + +def getandcopy(name): + fid = file(name) + filestr = fid.read() + fid.close() + base, ext = os.path.splitext(name) + makenewfile(base+'.orig', filestr) + return filestr + +def fromfile(filename): + filestr = getandcopy(filename) + filestr = fromstr(filestr) + makenewfile(filename, filestr) + +def fromargs(args): + filename = args[1] + fromfile(filename) + +def convertall(direc=''): + files = glob.glob(os.path.join(direc,'*.py')) + for afile in files: + fromfile(afile) + +if __name__ == '__main__': + fromargs(sys.argv) + + + diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py new file mode 100644 index 000000000..60e4b4be0 --- /dev/null +++ b/numpy/core/function_base.py @@ -0,0 +1,815 @@ + +l__all__ = ['logspace', 'linspace', 'round_', + 'select', 'piecewise', 'trim_zeros', + 'copy', 'iterable', 'base_repr', 'binary_repr', + 'diff', 'gradient', 'angle', 'unwrap', 'sort_complex', 'disp', + 'unique', 'extract', 'insert', 'nansum', 'nanmax', 'nanargmax', + 'nanargmin', 'nanmin', 'vectorize', 'asarray_chkfinite', 'average', + 'histogram', 'bincount', 'digitize', 'cov', 'corrcoef', 'msort', + 'median', 'sinc', 'hamming', 'hanning', 'bartlett', 'blackman', + 'kaiser', 'trapz' + ] + +import types +import math +import numeric as _nx +from numeric import ones, zeros, arange, concatenate, array, asarray, empty +from numeric import ScalarType, dot, where, newaxis +from umath import pi, multiply, add, arctan2, maximum, minimum, frompyfunc, \ + isnan, absolute, cos, less_equal, sqrt, sin, mod +from oldnumeric import ravel, nonzero, choose, \ + sometrue, alltrue, reshape, any, all, typecodes, ArrayType, squeeze,\ + sort +from type_check import ScalarType, isscalar +from shape_base import atleast_1d +from twodim_base import diag +from _compiled_base import digitize, bincount, _insert +from ufunclike import sign + +_lkup = {'0':'000', + '1':'001', + '2':'010', + '3':'011', + '4':'100', + '5':'101', + '6':'110', + '7':'111', + 'L':''} + +def binary_repr(num): + """Return the binary representation of the input number as a string. + + This is equivalent to using base_repr with base 2, but about 25x + faster. + """ + ostr = oct(num) + bin = '' + for ch in ostr[1:]: + bin += _lkup[ch] + ind = 0 + while bin[ind] == '0': + ind += 1 + return bin[ind:] + +def base_repr (number, base=2, padding=0): + """Return the representation of a number in any given base. + """ + chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + lnb = math.log(base) + res = padding*chars[0] + if number == 0: + return res + chars[0] + exponent = int (math.log (number)/lnb) + while(exponent >= 0): + term = long(base)**exponent + lead_digit = int(number / term) + res += chars[lead_digit] + number -= term*lead_digit + exponent -= 1 + return res +#end Fernando's utilities + + +def linspace(start, stop, num=50, endpoint=True, retstep=False): + """Return evenly spaced numbers. + + Return 'num' evenly spaced samples from 'start' to 'stop'. If + 'endpoint' is True, the last sample is 'stop'. If 'retstep' is + True then return the step value used. + """ + num = int(num) + if num <= 0: + return array([]) + if endpoint: + if num == 1: + return array([start]) + step = (stop-start)/float((num-1)) + else: + step = (stop-start)/float(num) + y = _nx.arange(0, num) * step + start + if retstep: + return y, step + else: + return y + +def logspace(start,stop,num=50,endpoint=True,base=10.0): + """Evenly spaced numbers on a logarithmic scale. + + Computes int(num) evenly spaced exponents from start to stop. + If endpoint=True, then last exponent is stop. + Returns base**exponents. + """ + y = linspace(start,stop,num=num,endpoint=endpoint) + return _nx.power(base,y) + +def iterable(y): + try: iter(y) + except: return 0 + return 1 + +def histogram(a, bins=10, range=None, normed=False): + a = asarray(a).ravel() + if not iterable(bins): + if range is None: + range = (a.min(), a.max()) + mn, mx = [mi+0.0 for mi in range] + if mn == mx: + mn -= 0.5 + mx += 0.5 + bins = linspace(mn, mx, bins, endpoint=False) + + n = sort(a).searchsorted(bins) + n = concatenate([n, [len(a)]]) + n = n[1:]-n[:-1] + + if normed: + db = bins[1] - bins[0] + return 1.0/(a.size*db) * n, bins + else: + return n, bins + +def average(a, axis=0, weights=None, returned=False): + """average(a, axis=0, weights=None, returned=False) + + Average the array over the given axis. If the axis is None, average + over all dimensions of the array. Equivalent to a.mean(axis), but + with a default axis of 0 instead of None. + + If an integer axis is given, this equals: + a.sum(axis) * 1.0 / len(a) + + If axis is None, this equals: + a.sum(axis) * 1.0 / product(a.shape) + + If weights are given, result is: + sum(a * weights) / sum(weights), + where the weights must have a's shape or be 1D with length the + size of a in the given axis. Integer weights are converted to + Float. Not specifying weights is equivalent to specifying + weights that are all 1. + + If 'returned' is True, return a tuple: the result and the sum of + the weights or count of values. The shape of these two results + will be the same. + + Raises ZeroDivisionError if appropriate. (The version in MA does + not -- it returns masked values). + """ + if axis is None: + a = array(a).ravel() + if weights is None: + n = add.reduce(a) + d = len(a) * 1.0 + else: + w = array(weights).ravel() * 1.0 + n = add.reduce(multiply(a, w)) + d = add.reduce(w) + else: + a = array(a) + ash = a.shape + if ash == (): + a.shape = (1,) + if weights is None: + n = add.reduce(a, axis) + d = ash[axis] * 1.0 + if returned: + d = ones(n.shape) * d + else: + w = array(weights, copy=False) * 1.0 + wsh = w.shape + if wsh == (): + wsh = (1,) + if wsh == ash: + n = add.reduce(a*w, axis) + d = add.reduce(w, axis) + elif wsh == (ash[axis],): + ni = ash[axis] + r = [newaxis]*ni + r[axis] = slice(None, None, 1) + w1 = eval("w["+repr(tuple(r))+"]*ones(ash, Float)") + n = add.reduce(a*w1, axis) + d = add.reduce(w1, axis) + else: + raise ValueError, 'averaging weights have wrong shape' + + if not isinstance(d, ArrayType): + if d == 0.0: + raise ZeroDivisionError, 'zero denominator in average()' + if returned: + return n/d, d + else: + return n/d + +def asarray_chkfinite(a): + """Like asarray, but check that no NaNs or Infs are present. + """ + a = asarray(a) + if (a.dtypechar in _nx.typecodes['AllFloat']) \ + and (_nx.isnan(a).any() or _nx.isinf(a).any()): + raise ValueError, "array must not contain infs or NaNs" + return a + + + + +def piecewise(x, condlist, funclist, *args, **kw): + """Return a piecewise-defined function. + + x is the domain + + condlist is a list of boolean arrays or a single boolean array + The length of the condition list must be n2 or n2-1 where n2 + is the length of the function list. If len(condlist)==n2-1, then + an 'otherwise' condition is formed by |'ing all the conditions + and inverting. + + funclist is a list of functions to call of length (n2). + Each function should return an array output for an array input + Each function can take (the same set) of extra arguments and + keyword arguments which are passed in after the function list. + + The output is the same shape and type as x and is found by + calling the functions on the appropriate portions of x. + + Note: This is similar to choose or select, except + the the functions are only evaluated on elements of x + that satisfy the corresponding condition. + + The result is + |-- + | f1(x) for condition1 + y = --| f2(x) for condition2 + | ... + | fn(x) for conditionn + |-- + + """ + n2 = len(funclist) + if not isinstance(condlist, type([])): + condlist = [condlist] + n = len(condlist) + if n == n2-1: # compute the "otherwise" condition. + totlist = condlist[0] + for k in range(1, n): + totlist |= condlist + condlist.append(~totlist) + n += 1 + if (n != n2): + raise ValueError, "function list and condition list must be the same" + y = empty(x.shape, x.dtype) + for k in range(n): + item = funclist[k] + if not callable(item): + y[condlist[k]] = item + else: + y[condlist[k]] = item(x[condlist[k]], *args, **kw) + return y + +def select(condlist, choicelist, default=0): + """ Return an array composed of different elements of choicelist + depending on the list of conditions. + + condlist is a list of condition arrays containing ones or zeros + + choicelist is a list of choice arrays (of the "same" size as the + arrays in condlist). The result array has the "same" size as the + arrays in choicelist. If condlist is [c0, ..., cN-1] then choicelist + must be of length N. The elements of the choicelist can then be + represented as [v0, ..., vN-1]. The default choice if none of the + conditions are met is given as the default argument. + + The conditions are tested in order and the first one statisfied is + used to select the choice. In other words, the elements of the + output array are found from the following tree (notice the order of + the conditions matters): + + if c0: v0 + elif c1: v1 + elif c2: v2 + ... + elif cN-1: vN-1 + else: default + + Note that one of the condition arrays must be large enough to handle + the largest array in the choice list. + """ + n = len(condlist) + n2 = len(choicelist) + if n2 != n: + raise ValueError, "list of cases must be same length as list of conditions" + choicelist.insert(0, default) + S = 0 + pfac = 1 + for k in range(1, n+1): + S += k * pfac * asarray(condlist[k-1]) + if k < n: + pfac *= (1-asarray(condlist[k-1])) + # handle special case of a 1-element condition but + # a multi-element choice + if type(S) in ScalarType or max(asarray(S).shape)==1: + pfac = asarray(1) + for k in range(n2+1): + pfac = pfac + asarray(choicelist[k]) + S = S*ones(asarray(pfac).shape) + return choose(S, tuple(choicelist)) + +def _asarray1d(arr, copy=False): + """Ensure 1D array for one array. + """ + if copy: + return asarray(arr).flatten() + else: + return asarray(arr).ravel() + +def copy(a): + """Return an array copy of the given object. + """ + return array(a, copy=True) + +# Basic operations + +def gradient(f, *varargs): + """Calculate the gradient of an N-dimensional scalar function. + + Uses central differences on the interior and first differences on boundaries + to give the same shape. + + Inputs: + + f -- An N-dimensional array giving samples of a scalar function + + varargs -- 0, 1, or N scalars giving the sample distances in each direction + + Outputs: + + N arrays of the same shape as f giving the derivative of f with respect + to each dimension. + """ + N = len(f.shape) # number of dimensions + n = len(varargs) + if n==0: + dx = [1.0]*N + elif n==1: + dx = [varargs[0]]*N + elif n==N: + dx = list(varargs) + else: + raise SyntaxError, "invalid number of arguments" + + # use central differences on interior and first differences on endpoints + + print dx + outvals = [] + + # create slice objects --- initially all are [:, :, ..., :] + slice1 = [slice(None)]*N + slice2 = [slice(None)]*N + slice3 = [slice(None)]*N + + otype = f.dtypechar + if otype not in ['f', 'd', 'F', 'D']: + otype = 'd' + + for axis in range(N): + # select out appropriate parts for this dimension + out = zeros(f.shape, f.dtypechar) + slice1[axis] = slice(1, -1) + slice2[axis] = slice(2, None) + slice3[axis] = slice(None, -2) + # 1D equivalent -- out[1:-1] = (f[2:] - f[:-2])/2.0 + out[slice1] = (f[slice2] - f[slice3])/2.0 + slice1[axis] = 0 + slice2[axis] = 1 + slice3[axis] = 0 + # 1D equivalent -- out[0] = (f[1] - f[0]) + out[slice1] = (f[slice2] - f[slice3]) + slice1[axis] = -1 + slice2[axis] = -1 + slice3[axis] = -2 + # 1D equivalent -- out[-1] = (f[-1] - f[-2]) + out[slice1] = (f[slice2] - f[slice3]) + + # divide by step size + outvals.append(out / dx[axis]) + + # reset the slice object in this dimension to ":" + slice1[axis] = slice(None) + slice2[axis] = slice(None) + slice3[axis] = slice(None) + + if N == 1: + return outvals[0] + else: + return outvals + + +def diff(a, n=1, axis=-1): + """Calculate the nth order discrete difference along given axis. + """ + if n==0: + return a + if n<0: + raise ValueError, 'order must be non-negative but got ' + `n` + a = asarray(a) + nd = len(a.shape) + slice1 = [slice(None)]*nd + slice2 = [slice(None)]*nd + slice1[axis] = slice(1, None) + slice2[axis] = slice(None, -1) + slice1 = tuple(slice1) + slice2 = tuple(slice2) + if n > 1: + return diff(a[slice1]-a[slice2], n-1, axis=axis) + else: + return a[slice1]-a[slice2] + +def angle(z, deg=0): + """Return the angle of the complex argument z. + """ + if deg: + fact = 180/pi + else: + fact = 1.0 + z = asarray(z) + if (issubclass(z.dtype, _nx.complexfloating)): + zimag = z.imag + zreal = z.real + else: + zimag = 0 + zreal = z + return arctan2(zimag, zreal) * fact + +def unwrap(p, discont=pi, axis=-1): + """Unwrap radian phase p by changing absolute jumps greater than + 'discont' to their 2*pi complement along the given axis. + """ + p = asarray(p) + nd = len(p.shape) + dd = diff(p, axis=axis) + slice1 = [slice(None, None)]*nd # full slices + slice1[axis] = slice(1, None) + ddmod = mod(dd+pi, 2*pi)-pi + _nx.putmask(ddmod, (ddmod==-pi) & (dd > 0), pi) + ph_correct = ddmod - dd; + _nx.putmask(ph_correct, abs(dd)<discont, 0) + up = array(p, copy=True, dtype='d') + up[slice1] = p[slice1] + ph_correct.cumsum(axis) + return up + +def sort_complex(a): + """ Sort 'a' as a complex array using the real part first and then + the imaginary part if the real part is equal (the default sort order + for complex arrays). This function is a wrapper ensuring a complex + return type. + """ + b = array(a,copy=True) + b.sort() + if not issubclass(b.dtype, _nx.complexfloating): + if b.dtypechar in 'bhBH': + return b.astype('F') + elif b.dtypechar == 'g': + return b.astype('G') + else: + return b.astype('D') + else: + return b + +def trim_zeros(filt, trim='fb'): + """ Trim the leading and trailing zeros from a 1D array. + + Example: + >>> import scipy + >>> a = array((0, 0, 0, 1, 2, 3, 2, 1, 0)) + >>> scipy.trim_zeros(a) + array([1, 2, 3, 2, 1]) + """ + first = 0 + trim = trim.upper() + if 'F' in trim: + for i in filt: + if i != 0.: break + else: first = first + 1 + last = len(filt) + if 'B' in trim: + for i in filt[::-1]: + if i != 0.: break + else: last = last - 1 + return filt[first:last] + +def unique(inseq): + """Return unique items from a 1-dimensional sequence. + """ + # Dictionary setting is quite fast. + set = {} + for item in inseq: + set[item] = None + return asarray(set.keys()) + +def extract(condition, arr): + """Return the elements of ravel(arr) where ravel(condition) is True + (in 1D). + + Equivalent to compress(ravel(condition), ravel(arr)). + """ + return _nx.take(ravel(arr), nonzero(ravel(condition))) + +def insert(arr, mask, vals): + """Similar to putmask arr[mask] = vals but the 1D array vals has the + same number of elements as the non-zero values of mask. Inverse of + extract. + """ + return _insert(arr, mask, vals) + +def nansum(a, axis=-1): + """Sum the array over the given axis, treating NaNs as 0. + """ + y = array(a) + if not issubclass(y.dtype, _nx.integer): + y[isnan(a)] = 0 + return y.sum(axis) + +def nanmin(a, axis=-1): + """Find the minimium over the given axis, ignoring NaNs. + """ + y = array(a) + if not issubclass(y.dtype, _nx.integer): + y[isnan(a)] = _nx.inf + return y.min(axis) + +def nanargmin(a, axis=-1): + """Find the indices of the minimium over the given axis ignoring NaNs. + """ + y = array(a) + if not issubclass(y.dtype, _nx.integer): + y[isnan(a)] = _nx.inf + return y.argmin(axis) + +def nanmax(a, axis=-1): + """Find the maximum over the given axis ignoring NaNs. + """ + y = array(a) + if not issubclass(y.dtype, _nx.integer): + y[isnan(a)] = -_nx.inf + return y.max(axis) + +def nanargmax(a, axis=-1): + """Find the maximum over the given axis ignoring NaNs. + """ + y = array(a) + if not issubclass(y.dtype, _nx.integer): + y[isnan(a)] = -_nx.inf + return y.argmax(axis) + +def disp(mesg, device=None, linefeed=True): + """Display a message to the given device (default is sys.stdout) + with or without a linefeed. + """ + if device is None: + import sys + device = sys.stdout + if linefeed: + device.write('%s\n' % mesg) + else: + device.write('%s' % mesg) + device.flush() + return + +class vectorize(object): + """ + vectorize(somefunction, otypes=None, doc=None) + Generalized Function class. + + Description: + + Define a vectorized function which takes nested sequence + objects or scipy arrays as inputs and returns a + scipy array as output, evaluating the function over successive + tuples of the input arrays like the python map function except it uses + the broadcasting rules of scipy. + + Input: + + somefunction -- a Python function or method + + Example: + + def myfunc(a, b): + if a > b: + return a-b + else + return a+b + + vfunc = vectorize(myfunc) + + >>> vfunc([1, 2, 3, 4], 2) + array([3, 4, 1, 2]) + + """ + def __init__(self, pyfunc, otypes='', doc=None): + try: + fcode = pyfunc.func_code + except AttributeError: + raise TypeError, "object is not a callable Python object" + + self.thefunc = pyfunc + self.ufunc = None + self.nin = fcode.co_argcount + if pyfunc.func_defaults: + self.nin_wo_defaults = self.nin - len(pyfunc.func_defaults) + else: + self.nin_wo_defaults = self.nin + self.nout = None + if doc is None: + self.__doc__ = pyfunc.__doc__ + else: + self.__doc__ = doc + if isinstance(otypes, types.StringType): + self.otypes=otypes + else: + raise ValueError, "output types must be a string" + for char in self.otypes: + if char not in typecodes['All']: + raise ValueError, "invalid typecode specified" + self.lastcallargs = 0 + + def __call__(self, *args): + # get number of outputs and output types by calling + # the function on the first entries of args + nargs = len(args) + if (nargs > self.nin) or (nargs < self.nin_wo_defaults): + raise ValueError, "mismatch between python function inputs"\ + " and received arguments" + if self.nout is None or self.otypes == '': + newargs = [] + for arg in args: + newargs.append(asarray(arg).flat[0]) + theout = self.thefunc(*newargs) + if isinstance(theout, types.TupleType): + self.nout = len(theout) + else: + self.nout = 1 + theout = (theout,) + if self.otypes == '': + otypes = [] + for k in range(self.nout): + otypes.append(asarray(theout[k]).dtypechar) + self.otypes = ''.join(otypes) + + if (self.ufunc is None) or (self.lastcallargs != nargs): + self.ufunc = frompyfunc(self.thefunc, nargs, self.nout) + self.lastcallargs = nargs + + if self.nout == 1: + return self.ufunc(*args).astype(self.otypes[0]) + else: + return tuple([x.astype(c) for x, c in zip(self.ufunc(*args), self.otypes)]) + + +def round_(a, decimals=0): + """Round 'a' to the given number of decimal places. Rounding + behaviour is equivalent to Python. + + Return 'a' if the array is not floating point. Round both the real + and imaginary parts separately if the array is complex. + """ + a = asarray(a) + if not issubclass(a.dtype, _nx.inexact): + return a + if issubclass(a.dtype, _nx.complexfloating): + return round_(a.real, decimals) + 1j*round_(a.imag, decimals) + if decimals is not 0: + decimals = asarray(decimals) + s = sign(a) + if decimals is not 0: + a = absolute(multiply(a, 10.**decimals)) + else: + a = absolute(a) + rem = a-asarray(a).astype(_nx.intp) + a = _nx.where(_nx.less(rem, 0.5), _nx.floor(a), _nx.ceil(a)) + # convert back + if decimals is not 0: + return multiply(a, s/(10.**decimals)) + else: + return multiply(a, s) + + +def cov(m,y=None, rowvar=0, bias=0): + """Estimate the covariance matrix. + + If m is a vector, return the variance. For matrices where each row + is an observation, and each column a variable, return the covariance + matrix. Note that in this case diag(cov(m)) is a vector of + variances for each column. + + cov(m) is the same as cov(m, m) + + Normalization is by (N-1) where N is the number of observations + (unbiased estimate). If bias is 1 then normalization is by N. + + If rowvar is zero, then each row is a variable with + observations in the columns. + """ + if y is None: + y = asarray(m) + else: + y = asarray(y) + m = asarray(m) + if rowvar: + m = m.transpose() + y = y.transpose() + if (m.shape[0] == 1): + m = m.transpose() + if (y.shape[0] == 1): + y = y.transpose() + N = m.shape[0] + if (y.shape[0] != N): + raise ValueError, "x and y must have the same number of observations." + m = m - m.mean(axis=0) + y = y - y.mean(axis=0) + if bias: + fact = N*1.0 + else: + fact = N-1.0 + + val = squeeze(dot(m.transpose(),y.conj()) / fact) + return val + +def corrcoef(x, y=None): + """The correlation coefficients + """ + c = cov(x, y) + d = diag(c) + return c/sqrt(multiply.outer(d,d)) + +def blackman(M): + """blackman(M) returns the M-point Blackman window. + """ + n = arange(0,M) + return 0.42-0.5*cos(2.0*pi*n/(M-1)) + 0.08*cos(4.0*pi*n/(M-1)) + +def bartlett(M): + """bartlett(M) returns the M-point Bartlett window. + """ + n = arange(0,M) + return where(less_equal(n,(M-1)/2.0),2.0*n/(M-1),2.0-2.0*n/(M-1)) + +def hanning(M): + """hanning(M) returns the M-point Hanning window. + """ + n = arange(0,M) + return 0.5-0.5*cos(2.0*pi*n/(M-1)) + +def hamming(M): + """hamming(M) returns the M-point Hamming window. + """ + n = arange(0,M) + return 0.54-0.46*cos(2.0*pi*n/(M-1)) + +def kaiser(M,beta): + """kaiser(M, beta) returns a Kaiser window of length M with shape parameter + beta. It depends on scipy.special (in full scipy) for the modified bessel + function i0. + """ + from scipy.special import i0 + n = arange(0,M) + alpha = (M-1)/2.0 + return i0(beta * sqrt(1-((n-alpha)/alpha)**2.0))/i0(beta) + +def sinc(x): + """sinc(x) returns sin(pi*x)/(pi*x) at all points of array x. + """ + y = pi* where(x == 0, 1.0e-20, x) + return sin(y)/y + +def msort(a): + b = array(a,copy=True) + b.sort(0) + return b + +def median(m): + """median(m) returns a median of m along the first dimension of m. + """ + sorted = msort(m) + if sorted.shape[0] % 2 == 1: + return sorted[int(sorted.shape[0]/2)] + else: + sorted = msort(m) + index=sorted.shape[0]/2 + return (sorted[index-1]+sorted[index])/2.0 + +def trapz(y, x=None, dx=1.0, axis=-1): + """Integrate y(x) using samples along the given axis and the composite + trapezoidal rule. If x is None, spacing given by dx is assumed. + """ + y = asarray(y) + if x is None: + d = dx + else: + d = diff(x,axis=axis) + nd = len(y.shape) + slice1 = [slice(None)]*nd + slice2 = [slice(None)]*nd + slice1[axis] = slice(1,None) + slice2[axis] = slice(None,-1) + return add.reduce(d * (y[slice1]+y[slice2])/2.0,axis) diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py new file mode 100644 index 000000000..41030af2d --- /dev/null +++ b/numpy/core/getlimits.py @@ -0,0 +1,118 @@ +""" Machine limits for Float32 and Float64 and (long double) if available... +""" + +__all__ = ['finfo'] + +from machar import MachAr +import numeric +from numeric import array + +def _frz(a): + """fix rank-0 --> rank-1""" + if a.ndim == 0: a.shape = (1,) + return a + +_convert_to_float = { + numeric.csingle: numeric.single, + numeric.complex_: numeric.float_, + numeric.clongfloat: numeric.longfloat + } + +class finfo(object): + + _finfo_cache = {} + + def __new__(cls, dtype): + obj = cls._finfo_cache.get(dtype,None) + if obj is not None: + return obj + dtypes = [dtype] + newdtype = numeric.obj2dtype(dtype) + if newdtype is not dtype: + dtypes.append(newdtype) + dtype = newdtype + if not issubclass(dtype, numeric.inexact): + raise ValueError, "data type %r not inexact" % (dtype) + obj = cls._finfo_cache.get(dtype,None) + if obj is not None: + return obj + if not issubclass(dtype, numeric.floating): + newdtype = _convert_to_float[dtype] + if newdtype is not dtype: + dtypes.append(newdtype) + dtype = newdtype + obj = cls._finfo_cache.get(dtype,None) + if obj is not None: + return obj + obj = object.__new__(cls)._init(dtype) + for dt in dtypes: + cls._finfo_cache[dt] = obj + return obj + + def _init(self, dtype): + self.dtype = dtype + if dtype is numeric.float_: + machar = MachAr(lambda v:array([v],'d'), + lambda v:_frz(v.astype('i'))[0], + lambda v:array(_frz(v)[0],'d'), + lambda v:'%24.16e' % array(_frz(v)[0],'d'), + 'scipy float precision floating point '\ + 'number') + elif dtype is numeric.single: + machar = MachAr(lambda v:array([v],'f'), + lambda v:_frz(v.astype('i'))[0], + lambda v:array(_frz(v)[0],'f'), # + lambda v:'%15.7e' % array(_frz(v)[0],'f'), + "scipy single precision floating "\ + "point number") + elif dtype is numeric.longfloat: + machar = MachAr(lambda v:array([v],'g'), + lambda v:_frz(v.astype('i'))[0], + lambda v:array(_frz(v)[0],'g'), # + lambda v:str(array(_frz(v)[0],'g')), + "scipy longfloat precision floating "\ + "point number") + else: + raise ValueError,`dtype` + + for word in ['tiny', 'precision', 'resolution','iexp', + 'maxexp','minexp','epsneg','negep', + 'machep']: + setattr(self,word,getattr(machar, word)) + self.max = machar.huge + self.min = -self.max + self.eps = machar.epsilon + self.nexp = machar.iexp + self.nmant = machar.it + self.machar = machar + self._str_tiny = machar._str_xmin + self._str_max = machar._str_xmax + self._str_epsneg = machar._str_epsneg + self._str_eps = machar._str_eps + self._str_resolution = machar._str_resolution + return self + + def __str__(self): + return '''\ +Machine parameters for %(dtype)s +--------------------------------------------------------------------- +precision=%(precision)3s resolution=%(_str_resolution)s +machep=%(machep)6s eps= %(_str_eps)s +negep =%(negep)6s epsneg= %(_str_epsneg)s +minexp=%(minexp)6s tiny= %(_str_tiny)s +maxexp=%(maxexp)6s max= %(_str_max)s +nexp =%(nexp)6s min= -max +--------------------------------------------------------------------- +''' % self.__dict__ + +if __name__ == '__main__': + f = finfo(numeric.single) + print 'single epsilon:',f.eps + print 'single tiny:',f.tiny + f = finfo(numeric.float) + print 'float epsilon:',f.eps + print 'float tiny:',f.tiny + f = finfo(numeric.longfloat) + print 'longfloat epsilon:',f.eps + print 'longfloat tiny:',f.tiny + diff --git a/numpy/core/include/scipy/arrayobject.h b/numpy/core/include/scipy/arrayobject.h new file mode 100644 index 000000000..bc9f685fc --- /dev/null +++ b/numpy/core/include/scipy/arrayobject.h @@ -0,0 +1,1484 @@ + +/* This expects the following variables to be defined (besides + the usual ones from pyconfig.h + + SIZEOF_LONG_DOUBLE -- sizeof(long double) or sizeof(double) if no + long double is present on platform. + CHAR_BIT -- number of bits in a char (usually 8) + (should be in limits.h) +*/ + +#ifndef Py_ARRAYOBJECT_H +#define Py_ARRAYOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" + +#ifdef PY_ARRAY_TYPES_PREFIX +# define CAT2(x,y) x ## y +# define CAT(x,y) CAT2(x,y) +# define NS(name) CAT(PY_ARRAY_TYPES_PREFIX, name) +# define longlong NS(longlong) +# define ulonglong NS(ulonglong) +# define Bool NS(Bool) +# define longdouble NS(longdouble) +# define byte NS(byte) +# define ubyte NS(ubyte) +# define ushort NS(ushort) +# define uint NS(uint) +# define ulong NS(ulong) +# define cfloat NS(cfloat) +# define cdouble NS(cdouble) +# define clongdouble NS(clongdouble) +# define Int8 NS(Int8) +# define UInt8 NS(UInt8) +# define Int16 NS(Int16) +# define UInt16 NS(UInt16) +# define Int32 NS(Int32) +# define UInt32 NS(UInt32) +# define Int64 NS(Int64) +# define UInt64 NS(UInt64) +# define Int128 NS(Int128) +# define UInt128 NS(UInt128) +# define Int256 NS(Int256) +# define UInt256 NS(UInt256) +# define Float16 NS(Float16) +# define Complex32 NS(Complex32) +# define Float32 NS(Float32) +# define Complex64 NS(Complex64) +# define Float64 NS(Float64) +# define Complex128 NS(Complex128) +# define Float80 NS(Float80) +# define Complex160 NS(Complex160) +# define Float96 NS(Float96) +# define Complex192 NS(Complex192) +# define Float128 NS(Float128) +# define Complex256 NS(Complex256) +# define intp NS(intp) +# define uintp NS(uintp) +#endif + +/* There are several places in the code where an array of dimensions is */ +/* allocated statically. This is the size of that static allocation. */ + +#define MAX_DIMS 40 + +/* Used for Converter Functions "O&" code in ParseTuple */ +#define PY_FAIL 0 +#define PY_SUCCEED 1 + + /* Helpful to distinguish what is installed */ +#define NDARRAY_VERSION 0x0802 + + /* Some platforms don't define bool, long long, or long double. + Handle that here. + */ + +#ifdef PY_LONG_LONG +typedef PY_LONG_LONG longlong; +typedef unsigned PY_LONG_LONG ulonglong; +# ifdef _MSC_VER +# define LONGLONG_FMT "I64d" +# define ULONGLONG_FMT "I64u" +# define LONGLONG_SUFFIX(x) (x##i64) +# define ULONGLONG_SUFFIX(x) (x##Ui64) +# else + /* #define LONGLONG_FMT "lld" Another possible variant + #define ULONGLONG_FMT "llu" + + #define LONGLONG_FMT "qd" -- BSD perhaps? + #define ULONGLONG_FMT "qu" + */ +# define LONGLONG_FMT "Ld" +# define ULONGLONG_FMT "Lu" +# define LONGLONG_SUFFIX(x) (x##LL) +# define ULONGLONG_SUFFIX(x) (x##ULL) +# endif +#else +typedef long longlong; +typedef unsigned long ulonglong; +# define LONGLONG_SUFFIX(x) (x##L) +# define ULONGLONG_SUFFIX(x) (x##UL) +#endif + +typedef unsigned char Bool; +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#if SIZEOF_LONG_DOUBLE==SIZEOF_DOUBLE + typedef double longdouble; + #define LONGDOUBLE_FMT "g" +#else + typedef long double longdouble; + #define LONGDOUBLE_FMT "Lg" +#endif + +#ifndef Py_USING_UNICODE +#define Py_UNICODE char +#endif + + +typedef signed char byte; +typedef unsigned char ubyte; +#ifndef _BSD_SOURCE +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +#endif + +typedef struct { float real, imag; } cfloat; +typedef struct { double real, imag; } cdouble; +typedef struct {longdouble real, imag;} clongdouble; + +enum PyArray_TYPES { PyArray_BOOL=0, + PyArray_BYTE, PyArray_UBYTE, + PyArray_SHORT, PyArray_USHORT, + PyArray_INT, PyArray_UINT, + PyArray_LONG, PyArray_ULONG, + PyArray_LONGLONG, PyArray_ULONGLONG, + PyArray_FLOAT, PyArray_DOUBLE, PyArray_LONGDOUBLE, + PyArray_CFLOAT, PyArray_CDOUBLE, PyArray_CLONGDOUBLE, + PyArray_OBJECT=17, + PyArray_STRING, PyArray_UNICODE, + PyArray_VOID, + PyArray_NTYPES, + PyArray_NOTYPE, + PyArray_USERDEF=256 /* leave room for characters */ +}; + + /* basetype array priority */ +#define PyArray_PRIORITY 0.0 +#define PyArray_BIG_PRIORITY 0.1 + /* default subtype priority */ +#define PyArray_SUBTYPE_PRIORITY 1.0 + + /* How many floating point types are there */ +#define PyArray_NUM_FLOATTYPE 3 + + + /* We need to match intp to a signed integer of the same size as + a pointer variable. uintp to the equivalent unsigned integer + */ + + + /* These characters correspond to the array type and the + struct module */ + + /* except 'p' -- signed integer for pointer type */ + +enum PyArray_TYPECHAR { PyArray_BOOLLTR = '?', + PyArray_BYTELTR = 'b', + PyArray_UBYTELTR = 'B', + PyArray_SHORTLTR = 'h', + PyArray_USHORTLTR = 'H', + PyArray_INTLTR = 'i', + PyArray_UINTLTR = 'I', + PyArray_LONGLTR = 'l', + PyArray_ULONGLTR = 'L', + PyArray_LONGLONGLTR = 'q', + PyArray_ULONGLONGLTR = 'Q', + PyArray_FLOATLTR = 'f', + PyArray_DOUBLELTR = 'd', + PyArray_LONGDOUBLELTR = 'g', + PyArray_CFLOATLTR = 'F', + PyArray_CDOUBLELTR = 'D', + PyArray_CLONGDOUBLELTR = 'G', + PyArray_OBJECTLTR = 'O', + PyArray_STRINGLTR = 'S', + PyArray_STRINGLTR2 = 'a', + PyArray_UNICODELTR = 'U', + PyArray_VOIDLTR = 'V', + + /* No Descriptor, just a define -- this let's + Python users specify an array of integers + large enough to hold a pointer on the platform*/ + PyArray_INTPLTR = 'p', + PyArray_UINTPLTR = 'P', + + PyArray_GENBOOLLTR ='b', + PyArray_SIGNEDLTR = 'i', + PyArray_UNSIGNEDLTR = 'u', + PyArray_FLOATINGLTR = 'f', + PyArray_COMPLEXLTR = 'c' +}; + +typedef enum { + PyArray_QUICKSORT=0, + PyArray_HEAPSORT=1, + PyArray_MERGESORT=2, + PyArray_TIMSORT=3, /* the sort Python uses -- specialized */ +} PyArray_SORTKIND; +#define PyArray_NSORTS PyArray_TIMSORT + 1 + + /* Define bit-width array types and typedefs */ + +#define MAX_INT8 127 +#define MIN_INT8 -128 +#define MAX_UINT8 255 +#define MAX_INT16 32767 +#define MIN_INT16 -32768 +#define MAX_UINT16 65535 +#define MAX_INT32 2147483647 +#define MIN_INT32 (-MAX_INT32 - 1) +#define MAX_UINT32 4294967295U +#define MAX_INT64 LONGLONG_SUFFIX(9223372036854775807) +#define MIN_INT64 (-MAX_INT64 - LONGLONG_SUFFIX(1)) +#define MAX_UINT64 ULONGLONG_SUFFIX(18446744073709551615) +#define MAX_INT128 LONGLONG_SUFFIX(85070591730234615865843651857942052864) +#define MIN_INT128 (-MAX_INT128 - LONGLONG_SUFFIX(1)) +#define MAX_UINT128 ULONGLONG_SUFFIX(170141183460469231731687303715884105728) +#define MAX_INT256 LONGLONG_SUFFIX(57896044618658097711785492504343953926634992332820282019728792003956564819967) +#define MIN_INT256 (-MAX_INT256 - LONGLONG_SUFFIX(1)) +#define MAX_UINT256 ULONGLONG_SUFFIX(115792089237316195423570985008687907853269984665640564039457584007913129639935) + + /* Need to find the number of bits for each type and + make definitions accordingly. + + C states that sizeof(char) == 1 by definition + + So, just using the sizeof keyword won't help. + + It also looks like Python itself uses sizeof(char) quite a + bit, which by definition should be 1 all the time. + + Idea: Make Use of CHAR_BIT which should tell us how many + BITS per CHARACTER + */ + + /* Include platform definitions -- These are in the C89/90 standard */ +#include <limits.h> +#define MAX_BYTE SCHAR_MAX +#define MIN_BYTE SCHAR_MIN +#define MAX_UBYTE UCHAR_MAX +#define MAX_SHORT SHRT_MAX +#define MIN_SHORT SHRT_MIN +#define MAX_USHORT USHRT_MAX +#define MAX_INT INT_MAX +#ifndef INT_MIN +#define INT_MIN (-INT_MAX - 1) +#endif +#define MIN_INT INT_MIN +#define MAX_UINT UINT_MAX +#define MAX_LONG LONG_MAX +#define MIN_LONG LONG_MIN +#define MAX_ULONG ULONG_MAX + +#define SIZEOF_LONGDOUBLE SIZEOF_LONG_DOUBLE +#define SIZEOF_LONGLONG SIZEOF_LONG_LONG +#define BITSOF_BOOL sizeof(Bool)*CHAR_BIT +#define BITSOF_CHAR CHAR_BIT +#define BITSOF_SHORT (SIZEOF_SHORT*CHAR_BIT) +#define BITSOF_INT (SIZEOF_INT*CHAR_BIT) +#define BITSOF_LONG (SIZEOF_LONG*CHAR_BIT) +#define BITSOF_LONGLONG (SIZEOF_LONGLONG*CHAR_BIT) +#define BITSOF_FLOAT (SIZEOF_FLOAT*CHAR_BIT) +#define BITSOF_DOUBLE (SIZEOF_DOUBLE*CHAR_BIT) +#define BITSOF_LONGDOUBLE (SIZEOF_LONGDOUBLE*CHAR_BIT) + + +#if BITSOF_LONG == 8 +#define PyArray_INT8 PyArray_LONG +#define PyArray_UINT8 PyArray_ULONG + typedef long Int8; + typedef unsigned long UInt8; +#define STRBITSOF_LONG "8" +#elif BITSOF_LONG == 16 +#define PyArray_INT16 PyArray_LONG +#define PyArray_UINT16 PyArray_ULONG + typedef long Int16; + typedef unsigned long UInt16; +#define STRBITSOF_LONG "16" +#elif BITSOF_LONG == 32 +#define PyArray_INT32 PyArray_LONG +#define PyArray_UINT32 PyArray_ULONG + typedef long Int32; + typedef unsigned long UInt32; +#define STRBITSOF_LONG "32" +#elif BITSOF_LONG == 64 +#define PyArray_INT64 PyArray_LONG +#define PyArray_UINT64 PyArray_ULONG + typedef long Int64; + typedef unsigned long UInt64; +#define STRBITSOF_LONG "64" +#elif BITSOF_LONG == 128 +#define PyArray_INT128 PyArray_LONG +#define PyArray_UINT128 PyArray_ULONG + typedef long Int128; + typedef unsigned long UInt128; +#define STRBITSOF_LONG "128" +#endif + +#if BITSOF_LONGLONG == 8 +# ifndef PyArray_INT8 +# define PyArray_INT8 PyArray_LONGLONG +# define PyArray_UINT8 PyArray_ULONGLONG + typedef longlong Int8; + typedef ulonglong UInt8; +# endif +# define MAX_LONGLONG MAX_INT8 +# define MIN_LONGLONG MIN_INT8 +# define MAX_ULONGLONG MAX_UINT8 +#define STRBITSOF_LONGLONG "8" +#elif BITSOF_LONGLONG == 16 +# ifndef PyArray_INT16 +# define PyArray_INT16 PyArray_LONGLONG +# define PyArray_UINT16 PyArray_ULONGLONG + typedef longlong Int16; + typedef ulonglong UInt16; +# endif +# define MAX_LONGLONG MAX_INT16 +# define MIN_LONGLONG MIN_INT16 +# define MAX_ULONGLONG MAX_UINT16 +#define STRBITSOF_LONGLONG "16" +#elif BITSOF_LONGLONG == 32 +# ifndef PyArray_INT32 +# define PyArray_INT32 PyArray_LONGLONG +# define PyArray_UINT32 PyArray_ULONGLONG + typedef longlong Int32; + typedef ulonglong UInt32; +# endif +# define MAX_LONGLONG MAX_INT32 +# define MIN_LONGLONG MIN_INT32 +# define MAX_ULONGLONG MAX_UINT32 +#define STRBITSOF_LONGLONG "32" +#elif BITSOF_LONGLONG == 64 +# ifndef PyArray_INT64 +# define PyArray_INT64 PyArray_LONGLONG +# define PyArray_UINT64 PyArray_ULONGLONG + typedef longlong Int64; + typedef ulonglong UInt64; +# endif +# define MAX_LONGLONG MAX_INT64 +# define MIN_LONGLONG MIN_INT64 +# define MAX_ULONGLONG MAX_UINT64 +#define STRBITSOF_LONGLONG "64" +#elif BITSOF_LONGLONG == 128 +# ifndef PyArray_INT128 +# define PyArray_INT128 PyArray_LONGLONG +# define PyArray_UINT128 PyArray_ULONGLONG + typedef longlong Int128; + typedef ulonglong UInt128; +# endif +# define MAX_LONGLONG MAX_INT128 +# define MIN_LONGLONG MIN_INT128 +# define MAX_ULONGLONG MAX_UINT128 +#define STRBITSOF_LONGLONG "128" +#elif BITSOF_LONGLONG == 256 +# define PyArray_INT256 PyArray_LONGLONG +# define PyArray_UINT256 PyArray_ULONGLONG + typedef longlong Int256; + typedef ulonglong UInt256; +# define MAX_LONGLONG MAX_INT256 +# define MIN_LONGLONG MIN_INT256 +# define MAX_ULONGLONG MAX_UINT256 +#define STRBITSOF_LONGLONG "256" +#endif + +#if BITSOF_INT == 8 +#ifndef PyArray_INT8 +#define PyArray_INT8 PyArray_INT +#define PyArray_UINT8 PyArray_UINT + typedef int Int8; + typedef unsigned int UInt8; +#endif +#define STRBITSOF_INT "8" +#elif BITSOF_INT == 16 +#ifndef PyArray_INT16 +#define PyArray_INT16 PyArray_INT +#define PyArray_UINT16 PyArray_UINT + typedef int Int16; + typedef unsigned int UInt16; +#endif +#define STRBITSOF_INT "16" +#elif BITSOF_INT == 32 +#ifndef PyArray_INT32 +#define PyArray_INT32 PyArray_INT +#define PyArray_UINT32 PyArray_UINT + typedef int Int32; + typedef unsigned int UInt32; +#endif +#define STRBITSOF_INT "32" +#elif BITSOF_INT == 64 +#ifndef PyArray_INT64 +#define PyArray_INT64 PyArray_INT +#define PyArray_UINT64 PyArray_UINT + typedef int Int64; + typedef unsigned int UInt64; +#endif +#define STRBITSOF_INT "64" +#elif BITSOF_INT == 128 +#ifndef PyArray_INT128 +#define PyArray_INT128 PyArray_INT +#define PyArray_UINT128 PyArray_UINT + typedef int Int128; + typedef unsigned int UInt128; +#endif +#define STRBITSOF_INT "128" +#endif + +#if BITSOF_SHORT == 8 +#ifndef PyArray_INT8 +#define PyArray_INT8 PyArray_SHORT +#define PyArray_UINT8 PyArray_USHORT + typedef short Int8; + typedef unsigned short UInt8; +#endif +#define STRBITSOF_SHORT "8" +#elif BITSOF_SHORT == 16 +#ifndef PyArray_INT16 +#define PyArray_INT16 PyArray_SHORT +#define PyArray_UINT16 PyArray_USHORT + typedef short Int16; + typedef unsigned short UInt16; +#endif +#define STRBITSOF_SHORT "16" +#elif BITSOF_SHORT == 32 +#ifndef PyArray_INT32 +#define PyArray_INT32 PyArray_SHORT +#define PyArray_UINT32 PyArray_USHORT + typedef short Int32; + typedef unsigned short UInt32; +#endif +#define STRBITSOF_SHORT "32" +#elif BITSOF_SHORT == 64 +#ifndef PyArray_INT64 +#define PyArray_INT64 PyArray_SHORT +#define PyArray_UINT64 PyArray_USHORT + typedef short Int64; + typedef unsigned short UInt64; +#endif +#define STRBITSOF_SHORT "64" +#elif BITSOF_SHORT == 128 +#ifndef PyArray_INT128 +#define PyArray_INT128 PyArray_SHORT +#define PyArray_UINT128 PyArray_USHORT + typedef short Int128; + typedef unsigned short UInt128; +#endif +#define STRBITSOF_SHORT "128" +#endif + + +#if BITSOF_CHAR == 8 +#ifndef PyArray_INT8 +#define PyArray_INT8 PyArray_BYTE +#define PyArray_UINT8 PyArray_UBYTE + typedef signed char Int8; + typedef unsigned char UInt8; +#endif +#define STRBITSOF_CHAR "8" +#elif BITSOF_CHAR == 16 +#ifndef PyArray_INT16 +#define PyArray_INT16 PyArray_BYTE +#define PyArray_UINT16 PyArray_UBYTE + typedef signed char Int16; + typedef unsigned char UInt16; +#endif +#define STRBITSOF_CHAR "16" +#elif BITSOF_CHAR == 32 +#ifndef PyArray_INT32 +#define PyArray_INT32 PyArray_BYTE +#define PyArray_UINT32 PyArray_UBYTE + typedef signed char Int32; + typedef unsigned char UInt32; +#endif +#define STRBITSOF_CHAR "32" +#elif BITSOF_CHAR == 64 +#ifndef PyArray_INT64 +#define PyArray_INT64 PyArray_BYTE +#define PyArray_UINT64 PyArray_UBYTE + typedef signed char Int64; + typedef unsigned char UInt64; +#endif +#define STRBITSOF_CHAR "64" +#elif BITSOF_CHAR == 128 +#ifndef PyArray_INT128 +#define PyArray_INT128 PyArray_BYTE +#define PyArray_UINT128 PyArray_UBYTE + typedef signed char Int128; + typedef unsigned char UInt128; +#endif +#define STRBITSOF_CHAR "128" +#endif + + + +#if BITSOF_DOUBLE == 16 +#define STRBITSOF_DOUBLE "16" +#define STRBITSOF_CDOUBLE "32" +#ifndef PyArray_FLOAT16 +#define PyArray_FLOAT16 PyArray_DOUBLE +#define PyArray_COMPLEX32 PyArray_CDOUBLE + typedef double Float16; + typedef cdouble Complex32; +#endif +#elif BITSOF_DOUBLE == 32 +#define STRBITSOF_DOUBLE "32" +#define STRBITSOF_CDOUBLE "64" +#ifndef PyArray_FLOAT32 +#define PyArray_FLOAT32 PyArray_DOUBLE +#define PyArray_COMPLEX64 PyArray_CDOUBLE + typedef double Float32; + typedef cdouble Complex64; +#endif +#elif BITSOF_DOUBLE == 64 +#define STRBITSOF_DOUBLE "64" +#define STRBITSOF_CDOUBLE "128" +#ifndef PyArray_FLOAT64 +#define PyArray_FLOAT64 PyArray_DOUBLE +#define PyArray_COMPLEX128 PyArray_CDOUBLE + typedef double Float64; + typedef cdouble Complex128; +#endif +#elif BITSOF_DOUBLE == 80 +#define STRBITSOF_DOUBLE "80" +#define STRBITSOF_CDOUBLE "160" +#ifndef PyArray_FLOAT80 +#define PyArray_FLOAT80 PyArray_DOUBLE +#define PyArray_COMPLEX160 PyArray_CDOUBLE + typedef double Float80; + typedef cdouble Complex160; +#endif +#elif BITSOF_DOUBLE == 96 +#define STRBITSOF_DOUBLE "96" +#define STRBITSOF_CDOUBLE "192" +#ifndef PyArray_FLOAT96 +#define PyArray_FLOAT96 PyArray_DOUBLE +#define PyArray_COMPLEX192 PyArray_CDOUBLE + typedef double Float96; + typedef cdouble Complex192; +#endif +#elif BITSOF_DOUBLE == 128 +#define STRBITSOF_DOUBLE "128" +#define STRBITSOF_CDOUBLE "256" +#ifndef PyArray_FLOAT128 +#define PyArray_FLOAT128 PyArray_DOUBLE +#define PyArray_COMPLEX256 PyArray_CDOUBLE + typedef double Float128; + typedef cdouble Complex256; +#endif +#endif + + + +#if BITSOF_FLOAT == 16 +#define STRBITSOF_FLOAT "16" +#define STRBITSOF_CFLOAT "32" +#ifndef PyArray_FLOAT16 +#define PyArray_FLOAT16 PyArray_FLOAT +#define PyArray_COMPLEX32 PyArray_CFLOAT + typedef float Float16; + typedef cfloat Complex32; +#endif +#elif BITSOF_FLOAT == 32 +#define STRBITSOF_FLOAT "32" +#define STRBITSOF_CFLOAT "64" +#ifndef PyArray_FLOAT32 +#define PyArray_FLOAT32 PyArray_FLOAT +#define PyArray_COMPLEX64 PyArray_CFLOAT + typedef float Float32; + typedef cfloat Complex64; +#endif +#elif BITSOF_FLOAT == 64 +#define STRBITSOF_FLOAT "64" +#define STRBITSOF_CFLOAT "128" +#ifndef PyArray_FLOAT64 +#define PyArray_FLOAT64 PyArray_FLOAT +#define PyArray_COMPLEX128 PyArray_CFLOAT + typedef float Float64; + typedef cfloat Complex128; +#endif +#elif BITSOF_FLOAT == 80 +#define STRBITSOF_FLOAT "80" +#define STRBITSOF_CFLOAT "160" +#ifndef PyArray_FLOAT80 +#define PyArray_FLOAT80 PyArray_FLOAT +#define PyArray_COMPLEX160 PyArray_CFLOAT + typedef float Float80; + typedef cfloat Complex160; +#endif +#elif BITSOF_FLOAT == 96 +#define STRBITSOF_FLOAT "96" +#define STRBITSOF_CFLOAT "192" +#ifndef PyArray_FLOAT96 +#define PyArray_FLOAT96 PyArray_FLOAT +#define PyArray_COMPLEX192 PyArray_CFLOAT + typedef float Float96; + typedef cfloat Complex192; +#endif +#elif BITSOF_FLOAT == 128 +#define STRBITSOF_FLOAT "128" +#define STRBITSOF_CFLOAT "256" +#ifndef PyArray_FLOAT128 +#define PyArray_FLOAT128 PyArray_FLOAT +#define PyArray_COMPLEX256 PyArray_CFLOAT + typedef float Float128; + typedef cfloat Complex256; +#endif +#endif + + +#if BITSOF_LONGDOUBLE == 16 +#define STRBITSOF_LONGDOUBLE "16" +#define STRBITSOF_CLONGDOUBLE "32" +#ifndef PyArray_FLOAT16 +#define PyArray_FLOAT16 PyArray_LONGDOUBLE +#define PyArray_COMPLEX32 PyArray_CLONGDOUBLE + typedef longdouble Float16; + typedef clongdouble Complex32; +#endif +#elif BITSOF_LONGDOUBLE == 32 +#define STRBITSOF_LONGDOUBLE "32" +#define STRBITSOF_CLONGDOUBLE "64" +#ifndef PyArray_FLOAT32 +#define PyArray_FLOAT32 PyArray_LONGDOUBLE +#define PyArray_COMPLEX64 PyArray_CLONGDOUBLE + typedef longdouble Float32; + typedef clongdouble Complex64; +#endif +#elif BITSOF_LONGDOUBLE == 64 +#define STRBITSOF_LONGDOUBLE "64" +#define STRBITSOF_CLONGDOUBLE "128" +#ifndef PyArray_FLOAT64 +#define PyArray_FLOAT64 PyArray_LONGDOUBLE +#define PyArray_COMPLEX128 PyArray_CLONGDOUBLE + typedef longdouble Float64; + typedef clongdouble Complex128; +#endif +#elif BITSOF_LONGDOUBLE == 80 +#define STRBITSOF_LONGDOUBLE "80" +#define STRBITSOF_CLONGDOUBLE "160" +#ifndef PyArray_FLOAT80 +#define PyArray_FLOAT80 PyArray_LONGDOUBLE +#define PyArray_COMPLEX160 PyArray_CLONGDOUBLE + typedef longdouble Float80; + typedef clongdouble Complex160; +#endif +#elif BITSOF_LONGDOUBLE == 96 +#define STRBITSOF_LONGDOUBLE "96" +#define STRBITSOF_CLONGDOUBLE "192" +#ifndef PyArray_FLOAT96 +#define PyArray_FLOAT96 PyArray_LONGDOUBLE +#define PyArray_COMPLEX192 PyArray_CLONGDOUBLE + typedef longdouble Float96; + typedef clongdouble Complex192; +#endif +#elif BITSOF_LONGDOUBLE == 128 +#define STRBITSOF_LONGDOUBLE "128" +#define STRBITSOF_CLONGDOUBLE "256" +#ifndef PyArray_FLOAT128 +#define PyArray_FLOAT128 PyArray_LONGDOUBLE +#define PyArray_COMPLEX256 PyArray_CLONGDOUBLE + typedef longdouble Float128; + typedef clongdouble Complex256; +#endif +#elif BITSOF_LONGDOUBLE == 256 +#define STRBITSOF_LONGDOUBLE "256" +#define STRBITSOF_CLONGDOUBLE "512" +#define PyArray_FLOAT256 PyArray_LONGDOUBLE +#define PyArray_COMPLEX512 PyArray_CLONGDOUBLE + typedef longdouble Float256; + typedef clongdouble Complex512; +#endif + + + /* End of typedefs for numarray style bit-width names */ + +/* This is to typedef Intp to the appropriate pointer size for this platform. + * Py_intptr_t, Py_uintptr_t are defined in pyport.h. */ +typedef Py_intptr_t intp; +typedef Py_uintptr_t uintp; + +#define INTP_FMT "d" + +#if SIZEOF_PY_INTPTR_T == SIZEOF_INT + #define PyArray_INTP PyArray_INT + #define PyArray_UINTP PyArray_UINT + #define PyIntpArrType_Type PyIntArrType_Type + #define PyUIntpArrType_Type PyUIntArrType_Type + #define MAX_INTP MAX_INT + #define MIN_INTP MIN_INT + #define MAX_UINTP MAX_UINT +#elif SIZEOF_PY_INTPTR_T == SIZEOF_LONG + #define PyArray_INTP PyArray_LONG + #define PyArray_UINTP PyArray_ULONG + #define PyIntpArrType_Type PyLongArrType_Type + #define PyUIntpArrType_Type PyULongArrType_Type + #define MAX_INTP MAX_LONG + #define MIN_INTP MIN_LONG + #define MAX_UINTP MAX_ULONG + #undef INTP_FMT + #define INTP_FMT "ld" +#elif defined(PY_LONG_LONG) && (SIZEOF_PY_INTPTR_T == SIZEOF_LONG_LONG) + #define PyArray_INTP PyArray_LONGLONG + #define PyArray_UINTP PyArray_ULONGLONG + #define PyIntpArrType_Type PyLongLongArrType_Type + #define PyUIntpArrType_Type PyULongLongArrType_Type + #define MAX_INTP MAX_LONGLONG + #define MIN_INTP MIN_LONGLONG + #define MAX_UINTP MAX_ULONGLONG + #undef INTP_FMT + #define INTP_FMT "Ld" +#endif + +#define ERR(str) fprintf(stderr, #str); fflush(stderr); +#define ERR2(str) fprintf(stderr, str); fflush(stderr); + + /* Macros to define how array, and dimension/strides data is + allocated. + */ + + /* Data buffer */ +#define PyDataMem_NEW(size) ((char *)malloc(size)) + /* #define PyArrayMem_NEW(size) PyMem_NEW(char, size)*/ +#define PyDataMem_FREE(ptr) free(ptr) + /* #define PyArrayMem_FREE(ptr) PyMem_Free(ptr) */ +#define PyDataMem_RENEW(ptr,size) ((char *)realloc(ptr,size)) + +#define PyArray_USE_PYMEM 0 + +#if PyArray_USE_PYMEM == 1 +#define _pya_malloc PyObject_Malloc +#define _pya_free PyObject_Free +#define _pya_realloc PyObject_Realloc +#else +#define _pya_malloc malloc +#define _pya_free free +#define _pya_realloc realloc +#endif + +/* Dimensions and strides */ +#define PyDimMem_NEW(size) ((intp *)_pya_malloc(size*sizeof(intp))) +#define PyDimMem_FREE(ptr) _pya_free(ptr) +#define PyDimMem_RENEW(ptr,size) ((intp *)_pya_realloc(ptr,size*sizeof(intp))) + + + /* These must deal with unaligned and swapped data if necessary */ +typedef PyObject * (PyArray_GetItemFunc) (void *, void *); +typedef int (PyArray_SetItemFunc)(PyObject *, void *, void *); + +typedef void (PyArray_CopySwapNFunc)(void *, void *, intp, int, int); +typedef void (PyArray_CopySwapFunc)(void *, void *, int, int); +typedef Bool (PyArray_NonzeroFunc)(void *, void *); + + + /* These assume aligned and notswapped data -- a buffer will be + used before or contiguous data will be obtained + */ +typedef int (PyArray_CompareFunc)(const void *, const void *, void *); +typedef int (PyArray_ArgFunc)(void*, intp, intp*, void *); +typedef void (PyArray_DotFunc)(void *, intp, void *, intp, void *, intp, + void *); +typedef void (PyArray_VectorUnaryFunc)(void *, void *, intp, void *, void *); +typedef int (PyArray_ScanFunc)(FILE *, void *, void *, void *); + +typedef int (PyArray_FillFunc)(void *, intp, void *); + +typedef int (PyArray_SortFunc)(void *, intp, void *); +typedef int (PyArray_ArgSortFunc)(void *, intp *, intp, void *); + +typedef struct { + intp *ptr; + int len; +} PyArray_Dims; + +typedef struct { + /* Functions to cast to all other standard types*/ + PyArray_VectorUnaryFunc *cast[PyArray_NTYPES]; + + /* Functions to get and set items with standard + Python types -- not array scalars */ + PyArray_GetItemFunc *getitem; + PyArray_SetItemFunc *setitem; + + /* Function to compare items */ + PyArray_CompareFunc *compare; + + /* Function to select largest */ + PyArray_ArgFunc *argmax; + + /* Function to compute dot product */ + PyArray_DotFunc *dotfunc; + + /* Function to scan an ASCII file and + place a single value plus possible separator */ + PyArray_ScanFunc *scanfunc; + + /* Copy and/or swap data. Memory areas may not overlap */ + /* Use memmove first if they might */ + PyArray_CopySwapNFunc *copyswapn; + PyArray_CopySwapFunc *copyswap; + + /* Function to determine if data is zero or not */ + PyArray_NonzeroFunc *nonzero; + + /* Used for arange */ + PyArray_FillFunc *fill; + + /* Sorting functions */ + PyArray_SortFunc *sort[PyArray_NSORTS]; + PyArray_ArgSortFunc *argsort[PyArray_NSORTS]; + +} PyArray_ArrFuncs; + + +typedef struct { + PyObject_HEAD + PyTypeObject *typeobj; /* the type object representing an + intance of this type */ + char kind; /* kind for this type */ + char type; /* unique-character representing this type */ + char byteorder; /* '>' (big), '<' (little), '|' + (not-applicable), or '=' (native). */ + int type_num; /* number representing this type */ + int elsize; /* element size for this type */ + int alignment; /* alignment needed for this type */ + struct _arr_descr \ + *subarray; /* Non-NULL if this type is + is an array (C-contiguous) + of some other type + */ + PyObject *fields; /* The fields dictionary for this type */ + /* For statically defined descr this + is always Py_None */ + + PyArray_ArrFuncs *f; /* a table of functions specific for each + basic data descriptor */ +} PyArray_Descr; + +typedef struct _arr_descr { + PyArray_Descr *base; + PyObject *shape; /* a tuple */ +} PyArray_ArrayDescr; + + +typedef struct PyArrayObject { + PyObject_HEAD + char *data; /* pointer to raw data buffer */ + int nd; /* number of dimensions, also called ndim */ + intp *dimensions; /* size in each dimension */ + intp *strides; /* bytes to jump to get to the + next element in each dimension */ + PyObject *base; /* This object should be decref'd + upon deletion of array */ + /* For views it points to the original array */ + /* For creation from buffer object it points + to an object that shold be decref'd on + deletion */ + /* For UPDATEIFCOPY flag this is an array + to-be-updated upon deletion of this one */ + PyArray_Descr *descr; /* Pointer to type structure */ + int flags; /* Flags describing array -- see below*/ + PyObject *weakreflist; /* For weakreferences */ +} PyArrayObject; + +#define fortran fortran_ /* For some compilers */ + +/* Mirrors buffer object to ptr */ + +typedef struct { + PyObject_HEAD + PyObject *base; + void *ptr; + intp len; + int flags; +} PyArray_Chunk; + +/* Array flags */ +#define CONTIGUOUS 1 /* means c-style contiguous (last index + varies the fastest) data elements right + after each other. */ + + /* All 0-d arrays are CONTIGUOUS and FORTRAN + contiguous. If a 1-d array is CONTIGUOUS + it is also FORTRAN contiguous + */ + +#define FORTRAN 2 /* set if array is a contiguous Fortran array */ + /* first index varies the fastest in memory + (strides array is reverse of C-contiguous + array)*/ + +#define OWNDATA 4 +#define OWN_DATA OWNDATA + + /* array never has these three set -- FromAny flags only */ +#define FORCECAST 0x010 +#define ENSURECOPY 0x020 +#define ENSUREARRAY 0x040 + +#define ALIGNED 0x100 +#define WRITEABLE 0x400 + + + /* If this flags is set, then base contains a pointer to + an array of the same size that should be updated with the + current contents of this array when this array is deallocated + */ +#define UPDATEIFCOPY 0x1000 + + +/* Size of internal buffers used for alignment */ +#define PyArray_BUFSIZE 10000 +#define PyArray_MIN_BUFSIZE 5 +#define PyArray_MAX_BUFSIZE 100000000 + +#define BEHAVED_FLAGS ALIGNED | WRITEABLE +#define CARRAY_FLAGS CONTIGUOUS | BEHAVED_FLAGS +#define CARRAY_FLAGS_RO CONTIGUOUS | ALIGNED +#define FARRAY_FLAGS FORTRAN | BEHAVED_FLAGS +#define FARRAY_FLAGS_RO FORTRAN | ALIGNED +#define DEFAULT_FLAGS CARRAY_FLAGS + +#define UPDATE_ALL_FLAGS CONTIGUOUS | FORTRAN | ALIGNED + + + +/* + * C API: consists of Macros and functions. The MACROS are defined here. + */ + + +#define PyArray_CHKFLAGS(m, FLAGS) \ + ((((PyArrayObject *)(m))->flags & (FLAGS)) == (FLAGS)) +#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS(m, CONTIGUOUS) +#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS(m, WRITEABLE) +#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS(m, ALIGNED) + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + + /* Useful if a and b have to be evaluated. */ + +#define tMAX(a,b,typ) {typ _x_=(a); typ _y_=(b); _x_>_y_ ? _x_ : _y_} +#define tMIN(a,b,typ) {typ _x_=(a); typ _y_=(b); _x_<_y_ ? _x_ : _y_} + +#if defined(ALLOW_THREADS) +#define BEGIN_THREADS_DEF PyThreadState *_save; +#define BEGIN_THREADS _save = PyEval_SaveThread(); +#define END_THREADS PyEval_RestoreThread(_save); +#define ALLOW_C_API_DEF PyGILState_STATE __save__; +#define ALLOW_C_API __save__ = PyGILState_Ensure(); +#define DISABLE_C_API PyGILState_Release(__save__); +#else +#define BEGIN_THREADS_DEF +#define BEGIN_THREADS +#define END_THREADS +#define ALLOW_C_API_DEF +#define ALLOW_C_API +#define DISABLE_C_API +#endif + +typedef struct { + PyObject_HEAD + int nd_m1; /* number of dimensions - 1 */ + intp index, size; + intp coordinates[MAX_DIMS];/* N-dimensional loop */ + intp dims_m1[MAX_DIMS]; /* ao->dimensions - 1 */ + intp strides[MAX_DIMS]; /* ao->strides or fake */ + intp backstrides[MAX_DIMS];/* how far to jump back */ + intp factors[MAX_DIMS]; /* shape factors */ + PyArrayObject *ao; + char *dataptr; /* pointer to current item*/ + Bool contiguous; +} PyArrayIterObject; + + +/* Iterator API */ +#define PyArrayIter_Check(op) PyObject_TypeCheck(op, &PyArrayIter_Type) + +#define PyArray_ITER_RESET(it) { \ + it->index = 0; \ + it->dataptr = it->ao->data; \ + memset(it->coordinates, 0, (it->nd_m1+1)*sizeof(intp)); \ +} + + +#define PyArray_ITER_NEXT(it) { \ + it->index++; \ + if (it->contiguous) it->dataptr += it->ao->descr->elsize; \ + else { \ + int _i_; \ + for (_i_ = it->nd_m1; _i_ >= 0; _i_--) { \ + if (it->coordinates[_i_] < \ + it->dims_m1[_i_]) { \ + it->coordinates[_i_]++; \ + it->dataptr += it->strides[_i_]; \ + break; \ + } \ + else { \ + it->coordinates[_i_] = 0; \ + it->dataptr -= it->backstrides[_i_]; \ + } \ + } \ + } \ +} + +#define PyArray_ITER_GOTO(it, destination) { \ + int _i_; \ + it->index = 0; \ + it->dataptr = it->ao->data; \ + for (_i_ = it->nd_m1; _i_>=0; _i_--) { \ + it->dataptr += destination[_i_] * \ + it->strides[_i_]; \ + it->coordinates[_i_] = destination[_i_]; \ + it->index += destination[_i_] * \ + ( _i_==it->nd_m1 ? 1 : \ + it->dims_m1[i+1]+1) ; \ + } \ + } + +#define PyArray_ITER_GOTO1D(it, ind) { \ + int _i_; \ + intp _lind_ = (intp) (ind); \ + it->index = _lind_; \ + if (it->contiguous) \ + it->dataptr = it->ao->data + (ind) * \ + it->ao->descr->elsize; \ + else { \ + it->dataptr = it->ao->data; \ + for (_i_ = 0; _i_<=it->nd_m1; _i_++) { \ + it->dataptr += (_lind_ / it->factors[_i_]) \ + * it->strides[_i_]; \ + _lind_ %= it->factors[_i_]; \ + } \ + } \ +} + +#define PyArray_ITER_DATA(it) ((PyArrayIterObject *)it)->dataptr + + +/* + Any object passed to PyArray_Broadcast must be binary compatible with + this structure. +*/ + +typedef struct { + PyObject_HEAD + + int numiter; /* number of iters */ + intp size; /* broadcasted size */ + intp index; /* current index */ + int nd; /* number of dims */ + intp dimensions[MAX_DIMS]; /* dimensions */ + PyArrayIterObject *iters[MAX_DIMS]; /* iterators */ +} PyArrayMultiIterObject; + +#define PyArray_MultiIter_RESET(multi) { \ + int _mi_; \ + PyArrayMultiIterObject *_mul_ = (multi); \ + _mul_->index = 0; \ + for (_mi_ = 0; _mi_ < _mul_->numiter; _mi_++) { \ + PyArray_ITER_RESET(_mul_->iters[_mi_]); \ + } \ + } + +#define PyArray_MultiIter_NEXT(multi) { \ + int _mi_; \ + PyArrayMultiIterObject *_mul_ = (multi); \ + _mul_->index += 1; \ + for (_mi_=0; _mi_<_mul_->numiter; _mi_++) { \ + PyArray_ITER_NEXT(_mul_->iters[_mi_]); \ + } \ + } + +#define PyArray_MultiIter_GOTO(multi, dest) { \ + int _mi_; \ + PyArrayMultiIterObject *_mul_ = (multi); \ + for (_mi_=0; _mi_<_mul_->numiter; _mi_++) { \ + PyArray_ITER_GOTO(_mul_->iters[_mi_], dest); \ + } \ + _mul_->index = _mul_->iters[0]->index; \ + } + +#define PyArray_MultiIter_GOTO1D(multi, ind) { \ + int _mi_; \ + PyArrayMultiIterObject *_mul_ = (multi); \ + for (_mi_=0; _mi_<_mul_->numiter; _mi_++) { \ + PyArray_ITER_GOTO1D(_mul_->iters[_mi_], ind); \ + } \ + _mul_->index = _mul_->iters[0]->index; \ + } + +#define PyArray_MultiIter_DATA(multi, i) \ + ((PyArrayMultiIterObject *)multi)->iters[i]->dataptr + +#define PyArray_MultiIter_SIZE(multi) \ + ((PyArrayMultiIterObject *)multi)->size; + + +/* Store the information needed for fancy-indexing over an array */ + +typedef struct { + PyObject_HEAD + /* Multi-iterator portion --- needs to be present in this order to + work with PyArray_Broadcast */ + + int numiter; /* number of index-array + iterators */ + intp size; /* size of broadcasted + result */ + intp index; /* current index */ + int nd; /* number of dims */ + intp dimensions[MAX_DIMS]; /* dimensions */ + PyArrayIterObject *iters[MAX_DIMS]; /* index object + iterators */ + PyArrayIterObject *ait; /* flat Iterator for + underlying array */ + + /* flat iterator for subspace (when numiter < nd) */ + PyArrayIterObject *subspace; + + /* if subspace iteration, then this is the array of + axes in the underlying array represented by the + index objects */ + int iteraxes[MAX_DIMS]; + /* if subspace iteration, the these are the coordinates + to the start of the subspace. + */ + intp bscoord[MAX_DIMS]; + + + PyObject *indexobj; /* reference to + creating obj */ + int view; + int consec; + char *dataptr; + +} PyArrayMapIterObject; + + +#define PyArray_NDIM(obj) (((PyArrayObject *)(obj))->nd) +#define PyArray_ISONESEGMENT(m) (PyArray_NDIM(m) == 0 || PyArray_CHKFLAGS(m, CONTIGUOUS) || \ + PyArray_CHKFLAGS(m, FORTRAN)) +#define PyArray_ISFORTRAN(m) (PyArray_CHKFLAGS(m, FORTRAN) && (PyArray_NDIM(m) > 1)) +#define FORTRAN_IF(m) ((PyArray_CHKFLAGS(m, FORTRAN) ? FORTRAN : 0)) +#define PyArray_DATA(obj) (((PyArrayObject *)(obj))->data) +#define PyArray_DIMS(obj) (((PyArrayObject *)(obj))->dimensions) +#define PyArray_STRIDES(obj) (((PyArrayObject *)(obj))->strides) +#define PyArray_DIM(obj,n) (((PyArrayObject *)(obj))->dimensions[n]) +#define PyArray_STRIDE(obj,n) (((PyArrayObject *)(obj))->strides[n]) +#define PyArray_BASE(obj) (((PyArrayObject *)(obj))->base) +#define PyArray_DESCR(obj) (((PyArrayObject *)(obj))->descr) +#define PyArray_FLAGS(obj) (((PyArrayObject *)(obj))->flags) +#define PyArray_ITEMSIZE(obj) (((PyArrayObject *)(obj))->descr->elsize) +#define PyArray_TYPE(obj) (((PyArrayObject *)(obj))->descr->type_num) +#define PyArray_GETITEM(obj,itemptr) \ + ((PyArrayObject *)(obj))->descr->getitem((char *)itemptr, \ + (PyArrayObject *)obj); +#define PyArray_SETITEM(obj,itemptr,v) \ + (obj)->descr->setitem((PyObject *)v,(char *)(itemptr), \ + (PyArrayObject *)(obj)); + + +#define PyTypeNum_ISBOOL(type) (type == PyArray_BOOL) +#define PyTypeNum_ISUNSIGNED(type) ((type == PyArray_UBYTE) || \ + (type == PyArray_USHORT) || \ + (type == PyArray_UINT) || \ + (type == PyArray_ULONG) || \ + (type == PyArray_ULONGLONG)) + +#define PyTypeNum_ISSIGNED(type) ((type == PyArray_BYTE) || \ + (type == PyArray_SHORT) || \ + (type == PyArray_INT) || \ + (type == PyArray_LONG) || \ + (type == PyArray_LONGLONG)) + +#define PyTypeNum_ISINTEGER(type) ((type >= PyArray_BYTE) && \ + (type <= PyArray_ULONGLONG)) + +#define PyTypeNum_ISFLOAT(type) ((type >= PyArray_FLOAT) && \ + (type <= PyArray_LONGDOUBLE)) + +#define PyTypeNum_ISNUMBER(type) (type <= PyArray_CLONGDOUBLE) + +#define PyTypeNum_ISSTRING(type) ((type == PyArray_UCHAR) || \ + (type == PyArray_UNICODE)) + +#define PyTypeNum_ISCOMPLEX(type) ((type >= PyArray_CFLOAT) && \ + (type <= PyArray_CLONGDOUBLE)) + +#define PyTypeNum_ISPYTHON(type) ((type == PyArray_LONG) || \ + (type == PyArray_DOUBLE) || \ + (type == PyArray_CDOUBLE) || \ + (type == PyArray_BOOL) || \ + (type == PyArray_OBJECT )) + +#define PyTypeNum_ISFLEXIBLE(type) ((type>=PyArray_STRING) && \ + (type<=PyArray_VOID)) + +#define PyTypeNum_ISUSERDEF(type) ((type >= PyArray_USERDEF) && \ + (type < PyArray_USERDEF+\ + PyArray_NUMUSERTYPES)) + +#define PyTypeNum_ISEXTENDED(type) (PyTypeNum_ISFLEXIBLE(type) || \ + PyTypeNum_ISUSERDEF(type)) + +#define PyTypeNum_ISOBJECT(type) ((type) == PyArray_OBJECT) + +#define _PyADt(o) ((PyArray_Descr *)o)->type_num +#define PyDescr_ISBOOL(obj) PyTypeNum_ISBOOL(_PyADt(obj)) +#define PyDescr_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(_PyADt(obj)) +#define PyDescr_ISSIGNED(obj) PyTypeNum_ISSIGNED(_PyADt(obj)) +#define PyDescr_ISINTEGER(obj) PyTypeNum_ISINTEGER(_PyADt(obj)) +#define PyDescr_ISFLOAT(obj) PyTypeNum_ISFLOAT(_PyADt(obj)) +#define PyDescr_ISNUMBER(obj) PyTypeNum_ISNUMBER(_PyADt(obj)) +#define PyDescr_ISSTRING(obj) PyTypeNum_ISSTRING(_PyADt(obj)) +#define PyDescr_ISCOMPLEX(obj) PyTypeNum_ISCOMPLEX(_PyADt(obj)) +#define PyDescr_ISPYTHON(obj) PyTypeNum_ISPYTHON(_PyADt(obj)) +#define PyDescr_ISFLEXIBLE(obj) PyTypeNum_ISFLEXIBLE(_PyADt(obj)) +#define PyDescr_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(_PyADt(obj)) +#define PyDescr_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(_PyADt(obj)) +#define PyDescr_ISOBJECT(obj) PyTypeNum_ISOBJECT(_PyADt(obj)) +#undef _PyAD + +#define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj)) +#define PyArray_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(PyArray_TYPE(obj)) +#define PyArray_ISSIGNED(obj) PyTypeNum_ISSIGNED(PyArray_TYPE(obj)) +#define PyArray_ISINTEGER(obj) PyTypeNum_ISINTEGER(PyArray_TYPE(obj)) +#define PyArray_ISFLOAT(obj) PyTypeNum_ISFLOAT(PyArray_TYPE(obj)) +#define PyArray_ISNUMBER(obj) PyTypeNum_ISNUMBER(PyArray_TYPE(obj)) +#define PyArray_ISSTRING(obj) PyTypeNum_ISSTRING(PyArray_TYPE(obj)) +#define PyArray_ISCOMPLEX(obj) PyTypeNum_ISCOMPLEX(PyArray_TYPE(obj)) +#define PyArray_ISPYTHON(obj) PyTypeNum_ISPYTHON(PyArray_TYPE(obj)) +#define PyArray_ISFLEXIBLE(obj) PyTypeNum_ISFLEXIBLE(PyArray_TYPE(obj)) +#define PyArray_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(PyArray_TYPE(obj)) +#define PyArray_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(PyArray_TYPE(obj)) +#define PyArray_ISOBJECT(obj) PyTypeNum_ISOBJECT(PyArray_TYPE(obj)) + +#define PyArray_LITTLE '<' +#define PyArray_BIG '>' +#define PyArray_NATIVE '=' +#define PyArray_SWAP 's' +#define PyArray_IGNORE '|' + +#ifdef WORDS_BIGENDIAN +#define PyArray_NATBYTE PyArray_BIG +#define PyArray_OPPBYTE PyArray_LITTLE +#else +#define PyArray_NATBYTE PyArray_LITTLE +#define PyArray_OPPBYTE PyArray_BIG +#endif + +#define PyArray_ISNBO(arg) ((arg) != PyArray_OPPBYTE) +#define PyArray_IsNativeByteOrder PyArray_ISNBO +#define PyArray_ISNOTSWAPPED(m) PyArray_ISNBO(PyArray_DESCR(m)->byteorder) + +#define PyArray_FLAGSWAP(m, flags) (PyArray_CHKFLAGS(m, flags) && \ + PyArray_ISNOTSWAPPED(m)) +#define PyArray_ISCARRAY(m) PyArray_FLAGSWAP(m, CARRAY_FLAGS) +#define PyArray_ISCARRAY_RO(m) PyArray_FLAGSWAP(m, CARRAY_FLAGS_RO) +#define PyArray_ISFARRAY(m) PyArray_FLAGSWAP(m, FARRAY_FLAGS) +#define PyArray_ISFARRAY_RO(m) PyArray_FLAGSWAP(m, FARRAY_FLAGS_RO) +#define PyArray_ISBEHAVED(m) PyArray_FLAGSWAP(m, BEHAVED_FLAGS) +#define PyArray_ISBEHAVED_RO(m) PyArray_FLAGSWAP(m, ALIGNED) + + +typedef struct { + int version; /* contains the integer 2 as a sanity check */ + int nd; /* number of dimensions */ + char typekind; /* kind in array --- character code of typestr */ + int itemsize; /* size of each element */ + int flags; /* how should be data interpreted */ + intp *shape; /* A length-nd array of shape information */ + intp *strides; /* A length-nd array of stride information */ + void *data; /* A pointer to the first element of the array */ +} PyArrayInterface; +#define NOTSWAPPED 0x200 /* part of the array interface */ + + /* Includes the "function" C-API -- these are all stored in a + list of pointers --- one for each file + The two lists are concatenated into one in multiarray. + + They are available as import_array() + */ + + +#include "__multiarray_api.h" + + + /* C-API that requries previous API to be defined */ + +#define PyArray_DescrCheck(op) ((op)->ob_type == &PyArrayDescr_Type) + +#define PyArray_Check(op) ((op)->ob_type == &PyArray_Type || \ + PyObject_TypeCheck((op), &PyBigArray_Type)) +#define PyBigArray_CheckExact(op) ((op)->ob_type == &PyBigArray_Type) +#define PyArray_CheckExact(op) ((op)->ob_type == &PyArray_Type) + +#define PyArray_IsZeroDim(op) (PyArray_Check(op) && (PyArray_NDIM(op) == 0)) +#define PyArray_IsScalar(obj, cls) \ + (PyObject_TypeCheck((obj), &Py##cls##ArrType_Type)) +#define PyArray_CheckScalar(m) (PyArray_IsScalar(m, Generic) || \ + PyArray_IsZeroDim(m)) +#define PyArray_IsPythonScalar(obj) \ + (PyInt_Check(obj) || PyFloat_Check(obj) || PyComplex_Check(obj) || \ + PyLong_Check(obj) || PyBool_Check(obj) || PyString_Check(obj) || \ + PyUnicode_Check(obj)) +#define PyArray_IsAnyScalar(obj) \ + (PyArray_IsScalar(obj, Generic) || PyArray_IsPythonScalar(obj)) +#define PyArray_CheckAnyScalar(obj) (PyArray_CheckScalar(obj) || \ + PyArray_IsPythonScalar(obj)) + +#define PyArray_GETCONTIGUOUS(m) (PyArray_ISCONTIGUOUS(m) ? Py_INCREF(m), m : \ + (PyArrayObject *)(PyArray_Copy(m))) + +#define PyArray_SIZE(m) PyArray_MultiplyList(PyArray_DIMS(m), PyArray_NDIM(m)) +#define PyArray_NBYTES(m) (PyArray_ITEMSIZE(m) * PyArray_SIZE(m)) +#define PyArray_FROM_O(m) PyArray_FromAny(m, NULL, 0, 0, 0) +#define PyArray_FROM_OF(m,flags) PyArray_FromAny(m, NULL, 0, 0, flags) +#define PyArray_FROM_OT(m,type) PyArray_FromAny(m, PyArray_DescrFromType(type),\ + 0, 0, 0); +#define PyArray_FROM_OTF(m, type, flags) \ + PyArray_FromAny(m, PyArray_DescrFromType(type), 0, 0, flags) +#define PyArray_FROMANY(m, type, min, max, flags) \ + PyArray_FromAny(m, PyArray_DescrFromType(type), min, max, flags) + +#define PyArray_FILLWBYTE(obj, val) memset(PyArray_DATA(obj), (val), PyArray_NBYTES(obj)) + +#define REFCOUNT(obj) (((PyObject *)(obj))->ob_refcnt) +#define MAX_ELSIZE 2*SIZEOF_LONGDOUBLE + + +#define PyArray_ContiguousFromAny(op, type, min_depth, max_depth) \ + PyArray_FromAny(op, PyArray_DescrFromType(type), min_depth, \ + max_depth, DEFAULT_FLAGS) + +#define PyArray_EquivArrTypes(a1, a2) \ + PyArray_EquivTypes(PyArray_DESCR(a1), PyArray_DESCR(a2)) +#define PyArray_EquivTypenums(typenum1, typenum2) \ + PyArray_EquivTypes(PyArray_DescrFromType(typenum1), \ + PyArray_DescrFromType(typenum2)) + +#define PyArray_EquivByteorders(b1, b2) \ + ((b1 == b2) || (PyArray_ISNBO(b1) == PyArray_ISNBO(b2))) + +#define PyArray_SimpleNew(nd, dims, typenum) \ + PyArray_New(&PyArray_Type, nd, dims, typenum, NULL, NULL, 0, 0, NULL) +#define PyArray_SimpleNewFromData(nd, dims, typenum, data) \ + PyArray_New(&PyArray_Type, nd, dims, typenum, NULL, data, 0, CARRAY_FLAGS, NULL) +#define PyArray_SimpleNewFromDescr(nd, dims, descr) \ + PyArray_NewFromDescr(&PyArray_Type, descr, nd, dims, NULL, NULL, 0, NULL) + + + /* These might be faster without the dereferencing of obj + going on inside -- of course an optimizing compiler should + inline the constants inside a for loop making it a moot point + */ + +#define PyArray_GETPTR1(obj, i) (PyArray_DATA(obj) + \ + i*PyArray_STRIDE(obj, 0)) + +#define PyArray_GETPTR2(obj, i, j) (PyArray_DATA(obj) + \ + i*PyArray_STRIDE(obj, 0) + \ + j*PyArray_STRIDE(obj, 1)) + +#define PyArray_GETPTR3(obj, i, j, k) (PyArray_DATA(obj) + \ + i*PyArray_STRIDE(obj, 0) + \ + j*PyArray_STRIDE(obj, 1) + \ + k*PyArray_STRIDE(obj, 2)) \ + +#define PyArray_GETPTR4(obj, i, j, k, l) (PyArray_DATA(obj) + \ + i*PyArray_STRIDE(obj, 0) + \ + j*PyArray_STRIDE(obj, 1) + \ + k*PyArray_STRIDE(obj, 2) + \ + l*PyArray_STRIDE(obj, 3)) + +#define PyArray_DESCR_REPLACE(descr) do { \ + PyArray_Descr *_new_; \ + _new_ = PyArray_DescrNew(descr); \ + Py_XDECREF(descr); \ + descr = _new_; \ + } while(0) + + /* Copy should always return contiguous array */ +#define PyArray_Copy(obj) PyArray_NewCopy(obj, 0) + +#define PyArray_FromObject(op, type, min_depth, max_depth) \ + PyArray_FromAny(op, PyArray_DescrFromType(type), min_depth, \ + max_depth, BEHAVED_FLAGS | ENSUREARRAY) + +#define PyArray_ContiguousFromObject(op, type, min_depth, max_depth) \ + PyArray_FromAny(op, PyArray_DescrFromType(type), min_depth, \ + max_depth, DEFAULT_FLAGS | ENSUREARRAY) + +#define PyArray_CopyFromObject(op, type, min_depth, max_depth) \ + PyArray_FromAny(op, PyArray_DescrFromType(type), min_depth, \ + max_depth, ENSURECOPY | ENSUREARRAY) + +#define PyArray_Cast(mp, type_num) \ + PyArray_CastToType(mp, PyArray_DescrFromType(type_num), 0) + + /*Compatibility with old Numeric stuff -- don't use in new code */ + +#define PyArray_FromDimsAndData(nd, d, type, data) \ + PyArray_FromDimsAndDataAndDescr(nd, d, PyArray_DescrFromType(type), \ + data) + +#define PyArray_UNSIGNED_TYPES +#define PyArray_SBYTE PyArray_BYTE +#define PyArray_CHAR PyArray_BYTE +#define PyArray_CopyArray PyArray_CopyInto +#define _PyArray_multiply_list PyArray_MultiplyIntList +#define PyArray_ISSPACESAVER(m) FALSE +#define PyScalarArray_Check PyArray_CheckScalar + +#ifdef PY_ARRAY_TYPES_PREFIX +# undef CAT +# undef CAT2 +# undef NS +# undef longlong +# undef ulonglong +# undef Bool +# undef longdouble +# undef byte +# undef ubyte +# undef ushort +# undef uint +# undef ulong +# undef cfloat +# undef cdouble +# undef clongdouble +# undef Int8 +# undef UInt8 +# undef Int16 +# undef UInt16 +# undef Int32 +# undef UInt32 +# undef Int64 +# undef UInt64 +# undef Int128 +# undef UInt128 +# undef Int256 +# undef UInt256 +# undef Float16 +# undef Complex32 +# undef Float32 +# undef Complex64 +# undef Float64 +# undef Complex128 +# undef Float80 +# undef Complex160 +# undef Float96 +# undef Complex192 +# undef Float128 +# undef Complex256 +# undef intp +# undef uintp +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_ARRAYOBJECT_H */ diff --git a/numpy/core/include/scipy/ufuncobject.h b/numpy/core/include/scipy/ufuncobject.h new file mode 100644 index 000000000..34e5ca061 --- /dev/null +++ b/numpy/core/include/scipy/ufuncobject.h @@ -0,0 +1,319 @@ +#ifndef Py_UFUNCOBJECT_H +#define Py_UFUNCOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_ARGS 40 + +typedef void (*PyUFuncGenericFunction) (char **, intp *, intp *, void *); + +typedef struct { + PyObject_HEAD + int nin, nout, nargs; + int identity; + PyUFuncGenericFunction *functions; + void **data; + int ntypes; + int check_return; + char *name, *types; + char *doc; + void *ptr; + PyObject *obj; + PyObject *userloops; +} PyUFuncObject; + +#include "arrayobject.h" + +#ifdef PY_ARRAY_TYPES_PREFIX +# define CAT2(x,y) x ## y +# define CAT(x,y) CAT2(x,y) +# define NS(name) CAT(PY_ARRAY_TYPES_PREFIX, name) +# define intp NS(intp) +#endif + +#define UFUNC_ERR_IGNORE 0 +#define UFUNC_ERR_WARN 1 +#define UFUNC_ERR_RAISE 2 +#define UFUNC_ERR_CALL 3 + + /* Python side integer mask */ + +#define UFUNC_MASK_DIVIDEBYZERO 0x03 +#define UFUNC_MASK_OVERFLOW 0x0c +#define UFUNC_MASK_UNDERFLOW 0x30 +#define UFUNC_MASK_INVALID 0xc0 + +#define UFUNC_SHIFT_DIVIDEBYZERO 0 +#define UFUNC_SHIFT_OVERFLOW 2 +#define UFUNC_SHIFT_UNDERFLOW 4 +#define UFUNC_SHIFT_INVALID 6 + + +/* platform-dependent code translates floating point + status to an integer sum of these values +*/ +#define UFUNC_FPE_DIVIDEBYZERO 1 +#define UFUNC_FPE_OVERFLOW 2 +#define UFUNC_FPE_UNDERFLOW 4 +#define UFUNC_FPE_INVALID 8 + +#define UFUNC_ERR_DEFAULT 0 /* Default error mode */ + + /* Only internal -- not exported, yet*/ +typedef struct { + /* Multi-iterator portion --- needs to be present in this order + to work with PyArray_Broadcast */ + PyObject_HEAD + int numiter; + intp size; + intp index; + int nd; + intp dimensions[MAX_DIMS]; + PyArrayIterObject *iters[MAX_ARGS]; + /* End of Multi-iterator portion */ + + /* The ufunc */ + PyUFuncObject *ufunc; + + /* The error handling */ + int errormask; /* Integer showing desired error handling */ + PyObject *errobj; /* currently a tuple with + (string, func or None) + */ + + /* Specific function and data to use */ + PyUFuncGenericFunction function; + void *funcdata; + + /* Loop method */ + int meth; + + /* Whether we need to copy to a buffer or not.*/ + int needbuffer[MAX_ARGS]; + int leftover; + int ninnerloops; + int lastdim; + + /* Whether or not to swap */ + int swap[MAX_ARGS]; + + /* Buffers for the loop */ + void *buffer[MAX_ARGS]; + int bufsize; + intp bufcnt; + void *dptr[MAX_ARGS]; + + /* For casting */ + void *castbuf[MAX_ARGS]; + PyArray_VectorUnaryFunc *cast[MAX_ARGS]; + + /* usually points to buffer but when a cast is to be + done it switches for that argument to castbuf. + */ + void *bufptr[MAX_ARGS]; + + /* Steps filled in from iters or sizeof(item) + depending on loop method. + */ + intp steps[MAX_ARGS]; + + int obj; /* This loop calls object functions */ + int notimplemented; /* The loop caused notimplemented */ + +} PyUFuncLoopObject; + +/* Could make this more clever someday */ +#define UFUNC_MAXIDENTITY 32 + +typedef struct { + PyObject_HEAD + PyArrayIterObject *it; + PyArrayObject *ret; + PyArrayIterObject *rit; /* Needed for Accumulate */ + int outsize; + intp index; + intp size; + char idptr[UFUNC_MAXIDENTITY]; + + /* The ufunc */ + PyUFuncObject *ufunc; + + /* The error handling */ + int errormask; + PyObject *errobj; + + PyUFuncGenericFunction function; + void *funcdata; + int meth; + int swap; + + void *buffer; + int bufsize; + + void *castbuf; + PyArray_VectorUnaryFunc *cast; + + void *bufptr[3]; + intp steps[3]; + + intp N; + int instrides; + int insize; + char *inptr; + + /* For copying small arrays */ + PyObject *decref; + + int obj; + +} PyUFuncReduceObject; + + +#if defined(ALLOW_THREADS) +#define LOOP_BEGIN_THREADS if (!(loop->obj)) {_save = PyEval_SaveThread();} +#define LOOP_END_THREADS if (!(loop->obj)) {PyEval_RestoreThread(_save);} +#else +#define LOOP_BEGIN_THREADS +#define LOOP_END_THREADS +#endif + +#define PyUFunc_One 1 +#define PyUFunc_Zero 0 +#define PyUFunc_None -1 + +#define UFUNC_REDUCE 0 +#define UFUNC_ACCUMULATE 1 +#define UFUNC_REDUCEAT 2 +#define UFUNC_OUTER 3 + + +typedef struct { + int nin; + int nout; + PyObject *callable; +} PyUFunc_PyFuncData; + + +#include "__ufunc_api.h" + +#define UFUNC_PYVALS_NAME "UFUNC_PYVALS" + +#define UFUNC_CHECK_ERROR(arg) \ + if (((arg)->obj && PyErr_Occurred()) || \ + ((arg)->errormask && \ + PyUFunc_checkfperr((arg)->errormask, \ + (arg)->errobj))) \ + goto fail + +/* This code checks the IEEE status flags in a platform-dependent way */ +/* Adapted from Numarray */ + +/* OSF/Alpha (Tru64) ---------------------------------------------*/ +#if defined(__osf__) && defined(__alpha) + +#include <machine/fpu.h> + +#define UFUNC_CHECK_STATUS(ret) { \ + unsigned long fpstatus; \ + \ + fpstatus = ieee_get_fp_control(); \ + /* clear status bits as well as disable exception mode if on */ \ + ieee_set_fp_control( 0 ); \ + ret = ((IEEE_STATUS_DZE & fpstatus) ? UFUNC_FPE_DIVIDEBYZERO : 0) \ + | ((IEEE_STATUS_OVF & fpstatus) ? UFUNC_FPE_OVERFLOW : 0) \ + | ((IEEE_STATUS_UNF & fpstatus) ? UFUNC_FPE_UNDERFLOW : 0) \ + | ((IEEE_STATUS_INV & fpstatus) ? UFUNC_FPE_INVALID : 0); \ + } + +/* MS Windows -----------------------------------------------------*/ +#elif defined(_MSC_VER) + +#include <float.h> + +#define UFUNC_CHECK_STATUS(ret) { \ + int fpstatus = (int) _clear87(); \ + \ + ret = ((SW_ZERODIVIDE & fpstatus) ? UFUNC_FPE_DIVIDEBYZERO : 0) \ + | ((SW_OVERFLOW & fpstatus) ? UFUNC_FPE_OVERFLOW : 0) \ + | ((SW_UNDERFLOW & fpstatus) ? UFUNC_FPE_UNDERFLOW : 0) \ + | ((SW_INVALID & fpstatus) ? UFUNC_FPE_INVALID : 0); \ + } + + +/* Solaris --------------------------------------------------------*/ +/* --------ignoring SunOS ieee_flags approach, someone else can +** deal with that! */ +#elif defined(sun) +#include <ieeefp.h> + +#define UFUNC_CHECK_STATUS(ret) { \ + int fpstatus; \ + \ + fpstatus = (int) fpgetsticky(); \ + ret = ((FP_X_DZ & fpstatus) ? UFUNC_FPE_DIVIDEBYZERO : 0) \ + | ((FP_X_OFL & fpstatus) ? UFUNC_FPE_OVERFLOW : 0) \ + | ((FP_X_UFL & fpstatus) ? UFUNC_FPE_UNDERFLOW : 0) \ + | ((FP_X_INV & fpstatus) ? UFUNC_FPE_INVALID : 0); \ + (void) fpsetsticky(0); \ + } + +#elif defined(linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__MINGW32__) + +#if defined(__GLIBC__) || defined(__APPLE__) || defined(__MINGW32__) +#include <fenv.h> +#elif defined(__CYGWIN__) +#include <mingw/fenv.h> +#endif + +#define UFUNC_CHECK_STATUS(ret) { \ + int fpstatus = (int) fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | \ + FE_UNDERFLOW | FE_INVALID); \ + ret = ((FE_DIVBYZERO & fpstatus) ? UFUNC_FPE_DIVIDEBYZERO : 0) \ + | ((FE_OVERFLOW & fpstatus) ? UFUNC_FPE_OVERFLOW : 0) \ + | ((FE_UNDERFLOW & fpstatus) ? UFUNC_FPE_UNDERFLOW : 0) \ + | ((FE_INVALID & fpstatus) ? UFUNC_FPE_INVALID : 0); \ + (void) feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | \ + FE_UNDERFLOW | FE_INVALID); \ +} + +#define generate_divbyzero_error() feraiseexcept(FE_DIVBYZERO) +#define generate_overflow_error() feraiseexcept(FE_OVERFLOW) + +#elif defined(AIX) + +#include <float.h> +#include <fpxcp.h> + +#define UFUNC_CHECK_STATUS(ret) { \ + fpflag_t fpstatus; \ + \ + fpstatus = fp_read_flag(); + ret = ((FP_DIV_BY_ZERO & fpstatus) ? UFUNC_FPE_DIVIDEBYZERO : 0) \ + | ((FP_OVERFLOW & fpstatus) ? UFUNC_FPE_OVERFLOW : 0) \ + | ((FP_UNDERFLOW & fpstatus) ? UFUNC_FPE_UNDERFLOW : 0) \ + | ((FP_INVALID & fpstatus) ? UFUNC_FPE_INVALID : 0); + fp_clr_flag( FP_DIV_BY_ZERO | FP_OVERFLOW | FP_UNDERFLOW | FP_INVALID); \ +} + +#else + +#define UFUNC_CHECK_STATUS(ret) { \ + printf("floating point flags not supported on this platform\n"); \ + ret = 0; \ + } + +#endif + +#ifdef PY_ARRAY_TYPES_PREFIX +# undef CAT +# undef CAT2 +# undef NS +# undef inpt +#endif + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_UFUNCOBJECT_H */ diff --git a/numpy/core/index_tricks.py b/numpy/core/index_tricks.py new file mode 100644 index 000000000..71d30a387 --- /dev/null +++ b/numpy/core/index_tricks.py @@ -0,0 +1,291 @@ +## Automatically adapted for scipy Sep 19, 2005 by convertcode.py + +__all__ = ['mgrid','ogrid','r_', 'c_', 'index_exp', 'ix_','ndenumerate'] + +import sys +import types +import numeric as _nx +from numeric import asarray + +from type_check import ScalarType +import function_base +import twodim_base as matrix_base +import matrix +makemat = matrix.matrix + +def ix_(*args): + """ Construct an open mesh from multiple sequences. + + This function takes n 1-d sequences and returns n outputs with n + dimensions each such that the shape is 1 in all but one dimension and + the dimension with the non-unit shape value cycles through all n + dimensions. + + Using ix_() one can quickly construct index arrays that will index + the cross product. + + a[ix_([1,3,7],[2,5,8])] returns the array + + a[1,2] a[1,5] a[1,8] + a[3,2] a[3,5] a[3,8] + a[7,2] a[7,5] a[7,8] + """ + out = [] + nd = len(args) + baseshape = [1]*nd + for k in range(nd): + new = _nx.array(args[k]) + if (new.ndim <> 1): + raise ValueError, "Cross index must be 1 dimensional" + baseshape[k] = len(new) + new.shape = tuple(baseshape) + out.append(new) + baseshape[k] = 1 + return tuple(out) + +class nd_grid(object): + """ Construct a "meshgrid" in N-dimensions. + + grid = nd_grid() creates an instance which will return a mesh-grid + when indexed. The dimension and number of the output arrays are equal + to the number of indexing dimensions. If the step length is not a + complex number, then the stop is not inclusive. + + However, if the step length is a COMPLEX NUMBER (e.g. 5j), then the + integer part of it's magnitude is interpreted as specifying the + number of points to create between the start and stop values, where + the stop value IS INCLUSIVE. + + If instantiated with an argument of 1, the mesh-grid is open or not + fleshed out so that only one-dimension of each returned argument is + greater than 1 + + Example: + + >>> mgrid = nd_grid() + >>> mgrid[0:5,0:5] + array([[[0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [2, 2, 2, 2, 2], + [3, 3, 3, 3, 3], + [4, 4, 4, 4, 4]], + [[0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4]]]) + >>> mgrid[-1:1:5j] + array([-1. , -0.5, 0. , 0.5, 1. ]) + + >>> ogrid = nd_grid(1) + >>> ogrid[0:5,0:5] + [array([[0],[1],[2],[3],[4]]), array([[0, 1, 2, 3, 4]])] + """ + def __init__(self, sparse=False): + self.sparse = sparse + def __getitem__(self,key): + try: + size = [] + typecode = _nx.Int + for k in range(len(key)): + step = key[k].step + start = key[k].start + if start is None: start=0 + if step is None: step=1 + if type(step) is type(1j): + size.append(int(abs(step))) + typecode = _nx.Float + else: + size.append(int((key[k].stop - start)/(step*1.0))) + if isinstance(step,types.FloatType) or \ + isinstance(start, types.FloatType) or \ + isinstance(key[k].stop, types.FloatType): + typecode = _nx.Float + if self.sparse: + nn = map(lambda x,t: _nx.arange(x,dtype=t),size,(typecode,)*len(size)) + else: + nn = _nx.indices(size,typecode) + for k in range(len(size)): + step = key[k].step + start = key[k].start + if start is None: start=0 + if step is None: step=1 + if type(step) is type(1j): + step = int(abs(step)) + step = (key[k].stop - start)/float(step-1) + nn[k] = (nn[k]*step+start) + if self.sparse: + slobj = [_nx.NewAxis]*len(size) + for k in range(len(size)): + slobj[k] = slice(None,None) + nn[k] = nn[k][slobj] + slobj[k] = _nx.NewAxis + return nn + except (IndexError, TypeError): + step = key.step + stop = key.stop + start = key.start + if start is None: start = 0 + if type(step) is type(1j): + step = abs(step) + length = int(step) + step = (key.stop-start)/float(step-1) + stop = key.stop+step + return _nx.arange(0,length,1,_nx.Float)*step + start + else: + return _nx.arange(start, stop, step) + + def __getslice__(self,i,j): + return _nx.arange(i,j) + + def __len__(self): + return 0 + +mgrid = nd_grid() +ogrid = nd_grid(1) + +class concatenator(object): + """ Translates slice objects to concatenation along an axis. + """ + def _retval(self, res): + if self.matrix: + oldndim = res.ndim + res = makemat(res) + if oldndim == 1 and self.col: + res = res.T + self.axis=self._axis + self.matrix=self._matrix + self.col=0 + return res + + def __init__(self, axis=0, matrix=False): + self._axis = axis + self._matrix = matrix + self.axis = axis + self.matrix = matrix + self.col = 0 + + def __getitem__(self,key): + if isinstance(key,types.StringType): + frame = sys._getframe().f_back + mymat = matrix.bmat(key,frame.f_globals,frame.f_locals) + return mymat + if type(key) is not types.TupleType: + key = (key,) + objs = [] + for k in range(len(key)): + if type(key[k]) is types.SliceType: + step = key[k].step + start = key[k].start + stop = key[k].stop + if start is None: start = 0 + if step is None: + step = 1 + if type(step) is type(1j): + size = int(abs(step)) + newobj = function_base.linspace(start, stop, num=size) + else: + newobj = _nx.arange(start, stop, step) + elif type(key[k]) is types.StringType: + if (key[k] in 'rc'): + self.matrix = True + self.col = (key[k] == 'c') + continue + try: + self.axis = int(key[k]) + continue + except: + raise ValueError, "Unknown special directive." + elif type(key[k]) in ScalarType: + newobj = asarray([key[k]]) + else: + newobj = key[k] + objs.append(newobj) + res = _nx.concatenate(tuple(objs),axis=self.axis) + return self._retval(res) + + def __getslice__(self,i,j): + res = _nx.arange(i,j) + return self._retval(res) + + def __len__(self): + return 0 + +r_=concatenator(0) +c_=concatenator(-1) +#row = concatenator(0,1) +#col = concatenator(-1,1) + + +# A simple nd index iterator over an array: + +class ndenumerate(object): + def __init__(self, arr): + arr = asarray(arr) + self.iter = enumerate(arr.flat) + self.ashape = arr.shape + self.nd = arr.ndim + self.factors = [None]*(self.nd-1) + val = self.ashape[-1] + for i in range(self.nd-1,0,-1): + self.factors[i-1] = val + val *= self.ashape[i-1] + + def next(self): + res = self.iter.next() + indxs = [None]*self.nd + val = res[0] + for i in range(self.nd-1): + indxs[i] = val / self.factors[i] + val = val % self.factors[i] + indxs[self.nd-1] = val + return tuple(indxs), res[1] + + def __iter__(self): + return self + + + +# A nicer way to build up index tuples for arrays. +# +# You can do all this with slice() plus a few special objects, +# but there's a lot to remember. This version is simpler because +# it uses the standard array indexing syntax. +# +# Written by Konrad Hinsen <hinsen@cnrs-orleans.fr> +# last revision: 1999-7-23 +# +# Cosmetic changes by T. Oliphant 2001 +# +# +# This module provides a convenient method for constructing +# array indices algorithmically. It provides one importable object, +# 'index_expression'. +# +# For any index combination, including slicing and axis insertion, +# 'a[indices]' is the same as 'a[index_expression[indices]]' for any +# array 'a'. However, 'index_expression[indices]' can be used anywhere +# in Python code and returns a tuple of slice objects that can be +# used in the construction of complex index expressions. + +class _index_expression_class(object): + maxint = sys.maxint + + def __getitem__(self, item): + if type(item) != type(()): + return (item,) + else: + return item + + def __len__(self): + return self.maxint + + def __getslice__(self, start, stop): + if stop == self.maxint: + stop = None + return self[start:stop:None] + +index_exp = _index_expression_class() + +# End contribution from Konrad. + diff --git a/numpy/core/info.py b/numpy/core/info.py new file mode 100644 index 000000000..b4c4b4e8c --- /dev/null +++ b/numpy/core/info.py @@ -0,0 +1,208 @@ +__doc__ = """Defines a multi-dimensional array and useful procedures for Numerical computation. + +Functions + +- array - NumPy Array construction +- zeros - Return an array of all zeros +- empty - Return an unitialized array +- shape - Return shape of sequence or array +- rank - Return number of dimensions +- size - Return number of elements in entire array or a + certain dimension +- fromstring - Construct array from (byte) string +- take - Select sub-arrays using sequence of indices +- put - Set sub-arrays using sequence of 1-D indices +- putmask - Set portion of arrays using a mask +- reshape - Return array with new shape +- repeat - Repeat elements of array +- choose - Construct new array from indexed array tuple +- cross_correlate - Correlate two 1-d arrays +- searchsorted - Search for element in 1-d array +- sum - Total sum over a specified dimension +- average - Average, possibly weighted, over axis or array. +- cumsum - Cumulative sum over a specified dimension +- product - Total product over a specified dimension +- cumproduct - Cumulative product over a specified dimension +- alltrue - Logical and over an entire axis +- sometrue - Logical or over an entire axis +- allclose - Tests if sequences are essentially equal + +More Functions: + +- arrayrange (arange) - Return regularly spaced array +- asarray - Guarantee NumPy array +- sarray - Guarantee a NumPy array that keeps precision +- convolve - Convolve two 1-d arrays +- swapaxes - Exchange axes +- concatenate - Join arrays together +- transpose - Permute axes +- sort - Sort elements of array +- argsort - Indices of sorted array +- argmax - Index of largest value +- argmin - Index of smallest value +- innerproduct - Innerproduct of two arrays +- dot - Dot product (matrix multiplication) +- outerproduct - Outerproduct of two arrays +- resize - Return array with arbitrary new shape +- indices - Tuple of indices +- fromfunction - Construct array from universal function +- diagonal - Return diagonal array +- trace - Trace of array +- dump - Dump array to file object (pickle) +- dumps - Return pickled string representing data +- load - Return array stored in file object +- loads - Return array from pickled string +- ravel - Return array as 1-D +- nonzero - Indices of nonzero elements for 1-D array +- shape - Shape of array +- where - Construct array from binary result +- compress - Elements of array where condition is true +- clip - Clip array between two values +- ones - Array of all ones +- identity - 2-D identity array (matrix) + +(Universal) Math Functions + + add logical_or exp + subtract logical_xor log + multiply logical_not log10 + divide maximum sin + divide_safe minimum sinh + conjugate bitwise_and sqrt + power bitwise_or tan + absolute bitwise_xor tanh + negative invert ceil + greater left_shift fabs + greater_equal right_shift floor + less arccos arctan2 + less_equal arcsin fmod + equal arctan hypot + not_equal cos around + logical_and cosh sign + arccosh arcsinh arctanh + +""" +__doc__ += \ +""" Basic functions used by several sub-packages and useful to have in the +main name-space + +Type handling +============== +iscomplexobj -- Test for complex object, scalar result +isrealobj -- Test for real object, scalar result +iscomplex -- Test for complex elements, array result +isreal -- Test for real elements, array result +imag -- Imaginary part +real -- Real part +real_if_close -- Turns complex number with tiny imaginary part to real +isneginf -- Tests for negative infinity ---| +isposinf -- Tests for positive infinity | +isnan -- Tests for nans |---- array results +isinf -- Tests for infinity | +isfinite -- Tests for finite numbers ---| +isscalar -- True if argument is a scalar +nan_to_num -- Replaces NaN's with 0 and infinities with large numbers +cast -- Dictionary of functions to force cast to each type +common_type -- Determine the 'minimum common type code' for a group + of arrays +mintypecode -- Return minimal allowed common typecode. + +Index tricks +================== +mgrid -- Method which allows easy construction of N-d 'mesh-grids' +r_ -- Append and construct arrays: turns slice objects into + ranges and concatenates them, for 2d arrays appends + rows. +index_exp -- Konrad Hinsen's index_expression class instance which + can be useful for building complicated slicing syntax. + +Useful functions +================== +select -- Extension of where to multiple conditions and choices +extract -- Extract 1d array from flattened array according to mask +insert -- Insert 1d array of values into Nd array according to mask +linspace -- Evenly spaced samples in linear space +logspace -- Evenly spaced samples in logarithmic space +fix -- Round x to nearest integer towards zero +mod -- Modulo mod(x,y) = x % y except keeps sign of y +amax -- Array maximum along axis +amin -- Array minimum along axis +ptp -- Array max-min along axis +cumsum -- Cumulative sum along axis +prod -- Product of elements along axis +cumprod -- Cumluative product along axis +diff -- Discrete differences along axis +angle -- Returns angle of complex argument +unwrap -- Unwrap phase along given axis (1-d algorithm) +sort_complex -- Sort a complex-array (based on real, then imaginary) +trim_zeros -- trim the leading and trailing zeros from 1D array. + +vectorize -- a class that wraps a Python function taking scalar + arguments into a generalized function which + can handle arrays of arguments using the broadcast + rules of numerix Python. + +alter_numeric -- enhance numeric array behavior +restore_numeric -- restore alterations done by alter_numeric + +Shape manipulation +=================== +squeeze -- Return a with length-one dimensions removed. +atleast_1d -- Force arrays to be > 1D +atleast_2d -- Force arrays to be > 2D +atleast_3d -- Force arrays to be > 3D +vstack -- Stack arrays vertically (row on row) +hstack -- Stack arrays horizontally (column on column) +column_stack -- Stack 1D arrays as columns into 2D array +dstack -- Stack arrays depthwise (along third dimension) +split -- Divide array into a list of sub-arrays +hsplit -- Split into columns +vsplit -- Split into rows +dsplit -- Split along third dimension + +Matrix (2d array) manipluations +=============================== +fliplr -- 2D array with columns flipped +flipud -- 2D array with rows flipped +rot90 -- Rotate a 2D array a multiple of 90 degrees +eye -- Return a 2D array with ones down a given diagonal +diag -- Construct a 2D array from a vector, or return a given + diagonal from a 2D array. +mat -- Construct a Matrix +bmat -- Build a Matrix from blocks + +Polynomials +============ +poly1d -- A one-dimensional polynomial class + +poly -- Return polynomial coefficients from roots +roots -- Find roots of polynomial given coefficients +polyint -- Integrate polynomial +polyder -- Differentiate polynomial +polyadd -- Add polynomials +polysub -- Substract polynomials +polymul -- Multiply polynomials +polydiv -- Divide polynomials +polyval -- Evaluate polynomial at given argument + +Import tricks +============= +ppimport -- Postpone module import until trying to use it +ppimport_attr -- Postpone module import until trying to use its + attribute +ppresolve -- Import postponed module and return it. + +Machine arithmetics +=================== +machar_single -- MachAr instance storing the parameters of system + single precision floating point arithmetics +machar_double -- MachAr instance storing the parameters of system + double precision floating point arithmetics + +Threading tricks +================ +ParallelExec -- Execute commands in parallel thread. +""" + +depends = ['testing'] +global_symbols = ['*'] diff --git a/numpy/core/ma.py b/numpy/core/ma.py new file mode 100644 index 000000000..245351349 --- /dev/null +++ b/numpy/core/ma.py @@ -0,0 +1,2062 @@ +"""MA: a facility for dealing with missing observations +MA is generally used as a scipy.array look-alike. +by Paul F. Dubois. + +Copyright 1999, 2000, 2001 Regents of the University of California. +Released for unlimited redistribution. +Adapted for scipy_core 2005 by Travis Oliphant and +(mainly) Paul Dubois. +""" +import string, types, sys + +import umath +import oldnumeric +import function_base +from numeric import e, pi, newaxis, ndarray, inf +from oldnumeric import typecodes, amax, amin +from numerictypes import * +import numeric + + +MaskType=bool_ +divide_tolerance = 1.e-35 + +class MAError (Exception): + def __init__ (self, args=None): + "Create an exception" + self.args = args + def __str__(self): + "Calculate the string representation" + return str(self.args) + __repr__ = __str__ + +class _MaskedPrintOption: + "One instance of this class, masked_print_option, is created." + def __init__ (self, display): + "Create the masked print option object." + self.set_display(display) + self._enabled = 1 + + def display (self): + "Show what prints for masked values." + return self._display + + def set_display (self, s): + "set_display(s) sets what prints for masked values." + self._display = s + + def enabled (self): + "Is the use of the display value enabled?" + return self._enabled + + def enable(self, flag=1): + "Set the enabling flag to flag." + self._enabled = flag + + def __str__ (self): + return str(self._display) + +#if you single index into a masked location you get this object. +masked_print_option = _MaskedPrintOption('--') + +# Use single element arrays or scalars. +default_real_fill_value = 1.e20 +default_complex_fill_value = 1.e20 + 0.0j +default_character_fill_value = '-' +default_integer_fill_value = 999999 +default_object_fill_value = '?' + +def default_fill_value (obj): + "Function to calculate default fill value for an object." + if isinstance(obj, types.FloatType): + return default_real_fill_value + elif isinstance(obj, types.IntType) or isinstance(obj, types.LongType): + return default_integer_fill_value + elif isinstance(obj, types.StringType): + return default_character_fill_value + elif isinstance(obj, types.ComplexType): + return default_complex_fill_value + elif isinstance(obj, MaskedArray) or isinstance(obj, ndarray): + x = obj.dtypechar + if x in typecodes['Float']: + return default_real_fill_value + if x in typecodes['Integer']: + return default_integer_fill_value + if x in typecodes['Complex']: + return default_complex_fill_value + if x in typecodes['Character']: + return default_character_fill_value + if x in typecodes['UnsignedInteger']: + return umath.absolute(default_integer_fill_value) + return default_object_fill_value + else: + return default_object_fill_value + +def minimum_fill_value (obj): + "Function to calculate default fill value suitable for taking minima." + if isinstance(obj, types.FloatType): + return numeric.inf + elif isinstance(obj, types.IntType) or isinstance(obj, types.LongType): + return sys.maxint + elif isinstance(obj, MaskedArray) or isinstance(obj, ndarray): + x = obj.dtypechar + if x in typecodes['Float']: + return numeric.inf + if x in typecodes['Integer']: + return sys.maxint + if x in typecodes['UnsignedInteger']: + return sys.maxint + else: + raise TypeError, 'Unsuitable type for calculating minimum.' + +def maximum_fill_value (obj): + "Function to calculate default fill value suitable for taking maxima." + if isinstance(obj, types.FloatType): + return -inf + elif isinstance(obj, types.IntType) or isinstance(obj, types.LongType): + return -sys.maxint + elif isinstance(obj, MaskedArray) or isinstance(obj, ndarray): + x = obj.dtypechar + if x in typecodes['Float']: + return -inf + if x in typecodes['Integer']: + return -sys.maxint + if x in typecodes['UnsignedInteger']: + return 0 + else: + raise TypeError, 'Unsuitable type for calculating maximum.' + +def set_fill_value (a, fill_value): + "Set fill value of a if it is a masked array." + if isMaskedArray(a): + a.set_fill_value (fill_value) + +def getmask (a): + """Mask of values in a; could be None. + Returns None if a is not a masked array. + To get an array for sure use getmaskarray.""" + if isinstance(a, MaskedArray): + return a.raw_mask() + else: + return None + +def getmaskarray (a): + """Mask of values in a; an array of zeros if mask is None + or not a masked array, and is a byte-sized integer. + Do not try to add up entries, for example. + """ + m = getmask(a) + if m is None: + return make_mask_none(shape(a)) + else: + return m + +def is_mask (m): + """Is m a legal mask? Does not check contents, only type. + """ + if m is None or (isinstance(m, ndarray) and \ + m.dtype is MaskType): + return 1 + else: + return 0 + +def make_mask (m, copy=0, flag=0): + """make_mask(m, copy=0, flag=0) + return m as a mask, creating a copy if necessary or requested. + Can accept any sequence of integers or None. Does not check + that contents must be 0s and 1s. + if flag, return None if m contains no true elements. + """ + if m is None: + return None + elif isinstance(m, ndarray): + if m.dtype is MaskType: + if copy: + result = numeric.array(m, dtype=MaskType, copy=copy) + else: + result = m + else: + result = m.astype(MaskType) + else: + result = filled(m,True).astype(MaskType) + + if flag and not oldnumeric.sometrue(oldnumeric.ravel(result)): + return None + else: + return result + +def make_mask_none (s): + "Return a mask of all zeros of shape s." + result = numeric.zeros(s, dtype=MaskType) + result.shape = s + return result + +def mask_or (m1, m2): + """Logical or of the mask candidates m1 and m2, treating None as false. + Result may equal m1 or m2 if the other is None. + """ + if m1 is None: return make_mask(m2) + if m2 is None: return make_mask(m1) + if m1 is m2 and is_mask(m1): return m1 + return make_mask(umath.logical_or(m1, m2)) + +def filled (a, value = None): + """a as a contiguous numeric array with any masked areas replaced by value + if value is None or the special element "masked", get_fill_value(a) + is used instead. + + If a is already a contiguous numeric array, a itself is returned. + + filled(a) can be used to be sure that the result is numeric when + passing an object a to other software ignorant of MA, in particular to + numeric itself. + """ + if isinstance(a, MaskedArray): + return a.filled(value) + elif isinstance(a, ndarray) and a.flags['CONTIGUOUS']: + return a + elif isinstance(a, types.DictType): + return numeric.array(a, 'O') + else: + return numeric.array(a) + +def get_fill_value (a): + """ + The fill value of a, if it has one; otherwise, the default fill value + for that type. + """ + if isMaskedArray(a): + result = a.fill_value() + else: + result = default_fill_value(a) + return result + +def common_fill_value (a, b): + "The common fill_value of a and b, if there is one, or None" + t1 = get_fill_value(a) + t2 = get_fill_value(b) + if t1 == t2: return t1 + return None + +# Domain functions return 1 where the argument(s) are not in the domain. +class domain_check_interval: + "domain_check_interval(a,b)(x) = true where x < a or y > b" + def __init__(self, y1, y2): + "domain_check_interval(a,b)(x) = true where x < a or y > b" + self.y1 = y1 + self.y2 = y2 + + def __call__ (self, x): + "Execute the call behavior." + return umath.logical_or(umath.greater (x, self.y2), + umath.less(x, self.y1) + ) + +class domain_tan: + "domain_tan(eps) = true where abs(cos(x)) < eps)" + def __init__(self, eps): + "domain_tan(eps) = true where abs(cos(x)) < eps)" + self.eps = eps + + def __call__ (self, x): + "Execute the call behavior." + return umath.less(umath.absolute(umath.cos(x)), self.eps) + +class domain_greater: + "domain_greater(v)(x) = true where x <= v" + def __init__(self, critical_value): + "domain_greater(v)(x) = true where x <= v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less_equal (x, self.critical_value) + +class domain_greater_equal: + "domain_greater_equal(v)(x) = true where x < v" + def __init__(self, critical_value): + "domain_greater_equal(v)(x) = true where x < v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less (x, self.critical_value) + +class masked_unary_operation: + def __init__ (self, aufunc, fill=0, domain=None): + """ masked_unary_operation(aufunc, fill=0, domain=None) + aufunc(fill) must be defined + self(x) returns aufunc(x) + with masked values where domain(x) is true or getmask(x) is true. + """ + self.f = aufunc + self.fill = fill + self.domain = domain + self.__doc__ = getattr(aufunc, "__doc__", str(aufunc)) + + def __call__ (self, a, *args, **kwargs): + "Execute the call behavior." +# numeric tries to return scalars rather than arrays when given scalars. + m = getmask(a) + d1 = filled(a, self.fill) + if self.domain is not None: + m = mask_or(m, self.domain(d1)) + if m is None: + result = self.f(d1, *args, **kwargs) + if type(result) is ndarray: + return masked_array (result) + else: + return result + else: + dx = masked_array(d1, m) + result = self.f(filled(dx, self.fill), *args, **kwargs) + if type(result) is ndarray: + return masked_array(result, m) + elif m[...]: + return masked + else: + return result + + def __str__ (self): + return "Masked version of " + str(self.f) + + +class domain_safe_divide: + def __init__ (self, tolerance=divide_tolerance): + self.tolerance = tolerance + def __call__ (self, a, b): + return umath.absolute(a) * self.tolerance >= umath.absolute(b) + +class domained_binary_operation: + """Binary operations that have a domain, like divide. These are complicated so they + are a separate class. They have no reduce, outer or accumulate. + """ + def __init__ (self, abfunc, domain, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = abfunc + self.domain = domain + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(abfunc, "__doc__", str(abfunc)) + + def __call__(self, a, b): + "Execute the call behavior." + ma = getmask(a) + mb = getmask(b) + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + t = self.domain(d1, d2) + + if oldnumeric.sometrue(t, None): + d2 = where(t, self.filly, d2) + mb = mask_or(mb, t) + m = mask_or(ma, mb) + if m is None: + result = self.f(d1, d2) + if type(result) is ndarray: + return masked_array(result) + else: + return result + result = self.f(d1, d2) + if type(result) is ndarray: + if m.shape != result.shape: + m = mask_or(getmaskarray(a), getmaskarray(b)) + return masked_array(result, m) + elif m[...]: + return masked + else: + return result + def __str__ (self): + return "Masked version of " + str(self.f) + +class masked_binary_operation: + def __init__ (self, abfunc, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = abfunc + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(abfunc, "__doc__", str(abfunc)) + + def __call__ (self, a, b, *args, **kwargs): + "Execute the call behavior." + m = mask_or(getmask(a), getmask(b)) + if m is None: + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + result = self.f(d1, d2, *args, **kwargs) + if type(result) is ndarray: + return masked_array(result) + else: + return result + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + result = self.f(d1, d2, *args, **kwargs) + if type(result) is ndarray: + if m.shape != result.shape: + m = mask_or(getmaskarray(a), getmaskarray(b)) + return masked_array(result, m) + elif m[...]: + return masked + else: + return result + + def reduce (self, target, axis=0): + """Reduce target along the given axis with this function.""" + m = getmask(target) + t = filled(target, self.filly) + if t.shape == (): + t = t.reshape(1) + if m is not None: + m = make_mask(m, copy=1) + m.shape = (1,) + if m is None: + return masked_array (self.f.reduce (t, axis)) + else: + t = masked_array (t, m) + t = self.f.reduce(filled(t, self.filly), axis) + m = umath.logical_and.reduce(m, axis) + if isinstance(t, ndarray): + return masked_array(t, m, get_fill_value(target)) + elif m: + return masked + else: + return t + + def outer (self, a, b): + "Return the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is None and mb is None: + m = None + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)) + return masked_array(d, m) + + def accumulate (self, target, axis=0): + """Accumulate target along axis after filling with y fill value.""" + t = filled(target, self.filly) + return masked_array (self.f.accumulate (t, axis)) + def __str__ (self): + return "Masked version of " + str(self.f) + +sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0)) +log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0)) +log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0)) +exp = masked_unary_operation(umath.exp) +conjugate = masked_unary_operation(umath.conjugate) +sin = masked_unary_operation(umath.sin) +cos = masked_unary_operation(umath.cos) +tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35)) +arcsin = masked_unary_operation(umath.arcsin, 0.0, domain_check_interval(-1.0, 1.0)) +arccos = masked_unary_operation(umath.arccos, 0.0, domain_check_interval(-1.0, 1.0)) +arctan = masked_unary_operation(umath.arctan) +# Missing from numeric +arcsinh = masked_unary_operation(umath.arcsinh) +arccosh = masked_unary_operation(umath.arccosh) +arctanh = masked_unary_operation(umath.arctanh) +sinh = masked_unary_operation(umath.sinh) +cosh = masked_unary_operation(umath.cosh) +tanh = masked_unary_operation(umath.tanh) +absolute = masked_unary_operation(umath.absolute) +fabs = masked_unary_operation(umath.fabs) +negative = masked_unary_operation(umath.negative) +nonzero = masked_unary_operation(oldnumeric.nonzero) +around = masked_unary_operation(function_base.round_) +floor = masked_unary_operation(umath.floor) +ceil = masked_unary_operation(umath.ceil) +sometrue = masked_unary_operation(oldnumeric.sometrue) +alltrue = masked_unary_operation(oldnumeric.alltrue, 1) +logical_not = masked_unary_operation(umath.logical_not) + +add = masked_binary_operation(umath.add) +subtract = masked_binary_operation(umath.subtract) +subtract.reduce = None +multiply = masked_binary_operation(umath.multiply, 1, 1) +divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1) +true_divide = domained_binary_operation(umath.true_divide, domain_safe_divide(), 0, 1) +floor_divide = domained_binary_operation(umath.floor_divide, domain_safe_divide(), 0, 1) +remainder = domained_binary_operation(umath.remainder, domain_safe_divide(), 0, 1) +fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1) +hypot = masked_binary_operation(umath.hypot) +arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0) +arctan2.reduce = None +equal = masked_binary_operation(umath.equal) +equal.reduce = None +not_equal = masked_binary_operation(umath.not_equal) +not_equal.reduce = None +less_equal = masked_binary_operation(umath.less_equal) +less_equal.reduce = None +greater_equal = masked_binary_operation(umath.greater_equal) +greater_equal.reduce = None +less = masked_binary_operation(umath.less) +less.reduce = None +greater = masked_binary_operation(umath.greater) +greater.reduce = None +logical_and = masked_binary_operation(umath.logical_and) +logical_or = masked_binary_operation(umath.logical_or) +logical_xor = masked_binary_operation(umath.logical_xor) +bitwise_and = masked_binary_operation(umath.bitwise_and) +bitwise_or = masked_binary_operation(umath.bitwise_or) +bitwise_xor = masked_binary_operation(umath.bitwise_xor) + +def rank (object): + return oldnumeric.rank(filled(object)) + +def shape (object): + return oldnumeric.shape(filled(object)) + +def size (object, axis=None): + return oldnumeric.size(filled(object), axis) + +class MaskedArray (object): + """Arrays with possibly masked values. + Masked values of 1 exclude the corresponding element from + any computation. + + Construction: + x = array(data, dtype=None, copy=True, fortran=False, + mask = None, fill_value=None) + + If copy=False, every effort is made not to copy the data: + If data is a MaskedArray, and argument mask=None, + then the candidate data is data.data and the + mask used is data.mask. If data is a numeric array, + it is used as the candidate raw data. + If dtypechar is not None and + is != data.dtypechar then a data copy is required. + Otherwise, the candidate is used. + + If a data copy is required, raw data stored is the result of: + numeric.array(data, dtype=dtypechar, copy=copy) + + If mask is None there are no masked values. Otherwise mask must + be convertible to an array of booleans with the same shape as x. + + fill_value is used to fill in masked values when necessary, + such as when printing and in method/function filled(). + The fill_value is not used for computation within this module. + """ + def __init__(self, data, dtype=None, copy=True, fortran=False, + mask=None, fill_value=None): + """array(data, dtype=None, copy=True, fortran=False, mask=None, fill_value=None) + If data already a numeric array, its dtype becomes the default value of dtype. + """ + tc = dtype + need_data_copied = copy + if isinstance(data, MaskedArray): + c = data.data + ctc = c.dtypechar + if tc is None: + tc = ctc + elif dtype2char(tc) != ctc: + need_data_copied = True + if mask is None: + mask = data.mask + elif mask is not None: #attempting to change the mask + need_data_copied = True + + elif isinstance(data, ndarray): + c = data + ctc = c.dtypechar + if tc is None: + tc = ctc + elif dtype2char(tc) != ctc: + need_data_copied = True + else: + need_data_copied = False #because I'll do it now + c = numeric.array(data, dtype=tc, copy=True, fortran=fortran) + + if need_data_copied: + if tc == ctc: + self._data = numeric.array(c, dtype=tc, copy=True, fortran=fortran) + else: + self._data = c.astype(tc) + else: + self._data = c + + if mask is None: + self._mask = None + self._shared_mask = 0 + else: + self._mask = make_mask (mask) + if self._mask is None: + self._shared_mask = 0 + else: + self._shared_mask = (self._mask is mask) + nm = size(self._mask) + nd = size(self._data) + if nm != nd: + if nm == 1: + self._mask = oldnumeric.resize(self._mask, self._data.shape) + self._shared_mask = 0 + elif nd == 1: + self._data = oldnumeric.resize(self._data, self._mask.shape) + self._data.shape = self._mask.shape + else: + raise MAError, "Mask and data not compatible." + elif nm == 1 and shape(self._mask) != shape(self._data): + self.unshare_mask() + self._mask.shape = self._data.shape + + self.set_fill_value(fill_value) + + def __array__ (self, t = None): + "Special hook for numeric. Converts to numeric if possible." + if self._mask is not None: + if oldnumeric.ravel(self._mask).any(): + raise MAError, \ + """Cannot automatically convert masked array to numeric because data + is masked in one or more locations. + """ + else: # Mask is all false + # Optimize to avoid future invocations of this section. + self._mask = None + self._shared_mask = 0 + if t: + return self._data.astype(t) + else: + return self._data + + def _get_shape(self): + "Return the current shape." + return self._data.shape + + def _set_shape (self, newshape): + "Set the array's shape." + self._data.shape = newshape + if self._mask is not None: + self._mask = self._mask.copy() + self._mask.shape = newshape + + def _get_flat(self): + """Calculate the flat value. + """ + if self._mask is None: + return masked_array(self._data.ravel(), mask=None, + fill_value = self.fill_value()) + else: + return masked_array(self._data.ravel(), + mask=self._mask.ravel(), + fill_value = self.fill_value()) + + def _set_flat (self, value): + "x.flat = value" + y = self.ravel() + y[:] = value + + def _get_real(self): + "Get the real part of a complex array." + if self._mask is None: + return masked_array(self._data.real, mask=None, + fill_value = self.fill_value()) + else: + return masked_array(self._data.real, mask=self._mask.ravel(), + fill_value = self.fill_value()) + + def _set_real (self, value): + "x.real = value" + y = self.real + y[...] = value + + def _get_imaginary(self): + "Get the imaginary part of a complex array." + if self._mask is None: + return masked_array(self._data.imag, mask=None, + fill_value = self.fill_value()) + else: + return masked_array(self._data.imag, mask=self._mask.ravel(), + fill_value = self.fill_value()) + + def _set_imaginary (self, value): + "x.imaginary = value" + y = self.imaginary + y[...] = value + + def __str__(self): + """Calculate the str representation, using masked for fill if + it is enabled. Otherwise fill with fill value. + """ + if masked_print_option.enabled(): + f = masked_print_option + else: + f = self.fill_value() + res = self.filled(f) + return str(res) + + def __repr__(self): + """Calculate the repr representation, using masked for fill if + it is enabled. Otherwise fill with fill value. + """ + with_mask = """\ +array(data = + %(data)s, + mask = + %(mask)s, + fill_value=%(fill)s) +""" + with_mask1 = """\ +array(data = %(data)s, + mask = %(mask)s, + fill_value=%(fill)s) +""" + without_mask = """array( + %(data)s)""" + without_mask1 = """array(%(data)s)""" + + n = len(self.shape) + if self._mask is None: + if n <=1: + return without_mask1 % {'data':str(self.filled())} + return without_mask % {'data':str(self.filled())} + else: + if n <=1: + return with_mask % { + 'data': str(self.filled()), + 'mask': str(self._mask), + 'fill': str(self.fill_value()) + } + return with_mask % { + 'data': str(self.filled()), + 'mask': str(self._mask), + 'fill': str(self.fill_value()) + } + without_mask1 = """array(%(data)s)""" + if self._mask is None: + return without_mask % {'data':str(self.filled())} + else: + return with_mask % { + 'data': str(self.filled()), + 'mask': str(self._mask), + 'fill': str(self.fill_value()) + } + + def __float__(self): + "Convert self to float." + self.unmask() + if self._mask is not None: + raise MAError, 'Cannot convert masked element to a Python float.' + return float(self.data.item()) + + def __int__(self): + "Convert self to int." + self.unmask() + if self._mask is not None: + raise MAError, 'Cannot convert masked element to a Python int.' + return int(self.data.item()) + + def __getitem__(self, i): + "Get item described by i. Not a copy as in previous versions." + self.unshare_mask() + m = self._mask + dout = self._data[i] + if m is None: + return dout + mi = m[i] + if mi.size == 1: + if mi: + return masked + else: + return dout + else: + return masked_array(dout, mi, fill_value=self._fill_value) + + def __getslice__(self, i, j): + "Get slice described by i, j" + self.unshare_mask() + m = self._mask + dout = self._data[i:j] + if m is None: + return masked_array(dout, fill_value=self._fill_value) + else: + return masked_array(dout, mask = m[i:j], fill_value=self._fill_value) + +# -------- +# setitem and setslice notes +# note that if value is masked, it means to mask those locations. +# setting a value changes the mask to match the value in those locations. + + def __setitem__(self, index, value): + "Set item described by index. If value is masked, mask those locations." + d = self._data + if self is masked: + raise MAError, 'Cannot alter the masked element.' + if value is masked: + if self._mask is None: + self._mask = make_mask_none(d.shape) + self._shared_mask = False + else: + self.unshare_mask() + self._mask[index] = True + return + m = getmask(value) + value = filled(value).astype(d.dtype) + d[index] = value + if m is None: + if self._mask is not None: + self.unshare_mask() + self._mask[index] = False + else: + if self._mask is None: + self._mask = make_mask_none(d.shape) + self._shared_mask = True + else: + self.unshare_mask() + self._mask[index] = m + + def __setslice__(self, i, j, value): + "Set slice i:j; if value is masked, mask those locations." + d = self._data + if self is masked: + raise MAError, "Cannot alter the 'masked' object." + if value is masked: + if self._mask is None: + self._mask = make_mask_none(d.shape) + self._shared_mask = False + self._mask[i:j] = True + return + m = getmask(value) + value = filled(value).astype(d.dtype) + d[i:j] = value + if m is None: + if self._mask is not None: + self.unshare_mask() + self._mask[i:j] = False + else: + if self._mask is None: + self._mask = make_mask_none(self._data.shape) + self._shared_mask = False + self._mask[i:j] = m + + def __len__ (self): + """Return length of first dimension. This is weird but Python's + slicing behavior depends on it.""" + return len(self._data) + + def __and__(self, other): + "Return bitwise_and" + return bitwise_and(self, other) + + def __or__(self, other): + "Return bitwise_or" + return bitwise_or(self, other) + + def __xor__(self, other): + "Return bitwise_xor" + return bitwise_xor(self, other) + + __rand__ = __and__ + __ror__ = __or__ + __rxor__ = __xor__ + + def __abs__(self): + "Return absolute(self)" + return absolute(self) + + def __neg__(self): + "Return negative(self)" + return negative(self) + + def __pos__(self): + "Return array(self)" + return array(self) + + def __add__(self, other): + "Return add(self, other)" + return add(self, other) + + __radd__ = __add__ + + def __mod__ (self, other): + "Return remainder(self, other)" + return remainder(self, other) + + def __rmod__ (self, other): + "Return remainder(other, self)" + return remainder(other, self) + + def __lshift__ (self, n): + return left_shift(self, n) + + def __rshift__ (self, n): + return right_shift(self, n) + + def __sub__(self, other): + "Return subtract(self, other)" + return subtract(self, other) + + def __rsub__(self, other): + "Return subtract(other, self)" + return subtract(other, self) + + def __mul__(self, other): + "Return multiply(self, other)" + return multiply(self, other) + + __rmul__ = __mul__ + + def __div__(self, other): + "Return divide(self, other)" + return divide(self, other) + + def __rdiv__(self, other): + "Return divide(other, self)" + return divide(other, self) + + def __truediv__(self, other): + "Return divide(self, other)" + return true_divide(self, other) + + def __rtruediv__(self, other): + "Return divide(other, self)" + return true_divide(other, self) + + def __floordiv__(self, other): + "Return divide(self, other)" + return floor_divide(self, other) + + def __rfloordiv__(self, other): + "Return divide(other, self)" + return floor_divide(other, self) + + def __pow__(self,other, third=None): + "Return power(self, other, third)" + return power(self, other, third) + + def __sqrt__(self): + "Return sqrt(self)" + return sqrt(self) + + def __iadd__(self, other): + "Add other to self in place." + t = self._data.dtypechar + f = filled(other,0) + t1 = f.dtypechar + if t == t1: + pass + elif t in typecodes['Integer']: + if t1 in typecodes['Integer']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Float']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Complex']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + elif t1 in typecodes['Complex']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + else: + raise TypeError, 'Incorrect type for in-place operation.' + + if self._mask is None: + self._data += f + m = getmask(other) + self._mask = m + self._shared_mask = m is not None + else: + result = add(self, masked_array(f, mask=getmask(other))) + self._data = result.data + self._mask = result.mask + self._shared_mask = 1 + return self + + def __imul__(self, other): + "Add other to self in place." + t = self._data.dtypechar + f = filled(other,0) + t1 = f.dtypechar + if t == t1: + pass + elif t in typecodes['Integer']: + if t1 in typecodes['Integer']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Float']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Complex']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + elif t1 in typecodes['Complex']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + else: + raise TypeError, 'Incorrect type for in-place operation.' + + if self._mask is None: + self._data *= f + m = getmask(other) + self._mask = m + self._shared_mask = m is not None + else: + result = multiply(self, masked_array(f, mask=getmask(other))) + self._data = result.data + self._mask = result.mask + self._shared_mask = 1 + return self + + def __isub__(self, other): + "Subtract other from self in place." + t = self._data.dtypechar + f = filled(other,0) + t1 = f.dtypechar + if t == t1: + pass + elif t in typecodes['Integer']: + if t1 in typecodes['Integer']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Float']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Complex']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + elif t1 in typecodes['Complex']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + else: + raise TypeError, 'Incorrect type for in-place operation.' + + if self._mask is None: + self._data -= f + m = getmask(other) + self._mask = m + self._shared_mask = m is not None + else: + result = subtract(self, masked_array(f, mask=getmask(other))) + self._data = result.data + self._mask = result.mask + self._shared_mask = 1 + return self + + + + def __idiv__(self, other): + "Divide self by other in place." + t = self._data.dtypechar + f = filled(other,0) + t1 = f.dtypechar + if t == t1: + pass + elif t in typecodes['Integer']: + if t1 in typecodes['Integer']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Float']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif t in typecodes['Complex']: + if t1 in typecodes['Integer']: + f = f.astype(t) + elif t1 in typecodes['Float']: + f = f.astype(t) + elif t1 in typecodes['Complex']: + f = f.astype(t) + else: + raise TypeError, 'Incorrect type for in-place operation.' + else: + raise TypeError, 'Incorrect type for in-place operation.' + mo = getmask(other) + result = divide(self, masked_array(f, mask=mo)) + self._data = result.data + dm = result.raw_mask() + if dm is not self._mask: + self._mask = dm + self._shared_mask = 1 + return self + + def __eq__(self,other): + return equal(self,other) + + def __ne__(self,other): + return not_equal(self,other) + + def __lt__(self,other): + return less(self,other) + + def __le__(self,other): + return less_equal(self,other) + + def __gt__(self,other): + return greater(self,other) + + def __ge__(self,other): + return greater_equal(self,other) + + def astype (self, tc): + "return self as array of given type." + d = self._data.astype(tc) + return array(d, mask=self._mask) + + def byte_swapped(self): + """Returns the raw data field, byte_swapped. Included for consistency + with numeric but doesn't make sense in this context. + """ + return self._data.byte_swapped() + + def compressed (self): + "A 1-D array of all the non-masked data." + d = oldnumeric.ravel(self._data) + if self._mask is None: + return array(d) + else: + m = 1 - oldnumeric.ravel(self._mask) + c = oldnumeric.compress(m, d) + return array(c, copy=0) + + def count (self, axis = None): + "Count of the non-masked elements in a, or along a certain axis." + m = self._mask + s = self._data.shape + ls = len(s) + if m is None: + if ls == 0: + return 1 + if ls == 1: + return s[0] + if axis is None: + return reduce(lambda x,y:x*y, s) + else: + n = s[axis] + t = list(s) + del t[axis] + return ones(t) * n + if axis is None: + w = oldnumeric.ravel(m).astype(int) + n1 = size(w) + if n1 == 1: + n2 = w[0] + else: + n2 = umath.add.reduce(w) + return n1 - n2 + else: + n1 = size(m, axis) + n2 = sum(m.astype(int), axis) + return n1 - n2 + + def dot (self, other): + "s.dot(other) = innerproduct(s, other)" + return innerproduct(self, other) + + def fill_value(self): + "Get the current fill value." + return self._fill_value + + def filled (self, fill_value=None): + """A numeric array with masked values filled. If fill_value is None, + use self.fill_value(). + + If mask is None, copy data only if not contiguous. + Result is always a contiguous, numeric array. +# Is contiguous really necessary now? + """ + d = self._data + m = self._mask + if m is None: + if d.flags['CONTIGUOUS']: + return d + else: + return d.copy() + else: + if fill_value is None: + value = self._fill_value + else: + value = fill_value + + if self is masked: + result = numeric.array(value).reshape(*d.shape) + else: + try: + result = numeric.array(d, dtype=d.dtype, copy=1) + result[m] = value + except: + #ok, can't put that value in here + value = numeric.array(value, dtype=object) + d = d.astype(object) + result = oldnumeric.choose(m, (d, value)) + return result + + def ids (self): + """Return the ids of the data and mask areas""" + return (id(self._data), id(self._mask)) + + def iscontiguous (self): + "Is the data contiguous?" + return self._data.flags['CONTIGUOUS'] + + def itemsize(self): + "Item size of each data item." + return self._data.itemsize + + + def outer(self, other): + "s.outer(other) = outerproduct(s, other)" + return outerproduct(self, other) + + def put (self, values): + """Set the non-masked entries of self to filled(values). + No change to mask + """ + iota = numeric.arange(self.size) + d = self._data + if self._mask is None: + ind = iota + else: + ind = oldnumeric.compress(1 - self._mask, iota) + d[ind] = filled(values).astype(d.dtype) + + def putmask (self, values): + """Set the masked entries of self to filled(values). + Mask changed to None. + """ + d = self._data + if self._mask is not None: + d[self._mask] = filled(values).astype(d.dtype) + self._shared_mask = 0 + self._mask = None + + def ravel (self): + """Return a 1-D view of self.""" + if self._mask is None: + return masked_array(self._data.ravel()) + else: + return masked_array(self._data.ravel(), self._mask.ravel()) + + def raw_data (self): + """ Obsolete; use data property instead. + The raw data; portions may be meaningless. + May be noncontiguous. Expert use only.""" + return self._data + data = property(fget=raw_data, + doc="The data, but values at masked locations are meaningless.") + + def raw_mask (self): + """ Obsolete; use mask property instead. + May be noncontiguous. Expert use only. + """ + return self._mask + mask = property(fget=raw_mask, + doc="The mask, may be None. Values where mask true are meaningless.") + + def reshape (self, *s): + """This array reshaped to shape s""" + d = self._data.reshape(*s) + if self._mask is None: + return masked_array(d) + else: + m = self._mask.reshape(*s) + return masked_array(d, m) + + def set_fill_value (self, v=None): + "Set the fill value to v. Omit v to restore default." + if v is None: + v = default_fill_value (self.raw_data()) + self._fill_value = v + + def _get_size (self): + return self._data.size + size = property(fget=_get_size, doc="Number of elements in the array.") +## CHECK THIS: signature of numeric.array.size? + + def _get_dtypechar(self): + return self._data.dtypechar + dtypechar = property(fget=_get_dtypechar, doc="type character of the array.") + + def _get_dtype(self): + return self._data.dtype + dtype = property(fget=_get_dtype, doc="type of the array elements.") + + def item(self): + "Return Python scalar if possible." + if self._mask is not None: + m = oldnumeric.ravel(self._mask) + try: + if m[0]: + return masked + except IndexError: + return masked + return self._data.item() + + def tolist(self, fill_value=None): + "Convert to list" + return self.filled(fill_value).tolist() + + def tostring(self, fill_value=None): + "Convert to string" + return self.filled(fill_value).tostring() + + def unmask (self): + "Replace the mask by None if possible." + if self._mask is None: return + m = make_mask(self._mask, flag=1) + if m is None: + self._mask = None + self._shared_mask = 0 + + def unshare_mask (self): + "If currently sharing mask, make a copy." + if self._shared_mask: + self._mask = make_mask (self._mask, copy=1, flag=0) + self._shared_mask = 0 + + shape = property(_get_shape, _set_shape, + doc = 'tuple giving the shape of the array') + + flat = property(_get_flat, _set_flat, + doc = 'Access array in flat form.') + + real = property(_get_real, _set_real, + doc = 'Access the real part of the array') + + imaginary = property(_get_imaginary, _set_imaginary, + doc = 'Access the imaginary part of the array') + + imag = imaginary + +#end class MaskedArray + +array = MaskedArray + +def isMaskedArray (x): + "Is x a masked array, that is, an instance of MaskedArray?" + return isinstance(x, MaskedArray) + +isarray = isMaskedArray +isMA = isMaskedArray #backward compatibility + +def allclose (a, b, fill_value=1, rtol=1.e-5, atol=1.e-8): + """ Returns true if all components of a and b are equal + subject to given tolerances. + If fill_value is 1, masked values considered equal. + If fill_value is 0, masked values considered unequal. + The relative error rtol should be positive and << 1.0 + The absolute error atol comes into play for those elements + of b that are very small or zero; it says how small a must be also. + """ + m = mask_or(getmask(a), getmask(b)) + d1 = filled(a) + d2 = filled(b) + x = filled(array(d1, copy=0, mask=m), fill_value).astype(float) + y = filled(array(d2, copy=0, mask=m), 1).astype(float) + d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y)) + return oldnumeric.alltrue(oldnumeric.ravel(d)) + +def allequal (a, b, fill_value=1): + """ + True if all entries of a and b are equal, using + fill_value as a truth value where either or both are masked. + """ + m = mask_or(getmask(a), getmask(b)) + if m is None: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + return oldnumeric.alltrue(oldnumeric.ravel(d)) + elif fill_value: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + dm = array(d, mask=m, copy=0) + return oldnumeric.alltrue(oldnumeric.ravel(filled(dm, 1))) + else: + return 0 + +def masked_values (data, value, rtol=1.e-5, atol=1.e-8, copy=1): + """ + masked_values(data, value, rtol=1.e-5, atol=1.e-8) + Create a masked array; mask is None if possible. + If copy==0, and otherwise possible, result + may share data values with original array. + Let d = filled(data, value). Returns d + masked where abs(data-value)<= atol + rtol * abs(value) + if d is of a floating point type. Otherwise returns + masked_object(d, value, copy) + """ + abs = umath.absolute + d = filled(data, value) + if issubclass(d.dtype, numeric.floating): + m = umath.less_equal(abs(d-value), atol+rtol*abs(value)) + m = make_mask(m, flag=1) + return array(d, mask = m, copy=copy, + fill_value=value) + else: + return masked_object(d, value, copy=copy) + +def masked_object (data, value, copy=1): + "Create array masked where exactly data equal to value" + d = filled(data, value) + dm = make_mask(umath.equal(d, value), flag=1) + return array(d, mask=dm, copy=copy, fill_value=value) + +def arrayrange(start, stop=None, step=1, dtype=None): + """Just like range() except it returns a array whose type can be specified + by the keyword argument dtypechar. + """ + return array(numeric.arrayrange(start, stop, step, dtype)) + +arange = arrayrange + +def fromstring (s, t): + "Construct a masked array from a string. Result will have no mask." + return masked_array(numeric.fromstring(s, t)) + +def left_shift (a, n): + "Left shift n bits" + m = getmask(a) + if m is None: + d = umath.left_shift(filled(a), n) + return masked_array(d) + else: + d = umath.left_shift(filled(a,0), n) + return masked_array(d, m) + +def right_shift (a, n): + "Right shift n bits" + m = getmask(a) + if m is None: + d = umath.right_shift(filled(a), n) + return masked_array(d) + else: + d = umath.right_shift(filled(a,0), n) + return masked_array(d, m) + +def resize (a, new_shape): + """resize(a, new_shape) returns a new array with the specified shape. + The original array's total size can be any size.""" + m = getmask(a) + if m is not None: + m = oldnumeric.resize(m, new_shape) + result = array(oldnumeric.resize(filled(a), new_shape), mask=m) + result.set_fill_value(get_fill_value(a)) + return result + +def repeat(a, repeats, axis=0): + """repeat elements of a repeats times along axis + repeats is a sequence of length a.shape[axis] + telling how many times to repeat each element. + """ + af = filled(a) + if isinstance(repeats, types.IntType): + repeats = tuple([repeats]*(shape(af)[axis])) + + m = getmask(a) + if m is not None: + m = oldnumeric.repeat(m, repeats, axis) + d = oldnumeric.repeat(af, repeats, axis) + result = masked_array(d, m) + result.set_fill_value(get_fill_value(a)) + return result + +def identity(n): + """identity(n) returns the identity matrix of shape n x n. + """ + return array(numeric.identity(n)) + +def indices (dimensions, dtype=None): + """indices(dimensions,dtype=None) returns an array representing a grid + of indices with row-only, and column-only variation. + """ + return array(numeric.indices(dimensions, dtype)) + +def zeros (shape, dtype=int): + """zeros(n, dtype=int) = + an array of all zeros of the given length or shape.""" + return array(numeric.zeros(shape, dtype)) + +def ones (shape, dtype=int): + """ones(n, dtype=int) = + an array of all ones of the given length or shape.""" + return array(numeric.ones(shape, dtype)) + + +def count (a, axis = None): + "Count of the non-masked elements in a, or along a certain axis." + a = masked_array(a) + return a.count(axis) + +def power (a, b, third=None): + "a**b" + if third is not None: + raise MAError, "3-argument power not supported." + ma = getmask(a) + mb = getmask(b) + m = mask_or(ma, mb) + fa = filled(a, 1) + fb = filled(b, 1) + if fb.dtypechar in typecodes["Integer"]: + return masked_array(umath.power(fa, fb), m) + md = make_mask(umath.less_equal (fa, 0), flag=1) + m = mask_or(m, md) + if m is None: + return masked_array(umath.power(fa, fb)) + else: + fa = numeric.where(m, 1, fa) + return masked_array(umath.power(fa, fb), m) + +def masked_array (a, mask=None, fill_value=None): + """masked_array(a, mask=None) = + array(a, mask=mask, copy=0, fill_value=fill_value) + """ + return array(a, mask=mask, copy=0, fill_value=fill_value) + +sum = add.reduce +product = multiply.reduce + +def average (a, axis=0, weights=None, returned = 0): + """average(a, axis=0, weights=None) + Computes average along indicated axis. + If axis is None, average over the entire array + Inputs can be integer or floating types; result is of type float. + + If weights are given, result is sum(a*weights)/(sum(weights)*1.0) + weights must have a's shape or be the 1-d with length the size + of a in the given axis. + + If returned, return a tuple: the result and the sum of the weights + or count of values. Results will have the same shape. + + masked values in the weights will be set to 0.0 + """ + a = masked_array(a) + mask = a.mask + ash = a.shape + if ash == (): + ash = (1,) + if axis is None: + if mask is None: + if weights is None: + n = add.reduce(a.raw_data().ravel()) + d = reduce(lambda x, y: x * y, ash, 1.0) + else: + w = filled(weights, 0.0).ravel() + n = umath.add.reduce(a.raw_data().ravel() * w) + d = umath.add.reduce(w) + del w + else: + if weights is None: + n = add.reduce(a.ravel()) + w = oldnumeric.choose(mask, (1.0,0.0)).ravel() + d = umath.add.reduce(w) + del w + else: + w = array(filled(weights, 0.0), float, mask=mask).ravel() + n = add.reduce(a.ravel() * w) + d = add.reduce(w) + del w + else: + if mask is None: + if weights is None: + d = ash[axis] * 1.0 + n = umath.add.reduce(a.raw_data(), axis) + else: + w = filled(weights, 0.0) + wsh = w.shape + if wsh == (): + wsh = (1,) + if wsh == ash: + w = numeric.array(w, float, copy=0) + n = add.reduce(a*w, axis) + d = add.reduce(w, axis) + del w + elif wsh == (ash[axis],): + ni = ash[axis] + r = [newaxis]*len(ash) + r[axis] = slice(None,None,1) + w = eval ("w["+ repr(tuple(r)) + "] * ones(ash, float)") + n = add.reduce(a*w, axis) + d = add.reduce(w, axis) + del w, r + else: + raise ValueError, 'average: weights wrong shape.' + else: + if weights is None: + n = add.reduce(a, axis) + w = numeric.choose(mask, (1.0, 0.0)) + d = umath.add.reduce(w, axis) + del w + else: + w = filled(weights, 0.0) + wsh = w.shape + if wsh == (): + wsh = (1,) + if wsh == ash: + w = array(w, float, mask=mask, copy=0) + n = add.reduce(a*w, axis) + d = add.reduce(w, axis) + elif wsh == (ash[axis],): + ni = ash[axis] + r = [newaxis]*len(ash) + r[axis] = slice(None,None,1) + w = eval ("w["+ repr(tuple(r)) + "] * masked_array(ones(ash, float), mask)") + n = add.reduce(a*w, axis) + d = add.reduce(w, axis) + else: + raise ValueError, 'average: weights wrong shape.' + del w + #print n, d, repr(mask), repr(weights) + if n is masked or d is masked: return masked + result = divide (n, d) + del n + + if isinstance(result, MaskedArray): + result.unmask() + if returned: + if not isinstance(d, MaskedArray): + d = masked_array(d) + if not d.shape == result.shape: + d = ones(result.shape, float) * d + d.unmask() + if returned: + return result, d + else: + return result + +def where (condition, x, y): + """where(condition, x, y) is x where condition is nonzero, y otherwise. + condition must be convertible to an integer array. + Answer is always the shape of condition. + The type depends on x and y. It is integer if both x and y are + the value masked. + """ + fc = filled(not_equal(condition,0), 0) + if x is masked: + xv = 0 + xm = 1 + else: + xv = filled(x) + xm = getmask(x) + if xm is None: xm = 0 + if y is masked: + yv = 0 + ym = 1 + else: + yv = filled(y) + ym = getmask(y) + if ym is None: ym = 0 + d = numeric.choose(fc, (yv, xv)) + md = numeric.choose(fc, (ym, xm)) + m = getmask(condition) + m = make_mask(mask_or(m, md), copy=0, flag=1) + return masked_array(d, m) + +def choose (indices, t): + "Returns array shaped like indices with elements chosen from t" + def fmask (x): + if x is masked: return 1 + return filled(x) + def nmask (x): + if x is masked: return 1 + m = getmask(x) + if m is None: return 0 + return m + c = filled(indices,0) + masks = [nmask(x) for x in t] + a = [fmask(x) for x in t] + d = numeric.choose(c, a) + m = numeric.choose(c, masks) + m = make_mask(mask_or(m, getmask(indices)), copy=0, flag=1) + return masked_array(d, m) + +def masked_where(condition, x, copy=1): + """Return x as an array masked where condition is true. + Also masked where x or condition masked. + """ + cm = filled(condition,1) + m = mask_or(getmask(x), cm) + return array(filled(x), copy=copy, mask=m) + +def masked_greater(x, value, copy=1): + "masked_greater(x, value) = x masked where x > value" + return masked_where(greater(x, value), x, copy) + +def masked_greater_equal(x, value, copy=1): + "masked_greater_equal(x, value) = x masked where x >= value" + return masked_where(greater_equal(x, value), x, copy) + +def masked_less(x, value, copy=1): + "masked_less(x, value) = x masked where x < value" + return masked_where(less(x, value), x, copy) + +def masked_less_equal(x, value, copy=1): + "masked_less_equal(x, value) = x masked where x <= value" + return masked_where(less_equal(x, value), x, copy) + +def masked_not_equal(x, value, copy=1): + "masked_not_equal(x, value) = x masked where x != value" + d = filled(x,0) + c = umath.not_equal(d, value) + m = mask_or(c, getmask(x)) + return array(d, mask=m, copy=copy) + +def masked_equal(x, value, copy=1): + """masked_equal(x, value) = x masked where x == value + For floating point consider masked_values(x, value) instead. + """ + d = filled(x,0) + c = umath.equal(d, value) + m = mask_or(c, getmask(x)) + return array(d, mask=m, copy=copy) + +def masked_inside(x, v1, v2, copy=1): + """x with mask of all values of x that are inside [v1,v2] + v1 and v2 can be given in either order. + """ + if v2 < v1: + t = v2 + v2 = v1 + v1 = t + d=filled(x, 0) + c = umath.logical_and(umath.less_equal(d, v2), umath.greater_equal(d, v1)) + m = mask_or(c, getmask(x)) + return array(d, mask = m, copy=copy) + +def masked_outside(x, v1, v2, copy=1): + """x with mask of all values of x that are outside [v1,v2] + v1 and v2 can be given in either order. + """ + if v2 < v1: + t = v2 + v2 = v1 + v1 = t + d = filled(x,0) + c = umath.logical_or(umath.less(d, v1), umath.greater(d, v2)) + m = mask_or(c, getmask(x)) + return array(d, mask = m, copy=copy) + +def reshape (a, *newshape): + "Copy of a with a new shape." + m = getmask(a) + d = filled(a).reshape(*newshape) + if m is None: + return masked_array(d) + else: + return masked_array(d, mask=numeric.reshape(m, *newshape)) + +def ravel (a): + "a as one-dimensional, may share data and mask" + m = getmask(a) + d = oldnumeric.ravel(filled(a)) + if m is None: + return masked_array(d) + else: + return masked_array(d, mask=numeric.ravel(m)) + +def concatenate (arrays, axis=0): + "Concatenate the arrays along the given axis" + d = [] + for x in arrays: + d.append(filled(x)) + d = numeric.concatenate(d, axis) + for x in arrays: + if getmask(x) is not None: break + else: + return masked_array(d) + dm = [] + for x in arrays: + dm.append(getmaskarray(x)) + dm = numeric.concatenate(dm, axis) + return masked_array(d, mask=dm) + +def take (a, indices, axis=0): + "take(a, indices, axis=0) returns selection of items from a." + m = getmask(a) + d = masked_array(a).raw_data() + if m is None: + return masked_array(numeric.take(d, indices, axis)) + else: + return masked_array(numeric.take(d, indices, axis), + mask = numeric.take(m, indices, axis)) + +def transpose(a, axes=None): + "transpose(a, axes=None) reorder dimensions per tuple axes" + m = getmask(a) + d = filled(a) + if m is None: + return masked_array(numeric.transpose(d, axes)) + else: + return masked_array(numeric.transpose(d, axes), + mask = numeric.transpose(m, axes)) + + +def put(a, indices, values): + """put(a, indices, values) sets storage-indexed locations to corresponding values. + + Values and indices are filled if necessary. + + """ + d = a.raw_data() + ind = filled(indices) + v = filled(values) + numeric.put (d, ind, v) + m = getmask(a) + if m is not None: + a.unshare_mask() + numeric.put(a.raw_mask(), ind, 0) + +def putmask(a, mask, values): + "putmask(a, mask, values) sets a where mask is true." + if mask is None: + return + numeric.putmask(a.raw_data(), mask, values) + m = getmask(a) + if m is None: return + a.unshare_mask() + numeric.putmask(a.raw_mask(), mask, 0) + +def innerproduct(a,b): + """innerproduct(a,b) returns the dot product of two arrays, which has + shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the + product of the elements from the last dimensions of a and b. + Masked elements are replace by zeros. + """ + fa = filled(a, 0) + fb = filled(b, 0) + if len(fa.shape) == 0: fa.shape = (1,) + if len(fb.shape) == 0: fb.shape = (1,) + return masked_array(numeric.innerproduct(fa, fb)) + +def outerproduct(a, b): + """outerproduct(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))""" + fa = filled(a,0).ravel() + fb = filled(b,0).ravel() + d = numeric.outerproduct(fa, fb) + ma = getmask(a) + mb = getmask(b) + if ma is None and mb is None: + return masked_array(d) + ma = getmaskarray(a) + mb = getmaskarray(b) + m = make_mask(1-numeric.outerproduct(1-ma,1-mb), copy=0) + return masked_array(d, m) + +def dot(a, b): + """dot(a,b) returns matrix-multiplication between a and b. The product-sum + is over the last dimension of a and the second-to-last dimension of b. + Masked values are replaced by zeros. See also innerproduct. + """ + return innerproduct(filled(a,0), numeric.swapaxes(filled(b,0), -1, -2)) + +def compress(condition, x, dimension=-1): + """Select those parts of x for which condition is true. + Masked values in condition are considered false. + """ + c = filled(condition, 0) + m = getmask(x) + if m is not None: + m=numeric.compress(c, m, dimension) + d = numeric.compress(c, filled(x), dimension) + return masked_array(d, m) + +class _minimum_operation: + "Object to calculate minima" + def __init__ (self): + """minimum(a, b) or minimum(a) + In one argument case returns the scalar minimum. + """ + pass + + def __call__ (self, a, b=None): + "Execute the call behavior." + if b is None: + m = getmask(a) + if m is None: + d = amin(filled(a).ravel()) + return d + ac = a.compressed() + if len(ac) == 0: + return masked + else: + return amin(ac.raw_data()) + else: + return where(less(a, b), a, b)[...] + + def reduce (self, target, axis=0): + """Reduce target along the given axis.""" + m = getmask(target) + if m is None: + t = filled(target) + return masked_array (umath.minimum.reduce (t, axis)) + else: + t = umath.minimum.reduce(filled(target, minimum_fill_value(target)), axis) + m = umath.logical_and.reduce(m, axis) + return masked_array(t, m, get_fill_value(target)) + + def outer (self, a, b): + "Return the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is None and mb is None: + m = None + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + d = umath.minimum.outer(filled(a), filled(b)) + return masked_array(d, m) + +minimum = _minimum_operation () + +class _maximum_operation: + "Object to calculate maxima" + def __init__ (self): + """maximum(a, b) or maximum(a) + In one argument case returns the scalar maximum. + """ + pass + + def __call__ (self, a, b=None): + "Execute the call behavior." + if b is None: + m = getmask(a) + if m is None: + d = amax(filled(a).ravel()) + return d + ac = a.compressed() + if len(ac) == 0: + return masked + else: + return amax(ac.raw_data()) + else: + return where(greater(a, b), a, b)[...] + + def reduce (self, target, axis=0): + """Reduce target along the given axis.""" + m = getmask(target) + if m is None: + t = filled(target) + return masked_array (umath.maximum.reduce (t, axis)) + else: + t = umath.maximum.reduce(filled(target, maximum_fill_value(target)), axis) + m = umath.logical_and.reduce(m, axis) + return masked_array(t, m, get_fill_value(target)) + + def outer (self, a, b): + "Return the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is None and mb is None: + m = None + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + d = umath.maximum.outer(filled(a), filled(b)) + return masked_array(d, m) + +maximum = _maximum_operation () + +def sort (x, axis = -1, fill_value=None): + """If x does not have a mask, return a masked array formed from the + result of numeric.sort(x, axis). + Otherwise, fill x with fill_value. Sort it. + Set a mask where the result is equal to fill_value. + Note that this may have unintended consequences if the data contains the + fill value at a non-masked site. + + If fill_value is not given the default fill value for x's type will be + used. + """ + if fill_value is None: + fill_value = default_fill_value (x) + d = filled(x, fill_value) + s = oldnumeric.sort(d, axis) + if getmask(x) is None: + return masked_array(s) + return masked_values(s, fill_value, copy=0) + +def diagonal(a, k = 0, axis1=0, axis2=1): + """diagonal(a,k=0,axis1=0, axis2=1) = the k'th diagonal of a""" + d = oldnumeric.diagonal(filled(a), k, axis1, axis2) + m = getmask(a) + if m is None: + return masked_array(d, m) + else: + return masked_array(d, oldnumeric.diagonal(m, k, axis1, axis2)) + +def argsort (x, axis = -1, fill_value=None): + """Treating masked values as if they have the value fill_value, + return sort indices for sorting along given axis. + if fill_value is None, use get_fill_value(x) + Returns a scipy array. + """ + d = filled(x, fill_value) + return oldnumeric.argsort(d, axis) + +def argmin (x, axis = -1, fill_value=None): + """Treating masked values as if they have the value fill_value, + return indices for minimum values along given axis. + if fill_value is None, use get_fill_value(x). + Returns a scipy array if x has more than one dimension. + Otherwise, returns a scalar index. + """ + d = filled(x, fill_value) + return oldnumeric.argmin(d, axis) + +def argmax (x, axis = -1, fill_value=None): + """Treating masked values as if they have the value fill_value, + return sort indices for maximum along given axis. + if fill_value is None, use -get_fill_value(x) if it exists. + Returns a scipy array if x has more than one dimension. + Otherwise, returns a scalar index. + """ + if fill_value is None: + fill_value = default_fill_value (x) + try: + fill_value = - fill_value + except: + pass + d = filled(x, fill_value) + return oldnumeric.argmax(d, axis) + +def fromfunction (f, s): + """apply f to s to create array as in umath.""" + return masked_array(numeric.fromfunction(f,s)) + +def asarray(data, dtype=None): + """asarray(data, dtype) = array(data, dtype, copy=0) + """ + if isinstance(data, MaskedArray) and \ + (dtype is None or dtype == data.dtype): + return data + return array(data, dtype=dtype, copy=0) + +masked = MaskedArray([0], int, mask=[1])[0:0] +masked = masked[0:0] diff --git a/numpy/core/machar.py b/numpy/core/machar.py new file mode 100644 index 000000000..e00d112ef --- /dev/null +++ b/numpy/core/machar.py @@ -0,0 +1,268 @@ +# +# Machine arithmetics - determine the parameters of the +# floating-point arithmetic system +# +# Author: Pearu Peterson, September 2003 +# + +__all__ = ['MachAr'] + +from numeric import array +from oldnumeric import any + +# Need to speed this up...especially for longfloat + +class MachAr(object): + """Diagnosing machine parameters. + + The following attributes are available: + + ibeta - radix in which numbers are represented + it - number of base-ibeta digits in the floating point mantissa M + machep - exponent of the smallest (most negative) power of ibeta that, + added to 1.0, + gives something different from 1.0 + eps - floating-point number beta**machep (floating point precision) + negep - exponent of the smallest power of ibeta that, substracted + from 1.0, gives something different from 1.0 + epsneg - floating-point number beta**negep + iexp - number of bits in the exponent (including its sign and bias) + minexp - smallest (most negative) power of ibeta consistent with there + being no leading zeros in the mantissa + xmin - floating point number beta**minexp (the smallest (in + magnitude) usable floating value) + maxexp - smallest (positive) power of ibeta that causes overflow + xmax - (1-epsneg)* beta**maxexp (the largest (in magnitude) + usable floating value) + irnd - in range(6), information on what kind of rounding is done + in addition, and on how underflow is handled + ngrd - number of 'guard digits' used when truncating the product + of two mantissas to fit the representation + + epsilon - same as eps + tiny - same as xmin + huge - same as xmax + precision - int(-log10(eps)) + resolution - 10**(-precision) + + Reference: + Numerical Recipies. + """ + def __init__(self, float_conv=float,int_conv=int, + float_to_float=float, + float_to_str = lambda v:'%24.16e' % v, + title = 'Python floating point number'): + """ + float_conv - convert integer to float (array) + int_conv - convert float (array) to integer + float_to_float - convert float array to float + float_to_str - convert array float to str + title - description of used floating point numbers + """ + one = float_conv(1) + two = one + one + zero = one - one + + # Do we really need to do this? Aren't they 2 and 2.0? + # Determine ibeta and beta + a = one + while 1: + a = a + a + temp = a + one + temp1 = temp - a + if any(temp1 - one != zero): + break + b = one + while 1: + b = b + b + temp = a + b + itemp = int_conv(temp-a) + if any(itemp != 0): + break + ibeta = itemp + beta = float_conv(ibeta) + + # Determine it and irnd + it = -1 + b = one + while 1: + it = it + 1 + b = b * beta + temp = b + one + temp1 = temp - b + if any(temp1 - one != zero): + break + + betah = beta / two + a = one + while 1: + a = a + a + temp = a + one + temp1 = temp - a + if any(temp1 - one != zero): + break + temp = a + betah + irnd = 0 + if any(temp-a != zero): + irnd = 1 + tempa = a + beta + temp = tempa + betah + if irnd==0 and any(temp-tempa != zero): + irnd = 2 + + # Determine negep and epsneg + negep = it + 3 + betain = one / beta + a = one + for i in range(negep): + a = a * betain + b = a + while 1: + temp = one - a + if any(temp-one != zero): + break + a = a * beta + negep = negep - 1 + # Prevent infinite loop on PPC with gcc 4.0: + if negep < 0: + raise RuntimeError, "could not determine machine tolerance " \ + "for 'negep'" + negep = -negep + epsneg = a + + # Determine machep and eps + machep = - it - 3 + a = b + + while 1: + temp = one + a + if any(temp-one != zero): + break + a = a * beta + machep = machep + 1 + eps = a + + # Determine ngrd + ngrd = 0 + temp = one + eps + if irnd==0 and any(temp*one - one != zero): + ngrd = 1 + + # Determine iexp + i = 0 + k = 1 + z = betain + t = one + eps + nxres = 0 + while 1: + y = z + z = y*y + a = z*one # Check here for underflow + temp = z*t + if any(a+a == zero) or any(abs(z)>=y): + break + temp1 = temp * betain + if any(temp1*beta == z): + break + i = i + 1 + k = k + k + if ibeta != 10: + iexp = i + 1 + mx = k + k + else: + iexp = 2 + iz = ibeta + while k >= iz: + iz = iz * ibeta + iexp = iexp + 1 + mx = iz + iz - 1 + + # Determine minexp and xmin + while 1: + xmin = y + y = y * betain + a = y * one + temp = y * t + if any(a+a != zero) and any(abs(y) < xmin): + k = k + 1 + temp1 = temp * betain + if any(temp1*beta == y) and any(temp != y): + nxres = 3 + xmin = y + break + else: + break + minexp = -k + + # Determine maxexp, xmax + if mx <= k + k - 3 and ibeta != 10: + mx = mx + mx + iexp = iexp + 1 + maxexp = mx + minexp + irnd = irnd + nxres + if irnd >= 2: + maxexp = maxexp - 2 + i = maxexp + minexp + if ibeta == 2 and not i: + maxexp = maxexp - 1 + if i > 20: + maxexp = maxexp - 1 + if any(a != y): + maxexp = maxexp - 2 + xmax = one - epsneg + if any(xmax*one != xmax): + xmax = one - beta*epsneg + xmax = xmax / (xmin*beta*beta*beta) + i = maxexp + minexp + 3 + for j in range(i): + if ibeta==2: + xmax = xmax + xmax + else: + xmax = xmax * beta + + self.ibeta = ibeta + self.it = it + self.negep = negep + self.epsneg = float_to_float(epsneg) + self._str_epsneg = float_to_str(epsneg) + self.machep = machep + self.eps = float_to_float(eps) + self._str_eps = float_to_str(eps) + self.ngrd = ngrd + self.iexp = iexp + self.minexp = minexp + self.xmin = float_to_float(xmin) + self._str_xmin = float_to_str(xmin) + self.maxexp = maxexp + self.xmax = float_to_float(xmax) + self._str_xmax = float_to_str(xmax) + self.irnd = irnd + + self.title = title + # Commonly used parameters + self.epsilon = self.eps + self.tiny = self.xmin + self.huge = self.xmax + + import math + self.precision = int(-math.log10(float_to_float(self.eps))) + ten = two + two + two + two + two + resolution = ten ** (-self.precision) + self.resolution = float_to_float(resolution) + self._str_resolution = float_to_str(resolution) + + def __str__(self): + return '''\ +Machine parameters for %(title)s +--------------------------------------------------------------------- +ibeta=%(ibeta)s it=%(it)s iexp=%(iexp)s ngrd=%(ngrd)s irnd=%(irnd)s +machep=%(machep)s eps=%(_str_eps)s (beta**machep == epsilon) +negep =%(negep)s epsneg=%(_str_epsneg)s (beta**epsneg) +minexp=%(minexp)s xmin=%(_str_xmin)s (beta**minexp == tiny) +maxexp=%(maxexp)s xmax=%(_str_xmax)s ((1-epsneg)*beta**maxexp == huge) +--------------------------------------------------------------------- +''' % self.__dict__ + + +if __name__ == '__main__': + print MachAr() diff --git a/numpy/core/matrix.py b/numpy/core/matrix.py new file mode 100644 index 000000000..1c7ca7cca --- /dev/null +++ b/numpy/core/matrix.py @@ -0,0 +1,273 @@ + +__all__ = ['matrix', 'bmat', 'mat', 'asmatrix'] + +import numeric as N +from numeric import ArrayType, concatenate, integer, multiply, power +from type_check import isscalar +from function_base import binary_repr +import types +import string as str_ +import sys + +# make translation table +_table = [None]*256 +for k in range(256): + _table[k] = chr(k) +_table = ''.join(_table) + +_numchars = str_.digits + ".-+jeEL" +del str_ +_todelete = [] +for k in _table: + if k not in _numchars: + _todelete.append(k) +_todelete = ''.join(_todelete) +del k + +def _eval(astr): + return eval(astr.translate(_table,_todelete)) + +def _convert_from_string(data): + rows = data.split(';') + newdata = [] + count = 0 + for row in rows: + trow = row.split(',') + newrow = [] + for col in trow: + temp = col.split() + newrow.extend(map(_eval,temp)) + if count == 0: + Ncols = len(newrow) + elif len(newrow) != Ncols: + raise ValueError, "Rows not the same size." + count += 1 + newdata.append(newrow) + return newdata + +def asmatrix(data, dtype=None): + """ Returns 'data' as a matrix. Unlike matrix(), no copy is performed + if 'data' is already a matrix or array. Equivalent to: + matrix(data, copy=False) + """ + return matrix(data, dtype=dtype, copy=False) + +class matrix(N.ndarray): + __array_priority__ = 10.0 + def __new__(subtype, data, dtype=None, copy=True): + if isinstance(data, matrix): + dtype2 = data.dtype + if (dtype is None): + dtype = dtype2 + if (dtype2 is dtype) and (not copy): + return data + return data.astype(dtype) + + if dtype is None: + if isinstance(data, N.ndarray): + dtype = data.dtype + intype = N.obj2dtype(dtype) + + if isinstance(data, types.StringType): + data = _convert_from_string(data) + + # now convert data to an array + arr = N.array(data, dtype=intype, copy=copy) + ndim = arr.ndim + shape = arr.shape + if (ndim > 2): + raise ValueError, "matrix must be 2-dimensional" + elif ndim == 0: + shape = (1,1) + elif ndim == 1: + shape = (1,shape[0]) + + fortran = False + if (ndim == 2) and arr.flags.fortran: + fortran = True + + if not (fortran or arr.flags.contiguous): + arr = arr.copy() + + ret = N.ndarray.__new__(subtype, shape, arr.dtypedescr, + buffer=arr, + fortran=fortran) + return ret + + def __array_finalize__(self, obj): + ndim = self.ndim + if ndim == 0: + self.shape = (1,1) + elif ndim == 1: + self.shape = (1,self.shape[0]) + return + + def __getitem__(self, index): + out = N.ndarray.__getitem__(self, index) + # Need to swap if slice is on first index + retscal = False + try: + n = len(index) + if (n==2): + if isinstance(index[0], types.SliceType): + if (isscalar(index[1])): + sh = out.shape + out.shape = (sh[1], sh[0]) + else: + if (isscalar(index[0])) and (isscalar(index[1])): + retscal = True + except TypeError: + pass + if retscal and out.shape == (1,1): # convert scalars + return out.A[0,0] + return out + + def __mul__(self, other): + if isinstance(other, N.ndarray) and other.ndim == 0: + return N.multiply(self, other) + else: + return N.dot(self, other) + + def __rmul__(self, other): + if isinstance(other, N.ndarray) and other.ndim == 0: + return N.multiply(other, self) + else: + return N.dot(other, self) + + def __imul__(self, other): + self[:] = self * other + return self + + def __pow__(self, other): + shape = self.shape + if len(shape) != 2 or shape[0] != shape[1]: + raise TypeError, "matrix is not square" + if type(other) in (type(1), type(1L)): + if other==0: + return matrix(N.identity(shape[0])) + if other<0: + x = self.I + other=-other + else: + x=self + result = x + if other <= 3: + while(other>1): + result=result*x + other=other-1 + return result + # binary decomposition to reduce the number of Matrix + # Multiplies for other > 3. + beta = binary_repr(other) + t = len(beta) + Z,q = x.copy(),0 + while beta[t-q-1] == '0': + Z *= Z + q += 1 + result = Z.copy() + for k in range(q+1,t): + Z *= Z + if beta[t-k-1] == '1': + result *= Z + return result + else: + raise TypeError, "exponent must be an integer" + + def __rpow__(self, other): + raise NotImplementedError + + def __repr__(self): + return repr(self.__array__()).replace('array','matrix') + + def __str__(self): + return str(self.__array__()) + + # Needed becase tolist method expects a[i] + # to have dimension a.ndim-1 + def tolist(self): + return self.__array__().tolist() + + def getA(self): + return self.__array__() + + def getT(self): + return self.transpose() + + def getH(self): + if issubclass(self.dtype, N.complexfloating): + return self.transpose().conjugate() + else: + return self.transpose() + + def getI(self): + from scipy.corelinalg import inv + return matrix(inv(self)) + + A = property(getA, None, doc="base array") + T = property(getT, None, doc="transpose") + H = property(getH, None, doc="hermitian (conjugate) transpose") + I = property(getI, None, doc="inverse") + + +def _from_string(str,gdict,ldict): + rows = str.split(';') + rowtup = [] + for row in rows: + trow = row.split(',') + newrow = [] + for x in trow: + newrow.extend(x.split()) + trow = newrow + coltup = [] + for col in trow: + col = col.strip() + try: + thismat = ldict[col] + except KeyError: + try: + thismat = gdict[col] + except KeyError: + raise KeyError, "%s not found" % (col,) + + coltup.append(thismat) + rowtup.append(concatenate(coltup,axis=-1)) + return concatenate(rowtup,axis=0) + + +def bmat(obj,ldict=None, gdict=None): + """Build a matrix object from string, nested sequence, or array. + + Ex: F = bmat('A, B; C, D') + F = bmat([[A,B],[C,D]]) + F = bmat(r_[c_[A,B],c_[C,D]]) + + all produce the same Matrix Object [ A B ] + [ C D ] + + if A, B, C, and D are appropriately shaped 2-d arrays. + """ + if isinstance(obj, types.StringType): + if gdict is None: + # get previous frame + frame = sys._getframe().f_back + glob_dict = frame.f_globals + loc_dict = frame.f_locals + else: + glob_dict = gdict + loc_dict = ldict + + return matrix(_from_string(obj, glob_dict, loc_dict)) + + if isinstance(obj, (types.TupleType, types.ListType)): + # [[A,B],[C,D]] + arr_rows = [] + for row in obj: + if isinstance(row, ArrayType): # not 2-d + return matrix(concatenate(obj,axis=-1)) + else: + arr_rows.append(concatenate(row,axis=-1)) + return matrix(concatenate(arr_rows,axis=0)) + if isinstance(obj, ArrayType): + return matrix(obj) + +mat = matrix diff --git a/numpy/core/memmap.py b/numpy/core/memmap.py new file mode 100644 index 000000000..8a791e5aa --- /dev/null +++ b/numpy/core/memmap.py @@ -0,0 +1,88 @@ +__all__ = ['memmap'] + +import mmap +from numeric import uint8, ndarray, dtypedescr +from numerictypes import nbytes + +valid_filemodes = ["r", "c", "r+", "w+"] +writeable_filemodes = ["r+","w+"] + +mode_equivalents = { + "readonly":"r", + "copyonwrite":"c", + "readwrite":"r+", + "write":"w+" + } + + +class memmap(ndarray): + def __new__(subtype, name, dtype=uint8, mode='r+', offset=0, + shape=None, fortran=0): + + try: + mode = mode_equivalents[mode] + except KeyError: + if mode not in valid_filemodes: + raise ValueError("mode must be one of %s" % \ + (valid_filemodes + mode_equivalents.keys())) + + fid = file(name, (mode == 'c' and 'r' or mode)+'b') + + if (mode == 'w+') and shape is None: + raise ValueError, "shape must be given" + + fid.seek(0,2) + flen = fid.tell() + descr = dtypedescr(dtype) + _dbytes = descr.itemsize + + if shape is None: + bytes = flen-offset + if (bytes % _dbytes): + fid.close() + raise ValueError, "Size of available data is not a "\ + "multiple of data-type size." + size = bytes // _dbytes + shape = (size,) + else: + if not isinstance(shape, tuple): + shape = (shape,) + size = 1 + for k in shape: + size *= k + + bytes = offset + size*_dbytes + + if mode == 'w+' or (mode == 'r+' and flen < bytes): + fid.seek(bytes-1,0) + fid.write(chr(0)) + fid.flush() + + if mode == 'c': + acc = mmap.ACCESS_COPY + elif mode == 'r': + acc = mmap.ACCESS_READ + else: + acc = mmap.ACCESS_WRITE + + mm = mmap.mmap(fid.fileno(), bytes, access=acc) + + self = ndarray.__new__(subtype, shape, dtype=descr, buffer=mm, + offset=offset, fortran=fortran) + self._mmap = mm + self._offset = offset + self._mode = mode + self._size = size + self._name = name + + fid.close() + return self + + def sync(self): + self._mmap.flush() + + def __del__(self): + self._mmap.flush() + del self._mmap + + diff --git a/numpy/core/mlab.py b/numpy/core/mlab.py new file mode 100644 index 000000000..749600d9b --- /dev/null +++ b/numpy/core/mlab.py @@ -0,0 +1,14 @@ +# This module is for compatibility only. All functions are defined elsewhere. + +from numeric import * + +from twodim_base import eye, tri, diag, fliplr, flipud, rot90, tril, triu +from oldnumeric import amax as max +from oldnumeric import amin as min +from function_base import msort, median, trapz, diff, cov, corrcoef, kaiser, blackman, \ + bartlett, hanning, hamming, sinc, angle +from oldnumeric import cumsum, ptp, mean, std, prod, cumprod, squeeze +from polynomial import roots + +from scipy.random import rand, randn +from scipy.corelinalg import eig, svd diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py new file mode 100644 index 000000000..03a2e520f --- /dev/null +++ b/numpy/core/numeric.py @@ -0,0 +1,428 @@ +__all__ = ['newaxis', 'ndarray', 'bigndarray', 'flatiter', 'ufunc', + 'arange', 'array', 'zeros', 'empty', 'broadcast', 'dtypedescr', + 'fromstring', 'fromfile', 'frombuffer','newbuffer','getbuffer', + 'where', 'concatenate', 'fastCopyAndTranspose', 'lexsort', + 'register_dtype', 'set_numeric_ops', 'can_cast', + 'asarray', 'asanyarray', 'isfortran', 'zeros_like', 'empty_like', + 'correlate', 'convolve', 'inner', 'dot', 'outer', 'vdot', + 'alterdot', 'restoredot', 'cross', + 'array2string', 'get_printoptions', 'set_printoptions', + 'array_repr', 'array_str', 'set_string_function', + 'little_endian', + 'indices', 'fromfunction', + 'load', 'loads', + 'ones', 'identity', 'allclose', + 'seterr', 'geterr', 'setbufsize', 'getbufsize', + 'seterrcall', 'geterrcall', + 'Inf', 'inf', 'infty', 'Infinity', + 'nan', 'NaN'] + +import sys +import multiarray +import umath +from umath import * +import numerictypes +from numerictypes import * + +def extend_all(module): + adict = {} + for a in __all__: + adict[a] = 1 + try: + mall = getattr(module, '__all__') + except AttributeError: + mall = [k for k in module.__dict__.keys() if not k.startswith('_')] + for a in mall: + if a not in adict: + __all__.append(a) + +extend_all(umath) +extend_all(numerictypes) + +newaxis = None + +ndarray = multiarray.ndarray +bigndarray = multiarray.bigndarray +flatiter = multiarray.flatiter +broadcast = multiarray.broadcast +dtypedescr=multiarray.dtypedescr +ufunc = type(sin) + +arange = multiarray.arange +array = multiarray.array +zeros = multiarray.zeros +empty = multiarray.empty +fromstring = multiarray.fromstring +fromfile = multiarray.fromfile +frombuffer = multiarray.frombuffer +newbuffer = multiarray.newbuffer +getbuffer = multiarray.getbuffer +where = multiarray.where +concatenate = multiarray.concatenate +fastCopyAndTranspose = multiarray._fastCopyAndTranspose +register_dtype = multiarray.register_dtype +set_numeric_ops = multiarray.set_numeric_ops +can_cast = multiarray.can_cast +lexsort = multiarray.lexsort + + +def asarray(a, dtype=None, fortran=False): + """returns a as an array. Unlike array(), + no copy is performed if a is already an array. Subclasses are converted + to base class ndarray. + """ + return array(a, dtype, copy=False, fortran=fortran) + +def asanyarray(a, dtype=None, copy=False, fortran=False): + """will pass subclasses through... + """ + return array(a, dtype, copy=False, fortran=fortran, subok=1) + +def isfortran(a): + return a.flags['FNC'] + +# from Fernando Perez's IPython +def zeros_like(a): + """Return an array of zeros of the shape and typecode of a. + + If you don't explicitly need the array to be zeroed, you should instead + use empty_like(), which is faster as it only allocates memory.""" + a = asanyarray(a) + return a.__array_wrap__(zeros(a.shape, a.dtype, a.flags['FNC'])) + +def empty_like(a): + """Return an empty (uninitialized) array of the shape and typecode of a. + + Note that this does NOT initialize the returned array. If you require + your array to be initialized, you should use zeros_like(). + + """ + a = asanyarray(a) + return a.__array_wrap__(empty(a.shape, a.dtype, a.flags['FNC'])) + +# end Fernando's utilities + +_mode_from_name_dict = {'v': 0, + 's' : 1, + 'f' : 2} + +def _mode_from_name(mode): + if isinstance(mode, type("")): + return _mode_from_name_dict[mode.lower()[0]] + return mode + +def correlate(a,v,mode='valid'): + mode = _mode_from_name(mode) + return multiarray.correlate(a,v,mode) + + +def convolve(a,v,mode='full'): + """Returns the discrete, linear convolution of 1-D + sequences a and v; mode can be 0 (valid), 1 (same), or 2 (full) + to specify size of the resulting sequence. + """ + if (len(v) > len(a)): + a, v = v, a + mode = _mode_from_name(mode) + return multiarray.correlate(a,asarray(v)[::-1],mode) + + +inner = multiarray.inner +dot = multiarray.dot + +def outer(a,b): + """outer(a,b) returns the outer product of two vectors. + result(i,j) = a(i)*b(j) when a and b are vectors + Will accept any arguments that can be made into vectors. + """ + a = asarray(a) + b = asarray(b) + return a.ravel()[:,newaxis]*b.ravel()[newaxis,:] + +def vdot(a, b): + """Returns the dot product of 2 vectors (or anything that can be made into + a vector). NB: this is not the same as `dot`, as it takes the conjugate + of its first argument if complex and always returns a scalar.""" + return dot(asarray(a).ravel().conj(), asarray(b).ravel()) + +# try to import blas optimized dot if available +try: + # importing this changes the dot function for basic 4 types + # to blas-optimized versions. + from _dotblas import dot, vdot, inner, alterdot, restoredot +except ImportError: + def alterdot(): + pass + def restoredot(): + pass + + +def _move_axis_to_0(a, axis): + if axis == 0: + return a + n = a.ndim + if axis < 0: + axis += n + axes = range(1, axis+1) + [0,] + range(axis+1, n) + return a.transpose(axes) + +def cross(a, b, axisa=-1, axisb=-1, axisc=-1): + """Return the cross product of two (arrays of) vectors. + + The cross product is performed over the last axis of a and b by default, + and can handle axes with dimensions 2 and 3. For a dimension of 2, + the z-component of the equivalent three-dimensional cross product is + returned. + """ + a = _move_axis_to_0(asarray(a), axisa) + b = _move_axis_to_0(asarray(b), axisb) + msg = "incompatible dimensions for cross product\n"\ + "(dimension must be 2 or 3)" + if (a.shape[0] not in [2,3]) or (b.shape[0] not in [2,3]): + raise ValueError(msg) + if a.shape[0] == 2: + if (b.shape[0] == 2): + cp = a[0]*b[1] - a[1]*b[0] + if cp.ndim == 0: + return cp + else: + return cp.swapaxes(0,axisc) + else: + x = a[1]*b[2] + y = -a[0]*b[2] + z = a[0]*b[1] - a[1]*b[0] + elif a.shape[0] == 3: + if (b.shape[0] == 3): + x = a[1]*b[2] - a[2]*b[1] + y = a[2]*b[0] - a[0]*b[2] + z = a[0]*b[1] - a[1]*b[0] + else: + x = -a[2]*b[1] + y = a[2]*b[0] + z = a[0]*b[1] - a[1]*b[0] + cp = array([x,y,z]) + if cp.ndim == 1: + return cp + else: + return cp.swapaxes(0,axisc) + + +#Use numarray's printing function +from arrayprint import array2string, get_printoptions, set_printoptions + +_typelessdata = [int_, float_, complex_] +if issubclass(intc, int): + _typelessdata.append(intc) + +if issubclass(longlong, int): + _typelessdata.append(longlong) + +def array_repr(arr, max_line_width=None, precision=None, suppress_small=None): + if arr.size > 0 or arr.shape==(0,): + lst = array2string(arr, max_line_width, precision, suppress_small, + ', ', "array(") + else: # show zero-length shape unless it is (0,) + lst = "[], shape=%s" % (repr(arr.shape),) + typeless = arr.dtype in _typelessdata + + if arr.__class__ is not ndarray: + cName= arr.__class__.__name__ + else: + cName = "array" + if typeless and arr.size: + return cName + "(%s)" % lst + else: + typename=arr.dtype.__name__[:-8] + if issubclass(arr.dtype, flexible): + if typename not in ['unicode','string','void']: + typename = arr.dtype.__name__ + typename = "(%s,%d)" % (typename, arr.itemsize) + return cName + "(%s, dtype=%s)" % (lst, typename) + +def array_str(a, max_line_width=None, precision=None, suppress_small=None): + return array2string(a, max_line_width, precision, suppress_small, ' ', "") + +set_string_function = multiarray.set_string_function +set_string_function(array_str, 0) +set_string_function(array_repr, 1) + + +little_endian = (sys.byteorder == 'little') + +def indices(dimensions, dtype=int_): + """indices(dimensions,dtype=int_) returns an array representing a grid + of indices with row-only, and column-only variation. + """ + tmp = ones(dimensions, dtype) + lst = [] + for i in range(len(dimensions)): + lst.append( add.accumulate(tmp, i, )-1 ) + return array(lst) + +def fromfunction(function, dimensions, **kwargs): + """fromfunction(function, dimensions) returns an array constructed by + calling function on a tuple of number grids. The function should + accept as many arguments as there are dimensions which is a list of + numbers indicating the length of the desired output for each axis. + + The function can also accept keyword arguments which will be + passed in as well. + """ + args = indices(dimensions) + return function(*args,**kwargs) + + +from cPickle import load, loads +_cload = load +_file = file + +def load(file): + if isinstance(file, type("")): + file = _file(file,"rb") + return _cload(file) + +# These are all essentially abbreviations +# These might wind up in a special abbreviations module + +def ones(shape, dtype=int_, fortran=False): + """ones(shape, dtype=int_) returns an array of the given + dimensions which is initialized to all ones. + """ + # This appears to be slower... + #a = empty(shape, dtype, fortran) + #a.fill(1) + a = zeros(shape, dtype, fortran) + a+=1 + return a + +def identity(n,dtype=int_): + """identity(n) returns the identity matrix of shape n x n. + """ + a = array([1]+n*[0],dtype=dtype) + b = empty((n,n),dtype=dtype) + b.flat = a + return b + +def allclose (a, b, rtol=1.e-5, atol=1.e-8): + """ allclose(a,b,rtol=1.e-5,atol=1.e-8) + Returns true if all components of a and b are equal + subject to given tolerances. + The relative error rtol must be positive and << 1.0 + The absolute error atol comes into play for those elements + of y that are very small or zero; it says how small x must be also. + """ + x = array(a, copy=False) + y = array(b, copy=False) + d = less(absolute(x-y), atol + rtol * absolute(y)) + return d.ravel().all() + +def _setpyvals(lst, frame, where=0): + if not isinstance(lst, list) or len(lst) != 3: + raise ValueError, "Invalid pyvalues (length 3 list needed)." + + try: + wh = where.lower()[0] + except (AttributeError, TypeError, IndexError): + wh = None + + if where==0 or wh == 'l': + frame.f_locals[UFUNC_PYVALS_NAME] = lst + elif where == 1 or wh == 'g': + frame.f_globals[UFUNC_PYVALS_NAME] = lst + elif where == 2 or wh == 'b': + frame.f_builtins[UFUNC_PYVALS_NAME] = lst + + umath.update_use_defaults() + return + +def _getpyvals(frame): + try: + return frame.f_locals[UFUNC_PYVALS_NAME] + except KeyError: + try: + return frame.f_globals[UFUNC_PYVALS_NAME] + except KeyError: + try: + return frame.f_builtins[UFUNC_PYVALS_NAME] + except KeyError: + return [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None] + +_errdict = {"ignore":ERR_IGNORE, + "warn":ERR_WARN, + "raise":ERR_RAISE, + "call":ERR_CALL} + +_errdict_rev = {} +for key in _errdict.keys(): + _errdict_rev[_errdict[key]] = key +del key + +def seterr(divide="ignore", over="ignore", under="ignore", + invalid="ignore", where=0): + maskvalue = ((_errdict[divide] << SHIFT_DIVIDEBYZERO) + + (_errdict[over] << SHIFT_OVERFLOW ) + + (_errdict[under] << SHIFT_UNDERFLOW) + + (_errdict[invalid] << SHIFT_INVALID)) + + frame = sys._getframe().f_back + pyvals = _getpyvals(frame) + pyvals[1] = maskvalue + _setpyvals(pyvals, frame, where) + +def geterr(): + frame = sys._getframe().f_back + maskvalue = _getpyvals(frame)[1] + + mask = 3 + res = {} + val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask + res['divide'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_OVERFLOW) & mask + res['over'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_UNDERFLOW) & mask + res['under'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_INVALID) & mask + res['invalid'] = _errdict_rev[val] + return res + +def setbufsize(size, where=0): + if size > 10e6: + raise ValueError, "Very big buffers.. %s" % size + + frame = sys._getframe().f_back + pyvals = _getpyvals(frame) + pyvals[0] = size + _setpyvals(pyvals, frame, where) + +def getbufsize(): + frame = sys._getframe().f_back + return _getpyvals(frame)[0] + +def seterrcall(func, where=0): + if not callable(func): + raise ValueError, "Only callable can be used as callback" + frame = sys._getframe().f_back + pyvals = _getpyvals(frame) + pyvals[2] = func + _setpyvals(pyvals, frame, where) + +def geterrcall(): + frame = sys._getframe().f_back + return _getpyvals(frame)[2] + +def _setdef(): + frame = sys._getframe() + defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None] + frame.f_globals[UFUNC_PYVALS_NAME] = defval + frame.f_builtins[UFUNC_PYVALS_NAME] = defval + umath.update_use_defaults() + +# set the default values +_setdef() + +Inf = inf = infty = Infinity = PINF +nan = NaN = NAN + +import oldnumeric +from oldnumeric import * +extend_all(oldnumeric) diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py new file mode 100644 index 000000000..81d80c73f --- /dev/null +++ b/numpy/core/numerictypes.py @@ -0,0 +1,382 @@ +# Borrowed and adapted from numarray + +"""numerictypes: Define the numeric type objects + +This module is designed so 'from numerictypes import *' is safe. +Exported symbols include: + + Dictionary with all registered number types (including aliases): + typeDict + + Type objects (not all will be available, depends on platform): + see variable arraytypes for which ones you have + + Bit-width names + + int8 int16 int32 int64 int128 + uint8 uint16 uint32 uint64 uint128 + float16 float32 float64 float96 float128 float256 + complex32 complex64 complex128 complex192 complex256 complex512 + + c-based names + + bool_ + + object_ + + void, str_, unicode_ + + byte, ubyte, + short, ushort + intc, uintc, + intp, uintp, + int_, uint, + longlong, ulonglong, + + single, csingle, + float_, complex_, + longfloat, clongfloat, + + As part of the type-hierarchy: xx -- is bit-width + + generic + bool_ + numeric + integer + signedinteger (intxx) + byte + short + intc + intp int0 + int_ + longlong + unsignedinteger (uintxx) + ubyte + ushort + uintc + uintp uint0 + uint_ + ulonglong + floating (floatxx) + single + float_ (double) + longfloat + complexfloating (complexxx) + csingle + complex_ (cfloat, cdouble) + clongfloat + + flexible + character + str_ (string) + unicode_ + void + + object_ + +$Id: numerictypes.py,v 1.17 2005/09/09 22:20:06 teoliphant Exp $ +""" + +# we add more at the bottom +__all__ = ['typeDict', 'typeNA', 'arraytypes', 'ScalarType', 'obj2dtype', 'cast', 'nbytes', 'dtype2char'] + +from multiarray import typeinfo, ndarray, array, empty +import types as _types + +# we don't export these for import *, but we do want them accessible +# as numerictypes.bool, etc. +from __builtin__ import bool, int, long, float, complex, object, unicode, str + +typeDict = {} # Contains all leaf-node numeric types with aliases +typeNA = {} # Contails all leaf-node types -> numarray type equivalences +allTypes = {} # Collect the types we will add to the module here + +def _evalname(name): + k = 0 + for ch in name: + if ch in '0123456789': + break + k += 1 + try: + bits = int(name[k:]) + except ValueError: + bits = 0 + base = name[:k] + return base, bits + +def bitname(obj): + """Return a bit-width name for a given type object""" + name = obj.__name__[:-8] + base = '' + char = '' + try: + info = typeinfo[name.upper()] + assert(info[-1] == obj) # sanity check + bits = info[2] + + except KeyError: # bit-width name + base, bits = _evalname(name) + char = base[0] + + if name == 'bool': + char = 'b' + base = 'bool' + elif name=='string': + char = 'S' + base = 'string' + elif name=='unicode': + char = 'U' + base = 'unicode' + elif name=='void': + char = 'V' + base = 'void' + elif name=='object': + char = 'O' + base = 'object' + bits = 0 + + bytes = bits / 8 + + if char != '' and bytes != 0: + char = "%s%d" % (char, bytes) + + return base, bits, char + +revdict = {} + +def _add_types(): + for a in typeinfo.keys(): + name = a.lower() + if isinstance(typeinfo[a], type(())): + typeobj = typeinfo[a][-1] + + # define C-name and insert typenum and typechar references also + allTypes[name] = typeobj + typeDict[name] = typeobj + typeDict[typeinfo[a][0]] = typeobj + typeDict[typeinfo[a][1]] = typeobj + + # insert bit-width version for this class (if relevant) + base, bit, char = bitname(typeobj) + revdict[typeobj] = (typeinfo[a][:-1], (base, bit, char), a) + if base != '': + allTypes["%s%d" % (base, bit)] = typeobj + typeDict["%s%d" % (base, bit)] = typeobj + if base == 'uint': + tmpstr = 'UInt%d' % bit + typeDict[tmpstr] = typeobj + na_name = tmpstr + elif base == 'complex': + na_num = '%s%d' % (base.capitalize(), bit/2) + elif base == 'bool': + na_name = base.capitalize() + typeDict[na_name] = typeobj + else: + na_name = "%s%d" % (base.capitalize(), bit) + typeDict[na_name] = typeobj + typeNA[na_name] = typeobj + typeNA[typeobj] = na_name + typeNA[typeinfo[a][0]] = na_name + if char != '': + typeDict[char] = typeobj + typeNA[char] = na_name + else: # generic class + allTypes[name] = typeinfo[a] +_add_types() + + +# We use these later +void = allTypes['void'] +generic = allTypes['generic'] + +# +# Rework the Python names (so that float and complex and int are consistent +# with Python usage) +# +def _set_up_aliases(): + type_pairs = [('complex_', 'cdouble'), + ('int0', 'intp'), + ('uint0', 'uintp'), + ('single', 'float'), + ('csingle', 'cfloat'), + ('float_', 'double'), + ('intc', 'int'), + ('uintc', 'uint'), + ('int_', 'long'), + ('uint', 'ulong'), + ('cfloat', 'cdouble'), + ('longfloat', 'longdouble'), + ('clongfloat', 'clongdouble'), + ('bool_', 'bool'), + ('unicode_', 'unicode'), + ('str_', 'string'), + ('object_', 'object')] + for alias, t in type_pairs: + allTypes[alias] = allTypes[t] + # Remove aliases overriding python types + for t in ['ulong', 'object', 'unicode', 'int', 'long', 'float', + 'complex', 'bool']: + try: + del allTypes[t] + except KeyError: + pass +_set_up_aliases() + +# Now, construct dictionary to lookup character codes from types +_dtype2char_dict = {} +def _construct_char_code_lookup(): + for name in typeinfo.keys(): + tup = typeinfo[name] + if isinstance(tup, tuple): + if tup[0] not in ['p','P']: + _dtype2char_dict[tup[-1]] = tup[0] +_construct_char_code_lookup() + + +arraytypes = {'int': [], + 'uint':[], + 'float':[], + 'complex':[], + 'others':[bool,object,str,unicode,void]} + +def _add_array_type(typename, bits): + try: + t = allTypes['%s%d' % (typename, bits)] + except KeyError: + pass + else: + arraytypes[typename].append(t) + +def _set_array_types(): + ibytes = [1, 2, 4, 8, 16, 32, 64] + fbytes = [2, 4, 8, 10, 12, 16, 32, 64] + for bytes in ibytes: + bits = 8*bytes + _add_array_type('int', bits) + _add_array_type('uint', bits) + for bytes in fbytes: + bits = 8*bytes + _add_array_type('float', bits) + _add_array_type('complex', bits) +_set_array_types() + +genericTypeRank = ['bool', 'int8', 'uint8', 'int16', 'uint16', + 'int32', 'uint32', 'int64', 'uint64', 'int128', + 'uint128', 'float16', + 'float32', 'float64', 'float80', 'float96', 'float128', + 'float256', + 'complex32', 'complex64', 'complex128', 'complex160', + 'complex192', 'complex256', 'complex512', 'object'] + +def maximum_dtype(t): + """returns the type of highest precision of the same general kind as 't'""" + g = obj2dtype(t) + if g is None: + return t + t = g + name = t.__name__[:-8] + base, bits = _evalname(name) + if bits == 0: + return t + else: + return arraytypes[base][-1] + +_python_types = {int : 'int_', + float: 'float_', + complex: 'complex_', + bool: 'bool_', + str: 'string', + unicode: 'unicode_', + _types.BufferType: 'void', + } +def _python_type(t): + """returns the type corresponding to a certain Python type""" + if not isinstance(t, _types.TypeType): + t = type(t) + return allTypes[_python_types.get(t, 'object_')] + +def isdtype(rep): + """Determines whether the given object represents + a numeric array type.""" + try: + char = dtype2char(rep) + return True + except (KeyError, ValueError): + return False + +def obj2dtype(rep, default=None): + try: + if issubclass(rep, generic): + return rep + except TypeError: + pass + if isinstance(rep, type): + return _python_type(rep) + if isinstance(rep, ndarray): + return rep.dtype + res = typeDict.get(rep, default) + return res + + +# This dictionary allows look up based on any alias for a type +class _typedict(dict): + def __getitem__(self, obj): + return dict.__getitem__(self, obj2dtype(obj)) + +nbytes = _typedict() +_alignment = _typedict() +_maxvals = _typedict() +_minvals = _typedict() +def _construct_lookups(): + for name, val in typeinfo.iteritems(): + if not isinstance(val, tuple): + continue + obj = val[-1] + nbytes[obj] = val[2] / 8 + _alignment[obj] = val[3] + if (len(val) > 5): + _maxvals[obj] = val[4] + _minvals[obj] = val[5] + else: + _maxvals[obj] = None + _minvals[obj] = None + +_construct_lookups() + +def dtype2char(dtype): + dtype = obj2dtype(dtype) + if dtype is None: + raise ValueError, "unrecognized type" + return _dtype2char_dict[dtype] + +# Create dictionary of casting functions that wrap sequences +# indexed by type or type character + + +cast = _typedict() +ScalarType = [_types.IntType, _types.FloatType, + _types.ComplexType, _types.LongType, _types.BooleanType, + _types.StringType, _types.UnicodeType, _types.BufferType] +ScalarType.extend(_dtype2char_dict.keys()) +ScalarType = tuple(ScalarType) +for key in _dtype2char_dict.keys(): + cast[key] = lambda x, k=key : array(x, copy=False).astype(k) + + +_unicodesize = array('u','U').itemsize + +# Create the typestring lookup dictionary +_typestr = _typedict() +for key in _dtype2char_dict.keys(): + if issubclass(key, allTypes['flexible']): + _typestr[key] = _dtype2char_dict[key] + else: + _typestr[key] = empty((1,),key).dtypestr[1:] + +# Now add the types we've determined to this module +for key in allTypes: + globals()[key] = allTypes[key] + __all__.append(key) + +del key + diff --git a/numpy/core/oldnumeric.py b/numpy/core/oldnumeric.py new file mode 100644 index 000000000..9cf87218e --- /dev/null +++ b/numpy/core/oldnumeric.py @@ -0,0 +1,432 @@ +# Compatibility module containing deprecated names + +__all__ = ['asarray', 'array', 'concatenate', + 'NewAxis', + 'UFuncType', 'UfuncType', 'ArrayType', 'arraytype', + 'LittleEndian', 'Bool', + 'Character', 'UnsignedInt8', 'UnsignedInt16', 'UnsignedInt', + 'UInt8','UInt16','UInt32', + # UnsignedInt64 and Unsigned128 added below if possible + # same for Int64 and Int128, Float128, and Complex128 + 'Int8', 'Int16', 'Int32', + 'Int0', 'Int', 'Float0', 'Float', 'Complex0', 'Complex', + 'PyObject', 'Float32', 'Float64', + 'Complex32', 'Complex64', + 'typecodes', 'sarray', 'arrayrange', 'cross_correlate', + 'matrixmultiply', 'outerproduct', 'innerproduct', + # from cPickle + 'dump', 'dumps', + # functions that are now methods + 'take', 'reshape', 'choose', 'repeat', 'put', 'putmask', + 'swapaxes', 'transpose', 'sort', 'argsort', 'argmax', 'argmin', + 'searchsorted', 'alen', + 'resize', 'diagonal', 'trace', 'ravel', 'nonzero', 'shape', + 'compress', 'clip', 'sum', 'product', 'prod', 'sometrue', 'alltrue', + 'any', 'all', 'cumsum', 'cumproduct', 'cumprod', 'ptp', 'ndim', + 'rank', 'size', 'around', 'mean', 'std', 'var', 'squeeze', 'amax', 'amin' + ] + +import multiarray as mu +import umath as um +import numerictypes as nt +from numeric import asarray, array, correlate, outer, concatenate +import sys +_dt_ = nt.dtype2char + +#Use this to add a new axis to an array +#compatibility only +NewAxis = None + +#deprecated +UFuncType = type(um.sin) +UfuncType = type(um.sin) +ArrayType = mu.ndarray +arraytype = mu.ndarray + +LittleEndian = (sys.byteorder == 'little') + +# backward compatible names from old Precision.py + +Character = 'S1' +UnsignedInt8 = _dt_(nt.uint8) +UInt8 = UnsignedInt8 +UnsignedInt16 = _dt_(nt.uint16) +UInt16 = UnsignedInt16 +UnsignedInt32 = _dt_(nt.uint32) +UInt32 = UnsignedInt32 +UnsignedInt = _dt_(nt.uint) + +try: + UnsignedInt64 = _dt_(nt.uint64) +except AttributeError: + pass +else: + UInt64 = UnsignedInt64 + __all__ += ['UnsignedInt64', 'UInt64'] +try: + UnsignedInt128 = _dt_(nt.uint128) +except AttributeError: + pass +else: + UInt128 = UnsignedInt128 + __all__ += ['UnsignedInt128','UInt128'] + +Int8 = _dt_(nt.int8) +Int16 = _dt_(nt.int16) +Int32 = _dt_(nt.int32) + +try: + Int64 = _dt_(nt.int64) +except AttributeError: + pass +else: + __all__ += ['Int64'] + +try: + Int128 = _dt_(nt.int128) +except AttributeError: + pass +else: + __all__ += ['Int128'] + +Bool = _dt_(bool) +Int0 = _dt_(int) +Int = _dt_(int) +Float0 = _dt_(float) +Float = _dt_(float) +Complex0 = _dt_(complex) +Complex = _dt_(complex) +PyObject = _dt_(nt.object_) +Float32 = _dt_(nt.float32) +Float64 = _dt_(nt.float64) + +try: + Float128 = _dt_(nt.float128) +except AttributeError: + pass +else: + __all__ += ['Float128'] + +Complex32 = _dt_(nt.complex64) +Complex64 = _dt_(nt.complex128) + +try: + Complex128 = _dt_(nt.complex256) +except AttributeError: + pass +else: + __all__ += ['Complex128'] + +typecodes = {'Character':'S1', + 'Integer':'bhilqp', + 'UnsignedInteger':'BHILQP', + 'Float':'fdg', + 'Complex':'FDG', + 'AllInteger':'bBhHiIlLqQpP', + 'AllFloat':'fdgFDG', + 'All':'?bhilqpBHILQPfdgFDGSUVO'} + +def sarray(a, dtype=None, copy=False): + return array(a, dtype, copy) + +# backward compatibility +arrayrange = mu.arange +cross_correlate = correlate + +# deprecated names +matrixmultiply = mu.dot +outerproduct = outer +innerproduct = mu.inner + +from cPickle import dump, dumps + +# functions that are now methods + +def take(a, indices, axis=0): + a = asarray(a) + return a.take(indices, axis) + +def reshape(a, newshape): + """Change the shape of a to newshape. Return a new view object. + """ + return asarray(a).reshape(newshape) + +def choose(a, choices): + a = asarray(a) + return a.choose(choices) + +def repeat(a, repeats, axis=0): + """repeat elements of a repeats times along axis + repeats is a sequence of length a.shape[axis] + telling how many times to repeat each element. + If repeats is an integer, it is interpreted as + a tuple of length a.shape[axis] containing repeats. + The argument a can be anything array(a) will accept. + """ + a = array(a, copy=False) + return a.repeat(repeats, axis) + +def put (a, ind, v): + """put(a, ind, v) results in a[n] = v[n] for all n in ind + If v is shorter than mask it will be repeated as necessary. + In particular v can be a scalar or length 1 array. + The routine put is the equivalent of the following (although the loop + is in C for speed): + + ind = array(indices, copy=False) + v = array(values, copy=False).astype(a, a.dtype) + for i in ind: a.flat[i] = v[i] + a must be a contiguous Numeric array. + """ + return a.put(v,ind) + +def putmask (a, mask, v): + """putmask(a, mask, v) results in a = v for all places mask is true. + If v is shorter than mask it will be repeated as necessary. + In particular v can be a scalar or length 1 array. + """ + return a.putmask(v, mask) + +def swapaxes(a, axis1, axis2): + """swapaxes(a, axis1, axis2) returns array a with axis1 and axis2 + interchanged. + """ + a = array(a, copy=False) + return a.swapaxes(axis1, axis2) + +def transpose(a, axes=None): + """transpose(a, axes=None) returns array with dimensions permuted + according to axes. If axes is None (default) returns array with + dimensions reversed. + """ + a = array(a,copy=False) + return a.transpose(axes) + +def sort(a, axis=-1): + """sort(a,axis=-1) returns array with elements sorted along given axis. + """ + a = array(a, copy=True) + a.sort(axis) + return a + +def argsort(a, axis=-1): + """argsort(a,axis=-1) return the indices into a of the sorted array + along the given axis, so that take(a,result,axis) is the sorted array. + """ + a = array(a, copy=False) + return a.argsort(axis) + +def argmax(a, axis=-1): + """argmax(a,axis=-1) returns the indices to the maximum value of the + 1-D arrays along the given axis. + """ + a = array(a, copy=False) + return a.argmax(axis) + +def argmin(a, axis=-1): + """argmin(a,axis=-1) returns the indices to the minimum value of the + 1-D arrays along the given axis. + """ + a = array(a,copy=False) + return a.argmin(axis) + +def searchsorted(a, v): + """searchsorted(a, v) + """ + a = array(a,copy=False) + return a.searchsorted(v) + +def resize(a, new_shape): + """resize(a,new_shape) returns a new array with the specified shape. + The original array's total size can be any size. It + fills the new array with repeated copies of a. + + Note that a.resize(new_shape) will fill array with 0's + beyond current definition of a. + """ + + a = ravel(a) + Na = len(a) + if not Na: return mu.zeros(new_shape, a.dtypechar) + total_size = um.multiply.reduce(new_shape) + n_copies = int(total_size / Na) + extra = total_size % Na + + if extra != 0: + n_copies = n_copies+1 + extra = Na-extra + + a = concatenate( (a,)*n_copies) + if extra > 0: + a = a[:-extra] + + return reshape(a, new_shape) + +def squeeze(a): + "Returns a with any ones from the shape of a removed" + return asarray(a).squeeze() + +def diagonal(a, offset=0, axis1=0, axis2=1): + """diagonal(a, offset=0, axis1=0, axis2=1) returns the given diagonals + defined by the last two dimensions of the array. + """ + return asarray(a).diagonal(offset, axis1, axis2) + +def trace(a, offset=0, axis1=0, axis2=1, dtype=None): + """trace(a,offset=0, axis1=0, axis2=1) returns the sum along diagonals + (defined by the last two dimenions) of the array. + """ + return asarray(a).trace(offset, axis1, axis2, dtype) + +def ravel(m): + """ravel(m) returns a 1d array corresponding to all the elements of it's + argument. + """ + return asarray(m).ravel() + +def nonzero(a): + """nonzero(a) returns the indices of the elements of a which are not zero, + a must be 1d + """ + return asarray(a).nonzero() + +def shape(a): + """shape(a) returns the shape of a (as a function call which + also works on nested sequences). + """ + return asarray(a).shape + +def compress(condition, m, axis=-1): + """compress(condition, x, axis=-1) = those elements of x corresponding + to those elements of condition that are "true". condition must be the + same size as the given dimension of x.""" + return asarray(m).compress(condition, axis) + +def clip(m, m_min, m_max): + """clip(m, m_min, m_max) = every entry in m that is less than m_min is + replaced by m_min, and every entry greater than m_max is replaced by + m_max. + """ + return asarray(m).clip(m_min, m_max) + +def sum(x, axis=0, dtype=None): + """Sum the array over the given axis. The optional dtype argument + is the data type for intermediate calculations. + + The default is to upcast (promote) smaller integer types to the + platform-dependent Int. For example, on 32-bit platforms: + + x.dtype default sum() dtype + --------------------------------------------------- + bool, Int8, Int16, Int32 Int32 + + Examples: + >>> sum([0.5, 1.5]) + 2.0 + >>> sum([0.5, 1.5], dtype=Int32) + 1 + >>> sum([[0, 1], [0, 5]]) + array([0, 6]) + >>> sum([[0, 1], [0, 5]], axis=1) + array([1, 5]) + """ + return asarray(x).sum(axis, dtype) + +def product (x, axis=0, dtype=None): + """Product of the array elements over the given axis.""" + return asarray(x).prod(axis, dtype) + +def sometrue (x, axis=0): + """Perform a logical_or over the given axis.""" + return asarray(x).any(axis) + +def alltrue (x, axis=0): + """Perform a logical_and over the given axis.""" + return asarray(x).all(axis) + +def any(x,axis=None): + """Return true if any elements of x are true: sometrue(ravel(x)) + """ + return ravel(x).any(axis) + +def all(x,axis=None): + """Return true if all elements of x are true: alltrue(ravel(x)) + """ + return ravel(x).all(axis) + +def cumsum (x, axis=0, dtype=None): + """Sum the array over the given axis.""" + return asarray(x).cumsum(axis, dtype) + +def cumproduct (x, axis=0, dtype=None): + """Sum the array over the given axis.""" + return asarray(x).cumprod(axis, dtype) + +def ptp(a, axis=0): + """Return maximum - minimum along the the given dimension + """ + return asarray(a).ptp(axis) + +def amax(a, axis=0): + """Return the maximum of 'a' along dimension axis. + """ + return asarray(a).max(axis) + +def amin(a, axis=0): + """Return the minimum of a along dimension axis. + """ + return asarray(a).min(axis) + +def alen(a): + """Return the length of a Python object interpreted as an array + """ + return len(asarray(a)) + +def prod(a, axis=0): + """Return the product of the elements along the given axis + """ + return asarray(a).prod(axis) + +def cumprod(a, axis=0): + """Return the cumulative product of the elments along the given axis + """ + return asarray(a).cumprod(axis) + +def ndim(a): + try: + return a.ndim + except AttributeError: + return asarray(a).ndim + +def rank (a): + """Get the rank of sequence a (the number of dimensions, not a matrix rank) + The rank of a scalar is zero. + """ + try: + return a.ndim + except AttributeError: + return asarray(a).ndim + +def size (a, axis=None): + "Get the number of elements in sequence a, or along a certain axis." + if axis is None: + try: + return a.size + except AttributeError: + return asarray(a).size + else: + try: + return a.shape[axis] + except AttributeError: + return asarray(a).shape[axis] + +from function_base import round_ as around + +def mean(a, axis=0, dtype=None): + return asarray(a).mean(axis, dtype) + +def std(a, axis=0, dtype=None): + return asarray(a).std(axis, dtype) + +def var(a, axis=0, dtype=None): + return asarray(a).var(axis, dtype) diff --git a/numpy/core/polynomial.py b/numpy/core/polynomial.py new file mode 100644 index 000000000..df7013bab --- /dev/null +++ b/numpy/core/polynomial.py @@ -0,0 +1,554 @@ +""" +Functions to operate on polynomials. +""" + +__all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd', + 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d', + 'polyfit'] + +import re +import numeric as NX + +from type_check import isscalar +from twodim_base import diag, vander +from shape_base import hstack, atleast_1d +from function_base import trim_zeros, sort_complex +eigvals = None +lstsq = None + +def get_linalg_funcs(): + "Look for linear algebra functions in scipy" + global eigvals, lstsq + from scipy.corelinalg import eigvals, lstsq + return + +def _eigvals(arg): + "Return the eigenvalues of the argument" + try: + return eigvals(arg) + except TypeError: + get_linalg_funcs() + return eigvals(arg) + +def _lstsq(X, y): + "Do least squares on the arguments" + try: + return lstsq(X, y) + except TypeError: + get_linalg_funcs() + return lstsq(X, y) + +def poly(seq_of_zeros): + """ Return a sequence representing a polynomial given a sequence of roots. + + If the input is a matrix, return the characteristic polynomial. + + Example: + + >>> b = roots([1,3,1,5,6]) + >>> poly(b) + array([1., 3., 1., 5., 6.]) + """ + seq_of_zeros = atleast_1d(seq_of_zeros) + sh = seq_of_zeros.shape + if len(sh) == 2 and sh[0] == sh[1]: + seq_of_zeros = _eigvals(seq_of_zeros) + elif len(sh) ==1: + pass + else: + raise ValueError, "input must be 1d or square 2d array." + + if len(seq_of_zeros) == 0: + return 1.0 + + a = [1] + for k in range(len(seq_of_zeros)): + a = NX.convolve(a, [1, -seq_of_zeros[k]], mode='full') + + if issubclass(a.dtype, NX.complexfloating): + # if complex roots are all complex conjugates, the roots are real. + roots = NX.asarray(seq_of_zeros, complex) + pos_roots = sort_complex(NX.compress(roots.imag > 0, roots)) + neg_roots = NX.conjugate(sort_complex( + NX.compress(roots.imag < 0,roots))) + if (len(pos_roots) == len(neg_roots) and + NX.alltrue(neg_roots == pos_roots)): + a = a.real.copy() + + return a + +def roots(p): + """ Return the roots of the polynomial coefficients in p. + + The values in the rank-1 array p are coefficients of a polynomial. + If the length of p is n+1 then the polynomial is + p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n] + """ + # If input is scalar, this makes it an array + p = atleast_1d(p) + if len(p.shape) != 1: + raise ValueError,"Input must be a rank-1 array." + + # find non-zero array entries + non_zero = NX.nonzero(NX.ravel(p)) + + # find the number of trailing zeros -- this is the number of roots at 0. + trailing_zeros = len(p) - non_zero[-1] - 1 + + # strip leading and trailing zeros + p = p[int(non_zero[0]):int(non_zero[-1])+1] + + # casting: if incoming array isn't floating point, make it floating point. + if not issubclass(p.dtype, (NX.floating, NX.complexfloating)): + p = p.astype(float) + + N = len(p) + if N > 1: + # build companion matrix and find its eigenvalues (the roots) + A = diag(NX.ones((N-2,), p.dtype), -1) + A[0, :] = -p[1:] / p[0] + roots = _eigvals(A) + else: + return NX.array([]) + + # tack any zeros onto the back of the array + roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype))) + return roots + +def polyint(p, m=1, k=None): + """Return the mth analytical integral of the polynomial p. + + If k is None, then zero-valued constants of integration are used. + otherwise, k should be a list of length m (or a scalar if m=1) to + represent the constants of integration to use for each integration + (starting with k[0]) + """ + m = int(m) + if m < 0: + raise ValueError, "Order of integral must be positive (see polyder)" + if k is None: + k = NX.zeros(m, float) + k = atleast_1d(k) + if len(k) == 1 and m > 1: + k = k[0]*NX.ones(m, float) + if len(k) < m: + raise ValueError, \ + "k must be a scalar or a rank-1 array of length 1 or >m." + if m == 0: + return p + else: + truepoly = isinstance(p, poly1d) + p = NX.asarray(p) + y = NX.zeros(len(p)+1, float) + y[:-1] = p*1.0/NX.arange(len(p), 0, -1) + y[-1] = k[0] + val = polyint(y, m-1, k=k[1:]) + if truepoly: + val = poly1d(val) + return val + +def polyder(p, m=1): + """Return the mth derivative of the polynomial p. + """ + m = int(m) + truepoly = isinstance(p, poly1d) + p = NX.asarray(p) + n = len(p)-1 + y = p[:-1] * NX.arange(n, 0, -1) + if m < 0: + raise ValueError, "Order of derivative must be positive (see polyint)" + if m == 0: + return p + else: + val = polyder(y, m-1) + if truepoly: + val = poly1d(val) + return val + +def polyfit(x, y, N): + """ + + Do a best fit polynomial of order N of y to x. Return value is a + vector of polynomial coefficients [pk ... p1 p0]. Eg, for N=2 + + p2*x0^2 + p1*x0 + p0 = y1 + p2*x1^2 + p1*x1 + p0 = y1 + p2*x2^2 + p1*x2 + p0 = y2 + ..... + p2*xk^2 + p1*xk + p0 = yk + + + Method: if X is a the Vandermonde Matrix computed from x (see + http://mathworld.wolfram.com/VandermondeMatrix.html), then the + polynomial least squares solution is given by the 'p' in + + X*p = y + + where X is a len(x) x N+1 matrix, p is a N+1 length vector, and y + is a len(x) x 1 vector + + This equation can be solved as + + p = (XT*X)^-1 * XT * y + + where XT is the transpose of X and -1 denotes the inverse. + + For more info, see + http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html, + but note that the k's and n's in the superscripts and subscripts + on that page. The linear algebra is correct, however. + + See also polyval + + """ + x = NX.asarray(x)+0. + y = NX.asarray(y)+0. + y = NX.reshape(y, (len(y), 1)) + X = vander(x, N+1) + c, resids, rank, s = _lstsq(X, y) + c.shape = (N+1,) + return c + + + +def polyval(p, x): + """Evaluate the polynomial p at x. If x is a polynomial then composition. + + Description: + + If p is of length N, this function returns the value: + p[0]*(x**N-1) + p[1]*(x**N-2) + ... + p[N-2]*x + p[N-1] + + x can be a sequence and p(x) will be returned for all elements of x. + or x can be another polynomial and the composite polynomial p(x) will be + returned. + + Notice: This can produce inaccurate results for polynomials with + significant variability. Use carefully. + """ + p = NX.asarray(p) + if isinstance(x, poly1d): + y = 0 + else: + x = NX.asarray(x) + y = NX.zeros_like(x) + for i in range(len(p)): + y = x * y + p[i] + return y + +def polyadd(a1, a2): + """Adds two polynomials represented as sequences + """ + truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) + a1 = atleast_1d(a1) + a2 = atleast_1d(a2) + diff = len(a2) - len(a1) + if diff == 0: + return a1 + a2 + elif diff > 0: + zr = NX.zeros(diff, a1.dtype) + val = NX.concatenate((zr, a1)) + a2 + else: + zr = NX.zeros(abs(diff), a2.dtype) + val = a1 + NX.concatenate((zr, a2)) + if truepoly: + val = poly1d(val) + return val + +def polysub(a1, a2): + """Subtracts two polynomials represented as sequences + """ + truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) + a1 = atleast_1d(a1) + a2 = atleast_1d(a2) + diff = len(a2) - len(a1) + if diff == 0: + return a1 - a2 + elif diff > 0: + zr = NX.zeros(diff, a1) + val = NX.concatenate((zr, a1)) - a2 + else: + zr = NX.zeros(abs(diff), a2) + val = a1 - NX.concatenate((zr, a2)) + if truepoly: + val = poly1d(val) + return val + + +def polymul(a1, a2): + """Multiplies two polynomials represented as sequences. + """ + truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d)) + val = NX.convolve(a1, a2) + if truepoly: + val = poly1d(val) + return val + + +def deconvolve(signal, divisor): + """Deconvolves divisor out of signal. Requires scipy.signal library + """ + import scipy.signal + num = atleast_1d(signal) + den = atleast_1d(divisor) + N = len(num) + D = len(den) + if D > N: + quot = []; + rem = num; + else: + input = NX.ones(N-D+1, float) + input[1:] = 0 + quot = scipy.signal.lfilter(num, den, input) + rem = num - NX.convolve(den, quot, mode='full') + return quot, rem + +def polydiv(u, v): + """Computes q and r polynomials so that u(s) = q(s)*v(s) + r(s) + and deg r < deg v. + """ + truepoly = (isinstance(u, poly1d) or isinstance(u, poly1d)) + u = atleast_1d(u) + v = atleast_1d(v) + m = len(u) - 1 + n = len(v) - 1 + scale = 1. / v[0] + q = NX.zeros((m-n+1,), float) + r = u.copy() + for k in range(0, m-n+1): + d = scale * r[k] + q[k] = d + r[k:k+n+1] -= d*v + while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1): + r = r[1:] + if truepoly: + q = poly1d(q) + r = poly1d(r) + return q, r + +_poly_mat = re.compile(r"[*][*]([0-9]*)") +def _raise_power(astr, wrap=70): + n = 0 + line1 = '' + line2 = '' + output = ' ' + while 1: + mat = _poly_mat.search(astr, n) + if mat is None: + break + span = mat.span() + power = mat.groups()[0] + partstr = astr[n:span[0]] + n = span[1] + toadd2 = partstr + ' '*(len(power)-1) + toadd1 = ' '*(len(partstr)-1) + power + if ((len(line2)+len(toadd2) > wrap) or \ + (len(line1)+len(toadd1) > wrap)): + output += line1 + "\n" + line2 + "\n " + line1 = toadd1 + line2 = toadd2 + else: + line2 += partstr + ' '*(len(power)-1) + line1 += ' '*(len(partstr)-1) + power + output += line1 + "\n" + line2 + return output + astr[n:] + + +class poly1d(object): + """A one-dimensional polynomial class. + + p = poly1d([1,2,3]) constructs the polynomial x**2 + 2 x + 3 + + p(0.5) evaluates the polynomial at the location + p.r is a list of roots + p.c is the coefficient array [1,2,3] + p.order is the polynomial order (after leading zeros in p.c are removed) + p[k] is the coefficient on the kth power of x (backwards from + sequencing the coefficient array. + + polynomials can be added, substracted, multplied and divided (returns + quotient and remainder). + asarray(p) will also give the coefficient array, so polynomials can + be used in all functions that accept arrays. + + p = poly1d([1,2,3], variable='lambda') will use lambda in the + string representation of p. + """ + def __init__(self, c_or_r, r=0, variable=None): + if isinstance(c_or_r, poly1d): + for key in c_or_r.__dict__.keys(): + self.__dict__[key] = c_or_r.__dict__[key] + if variable is not None: + self.__dict__['variable'] = variable + return + if r: + c_or_r = poly(c_or_r) + c_or_r = atleast_1d(c_or_r) + if len(c_or_r.shape) > 1: + raise ValueError, "Polynomial must be 1d only." + c_or_r = trim_zeros(c_or_r, trim='f') + if len(c_or_r) == 0: + c_or_r = NX.array([0.]) + self.__dict__['coeffs'] = c_or_r + self.__dict__['order'] = len(c_or_r) - 1 + if variable is None: + variable = 'x' + self.__dict__['variable'] = variable + + def __array__(self, t=None): + if t: + return NX.asarray(self.coeffs, t) + else: + return NX.asarray(self.coeffs) + + def __repr__(self): + vals = repr(self.coeffs) + vals = vals[6:-1] + return "poly1d(%s)" % vals + + def __len__(self): + return self.order + + def __str__(self): + N = self.order + thestr = "0" + var = self.variable + for k in range(len(self.coeffs)): + coefstr ='%.4g' % abs(self.coeffs[k]) + if coefstr[-4:] == '0000': + coefstr = coefstr[:-5] + power = (N-k) + if power == 0: + if coefstr != '0': + newstr = '%s' % (coefstr,) + else: + if k == 0: + newstr = '0' + else: + newstr = '' + elif power == 1: + if coefstr == '0': + newstr = '' + elif coefstr == 'b': + newstr = var + else: + newstr = '%s %s' % (coefstr, var) + else: + if coefstr == '0': + newstr = '' + elif coefstr == 'b': + newstr = '%s**%d' % (var, power,) + else: + newstr = '%s %s**%d' % (coefstr, var, power) + + if k > 0: + if newstr != '': + if self.coeffs[k] < 0: + thestr = "%s - %s" % (thestr, newstr) + else: + thestr = "%s + %s" % (thestr, newstr) + elif (k == 0) and (newstr != '') and (self.coeffs[k] < 0): + thestr = "-%s" % (newstr,) + else: + thestr = newstr + return _raise_power(thestr) + + + def __call__(self, val): + return polyval(self.coeffs, val) + + def __mul__(self, other): + if isscalar(other): + return poly1d(self.coeffs * other) + else: + other = poly1d(other) + return poly1d(polymul(self.coeffs, other.coeffs)) + + def __rmul__(self, other): + if isscalar(other): + return poly1d(other * self.coeffs) + else: + other = poly1d(other) + return poly1d(polymul(self.coeffs, other.coeffs)) + + def __add__(self, other): + other = poly1d(other) + return poly1d(polyadd(self.coeffs, other.coeffs)) + + def __radd__(self, other): + other = poly1d(other) + return poly1d(polyadd(self.coeffs, other.coeffs)) + + def __pow__(self, val): + if not isscalar(val) or int(val) != val or val < 0: + raise ValueError, "Power to non-negative integers only." + res = [1] + for k in range(val): + res = polymul(self.coeffs, res) + return poly1d(res) + + def __sub__(self, other): + other = poly1d(other) + return poly1d(polysub(self.coeffs, other.coeffs)) + + def __rsub__(self, other): + other = poly1d(other) + return poly1d(polysub(other.coeffs, self.coeffs)) + + def __div__(self, other): + if isscalar(other): + return poly1d(self.coeffs/other) + else: + other = poly1d(other) + return polydiv(self, other) + + def __rdiv__(self, other): + if isscalar(other): + return poly1d(other/self.coeffs) + else: + other = poly1d(other) + return polydiv(other, self) + + def __setattr__(self, key, val): + raise ValueError, "Attributes cannot be changed this way." + + def __getattr__(self, key): + if key in ['r', 'roots']: + return roots(self.coeffs) + elif key in ['c','coef','coefficients']: + return self.coeffs + elif key in ['o']: + return self.order + else: + return self.__dict__[key] + + def __getitem__(self, val): + ind = self.order - val + if val > self.order: + return 0 + if val < 0: + return 0 + return self.coeffs[ind] + + def __setitem__(self, key, val): + ind = self.order - key + if key < 0: + raise ValueError, "Does not support negative powers." + if key > self.order: + zr = NX.zeros(key-self.order, self.coeffs.dtype) + self.__dict__['coeffs'] = NX.concatenate((zr, self.coeffs)) + self.__dict__['order'] = key + ind = 0 + self.__dict__['coeffs'][ind] = val + return + + def integ(self, m=1, k=0): + """Return the mth analytical integral of this polynomial. + See the documentation for polyint. + """ + return poly1d(polyint(self.coeffs, m=m, k=k)) + + def deriv(self, m=1): + """Return the mth derivative of this polynomial. + """ + return poly1d(polyder(self.coeffs, m=m)) diff --git a/numpy/core/records.py b/numpy/core/records.py new file mode 100644 index 000000000..ee697cff4 --- /dev/null +++ b/numpy/core/records.py @@ -0,0 +1,402 @@ +__all__ = ['record', 'recarray','format_parser'] + +import numeric as sb +import numerictypes as nt +import sys +import types +import stat, os +import _internal + +_byteorderconv = {'b':'>', + 'l':'<', + 'n':'=', + 'B':'>', + 'L':'<', + 'N':'=', + 'S':'s', + 's':'s', + '>':'>', + '<':'<', + '=':'=', + '|':'|', + 'I':'|', + 'i':'|'} + +# formats regular expression +# allows multidimension spec with a tuple syntax in front +# of the letter code '(2,3)f4' and ' ( 2 , 3 ) f4 ' +# are equally allowed + +numfmt = nt.typeDict +_typestr = nt._typestr + +def find_duplicate(list): + """Find duplication in a list, return a list of duplicated elements""" + dup = [] + for i in range(len(list)): + if (list[i] in list[i+1:]): + if (list[i] not in dup): + dup.append(list[i]) + return dup + + +class format_parser: + def __init__(self, formats, names, titles, aligned=False): + self._parseFormats(formats, aligned) + self._setfieldnames(names, titles) + self._createdescr() + + def _parseFormats(self, formats, aligned=0): + """ Parse the field formats """ + + dtypedescr = sb.dtypedescr(formats, aligned) + fields = dtypedescr.fields + keys = fields[-1] + self._f_formats = [fields[key][0] for key in keys] + self._offsets = [fields[key][1] for key in keys] + self._nfields = len(keys) + + def _setfieldnames(self, names, titles): + """convert input field names into a list and assign to the _names + attribute """ + + if (names): + if (type(names) in [types.ListType, types.TupleType]): + pass + elif (type(names) == types.StringType): + names = names.split(',') + else: + raise NameError, "illegal input names %s" % `names` + + self._names = map(lambda n:n.strip(), names)[:self._nfields] + else: + self._names = [] + + # if the names are not specified, they will be assigned as "f1, f2,..." + # if not enough names are specified, they will be assigned as "f[n+1], + # f[n+2],..." etc. where n is the number of specified names..." + self._names += map(lambda i: + 'f'+`i`, range(len(self._names)+1,self._nfields+1)) + + # check for redundant names + _dup = find_duplicate(self._names) + if _dup: + raise ValueError, "Duplicate field names: %s" % _dup + + if (titles): + self._titles = [n.strip() for n in titles][:self._nfields] + else: + self._titles = [] + titles = [] + + if (self._nfields > len(titles)): + self._titles += [None]*(self._nfields-len(titles)) + + def _createdescr(self): + self._descr = sb.dtypedescr({'names':self._names, + 'formats':self._f_formats, + 'offsets':self._offsets, + 'titles':self._titles}) + +class record(nt.void): + def __repr__(self): + return self.__str__() + + def __str__(self): + return str(self.item()) + + def __getattribute__(self, attr): + if attr in ['setfield', 'getfield', 'dtypedescr']: + return nt.void.__getattribute__(self, attr) + fielddict = nt.void.__getattribute__(self, 'dtypedescr').fields + res = fielddict.get(attr,None) + if res: + return self.getfield(*res[:2]) + return nt.void.__getattribute__(self, attr) + + def __setattr__(self, attr, val): + if attr in ['setfield', 'getfield', 'dtypedescr']: + raise AttributeError, "Cannot set '%s' attribute" % attr; + fielddict = nt.void.__getattribute__(self,'dtypedescr').fields + res = fielddict.get(attr,None) + if res: + return self.setfield(val,*res[:2]) + + return nt.void.__setattr__(self,attr,val) + + def __getitem__(self, obj): + return self.getfield(*(self.dtypedescr.fields[obj][:2])) + + def __setitem__(self, obj, val): + return self.setfield(val, *(self.dtypedescr.fields[obj][:2])) + + +# The recarray is almost identical to a standard array (which supports +# named fields already) The biggest difference is that it can use +# attribute-lookup to find the fields and it returns a record item. + +# If byteorder is given it forces a particular byteorder on all +# the fields (and any subfields) + +class recarray(sb.ndarray): + def __new__(subtype, shape, formats, names=None, titles=None, + buf=None, offset=0, strides=None, byteorder=None, + aligned=0): + + if isinstance(formats, sb.dtypedescr): + descr = formats + else: + parsed = format_parser(formats, names, titles, aligned) + descr = parsed._descr + + if (byteorder is not None): + byteorder = _byteorderconv[byteorder[0]] + descr = descr.newbyteorder(byteorder) + + if buf is None: + self = sb.ndarray.__new__(subtype, shape, (record, descr)) + else: + self = sb.ndarray.__new__(subtype, shape, (record, descr), + buffer=buf, offset=offset, + strides=strides) + return self + + def __getattribute__(self, attr): + fielddict = sb.ndarray.__getattribute__(self,'dtypedescr').fields + try: + res = fielddict[attr][:2] + except: + return sb.ndarray.__getattribute__(self,attr) + + return self.getfield(*res) + + def __setattr__(self, attr, val): + fielddict = sb.ndarray.__getattribute__(self,'dtypedescr').fields + try: + res = fielddict[attr][:2] + except: + return sb.ndarray.__setattr__(self,attr,val) + + return self.setfield(val,*res) + + +def fromarrays(arrayList, formats=None, names=None, titles=None, shape=None, + aligned=0): + """ create a record array from a (flat) list of arrays + + >>> x1=array([1,2,3,4]) + >>> x2=array(['a','dd','xyz','12']) + >>> x3=array([1.1,2,3,4]) + >>> r=fromarrays([x1,x2,x3],names='a,b,c') + >>> print r[1] + (2, 'dd', 2.0) + >>> x1[1]=34 + >>> r.a + recarray([1, 2, 3, 4]) + """ + + if shape is None or shape == 0: + shape = arrayList[0].shape + + if isinstance(shape, int): + shape = (shape,) + + if formats is None: + # go through each object in the list to see if it is an ndarray + # and determine the formats. + formats = '' + for obj in arrayList: + if not isinstance(obj, sb.ndarray): + raise ValueError, "item in the array list must be an ndarray." + if obj.ndim == 1: + _repeat = '' + elif len(obj._shape) >= 2: + _repeat = `obj._shape[1:]` + formats += _repeat + _typestr[obj.dtype] + if issubclass(obj.dtype, nt.flexible): + formats += `obj.itemsize` + formats += ',' + formats=formats[:-1] + + for obj in arrayList: + if obj.shape != shape: + raise ValueError, "array has different shape" + + parsed = format_parser(formats, names, titles, aligned) + _names = parsed._names + _array = recarray(shape, parsed._descr) + + # populate the record array (makes a copy) + for i in range(len(arrayList)): + _array[_names[i]] = arrayList[i] + + return _array + +# shape must be 1-d +def fromrecords(recList, formats=None, names=None, titles=None, shape=None, + aligned=0): + """ create a Record Array from a list of records in text form + + The data in the same field can be heterogeneous, they will be promoted + to the highest data type. This method is intended for creating + smaller record arrays. If used to create large array e.g. + + r=fromrecords([[2,3.,'abc']]*100000) + + it is slow. + + >>> r=fromrecords([[456,'dbe',1.2],[2,'de',1.3]],names='col1,col2,col3') + >>> print r[0] + (456, 'dbe', 1.2) + >>> r.col1 + recarray([456, 2]) + >>> r.col2 + recarray(['dbe', 'de']) + >>> import cPickle + >>> print cPickle.loads(cPickle.dumps(r)) + recarray[ + (456, 'dbe', 1.2), + (2, 'de', 1.3) + ] + """ + + if (shape is None or shape == 0): + shape = len(recList) + + if isinstance(shape, (int, long)): + shape = (shape,) + + if len(shape) > 1: + raise ValueError, "Can only deal with 1-d list of records" + + nfields = len(recList[0]) + if formats is None: # slower + obj = sb.array(recList,dtype=object) + arrlist = [sb.array(obj[:,i].tolist()) for i in xrange(nfields)] + return fromarrays(arrlist, formats=formats, shape=shape, names=names, + titles=titles, aligned=aligned) + + parsed = format_parser(formats, names, titles, aligned) + _array = recarray(shape, parsed._descr) + + for k in xrange(_array.size): + _array[k] = tuple(recList[k]) + + return _array + +def fromstring(datastring, formats, shape=None, names=None, titles=None, + byteorder=None, aligned=0, offset=0): + """ create a (read-only) record array from binary data contained in + a string""" + + parsed = format_parser(formats, names, titles, aligned) + itemsize = parsed._descr.itemsize + if (shape is None or shape == 0 or shape == -1): + shape = (len(datastring)-offset) / itemsize + + _array = recarray(shape, parsed._descr, names=names, + titles=titles, buf=datastring, offset=offset, + byteorder=byteorder) + return _array + +def fromfile(fd, formats, shape=None, names=None, titles=None, + byteorder=None, aligned=0, offset=0): + """Create an array from binary file data + + If file is a string then that file is opened, else it is assumed + to be a file object. No options at the moment, all file positioning + must be done prior to this function call with a file object + + >>> import testdata, sys + >>> fd=open(testdata.filename) + >>> fd.seek(2880*2) + >>> r=fromfile(fd, formats='f8,i4,a5', shape=3, byteorder='big') + >>> print r[0] + (5.1000000000000005, 61, 'abcde') + >>> r._shape + (3,) + """ + + if (shape is None or shape == 0): + shape = (-1,) + elif isinstance(shape, (int, long)): + shape = (shape,) + + name = 0 + if isinstance(fd, str): + name = 1 + fd = open(fd, 'rb') + if (offset > 0): + fd.seek(offset, 1) + try: + size = os.fstat(fd.fileno())[stat.ST_SIZE] - fd.tell() + except: + size = os.path.getsize(fd.name) - fd.tell() + + parsed = format_parser(formats, names, titles, aligned) + itemsize = parsed._descr.itemsize + + shapeprod = sb.array(shape).prod() + shapesize = shapeprod*itemsize + if shapesize < 0: + shape = list(shape) + shape[ shape.index(-1) ] = size / -shapesize + shape = tuple(shape) + shapeprod = sb.array(shape).prod() + + nbytes = shapeprod*itemsize + + if nbytes > size: + raise ValueError( + "Not enough bytes left in file for specified shape and type") + + # create the array + _array = recarray(shape, parsed._descr, byteorder=byteorder) + nbytesread = fd.readinto(_array.data) + if nbytesread != nbytes: + raise IOError("Didn't read as many bytes as expected") + if name: + fd.close() + + return _array + + +def array(obj, formats=None, names=None, titles=None, shape=None, + byteorder=None, aligned=0, offset=0, strides=None): + + if isinstance(obj, (type(None), str, file)) and (formats is None): + raise ValueError("Must define formats if object is "\ + "None, string, or a file pointer") + + elif obj is None: + if shape is None: + raise ValueError("Must define a shape if obj is None") + return recarray(shape, formats, names=names, titles=titles, + buf=obj, offset=offset, strides=strides, + byteorder=byteorder, aligned=aligned) + elif isinstance(obj, str): + return fromstring(obj, formats, names=names, titles=titles, + shape=shape, byteorder=byteorder, aligned=aligned, + offset=offset) + elif isinstance(obj, (list, tuple)): + if isinstance(obj[0], sb.ndarray): + return fromarrays(obj, formats=formats, names=names, titles=titles, + shape=shape, aligned=aligned) + else: + return fromrecords(obj, formats=formats, names=names, titles=titles, + shape=shape, aligned=aligned) + elif isinstance(obj, recarray): + new = obj.copy() + parsed = format_parser(formats, names, titles, aligned) + new.dtypedescr = parsed._descr + return new + elif isinstance(obj, file): + return fromfile(obj, formats=formats, names=names, titles=titles, + shape=shape, byteorder=byteorder, aligned=aligned, + offset=offset) + else: + raise ValueError("Unknown input type") + + + + diff --git a/numpy/core/scimath.py b/numpy/core/scimath.py new file mode 100644 index 000000000..4202fa640 --- /dev/null +++ b/numpy/core/scimath.py @@ -0,0 +1,77 @@ +""" +Wrapper functions to more user-friendly calling of certain math functions +whose output data-type is different than the input data-type in certain domains of the input. +""" + +__all__ = ['sqrt', 'log', 'log2', 'logn','log10', 'power', 'arccos', + 'arcsin', 'arctanh'] + +import numeric as nx +from numeric import * + +from type_check import isreal, asscalar + +__all__.extend([key for key in dir(nx.umath) + if key[0] != '_' and key not in __all__]) + +_ln2 = log(2.0) + +def _tocomplex(arr): + if isinstance(arr.dtype, (nx.single, nx.byte, nx.short, nx.ubyte, + nx.ushort)): + return arr.astype(nx.csingle) + else: + return arr.astype(nx.cdouble) + +def _fix_real_lt_zero(x): + x = asarray(x) + if any(isreal(x) & (x<0)): + x = _tocomplex(x) + return asscalar(x) + +def _fix_real_abs_gt_1(x): + x = asarray(x) + if any(isreal(x) & (abs(x)>1)): + x = _tocomplex(x) + return x + +def sqrt(x): + x = _fix_real_lt_zero(x) + return nx.sqrt(x) + +def log(x): + x = _fix_real_lt_zero(x) + return nx.log(x) + +def log10(x): + x = _fix_real_lt_zero(x) + return nx.log10(x) + +def logn(n, x): + """ Take log base n of x. + """ + x = _fix_real_lt_zero(x) + n = _fix_real_lt_zero(n) + return log(x)/log(n) + +def log2(x): + """ Take log base 2 of x. + """ + x = _fix_real_lt_zero(x) + return log(x)/_ln2 + +def power(x, p): + x = _fix_real_lt_zero(x) + return nx.power(x, p) + +def arccos(x): + x = _fix_real_abs_gt_1(x) + return arccos(x) + +def arcsin(x): + x = _fix_real_abs_gt_1(x) + return arcsin(x) + +def arctanh(x): + x = _fix_real_abs_gt_1(x) + return arctanh(x) diff --git a/numpy/core/setup.py b/numpy/core/setup.py new file mode 100644 index 000000000..5f0e0fa94 --- /dev/null +++ b/numpy/core/setup.py @@ -0,0 +1,284 @@ + +import imp +import os +from os.path import join +from glob import glob +from distutils.dep_util import newer,newer_group + +def configuration(parent_package='',top_path=None): + from scipy.distutils.misc_util import Configuration,dot_join + from scipy.distutils.system_info import get_info + + config = Configuration('base',parent_package,top_path) + local_dir = config.local_path + codegen_dir = join(local_dir,'code_generators') + + generate_umath_py = join(codegen_dir,'generate_umath.py') + n = dot_join(config.name,'generate_umath') + generate_umath = imp.load_module('_'.join(n.split('.')), + open(generate_umath_py,'U'),generate_umath_py, + ('.py','U',1)) + + header_dir = join(*(config.name.split('.')+['include','scipy'])) + + def generate_config_h(ext, build_dir): + target = join(build_dir,'config.h') + if newer(__file__,target): + config_cmd = config.get_config_cmd() + print 'Generating',target + # + tc = generate_testcode(target) + from distutils import sysconfig + python_include = sysconfig.get_python_inc() + result = config_cmd.try_run(tc,include_dirs=[python_include]) + if not result: + raise "ERROR: Failed to test configuration" + moredefs = [] + + # + mathlibs = [] + tc = testcode_mathlib() + mathlibs_choices = [[],['m'],['cpml']] + mathlib = os.environ.get('MATHLIB') + if mathlib: + mathlibs_choices.insert(0,mathlib.split(',')) + for libs in mathlibs_choices: + if config_cmd.try_run(tc,libraries=libs): + mathlibs = libs + break + else: + raise "math library missing; rerun setup.py after setting the MATHLIB env variable" + ext.libraries.extend(mathlibs) + moredefs.append(('MATHLIB',','.join(mathlibs))) + + libs = mathlibs + kws_args = {'libraries':libs,'decl':0,'headers':['math.h']} + if config_cmd.check_func('expl', **kws_args): + moredefs.append('HAVE_LONGDOUBLE_FUNCS') + if config_cmd.check_func('expf', **kws_args): + moredefs.append('HAVE_FLOAT_FUNCS') + if config_cmd.check_func('asinh', **kws_args): + moredefs.append('HAVE_INVERSE_HYPERBOLIC') + if config_cmd.check_func('atanhf', **kws_args): + moredefs.append('HAVE_INVERSE_HYPERBOLIC_FLOAT') + if config_cmd.check_func('atanhl', **kws_args): + moredefs.append('HAVE_INVERSE_HYPERBOLIC_LONGDOUBLE') + if config_cmd.check_func('isnan', **kws_args): + moredefs.append('HAVE_ISNAN') + if config_cmd.check_func('isinf', **kws_args): + moredefs.append('HAVE_ISINF') + + if moredefs: + target_f = open(target,'a') + for d in moredefs: + if isinstance(d,str): + target_f.write('#define %s\n' % (d)) + else: + target_f.write('#define %s %s\n' % (d[0],d[1])) + target_f.close() + else: + mathlibs = [] + target_f = open(target) + for line in target_f.readlines(): + s = '#define MATHLIB' + if line.startswith(s): + value = line[len(s):].strip() + if value: + mathlibs.extend(value.split(',')) + target_f.close() + + ext.libraries.extend(mathlibs) + + incl_dir = os.path.dirname(target) + if incl_dir not in config.scipy_include_dirs: + config.scipy_include_dirs.append(incl_dir) + + config.add_data_files((header_dir,target)) + return target + + def generate_api_func(header_file, module_name): + def generate_api(ext,build_dir): + target = join(build_dir, header_file) + script = join(codegen_dir, module_name + '.py') + if newer(script, target): + sys.path.insert(0, codegen_dir) + try: + m = __import__(module_name) + print 'executing',script + m.generate_api(build_dir) + finally: + del sys.path[0] + config.add_data_files((header_dir,target)) + return target + return generate_api + + generate_array_api = generate_api_func('__multiarray_api.h', + 'generate_array_api') + generate_ufunc_api = generate_api_func('__ufunc_api.h', + 'generate_ufunc_api') + + def generate_umath_c(ext,build_dir): + target = join(build_dir,'__umath_generated.c') + script = generate_umath_py + if newer(script,target): + f = open(target,'w') + f.write(generate_umath.make_code(generate_umath.defdict, + generate_umath.__file__)) + f.close() + return [] + + config.add_data_files(join('include','scipy','*.h')) + config.add_include_dirs('src') + + config.scipy_include_dirs.extend(config.paths('include')) + + deps = [join('src','arrayobject.c'), + join('src','arraymethods.c'), + join('src','scalartypes.inc.src'), + join('src','arraytypes.inc.src'), + join('include','scipy','*object.h'), + join(codegen_dir,'genapi.py'), + join(codegen_dir,'*.txt') + ] + + config.add_extension('multiarray', + sources = [join('src','multiarraymodule.c'), + generate_config_h, + generate_array_api, + join('src','scalartypes.inc.src'), + join('src','arraytypes.inc.src'), + join(codegen_dir,'generate_array_api.py'), + join('*.py') + ], + depends = deps, + ) + + config.add_extension('umath', + sources = [generate_config_h, + join('src','umathmodule.c.src'), + generate_umath_c, + generate_ufunc_api, + join('src','scalartypes.inc.src'), + join('src','arraytypes.inc.src'), + ], + depends = [join('src','ufuncobject.c'), + generate_umath_py, + join(codegen_dir,'generate_ufunc_api.py'), + ]+deps, + ) + + config.add_extension('_compiled_base', + sources=[join('src','_compiled_base.c'), + generate_config_h, + generate_array_api, + ], + ) + + config.add_extension('_sort', + sources=[join('src','_sortmodule.c.src'), + generate_config_h, + generate_array_api, + ], + ) + + # Configure blasdot + blas_info = get_info('blas_opt',0) + #blas_info = {} + def get_dotblas_sources(ext, build_dir): + if blas_info: + return ext.depends[:1] + return None # no extension module will be built + + config.add_extension('_dotblas', + sources = [get_dotblas_sources], + depends=[join('blasdot','_dotblas.c'), + join('blasdot','cblas.h'), + ], + include_dirs = ['blasdot'], + extra_info = blas_info + ) + + + config.add_data_dir('tests') + config.make_svn_version_py() + + return config + +def testcode_mathlib(): + return """\ +/* check whether libm is broken */ +#include <math.h> +int main(int argc, char *argv[]) +{ + return exp(-720.) > 1.0; /* typically an IEEE denormal */ +} +""" + +import sys +def generate_testcode(target): + if sys.platform == 'win32': + target = target.replace('\\','\\\\') + testcode = [r''' +#include <Python.h> +#include <limits.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + + FILE *fp; + + fp = fopen("'''+target+'''","w"); + '''] + + c_size_test = r''' +#ifndef %(sz)s + fprintf(fp,"#define %(sz)s %%d\n", sizeof(%(type)s)); +#else + fprintf(fp,"/* #define %(sz)s %%d */\n", %(sz)s); +#endif +''' + for sz, t in [('SIZEOF_SHORT', 'short'), + ('SIZEOF_INT', 'int'), + ('SIZEOF_LONG', 'long'), + ('SIZEOF_FLOAT', 'float'), + ('SIZEOF_DOUBLE', 'double'), + ('SIZEOF_LONG_DOUBLE', 'long double'), + ('SIZEOF_PY_INTPTR_T', 'Py_intptr_t'), + ]: + testcode.append(c_size_test % {'sz' : sz, 'type' : t}) + + testcode.append('#ifdef PY_LONG_LONG') + testcode.append(c_size_test % {'sz' : 'SIZEOF_LONG_LONG', + 'type' : 'PY_LONG_LONG'}) + testcode.append(c_size_test % {'sz' : 'SIZEOF_PY_LONG_LONG', + 'type' : 'PY_LONG_LONG'}) + + + testcode.append(r''' +#else + fprintf(fp, "/* PY_LONG_LONG not defined */\n"); +#endif +#ifndef CHAR_BIT + { + unsigned char var = 2; + int i=0; + while (var >= 2) { + var = var << 1; + i++; + } + fprintf(fp,"#define CHAR_BIT %d\n", i+1); + } +#else + fprintf(fp, "/* #define CHAR_BIT %d */\n", CHAR_BIT); +#endif + fclose(fp); + return 0; +} +''') + testcode = '\n'.join(testcode) + return testcode + +if __name__=='__main__': + from scipy.distutils.core import setup + setup(**configuration(top_path='').todict()) diff --git a/numpy/core/shape_base.py b/numpy/core/shape_base.py new file mode 100644 index 000000000..8d66b41d1 --- /dev/null +++ b/numpy/core/shape_base.py @@ -0,0 +1,539 @@ +__all__ = ['atleast_1d','atleast_2d','atleast_3d','vstack','hstack', + 'column_stack','dstack','array_split','split','hsplit', + 'vsplit','dsplit','apply_over_axes','expand_dims', + 'apply_along_axis'] + +import numeric as _nx +from numeric import * +from oldnumeric import product + +from type_check import isscalar + +def apply_along_axis(func1d,axis,arr,*args): + """ Execute func1d(arr[i],*args) where func1d takes 1-D arrays + and arr is an N-d array. i varies so as to apply the function + along the given axis for each 1-d subarray in arr. + """ + arr = asarray(arr) + nd = arr.ndim + if axis < 0: + axis += nd + if (axis >= nd): + raise ValueError("axis must be less than arr.ndim; axis=%d, rank=%d." + % (axis,nd)) + ind = [0]*(nd-1) + i = zeros(nd,'O') + indlist = range(nd) + indlist.remove(axis) + i[axis] = slice(None,None) + outshape = asarray(arr.shape).take(indlist) + i.put(ind, indlist) + res = func1d(arr[tuple(i.tolist())],*args) + # if res is a number, then we have a smaller output array + if isscalar(res): + outarr = zeros(outshape,asarray(res).dtypechar) + outarr[ind] = res + Ntot = product(outshape) + k = 1 + while k < Ntot: + # increment the index + ind[-1] += 1 + n = -1 + while (ind[n] >= outshape[n]) and (n > (1-nd)): + ind[n-1] += 1 + ind[n] = 0 + n -= 1 + i.put(ind,indlist) + res = func1d(arr[tuple(i.tolist())],*args) + outarr[ind] = res + k += 1 + return outarr + else: + Ntot = product(outshape) + holdshape = outshape + outshape = list(arr.shape) + outshape[axis] = len(res) + outarr = zeros(outshape,asarray(res).dtypechar) + outarr[tuple(i.tolist())] = res + k = 1 + while k < Ntot: + # increment the index + ind[-1] += 1 + n = -1 + while (ind[n] >= holdshape[n]) and (n > (1-nd)): + ind[n-1] += 1 + ind[n] = 0 + n -= 1 + i.put(ind, indlist) + res = func1d(arr[tuple(i.tolist())],*args) + outarr[tuple(i.tolist())] = res + k += 1 + return outarr + + +def apply_over_axes(func, a, axes): + """Apply a function repeatedly over multiple axes, keeping the same shape + for the resulting array. + + func is called as res = func(a, axis). The result is assumed + to be either the same shape as a or have one less dimension. + This call is repeated for each axis in the axes sequence. + """ + val = asarray(a) + N = a.ndim + if array(axes).ndim == 0: + axes = (axes,) + for axis in axes: + if axis < 0: axis = N + axis + args = (val, axis) + res = func(*args) + if res.ndim == val.ndim: + val = res + else: + res = expand_dims(res,axis) + if res.ndim == val.ndim: + val = res + else: + raise ValueError, "function is not returning"\ + " an array of correct shape" + return val + +def expand_dims(a, axis): + """Expand the shape of a by including newaxis before given axis. + """ + a = asarray(a) + shape = a.shape + if axis < 0: + axis = axis + len(shape) + 1 + return a.reshape(shape[:axis] + (1,) + shape[axis:]) + + +def atleast_1d(*arys): + """ Force a sequence of arrays to each be at least 1D. + + Description: + Force an array to be at least 1D. If an array is 0D, the + array is converted to a single row of values. Otherwise, + the array is unaltered. + Arguments: + *arys -- arrays to be converted to 1 or more dimensional array. + Returns: + input array converted to at least 1D array. + """ + res = [] + for ary in arys: + ary = asarray(ary) + if len(ary.shape) == 0: + ary = ary.reshape(1) + res.append(ary) + if len(res) == 1: + return res[0] + else: + return res + +def atleast_2d(*arys): + """ Force a sequence of arrays to each be at least 2D. + + Description: + Force an array to each be at least 2D. If the array + is 0D or 1D, the array is converted to a single + row of values. Otherwise, the array is unaltered. + Arguments: + arys -- arrays to be converted to 2 or more dimensional array. + Returns: + input array converted to at least 2D array. + """ + res = [] + for ary in arys: + ary = asarray(ary) + if len(ary.shape) == 0: + result = ary.reshape(1,1) + elif len(ary.shape) == 1: + result = ary[newaxis,:] + else: + result = ary + res.append(result) + if len(res) == 1: + return res[0] + else: + return res + +def atleast_3d(*arys): + """ Force a sequence of arrays to each be at least 3D. + + Description: + Force an array each be at least 3D. If the array is 0D or 1D, + the array is converted to a single 1xNx1 array of values where + N is the orginal length of the array. If the array is 2D, the + array is converted to a single MxNx1 array of values where MxN + is the orginal shape of the array. Otherwise, the array is + unaltered. + Arguments: + arys -- arrays to be converted to 3 or more dimensional array. + Returns: + input array converted to at least 3D array. + """ + res = [] + for ary in arys: + ary = asarray(ary) + if len(ary.shape) == 0: + result = ary.reshape(1,1,1) + elif len(ary.shape) == 1: + result = ary[newaxis,:,newaxis] + elif len(ary.shape) == 2: + result = ary[:,:,newaxis] + else: + result = ary + res.append(result) + if len(res) == 1: + return res[0] + else: + return res + + +def vstack(tup): + """ Stack arrays in sequence vertically (row wise) + + Description: + Take a sequence of arrays and stack them veritcally + to make a single array. All arrays in the sequence + must have the same shape along all but the first axis. + vstack will rebuild arrays divided by vsplit. + Arguments: + tup -- sequence of arrays. All arrays must have the same + shape. + Examples: + >>> import scipy + >>> a = array((1,2,3)) + >>> b = array((2,3,4)) + >>> scipy.vstack((a,b)) + array([[1, 2, 3], + [2, 3, 4]]) + >>> a = array([[1],[2],[3]]) + >>> b = array([[2],[3],[4]]) + >>> scipy.vstack((a,b)) + array([[1], + [2], + [3], + [2], + [3], + [4]]) + + """ + return _nx.concatenate(map(atleast_2d,tup),0) + +def hstack(tup): + """ Stack arrays in sequence horizontally (column wise) + + Description: + Take a sequence of arrays and stack them horizontally + to make a single array. All arrays in the sequence + must have the same shape along all but the second axis. + hstack will rebuild arrays divided by hsplit. + Arguments: + tup -- sequence of arrays. All arrays must have the same + shape. + Examples: + >>> import scipy + >>> a = array((1,2,3)) + >>> b = array((2,3,4)) + >>> scipy.hstack((a,b)) + array([1, 2, 3, 2, 3, 4]) + >>> a = array([[1],[2],[3]]) + >>> b = array([[2],[3],[4]]) + >>> scipy.hstack((a,b)) + array([[1, 2], + [2, 3], + [3, 4]]) + + """ + return _nx.concatenate(map(atleast_1d,tup),1) + +def column_stack(tup): + """ Stack 1D arrays as columns into a 2D array + + Description: + Take a sequence of 1D arrays and stack them as columns + to make a single 2D array. All arrays in the sequence + must have the same length. + Arguments: + tup -- sequence of 1D arrays. All arrays must have the same + length. + Examples: + >>> import scipy + >>> a = array((1,2,3)) + >>> b = array((2,3,4)) + >>> scipy.column_stack((a,b)) + array([[1, 2], + [2, 3], + [3, 4]]) + + """ + arrays = map(_nx.transpose,map(atleast_2d,tup)) + return _nx.concatenate(arrays,1) + +def dstack(tup): + """ Stack arrays in sequence depth wise (along third dimension) + + Description: + Take a sequence of arrays and stack them along the third axis. + All arrays in the sequence must have the same shape along all + but the third axis. This is a simple way to stack 2D arrays + (images) into a single 3D array for processing. + dstack will rebuild arrays divided by dsplit. + Arguments: + tup -- sequence of arrays. All arrays must have the same + shape. + Examples: + >>> import scipy + >>> a = array((1,2,3)) + >>> b = array((2,3,4)) + >>> scipy.dstack((a,b)) + array([ [[1, 2], + [2, 3], + [3, 4]]]) + >>> a = array([[1],[2],[3]]) + >>> b = array([[2],[3],[4]]) + >>> scipy.dstack((a,b)) + array([[ [1, 2]], + [ [2, 3]], + [ [3, 4]]]) + """ + return _nx.concatenate(map(atleast_3d,tup),2) + +def _replace_zero_by_x_arrays(sub_arys): + for i in range(len(sub_arys)): + if len(_nx.shape(sub_arys[i])) == 0: + sub_arys[i] = _nx.array([]) + elif _nx.sometrue(_nx.equal(_nx.shape(sub_arys[i]),0)): + sub_arys[i] = _nx.array([]) + return sub_arys + +def array_split(ary,indices_or_sections,axis = 0): + """ Divide an array into a list of sub-arrays. + + Description: + Divide ary into a list of sub-arrays along the + specified axis. If indices_or_sections is an integer, + ary is divided into that many equally sized arrays. + If it is impossible to make an equal split, each of the + leading arrays in the list have one additional member. If + indices_or_sections is a list of sorted integers, its + entries define the indexes where ary is split. + + Arguments: + ary -- N-D array. + Array to be divided into sub-arrays. + indices_or_sections -- integer or 1D array. + If integer, defines the number of (close to) equal sized + sub-arrays. If it is a 1D array of sorted indices, it + defines the indexes at which ary is divided. Any empty + list results in a single sub-array equal to the original + array. + axis -- integer. default=0. + Specifies the axis along which to split ary. + Caveats: + Currently, the default for axis is 0. This + means a 2D array is divided into multiple groups + of rows. This seems like the appropriate default, but + we've agreed most other functions should default to + axis=-1. Perhaps we should use axis=-1 for consistency. + However, we could also make the argument that SciPy + works on "rows" by default. sum() sums up rows of + values. split() will split data into rows. Opinions? + """ + try: + Ntotal = ary.shape[axis] + except AttributeError: + Ntotal = len(ary) + try: # handle scalar case. + Nsections = len(indices_or_sections) + 1 + div_points = [0] + list(indices_or_sections) + [Ntotal] + except TypeError: #indices_or_sections is a scalar, not an array. + Nsections = int(indices_or_sections) + if Nsections <= 0: + raise ValueError, 'number sections must be larger than 0.' + Neach_section,extras = divmod(Ntotal,Nsections) + section_sizes = [0] + \ + extras * [Neach_section+1] + \ + (Nsections-extras) * [Neach_section] + div_points = _nx.array(section_sizes).cumsum() + + sub_arys = [] + sary = _nx.swapaxes(ary,axis,0) + for i in range(Nsections): + st = div_points[i]; end = div_points[i+1] + sub_arys.append(_nx.swapaxes(sary[st:end],axis,0)) + + # there is a wierd issue with array slicing that allows + # 0x10 arrays and other such things. The following cluge is needed + # to get around this issue. + sub_arys = _replace_zero_by_x_arrays(sub_arys) + # end cluge. + + return sub_arys + +def split(ary,indices_or_sections,axis=0): + """ Divide an array into a list of sub-arrays. + + Description: + Divide ary into a list of sub-arrays along the + specified axis. If indices_or_sections is an integer, + ary is divided into that many equally sized arrays. + If it is impossible to make an equal split, an error is + raised. This is the only way this function differs from + the array_split() function. If indices_or_sections is a + list of sorted integers, its entries define the indexes + where ary is split. + + Arguments: + ary -- N-D array. + Array to be divided into sub-arrays. + indices_or_sections -- integer or 1D array. + If integer, defines the number of (close to) equal sized + sub-arrays. If it is a 1D array of sorted indices, it + defines the indexes at which ary is divided. Any empty + list results in a single sub-array equal to the original + array. + axis -- integer. default=0. + Specifies the axis along which to split ary. + Caveats: + Currently, the default for axis is 0. This + means a 2D array is divided into multiple groups + of rows. This seems like the appropriate default, but + we've agreed most other functions should default to + axis=-1. Perhaps we should use axis=-1 for consistency. + However, we could also make the argument that SciPy + works on "rows" by default. sum() sums up rows of + values. split() will split data into rows. Opinions? + """ + try: len(indices_or_sections) + except TypeError: + sections = indices_or_sections + N = ary.shape[axis] + if N % sections: + raise ValueError, 'array split does not result in an equal division' + res = array_split(ary,indices_or_sections,axis) + return res + +def hsplit(ary,indices_or_sections): + """ Split ary into multiple columns of sub-arrays + + Description: + Split a single array into multiple sub arrays. The array is + divided into groups of columns. If indices_or_sections is + an integer, ary is divided into that many equally sized sub arrays. + If it is impossible to make the sub-arrays equally sized, the + operation throws a ValueError exception. See array_split and + split for other options on indices_or_sections. + Arguments: + ary -- N-D array. + Array to be divided into sub-arrays. + indices_or_sections -- integer or 1D array. + If integer, defines the number of (close to) equal sized + sub-arrays. If it is a 1D array of sorted indices, it + defines the indexes at which ary is divided. Any empty + list results in a single sub-array equal to the original + array. + Returns: + sequence of sub-arrays. The returned arrays have the same + number of dimensions as the input array. + Related: + hstack, split, array_split, vsplit, dsplit. + Examples: + >>> import scipy + >>> a= array((1,2,3,4)) + >>> scipy.hsplit(a,2) + [array([1, 2]), array([3, 4])] + >>> a = array([[1,2,3,4],[1,2,3,4]]) + [array([[1, 2], + [1, 2]]), array([[3, 4], + [3, 4]])] + + """ + if len(_nx.shape(ary)) == 0: + raise ValueError, 'hsplit only works on arrays of 1 or more dimensions' + if len(ary.shape) > 1: + return split(ary,indices_or_sections,1) + else: + return split(ary,indices_or_sections,0) + +def vsplit(ary,indices_or_sections): + """ Split ary into multiple rows of sub-arrays + + Description: + Split a single array into multiple sub arrays. The array is + divided into groups of rows. If indices_or_sections is + an integer, ary is divided into that many equally sized sub arrays. + If it is impossible to make the sub-arrays equally sized, the + operation throws a ValueError exception. See array_split and + split for other options on indices_or_sections. + Arguments: + ary -- N-D array. + Array to be divided into sub-arrays. + indices_or_sections -- integer or 1D array. + If integer, defines the number of (close to) equal sized + sub-arrays. If it is a 1D array of sorted indices, it + defines the indexes at which ary is divided. Any empty + list results in a single sub-array equal to the original + array. + Returns: + sequence of sub-arrays. The returned arrays have the same + number of dimensions as the input array. + Caveats: + How should we handle 1D arrays here? I am currently raising + an error when I encounter them. Any better approach? + + Should we reduce the returned array to their minium dimensions + by getting rid of any dimensions that are 1? + Related: + vstack, split, array_split, hsplit, dsplit. + Examples: + import scipy + >>> a = array([[1,2,3,4], + ... [1,2,3,4]]) + >>> scipy.vsplit(a) + [array([ [1, 2, 3, 4]]), array([ [1, 2, 3, 4]])] + + """ + if len(_nx.shape(ary)) < 2: + raise ValueError, 'vsplit only works on arrays of 2 or more dimensions' + return split(ary,indices_or_sections,0) + +def dsplit(ary,indices_or_sections): + """ Split ary into multiple sub-arrays along the 3rd axis (depth) + + Description: + Split a single array into multiple sub arrays. The array is + divided into groups along the 3rd axis. If indices_or_sections is + an integer, ary is divided into that many equally sized sub arrays. + If it is impossible to make the sub-arrays equally sized, the + operation throws a ValueError exception. See array_split and + split for other options on indices_or_sections. + Arguments: + ary -- N-D array. + Array to be divided into sub-arrays. + indices_or_sections -- integer or 1D array. + If integer, defines the number of (close to) equal sized + sub-arrays. If it is a 1D array of sorted indices, it + defines the indexes at which ary is divided. Any empty + list results in a single sub-array equal to the original + array. + Returns: + sequence of sub-arrays. The returned arrays have the same + number of dimensions as the input array. + Caveats: + See vsplit caveats. + Related: + dstack, split, array_split, hsplit, vsplit. + Examples: + >>> a = array([[[1,2,3,4],[1,2,3,4]]]) + [array([ [[1, 2], + [1, 2]]]), array([ [[3, 4], + [3, 4]]])] + + """ + if len(_nx.shape(ary)) < 3: + raise ValueError, 'vsplit only works on arrays of 3 or more dimensions' + return split(ary,indices_or_sections,2) + 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"); +} diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py new file mode 100644 index 000000000..fafd75eef --- /dev/null +++ b/numpy/core/tests/test_function_base.py @@ -0,0 +1,338 @@ + +import sys + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base) +from scipy.base import * +del sys.path[0] + +class test_any(ScipyTestCase): + def check_basic(self): + y1 = [0,0,1,0] + y2 = [0,0,0,0] + y3 = [1,0,1,0] + assert(any(y1)) + assert(any(y3)) + assert(not any(y2)) + + def check_nd(self): + y1 = [[0,0,0],[0,1,0],[1,1,0]] + assert(any(y1)) + assert_array_equal(sometrue(y1),[1,1,0]) + assert_array_equal(sometrue(y1,axis=1),[0,1,1]) + +class test_all(ScipyTestCase): + def check_basic(self): + y1 = [0,1,1,0] + y2 = [0,0,0,0] + y3 = [1,1,1,1] + assert(not all(y1)) + assert(all(y3)) + assert(not all(y2)) + assert(all(~array(y2))) + + def check_nd(self): + y1 = [[0,0,1],[0,1,1],[1,1,1]] + assert(not all(y1)) + assert_array_equal(alltrue(y1),[0,0,1]) + assert_array_equal(alltrue(y1,axis=1),[0,0,1]) + +class test_average(ScipyTestCase): + def check_basic(self): + y1 = array([1,2,3]) + assert(average(y1) == 2.) + y2 = array([1.,2.,3.]) + assert(average(y2) == 2.) + y3 = [0.,0.,0.] + assert(average(y3) == 0.) + + y4 = ones((4,4)) + y4[0,1] = 0 + y4[1,0] = 2 + assert_array_equal(y4.mean(0), average(y4, 0)) + assert_array_equal(y4.mean(1), average(y4, 1)) + + y5 = rand(5,5) + assert_array_equal(y5.mean(0), average(y5, 0)) + assert_array_equal(y5.mean(1), average(y5, 1)) + +class test_logspace(ScipyTestCase): + def check_basic(self): + y = logspace(0,6) + assert(len(y)==50) + y = logspace(0,6,num=100) + assert(y[-1] == 10**6) + y = logspace(0,6,endpoint=0) + assert(y[-1] < 10**6) + y = logspace(0,6,num=7) + assert_array_equal(y,[1,10,100,1e3,1e4,1e5,1e6]) + +class test_linspace(ScipyTestCase): + def check_basic(self): + y = linspace(0,10) + assert(len(y)==50) + y = linspace(2,10,num=100) + assert(y[-1] == 10) + y = linspace(2,10,endpoint=0) + assert(y[-1] < 10) + y,st = linspace(2,10,retstep=1) + assert_almost_equal(st,8/49.0) + assert_array_almost_equal(y,mgrid[2:10:50j],13) + + def check_corner(self): + y = list(linspace(0,1,1)) + assert y == [0.0], y + y = list(linspace(0,1,2.5)) + assert y == [0.0, 1.0] + +class test_amax(ScipyTestCase): + def check_basic(self): + a = [3,4,5,10,-3,-5,6.0] + assert_equal(amax(a),10.0) + b = [[3,6.0, 9.0], + [4,10.0,5.0], + [8,3.0,2.0]] + assert_equal(amax(b,axis=0),[8.0,10.0,9.0]) + assert_equal(amax(b,axis=1),[9.0,10.0,8.0]) + +class test_amin(ScipyTestCase): + def check_basic(self): + a = [3,4,5,10,-3,-5,6.0] + assert_equal(amin(a),-5.0) + b = [[3,6.0, 9.0], + [4,10.0,5.0], + [8,3.0,2.0]] + assert_equal(amin(b,axis=0),[3.0,3.0,2.0]) + assert_equal(amin(b,axis=1),[3.0,4.0,2.0]) + +class test_ptp(ScipyTestCase): + def check_basic(self): + a = [3,4,5,10,-3,-5,6.0] + assert_equal(ptp(a),15.0) + b = [[3,6.0, 9.0], + [4,10.0,5.0], + [8,3.0,2.0]] + assert_equal(ptp(b,axis=0),[5.0,7.0,7.0]) + assert_equal(ptp(b,axis=-1),[6.0,6.0,6.0]) + +class test_cumsum(ScipyTestCase): + def check_basic(self): + ba = [1,2,10,11,6,5,4] + ba2 = [[1,2,3,4],[5,6,7,9],[10,3,4,5]] + for ctype in [int8,uint8,int16,uint16,int32,uint32, + float32,float64,complex64,complex128]: + a = array(ba,ctype) + a2 = array(ba2,ctype) + assert_array_equal(cumsum(a), array([1,3,13,24,30,35,39],ctype)) + assert_array_equal(cumsum(a2,axis=0), array([[1,2,3,4],[6,8,10,13], + [16,11,14,18]],ctype)) + assert_array_equal(cumsum(a2,axis=1), + array([[1,3,6,10], + [5,11,18,27], + [10,13,17,22]],ctype)) + +class test_prod(ScipyTestCase): + def check_basic(self): + ba = [1,2,10,11,6,5,4] + ba2 = [[1,2,3,4],[5,6,7,9],[10,3,4,5]] + for ctype in [int16,uint16,int32,uint32, + float32,float64,complex64,complex128]: + a = array(ba,ctype) + a2 = array(ba2,ctype) + if ctype in ['1', 'b']: + self.failUnlessRaises(ArithmeticError, prod, a) + self.failUnlessRaises(ArithmeticError, prod, a2, 1) + self.failUnlessRaises(ArithmeticError, prod, a) + else: + assert_equal(prod(a),26400) + assert_array_equal(prod(a2,axis=0), + array([50,36,84,180],ctype)) + assert_array_equal(prod(a2,axis=-1),array([24, 1890, 600],ctype)) + +class test_cumprod(ScipyTestCase): + def check_basic(self): + ba = [1,2,10,11,6,5,4] + ba2 = [[1,2,3,4],[5,6,7,9],[10,3,4,5]] + for ctype in [int16,uint16,int32,uint32, + float32,float64,complex64,complex128]: + a = array(ba,ctype) + a2 = array(ba2,ctype) + if ctype in ['1', 'b']: + self.failUnlessRaises(ArithmeticError, cumprod, a) + self.failUnlessRaises(ArithmeticError, cumprod, a2, 1) + self.failUnlessRaises(ArithmeticError, cumprod, a) + else: + assert_array_equal(cumprod(a,axis=-1), + array([1, 2, 20, 220, + 1320, 6600, 26400],ctype)) + assert_array_equal(cumprod(a2,axis=0), + array([[ 1, 2, 3, 4], + [ 5, 12, 21, 36], + [50, 36, 84, 180]],ctype)) + assert_array_equal(cumprod(a2,axis=-1), + array([[ 1, 2, 6, 24], + [ 5, 30, 210, 1890], + [10, 30, 120, 600]],ctype)) + +class test_diff(ScipyTestCase): + def check_basic(self): + x = [1,4,6,7,12] + out = array([3,2,1,5]) + out2 = array([-1,-1,4]) + out3 = array([0,5]) + assert_array_equal(diff(x),out) + assert_array_equal(diff(x,n=2),out2) + assert_array_equal(diff(x,n=3),out3) + + def check_nd(self): + x = 20*rand(10,20,30) + out1 = x[:,:,1:] - x[:,:,:-1] + out2 = out1[:,:,1:] - out1[:,:,:-1] + out3 = x[1:,:,:] - x[:-1,:,:] + out4 = out3[1:,:,:] - out3[:-1,:,:] + assert_array_equal(diff(x),out1) + assert_array_equal(diff(x,n=2),out2) + assert_array_equal(diff(x,axis=0),out3) + assert_array_equal(diff(x,n=2,axis=0),out4) + +class test_angle(ScipyTestCase): + def check_basic(self): + x = [1+3j,sqrt(2)/2.0+1j*sqrt(2)/2,1,1j,-1,-1j,1-3j,-1+3j] + y = angle(x) + yo = [arctan(3.0/1.0),arctan(1.0),0,pi/2,pi,-pi/2.0, + -arctan(3.0/1.0),pi-arctan(3.0/1.0)] + z = angle(x,deg=1) + zo = array(yo)*180/pi + assert_array_almost_equal(y,yo,11) + assert_array_almost_equal(z,zo,11) + +class test_trim_zeros(ScipyTestCase): + """ only testing for integer splits. + """ + def check_basic(self): + a= array([0,0,1,2,3,4,0]) + res = trim_zeros(a) + assert_array_equal(res,array([1,2,3,4])) + def check_leading_skip(self): + a= array([0,0,1,0,2,3,4,0]) + res = trim_zeros(a) + assert_array_equal(res,array([1,0,2,3,4])) + def check_trailing_skip(self): + a= array([0,0,1,0,2,3,0,4,0]) + res = trim_zeros(a) + assert_array_equal(res,array([1,0,2,3,0,4])) + + +class test_extins(ScipyTestCase): + def check_basic(self): + a = array([1,3,2,1,2,3,3]) + b = extract(a>1,a) + assert_array_equal(b,[3,2,2,3,3]) + def check_insert(self): + a = array([1,4,3,2,5,8,7]) + insert(a,[0,1,0,1,0,1,0],[2,4,6]) + assert_array_equal(a,[1,2,3,4,5,6,7]) + def check_both(self): + a = rand(10) + mask = a > 0.5 + ac = a.copy() + c = extract(mask, a) + insert(a,mask,0) + insert(a,mask,c) + assert_array_equal(a,ac) + +class test_vectorize(ScipyTestCase): + def check_simple(self): + def addsubtract(a,b): + if a > b: + return a - b + else: + return a + b + f = vectorize(addsubtract) + r = f([0,3,6,9],[1,3,5,7]) + assert_array_equal(r,[1,6,1,2]) + def check_scalar(self): + def addsubtract(a,b): + if a > b: + return a - b + else: + return a + b + f = vectorize(addsubtract) + r = f([0,3,6,9],5) + assert_array_equal(r,[5,8,1,4]) + + + +class test_unwrap(ScipyTestCase): + def check_simple(self): + #check that unwrap removes jumps greather that 2*pi + assert_array_equal(unwrap([1,1+2*pi]),[1,1]) + #check that unwrap maintans continuity + assert(all(diff(unwrap(rand(10)*100))<pi)) + + +class test_filterwindows(ScipyTestCase): + def check_hanning(self): + #check symmetry + w=hanning(10) + assert_array_almost_equal(w,flipud(w),7) + #check known value + assert_almost_equal(sum(w),4.500,4) + + def check_hamming(self): + #check symmetry + w=hamming(10) + assert_array_almost_equal(w,flipud(w),7) + #check known value + assert_almost_equal(sum(w),4.9400,4) + + def check_bartlett(self): + #check symmetry + w=bartlett(10) + assert_array_almost_equal(w,flipud(w),7) + #check known value + assert_almost_equal(sum(w),4.4444,4) + + def check_blackman(self): + #check symmetry + w=blackman(10) + assert_array_almost_equal(w,flipud(w),7) + #check known value + assert_almost_equal(sum(w),3.7800,4) + + +class test_trapz(ScipyTestCase): + def check_simple(self): + r=trapz(exp(-1.0/2*(arange(-10,10,.1))**2)/sqrt(2*pi),dx=0.1) + #check integral of normal equals 1 + assert_almost_equal(sum(r),1,7) + +class test_sinc(ScipyTestCase): + def check_simple(self): + assert(sinc(0)==1) + w=sinc(linspace(-1,1,100)) + #check symmetry + assert_array_almost_equal(w,flipud(w),7) + +class test_histogram(ScipyTestCase): + def check_simple(self): + n=100 + v=rand(n) + (a,b)=histogram(v) + #check if the sum of the bins equals the number of samples + assert(sum(a)==n) + #check that the bin counts are evenly spaced when the data is from a linear function + (a,b)=histogram(linspace(0,10,100)) + assert(all(a==10)) + + + + + +def compare_results(res,desired): + for i in range(len(desired)): + assert_array_equal(res[i],desired[i]) + +if __name__ == "__main__": + ScipyTest('scipy.base.function_base').run() diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py new file mode 100644 index 000000000..99a6f5160 --- /dev/null +++ b/numpy/core/tests/test_getlimits.py @@ -0,0 +1,38 @@ +""" Test functions for limits module. +""" + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base) +from scipy.base.getlimits import finfo +from scipy import single,double,longdouble +restore_path() + +################################################## + +class test_python_float(ScipyTestCase): + def check_singleton(self): + ftype = finfo(float) + ftype2 = finfo(float) + assert_equal(id(ftype),id(ftype2)) + +class test_single(ScipyTestCase): + def check_singleton(self): + ftype = finfo(single) + ftype2 = finfo(single) + assert_equal(id(ftype),id(ftype2)) + +class test_double(ScipyTestCase): + def check_singleton(self): + ftype = finfo(double) + ftype2 = finfo(double) + assert_equal(id(ftype),id(ftype2)) + +class test_longdouble(ScipyTestCase): + def check_singleton(self,level=2): + ftype = finfo(longdouble) + ftype2 = finfo(longdouble) + assert_equal(id(ftype),id(ftype2)) + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_index_tricks.py b/numpy/core/tests/test_index_tricks.py new file mode 100644 index 000000000..96e9dff84 --- /dev/null +++ b/numpy/core/tests/test_index_tricks.py @@ -0,0 +1,53 @@ + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base) +from scipy.base import * +restore_path() + +class test_grid(ScipyTestCase): + def check_basic(self): + a = mgrid[-1:1:10j] + b = mgrid[-1:1:0.1] + assert(a.shape == (10,)) + assert(b.shape == (20,)) + assert(a[0] == -1) + assert_almost_equal(a[-1],1) + assert(b[0] == -1) + assert_almost_equal(b[1]-b[0],0.1,11) + assert_almost_equal(b[-1],b[0]+19*0.1,11) + assert_almost_equal(a[1]-a[0],2.0/9.0,11) + + def check_nd(self): + c = mgrid[-1:1:10j,-2:2:10j] + d = mgrid[-1:1:0.1,-2:2:0.2] + assert(c.shape == (2,10,10)) + assert(d.shape == (2,20,20)) + assert_array_equal(c[0][0,:],-ones(10,'d')) + assert_array_equal(c[1][:,0],-2*ones(10,'d')) + assert_array_almost_equal(c[0][-1,:],ones(10,'d'),11) + assert_array_almost_equal(c[1][:,-1],2*ones(10,'d'),11) + assert_array_almost_equal(d[0,1,:]-d[0,0,:], 0.1*ones(20,'d'),11) + assert_array_almost_equal(d[1,:,1]-d[1,:,0], 0.2*ones(20,'d'),11) + +class test_concatenator(ScipyTestCase): + def check_1d(self): + assert_array_equal(r_[1,2,3,4,5,6],array([1,2,3,4,5,6])) + b = ones(5) + c = r_[b,0,0,b] + assert_array_equal(c,[1,1,1,1,1,0,0,1,1,1,1,1]) + + def check_2d(self): + b = rand(5,5) + c = rand(5,5) + d = r_[b,c,'1'] # append columns + assert(d.shape == (5,10)) + assert_array_equal(d[:,:5],b) + assert_array_equal(d[:,5:],c) + d = r_[b,c] + assert(d.shape == (10,5)) + assert_array_equal(d[:5,:],b) + assert_array_equal(d[5:,:],c) + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_ma.py b/numpy/core/tests/test_ma.py new file mode 100644 index 000000000..884a4a277 --- /dev/null +++ b/numpy/core/tests/test_ma.py @@ -0,0 +1,637 @@ +import scipy +import types, time +from scipy.base.ma import * +from scipy.testing import ScipyTestCase, ScipyTest +def eq(v,w): + result = allclose(v,w) + if not result: + print """Not eq: +%s +---- +%s"""% (str(v), str(w)) + return result + +class test_ma(ScipyTestCase): + def __init__(self, *args, **kwds): + ScipyTestCase.__init__(self, *args, **kwds) + self.setUp() + + def setUp (self): + x=scipy.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) + y=scipy.array([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.]) + a10 = 10. + m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1] + xm = array(x, mask=m1) + ym = array(y, mask=m2) + z = scipy.array([-.5, 0., .5, .8]) + zm = array(z, mask=[0,1,0,0]) + xf = scipy.where(m1, 1.e+20, x) + s = x.shape + xm.set_fill_value(1.e+20) + self.d = (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) + + def check_testBasic1d(self): + "Test of basic array creation and properties in 1 dimension." + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + self.failIf(isMaskedArray(x)) + self.failUnless(isMaskedArray(xm)) + self.assertEqual(shape(xm), s) + self.assertEqual(xm.shape, s) + self.assertEqual(xm.dtype, x.dtype) + self.assertEqual(xm.dtypechar, x.dtypechar) + self.assertEqual( xm.size , reduce(lambda x,y:x*y, s)) + self.assertEqual(count(xm) , len(m1) - reduce(lambda x,y:x+y, m1)) + self.failUnless(eq(xm, xf)) + self.failUnless(eq(filled(xm, 1.e20), xf)) + self.failUnless(eq(x, xm)) + + def check_testBasic2d(self): + "Test of basic array creation and properties in 2 dimensions." + for s in [(4,3), (6,2)]: + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + x.shape = s + y.shape = s + xm.shape = s + ym.shape = s + xf.shape = s + + self.failIf(isMaskedArray(x)) + self.failUnless(isMaskedArray(xm)) + self.assertEqual(shape(xm), s) + self.assertEqual(xm.shape, s) + self.assertEqual( xm.size , reduce(lambda x,y:x*y, s)) + self.assertEqual( count(xm) , len(m1) - reduce(lambda x,y:x+y, m1)) + self.failUnless(eq(xm, xf)) + self.failUnless(eq(filled(xm, 1.e20), xf)) + self.failUnless(eq(x, xm)) + self.setUp() + + def check_testArithmetic (self): + "Test of basic arithmetic." + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + a2d = array([[1,2],[0,4]]) + a2dm = masked_array(a2d, [[0,0],[1,0]]) + self.failUnless(eq (a2d * a2d, a2d * a2dm)) + self.failUnless(eq (a2d + a2d, a2d + a2dm)) + self.failUnless(eq (a2d - a2d, a2d - a2dm)) + for s in [(12,), (4,3), (2,6)]: + x = x.reshape(s) + y = y.reshape(s) + xm = xm.reshape(s) + ym = ym.reshape(s) + xf = xf.reshape(s) + self.failUnless(eq(-x, -xm)) + self.failUnless(eq(x + y, xm + ym)) + self.failUnless(eq(x - y, xm - ym)) + self.failUnless(eq(x * y, xm * ym)) + self.failUnless(eq(x / y, xm / ym)) + self.failUnless(eq(a10 + y, a10 + ym)) + self.failUnless(eq(a10 - y, a10 - ym)) + self.failUnless(eq(a10 * y, a10 * ym)) + self.failUnless(eq(a10 / y, a10 / ym)) + self.failUnless(eq(x + a10, xm + a10)) + self.failUnless(eq(x - a10, xm - a10)) + self.failUnless(eq(x * a10, xm * a10)) + self.failUnless(eq(x / a10, xm / a10)) + self.failUnless(eq(x**2, xm**2)) + self.failUnless(eq(abs(x)**2.5, abs(xm) **2.5)) + self.failUnless(eq(x**y, xm**ym)) + self.failUnless(eq(scipy.add(x,y), add(xm, ym))) + self.failUnless(eq(scipy.subtract(x,y), subtract(xm, ym))) + self.failUnless(eq(scipy.multiply(x,y), multiply(xm, ym))) + self.failUnless(eq(scipy.divide(x,y), divide(xm, ym))) + + + def check_testUfuncs1 (self): + "Test various functions such as sin, cos." + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + self.failUnless (eq(scipy.cos(x), cos(xm))) + self.failUnless (eq(scipy.cosh(x), cosh(xm))) + self.failUnless (eq(scipy.sin(x), sin(xm))) + self.failUnless (eq(scipy.sinh(x), sinh(xm))) + self.failUnless (eq(scipy.tan(x), tan(xm))) + self.failUnless (eq(scipy.tanh(x), tanh(xm))) + self.failUnless (eq(scipy.sqrt(abs(x)), sqrt(xm))) + self.failUnless (eq(scipy.log(abs(x)), log(xm))) + self.failUnless (eq(scipy.log10(abs(x)), log10(xm))) + self.failUnless (eq(scipy.exp(x), exp(xm))) + self.failUnless (eq(scipy.arcsin(z), arcsin(zm))) + self.failUnless (eq(scipy.arccos(z), arccos(zm))) + self.failUnless (eq(scipy.arctan(z), arctan(zm))) + self.failUnless (eq(scipy.arctan2(x, y), arctan2(xm, ym))) + self.failUnless (eq(scipy.absolute(x), absolute(xm))) + self.failUnless (eq(scipy.equal(x,y), equal(xm, ym))) + self.failUnless (eq(scipy.not_equal(x,y), not_equal(xm, ym))) + self.failUnless (eq(scipy.less(x,y), less(xm, ym))) + self.failUnless (eq(scipy.greater(x,y), greater(xm, ym))) + self.failUnless (eq(scipy.less_equal(x,y), less_equal(xm, ym))) + self.failUnless (eq(scipy.greater_equal(x,y), greater_equal(xm, ym))) + self.failUnless (eq(scipy.conjugate(x), conjugate(xm))) + self.failUnless (eq(scipy.concatenate((x,y)), concatenate((xm,ym)))) + self.failUnless (eq(scipy.concatenate((x,y)), concatenate((x,y)))) + self.failUnless (eq(scipy.concatenate((x,y)), concatenate((xm,y)))) + self.failUnless (eq(scipy.concatenate((x,y,x)), concatenate((x,ym,x)))) + + def check_xtestCount (self): + "Test count" + ott = array([0.,1.,2.,3.], mask=[1,0,0,0]) + self.failUnless( isinstance(count(ott), types.IntType)) + self.assertEqual(3, count(ott)) + self.assertEqual(1, count(1)) + self.failUnless (eq(0, array(1,mask=[1]))) + ott=ott.reshape((2,2)) + assert isMaskedArray(count(ott,0)) + assert isinstance(count(ott), types.IntType) + self.failUnless (eq(3, count(ott))) + assert getmask(count(ott,0)) is None + self.failUnless (eq([1,2],count(ott,0))) + + def check_testMinMax (self): + "Test minimum and maximum." + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + xr = scipy.ravel(x) #max doesn't work if shaped + xmr = ravel(xm) + self.failUnless (eq(max(xr), maximum(xmr))) #true because of careful selection of data + self.failUnless (eq(min(xr), minimum(xmr))) #true because of careful selection of data + + def check_testAddSumProd (self): + "Test add, sum, product." + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + self.failUnless (eq(scipy.add.reduce(x), add.reduce(x))) + self.failUnless (eq(scipy.add.accumulate(x), add.accumulate(x))) + self.failUnless (eq(4, sum(array(4)))) + self.failUnless (eq(4, sum(array(4), axis=0))) + self.failUnless (eq(scipy.sum(x), sum(x))) + self.failUnless (eq(scipy.sum(filled(xm,0)), sum(xm))) + self.failUnless (eq(scipy.sum(x,0), sum(x,0))) + self.failUnless (eq(scipy.product(x), product(x))) + self.failUnless (eq(scipy.product(x,0), product(x,0))) + self.failUnless (eq(scipy.product(filled(xm,1)), product(xm))) + if len(s) > 1: + self.failUnless (eq(scipy.concatenate((x,y),1), concatenate((xm,ym),1))) + self.failUnless (eq(scipy.add.reduce(x,1), add.reduce(x,1))) + self.failUnless (eq(scipy.sum(x,1), sum(x,1))) + self.failUnless (eq(scipy.product(x,1), product(x,1))) + + + def check_testCI(self): + "Test of conversions and indexing" + x1 = scipy.array([1,2,4,3]) + x2 = array(x1, mask = [1,0,0,0]) + x3 = array(x1, mask = [0,1,0,1]) + x4 = array(x1) + # test conversion to strings + junk, garbage = str(x2), repr(x2) + assert eq(scipy.sort(x1),sort(x2, fill_value=0)) + # tests of indexing + assert type(x2[1]) is type(x1[1]) + assert x1[1] == x2[1] + assert x2[0] is masked + assert eq(x1[2],x2[2]) + assert eq(x1[2:5],x2[2:5]) + assert eq(x1[:],x2[:]) + assert eq(x1[1:], x3[1:]) + x1[2]=9 + x2[2]=9 + assert eq(x1,x2) + x1[1:3] = 99 + x2[1:3] = 99 + assert eq(x1,x2) + x2[1] = masked + assert eq(x1,x2) + x2[1:3]=masked + assert eq(x1,x2) + x2[:] = x1 + x2[1] = masked + assert allequal(getmask(x2),array([0,1,0,0])) + x3[:] = masked_array([1,2,3,4],[0,1,1,0]) + assert allequal(getmask(x3), array([0,1,1,0])) + x4[:] = masked_array([1,2,3,4],[0,1,1,0]) + assert allequal(getmask(x4), array([0,1,1,0])) + assert allequal(x4, array([1,2,3,4])) + x1 = scipy.arange(5)*1.0 + x2 = masked_values(x1, 3.0) + assert eq(x1,x2) + assert allequal(array([0,0,0,1,0],MaskType), x2.mask) + assert eq(3.0, x2.fill_value()) + x1 = array([1,'hello',2,3],object) + x2 = scipy.array([1,'hello',2,3],object) + s1 = x1[1].item() + s2 = x2[1].item() + self.assertEqual(type(s2), str) + self.assertEqual(type(s1), str) + self.assertEqual(s1, s2) + assert x1[1:1].shape == (0,) + + def check_testCopySize(self): + "Tests of some subtle points of copying and sizing." + n = [0,0,1,0,0] + m = make_mask(n) + m2 = make_mask(m) + self.failUnless(m is m2) + m3 = make_mask(m, copy=1) + self.failUnless(m is not m3) + + x1 = scipy.arange(5) + y1 = array(x1, mask=m) + self.failUnless( y1.raw_data() is not x1) + self.failUnless( allequal(x1,y1.raw_data())) + self.failUnless( y1.mask is m) + + y1a = array(y1, copy=0) + self.failUnless( y1a.raw_data() is y1.raw_data()) + self.failUnless( y1a.mask is y1.mask) + + y2 = array(x1, mask=m, copy=0) + self.failUnless( y2.raw_data() is x1) + self.failUnless( y2.mask is m) + self.failUnless( y2[2] is masked) + y2[2]=9 + self.failUnless( y2[2] is not masked) + self.failUnless( y2.mask is not m) + self.failUnless( allequal(y2.mask, 0)) + + y3 = array(x1*1.0, mask=m) + self.failUnless(filled(y3).dtype is (x1*1.0).dtype) + + x4 = arange(4) + x4[2] = masked + y4 = resize(x4, (8,)) + self.failUnless( eq(concatenate([x4,x4]), y4)) + self.failUnless( eq(getmask(y4),[0,0,1,0,0,0,1,0])) + y5 = repeat(x4, (2,2,2,2)) + self.failUnless( eq(y5, [0,0,1,1,2,2,3,3])) + y6 = repeat(x4, 2) + self.failUnless( eq(y5, y6)) + + def check_testPut(self): + "Test of put" + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + x = array(d, mask = m) + self.failUnless( x[3] is masked) + self.failUnless( x[4] is masked) + x[[1,4]] = [10,40] + self.failUnless( x.mask is not m) + self.failUnless( x[3] is masked) + self.failUnless( x[4] is not masked) + self.failUnless( eq(x, [0,10,2,-1,40])) + + x = array(d, mask = m) + x.put([-1,100,200]) + self.failUnless( eq(x, [-1,100,200,0,0])) + self.failUnless( x[3] is masked) + self.failUnless( x[4] is masked) + + x = array(d, mask = m) + x.putmask([30,40]) + self.failUnless( eq(x, [0,1,2,30,40])) + self.failUnless( x.mask is None) + + x = array(d, mask = m) + y = x.compressed() + z = array(x, mask = m) + z.put(y) + assert eq (x, z) + + def check_testMaPut(self): + (x, y, a10, m1, m2, xm, ym, z, zm, xf, s) = self.d + m = [1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1] + i = scipy.nonzero(m) + putmask(xm, m, z) + assert take(xm, i) == z + put(ym, i, zm) + assert take(ym, i) == zm + + def check_testOddFeatures(self): + "Test of other odd features" + x = arange(20); x=x.reshape(4,5) + x.flat[5] = 12 + assert x[1,0] == 12 + z = x + 10j * x + assert eq(z.real, x) + assert eq(z.imag, 10*x) + assert eq((z*conjugate(z)).real, 101*x*x) + z.imag[...] = 0.0 + + x = arange(10) + x[3] = masked + assert str(x[3]) == str(masked) + c = x >= 8 + assert count(where(c,masked,masked)) == 0 + assert shape(where(c,masked,masked)) == c.shape + z = where(c , x, masked) + assert z.dtype is x.dtype + assert z[3] is masked + assert z[4] is masked + assert z[7] is masked + assert z[8] is not masked + assert z[9] is not masked + assert eq(x,z) + z = where(c , masked, x) + assert z.dtype is x.dtype + assert z[3] is masked + assert z[4] is not masked + assert z[7] is not masked + assert z[8] is masked + assert z[9] is masked + z = masked_where(c, x) + assert z.dtype is x.dtype + assert z[3] is masked + assert z[4] is not masked + assert z[7] is not masked + assert z[8] is masked + assert z[9] is masked + assert eq(x,z) + x = array([1.,2.,3.,4.,5.]) + c = array([1,1,1,0,0]) + x[2] = masked + z = where(c, x, -x) + assert eq(z, [1.,2.,0., -4., -5]) + c[0] = masked + z = where(c, x, -x) + assert eq(z, [1.,2.,0., -4., -5]) + assert z[0] is masked + assert z[1] is not masked + assert z[2] is masked + assert eq(masked_where(greater(x, 2), x), masked_greater(x,2)) + assert eq(masked_where(greater_equal(x, 2), x), masked_greater_equal(x,2)) + assert eq(masked_where(less(x, 2), x), masked_less(x,2)) + assert eq(masked_where(less_equal(x, 2), x), masked_less_equal(x,2)) + assert eq(masked_where(not_equal(x, 2), x), masked_not_equal(x,2)) + assert eq(masked_where(equal(x, 2), x), masked_equal(x,2)) + assert eq(masked_where(not_equal(x,2), x), masked_not_equal(x,2)) + assert eq(masked_inside(range(5), 1, 3), [0, 199, 199, 199, 4]) + assert eq(masked_outside(range(5), 1, 3),[199,1,2,3,199]) + assert eq(masked_inside(array(range(5), mask=[1,0,0,0,0]), 1, 3).mask, [1,1,1,1,0]) + assert eq(masked_outside(array(range(5), mask=[0,1,0,0,0]), 1, 3).mask, [1,1,0,0,1]) + assert eq(masked_equal(array(range(5), mask=[1,0,0,0,0]), 2).mask, [1,0,1,0,0]) + assert eq(masked_not_equal(array([2,2,1,2,1], mask=[1,0,0,0,0]), 2).mask, [1,0,1,0,1]) + assert eq(masked_where([1,1,0,0,0], [1,2,3,4,5]), [99,99,3,4,5]) + atest = ones((10,10,10), dtype=float32) + btest = zeros(atest.shape, MaskType) + ctest = masked_where(btest,atest) + assert eq(atest,ctest) + z = choose(c, (-x, x)) + assert eq(z, [1.,2.,0., -4., -5]) + assert z[0] is masked + assert z[1] is not masked + assert z[2] is masked + x = arange(6) + x[5] = masked + y = arange(6)*10 + y[2]= masked + c = array([1,1,1,0,0,0], mask=[1,0,0,0,0,0]) + cm = c.filled(1) + z = where(c,x,y) + zm = where(cm,x,y) + assert eq(z, zm) + assert getmask(zm) is None + assert eq(zm, [0,1,2,30,40,50]) + z = where(c, masked, 1) + assert eq(z, [99,99,99,1,1,1]) + z = where(c, 1, masked) + assert eq(z, [99, 1, 1, 99, 99, 99]) + + def check_testMinMax(self): + "Test of minumum, maximum." + assert eq(minimum([1,2,3],[4,0,9]), [1,0,3]) + assert eq(maximum([1,2,3],[4,0,9]), [4,2,9]) + x = arange(5) + y = arange(5) - 2 + x[3] = masked + y[0] = masked + assert eq(minimum(x,y), where(less(x,y), x, y)) + assert eq(maximum(x,y), where(greater(x,y), x, y)) + assert minimum(x) == 0 + assert maximum(x) == 4 + + def check_testTakeTransposeInnerOuter(self): + "Test of take, transpose, inner, outer products" + x = arange(24) + y = scipy.arange(24) + x[5:6] = masked + x=x.reshape(2,3,4) + y=y.reshape(2,3,4) + assert eq(scipy.transpose(y,(2,0,1)), transpose(x,(2,0,1))) + assert eq(scipy.take(y, (2,0,1), 1), take(x, (2,0,1), 1)) + assert eq(scipy.innerproduct(filled(x,0),filled(y,0)), + innerproduct(x, y)) + assert eq(scipy.outerproduct(filled(x,0),filled(y,0)), + outerproduct(x, y)) + y = array(['abc', 1, 'def', 2, 3], object) + y[2] = masked + t = take(y,[0,3,4]) + assert t[0].item() == 'abc' + assert t[1].item() == 2 + assert t[2].item() == 3 + + def check_testInplace(self): + """Test of inplace operations and rich comparisons""" + y = arange(10) + + x = arange(10) + xm = arange(10) + xm[2] = masked + x += 1 + assert eq(x, y+1) + xm += 1 + assert eq(x, y+1) + + x = arange(10) + xm = arange(10) + xm[2] = masked + x -= 1 + assert eq(x, y-1) + xm -= 1 + assert eq(xm, y-1) + + x = arange(10)*1.0 + xm = arange(10)*1.0 + xm[2] = masked + x *= 2.0 + assert eq(x, y*2) + xm *= 2.0 + assert eq(xm, y*2) + + x = arange(10)*2 + xm = arange(10) + xm[2] = masked + x /= 2 + assert eq(x, y) + xm /= 2 + assert eq(x, y) + + x = arange(10)*1.0 + xm = arange(10)*1.0 + xm[2] = masked + x /= 2.0 + assert eq(x, y/2.0) + xm /= arange(10) + assert eq(xm, ones((10,))) + + x = arange(10).astype(float32) + xm = arange(10) + xm[2] = masked + id1 = id(x.raw_data()) + x += 1. + assert id1 == id(x.raw_data()) + assert eq(x, y+1.) + + def check_testPickle(self): + "Test of pickling" + x = arange(12) + x[4:10:2] = masked + x=x.reshape(4,3) + f = open('test9.pik','wb') + import pickle + pickle.dump(x, f) + f.close() + f = open('test9.pik', 'rb') + y = pickle.load(f) + assert eq(x,y) + + def check_testMasked(self): + "Test of masked element" + xx=arange(6) + xx[1] = masked + self.failUnless(xx[1] is masked) + self.failUnlessRaises(Exception, lambda x,y: x+y, masked, masked) + self.failUnlessRaises(Exception, lambda x,y: x+y, masked, 2) + self.failUnlessRaises(Exception, lambda x,y: x+y, masked, xx) + self.failUnlessRaises(Exception, lambda x,y: x+y, xx, masked) + + def check_testAverage1(self): + "Test of average." + ott = array([0.,1.,2.,3.], mask=[1,0,0,0]) + self.failUnless(eq(2.0, average(ott))) + self.failUnless(eq(2.0, average(ott, weights=[1., 1., 2., 1.]))) + result, wts = average(ott, weights=[1.,1.,2.,1.], returned=1) + self.failUnless(eq(2.0, result)) + self.failUnless(wts == 4.0) + ott[:] = masked + self.failUnless(average(ott) is masked) + ott = array([0.,1.,2.,3.], mask=[1,0,0,0]) + ott=ott.reshape(2,2) + ott[:,1] = masked + self.failUnless(eq(average(ott), [2.0, 0.0])) + self.failUnless(average(ott,axis=1)[0] is masked) + self.failUnless(eq([2.,0.], average(ott))) + result, wts = average(ott, returned=1) + self.failUnless(eq(wts, [1., 0.])) + + def check_testAverage2(self): + "More tests of average." + w1 = [0,1,1,1,1,0] + w2 = [[0,1,1,1,1,0],[1,0,0,0,0,1]] + x=arange(6) + self.failUnless(allclose(average(x), 2.5)) + self.failUnless(allclose(average(x, weights=w1), 2.5)) + y=array([arange(6), 2.0*arange(6)]) + self.failUnless(allclose(average(y, None), scipy.add.reduce(scipy.arange(6))*3./12.)) + self.failUnless(allclose(average(y, axis=0), scipy.arange(6) * 3./2.)) + self.failUnless(allclose(average(y, axis=1), [average(x), average(x) * 2.0])) + self.failUnless(allclose(average(y, None, weights=w2), 20./6.)) + self.failUnless(allclose(average(y, axis=0, weights=w2), [0.,1.,2.,3.,4.,10.])) + self.failUnless(allclose(average(y, axis=1), [average(x), average(x) * 2.0])) + m1 = zeros(6) + m2 = [0,0,1,1,0,0] + m3 = [[0,0,1,1,0,0],[0,1,1,1,1,0]] + m4 = ones(6) + m5 = [0, 1, 1, 1, 1, 1] + self.failUnless(allclose(average(masked_array(x, m1)), 2.5)) + self.failUnless(allclose(average(masked_array(x, m2)), 2.5)) + self.failUnless(average(masked_array(x, m4)) is masked) + self.assertEqual(average(masked_array(x, m5)), 0.0) + self.assertEqual(count(average(masked_array(x, m4))), 0) + z = masked_array(y, m3) + self.failUnless(allclose(average(z, None), 20./6.)) + self.failUnless(allclose(average(z, axis=0), [0.,1.,99.,99.,4.0, 7.5])) + self.failUnless(allclose(average(z, axis=1), [2.5, 5.0])) + self.failUnless(allclose( average(z,weights=w2), [0.,1., 99., 99., 4.0, 10.0])) + + a = arange(6) + b = arange(6) * 3 + r1, w1 = average([[a,b],[b,a]], axis=1, returned=1) + self.assertEqual(shape(r1) , shape(w1)) + self.assertEqual(r1.shape , w1.shape) + r2, w2 = average(ones((2,2,3)), axis=0, weights=[3,1], returned=1) + self.assertEqual(shape(w2) , shape(r2)) + r2, w2 = average(ones((2,2,3)), returned=1) + self.assertEqual(shape(w2) , shape(r2)) + r2, w2 = average(ones((2,2,3)), weights=ones((2,2,3)), returned=1) + self.failUnless(shape(w2) == shape(r2)) + a2d = array([[1,2],[0,4]], float) + a2dm = masked_array(a2d, [[0,0],[1,0]]) + a2da = average(a2d) + self.failUnless(eq (a2da, [0.5, 3.0])) + a2dma = average(a2dm) + self.failUnless(eq( a2dma, [1.0, 3.0])) + a2dma = average(a2dm, axis=None) + self.failUnless(eq(a2dma, 7./3.)) + a2dma = average(a2dm, axis=1) + self.failUnless(eq(a2dma, [1.5, 4.0])) + + def check_testToPython(self): + self.assertEqual(1, int(array(1))) + self.assertEqual(1.0, float(array(1))) + self.assertEqual(1, int(array([[[1]]]))) + self.assertEqual(1.0, float(array([[1]]))) + self.failUnlessRaises(ValueError, float, array([1,1])) + self.failUnlessRaises(MAError, float, array([1],mask=[1])) + +def timingTest(): + for f in [testf, testinplace]: + for n in [1000,10000,50000]: + t = testta(n, f) + t1 = testtb(n, f) + t2 = testtc(n, f) + print f.test_name + print """\ +n = %7d +scipy time (ms) %6.1f +MA maskless ratio %6.1f +MA masked ratio %6.1f +""" % (n, t*1000.0, t1/t, t2/t) + +def testta(n, f): + x=scipy.arange(n) + 1.0 + tn0 = time.time() + z = f(x) + return time.time() - tn0 + +def testtb(n, f): + x=arange(n) + 1.0 + tn0 = time.time() + z = f(x) + return time.time() - tn0 + +def testtc(n, f): + x=arange(n) + 1.0 + x[0] = masked + tn0 = time.time() + z = f(x) + return time.time() - tn0 + +def testf(x): + for i in range(25): + y = x **2 + 2.0 * x - 1.0 + w = x **2 + 1.0 + z = (y / w) ** 2 + return z +testf.test_name = 'Simple arithmetic' + +def testinplace(x): + for i in range(25): + y = x**2 + y += 2.0*x + y -= 1.0 + y /= x + return y +testinplace.test_name = 'Inplace operations' + +if __name__ == "__main__": + ScipyTest('scipy.base.ma').run() + #timingTest() diff --git a/numpy/core/tests/test_matrix.py b/numpy/core/tests/test_matrix.py new file mode 100644 index 000000000..59b0a131e --- /dev/null +++ b/numpy/core/tests/test_matrix.py @@ -0,0 +1,117 @@ + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base) +from scipy.base import * +restore_path() + +class test_ctor(ScipyTestCase): + def test_basic(self): + A = array([[1,2],[3,4]]) + mA = matrix(A) + assert all(mA.A == A) + + B = bmat("A,A;A,A") + C = bmat([[A,A], [A,A]]) + D = array([[1,2,1,2], + [3,4,3,4], + [1,2,1,2], + [3,4,3,4]]) + assert all(B.A == D) + assert all(C.A == D) + + vec = arange(5) + mvec = matrix(vec) + assert mvec.shape == (1,5) + +class test_properties(ScipyTestCase): + def test_basic(self): + import scipy.corelinalg as linalg + + A = array([[1., 2.], + [3., 4.]]) + mA = matrix(A) + assert allclose(linalg.inv(A), mA.I) + assert all(array(transpose(A) == mA.T)) + assert all(array(transpose(A) == mA.H)) + assert all(A == mA.A) + + B = A + 2j*A + mB = matrix(B) + assert allclose(linalg.inv(B), mB.I) + assert all(array(transpose(B) == mB.T)) + assert all(array(conjugate(transpose(B)) == mB.H)) + + def test_comparisons(self): + A = arange(100).reshape(10,10) + mA = matrix(A) + mB = matrix(A) + 0.1 + assert all(mB == A+0.1) + assert all(mB == matrix(A+0.1)) + assert not any(mB == matrix(A-0.1)) + assert all(mA < mB) + assert all(mA <= mB) + assert all(mA <= mA) + assert not any(mA < mA) + + assert not any(mB < mA) + assert all(mB >= mA) + assert all(mB >= mB) + assert not any(mB > mB) + + assert all(mA == mA) + assert not any(mA == mB) + assert all(mB != mA) + + assert not all(abs(mA) > 0) + assert all(abs(mB > 0)) + + def test_asmatrix(self): + A = arange(100).reshape(10,10) + mA = asmatrix(A) + mB = matrix(A) + A[0,0] = -10 + assert A[0,0] == mA[0,0] + assert A[0,0] != mB[0,0] + +class test_autocasting(ScipyTestCase): + def test_basic(self): + A = arange(100).reshape(10,10) + mA = matrix(A) + + mB = mA.copy() + O = ones((10,10), float64) * 0.1 + mB = mB + O + assert mB.dtype == float64 + assert all(mA != mB) + assert all(mB == mA+0.1) + + mC = mA.copy() + O = ones((10,10), complex128) + mC = mC * O + assert mC.dtype == complex128 + assert all(mA != mB) + +class test_algebra(ScipyTestCase): + def test_basic(self): + import scipy.corelinalg as linalg + + A = array([[1., 2.], + [3., 4.]]) + mA = matrix(A) + + B = identity(2) + for i in xrange(6): + assert allclose((mA ** i).A, B) + B = dot(B, A) + + Ainv = linalg.inv(A) + B = identity(2) + for i in xrange(6): + assert allclose((mA ** -i).A, B) + B = dot(B, Ainv) + + assert allclose((mA * mA).A, dot(A, A)) + assert allclose((mA + mA).A, (A + A)) + assert allclose((3*mA).A, (3*A)) + diff --git a/numpy/core/tests/test_polynomial.py b/numpy/core/tests/test_polynomial.py new file mode 100644 index 000000000..51d4b5707 --- /dev/null +++ b/numpy/core/tests/test_polynomial.py @@ -0,0 +1,83 @@ +""" +>>> import scipy.base as nx +>>> from scipy.base.polynomial import poly1d, polydiv + +>>> p = poly1d([1.,2,3]) +>>> p +poly1d([ 1., 2., 3.]) +>>> print p + 2 +1 x + 2 x + 3 +>>> q = poly1d([3.,2,1]) +>>> q +poly1d([ 3., 2., 1.]) +>>> print q + 2 +3 x + 2 x + 1 + +>>> p(0) +3.0 +>>> p(5) +38.0 +>>> q(0) +1.0 +>>> q(5) +86.0 + +>>> p * q +poly1d([ 3., 8., 14., 8., 3.]) +>>> p / q +(poly1d([ 0.33333333]), poly1d([ 1.33333333, 2.66666667])) +>>> p + q +poly1d([ 4., 4., 4.]) +>>> p - q +poly1d([-2., 0., 2.]) +>>> p ** 4 +poly1d([ 1., 8., 36., 104., 214., 312., 324., 216., 81.]) + +>>> p(q) +poly1d([ 9., 12., 16., 8., 6.]) +>>> q(p) +poly1d([ 3., 12., 32., 40., 34.]) + +>>> nx.asarray(p) +array([ 1., 2., 3.]) +>>> len(p) +2 + +>>> p[0], p[1], p[2], p[3] +(3.0, 2.0, 1.0, 0) + +>>> p.integ() +poly1d([ 0.33333333, 1. , 3. , 0. ]) +>>> p.integ(1) +poly1d([ 0.33333333, 1. , 3. , 0. ]) +>>> p.integ(5) +poly1d([ 0.00039683, 0.00277778, 0.025 , 0. , 0. , + 0. , 0. , 0. ]) +>>> p.deriv() +poly1d([ 2., 2.]) +>>> p.deriv(2) +poly1d([ 2.]) + +>>> q = poly1d([1.,2,3], variable='y') +>>> print q + 2 +1 y + 2 y + 3 +>>> q = poly1d([1.,2,3], variable='lambda') +>>> print q + 2 +1 lambda + 2 lambda + 3 + +>>> polydiv(poly1d([1,0,-1]), poly1d([1,1])) +(poly1d([ 1., -1.]), poly1d([ 0.])) +""" + +from scipy.testing import * + +import doctest +def test_suite(level=1): + return doctest.DocTestSuite() + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py new file mode 100644 index 000000000..8135a55a8 --- /dev/null +++ b/numpy/core/tests/test_records.py @@ -0,0 +1,44 @@ + +from scipy.testing import * +set_package_path() +import os as _os +import scipy.base;reload(scipy.base) +from scipy.base import * +from scipy.base import records as rec +restore_path() + +class test_fromrecords(ScipyTestCase): + def check_fromrecords(self): + r = rec.fromrecords([[456,'dbe',1.2],[2,'de',1.3]],names='col1,col2,col3') + assert_equal(r[0].item(),(456, 'dbe', 1.2)) + + def check_method_array(self): + r = rec.array('abcdefg'*100,formats='i2,a3,i4',shape=3,byteorder='big') + assert_equal(r[1].item(),(25444, 'efg', 1633837924)) + + def check_method_array2(self): + r=rec.array([(1,11,'a'),(2,22,'b'),(3,33,'c'),(4,44,'d'),(5,55,'ex'),(6,66,'f'),(7,77,'g')],formats='u1,f4,a1') + assert_equal(r[1].item(),(2, 22.0, 'b')) + + def check_recarray_slices(self): + r=rec.array([(1,11,'a'),(2,22,'b'),(3,33,'c'),(4,44,'d'),(5,55,'ex'),(6,66,'f'),(7,77,'g')],formats='u1,f4,a1') + assert_equal(r[1::2][1].item(),(4, 44.0, 'd')) + + def check_recarray_fromarrays(self): + x1 = array([1,2,3,4]) + x2 = array(['a','dd','xyz','12']) + x3 = array([1.1,2,3,4]) + r = rec.fromarrays([x1,x2,x3],names='a,b,c') + assert_equal(r[1].item(),(2,'dd',2.0)) + x1[1] = 34 + assert_equal(r.a,array([1,2,3,4])) + + def check_recarray_fromfile(self): + __path__ = _os.path.split(__file__) + filename = _os.path.join(__path__[0], "testdata.fits") + fd = open(filename) + fd.seek(2880*2) + r = rec.fromfile(fd, formats='f8,i4,a5', shape=3, byteorder='big') + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py new file mode 100644 index 000000000..005868e96 --- /dev/null +++ b/numpy/core/tests/test_shape_base.py @@ -0,0 +1,364 @@ + +from scipy.testing import * +set_package_path() +import scipy.base; +from scipy.base import * +restore_path() + +class test_apply_along_axis(ScipyTestCase): + def check_simple(self): + a = ones((20,10),'d') + assert_array_equal(apply_along_axis(len,0,a),len(a)*ones(shape(a)[1])) + def check_simple101(self,level=11): + # This test causes segmentation fault (Numeric 23.3,23.6,Python 2.3.4) + # when enabled and shape(a)[1]>100. See Issue 202. + a = ones((10,101),'d') + assert_array_equal(apply_along_axis(len,0,a),len(a)*ones(shape(a)[1])) + +class test_array_split(ScipyTestCase): + def check_integer_0_split(self): + a = arange(10) + try: + res = array_split(a,0) + assert(0) # it should have thrown a value error + except ValueError: + pass + def check_integer_split(self): + a = arange(10) + res = array_split(a,1) + desired = [arange(10)] + compare_results(res,desired) + + res = array_split(a,2) + desired = [arange(5),arange(5,10)] + compare_results(res,desired) + + res = array_split(a,3) + desired = [arange(4),arange(4,7),arange(7,10)] + compare_results(res,desired) + + res = array_split(a,4) + desired = [arange(3),arange(3,6),arange(6,8),arange(8,10)] + compare_results(res,desired) + + res = array_split(a,5) + desired = [arange(2),arange(2,4),arange(4,6),arange(6,8),arange(8,10)] + compare_results(res,desired) + + res = array_split(a,6) + desired = [arange(2),arange(2,4),arange(4,6),arange(6,8),arange(8,9), + arange(9,10)] + compare_results(res,desired) + + res = array_split(a,7) + desired = [arange(2),arange(2,4),arange(4,6),arange(6,7),arange(7,8), + arange(8,9), arange(9,10)] + compare_results(res,desired) + + res = array_split(a,8) + desired = [arange(2),arange(2,4),arange(4,5),arange(5,6),arange(6,7), + arange(7,8), arange(8,9), arange(9,10)] + compare_results(res,desired) + + res = array_split(a,9) + desired = [arange(2),arange(2,3),arange(3,4),arange(4,5),arange(5,6), + arange(6,7), arange(7,8), arange(8,9), arange(9,10)] + compare_results(res,desired) + + res = array_split(a,10) + desired = [arange(1),arange(1,2),arange(2,3),arange(3,4), + arange(4,5),arange(5,6), arange(6,7), arange(7,8), + arange(8,9), arange(9,10)] + compare_results(res,desired) + + res = array_split(a,11) + desired = [arange(1),arange(1,2),arange(2,3),arange(3,4), + arange(4,5),arange(5,6), arange(6,7), arange(7,8), + arange(8,9), arange(9,10),array([])] + compare_results(res,desired) + def check_integer_split_2D_rows(self): + a = array([arange(10),arange(10)]) + res = array_split(a,3,axis=0) + desired = [array([arange(10)]),array([arange(10)]),array([])] + compare_results(res,desired) + def check_integer_split_2D_cols(self): + a = array([arange(10),arange(10)]) + res = array_split(a,3,axis=-1) + desired = [array([arange(4),arange(4)]), + array([arange(4,7),arange(4,7)]), + array([arange(7,10),arange(7,10)])] + compare_results(res,desired) + def check_integer_split_2D_default(self): + """ This will fail if we change default axis + """ + a = array([arange(10),arange(10)]) + res = array_split(a,3) + desired = [array([arange(10)]),array([arange(10)]),array([])] + compare_results(res,desired) + #perhaps should check higher dimensions + + def check_index_split_simple(self): + a = arange(10) + indices = [1,5,7] + res = array_split(a,indices,axis=-1) + desired = [arange(0,1),arange(1,5),arange(5,7),arange(7,10)] + compare_results(res,desired) + + def check_index_split_low_bound(self): + a = arange(10) + indices = [0,5,7] + res = array_split(a,indices,axis=-1) + desired = [array([]),arange(0,5),arange(5,7),arange(7,10)] + compare_results(res,desired) + def check_index_split_high_bound(self): + a = arange(10) + indices = [0,5,7,10,12] + res = array_split(a,indices,axis=-1) + desired = [array([]),arange(0,5),arange(5,7),arange(7,10), + array([]),array([])] + compare_results(res,desired) + +class test_split(ScipyTestCase): + """* This function is essentially the same as array_split, + except that it test if splitting will result in an + equal split. Only test for this case. + *""" + def check_equal_split(self): + a = arange(10) + res = split(a,2) + desired = [arange(5),arange(5,10)] + compare_results(res,desired) + + def check_unequal_split(self): + a = arange(10) + try: + res = split(a,3) + assert(0) # should raise an error + except ValueError: + pass + +class test_atleast_1d(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=map(atleast_1d,[a,b]) + desired = [array([1]),array([2])] + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1,2]); b = array([2,3]); + res=map(atleast_1d,[a,b]) + desired = [array([1,2]),array([2,3])] + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + res=map(atleast_1d,[a,b]) + desired = [a,b] + assert_array_equal(res,desired) + def check_3D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + a = array([a,a]);b = array([b,b]); + res=map(atleast_1d,[a,b]) + desired = [a,b] + assert_array_equal(res,desired) + def check_r1array(self): + """ Test to make sure equivalent Travis O's r1array function + """ + assert(atleast_1d(3).shape == (1,)) + assert(atleast_1d(3j).shape == (1,)) + assert(atleast_1d(3L).shape == (1,)) + assert(atleast_1d(3.0).shape == (1,)) + assert(atleast_1d([[2,3],[4,5]]).shape == (2,2)) + +class test_atleast_2d(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=map(atleast_2d,[a,b]) + desired = [array([[1]]),array([[2]])] + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1,2]); b = array([2,3]); + res=map(atleast_2d,[a,b]) + desired = [array([[1,2]]),array([[2,3]])] + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + res=map(atleast_2d,[a,b]) + desired = [a,b] + assert_array_equal(res,desired) + def check_3D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + a = array([a,a]);b = array([b,b]); + res=map(atleast_2d,[a,b]) + desired = [a,b] + assert_array_equal(res,desired) + def check_r2array(self): + """ Test to make sure equivalent Travis O's r2array function + """ + assert(atleast_2d(3).shape == (1,1)) + assert(atleast_2d([3j,1]).shape == (1,2)) + assert(atleast_2d([[[3,1],[4,5]],[[3,5],[1,2]]]).shape == (2,2,2)) + +class test_atleast_3d(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=map(atleast_3d,[a,b]) + desired = [array([[[1]]]),array([[[2]]])] + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1,2]); b = array([2,3]); + res=map(atleast_3d,[a,b]) + desired = [array([[[1],[2]]]),array([[[2],[3]]])] + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + res=map(atleast_3d,[a,b]) + desired = [a[:,:,NewAxis],b[:,:,NewAxis]] + assert_array_equal(res,desired) + def check_3D_array(self): + a = array([[1,2],[1,2]]); b = array([[2,3],[2,3]]); + a = array([a,a]);b = array([b,b]); + res=map(atleast_3d,[a,b]) + desired = [a,b] + assert_array_equal(res,desired) + +class test_hstack(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=hstack([a,b]) + desired = array([1,2]) + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1]); b = array([2]); + res=hstack([a,b]) + desired = array([1,2]) + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1],[2]]); b = array([[1],[2]]); + res=hstack([a,b]) + desired = array([[1,1],[2,2]]) + assert_array_equal(res,desired) + +class test_vstack(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=vstack([a,b]) + desired = array([[1],[2]]) + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1]); b = array([2]); + res=vstack([a,b]) + desired = array([[1],[2]]) + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1],[2]]); b = array([[1],[2]]); + res=vstack([a,b]) + desired = array([[1],[2],[1],[2]]) + assert_array_equal(res,desired) + def check_2D_array2(self): + a = array([1,2]); b = array([1,2]); + res=vstack([a,b]) + desired = array([[1,2],[1,2]]) + assert_array_equal(res,desired) + +class test_dstack(ScipyTestCase): + def check_0D_array(self): + a = array(1); b = array(2); + res=dstack([a,b]) + desired = array([[[1,2]]]) + assert_array_equal(res,desired) + def check_1D_array(self): + a = array([1]); b = array([2]); + res=dstack([a,b]) + desired = array([[[1,2]]]) + assert_array_equal(res,desired) + def check_2D_array(self): + a = array([[1],[2]]); b = array([[1],[2]]); + res=dstack([a,b]) + desired = array([[[1,1]],[[2,2,]]]) + assert_array_equal(res,desired) + def check_2D_array2(self): + a = array([1,2]); b = array([1,2]); + res=dstack([a,b]) + desired = array([[[1,1],[2,2]]]) + assert_array_equal(res,desired) + +""" array_split has more comprehensive test of splitting. + only do simple test on hsplit, vsplit, and dsplit +""" +class test_hsplit(ScipyTestCase): + """ only testing for integer splits. + """ + def check_0D_array(self): + a= array(1) + try: + hsplit(a,2) + assert(0) + except ValueError: + pass + def check_1D_array(self): + a= array([1,2,3,4]) + res = hsplit(a,2) + desired = [array([1,2]),array([3,4])] + compare_results(res,desired) + def check_2D_array(self): + a= array([[1,2,3,4], + [1,2,3,4]]) + res = hsplit(a,2) + desired = [array([[1,2],[1,2]]),array([[3,4],[3,4]])] + compare_results(res,desired) + +class test_vsplit(ScipyTestCase): + """ only testing for integer splits. + """ + def check_1D_array(self): + a= array([1,2,3,4]) + try: + vsplit(a,2) + assert(0) + except ValueError: + pass + def check_2D_array(self): + a= array([[1,2,3,4], + [1,2,3,4]]) + res = vsplit(a,2) + desired = [array([[1,2,3,4]]),array([[1,2,3,4]])] + compare_results(res,desired) + +class test_dsplit(ScipyTestCase): + """ only testing for integer splits. + """ + def check_2D_array(self): + a= array([[1,2,3,4], + [1,2,3,4]]) + try: + dsplit(a,2) + assert(0) + except ValueError: + pass + def check_3D_array(self): + a= array([[[1,2,3,4], + [1,2,3,4]], + [[1,2,3,4], + [1,2,3,4]]]) + res = dsplit(a,2) + desired = [array([[[1,2],[1,2]],[[1,2],[1,2]]]), + array([[[3,4],[3,4]],[[3,4],[3,4]]])] + compare_results(res,desired) + +class test_squeeze(ScipyTestCase): + def check_basic(self): + a = rand(20,10,10,1,1) + b = rand(20,1,10,1,20) + c = rand(1,1,20,10) + assert_array_equal(squeeze(a),reshape(a,(20,10,10))) + assert_array_equal(squeeze(b),reshape(b,(20,10,20))) + assert_array_equal(squeeze(c),reshape(c,(20,10))) + +# Utility + +def compare_results(res,desired): + for i in range(len(desired)): + assert_array_equal(res[i],desired[i]) + + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_twodim_base.py b/numpy/core/tests/test_twodim_base.py new file mode 100644 index 000000000..b061d4a5d --- /dev/null +++ b/numpy/core/tests/test_twodim_base.py @@ -0,0 +1,134 @@ +""" Test functions for matrix module + +""" + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base) +from scipy.base import * +restore_path() + +################################################## + + +def get_mat(n): + data = arange(n) + data = add.outer(data,data) + return data + +class test_eye(ScipyTestCase): + def check_basic(self): + assert_equal(eye(4),array([[1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1]])) + assert_equal(eye(4,dtype='f'),array([[1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1]],'f')) + def check_diag(self): + assert_equal(eye(4,k=1),array([[0,1,0,0], + [0,0,1,0], + [0,0,0,1], + [0,0,0,0]])) + assert_equal(eye(4,k=-1),array([[0,0,0,0], + [1,0,0,0], + [0,1,0,0], + [0,0,1,0]])) + def check_2d(self): + assert_equal(eye(4,3),array([[1,0,0], + [0,1,0], + [0,0,1], + [0,0,0]])) + assert_equal(eye(3,4),array([[1,0,0,0], + [0,1,0,0], + [0,0,1,0]])) + def check_diag2d(self): + assert_equal(eye(3,4,k=2),array([[0,0,1,0], + [0,0,0,1], + [0,0,0,0]])) + assert_equal(eye(4,3,k=-2),array([[0,0,0], + [0,0,0], + [1,0,0], + [0,1,0]])) + +class test_diag(ScipyTestCase): + def check_vector(self): + vals = (100*arange(5)).astype('l') + b = zeros((5,5)) + for k in range(5): + b[k,k] = vals[k] + assert_equal(diag(vals),b) + b = zeros((7,7)) + c = b.copy() + for k in range(5): + b[k,k+2] = vals[k] + c[k+2,k] = vals[k] + assert_equal(diag(vals,k=2), b) + assert_equal(diag(vals,k=-2), c) + + def check_matrix(self): + vals = (100*get_mat(5)+1).astype('l') + b = zeros((5,)) + for k in range(5): + b[k] = vals[k,k] + assert_equal(diag(vals),b) + b = b*0 + for k in range(3): + b[k] = vals[k,k+2] + assert_equal(diag(vals,2),b[:3]) + for k in range(3): + b[k] = vals[k+2,k] + assert_equal(diag(vals,-2),b[:3]) + +class test_fliplr(ScipyTestCase): + def check_basic(self): + self.failUnlessRaises(ValueError, fliplr, ones(4)) + a = get_mat(4) + b = a[:,::-1] + assert_equal(fliplr(a),b) + a = [[0,1,2], + [3,4,5]] + b = [[2,1,0], + [5,4,3]] + assert_equal(fliplr(a),b) + +class test_flipud(ScipyTestCase): + def check_basic(self): + a = get_mat(4) + b = a[::-1,:] + assert_equal(flipud(a),b) + a = [[0,1,2], + [3,4,5]] + b = [[3,4,5], + [0,1,2]] + assert_equal(flipud(a),b) + +class test_rot90(ScipyTestCase): + def check_basic(self): + self.failUnlessRaises(ValueError, rot90, ones(4)) + + a = [[0,1,2], + [3,4,5]] + b1 = [[2,5], + [1,4], + [0,3]] + b2 = [[5,4,3], + [2,1,0]] + b3 = [[3,0], + [4,1], + [5,2]] + b4 = [[0,1,2], + [3,4,5]] + + for k in range(-3,13,4): + assert_equal(rot90(a,k=k),b1) + for k in range(-2,13,4): + assert_equal(rot90(a,k=k),b2) + for k in range(-1,13,4): + assert_equal(rot90(a,k=k),b3) + for k in range(0,13,4): + assert_equal(rot90(a,k=k),b4) + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_type_check.py b/numpy/core/tests/test_type_check.py new file mode 100644 index 000000000..aac24bd6e --- /dev/null +++ b/numpy/core/tests/test_type_check.py @@ -0,0 +1,238 @@ + +import sys + +from scipy.testing import * +set_package_path() +import scipy.base;reload(scipy.base);reload(scipy.base.type_check) +from scipy.base import * +restore_path() + +def assert_all(x): + assert(all(x)), x + +class test_mintypecode(ScipyTestCase): + + def check_default_1(self): + for itype in '1bcsuwil': + assert_equal(mintypecode(itype),'d') + assert_equal(mintypecode('f'),'f') + assert_equal(mintypecode('d'),'d') + assert_equal(mintypecode('F'),'F') + assert_equal(mintypecode('D'),'D') + + def check_default_2(self): + for itype in '1bcsuwil': + assert_equal(mintypecode(itype+'f'),'f') + assert_equal(mintypecode(itype+'d'),'d') + assert_equal(mintypecode(itype+'F'),'F') + assert_equal(mintypecode(itype+'D'),'D') + assert_equal(mintypecode('ff'),'f') + assert_equal(mintypecode('fd'),'d') + assert_equal(mintypecode('fF'),'F') + assert_equal(mintypecode('fD'),'D') + assert_equal(mintypecode('df'),'d') + assert_equal(mintypecode('dd'),'d') + #assert_equal(mintypecode('dF',savespace=1),'F') + assert_equal(mintypecode('dF'),'D') + assert_equal(mintypecode('dD'),'D') + assert_equal(mintypecode('Ff'),'F') + #assert_equal(mintypecode('Fd',savespace=1),'F') + assert_equal(mintypecode('Fd'),'D') + assert_equal(mintypecode('FF'),'F') + assert_equal(mintypecode('FD'),'D') + assert_equal(mintypecode('Df'),'D') + assert_equal(mintypecode('Dd'),'D') + assert_equal(mintypecode('DF'),'D') + assert_equal(mintypecode('DD'),'D') + + def check_default_3(self): + assert_equal(mintypecode('fdF'),'D') + #assert_equal(mintypecode('fdF',savespace=1),'F') + assert_equal(mintypecode('fdD'),'D') + assert_equal(mintypecode('fFD'),'D') + assert_equal(mintypecode('dFD'),'D') + + assert_equal(mintypecode('ifd'),'d') + assert_equal(mintypecode('ifF'),'F') + assert_equal(mintypecode('ifD'),'D') + assert_equal(mintypecode('idF'),'D') + #assert_equal(mintypecode('idF',savespace=1),'F') + assert_equal(mintypecode('idD'),'D') + +class test_isscalar(ScipyTestCase): + def check_basic(self): + assert(isscalar(3)) + assert(not isscalar([3])) + assert(not isscalar((3,))) + assert(isscalar(3j)) + assert(isscalar(10L)) + assert(isscalar(4.0)) + +class test_real(ScipyTestCase): + def check_real(self): + y = rand(10,) + assert_array_equal(y,real(y)) + + def check_cmplx(self): + y = rand(10,)+1j*rand(10,) + assert_array_equal(y.real,real(y)) + +class test_imag(ScipyTestCase): + def check_real(self): + y = rand(10,) + assert_array_equal(0,imag(y)) + + def check_cmplx(self): + y = rand(10,)+1j*rand(10,) + assert_array_equal(y.imag,imag(y)) + +class test_iscomplex(ScipyTestCase): + def check_fail(self): + z = array([-1,0,1]) + res = iscomplex(z) + assert(not sometrue(res)) + def check_pass(self): + z = array([-1j,1,0]) + res = iscomplex(z) + assert_array_equal(res,[1,0,0]) + +class test_isreal(ScipyTestCase): + def check_pass(self): + z = array([-1,0,1j]) + res = isreal(z) + assert_array_equal(res,[1,1,0]) + def check_fail(self): + z = array([-1j,1,0]) + res = isreal(z) + assert_array_equal(res,[0,1,1]) + +class test_iscomplexobj(ScipyTestCase): + def check_basic(self): + z = array([-1,0,1]) + assert(not iscomplexobj(z)) + z = array([-1j,0,-1]) + assert(iscomplexobj(z)) + +class test_isrealobj(ScipyTestCase): + def check_basic(self): + z = array([-1,0,1]) + assert(isrealobj(z)) + z = array([-1j,0,-1]) + assert(not isrealobj(z)) + +class test_isnan(ScipyTestCase): + def check_goodvalues(self): + z = array((-1.,0.,1.)) + res = isnan(z) == 0 + assert_all(alltrue(res)) + def check_posinf(self): + assert_all(isnan(array((1.,))/0.) == 0) + def check_neginf(self): + assert_all(isnan(array((-1.,))/0.) == 0) + def check_ind(self): + assert_all(isnan(array((0.,))/0.) == 1) + #def check_qnan(self): log(-1) return pi*j now + # assert_all(isnan(log(-1.)) == 1) + def check_integer(self): + assert_all(isnan(1) == 0) + def check_complex(self): + assert_all(isnan(1+1j) == 0) + def check_complex1(self): + assert_all(isnan(array(0+0j)/0.) == 1) + +class test_isfinite(ScipyTestCase): + def check_goodvalues(self): + z = array((-1.,0.,1.)) + res = isfinite(z) == 1 + assert_all(alltrue(res)) + def check_posinf(self): + assert_all(isfinite(array((1.,))/0.) == 0) + def check_neginf(self): + assert_all(isfinite(array((-1.,))/0.) == 0) + def check_ind(self): + assert_all(isfinite(array((0.,))/0.) == 0) + #def check_qnan(self): + # assert_all(isfinite(log(-1.)) == 0) + def check_integer(self): + assert_all(isfinite(1) == 1) + def check_complex(self): + assert_all(isfinite(1+1j) == 1) + def check_complex1(self): + assert_all(isfinite(array(1+1j)/0.) == 0) + +class test_isinf(ScipyTestCase): + def check_goodvalues(self): + z = array((-1.,0.,1.)) + res = isinf(z) == 0 + assert_all(alltrue(res)) + def check_posinf(self): + assert_all(isinf(array((1.,))/0.) == 1) + def check_posinf_scalar(self): + assert_all(isinf(array(1.,)/0.) == 1) + def check_neginf(self): + assert_all(isinf(array((-1.,))/0.) == 1) + def check_neginf_scalar(self): + assert_all(isinf(array(-1.)/0.) == 1) + def check_ind(self): + assert_all(isinf(array((0.,))/0.) == 0) + #def check_qnan(self): + # assert_all(isinf(log(-1.)) == 0) + # assert_all(isnan(log(-1.)) == 1) + +class test_isposinf(ScipyTestCase): + def check_generic(self): + vals = isposinf(array((-1.,0,1))/0.) + assert(vals[0] == 0) + assert(vals[1] == 0) + assert(vals[2] == 1) + +class test_isneginf(ScipyTestCase): + def check_generic(self): + vals = isneginf(array((-1.,0,1))/0.) + assert(vals[0] == 1) + assert(vals[1] == 0) + assert(vals[2] == 0) + +class test_nan_to_num(ScipyTestCase): + def check_generic(self): + vals = nan_to_num(array((-1.,0,1))/0.) + assert_all(vals[0] < -1e10) and assert_all(isfinite(vals[0])) + assert(vals[1] == 0) + assert_all(vals[2] > 1e10) and assert_all(isfinite(vals[2])) + def check_integer(self): + vals = nan_to_num(1) + assert_all(vals == 1) + def check_complex_good(self): + vals = nan_to_num(1+1j) + assert_all(vals == 1+1j) + def check_complex_bad(self): + v = 1+1j + v += array(0+1.j)/0. + vals = nan_to_num(v) + # !! This is actually (unexpectedly) zero + assert_all(isfinite(vals)) + def check_complex_bad2(self): + v = 1+1j + v += array(-1+1.j)/0. + vals = nan_to_num(v) + assert_all(isfinite(vals)) + #assert_all(vals.imag > 1e10) and assert_all(isfinite(vals)) + # !! This is actually (unexpectedly) positive + # !! inf. Comment out for now, and see if it + # !! changes + #assert_all(vals.real < -1e10) and assert_all(isfinite(vals)) + + +class test_real_if_close(ScipyTestCase): + def check_basic(self): + a = rand(10) + b = real_if_close(a+1e-15j) + assert_all(isrealobj(b)) + assert_array_equal(a,b) + b = real_if_close(a+1e-7j) + assert_all(iscomplexobj(b)) + b = real_if_close(a+1e-7j,tol=1e-6) + assert_all(isrealobj(b)) + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_ufunclike.py b/numpy/core/tests/test_ufunclike.py new file mode 100644 index 000000000..ca06140c7 --- /dev/null +++ b/numpy/core/tests/test_ufunclike.py @@ -0,0 +1,63 @@ +""" +>>> import scipy.base as nx +>>> import scipy.base.ufunclike as U + +Test fix: +>>> a = nx.array([[1.0, 1.1, 1.5, 1.8], [-1.0, -1.1, -1.5, -1.8]]) +>>> U.fix(a) +array([[ 1., 1., 1., 1.], + [ 0., -1., -1., -1.]]) +>>> y = nx.zeros(a.shape, float) +>>> U.fix(a, y) +array([[ 1., 1., 1., 1.], + [ 0., -1., -1., -1.]]) +>>> y +array([[ 1., 1., 1., 1.], + [ 0., -1., -1., -1.]]) + +Test isposinf, isneginf, sign +>>> a = nx.array([nx.Inf, -nx.Inf, nx.NaN, 0.0, 3.0, -3.0]) +>>> U.isposinf(a) +array([True, False, False, False, False, False], dtype=bool) +>>> U.isneginf(a) +array([False, True, False, False, False, False], dtype=bool) +>>> U.sign(a) +array([ 1, -1, 0, 0, 1, -1]) + +Same thing with an output array: +>>> y = nx.zeros(a.shape, bool) +>>> U.isposinf(a, y) +array([True, False, False, False, False, False], dtype=bool) +>>> y +array([True, False, False, False, False, False], dtype=bool) +>>> U.isneginf(a, y) +array([False, True, False, False, False, False], dtype=bool) +>>> y +array([False, True, False, False, False, False], dtype=bool) +>>> U.sign(a, y) +array([True, True, False, False, True, True], dtype=bool) +>>> y +array([True, True, False, False, True, True], dtype=bool) + +Now log2: +>>> a = nx.array([4.5, 2.3, 6.5]) +>>> U.log2(a) +array([ 2.169925 , 1.20163386, 2.70043972]) +>>> 2**_ +array([ 4.5, 2.3, 6.5]) +>>> y = nx.zeros(a.shape, float) +>>> U.log2(a, y) +array([ 2.169925 , 1.20163386, 2.70043972]) +>>> y +array([ 2.169925 , 1.20163386, 2.70043972]) + +""" + +from scipy.testing import * + +import doctest +def test_suite(level=1): + return doctest.DocTestSuite() + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py new file mode 100644 index 000000000..9cd99f7e1 --- /dev/null +++ b/numpy/core/tests/test_umath.py @@ -0,0 +1,18 @@ + +from scipy.testing import * +set_package_path() +from scipy.base.umath import minimum, maximum +restore_path() + + +class test_maximum(ScipyTestCase): + def check_reduce_complex(self): + assert_equal(maximum.reduce([1,2j]),1) + assert_equal(maximum.reduce([1+3j,2j]),1+3j) + +class test_minimum(ScipyTestCase): + def check_reduce_complex(self): + assert_equal(minimum.reduce([1,2j]),2j) + +if __name__ == "__main__": + ScipyTest().run() diff --git a/numpy/core/tests/testdata.fits b/numpy/core/tests/testdata.fits Binary files differnew file mode 100644 index 000000000..ca48ee851 --- /dev/null +++ b/numpy/core/tests/testdata.fits diff --git a/numpy/core/twodim_base.py b/numpy/core/twodim_base.py new file mode 100644 index 000000000..b21532ea6 --- /dev/null +++ b/numpy/core/twodim_base.py @@ -0,0 +1,123 @@ +""" Basic functions for manipulating 2d arrays + +""" + +__all__ = ['diag','eye','fliplr','flipud','rot90','tri','triu','tril', + 'vander'] + +from numeric import * +import sys + +def fliplr(m): + """ returns an array m with the rows preserved and columns flipped + in the left/right direction. Works on the first two dimensions of m. + """ + m = asarray(m) + if m.ndim < 2: + raise ValueError, "Input must be >= 2-d." + return m[:, ::-1] + +def flipud(m): + """ returns an array with the columns preserved and rows flipped in + the up/down direction. Works on the first dimension of m. + """ + m = asarray(m) + if m.ndim < 1: + raise ValueError, "Input must be >= 1-d." + return m[::-1] + +def rot90(m, k=1): + """ returns the array found by rotating m by k*90 + degrees in the counterclockwise direction. Works on the first two + dimensions of m. + """ + m = asarray(m) + if m.ndim < 2: + raise ValueError, "Input must >= 2-d." + k = k % 4 + if k == 0: return m + elif k == 1: return fliplr(m).transpose() + elif k == 2: return fliplr(flipud(m)) + else: return fliplr(m.transpose()) # k==3 + +def eye(N, M=None, k=0, dtype=int_): + """ eye returns a N-by-M 2-d array where the k-th diagonal is all ones, + and everything else is zeros. + """ + if M is None: M = N + m = equal(subtract.outer(arange(N), arange(M)),-k) + return m.astype(dtype) + +def diag(v, k=0): + """ returns the k-th diagonal if v is a array or returns a array + with v as the k-th diagonal if v is a vector. + """ + v = asarray(v) + s = v.shape + if len(s)==1: + n = s[0]+abs(k) + res = zeros((n,n), v.dtype) + if (k>=0): + i = arange(0,n-k) + fi = i+k+i*n + else: + i = arange(0,n+k) + fi = i+(i-k)*n + res.flat[fi] = v + return res + elif len(s)==2: + N1,N2 = s + if k >= 0: + M = min(N1,N2-k) + i = arange(0,M) + fi = i+k+i*N2 + else: + M = min(N1+k,N2) + i = arange(0,M) + fi = i + (i-k)*N2 + return v.flat[fi] + else: + raise ValueError, "Input must be 1- or 2-d." + + +def tri(N, M=None, k=0, dtype=int_): + """ returns a N-by-M array where all the diagonals starting from + lower left corner up to the k-th are all ones. + """ + if M is None: M = N + m = greater_equal(subtract.outer(arange(N), arange(M)),-k) + return m.astype(dtype) + +def tril(m, k=0): + """ returns the elements on and below the k-th diagonal of m. k=0 is the + main diagonal, k > 0 is above and k < 0 is below the main diagonal. + """ + m = asarray(m) + out = multiply(tri(m.shape[0], m.shape[1], k=k, dtype=m.dtype),m) + return out + +def triu(m, k=0): + """ returns the elements on and above the k-th diagonal of m. k=0 is the + main diagonal, k > 0 is above and k < 0 is below the main diagonal. + """ + m = asarray(m) + out = multiply((1-tri(m.shape[0], m.shape[1], k-1, m.dtype)),m) + return out + + +# borrowed from John Hunter and matplotlib +def vander(x, N=None): + """ + X = vander(x,N=None) + + The Vandermonde matrix of vector x. The i-th column of X is the + the i-th power of x. N is the maximum power to compute; if N is + None it defaults to len(x). + + """ + x = asarray(x) + if N is None: N=len(x) + X = ones( (len(x),N), x.dtypechar) + for i in range(N-1): + X[:,i] = x**(N-i-1) + return X diff --git a/numpy/core/type_check.py b/numpy/core/type_check.py new file mode 100644 index 000000000..4c802ca86 --- /dev/null +++ b/numpy/core/type_check.py @@ -0,0 +1,180 @@ +## Automatically adapted for scipy Sep 19, 2005 by convertcode.py + +__all__ = ['iscomplexobj','isrealobj','imag','iscomplex', + 'isscalar', + 'isreal','nan_to_num','real','real_if_close', + 'typename','asfarray','mintypecode','asscalar', + 'common_type'] + +import numeric as _nx +from numeric import ndarray, asarray, array, isinf, isnan, isfinite, signbit, \ + ufunc, ScalarType, obj2dtype +from ufunclike import isneginf, isposinf +import umath + +_typecodes_by_elsize = 'GDFgdfQqLlIiHhBb?' + +def mintypecode(typechars,typeset='GDFgdf',default='d'): + """ Return a minimum data type character from typeset that + handles all typechars given + + The returned type character must be the smallest size such that + an array of the returned type can handle the data from an array of + type t for each t in typechars (or if typechars is an array, + then its dtypechar). + + If the typechars does not intersect with the typeset, then default + is returned. + + If t in typechars is not a string then t=asarray(t).dtypechar is + applied. + """ + typecodes = [(type(t) is type('') and t) or asarray(t).dtypechar\ + for t in typechars] + intersection = [t for t in typecodes if t in typeset] + if not intersection: + return default + if 'F' in intersection and 'd' in intersection: + return 'D' + l = [] + for t in intersection: + i = _typecodes_by_elsize.index(t) + l.append((i,t)) + l.sort() + return l[0][1] + +def asfarray(a, dtype=_nx.float_): + """asfarray(a,dtype=None) returns a as a float array.""" + dtype = _nx.obj2dtype(dtype) + if not issubclass(dtype, _nx.inexact): + dtype = _nx.float_ + a = asarray(a,dtype=dtype) + return a + +def isscalar(num): + if isinstance(num, _nx.generic): + return True + else: + return type(num) in ScalarType + +def real(val): + return asarray(val).real + +def imag(val): + return asarray(val).imag + +def iscomplex(x): + return imag(x) != _nx.zeros_like(x) + +def isreal(x): + return imag(x) == _nx.zeros_like(x) + +def iscomplexobj(x): + return issubclass( asarray(x).dtype, _nx.complexfloating) + +def isrealobj(x): + return not issubclass( asarray(x).dtype, _nx.complexfloating) + +#----------------------------------------------------------------------------- + +def _getmaxmin(t): + import getlimits + f = getlimits.finfo(t) + return f.max, f.min + +def nan_to_num(x): + # mapping: + # NaN -> 0 + # Inf -> limits.double_max + # -Inf -> limits.double_min + try: + t = x.dtype + except AttributeError: + t = obj2dtype(type(x)) + if issubclass(t, _nx.complexfloating): + y = nan_to_num(x.real) + 1j * nan_to_num(x.imag) + elif issubclass(t, _nx.integer): + y = array(x) + else: + y = array(x) + if not y.shape: + y = array([x]) + scalar = True + else: + scalar = False + are_inf = isposinf(y) + are_neg_inf = isneginf(y) + are_nan = isnan(y) + maxf, minf = _getmaxmin(y.dtype) + y[are_nan] = 0 + y[are_inf] = maxf + y[are_neg_inf] = minf + if scalar: + y = y[0] + return y + +#----------------------------------------------------------------------------- + +def real_if_close(a,tol=100): + a = asarray(a) + if a.dtypechar not in 'FDG': + return a + if tol > 1: + import getlimits + f = getlimits.finfo(a.dtype) + tol = f.eps * tol + if _nx.allclose(a.imag, 0, atol=tol): + a = a.real + return a + + +def asscalar(a): + return a.item() + +#----------------------------------------------------------------------------- + +_namefromtype = {'S1' : 'character', + '?' : 'bool', + 'b' : 'signed char', + 'B' : 'unsigned char', + 'h' : 'short', + 'H' : 'unsigned short', + 'i' : 'integer', + 'I' : 'unsigned integer', + 'l' : 'long integer', + 'L' : 'unsigned long integer', + 'q' : 'long long integer', + 'Q' : 'unsigned long long integer', + 'f' : 'single precision', + 'd' : 'double precision', + 'g' : 'long precision', + 'F' : 'complex single precision', + 'D' : 'complex double precision', + 'G' : 'complex long double precision', + 'S' : 'string', + 'U' : 'unicode', + 'V' : 'void', + 'O' : 'object' + } + +def typename(char): + """Return an english description for the given data type character. + """ + return _namefromtype[char] + +#----------------------------------------------------------------------------- + +#determine the "minimum common type code" for a group of arrays. +array_kind = {'i':0, 'l': 0, 'f': 0, 'd': 0, 'g':0, 'F': 1, 'D': 1, 'G':1} +array_precision = {'i': 1, 'l': 1, + 'f': 0, 'd': 1, 'g':2, + 'F': 0, 'D': 1, 'G':2} +array_type = [['f', 'd', 'g'], ['F', 'D', 'G']] +def common_type(*arrays): + kind = 0 + precision = 0 + for a in arrays: + t = a.dtypechar + kind = max(kind, array_kind[t]) + precision = max(precision, array_precision[t]) + return array_type[kind][precision] diff --git a/numpy/core/ufunclike.py b/numpy/core/ufunclike.py new file mode 100644 index 000000000..7e8d44c7d --- /dev/null +++ b/numpy/core/ufunclike.py @@ -0,0 +1,77 @@ +""" +Module of functions that are like ufuncs in acting on arrays and optionally +storing results in an output array. +""" +__all__ = ['fix', 'isneginf', 'isposinf', 'sign', 'log2'] + +import numeric as nx +from numeric import asarray, empty, empty_like, isinf, signbit, zeros +import umath + +def fix(x, y=None): + """ Round x to nearest integer towards zero. + """ + x = asarray(x) + if y is None: + y = nx.floor(x) + else: + nx.floor(x, y) + if x.ndim == 0: + if (x<0): + y += 1 + else: + y[x<0] = y[x<0]+1 + return y + +def isposinf(x, y=None): + """Return a boolean array y with y[i] True for x[i] = +Inf. + + If y is an array, the result replaces the contents of y. + """ + if y is None: + y = empty(x.shape, dtype=nx.bool_) + umath.logical_and(isinf(x), ~signbit(x), y) + return y + +def isneginf(x, y=None): + """Return a boolean array y with y[i] True for x[i] = -Inf. + + If y is an array, the result replaces the contents of y. + """ + if y is None: + y = empty(x.shape, dtype=nx.bool_) + umath.logical_and(isinf(x), signbit(x), y) + return y + +def sign(x, y=None): + """sign(x) gives an array with shape of x with elexents defined by sign + function: where x is less than 0 return -1, where x greater than 0, a=1, + elsewhere a=0. + """ + x = asarray(x) + if y is None: + y = zeros(x.shape, dtype=nx.int_) + if x.ndim == 0: + if x < 0: + y -= 1 + elif x > 0: + y += 1 + else: + y[x<0] = -1 + y[x>0] = 1 + return y + +_log2 = umath.log(2) +def log2(x, y=None): + """Returns the base 2 logarithm of x + + If y is an array, the result replaces the contents of y. + """ + x = asarray(x) + if y is None: + y = umath.log(x) + else: + umath.log(x, y) + y /= _log2 + return y + diff --git a/numpy/core/utils.py b/numpy/core/utils.py new file mode 100644 index 000000000..19fb18d4d --- /dev/null +++ b/numpy/core/utils.py @@ -0,0 +1,28 @@ +from numerictypes import obj2dtype + +__all__ = ['issubclass_', 'get_scipy_include', 'issubdtype'] + +def issubclass_(arg1, arg2): + try: + return issubclass(arg1, arg2) + except TypeError: + return False + +def issubdtype(arg1, arg2): + return issubclass(obj2dtype(arg1), obj2dtype(arg2)) + +def get_scipy_include(): + """Return the directory in the package that contains the scipy/*.h header + files. + + Extension modules that need to compile against scipy.base should use this + function to locate the appropriate include directory. Using distutils: + + import scipy + Extension('extension_name', ... + include_dirs=[scipy.get_scipy_include()]) + """ + from scipy.distutils.misc_util import get_scipy_include_dirs + include_dirs = get_scipy_include_dirs() + assert len(include_dirs)==1,`include_dirs` + return include_dirs[0] |
