nodes: 0, module; 1, function_definition; 2, function_name:partition_range; 3, parameters; 4, block; 5, identifier:stop; 6, default_parameter; 7, expression_statement; 8, expression_statement; 9, expression_statement; 10, expression_statement; 11, comment:# We loop over the range, only touching positions where levels potentially; 12, comment:# change.; 13, for_statement; 14, return_statement; 15, identifier:annotations; 16, None; 17, comment:"""
      Partition the range from 0 to `stop` based on annotations.
  
          >>> partition_range(50, annotations=[[(0, 21), (30, 35)],
          ...                                  [(15, 32), (40, 46)]])
          [(0, 15, {0}),
           (15, 21, {0, 1}),
           (21, 30, {1}),
           (30, 32, {0, 1}),
           (32, 35, {0}),
           (35, 40, set()),
           (40, 46, {1}),
           (46, 50, set())]
  
      :arg stop: End point (not included) of the range (similar to the `stop`
          argument of the built-in :func:`range` function).
      :type stop: int
      :arg annotations: For each annotation level, a list of (`start`, `stop`)
          pairs defining an annotated region.
      :type annotations: list
  
      :return: Partitioning of the range as (`start`, `stop`, `levels`) tuples
          defining a region with a set of annotation levels.
      :rtype: list
  
      All regions (`start`, `stop`) are defined as in slicing notation, so
      zero-based and `stop` is not included.
  
      The `annotations` argument is a list of annotations. An annotation is a
      list of regions as (`start`, `stop`) tuples. The level of each annotation
      is its index in `annotations`.
  
      Annotation regions can overlap (overlap within one level is ignored) and
      do not need to be sorted.
      """; 18, assignment; 19, assignment; 20, assignment; 21, identifier:p; 22, call; 23, block; 24, identifier:partitioning; 25, identifier:annotations; 26, boolean_operator; 27, identifier:partitioning; 28, list; 29, pattern_list; 30, expression_list; 31, identifier:sorted; 32, argument_list; 33, if_statement; 34, comment:# Annotation levels for position p.; 35, expression_statement; 36, if_statement; 37, if_statement; 38, identifier:annotations; 39, list; 40, identifier:part_start; 41, identifier:part_levels; 42, integer:0; 43, None; 44, call; 45, comparison_operator:p == stop; 46, block; 47, assignment; 48, comparison_operator:p == 0; 49, block; 50, comparison_operator:levels != part_levels; 51, block; 52, identifier:set; 53, argument_list; 54, identifier:p; 55, identifier:stop; 56, expression_statement; 57, break_statement; 58, identifier:levels; 59, set_comprehension; 60, identifier:p; 61, integer:0; 62, expression_statement; 63, continue_statement; 64, identifier:levels; 65, identifier:part_levels; 66, expression_statement; 67, expression_statement; 68, call; 69, call; 70, identifier:level; 71, for_in_clause; 72, if_clause; 73, assignment; 74, call; 75, assignment; 76, attribute; 77, argument_list; 78, attribute; 79, argument_list; 80, pattern_list; 81, call; 82, call; 83, identifier:part_levels; 84, identifier:levels; 85, attribute; 86, argument_list; 87, pattern_list; 88, expression_list; 89, identifier:itertools; 90, identifier:chain; 91, list; 92, list_splat; 93, identifier:partitioning; 94, identifier:append; 95, tuple; 96, identifier:level; 97, identifier:regions; 98, identifier:enumerate; 99, argument_list; 100, identifier:any; 101, generator_expression; 102, identifier:partitioning; 103, identifier:append; 104, tuple; 105, identifier:part_start; 106, identifier:part_levels; 107, identifier:p; 108, identifier:levels; 109, integer:0; 110, identifier:stop; 111, call; 112, identifier:part_start; 113, identifier:p; 114, identifier:part_levels; 115, identifier:annotations; 116, comparison_operator:x <= p < y; 117, for_in_clause; 118, identifier:part_start; 119, identifier:p; 120, identifier:part_levels; 121, attribute; 122, argument_list; 123, identifier:x; 124, identifier:p; 125, identifier:y; 126, pattern_list; 127, identifier:regions; 128, identifier:itertools; 129, identifier:chain; 130, list_splat; 131, identifier:x; 132, identifier:y; 133, identifier:annotations
code: def partition_range(stop, annotations=None):
      """
      Partition the range from 0 to `stop` based on annotations.
  
          >>> partition_range(50, annotations=[[(0, 21), (30, 35)],
          ...                                  [(15, 32), (40, 46)]])
          [(0, 15, {0}),
           (15, 21, {0, 1}),
           (21, 30, {1}),
           (30, 32, {0, 1}),
           (32, 35, {0}),
           (35, 40, set()),
           (40, 46, {1}),
           (46, 50, set())]
  
      :arg stop: End point (not included) of the range (similar to the `stop`
          argument of the built-in :func:`range` function).
      :type stop: int
      :arg annotations: For each annotation level, a list of (`start`, `stop`)
          pairs defining an annotated region.
      :type annotations: list
  
      :return: Partitioning of the range as (`start`, `stop`, `levels`) tuples
          defining a region with a set of annotation levels.
      :rtype: list
  
      All regions (`start`, `stop`) are defined as in slicing notation, so
      zero-based and `stop` is not included.
  
      The `annotations` argument is a list of annotations. An annotation is a
      list of regions as (`start`, `stop`) tuples. The level of each annotation
      is its index in `annotations`.
  
      Annotation regions can overlap (overlap within one level is ignored) and
      do not need to be sorted.
      """
      annotations = annotations or []
  
      partitioning = []
      part_start, part_levels = 0, None
  
      # We loop over the range, only touching positions where levels potentially
      # change.
      for p in sorted(set(itertools.chain([0, stop],
                                          *itertools.chain(*annotations)))):
          if p == stop:
              partitioning.append( (part_start, p, part_levels) )
              break
  
          # Annotation levels for position p.
          levels = {level for level, regions in enumerate(annotations)
                    if any(x <= p < y for x, y in regions)}
  
          if p == 0:
              part_levels = levels
              continue
  
          if levels != part_levels:
              partitioning.append( (part_start, p, part_levels) )
              part_start, part_levels = p, levels
  
      return partitioning