def insert(
            self,
            index: int,
            moment_or_operation_tree: Union[ops.Moment, ops.OP_TREE],
            strategy: InsertStrategy = InsertStrategy.EARLIEST) -> int:
        """ Inserts operations into the circuit.
            Operations are inserted into the moment specified by the index and
            'InsertStrategy'.
            Moments within the operation tree are inserted intact.

        Args:
            index: The index to insert all of the operations at.
            moment_or_operation_tree: The moment or operation tree to insert.
            strategy: How to pick/create the moment to put operations into.

        Returns:
            The insertion index that will place operations just after the
            operations that were inserted by this method.

        Raises:
            ValueError: Bad insertion strategy.
        """
        moments_and_operations = list(ops.flatten_op_tree(
            ops.transform_op_tree(moment_or_operation_tree,
                                  self._device.decompose_operation,
                                  preserve_moments=True),
            preserve_moments=True))

        for moment_or_op in moments_and_operations:
            if isinstance(moment_or_op, ops.Moment):
                self._device.validate_moment(cast(ops.Moment, moment_or_op))
            else:
                self._device.validate_operation(
                    cast(ops.Operation, moment_or_op))

        # limit index to 0..len(self._moments), also deal with indices smaller 0
        k = max(min(index if index >= 0 else len(self._moments) + index,
                    len(self._moments)), 0)
        for moment_or_op in moments_and_operations:
            if isinstance(moment_or_op, ops.Moment):
                self._moments.insert(k, moment_or_op)
                k += 1
            else:
                p = self._pick_or_create_inserted_op_moment_index(
                    k, moment_or_op, strategy)
                while p >= len(self._moments):
                    self._moments.append(ops.Moment())
                self._moments[p] = self._moments[p].with_operation(moment_or_op)
                self._device.validate_moment(self._moments[p])
                k = max(k, p + 1)
                if strategy is InsertStrategy.NEW_THEN_INLINE:
                    strategy = InsertStrategy.INLINE
        return k