1
0
forked from Mirror/wren

Add a random module.

This commit is contained in:
Bob Nystrom
2015-10-17 11:03:15 -07:00
parent 49601e67c5
commit 8436ce1934
17 changed files with 346 additions and 0 deletions

View File

@ -4,6 +4,7 @@
#include "modules.h"
#include "io.wren.inc"
#include "random.wren.inc"
#include "scheduler.wren.inc"
#include "timer.wren.inc"
@ -15,6 +16,12 @@ extern void fileClose(WrenVM* vm);
extern void fileDescriptor(WrenVM* vm);
extern void fileReadBytes(WrenVM* vm);
extern void fileSize(WrenVM* vm);
extern void randomAllocate(WrenVM* vm);
extern void randomSeed0(WrenVM* vm);
extern void randomSeed1(WrenVM* vm);
extern void randomSeed16(WrenVM* vm);
extern void randomFloat(WrenVM* vm);
extern void randomInt0(WrenVM* vm);
extern void stdinReadStart(WrenVM* vm);
extern void stdinReadStop(WrenVM* vm);
extern void schedulerCaptureMethods(WrenVM* vm);
@ -98,6 +105,16 @@ static ModuleRegistry modules[] =
STATIC_METHOD("readStop_()", stdinReadStop)
END_CLASS
END_MODULE
MODULE(random)
CLASS(Random)
STATIC_METHOD("<allocate>", randomAllocate)
METHOD("seed_()", randomSeed0)
METHOD("seed_(_)", randomSeed1)
METHOD("seed_(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", randomSeed16)
METHOD("float()", randomFloat)
METHOD("int()", randomInt0)
END_CLASS
END_MODULE
MODULE(scheduler)
CLASS(Scheduler)
STATIC_METHOD("captureMethods_()", schedulerCaptureMethods)

94
src/module/random.c Normal file
View File

@ -0,0 +1,94 @@
#include "vm.h"
#include "wren.h"
// Implements the well equidistributed long-period linear PRNG (WELL512a).
//
// https://en.wikipedia.org/wiki/Well_equidistributed_long-period_linear
//
// Code from: http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf
typedef struct
{
uint32_t state[16];
uint32_t index;
} Well512;
static uint32_t advanceState(Well512* well)
{
uint32_t a, b, c, d;
a = well->state[well->index];
c = well->state[(well->index + 13) & 15];
b = a ^ c ^ (a << 16) ^ (c << 15);
c = well->state[(well->index + 9) & 15];
c ^= (c >> 11);
a = well->state[well->index] = b ^ c;
d = a ^ ((a << 5) & 0xda442d24U);
well->index = (well->index + 15) & 15;
a = well->state[well->index];
well->state[well->index] = a ^ b ^ d ^ (a << 2) ^ (b << 18) ^ (c << 28);
return well->state[well->index];
}
void randomAllocate(WrenVM* vm)
{
Well512* well = (Well512*)wrenAllocateForeign(vm, sizeof(Well512));
well->index = 0;
}
void randomSeed0(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetArgumentForeign(vm, 0);
srand((uint32_t)time(NULL));
for (int i = 0; i < 16; i++)
{
well->state[i] = rand();
}
}
void randomSeed1(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetArgumentForeign(vm, 0);
srand((uint32_t)wrenGetArgumentDouble(vm, 1));
for (int i = 0; i < 16; i++)
{
well->state[i] = rand();
}
}
void randomSeed16(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetArgumentForeign(vm, 0);
for (int i = 0; i < 16; i++)
{
well->state[i] = (uint32_t)wrenGetArgumentDouble(vm, i + 1);
}
}
void randomFloat(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetArgumentForeign(vm, 0);
// A double has 53 bits of precision in its mantissa, and we'd like to take
// full advantage of that, so we need 53 bits of random source data.
// First, start with 32 random bits, shifted to the left 21 bits.
double result = (double)advanceState(well) * (1 << 21);
// Then add another 21 random bits.
result += (double)(advanceState(well) & ((1 << 21) - 1));
// Now we have a number from 0 - (2^53). Divide be the range to get a double
// from 0 to 1.0 (half-inclusive).
result /= (1UL << 53);
wrenReturnDouble(vm, result);
}
void randomInt0(WrenVM* vm)
{
Well512* well = (Well512*)wrenGetArgumentForeign(vm, 0);
wrenReturnDouble(vm, (double)advanceState(well));
}

49
src/module/random.wren Normal file
View File

