[Numpy-svn] r3981 - in trunk/numpy/core: include/numpy src tests

numpy-svn@scip... numpy-svn@scip...
Tue Aug 21 02:58:38 CDT 2007


Author: stefan
Date: 2007-08-21 02:57:01 -0500 (Tue, 21 Aug 2007)
New Revision: 3981

Modified:
   trunk/numpy/core/include/numpy/ndarrayobject.h
   trunk/numpy/core/src/arraytypes.inc.src
   trunk/numpy/core/src/multiarraymodule.c
   trunk/numpy/core/tests/test_multiarray.py
Log:
Fast putmask implementation (patch by Eric Firing).


Modified: trunk/numpy/core/include/numpy/ndarrayobject.h
===================================================================
--- trunk/numpy/core/include/numpy/ndarrayobject.h	2007-08-21 06:58:53 UTC (rev 3980)
+++ trunk/numpy/core/include/numpy/ndarrayobject.h	2007-08-21 07:57:01 UTC (rev 3981)
@@ -1049,6 +1049,8 @@
 
 typedef void (PyArray_FastClipFunc)(void *in, npy_intp n_in, void *min,
                                     void *max, void *out);
+typedef void (PyArray_FastPutmaskFunc)(void *in, void *mask, npy_intp n_in,
+                                    void *values, npy_intp nv);
 
 typedef struct {
         npy_intp *ptr;
@@ -1126,6 +1128,7 @@
         int *cancastto;
 
         PyArray_FastClipFunc *fastclip;
+        PyArray_FastPutmaskFunc *fastputmask;
 } PyArray_ArrFuncs;
 
 #define NPY_ITEM_REFCOUNT   0x01  /* The item must be reference counted
@@ -1933,7 +1936,7 @@
 #define PyArray_GETPTR3(obj, i, j, k) ((void *)(PyArray_BYTES(obj) +          \
                                             (i)*PyArray_STRIDES(obj)[0] +     \
                                             (j)*PyArray_STRIDES(obj)[1] +     \
-                                            (k)*PyArray_STRIDES(obj)[2])) 
+                                            (k)*PyArray_STRIDES(obj)[2]))
 
 #define PyArray_GETPTR4(obj, i, j, k, l) ((void *)(PyArray_BYTES(obj) +       \
                                             (i)*PyArray_STRIDES(obj)[0] +     \

Modified: trunk/numpy/core/src/arraytypes.inc.src
===================================================================
--- trunk/numpy/core/src/arraytypes.inc.src	2007-08-21 06:58:53 UTC (rev 3980)
+++ trunk/numpy/core/src/arraytypes.inc.src	2007-08-21 07:57:01 UTC (rev 3981)
@@ -312,9 +312,9 @@
         char *buffer;
 #endif
 
-        if (!PyString_Check(op) && !PyUnicode_Check(op) && 
+        if (!PyString_Check(op) && !PyUnicode_Check(op) &&
             PySequence_Check(op) && PySequence_Size(op) > 0) {
-                PyErr_SetString(PyExc_ValueError, 
+                PyErr_SetString(PyExc_ValueError,
                                 "setting an array element with a sequence");
                 return -1;
         }
@@ -360,9 +360,9 @@
         return 0;
 }
 
-/* STRING -- can handle both NULL-terminated and not NULL-terminated cases 
+/* STRING -- can handle both NULL-terminated and not NULL-terminated cases
              will truncate all ending NULLs in returned string.
-*/    
+*/
 static PyObject *
 STRING_getitem(char *ip, PyArrayObject *ap)
 {
@@ -384,7 +384,7 @@
 
         if (!PyString_Check(op) && !PyUnicode_Check(op) &&
             PySequence_Check(op) && PySequence_Size(op) > 0) {
-                PyErr_SetString(PyExc_ValueError, 
+                PyErr_SetString(PyExc_ValueError,
                                 "setting an array element with a sequence");
                 return -1;
         }
@@ -520,7 +520,7 @@
 
  finish:
         if (PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) ||
-            PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) {                
+            PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) {
                 PyErr_SetString(PyExc_ValueError,
                                 "tried to get void-array with object"
                                 " members as buffer.");
@@ -1104,15 +1104,15 @@
                         memcpy(dst, src, n*sizeof(@type@));
                 }
                 else {
-                        _unaligned_strided_byte_copy(dst, dstride, src, 
-                                                     sstride, n, 
+                        _unaligned_strided_byte_copy(dst, dstride, src,
+                                                     sstride, n,
                                                      sizeof(@type@));
                 }
         }
 
         if (swap) {
                 _strided_byte_swap(dst, dstride, n, SIZEOF_@fsize@);
-                _strided_byte_swap(((char *)dst + SIZEOF_@fsize@), dstride, 
+                _strided_byte_swap(((char *)dst + SIZEOF_@fsize@), dstride,
                                    n, SIZEOF_@fsize@);
         }
 }
@@ -1306,7 +1306,7 @@
                                 arr->descr=descr;return;
                         }
                         arr->descr = new;
-                        new->f->copyswapn(dst+offset, dstride, 
+                        new->f->copyswapn(dst+offset, dstride,
                                           (src != NULL ? src+offset : NULL),
                                           sstride, n, swap, arr);
                 }
@@ -1315,7 +1315,7 @@
         }
         if (swap && arr->descr->subarray != NULL) {
                 PyArray_Descr *descr, *new;
-                npy_intp num;                
+                npy_intp num;
                 npy_intp i;
                 int subitemsize;
                 char *dstptr, *srcptr;
@@ -1357,8 +1357,8 @@
                                 arr->descr=descr;return;
                         }
                         arr->descr = new;
