/* Copyright (c) 1996, 1997, The Regents of the University of California. * All rights reserved. See Legal.htm for full text and disclaimer. */ #include "Python.h" #include "arrayobject.h" /*#include "hlevel.h"*/ #include #include static PyObject *ErrorObject; /* Define 2 macros for error handling: Py_Try(BOOLEAN) If BOOLEAN is FALSE, assume the error object has been set and return NULL Py_Assert(BOOLEAN,ERROBJ,MESS) If BOOLEAN is FALSE set the error object to ERROBJ, and the message to MESS */ static char * errstr = NULL ; #define Py_Try(BOOLEAN) {if (!(BOOLEAN)) return NULL;} #define Py_Assert(BOOLEAN,ERROBJ,MESS) {if (!(BOOLEAN)) { \ PyErr_SetString((ERROBJ), (MESS)); \ return NULL;} \ } #define A_DATA(a) (((PyArrayObject *)a)->data) #define A_SIZE(a) PyArray_Size((PyObject *) a) #define A_TYPE(a) (int)(((PyArrayObject *)a)->descr->type_num) #define isARRAY(a) ((a) && PyArray_Check((PyArrayObject *)a)) #define A_NDIM(a) (((PyArrayObject *)a)->nd) #define A_DIM(a,i) (((PyArrayObject *)a)->dimensions[i]) #define GET_ARR(ap,op,type,dim) \ Py_Try(ap=(PyArrayObject *)PyArray_ContiguousFromObject(op,type,dim,dim)) #define ERRSS(s) ((PyObject *)(PyErr_SetString(ErrorObject,s),0)) #define SETERR(s) if(!PyErr_Occurred()) ERRSS(errstr ? errstr : s) #define DECREF_AND_ZERO(p) do{Py_XDECREF(p);p=0;}while(0) /* ----------------------------------------------------- */ static char arr_histogram__doc__[] = "" ; static int mxx ( int * i , int len) { /* find the index of the maximum element of an integer array */ int mx = 0, max = i [0] ; int j ; for ( j = 1 ; j < len; j ++ ) if ( i [j] > max ) {max = i [j] ; mx = j ;} return mx; } static int mnx ( int * i , int len) { /* find the index of the minimum element of an integer array */ int mn = 0, min = i [0] ; int j ; for ( j = 1 ; j < len; j ++ ) if ( i [j] < min ) {min = i [j] ; mn = j ;} return mn; } static PyObject * arr_histogram(PyObject *self, PyObject *args) { /* 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) >= max (list) + 1. * 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 */ PyObject * list = NULL, * weight = NULL ; PyArrayObject *lst, *wts , *ans; int * numbers, *ians, len , mxi, mni, i, ans_size; double * weights , * dans ; Py_Try(PyArg_ParseTuple(args, "O|O", &list, &weight)); GET_ARR(lst,list,PyArray_INT,1); len = A_SIZE (lst) ; numbers = (int *) A_DATA (lst) ; mxi = mxx (numbers, len) ; mni = mnx (numbers, len) ; if (numbers [mni] < 0) {SETERR ("First argument of histogram must be nonnegative."); Py_DECREF(lst); return NULL;} ans_size = numbers [mxi] + 1 ; if (weight == NULL) { Py_Try(ans = (PyArrayObject *) PyArray_FromDims (1, &ans_size, PyArray_INT)); ians = (int *) A_DATA (ans) ; for (i = 0 ; i < len ; i++) ians [numbers [i]] += 1 ; Py_DECREF(lst); } else { GET_ARR(wts,weight,PyArray_DOUBLE, 1); weights = (double *) A_DATA (wts) ; if (A_SIZE (wts) <= numbers [mxi]) {SETERR ("histogram: length of weights is <= max (list)."); Py_DECREF(lst); Py_DECREF(wts); return NULL;} Py_Try(ans = (PyArrayObject *) PyArray_FromDims (1, &ans_size, PyArray_DOUBLE)); dans = (double *) A_DATA (ans); for (i = 0 ; i < len ; i++) { dans [numbers [i]] += weights [i]; } Py_DECREF(lst); Py_DECREF(wts); } return PyArray_Return (ans); } static char arr_array_set__doc__[] = "" ; static PyObject * arr_array_set(PyObject *self, PyObject *args) { /* array_set accepts three arguments. The first is an array of * numerics (Python characters, integers, or floats), and the * third is of the same type. The second is an array of integers * which are valid subscripts into the first. The third array * must be at least long enough to supply all the elements * called for by the subscript array. (It can also be a scalar, * in which case its value will be broadcast.) The result is that * elements of the third array are assigned in order to elements * of the first whose subscripts are elements of the second. * arr_array_set (vals1, indices, vals2) * is equivalent to the Yorick assignment vals1 (indices) = vals2. * I have generalized this so that the source and target arrays * may be two dimensional; the second dimensions must match. * Then the array of subscripts is assumed to apply to the first * subscript only of the target. The target had better be contiguous. */ /* self is not used */ PyObject * tararg, * subsarg, *srcarg; PyArrayObject * tararr, * subsarr, * srcarr; double * dtar, * dsrc, ds; float * ftar, * fsrc, fs; char * ctar, * csrc, cs; unsigned char * utar, * usrc, us; int * itar, * isrc, * isubs; long * ltar, * lsrc; long is; int i, j, len, mn, mx; int scalar_source = 0; char scalar_type; int nd, d1; /* number of dimensions and value of second dim. */ Py_Try(PyArg_ParseTuple(args, "OOO", &tararg, &subsarg, &srcarg)); d1 = 1; nd = A_NDIM (tararg) ; if (PyFloat_Check (srcarg)) { scalar_source = 1 ; scalar_type = 'f' ; ds = PyFloat_AS_DOUBLE ( (PyFloatObject *) srcarg) ; } else if (PyInt_Check (srcarg)) { scalar_source = 1 ; scalar_type = 'i' ; is = PyInt_AS_LONG ( (PyIntObject *) srcarg) ; } else if (PyString_Check (srcarg)) { scalar_source = 1 ; scalar_type = 'c' ; cs = PyString_AS_STRING ( (PyStringObject *) srcarg) [0] ; } else if (nd == 2) { d1 = A_DIM (tararg, 1) ; if (A_NDIM (srcarg) != 2 || A_DIM (srcarg,1) != d1) { SETERR ("array_set: dimension mismatch between source and target."); return NULL ; } } else if (nd != 1) { SETERR ("array_set: target must have one or two dimensions."); return NULL ; } GET_ARR(subsarr,subsarg,PyArray_INT,1); isubs = (int *)A_DATA(subsarr); len = A_SIZE(subsarr); mn = mnx (isubs, len); if (isubs [mn] < 0) {SETERR ("array_set: negative subscript specified."); Py_DECREF (subsarr); return NULL;} mx = mxx (isubs, len); switch (A_TYPE(tararg)) { case PyArray_UBYTE: GET_ARR(tararr,tararg,PyArray_UBYTE,nd); if (d1 * isubs [mx] > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_UBYTE,1); if (A_SIZE(srcarr) < d1 * len) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} utar = (unsigned char *)A_DATA(tararr); usrc = (unsigned char *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) utar [d1 * isubs [i] + j] = usrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : us = (unsigned char) cs ; break ; case 'i' : us = (unsigned char) is ; break ; case 'f' : us = (unsigned char) ds ; break ; } utar = (unsigned char *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) utar [d1 * isubs [i] + j] = us; } break; case PyArray_CHAR: GET_ARR(tararr,tararg,PyArray_CHAR,nd); if (d1 * isubs [mx] > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_CHAR,nd); if (A_SIZE(srcarr) < d1 * len) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} ctar = (char *)A_DATA(tararr); csrc = (char *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ctar [isubs [i] * d1 + j] = csrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : break ; case 'i' : cs = (unsigned char) is ; break ; case 'f' : cs = (unsigned char) ds ; break ; } ctar = (char *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ctar [d1 * isubs [i] + j] = cs; } break; case PyArray_INT: GET_ARR(tararr,tararg,PyArray_INT,nd); if (isubs [mx] * d1 > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_INT,nd); if (A_SIZE(srcarr) < len * d1) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} itar = (int *)A_DATA(tararr); isrc = (int *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) itar [isubs [i] * d1 + j] = isrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : is = (long) cs ; break ; case 'i' : break ; case 'f' : is = (long) ds ; break ; } itar = (int *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) itar [d1 * isubs [i] + j] = is; } break; case PyArray_LONG: GET_ARR(tararr,tararg,PyArray_LONG,nd); if (isubs [mx] * d1 > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_LONG,nd); if (A_SIZE(srcarr) < len * d1) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} ltar = (long *)A_DATA(tararr); lsrc = (long *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ltar [isubs [i] * d1 + j] = lsrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : is = (long) cs ; break ; case 'i' : break ; case 'f' : is = (long) ds ; break ; } ltar = (long *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ltar [d1 * isubs [i] + j] = is; } break; case PyArray_FLOAT: GET_ARR(tararr,tararg,PyArray_FLOAT,nd); if (isubs [mx] * d1 > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_FLOAT,nd); if (A_SIZE(srcarr) < len * d1) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} ftar = (float *)A_DATA(tararr); fsrc = (float *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ftar [isubs [i] * d1 + j] = fsrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : fs = (float) cs ; break ; case 'i' : fs = (float) is ; break ; case 'f' : fs = (float) ds ; break ; } ftar = (float *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) ftar [d1 * isubs [i] + j] = fs; } break; case PyArray_DOUBLE: GET_ARR(tararr,tararg,PyArray_DOUBLE,nd); if (isubs [mx] * d1 > A_SIZE(tararr)) {SETERR ("array_set: a subscript is out of range."); Py_DECREF (subsarr); Py_DECREF (tararr); return NULL;} if (! scalar_source) { GET_ARR(srcarr,srcarg,PyArray_DOUBLE,nd); if (A_SIZE(srcarr) < len * d1) {SETERR ("array_set: source is too short for number of subscripts."); Py_DECREF (subsarr); Py_DECREF (tararr); Py_DECREF (srcarr); return NULL;} dtar = (double *)A_DATA(tararr); dsrc = (double *)A_DATA(srcarr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) dtar [isubs [i] * d1 + j] = dsrc [i * d1 + j]; } else { switch (scalar_type) { case 'c' : ds = (double) cs ; break ; case 'i' : ds = (double) is ; break ; case 'f' : break ; } dtar = (double *)A_DATA(tararr); for (i = 0; i < len; i++ ) for (j = 0; j < d1; j++) dtar [d1 * isubs [i] + j] = ds; } break; default: SETERR("array_set: Not implemented for this type."); Py_DECREF(subsarr); return NULL; } Py_DECREF (subsarr); Py_DECREF (tararr); if (! scalar_source) Py_DECREF (srcarr); Py_INCREF(Py_None); return Py_None; } static void * adjust (double * k, int * list, int i, int n) { /* adjust the binary tree k with root list [i] to satisfy the heap * property. The left and right subtrees of list [i], with roots * list [2 * i + 1] and list [2 * i + 2], already satisfy the heap * property. No node has index greater than n. * Horowitz & Sahni, p. 358. */ double kt; /* will contain root value which may need to be moved */ int kj , /* root value is at kj position in list */ j , lowj ; /* parent of current j node */ kj = list [i] ; kt = k [kj] ; j = 2 * i + 1 ; lowj = i ; while ( j < n ) { if (j < n - 1 && k [list [j]] < k [list [j + 1]]) /* make list [j] point to the larger child */ j = j + 1 ; if ( kt >= k [list [j]] ) { list [lowj] = kj ; return ; } list [lowj] = list [j] ; lowj = j ; j = 2 * j + 1 ; } list [lowj] = kj ; } static char arr_index_sort__doc__[] = "" ; static PyObject * arr_index_sort(PyObject *self, PyObject *args) { /* index_sort accepts one array of some numerical type and returns * an integer array of the same length whose entries are the * subscripts of the elements of the original array arranged * in increasing order. I chose to use heap sort because its * worst behavior is n*log(n), unlike quicksort, whose worst * behavior is n**2. */ /* self is not used */ PyObject * list; PyArrayObject * alist, * ilist; double * data; int len, i, * isubs, itmp; Py_Try(PyArg_ParseTuple(args, "O", &list)); GET_ARR(alist,list,PyArray_DOUBLE,1); len = A_SIZE(alist); Py_Try(ilist = (PyArrayObject *) PyArray_FromDims (1, &len, PyArray_INT)); isubs = (int *) A_DATA (ilist); for ( i = 0 ; i < len ; i ++ ) isubs [i] = i ; data = (double *) A_DATA(alist) ; /* now do heap sort on subscripts */ for (i = len / 2; i >= 0; i--) { adjust (data, isubs, i, len) ; } for (i = len - 1; i >= 0; i-- ) { itmp = isubs [i] ; isubs [i] = isubs [0] ; isubs [0] = itmp ; adjust (data, isubs, 0, i) ; } Py_DECREF(alist); return (PyObject *) ilist ; } static char arr_binary_search__doc__[] = "" ; static int binary_search(double dval, double dlist [], int len) { /* binary_search accepts three arguments: a numeric value and * a numeric array and its length. It assumes that the array is sorted in * increasing order. It returns the index of the array's * largest element which is <= the value. It will return -1 if * the value is less than the least element of the array. */ /* self is not used */ int bottom , top , middle, result; if (dval < dlist [0]) result = -1 ; else { bottom = 0; top = len - 1; while (bottom < top) { middle = (top + bottom) / 2 ; if (dlist [middle] < dval) bottom = middle + 1 ; else if (dlist [middle] > dval) top = middle - 1 ; else return middle ; } if (dlist [bottom] > dval) result = bottom - 1 ; else result = bottom ; } return result ; } static char arr_interp__doc__[] = "" ; static PyObject * arr_interp(PyObject *self, PyObject *args) { /* interp (y, x, z) treats (x, y) as a piecewise linear function * whose value is y [0] for x < x [0] and y [len (y) -1] for x > * x [len (y) -1]. An array of floats the same length as z is * returned, whose values are ordinates for the corresponding z * abscissae interpolated into the piecewise linear function. */ /* self is not used */ PyObject * oy, * ox, * oz ; PyArrayObject * ay, * ax, * az , * _interp; double * dy, * dx, * dz , * dres, * slopes; int leny, lenz, i, left ; Py_Try(PyArg_ParseTuple(args, "OOO", &oy, &ox, &oz)); GET_ARR(ay,oy,PyArray_DOUBLE,1); GET_ARR(ax,ox,PyArray_DOUBLE,1); if ( (leny = A_SIZE (ay)) != A_SIZE (ax)) { SETERR ("interp: x and y are not the same length."); Py_DECREF(ay); Py_DECREF(ax); return NULL ;} GET_ARR(az,oz,PyArray_DOUBLE,1); lenz = A_SIZE (az); dy = (double *) A_DATA (ay); dx = (double *) A_DATA (ax); dz = (double *) A_DATA (az); Py_Try (_interp = (PyArrayObject *) PyArray_FromDims (1, &lenz, PyArray_DOUBLE)); dres = (double *) A_DATA (_interp) ; slopes = (double *) malloc ( (leny - 1) * sizeof (double)) ; for (i = 0 ; i < leny - 1; i++) { slopes [i] = (dy [i + 1] - dy [i]) / (dx [i + 1] - dx [i]) ; } for ( i = 0 ; i < lenz ; i ++ ) { left = binary_search (dz [i], dx, leny) ; if (left < 0) dres [i] = dy [0] ; else if (left >= leny - 1) dres [i] = dy [leny - 1] ; else dres [i] = slopes [left] * (dz [i] - dx [left]) + dy [left]; } free (slopes); Py_DECREF(ay); Py_DECREF(ax); Py_DECREF(az); return PyArray_Return (_interp); } static int incr_slot_ (float x, double *bins, int lbins) { int i ; for ( i = 0 ; i < lbins ; i ++ ) if ( x < bins [i] ) return i ; return lbins ; } static int decr_slot_ (double x, double * bins, int lbins) { int 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 (lena < 2) {SETERR ("digitize: If a vector, second argument must have at least 2 elements."); return NULL ;} 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 char arr_digitize__doc__[] = "" ; static PyObject * arr_zmin_zmax(PyObject *self, PyObject *args) { /* zmin_zmax (z, ireg) returns a 2-tuple which consists of the minimum and maximum values of z on the portion of the mesh where ireg is not zero. z is a 2d array of Float and ireg is an array of the same shape of Integer. By convention the first row and column of ireg are zero, and the remaining entries are used to determine which region each cell belongs to. A zero entry says that this cell is excluded from the mesh. */ PyObject * zobj, * iregobj; PyArrayObject * zarr, * iregarr; double * z, zmin, zmax; int * ireg; int have_min_max = 0; int i, j, k, n, m; Py_Try(PyArg_ParseTuple(args, "OO", &zobj, &iregobj)); GET_ARR (zarr, zobj, PyArray_DOUBLE, 2); if (! (iregarr = (PyArrayObject *)PyArray_ContiguousFromObject(iregobj, PyArray_INT, 2, 2))) { Py_DECREF (zarr); return NULL; } n = A_DIM (iregarr, 0); m = A_DIM (iregarr, 1); if (n != A_DIM (zarr, 0) || m != A_DIM (zarr, 1)) { SETERR ("zmin_zmax: z and ireg do not have the same shape."); Py_DECREF (iregarr); Py_DECREF (zarr); return NULL; } ireg = (int *) A_DATA (iregarr); z = (double *) A_DATA (zarr); k = 0; /* k is i * m + j */ for ( i = 0; i < n; i++) { for (j = 0; j < m; j++) { if ( (ireg [k] != 0) || (i != n - 1 && j != m - 1 && (ireg [k + m] != 0 || ireg [k + 1] != 0 || ireg [k + m + 1] != 0))) { if (! have_min_max ) { have_min_max = 1; zmin = zmax = z [k]; } else { if (z [k] < zmin) zmin = z [k]; else if (z [k] > zmax) zmax = z [k]; } } k++; } } Py_DECREF (iregarr); Py_DECREF (zarr); if (!have_min_max) { SETERR ("zmin_zmax: unable to calculate zmin and zmax!"); return NULL; } return Py_BuildValue ("dd", zmin, zmax); } static char arr_zmin_zmax__doc__[] = "" ; static PyObject * arr_digitize(PyObject *self, PyObject *args) { /* digitize (x, bins) returns an array of python integers the same length of x (if x is a one-dimensional array), or just an integer (if x is a scalar). 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 ; PyArrayObject * ax, * abins , * aret ; double x, bins ; /* if either or both is a scalar */ double * dx, * dbins ; /* if either or both is a vector */ int lbins, lx ; /* lengths, if vectors */ long * iret ; int m, i ; int x_is_scalar, bins_is_scalar ; Py_Try(PyArg_ParseTuple(args, "OO", & ox, & obins)); x_is_scalar = ! isARRAY (ox) ; bins_is_scalar = ! isARRAY (obins) ; if ( !x_is_scalar ) { GET_ARR(ax,ox,PyArray_DOUBLE,1); if (A_NDIM (ax) > 1) { SETERR ("digitize: first argument has too many dimensions.") ; Py_DECREF (ax) ; return NULL ; } lx = A_SIZE (ax) ; dx = (double *) A_DATA (ax) ; } else { if (PyInt_Check (ox)) x = (double) PyInt_AsLong (ox) ; else if (PyFloat_Check (ox)) x = PyFloat_AS_DOUBLE ((PyFloatObject *)ox) ; else { SETERR ("digitize: bad type for first argument.") ; return NULL ; } } if ( !bins_is_scalar ) { GET_ARR(abins,obins,PyArray_DOUBLE,1); if (A_NDIM (abins) > 1) { SETERR ("digitize: second argument has too many dimensions.") ; Py_DECREF (abins) ; if (! x_is_scalar) Py_DECREF (ax) ; return NULL ; } lbins = A_SIZE (abins) ; dbins = (double *) A_DATA (abins) ; } else { if (PyInt_Check (obins)) bins = (double) PyInt_AsLong (obins) ; else if (PyFloat_Check (obins)) bins = PyFloat_AS_DOUBLE ((PyFloatObject *)obins) ; else { SETERR ("digitize: bad type for second argument.") ; return NULL ; } } if (bins_is_scalar) if (x_is_scalar) if (x < bins) return PyInt_FromLong (0) ; else return PyInt_FromLong (1) ; else { aret = (PyArrayObject *) PyArray_FromDims (1, &lx, PyArray_INT) ; iret = (long *) A_DATA (aret) ; for ( i = 0 ; i < lx ; i ++ ) if (dx [i] >= bins) iret [i] = (long) 1 ; } else { m = monotonic_ (dbins, lbins) ; if ( m == -1 ) { if (x_is_scalar) return PyInt_FromLong (decr_slot_ (x, dbins, lbins)) ; aret = (PyArrayObject *) PyArray_FromDims (1, &lx, PyArray_INT) ; iret = (long *) A_DATA (aret) ; for ( i = 0 ; i < lx ; i ++ ) iret [i] = (long) decr_slot_ (dx [i], dbins, lbins) ; } else if ( m == 1 ) { if (x_is_scalar) return PyInt_FromLong (incr_slot_ (x, dbins, lbins)) ; aret = (PyArrayObject *) PyArray_FromDims (1, &lx, PyArray_INT) ; iret = (long *) A_DATA (aret) ; for ( i = 0 ; i < lx ; i ++ ) iret [i] = (long) incr_slot_ (dx [i], dbins, lbins) ; } else { SETERR ("digitize: Second argument must be monotonic.") ; if (! x_is_scalar) Py_DECREF (ax) ; if (! bins_is_scalar) Py_DECREF (abins) ; return NULL ; } } if (! x_is_scalar) Py_DECREF (ax) ; if (! bins_is_scalar) Py_DECREF (abins) ; return PyArray_Return (aret) ; } static char arr_reverse__doc__[] = "" ; static PyObject * arr_reverse(PyObject *self, PyObject *args) { /* reverse (x, n) returns a PyFloat Matrix the same size and shape as x, but with the elements along the nth dimension reversed. x must be two-dimensional. */ /* self is not used */ PyObject * ox; int n; PyArrayObject * ax, * ares ; double * dx, * dres; int i0, i0p, i1, i1p, d0, d1, i0d1, i0pd1, dims [2] ; int i, jl, jh; Py_Try(PyArg_ParseTuple(args, "Oi", &ox, &n)); if (n != 0 && n != 1) { SETERR("reverse: Second argument must be 0 or 1."); return NULL; } GET_ARR(ax,ox,PyArray_DOUBLE,2); dx = (double *) A_DATA (ax); d0 = dims [0] = A_DIM (ax, 0); d1 = dims [1] = A_DIM (ax, 1); Py_Try(ares = (PyArrayObject *) PyArray_FromDims (2, dims, PyArray_DOUBLE)); dres = (double *) A_DATA (ares); if (n == 0) /* reverse the columns */ for (i = 0; i < d1 ; i ++ ) {for (jl = i, jh = (d0 - 1) * d1 + i; jl < jh; jl += d1, jh -= d1) { dres [jl] = dx [jh] ; dres [jh] = dx [jl] ; } if (jl == jh) dres [jl] = dx [jl] ; } else /* reverse the rows */ for (i = 0; i < d0 ; i ++ ) {for (jl = i * d1, jh = (i + 1) * d1 - 1; jl < jh; jl +=1, jh -= 1) { dres [jl] = dx [jh] ; dres [jh] = dx [jl] ; } if (jl == jh) dres [jl] = dx [jl] ; } Py_DECREF(ax); return PyArray_Return (ares) ; } static char arr_span__doc__[] = "" ; static PyObject * arr_span(PyObject *self, PyObject *args) { /* span (lo, hi, num, d2 = 0) returns an array of num equally spaced PyFloats starting with lo and ending with hi. if d2 is not zero, it will return a two-dimensional array, each of the d2 rows of which is the array of equally spaced numbers. */ /* self is not used */ int num, d2 = 0; int dims [2]; double lo, hi, * drow, * dres; int i, j, id2; PyArrayObject * arow, * ares ; Py_Try(PyArg_ParseTuple(args, "ddi|i", &lo, &hi, &num, &d2)); dims [1] = num; dims [0] = d2; Py_Try(arow = (PyArrayObject *) PyArray_FromDims (1, &num, PyArray_DOUBLE)); drow = (double *) A_DATA (arow) ; for (i = 0; i < num; i++) drow [i] = lo + ( (double) i) * (hi - lo) / ( (double) (num - 1)) ; if ( d2 == 0 ) return PyArray_Return (arow) ; else { Py_Try (ares = (PyArrayObject *) PyArray_FromDims (2, dims, PyArray_DOUBLE)); dres = (double *) A_DATA (ares) ; for ( id2 = 0 ; id2 < num * d2 ; id2 += num ) for (j = 0; j < num; j ++ ) dres [id2 + j] = drow [j] ; Py_DECREF (arow) ; } return PyArray_Return (ares) ; } static char arr_nz__doc__ [] = "" ; static PyObject * arr_nz (PyObject *self, PyObject *args) { /* nz_ (x): x is an array of unsigned bytes. If x ends with a bunch of zeros, this returns with the index of the first zero element after the last nonzero element. It returns the length of the array if its last element is nonzero. This is essentially the "effective length" of the array. */ /* self is not used */ int i , len ; unsigned char * cdat; PyObject * odat; PyArrayObject * adat; Py_Try(PyArg_ParseTuple(args, "O", &odat)) ; GET_ARR(adat,odat,PyArray_UBYTE,1) ; cdat = (unsigned char *) A_DATA (adat) ; len = A_SIZE (adat) ; for (i = len; i > 0; i --) if (cdat [i - 1] != (unsigned char) 0) break ; Py_DECREF (adat) ; return PyInt_FromLong ( (long) i) ; } static char arr_find_mask__doc__ [] = "" ; static PyObject * arr_find_mask (PyObject * self, PyObject * args) { /* find_mask (fs, node_edges): This function is used to calculate a mask whose corresponding entry is 1 precisely if an edge of a cell is cut by an isosurface, i. e., if the function fs is one on one of the two vertices of an edge and zero on the other (fs = 1 represents where some function on the mesh was found to be negative by the calling routine). fs is ntotal by nv, where nv is the number of vertices of a cell (4 for a tetrahedren, 5 for a pyramid, 6 for a prism). node_edges is a nv by ne array, where ne is the number of edges on a cell (6 for a tet, 8 for a pyramid, 9 for a prism). The entries in each row are 1 precisely if the corresponding edge is incident on the vertex. The exclusive or of the rows which correspond to nonzero entries in fs contains 1 in entries corresponding to edges where fs has opposite values on the vertices. */ PyObject * fso, * node_edgeso ; PyArrayObject * fsa, * node_edgesa, * maska ; int * fs, * node_edges, * mask ; int i, j, k, l, ll, ifs, imask, ntotal, ne, nv, ans_size ; Py_Try (PyArg_ParseTuple ( args, "OO", & fso, & node_edgeso ) ) ; GET_ARR (fsa, fso, PyArray_INT, 2) ; GET_ARR (node_edgesa, node_edgeso, PyArray_INT, 2) ; fs = (int *) A_DATA (fsa) ; node_edges = (int *) A_DATA (node_edgesa) ; ntotal = A_DIM (fsa, 0) ; nv = A_DIM (fsa, 1) ; if ( nv != A_DIM (node_edgesa, 0) ) { SETERR ("2nd dimension of 1st arg and 1st dimension of 2nd not equal."); Py_DECREF (fsa) ; Py_DECREF (node_edgesa) ; return (NULL) ; } ne = A_DIM (node_edgesa, 1) ; ans_size = ntotal * ne ; Py_Try (maska = (PyArrayObject *) PyArray_FromDims (1, & ans_size, PyArray_INT)) ; mask = (int *) A_DATA (maska) ; for (i = 0, ifs = 0, imask = 0 ; i < ntotal ; i ++, imask += ne, ifs += nv) { for (j = ifs, k = 0; k < nv; j ++, k ++) { if ( fs [j] ) { for ( l = imask , ll = 0; ll < ne ; l ++ , ll ++) { mask [l] ^= node_edges [j % nv * ne + ll] ; } } } } return PyArray_Return (maska) ; } /* Data for construct3 and walk3 */ int start_face4 [] = {0, 0, 1, 0, 2, 1} ; int start_face5 [] = {0, 0, 1, 2, 0, 1, 2, 3} ; int start_face6 [] = {1, 1, 0, 0, 2, 2, 0, 0, 1} ; int start_face8 [] = {0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3} ; static int * start_face [4] = {start_face4, start_face5, start_face6, start_face8} ; int ef0 [] = {0, 1} ; int ef1 [] = {0, 2} ; int ef2 [] = {0, 3} ; int ef3 [] = {0, 4} ; int ef4 [] = {0, 5} ; int ef5 [] = {1, 2} ; int ef6 [] = {1, 3} ; int ef7 [] = {1, 4} ; int ef8 [] = {1, 5} ; int ef9 [] = {2, 3} ; int ef10 [] = {2, 4} ; int ef11 [] = {2, 5} ; int ef12 [] = {3, 4} ; int ef13 [] = {3, 5} ; int * edge_faces4 [] = {ef0, ef1, ef5, ef2, ef9, ef6} ; int * edge_faces5 [] = {ef2, ef0, ef5, ef9, ef3, ef7, ef10, ef12} ; int * edge_faces6 [] = {ef6, ef7, ef2, ef3, ef9, ef10, ef0, ef1, ef5} ; int * edge_faces8 [] = {ef1, ef5, ef2, ef6, ef3, ef7, ef4, ef8, ef10, ef12, ef11, ef13} ; static int ** edge_faces [] = {edge_faces4, edge_faces5, edge_faces6, edge_faces8} ; int fe40 [] = {0, 1, 3} ; int fe41 [] = {0, 5, 2} ; int fe42 [] = {1, 2, 4} ; int fe43 [] = {3, 4, 5} ; int * face_edges4 [] = {fe40, fe41, fe42, fe43} ; int fe50 [] = {0, 1, 4} ; int fe51 [] = {1, 2, 5} ; int fe52 [] = {2, 3, 6} ; int fe53 [] = {0, 7, 3} ; int fe54 [] = {4, 5, 6, 7} ; int * face_edges5 [] = {fe50, fe51, fe52, fe53, fe54} ; int fe60 [] = {2, 7, 3, 6} ; int fe61 [] = {0, 6, 1, 8} ; int fe62 [] = {4, 8, 5, 7} ; int fe63 [] = {0, 4, 2} ; int fe64 [] = {1, 3, 5} ; int * face_edges6 [] = {fe60, fe61, fe62, fe63, fe64} ; int fe80 [] = {0, 6, 2, 4} ; int fe81 [] = {1, 5, 3, 7} ; int fe82 [] = {0, 8, 1, 10} ; int fe83 [] = {2, 11, 3, 9} ; int fe84 [] = {4, 9, 5, 8} ; int fe85 [] = {6, 10, 7, 11} ; int * face_edges8 [] = {fe80, fe81, fe82, fe83, fe84, fe85} ; static int ** face_edges [] = {face_edges4, face_edges5, face_edges6, face_edges8} ; int lens4 [] = {3, 3, 3, 3} ; int lens5 [] = {3, 3, 3, 3, 4} ; int lens6 [] = {4, 4, 4, 3, 3} ; int lens8 [] = {4, 4, 4, 4, 4, 4} ; static int * lens [] = {lens4, lens5, lens6, lens8} ; static int no_edges [4] = {6, 8, 9, 12} ; static int no_verts [4] = {4, 5, 6, 8} ; static int powers [4] = {14, 30, 62, 254} ; /* FILE * dbg; */ static void walk3 ( int * permute, int * mask, int itype, int pt ) { int split , splits [12] , list [12] , nlist , edge , face , i , j , * ttry , lttry , now ; for (i = 0; i < 12; i++) splits [i] = 0 ; /* list = mask.nonzero () */ for (i = 0, nlist = 0 ; i < no_edges [itype] ; i ++) if (mask [i]) { list [nlist++] = i ; if (nlist == 1) edge = i ; } split = 0 ; face = start_face [itype] [edge] ; for (i = 0 ; i < nlist - 1 ; i ++ ) { /* record this edge */ permute [edge * pt] = i ; splits [edge] = split ; mask [edge] = 0 ; /* advance to next edge */ ttry = face_edges [itype] [face] ; lttry = lens [itype] [face] ; now = 0 ; for ( j = 1 ; j < lttry ; j ++ ) if (abs (ttry [now] - edge) > abs (ttry [j] - edge)) now = j ; now ++ ; /* test opposite edge first (make X, never // or \\) */ edge = ttry [(now + 1) % lttry] ; if ( ! mask [edge]) { /* otherwise one or the other opposite edges must be set */ edge = ttry [now % lttry] ; if ( ! mask [edge]) { edge = ttry [ (now + 2) % lttry] ; if ( ! mask [edge]) { split ++ ; for (edge = 0 ; edge < no_edges [itype] ; edge++) { if ( mask [edge] != 0 ) { break ; } } } } } ttry = edge_faces [itype] [edge] ; face = (face == ttry [0]) ? ttry [1] : ttry [0] ; } permute [edge * pt] = nlist - 1 ; splits [edge] = split ; mask [edge] = 0 ; if (split != 0) for ( i = 0 , j = 0 ; i < no_edges [itype] ; i ++ , j += pt) { permute [j] += no_edges [itype] * splits [i] ; } return ; } static char arr_construct3__doc__ [] = "" ; static PyObject * arr_construct3 (PyObject * self, PyObject * args) { /* construct3 (mask, itype) computes how the cut edges of a particular type of cell must be ordered so that the polygon of intersection can be drawn correctly. itype = 0 for tetrahedra; 1 for pyramids; 2 for prisms; 3 for hexahedra. Suppose nv is the number of vertices of the cell type, and ne is the number of edges. Mask has been ravelled so that it was flat, originally it had 2**nv-2 rows, each with ne entries. Each row is ne long, and has an entry of 1 corresponding to each edge that is cut when the set of vertices corresponding to the row index has negative values. (The binary number for the row index + 1 has a one in position i if vertex i has a negative value.) The return array permute is ne by 2**nv-2, and the rows of permute tell how the edges should be ordered to draw the polygon properly. */ PyObject * masko ; PyArrayObject * permutea, * maska ; int itype, ne, pt, nm ; int * permute, * mask ; int permute_dims [2] ; int i ; /* dbg = fopen ("dbg","w"); */ Py_Try (PyArg_ParseTuple ( args, "Oi", & masko, & itype ) ) ; GET_ARR (maska, masko, PyArray_INT, 1) ; mask = (int *) A_DATA (maska) ; permute_dims [0] = ne = no_edges [itype] ; permute_dims [1] = pt = powers [itype] ; nm = A_DIM (maska, 0) ; if ( ne * pt != nm ) { SETERR ("permute and mask must have same number of elements.") ; Py_DECREF (maska) ; return NULL ; } Py_Try(permutea = (PyArrayObject *) PyArray_FromDims (2, permute_dims, PyArray_INT)); permute = (int *) A_DATA (permutea) ; for ( i = 0 ; i < pt ; i ++, permute ++, mask += ne ) { walk3 (permute, mask, itype, pt) ; } Py_DECREF (maska) ; /* fclose (dbg) ; */ return PyArray_Return (permutea) ; } static char arr_to_corners__doc__ [] = "" ; static PyObject * arr_to_corners (PyObject * self, PyObject * args) { /* This routine takes an array of floats describing cell-centered values and expands it to node-centered values. */ PyObject * oarr, * onv; int sum_nv; PyArrayObject * aarr, *anv, *ares; int * nv , i, j, snv, jtot; double * arr, * res; Py_Try (PyArg_ParseTuple (args, "OOi", & oarr, & onv, & sum_nv)); GET_ARR (aarr, oarr, PyArray_DOUBLE, 1) ; if isARRAY (onv) { GET_ARR (anv, onv, PyArray_INT, 1) ; } else { ERRSS ("The second argument must be an Int array") ; Py_DECREF (aarr) ; return (NULL) ; } snv = A_SIZE (anv) ; if (snv != A_SIZE (aarr)) { ERRSS ("The first and second arguments must be the same size.") ; Py_DECREF (aarr) ; Py_DECREF (anv) ; return NULL ; } if ( ! (ares = (PyArrayObject *) PyArray_FromDims (1, & sum_nv, PyArray_DOUBLE))) { ERRSS ("Unable to create result array.") ; Py_DECREF (aarr) ; Py_DECREF (anv) ; return NULL ; } res = (double *) A_DATA (ares) ; arr = (double *) A_DATA (aarr) ; nv = (int *) A_DATA (anv) ; jtot = 0; for ( i = 0; i < snv; i++) { for (j = 0; j < nv [i]; j++) { res [j + jtot] = arr [i]; } jtot = jtot + nv [i]; } Py_DECREF (aarr) ; Py_DECREF (anv) ; return PyArray_Return (ares) ; } /* List of methods defined in the module */ static struct PyMethodDef arr_methods[] = { {"histogram", arr_histogram, 1, arr_histogram__doc__}, {"array_set", arr_array_set, 1, arr_array_set__doc__}, {"index_sort", arr_index_sort, 1, arr_index_sort__doc__}, {"interp", arr_interp, 1, arr_interp__doc__}, {"digitize", arr_digitize, 1, arr_digitize__doc__}, {"zmin_zmax", arr_zmin_zmax, 1, arr_zmin_zmax__doc__}, {"reverse", arr_reverse, 1, arr_reverse__doc__}, {"span", arr_span, 1, arr_span__doc__}, {"nz", arr_nz, 1, arr_nz__doc__}, {"find_mask",arr_find_mask, 1, arr_find_mask__doc__}, {"construct3", arr_construct3, 1, arr_construct3__doc__}, {"to_corners", arr_to_corners, 1, arr_to_corners__doc__}, {NULL, NULL} /* sentinel */ }; /* Initialization function for the module (*must* be called initarrayfns) */ static char arrayfns_module_documentation[] = "" ; void initarrayfns() { PyObject *m, *d; /* Create the module and add the functions */ m = Py_InitModule4("arrayfns", arr_methods, arrayfns_module_documentation, (PyObject*)NULL, PYTHON_API_VERSION); /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); ErrorObject = PyString_FromString("arrayfns.error"); PyDict_SetItemString(d, "error", ErrorObject); /* XXXX Add constants here */ /* Check for errors */ if (PyErr_Occurred()) { Py_FatalError("can't initialize module arrayfns"); } #ifdef import_array import_array(); #endif }