@ -0,0 +1,49 @@
foreign class Random {
construct new() {
seed_()
}
construct new(seed) {
if (seed is Num) {
seed_(seed)
} else if (seed is Sequence) {
if (seed.isEmpty) Fiber.abort("Sequence cannot be empty.")
// TODO: Empty sequence.
var seeds = []
for (element in seed) {
if (!(element is Num)) Fiber.abort("Sequence elements must all be numbers.")
seeds.add(element)
if (seeds.count == 16) break
}
// Cycle the values to fill in any missing slots.
var i = 0
while (seeds.count < 16) {
seeds.add(seeds[i])
i = i + 1
}
seed_(
seeds[0], seeds[1], seeds[2], seeds[3],
seeds[4], seeds[5], seeds[6], seeds[7],
seeds[8], seeds[9], seeds[10], seeds[11],
seeds[12], seeds[13], seeds[14], seeds[15])
} else {
Fiber.abort("Seed must be a number or a sequence of numbers.")
}
}
foreign seed_()
foreign seed_(seed)
foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)
foreign float()
float(max) { float() * max }
float(min, max) { float() * (max - min) + min }
foreign int()
int(max) { (float() * max).floor }
int(min, max) { (float() * (max - min)).floor + min }
}

View File

@ -0,0 +1,51 @@
// Generated automatically from src/module/random.wren. Do not edit.
static const char* randomModuleSource =
"foreign class Random {\n"
" construct new() {\n"
" seed_()\n"
" }\n"
"\n"
" construct new(seed) {\n"
" if (seed is Num) {\n"
" seed_(seed)\n"
" } else if (seed is Sequence) {\n"
" if (seed.isEmpty) Fiber.abort(\"Sequence cannot be empty.\")\n"
"\n"
" // TODO: Empty sequence.\n"
" var seeds = []\n"
" for (element in seed) {\n"
" if (!(element is Num)) Fiber.abort(\"Sequence elements must all be numbers.\")\n"
"\n"
" seeds.add(element)\n"
" if (seeds.count == 16) break\n"
" }\n"
"\n"
" // Cycle the values to fill in any missing slots.\n"
" var i = 0\n"
" while (seeds.count < 16) {\n"
" seeds.add(seeds[i])\n"
" i = i + 1\n"
" }\n"
"\n"
" seed_(\n"
" seeds[0], seeds[1], seeds[2], seeds[3],\n"
" seeds[4], seeds[5], seeds[6], seeds[7],\n"
" seeds[8], seeds[9], seeds[10], seeds[11],\n"
" seeds[12], seeds[13], seeds[14], seeds[15])\n"
" } else {\n"
" Fiber.abort(\"Seed must be a number or a sequence of numbers.\")\n"
" }\n"
" }\n"
"\n"
" foreign seed_()\n"
" foreign seed_(seed)\n"
" foreign seed_(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16)\n"
"\n"
" foreign float()\n"
" float(max) { float() * max }\n"
" float(min, max) { float() * (max - min) + min }\n"
"\n"
" foreign int()\n"
" int(max) { (float() * max).floor }\n"
" int(min, max) { (float() * (max - min)).floor + min }\n"
"}\n";

13
test/random/float.wren Normal file
View File

@ -0,0 +1,13 @@
import "random" for Random
var random = Random.new(12345)
var below = 0
for (i in 1..1000) {
var n = random.float()
if (n < 0.5) below = below + 1
}
// Should be roughly evenly distributed.
System.print(below > 450) // expect: true
System.print(below < 550) // expect: true

View File

@ -0,0 +1,10 @@
import "random" for Random
var random = Random.new(12345)
var below = 0
for (i in 1..100) {
var n = random.float(5)
if (n < 0) System.print("too low")
if (n >= 5) System.print("too high")
}

View File

@ -0,0 +1,10 @@
import "random" for Random
var random = Random.new(12345)
var below = 0
for (i in 1..100) {
var n = random.float(2, 5)
if (n < 2) System.print("too low")
if (n >= 5) System.print("too high")
}

13
test/random/int.wren Normal file
View File

@ -0,0 +1,13 @@
import "random" for Random
var random = Random.new(12345)
var below = 0
for (i in 1..1000) {
var n = random.int()
if (n < 2147483648) below = below + 1
}
// Should be roughly evenly distributed.
System.print(below > 450) // expect: true
System.print(below < 550) // expect: true

14
test/random/int_max.wren Normal file
View File

@ -0,0 +1,14 @@
import "random" for Random
var random = Random.new(12345)
var counts = [0, 0, 0, 0, 0]
for (i in 1..10000) {
var n = random.int(5)
counts[n] = counts[n] + 1
}
for (count in counts) {
if (count < 1900) System.print("too few")
if (count > 2100) System.print("too many")
}

View File

@ -0,0 +1,18 @@
import "random" for Random
var random = Random.new(12345)
var counts = [0, 0, 0, 0, 0, 0, 0, 0]
for (i in 1..10000) {
var n = random.int(3, 8)
counts[n] = counts[n] + 1
}
for (i in 0..2) {
if (counts[i] != 0) System.print("too low value")
}
for (i in 3..7) {
if (counts[i] < 1900) System.print("too few")
if (counts[i] > 2100) System.print("too many")
}

12
test/random/new.wren Normal file
View File

@ -0,0 +1,12 @@
import "random" for Random
var random = Random.new()
var correct = 0
for (i in 1..100) {
var n = random.float()
if (n >= 0) correct = correct + 1
if (n < 1) correct = correct + 1
}
System.print(correct) // expect: 200

View File

@ -0,0 +1,3 @@
import "random" for Random
Random.new([]) // expect runtime error: Sequence cannot be empty.

