text: # coding: utf-8
  # pylint: disable= too-many-lines, redefined-builtin, protected-access
  # pylint: disable=import-error, no-name-in-module, undefined-variable
  """NDArray API of mxnet."""
  from __future__ import absolute_import
  from __future__ import division
  
  import ctypes
  import warnings
  
  import os as _os
  import sys as _sys
  
  import operator
  import numpy as np
  from .base import _LIB, string_types, numeric_types
  from .base import c_array, py_str, c_str, mx_real_t
  from .base import mx_uint, NDArrayHandle, check_call
  from .base import ctypes2buffer
  from .context import Context
  from . import _ndarray_internal as _internal
  
  # Use different verison of SymbolBase
  # When possible, use cython to speedup part of computation.
  try:
      if int(_os.environ.get("MXNET_ENABLE_CYTHON", True)) == 0:
          from ._ctypes.ndarray import NDArrayBase, _init_ndarray_module
      elif _sys.version_info >= (3, 0):
          from ._cy3.ndarray import NDArrayBase, _init_ndarray_module
      else:
          from ._cy2.ndarray import NDArrayBase, _init_ndarray_module
  except ImportError:
      if int(_os.environ.get("MXNET_ENFORCE_CYTHON", False)) != 0:
          raise ImportError("Cython Module cannot be loaded but MXNET_ENFORCE_CYTHON=1")
      from ._ctypes.ndarray import NDArrayBase, _init_ndarray_module
  
  
  # pylint: disable= no-member
  _DTYPE_NP_TO_MX = {
      np.float32 : 0,
      np.float64 : 1,
      np.float16 : 2,
      np.uint8   : 3,
      np.int32   : 4
  }
  
  _DTYPE_MX_TO_NP = {
      0 : np.float32,
      1 : np.float64,
      2 : np.float16,
      3 : np.uint8,
      4 : np.int32
  }
  # pylint: enable= no-member
  
  def _new_empty_handle():
      """Return a new empty handle.
  
      Empty handle can be used to hold result
  
      Returns
      -------
      a new empty ndarray handle
      """
      hdl = NDArrayHandle()
      check_call(_LIB.MXNDArrayCreateNone(ctypes.byref(hdl)))
      return hdl
  
  def _new_alloc_handle(shape, ctx, delay_alloc, dtype=mx_real_t):
      """Return a new handle with specified shape and context.
  
      Empty handle is only used to hold results
  
      Returns
      -------
      a new empty ndarray handle
      """
      hdl = NDArrayHandle()
      check_call(_LIB.MXNDArrayCreateEx(
          c_array(mx_uint, shape),
          mx_uint(len(shape)),
          ctypes.c_int(ctx.device_typeid),
          ctypes.c_int(ctx.device_id),
          ctypes.c_int(int(delay_alloc)),
          ctypes.c_int(int(_DTYPE_NP_TO_MX[np.dtype(dtype).type])),
          ctypes.byref(hdl)))
      return hdl
  
  def waitall():
      """Wait all async operation to finish in MXNet
  
      This function is used for benchmark only
      """
      check_call(_LIB.MXNDArrayWaitAll())
  
  class NDArray(NDArrayBase):
      """NDArray object in mxnet.
  
      NDArray is basic ndarray/Tensor like data structure in mxnet.
      """
      __slots__ = []
      # pylint: disable= no-member, undefined-variable
      def __repr__(self):
          shape_info = 'x'.join(['%d' % x for x in self.shape])
          return '<%s %s @%s>' % (self.__class__.__name__,
                                  shape_info, self.context)
  
      def __add__(self, other):
          return add(self, other)
  
      def __iadd__(self, other):
          if not self.writable:
              raise ValueError('trying to add to a readonly NDArray')
          if isinstance(other, NDArray):
              return broadcast_add(self, other, out=self)
          elif isinstance(other, numeric_types):
              return _internal._plus_scalar(self, float(other), out=self)
          else:
              raise TypeError('type %s not supported' % str(type(other)))
  
      def __radd__(self, other):
          return self.__add__(other)
  
      def __sub__(self, other):
          return subtract(self, other)
  
      def __isub__(self, other):
          if not self.writable:
              raise ValueError('trying to subtract from a readonly NDArray')
          if isinstance(other, NDArray):
              return broadcast_sub(self, other, out=self)
          elif isinstance(other, numeric_types):
              return _internal._minus_scalar(self, float(other), out=self)
          else:
              raise TypeError('type %s not supported' % str(type(other)))
  
      def __rsub__(self, other):
          return subtract(other, self)
  
      def __mul__(self, other):
          return multiply(self, other)
  
      def __neg__(self):
          return _internal._mul_scalar(self, -1.0)
  
      def __imul__(self, other):
          if not self.writable:
              raise ValueError('trying to multiply to a readonly NDArray')
          if isinstance(other, NDArray):
              return broadcast_mul(self, other, out=self)
          elif isinstance(other, numeric_types):
              return _internal._mul_scalar(self, float(other), out=self)
          else:
              raise TypeError('type %s not supported' % str(type(other)))
  
      def __rmul__(self, other):
          return self.__mul__(other)
  
      def __div__(self, other):
          return divide(self, other)
  
      def __rdiv__(self, other):
          return divide(other, self)
  
      def __idiv__(self, other):
          if not self.writable:
              raise ValueError('trying to divide from a readonly NDArray')
          if isinstance(other, NDArray):
              return broadcast_div(self, other, out=self)
          elif isinstance(other, numeric_types):
              return _internal._div_scalar(self, float(other), out=self)
          else:
              raise TypeError('type %s not supported' % str(type(other)))
  
      def __truediv__(self, other):
          return divide(self, other)
  
      def __rtruediv__(self, other):
          return divide(other, self)
  
      def __itruediv__(self, other):
          return self.__idiv__(other)
  
      def __pow__(self, other):
          return power(self, other)
  
      def __rpow__(self, other):
          return power(other, self)
  
      def __eq__(self, other):
          return equal(self, other)
  
      def __ne__(self, other):
          return not_equal(self, other)
  
      def __gt__(self, other):
          return greater(self, other)
  
      def __ge__(self, other):
          return greater_equal(self, other)
  
      def __lt__(self, other):
          return lesser(self, other)
  
      def __le__(self, other):
          return lesser_equal(self, other)
  
      def __getstate__(self):
          handle = self.handle
          this = {'handle' : None}
          if handle is not None:
              length = ctypes.c_size_t()
              cptr = ctypes.POINTER(ctypes.c_char)()
              check_call(_LIB.MXNDArraySaveRawBytes(self.handle,
                                                    ctypes.byref(length),
                                                    ctypes.byref(cptr)))
              this['handle'] = ctypes2buffer(cptr, length.value)
          return this
  
      def __setstate__(self, state):
          # pylint: disable=assigning-non-slot
          handle = state['handle']
          if handle is not None:
              buf = handle
              handle = NDArrayHandle()
              ptr = (ctypes.c_char * len(buf)).from_buffer(buf)
              length = ctypes.c_size_t(len(buf))
              check_call(_LIB.MXNDArrayLoadFromRawBytes(ptr, length, ctypes.byref(handle)))
              self.handle = handle
          else:
              self.handle = None
  
      def __setitem__(self, in_slice, value):
          """Set ndarray value.
  
          `value` can be a scalar, an `NDArray` or numpy array of compatible shape.
          The following modes are supported:
  
          - `array[:] = value`: set all the contents
          - `array[i] = value`: set the i-th slice. If the array is of dimension
            `(d1, d2, d3)`, it sets value of a slice of shape `(1, d2, d3)`.
          - `array[i:j] = value`: similarly, if the array is of dimension
            `(d1, d2, d3)`, it sets value of a slice of shape `(j-i, d2, d3)`.
  
          Fully-dimensional indexing is also supported. For example, if array is
          of shape `(d1, d2, d3)`, one can do
  
          - `array[:, :, :] = value`: achieving the same effect of `array[:] = value`
          - `array[:, i, j:k] = value`: each index could be a python slice or an int.
          """
          # pylint: disable=too-many-branches
          if not self.writable:
              raise ValueError('trying to assign to a readonly NDArray')
          if isinstance(in_slice, int):
              sliced_arr = self._at(in_slice)
              sliced_arr[:] = value
              return
          if isinstance(in_slice, slice):
              if in_slice.step is not None:
                  raise ValueError('NDArray only support continuous slicing on axis 0')
              if in_slice.start is not None or in_slice.stop is not None:
                  sliced_arr = self._slice(in_slice.start, in_slice.stop)
                  sliced_arr[:] = value
                  return
              if isinstance(value, NDArray):
                  if value.handle is not self.handle:
                      value.copyto(self)
              elif isinstance(value, numeric_types):
                  _internal._set_value(float(value), out=self)
              elif isinstance(value, (np.ndarray, np.generic)):
                  self._sync_copyfrom(value)
              else:
                  raise TypeError('type %s not supported' % str(type(value)))
          if isinstance(in_slice, tuple):
              # multi-dimension indexing
              my_shape = self.shape
              assert len(in_slice) == len(my_shape)
              for slice_i in in_slice:
                  assert isinstance(slice_i, (slice, int))
              begin = [0 for _ in my_shape]
              end = [x for x in my_shape]
              for i, slice_i in enumerate(in_slice):
                  if isinstance(slice_i, int):
                      assert slice_i < my_shape[i]
                      begin[i] = slice_i
                      end[i] = slice_i + 1
                  if isinstance(slice_i, slice):
                      # only support continuous slicing
                      assert slice_i.step is None
                      begin[i] = slice_i.start or 0
                      end[i] = slice_i.stop or my_shape[i]
                      assert begin[i] < end[i]
                      assert end[i] <= my_shape[i]
              begin = tuple(begin)
              end = tuple(end)
              if isinstance(value, NDArray):
                  value = value.as_in_context(self.context)
                  _internal._crop_assign(self, value, out=self,
                                         begin=begin, end=end)
              elif isinstance(value, numeric_types):
                  _internal._crop_assign_scalar(self, out=self,
                                                begin=begin, end=end,
                                                scalar=value)
              elif isinstance(value, (np.ndarray, np.generic)):
                  value = array(value, ctx=self.context)
                  _internal._crop_assign(self, value, out=self,
                                         begin=begin, end=end)
              else:
                  raise TypeError('type %s not supported' % str(type(value)))
          # pylint: enable=too-many-branches
  
      def __getitem__(self, in_slice):
          """Get ndarray"""
          if isinstance(in_slice, int):
              return self._at(in_slice)
          if not isinstance(in_slice, slice) or in_slice.step is not None:
              raise ValueError('NDArray only support continuous slicing on axis 0')
          if in_slice.start is not None or in_slice.stop is not None:
              return self._slice(in_slice.start, in_slice.stop)
          else:
              return self
  
      def _sync_copyfrom(self, source_array):
          """Peform an synchronize copy from the array.
  
          Parameters
          ----------
          source_array : array_like
              The data source we should like to copy from.
          """
          if not isinstance(source_array, np.ndarray):
              try:
                  source_array = np.array(source_array, dtype=self.dtype)
              except:
                  raise TypeError('array must be an array_like data,' +
                                  'type %s is not supported' % str(type(array)))
          source_array = np.ascontiguousarray(source_array, dtype=self.dtype)
          if source_array.shape != self.shape:
              raise ValueError('Shape inconsistant: expected %s vs got %s'%(
                  str(self.shape), str(source_array.shape)))
          check_call(_LIB.MXNDArraySyncCopyFromCPU(
              self.handle,
              source_array.ctypes.data_as(ctypes.c_void_p),
              ctypes.c_size_t(source_array.size)))
  
      def _slice(self, start, stop):
          """Return a sliced NDArray that shares memory with current one.
  
          Parameters
          ----------
          start : int
              Starting index of slice.
          stop : int
              Finishing index of slice.
          """
          handle = NDArrayHandle()
          start = mx_uint(start) if start else mx_uint(0)
          stop = mx_uint(stop) if stop else mx_uint(self.shape[0])
          check_call(_LIB.MXNDArraySlice(
              self.handle, start, stop, ctypes.byref(handle)))
          return NDArray(handle=handle, writable=self.writable)
  
      def _at(self, idx):
          """Return a sub NDArray that shares memory with current one.
  
          Parameters
          ----------
          idx : int
              index of sub array.
          """
          handle = NDArrayHandle()
          idx = mx_uint(idx)
          check_call(_LIB.MXNDArrayAt(
              self.handle, idx, ctypes.byref(handle)))
          return NDArray(handle=handle, writable=self.writable)
  
      def reshape(self, new_shape):
          """Return a reshaped NDArray that shares memory with current one.
  
          Parameters
          ----------
          new_shape : iterable of int
              new shape of NDArray
          """
          handle = NDArrayHandle()
          check_call(_LIB.MXNDArrayReshape(self.handle,
                                           len(new_shape),
                                           c_array(ctypes.c_int, new_shape),
                                           ctypes.byref(handle)))
          return NDArray(handle=handle, writable=self.writable)
  
      # pylint: disable= undefined-variable
      def broadcast_to(self, shape):
          """ Broadcasting the current NDArray into the given shape. The semantics is
          the same with `numpy`'s broadcasting
  
          Parameters
          ---------
          shape : the shape to broadcast
              the broadcast shape
          """
          cur_shape = self.shape
          err_str = 'operands could not be broadcast together with remapped shapes' \
                    '[original->remapped]: {} and requested shape {}'.format(cur_shape, shape)
          if len(shape) < len(cur_shape):
              raise ValueError(err_str)
          cur_shape = (1,) * (len(shape) - len(cur_shape)) + cur_shape
          cur_shape_arr = np.array(cur_shape)
          broadcasting_axes = np.nonzero(cur_shape_arr != np.array(shape))
          if (cur_shape_arr[broadcasting_axes] != 1).any():
              raise ValueError(err_str)
          if cur_shape != self.shape:
              return broadcast_to(self.reshape(cur_shape), shape=shape)
          else:
              return broadcast_to(self, shape=tuple(shape))
      # pylint: enable= undefined-variable
  
      def wait_to_read(self):
          """Block until all pending writes operations on current NDArray are finished.
  
          This function will return when all the pending writes to the current
          NDArray finishes. There can still be pending read going on when the
          function returns.
          """
          check_call(_LIB.MXNDArrayWaitToRead(self.handle))
  
      @property
      def shape(self):
          """Get shape of current NDArray.
  
          Returns
          -------
          a tuple representing shape of current ndarray
          """
          ndim = mx_uint()
          pdata = ctypes.POINTER(mx_uint)()
          check_call(_LIB.MXNDArrayGetShape(
              self.handle, ctypes.byref(ndim), ctypes.byref(pdata)))
          return tuple(pdata[:ndim.value])
  
      @property
      def size(self):
          """Get size of current NDArray.
  
          Returns
          -------
          an int representing size of current ndarray
          """
          return np.prod(self.shape)
  
      @property
      def context(self):
          """Get context of current NDArray.
  
          Returns
          -------
          context : mxnet.Context
              The context of current NDArray.
          """
          dev_typeid = ctypes.c_int()
          dev_id = ctypes.c_int()
          check_call(_LIB.MXNDArrayGetContext(
              self.handle, ctypes.byref(dev_typeid), ctypes.byref(dev_id)))
          return Context(Context.devtype2str[dev_typeid.value], dev_id.value)
  
      @property
      def dtype(self):
          """Get data type of current NDArray.
  
          Returns
          -------
          an numpy.dtype object representing type of current ndarray
          """
          mx_dtype = ctypes.c_int()
          check_call(_LIB.MXNDArrayGetDType(
              self.handle, ctypes.byref(mx_dtype)))
          return _DTYPE_MX_TO_NP[mx_dtype.value]
  
      @property
      # pylint: disable= invalid-name, undefined-variable
      def T(self):
          """Get transpose of current NDArray"""
          if len(self.shape) != 2:
              raise ValueError('Only 2D matrix is allowed to be transposed')
          return transpose(self)
      # pylint: enable= invalid-name, undefined-variable
  
      def asnumpy(self):
          """Return a copied numpy array of current array.
  
          Returns
          -------
          array : numpy.ndarray
              A copy of array content.
          """
          data = np.empty(self.shape, dtype=self.dtype)
          check_call(_LIB.MXNDArraySyncCopyToCPU(
              self.handle,
              data.ctypes.data_as(ctypes.c_void_p),
              ctypes.c_size_t(data.size)))
          return data
  
      def asscalar(self):
          """Return a CPU scalar(float) of current ndarray.
  
          This ndarray must have shape (1,)
  
          Returns
          -------
          scalar : np.float
              The scalar representation of the ndarray.
          """
          if self.shape != (1,):
              raise ValueError("The current array is not a scalar")
          return self.asnumpy()[0]
  
      def astype(self, dtype):
          """Return a copied numpy array of current array with specified type.
  
          Parameters
          ----------
          dtype : numpy.dtype or string
              Desired type of result array.
  
          Returns
          -------
          array : numpy.ndarray
              A copy of array content.
          """
          res = empty(self.shape, ctx=self.context, dtype=dtype)
          self.copyto(res)
          return res
  
      def copyto(self, other):
          """Copy the content of current array to other.
  
          When other is NDArray, the content is copied over.
          When other is a Context, a new NDArray in the context
          will be created as target
  
          Parameters
          ----------
          other : NDArray or Context
              Target NDArray or context we want to copy data to.
  
          Returns
          -------
          dst : NDArray
              The copy target NDArray
          """
          if isinstance(other, NDArray):
              if other.handle is self.handle:
                  warnings.warn('copy an array to itself, is it intended?',
                                RuntimeWarning)
                  return
              return _internal._copyto(self, out=other)
          elif isinstance(other, Context):
              hret = NDArray(_new_alloc_handle(self.shape, other, True, self.dtype))
              return _internal._copyto(self, out=hret)
          else:
              raise TypeError('copyto do not support type ' + str(type(other)))
  
      def copy(self):
          """Make a copy of the current ndarray on the same context
  
          Return
          ------
          cpy : NDArray
              The copy
          """
          return self.copyto(self.context)
  
      # pylint: enable= no-member
  
      def as_in_context(self, context):
          """Return an `NDArray` that lives in the target context. If the array
          is already in that context, `self` is returned. Otherwise, a copy is
          made.
  
          Parameters
          ----------
          context : Context
              The target context we want the return value to live in.
  
          Returns
          -------
          A copy or `self` as an `NDArray` that lives in the target context.
          """
          if self.context == context:
              return self
          return self.copyto(context)
  
  
  _init_ndarray_module(NDArray, "mxnet")
  
  
  def onehot_encode(indices, out):
      """One hot encoding indices into matrix out.
  
      Parameters
      ----------
      indices: NDArray
          An NDArray containing indices of the categorical features.
  
      out: NDArray
          The result holder of the encoding.
  
      Returns
      -------
      out: Array
          Same as out.
      """
      # pylint: disable= no-member, protected-access
      return _internal._onehot_encode(indices, out, out=out)
      # pylint: enable= no-member, protected-access
  
  
  def empty(shape, ctx=None, dtype=mx_real_t):
      """Create an empty uninitialized new NDArray, with specified shape.
  
      Parameters
      ----------
      shape : tuple
          shape of the NDArray.
  
      ctx : Context, optional
          The context of the NDArray, default to current default context.
  
      Returns
      -------
      out: Array
          The created NDArray.
      """
      if isinstance(shape, int):
          shape = (shape, )
      if ctx is None:
          ctx = Context.default_ctx
      return NDArray(handle=_new_alloc_handle(shape, ctx, False, dtype))
  
  #pylint: disable= too-many-arguments, no-member, protected-access
  def _ufunc_helper(lhs, rhs, fn_array, fn_scalar, lfn_scalar, rfn_scalar=None):
      """ Helper function for element-wise operation
      The function will perform numpy-like broadcasting if needed and call different functions
  
      Parameters
      ----------
      lhs : NDArray or numeric value
          left hande side operand
  
      rhs : NDArray or numeric value
          right hand side operand
  
      fn_array : function
          function to be called if both lhs and rhs are of NDArray type
  
      fn_scalar : function
          function to be called if both lhs and rhs are numeric values
  
      lfn_scalar : function
          function to be called if lhs is NDArray while rhs is numeric value
  
      rfn_scalar : function
          function to be called if lhs is numeric value while rhs is NDArray;
          if none is provided, then the function is commutative, so rfn_scalar is equal to lfn_scalar
  
      Returns
      -------
      out: NDArray
          result array
      """
      if isinstance(lhs, numeric_types):
          if isinstance(rhs, numeric_types):
              return fn_scalar(lhs, rhs)
          else:
              if rfn_scalar is None:
                  # commutative function
                  return lfn_scalar(rhs, float(lhs))
              else:
                  return rfn_scalar(rhs, float(lhs))
      elif isinstance(rhs, numeric_types):
          return lfn_scalar(lhs, float(rhs))
      elif isinstance(rhs, NDArray):
          return fn_array(lhs, rhs)
      else:
          raise TypeError('type %s not supported' % str(type(rhs)))
  #pylint: enable= too-many-arguments, no-member, protected-access
  
  def add(lhs, rhs):
      """ Perform element-wise addition
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_add,
          operator.add,
          _internal._plus_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def subtract(lhs, rhs):
      """ Perform element-wise subtract
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_sub,
          operator.sub,
          _internal._minus_scalar,
          _internal._rminus_scalar)
      # pylint: enable= no-member, protected-access
  
  def multiply(lhs, rhs):
      """ Perform element-wise multiplication
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_mul,
          operator.mul,
          _internal._mul_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def divide(lhs, rhs):
      """ Perform element-wise divide
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_div,
          operator.truediv,
          _internal._div_scalar,
          _internal._rdiv_scalar)
      # pylint: enable= no-member, protected-access
  
  def power(lhs, rhs):
      """ Perform power operator
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_power,
          operator.pow,
          _internal._power_scalar,
          _internal._rpower_scalar)
      # pylint: enable= no-member, protected-access
  
  def maximum(lhs, rhs):
      """ Perform maximum operator
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_maximum,
          lambda x, y: x if x > y else y,
          _internal._maximum_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def minimum(lhs, rhs):
      """ Perform minimum operator
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_minimum,
          lambda x, y: x if x < y else y,
          _internal._minimum_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def equal(lhs, rhs):
      """Return (lhs == rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_equal,
          lambda x, y: 1 if x == y else 0,
          _internal._equal_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def not_equal(lhs, rhs):
      """Return (lhs != rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_not_equal,
          lambda x, y: 1 if x != y else 0,
          _internal._not_equal_scalar,
          None)
      # pylint: enable= no-member, protected-access
  
  def greater(lhs, rhs):
      """Return (lhs > rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_greater,
          lambda x, y: 1 if x > y else 0,
          _internal._greater_scalar,
          _internal._lesser_scalar)
      # pylint: enable= no-member, protected-access
  
  def greater_equal(lhs, rhs):
      """Return (lhs >= rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_greater_equal,
          lambda x, y: 1 if x >= y else 0,
          _internal._greater_equal_scalar,
          _internal._lesser_equal_scalar)
      # pylint: enable= no-member, protected-access
  
  def lesser(lhs, rhs):
      """Return (lhs < rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_lesser,
          lambda x, y: 1 if x < y else 0,
          _internal._lesser_scalar,
          _internal._greater_scalar)
      # pylint: enable= no-member, protected-access
  
  
  def lesser_equal(lhs, rhs):
      """Return (lhs <= rhs) element-wise.
  
      Parameters
      ----------
      lhs : Array or float value
          left hand side operand
  
      rhs : Array of float value
          right hand side operand
  
      Returns
      -------
      out: Array
          result array
      """
      # pylint: disable= no-member, protected-access
      return _ufunc_helper(
          lhs,
          rhs,
          broadcast_lesser_equal,
          lambda x, y: 1 if x <= y else 0,
          _internal._lesser_equal_scalar,
          _internal._greater_equal_scalar)
      # pylint: enable= no-member, protected-access
  
  def true_divide(lhs, rhs):
      """ Same as numpy's true_divide. It adjusts the output type to present the best answer,
      regardless of input types.
      """
      return divide(lhs, rhs)
  
  def negative(arr):
      """ Return the negation of array values """
      return multiply(arr, -1.0)
  
  def zeros(shape, ctx=None, dtype=mx_real_t):
      """Create a new NDArray filled with 0, with specified shape.
  
      Parameters
      ----------
      shape : tuple
          shape of the NDArray.
      ctx : Context, optional.
          The context of the NDArray, default to current default context.
  
      Returns
      -------
      out: Array
          The created NDArray.
      """
      if ctx is None:
          ctx = Context.default_ctx
      # pylint: disable= no-member, protected-access
      return _internal._zeros(shape=shape, ctx=ctx, dtype=dtype)
      # pylint: enable= no-member, protected-access
  
  def ones(shape, ctx=None, dtype=mx_real_t):
      """Create a new NDArray filled with 1, with specified shape.
  
      Parameters
      ----------
      shape : tuple
          shape of the NDArray.
      ctx : Context, optional
          The context of the NDArray, default to current default context.
  
      Returns
      -------
      out: Array
          The created NDArray.
      """
      if ctx is None:
          ctx = Context.default_ctx
      # pylint: disable= no-member, protected-access
      return _internal._ones(shape=shape, ctx=ctx, dtype=dtype)
      # pylint: enable= no-member, protected-access
  
  def full(shape, val, ctx=None, dtype=mx_real_t):
      """Create a new NDArray filled with given value, with specified shape.
  
      Parameters
      ----------
      shape : tuple
          shape of the NDArray.
      val : float or int
          value to be filled with.
      ctx : Context, optional
          The context of the NDArray, default to current default context.
  
      Returns
      -------
      out: Array
          The created NDArray.
      """
      arr = empty(shape, ctx, dtype)
      arr[:] = val
      return arr
  
  def array(source_array, ctx=None, dtype=mx_real_t):
      """Create a new NDArray that copies content from source_array.
  
      Parameters
      ----------
      source_array : array_like
          Source data to create NDArray from.
  
      ctx : Context, optional
          The context of the NDArray, default to current default context.
  
      Returns
      -------
      out: Array
          The created NDArray.
      """
  
      if not isinstance(source_array, np.ndarray):
          try:
              source_array = np.array(source_array, dtype=dtype)
          except:
              raise TypeError('source_array must be array like object')
      arr = empty(source_array.shape, ctx, dtype)
      arr[:] = source_array
      return arr
  
  def concatenate(arrays, axis=0, always_copy=True):
      """Concatenate a list of NDArrays along the first dimension.
  
      Parameters
      ----------
      arrays : list of NDArray
          Arrays to be concatenate. They must have identical shape except
          the first dimension. They also must have the same data type.
      axis : int
          The axis along which to concatenate.
      always_copy : bool
          Default `True`. When not `True`, if the arrays only contain one
          `NDArray`, that element will be returned directly, avoid copying.
  
      Returns
      -------
      An `NDArray` that lives on the same context as `arrays[0].context`.
      """
      assert isinstance(arrays, list)
      assert len(arrays) > 0
      assert isinstance(arrays[0], NDArray)
  
      if not always_copy and len(arrays) == 1:
          return arrays[0]
  
      shape_axis = arrays[0].shape[axis]
      shape_rest1 = arrays[0].shape[0:axis]
      shape_rest2 = arrays[0].shape[axis+1:]
      dtype = arrays[0].dtype
      for arr in arrays[1:]:
          shape_axis += arr.shape[axis]
          assert shape_rest1 == arr.shape[0:axis]
          assert shape_rest2 == arr.shape[axis+1:]
          assert dtype == arr.dtype
      ret_shape = shape_rest1 + (shape_axis,) + shape_rest2
      ret = empty(ret_shape, ctx=arrays[0].context, dtype=dtype)
  
      idx = 0
      begin = [0 for _ in ret_shape]
      end = list(ret_shape)
      for arr in arrays:
          if axis == 0:
              ret[idx:idx+arr.shape[0]] = arr
          else:
              begin[axis] = idx
              end[axis] = idx+arr.shape[axis]
              # pylint: disable=no-member,protected-access
              _internal._crop_assign(ret, arr, out=ret,
                                     begin=tuple(begin),
                                     end=tuple(end))
              # pylint: enable=no-member,protected-access
          idx += arr.shape[axis]
  
      return ret
  
  # pylint: disable= no-member, protected-access, too-many-arguments
  def arange(start, stop=None, step=1.0, repeat=1, ctx=None, dtype=mx_real_t):
      """Simlar function in the MXNet ndarray as numpy.arange
          See Also https://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html.
  
      Parameters
      ----------
      start : number, optional
          Start of interval. The interval includes this value. The default start value is 0.
      stop : number, optional
          End of interval. The interval does not include this value.
      step : number, optional
          Spacing between values
      repeat : number, optional
          "The repeating time of all elements.
          E.g repeat=3, the element a will be repeated three times --> a, a, a.
      ctx : Context, optional
          The context of the NDArray, default to current default context.
      dtype : type, optional
          The value type of the NDArray, default to np.float32
  
      Returns
      -------
      out : NDArray
          The created NDArray
      """
      if ctx is None:
          ctx = Context.default_ctx
      return _internal._arange(start=start, stop=stop, step=step, repeat=repeat,
                               dtype=dtype, ctx=str(ctx))
  # pylint: enable= no-member, protected-access, too-many-arguments
  
  
  def load(fname):
      """Load ndarray from binary file.
  
      You can also use pickle to do the job if you only work on python.
      The advantage of load/save is the file is language agnostic.
      This means the file saved using save can be loaded by other language binding of mxnet.
      You also get the benefit being able to directly load/save from cloud storage(S3, HDFS)
  
      Parameters
      ----------
      fname : str
          The name of the file.Can be S3 or HDFS address (remember built with S3 support).
          Example of fname:
  
          - `s3://my-bucket/path/my-s3-ndarray`
          - `hdfs://my-bucket/path/my-hdfs-ndarray`
          - `/path-to/my-local-ndarray`
  
      Returns
      -------
      out : list of NDArray or dict of str to NDArray
          List of NDArray or dict of str->NDArray, depending on what was saved.
      """
      if not isinstance(fname, string_types):
          raise TypeError('fname need to be string')
      out_size = mx_uint()
      out_name_size = mx_uint()
      handles = ctypes.POINTER(NDArrayHandle)()
      names = ctypes.POINTER(ctypes.c_char_p)()
      check_call(_LIB.MXNDArrayLoad(c_str(fname),
                                    ctypes.byref(out_size),
                                    ctypes.byref(handles),
                                    ctypes.byref(out_name_size),
                                    ctypes.byref(names)))
      if out_name_size.value == 0:
          return [NDArray(NDArrayHandle(handles[i])) for i in range(out_size.value)]
      else:
          assert out_name_size.value == out_size.value
          return dict(
              (py_str(names[i]), NDArray(NDArrayHandle(handles[i]))) for i in range(out_size.value))
  
  
  def save(fname, data):
      """Save list of NDArray or dict of str->NDArray to binary file.
  
      You can also use pickle to do the job if you only work on python.
      The advantage of load/save is the file is language agnostic.
      This means the file saved using save can be loaded by other language binding of mxnet.
      You also get the benefit being able to directly load/save from cloud storage(S3, HDFS)
  
      Parameters
      ----------
      fname : str
          The name of the file.Can be S3 or HDFS address (remember built with S3 support).
          Example of fname:
  
          - `s3://my-bucket/path/my-s3-ndarray`
          - `hdfs://my-bucket/path/my-hdfs-ndarray`
          - `/path-to/my-local-ndarray`
  
      data : list of NDArray or dict of str to NDArray
          The data to be saved.
      """
      handles = []
      if isinstance(data, dict):
          keys = []
          for key, val in data.items():
              if not isinstance(key, string_types):
                  raise TypeError('save only accept dict str->NDArray or list of NDArray')
              if not isinstance(val, NDArray):
                  raise TypeError('save only accept dict str->NDArray or list of NDArray')
              keys.append(c_str(key))
              handles.append(val.handle)
          keys = c_array(ctypes.c_char_p, keys)
      else:
          for val in data:
              if not isinstance(val, NDArray):
                  raise TypeError('save only accept dict str->NDArray or list of NDArray')
              handles.append(val.handle)
          keys = None
      check_call(_LIB.MXNDArraySave(c_str(fname),
                                    mx_uint(len(handles)),
                                    c_array(NDArrayHandle, handles),
                                    keys))
  
  def imdecode(str_img, clip_rect=(0, 0, 0, 0), out=None, index=0, channels=3, mean=None):
      """Decode an image from string. Requires OpenCV to work.
  
      Parameters
      ----------
      str_img : str
          binary image data
      clip_rect : iterable of 4 int
          clip decoded image to rectangle (x0, y0, x1, y1)
      out : NDArray
          output buffer. can be 3 dimensional (c, h, w) or 4 dimensional (n, c, h, w)
      index : int
          output decoded image to i-th slice of 4 dimensional buffer
      channels : int
          number of channels to output. Decode to grey scale when channels = 1.
      mean : NDArray
          subtract mean from decode image before outputing.
      """
      # pylint: disable= no-member, protected-access, too-many-arguments
      if mean is None:
          mean = NDArray(_new_empty_handle())
      if out is None:
          return _internal._imdecode(mean, index,
                                     clip_rect[0],
                                     clip_rect[1],
                                     clip_rect[2],
                                     clip_rect[3],
                                     channels,
                                     len(str_img),
                                     str_img=str_img)
      else:
          return _internal._imdecode(mean, index,
                                     clip_rect[0],
                                     clip_rect[1],
                                     clip_rect[2],
                                     clip_rect[3],
                                     channels,
                                     len(str_img),
                                     str_img=str_img,
                                     out=out)
  