// Paso 3: mide su Vector<int> duplicando y creciendo de a 1000, y cuenta las copias.
// Se compila con -O2 y sin sanitizers. Cada tiempo es la mediana de 5 repeticiones.
//   build/lab/growth_bench            tiempos y copias
//   build/lab/growth_bench --counts   solo las copias
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <initializer_list>

#include "bench.h"
#include "tracked.h"
#include "vector.h"

// La regla del Vector: duplicar.
void fill_doubling(int n) {
    Vector<int> v;
    for (int i = 0; i < n; i++) {
        v.push_back(i);
    }
    keep(v[n - 1]);
}

// Otra regla, desde fuera: pedir 1000 casillas más justo antes de llenarse.
void fill_step(int n) {
    Vector<int> v;
    for (int i = 0; i < n; i++) {
        if (v.size() == v.capacity()) {
            v.reserve(v.capacity() + 1000);
        }
        v.push_back(i);
    }
    keep(v[n - 1]);
}

// Este tipo no mueve con noexcept: cada traslado al crecer es una copia, y se cuenta.
long copies_doubling(int n) {
    Vector<TrackedRiskyMove> v;
    TrackedRiskyMove::reset();
    for (int i = 0; i < n; i++) {
        v.push_back(TrackedRiskyMove(i));
    }
    return TrackedRiskyMove::counts.copies;
}

long copies_step(int n) {
    Vector<TrackedRiskyMove> v;
    TrackedRiskyMove::reset();
    for (int i = 0; i < n; i++) {
        if (v.size() == v.capacity()) {
            v.reserve(v.capacity() + 1000);
        }
        v.push_back(TrackedRiskyMove(i));
    }
    return TrackedRiskyMove::counts.copies;
}

int main(int argc, char** argv) {
    bool only_counts = argc == 2 && std::strcmp(argv[1], "--counts") == 0;
    std::printf("%9s %14s %14s\n", "n", "copias dup.", "copias +1000");
    for (int n : {25000, 50000, 100000, 200000}) {
        std::printf("%9d %14ld %14ld\n", n, copies_doubling(n), copies_step(n));
    }
    if (only_counts) {
        return 0;
    }
    std::printf("\n%9s %13s %13s\n", "n", "duplicar(ms)", "de a 1000(ms)");
    for (int n : {100000, 200000, 400000, 800000, 1600000}) {
        double doubling = median_seconds(5, [n] { fill_doubling(n); });
        double step = median_seconds(5, [n] { fill_step(n); });
        std::printf("%9d %13.3f %13.3f\n", n, doubling * 1000, step * 1000);
    }
    return 0;
}
