//  [python]
def _merge_intervals(
        self, covered: np.ndarray, coords: list[obspy.UTCDateTime], min_length_s: float
    ) -> tuple[np.ndarray, list[obspy.UTCDateTime]]:
        # Goal: Maximize "stations * time" while ensuring no segment is too short
        # Use a greedy approach for maximizing which will not always lead to the globally optimal results
        # but usually to reasonably good results.
        has_warned = np.zeros(1, dtype=bool)

        def encompassing_interval(t0: obspy.UTCDateTime, t1: obspy.UTCDateTime):
            p0 = self._bin_search_idx(coords, t0)
            # Move index to actual left border of this segment
            while p0 > 0:
                if (covered[:, p0 - 1] == covered[:, p0]).all():
                    p0 -= 1
                else:
                    break

            p1 = self._bin_search_idx(coords, t1)
            if coords[p1] != t1:
                # This ensures that coords[p1] is greater or equal than t1
                p1 += 1
            # Move index to actual right border of this segment
            while p1 < covered.shape[-1] - 1:
                if (covered[:, p1 + 1] == covered[:, p1]).all():
                    p1 += 1
                else:
                    break

            return p0, p1

        def merge_costs(t0: obspy.UTCDateTime, t1: obspy.UTCDateTime) -> float:
            if t0 < coords[0] or t1 > coords[-1]:
                # The interval is not actually fully covered
                return np.inf

            p0, p1 = encompassing_interval(t0, t1)

            cost = 0

            merged_cover = np.all(
                covered[:, p0:p1], axis=1
            )  # Stations present in the whole interval
            if (
                not merged_cover.any()
            ):  # This is never better than just deleting the center interval
                return np.inf

            for p in range(p0, p1):
                if t0 - coords[p] > min_length_s:  # Left border profits from splitting
                    delta_t = t0 - (coords[p + 1] - min_length_s)
                elif (
                    coords[p + 1] - t1 > min_length_s
                ):  # Right border profits from splitting
                    delta_t = t1 - (coords[p] + min_length_s)
                else:
                    delta_t = coords[p + 1] - coords[p]
                delta_sta = np.sum(covered[:, p]) - np.sum(merged_cover)
                cost += delta_t * delta_sta

            return cost

        def merge_interval(t0: obspy.UTCDateTime, t1: obspy.UTCDateTime) -> None:
            p0, p1 = encompassing_interval(t0, t1)
            merged_cover = np.all(covered[:, p0:p1], axis=1)
            for p in range(p0, p1):
                if t0 - coords[p] > min_length_s:  # Left border profits from splitting
                    coords[p + 1] = t0  # End interval earlier
                elif (
                    coords[p + 1] - t1 > min_length_s
                ):  # Right border profits from splitting
                    coords[p] = t1  # Start interval later
                else:
                    covered[:, p] = merged_cover

        def fix_interval_if_necessary(act: np.ndarray, p0: int, p1: int):
            t0 = coords[p0]
            t1 = coords[p1]

            if act.any() and t1 - t0 < min_length_s:
                # Fixing required
                # Iterate over all reasonable merging times and find the cheapest
                # Reasonable merge intervals:
                # - every interval covering the target, starting either at a coord to the left
                #   or ending at a coord to the right
                # - if corner interval has at least min_length_s left, make new coord
                # - else, just merge intervals

                if not has_warned[0]:
                    has_warned[0] = True
                    seisbench.logger.warning(
                        "Parts of the input stream consist of fragments shorter than the number "
                        "of input samples or misaligned traces. Output might be empty."
                    )

                candidate_starts = []
                for p in range(p0, -1, -1):
                    if t1 - coords[p] > min_length_s:
                        break
                    candidate_starts.append(coords[p])
                for p in range(p0 + 1, len(coords)):
                    if coords[p] - t0 > min_length_s:
                        break
                    candidate_starts.append(coords[p] - min_length_s)

                candidate_starts = np.unique(candidate_starts)

                candidate_merge_costs = [
                    merge_costs(t, t + min_length_s) for t in candidate_starts
                ]

                if np.isinf(np.min(candidate_merge_costs)):
                    # Only delete if there is no other option.
                    # This improves coverage over time
                    act &= False
                else:
                    best_merge = np.argmin(candidate_merge_costs)
                    merge_interval(
                        candidate_starts[best_merge],
                        candidate_starts[best_merge] + min_length_s,
                    )

        act = covered[:, 0]
        start_i = 0

        for i in range(1, covered.shape[1]):
            if np.all(act == covered[:, i]):
                # Same station coverage in both blocks, merge the intervals
                continue
            else:
                fix_interval_if_necessary(act, start_i, i)

                start_i = i
                act = covered[:, i]

        fix_interval_if_necessary(act, start_i, covered.shape[1])

        return covered, coords