#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Obliczenie n-tego wyrazu ciągu fibonacciego na dwa sposoby.
1. Naiwna rekurencja: podstawienie do wzoru.
2. Wersja z cachem: każdy wyraz jest obliczany dokładnie raz.
"""

def naive_fibonacci(n):
    if n <= 0:
        return 0
    if n in [1,2]:
        return 1
    return naive_fibonacci(n-1) + naive_fibonacci(n-2)


def cache_fibonacci(n, cache=None):
    if cache is None:
        cache = [None for i in range(n+1)]
        cache[0] = 0
        cache[1] = cache[2] = 1
        return cache_fibonacci(n, cache)
    else:
        if cache[n] is not None:
            return cache[n]
        else:
            cache[n] = cache_fibonacci(n-1, cache) + cache_fibonacci(n-2, cache)
            return cache[n]

def non_reccurent_fibonacci(n):
    cache = [None for i in range(n+1)]
    cache[0] = 0
    cache[1] = cache[2] = 1
    for i in range(2, n + 1):
        cache[i] = cache[i-1] + cache[i-2]
    return cache[n]

for i in [5, 10, 15, 20, 30, 40]:
    print("Naive fibonacci for ", i, ":", naive_fibonacci(i))

for i in [5, 10, 15, 20, 30, 40, 100]:
    print("cache fibonacci for ", i, ":", cache_fibonacci(i))

for i in [5, 10, 15, 20, 30, 40, 100]:
    print("no-recurrent fibonacci for ", i, ":", non_reccurent_fibonacci(i))