mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-10 15:42:01 +03:00
random() with no arguments threw before reaching its own branch
evalCall checked args[0] and threw "needs an argument" before ever falling into the random case, which is the one built-in that's valid with zero args. Dispatch it first.
This commit is contained in:
parent
01697daaac
commit
df7761401b
2 changed files with 25 additions and 10 deletions
|
|
@ -207,3 +207,16 @@ describe("regressions", () => {
|
|||
expect(v?.kind).toBe("date");
|
||||
});
|
||||
});
|
||||
|
||||
describe("review fixes", () => {
|
||||
it("random() with no arguments returns a value in [0,1]", () => {
|
||||
const r = calc("random()");
|
||||
expect(r).not.toBeNull();
|
||||
const v = Number(r);
|
||||
expect(v).toBeGreaterThanOrEqual(0);
|
||||
expect(v).toBeLessThanOrEqual(1);
|
||||
});
|
||||
it("random() participates in arithmetic", () => {
|
||||
expect(calc("random() + 1")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -529,6 +529,7 @@ function evalAgg(name: "sum" | "avg" | "prev" | "count" | "min" | "max" | "produ
|
|||
function evalCall(name: string, args: Value[], ctx: EvalCtx): Value {
|
||||
const custom = ctx.reg.customFuncs.get(name);
|
||||
if (custom) return custom(args);
|
||||
if (name === "random") return evalRandom(args, ctx); // random() is valid with no args
|
||||
|
||||
const x = args[0];
|
||||
if (!x) throw new EvalError(`${name} needs an argument`);
|
||||
|
|
@ -570,16 +571,6 @@ function evalCall(name: string, args: Value[], ctx: EvalCtx): Value {
|
|||
case "ceil": return x.kind === "quantity" ? { ...x, value: x.value.ceil() } : qty(n().ceil());
|
||||
case "floor": return x.kind === "quantity" ? { ...x, value: x.value.floor() } : qty(n().floor());
|
||||
case "fact": case "factorial": return qty(factorial(n()));
|
||||
case "random": {
|
||||
const seed = strHash(ctx.line.lineText) ^ (ctx.line.index * 0x9e3779b9);
|
||||
const rand = mulberry32(seed);
|
||||
if (args.length === 0) return qty(new Decimal(rand));
|
||||
const toNum = (v: Value) => v.kind === "quantity" ? v.value.toNumber() : 0;
|
||||
const [lo, hi] = args.length === 1 ? [0, toNum(args[0]!)] : [toNum(args[0]!), toNum(args[1]!)];
|
||||
const isInt = Number.isInteger(lo) && Number.isInteger(hi);
|
||||
const raw = isInt ? Math.floor(rand * (hi - lo + 1)) + lo : rand * (hi - lo) + lo;
|
||||
return qty(new Decimal(raw));
|
||||
}
|
||||
case "until": {
|
||||
if (x.kind !== "date") throw new EvalError("until needs a date");
|
||||
return evalDateArith("minus", x, { kind: "date", ms: startOfToday(), hasTime: false });
|
||||
|
|
@ -592,6 +583,17 @@ function evalCall(name: string, args: Value[], ctx: EvalCtx): Value {
|
|||
}
|
||||
}
|
||||
|
||||
function evalRandom(args: Value[], ctx: EvalCtx): Value {
|
||||
const seed = strHash(ctx.line.lineText) ^ (ctx.line.index * 0x9e3779b9);
|
||||
const rand = mulberry32(seed);
|
||||
if (args.length === 0) return qty(new Decimal(rand));
|
||||
const toNum = (v: Value) => v.kind === "quantity" ? v.value.toNumber() : 0;
|
||||
const [lo, hi] = args.length === 1 ? [0, toNum(args[0]!)] : [toNum(args[0]!), toNum(args[1]!)];
|
||||
const isInt = Number.isInteger(lo) && Number.isInteger(hi);
|
||||
const raw = isInt ? Math.floor(rand * (hi - lo + 1)) + lo : rand * (hi - lo) + lo;
|
||||
return qty(new Decimal(raw));
|
||||
}
|
||||
|
||||
function strHash(s: string): number {
|
||||
let h = 0x811c9dc5;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue