import time
from timeit import default_timer as timer
from lazysequence import lazysequence

# Arvutuse kiiruse jälgimiseks kasutame järgnevat abifunktsiooni

def t(x):
    t = timer()
    print(x())
    print("Aeg(s):", timer() - t)

# >>> t(lambda: time.sleep(1))
# None
# Aeg(s): 1.005166042

# >>> t(lambda: sum(range(1,1000000000)))
# 499999999500000000
# Aeg(s): 6.780336375

# Pythonis on listid

nums: list[int] = [1, 2, 3, 4, 5, 6]

# listikomprehensioon
def even_squares(lst: list[int]): 
    return lazysequence(n * n for n in lst if n % 2 == 0)

# even_squares(nums)
# even_squares(range(1,100))

def even_squares_gen(lst: list[int]): 
    return (n * n for n in lst if n % 2 == 0)

xs = even_squares_gen(nums)
# next(xs) 

# listid on agarad

# >>> t(lambda: even_squares(range(1,100000000))[0])
# 4
# Aeg(s): 2.715277624999999

# >>> t(lambda: next(even_squares_gen(range(1,100000000))))
# 4
# Aeg(s): 6.84580001006907e-05

def sum_three(xs):
    return (xs[0] + xs[1] + xs[2])

# >>> t(lambda: sum_three(even_squares(range(1,100000000))))
# 56
# Aeg(s): 3.0259997920000075

# Laiskus on ka võimalik: list -> lazysequence


# listioperatsioonid funktsioonidena

from functools import reduce

doubles = list(map(lambda x: x * 2, nums))
evens   = list(filter(lambda x: x % 2 == 0, nums))
total   = reduce(lambda acc, x: acc + x, nums)

scores = [0.9, 0.85, 0.95]
all_pass = all(s >= 0.8 for s in scores)   

user_inputs = ["ok", "", "cancel"]
has_empty = any(not s for s in user_inputs) 


# mitme funktsiooni kombineerimine

odds_squared = list(map(lambda n: n * n,
                        filter(lambda n: n % 2, nums)))
                        
names = ["Alice", "Bob", "Charlie"]
list_names1 = list(zip(range(1, len(names) + 1), names))
# -> [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie

# generaatoreid saab teha ka `yield` lausega

def items():
    a = 1
    while True:
        yield a
        a = a+1

list_names2 = list(zip(items(), names))

def dekoraator(fn):
    def wrapper():
        print("enne")
        r = fn()
        print("pärast")
        return r
    return wrapper

def kirjuta():
    print("mida?")

# kirjuta()
# dekoraator(kirjuta)()

# lihtsam näide

def log_calls(fn):
    def wrapper(*args, **kwargs):
        print(f"-> {fn.__name__}{args} {kwargs}")
        result = fn(*args, **kwargs)
        print(f"<- {result}")
        return result
    return wrapper

def time_function(fn):
    def wrapper(*args, **kwargs):
        t = timer()
        r = fn(*args, **kwargs)
        print("Aeg(s):", timer() - t)
        return r
    return wrapper

@time_function
@log_calls
def area(r):
    return 3.14 * r * r

# area(10)

@log_calls
def volume(r, h):
    return area(r) * h  

# volume(2,9)


from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube = partial(power, exp=3)

def multiply(factor):
    return lambda x: x * factor  # käsitsi karrimine

double = multiply(2)
# double(10) 

from functools import lru_cache
from functools import cache

@cache
# @lru_cache(maxsize=None)  # unbounded; vali maht vastavalt
def fib(n: int) -> int:
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

asdf = fib("abc")

# t(lambda: fib(38))