Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export class Random {
private state: number;

constructor(seed: number) {
this.state = seed % 4294967296.0;
if (this.state < 0.0) {
this.state = this.state + 4294967296.0;
}
if (this.state === 0.0) {
this.state = 1.0;
}
}

next(): number {
this.state = (this.state * 1664525.0 + 1013904223.0) % 4294967296.0;
return this.state / 4294967296.0;
}

nextInt(min: number, max: number): number {
return Math.floor(this.next() * (max - min)) + min;
}
}
15 changes: 15 additions & 0 deletions src/codegen/stdlib/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class MathGenerator {
"cos",
"sign",
"random",
"seedRandom",
];
}

Expand Down Expand Up @@ -95,6 +96,8 @@ export class MathGenerator {
return this.generateSign(expr, params);
case "random":
return this.generateRandom(expr);
case "seedRandom":
return this.generateSeedRandom(expr, params);
default:
return this.ctx.emitError(`Unsupported Math method: ${method}`, expr.loc);
}
Expand Down Expand Up @@ -216,6 +219,18 @@ export class MathGenerator {
return result;
}

private generateSeedRandom(expr: MethodCallNode, params: string[]): string {
if (expr.args.length !== 1) {
return this.ctx.emitError("Math.seedRandom() requires 1 argument", expr.loc);
}
const seed = this.ctx.generateExpression(expr.args[0], params);
const dblSeed = this.ctx.ensureDouble(seed);
const i64Seed = this.ctx.nextTemp();
this.ctx.emit(`${i64Seed} = fptosi double ${dblSeed} to i64`);
this.ctx.emitCallVoid("@srand48", `i64 ${i64Seed}`);
return "0.0";
}

private generateSign(expr: MethodCallNode, params: string[]): string {
if (expr.args.length !== 1) {
return this.ctx.emitError("Math.sign() requires 1 argument", expr.loc);
Expand Down
27 changes: 27 additions & 0 deletions tests/fixtures/math/math-seed-random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Math.seedRandom(42);
const a1 = Math.random();
const a2 = Math.random();

Math.seedRandom(42);
const b1 = Math.random();
const b2 = Math.random();

if (a1 !== b1 || a2 !== b2) {
console.log("FAIL: same seed produced different sequences");
process.exit(1);
}

Math.seedRandom(99);
const c1 = Math.random();

if (c1 === a1) {
console.log("FAIL: different seeds produced same first value");
process.exit(1);
}

if (a1 < 0 || a1 >= 1 || b1 < 0 || b1 >= 1) {
console.log("FAIL: values out of [0, 1) range");
process.exit(1);
}

console.log("TEST_PASSED");
41 changes: 41 additions & 0 deletions tests/fixtures/random/random-lib-basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Random } from "../../../lib/random.js";

const rng1 = new Random(12345);
const a = rng1.next();
const b = rng1.next();
const c = rng1.next();

if (a < 0 || a >= 1 || b < 0 || b >= 1 || c < 0 || c >= 1) {
console.log("FAIL: values out of [0, 1) range");
process.exit(1);
}

if (a === b && b === c) {
console.log("FAIL: all values identical");
process.exit(1);
}

const rng2 = new Random(12345);
const d = rng2.next();
const e = rng2.next();
const f = rng2.next();

if (a !== d || b !== e || c !== f) {
console.log("FAIL: same seed produced different sequences");
process.exit(1);
}

const rng3 = new Random(99999);
const g = rng3.next();
if (g === a) {
console.log("FAIL: different seeds produced same first value");
process.exit(1);
}

const n = rng1.nextInt(0, 10);
if (n < 0 || n >= 10) {
console.log("FAIL: nextInt out of range");
process.exit(1);
}

console.log("TEST_PASSED");
Loading