// Sesión 2: compara las dos maneras de responder una consulta, en términos leídos y en
// tiempo. Con -O2, sin sanitizers y con mediana de 5. Los tiempos dependen de la máquina.
// Uso: bench_query archivo_de_terminos
#include <cstddef>
#include <cstdio>
#include <iostream>
#include <random>
#include <string>
#include <vector>

#include "autocomplete.h"
#include "bench.h"

namespace {

const int REPS = 5;
const int QUERIES_PER_REP = 200;
const std::size_t K = 5;

// Lee como un vector, pero cuenta las lecturas.
class CountingTerms {
public:
    explicit CountingTerms(const std::vector<Term>& terms) : terms_(terms) {}
    std::size_t size() const { return terms_.size(); }
    const Term& operator[](std::size_t i) const {
        reads_++;
        return terms_[i];
    }
    long long reads() const { return reads_; }

private:
    const std::vector<Term>& terms_;
    mutable long long reads_ = 0;
};

// Microsegundos por consulta.
template <typename Query>
double micros(Query query) {
    double seconds = median_seconds(REPS, [&] {
        for (int i = 0; i < QUERIES_PER_REP; i++) {
            query();
        }
    });
    return seconds * 1e6 / QUERIES_PER_REP;
}

}  // namespace

int main(int argc, char** argv) {
    if (argc != 2) {
        std::cerr << "uso: bench_query archivo_de_terminos\n";
        return 2;
    }
    std::vector<Term> terms;
    if (!load_terms(argv[1], terms)) {
        std::cerr << "no se pudo leer " << argv[1] << "\n";
        return 1;
    }
    sort_by_text(terms);
    std::printf("%zu términos, k = %zu\n", terms.size(), K);
    std::printf("%-8s %6s %10s %10s %9s %9s %9s %8s\n", "prefijo", "rango", "lee rango", "lee todo", "rango us",
                "top us", "todo us", "todo/bin");
    for (const char* prefix : {"", "a", "de", "ca", "cas", "casa", "cañ", "zzz"}) {
        Range range = prefix_range(terms, prefix);
        CountingTerms counting(terms);
        Range counted = prefix_range(counting, prefix);
        top_k(counting, counted, K);
        long long reads_fast = counting.reads();
        CountingTerms counting_all(terms);
        top_k_scan(counting_all, prefix, K);

        double range_us = micros([&] { keep(prefix_range(terms, prefix).first); });
        double top_us = micros([&] { keep(top_k(terms, range, K).size()); });
        double scan_us = micros([&] { keep(top_k_scan(terms, prefix, K).size()); });
        std::printf("%-8s %6zu %10lld %10lld %9.2f %9.2f %9.2f %8.1f\n", prefix[0] == '\0' ? "(vacío)" : prefix,
                    range.last - range.first, reads_fast, counting_all.reads(), range_us, top_us, scan_us,
                    scan_us / (range_us + top_us));
    }
    return 0;
}
