Module gatenlp.impl.sortedintvls

Module that provides a simple class that represents a collection of sorted intervals and allows for some basic interval-based operations.

Internally this stores the intervals using standard sorted lists. This is not optimal and may incur a O(n) overhead on some operations depending on the result set. It also may incur a significant overhead for creating and maintaning the sorted lists.

NOTE: this stores a tuple (start, end, annid, object) in the sorted lists for the start and end offsets and uses a key function that returns which elements to use for sorting.

For the list sorted by start offset, we use the start offset and annotation id. For the end offset we use the end offset of the annotation only.

Expand source code
"""
Module that provides a simple class that represents a collection of sorted intervals and allows for some
basic interval-based operations.

Internally this stores the intervals using standard sorted lists. This is not optimal and may
incur a O(n) overhead on some operations depending on the result set. It also may incur a significant overhead
for creating and maintaning the sorted lists.

NOTE: this stores a tuple (start, end, annid, object) in the sorted lists for the start  and end offsets
and uses a key function that returns which elements to use for sorting.

For the list sorted by start offset, we use the start offset and annotation id. For the end offset we use the
end offset of the annotation only.
"""

import sys
from sortedcontainers import SortedKeyList


class _KeyStartAnnid:
    def __call__(self, anninfo):
        return (anninfo[0], anninfo[2])


class _KeyStartEndAnnid:
    def __call__(self, anninfo):
        return (anninfo[0], anninfo[1], anninfo[2])


class _KeyEndAnnid:
    def __call__(self, anninfo):
        return (anninfo[1], anninfo[2])


