From f6f0c77995fcd62863a7f24cbb407a68d2277759 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Fri, 13 Feb 2026 14:49:50 +0100 Subject: feat(random): add rnd() to mimic math.random() Matches Lua 5.4+ implementation. But uses crypto secure random data. --- spec/02-random_spec.lua | 133 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'spec') diff --git a/spec/02-random_spec.lua b/spec/02-random_spec.lua index 23b6d95..db5d5ce 100644 --- a/spec/02-random_spec.lua +++ b/spec/02-random_spec.lua @@ -44,4 +44,137 @@ describe("Random:", function() end) + + + describe("rnd()", function() + + local has_math_type = type(math.type) == "function" + + it("with no args returns a number in [0, 1)", function() + local v, err = system.rnd() + assert.is_nil(err) + assert.is_number(v) + assert.is_true(v >= 0 and v < 1) + end) + + + it("with no args returns different values on multiple calls", function() + local seen = {} + for _ = 1, 20 do + local v = system.rnd() + seen[v] = true + end + local count = 0 + for _ in pairs(seen) do count = count + 1 end + assert.is_true(count >= 2, "expected at least 2 distinct values in 20 calls") + end) + + + it("with one arg m returns integer in [1, m]", function() + for _ = 1, 50 do + local v, err = system.rnd(6) + assert.is_nil(err) + assert.is_true(v >= 1 and v <= 6 and math.floor(v) == v) + end + end) + + + it("matches math.random type behaviour for one-arg m when available", function() + if not has_math_type then + return + end + -- use math.random as ground truth for numeric type + local mval = math.random(6) + local rval = system.rnd(6) + assert.are.equal(math.type(mval), math.type(rval)) + end) + + + it("with one arg 1 always returns 1", function() + for _ = 1, 10 do + local v, err = system.rnd(1) + assert.is_nil(err) + assert.are.equal(1, v) + end + end) + + + it("with one arg 0 returns a full-range integer", function() + local v, err = system.rnd(0) + assert.is_nil(err) + assert.is_true(type(v) == "number" or (math.type and math.type(v) == "integer")) + assert.is_true(v >= -0x8000000000000000 and v <= 0x7fffffffffffffff) + end) + + + it("matches math.random type behaviour for arg 0 when math.random(0) is supported", function() + if not has_math_type then + return + end + local ok, mval = pcall(math.random, 0) + if not ok then + return -- older Lua where math.random(0) is not supported + end + local rval = system.rnd(0) + assert.are.equal(math.type(mval), math.type(rval)) + end) + + + it("with two args returns integer in [m, n]", function() + for _ = 1, 30 do + local v, err = system.rnd(10, 20) + assert.is_nil(err) + assert.is_true(v >= 10 and v <= 20 and math.floor(v) == v) + end + end) + + + it("matches math.random type behaviour for two-arg range when available", function() + if not has_math_type then + return + end + local mval = math.random(10, 20) + local rval = system.rnd(10, 20) + assert.are.equal(math.type(mval), math.type(rval)) + end) + + + it("with two args supports negative range", function() + for _ = 1, 30 do + local v, err = system.rnd(-5, 5) + assert.is_nil(err) + assert.is_true(v >= -5 and v <= 5 and math.floor(v) == v) + end + end) + + + it("with two equal args returns that value", function() + local v, err = system.rnd(7, 7) + assert.is_nil(err) + assert.are.equal(7, v) + end) + + + it("returns nil and error for empty interval (m > n)", function() + local v, err = system.rnd(10, 5) + assert.is_falsy(v) + assert.are.equal("interval is empty", err) + end) + + + it("returns nil and error for invalid one-arg (m < 1, m ~= 0)", function() + local v, err = system.rnd(-1) + assert.is_falsy(v) + assert.are.equal("interval is empty", err) + end) + + + it("returns nil and error for wrong number of arguments", function() + local v, err = system.rnd(1, 2, 3) + assert.is_falsy(v) + assert.are.equal("wrong number of arguments", err) + end) + + end) + end) -- cgit v1.2.3-55-g6feb