text: /*
   * This file implements assignment from an ndarray to another ndarray.
   *
   * Written by Mark Wiebe (mwwiebe@gmail.com)
   * Copyright (c) 2011 by Enthought, Inc.
   *
   * See LICENSE.txt for the license.
   */
  
  #define PY_SSIZE_T_CLEAN
  #include <Python.h>
  
  #define NPY_NO_DEPRECATED_API NPY_API_VERSION
  #define _MULTIARRAYMODULE
  #include <numpy/ndarraytypes.h>
  
  #include "npy_config.h"
  #include "npy_pycompat.h"
  
  #include "convert_datatype.h"
  #include "methods.h"
  #include "shape.h"
  #include "lowlevel_strided_loops.h"
  
  #include "array_assign.h"
  
  /*
   * Assigns the array from 'src' to 'dst'. The strides must already have
   * been broadcast.
   *
   * Returns 0 on success, -1 on failure.
   */
  NPY_NO_EXPORT int
  raw_array_assign_array(int ndim, npy_intp *shape,
          PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides,
          PyArray_Descr *src_dtype, char *src_data, npy_intp *src_strides)
  {
      int idim;
      npy_intp shape_it[NPY_MAXDIMS];
      npy_intp dst_strides_it[NPY_MAXDIMS];
      npy_intp src_strides_it[NPY_MAXDIMS];
      npy_intp coord[NPY_MAXDIMS];
  
      PyArray_StridedUnaryOp *stransfer = NULL;
      NpyAuxData *transferdata = NULL;
      int aligned, needs_api = 0;
      npy_intp src_itemsize = src_dtype->elsize;
  
      NPY_BEGIN_THREADS_DEF;
  
      /* Check alignment */
      aligned = raw_array_is_aligned(ndim,
                          dst_data, dst_strides, dst_dtype->alignment) &&
                raw_array_is_aligned(ndim,
                          src_data, src_strides, src_dtype->alignment);
  
      /* Use raw iteration with no heap allocation */
      if (PyArray_PrepareTwoRawArrayIter(
                      ndim, shape,
                      dst_data, dst_strides,
                      src_data, src_strides,
                      &ndim, shape_it,
                      &dst_data, dst_strides_it,
                      &src_data, src_strides_it) < 0) {
          return -1;
      }
  
      /*
       * Overlap check for the 1D case. Higher dimensional arrays and
       * opposite strides cause a temporary copy before getting here.
       */
      if (ndim == 1 && src_data < dst_data &&
                  src_data + shape_it[0] * src_strides_it[0] > dst_data) {
          src_data += (shape_it[0] - 1) * src_strides_it[0];
          dst_data += (shape_it[0] - 1) * dst_strides_it[0];
          src_strides_it[0] = -src_strides_it[0];
          dst_strides_it[0] = -dst_strides_it[0];
      }
  
      /* Get the function to do the casting */
      if (PyArray_GetDTypeTransferFunction(aligned,
                          src_strides_it[0], dst_strides_it[0],
                          src_dtype, dst_dtype,
                          0,
                          &stransfer, &transferdata,
                          &needs_api) != NPY_SUCCEED) {
          return -1;
      }
  
      if (!needs_api) {
          NPY_BEGIN_THREADS;
      }
  
      NPY_RAW_ITER_START(idim, ndim, coord, shape_it) {
          /* Process the innermost dimension */
          stransfer(dst_data, dst_strides_it[0], src_data, src_strides_it[0],
                      shape_it[0], src_itemsize, transferdata);
      } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it,
                              dst_data, dst_strides_it,
                              src_data, src_strides_it);
  
      NPY_END_THREADS;
  
      NPY_AUXDATA_FREE(transferdata);
  
      return (needs_api && PyErr_Occurred()) ? -1 : 0;
  }
  
  /*
   * Assigns the array from 'src' to 'dst, wherever the 'wheremask'
   * value is True. The strides must already have been broadcast.
   *
   * Returns 0 on success, -1 on failure.
   */
  NPY_NO_EXPORT int
  raw_array_wheremasked_assign_array(int ndim, npy_intp *shape,
          PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides,
          PyArray_Descr *src_dtype, char *src_data, npy_intp *src_strides,
          PyArray_Descr *wheremask_dtype, char *wheremask_data,
          npy_intp *wheremask_strides)
  {
      int idim;
      npy_intp shape_it[NPY_MAXDIMS];
      npy_intp dst_strides_it[NPY_MAXDIMS];
      npy_intp src_strides_it[NPY_MAXDIMS];
      npy_intp wheremask_strides_it[NPY_MAXDIMS];
      npy_intp coord[NPY_MAXDIMS];
  
      PyArray_MaskedStridedUnaryOp *stransfer = NULL;
      NpyAuxData *transferdata = NULL;
      int aligned, needs_api = 0;
      npy_intp src_itemsize = src_dtype->elsize;
  
      NPY_BEGIN_THREADS_DEF;
  
      /* Check alignment */
      aligned = raw_array_is_aligned(ndim,
                          dst_data, dst_strides, dst_dtype->alignment) &&
                raw_array_is_aligned(ndim,
                          src_data, src_strides, src_dtype->alignment);
  
      /* Use raw iteration with no heap allocation */
      if (PyArray_PrepareThreeRawArrayIter(
                      ndim, shape,
                      dst_data, dst_strides,
                      src_data, src_strides,
                      wheremask_data, wheremask_strides,
                      &ndim, shape_it,
                      &dst_data, dst_strides_it,
                      &src_data, src_strides_it,
                      &wheremask_data, wheremask_strides_it) < 0) {
          return -1;
      }
  
      /*
       * Overlap check for the 1D case. Higher dimensional arrays cause
       * a temporary copy before getting here.
       */
      if (ndim == 1 && src_data < dst_data &&
                  src_data + shape_it[0] * src_strides_it[0] > dst_data) {
          src_data += (shape_it[0] - 1) * src_strides_it[0];
          dst_data += (shape_it[0] - 1) * dst_strides_it[0];
          wheremask_data += (shape_it[0] - 1) * wheremask_strides_it[0];
          src_strides_it[0] = -src_strides_it[0];
          dst_strides_it[0] = -dst_strides_it[0];
          wheremask_strides_it[0] = -wheremask_strides_it[0];
      }
  
      /* Get the function to do the casting */
      if (PyArray_GetMaskedDTypeTransferFunction(aligned,
                          src_strides_it[0],
                          dst_strides_it[0],
                          wheremask_strides_it[0],
                          src_dtype, dst_dtype, wheremask_dtype,
                          0,
                          &stransfer, &transferdata,
                          &needs_api) != NPY_SUCCEED) {
          return -1;
      }
  
      if (!needs_api) {
          NPY_BEGIN_THREADS;
      }
  
      NPY_RAW_ITER_START(idim, ndim, coord, shape_it) {
          /* Process the innermost dimension */
          stransfer(dst_data, dst_strides_it[0], src_data, src_strides_it[0],
                      (npy_bool *)wheremask_data, wheremask_strides_it[0],
                      shape_it[0], src_itemsize, transferdata);
      } NPY_RAW_ITER_THREE_NEXT(idim, ndim, coord, shape_it,
                              dst_data, dst_strides_it,
                              src_data, src_strides_it,
                              wheremask_data, wheremask_strides_it);
  
      NPY_END_THREADS;
  
      NPY_AUXDATA_FREE(transferdata);
  
      return (needs_api && PyErr_Occurred()) ? -1 : 0;
  }
  
  /*
   * An array assignment function for copying arrays, broadcasting 'src' into
   * 'dst'. This function makes a temporary copy of 'src' if 'src' and
   * 'dst' overlap, to be able to handle views of the same data with
   * different strides.
   *
   * dst: The destination array.
   * src: The source array.
   * wheremask: If non-NULL, a boolean mask specifying where to copy.
   * casting: An exception is raised if the copy violates this
   *          casting rule.
   *
   * Returns 0 on success, -1 on failure.
   */
  NPY_NO_EXPORT int
  PyArray_AssignArray(PyArrayObject *dst, PyArrayObject *src,
                      PyArrayObject *wheremask,
                      NPY_CASTING casting)
  {
      int copied_src = 0;
  
      npy_intp src_strides[NPY_MAXDIMS];
  
      /* Use array_assign_scalar if 'src' NDIM is 0 */
      if (PyArray_NDIM(src) == 0) {
          return PyArray_AssignRawScalar(
                              dst, PyArray_DESCR(src), PyArray_DATA(src),
                              wheremask, casting);
      }
  
      /*
       * Performance fix for expressions like "a[1000:6000] += x".  In this
       * case, first an in-place add is done, followed by an assignment,
       * equivalently expressed like this:
       *
       *   tmp = a[1000:6000]   # Calls array_subscript in mapping.c
       *   np.add(tmp, x, tmp)
       *   a[1000:6000] = tmp   # Calls array_assign_subscript in mapping.c
       *
       * In the assignment the underlying data type, shape, strides, and
       * data pointers are identical, but src != dst because they are separately
       * generated slices.  By detecting this and skipping the redundant
       * copy of values to themselves, we potentially give a big speed boost.
       *
       * Note that we don't call EquivTypes, because usually the exact same
       * dtype object will appear, and we don't want to slow things down
       * with a complicated comparison.  The comparisons are ordered to
       * try and reject this with as little work as possible.
       */
      if (PyArray_DATA(src) == PyArray_DATA(dst) &&
                          PyArray_DESCR(src) == PyArray_DESCR(dst) &&
                          PyArray_NDIM(src) == PyArray_NDIM(dst) &&
                          PyArray_CompareLists(PyArray_DIMS(src),
                                               PyArray_DIMS(dst),
                                               PyArray_NDIM(src)) &&
                          PyArray_CompareLists(PyArray_STRIDES(src),
                                               PyArray_STRIDES(dst),
                                               PyArray_NDIM(src))) {
          /*printf("Redundant copy operation detected\n");*/
          return 0;
      }
  
      if (PyArray_FailUnlessWriteable(dst, "assignment destination") < 0) {
          goto fail;
      }
  
      /* Check the casting rule */
      if (!PyArray_CanCastTypeTo(PyArray_DESCR(src),
                                  PyArray_DESCR(dst), casting)) {
          PyObject *errmsg;
          errmsg = PyUString_FromString("Cannot cast scalar from ");
          PyUString_ConcatAndDel(&errmsg,
                  PyObject_Repr((PyObject *)PyArray_DESCR(src)));
          PyUString_ConcatAndDel(&errmsg,
                  PyUString_FromString(" to "));
          PyUString_ConcatAndDel(&errmsg,
                  PyObject_Repr((PyObject *)PyArray_DESCR(dst)));
          PyUString_ConcatAndDel(&errmsg,
                  PyUString_FromFormat(" according to the rule %s",
                          npy_casting_to_string(casting)));
          PyErr_SetObject(PyExc_TypeError, errmsg);
          Py_DECREF(errmsg);
          goto fail;
      }
  
      /*
       * When ndim is 1 and the strides point in the same direction,
       * the lower-level inner loop handles copying
       * of overlapping data. For bigger ndim and opposite-strided 1D
       * data, we make a temporary copy of 'src' if 'src' and 'dst' overlap.'
       */
      if (((PyArray_NDIM(dst) == 1 && PyArray_NDIM(src) >= 1 &&
                      PyArray_STRIDES(dst)[0] *
                              PyArray_STRIDES(src)[PyArray_NDIM(src) - 1] < 0) ||
                      PyArray_NDIM(dst) > 1 || PyArray_HASFIELDS(dst)) &&
                      arrays_overlap(src, dst)) {
          PyArrayObject *tmp;
  
          /*
           * Allocate a temporary copy array.
           */
          tmp = (PyArrayObject *)PyArray_NewLikeArray(dst,
                                          NPY_KEEPORDER, NULL, 0);
          if (tmp == NULL) {
              goto fail;
          }
  
          if (PyArray_AssignArray(tmp, src, NULL, NPY_UNSAFE_CASTING) < 0) {
              Py_DECREF(tmp);
              goto fail;
          }
  
          src = tmp;
          copied_src = 1;
      }
  
      /* Broadcast 'src' to 'dst' for raw iteration */
      if (PyArray_NDIM(src) > PyArray_NDIM(dst)) {
          int ndim_tmp = PyArray_NDIM(src);
          npy_intp *src_shape_tmp = PyArray_DIMS(src);
          npy_intp *src_strides_tmp = PyArray_STRIDES(src);
          /*
           * As a special case for backwards compatibility, strip
           * away unit dimensions from the left of 'src'
           */
          while (ndim_tmp > PyArray_NDIM(dst) && src_shape_tmp[0] == 1) {
              --ndim_tmp;
              ++src_shape_tmp;
              ++src_strides_tmp;
          }
  
          if (broadcast_strides(PyArray_NDIM(dst), PyArray_DIMS(dst),
                      ndim_tmp, src_shape_tmp,
                      src_strides_tmp, "input array",
                      src_strides) < 0) {
              goto fail;
          }
      }
      else {
          if (broadcast_strides(PyArray_NDIM(dst), PyArray_DIMS(dst),
                      PyArray_NDIM(src), PyArray_DIMS(src),
                      PyArray_STRIDES(src), "input array",
                      src_strides) < 0) {
              goto fail;
          }
      }
  
      if (wheremask == NULL) {
          /* A straightforward value assignment */
          /* Do the assignment with raw array iteration */
          if (raw_array_assign_array(PyArray_NDIM(dst), PyArray_DIMS(dst),
                  PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
                  PyArray_DESCR(src), PyArray_DATA(src), src_strides) < 0) {
              goto fail;
          }
      }
      else {
          npy_intp wheremask_strides[NPY_MAXDIMS];
  
          /* Broadcast the wheremask to 'dst' for raw iteration */
          if (broadcast_strides(PyArray_NDIM(dst), PyArray_DIMS(dst),
                      PyArray_NDIM(wheremask), PyArray_DIMS(wheremask),
                      PyArray_STRIDES(wheremask), "where mask",
                      wheremask_strides) < 0) {
              goto fail;
          }
  
          /* A straightforward where-masked assignment */
           /* Do the masked assignment with raw array iteration */
           if (raw_array_wheremasked_assign_array(
                   PyArray_NDIM(dst), PyArray_DIMS(dst),
                   PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
                   PyArray_DESCR(src), PyArray_DATA(src), src_strides,
                   PyArray_DESCR(wheremask), PyArray_DATA(wheremask),
                           wheremask_strides) < 0) {
               goto fail;
           }
      }
  
      if (copied_src) {
          Py_DECREF(src);
      }
      return 0;
  
  fail:
      if (copied_src) {
          Py_DECREF(src);
      }
      return -1;
  }
  