def _determine_to_dos(task, cache_entry, derivative, criterion_and_derivative):
    """Determine which functions have to be evaluated at the new parameters.

    Args:
        task (str): One of "criterion", "derivative", "criterion_and_derivative"
        cache_entry (dict): Possibly empty dict.
        derivative (Callable or None): Only used to determine if a closed form
            derivative is available.
        criterion_and_derivative (callable or None): Only used to determine if this
            function is available.

    Returns:
        list: List of functions that have to be evaluated. Possible values are:
            - [] if nothing has to be done
            - ["criterion_and_derivative"]
            - ["numerical_criterion_and_derivative"]
            - ["criterion", "derivative"]
            - ["criterion"]
            - ["derivative"]

    """
    criterion_needed = "criterion" in task and "criterion" not in cache_entry
    derivative_needed = "derivative" in task and "derivative" not in cache_entry

    to_dos = []
    if criterion_and_derivative is not None and criterion_needed and derivative_needed:
        to_dos.append("criterion_and_derivative")
    elif (
        derivative is None
        and criterion_and_derivative is not None
        and derivative_needed
    ):
        to_dos.append("criterion_and_derivative")
    elif derivative is None and derivative_needed:
        to_dos.append("numerical_criterion_and_derivative")
    else:
        if derivative_needed:
            to_dos.append("derivative")
        if criterion_needed:
            to_dos.append("criterion")
    return to_dos