from __future__ import annotations import unittest from typing import Any, Protocol, Sequence, TypeVar class SupportsLessThan(Protocol): def __lt__(self, other: Any) -> bool: ... T = TypeVar("T", bound=SupportsLessThan) def merge_sort(items: Sequence[T]) -> list[T]: """Return a sorted copy of items using merge sort.""" if len(items) <= 1: return list(items) middle = len(items) // 2 left = merge_sort(items[:middle]) right = merge_sort(items[middle:]) return _merge(left, right) def _merge(left: Sequence[T], right: Sequence[T]) -> list[T]: """Merge two sorted sequences into one sorted list.""" merged: list[T] = [] left_index = 0 right_index = 0 # Prefer the left item on ties to keep the sort stable. while left_index < len(left) and right_index < len(right): if right[right_index] < left[left_index]: merged.append(right[right_index]) right_index += 1 else: merged.append(left[left_index]) left_index += 1 merged.extend(left[left_index:]) merged.extend(right[right_index:]) return merged class TestMergeSort(unittest.TestCase): def test_sorts_unsorted_numbers(self) -> None: self.assertEqual(merge_sort([5, 2, 9, 1, 5, 6]), [1, 2, 5, 5, 6, 9]) def test_handles_empty_and_single_item_lists(self) -> None: self.assertEqual(merge_sort([]), []) self.assertEqual(merge_sort([42]), [42]) def test_keeps_original_sequence_unchanged(self) -> None: values = [3, 1, 2] self.assertEqual(merge_sort(values), [1, 2, 3]) self.assertEqual(values, [3, 1, 2]) def test_sorts_strings(self) -> None: self.assertEqual(merge_sort(["pear", "apple", "orange"]), ["apple", "orange", "pear"]) if __name__ == "__main__": unittest.main()