# Copyright (C) 2022 Exaloop Inc. <https://exaloop.io>

def _insertion_sort(
    arr: Array[T], begin: int, end: int, keyf: Callable[[T], S], T: type, S: type
):
    i = begin + 1
    while i < end:
        x = arr[i]
        j = i - 1
        while j >= begin and keyf(x) < keyf(arr[j]):
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = x
        i += 1

def insertion_sort_array(
    collection: Array[T], size: int, keyf: Callable[[T], S], T: type, S: type
):
    """
    Insertion Sort
    Sorts the array inplace.
    """
    _insertion_sort(collection, 0, size, keyf)

def insertion_sort_inplace(
    collection: List[T], keyf: Callable[[T], S], T: type, S: type
):
    """
    Insertion Sort
    Sorts the list inplace.
    """
    insertion_sort_array(collection.arr, collection.len, keyf)

def insertion_sort(
    collection: List[T], keyf: Callable[[T], S], T: type, S: type
) -> List[T]:
    """
    Insertion Sort
    Returns the sorted list.
    """
    newlst = collection.__copy__()
    insertion_sort_inplace(newlst, keyf)
    return newlst
