X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=interpreters%2Fbocfel%2Frandom.c;fp=interpreters%2Fbocfel%2Frandom.c;h=2d153473155ea8e31fe588c6cfbe35a68bab5a51;hb=aa30979369091c96bca34499c28cb01bc16efb1d;hp=0000000000000000000000000000000000000000;hpb=61180dab8f5c29f5a29b83fcb7d62942f7a741d1;p=projects%2Fchimara%2Fchimara.git diff --git a/interpreters/bocfel/random.c b/interpreters/bocfel/random.c new file mode 100644 index 0000000..2d15347 --- /dev/null +++ b/interpreters/bocfel/random.c @@ -0,0 +1,162 @@ +/*- + * Copyright 2010-2012 Chris Spiegel. + * + * This file is part of Bocfel. + * + * Bocfel is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version + * 2 or 3, as published by the Free Software Foundation. + * + * Bocfel is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Bocfel. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "random.h" +#include "process.h" +#include "zterp.h" + +/* Mersenne Twister. */ +static uint32_t mt[624]; +static uint32_t mt_idx = 0; + +static void zterp_srand(uint32_t s) +{ + mt[0] = s; + for(int i = 1; i < 624; i++) + { + mt[i] = 1812433253UL * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i; + } + mt_idx = 0; +} + +static void mt_gen_num(void) +{ + for(int i = 0; i < 624; i++) + { + uint32_t y; + + y = mt[i] & 0x80000000UL; + y |= (mt[(i + 1) % 624]) & 0x7fffffffUL; + + mt[i] = mt[(i + 397) % 624] ^ (y >> 1); + + if(y % 2 == 1) mt[i] ^= 2567483615UL; + } +} + +static uint32_t zterp_rand(void) +{ + uint32_t y; + + if(mt_idx == 0) mt_gen_num(); + + y = mt[mt_idx]; + y ^= (y >> 11); + y ^= (y << 7) & 2636928640UL; + y ^= (y << 15) & 4022730752UL; + y ^= (y >> 18); + + mt_idx = (mt_idx + 1) % 624; + + return y; +} + +static int rng_interval = 0; +static int rng_counter = 0; + +/* Called with 0, seed the PRNG with either + * a) a user-provided seed (via -z) if available, or + * b) a seed read from a user-provided file/device (via -Z) if + * available, or + * c) a seed derived from a hash of the constituent bytes of the value + * returned by time(NULL) + * + * Called with a value 0 < S < 1000, generate a string of numbers 1, 2, + * 3, ..., S, 1, 2, 3, ... S, ... as recommended in the remarks to §2. + * + * Called with a value >= 1000, use that value as a normal seed. + */ +void seed_random(long value) +{ + if(value == 0) + { + if(options.random_seed == -1) + { + time_t t = time(NULL); + unsigned char *p = (unsigned char *)&t; + uint32_t s = 0; + + /* time_t hashing based on code by Lawrence Kirby. */ + for(size_t i = 0; i < sizeof t; i++) s = s * (UCHAR_MAX + 2U) + p[i]; + + if(options.random_device != NULL) + { + FILE *fp; + uint32_t temp; + + fp = fopen(options.random_device, "r"); + if(fp != NULL) + { + if(fread(&temp, sizeof temp, 1, fp) == 1) s = temp; + fclose(fp); + } + } + + zterp_srand(s); + } + else + { + zterp_srand(options.random_seed); + } + + rng_interval = 0; + } + else if(value < 1000) + { + rng_counter = 0; + rng_interval = value; + } + else + { + zterp_srand(value); + rng_interval = 0; + } +} + +void zrandom(void) +{ + long v = (int16_t)zargs[0]; + + if(v <= 0) + { + seed_random(-v); + store(0); + } + else + { + uint32_t res; + + if(rng_interval != 0) + { + res = rng_counter++; + if(rng_counter == rng_interval) rng_counter = 0; + } + else + { + res = zterp_rand(); + } + + store(res % zargs[0] + 1); + } +}