from typing import Callable


def evaluate(expr: str, func: Callable[[str], int]) -> int:
    """
    >>> evaluate("1 + (2 * 3) + (4 * (5 + 6))", simple)
    51
    """
    # If we're out of parens then we're done.
    if ('(' not in expr):
        return func(expr)

    # Otherwise find the parenthesis depth.
    depths, d, last = [], 0, 0
    for char in expr:
        d += 1 if char=='(' else -1 if char==')' else 0
        if last - d == 1:
            depths.append(d+1)
        else:
            depths.append(d)
        last = d

    # Break up the expression to find the innermost parentheses.
    before, inner, after = "", "", ""
    captured = False
    for d, char in zip(depths, expr):
        if d == max(depths) and not captured:
            inner += char
        elif inner and d <= max(depths):
            captured = True
            after += char
        else:
            before += char

    # Delete the parens and replace the innermost with its result.
    inner = inner.replace('(', '').replace(')', '')
    val = func(inner) if inner else ""
    new_expr = before + str(val) + after

    return evaluate(new_expr, func=func)