def slice_alike_object_to_indices(
    slice_alike_object: Union[slice, int, Iterable[int], Tensor, ndarray],
    max_length: int,
) -> Iterable[int]:
    """
    Utility function used to obtain the sequence of indices given a slice
    object.

    This fuction offers some additional flexibility by also accepting generic
    Iterable[int], PyTorch Tensor and NumPy ndarray.

    Beware that this function only supports 1-D slicing.

    This will also take care of managing negative indices.

    If the input object is a native slice or int, then negative indices will be
    managed as usual (like when used on a native Python list). If a tensor or
    generic iterable is passed, then indices will be transformed as they where
    int(s).
    """

    indexes_iterator: Iterable[int]
    check_bounds = True

    # Makes dataset sliceable
    if isinstance(slice_alike_object, (int, np.integer)):
        # Single index (Python native or NumPy)
        indexes_iterator = [int(slice_alike_object)]
    elif isinstance(slice_alike_object, slice):
        # Slice object (Python native)
        indexes_iterator = range(*slice_alike_object.indices(max_length))
        check_bounds = False
    elif hasattr(slice_alike_object, "shape"):
        tensor_shape = getattr(slice_alike_object, "shape")
        if len(tensor_shape) == 0:
            # Manages 0-d ndarray / Tensor
            indexes_iterator = [int(slice_alike_object)]  # type: ignore
        else:
            if len(tensor_shape) == 1:
                if tensor_shape[0] == 0:
                    # Empty Tensor (NumPy or PyTorch)
                    indexes_iterator = []
                else:
                    # Flat Tensor (NumPy or PyTorch)
                    indexes_iterator = slice_alike_object.tolist()  # type: ignore
            else:
                # Last attempt
                indexes_iterator = [slice_alike_object.item()]  # type: ignore
            if len(indexes_iterator) > 0:  # type: ignore
                assert isinstance(indexes_iterator, int)
    else:
        # Generic iterable
        indexes_iterator = slice_alike_object  # type: ignore

    if check_bounds:
        # Executed only if slice_alike_object is not a slice
        # (because "slice" already takes care of managing negative indices)

        # This will:
        # 1) transform negative indices to positive indices
        # 2) check that max(indices) < max_length
        # 3) check that min(indices) >= 0

        iterator_as_list = []
        for idx in indexes_iterator:
            assert isinstance(idx, int)
            if idx >= 0:
                if idx >= max_length:
                    raise IndexError(
                        f"Index {idx} out of range for sequence "
                        f"of length {max_length}"
                    )
            else:
                pos_idx = max_length - idx  # Negative to positive
                if pos_idx < 0:
                    raise IndexError(
                        f"Index {idx} out of range for sequence "
                        f"of length {max_length}"
                    )
                idx = pos_idx

            iterator_as_list.append(idx)
        indexes_iterator = iterator_as_list

    return indexes_iterator  # type: ignore

    Utility function used to obtain the sequence of indices given a slice
    object.

    This fuction offers some additional flexibility by also accepting generic
    Iterable[int], PyTorch Tensor and NumPy ndarray.

    Beware that this function only supports 1-D slicing.

    This will also take care of managing negative indices.

    If the input object is a native slice or int, then negative indices will be
    managed as usual (like when used on a native Python list). If a tensor or
    generic iterable is passed, then indices will be transformed as they where
    int(s).
    
avalanche/benchmarks/utils/dataset_utils.py