Skip to content

Instantly share code, notes, and snippets.

@Slipyx
Created December 11, 2011 04:57
Show Gist options
  • Save Slipyx/1458441 to your computer and use it in GitHub Desktop.
Save Slipyx/1458441 to your computer and use it in GitHub Desktop.
LCMWC
/*
** lcmwc
** A Complimentary-Multiply-With-Carry Random Number Generator library for Lua
**
** Copyright (C) 2011-2012 Josh Koch.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to
** deal in the Software without restriction, including without limitation the
** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
** sell copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
** IN THE SOFTWARE.
*/
#include <lauxlib.h>
#include <time.h>
/* Custom integer types */
#include <limits.h>
#define UINT32_MAX ULONG_MAX
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef long int32_t;
typedef unsigned long uint32_t;
typedef unsigned long long uint64_t;
/* Global states */
static uint32_t Q[4096] = {0}, c = 362436, id = 4095;
/* Random unsigned 32bit integer */
static uint32_t CMWC4096( void ) {
uint64_t t = 0;
uint32_t x = 0;
id = (id + 1) & 4095;
t = 18782LL * Q[id] + c;
c = (t >> 32);
x = (uint32_t)(t + c);
if ( x < c ) {
++x;
++c;
}
return (Q[id] = 0xFFFFFFFE - x);
}
/* Gets a "random" seed based on hash of current time or something */
static uint32_t TimeSeed( void ) {
time_t now = time( NULL );
uint8_t* p = (uint8_t*)&now;
uint32_t seed = 0;
size_t i = 0;
for ( i = 0; i < sizeof(now); ++i ) {
seed = seed * (0xFF + 2U) + p[i];
}
return seed;
}
/*
** The functions for Lua
*/
/* Seed the random generator */
static int lcmwc_seed( lua_State* L ) {
int32_t argc = lua_gettop( L ); /* Get number of arguments */
uint16_t i = 0;
uint32_t seed = 0;
/* No seed specified, so use the time hash thing */
if ( argc < 1 ) {
seed = TimeSeed();
/* Is first argument a number? */
} else if ( lua_isnumber( L, 1 ) == 0 ) {
lua_pushstring( L, "bad argument #1 to 'seed' (number expected)" );
lua_error( L );
/* Use the seed given */
} else {
seed = (uint32_t)lua_tonumber( L, 1 );
}
/* Do the actual seeding and state initialization */
Q[0] = seed;
for ( i = 1; i < 4096; ++i ) {
Q[i] = 1812433253 * (Q[i - 1] ^ (Q[i - 1] >> 30)) + i;
}
c = Q[4095] % 809430660;
id = 4095;
return 0; /* No return values */
}
/* Return a random number depending on range specified */
static int32_t lcmwc_random( lua_State* L ) {
int32_t argc = lua_gettop( L ); /* Get number of arguments */
/* Random lua_Number in range [0.0 - 1.0], not quite inclusive */
if ( argc < 1 ) {
lua_pushnumber( L, CMWC4096() / (lua_Number)UINT32_MAX );
} else {
int32_t min = 0, max = 0;
/* Is there at least one number given? */
if ( lua_isnumber( L, 1 ) == 0 ) {
lua_pushstring( L, "bad argument #1 to 'random' "
"(number expected)" );
lua_error( L );
}
/* Random integer in range [1 - max], inclusive */
if ( argc == 1 ) {
min = 1;
max = (int32_t)lua_tonumber( L, 1 );
/* For a single negative argument, range is [(max + 1) - 0] */
if ( max < 0 ) {
min = max + 1;
max = 0;
}
/* Random integer in range [min - max], inclusive */
} else if ( lua_isnumber( L, 2 ) ) {
min = (int32_t)lua_tonumber( L, 1 );
max = (int32_t)lua_tonumber( L, 2 );
/* If first argument isn't the minimum, swap the two */
if ( min > max ) {
int32_t tmp = min;
min = max; max = tmp;
}
/* Second argument is not a number */
} else {
lua_pushstring( L, "bad argument #2 to 'random' "
"(number expected)" );
lua_error( L );
}
/* Get random number in range */
lua_pushnumber( L, (int32_t)(CMWC4096() % (max - min + 1) + min) );
}
return 1; /* One random number is returned */
}
/* The table of registered functions */
static const struct luaL_Reg lcmwc_reg[] = {
{"seed", lcmwc_seed},
{"random", lcmwc_random},
{NULL, NULL}
};
/* Open the lib and register the functions */
#if defined(_WIN32) || defined(WIN32)
__declspec(dllexport)
#endif
int32_t luaopen_lcmwc( lua_State* L ) {
luaL_register( L, "lcmwc", lcmwc_reg );
/* Seed and initialize the PRNG on lib open */
lua_pushcfunction( L, lcmwc_seed );
lua_call( L, 0, 0 );
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment