X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=interpreters%2Fbocfel%2Frandom.c;fp=interpreters%2Fbocfel%2Frandom.c;h=2d153473155ea8e31fe588c6cfbe35a68bab5a51;hb=3c59ba5eef5cb4d39c06eb7f523b9c3b026bdc9b;hp=0000000000000000000000000000000000000000;hpb=ed91d840318ed6ebfe3a5a77fa17114ddbf56640;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);
+ }
+}