-                        new->f->copyswap(dst+offset, 
-                                         (src != NULL ? src+offset : NULL), 
+                        new->f->copyswap(dst+offset,
+                                         (src != NULL ? src+offset : NULL),
                                          swap, arr);
                 }
                 arr->descr = descr;
@@ -1366,7 +1366,7 @@
         }
         if (swap && arr->descr->subarray != NULL) {
                 PyArray_Descr *descr, *new;
-                npy_intp num;                
+                npy_intp num;
                 int itemsize;
                 descr = arr->descr;
                 new = descr->subarray->base;
@@ -1668,7 +1668,7 @@
                 if (ip1 == ip2) return 1;
                 if (ip1 == NULL) return -1;
                 return 1;
-        }                
+        }
         return PyObject_Compare(*ip1, *ip2);
 }
 
@@ -1700,9 +1700,9 @@
 
 /* If fields are defined, then compare on first field and if equal
    compare on second field.  Continue until done or comparison results
-   in not_equal. 
+   in not_equal.
 
-   Must align data passed on to sub-comparisons. 
+   Must align data passed on to sub-comparisons.
 */
 
 static int
@@ -1714,9 +1714,9 @@
         char *nip1, *nip2;
         int i, offset, res=0;
 
-        if (!PyArray_HASFIELDS(ap)) 
+        if (!PyArray_HASFIELDS(ap))
                 return STRING_compare(ip1, ip2, ap);
-        
+
         descr = ap->descr;
         /* Compare on the first-field.  If equal, then
            compare on the second-field, etc.
@@ -1743,7 +1743,7 @@
                             /* copy data to a buffer */
                                 nip2 = _pya_malloc(new->elsize);
                                 if (nip2 == NULL) {
-                                        if (nip1 != ip1+offset) 
+                                        if (nip1 != ip1+offset)
                                                 _pya_free(nip1);
                                         goto finish;
                                 }
@@ -2044,62 +2044,97 @@
 
 
 
-/************************ 
- * Fast clip functions 
- *************************/ 
- 
-/**begin repeat 
-#name=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# 
-*/ 
+/************************
+ * Fast clip functions
+ *************************/
+
+/**begin repeat
+#name=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 void
 @name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out)
-{ 
-        register npy_intp i; 
+{
+        register npy_intp i;
         @type@ max_val, min_val;
-        
+
         max_val = *max;
         min_val = *min;
-  
-        for (i = 0; i < ni; i++) {   
-                if (in[i] < min_val) { 
-                        out[i]   = min_val; 
-                } else if (in[i] > max_val) { 
-                        out[i]   = max_val; 
-                } 
-        } 
-        
+
+        for (i = 0; i < ni; i++) {
+                if (in[i] < min_val) {
+                        out[i]   = min_val;
+                } else if (in[i] > max_val) {
+                        out[i]   = max_val;
+                }
+        }
+
         return;
-} 
-/**end repeat**/ 
+}
+/**end repeat**/
 
