1#include "wren_opt_random.h"
2
3#if WREN_OPT_RANDOM
4
5#include <string.h>
6#include <time.h>
7
8#include "wren.h"
9#include "wren_vm.h"
10
11#include "wren_opt_random.wren.inc"
12
13// Implements the well equidistributed long-period linear PRNG (WELL512a).
14//
15// https://en.wikipedia.org/wiki/Well_equidistributed_long-period_linear
16typedef struct
17{
18 uint32_t state[16];
19 uint32_t index;
20} Well512;
21
22// Code from: http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf
23static uint32_t advanceState(Well512* well)
24{
25 uint32_t a, b, c, d;
26 a = well->state[well->index];
27 c = well->state[(well->index + 13) & 15];
28 b = a ^ c ^ (a << 16) ^ (c << 15);
29 c = well->state[(well->index + 9) & 15];
30 c ^= (c >> 11);
31 a = well->state[well->index] = b ^ c;
32 d = a ^ ((a << 5) & 0xda442d24U);
33
34 well->index = (well->index + 15) & 15;
35 a = well->state[well->index];
36 well->state[well->index] = a ^ b ^ d ^ (a << 2) ^ (b << 18) ^ (c << 28);
37 return well->state[well->index];
38}
39
40static void randomAllocate(WrenVM* vm)
41{
42 Well512* well = (Well512*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(Well512));
43 well->index = 0;
44}
45
46static void randomSeed0(WrenVM* vm)
47{
48 Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
49
50 srand((uint32_t)time(NULL));
51 for (int i = 0; i < 16; i++)
52 {
53 well->state[i] = rand();
54 }
55}
56
57static void randomSeed1(WrenVM* vm)
58{
59 Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
60
61 srand((uint32_t)wrenGetSlotDouble(vm, 1));
62 for (int i = 0; i < 16; i++)
63 {
64 well->state[i] = rand();
65 }
66}
67
68static void randomSeed16(WrenVM* vm)
69{
70 Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
71
72 for (int i = 0; i < 16; i++)
73 {
74 well->state[i] = (uint32_t)wrenGetSlotDouble(vm, i + 1);
75 }
76}
77
78static void randomFloat(WrenVM* vm)
79{
80 Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
81
82 // A double has 53 bits of precision in its mantissa, and we'd like to take
83 // full advantage of that, so we need 53 bits of random source data.
84
85 // First, start with 32 random bits, shifted to the left 21 bits.
86 double result = (double)advanceState(well) * (1 << 21);
87
88 // Then add another 21 random bits.
89 result += (double)(advanceState(well) & ((1 << 21) - 1));
90
91 // Now we have a number from 0 - (2^53). Divide be the range to get a double
92 // from 0 to 1.0 (half-inclusive).
93 result /= 9007199254740992.0;
94
95 wrenSetSlotDouble(vm, 0, result);
96}
97
98static void randomInt0(WrenVM* vm)
99{
100 Well512* well = (Well512*)wrenGetSlotForeign(vm, 0);
101
102 wrenSetSlotDouble(vm, 0, (double)advanceState(well));
103}
104
105const char* wrenRandomSource()
106{
107 return randomModuleSource;
108}
109
110WrenForeignClassMethods wrenRandomBindForeignClass(WrenVM* vm,
111 const char* module,
112 const char* className)
113{
114 ASSERT(strcmp(className, "Random") == 0, "Should be in Random class.");
115 WrenForeignClassMethods methods;
116 methods.allocate = randomAllocate;
117 methods.finalize = NULL;
118 return methods;
119}
120
121WrenForeignMethodFn wrenRandomBindForeignMethod(WrenVM* vm,
122 const char* className,
123 bool isStatic,
124 const char* signature)
125{
126 ASSERT(strcmp(className, "Random") == 0, "Should be in Random class.");
127
128 if (strcmp(signature, "<allocate>") == 0) return randomAllocate;
129 if (strcmp(signature, "seed_()") == 0) return randomSeed0;
130 if (strcmp(signature, "seed_(_)") == 0) return randomSeed1;
131
132 if (strcmp(signature, "seed_(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)") == 0)
133 {
134 return randomSeed16;
135 }
136
137 if (strcmp(signature, "float()") == 0) return randomFloat;
138 if (strcmp(signature, "int()") == 0) return randomInt0;
139
140 ASSERT(false, "Unknown method.");
141 return NULL;
142}
143
144#endif
145