numpy  2.0.0
src/private/lowlevel_strided_loops.h
Go to the documentation of this file.
00001 #ifndef __LOWLEVEL_STRIDED_LOOPS_H
00002 #define __LOWLEVEL_STRIDED_LOOPS_H
00003 
00004 /*
00005  * NOTE: This API should remain private for the time being, to allow
00006  *       for further refinement.  I think the 'aligned' mechanism
00007  *       needs changing, for example.
00008  */
00009 
00010 /*
00011  * This function pointer is for unary operations that input an
00012  * arbitrarily strided one-dimensional array segment and output
00013  * an arbitrarily strided array segment of the same size.
00014  * It may be a fully general function, or a specialized function
00015  * when the strides or item size have particular known values.
00016  *
00017  * Examples of unary operations are a straight copy, a byte-swap,
00018  * and a casting operation,
00019  *
00020  * The 'transferdata' parameter is slightly special, following a
00021  * generic auxiliary data pattern defined in ndarraytypes.h
00022  * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data.
00023  *
00024  */
00025 typedef void (PyArray_StridedUnaryOp)(char *dst, npy_intp dst_stride,
00026                                     char *src, npy_intp src_stride,
00027                                     npy_intp N, npy_intp src_itemsize,
00028                                     NpyAuxData *transferdata);
00029 
00030 /*
00031  * This is for pointers to functions which behave exactly as
00032  * for PyArray_StridedUnaryOp, but with an additional mask controlling
00033  * which values are transformed.
00034  *
00035  * In particular, the 'i'-th element is operated on if and only if
00036  * mask[i*mask_stride] is true.
00037  */
00038 typedef void (PyArray_MaskedStridedUnaryOp)(char *dst, npy_intp dst_stride,
00039                                     char *src, npy_intp src_stride,
00040                                     npy_bool *mask, npy_intp mask_stride,
00041                                     npy_intp N, npy_intp src_itemsize,
00042                                     NpyAuxData *transferdata);
00043 
00044 /*
00045  * This function pointer is for binary operations that input two
00046  * arbitrarily strided one-dimensional array segments and output
00047  * an arbitrarily strided array segment of the same size.
00048  * It may be a fully general function, or a specialized function
00049  * when the strides or item size have particular known values.
00050  *
00051  * Examples of binary operations are the basic arithmetic operations,
00052  * logical operators AND, OR, and many others.
00053  *
00054  * The 'transferdata' parameter is slightly special, following a
00055  * generic auxiliary data pattern defined in ndarraytypes.h
00056  * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data.
00057  *
00058  */
00059 typedef void (PyArray_StridedBinaryOp)(char *dst, npy_intp dst_stride,
00060                                     char *src0, npy_intp src0_stride,
00061                                     char *src1, npy_intp src1_stride,
00062                                     npy_intp N, NpyAuxData *transferdata);
00063 
00064 /*
00065  * Gives back a function pointer to a specialized function for copying
00066  * strided memory.  Returns NULL if there is a problem with the inputs.
00067  *
00068  * aligned:
00069  *      Should be 1 if the src and dst pointers are always aligned,
00070  *      0 otherwise.
00071  * src_stride:
00072  *      Should be the src stride if it will always be the same,
00073  *      NPY_MAX_INTP otherwise.
00074  * dst_stride:
00075  *      Should be the dst stride if it will always be the same,
00076  *      NPY_MAX_INTP otherwise.
00077  * itemsize:
00078  *      Should be the item size if it will always be the same, 0 otherwise.
00079  *
00080  */
00081 NPY_NO_EXPORT PyArray_StridedUnaryOp *
00082 PyArray_GetStridedCopyFn(int aligned,
00083                         npy_intp src_stride, npy_intp dst_stride,
00084                         npy_intp itemsize);
00085 
00086 /*
00087  * Gives back a function pointer to a specialized function for copying
00088  * and swapping strided memory.  This assumes each element is a single
00089  * value to be swapped.
00090  *
00091  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00092  * see above.
00093  *
00094  * Parameters are as for PyArray_GetStridedCopyFn.
00095  */
00096 NPY_NO_EXPORT PyArray_StridedUnaryOp *
00097 PyArray_GetStridedCopySwapFn(int aligned,
00098                             npy_intp src_stride, npy_intp dst_stride,
00099                             npy_intp itemsize);
00100 
00101 /*
00102  * Gives back a function pointer to a specialized function for copying
00103  * and swapping strided memory.  This assumes each element is a pair
00104  * of values, each of which needs to be swapped.
00105  *
00106  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00107  * see above.
00108  *
00109  * Parameters are as for PyArray_GetStridedCopyFn.
00110  */
00111 NPY_NO_EXPORT PyArray_StridedUnaryOp *
00112 PyArray_GetStridedCopySwapPairFn(int aligned,
00113                             npy_intp src_stride, npy_intp dst_stride,
00114                             npy_intp itemsize);
00115 
00116 /*
00117  * Gives back a transfer function and transfer data pair which copies
00118  * the data from source to dest, truncating it if the data doesn't
00119  * fit, and padding with zero bytes if there's too much space.
00120  *
00121  * For information on the 'aligned', 'src_stride' and 'dst_stride' parameters
00122  * see above.
00123  *
00124  * Returns NPY_SUCCEED or NPY_FAIL
00125  */
00126 NPY_NO_EXPORT int
00127 PyArray_GetStridedZeroPadCopyFn(int aligned,
00128                             npy_intp src_stride, npy_intp dst_stride,
00129                             npy_intp src_itemsize, npy_intp dst_itemsize,
00130                             PyArray_StridedUnaryOp **outstransfer,
00131                             NpyAuxData **outtransferdata);
00132 
00133 /*
00134  * For casts between built-in numeric types,
00135  * this produces a function pointer for casting from src_type_num
00136  * to dst_type_num.  If a conversion is unsupported, returns NULL
00137  * without setting a Python exception.
00138  */
00139 NPY_NO_EXPORT PyArray_StridedUnaryOp *
00140 PyArray_GetStridedNumericCastFn(int aligned,
00141                             npy_intp src_stride, npy_intp dst_stride,
00142                             int src_type_num, int dst_type_num);
00143 
00144 /*
00145  * Gets an operation which copies elements of the given dtype,
00146  * swapping if the dtype isn't in NBO.
00147  *
00148  * Returns NPY_SUCCEED or NPY_FAIL
00149  */
00150 NPY_NO_EXPORT int
00151 PyArray_GetDTypeCopySwapFn(int aligned,
00152                             npy_intp src_stride, npy_intp dst_stride,
00153                             PyArray_Descr *dtype,
00154                             PyArray_StridedUnaryOp **outstransfer,
00155                             NpyAuxData **outtransferdata);
00156 
00157 /*
00158  * If it's possible, gives back a transfer function which casts and/or
00159  * byte swaps data with the dtype 'src_dtype' into data with the dtype
00160  * 'dst_dtype'.  If the outtransferdata is populated with a non-NULL value,
00161  * it must be deallocated with the NPY_AUXDATA_FREE
00162  * function when the transfer function is no longer required.
00163  *
00164  * aligned:
00165  *      Should be 1 if the src and dst pointers are always aligned,
00166  *      0 otherwise.
00167  * src_stride:
00168  *      Should be the src stride if it will always be the same,
00169  *      NPY_MAX_INTP otherwise.
00170  * dst_stride:
00171  *      Should be the dst stride if it will always be the same,
00172  *      NPY_MAX_INTP otherwise.
00173  * src_dtype:
00174  *      The data type of source data.  If this is NULL, a transfer
00175  *      function which sets the destination to zeros is produced.
00176  * dst_dtype:
00177  *      The data type of destination data.  If this is NULL and
00178  *      move_references is 1, a transfer function which decrements
00179  *      source data references is produced.
00180  * move_references:
00181  *      If 0, the destination data gets new reference ownership.
00182  *      If 1, the references from the source data are moved to
00183  *      the destination data.
00184  * out_stransfer:
00185  *      The resulting transfer function is placed here.
00186  * out_transferdata:
00187  *      The auxiliary data for the transfer function is placed here.
00188  *      When finished with the transfer function, the caller must call
00189  *      NPY_AUXDATA_FREE on this data.
00190  * out_needs_api:
00191  *      If this is non-NULL, and the transfer function produced needs
00192  *      to call into the (Python) API, this gets set to 1.  This
00193  *      remains untouched if no API access is required.
00194  *
00195  * WARNING: If you set move_references to 1, it is best that src_stride is
00196  *          never zero when calling the transfer function.  Otherwise, the
00197  *          first destination reference will get the value and all the rest
00198  *          will get NULL.
00199  *
00200  * Returns NPY_SUCCEED or NPY_FAIL.
00201  */
00202 NPY_NO_EXPORT int
00203 PyArray_GetDTypeTransferFunction(int aligned,
00204                             npy_intp src_stride, npy_intp dst_stride,
00205                             PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
00206                             int move_references,
00207                             PyArray_StridedUnaryOp **out_stransfer,
00208                             NpyAuxData **out_transferdata,
00209                             int *out_needs_api);
00210 
00211 /*
00212  * This is identical to PyArray_GetDTypeTransferFunction, but returns a
00213  * transfer function which also takes a mask as a parameter.  The mask is used
00214  * to determine which values to copy, and data is transfered exactly when
00215  * mask[i*mask_stride] is true.
00216  *
00217  * If move_references is true, values which are not copied to the
00218  * destination will still have their source reference decremented.
00219  *
00220  * If mask_dtype is NPY_BOOL or NPY_UINT8, each full element is either
00221  * transferred or not according to the mask as described above. If
00222  * dst_dtype and mask_dtype are both struct dtypes, their names must
00223  * match exactly, and the dtype of each leaf field in mask_dtype must
00224  * be either NPY_BOOL or NPY_UINT8.
00225  */
00226 NPY_NO_EXPORT int
00227 PyArray_GetMaskedDTypeTransferFunction(int aligned,
00228                             npy_intp src_stride,
00229                             npy_intp dst_stride,
00230                             npy_intp mask_stride,
00231                             PyArray_Descr *src_dtype,
00232                             PyArray_Descr *dst_dtype,
00233                             PyArray_Descr *mask_dtype,
00234                             int move_references,
00235                             PyArray_MaskedStridedUnaryOp **out_stransfer,
00236                             NpyAuxData **out_transferdata,
00237                             int *out_needs_api);
00238 
00239 /*
00240  * Casts the specified number of elements from 'src' with data type
00241  * 'src_dtype' to 'dst' with 'dst_dtype'. See
00242  * PyArray_GetDTypeTransferFunction for more details.
00243  *
00244  * Returns NPY_SUCCEED or NPY_FAIL.
00245  */
00246 NPY_NO_EXPORT int
00247 PyArray_CastRawArrays(npy_intp count,
00248                       char *src, char *dst,
00249                       npy_intp src_stride, npy_intp dst_stride,
00250                       PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
00251                       int move_references);
00252 
00253 /*
00254  * These two functions copy or convert the data of an n-dimensional array
00255  * to/from a 1-dimensional strided buffer.  These functions will only call
00256  * 'stransfer' with the provided dst_stride/src_stride and
00257  * dst_strides[0]/src_strides[0], so the caller can use those values to
00258  * specialize the function.
00259  *
00260  * The return value is the number of elements it couldn't copy.  A return value
00261  * of 0 means all elements were copied, a larger value means the end of
00262  * the n-dimensional array was reached before 'count' elements were copied.
00263  *
00264  * ndim:
00265  *      The number of dimensions of the n-dimensional array.
00266  * dst/src/mask:
00267  *      The destination, source or mask starting pointer.
00268  * dst_stride/src_stride/mask_stride:
00269  *      The stride of the 1-dimensional strided buffer
00270  * dst_strides/src_strides:
00271  *      The strides of the n-dimensional array.
00272  * dst_strides_inc/src_strides_inc:
00273  *      How much to add to the ..._strides pointer to get to the next stride.
00274  * coords:
00275  *      The starting coordinates in the n-dimensional array.
00276  * coords_inc:
00277  *      How much to add to the coords pointer to get to the next coordinate.
00278  * shape:
00279  *      The shape of the n-dimensional array.
00280  * shape_inc:
00281  *      How much to add to the shape pointer to get to the next shape entry.
00282  * count:
00283  *      How many elements to transfer
00284  * src_itemsize:
00285  *      How big each element is.  If transfering between elements of different
00286  *      sizes, for example a casting operation, the 'stransfer' function
00287  *      should be specialized for that, in which case 'stransfer' will use
00288  *      this parameter as the source item size.
00289  * stransfer:
00290  *      The strided transfer function.
00291  * transferdata:
00292  *      An auxiliary data pointer passed to the strided transfer function.
00293  *      This follows the conventions of NpyAuxData objects.
00294  */
00295 NPY_NO_EXPORT npy_intp
00296 PyArray_TransferNDimToStrided(npy_intp ndim,
00297                 char *dst, npy_intp dst_stride,
00298                 char *src, npy_intp *src_strides, npy_intp src_strides_inc,
00299                 npy_intp *coords, npy_intp coords_inc,
00300                 npy_intp *shape, npy_intp shape_inc,
00301                 npy_intp count, npy_intp src_itemsize,
00302                 PyArray_StridedUnaryOp *stransfer,
00303                 NpyAuxData *transferdata);
00304 
00305 NPY_NO_EXPORT npy_intp
00306 PyArray_TransferStridedToNDim(npy_intp ndim,
00307                 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc,
00308                 char *src, npy_intp src_stride,
00309                 npy_intp *coords, npy_intp coords_inc,
00310                 npy_intp *shape, npy_intp shape_inc,
00311                 npy_intp count, npy_intp src_itemsize,
00312                 PyArray_StridedUnaryOp *stransfer,
00313                 NpyAuxData *transferdata);
00314 
00315 NPY_NO_EXPORT npy_intp
00316 PyArray_TransferMaskedStridedToNDim(npy_intp ndim,
00317                 char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc,
00318                 char *src, npy_intp src_stride,
00319                 npy_bool *mask, npy_intp mask_stride,
00320                 npy_intp *coords, npy_intp coords_inc,
00321                 npy_intp *shape, npy_intp shape_inc,
00322                 npy_intp count, npy_intp src_itemsize,
00323                 PyArray_MaskedStridedUnaryOp *stransfer,
00324                 NpyAuxData *data);
00325 
00326 /*
00327  * Prepares shape and strides for a simple raw array iteration.
00328  * This sorts the strides into FORTRAN order, reverses any negative
00329  * strides, then coalesces axes where possible. The results are
00330  * filled in the output parameters.
00331  *
00332  * This is intended for simple, lightweight iteration over arrays
00333  * where no buffering of any kind is needed, and the array may
00334  * not be stored as a PyArrayObject.
00335  *
00336  * You can use this together with NPY_RAW_ITER_START and
00337  * NPY_RAW_ITER_ONE_NEXT to handle the looping boilerplate of everything
00338  * but the innermost loop (which is for idim == 0).
00339  *
00340  * Returns 0 on success, -1 on failure.
00341  */
00342 NPY_NO_EXPORT int
00343 PyArray_PrepareOneRawArrayIter(int ndim, npy_intp *shape,
00344                             char *data, npy_intp *strides,
00345                             int *out_ndim, npy_intp *out_shape,
00346                             char **out_data, npy_intp *out_strides);
00347 
00348 /*
00349  * The same as PyArray_PrepareOneRawArrayIter, but for two
00350  * operands instead of one. Any broadcasting of the two operands
00351  * should have already been done before calling this function,
00352  * as the ndim and shape is only specified once for both operands.
00353  *
00354  * Only the strides of the first operand are used to reorder
00355  * the dimensions, no attempt to consider all the strides together
00356  * is made, as is done in the NpyIter object.
00357  *
00358  * You can use this together with NPY_RAW_ITER_START and
00359  * NPY_RAW_ITER_TWO_NEXT to handle the looping boilerplate of everything
00360  * but the innermost loop (which is for idim == 0).
00361  *
00362  * Returns 0 on success, -1 on failure.
00363  */
00364 NPY_NO_EXPORT int
00365 PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp *shape,
00366                             char *dataA, npy_intp *stridesA,
00367                             char *dataB, npy_intp *stridesB,
00368                             int *out_ndim, npy_intp *out_shape,
00369                             char **out_dataA, npy_intp *out_stridesA,
00370                             char **out_dataB, npy_intp *out_stridesB);
00371 
00372 /*
00373  * The same as PyArray_PrepareOneRawArrayIter, but for three
00374  * operands instead of one. Any broadcasting of the three operands
00375  * should have already been done before calling this function,
00376  * as the ndim and shape is only specified once for all operands.
00377  *
00378  * Only the strides of the first operand are used to reorder
00379  * the dimensions, no attempt to consider all the strides together
00380  * is made, as is done in the NpyIter object.
00381  *
00382  * You can use this together with NPY_RAW_ITER_START and
00383  * NPY_RAW_ITER_THREE_NEXT to handle the looping boilerplate of everything
00384  * but the innermost loop (which is for idim == 0).
00385  *
00386  * Returns 0 on success, -1 on failure.
00387  */
00388 NPY_NO_EXPORT int
00389 PyArray_PrepareThreeRawArrayIter(int ndim, npy_intp *shape,
00390                             char *dataA, npy_intp *stridesA,
00391                             char *dataB, npy_intp *stridesB,
00392                             char *dataC, npy_intp *stridesC,
00393                             int *out_ndim, npy_intp *out_shape,
00394                             char **out_dataA, npy_intp *out_stridesA,
00395                             char **out_dataB, npy_intp *out_stridesB,
00396                             char **out_dataC, npy_intp *out_stridesC);
00397 
00398 /* Start raw iteration */
00399 #define NPY_RAW_ITER_START(idim, ndim, coord, shape) \
00400         memset((coord), 0, (ndim) * sizeof(coord[0])); \
00401         do {
00402 
00403 /* Increment to the next n-dimensional coordinate for one raw array */
00404 #define NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord, shape, data, strides) \
00405             for ((idim) = 1; (idim) < (ndim); ++(idim)) { \
00406                 if (++(coord)[idim] == (shape)[idim]) { \
00407                     (coord)[idim] = 0; \
00408                     (data) -= ((shape)[idim] - 1) * (strides)[idim]; \
00409                 } \
00410                 else { \
00411                     (data) += (strides)[idim]; \
00412                     break; \
00413                 } \
00414             } \
00415         } while ((idim) < (ndim))
00416 
00417 /* Increment to the next n-dimensional coordinate for two raw arrays */
00418 #define NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape, \
00419                               dataA, stridesA, dataB, stridesB) \
00420             for ((idim) = 1; (idim) < (ndim); ++(idim)) { \
00421                 if (++(coord)[idim] == (shape)[idim]) { \
00422                     (coord)[idim] = 0; \
00423                     (dataA) -= ((shape)[idim] - 1) * (stridesA)[idim]; \
00424                     (dataB) -= ((shape)[idim] - 1) * (stridesB)[idim]; \
00425                 } \
00426                 else { \
00427                     (dataA) += (stridesA)[idim]; \
00428                     (dataB) += (stridesB)[idim]; \
00429                     break; \
00430                 } \
00431             } \
00432         } while ((idim) < (ndim))
00433 
00434 /* Increment to the next n-dimensional coordinate for three raw arrays */
00435 #define NPY_RAW_ITER_THREE_NEXT(idim, ndim, coord, shape, \
00436                               dataA, stridesA, \
00437                               dataB, stridesB, \
00438                               dataC, stridesC) \
00439             for ((idim) = 1; (idim) < (ndim); ++(idim)) { \
00440                 if (++(coord)[idim] == (shape)[idim]) { \
00441                     (coord)[idim] = 0; \
00442                     (dataA) -= ((shape)[idim] - 1) * (stridesA)[idim]; \
00443                     (dataB) -= ((shape)[idim] - 1) * (stridesB)[idim]; \
00444                     (dataC) -= ((shape)[idim] - 1) * (stridesC)[idim]; \
00445                 } \
00446                 else { \
00447                     (dataA) += (stridesA)[idim]; \
00448                     (dataB) += (stridesB)[idim]; \
00449                     (dataC) += (stridesC)[idim]; \
00450                     break; \
00451                 } \
00452             } \
00453         } while ((idim) < (ndim))
00454 
00455 /* Increment to the next n-dimensional coordinate for four raw arrays */
00456 #define NPY_RAW_ITER_FOUR_NEXT(idim, ndim, coord, shape, \
00457                               dataA, stridesA, \
00458                               dataB, stridesB, \
00459                               dataC, stridesC, \
00460                               dataD, stridesD) \
00461             for ((idim) = 1; (idim) < (ndim); ++(idim)) { \
00462                 if (++(coord)[idim] == (shape)[idim]) { \
00463                     (coord)[idim] = 0; \
00464                     (dataA) -= ((shape)[idim] - 1) * (stridesA)[idim]; \
00465                     (dataB) -= ((shape)[idim] - 1) * (stridesB)[idim]; \
00466                     (dataC) -= ((shape)[idim] - 1) * (stridesC)[idim]; \
00467                     (dataD) -= ((shape)[idim] - 1) * (stridesD)[idim]; \
00468                 } \
00469                 else { \
00470                     (dataA) += (stridesA)[idim]; \
00471                     (dataB) += (stridesB)[idim]; \
00472                     (dataC) += (stridesC)[idim]; \
00473                     (dataD) += (stridesD)[idim]; \
00474                     break; \
00475                 } \
00476             } \
00477         } while ((idim) < (ndim))
00478 
00479 
00480 /*
00481  *            TRIVIAL ITERATION
00482  *
00483  * In some cases when the iteration order isn't important, iteration over
00484  * arrays is trivial.  This is the case when:
00485  *   * The array has 0 or 1 dimensions.
00486  *   * The array is C or Fortran contiguous.
00487  * Use of an iterator can be skipped when this occurs.  These macros assist
00488  * in detecting and taking advantage of the situation.  Note that it may
00489  * be worthwhile to further check if the stride is a contiguous stride
00490  * and take advantage of that.
00491  *
00492  * Here is example code for a single array:
00493  *
00494  *      if (PyArray_TRIVIALLY_ITERABLE(self) {
00495  *          char *data;
00496  *          npy_intp count, stride;
00497  *
00498  *          PyArray_PREPARE_TRIVIAL_ITERATION(self, count, data, stride);
00499  *
00500  *          while (count--) {
00501  *              // Use the data pointer
00502  *
00503  *              data += stride;
00504  *          }
00505  *      }
00506  *      else {
00507  *          // Create iterator, etc...
00508  *      }
00509  *
00510  * Here is example code for a pair of arrays:
00511  *
00512  *      if (PyArray_TRIVIALLY_ITERABLE_PAIR(a1, a2) {
00513  *          char *data1, *data2;
00514  *          npy_intp count, stride1, stride2;
00515  *
00516  *          PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(a1, a2, count,
00517  *                                  data1, data2, stride1, stride2);
00518  *
00519  *          while (count--) {
00520  *              // Use the data1 and data2 pointers
00521  *
00522  *              data1 += stride1;
00523  *              data2 += stride2;
00524  *          }
00525  *      }
00526  *      else {
00527  *          // Create iterator, etc...
00528  *      }
00529  */
00530 
00531 /*
00532  * Note: Equivalently iterable macro requires one of arr1 or arr2 be
00533  *       trivially iterable to be valid.
00534  */
00535 #define PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) ( \
00536                         PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \
00537                         PyArray_CompareLists(PyArray_DIMS(arr1), \
00538                                              PyArray_DIMS(arr2), \
00539                                              PyArray_NDIM(arr1)) && \
00540                         (PyArray_FLAGS(arr1)&(NPY_ARRAY_C_CONTIGUOUS| \
00541                                       NPY_ARRAY_F_CONTIGUOUS)) == \
00542                                 (PyArray_FLAGS(arr2)&(NPY_ARRAY_C_CONTIGUOUS| \
00543                                               NPY_ARRAY_F_CONTIGUOUS)) \
00544                         )
00545 
00546 #define PyArray_TRIVIALLY_ITERABLE(arr) ( \
00547                     PyArray_NDIM(arr) <= 1 || \
00548                     PyArray_CHKFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS) || \
00549                     PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) \
00550                     )
00551 #define PyArray_PREPARE_TRIVIAL_ITERATION(arr, count, data, stride) \
00552                     count = PyArray_SIZE(arr), \
00553                     data = PyArray_BYTES(arr), \
00554                     stride = ((PyArray_NDIM(arr) == 0) ? 0 : \
00555                             (PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) ? \
00556                                             PyArray_STRIDE(arr, 0) : \
00557                                             PyArray_STRIDE(arr, \
00558                                                 PyArray_NDIM(arr)-1)))
00559 
00560 #define PyArray_TRIVIALLY_ITERABLE_PAIR(arr1, arr2) (\
00561                     PyArray_TRIVIALLY_ITERABLE(arr1) && \
00562                         (PyArray_NDIM(arr2) == 0 || \
00563                          PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) || \
00564                          (PyArray_NDIM(arr1) == 0 && \
00565                              PyArray_TRIVIALLY_ITERABLE(arr2) \
00566                          ) \
00567                         ) \
00568                     )
00569 #define PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(arr1, arr2, \
00570                                         count, \
00571                                         data1, data2, \
00572                                         stride1, stride2) { \
00573                     npy_intp size1 = PyArray_SIZE(arr1); \
00574                     npy_intp size2 = PyArray_SIZE(arr2); \
00575                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00576                     data1 = PyArray_BYTES(arr1); \
00577                     data2 = PyArray_BYTES(arr2); \
00578                     stride1 = (size1 == 1 ? 0 : \
00579                             (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \
00580                                             PyArray_STRIDE(arr1, 0) : \
00581                                             PyArray_STRIDE(arr1, \
00582                                                 PyArray_NDIM(arr1)-1))); \
00583                     stride2 = (size2 == 1 ? 0 : \
00584                             (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \
00585                                             PyArray_STRIDE(arr2, 0) : \
00586                                             PyArray_STRIDE(arr2, \
00587                                                 PyArray_NDIM(arr2)-1))); \
00588                 }
00589 
00590 #define PyArray_TRIVIALLY_ITERABLE_TRIPLE(arr1, arr2, arr3) (\
00591                 PyArray_TRIVIALLY_ITERABLE(arr1) && \
00592                     ((PyArray_NDIM(arr2) == 0 && \
00593                         (PyArray_NDIM(arr3) == 0 || \
00594                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00595                         ) \
00596                      ) || \
00597                      (PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2) && \
00598                         (PyArray_NDIM(arr3) == 0 || \
00599                             PyArray_EQUIVALENTLY_ITERABLE(arr1, arr3) \
00600                         ) \
00601                      ) || \
00602                      (PyArray_NDIM(arr1) == 0 && \
00603                         PyArray_TRIVIALLY_ITERABLE(arr2) && \
00604                             (PyArray_NDIM(arr3) == 0 || \
00605                                 PyArray_EQUIVALENTLY_ITERABLE(arr2, arr3) \
00606                             ) \
00607                      ) \
00608                     ) \
00609                 )
00610 
00611 #define PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(arr1, arr2, arr3, \
00612                                         count, \
00613                                         data1, data2, data3, \
00614                                         stride1, stride2, stride3) { \
00615                     npy_intp size1 = PyArray_SIZE(arr1); \
00616                     npy_intp size2 = PyArray_SIZE(arr2); \
00617                     npy_intp size3 = PyArray_SIZE(arr3); \
00618                     count = ((size1 > size2) || size1 == 0) ? size1 : size2; \
00619                     count = ((size3 > count) || size3 == 0) ? size3 : count; \
00620                     data1 = PyArray_BYTES(arr1); \
00621                     data2 = PyArray_BYTES(arr2); \
00622                     data3 = PyArray_BYTES(arr3); \
00623                     stride1 = (size1 == 1 ? 0 : \
00624                             (PyArray_CHKFLAGS(arr1, NPY_ARRAY_F_CONTIGUOUS) ? \
00625                                             PyArray_STRIDE(arr1, 0) : \
00626                                             PyArray_STRIDE(arr1, \
00627                                                 PyArray_NDIM(arr1)-1))); \
00628                     stride2 = (size2 == 1 ? 0 : \
00629                             (PyArray_CHKFLAGS(arr2, NPY_ARRAY_F_CONTIGUOUS) ? \
00630                                             PyArray_STRIDE(arr2, 0) : \
00631                                             PyArray_STRIDE(arr2, \
00632                                                 PyArray_NDIM(arr2)-1))); \
00633                     stride3 = (size3 == 1 ? 0 : \
00634                             (PyArray_CHKFLAGS(arr3, NPY_ARRAY_F_CONTIGUOUS) ? \
00635                                             PyArray_STRIDE(arr3, 0) : \
00636                                             PyArray_STRIDE(arr3, \
00637                                                 PyArray_NDIM(arr3)-1))); \
00638                 }
00639 
00640 #endif