-/**begin repeat 
-#name=CFLOAT, CDOUBLE, CLONGDOUBLE# 
-#type= cfloat, cdouble, clongdouble# 
-*/ 
+/**begin repeat
+#name=CFLOAT, CDOUBLE, CLONGDOUBLE#
+#type= cfloat, cdouble, clongdouble#
+*/
 static void
-@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out) 
-{ 
-        register npy_intp i; 
+@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out)
+{
+        register npy_intp i;
         @type@ max_val, min_val;
-        
+
         min_val = *min;
         max_val = *max;
 
-        for (i = 0; i < ni; i++) {   
-                if (PyArray_CLT(in[i], min_val)) { 
+        for (i = 0; i < ni; i++) {
+                if (PyArray_CLT(in[i], min_val)) {
                         out[i] = min_val;
-                } else if (PyArray_CGT(in[i], max_val)) { 
+                } else if (PyArray_CGT(in[i], max_val)) {
                         out[i] = max_val;
-                } 
-        }         
+                }
+        }
         return;
-} 
- 
-/**end repeat**/ 
+}
 
+/**end repeat**/
+
 #define OBJECT_fastclip NULL
 
+/************************
+ * Fast putmask functions
+ *************************/
+
+/**begin repeat
+#name=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#
+*/
+static void
+@name@_fastputmask(@type@ *in, Bool *mask, intp ni, @type@ *vals, intp nv)
+{
+        register npy_intp i;
+        @type@ s_val;
+        if (nv == 1) {
+                s_val = *vals;
+                for (i = 0; i < ni; i++) {
+                        if (mask[i]) {
+                                in[i] = s_val;
+                        }
+                }
+        }
+        else {
+                for (i = 0; i < ni; i++) {
+                        if (mask[i]) {
+                                in[i] = vals[i%nv];
+                        }
+                }
+        }
+        return;
+}
+/**end repeat**/
+
+#define OBJECT_fastputmask NULL
+
+
 #define _ALIGN(type) offsetof(struct {char c; type v;},v)
 
 /* Disable harmless compiler warning "4116: unnamed type definition in
@@ -2164,7 +2199,8 @@
         (PyArray_ScalarKindFunc*)NULL,
         NULL,
         NULL,
-        (PyArray_FastClipFunc *)NULL
+        (PyArray_FastClipFunc *)NULL,
+        (PyArray_FastPutmaskFunc *)NULL
 };
 
 static PyArray_Descr @from@_Descr = {
@@ -2241,7 +2277,8 @@
         (PyArray_ScalarKindFunc*)NULL,
         NULL,
         NULL,
-        (PyArray_FastClipFunc*)@from@_fastclip
+        (PyArray_FastClipFunc*)@from@_fastclip,
+        (PyArray_FastPutmaskFunc*)@from@_fastputmask
 };
 
 static PyArray_Descr @from@_Descr = {

Modified: trunk/numpy/core/src/multiarraymodule.c
===================================================================
--- trunk/numpy/core/src/multiarraymodule.c	2007-08-21 06:58:53 UTC (rev 3980)
+++ trunk/numpy/core/src/multiarraymodule.c	2007-08-21 07:57:01 UTC (rev 3981)
@@ -23,8 +23,8 @@
 #include "numpy/arrayobject.h"
 
 #define PyAO PyArrayObject
-        
 
+
 static PyObject *typeDict=NULL;   /* Must be explicitly loaded */
 
 static PyArray_Descr *
@@ -68,7 +68,7 @@
                 return NULL;
         }
         /* Understand ctypes structures --
-           bit-fields are not supported 
+           bit-fields are not supported
            automatically aligns */
         dtypedescr = PyObject_GetAttrString(obj, "_fields_");
         PyErr_Clear();
