def _bisect_left(
        seq,
        tgt,
        key_func,
        lower_search_bound=0,
        upper_search_bound=None
):
    """Return the index of the last item in seq such that all e in seq[:index]
    have key_func(e) < tgt, and all e in seq[index:] have key_func(e) >= tgt.

    Thus, seq.insert(index, value) will insert value before the leftmost item
    such that meets the above condition.

    lower_search_bound and upper_search_bound bound the slice to be searched.

    Assumes that seq is already sorted.
    """

    if lower_search_bound < 0:
        raise ValueError('lower_search_bound must be non-negative')

    if upper_search_bound is None:
        upper_search_bound = len(seq)

    while lower_search_bound < upper_search_bound:
        midpoint_index = (lower_search_bound + upper_search_bound) // 2

        if key_func(seq[midpoint_index]) < tgt:
            lower_search_bound = midpoint_index + 1
        else:
            upper_search_bound = midpoint_index

    return lower_search_bound