// Medidor pequeño para las semanas que comparan tiempos.
// Compilar las mediciones con -O2 y sin sanitizers.
#ifndef BENCH_H
#define BENCH_H

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <vector>

// Mediana de una lista de números. Con cantidad par, promedia los dos del medio.
inline double median_of(std::vector<double> values) {
    assert(!values.empty());
    std::sort(values.begin(), values.end());
    std::size_t middle = values.size() / 2;
    if (values.size() % 2 == 1) {
        return values[middle];
    }
    return (values[middle - 1] + values[middle]) / 2.0;
}

// Corre fn `reps` veces y devuelve la mediana en segundos.
// Todo lo que pase dentro de fn cuenta como tiempo medido.
template <typename Fn>
double median_seconds(int reps, Fn fn) {
    assert(reps >= 1);
    std::vector<double> times;
    times.reserve(static_cast<std::size_t>(reps));
    for (int i = 0; i < reps; i++) {
        auto start = std::chrono::steady_clock::now();
        fn();
        auto stop = std::chrono::steady_clock::now();
        times.push_back(std::chrono::duration<double>(stop - start).count());
    }
    return median_of(times);
}

// Evita que el compilador borre un cálculo cuyo resultado no se usa.
// No genera ninguna instrucción: le avisa al compilador que este bloque vacío
// podría leer value y cualquier memoria. Es una extensión de g++ y clang, no
// parte del estándar de C++.
template <typename T>
void keep(const T& value) {
    asm volatile("" : : "r"(&value) : "memory");
}

#endif