@@ -356,7 +356,7 @@
         Py_DECREF(out);
         if (ret_int) {
                 Py_INCREF(a->descr);
-                tmp = PyArray_CastToType((PyArrayObject *)ret, 
+                tmp = PyArray_CastToType((PyArrayObject *)ret,
                                          a->descr, PyArray_ISFORTRAN(a));
                 Py_DECREF(ret);
                 return tmp;
@@ -447,15 +447,15 @@
 
 /* attempt to reshape an array without copying data
  *
- * This function should correctly handle all reshapes, including 
+ * This function should correctly handle all reshapes, including
  * axes of length 1. Zero strides should work but are untested.
  *
  * If a copy is needed, returns 0
- * If no copy is needed, returns 1 and fills newstrides 
+ * If no copy is needed, returns 1 and fills newstrides
  *     with appropriate strides
  *
  * The "fortran" argument describes how the array should be viewed
- * during the reshape, not how it is stored in memory (that 
+ * during the reshape, not how it is stored in memory (that
  * information is in self->strides).
  *
  * If some output dimensions have length 1, the strides assigned to
@@ -463,7 +463,7 @@
  * stride of the next-fastest index.
  */
 static int
-_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, 
+_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
 			intp *newstrides, int fortran)
 {
 	int oldnd;
@@ -483,10 +483,10 @@
 
 	/*
 	fprintf(stderr, "_attempt_nocopy_reshape( (");
-	for (oi=0; oi<oldnd; oi++) 
+	for (oi=0; oi<oldnd; oi++)
 		fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]);
 	fprintf(stderr, ") -> (");
-	for (ni=0; ni<newnd; ni++) 
+	for (ni=0; ni<newnd; ni++)
 		fprintf(stderr, "(%d,*), ", newdims[ni]);
 	fprintf(stderr, "), fortran=%d)\n", fortran);
 	*/
@@ -522,22 +522,22 @@
 		for(ok=oi; ok<oj-1; ok++) {
 			if (fortran) {
 				if (oldstrides[ok+1] !=		\
-				    olddims[ok]*oldstrides[ok]) 
+				    olddims[ok]*oldstrides[ok])
 					return 0; /* not contiguous enough */
 			} else { /* C order */
 				if (oldstrides[ok] !=			\
-				    olddims[ok+1]*oldstrides[ok+1]) 
+				    olddims[ok+1]*oldstrides[ok+1])
 					return 0; /* not contiguous enough */
 			}
 		}
 
 		if (fortran) {
 			newstrides[ni]=oldstrides[oi];
-			for (nk=ni+1;nk<nj;nk++) 
+			for (nk=ni+1;nk<nj;nk++)
 				newstrides[nk]=newstrides[nk-1]*newdims[nk-1];
 		} else { /* C order */
 			newstrides[nj-1]=oldstrides[oj-1];
-			for (nk=nj-1;nk>ni;nk--) 
+			for (nk=nj-1;nk>ni;nk--)
 				newstrides[nk-1]=newstrides[nk]*newdims[nk];
 		}
 
@@ -548,10 +548,10 @@
 
 	/*
 	fprintf(stderr, "success: _attempt_nocopy_reshape (");
-	for (oi=0; oi<oldnd; oi++) 
+	for (oi=0; oi<oldnd; oi++)
 		fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]);
 	fprintf(stderr, ") -> (");
-	for (ni=0; ni<newnd; ni++) 
+	for (ni=0; ni<newnd; ni++)
 		fprintf(stderr, "(%d,%d), ", newdims[ni], newstrides[ni]);
 	fprintf(stderr, ")\n");
 	*/
@@ -605,7 +605,7 @@
 /* Returns a new array
    with the new shape from the data
    in the old array --- order-perspective depends on fortran argument.
-   copy-only-if-necessary 
+   copy-only-if-necessary
 */
 
 /*MULTIARRAY_API
@@ -642,8 +642,8 @@
 	/* 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  
-	   In this case we don't need to do anything but update strides and 
+	   or doing a combination of the two
+	   In this case we don't need to do anything but update strides and
 	   dimensions.	So, we can handle non single-segment cases.
 	*/
 	i=_check_ones(self, n, dimensions, newstrides);
