// Compara el largo de las cadenas con cada función hash, y comprueba su HashMap con las
// mismas palabras. Uso: chain_report archivo_de_palabras
#include <algorithm>
#include <cstddef>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "chain_lengths.h"
#include "hash_map.h"

namespace {

std::vector<std::string> read_words(const std::string& path) {
    std::ifstream file(path);
    if (!file) {
        std::cerr << "no se pudo abrir " << path << "\n";
        std::exit(1);
    }
    std::vector<std::string> words;
    std::string line;
    while (std::getline(file, line)) {
        std::size_t tab = line.find('\t');
        words.push_back(tab == std::string::npos ? line : line.substr(tab + 1));
    }
    return words;
}

void report(const char* name, const std::vector<std::string>& words, std::size_t m, HashFunction hash) {
    std::vector<std::size_t> lengths = chain_lengths(words, m, hash);
    std::size_t empty = 0;
    std::size_t longest = 0;
    double comparisons = 0;
    std::size_t by_length[5] = {0, 0, 0, 0, 0};
    std::size_t total = 0;
    for (std::size_t length : lengths) {
        total += length;
        empty += length == 0 ? 1 : 0;
        longest = std::max(longest, length);
        comparisons += static_cast<double>(length) * static_cast<double>(length + 1) / 2;
        by_length[std::min<std::size_t>(length, 4)]++;
    }
    std::printf("%-6s %10zu %10zu %10.1f %10zu %8zu %8zu %8zu\n", name, empty, longest,
                total == 0 ? 0.0 : comparisons / static_cast<double>(total), by_length[1], by_length[2], by_length[3],
                by_length[4]);
}

}  // namespace

int main(int argc, char** argv) {
    if (argc != 2) {
        std::cerr << "uso: chain_report archivo_de_palabras\n";
        return 1;
    }
    std::vector<std::string> words = read_words(argv[1]);

    // Su tabla decide el número de casillas: las dos funciones se comparan con ese m.
    HashMap<int> table;
    for (std::size_t i = 0; i < words.size(); i++) {
        table.set(words[i], static_cast<int>(i));
    }
    std::size_t found = 0;
    for (const std::string& word : words) {
        found += table.contains(word) ? 1 : 0;
    }
    std::size_t m = table.capacity();
    std::printf("palabras %zu, distintas en la tabla %zu, encontradas %zu, casillas %zu, alfa %.2f\n", words.size(),
                table.size(), found, m, static_cast<double>(table.size()) / static_cast<double>(m));
    std::printf("%-6s %10s %10s %10s %10s %8s %8s %8s\n", "hash", "vacias", "la_mayor", "comp_prom", "largo_1", "largo_2",
                "largo_3", "largo_4+");
    report("suma", words, m, sum_hash);
    report("fnv1a", words, m, fnv1a);
}
