Decompose vectorized 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 loaded on-memory np.ndarray.

    Parameters
    ----------
    indexer: VectorizedIndexer
    indexing_support: one of IndexerSupport entries

    Returns
    -------
    backend_indexer: OuterIndexer or BasicIndexer
    np_indexers: an ExplicitIndexer (VectorizedIndexer / 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 vectorized indexer ([0, 3, 1], [2, 3, 2]).
    Even if the backend array only supports outer indexing, it is more
    efficient to load a subslice of the array than loading the entire array,

    >>> backend_indexer = OuterIndexer([0, 1, 3], [2, 3])
    >>> array = array[backend_indexer]  # load subslice of the array
    >>> np_indexer = VectorizedIndexer([0, 2, 1], [0, 1, 0])
    >>> array[np_indexer]  # vectorized indexing for on-memory np.ndarray.

def _decompose_vectorized_indexer(indexer, shape, indexing_support):
    """
    Decompose vectorized 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 loaded on-memory np.ndarray.

    Parameters
    ----------
    indexer: VectorizedIndexer
    indexing_support: one of IndexerSupport entries

    Returns
    -------
    backend_indexer: OuterIndexer or BasicIndexer
    np_indexers: an ExplicitIndexer (VectorizedIndexer / 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 vectorized indexer ([0, 3, 1], [2, 3, 2]).
    Even if the backend array only supports outer indexing, it is more
    efficient to load a subslice of the array than loading the entire array,

    >>> backend_indexer = OuterIndexer([0, 1, 3], [2, 3])
    >>> array = array[backend_indexer]  # load subslice of the array
    >>> np_indexer = VectorizedIndexer([0, 2, 1], [0, 1, 0])
    >>> array[np_indexer]  # vectorized indexing for on-memory np.ndarray.
    """
    assert isinstance(indexer, VectorizedIndexer)

    if indexing_support is IndexingSupport.VECTORIZED:
        return indexer, BasicIndexer(())

    backend_indexer = []
    np_indexer = []
    # convert negative indices
    indexer = [np.where(k < 0, k + s, k) if isinstance(k, np.ndarray) else k
               for k, s in zip(indexer.tuple, shape)]

    for k, s in zip(indexer, shape):
        if isinstance(k, slice):
            # If it is a slice, then we will slice it as-is
            # (but make its step positive) in the backend,
            # and then use all of it (slice(None)) for the in-memory portion.
            bk_slice, np_slice = _decompose_slice(k, s)
            backend_indexer.append(bk_slice)
            np_indexer.append(np_slice)
        else:
            # If it is a (multidimensional) np.ndarray, just pickup the used
            # keys without duplication and store them as a 1d-np.ndarray.
            oind, vind = np.unique(k, return_inverse=True)
            backend_indexer.append(oind)
            np_indexer.append(vind.reshape(*k.shape))

    backend_indexer = OuterIndexer(tuple(backend_indexer))
    np_indexer = VectorizedIndexer(tuple(np_indexer))

    if indexing_support is IndexingSupport.OUTER:
        return backend_indexer, np_indexer

    # If the backend does not support outer indexing,
    # backend_indexer (OuterIndexer) is also decomposed.
    backend_indexer, np_indexer1 = _decompose_outer_indexer(
        backend_indexer, shape, indexing_support)
    np_indexer = _combine_indexers(np_indexer1, shape, np_indexer)
    return backend_indexer, np_indexer