docs: def calculate_tile_shape_for_max_bytes(
      array_shape,
      array_itemsize,
      max_tile_bytes,
      max_tile_shape=None,
      sub_tile_shape=None,
      halo=None
  ):
      """
      Returns a tile shape :samp:`tile_shape`
      such that :samp:`numpy.product(tile_shape)*numpy.sum({array_itemsize}) <= {max_tile_bytes}`.
      Also, if :samp:`{max_tile_shape} is not None`
      then :samp:`numpy.all(tile_shape <= {max_tile_shape}) is True` and
      if :samp:`{sub_tile_shape} is not None`
      the :samp:`numpy.all((tile_shape % {sub_tile_shape}) == 0) is True`.
  
      :type array_shape: sequence of :obj:`int`
      :param array_shape: Shape of the array which is to be split into tiles.
      :type array_itemsize: :obj:`int`
      :param array_itemsize: The number of bytes per element of the array to be tiled.
      :type max_tile_bytes: :obj:`int`
      :param max_tile_bytes: The maximum number of bytes for the returned :samp:`tile_shape`.
      :type max_tile_shape: sequence of :obj:`int`
      :param max_tile_shape: Per axis maximum shapes for the returned :samp:`tile_shape`.
      :type sub_tile_shape: sequence of :obj:`int`
      :param sub_tile_shape: The returned :samp:`tile_shape` will be an even multiple
         of this sub-tile shape.
      :type halo: :obj:`int`, sequence of :obj:`int`, or :samp:`(len({array_shape}), 2)`
         shaped :obj:`numpy.ndarray`
      :param halo: How tiles are extended in each axis direction with *halo*
         elements. See :ref:`the-halo-parameter-examples` for meaning of :samp:`{halo}` values.
      :rtype: :obj:`numpy.ndarray`
      :return: A 1D array of shape :samp:`(len(array_shape),)` indicating a *tile shape*
         which will (approximately) uniformly divide the given :samp:`{array_shape}` into
         tiles (sub-arrays).
  
      Examples::
  
         >>> from array_split.split import calculate_tile_shape_for_max_bytes
         >>> calculate_tile_shape_for_max_bytes(
         ... array_shape=[512,],
         ... array_itemsize=1,
         ... max_tile_bytes=512
         ... )
         array([512])
         >>> calculate_tile_shape_for_max_bytes(
         ... array_shape=[512,],
         ... array_itemsize=2,  # Doubling the itemsize halves the tile size.
         ... max_tile_bytes=512
         ... )
         array([256])
         >>> calculate_tile_shape_for_max_bytes(
         ... array_shape=[512,],
         ... array_itemsize=1,
         ... max_tile_bytes=512-1  # tile shape will now be halved
         ... )
         array([256])
  
  
      """
  
      logger = _logging.getLogger(__name__ + ".calculate_tile_shape_for_max_bytes")
      logger.debug("calculate_tile_shape_for_max_bytes: enter:")
      logger.debug("array_shape=%s", array_shape)
      logger.debug("array_itemsize=%s", array_itemsize)
      logger.debug("max_tile_bytes=%s", max_tile_bytes)
      logger.debug("max_tile_shape=%s", max_tile_shape)
      logger.debug("sub_tile_shape=%s", sub_tile_shape)
      logger.debug("halo=%s", halo)
  
      array_shape = _np.array(array_shape, dtype="int64")
      array_itemsize = _np.sum(array_itemsize, dtype="int64")
  
      if max_tile_shape is None:
          max_tile_shape = _np.array(array_shape, copy=True)
      max_tile_shape = \
          _np.array(_np.minimum(max_tile_shape, array_shape), copy=True, dtype=array_shape.dtype)
  
      if sub_tile_shape is None:
          sub_tile_shape = _np.ones((len(array_shape),), dtype="int64")
  
      sub_tile_shape = _np.array(sub_tile_shape, dtype="int64")
  
      halo = convert_halo_to_array_form(halo=halo, ndim=len(array_shape))
  
      if _np.any(array_shape < sub_tile_shape):
          raise ValueError(
              "Got array_shape=%s element less than corresponding sub_tile_shape=%s element."
              %
              (
                  array_shape,
                  sub_tile_shape
              )
          )
  
      logger.debug("max_tile_shape=%s", max_tile_shape)
      logger.debug("sub_tile_shape=%s", sub_tile_shape)
      logger.debug("halo=%s", halo)
      array_sub_tile_split_shape = ((array_shape - 1) // sub_tile_shape) + 1
      tile_sub_tile_split_shape = array_shape // sub_tile_shape
      if len(tile_sub_tile_split_shape) <= 1:
          tile_sub_tile_split_shape[0] = \
              int(_np.floor(
                  (
                      (max_tile_bytes / float(array_itemsize))
                      -
                      _np.sum(halo)
                  )
                  /
                  float(sub_tile_shape[0])
              ))
  
      tile_sub_tile_split_shape = \
          _np.minimum(
              tile_sub_tile_split_shape,
              max_tile_shape // sub_tile_shape
          )
      logger.debug("Pre loop: tile_sub_tile_split_shape=%s", tile_sub_tile_split_shape)
  
      current_axis = 0
      while (
          (current_axis < len(tile_sub_tile_split_shape))
          and
          (
              (
                  _np.product(tile_sub_tile_split_shape * sub_tile_shape + _np.sum(halo, axis=1))
                  *
                  array_itemsize
              )
              >
              max_tile_bytes
          )
      ):
          if current_axis < (len(tile_sub_tile_split_shape) - 1):
              tile_sub_tile_split_shape[current_axis] = 1
              tile_sub_tile_split_shape[current_axis] = \
                  (
                      max_tile_bytes
                      //
                      (
                          _np.product(
                              tile_sub_tile_split_shape *
                              sub_tile_shape +
                              _np.sum(
                                  halo,
                                  axis=1))
                          *
                          array_itemsize
                      )
              )
              tile_sub_tile_split_shape[current_axis] = \
                  max([1, tile_sub_tile_split_shape[current_axis]])
          else:
              sub_tile_shape_h = sub_tile_shape.copy()
              sub_tile_shape_h[0:current_axis] += _np.sum(halo[0:current_axis, :], axis=1)
              tile_sub_tile_split_shape[current_axis] = \
                  int(_np.floor(
                      (
                          (max_tile_bytes / float(array_itemsize))
                          -
                          _np.sum(halo[current_axis]) * _np.product(sub_tile_shape_h[0:current_axis])
                      )
                      /
                      float(_np.product(sub_tile_shape_h))
                  ))
          current_axis += 1
  
      logger.debug("Post loop: tile_sub_tile_split_shape=%s", tile_sub_tile_split_shape)
      tile_shape = _np.minimum(array_shape, tile_sub_tile_split_shape * sub_tile_shape)
      logger.debug("pre cannonicalise tile_shape=%s", tile_shape)
  
      tile_split_shape = ((array_shape - 1) // tile_shape) + 1
      logger.debug("tile_split_shape=%s", tile_split_shape)
  
      tile_shape = (((array_sub_tile_split_shape - 1) // tile_split_shape) + 1) * sub_tile_shape
      logger.debug("post cannonicalise tile_shape=%s", tile_shape)
  
      return tile_shape