// Paso 5: el tiempo de insertar y de buscar en un mapa. Se elige cuál con un argumento: avl, rn o std.
// Cada mapa y cada n corren en su propio proceso, porque lo que corrió antes en el mismo proceso cambia los tiempos.
// Los tiempos dependen de la máquina. Compilar con -O2 y sin sanitizers.
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <random>
#include <string>
#include <vector>

#include "avl_map.h"
#include "measure.h"
#include "red_black_map.h"

namespace {

// Las primeras repeticiones de un proceso cuestan más: piden memoria nueva. Con 7, la mediana ya no las toca.
const int repetitions = 7;

// Busca todas las claves, en un orden distinto del de inserción.
template <typename Map>
void print_row(int n, std::mt19937& random) {
    std::vector<int> ordered(n);
    std::iota(ordered.begin(), ordered.end(), 0);
    std::vector<int> lookups = ordered;
    std::shuffle(lookups.begin(), lookups.end(), random);
    std::vector<int> shuffled = ordered;
    std::shuffle(shuffled.begin(), shuffled.end(), random);
    std::cout << std::setw(9) << n << std::fixed << std::setprecision(4);
    for (const std::vector<int>& keys : {ordered, shuffled}) {
        std::cout << std::setw(10) << time_inserts<Map>(keys, repetitions);
        Map map;
        for (int key : keys) {
            put(map, key);
        }
        std::cout << std::setw(10) << time_finds(map, lookups, repetitions) << std::flush;
    }
    std::cout << "\n";
}

}  // namespace

int main(int argc, char** argv) {
    std::string which = argc == 3 ? argv[1] : "";
    int n = argc == 3 ? std::stoi(argv[2]) : 0;
    std::mt19937 random(12);
    if (which == "avl") {
        print_row<AvlMap<int, int>>(n, random);
    } else if (which == "rn") {
        print_row<RedBlackMap<int, int>>(n, random);
    } else if (which == "std") {
        print_row<std::map<int, int>>(n, random);
    } else {
        std::cerr << "uso: bench_times avl|rn|std n\n";
        return 2;
    }
    return 0;
}
