docs: def _decompose_outer_indexer(indexer, shape, indexing_support):
      """
      Decompose outer indexer to the successive two indexers, where the
      first indexer will be used to index backend arrays, while the second one
      is used to index the loaded on-memory np.ndarray.
  
      Parameters
      ----------
      indexer: VectorizedIndexer
      indexing_support: One of the entries of IndexingSupport
  
      Returns
      -------
      backend_indexer: OuterIndexer or BasicIndexer
      np_indexers: an ExplicitIndexer (OuterIndexer / BasicIndexer)
  
      Notes
      -----
      This function is used to realize the vectorized indexing for the backend
      arrays that only support basic or outer indexing.
  
      As an example, let us consider to index a few elements from a backend array
      with a orthogonal indexer ([0, 3, 1], [2, 3, 2]).
      Even if the backend array only supports basic indexing, it is more
      efficient to load a subslice of the array than loading the entire array,
  
      >>> backend_indexer = BasicIndexer(slice(0, 3), slice(2, 3))
      >>> array = array[backend_indexer]  # load subslice of the array
      >>> np_indexer = OuterIndexer([0, 2, 1], [0, 1, 0])
      >>> array[np_indexer]  # outer indexing for on-memory np.ndarray.
      """
      if indexing_support == IndexingSupport.VECTORIZED:
          return indexer, BasicIndexer(())
      assert isinstance(indexer, (OuterIndexer, BasicIndexer))
  
      backend_indexer = []
      np_indexer = []
      # make indexer positive
      pos_indexer = []
      for k, s in zip(indexer.tuple, shape):
          if isinstance(k, np.ndarray):
              pos_indexer.append(np.where(k < 0, k + s, k))
          elif isinstance(k, integer_types) and k < 0:
              pos_indexer.append(k + s)
          else:
              pos_indexer.append(k)
      indexer = pos_indexer
  
      if indexing_support is IndexingSupport.OUTER_1VECTOR:
          # some backends such as h5py supports only 1 vector in indexers
          # We choose the most efficient axis
          gains = [(np.max(k) - np.min(k) + 1.0) / len(np.unique(k))
                   if isinstance(k, np.ndarray) else 0 for k in indexer]
          array_index = np.argmax(np.array(gains)) if len(gains) > 0 else None
  
          for i, (k, s) in enumerate(zip(indexer, shape)):
              if isinstance(k, np.ndarray) and i != array_index:
                  # np.ndarray key is converted to slice that covers the entire
                  # entries of this key.
                  backend_indexer.append(slice(np.min(k), np.max(k) + 1))
                  np_indexer.append(k - np.min(k))
              elif isinstance(k, np.ndarray):
                  # Remove duplicates and sort them in the increasing order
                  pkey, ekey = np.unique(k, return_inverse=True)
                  backend_indexer.append(pkey)
                  np_indexer.append(ekey)
              elif isinstance(k, integer_types):
                  backend_indexer.append(k)
              else:  # slice:  convert positive step slice for backend
                  bk_slice, np_slice = _decompose_slice(k, s)
                  backend_indexer.append(bk_slice)
                  np_indexer.append(np_slice)
  
          return (OuterIndexer(tuple(backend_indexer)),
                  OuterIndexer(tuple(np_indexer)))
  
      if indexing_support == IndexingSupport.OUTER:
          for k, s in zip(indexer, shape):
              if isinstance(k, slice):
                  # slice:  convert positive step slice for backend
                  bk_slice, np_slice = _decompose_slice(k, s)
                  backend_indexer.append(bk_slice)
                  np_indexer.append(np_slice)
              elif isinstance(k, integer_types):
                  backend_indexer.append(k)
              elif isinstance(k, np.ndarray) and (np.diff(k) >= 0).all():
                  backend_indexer.append(k)
                  np_indexer.append(slice(None))
              else:
                  # Remove duplicates and sort them in the increasing order
                  oind, vind = np.unique(k, return_inverse=True)
                  backend_indexer.append(oind)
                  np_indexer.append(vind.reshape(*k.shape))
  
          return (OuterIndexer(tuple(backend_indexer)),
                  OuterIndexer(tuple(np_indexer)))
  
      # basic indexer
      assert indexing_support == IndexingSupport.BASIC
  
      for k, s in zip(indexer, shape):
          if isinstance(k, np.ndarray):
              # np.ndarray key is converted to slice that covers the entire
              # entries of this key.
              backend_indexer.append(slice(np.min(k), np.max(k) + 1))
              np_indexer.append(k - np.min(k))
          elif isinstance(k, integer_types):
              backend_indexer.append(k)
          else:  # slice:  convert positive step slice for backend
              bk_slice, np_slice = _decompose_slice(k, s)
              backend_indexer.append(bk_slice)
              np_indexer.append(np_slice)
  
      return (BasicIndexer(tuple(backend_indexer)),
              OuterIndexer(tuple(np_indexer)))