// Qué promete std::map sobre sus elementos, y cuánta memoria pide esta biblioteca por nodo.
#include <cassert>
#include <cstddef>
#include <iostream>
#include <map>
#include <memory>
#include <unordered_map>

#include "alloc_counter.h"

namespace {

// Un asignador que anota cuántos bytes pidió el contenedor en cada reserva.
inline std::size_t last_block = 0;

template <typename T>
struct SpyAllocator {
    using value_type = T;
    SpyAllocator() = default;
    template <typename U>
    SpyAllocator(const SpyAllocator<U>&) {}
    T* allocate(std::size_t n) {
        last_block = n * sizeof(T);
        return std::allocator<T>{}.allocate(n);
    }
    void deallocate(T* block, std::size_t n) { std::allocator<T>{}.deallocate(block, n); }
    bool operator==(const SpyAllocator&) const { return true; }
};

}  // namespace

int main() {
    std::map<int, int> map;
    map[5] = 50;
    map[3] = 30;
    map[8] = 80;
    map[7] = 70;
    int* to_seven = &map[7];

    for (int i = 100; i < 1100; i++) {
        map[i] = i;
    }
    // Borrar el 5, que tiene dos hijos: el 7 sigue en su sitio, aunque es su sucesor.
    map.erase(5);
    std::cout << "puntero al 7 tras mil inserciones y un borrado: " << *to_seven << "\n";
    assert(to_seven == &map[7]);

    std::size_t before = allocation_count();
    std::map<int, int> counted;
    for (int i = 0; i < 1000; i++) {
        counted[i] = i;
    }
    std::cout << "reservas de std::map al insertar 1000 claves: " << allocation_count() - before << "\n";

    std::map<int, int, std::less<int>, SpyAllocator<std::pair<const int, int>>> spied;
    spied[1] = 1;
    std::cout << "bytes por nodo de std::map<int, int>: " << last_block << "\n";

    std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, SpyAllocator<std::pair<const int, int>>> spied_hash;
    spied_hash.reserve(16);
    spied_hash[1] = 1;
    std::cout << "bytes por nodo de std::unordered_map<int, int>: " << last_block << "\n";
    return 0;
}
