Skip to content

Instantly share code, notes, and snippets.

@hmenke hmenke/rand.c
Last active Jan 5, 2019

Embed
What would you like to do?
Legacy RNG from Lua 5.2 for Lua 5.3
/*
This program reimplements the GNU glibc random number generator.
https://www.mathstat.dal.ca/~selinger/random/
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=stdlib/random_r.c
*/
#include <assert.h>
#include <stdlib.h>
static int r[344];
#define GNU_RAND_MAX 2147483647
// https://stackoverflow.com/a/27030128
int add(int a, int b) {
if(b == 0)
return a;
return add(a ^ b, (a & b) << 1);
}
void gnu_srand(unsigned int seed) {
if (seed == 0) {
seed = 1;
}
r[0] = seed;
for (int i = 1; i < 31; ++i) {
/* (from stdlib/random_r.c) This does:
r[i] = (16807 * r[i - 1]) % 2147483647;
but avoids overflowing 31 bits. */
int hi = r[i - 1] / 127773;
int lo = r[i - 1] % 127773;
r[i] = (16807 * lo - 2836 * hi) & 0xffffffff;
}
for (int i = 31; i < 34; ++i) {
r[i] = r[i - 31];
}
for (int i = 34; i < 344; ++i) {
r[i] = add(r[i - 31], r[i - 3]);
}
}
int gnu_rand() {
static int i = 0;
r[i] = add(r[(i - 31 + 344) % 344], r[(i - 3 + 344) % 344]);
int x = (r[i] & 0xffffffff) >> 1;
i = (i + 1) % 344;
return x;
}
int main() {
/* The default seed is 1 */
gnu_srand(1);
assert(GNU_RAND_MAX == RAND_MAX);
srand(42);
gnu_srand(42);
for (int i = 0; i < 1000000; ++i) {
assert(gnu_rand() == rand());
}
}
--[[
This snippet implements the legacy Lua 5.2 random number generator for
Lua 5.3. Even though Lua 5.3 can do bitwise operations, we use the
bit32 library such that the code can be parsed by both Lua 5.2 and Lua
5.3. If you don't care about that, you can simply replace the bit32
calls with bitwise operations.
--]]
local ok, bit32 = pcall(require, "bit32")
if not ok then
ok, bit32 = pcall(require, "bit")
end
if not ok then
error("No bitwise operations available")
end
--if _VERSION == "Lua 5.3" or type(jit) == "table" then
do
local add -- https://stackoverflow.com/a/27030128
add = function(a,b)
if (bit32.bxor(b,0x0) == 0) then
return a
end
return add(bit32.bxor(a,b), bit32.lshift(bit32.band(a,b),1))
end
local r = {}
local ffffffff = bit32.bnot(0x00000000)
local RAND_MAX = 2147483647
local i = 0
local rand = function()
i = i % 344 + 1
r[i] = add(r[(i - 32 + 344) % 344 + 1], r[(i - 4 + 344) % 344 + 1])
local r = bit32.rshift(bit32.band(r[i], ffffffff), 1)
return r
end
local srand = function(seed)
-- can't seed with 0
if seed == 0 then
seed = 1
end
r[1] = seed
for i = 2, 31 do
--[[ (from stdlib/random_r.c) This does:
r[i] = (16807 * r[i - 1]) % 2147483647;
but avoids overflowing 31 bits. ]]
local hi = math.floor(r[i - 1] / 127773)
local lo = r[i - 1] % 127773
r[i] = bit32.band(16807 * lo - 2836 * hi, ffffffff)
end
for i = 32, 34 do
r[i] = r[i-31]
end
for i = 35, 344 do
r[i] = add(r[i-31], r[i-3])
end
end
function math.random(l,u)
local r = rand() / RAND_MAX
if l and u then -- lower and upper limits
assert(l <= u, "interval is empty")
return math.floor(r*(u-l+1)) + l
elseif l then -- only upper limit
assert(1.0 <= u, "interval is empty")
return math.floor(r*u) + 1.0
else -- no arguments
return r
end
end
function math.randomseed(seed)
if seed < 0 then
srand(math.floor(seed))
else
srand(math.ceil(seed))
end
rand() -- discard first value to avoid undesirable correlations
end
-- the default seed is 1
srand(1)
end
math.randomseed(42)
for i = 0, 10000000 do
print(math.random())
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.