Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 882 lines (814 sloc) 21.85 kb
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
1 /*
2 * Random Numbers.
3 *
4 * This file is covered by the Ruby license. See COPYING for more details.
5 *
6 * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
7 * Copyright (C) 1993-2007 Yukihiro Matsumoto
8 */
9c1d230 committing experimental branch content
Laurent Sansonetti authored
9
468a2ea Move Obj-C related headers around.
Thibault Martin-Lagardette authored
10 #include "ruby/macruby.h"
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
11 #include "ruby/node.h"
12 #include "vm.h"
13 #include "id.h"
cb8cef0 migrating bytestrings + misc cleanup
Laurent Sansonetti authored
14 #include "encoding.h"
9c1d230 committing experimental branch content
Laurent Sansonetti authored
15
16 #include <unistd.h>
17 #include <time.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
21 #include <math.h>
22 #include <errno.h>
9c1d230 committing experimental branch content
Laurent Sansonetti authored
23
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
24 #include "mt.c"
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
25
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
26 VALUE rb_cRandom;
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
27
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
28 typedef struct {
29 VALUE seed;
30 struct MT mt;
31 } rb_random_t;
32
33 #define get_rnd(obj) ((rb_random_t *)DATA_PTR(obj))
34 #define default_rnd() (get_rnd(rb_vm_default_random()))
35
36 unsigned int
37 rb_genrand_int32(void)
38 {
39 return genrand_int32(&default_rnd()->mt);
40 }
41
42 double
43 rb_genrand_real(void)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
44 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
45 return genrand_real(&default_rnd()->mt);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
46 }
47
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
48 #define BDIGITS(x) (RBIGNUM_DIGITS(x))
49 #define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
50 #define BIGRAD ((BDIGIT_DBL)1 << BITSPERDIG)
51 #define DIGSPERINT (SIZEOF_INT/SIZEOF_BDIGITS)
52 #define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG)
53 #define BIGDN(x) RSHIFT(x,BITSPERDIG)
54 #define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1)))
55 #define BDIGMAX ((BDIGIT)-1)
56
57 #define roomof(n, m) (int)(((n)+(m)-1) / (m))
58 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
59 #define SIZEOF_INT32 (31/CHAR_BIT + 1)
60
61 static VALUE random_seed(VALUE, SEL);
62
63 /* :nodoc: */
9c1d230 committing experimental branch content
Laurent Sansonetti authored
64 static VALUE
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
65 random_alloc(VALUE klass, SEL sel)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
66 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
67 rb_random_t *r = (rb_random_t *)xmalloc(sizeof(rb_random_t));
68 r->seed = INT2FIX(0);
69 return Data_Wrap_Struct(rb_cRandom, NULL, NULL, r);
70 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
71
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
72 static VALUE
73 rand_init(struct MT *mt, VALUE vseed)
74 {
75 VALUE seed;
76 long blen = 0;
77 int i, j, len;
78 unsigned int buf0[SIZEOF_LONG / SIZEOF_INT32 * 4], *buf = buf0;
79
80 seed = rb_to_int(vseed);
81 switch (TYPE(seed)) {
82 case T_FIXNUM:
83 len = 1;
84 buf[0] = (unsigned int)(FIX2ULONG(seed) & 0xffffffff);
85 #if SIZEOF_LONG > SIZEOF_INT32
86 if ((buf[1] = (unsigned int)(FIX2ULONG(seed) >> 32)) != 0) ++len;
87 #endif
88 break;
89 case T_BIGNUM:
90 blen = RBIGNUM_LEN(seed);
91 if (blen == 0) {
92 len = 1;
93 }
94 else {
95 if (blen > MT_MAX_STATE * SIZEOF_INT32 / SIZEOF_BDIGITS)
96 blen = (len = MT_MAX_STATE) * SIZEOF_INT32 / SIZEOF_BDIGITS;
97 len = roomof((int)blen * SIZEOF_BDIGITS, SIZEOF_INT32);
98 }
99 /* allocate ints for init_by_array */
100 if (len > numberof(buf0)) buf = ALLOC_N(unsigned int, len);
101 memset(buf, 0, len * sizeof(*buf));
102 len = 0;
103 for (i = (int)(blen-1); 0 <= i; i--) {
104 j = i * SIZEOF_BDIGITS / SIZEOF_INT32;
105 #if SIZEOF_BDIGITS < SIZEOF_INT32
106 buf[j] <<= BITSPERDIG;
107 #endif
108 buf[j] |= RBIGNUM_DIGITS(seed)[i];
109 if (!len && buf[j]) len = j;
110 }
111 ++len;
112 break;
113 default:
114 rb_raise(rb_eTypeError, "failed to convert %s into Integer",
115 rb_obj_classname(vseed));
116 }
117 if (len <= 1) {
118 init_genrand(mt, buf[0]);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
119 }
120 else {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
121 if (buf[len-1] == 1) /* remove leading-zero-guard */
122 len--;
123 init_by_array(mt, buf, len);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
124 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
125 return seed;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
126 }
127
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
128 /*
129 * call-seq: Random.new([seed]) -> prng
130 *
131 * Creates new Mersenne Twister based pseudorandom number generator with
132 * seed. When the argument seed is omitted, the generator is initialized
133 * with Random.seed.
134 *
135 * The argument seed is used to ensure repeatable sequences of random numbers
136 * between different runs of the program.
137 *
138 * prng = Random.new(1234)
139 * [ prng.rand, prng.rand ] #=> [0.191519450378892, 0.622108771039832]
140 * [ prng.integer(10), prng.integer(1000) ] #=> [4, 664]
141 * prng = Random.new(1234)
142 * [ prng.rand, prng.rand ] #=> [0.191519450378892, 0.622108771039832]
143 */
9c1d230 committing experimental branch content
Laurent Sansonetti authored
144 static VALUE
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
145 random_init(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
146 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
147 VALUE vseed;
148 rb_random_t *rnd = get_rnd(obj);
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
149
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
150 if (argc == 0) {
151 vseed = random_seed(0, 0);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
152 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
153 else {
154 rb_scan_args(argc, argv, "01", &vseed);
155 }
156 GC_WB(&rnd->seed, rand_init(&rnd->mt, vseed));
157 return obj;
158 }
159
160 #define DEFAULT_SEED_CNT 4
161 #define DEFAULT_SEED_LEN (DEFAULT_SEED_CNT * sizeof(int))
162
163 static void
164 fill_random_seed(unsigned int seed[DEFAULT_SEED_CNT])
165 {
166 static int n = 0;
167 struct timeval tv;
168 int fd;
169 struct stat statbuf;
170
171 memset(seed, 0, DEFAULT_SEED_LEN);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
172
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
173 if ((fd = open("/dev/urandom", O_RDONLY
174 #ifdef O_NONBLOCK
175 |O_NONBLOCK
176 #endif
177 #ifdef O_NOCTTY
178 |O_NOCTTY
179 #endif
180 #ifdef O_NOFOLLOW
181 |O_NOFOLLOW
182 #endif
183 )) >= 0) {
184 if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
185 (void)read(fd, seed, DEFAULT_SEED_LEN);
186 }
187 close(fd);
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
188 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
189
190 gettimeofday(&tv, 0);
191 seed[0] ^= tv.tv_usec;
192 seed[1] ^= (unsigned int)tv.tv_sec;
193 #if SIZEOF_TIME_T > SIZEOF_INT
194 seed[0] ^= (unsigned int)((time_t)tv.tv_sec >> SIZEOF_INT * CHAR_BIT);
195 #endif
196 seed[2] ^= getpid() ^ (n++ << 16);
197 seed[3] ^= (unsigned int)(VALUE)&seed;
198 #if SIZEOF_VOIDP > SIZEOF_INT
199 seed[2] ^= (unsigned int)((VALUE)&seed >> SIZEOF_INT * CHAR_BIT);
200 #endif
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
201 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
202
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
203 static VALUE
204 make_seed_value(const void *ptr)
205 {
206 BDIGIT *digits;
207 NEWOBJ(big, struct RBignum);
208 OBJSETUP(big, rb_cBignum, T_BIGNUM);
209
210 RBIGNUM_SET_SIGN(big, 1);
211 rb_big_resize((VALUE)big, DEFAULT_SEED_LEN / SIZEOF_BDIGITS + 1);
212 digits = RBIGNUM_DIGITS(big);
213
214 MEMCPY(digits, ptr, char, DEFAULT_SEED_LEN);
215
216 /* set leading-zero-guard if need. */
217 digits[RBIGNUM_LEN(big)-1] = digits[RBIGNUM_LEN(big)-2] <= 1 ? 1 : 0;
218
219 return rb_big_norm((VALUE)big);
220 }
221
222 /*
223 * call-seq: Random.seed -> integer
224 *
225 * Returns arbitrary value for seed.
226 */
227 static VALUE
228 random_seed(VALUE rcv, SEL sel)
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
229 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
230 unsigned int buf[DEFAULT_SEED_CNT];
231 fill_random_seed(buf);
232 return make_seed_value(buf);
233 }
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
234
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
235 /*
236 * call-seq: prng.seed -> integer
237 *
238 * Returns the seed of the generator.
239 */
240 static VALUE
241 random_get_seed(VALUE obj, SEL sel)
242 {
243 return get_rnd(obj)->seed;
244 }
245
246 /* :nodoc: */
247 static VALUE
248 random_copy(VALUE obj, SEL sel, VALUE orig)
249 {
250 rb_random_t *rnd1 = get_rnd(obj);
251 rb_random_t *rnd2 = get_rnd(orig);
252 struct MT *mt = &rnd1->mt;
253
254 *rnd1 = *rnd2;
255 GC_WB(&rnd1->seed, rnd2->seed);
256 mt->next = mt->state + numberof(mt->state) - mt->left + 1;
257 return obj;
258 }
259
260 static VALUE
261 mt_state(const struct MT *mt)
262 {
263 const int n = numberof(mt->state);
264 VALUE bigo = rb_big_new(n, 1);
265 BDIGIT *d = RBIGNUM_DIGITS(bigo);
266 for (int i = 0; i < n; i++) {
267 unsigned int x = mt->state[i];
268 *d++ = (BDIGIT)x;
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
269 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
270 return rb_big_norm(bigo);
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
271 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
272
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
273 /* :nodoc: */
274 static VALUE
275 random_state(VALUE obj, SEL sel)
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
276 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
277 rb_random_t *rnd = get_rnd(obj);
278 return mt_state(&rnd->mt);
279 }
280
281 /* :nodoc: */
282 static VALUE
283 random_s_state(VALUE klass, SEL sel)
284 {
285 return mt_state(&default_rnd()->mt);
286 }
287
288 /* :nodoc: */
289 static VALUE
290 random_left(VALUE obj, SEL sel)
291 {
292 rb_random_t *rnd = get_rnd(obj);
293 return INT2FIX(rnd->mt.left);
294 }
295
296 /* :nodoc: */
297 static VALUE
298 random_s_left(VALUE klass, SEL sel)
299 {
300 return INT2FIX(default_rnd()->mt.left);
301 }
302
303 /* :nodoc: */
304 static VALUE
305 random_dump(VALUE obj, SEL sel)
306 {
307 rb_random_t *rnd = get_rnd(obj);
308 VALUE dump = rb_ary_new2(3);
309
310 rb_ary_push(dump, mt_state(&rnd->mt));
311 rb_ary_push(dump, INT2FIX(rnd->mt.left));
312 rb_ary_push(dump, rnd->seed);
313
314 return dump;
315 }
316
317 /* :nodoc: */
318 static VALUE
319 random_load(VALUE obj, SEL sel, VALUE dump)
320 {
321 rb_random_t *rnd = get_rnd(obj);
322 struct MT *mt = &rnd->mt;
323 VALUE state, left = INT2FIX(1), seed = INT2FIX(0);
324 unsigned long x;
325
326 Check_Type(dump, T_ARRAY);
327 switch (RARRAY_LEN(dump)) {
328 case 3:
329 seed = RARRAY_AT(dump, 2);
330 case 2:
331 left = RARRAY_AT(dump, 1);
332 case 1:
333 state = RARRAY_AT(dump, 0);
334 break;
335 default:
336 rb_raise(rb_eArgError, "wrong dump data");
337 }
338 memset(mt->state, 0, sizeof(mt->state));
339 #if 0
340 if (FIXNUM_P(state)) {
341 x = FIX2ULONG(state);
342 mt->state[0] = (unsigned int)x;
343 #if SIZEOF_LONG / SIZEOF_INT >= 2
344 mt->state[1] = (unsigned int)(x >> BITSPERDIG);
345 #endif
346 #if SIZEOF_LONG / SIZEOF_INT >= 3
347 mt->state[2] = (unsigned int)(x >> 2 * BITSPERDIG);
348 #endif
349 #if SIZEOF_LONG / SIZEOF_INT >= 4
350 mt->state[3] = (unsigned int)(x >> 3 * BITSPERDIG);
351 #endif
352 }
353 else {
354 BDIGIT *d;
355 long len;
356 Check_Type(state, T_BIGNUM);
357 len = RBIGNUM_LEN(state);
358 if (len > roomof(sizeof(mt->state), SIZEOF_BDIGITS)) {
359 len = roomof(sizeof(mt->state), SIZEOF_BDIGITS);
360 }
361 #if SIZEOF_BDIGITS < SIZEOF_INT
362 else if (len % DIGSPERINT) {
363 d = RBIGNUM_DIGITS(state) + len;
364 # if DIGSPERINT == 2
365 --len;
366 x = *--d;
367 # else
368 x = 0;
369 do {
370 x = (x << BITSPERDIG) | *--d;
371 } while (--len % DIGSPERINT);
372 # endif
373 mt->state[len / DIGSPERINT] = (unsigned int)x;
374 }
375 #endif
376 if (len > 0) {
377 d = BDIGITS(state) + len;
378 do {
379 --len;
380 x = *--d;
381 # if DIGSPERINT == 2
382 --len;
383 x = (x << BITSPERDIG) | *--d;
384 # elif SIZEOF_BDIGITS < SIZEOF_INT
385 do {
386 x = (x << BITSPERDIG) | *--d;
387 } while (--len % DIGSPERINT);
388 # endif
389 mt->state[len / DIGSPERINT] = (unsigned int)x;
390 } while (len > 0);
391 }
392 }
393 #endif
394 x = NUM2ULONG(left);
395 if (x > numberof(mt->state)) {
396 rb_raise(rb_eArgError, "wrong value");
397 }
398 mt->left = (unsigned int)x;
399 mt->next = mt->state + numberof(mt->state) - x + 1;
400 GC_WB(&rnd->seed, rb_to_int(seed));
401
402 return obj;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
403 }
404
405 /*
406 * call-seq:
407 * srand(number=0) => old_seed
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
408 *
9c1d230 committing experimental branch content
Laurent Sansonetti authored
409 * Seeds the pseudorandom number generator to the value of
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
410 * <i>number</i>. If <i>number</i> is omitted
9c1d230 committing experimental branch content
Laurent Sansonetti authored
411 * or zero, seeds the generator using a combination of the time, the
412 * process id, and a sequence number. (This is also the behavior if
413 * <code>Kernel::rand</code> is called without previously calling
414 * <code>srand</code>, but without the sequence.) By setting the seed
415 * to a known value, scripts can be made deterministic during testing.
416 * The previous seed value is returned. Also see <code>Kernel::rand</code>.
417 */
418
419 static VALUE
349dd2b ported the random.c apis to the new runtime
Laurent Sansonetti authored
420 rb_f_srand(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
421 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
422 VALUE seed, old;
423 rb_random_t *r = default_rnd();
9c1d230 committing experimental branch content
Laurent Sansonetti authored
424
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
425 rb_secure(4);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
426 if (argc == 0) {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
427 seed = random_seed(0, 0);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
428 }
429 else {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
430 rb_scan_args(argc, argv, "01", &seed);
431 }
432 old = r->seed;
433 GC_WB(&r->seed, rand_init(&r->mt, seed));
434
435 return old;
436 }
437
438 static unsigned long
439 make_mask(unsigned long x)
440 {
441 x = x | x >> 1;
442 x = x | x >> 2;
443 x = x | x >> 4;
444 x = x | x >> 8;
445 x = x | x >> 16;
446 #if 4 < SIZEOF_LONG
447 x = x | x >> 32;
448 #endif
449 return x;
450 }
451
452 static unsigned long
453 limited_rand(struct MT *mt, unsigned long limit)
454 {
455 if (limit == 0) {
456 return 0;
457 }
458 unsigned long val, mask = make_mask(limit);
459 retry:
460 val = 0;
461 for (int i = SIZEOF_LONG / SIZEOF_INT32 - 1; 0 <= i; i--) {
462 if ((mask >> (i * 32)) & 0xffffffff) {
463 val |= (unsigned long)genrand_int32(mt) << (i * 32);
464 val &= mask;
465 if (limit < val) {
466 goto retry;
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
467 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
468 }
469 }
470 return val;
471 }
472
473 static VALUE
474 limited_big_rand(struct MT *mt, struct RBignum *limit)
475 {
9bb2751 Fix random generation of bignums on 64-bit 10.6+ machines.
Patrick Thomson authored
476 #if SIZEOF_BDIGITS < 8
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
477 const long len = (RBIGNUM_LEN(limit) * SIZEOF_BDIGITS + 3) / 4;
9bb2751 Fix random generation of bignums on 64-bit 10.6+ machines.
Patrick Thomson authored
478 #else
479 const long len = (RBIGNUM_LEN(limit) * SIZEOF_BDIGITS + 3) / 8;
480 #endif
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
481 struct RBignum *val = (struct RBignum *)rb_big_clone((VALUE)limit);
482 RBIGNUM_SET_SIGN(val, 1);
483 #if SIZEOF_BDIGITS == 2
484 # define BIG_GET32(big,i) \
485 (RBIGNUM_DIGITS(big)[(i)*2] | \
486 ((i)*2+1 < RBIGNUM_LEN(big) ? \
487 (RBIGNUM_DIGITS(big)[(i)*2+1] << 16) : \
488 0))
489 # define BIG_SET32(big,i,d) \
490 ((RBIGNUM_DIGITS(big)[(i)*2] = (d) & 0xffff), \
491 ((i)*2+1 < RBIGNUM_LEN(big) ? \
492 (RBIGNUM_DIGITS(big)[(i)*2+1] = (d) >> 16) : \
493 0))
494 #else
495 /* SIZEOF_BDIGITS == 4 */
496 # define BIG_GET32(big,i) (RBIGNUM_DIGITS(big)[i])
497 # define BIG_SET32(big,i,d) (RBIGNUM_DIGITS(big)[i] = (d))
498 #endif
499 unsigned long mask;
500 int boundary;
501 retry:
502 mask = 0;
503 boundary = 1;
504 for (long i = len - 1; 0 <= i; i--) {
505 const unsigned long lim = BIG_GET32(limit, i);
506 unsigned long rnd;
507 mask = mask != 0 ? 0xffffffff : make_mask(lim);
508 if (mask != 0) {
509 rnd = genrand_int32(mt) & mask;
510 if (boundary) {
511 if (lim < rnd) {
512 goto retry;
513 }
514 if (rnd < lim) {
515 boundary = 0;
516 }
517 }
518 }
519 else {
520 rnd = 0;
521 }
522 BIG_SET32(val, i, (BDIGIT)rnd);
523 }
524 return rb_big_norm((VALUE)val);
525 }
526
527 unsigned long
528 rb_rand_internal(unsigned long i)
529 {
530 struct MT *mt = &default_rnd()->mt;
531 if (!genrand_initialized(mt)) {
532 rand_init(mt, random_seed(0, 0));
533 }
534 return limited_rand(mt, i);
535 }
536
537 unsigned int
538 rb_random_int32(VALUE obj)
539 {
540 rb_random_t *rnd = get_rnd(obj);
541 return genrand_int32(&rnd->mt);
542 }
543
544 double
545 rb_random_real(VALUE obj)
546 {
547 rb_random_t *rnd = get_rnd(obj);
548 return genrand_real(&rnd->mt);
549 }
550
551 /*
552 * call-seq: prng.bytes(size) -> prng
553 *
554 * Returns a random binary string. The argument size specified the length of
555 * the result string.
556 */
557 static VALUE
558 random_bytes(VALUE obj, SEL sel, VALUE len)
559 {
560 long n = NUM2LONG(rb_to_int(len));
561 if (n <= 0) {
562 return rb_str_new2("");
563 }
564 rb_random_t *rnd = get_rnd(obj);
565 UInt8 *ptr = (UInt8 *)malloc(n);
566 unsigned int r = genrand_int32(&rnd->mt);
567 for (long i = 0; i < n; i++) {
568 ptr[i] = (char)r;
569 r >>= CHAR_BIT;
570 }
adfb972 expose the bstr APIs
Laurent Sansonetti authored
571 VALUE bytes = rb_bstr_new_with_data(ptr, n);
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
572 free(ptr);
573 return bytes;
574 }
575
576 static VALUE
577 range_values(VALUE vmax, VALUE *begp, int *exclp)
578 {
579 VALUE end;
580 if (!rb_range_values(vmax, begp, &end, exclp)) {
581 return Qfalse;
582 }
583 if (!rb_vm_respond_to(end, selMINUS, false)) {
584 return Qfalse;
585 }
70ea0b5 per-vm method cache + misc fixes/improvements
Laurent Sansonetti authored
586 VALUE r = rb_vm_call(end, selMINUS, 1, begp);
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
587 if (NIL_P(r)) {
588 return Qfalse;
589 }
590 return r;
591 }
592
593 static VALUE
594 rand_int(struct MT *mt, VALUE vmax, int restrictive)
595 {
596 long max;
597 unsigned long r;
598
599 if (FIXNUM_P(vmax)) {
600 max = FIX2LONG(vmax);
601 if (!max) return Qnil;
602 if (max < 0) {
603 if (restrictive) return Qnil;
604 max = -max;
605 }
606 r = limited_rand(mt, (unsigned long)max - 1);
607 return ULONG2NUM(r);
608 }
609 else {
610 VALUE ret;
611 if (rb_bigzero_p(vmax)) return Qnil;
612 if (!RBIGNUM_SIGN(vmax)) {
613 if (restrictive) return Qnil;
614 vmax = rb_big_clone(vmax);
615 RBIGNUM_SET_SIGN(vmax, 1);
616 }
617 vmax = rb_big_minus(vmax, INT2FIX(1));
618 if (FIXNUM_P(vmax)) {
619 max = FIX2LONG(vmax);
620 if (max == -1) return Qnil;
621 r = limited_rand(mt, max);
622 return LONG2NUM(r);
623 }
624 ret = limited_big_rand(mt, RBIGNUM(vmax));
625 return ret;
626 }
627 }
628
629 static inline double
630 float_value(VALUE v)
631 {
632 double x = RFLOAT_VALUE(v);
633 if (isinf(x) || isnan(x)) {
634 VALUE error = INT2FIX(EDOM);
635 rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
636 }
637 return x;
638 }
639
640 /*
641 * call-seq:
642 * prng.rand -> float
643 * prng.rand(limit) -> number
644 *
645 * When the argument is an +Integer+ or a +Bignum+, it returns a
646 * random integer greater than or equal to zero and less than the
647 * argument. Unlike Random.rand, when the argument is a negative
648 * integer or zero, it raises an ArgumentError.
649 *
650 * When the argument is a +Float+, it returns a random floating point
651 * number between 0.0 and _max_, including 0.0 and excluding _max_.
652 *
653 * When the argument _limit_ is a +Range+, it returns a random
654 * number where range.member?(number) == true.
655 * prng.rand(5..9) # => one of [5, 6, 7, 8, 9]
656 * prng.rand(5...9) # => one of [5, 6, 7, 8]
657 * prng.rand(5.0..9.0) # => between 5.0 and 9.0, including 9.0
658 * prng.rand(5.0...9.0) # => between 5.0 and 9.0, excluding 9.0
659 *
660 * +begin+/+end+ of the range have to have subtract and add methods.
661 *
662 * Otherwise, it raises an ArgumentError.
663 */
664 static VALUE
665 random_rand(VALUE obj, SEL sel, int argc, VALUE *argv)
666 {
667 rb_random_t *rnd = get_rnd(obj);
668 VALUE beg = Qundef, v;
669 int excl = 0;
670
671 if (argc == 0) {
672 zero_arg:
673 return rb_float_new(genrand_real(&rnd->mt));
674 }
675 else if (argc != 1) {
676 rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc);
677 }
678 VALUE vmax = argv[0];
679 if (NIL_P(vmax)) {
680 goto zero_arg;
681 }
682 else if (TYPE(vmax) != T_FLOAT
683 && (v = rb_check_to_integer(vmax, "to_int"), !NIL_P(v))) {
684 v = rand_int(&rnd->mt, vmax = v, 1);
685 }
686 else if (v = rb_check_to_float(vmax), !NIL_P(v)) {
687 const double max = float_value(v);
688 if (max > 0.0) {
689 v = rb_float_new(max * genrand_real(&rnd->mt));
690 }
691 else {
692 v = Qnil;
693 }
694 }
695 else if ((v = range_values(vmax, &beg, &excl)) != Qfalse) {
696 vmax = v;
697 if (TYPE(vmax) != T_FLOAT
698 && (v = rb_check_to_integer(vmax, "to_int"), !NIL_P(v))) {
699 long max;
700 vmax = v;
701 v = Qnil;
702 if (FIXNUM_P(vmax)) {
703 fixnum:
704 if ((max = FIX2LONG(vmax) - excl) >= 0) {
705 unsigned long r = limited_rand(&rnd->mt, (unsigned long)max);
706 v = ULONG2NUM(r);
707 }
708 }
709 else if (BUILTIN_TYPE(vmax) == T_BIGNUM && RBIGNUM_SIGN(vmax)
710 && !rb_bigzero_p(vmax)) {
711 vmax = excl ? rb_big_minus(vmax, INT2FIX(1)) : rb_big_norm(vmax);
712 if (FIXNUM_P(vmax)) {
713 excl = 0;
714 goto fixnum;
715 }
716 v = limited_big_rand(&rnd->mt, RBIGNUM(vmax));
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
717 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
718 }
719 else if (v = rb_check_to_float(vmax), !NIL_P(v)) {
720 double max = float_value(v), r;
721 v = Qnil;
722 if (max > 0.0) {
723 if (excl) {
724 r = genrand_real(&rnd->mt);
725 }
726 else {
727 r = genrand_real2(&rnd->mt);
728 }
729 v = rb_float_new(r * max);
730 }
731 else if (max == 0.0 && !excl) {
732 v = rb_float_new(0.0);
8f9d863 re-implemented #rand and #srand to use random(3) and srandom(3), faster ...
Laurent Sansonetti authored
733 }
734 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
735 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
736 else {
737 v = Qnil;
5950c89 fix clang build (patch by Jordan Hubbard)
Laurent Sansonetti authored
738 (void)NUM2LONG(vmax);
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
739 }
740 if (NIL_P(v)) {
741 VALUE mesg = rb_str_new2("invalid argument - ");
742 rb_str_append(mesg, rb_obj_as_string(argv[0]));
743 rb_exc_raise(rb_exc_new3(rb_eArgError, mesg));
744 }
745 if (beg == Qundef) {
746 return v;
747 }
748 if (FIXNUM_P(beg) && FIXNUM_P(v)) {
749 long x = FIX2LONG(beg) + FIX2LONG(v);
750 return LONG2NUM(x);
751 }
752 switch (TYPE(v)) {
753 case T_BIGNUM:
754 return rb_big_plus(v, beg);
755 case T_FLOAT:
756 {
757 double d = RFLOAT_VALUE(v) + RFLOAT_VALUE(rb_check_to_float(beg));
758 return DOUBLE2NUM(d);
759 }
760 default:
70ea0b5 per-vm method cache + misc fixes/improvements
Laurent Sansonetti authored
761 return rb_vm_call(v, selPLUS, 1, &beg);
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
762 }
763 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
764
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
765 /*
766 * call-seq:
767 * prng1 == prng2 -> true or false
768 *
769 * Returns true if the generators' states equal.
770 */
771 static VALUE
772 random_equal(VALUE self, SEL sel, VALUE other)
773 {
774 if (rb_obj_class(self) != rb_obj_class(other)) {
775 return Qfalse;
776 }
777 rb_random_t *r1 = get_rnd(self);
778 rb_random_t *r2 = get_rnd(other);
779 if (rb_equal(r1->seed, r2->seed) != Qtrue) {
780 return Qfalse;
781 }
782 if (memcmp(r1->mt.state, r2->mt.state, sizeof(r1->mt.state))) {
783 return Qfalse;
784 }
785 if ((r1->mt.next - r1->mt.state) != (r2->mt.next - r2->mt.state)) {
786 return Qfalse;
787 }
788 if (r1->mt.left != r2->mt.left) {
789 return Qfalse;
790 }
791 return Qtrue;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
792 }
793
794 /*
795 * call-seq:
796 * rand(max=0) => number
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
797 *
9c1d230 committing experimental branch content
Laurent Sansonetti authored
798 * Converts <i>max</i> to an integer using max1 =
799 * max<code>.to_i.abs</code>. If the result is zero, returns a
800 * pseudorandom floating point number greater than or equal to 0.0 and
801 * less than 1.0. Otherwise, returns a pseudorandom integer greater
802 * than or equal to zero and less than max1. <code>Kernel::srand</code>
803 * may be used to ensure repeatable sequences of random numbers between
804 * different runs of the program. Ruby currently uses a modified
805 * Mersenne Twister with a period of 2**19937-1.
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
806 *
9c1d230 committing experimental branch content
Laurent Sansonetti authored
807 * srand 1234 #=> 0
808 * [ rand, rand ] #=> [0.191519450163469, 0.49766366626136]
809 * [ rand(10), rand(1000) ] #=> [6, 817]
810 * srand 1234 #=> 1234
811 * [ rand, rand ] #=> [0.191519450163469, 0.49766366626136]
812 */
813
814 static VALUE
349dd2b ported the random.c apis to the new runtime
Laurent Sansonetti authored
815 rb_f_rand(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
816 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
817 struct MT *mt = &default_rnd()->mt;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
818
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
819 if (!genrand_initialized(mt)) {
820 rand_init(mt, random_seed(0, 0));
821 }
822 if (argc == 0) {
823 goto zero_arg;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
824 }
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
825 VALUE vmax;
826 rb_scan_args(argc, argv, "01", &vmax);
827 if (NIL_P(vmax)) {
828 goto zero_arg;
829 }
830 vmax = rb_to_int(vmax);
831 VALUE r;
832 if (vmax == INT2FIX(0) || NIL_P(r = rand_int(mt, vmax, 0))) {
833 zero_arg:
834 return DBL2NUM(genrand_real(mt));
835 }
836 return r;
837 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
838
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
839 static void
840 Init_RandomSeed(void)
841 {
842 VALUE random = random_alloc(0, 0);
843 unsigned int initial[DEFAULT_SEED_CNT];
844 GC_WB(&get_rnd(random)->seed, make_seed_value(initial));
845 rb_vm_set_default_random(random);
846 }
847
848 void
849 rb_reset_random_seed(void)
850 {
851 rb_random_t *r = default_rnd();
852 uninit_genrand(&r->mt);
853 r->seed = INT2FIX(0);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
854 }
855
856 void
857 Init_Random(void)
858 {
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
859 Init_RandomSeed();
cbe906e introduce rb_objc_define_module_function() which mimics the ruby spec
Laurent Sansonetti authored
860 rb_objc_define_module_function(rb_mKernel, "srand", rb_f_srand, -1);
861 rb_objc_define_module_function(rb_mKernel, "rand", rb_f_rand, -1);
6581400 integrate Mersenne Twister based random number generator from 1.9 upstre...
Laurent Sansonetti authored
862
863 rb_cRandom = rb_define_class("Random", rb_cObject);
864 rb_objc_define_method(*(VALUE *)rb_cRandom, "alloc", random_alloc, 0);
865 rb_objc_define_method(rb_cRandom, "initialize", random_init, -1);
866 rb_objc_define_method(rb_cRandom, "rand", random_rand, -1);
867 rb_objc_define_method(rb_cRandom, "bytes", random_bytes, 1);
868 rb_objc_define_method(rb_cRandom, "seed", random_get_seed, 0);
869 rb_objc_define_method(rb_cRandom, "initialize_copy", random_copy, 1);
870 rb_objc_define_method(rb_cRandom, "marshal_dump", random_dump, 0);
871 rb_objc_define_method(rb_cRandom, "marshal_load", random_load, 1);
872 rb_objc_define_private_method(rb_cRandom, "state", random_state, 0);
873 rb_objc_define_private_method(rb_cRandom, "left", random_left, 0);
874 rb_objc_define_method(rb_cRandom, "==", random_equal, 1);
875
876 rb_objc_define_method(*(VALUE *)rb_cRandom, "srand", rb_f_srand, -1);
877 rb_objc_define_method(*(VALUE *)rb_cRandom, "rand", rb_f_rand, -1);
878 rb_objc_define_method(*(VALUE *)rb_cRandom, "new_seed", random_seed, 0);
879 rb_objc_define_private_method(*(VALUE *)rb_cRandom, "state", random_s_state, 0);
880 rb_objc_define_private_method(*(VALUE *)rb_cRandom, "left", random_s_left, 0);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
881 }
Something went wrong with that request. Please try again.