class SortedIntvls:
    """ """

    def __init__(self, by_ol=False):
        """
        Create an interval index. By default, this sorts by start offset and
        annotation id. If by_ol is True, sorts by start offset, end offset and annotation id.

        Args:
            by_ol: if True, use start offset, end offset, annotation id
        """
        if by_ol:
            # we sort by increasing start offset then increasing annotation id for this
            self._key_start = _KeyStartEndAnnid()
            self._by_start = SortedKeyList(key=self._key_start)
        else:
            # we sort by increasing start offset then increasing annotation id for this
            self._key_start = _KeyStartAnnid()
            self._by_start = SortedKeyList(key=self._key_start)
        # for this we sort by end offset only
        self._key_end = _KeyEndAnnid()
        self._by_end = SortedKeyList(key=self._key_end)

    def add(self, start, end, data):
        """
        Adds an interval.
        """
        self._by_start.add((start, end, data))
        self._by_end.add((start, end, data))

    def update(self, tupleiterable):
        """
        Updates from an iterable of intervals.
        """
        self._by_start.update(tupleiterable)
        self._by_end.update(tupleiterable)

    def remove(self, start, end, data):
        """
        Removes an interval, exception if the interval does not exist.
        """
        self._by_start.remove((start, end, data))
        self._by_end.remove((start, end, data))

    def discard(self, start, end, data):
        """
        Removes and interval, do nothing if the interval does not exist.
        """
        self._by_start.discard((start, end, data))
        self._by_end.discard((start, end, data))

    def __len__(self):
        """
        Returns the number of intervals.
        """
        return len(self._by_start)

    def starting_at(self, offset):
        """
        Returns an iterable of (start, end, data) tuples where start==offset
        """
        return self._by_start.irange(
            minimum=(offset, 0, 0), maximum=(offset, sys.maxsize, sys.maxsize)
        )

    def ending_at(self, offset):
        """
        Returns an iterable of (start, end, data) tuples where end==offset
        """
        return self._by_end.irange(minimum=(0, offset, 0), maximum=(sys.maxsize, offset, sys.maxsize))

    def at(self, start, end):
        """
        Returns an iterable of tuples where start==start and end==end
        """
        for intvl in self._by_start.irange(
            minimum=(start, 0, 0), maximum=(start, sys.maxsize, sys.maxsize)
        ):
            if intvl[1] == end:
                yield intvl

    def within(self, start, end):
        """
        Returns intervals which are fully contained within start...end
        """
        # get all the intervals that start within the range, then keep those which also end within the range
        for intvl in self._by_start.irange(
            minimum=(start, 0, 0), maximum=(end, sys.maxsize, sys.maxsize)
        ):
            if intvl[1] <= end:
                yield intvl

    def starting_from(self, offset):
        """
        Returns intervals that start at or after offset.
        """
        return self._by_start.irange(minimum=(offset, 0, 0))

    def starting_before(self, offset):
        """
        Returns intervals  that start before offset.
        """
        return self._by_start.irange(maximum=(offset - 1, sys.maxsize, sys.maxsize))

    def starting_within(self, start, end):
        return self._by_start.irange(minimum=(start, 0, 0), maximum=(end - 1, sys.maxsize, sys.maxsize))

    def ending_within(self, start, end):
        return self._by_end.irange(minimum=(start+1, start+1, 0), maximum=(end, end, sys.maxsize))

    def ending_to(self, offset):
        """
        Returns intervals that end before or at the given end offset.
        """
        return self._by_end.irange(maximum=(0, offset, sys.maxsize))

    def ending_after(self, offset):
        """
        Returns intervals the end after the given offset.
        """
        return self._by_end.irange(minimum=(0, offset + 1, sys.maxsize))

    def covering(self, start, end):
        """
        Returns intervals that contain the given range.
        """
        # All intervals that start at or before the start and end at or after the end offset
        # we do this by first getting the intervals the start before or at the start
        # then filtering by end
        # NOTE: if the given range is zero length, then if the interval starts before the start
        # it must end at least 1 after the end!
        if start == end:
            for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
                if intvl[0] < start and intvl[1] > end:
                    yield intvl
                elif intvl[0] == start and intvl[1] >= end:
                    yield intvl
        else:
            for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
                if intvl[1] >= end:
                    yield intvl

    def overlapping(self, start, end):
        """
        Returns intervals that overlap with the given range.
        """
        # All intervals where the start or end offset lies within the given range.
        # This excludes the ones where the end offset is before the start or
        # where the start offset is after the end of the range.
        # Here we do this by looking at all intervals where the start offset is before the
        # end of the range. This still includes those which also end before the start of the range
        # so we check in addition that the end is larger than the start of the range.
        # NOTE: if the given range has zero length, then any annotation that starts before or at start
        # AND ends at or after start==end is overlapping
        if start == end:
            # in order to check what is overlapping with the zero length span, we need to find
            # intervals which start before the start of that span AND end at least one after that span;
            # or intervals which start at the span and end with the span or after.
            # In other words we look at all intervals which start anywhere before or at the start(=end)
            # of the span.
            # if the start of what we found is < start then end must be > start, otherwise (if starts are same)
            # end must be >= start
            for intvl in self._by_start.irange(maximum=(end, sys.maxsize, sys.maxsize)):
                if intvl[0] < start and intvl[1] > start:
                    yield intvl
                elif intvl[0] == start and intvl[1] >= start:
                    yield intvl
        else:
            # check all intervals which start before then end of the given span: if they would
            # start later, then the interval cannot overlap!
            # This inclues intervals which both start and end before the given span, so
            # we need to check that the end of the interval is at least after the
            for intvl in self._by_start.irange(maximum=(end - 1, sys.maxsize, sys.maxsize)):
                if intvl[0] == intvl[1]:  # we need to check a zero length interval
                    if intvl[0] >= start:
                        yield intvl
                else:
                    # if the interval starts
                    if intvl[1] > start:
                        yield intvl

    def firsts(self):
        """
        Yields all intervals which start at the smallest known offset.
        """
        laststart = None
        # logger.info("DEBUG: set laststart to None")
        for intvl in self._by_start.irange_key():
            # logger.info("DEBUG: checking interval {}".format(intvl))
            if laststart is None:
                laststart = intvl[0]
                # logger.info("DEBUG: setting laststart to {} and yielding {}".format(intvl[0], intvl))
                yield intvl
            elif intvl[0] == laststart:
                # logger.info("DEBUG: yielding {}".format(intvl))
                yield intvl
            else:
                # logger.info("DEBUG: returning since we got {}".format(intvl))
                return

    def lasts(self):
        """
        Yields all intervals which start at the last known start offset.
        """
        laststart = None
        for intvl in reversed(self._by_start):
            if laststart is None:
                laststart = intvl[0]
                yield intvl
            elif intvl[0] == laststart:
                yield intvl
            else:
                return

    def min_start(self):
        """
        Returns the smallest known start offset.
        """
        return self._by_start[0][0]

    def max_end(self):
        """
        Returns the biggest known end offset.
        """
        return self._by_end[-1][1]

    def irange(self, minoff=None, maxoff=None, reverse=False, inclusive=(True, True)):
        """
        Yields an iterator of intervals with a start offset between minoff and maxoff, inclusive.

        Args:
          minoff: minimum offset, default None indicates any
          maxoff: maximum offset, default None indicates any
          reverse: if `True` yield in reverse order
          inclusive: if the minoff and maxoff values should be inclusive, default is (True,True)

        Returns:

        """
        # return self._by_start.irange_key(
        #     min_key=minoff, max_key=maxoff, reverse=reverse, inclusive=inclusive
        # )
        if minoff is not None and not inclusive[0]:
            minoff += 1
        if maxoff is not None and not inclusive[1]:
            maxoff -= 1
        if minoff is not None:
            minoff = (minoff, 0, 0)
        if maxoff is not None:
            maxoff = (maxoff, sys.maxsize, sys.maxsize)
        return self._by_start.irange(
            minimum=minoff, maximum=maxoff, reverse=reverse, inclusive=inclusive
        )

    def __repr__(self):
        return "SortedIntvls({},{})".format(self._by_start, self._by_end)

