// Mide mergesort con el auxiliar por referencia y por valor. Se compila con -O2 y sin sanitizers.
// Cada fila es el menor de 3 tiempos. Los tiempos dependen de la máquina.
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

#include "copy_cost.h"

template <typename Sort>
static double measure_ms(const std::vector<int>& input, Sort sort) {
    double best = 1e18;
    for (int run = 0; run < 3; run++) {
        std::vector<int> a = input;
        auto start = std::chrono::steady_clock::now();
        sort(a);
        auto end = std::chrono::steady_clock::now();
        best = std::min(best, std::chrono::duration<double, std::milli>(end - start).count());
    }
    return best;
}

int main() {
    std::mt19937 rng(2);
    std::cout << "n       referencia ms  razón  por valor ms  razón\n" << std::fixed;
    double previous_good = 0;
    double previous_bad = 0;
    for (std::size_t n : {2000u, 4000u, 8000u, 16000u, 32000u}) {
        std::vector<int> input(n);
        for (int& x : input) {
            x = static_cast<int>(rng() >> 1);
        }
        double good = measure_ms(input, [](std::vector<int>& a) { merge_sort(a); });
        double bad = measure_ms(input, [](std::vector<int>& a) {
            std::vector<int> buffer(a.size());
            sort_range_buffer_by_value(a, buffer, 0, a.size());
        });
        std::cout << std::left << std::setw(8) << n << std::right << std::setprecision(2) << std::setw(13) << good;
        if (previous_good > 0) {
            std::cout << std::setw(7) << good / previous_good;
        } else {
            std::cout << std::setw(7) << "";
        }
        std::cout << std::setw(14) << bad;
        if (previous_bad > 0) {
            std::cout << std::setw(7) << bad / previous_bad;
        }
        std::cout << '\n';
        previous_good = good;
        previous_bad = bad;
    }
}
