/**
* @file rand.004.c
* @ingroup experimental
* Random number generation using custom LCG as bitstream.
* @date 01/04/2025
*/
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <stdio.h>
_Static_assert((-1 & 3) == 3, "Not 2's complement");
_Static_assert(UINT_MAX == (unsigned int) INT_MAX - INT_MIN, "Bad integer");
//
// Utility.
//
#define BITS(x) (sizeof(x) * CHAR_BIT)
int bitlength(unsigned int x)
{
return x ? BITS(x) - __builtin_clz(x) : 0;
}
//
// Private.
//
#define GENERATOR_BITS 32
static _Thread_local uint_fast64_t generator_state = UINT64_C(0x1234ABCD330E);
static void generator_seed(uint_fast64_t seed)
{
generator_state = seed;
generator_state %= (uint_fast64_t) 1 << 48;
}
static uint_fast32_t generator_next(void)
{
generator_state *= (uint_fast64_t) 25214903917;
generator_state += (uint_fast64_t) 11;
generator_state %= (uint_fast64_t) 1 << 48;
return generator_state >> 16;
}
static uint_fast32_t takebits(uint_fast32_t x, int available, int take)
{
return (x >> (available - take)) & (UINT32_C(-1) >> (BITS(x) - take));
}
//
// Public.
//
void randseed(uint_fast64_t seed)
{
generator_seed(seed);
}
uint_fast64_t randbits(int n)
{
static _Thread_local uint_fast32_t reservoir = 0;
static _Thread_local int available = 0;
uint_fast64_t r = 0;
while (n > 0)
{
if (available == 0)
{
reservoir = generator_next();
available = GENERATOR_BITS;
}
int take = (available < n) ? available : n;
r = (r << take) | takebits(reservoir, available, take);
available -= take;
n -= take;
}
return r;
}
unsigned int randuint(unsigned int max)
{
if (max == 0)
return 0;
int n = bitlength(max);
unsigned int bit_max = -1U >> (BITS(max) - n);
unsigned int r = randbits(n);
if (max == bit_max)
return r;
unsigned int mod = max + 1;
unsigned int min = (bit_max + 1 - mod) % mod;
while (r < min)
r = randbits(n);
return r % mod;
}
double randreal(void)
{
double r = randbits(DBL_MANT_DIG) / exp2(DBL_MANT_DIG);
return r;
}
_Bool randbool(double p)
{
return randreal() < p;
}
//
// Test.
//
int uniform_int_distribution_uint(int a, int b)
{
return randuint((unsigned int) b - a) + a;
}
int uniform_int_distribution_real(int a, int b)
{
return randreal() * (1.0 + b - a) + a;
}
int bernoulli_distribution_50_50(int a, int b)
{
return randbool(0.5);
}
unsigned long random_device()
{
unsigned long r = -1;
FILE
*fp
= fopen("/dev/urandom", "r"); if (fp)
{
fread(&r
, sizeof r
, 1, fp
); }
return r;
}
double chi_square(int n, int k, int (*f)(int, int))
{
int *h
= calloc(k
, sizeof *h
); for (int j = 0; j < n; j++)
{
int i = f(0, k-1);
h[i] += 1;
}
double expect = (double) n / k;
double x2 = 0;
for (int i = 0; i < k; i++)
x2
+= pow(h
[i
] - expect
, 2) / expect
; return x2;
}
void test(int m, int n, int df, double x2, int (*f)(int, int),
const char *header)
{
for (int i = 0; i < m; i++)
{
double r = chi_square(n, df, f);
printf(" significant: %-5s [%6.2f][%6.2f]\n", r >= x2 ? "true" : "false", r, x2);
}
}
int main(void)
{
unsigned long seed = random_device();
randseed(seed);
int m = 5;
int n = 10000;
// PV=0.05 for all tests.
test(m, n, 100, 124.34, uniform_int_distribution_uint,
"uniform_int_distribution_uint");
test(m, n, 100, 124.34, uniform_int_distribution_real,
"uniform_int_distribution_real");
test(m, n, 2, 5.99, bernoulli_distribution_50_50,
"bernoulli_distribution_50_50");
return 0;
}
LyoqCiAqIEBmaWxlIHJhbmQuMDA0LmMKICogQGluZ3JvdXAgZXhwZXJpbWVudGFsCiAqIFJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB1c2luZyBjdXN0b20gTENHIGFzIGJpdHN0cmVhbS4KICogQGRhdGUgMDEvMDQvMjAyNQogKi8KCiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHN0ZGludC5oPgojaW5jbHVkZSA8bGltaXRzLmg+CiNpbmNsdWRlIDxhc3NlcnQuaD4KI2luY2x1ZGUgPG1hdGguaD4KI2luY2x1ZGUgPGZsb2F0Lmg+CiNpbmNsdWRlIDxzdGRpby5oPgoKX1N0YXRpY19hc3NlcnQoKC0xICYgMykgPT0gMywgIk5vdCAyJ3MgY29tcGxlbWVudCIpOwpfU3RhdGljX2Fzc2VydChVSU5UX01BWCA9PSAodW5zaWduZWQgaW50KSBJTlRfTUFYIC0gSU5UX01JTiwgIkJhZCBpbnRlZ2VyIik7CgovLwovLyBVdGlsaXR5LgovLwoKI2RlZmluZSBCSVRTKHgpIChzaXplb2YoeCkgKiBDSEFSX0JJVCkKCmludCBiaXRsZW5ndGgodW5zaWduZWQgaW50IHgpCnsKICAgIHJldHVybiB4ID8gQklUUyh4KSAtIF9fYnVpbHRpbl9jbHooeCkgOiAwOwp9CgovLwovLyBQcml2YXRlLgovLwoKI2RlZmluZSBHRU5FUkFUT1JfQklUUyAzMgoKc3RhdGljIF9UaHJlYWRfbG9jYWwgdWludF9mYXN0NjRfdCBnZW5lcmF0b3Jfc3RhdGUgPSBVSU5UNjRfQygweDEyMzRBQkNEMzMwRSk7CgpzdGF0aWMgdm9pZCBnZW5lcmF0b3Jfc2VlZCh1aW50X2Zhc3Q2NF90IHNlZWQpCnsKICAgIGdlbmVyYXRvcl9zdGF0ZSA9IHNlZWQ7CiAgICBnZW5lcmF0b3Jfc3RhdGUgJT0gKHVpbnRfZmFzdDY0X3QpIDEgPDwgNDg7Cn0KCnN0YXRpYyB1aW50X2Zhc3QzMl90IGdlbmVyYXRvcl9uZXh0KHZvaWQpCnsKICAgIGdlbmVyYXRvcl9zdGF0ZSAqPSAodWludF9mYXN0NjRfdCkgMjUyMTQ5MDM5MTc7CiAgICBnZW5lcmF0b3Jfc3RhdGUgKz0gKHVpbnRfZmFzdDY0X3QpIDExOwogICAgZ2VuZXJhdG9yX3N0YXRlICU9ICh1aW50X2Zhc3Q2NF90KSAxIDw8IDQ4OwogICAgcmV0dXJuIGdlbmVyYXRvcl9zdGF0ZSA+PiAxNjsKfQoKc3RhdGljIHVpbnRfZmFzdDMyX3QgdGFrZWJpdHModWludF9mYXN0MzJfdCB4LCBpbnQgYXZhaWxhYmxlLCBpbnQgdGFrZSkKewogICAgYXNzZXJ0KHRha2UgPj0gMSk7CiAgICBhc3NlcnQodGFrZSA8PSBhdmFpbGFibGUpOwogICAgcmV0dXJuICh4ID4+IChhdmFpbGFibGUgLSB0YWtlKSkgJiAoVUlOVDMyX0MoLTEpID4+IChCSVRTKHgpIC0gdGFrZSkpOwp9CgovLwovLyBQdWJsaWMuCi8vCgp2b2lkIHJhbmRzZWVkKHVpbnRfZmFzdDY0X3Qgc2VlZCkKewogICAgZ2VuZXJhdG9yX3NlZWQoc2VlZCk7Cn0KCnVpbnRfZmFzdDY0X3QgcmFuZGJpdHMoaW50IG4pCnsKICAgIGFzc2VydChuIDw9IDY0KTsKICAgIHN0YXRpYyBfVGhyZWFkX2xvY2FsIHVpbnRfZmFzdDMyX3QgcmVzZXJ2b2lyID0gMDsKICAgIHN0YXRpYyBfVGhyZWFkX2xvY2FsIGludCBhdmFpbGFibGUgPSAwOwoKICAgIHVpbnRfZmFzdDY0X3QgciA9IDA7CgogICAgd2hpbGUgKG4gPiAwKQogICAgewogICAgICAgIGlmIChhdmFpbGFibGUgPT0gMCkKICAgICAgICB7CiAgICAgICAgICAgIHJlc2Vydm9pciA9IGdlbmVyYXRvcl9uZXh0KCk7CiAgICAgICAgICAgIGF2YWlsYWJsZSA9IEdFTkVSQVRPUl9CSVRTOwogICAgICAgIH0KCiAgICAgICAgaW50IHRha2UgPSAoYXZhaWxhYmxlIDwgbikgPyBhdmFpbGFibGUgOiBuOwogICAgICAgIHIgPSAociA8PCB0YWtlKSB8IHRha2ViaXRzKHJlc2Vydm9pciwgYXZhaWxhYmxlLCB0YWtlKTsKICAgICAgICBhdmFpbGFibGUgLT0gdGFrZTsKICAgICAgICBuIC09IHRha2U7CiAgICB9CiAgICByZXR1cm4gcjsKfQoKdW5zaWduZWQgaW50IHJhbmR1aW50KHVuc2lnbmVkIGludCBtYXgpCnsKICAgIGlmIChtYXggPT0gMCkKICAgICAgICByZXR1cm4gMDsKCiAgICBpbnQgbiA9IGJpdGxlbmd0aChtYXgpOwoKICAgIHVuc2lnbmVkIGludCBiaXRfbWF4ID0gLTFVID4+IChCSVRTKG1heCkgLSBuKTsKICAgIHVuc2lnbmVkIGludCByID0gcmFuZGJpdHMobik7CgogICAgaWYgKG1heCA9PSBiaXRfbWF4KQogICAgICAgIHJldHVybiByOwoKICAgIHVuc2lnbmVkIGludCBtb2QgPSBtYXggKyAxOwogICAgdW5zaWduZWQgaW50IG1pbiA9IChiaXRfbWF4ICsgMSAtIG1vZCkgJSBtb2Q7CgogICAgd2hpbGUgKHIgPCBtaW4pCiAgICAgICAgciA9IHJhbmRiaXRzKG4pOwogICAgcmV0dXJuIHIgJSBtb2Q7Cn0KCmRvdWJsZSByYW5kcmVhbCh2b2lkKQp7CiAgICBkb3VibGUgciA9IHJhbmRiaXRzKERCTF9NQU5UX0RJRykgLyBleHAyKERCTF9NQU5UX0RJRyk7CiAgICBhc3NlcnQociA8IDEuMCk7CiAgICByZXR1cm4gcjsKfQoKX0Jvb2wgcmFuZGJvb2woZG91YmxlIHApCnsKICAgIGFzc2VydCgwLjAgPD0gcCAmJiBwIDw9IDEuMCk7CiAgICByZXR1cm4gcmFuZHJlYWwoKSA8IHA7Cn0KCi8vCi8vIFRlc3QuCi8vCgppbnQgdW5pZm9ybV9pbnRfZGlzdHJpYnV0aW9uX3VpbnQoaW50IGEsIGludCBiKQp7CiAgICBhc3NlcnQoYSA8PSBiKTsKICAgIHJldHVybiByYW5kdWludCgodW5zaWduZWQgaW50KSBiIC0gYSkgKyBhOwp9CgppbnQgdW5pZm9ybV9pbnRfZGlzdHJpYnV0aW9uX3JlYWwoaW50IGEsIGludCBiKQp7CiAgICBhc3NlcnQoYSA8PSBiKTsKICAgIHJldHVybiByYW5kcmVhbCgpICogKDEuMCArIGIgLSBhKSArIGE7Cn0KCmludCBiZXJub3VsbGlfZGlzdHJpYnV0aW9uXzUwXzUwKGludCBhLCBpbnQgYikKewogICAgYXNzZXJ0KGEgPT0gMCAmJiBiID09IDEpOwogICAgcmV0dXJuIHJhbmRib29sKDAuNSk7Cn0KCnVuc2lnbmVkIGxvbmcgcmFuZG9tX2RldmljZSgpCnsKICAgIHVuc2lnbmVkIGxvbmcgciA9IC0xOwogICAgRklMRSAqZnAgPSBmb3BlbigiL2Rldi91cmFuZG9tIiwgInIiKTsKICAgIGlmIChmcCkKICAgIHsKICAgICAgICBmcmVhZCgmciwgc2l6ZW9mIHIsIDEsIGZwKTsKICAgICAgICBmY2xvc2UoZnApOwogICAgfQogICAgcmV0dXJuIHI7Cn0KCmRvdWJsZSBjaGlfc3F1YXJlKGludCBuLCBpbnQgaywgaW50ICgqZikoaW50LCBpbnQpKQp7CiAgICBpbnQgKmggPSBjYWxsb2Moaywgc2l6ZW9mICpoKTsKICAgIGFzc2VydChoICE9IDAgfHwgayA9PSAwKTsKICAgIGZvciAoaW50IGogPSAwOyBqIDwgbjsgaisrKQogICAgewogICAgICAgIGludCBpID0gZigwLCBrLTEpOwogICAgICAgIGFzc2VydCgwIDw9IGkgJiYgaSA8IGspOwogICAgICAgIGhbaV0gKz0gMTsKICAgIH0KICAgIGRvdWJsZSBleHBlY3QgPSAoZG91YmxlKSBuIC8gazsKICAgIGRvdWJsZSB4MiA9IDA7CiAgICBmb3IgKGludCBpID0gMDsgaSA8IGs7IGkrKykKICAgICAgICB4MiArPSBwb3coaFtpXSAtIGV4cGVjdCwgMikgLyBleHBlY3Q7CiAgICBmcmVlKGgpOwogICAgcmV0dXJuIHgyOwp9Cgp2b2lkIHRlc3QoaW50IG0sIGludCBuLCBpbnQgZGYsIGRvdWJsZSB4MiwgaW50ICgqZikoaW50LCBpbnQpLAogICAgICAgICAgY29uc3QgY2hhciAqaGVhZGVyKQp7CiAgICBwdXRzKGhlYWRlcik7CiAgICBmb3IgKGludCBpID0gMDsgaSA8IG07IGkrKykKICAgIHsKICAgICAgICBkb3VibGUgciA9IGNoaV9zcXVhcmUobiwgZGYsIGYpOwogICAgICAgIHByaW50ZigiIHNpZ25pZmljYW50OiAlLTVzIFslNi4yZl1bJTYuMmZdXG4iLAogICAgICAgICAgICAgICByID49IHgyID8gInRydWUiIDogImZhbHNlIiwgciwgeDIpOwogICAgfQp9CgppbnQgbWFpbih2b2lkKQp7CiAgICB1bnNpZ25lZCBsb25nIHNlZWQgPSByYW5kb21fZGV2aWNlKCk7CiAgICBwcmludGYoInNlZWQ6ICVsdVxuIiwgc2VlZCk7CiAgICByYW5kc2VlZChzZWVkKTsKCiAgICBpbnQgbSA9IDU7CiAgICBpbnQgbiA9IDEwMDAwOwoKICAgIC8vIFBWPTAuMDUgZm9yIGFsbCB0ZXN0cy4KCiAgICB0ZXN0KG0sIG4sIDEwMCwgMTI0LjM0LCB1bmlmb3JtX2ludF9kaXN0cmlidXRpb25fdWludCwKICAgICAgICAgInVuaWZvcm1faW50X2Rpc3RyaWJ1dGlvbl91aW50Iik7CiAgICB0ZXN0KG0sIG4sIDEwMCwgMTI0LjM0LCB1bmlmb3JtX2ludF9kaXN0cmlidXRpb25fcmVhbCwKICAgICAgICAgInVuaWZvcm1faW50X2Rpc3RyaWJ1dGlvbl9yZWFsIik7CiAgICB0ZXN0KG0sIG4sIDIsIDUuOTksIGJlcm5vdWxsaV9kaXN0cmlidXRpb25fNTBfNTAsCiAgICAgICAgICJiZXJub3VsbGlfZGlzdHJpYnV0aW9uXzUwXzUwIik7CiAgICByZXR1cm4gMDsKfQ==