Classes

class SortedIntvls (by_ol=False)

Create an interval index. By default, this sorts by start offset and annotation id. If by_ol is True, sorts by start offset, end offset and annotation id.

Args

by_ol
if True, use start offset, end offset, annotation id
Expand source code
class SortedIntvls:
    """ """

    def __init__(self, by_ol=False):
        """
        Create an interval index. By default, this sorts by start offset and
        annotation id. If by_ol is True, sorts by start offset, end offset and annotation id.

        Args:
            by_ol: if True, use start offset, end offset, annotation id
        """
        if by_ol:
            # we sort by increasing start offset then increasing annotation id for this
            self._key_start = _KeyStartEndAnnid()
            self._by_start = SortedKeyList(key=self._key_start)
        else:
            # we sort by increasing start offset then increasing annotation id for this
            self._key_start = _KeyStartAnnid()
            self._by_start = SortedKeyList(key=self._key_start)
        # for this we sort by end offset only
        self._key_end = _KeyEndAnnid()
        self._by_end = SortedKeyList(key=self._key_end)

    def add(self, start, end, data):
        """
        Adds an interval.
        """
        self._by_start.add((start, end, data))
        self._by_end.add((start, end, data))

    def update(self, tupleiterable):
        """
        Updates from an iterable of intervals.
        """
        self._by_start.update(tupleiterable)
        self._by_end.update(tupleiterable)

    def remove(self, start, end, data):
        """
        Removes an interval, exception if the interval does not exist.
        """
        self._by_start.remove((start, end, data))
        self._by_end.remove((start, end, data))

    def discard(self, start, end, data):
        """
        Removes and interval, do nothing if the interval does not exist.
        """
        self._by_start.discard((start, end, data))
        self._by_end.discard((start, end, data))

    def __len__(self):
        """
        Returns the number of intervals.
        """
        return len(self._by_start)

    def starting_at(self, offset):
        """
        Returns an iterable of (start, end, data) tuples where start==offset
        """
        return self._by_start.irange(
            minimum=(offset, 0, 0), maximum=(offset, sys.maxsize, sys.maxsize)
        )

    def ending_at(self, offset):
        """
        Returns an iterable of (start, end, data) tuples where end==offset
        """
        return self._by_end.irange(minimum=(0, offset, 0), maximum=(sys.maxsize, offset, sys.maxsize))

    def at(self, start, end):
        """
        Returns an iterable of tuples where start==start and end==end
        """
        for intvl in self._by_start.irange(
            minimum=(start, 0, 0), maximum=(start, sys.maxsize, sys.maxsize)
        ):
            if intvl[1] == end:
                yield intvl

    def within(self, start, end):
        """
        Returns intervals which are fully contained within start...end
        """
        # get all the intervals that start within the range, then keep those which also end within the range
        for intvl in self._by_start.irange(
            minimum=(start, 0, 0), maximum=(end, sys.maxsize, sys.maxsize)
        ):
            if intvl[1] <= end:
                yield intvl

    def starting_from(self, offset):
        """
        Returns intervals that start at or after offset.
        """
        return self._by_start.irange(minimum=(offset, 0, 0))

    def starting_before(self, offset):
        """
        Returns intervals  that start before offset.
        """
        return self._by_start.irange(maximum=(offset - 1, sys.maxsize, sys.maxsize))

    def starting_within(self, start, end):
        return self._by_start.irange(minimum=(start, 0, 0), maximum=(end - 1, sys.maxsize, sys.maxsize))

    def ending_within(self, start, end):
        return self._by_end.irange(minimum=(start+1, start+1, 0), maximum=(end, end, sys.maxsize))

    def ending_to(self, offset):
        """
        Returns intervals that end before or at the given end offset.
        """
        return self._by_end.irange(maximum=(0, offset, sys.maxsize))

    def ending_after(self, offset):
        """
        Returns intervals the end after the given offset.
        """
        return self._by_end.irange(minimum=(0, offset + 1, sys.maxsize))

    def covering(self, start, end):
        """
        Returns intervals that contain the given range.
        """
        # All intervals that start at or before the start and end at or after the end offset
        # we do this by first getting the intervals the start before or at the start
        # then filtering by end
        # NOTE: if the given range is zero length, then if the interval starts before the start
        # it must end at least 1 after the end!
        if start == end:
            for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
                if intvl[0] < start and intvl[1] > end:
                    yield intvl
                elif intvl[0] == start and intvl[1] >= end:
                    yield intvl
        else:
            for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
                if intvl[1] >= end:
                    yield intvl

    def overlapping(self, start, end):
        """
        Returns intervals that overlap with the given range.
        """
        # All intervals where the start or end offset lies within the given range.
        # This excludes the ones where the end offset is before the start or
        # where the start offset is after the end of the range.
        # Here we do this by looking at all intervals where the start offset is before the
        # end of the range. This still includes those which also end before the start of the range
        # so we check in addition that the end is larger than the start of the range.
        # NOTE: if the given range has zero length, then any annotation that starts before or at start
        # AND ends at or after start==end is overlapping
        if start == end:
            # in order to check what is overlapping with the zero length span, we need to find
            # intervals which start before the start of that span AND end at least one after that span;
            # or intervals which start at the span and end with the span or after.
            # In other words we look at all intervals which start anywhere before or at the start(=end)
            # of the span.
            # if the start of what we found is < start then end must be > start, otherwise (if starts are same)
            # end must be >= start
            for intvl in self._by_start.irange(maximum=(end, sys.maxsize, sys.maxsize)):
                if intvl[0] < start and intvl[1] > start:
                    yield intvl
                elif intvl[0] == start and intvl[1] >= start:
                    yield intvl
        else:
            # check all intervals which start before then end of the given span: if they would
            # start later, then the interval cannot overlap!
            # This inclues intervals which both start and end before the given span, so
            # we need to check that the end of the interval is at least after the
            for intvl in self._by_start.irange(maximum=(end - 1, sys.maxsize, sys.maxsize)):
                if intvl[0] == intvl[1]:  # we need to check a zero length interval
                    if intvl[0] >= start:
                        yield intvl
                else:
                    # if the interval starts
                    if intvl[1] > start:
                        yield intvl

    def firsts(self):
        """
        Yields all intervals which start at the smallest known offset.
        """
        laststart = None
        # logger.info("DEBUG: set laststart to None")
        for intvl in self._by_start.irange_key():
            # logger.info("DEBUG: checking interval {}".format(intvl))
            if laststart is None:
                laststart = intvl[0]
                # logger.info("DEBUG: setting laststart to {} and yielding {}".format(intvl[0], intvl))
                yield intvl
            elif intvl[0] == laststart:
                # logger.info("DEBUG: yielding {}".format(intvl))
                yield intvl
            else:
                # logger.info("DEBUG: returning since we got {}".format(intvl))
                return

    def lasts(self):
        """
        Yields all intervals which start at the last known start offset.
        """
        laststart = None
        for intvl in reversed(self._by_start):
            if laststart is None:
                laststart = intvl[0]
                yield intvl
            elif intvl[0] == laststart:
                yield intvl
            else:
                return

    def min_start(self):
        """
        Returns the smallest known start offset.
        """
        return self._by_start[0][0]

    def max_end(self):
        """
        Returns the biggest known end offset.
        """
        return self._by_end[-1][1]

    def irange(self, minoff=None, maxoff=None, reverse=False, inclusive=(True, True)):
        """
        Yields an iterator of intervals with a start offset between minoff and maxoff, inclusive.

        Args:
          minoff: minimum offset, default None indicates any
          maxoff: maximum offset, default None indicates any
          reverse: if `True` yield in reverse order
          inclusive: if the minoff and maxoff values should be inclusive, default is (True,True)

        Returns:

        """
        # return self._by_start.irange_key(
        #     min_key=minoff, max_key=maxoff, reverse=reverse, inclusive=inclusive
        # )
        if minoff is not None and not inclusive[0]:
            minoff += 1
        if maxoff is not None and not inclusive[1]:
            maxoff -= 1
        if minoff is not None:
            minoff = (minoff, 0, 0)
        if maxoff is not None:
            maxoff = (maxoff, sys.maxsize, sys.maxsize)
        return self._by_start.irange(
            minimum=minoff, maximum=maxoff, reverse=reverse, inclusive=inclusive
        )

    def __repr__(self):
        return "SortedIntvls({},{})".format(self._by_start, self._by_end)

