#pragma once

#include <cstdint>

namespace fn::ds {

// Estado RNG opaco (xoshiro256++). Periodo 2^256 - 1, paso muy rapido,
// excelente calidad estadistica. No criptografico.
struct Rng {
    std::uint64_t s[4] = {0, 0, 0, 0};
};

// Inicializa el state de una Rng a partir de una semilla maestra usando
// SplitMix64. seed=0 se sustituye por la constante de Knuth para evitar
// arranque degenerado. Funcion pura (modifica r in-place a partir de seed).
void rng_seed(Rng& r, std::uint64_t seed);

// Avanza el RNG y devuelve un uint64 uniformemente distribuido.
std::uint64_t rng_u64(Rng& r);

// Float [0, 1). 53 bits de mantisa.
double rng_uniform(Rng& r);

// N(0, 1) Box-Muller polar.
double rng_normal(Rng& r);

// Entero uniforme en [0, n). Rejection-sampling para evitar sesgo modular.
std::uint64_t rng_below(Rng& r, std::uint64_t n);

// Sample categorico: dado un array de probabilidades NO necesariamente
// normalizadas (positivas), devuelve el indice [0, n) con probabilidad
// proporcional. O(n). Si la suma es 0 devuelve n-1.
int rng_categorical(Rng& r, const double* weights, int n);

} // namespace fn::ds
