// Compara dos reglas para encoger. Solo sigue el tamaño y la capacidad, y cuenta
// cuántos elementos se trasladan. Crecer siempre es duplicar.
#include <cstdio>

// Por debajo de esta capacidad no se encoge.
const long MIN_CAPACITY = 4;

struct Simulated {
    long size = 0;
    long capacity = 0;
    long moved = 0;
    long resizes = 0;
};

void resize_to(Simulated& a, long capacity) {
    a.moved += a.size;
    a.resizes++;
    a.capacity = capacity;
}

void push(Simulated& a) {
    if (a.size == a.capacity) {
        resize_to(a, a.capacity == 0 ? 1 : 2 * a.capacity);
    }
    a.size++;
}

// divisor es 2 para encoger al llegar a la mitad, y 4 para hacerlo al llegar a un cuarto.
void pop(Simulated& a, long divisor) {
    a.size--;
    if (a.capacity > MIN_CAPACITY && a.size * divisor <= a.capacity) {
        resize_to(a, a.capacity / 2);
    }
}

void alternate_at_edge(long divisor) {
    Simulated a;
    for (int i = 0; i < 1025; i++) {
        push(a);
    }
    std::printf("tras 1025 push: size %ld, capacity %ld, traslados %ld\n", a.size, a.capacity, a.moved);
    long before = a.moved;
    for (int i = 0; i < 1000; i++) {
        pop(a, divisor);
        push(a);
    }
    std::printf("tras 2000 operaciones más: capacity %ld, traslados nuevos %ld\n", a.capacity,
                a.moved - before);
}

int main() {
    std::printf("Encoger al llegar a la mitad\n");
    alternate_at_edge(2);
    std::printf("Encoger al llegar a un cuarto\n");
    alternate_at_edge(4);
    return 0;
}