Methods

def add(self, start, end, data)

Adds an interval.

Expand source code
def add(self, start, end, data):
    """
    Adds an interval.
    """
    self._by_start.add((start, end, data))
    self._by_end.add((start, end, data))
def at(self, start, end)

Returns an iterable of tuples where start==start and end==end

Expand source code
def at(self, start, end):
    """
    Returns an iterable of tuples where start==start and end==end
    """
    for intvl in self._by_start.irange(
        minimum=(start, 0, 0), maximum=(start, sys.maxsize, sys.maxsize)
    ):
        if intvl[1] == end:
            yield intvl
def covering(self, start, end)

Returns intervals that contain the given range.

Expand source code
def covering(self, start, end):
    """
    Returns intervals that contain the given range.
    """
    # All intervals that start at or before the start and end at or after the end offset
    # we do this by first getting the intervals the start before or at the start
    # then filtering by end
    # NOTE: if the given range is zero length, then if the interval starts before the start
    # it must end at least 1 after the end!
    if start == end:
        for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
            if intvl[0] < start and intvl[1] > end:
                yield intvl
            elif intvl[0] == start and intvl[1] >= end:
                yield intvl
    else:
        for intvl in self._by_start.irange(maximum=(start, sys.maxsize, sys.maxsize)):
            if intvl[1] >= end:
                yield intvl
