<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def batch_insert(self, insertions: Iterable[Tuple[int, ops.OP_TREE]]) -> None: """Applies a batched insert operation to the circuit. Transparently handles the fact that earlier insertions may shift the index that later insertions should occur at. For example, if you insert an operation at index 2 and at index 4, but the insert at index 2 causes a new moment to be created, then the insert at "4" will actually occur at index 5 to account for the shift from the new moment. All insertions are done with the strategy 'EARLIEST'. When multiple inserts occur at the same index, the gates from the later inserts end up before the gates from the earlier inserts (exactly as if you'd called list.insert several times with the same index: the later inserts shift the earliest inserts forward). Args: insertions: A sequence of (insert_index, operations) pairs indicating operations to add into the circuit at specific places. """

        # Work on a copy in case validation fails halfway through.
        copy = self.copy()
        shift = 0
        # Note: python `sorted` is guaranteed to be stable. This matters.
        insertions = sorted(insertions, key=lambda e: e[0])
        groups = _group_until_different(insertions,
                                        key=lambda e: e[0],
                                        value=lambda e: e[1])
        for i, group in groups:
            insert_index = i + shift
            next_index = copy.insert(insert_index,
                                     reversed(group),
                                     InsertStrategy.EARLIEST)
            if next_index > insert_index:
                shift += next_index - insert_index
        self._moments = copy._moments