---
name: rng
kind: function
lang: cpp
domain: datascience
version: "1.0.0"
purity: pure
signature: "void rng_seed(Rng&, uint64 seed); uint64 rng_u64(Rng&); double rng_uniform(Rng&); double rng_normal(Rng&); uint64 rng_below(Rng&, uint64 n); int rng_categorical(Rng&, const double* weights, int n)"
description: "Generador pseudoaleatorio xoshiro256++ con state inout (struct Rng). Helpers: uniform, normal (Box-Muller), below (Lemire sin sesgo), categorical (O(n) cumulative). Determinista dado seed — pareja CPU del gpu_rng_glsl."
tags: [rng, xoshiro, uniform, normal, categorical, montecarlo, datascience]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [cstdint, cmath]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/datascience/rng.cpp"
params:
  - name: seed
    desc: "Semilla maestra. 0 se sustituye por la constante de Knuth para evitar arranque degenerado."
  - name: r
    desc: "Estado RNG (struct con 4 uint64). Se muta in-place en cada llamada."
  - name: n
    desc: "(rng_below) limite superior exclusivo. (rng_categorical) numero de pesos."
  - name: weights
    desc: "(rng_categorical) array de pesos NO necesariamente normalizados. Pesos negativos o cero se ignoran."
output: "Numeros pseudoaleatorios deterministas dado el state inicial. rng_seed inicializa los 4 lanes via SplitMix64. Mismo patron que glsl_rng_preamble (gpu) para reutilizar GLSL kernels en CPU."
---

# rng

Pareja CPU del `glsl_rng_preamble` (de `gpu_rng_glsl`). Misma semantica, mismas garantias.

## Calidad

xoshiro256++ (Vigna 2018): periodo 2^256 - 1, 1.2 ns/u64 en x86, supera PractRand 32 TB. Reemplaza al `std::mt19937_64` (mas lento y mayor estado, sin ventaja para Monte Carlo).

`rng_normal` usa Box-Muller polar (~30 ns por sample). Para volumen extremo (>10^9 normals CPU) considerar Ziggurat — no incluido aqui por complejidad.

`rng_below` usa el metodo de Lemire (2019): rejection sampling sin division en el caso comun, sesgo cero. Mejor que `rng_u64() % n` que tiene sesgo modular.

## Uso

```cpp
fn::ds::Rng r;
fn::ds::rng_seed(r, 0xC0FFEE);

double x = fn::ds::rng_normal(r);        // ~ N(0, 1)
double u = fn::ds::rng_uniform(r);       // [0, 1)
int    k = fn::ds::rng_below(r, 100);    // 0..99 sin sesgo

double weights[] = {0.5, 0.3, 0.2};
int idx = fn::ds::rng_categorical(r, weights, 3);
```

## Notas

- Determinista bit-exacto dado el seed. Util para tests numericos y para comparar contra el GLSL kernel (que usa PCG32 — distinto stream pero misma semantica de uniform/normal).
- No thread-safe en una unica Rng. Para Monte Carlo paralelo: una Rng por thread, sembrada con seeds derivados de un master via `rng_seed` con seeds distintos.
- Pure: la mutacion del state es referencialmente transparente (la misma secuencia de llamadas produce los mismos resultados). Sin I/O, sin globals.