@@ -652,22 +652,22 @@
 	flags = self->flags;
 
 	if (strides==NULL) { /* we are really re-shaping not just adding ones
-				to the shape somewhere */		 
-		
-		/* fix any -1 dimensions and check new-dimensions against 
+				to the shape somewhere */
+
+		/* fix any -1 dimensions and check new-dimensions against
 		   old size */
 		if (_fix_unknown_dimension(newdims, PyArray_SIZE(self)) < 0)
 			return NULL;
 
 		/* sometimes we have to create a new copy of the array
 		   in order to get the right orientation and
-                   because we can't just re-use the buffer with the 
+                   because we can't just re-use the buffer with the
                    data in the order it is in.
 		*/
-		if (!(PyArray_ISONESEGMENT(self)) || 
-                    (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && 
+		if (!(PyArray_ISONESEGMENT(self)) ||
+                    (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) &&
                        fortran == NPY_FORTRANORDER)
-                      || (PyArray_CHKFLAGS(self, NPY_FORTRAN) && 
+                      || (PyArray_CHKFLAGS(self, NPY_FORTRAN) &&
                           fortran == NPY_CORDER)) && (self->nd > 1))) {
 
 		    int success=0;
@@ -690,7 +690,7 @@
 		/* We always have to interpret the contiguous buffer correctly
 		 */
 
-		/* Make sure the flags argument is set. 
+		/* Make sure the flags argument is set.
 		*/
 		if (n > 1) {
 			if (fortran == NPY_FORTRANORDER) {
@@ -1139,7 +1139,7 @@
 PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *out)
 {
         PyArray_FastClipFunc *func;
-        int outgood=0, ingood=0;                
+        int outgood=0, ingood=0;
         PyArrayObject *maxa=NULL;
         PyArrayObject *mina=NULL;
         PyArrayObject *newout=NULL, *newin=NULL;
@@ -1147,7 +1147,7 @@
         PyObject *zero;
 
         func = self->descr->f->fastclip;
-        if (func == NULL || !PyArray_CheckAnyScalar(min) || 
+        if (func == NULL || !PyArray_CheckAnyScalar(min) ||
             !PyArray_CheckAnyScalar(max))
                 return _slow_array_clip(self, min, max, out);
 
@@ -1164,7 +1164,7 @@
            KIND than the input array (and then find the
            type that matches both).
         */
-        if (PyArray_ScalarKind(newdescr->type_num, NULL) > 
+        if (PyArray_ScalarKind(newdescr->type_num, NULL) >
             PyArray_ScalarKind(self->descr->type_num, NULL)) {
                 indescr = _array_small_type(newdescr, self->descr);
                 func = indescr->f->fastclip;
@@ -1174,7 +1174,7 @@
                 Py_INCREF(indescr);
         }
         Py_DECREF(newdescr);
-        
+
         if (!PyDataType_ISNOTSWAPPED(indescr)) {
                 PyArray_Descr *descr2;
                 descr2 = PyArray_DescrNewByteorder(indescr, '=');
@@ -1184,19 +1184,19 @@
         }
 
         /* Convert max to an array */
-        maxa = (NPY_AO *)PyArray_FromAny(max, indescr, 0, 0, 
+        maxa = (NPY_AO *)PyArray_FromAny(max, indescr, 0, 0,
                                          NPY_DEFAULT, NULL);
         if (maxa == NULL) return NULL;
 
 
         /* If we are unsigned, then make sure min is not <0 */
-        /* This is to match the behavior of 
+        /* This is to match the behavior of
            _slow_array_clip
 
            We allow min and max to go beyond the limits
            for other data-types in which case they
            are interpreted as their modular counterparts.
-        */           
+        */
         if (PyArray_ISUNSIGNED(self)) {
                 int cmp;
                 zero = PyInt_FromLong(0);
@@ -1216,7 +1216,7 @@
 
         /* Convert min to an array */
         Py_INCREF(indescr);
-        mina = (NPY_AO *)PyArray_FromAny(min, indescr, 0, 0, 
+        mina = (NPY_AO *)PyArray_FromAny(min, indescr, 0, 0,
                                          NPY_DEFAULT, NULL);
         Py_DECREF(min);
         if (mina == NULL) goto fail;
@@ -1244,11 +1244,11 @@
         /* At this point, newin is a single-segment, aligned, and correct
            byte-order array of the correct type
 
-           if ingood == 0, then it is a copy, otherwise, 
+           if ingood == 0, then it is a copy, otherwise,
            it is the original input.
         */
 
-        /* If we have already made a copy of the data, then use 
+        /* If we have already made a copy of the data, then use
            that as the output array
         */
         if (out == NULL && !ingood) {
@@ -1258,10 +1258,10 @@
         /* Now, we know newin is a usable array for fastclip,
            we need to make sure the output array is available
            and usable */
-        if (out == NULL) { 
+        if (out == NULL) {
                 Py_INCREF(indescr);
                 out = (NPY_AO*)PyArray_NewFromDescr(self->ob_type,
-						    indescr, self->nd, 
+						    indescr, self->nd,
                                                     self->dimensions,
 						    NULL, NULL,
 						    PyArray_ISFORTRAN(self),
@@ -1274,8 +1274,8 @@
         if (out == newin) {
                 outgood = 1;
         }
-        if (!outgood && PyArray_ISONESEGMENT(out) && 
-            PyArray_CHKFLAGS(out, ALIGNED) && PyArray_ISNOTSWAPPED(out) &&  
+        if (!outgood && PyArray_ISONESEGMENT(out) &&
+            PyArray_CHKFLAGS(out, ALIGNED) && PyArray_ISNOTSWAPPED(out) &&
             PyArray_EquivTypes(out->descr, indescr)) {
                 outgood = 1;
         }
@@ -1284,9 +1284,9 @@
         /* Create one, now */
         if (!outgood) {
                 int oflags;
-                if (PyArray_ISFORTRAN(out)) 
+                if (PyArray_ISFORTRAN(out))
                         oflags = NPY_FARRAY;
-                else 
+                else
                         oflags = NPY_CARRAY;
                 oflags |= NPY_UPDATEIFCOPY | NPY_FORCECAST;
                 Py_INCREF(indescr);
@@ -1308,12 +1308,12 @@
         if (newout->data != newin->data) {
                 memcpy(newout->data, newin->data, PyArray_NBYTES(newin));
         }
-        
+
         /* Now we can call the fast-clip function */
 
         func(newin->data, PyArray_SIZE(newin), mina->data, maxa->data,
              newout->data);
-        
+
         /* Clean up temporary variables */
         Py_DECREF(mina);
         Py_DECREF(maxa);
@@ -1321,7 +1321,7 @@
         /* Copy back into out if out was not already a nice array. */
         Py_DECREF(newout);
         return (PyObject *)out;
-        
+
  fail:
         Py_XDECREF(maxa);
         Py_XDECREF(mina);
@@ -1339,11 +1339,11 @@
 {
 	if (PyArray_ISCOMPLEX(self)) {
                 if (out == NULL) {
-                        return PyArray_GenericUnaryFunction(self, 
+                        return PyArray_GenericUnaryFunction(self,
                                                             n_ops.conjugate);
                 }
                 else {
-                        return PyArray_GenericBinaryFunction(self, 
+                        return PyArray_GenericBinaryFunction(self,
                                                              (PyObject *)out,
                                                              n_ops.conjugate);
                 }
@@ -2114,7 +2114,7 @@
                                 Py_DECREF(mps[i]);
                                 mps[i] = (PyArrayObject *)obj;
                         }
-                }                        
+                }
                 return mps;
         }
 
@@ -2186,7 +2186,7 @@
                 if (mps[i] == NULL) goto fail;
 	}
 	Py_DECREF(intype);
- 	Py_XDECREF(stype);        
+ 	Py_XDECREF(stype);
 	return mps;
 
  fail:
@@ -2567,7 +2567,7 @@
 
 	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,
@@ -2667,7 +2667,7 @@
 
         /* Creates new reference op2 */
 	if ((op2=(PyAO *)_check_axis(op, &axis, 0))==NULL) return NULL;
-        
+
 	/* Determine if we should use new algorithm or not */
 	if (op2->descr->f->argsort[which] != NULL) {
 		ret = (PyArrayObject *)_new_argsort(op2, axis, which);
@@ -2790,7 +2790,7 @@
 				     "merge sort not available for item %d", i);
 			goto fail;
 		}
-		if (!object && 
+		if (!object &&
                     PyDataType_FLAGCHK(mps[i]->descr, NPY_NEEDS_PYAPI))
                         object = 1;
 		its[i] = (PyArrayIterObject *)PyArray_IterAllButAxis	\
@@ -2805,7 +2805,7 @@
 						   mps[0]->dimensions,
 						   PyArray_INTP,
 						   NULL, NULL, 0, 0, NULL);
-		
+
 		if (ret == NULL) goto fail;
 		*((intp *)(ret->data)) = 0;
 		goto finish;
@@ -3288,7 +3288,7 @@
 
 	nd = ap1->nd+ap2->nd-2;
         if (nd > NPY_MAXDIMS) {
-                PyErr_SetString(PyExc_ValueError, 
+                PyErr_SetString(PyExc_ValueError,
                                 "dot: too many dimensions in result");
                 goto fail;
         }
@@ -4047,8 +4047,8 @@
 array_putmask(PyObject *module, PyObject *args, PyObject *kwds)
 {
 	PyObject *mask, *values;
-        PyObject *array; 
-        
+        PyObject *array;
+
 	static char *kwlist[] = {"arr", "mask", "values", NULL};
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!OO:putmask", kwlist,
@@ -4065,6 +4065,7 @@
 static PyObject *
 PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
 {
+        PyArray_FastPutmaskFunc *func;
         PyArrayObject  *mask, *values;
         int i, chunk, ni, max_item, nv, tmp;
         char *src, *dest;
@@ -4075,8 +4076,8 @@
 
         if (!PyArray_Check(self)) {
                 PyErr_SetString(PyExc_TypeError,
-				"putmask: first argument must "\
-				"be an array");
+                                "putmask: first argument must "\
+                                 "be an array");
                 return NULL;
         }
         if (!PyArray_ISCONTIGUOUS(self)) {
@@ -4094,45 +4095,51 @@
         chunk = self->descr->elsize;
 
         mask = (PyArrayObject *)\
-		PyArray_FROM_OTF(mask0, PyArray_BOOL, CARRAY | FORCECAST);
-	if (mask == NULL) goto fail;
+                     PyArray_FROM_OTF(mask0, PyArray_BOOL, CARRAY | 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");
+                        "putmask: mask and data must be "\
+                        "the same size");
                 goto fail;
         }
 
         values = (PyArrayObject *)\
-		PyArray_ContiguousFromAny(values0, self->descr->type_num, 0, 0);
-	if (values == NULL) goto fail;
-        nv = PyArray_SIZE(values);	 /* zero if null array */
+                PyArray_ContiguousFromAny(values0, self->descr->type_num, 0, 0);
+        if (values == NULL) goto fail;
+        nv = PyArray_SIZE(values);        /* zero if null array */
         if (nv <= 0) {
                 Py_XDECREF(values);
                 Py_XDECREF(mask);
                 Py_INCREF(Py_None);
                 return Py_None;
         }
-        if (nv > 0) {
-                if (PyDataType_REFCHK(self->descr)) {
+        if (PyDataType_REFCHK(self->descr)) {
+                for(i=0; i<ni; i++) {
+                        tmp = ((Bool *)(mask->data))[i];
+                        if (tmp) {
+                                src = values->data + chunk * (i % nv);
+                                PyArray_Item_INCREF(src, self->descr);
+                                PyArray_Item_XDECREF(dest+i*chunk, self->descr);
+                                memmove(dest + i * chunk, src, chunk);
+                        }
+                }
+        }
+        else {
+                func = self->descr->f->fastputmask;
+                if (func == NULL) {
                         for(i=0; i<ni; i++) {
-                                src = values->data + chunk * (i % nv);
                                 tmp = ((Bool *)(mask->data))[i];
                                 if (tmp) {
-					PyArray_Item_INCREF(src, self->descr);
-					PyArray_Item_XDECREF(dest+i*chunk, self->descr);
+                                        src = values->data + chunk * (i % nv);
                                         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);
-			}
-		}
+                        func(dest, mask->data, ni, values->data, nv);
+                }
         }
 
         Py_XDECREF(values);
@@ -4625,7 +4632,7 @@
 					"invalid shape in fixed-type tuple.");
 			goto fail;
 		}
-		/* If (type, 1) was given, it is equivalent to type... 
+		/* If (type, 1) was given, it is equivalent to type...
                    or (type, ()) was given it is equivalent to type... */
 		if ((shape.len == 1 && shape.ptr[0] == 1 && PyNumber_Check(val)) || \
                     (shape.len == 0 && PyTuple_Check(val))) {
@@ -4763,7 +4770,7 @@
 	if (maxalign > 1) {
 		totalsize = ((totalsize+maxalign-1)/maxalign)*maxalign;
 	}
-	if (align) new->alignment = maxalign;        
+	if (align) new->alignment = maxalign;
 	return new;
 
  fail:
@@ -5092,16 +5099,16 @@
 
         /* Check for ints at start of string */
 	if ((type[0] >= '0' && type[0] <= '9') ||
-            ((len > 1) && _chk_byteorder(type[0]) && 
+            ((len > 1) && _chk_byteorder(type[0]) &&
              (type[1] >= '0' && type[1] <= '9')))
                 return 1;
 
         /* Check for empty tuple */
         if (((len > 1) && (type[0] == '(' && type[1] == ')')) ||
-            ((len > 3) && _chk_byteorder(type[0]) && 
+            ((len > 3) && _chk_byteorder(type[0]) &&
              (type[1] == '(' && type[2] == ')')))
                 return 1;
-             
+
         /* Check for presence of commas */
 	for (i=1;i<len;i++)
 		if (type[i] == ',') return 1;
@@ -5148,7 +5155,7 @@
                                         "data-type-descriptor not understood");
                 }
                 return PY_FAIL;
-        } 
+        }
         return PY_SUCCEED;
 }
 
@@ -5177,7 +5184,7 @@
                                         "data-type-descriptor not understood");
                 }
                 return PY_FAIL;
-        } 
+        }
         return PY_SUCCEED;
 }
 
@@ -5724,7 +5731,7 @@
 				"itemsize cannot be zero");
 		return NULL;
 	}
-        
+
 	if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) {
 		if (obj == NULL) obj = Py_None;
 		dptr = &obj;
@@ -6652,7 +6659,7 @@
 	if (!(*next)) {
                 if (PyTuple_Check(stop)) {
                         PyErr_Clear();
-                        PyErr_SetString(PyExc_TypeError, 
+                        PyErr_SetString(PyExc_TypeError,
                                         "arange: scalar arguments expected "\
                                         "instead of a tuple.");
                 }
@@ -7089,10 +7096,10 @@
                 __except(1) {
                         err = 1;
                 }
-#else 
+#else
                 PyOS_sighandler_t _npy_sig_save;
                 _npy_sig_save = PyOS_setsig(SIGSEGV, _SigSegv_Handler);
-                
+
                 if (setjmp(_NPY_SIGSEGV_BUF) == 0) {
                         _test_code();
                 }
@@ -7102,10 +7109,10 @@
                 PyOS_setsig(SIGSEGV, _npy_sig_save);
 #endif
                 if (err) {
-                        PyErr_SetString(PyExc_ValueError, 
+                        PyErr_SetString(PyExc_ValueError,
                                         "cannot use memory location as " \
                                         "a buffer.");
-                        return NULL;                        
+                        return NULL;
                 }
         }
 
@@ -7493,7 +7500,7 @@
         _addnew(WRITEABLE, W);
         _addone(C_CONTIGUOUS);
         _addone(F_CONTIGUOUS);
-        
+
 #undef _addone
 #undef _addnew
 

Modified: trunk/numpy/core/tests/test_multiarray.py
===================================================================
--- trunk/numpy/core/tests/test_multiarray.py	2007-08-21 06:58:53 UTC (rev 3980)
+++ trunk/numpy/core/tests/test_multiarray.py	2007-08-21 07:57:01 UTC (rev 3981)
@@ -420,6 +420,29 @@
         y = rec['x'].clip(-0.3,0.5)
         self._check_range(y,-0.3,0.5)
 
+class test_putmask(ParametricTestCase):
+    def tst_basic(self,x,T,mask,val):
+        N.putmask(x,mask,val)
+        assert N.all(x[mask] == T(val))
+        assert x.dtype == T
+
+    def testip_types(self):
+        unchecked_types = [str,unicode,N.void,object]
+
+        x = N.random.random(1000)*100
+        mask = x < 40
+
+        tests = []
+        for val in [-100,0,15]:
+            for types in N.sctypes.itervalues():
+                tests.extend((self.tst_basic,x.copy().astype(T),T,mask,val)
+                             for T in types if T not in unchecked_types)
+        return tests
+
+    def test_mask_size(self):
+        self.failUnlessRaises(ValueError,N.putmask,
+                              N.array([1,2,3]),[True],5)
+
 # Import tests from unicode
 set_local_path()
 from test_unicode import *



More information about the Numpy-svn mailing list