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