#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL
#define UMASK 0x80000000UL
#define LMASK 0x7fffffffUL
#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
static unsigned long state[N];
static int left = 1;
static int initf = 0;
static unsigned long *next;
static void
init_genrand(s)
unsigned long s;
{
int j;
state[0]= s & 0xffffffffUL;
for (j=1; j<N; j++) {
state[j] = (1812433253UL * (state[j-1] ^ (state[j-1] >> 30)) + j);
state[j] &= 0xffffffffUL;
}
left = 1; initf = 1;
}
static void
init_by_array(unsigned long init_key[], int key_length)
{
int i, j, k;
init_genrand(19650218UL);
i=1; j=0;
k = (N>key_length ? N : key_length);
for (; k; k--) {
state[i] = (state[i] ^ ((state[i-1] ^ (state[i-1] >> 30)) * 1664525UL))
+ init_key[j] + j;
state[i] &= 0xffffffffUL;
i++; j++;
if (i>=N) { state[0] = state[N-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=N-1; k; k--) {
state[i] = (state[i] ^ ((state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL))
- i;
state[i] &= 0xffffffffUL;
i++;
if (i>=N) { state[0] = state[N-1]; i=1; }
}
state[0] = 0x80000000UL;
left = 1; initf = 1;
}
static void
next_state()
{
unsigned long *p=state;
int j;
if (initf==0) init_genrand(5489UL);
left = N;
next = state;
for (j=N-M+1; --j; p++)
*p = p[M] ^ TWIST(p[0], p[1]);
for (j=M; --j; p++)
*p = p[M-N] ^ TWIST(p[0], p[1]);
*p = p[M-N] ^ TWIST(p[0], state[0]);
}
unsigned long
rb_genrand_int32(void)
{
unsigned long y;
if (--left == 0) next_state();
y = *next++;
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
double
rb_genrand_real(void)
{
unsigned long a=rb_genrand_int32()>>5, b=rb_genrand_int32()>>6;
return(a*67108864.0+b)*(1.0/9007199254740992.0);
}
#undef N
#undef M
#include "ruby.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
static int first = 1;
static VALUE saved_seed = INT2FIX(0);
static VALUE
rand_init(vseed)
VALUE vseed;
{
volatile VALUE seed;
VALUE old;
long len;
unsigned long *buf;
seed = rb_to_int(vseed);
switch (TYPE(seed)) {
case T_FIXNUM:
len = sizeof(VALUE);
break;
case T_BIGNUM:
len = RBIGNUM(seed)->len * SIZEOF_BDIGITS;
if (len == 0)
len = 4;
break;
default:
rb_raise(rb_eTypeError, "failed to convert %s into Integer",
rb_obj_classname(vseed));
}
len = (len + 3) / 4;
buf = ALLOC_N(unsigned long, len);
memset(buf, 0, len * sizeof(long));
if (FIXNUM_P(seed)) {
buf[0] = FIX2ULONG(seed) & 0xffffffff;
#if SIZEOF_LONG > 4
buf[1] = FIX2ULONG(seed) >> 32;
#endif
}
else {
int i, j;
for (i = RBIGNUM(seed)->len-1; 0 <= i; i--) {
j = i * SIZEOF_BDIGITS / 4;
#if SIZEOF_BDIGITS < 4
buf[j] <<= SIZEOF_BDIGITS * 8;
#endif
buf[j] |= ((BDIGIT *)RBIGNUM(seed)->digits)[i];
}
}
while (1 < len && buf[len-1] == 0) {
len--;
}
if (len <= 1) {
init_genrand(buf[0]);
}
else {
if (buf[len-1] == 1)
len--;
init_by_array(buf, len);
}
first = 0;
old = saved_seed;
saved_seed = seed;
free(buf);
return old;
}
static VALUE
random_seed()
{
static int n = 0;
struct timeval tv;
int fd;
struct stat statbuf;
int seed_len;
BDIGIT *digits;
unsigned long *seed;
NEWOBJ(big, struct RBignum);
OBJSETUP(big, rb_cBignum, T_BIGNUM);
seed_len = 4 * sizeof(long);
big->sign = 1;
big->len = seed_len / SIZEOF_BDIGITS + 1;
digits = big->digits = ALLOC_N(BDIGIT, big->len);
seed = (unsigned long *)big->digits;
memset(digits, 0, big->len * SIZEOF_BDIGITS);
#ifdef S_ISCHR
if ((fd = open("/dev/urandom", O_RDONLY
#ifdef O_NONBLOCK
|O_NONBLOCK
#endif
#ifdef O_NOCTTY
|O_NOCTTY
#endif
#ifdef O_NOFOLLOW
|O_NOFOLLOW
#endif
)) >= 0) {
if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
read(fd, seed, seed_len);
}
close(fd);
}
#endif
gettimeofday(&tv, 0);
seed[0] ^= tv.tv_usec;
seed[1] ^= tv.tv_sec;
seed[2] ^= getpid() ^ (n++ << 16);
seed[3] ^= (unsigned long)&seed;
digits[big->len-1] = digits[big->len-2] <= 1 ? 1 : 0;
return rb_big_norm((VALUE)big);
}
static VALUE
rb_f_srand(argc, argv, obj)
int argc;
VALUE *argv;
VALUE obj;
{
VALUE seed, old;
rb_secure(4);
if (rb_scan_args(argc, argv, "01", &seed) == 0) {
seed = random_seed();
}
old = rand_init(seed);
return old;
}
static unsigned long
make_mask(unsigned long x)
{
x = x | x >> 1;
x = x | x >> 2;
x = x | x >> 4;
x = x | x >> 8;
x = x | x >> 16;
#if 4 < SIZEOF_LONG
x = x | x >> 32;
#endif
return x;
}
static unsigned long
limited_rand(unsigned long limit)
{
unsigned long mask = make_mask(limit);
int i;
unsigned long val;
retry:
val = 0;
for (i = SIZEOF_LONG/4-1; 0 <= i; i--) {
if (mask >> (i * 32)) {
val |= rb_genrand_int32() << (i * 32);
val &= mask;
if (limit < val)
goto retry;
}
}
return val;
}
static VALUE
limited_big_rand(struct RBignum *limit)
{
unsigned long mask, lim, rnd;
struct RBignum *val;
int i, len, boundary;
len = (limit->len * SIZEOF_BDIGITS + 3) / 4;
val = (struct RBignum *)rb_big_clone((VALUE)limit);
val->sign = 1;
#if SIZEOF_BDIGITS == 2
# define BIG_GET32(big,i) (((BDIGIT *)(big)->digits)[(i)*2] | \
((i)*2+1 < (big)->len ? (((BDIGIT *)(big)->digits)[(i)*2+1] << 16) \
: 0))
# define BIG_SET32(big,i,d) ((((BDIGIT *)(big)->digits)[(i)*2] = (d) & 0xffff), \
((i)*2+1 < (big)->len ? (((BDIGIT *)(big)->digits)[(i)*2+1] = (d) >> 16) \
: 0))
#else
# define BIG_GET32(big,i) (((BDIGIT *)(big)->digits)[i])
# define BIG_SET32(big,i,d) (((BDIGIT *)(big)->digits)[i] = (d))
#endif
retry:
mask = 0;
boundary = 1;
for (i = len-1; 0 <= i; i--) {
lim = BIG_GET32(limit, i);
mask = mask ? 0xffffffff : make_mask(lim);
if (mask) {
rnd = rb_genrand_int32() & mask;
if (boundary) {
if (lim < rnd)
goto retry;
if (rnd < lim)
boundary = 0;
}
}
else {
rnd = 0;
}
BIG_SET32(val, i, rnd);
}
return rb_big_norm((VALUE)val);
}
static VALUE
rb_f_rand(argc, argv, obj)
int argc;
VALUE *argv;
VALUE obj;
{
VALUE vmax;
long val, max;
rb_scan_args(argc, argv, "01", &vmax);
if (first) {
rand_init(random_seed());
}
switch (TYPE(vmax)) {
case T_FLOAT:
if (RFLOAT(vmax)->value <= LONG_MAX && RFLOAT(vmax)->value >= LONG_MIN) {
max = (long)RFLOAT(vmax)->value;
break;
}
if (RFLOAT(vmax)->value < 0)
vmax = rb_dbl2big(-RFLOAT(vmax)->value);
else
vmax = rb_dbl2big(RFLOAT(vmax)->value);
case T_BIGNUM:
bignum:
{
struct RBignum *limit = (struct RBignum *)vmax;
if (!limit->sign) {
limit = (struct RBignum *)rb_big_clone(vmax);
limit->sign = 1;
}
limit = (struct RBignum *)rb_big_minus((VALUE)limit, INT2FIX(1));
if (FIXNUM_P((VALUE)limit)) {
if (FIX2LONG((VALUE)limit) == -1)
return rb_float_new(rb_genrand_real());
return LONG2NUM(limited_rand(FIX2LONG((VALUE)limit)));
}
return limited_big_rand(limit);
}
case T_NIL:
max = 0;
break;
default:
vmax = rb_Integer(vmax);
if (TYPE(vmax) == T_BIGNUM) goto bignum;
case T_FIXNUM:
max = FIX2LONG(vmax);
break;
}
if (max == 0) {
return rb_float_new(rb_genrand_real());
}
if (max < 0) max = -max;
val = limited_rand(max-1);
return LONG2NUM(val);
}
void
Init_Random()
{
rb_define_global_function("srand", rb_f_srand, -1);
rb_define_global_function("rand", rb_f_rand, -1);
rb_global_variable(&saved_seed);
}