"""
Własne narzędzia do pomiaru czasów wywołań funkcji. 
Zwraca całkowity czas pojedynczego wywołania, całkowity czas N wywołań, najlepszy czas z N wywołań i najlepszy czas całkowity

"""

import time
timer = time.perf_counter                         # Zobacz również time.process_time()

def once(func, *pargs, **kargs):                  # Zebranie argumentów dla func
    """
    Czas wykonania jednego wywołania func(...).
    Zwraca (czas, wynik).
    """
    start   = timer()
    result  = func(*pargs, **kargs)               # Rozpakowanie argumentów dla func
    elapsed = timer() - start
    return (elapsed, result)                      # Zwrócenie wyniku do sprawdzenia

def total(reps, func, *pargs, **kargs):           # Zebranie argumentów dla func
    """
    Całkowity czas wszystkich powtórzeń func(...)
    Zwraca (czas całkowity, ostatni wynik)
    """
    total = 0                                     # Nie zmieniaj czasu range()
    for i in range(reps):
        time, result = once(func, *pargs, **kargs)
        total += time
    return (total, result)                        # Zwrócenie ostatniego wyniku do sprawdzenia

def bestof(reps, func, *pargs, **kargs):
    """
    Najlepszy czas spośród wszystkich powtórzeń func(...).
    Zwraca (najlepszy czas, wynik najszybszego wywołania).
    """
    return min(once(func, *pargs, **kargs) for i in range(reps))

def bestoftotal(reps1, reps2, func, *pargs, **kargs):
    """
    Najlepszy czas z reps1 wywołań spośród (całkowity czas reps2 wywołań funkcji func()).
    Zwraca (najlepszy czas całkowity, wynik najlepszego czasu całkowitego).
    """
    return min(total(reps2, func, *pargs, **kargs) for i in range(reps1))
