def factorial(n):
    def term(i):
        return i
    def next(i):
        return i + 1
    return product_r(term, 1, next, n)
# pi product
def pi(n):
    def is_odd(i):
        return i % 2 == 1
    def term(i):
        return (i + 1) / (i + 2) if is_odd(i) else (i + 2) / (i + 1)
    def next(i):
        return i + 1
    return 4 * (product_i(term, 1.0, next, n))
# recursive process
def accumulate_r(combiner, null_value, term, a, next, b):
    return (null_value if a > b
            else combiner(term(a),
                          accumulate_r(combiner,
                                       null_value,
                                       term, next(a), next, b)))

def sum_r(term, a, next, b):
    def plus(x, y):
        return x + y
    return accumulate_r(plus, 0, term, a, next, b)

def product_r(term, a, next, b):
    def times(x, y):
        return x * y
    return accumulate_r(times, 1, term, a, next, b)

# iterative process
def accumulate_i(combiner, null_value, term, a, next, b):
    def iterate(a, result):
        return result if a > b else iterate(next(a), combiner(term(a), result))
    return iterate(a, null_value)

def sum_i(term, a, next, b):
    def plus(x, y):
        return x + y
    return accumulate_i(plus, 0, term, a, next, b)

def product_i(term, a, next, b):
    def times(x, y):
        return x * y
    return accumulate_i(times, 1, term, a, next, b)

print(factorial(5))

# expected: 120
