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 |
16 | typedef 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 |
23 | static 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 | |
40 | static void randomAllocate(WrenVM* vm) |
41 | { |
42 | Well512* well = (Well512*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(Well512)); |
43 | well->index = 0; |
44 | } |
45 | |
46 | static 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 | |
57 | static 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 | |
68 | static 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 | |
78 | static 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 | |
98 | static void randomInt0(WrenVM* vm) |
99 | { |
100 | Well512* well = (Well512*)wrenGetSlotForeign(vm, 0); |
101 | |
102 | wrenSetSlotDouble(vm, 0, (double)advanceState(well)); |
103 | } |
104 | |
105 | const char* wrenRandomSource() |
106 | { |
107 | return randomModuleSource; |
108 | } |
109 | |
110 | WrenForeignClassMethods 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 | |
121 | WrenForeignMethodFn 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 | |