diff options
Diffstat (limited to 'mrbgems/mruby-random/src/random.c')
| -rw-r--r-- | mrbgems/mruby-random/src/random.c | 281 |
1 files changed, 157 insertions, 124 deletions
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c index 68209840a..a970e65a3 100644 --- a/mrbgems/mruby-random/src/random.c +++ b/mrbgems/mruby-random/src/random.c @@ -9,62 +9,105 @@ #include <mruby/class.h> #include <mruby/data.h> #include <mruby/array.h> -#include "mt19937ar.h" +#include <mruby/istruct.h> +#if INT32_MAX <= INTPTR_MAX +# define XORSHIFT96 +# define NSEEDS 3 +#else +# define NSEEDS 4 +#endif +#define LASTSEED (NSEEDS-1) #include <time.h> -static char const MT_STATE_KEY[] = "$mrb_i_mt_state"; - -static const struct mrb_data_type mt_state_type = { - MT_STATE_KEY, mrb_free, -}; - -static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self); -static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self); +typedef struct rand_state { + uint32_t seed[NSEEDS]; +} rand_state; static void -mt_srand(mt_state *t, unsigned long seed) +rand_init(rand_state *t) { - mrb_random_init_genrand(t, seed); + t->seed[0] = 123456789; + t->seed[1] = 362436069; + t->seed[2] = 521288629; +#ifndef XORSHIFT96 + t->seed[3] = 88675123; +#endif } -static unsigned long -mt_rand(mt_state *t) +static uint32_t +rand_seed(rand_state *t, uint32_t seed) { - return mrb_random_genrand_int32(t); + uint32_t old_seed = t->seed[LASTSEED]; + rand_init(t); + t->seed[LASTSEED] = seed; + return old_seed; } -static double -mt_rand_real(mt_state *t) +#ifdef XORSHIFT96 +static uint32_t +rand_uint32(rand_state *state) { - return mrb_random_genrand_real1(t); + uint32_t *seed = state->seed; + uint32_t x = seed[0]; + uint32_t y = seed[1]; + uint32_t z = seed[2]; + uint32_t t; + + t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6)); + x = y; y = z; z = t; + seed[0] = x; + seed[1] = y; + seed[2] = z; + + return z; } - -static mrb_value -mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed) +#else /* XORSHIFT96 */ +static uint32_t +rand_uint32(rand_state *state) { - if (mrb_nil_p(seed)) { - seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t))); - if (mrb_fixnum(seed) < 0) { - seed = mrb_fixnum_value(0 - mrb_fixnum(seed)); - } - } - - mt_srand(t, (unsigned) mrb_fixnum(seed)); + uint32_t *seed = state->seed; + uint32_t x = seed[0]; + uint32_t y = seed[1]; + uint32_t z = seed[2]; + uint32_t w = seed[3]; + uint32_t t; + + t = x ^ (x << 11); + x = y; y = z; z = w; + w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); + seed[0] = x; + seed[1] = y; + seed[2] = z; + seed[3] = w; + + return w; +} +#endif /* XORSHIFT96 */ - return seed; +#ifndef MRB_WITHOUT_FLOAT +static double +rand_real(rand_state *t) +{ + uint32_t x = rand_uint32(t); + return x*(1.0/4294967295.0); } +#endif static mrb_value -mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max) +random_rand(mrb_state *mrb, rand_state *t, mrb_value max) { mrb_value value; if (mrb_fixnum(max) == 0) { - value = mrb_float_value(mrb, mt_rand_real(t)); +#ifndef MRB_WITHOUT_FLOAT + value = mrb_float_value(mrb, rand_real(t)); +#else + mrb_raise(mrb, E_ARGUMENT_ERROR, "Float not supported"); +#endif } else { - value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max)); + value = mrb_fixnum_value(rand_uint32(t) % mrb_fixnum(max)); } return value; @@ -90,106 +133,74 @@ get_opt(mrb_state* mrb) return arg; } -static mrb_value -get_random(mrb_state *mrb) { - return mrb_const_get(mrb, - mrb_obj_value(mrb_class_get(mrb, "Random")), - mrb_intern_lit(mrb, "DEFAULT")); -} - -static mt_state * -get_random_state(mrb_state *mrb) -{ - mrb_value random_val = get_random(mrb); - return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state); +static void +random_check(mrb_state *mrb, mrb_value random) { + struct RClass *c = mrb_class_get(mrb, "Random"); + if (!mrb_obj_is_kind_of(mrb, random, c) || mrb_type(random) != MRB_TT_ISTRUCT) { + mrb_raise(mrb, E_TYPE_ERROR, "Random instance required"); + } } static mrb_value -mrb_random_g_rand(mrb_state *mrb, mrb_value self) -{ - mrb_value random = get_random(mrb); - return mrb_random_rand(mrb, random); +random_default(mrb_state *mrb) { + struct RClass *c = mrb_class_get(mrb, "Random"); + mrb_value d = mrb_const_get(mrb, mrb_obj_value(c), mrb_intern_lit(mrb, "DEFAULT")); + if (!mrb_obj_is_kind_of(mrb, d, c)) { + mrb_raise(mrb, E_TYPE_ERROR, "Random::DEFAULT replaced"); + } + return d; } -static mrb_value -mrb_random_g_srand(mrb_state *mrb, mrb_value self) -{ - mrb_value random = get_random(mrb); - return mrb_random_srand(mrb, random); -} +#define random_ptr(v) (rand_state*)mrb_istruct_ptr(v) +#define random_default_state(mrb) random_ptr(random_default(mrb)) static mrb_value -mrb_random_init(mrb_state *mrb, mrb_value self) +random_m_init(mrb_state *mrb, mrb_value self) { mrb_value seed; - mt_state *t; + rand_state *t; seed = get_opt(mrb); - /* avoid memory leaks */ - t = (mt_state*)DATA_PTR(self); - if (t) { - mrb_free(mrb, t); - } - mrb_data_init(self, NULL, &mt_state_type); - - t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state)); - t->mti = N + 1; - - seed = mrb_random_mt_srand(mrb, t, seed); + t = random_ptr(self); if (mrb_nil_p(seed)) { - t->has_seed = FALSE; + rand_init(t); } else { - mrb_assert(mrb_fixnum_p(seed)); - t->has_seed = TRUE; - t->seed = mrb_fixnum(seed); + rand_seed(t, (uint32_t)mrb_fixnum(seed)); } - mrb_data_init(self, t, &mt_state_type); - return self; } -static void -mrb_random_rand_seed(mrb_state *mrb, mt_state *t) -{ - if (!t->has_seed) { - mrb_random_mt_srand(mrb, t, mrb_nil_value()); - } -} - static mrb_value -mrb_random_rand(mrb_state *mrb, mrb_value self) +random_m_rand(mrb_state *mrb, mrb_value self) { mrb_value max; - mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); + rand_state *t = random_ptr(self); max = get_opt(mrb); - mrb_random_rand_seed(mrb, t); - return mrb_random_mt_rand(mrb, t, max); + return random_rand(mrb, t, max); } static mrb_value -mrb_random_srand(mrb_state *mrb, mrb_value self) +random_m_srand(mrb_state *mrb, mrb_value self) { - mrb_value seed; - mrb_value old_seed; - mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state); - - seed = get_opt(mrb); - seed = mrb_random_mt_srand(mrb, t, seed); - old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value(); - if (mrb_nil_p(seed)) { - t->has_seed = FALSE; + uint32_t seed; + uint32_t old_seed; + mrb_value sv; + rand_state *t = random_ptr(self); + + sv = get_opt(mrb); + if (mrb_nil_p(sv)) { + seed = (uint32_t)time(NULL) + rand_uint32(t); } else { - mrb_assert(mrb_fixnum_p(seed)); - t->has_seed = TRUE; - t->seed = mrb_fixnum(seed); + seed = (uint32_t)mrb_fixnum(sv); } + old_seed = rand_seed(t, seed); - return old_seed; + return mrb_fixnum_value((mrb_int)old_seed); } /* @@ -203,25 +214,28 @@ static mrb_value mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) { mrb_int i; - mt_state *random = NULL; + mrb_value max; + mrb_value r = mrb_nil_value(); + rand_state *random; if (RARRAY_LEN(ary) > 1) { - mrb_get_args(mrb, "|d", &random, &mt_state_type); + mrb_get_args(mrb, "|o", &r); - if (random == NULL) { - random = get_random_state(mrb); + if (mrb_nil_p(r)) { + random = random_default_state(mrb); + } + else { + random_check(mrb, r); + random = random_ptr(r); } - mrb_random_rand_seed(mrb, random); - mrb_ary_modify(mrb, mrb_ary_ptr(ary)); - + max = mrb_fixnum_value(RARRAY_LEN(ary)); for (i = RARRAY_LEN(ary) - 1; i > 0; i--) { mrb_int j; mrb_value *ptr = RARRAY_PTR(ary); mrb_value tmp; - - j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary)))); + j = mrb_fixnum(random_rand(mrb, random, max)); tmp = ptr[i]; ptr[i] = ptr[j]; @@ -268,15 +282,18 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) { mrb_int n = 0; mrb_bool given; - mt_state *random = NULL; + mrb_value r = mrb_nil_value(); + rand_state *random; mrb_int len; - mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type); - if (random == NULL) { - random = get_random_state(mrb); + mrb_get_args(mrb, "|i?o", &n, &given, &r); + if (mrb_nil_p(r)) { + random = random_default_state(mrb); + } + else { + random_check(mrb, r); + random = random_ptr(r); } - mrb_random_rand_seed(mrb, random); - mt_rand(random); len = RARRAY_LEN(ary); if (!given) { /* pick one element */ switch (len) { @@ -285,7 +302,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) case 1: return RARRAY_PTR(ary)[0]; default: - return RARRAY_PTR(ary)[mt_rand(random) % len]; + return RARRAY_PTR(ary)[rand_uint32(random) % len]; } } else { @@ -300,7 +317,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) for (;;) { retry: - r = mt_rand(random) % len; + r = (mrb_int)rand_uint32(random) % len; for (j=0; j<i; j++) { if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) { @@ -318,23 +335,39 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) } } +static mrb_value +random_f_rand(mrb_state *mrb, mrb_value self) +{ + rand_state *t = random_default_state(mrb); + return random_rand(mrb, t, get_opt(mrb)); +} + +static mrb_value +random_f_srand(mrb_state *mrb, mrb_value self) +{ + mrb_value random = random_default(mrb); + return random_m_srand(mrb, random); +} + void mrb_mruby_random_gem_init(mrb_state *mrb) { struct RClass *random; struct RClass *array = mrb->array_class; - mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); - mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); + mrb_assert(sizeof(rand_state) < ISTRUCT_DATA_SIZE); + + mrb_define_method(mrb, mrb->kernel_module, "rand", random_f_rand, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, mrb->kernel_module, "srand", random_f_srand, MRB_ARGS_OPT(1)); random = mrb_define_class(mrb, "Random", mrb->object_class); - MRB_SET_INSTANCE_TT(random, MRB_TT_DATA); - mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1)); - mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1)); + MRB_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT); + mrb_define_class_method(mrb, random, "rand", random_f_rand, MRB_ARGS_OPT(1)); + mrb_define_class_method(mrb, random, "srand", random_f_srand, MRB_ARGS_OPT(1)); - mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1)); - mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1)); - mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, random, "initialize", random_m_init, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, random, "rand", random_m_rand, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, random, "srand", random_m_srand, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1)); mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1)); |