def discard(self, start, end, data)

Removes and interval, do nothing if the interval does not exist.

Expand source code
def discard(self, start, end, data):
    """
    Removes and interval, do nothing if the interval does not exist.
    """
    self._by_start.discard((start, end, data))
    self._by_end.discard((start, end, data))
def ending_after(self, offset)

Returns intervals the end after the given offset.

Expand source code
def ending_after(self, offset):
    """
    Returns intervals the end after the given offset.
    """
    return self._by_end.irange(minimum=(0, offset + 1, sys.maxsize))
def ending_at(self, offset)

Returns an iterable of (start, end, data) tuples where end==offset

Expand source code
def ending_at(self, offset):
    """
    Returns an iterable of (start, end, data) tuples where end==offset
    """
    return self._by_end.irange(minimum=(0, offset, 0), maximum=(sys.maxsize, offset, sys.maxsize))
def ending_to(self, offset)

Returns intervals that end before or at the given end offset.

Expand source code
def ending_to(self, offset):
    """
    Returns intervals that end before or at the given end offset.
    """
    return self._by_end.irange(maximum=(0, offset, sys.maxsize))
def ending_within(self, start, end)
Expand source code
def ending_within(self, start, end):
    return self._by_end.irange(minimum=(start+1, start+1, 0), maximum=(end, end, sys.maxsize))
