def armijo(fun, xk, xkp1, p, p_gradf, fun_xk, eta=0.5, nu=0.9):
    """ Determine step size using backtracking

        f(xk + alpha*p) <= f(xk) + alpha*nu*<p,Df>

    Args:
        fun : objective function `f`
        xk : starting position
        xkp1 : where new position `xk + alpha*p` is stored
        p : search direction
        p_gradf : local slope along the search direction `<p,Df>`
        fun_xk : starting value `f(xk)`
        eta : control parameter (shrinkage)
        nu : control parameter (sufficient decrease)

    Returns:
        Set `xkp1[:] = xk + alpha*p` and return a tuple (f_xkp1, alpha)
        f_xkp1 : new value `f(xk + alpha*p)`
        alpha : determined step size
    """
    if p_gradf >= 0:
        raise Exception("Armijo: Not a descent direction!")

    alpha = 1.0
    while True:
        xkp1[:] = xk + alpha*p
        f_xkp1 = fun(xkp1)
        if f_xkp1 <= fun_xk + alpha*nu*p_gradf:
            break
        else:
            alpha *= eta
        if alpha < 1e-10:
            raise Exception("Armijo: Not a descent direction!")
    return f_xkp1, alpha