def get_intervals(indices):
    """Yield intervals between gaps in indices as 2-tuples.

    Works by yielding the different intervals contained
    in the given indices after sorting them. The intervals
    are defined by the gaps between them (differences of
    more than one unit).

    Parameters
    ==========
    indices (iterable/iterator of integers)
        indices to be separated into intervals by the
        gaps between them (differences of more than one
        unit)

    For instance...

    >>> # here we give the indices already sorted
    >>> list(get_intervals([3, 4, 5, 6, 22, 23, 24, 33]))
    [(3, 7), (22, 25), (33, 34)]

    >>> # but it isn't required...
    >>> list(get_intervals([3, 23, 5, 6, 22, 33, 4, 24]))
    [(3, 7), (22, 25), (33, 34)]

    >>> # if you provide an empty iterable, nothing is
    >>> # yielded...
    >>> list(get_intervals([]));
    []

    The tuples represent, in math notation, [a, b[
    intervals, that is, intervals where a <= x < b,
    also called left-closed, right-open intervals.

    Those intevals are useful, for instance, to create
    range() or slice() objects.
    """
    ### sort indices, obtaining an iterator from them
    indices_iterator = iter(sorted(indices))

    ### assign the first index from the iterator as the
    ### left boundary of an interval
    ###
    ### note that it is ok if a StopIteration exception
    ### is raised here, when next() is executed, because
    ### raising such kind of exception is valid behaviour
    ### inside generators
    including_left_boundary = next(indices_iterator)

    ### we expect the next index from the iterator to
    ### be equal to the current index (the left boundary)
    ### plus one unit, so we define a variable representing
    ### this
    expected_index = including_left_boundary + 1

    ### iterate over the remaining indices in the iterator,
    ### yielding intervals every time you find gaps between
    ### the indices (differences of more than one unit)

    for current_index in indices_iterator:

        ### if the index isn't equal to the expected value
        ### (which is one unit higher than the previous
        ### index), then it can only be higher, since we
        ### assume the indices to be sorted; this means we
        ### have a gap here;
        ###
        ### in this case we yield the current interval
        ### and set a new including_left_boundary for the
        ### next time we yield another interval

        if current_index != expected_index:

            ### yield the 2 indices representing the
            ### boundaries of the current interval (the
            ### left boundary and the expected index,
            ### which work as the excluding right boundary);
            yield (including_left_boundary, expected_index)

            ### we also set the current index as the left
            ### boundary, so it is used for the next
            ### interval (once we find another gap again)
            including_left_boundary = current_index

        ### update the expected index, which is always the
        ### current index plus one unit
        expected_index = current_index + 1

    ### once you leave the "for loop", you'll still have
    ### a including_left_boundary set, waiting for its
    ### right boundary;
    ###
    ### thus we use the expected index as such excluding
    ### right boundary, yielding the last interval
    yield (including_left_boundary, expected_index)
Yield intervals between gaps in indices as 2-tuples.

    Works by yielding the different intervals contained
    in the given indices after sorting them. The intervals
    are defined by the gaps between them (differences of
    more than one unit).

    Parameters
    ==========
    indices (iterable/iterator of integers)
        indices to be separated into intervals by the
        gaps between them (differences of more than one
        unit)

    For instance...

    >>> # here we give the indices already sorted
    >>> list(get_intervals([3, 4, 5, 6, 22, 23, 24, 33]))
    [(3, 7), (22, 25), (33, 34)]

    >>> # but it isn't required...
    >>> list(get_intervals([3, 23, 5, 6, 22, 33, 4, 24]))
    [(3, 7), (22, 25), (33, 34)]

    >>> # if you provide an empty iterable, nothing is
    >>> # yielded...
    >>> list(get_intervals([]));
    []

    The tuples represent, in math notation, [a, b[
    intervals, that is, intervals where a <= x < b,
    also called left-closed, right-open intervals.

    Those intevals are useful, for instance, to create
    range() or slice() objects.
    
nodezator/ourstdlibs/mathutils.py