This function is called by __setitem__ when key is a basic index, i.e.
        an integer, or a slice, or a tuple of integers and slices. No restrictions
        on the values of slices' steps.

def _set_nd_basic_indexing(self, key, value):
        """This function is called by __setitem__ when key is a basic index, i.e.
        an integer, or a slice, or a tuple of integers and slices. No restrictions
        on the values of slices' steps."""
        shape = self.shape
        if isinstance(key, integer_types):
            if key < 0:
                key += shape[0]
            if key < 0 or key >= shape[0]:
                if key < 0:
                    key -= shape[0]
                raise IndexError('index %d is out of bounds for axis 0 with size %d'
                                 % (key, shape[0]))
            key = py_slice(key, key+1)  # key must be >= 0 here

        if isinstance(key, py_slice):
            assign_to_self = key.step is None or key.step == 1
            assign_to_self &= key.start is None or key.start == 0
            assign_to_self &= key.stop is None or key.stop == shape[0]
            if assign_to_self:  # trivial case, assign value to self
                if isinstance(value, NDArray):
                    if value.handle is not self.handle:
                        if value.shape != shape:
                            value = value.broadcast_to(shape)
                        value.copyto(self)
                elif isinstance(value, numeric_types):
                    _internal._full(shape=shape, ctx=self.context,
                                    dtype=self.dtype, value=float(value), out=self)
                elif isinstance(value, (np.ndarray, np.generic)):
                    if isinstance(value, np.generic) or value.shape != shape:
                        value = np.broadcast_to(value, shape)
                    self._sync_copyfrom(value)
                else:  # value might be a list or a tuple
                    value_nd = self._prepare_value_nd(value, shape)
                    value_nd.copyto(self)
                return
            else:  # non-trivial case, use _slice_assign or _slice_assign_scalar
                key = (key,)

        assert isinstance(key, tuple), "key=%s must be a tuple of slices and integers" % str(key)

        assert len(key) <= len(shape), "Indexing dimensions exceed array dimensions, %d vs %d"\
                                       % (len(key), len(shape))
        begin = []
        end = []
        steps = []
        oshape = []  # output shape of slice using key
        vshape = []  # value shape of data[key]
        for i, slice_i in enumerate(key):
            dim_size = 1
            if isinstance(slice_i, py_slice):
                begin.append(slice_i.start)
                end.append(slice_i.stop)
                steps.append(slice_i.step)
                start, stop, step = _get_index_range(slice_i.start, slice_i.stop,
                                                     shape[i], slice_i.step)
                dim_size = _get_dim_size(start, stop, step)
                vshape.append(dim_size)
            elif isinstance(slice_i, integer_types):
                begin.append(slice_i)
                end.append(slice_i+1 if slice_i != -1 else self.shape[i])
                steps.append(1)
            else:
                raise ValueError("basic indexing does not support index=%s of type=%s"
                                 % (str(slice_i), str(type(slice_i))))
            oshape.append(dim_size)

        oshape.extend(shape[len(key):])
        vshape.extend(shape[len(key):])
        # if key contains all integers, vshape should be (1,)
        if len(vshape) == 0:
            vshape.append(1)
        oshape = tuple(oshape)
        vshape = tuple(vshape)

        if isinstance(value, numeric_types):
            _internal._slice_assign_scalar(self, out=self, begin=begin, end=end,
                                           step=steps, scalar=float(value))
        else:
            value_nd = self._prepare_value_nd(value, vshape)
            if vshape != oshape:
                value_nd = value_nd.reshape(oshape)
            _internal._slice_assign(self, value_nd, begin, end, steps, out=self)