def firsts(self)

Yields all intervals which start at the smallest known offset.

Expand source code
def firsts(self):
    """
    Yields all intervals which start at the smallest known offset.
    """
    laststart = None
    # logger.info("DEBUG: set laststart to None")
    for intvl in self._by_start.irange_key():
        # logger.info("DEBUG: checking interval {}".format(intvl))
        if laststart is None:
            laststart = intvl[0]
            # logger.info("DEBUG: setting laststart to {} and yielding {}".format(intvl[0], intvl))
            yield intvl
        elif intvl[0] == laststart:
            # logger.info("DEBUG: yielding {}".format(intvl))
            yield intvl
        else:
            # logger.info("DEBUG: returning since we got {}".format(intvl))
            return
def irange(self, minoff=None, maxoff=None, reverse=False, inclusive=(True, True))

Yields an iterator of intervals with a start offset between minoff and maxoff, inclusive.

Args

minoff
minimum offset, default None indicates any
maxoff
maximum offset, default None indicates any
reverse
if True yield in reverse order
inclusive
if the minoff and maxoff values should be inclusive, default is (True,True)

Returns:

Expand source code
def irange(self, minoff=None, maxoff=None, reverse=False, inclusive=(True, True)):
    """
    Yields an iterator of intervals with a start offset between minoff and maxoff, inclusive.

    Args:
      minoff: minimum offset, default None indicates any
      maxoff: maximum offset, default None indicates any
      reverse: if `True` yield in reverse order
      inclusive: if the minoff and maxoff values should be inclusive, default is (True,True)

    Returns:

    """
    # return self._by_start.irange_key(
    #     min_key=minoff, max_key=maxoff, reverse=reverse, inclusive=inclusive
    # )
    if minoff is not None and not inclusive[0]:
        minoff += 1
    if maxoff is not None and not inclusive[1]:
        maxoff -= 1
    if minoff is not None:
        minoff = (minoff, 0, 0)
    if maxoff is not None:
        maxoff = (maxoff, sys.maxsize, sys.maxsize)
    return self._by_start.irange(
        minimum=minoff, maximum=maxoff, reverse=reverse, inclusive=inclusive
    )
def lasts(self)

Yields all intervals which start at the last known start offset.

Expand source code
def lasts(self):
    """
    Yields all intervals which start at the last known start offset.
    """
    laststart = None
    for intvl in reversed(self._by_start):
        if laststart is None:
            laststart = intvl[0]
            yield intvl
        elif intvl[0] == laststart:
            yield intvl
        else:
            return
def max_end(self)

Returns the biggest known end offset.

Expand source code
def max_end(self):
    """
    Returns the biggest known end offset.
    """
    return self._by_end[-1][1]
