// Mide los ordenamientos de la clase. Se compila con -O2 y sin sanitizers, con mediana de 5.
// Los tiempos dependen de la máquina.
#include <algorithm>
#include <cstddef>
#include <cstdio>
#include <vector>

#include "bench.h"
#include "inputs.h"
#include "int_sorts.h"

namespace {

const int REPS = 5;
const unsigned SEED = 2026;

template <typename Sort>
double millis(const std::vector<int>& input, Sort sort) {
    std::vector<std::vector<int>> copies(REPS, input);
    int rep = 0;
    double seconds = median_seconds(REPS, [&] {
        sort(copies[rep]);
        keep(copies[rep][0]);
        rep++;
    });
    return seconds * 1000;
}

void row(const char* name, const std::vector<int>& input) {
    long long ignored = 0;
    std::printf("%-10s %8.2f", name, millis(input, [&](std::vector<int>& v) { merge_sort_ints(v, ignored); }));
    std::printf(" %8.2f", millis(input, [&](std::vector<int>& v) { quick_sort_first_ints(v, ignored); }));
    std::printf(" %8.2f", millis(input, [&](std::vector<int>& v) { quick_sort_random_ints(v, SEED, ignored); }));
    std::printf(" %8.2f", millis(input, [&](std::vector<int>& v) { quick_sort_three_ints(v, SEED, ignored); }));
    std::printf(" %8.2f", millis(input, [](std::vector<int>& v) { std::sort(v.begin(), v.end()); }));
    std::printf(" %8.2f\n", millis(input, [](std::vector<int>& v) { std::stable_sort(v.begin(), v.end()); }));
    keep(ignored);
}

}  // namespace

int main() {
    const std::size_t n = 16000;
    std::printf("milisegundos con n = %zu\n", n);
    std::printf("%-10s %8s %8s %8s %8s %8s %8s\n", "entrada", "merge", "q.primero", "q.azar", "q.tres",
                "std::sort", "stable");
    row("al azar", random_input(n, SEED));
    row("ordenada", sorted_input(n));
    row("10 valores", repeated_input(n, 10, SEED));
    row("iguales", std::vector<int>(n, 7));

    std::printf("\nentrada al azar, milisegundos al duplicar n\n");
    std::printf("%8s %8s %8s %8s %9s %8s\n", "n", "merge", "q.azar", "q.tres", "std::sort", "stable");
    for (std::size_t size : {250000, 500000, 1000000}) {
        std::vector<int> input = random_input(size, SEED);
        long long ignored = 0;
        std::printf("%8zu", size);
        std::printf(" %8.1f", millis(input, [&](std::vector<int>& v) { merge_sort_ints(v, ignored); }));
        std::printf(" %8.1f", millis(input, [&](std::vector<int>& v) { quick_sort_random_ints(v, SEED, ignored); }));
        std::printf(" %8.1f", millis(input, [&](std::vector<int>& v) { quick_sort_three_ints(v, SEED, ignored); }));
        std::printf(" %9.1f", millis(input, [](std::vector<int>& v) { std::sort(v.begin(), v.end()); }));
        std::printf(" %8.1f\n", millis(input, [](std::vector<int>& v) { std::stable_sort(v.begin(), v.end()); }));
        keep(ignored);
    }
    return 0;
}
