/src/mruby/mrbgems/mruby-random/src/random.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** random.c - Random module |
3 | | ** |
4 | | ** See Copyright Notice in mruby.h |
5 | | */ |
6 | | |
7 | | #include <mruby.h> |
8 | | #include <mruby/variable.h> |
9 | | #include <mruby/class.h> |
10 | | #include <mruby/data.h> |
11 | | #include <mruby/array.h> |
12 | | #include <mruby/istruct.h> |
13 | | #include <mruby/presym.h> |
14 | | #include <mruby/range.h> |
15 | | #include <mruby/string.h> |
16 | | |
17 | | #include <time.h> |
18 | | |
19 | | /* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) |
20 | | |
21 | | To the extent possible under law, the author has dedicated all copyright |
22 | | and related and neighboring rights to this software to the public domain |
23 | | worldwide. This software is distributed without any warranty. |
24 | | |
25 | | See <https://creativecommons.org/publicdomain/zero/1.0/>. */ |
26 | | |
27 | | /* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid |
28 | | generators. It has excellent speed, a state size (128 bits) that is |
29 | | large enough for mild parallelism, and it passes all tests we are aware |
30 | | of. |
31 | | |
32 | | For generating just single-precision (i.e., 32-bit) floating-point |
33 | | numbers, xoshiro128+ is even faster. |
34 | | |
35 | | The state must be seeded so that it is not everywhere zero. */ |
36 | | |
37 | | |
38 | | #ifdef MRB_32BIT |
39 | | # define XORSHIFT96 |
40 | | # define NSEEDS 3 |
41 | | # define SEEDPOS 2 |
42 | | #else |
43 | | # define NSEEDS 4 |
44 | 4.10k | # define SEEDPOS 0 |
45 | | #endif |
46 | | #define LASTSEED (NSEEDS-1) |
47 | | |
48 | | typedef struct rand_state { |
49 | | uint32_t seed[NSEEDS]; |
50 | | } rand_state; |
51 | | |
52 | | static void |
53 | | rand_init(rand_state *t) |
54 | 4.10k | { |
55 | 4.10k | t->seed[0] = 123456789; |
56 | 4.10k | t->seed[1] = 362436069; |
57 | 4.10k | t->seed[2] = 521288629; |
58 | 4.10k | #ifndef XORSHIFT96 |
59 | 4.10k | t->seed[3] = 88675123; |
60 | 4.10k | #endif |
61 | 4.10k | } |
62 | | |
63 | | static uint32_t rand_uint32(rand_state *state); |
64 | | |
65 | | static uint32_t |
66 | | rand_seed(rand_state *t, uint32_t seed) |
67 | 2.05k | { |
68 | 2.05k | uint32_t old_seed = t->seed[SEEDPOS]; |
69 | 2.05k | rand_init(t); |
70 | 2.05k | t->seed[SEEDPOS] = seed; |
71 | 22.5k | for (int i = 0; i < 10; i++) { |
72 | 20.5k | rand_uint32(t); |
73 | 20.5k | } |
74 | 2.05k | return old_seed; |
75 | 2.05k | } |
76 | | |
77 | | #ifndef XORSHIFT96 |
78 | | static inline uint32_t |
79 | 41.0k | rotl(const uint32_t x, int k) { |
80 | 41.0k | return (x << k) | (x >> (32 - k)); |
81 | 41.0k | } |
82 | | #endif |
83 | | |
84 | | static uint32_t |
85 | | rand_uint32(rand_state *state) |
86 | 20.5k | { |
87 | | #ifdef XORSHIFT96 |
88 | | uint32_t *seed = state->seed; |
89 | | uint32_t x = seed[0]; |
90 | | uint32_t y = seed[1]; |
91 | | uint32_t z = seed[2]; |
92 | | uint32_t t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6)); |
93 | | |
94 | | x = y; y = z; z = t; |
95 | | seed[0] = x; |
96 | | seed[1] = y; |
97 | | seed[2] = z; |
98 | | |
99 | | return z; |
100 | | #else |
101 | 20.5k | uint32_t *s = state->seed; |
102 | 20.5k | const uint32_t result = rotl(s[0] + s[3], 7) + s[0]; |
103 | 20.5k | const uint32_t t = s[1] << 9; |
104 | | |
105 | 20.5k | s[2] ^= s[0]; |
106 | 20.5k | s[3] ^= s[1]; |
107 | 20.5k | s[1] ^= s[2]; |
108 | 20.5k | s[0] ^= s[3]; |
109 | | |
110 | 20.5k | s[2] ^= t; |
111 | 20.5k | s[3] = rotl(s[3], 11); |
112 | | |
113 | 20.5k | return result; |
114 | 20.5k | #endif /* XORSHIFT96 */ |
115 | 20.5k | } |
116 | | |
117 | | #ifndef MRB_NO_FLOAT |
118 | | static double |
119 | | rand_real(rand_state *t) |
120 | 0 | { |
121 | 0 | uint32_t x = rand_uint32(t); |
122 | 0 | return x*(1.0/4294967296.0); |
123 | 0 | } |
124 | | #endif |
125 | | |
126 | | static mrb_value |
127 | | random_rand(mrb_state *mrb, rand_state *t, mrb_int max) |
128 | 0 | { |
129 | 0 | if (max == 0) { |
130 | 0 | #ifndef MRB_NO_FLOAT |
131 | 0 | return mrb_float_value(mrb, rand_real(t)); |
132 | | #else |
133 | | max = 100; |
134 | | #endif |
135 | 0 | } |
136 | 0 | return mrb_int_value(mrb, rand_uint32(t) % max); |
137 | 0 | } |
138 | | |
139 | | static mrb_int |
140 | | rand_i(rand_state *t, mrb_int max) |
141 | 0 | { |
142 | 0 | return rand_uint32(t) % max; |
143 | 0 | } |
144 | | |
145 | | static mrb_value |
146 | | rand_range_int(mrb_state *mrb, rand_state *t, mrb_int begin, |
147 | 0 | mrb_int end, mrb_bool excl) { |
148 | 0 | mrb_int span = end - begin + (excl ? 0 : 1); |
149 | 0 | if (span <= 0) |
150 | 0 | return mrb_nil_value(); |
151 | | |
152 | 0 | return mrb_int_value(mrb, (rand_i(t, span)) + begin); |
153 | 0 | } |
154 | | |
155 | | #ifndef MRB_NO_FLOAT |
156 | | static mrb_value |
157 | | rand_range_float(mrb_state *mrb, rand_state *t, |
158 | | mrb_float begin, mrb_float end, |
159 | 0 | mrb_bool excl) { |
160 | 0 | mrb_float span = end - begin + (excl ? 0.0 : 1.0); |
161 | 0 | if (span <= 0.0) |
162 | 0 | return mrb_nil_value(); |
163 | | |
164 | 0 | return mrb_float_value(mrb, rand_real(t) * span + begin); |
165 | 0 | } |
166 | | #endif |
167 | | |
168 | | static mrb_noreturn void |
169 | | range_error(mrb_state *mrb, mrb_value v) |
170 | 0 | { |
171 | 0 | mrb_raisef(mrb, E_TYPE_ERROR, "no implicit conversion of %Y into Integer", v); |
172 | 0 | } |
173 | | |
174 | | static mrb_value |
175 | | random_range(mrb_state *mrb, rand_state *t, mrb_value rv) |
176 | 0 | { |
177 | 0 | struct RRange *r = mrb_range_ptr(mrb, rv); |
178 | 0 | if (mrb_integer_p(RANGE_BEG(r)) && mrb_integer_p(RANGE_END(r))) { |
179 | 0 | return rand_range_int(mrb, t, mrb_integer(RANGE_BEG(r)), |
180 | 0 | mrb_integer(RANGE_END(r)), RANGE_EXCL(r)); |
181 | 0 | } |
182 | | |
183 | 0 | #define cast_to_float(v) \ |
184 | 0 | (mrb_float_p(v) ? mrb_float(v) \ |
185 | 0 | : mrb_integer_p(v) ? (mrb_float)mrb_integer(v) \ |
186 | 0 | : (range_error(mrb, v), 0.0)) |
187 | | |
188 | 0 | return rand_range_float(mrb, t, cast_to_float(RANGE_BEG(r)), |
189 | 0 | cast_to_float(RANGE_END(r)), RANGE_EXCL(r)); |
190 | 0 | #undef cast_to_float |
191 | 0 | } |
192 | | |
193 | | static mrb_value |
194 | | random_rand_impl(mrb_state *mrb, rand_state *t, mrb_value self) |
195 | 0 | { |
196 | 0 | mrb_value arg; |
197 | 0 | if (mrb_get_args(mrb, "|o", &arg) == 0) { |
198 | 0 | return random_rand(mrb, t, 0); |
199 | 0 | } |
200 | | |
201 | 0 | if (mrb_float_p(arg)) { |
202 | 0 | return random_rand(mrb, t, (mrb_int)mrb_float(arg)); |
203 | 0 | } |
204 | | |
205 | 0 | if (mrb_integer_p(arg)) { |
206 | 0 | return random_rand(mrb, t, mrb_integer(arg)); |
207 | 0 | } |
208 | | |
209 | 0 | if (mrb_range_p(arg)) { |
210 | 0 | return random_range(mrb, t, arg); |
211 | 0 | } |
212 | | |
213 | 0 | range_error(mrb, arg); |
214 | 0 | } |
215 | | |
216 | 4.10k | #define ID_RANDOM MRB_SYM(mruby_Random) |
217 | | |
218 | | static mrb_value |
219 | | random_default(mrb_state *mrb) |
220 | 0 | { |
221 | 0 | struct RClass *c = mrb_class_get_id(mrb, ID_RANDOM); |
222 | 0 | mrb_value d = mrb_iv_get(mrb, mrb_obj_value(c), ID_RANDOM); |
223 | 0 | if (!mrb_obj_is_kind_of(mrb, d, c)) { |
224 | 0 | mrb_raise(mrb, E_RUNTIME_ERROR, "[BUG] default Random replaced"); |
225 | 0 | } |
226 | 0 | return d; |
227 | 0 | } |
228 | | |
229 | 4.10k | #define random_ptr(v) (rand_state*)mrb_istruct_ptr(v) |
230 | 0 | #define random_default_state(mrb) random_ptr(random_default(mrb)) |
231 | | |
232 | | static mrb_value |
233 | | random_m_init(mrb_state *mrb, mrb_value self) |
234 | 2.05k | { |
235 | 2.05k | mrb_int seed; |
236 | 2.05k | rand_state *t = random_ptr(self); |
237 | | |
238 | 2.05k | if (mrb_get_args(mrb, "|i", &seed) == 0) { |
239 | 2.05k | rand_init(t); |
240 | 2.05k | } |
241 | 0 | else { |
242 | 0 | rand_seed(t, (uint32_t)seed); |
243 | 0 | } |
244 | | |
245 | 2.05k | return self; |
246 | 2.05k | } |
247 | | |
248 | | static mrb_value |
249 | | random_m_rand(mrb_state *mrb, mrb_value self) |
250 | 0 | { |
251 | 0 | rand_state *t = random_ptr(self); |
252 | 0 | return random_rand_impl(mrb, t, self); |
253 | 0 | } |
254 | | |
255 | | static mrb_value |
256 | | random_m_srand(mrb_state *mrb, mrb_value self) |
257 | 0 | { |
258 | 0 | uint32_t seed; |
259 | 0 | mrb_int i; |
260 | 0 | rand_state *t = random_ptr(self); |
261 | |
|
262 | 0 | if (mrb_get_args(mrb, "|i", &i) == 0) { |
263 | 0 | seed = (uint32_t)time(NULL) ^ rand_uint32(t) ^ (uint32_t)(uintptr_t)t; |
264 | 0 | } |
265 | 0 | else { |
266 | 0 | seed = (uint32_t)i; |
267 | 0 | } |
268 | |
|
269 | 0 | uint32_t old_seed = rand_seed(t, seed); |
270 | 0 | return mrb_int_value(mrb, (mrb_int)old_seed); |
271 | 0 | } |
272 | | |
273 | | static mrb_value |
274 | | random_m_bytes(mrb_state *mrb, mrb_value self) |
275 | 0 | { |
276 | 0 | rand_state *t = random_ptr(self); |
277 | 0 | mrb_int i = mrb_as_int(mrb, mrb_get_arg1(mrb)); |
278 | 0 | mrb_value bytes = mrb_str_new(mrb, NULL, i); |
279 | 0 | uint8_t *p = (uint8_t*)RSTRING_PTR(bytes); |
280 | |
|
281 | 0 | for (; i > 0; i--, p++) { |
282 | 0 | *p = (uint8_t)rand_uint32(t); |
283 | 0 | } |
284 | |
|
285 | 0 | return bytes; |
286 | 0 | } |
287 | | |
288 | | static rand_state* |
289 | | check_random_arg(mrb_state *mrb, mrb_value r) |
290 | 0 | { |
291 | 0 | struct RClass *c = mrb_class_get_id(mrb, ID_RANDOM); |
292 | 0 | rand_state *random; |
293 | |
|
294 | 0 | if (mrb_undef_p(r)) { |
295 | 0 | random = random_default_state(mrb); |
296 | 0 | } |
297 | 0 | else if (mrb_istruct_p(r) && mrb_obj_is_kind_of(mrb, r, c)){ |
298 | 0 | random = (rand_state*)mrb_istruct_ptr(r); |
299 | 0 | } |
300 | 0 | else { |
301 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "Random object required"); |
302 | 0 | } |
303 | 0 | return random; |
304 | 0 | } |
305 | | /* |
306 | | * call-seq: |
307 | | * ary.shuffle! -> ary |
308 | | * |
309 | | * Shuffles elements in self in place. |
310 | | */ |
311 | | |
312 | | static mrb_value |
313 | | mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) |
314 | 0 | { |
315 | 0 | if (RARRAY_LEN(ary) > 1) { |
316 | 0 | mrb_sym kname = MRB_SYM(random); |
317 | 0 | mrb_value r; |
318 | 0 | const mrb_kwargs kw = {1, 0, &kname, &r, NULL}; |
319 | |
|
320 | 0 | mrb_get_args(mrb, ":", &kw); |
321 | 0 | rand_state *random = check_random_arg(mrb, r); |
322 | 0 | mrb_ary_modify(mrb, mrb_ary_ptr(ary)); |
323 | 0 | for (mrb_int i = RARRAY_LEN(ary) - 1; i > 0; i--) { |
324 | 0 | mrb_value *ptr = RARRAY_PTR(ary); |
325 | 0 | mrb_int j = rand_i(random, i + 1); |
326 | 0 | mrb_value tmp = ptr[i]; |
327 | 0 | ptr[i] = ptr[j]; |
328 | 0 | ptr[j] = tmp; |
329 | 0 | } |
330 | 0 | } |
331 | |
|
332 | 0 | return ary; |
333 | 0 | } |
334 | | |
335 | | /* |
336 | | * call-seq: |
337 | | * ary.shuffle -> new_ary |
338 | | * |
339 | | * Returns a new array with elements of self shuffled. |
340 | | */ |
341 | | |
342 | | static mrb_value |
343 | | mrb_ary_shuffle(mrb_state *mrb, mrb_value ary) |
344 | 0 | { |
345 | 0 | mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); |
346 | 0 | mrb_ary_shuffle_bang(mrb, new_ary); |
347 | |
|
348 | 0 | return new_ary; |
349 | 0 | } |
350 | | |
351 | | /* |
352 | | * call-seq: |
353 | | * ary.sample -> obj |
354 | | * ary.sample(n) -> new_ary |
355 | | * |
356 | | * Choose a random element or +n+ random elements from the array. |
357 | | * |
358 | | * The elements are chosen by using random and unique indices into the array |
359 | | * in order to ensure that an element doesn't repeat itself unless the array |
360 | | * already contained duplicate elements. |
361 | | * |
362 | | * If the array is empty the first form returns +nil+ and the second form |
363 | | * returns an empty array. |
364 | | */ |
365 | | |
366 | | static mrb_value |
367 | | mrb_ary_sample(mrb_state *mrb, mrb_value ary) |
368 | 0 | { |
369 | 0 | mrb_int n = 0; |
370 | 0 | mrb_bool given; |
371 | 0 | mrb_sym kname = MRB_SYM(random); |
372 | 0 | mrb_value r; |
373 | 0 | const mrb_kwargs kw = {1, 0, &kname, &r, NULL}; |
374 | |
|
375 | 0 | mrb_get_args(mrb, "|i?:", &n, &given, &kw); |
376 | 0 | rand_state *random = check_random_arg(mrb, r); |
377 | 0 | mrb_int len = RARRAY_LEN(ary); |
378 | 0 | if (!given) { /* pick one element */ |
379 | 0 | switch (len) { |
380 | 0 | case 0: |
381 | 0 | return mrb_nil_value(); |
382 | 0 | case 1: |
383 | 0 | return RARRAY_PTR(ary)[0]; |
384 | 0 | default: |
385 | 0 | return RARRAY_PTR(ary)[rand_i(random, len)]; |
386 | 0 | } |
387 | 0 | } |
388 | 0 | else { |
389 | 0 | if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number"); |
390 | 0 | if (n > len) n = len; |
391 | 0 | mrb_value result = mrb_ary_new_capa(mrb, n); |
392 | 0 | for (mrb_int i=0; i<n; i++) { |
393 | 0 | mrb_int idx; |
394 | |
|
395 | 0 | for (;;) { |
396 | 0 | retry: |
397 | 0 | idx = rand_i(random, len); |
398 | |
|
399 | 0 | for (mrb_int j=0; j<i; j++) { |
400 | 0 | if (mrb_integer(RARRAY_PTR(result)[j]) == idx) { |
401 | 0 | goto retry; /* retry if duplicate */ |
402 | 0 | } |
403 | 0 | } |
404 | 0 | break; |
405 | 0 | } |
406 | 0 | mrb_ary_push(mrb, result, mrb_int_value(mrb, idx)); |
407 | 0 | } |
408 | 0 | for (mrb_int i=0; i<n; i++) { |
409 | 0 | mrb_int idx = mrb_integer(RARRAY_PTR(result)[i]); |
410 | 0 | mrb_value elem = RARRAY_PTR(ary)[idx]; |
411 | 0 | mrb_ary_set(mrb, result, i, elem); |
412 | 0 | } |
413 | 0 | return result; |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | | static mrb_value |
418 | | random_f_rand(mrb_state *mrb, mrb_value self) |
419 | 0 | { |
420 | 0 | rand_state *t = random_default_state(mrb); |
421 | 0 | return random_rand_impl(mrb, t, self); |
422 | 0 | } |
423 | | |
424 | | static mrb_value |
425 | | random_f_srand(mrb_state *mrb, mrb_value self) |
426 | 0 | { |
427 | 0 | mrb_value random = random_default(mrb); |
428 | 0 | return random_m_srand(mrb, random); |
429 | 0 | } |
430 | | |
431 | | static mrb_value |
432 | | random_f_bytes(mrb_state *mrb, mrb_value self) |
433 | 0 | { |
434 | 0 | mrb_value random = random_default(mrb); |
435 | 0 | return random_m_bytes(mrb, random); |
436 | 0 | } |
437 | | |
438 | | |
439 | | void mrb_mruby_random_gem_init(mrb_state *mrb) |
440 | 2.05k | { |
441 | 2.05k | struct RClass *array = mrb->array_class; |
442 | | |
443 | 2.05k | mrb_static_assert(sizeof(rand_state) <= ISTRUCT_DATA_SIZE); |
444 | | |
445 | 2.05k | mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(rand), random_f_rand, MRB_ARGS_OPT(1)); |
446 | 2.05k | mrb_define_private_method_id(mrb, mrb->kernel_module, MRB_SYM(srand), random_f_srand, MRB_ARGS_OPT(1)); |
447 | | |
448 | 2.05k | struct RClass *random = mrb_define_class_id(mrb, MRB_SYM(Random), mrb->object_class); |
449 | 2.05k | mrb_const_set(mrb, mrb_obj_value(mrb->object_class), ID_RANDOM, mrb_obj_value(random)); // for class check |
450 | 2.05k | MRB_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT); |
451 | 2.05k | mrb_define_class_method_id(mrb, random, MRB_SYM(rand), random_f_rand, MRB_ARGS_OPT(1)); |
452 | 2.05k | mrb_define_class_method_id(mrb, random, MRB_SYM(srand), random_f_srand, MRB_ARGS_OPT(1)); |
453 | 2.05k | mrb_define_class_method_id(mrb, random, MRB_SYM(bytes), random_f_bytes, MRB_ARGS_REQ(1)); |
454 | | |
455 | 2.05k | mrb_define_method_id(mrb, random, MRB_SYM(initialize), random_m_init, MRB_ARGS_OPT(1)); |
456 | 2.05k | mrb_define_method_id(mrb, random, MRB_SYM(rand), random_m_rand, MRB_ARGS_OPT(1)); |
457 | 2.05k | mrb_define_method_id(mrb, random, MRB_SYM(srand), random_m_srand, MRB_ARGS_OPT(1)); |
458 | 2.05k | mrb_define_method_id(mrb, random, MRB_SYM(bytes), random_m_bytes, MRB_ARGS_REQ(1)); |
459 | | |
460 | 2.05k | mrb_define_method_id(mrb, array, MRB_SYM(shuffle), mrb_ary_shuffle, MRB_ARGS_OPT(1)); |
461 | 2.05k | mrb_define_method_id(mrb, array, MRB_SYM_B(shuffle), mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); |
462 | 2.05k | mrb_define_method_id(mrb, array, MRB_SYM(sample), mrb_ary_sample, MRB_ARGS_OPT(2)); |
463 | | |
464 | 2.05k | mrb_value d = mrb_obj_new(mrb, random, 0, NULL); |
465 | 2.05k | rand_state *t = random_ptr(d); |
466 | 2.05k | mrb_iv_set(mrb, mrb_obj_value(random), ID_RANDOM, d); |
467 | | |
468 | 2.05k | uint32_t seed = (uint32_t)time(NULL); |
469 | 2.05k | rand_seed(t, seed ^ (uint32_t)(uintptr_t)t); |
470 | 2.05k | } |
471 | | |
472 | | void mrb_mruby_random_gem_final(mrb_state *mrb) |
473 | 2.05k | { |
474 | 2.05k | } |