from typing import Callable


def exp_decay_with_warmup(warmup: int, gamma: float, min_val: float = 1e-8) -> Callable:
    """Returns exponential decay with warmup function.

    The function increases linearly from ``min_val`` to 1.0 until ``step`` is equal
    to warmup. For a ``step`` larger than ``warmup``, the function decays with a
    given ``gamma`` (last_val * gamma).

    Parameters
    ----------
    warmup
        The number of steps until which the function increases linearly.
    gamma
        The parameter of decay in (0, 1). Large numbers for slow decay.
    min_val
        The minimum lr factor that is used for the 0-th step, a small number > 0.

    Returns
    ----------
    A function taking the current step as single argument.

    """

    def f(x):
        return min_val + x * (1.0 - min_val) / warmup if x < warmup else gamma ** (x - warmup)

    return f