docs: def envelop(self, begin, end=None):
          """
          Returns the set of all intervals fully contained in the range
          [begin, end).
  
          Completes in O(m + k*log n) time, where:
            * n = size of the tree
            * m = number of matches
            * k = size of the search range
          :rtype: set of Interval
          """
          root = self.top_node
          if not root:
              return set()
          if end is None:
              iv = begin
              return self.envelop(iv.begin, iv.end)
          elif begin >= end:
              return set()
          result = root.search_point(begin, set()) # bound_begin might be greater
          boundary_table = self.boundary_table
          bound_begin = boundary_table.bisect_left(begin)
          bound_end = boundary_table.bisect_left(end)  # up to, but not including end
          result.update(root.search_overlap(
              # slice notation is slightly slower
              boundary_table.keys()[index] for index in xrange(bound_begin, bound_end)
          ))
  
          # TODO: improve envelop() to use node info instead of less-efficient filtering
          result = set(
              iv for iv in result
              if iv.begin >= begin and iv.end <= end
          )
          return result