def min_start(self)

Returns the smallest known start offset.

Expand source code
def min_start(self):
    """
    Returns the smallest known start offset.
    """
    return self._by_start[0][0]
def overlapping(self, start, end)

Returns intervals that overlap with the given range.

Expand source code
def overlapping(self, start, end):
    """
    Returns intervals that overlap with the given range.
    """
    # All intervals where the start or end offset lies within the given range.
    # This excludes the ones where the end offset is before the start or
    # where the start offset is after the end of the range.
    # Here we do this by looking at all intervals where the start offset is before the
    # end of the range. This still includes those which also end before the start of the range
    # so we check in addition that the end is larger than the start of the range.
    # NOTE: if the given range has zero length, then any annotation that starts before or at start
    # AND ends at or after start==end is overlapping
    if start == end:
        # in order to check what is overlapping with the zero length span, we need to find
        # intervals which start before the start of that span AND end at least one after that span;
        # or intervals which start at the span and end with the span or after.
        # In other words we look at all intervals which start anywhere before or at the start(=end)
        # of the span.
        # if the start of what we found is < start then end must be > start, otherwise (if starts are same)
        # end must be >= start
        for intvl in self._by_start.irange(maximum=(end, sys.maxsize, sys.maxsize)):
            if intvl[0] < start and intvl[1] > start:
                yield intvl
            elif intvl[0] == start and intvl[1] >= start:
                yield intvl
    else:
        # check all intervals which start before then end of the given span: if they would
        # start later, then the interval cannot overlap!
        # This inclues intervals which both start and end before the given span, so
        # we need to check that the end of the interval is at least after the
        for intvl in self._by_start.irange(maximum=(end - 1, sys.maxsize, sys.maxsize)):
            if intvl[0] == intvl[1]:  # we need to check a zero length interval
                if intvl[0] >= start:
                    yield intvl
            else:
                # if the interval starts
                if intvl[1] > start:
                    yield intvl
def remove(self, start, end, data)

Removes an interval, exception if the interval does not exist.

Expand source code
def remove(self, start, end, data):
    """
    Removes an interval, exception if the interval does not exist.
    """
    self._by_start.remove((start, end, data))
    self._by_end.remove((start, end, data))
def starting_at(self, offset)

Returns an iterable of (start, end, data) tuples where start==offset

Expand source code
def starting_at(self, offset):
    """
    Returns an iterable of (start, end, data) tuples where start==offset
    """
    return self._by_start.irange(
        minimum=(offset, 0, 0), maximum=(offset, sys.maxsize, sys.maxsize)
    )
def starting_before(self, offset)

Returns intervals that start before offset.

Expand source code
def starting_before(self, offset):
    """
    Returns intervals  that start before offset.
    """
    return self._by_start.irange(maximum=(offset - 1, sys.maxsize, sys.maxsize))
def starting_from(self, offset)

Returns intervals that start at or after offset.

Expand source code
def starting_from(self, offset):
    """
    Returns intervals that start at or after offset.
    """
    return self._by_start.irange(minimum=(offset, 0, 0))
def starting_within(self, start, end)
Expand source code
def starting_within(self, start, end):
    return self._by_start.irange(minimum=(start, 0, 0), maximum=(end - 1, sys.maxsize, sys.maxsize))
def update(self, tupleiterable)

Updates from an iterable of intervals.

Expand source code
def update(self, tupleiterable):
    """
    Updates from an iterable of intervals.
    """
    self._by_start.update(tupleiterable)
    self._by_end.update(tupleiterable)
def within(self, start, end)

Returns intervals which are fully contained within start…end

Expand source code
def within(self, start, end):
    """
    Returns intervals which are fully contained within start...end
    """
    # get all the intervals that start within the range, then keep those which also end within the range
    for intvl in self._by_start.irange(
        minimum=(start, 0, 0), maximum=(end, sys.maxsize, sys.maxsize)
    ):
        if intvl[1] <= end:
            yield intvl