numpy 2.0.0
src/multiarray/nditer_api.c File Reference
#include "nditer_impl.h"

Defines

#define NPY_ITERATOR_IMPLEMENTATION_CODE

Functions

static npy_intp npyiter_checkreducesize (NpyIter *iter, npy_intp count, npy_intp *reduce_innersize, npy_intp *reduce_outerdim)
NPY_NO_EXPORT int NpyIter_RemoveAxis (NpyIter *iter, int axis)
NPY_NO_EXPORT int NpyIter_RemoveMultiIndex (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_EnableExternalLoop (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_Reset (NpyIter *iter, char **errmsg)
NPY_NO_EXPORT int NpyIter_ResetBasePointers (NpyIter *iter, char **baseptrs, char **errmsg)
NPY_NO_EXPORT int NpyIter_ResetToIterIndexRange (NpyIter *iter, npy_intp istart, npy_intp iend, char **errmsg)
NPY_NO_EXPORT int NpyIter_GotoMultiIndex (NpyIter *iter, npy_intp *multi_index)
NPY_NO_EXPORT int NpyIter_GotoIndex (NpyIter *iter, npy_intp flat_index)
NPY_NO_EXPORT int NpyIter_GotoIterIndex (NpyIter *iter, npy_intp iterindex)
NPY_NO_EXPORT npy_intp NpyIter_GetIterIndex (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasDelayedBufAlloc (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasExternalLoop (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasMultiIndex (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_HasIndex (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_RequiresBuffering (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IterationNeedsAPI (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_GetNDim (NpyIter *iter)
NPY_NO_EXPORT int NpyIter_GetNOp (NpyIter *iter)
NPY_NO_EXPORT npy_intp NpyIter_GetIterSize (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IsBuffered (NpyIter *iter)
NPY_NO_EXPORT npy_bool NpyIter_IsGrowInner (NpyIter *iter)
NPY_NO_EXPORT npy_intp NpyIter_GetBufferSize (NpyIter *iter)
NPY_NO_EXPORT void NpyIter_GetIterIndexRange (NpyIter *iter, npy_intp *istart, npy_intp *iend)
NPY_NO_EXPORT int NpyIter_GetShape (NpyIter *iter, npy_intp *outshape)
NPY_NO_EXPORT int NpyIter_CreateCompatibleStrides (NpyIter *iter, npy_intp itemsize, npy_intp *outstrides)
NPY_NO_EXPORT char ** NpyIter_GetDataPtrArray (NpyIter *iter)
NPY_NO_EXPORT char ** NpyIter_GetInitialDataPtrArray (NpyIter *iter)
NPY_NO_EXPORT PyArray_Descr ** NpyIter_GetDescrArray (NpyIter *iter)
NPY_NO_EXPORT PyArrayObject ** NpyIter_GetOperandArray (NpyIter *iter)
NPY_NO_EXPORT PyArrayObjectNpyIter_GetIterView (NpyIter *iter, npy_intp i)
NPY_NO_EXPORT npy_intpNpyIter_GetIndexPtr (NpyIter *iter)
NPY_NO_EXPORT void NpyIter_GetReadFlags (NpyIter *iter, char *outreadflags)
NPY_NO_EXPORT void NpyIter_GetWriteFlags (NpyIter *iter, char *outwriteflags)
NPY_NO_EXPORT npy_intpNpyIter_GetInnerStrideArray (NpyIter *iter)
NPY_NO_EXPORT npy_intpNpyIter_GetAxisStrideArray (NpyIter *iter, int axis)
NPY_NO_EXPORT void NpyIter_GetInnerFixedStrideArray (NpyIter *iter, npy_intp *out_strides)
NPY_NO_EXPORT npy_intpNpyIter_GetInnerLoopSizePtr (NpyIter *iter)
NPY_NO_EXPORT void NpyIter_DebugPrint (NpyIter *iter)
NPY_NO_EXPORT void npyiter_coalesce_axes (NpyIter *iter)
NPY_NO_EXPORT int npyiter_allocate_buffers (NpyIter *iter, char **errmsg)
NPY_NO_EXPORT void npyiter_goto_iterindex (NpyIter *iter, npy_intp iterindex)
NPY_NO_EXPORT void npyiter_copy_from_buffers (NpyIter *iter)
NPY_NO_EXPORT void npyiter_copy_to_buffers (NpyIter *iter, char **prev_dataptrs)

Define Documentation

#define NPY_ITERATOR_IMPLEMENTATION_CODE
Indicate that this .c file is allowed to include the header

Function Documentation

NPY_NO_EXPORT int npyiter_allocate_buffers ( NpyIter iter,
char **  errmsg 
)
If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);
If we have determined that a buffer may be needed, allocate one.

Referenced by NpyIter_EnableExternalLoop().

static npy_intp npyiter_checkreducesize ( NpyIter iter,
npy_intp  count,
npy_intp reduce_innersize,
npy_intp reduce_outerdim 
) [static]
Internal helper functions private to this file
This checks how much space can be buffered without encountering the same value twice, or for operands whose innermost stride is zero, without encountering a different value. By reducing the buffered amount to this size, reductions can be safely buffered.
Reductions are buffered with two levels of looping, to avoid frequent copying to the buffers. The return value is the over-all buffer size, and when the flag NPY_ITFLAG_REDUCE is set, reduce_innersize receives the size of the inner of the two levels of looping.
The value placed in reduce_outerdim is the index into the AXISDATA for where the second level of the double loop begins.
The return value is always a multiple of the value placed in reduce_innersize.

Default to no outer axis
If there's only one dimension, no need to calculate anything
Indicate which REDUCE operands have stride 0 in the inner loop
Go forward through axisdata, calculating the space available
If a reduce stride switched from zero to non-zero, or vice versa, that's the point where the data will stop being the same element or will repeat, and if the buffer starts with an all zero multi-index up to this point, gives us the reduce_innersize.
If we already found more elements than count, or the starting coordinate wasn't zero, the two-level looping is unnecessary/can't be done, so return.
If we broke out of the loop early, we found reduce_innersize
If there was any non-zero coordinate, the reduction inner loop doesn't fit in the buffersize, or the reduction inner loop covered the entire iteration size, can't do the double loop.
In this case, we can't reuse the reduce loops
In this case, we can reuse the reduce loops
Continue through the rest of the dimensions. If there are two separated reduction axes, we may have to cut the buffer short again.
Indicate which REDUCE operands have stride 0 at the current level
If a reduce stride switched from zero to non-zero, or vice versa, that's the point where the data will stop being the same element or will repeat, and if the buffer starts with an all zero multi-index up to this point, gives us the reduce_innersize.
This terminates the outer level of our double loop.

NPY_NO_EXPORT void npyiter_coalesce_axes ( NpyIter iter)
Internal helper functions shared between implementation files

The HASMULTIINDEX or IDENTPERM flags do not apply after coalescing
Check that all the axes can be coalesced
If the number of axes shrunk, reset the perm and compress the data into the new layout.
Reset to an identity perm

References NAD_INDEX, NAD_SHAPE, NAD_STRIDES, NBF_REDUCE_OUTERSIZE, NBF_SIZE, NPY_IT_DBG_PRINT1, NPY_IT_DBG_PRINT2, NPY_OP_ITFLAG_WRITEMASKED, and PyArray_TransferMaskedStridedToNDim().

NPY_NO_EXPORT void npyiter_copy_from_buffers ( NpyIter iter)
This gets called after the the buffers have been exhausted, and their data needs to be written back to the arrays. The multi-index must be positioned for the beginning of the buffer.

If we're past the end, nothing to copy
Copy the data back to the arrays. If the type has refs, this function moves them so the buffer's refs are released.
Copy back only if the pointer was pointing to the buffer
If this operand is being reduced in the inner loop, its buffering stride was set to zero, and just one element was copied.
The mask pointer may be in the buffer or in the array, detect which one.
If there's no copy back, we may have to decrement refs. In this case, the transfer function has a 'decsrcref' transfer function, so we can use it to do the decrement.
Decrement refs only if the pointer was pointing to the buffer
Decrement refs
Zero out the memory for safety. For instance, if during iteration some Python code copied an array pointing into the buffer, it will get None values for its references after this.

Referenced by NpyIter_EnableExternalLoop(), and NpyIter_GotoIndex().

NPY_NO_EXPORT void npyiter_copy_to_buffers ( NpyIter iter,
char **  prev_dataptrs 
)
This gets called after the iterator has been positioned to a multi-index for the start of a buffer. It decides which operands need a buffer, and copies the data into the buffers.

Have to get this flag before npyiter_checkreducesize sets it for the next iteration.
Calculate the size if using any buffers
If last time around, the reduce loop structure was full, we reuse it
Try to do make the outersize as big as possible. This allows it to shrink when processing the last bit of the outer reduce loop, then grow again at the beginnning of the next outer reduce loop.
If the full transfer size doesn't fit in the buffer, truncate it
If there are any reduction operands, we may have to make the size smaller so we don't copy the same value into a buffer twice, as the buffering does not have a mechanism to combine values itself.
Calculate the maximum size if using a single stride and no buffers
If the buffer is write-only, these two are NULL, and the buffer pointers will be set up but the read copy won't be done
Never need to buffer this operand
Should not adjust the stride - ad_strides[iop] could be zero, but strides[iop] was initialized to the first non-trivial stride.
Never need to buffer this operand
Should not adjust the stride - ad_strides[iop] could be zero, but strides[iop] was initialized to the first non-trivial stride.
Just a copy
No copyswap or cast was requested, so all we're doing is copying the data to fill the buffer and produce a single stride. If the underlying data already does that, no need to copy it.
If some other op is reduced, we have a double reduce loop
In this case, the buffer is being used
Just a copy, but with a reduction
It's all in one stride in the inner loop dimension
It's all in one stride in the reduce outer loop
Outer reduce loop advances by one item
In this case, the buffer is being used
Both outer and inner reduce loops have stride 0
Outer reduce loop advances by one item
It's all in one stride in the reduce outer loop
Outer reduce loop advances by one item
In this case, the buffer is being used
Reduction in outer reduce loop
Advance to next items in outer reduce loop
In this case, the buffer is always being used
The buffer is being used with reduction
Both outer and inner reduce loops have stride 0
Outer reduce loop advances by one item
Reduction in outer reduce loop
Advance to next items in outer reduce loop
If stransfer wasn't set to NULL, buffering is required
If this operand is being reduced in the inner loop, set its buffering stride to zero, and just copy one element.
When we're reducing a single element, and it's still the same element, don't overwrite it even when reuse reduce loops is unset. This preserves the precision of the intermediate calculation.
If the whole buffered loop structure remains the same, and the source pointer for this data didn't change, we don't have to copy the data again.
If the data type requires zero-inititialization
Can't skip the transfer in this case
If the data type requires zero-inititialization
If buffering wasn't needed, we can grow the inner loop to as large as possible.
TODO: Could grow REDUCE loop too with some more logic above.

References _PyArray_Descr::elsize, and NPY_IT_DBG_PRINT1.

Referenced by NpyIter_EnableExternalLoop(), NpyIter_GotoIndex(), and NpyIter_New().

NPY_NO_EXPORT int NpyIter_CreateCompatibleStrides ( NpyIter iter,
npy_intp  itemsize,
npy_intp outstrides 
)
Builds a set of strides which are the same as the strides of an

output array created using the NPY_ITER_ALLOCATE flag, where NULL was passed for op_axes. This is for data packed contiguously, but not necessarily in C or Fortran order. This should be used together with NpyIter_GetShape and NpyIter_GetNDim.

A use case for this function is to match the shape and layout of the iterator and tack on one or more dimensions. For example, in order to generate a vector per input value for a numerical gradient, you pass in ndim*itemsize for itemsize, then add another dimension to the end with size ndim and stride itemsize. To do the Hessian matrix, you do the same thing but add two dimensions, or take advantage of the symmetry and pack it into 1 dimension with a particular encoding.

This function may only be called if the iterator is tracking a multi-index and if NPY_ITER_DONT_NEGATE_STRIDES was used to prevent an axis from being iterated in reverse order.

If an array is created with this method, simply adding 'itemsize' for each iteration will traverse the new array matching the iterator.

Returns NPY_SUCCEED or NPY_FAIL.

NPY_NO_EXPORT void NpyIter_DebugPrint ( NpyIter iter)
For debugging

Print the fixed strides when there's no inner loop

NPY_NO_EXPORT int NpyIter_EnableExternalLoop ( NpyIter iter)
Removes the inner loop handling (so HasExternalLoop returns true)

int ndim = NIT_NDIM(iter);
Check conditions under which this can be done
Set the flag
Check whether we can apply the single iteration optimization to the iternext function.
Reset the iterator

References NBF_BUFITEREND, NBF_SIZE, NIT_BUFFERDATA, NIT_ITEREND, NIT_ITERINDEX, NIT_ITERSTART, NIT_ITFLAGS, NIT_NOP, NPY_FAIL, NPY_ITFLAG_DELAYBUF, NPY_SUCCEED, npyiter_allocate_buffers(), npyiter_copy_from_buffers(), npyiter_copy_to_buffers(), and npyiter_goto_iterindex().

NPY_NO_EXPORT npy_intp* NpyIter_GetAxisStrideArray ( NpyIter iter,
int  axis 
)
Gets the array of strides for the specified axis.

If the iterator is tracking a multi-index, gets the strides for the axis specified, otherwise gets the strides for the iteration axis as Fortran order (fastest-changing axis first).

Returns NULL if an error occurs.

Reverse axis, since the iterator treats them that way
First find the axis in question

References NIT_BASEOFFSETS.

NPY_NO_EXPORT npy_intp NpyIter_GetBufferSize ( NpyIter iter)
Gets the size of the buffer, or 0 if buffering is not enabled

int ndim = NIT_NDIM(iter);

References NBF_PTRS, and NIT_BUFFERDATA.

NPY_NO_EXPORT char** NpyIter_GetDataPtrArray ( NpyIter iter)
Get the array of data pointers (1 per object being iterated) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

References NIT_NOP, NIT_OPITFLAGS, and NPY_OP_ITFLAG_WRITE.

Referenced by apply_business_day_count(), and business_day_offset().

NPY_NO_EXPORT PyArray_Descr** NpyIter_GetDescrArray ( NpyIter iter)
Get the array of data type pointers (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);
int nop = NIT_NOP(iter);

NPY_NO_EXPORT npy_intp* NpyIter_GetIndexPtr ( NpyIter iter)
Get a pointer to the index, if it is being tracked

int ndim = NIT_NDIM(iter);
The index is just after the data pointers

References NAD_SHAPE, NBF_SIZE, NIT_AXISDATA, NIT_BUFFERDATA, NIT_ITFLAGS, and NIT_NOP.

NPY_NO_EXPORT char** NpyIter_GetInitialDataPtrArray ( NpyIter iter)
Get the array of data pointers (1 per object being iterated),

directly into the arrays (never pointing to a buffer), for starting unbuffered iteration. This always returns the addresses for the iterator position as reset to iterator index 0.

These pointers are different from the pointers accepted by NpyIter_ResetBasePointers, because the direction along some axes may have been reversed, requiring base offsets.

This function may be safely called without holding the Python GIL.

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT void NpyIter_GetInnerFixedStrideArray ( NpyIter iter,
npy_intp out_strides 
)
Get an array of strides which are fixed. Any strides which may

change during iteration receive the value NPY_MAX_INTP. Once the iterator is ready to iterate, call this to get the strides which will always be fixed in the inner loop, then choose optimized inner loop functions which take advantage of those fixed strides.

This function may be safely called without holding the Python GIL.

Operands which are always/never buffered have fixed strides, and everything has fixed strides when ndim is 0 or 1
If it's a reduction, 0-stride inner loop may have fixed stride
If it's a reduction operand, definitely fixed stride
Otherwise it's a fixed stride if the stride is 0 for all inner dimensions of the reduction double loop
If all the strides were 0, the stride won't change
Inner loop contiguous array means its stride won't change when switching between buffering and not buffering
Otherwise the strides can change if the operand is sometimes buffered, sometimes not.
If there's no buffering, the strides are always fixed

NPY_NO_EXPORT npy_intp* NpyIter_GetInnerLoopSizePtr ( NpyIter iter)
Get a pointer to the size of the inner loop (when HasExternalLoop is true) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

Referenced by apply_business_day_count(), and business_day_offset().

NPY_NO_EXPORT npy_intp* NpyIter_GetInnerStrideArray ( NpyIter iter)
Get the array of strides for the inner loop (when HasExternalLoop is true) <blockquote> This function may be safely called without holding the Python GIL.</blockquote>

int ndim = NIT_NDIM(iter);

Referenced by apply_business_day_count(), and business_day_offset().

NPY_NO_EXPORT npy_intp NpyIter_GetIterIndex ( NpyIter iter)
Gets the current iteration index

iterindex is only used if NPY_ITER_RANGED or NPY_ITER_BUFFERED was set

References NIT_ITFLAGS.

NPY_NO_EXPORT void NpyIter_GetIterIndexRange ( NpyIter iter,
npy_intp istart,
npy_intp iend 
)
Gets the range of iteration indices being iterated

References NIT_NOP, and NIT_RESETDATAPTR.

NPY_NO_EXPORT npy_intp NpyIter_GetIterSize ( NpyIter iter)
Gets the number of elements being iterated

References NPY_FAIL.

Referenced by apply_business_day_count(), and business_day_offset().

NPY_NO_EXPORT PyArrayObject* NpyIter_GetIterView ( NpyIter iter,
npy_intp  i 
)
Returns a view to the i-th object with the iterator's internal axes

Don't provide views if buffering is enabled
Retrieve the shape and strides from the axisdata
Tell the view who owns the data
Make sure all the flags are good

Referenced by npyiter_remove_axis().

NPY_NO_EXPORT int NpyIter_GetNDim ( NpyIter iter)
Gets the number of dimensions being iterated
NPY_NO_EXPORT int NpyIter_GetNOp ( NpyIter iter)
Gets the number of operands being iterated

Referenced by npyiter_iterrange_set(), and npyiter_remove_axis().

NPY_NO_EXPORT PyArrayObject** NpyIter_GetOperandArray ( NpyIter iter)
Get the array of objects being iterated

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

Referenced by apply_business_day_count(), and business_day_offset().

NPY_NO_EXPORT void NpyIter_GetReadFlags ( NpyIter iter,
char *  outreadflags 
)
Gets an array of read flags (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT int NpyIter_GetShape ( NpyIter iter,
npy_intp outshape 
)
Gets the broadcast shape if a multi-index is being tracked by the iterator,

otherwise gets the shape of the iteration as Fortran-order (fastest-changing index first).

The reason Fortran-order is returned when a multi-index is not enabled is that this is providing a direct view into how the iterator traverses the n-dimensional space. The iterator organizes its memory from fastest index to slowest index, and when a multi-index is enabled, it uses a permutation to recover the original order.

Returns NPY_SUCCEED or NPY_FAIL.

References NIT_NOP, and NIT_OPERANDS.

NPY_NO_EXPORT void NpyIter_GetWriteFlags ( NpyIter iter,
char *  outwriteflags 
)
Gets an array of write flags (1 per object being iterated)

npy_uint32 itflags = NIT_ITFLAGS(iter);
int ndim = NIT_NDIM(iter);

NPY_NO_EXPORT void npyiter_goto_iterindex ( NpyIter iter,
npy_intp  iterindex 
)
This sets the AXISDATA portion of the iterator to the specified iterindex, updating the pointers as well. This function does no error checking.

Set the multi-index, from the fastest-changing to the slowest-changing.
Accumulate the successive pointers with their offsets in the opposite order, starting from the original data pointers.

Referenced by NpyIter_EnableExternalLoop(), NpyIter_GotoIndex(), and NpyIter_New().

NPY_NO_EXPORT int NpyIter_GotoIndex ( NpyIter iter,
npy_intp  flat_index 
)
If the iterator is tracking an index, sets the iterator

to the specified index.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

Compute the iterindex corresponding to the flat_index
Extract the index from the flat_index
Add its contribution to iterindex

References NBF_BUFITEREND, NBF_PTRS, NBF_SIZE, NBF_STRIDES, NIT_BUFFERDATA, NIT_ITERINDEX, NPY_ITFLAG_REDUCE, npyiter_copy_from_buffers(), npyiter_copy_to_buffers(), and npyiter_goto_iterindex().

NPY_NO_EXPORT int NpyIter_GotoIterIndex ( NpyIter iter,
npy_intp  iterindex 
)
Sets the iterator position to the specified iterindex,

which matches the iteration order of the iterator.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

int ndim = NIT_NDIM(iter);
Check if the new iterindex is already within the buffer
Start the buffer at the provided iterindex
Write back to the arrays
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_GotoMultiIndex ( NpyIter iter,
npy_intp multi_index 
)
Sets the iterator to the specified multi-index, which must have the

correct number of entries for 'ndim'. It is only valid when NPY_ITER_MULTI_INDEX was passed to the constructor. This operation fails if the multi-index is out of bounds.

Returns NPY_SUCCEED on success, NPY_FAIL on failure.

Compute the iterindex corresponding to the multi-index
If the perm entry is negative, reverse the index
Bounds-check this index

References NPY_FAIL.

NPY_NO_EXPORT npy_bool NpyIter_HasDelayedBufAlloc ( NpyIter iter)
Whether the buffer allocation is being delayed

References NIT_ITEREND, and NIT_ITERSTART.

Referenced by npyiter_iterrange_set().

NPY_NO_EXPORT npy_bool NpyIter_HasExternalLoop ( NpyIter iter)
Whether the iterator handles the inner loop

Referenced by npyiter_iterrange_set().

NPY_NO_EXPORT npy_bool NpyIter_HasIndex ( NpyIter iter)
Whether the iterator is tracking an index
NPY_NO_EXPORT npy_bool NpyIter_HasMultiIndex ( NpyIter iter)
Whether the iterator is tracking a multi-index

References NIT_ITFLAGS, NIT_NDIM, and NIT_NOP.

Referenced by npyiter_index_set().

NPY_NO_EXPORT npy_bool NpyIter_IsBuffered ( NpyIter iter)
Whether the iterator is buffered
NPY_NO_EXPORT npy_bool NpyIter_IsGrowInner ( NpyIter iter)
Whether the inner loop can grow if buffering is unneeded

References NIT_ITFLAGS, and NIT_NOP.

NPY_NO_EXPORT npy_bool NpyIter_IterationNeedsAPI ( NpyIter iter)
Whether the iteration loop, and in particular the iternext()
function, needs API access. If this is true, the GIL must be retained while iterating.
NPY_NO_EXPORT int NpyIter_RemoveAxis ( NpyIter iter,
int  axis 
)
Removes an axis from iteration. This requires that NPY_ITER_MULTI_INDEX

was set for iterator creation, and does not work if buffering is enabled. This function also resets the iterator to its initial state.

Returns NPY_SUCCEED or NPY_FAIL.

Reverse axis, since the iterator treats them that way
First find the axis in question
If this is it, and it's iterated forward, done
If this is it, but it's iterated backward, must reverse the axis
Adjust baseoffsets and resetbaseptr back to the start of this axis.
Adjust the permutation
Adjust the iteration size
Shift all the axisdata structures by one
If there is more than one dimension, shrink the iterator
Otherwise convert it to a singleton dimension

NPY_NO_EXPORT int NpyIter_RemoveMultiIndex ( NpyIter iter)
Removes multi-index support from an iterator. <blockquote> Returns NPY_SUCCEED or NPY_FAIL.</blockquote>

Make sure the iterator is reset

NPY_NO_EXPORT npy_bool NpyIter_RequiresBuffering ( NpyIter iter)
Whether the iteration could be done with no buffering.

int ndim = NIT_NDIM(iter);
If any operand requires a cast, buffering is mandatory

References NAD_SHAPE.

NPY_NO_EXPORT int NpyIter_Reset ( NpyIter iter,
char **  errmsg 
)
Resets the iterator to its initial state <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
If buffer allocation was delayed, do it now
If the iterindex is already right, no need to do anything
Copy any data from the buffers back to the arrays
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_ResetBasePointers ( NpyIter iter,
char **  baseptrs,
char **  errmsg 
)
Resets the iterator to its initial state, with new base data pointers <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
If buffer allocation was delayed, do it now
Copy any data from the buffers back to the arrays
The new data pointers for resetting
Prepare the next buffers and set iterend/size

NPY_NO_EXPORT int NpyIter_ResetToIterIndexRange ( NpyIter iter,
npy_intp  istart,
npy_intp  iend,
char **  errmsg 
)
Resets the iterator to a new iterator index range <blockquote> If errmsg is non-NULL, it should point to a variable which will receive the error message, and no Python exception will be set. This is so that the function can be called from code not holding the GIL.</blockquote>

int ndim = NIT_NDIM(iter);
int nop = NIT_NOP(iter);