Lua math library

pull/1212/head^2
Zef Hemel 2025-01-17 10:40:47 +01:00
parent b398a6b2ff
commit 66433d27cc
5 changed files with 369 additions and 0 deletions

View File

@ -29,6 +29,10 @@ Deno.test("[Lua] OS tests", async () => {
await runLuaTest("./stdlib/os_test.lua");
});
Deno.test("[Lua] Math tests", async () => {
await runLuaTest("./stdlib/math_test.lua");
});
Deno.test("[Lua] JS tests", async () => {
await runLuaTest("./stdlib/js_test.lua");
});

View File

@ -27,6 +27,7 @@ import {
type LuaQueryCollection,
} from "$common/space_lua/query_collection.ts";
import { templateApi } from "$common/space_lua/stdlib/template.ts";
import { mathApi } from "$common/space_lua/stdlib/math.ts";
const printFunction = new LuaBuiltinFunction(async (_sf, ...args) => {
console.log("[Lua]", ...(await Promise.all(args.map(luaToString))));
@ -235,6 +236,7 @@ export function luaBuildStandardEnv() {
env.set("table", tableApi);
env.set("os", osApi);
env.set("js", jsApi);
env.set("math", mathApi);
// Non-standard
env.set("each", eachFunction);
env.set("space_lua", spaceLuaApi);

View File

@ -0,0 +1,221 @@
API docs for Lua's `math` module.
## math.random(m?, n?)
Generates random numbers. Without arguments, returns a float in [0,1). With one argument m, returns integer in [1,m]. With two arguments, returns integer in [m,n].
Example:
```lua
print(math.random()) -- prints: 0.7259081864761557
print(math.random(10)) -- prints: 7 (random number from 1-10)
print(math.random(5,10)) -- prints: 8 (random number from 5-10)
```
## math.abs(x)
Returns the absolute value of `x`.
Example:
```lua
print(math.abs(-5)) -- prints: 5
print(math.abs(3.7)) -- prints: 3.7
```
## math.ceil(x)
Returns the smallest integer larger than or equal to `x`.
Example:
```lua
print(math.ceil(3.3)) -- prints: 4
print(math.ceil(-3.3)) -- prints: -3
```
## math.floor(x)
Returns the largest integer smaller than or equal to `x`.
Example:
```lua
print(math.floor(3.7)) -- prints: 3
print(math.floor(-3.7)) -- prints: -4
```
## math.max(...)
Returns the maximum value among its arguments.
Example:
```lua
print(math.max(1, 2, 3, 4)) -- prints: 4
print(math.max(-5, -2, -10)) -- prints: -2
```
## math.min(...)
Returns the minimum value among its arguments.
Example:
```lua
print(math.min(1, 2, 3, 4)) -- prints: 1
print(math.min(-5, -2, -10)) -- prints: -10
```
## math.fmod(x, y)
Returns the remainder of the division of `x` by `y`.
Example:
```lua
print(math.fmod(7, 3)) -- prints: 1
print(math.fmod(7, 2)) -- prints: 1
```
## math.modf(x)
Returns the integral part and fractional part of `x`.
Example:
```lua
local int, frac = table.unpack(math.modf(3.7))
print(int, frac) -- prints: 3 0.7
```
## math.exp(x)
Returns e raised to the power of `x`.
Example:
```lua
print(math.exp(0)) -- prints: 1
print(math.exp(1)) -- prints: 2.718281828459045
```
## math.log(x, base?)
Returns the natural logarithm of `x` or the logarithm of `x` to the given base.
Example:
```lua
print(math.log(math.exp(1))) -- prints: 1
print(math.log(8, 2)) -- prints: 3
```
## math.pow(x, y)
Returns `x` raised to the power `y`.
Example:
```lua
print(math.pow(2, 3)) -- prints: 8
print(math.pow(3, 2)) -- prints: 9
```
## math.sqrt(x)
Returns the square root of `x`.
Example:
```lua
print(math.sqrt(9)) -- prints: 3
print(math.sqrt(2)) -- prints: 1.4142135623730951
```
## math.cos(x)
Returns the cosine of `x` (in radians).
Example:
```lua
print(math.cos(0)) -- prints: 1
```
## math.sin(x)
Returns the sine of `x` (in radians).
Example:
```lua
print(math.sin(0)) -- prints: 0
```
## math.tan(x)
Returns the tangent of `x` (in radians).
Example:
```lua
print(math.tan(0)) -- prints: 0
```
## math.acos(x)
Returns the arc cosine of `x` (in radians).
Example:
```lua
print(math.acos(1)) -- prints: 0
print(math.acos(0)) -- prints: 1.5707963267948966
```
## math.asin(x)
Returns the arc sine of `x` (in radians).
Example:
```lua
print(math.asin(0)) -- prints: 0
print(math.asin(1)) -- prints: 1.5707963267948966
```
## math.atan(y, x?)
Returns the arc tangent of `y/x` (in radians). If `x` is not provided, returns the arc tangent of `y`.
Example:
```lua
print(math.atan(0)) -- prints: 0
print(math.atan(1, 1)) -- prints: 0.7853981633974483
```
## math.cosh(x)
Returns the hyperbolic cosine of `x`.
Example:
```lua
print(math.cosh(0)) -- prints: 1
print(math.cosh(1)) -- prints: 1.5430806348152437
```
## math.sinh(x)
Returns the hyperbolic sine of `x`.
Example:
```lua
print(math.sinh(0)) -- prints: 0
print(math.sinh(1)) -- prints: 1.1752011936438014
```
## math.tanh(x)
Returns the hyperbolic tangent of `x`.
Example:
```lua
print(math.tanh(0)) -- prints: 0
print(math.tanh(1)) -- prints: 0.7615941559557649
```
## math.deg(x)
Converts angle `x` from radians to degrees.
## math.rad(x)
Converts angle `x` from degrees to radians.
Example:
```lua
print(math.rad(180)) -- prints: 3.141592653589793
print(math.rad(90)) -- prints: 1.5707963267948966
```
## math.ult(m, n)
Returns true if `m` is less than `n` when they are considered unsigned integers.
Example:
```lua
print(math.ult(1, 2)) -- prints: true
print(math.ult(2, 1)) -- prints: false
```
# Non-standard Extensions
## math.cosine_similarity(vecA, vecB)
Returns the cosine similarity between two vectors.
Example:
```lua
local vec1 = {1, 2, 3}
local vec2 = {4, 5, 6}
print(math.cosine_similarity(vec1, vec2)) -- prints: 0.9746318461970762
```

View File

@ -0,0 +1,94 @@
import {
LuaBuiltinFunction,
LuaRuntimeError,
LuaTable,
} from "$common/space_lua/runtime.ts";
export const mathApi = new LuaTable({
// Random number generation
random: new LuaBuiltinFunction((_sf, m?: number, n?: number) => {
if (m === undefined && n === undefined) {
// random() returns [0,1)
return Math.random();
} else if (n === undefined) {
// random(m) returns [1,m]
return Math.floor(Math.random() * m!) + 1;
} else {
// random(m,n) returns [m,n]
return Math.floor(Math.random() * (n - m! + 1)) + m!;
}
}),
// Basic functions
abs: new LuaBuiltinFunction((_sf, x: number) => Math.abs(x)),
ceil: new LuaBuiltinFunction((_sf, x: number) => Math.ceil(x)),
floor: new LuaBuiltinFunction((_sf, x: number) => Math.floor(x)),
max: new LuaBuiltinFunction((_sf, ...args: number[]) => Math.max(...args)),
min: new LuaBuiltinFunction((_sf, ...args: number[]) => Math.min(...args)),
// Rounding and remainder
fmod: new LuaBuiltinFunction((_sf, x: number, y: number) => x % y),
modf: new LuaBuiltinFunction((_sf, x: number) => {
const int = Math.floor(x);
const frac = x - int;
return new LuaTable([int, frac]);
}),
// Power and logarithms
exp: new LuaBuiltinFunction((_sf, x: number) => Math.exp(x)),
log: new LuaBuiltinFunction((_sf, x: number, base?: number) => {
if (base === undefined) {
return Math.log(x);
}
return Math.log(x) / Math.log(base);
}),
pow: new LuaBuiltinFunction((_sf, x: number, y: number) => Math.pow(x, y)),
sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(x)),
// Trigonometric functions
cos: new LuaBuiltinFunction((_sf, x: number) => Math.cos(x)),
sin: new LuaBuiltinFunction((_sf, x: number) => Math.sin(x)),
tan: new LuaBuiltinFunction((_sf, x: number) => Math.tan(x)),
acos: new LuaBuiltinFunction((_sf, x: number) => Math.acos(x)),
asin: new LuaBuiltinFunction((_sf, x: number) => Math.asin(x)),
atan: new LuaBuiltinFunction((_sf, y: number, x?: number) => {
if (x === undefined) {
return Math.atan(y);
}
return Math.atan2(y, x);
}),
// Hyperbolic functions
cosh: new LuaBuiltinFunction((_sf, x: number) => Math.cosh(x)),
sinh: new LuaBuiltinFunction((_sf, x: number) => Math.sinh(x)),
tanh: new LuaBuiltinFunction((_sf, x: number) => Math.tanh(x)),
// Additional utility
deg: new LuaBuiltinFunction((_sf, x: number) => x * 180 / Math.PI),
rad: new LuaBuiltinFunction((_sf, x: number) => x * Math.PI / 180),
ult: new LuaBuiltinFunction((_sf, m: number, n: number) => {
// Unsigned less than comparison
return (m >>> 0) < (n >>> 0);
}),
// Keep the cosine_similarity utility function
cosine_similarity: new LuaBuiltinFunction(
(sf, vecA: number[], vecB: number[]) => {
if (vecA.length !== vecB.length) {
throw new LuaRuntimeError("Vectors must be of the same length", sf);
}
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < vecA.length; i++) {
dotProduct += vecA[i] * vecB[i];
normA += vecA[i] ** 2;
normB += vecB[i] ** 2;
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
},
),
});

View File

@ -0,0 +1,48 @@
local function assert_equal(a, b)
if a ~= b then
error("Assertion failed: " .. a .. " is not equal to " .. b)
end
end
-- Trigonometric functions
assert_equal(math.cos(0), 1)
assert_equal(math.sin(0), 0)
assert_equal(math.tan(0), 0)
assert_equal(math.acos(1), 0)
assert_equal(math.asin(0), 0)
assert_equal(math.atan(0), 0)
-- Hyperbolic functions
assert_equal(math.cosh(0), 1)
assert_equal(math.sinh(0), 0)
assert_equal(math.tanh(0), 0)
-- Basic functions
assert_equal(math.abs(-5), 5)
assert_equal(math.ceil(3.3), 4)
assert_equal(math.floor(3.7), 3)
assert_equal(math.max(1, 2, 3, 4), 4)
assert_equal(math.min(1, 2, 3, 4), 1)
-- Rounding and remainder
assert_equal(math.fmod(7, 3), 1)
-- Power and logarithms
assert_equal(math.exp(0), 1)
assert_equal(math.log(math.exp(1)), 1)
assert_equal(math.log(8, 2), 3) -- log base 2 of 8
assert_equal(math.pow(2, 3), 8)
assert_equal(math.sqrt(9), 3)
-- Random number tests (basic range checks)
local rand = math.random()
assert_equal(rand >= 0 and rand < 1, true)
local rand_n = math.random(10)
assert_equal(rand_n >= 1 and rand_n <= 10, true)
local rand_range = math.random(5, 10)
assert_equal(rand_range >= 5 and rand_range <= 10, true)
-- Unsigned less than comparison
assert_equal(math.ult(1, 2), true)
assert_equal(math.ult(2, 1), false)