// Paso 3: las k palabras más frecuentes con un heap, frente a ordenar todas.
// Cuenta comparaciones y mide tiempos, con -O2 y sin sanitizers. Los tiempos dependen de la máquina.
// Se mide solo la selección: contar las palabras cuesta lo mismo en los dos casos.
#include <algorithm>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

#include "bench.h"
#include "top_k.h"
#include "word_count.h"

namespace {

std::vector<WordCount> by_sorting(std::vector<WordCount> counts, std::size_t k) {
    std::sort(counts.begin(), counts.end(), MoreFrequent());
    counts.resize(std::min(k, counts.size()));
    return counts;
}

bool same_words(const std::vector<WordCount>& a, const std::vector<WordCount>& b) {
    if (a.size() != b.size()) {
        return false;
    }
    for (std::size_t i = 0; i < a.size(); i++) {
        if (a[i].word != b[i].word || a[i].count != b[i].count) {
            return false;
        }
    }
    return true;
}

}  // namespace

int main(int argc, char** argv) {
    if (argc != 2) {
        std::cerr << "uso: bench_top archivo\n";
        return 2;
    }
    std::vector<WordCount> counts = count_words(read_text(argv[1]));
    std::cout << "palabras distintas: " << counts.size() << "\n";
    std::cout << "       k  comp.heap  comp.sort    t.heap    t.sort\n";
    for (std::size_t k : {std::size_t{10}, std::size_t{100}, std::size_t{1000}, counts.size()}) {
        long heap_count = 0;
        long sort_count = 0;
        std::vector<WordCount> from_heap = top_k(counts, k, [&heap_count](const WordCount& a, const WordCount& b) {
            heap_count++;
            return MoreFrequent()(a, b);
        });
        std::vector<WordCount> copy = counts;
        std::sort(copy.begin(), copy.end(), [&sort_count](const WordCount& a, const WordCount& b) {
            sort_count++;
            return MoreFrequent()(a, b);
        });
        copy.resize(std::min(k, copy.size()));
        if (!same_words(from_heap, copy)) {
            std::cout << "no coinciden, con k = " << k << "\n";
            return 1;
        }
        double heap_time = median_seconds(5, [&] { keep(top_k(counts, k, MoreFrequent()).size()); });
        double sort_time = median_seconds(5, [&] { keep(by_sorting(counts, k).size()); });
        std::cout << std::setw(8) << k << std::setw(11) << heap_count << std::setw(11) << sort_count << std::fixed
                  << std::setprecision(5) << std::setw(10) << heap_time << std::setw(10) << sort_time << "\n";
    }
    return 0;
}