View File

@ -0,0 +1,12 @@
import "random" for Random
var random1 = Random.new(123)
var random2 = Random.new(123)
var correct = 0
for (i in 1..100) {
// Should produce the same values.
if (random1.float() == random2.float()) correct = correct + 1
}
System.print(correct) // expect: 100

View File

@ -0,0 +1,12 @@
import "random" for Random
var random1 = Random.new([1, 2, 3])
var random2 = Random.new([1, 2, 3])
var correct = 0
for (i in 1..100) {
// Should produce the same values.
if (random1.float() == random2.float()) correct = correct + 1
}
System.print(correct) // expect: 100

View File

@ -0,0 +1,3 @@
import "random" for Random
Random.new(false) // expect runtime error: Seed must be a number or a sequence of numbers.

View File

@ -0,0 +1,3 @@
import "random" for Random
Random.new(["wrong type"]) // expect runtime error: Sequence elements must all be numbers.

View File

@ -42,6 +42,10 @@
29DC14AC1BBA303D008A8274 /* returns.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009AA1B7E39A8000CE58C /* returns.c */; };
29DC14AD1BBA3040008A8274 /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009AC1B7E39A8000CE58C /* value.c */; };
29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 29DE39511AC3A50A00987D41 /* wren_meta.c */; };
29F384141BD20FF1002F84E0 /* random.c in Sources */ = {isa = PBXBuildFile; fileRef = 29F384121BD20FF1002F84E0 /* random.c */; };
29F384151BD20FF1002F84E0 /* random.c in Sources */ = {isa = PBXBuildFile; fileRef = 29F384121BD20FF1002F84E0 /* random.c */; };
29F384161BD20FF1002F84E0 /* random.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29F384131BD20FF1002F84E0 /* random.wren.inc */; };
29F384171BD20FF1002F84E0 /* random.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29F384131BD20FF1002F84E0 /* random.wren.inc */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -110,6 +114,8 @@
29DE39511AC3A50A00987D41 /* wren_meta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wren_meta.c; path = ../../src/vm/wren_meta.c; sourceTree = "<group>"; };
29DE39521AC3A50A00987D41 /* wren_meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_meta.h; path = ../../src/vm/wren_meta.h; sourceTree = "<group>"; };
29F384111BD19706002F84E0 /* io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../src/module/io.h; sourceTree = "<group>"; };
29F384121BD20FF1002F84E0 /* random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = random.c; path = ../../src/module/random.c; sourceTree = "<group>"; };
29F384131BD20FF1002F84E0 /* random.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = random.wren.inc; path = ../../src/module/random.wren.inc; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -138,6 +144,8 @@
29F384111BD19706002F84E0 /* io.h */,
29729F2E1BA70A620099CA20 /* io.c */,
29729F301BA70A620099CA20 /* io.wren.inc */,
29F384121BD20FF1002F84E0 /* random.c */,
29F384131BD20FF1002F84E0 /* random.wren.inc */,
291647C31BA5EA45006142EE /* scheduler.h */,
291647C21BA5EA45006142EE /* scheduler.c */,
291647CD1BA5ED26006142EE /* scheduler.wren.inc */,
@ -314,11 +322,13 @@
291647C41BA5EA45006142EE /* scheduler.c in Sources */,
29205C9B1AB4E6430073018D /* wren_debug.c in Sources */,
29205C9D1AB4E6430073018D /* wren_utils.c in Sources */,
29F384141BD20FF1002F84E0 /* random.c in Sources */,
29729F311BA70A620099CA20 /* io.c in Sources */,
29205C9E1AB4E6430073018D /* wren_value.c in Sources */,
29205C9F1AB4E6430073018D /* wren_vm.c in Sources */,
29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */,
29205C8F1AB4E5C90073018D /* main.c in Sources */,
29F384161BD20FF1002F84E0 /* random.wren.inc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -332,6 +342,7 @@
29DC14A01BBA2FD6008A8274 /* timer.c in Sources */,
29DC149F1BBA2FCC008A8274 /* vm.c in Sources */,
29DC14A21BBA300A008A8274 /* wren_compiler.c in Sources */,
29F384151BD20FF1002F84E0 /* random.c in Sources */,
29DC14A31BBA300D008A8274 /* wren_core.c in Sources */,
29DC14A41BBA3010008A8274 /* wren_debug.c in Sources */,
29DC14A51BBA3013008A8274 /* wren_meta.c in Sources */,
@ -339,6 +350,7 @@
29DC14A71BBA301A008A8274 /* wren_utils.c in Sources */,
29DC14A81BBA301D008A8274 /* wren_value.c in Sources */,
29DC14A91BBA302F008A8274 /* wren_vm.c in Sources */,
29F384171BD20FF1002F84E0 /* random.wren.inc in Sources */,
29DC14AA1BBA3032008A8274 /* main.c in Sources */,
293D46961BB43F9900200083 /* call.c in Sources */,
29DC14AB1BBA3038008A8274 /* foreign_class.c in Sources */,