Get the least fixed point when it can be computed piecewise.

    .. testsetup::

        from proso.func import fixed_point

    .. doctest::

        >>> sorted(fixed_point(
        ...    is_zero=lambda xs: len(xs) == 0,
        ...    plus=lambda xs, ys: xs + ys,
        ...    minus=lambda xs, ys: [x for x in xs if x not in ys],
        ...    f=lambda xs: [x + 1 for x in xs if x < 10],
        ...    x=[0, 5, 8]
        ... ))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Args:
        is_zero: function returning True if the given value is zero
        plus: function taking two values and returning their addition
        minus: function taking two values and returning ther difference
        f: function computing the expected value
        x: initial value

    Returns:
        The least fixed point.

def fixed_point(is_zero, plus, minus, f, x):
    """
    Get the least fixed point when it can be computed piecewise.

    .. testsetup::

        from proso.func import fixed_point

    .. doctest::

        >>> sorted(fixed_point(
        ...    is_zero=lambda xs: len(xs) == 0,
        ...    plus=lambda xs, ys: xs + ys,
        ...    minus=lambda xs, ys: [x for x in xs if x not in ys],
        ...    f=lambda xs: [x + 1 for x in xs if x < 10],
        ...    x=[0, 5, 8]
        ... ))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    Args:
        is_zero: function returning True if the given value is zero
        plus: function taking two values and returning their addition
        minus: function taking two values and returning ther difference
        f: function computing the expected value
        x: initial value

    Returns:
        The least fixed point.
    """

    @memo_Y
    def _fixed_point(fixed_point_fun):
        def __fixed_point(collected, new):
            diff = minus(new, collected)
            if is_zero(diff):
                return collected
            return fixed_point_fun(plus(collected, diff), f(diff))
        return __fixed_point

    return _fixed_point(x, f(x))