/*
 * aloha.c: simula ALOHA ranurado y ALOHA puro, y compara con la fórmula.
 *
 *   aloha [semilla]
 *
 * G es la carga ofrecida: frames (nuevos o reintentos) que se intenta enviar por cada
 * tiempo de frame. S es el throughput: la fracción del tiempo con un frame exitoso.
 * Los intentos son independientes (el modelo clásico): no se simula la cola de cada nodo.
 */
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define NODES 50          /* nodos en el modelo ranurado */
#define SLOTS 400000      /* ranuras simuladas */
#define PURE_FRAMES 400000 /* intentos simulados en ALOHA puro */

/* Generador xorshift propio: el resultado es igual en todas las máquinas. */
static uint64_t rng_state;

static double uniform(void)
{
    rng_state ^= rng_state << 13;
    rng_state ^= rng_state >> 7;
    rng_state ^= rng_state << 17;
    return (double)(rng_state >> 11) / 9007199254740992.0; /* 2^53 */
}

/* Una ranura es exitosa si exactamente un nodo decide transmitir. */
static double simulate_slotted(double load)
{
    double p = load / NODES;
    long successes = 0;
    for (long slot = 0; slot < SLOTS; slot++) {
        int senders = 0;
        for (int node = 0; node < NODES; node++) {
            if (uniform() < p) {
                senders++;
            }
        }
        if (senders == 1) {
            successes++;
        }
    }
    return (double)successes / SLOTS;
}

/*
 * Intentos en instantes aleatorios (proceso de Poisson de tasa G). Un frame dura 1 y
 * sale bien solo si nadie empezó en el tiempo 1 anterior ni empieza en el tiempo 1
 * siguiente: el período vulnerable mide 2.
 */
static double simulate_pure(double load)
{
    double gap_before = -log(1.0 - uniform()) / load;
    double total_time = 0.0;
    long successes = 0;
    for (long i = 0; i < PURE_FRAMES; i++) {
        double gap_after = -log(1.0 - uniform()) / load;
        if (gap_before > 1.0 && gap_after > 1.0) {
            successes++;
        }
        total_time += gap_after;
        gap_before = gap_after;
    }
    return (double)successes / total_time;
}

int main(int argc, char *argv[])
{
    static const double loads[] = {0.1, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0};
    uint64_t seed = 20260303;

    if (argc > 2) {
        fprintf(stderr, "uso: %s [semilla]\n", argv[0]);
        return 2;
    }
    if (argc == 2) {
        char *end;
        seed = strtoull(argv[1], &end, 10);
        if (*end != '\0' || seed == 0) {
            fprintf(stderr, "la semilla debe ser un entero positivo\n");
            return 2;
        }
    }
    rng_state = seed;

    printf("   G | ranurado sim  G*e^-G | puro sim  G*e^-2G\n");
    for (size_t i = 0; i < sizeof loads / sizeof loads[0]; i++) {
        double g = loads[i];
        printf("%4.2f | %12.3f  %6.3f | %8.3f  %7.3f\n", g, simulate_slotted(g), g * exp(-g),
               simulate_pure(g), g * exp(-2.0 * g));
    }
    return 0;
}
