import timeit
from collections import deque, defaultdict
import numpy as np

def measure_execution_time(circuit, state, repeat=100, **kwargs):
    results = deque()
    for _ in range(repeat):

        s = state.deepcopy()
        start = timeit.default_timer()
        result = circuit * s
        stop = timeit.default_timer()

        results.append(stop - start)
    return min(results)

def measure_all(input_circuits_and_states, **kwargs):
    results = defaultdict(deque)

    N = len(input_circuits_and_states)
    for n, (key, circuit, state) in enumerate(input_circuits_and_states):
        print(f"running tests... {int(n/N * 100)} %", end="\r", flush=True)
        results[key].append(measure_execution_time(circuit, state, **kwargs))
    print("running tests... done  ")
    return results


def execution_statistics(results, scale=None, **kwargs):
    Ns = deque()
    avgs = deque()
    std_devs = deque()
    for k,v in sorted(results.items(), key=lambda x: x[0]):
        result = np.array(v, dtype=np.double)
        if(scale is not None):
            result /= scale[k]
        N = len(result)
        avg = np.average(result)
        std_dev = np.std(result) / np.sqrt(N)

        Ns.append(N)
        avgs.append(avg)
        std_devs.append(std_dev)

    Ns = np.array(Ns).astype(np.int64)
    avgs = np.array(avgs).astype(np.double)
    std_devs = np.array(std_devs).astype(np.double)

    return Ns, avgs, std_devs
