// El medidor del laboratorio: mide una función con varios n y escribe un CSV.
#ifndef MEASURE_H
#define MEASURE_H

#include <iosfwd>
#include <string>
#include <vector>

#include "bench.h"
#include "mysteries.h"

// Una fila del experimento de duplicar.
struct Row {
    long long n = 0;
    long long steps = 0;   // pasos de trabajo de una llamada
    double seconds = 0;    // tiempo de una llamada
    double ratio = 0;      // seconds de esta fila / seconds de la anterior; 0 en la primera
};

// Tiempo de una llamada a fn. Una llamada corta dura menos que el salto del reloj, así que
// se mide una tanda de llamadas que tarde al menos 1 ms y se divide entre su tamaño.
// Este ya está hecho.
template <typename Fn>
double seconds_per_call(int reps, Fn fn) {
    long long batch = 1;
    while (true) {
        double seconds = median_seconds(1, [&] {
            for (long long i = 0; i < batch; i++) {
                fn();
            }
        });
        if (seconds >= 0.001) {
            break;
        }
        batch *= 2;
    }
    double seconds = median_seconds(reps, [&] {
        for (long long i = 0; i < batch; i++) {
            fn();
        }
    });
    return seconds / static_cast<double>(batch);
}

// Llena ratio en cada fila. La primera queda en 0.
void fill_ratios(std::vector<Row>& rows);

// Mide fn con first_n, 2 * first_n, ... (`row_count` filas), con `reps` repeticiones cada una.
// Devuelve las filas con n, steps, seconds y ratio llenos.
std::vector<Row> doubling_experiment(MysteryFn fn, long long first_n, int row_count, int reps);

// Escribe una línea por fila: nombre,n,steps,seconds,ratio
void write_csv(std::ostream& out, const std::string& name, const std::vector<Row>& rows);

#endif
