luau-src-rs/luau/VM/src/lmathlib.cpp

447 lines
12 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#include "lualib.h"
#include "lstate.h"
#include <math.h>
#include <time.h>
#undef PI
#define PI (3.14159265358979323846)
#define RADIANS_PER_DEGREE (PI / 180.0)
#define PCG32_INC 105
static uint32_t pcg32_random(uint64_t* state)
{
uint64_t oldstate = *state;
*state = oldstate * 6364136223846793005ULL + (PCG32_INC | 1);
uint32_t xorshifted = uint32_t(((oldstate >> 18u) ^ oldstate) >> 27u);
uint32_t rot = uint32_t(oldstate >> 59u);
return (xorshifted >> rot) | (xorshifted << ((-int32_t(rot)) & 31));
}
static void pcg32_seed(uint64_t* state, uint64_t seed)
{
*state = 0;
pcg32_random(state);
*state += seed;
pcg32_random(state);
}
static int math_abs(lua_State* L)
{
lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
return 1;
}
static int math_sin(lua_State* L)
{
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}
static int math_sinh(lua_State* L)
{
lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
return 1;
}
static int math_cos(lua_State* L)
{
lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
return 1;
}
static int math_cosh(lua_State* L)
{
lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
return 1;
}
static int math_tan(lua_State* L)
{
lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
return 1;
}
static int math_tanh(lua_State* L)
{
lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
return 1;
}
static int math_asin(lua_State* L)
{
lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
return 1;
}
static int math_acos(lua_State* L)
{
lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan(lua_State* L)
{
lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan2(lua_State* L)
{
lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_ceil(lua_State* L)
{
lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
return 1;
}
static int math_floor(lua_State* L)
{
lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
return 1;
}
static int math_fmod(lua_State* L)
{
lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_modf(lua_State* L)
{
double ip;
double fp = modf(luaL_checknumber(L, 1), &ip);
lua_pushnumber(L, ip);
lua_pushnumber(L, fp);
return 2;
}
static int math_sqrt(lua_State* L)
{
lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
return 1;
}
static int math_pow(lua_State* L)
{
lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_log(lua_State* L)
{
double x = luaL_checknumber(L, 1);
double res;
if (lua_isnoneornil(L, 2))
res = log(x);
else
{
double base = luaL_checknumber(L, 2);
if (base == 2.0)
res = log2(x);
else if (base == 10.0)
res = log10(x);
else
res = log(x) / log(base);
}
lua_pushnumber(L, res);
return 1;
}
static int math_log10(lua_State* L)
{
lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
return 1;
}
static int math_exp(lua_State* L)
{
lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
return 1;
}
static int math_deg(lua_State* L)
{
lua_pushnumber(L, luaL_checknumber(L, 1) / RADIANS_PER_DEGREE);
return 1;
}
static int math_rad(lua_State* L)
{
lua_pushnumber(L, luaL_checknumber(L, 1) * RADIANS_PER_DEGREE);
return 1;
}
static int math_frexp(lua_State* L)
{
int e;
lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
lua_pushinteger(L, e);
return 2;
}
static int math_ldexp(lua_State* L)
{
lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkinteger(L, 2)));
return 1;
}
static int math_min(lua_State* L)
{
int n = lua_gettop(L); // number of arguments
double dmin = luaL_checknumber(L, 1);
int i;
for (i = 2; i <= n; i++)
{
double d = luaL_checknumber(L, i);
if (d < dmin)
dmin = d;
}
lua_pushnumber(L, dmin);
return 1;
}
static int math_max(lua_State* L)
{
int n = lua_gettop(L); // number of arguments
double dmax = luaL_checknumber(L, 1);
int i;
for (i = 2; i <= n; i++)
{
double d = luaL_checknumber(L, i);
if (d > dmax)
dmax = d;
}
lua_pushnumber(L, dmax);
return 1;
}
static int math_random(lua_State* L)
{
global_State* g = L->global;
switch (lua_gettop(L))
{ // check number of arguments
case 0:
{ // no arguments
// Using ldexp instead of division for speed & clarity.
// See http://mumble.net/~campbell/tmp/random_real.c for details on generating doubles from integer ranges.
uint32_t rl = pcg32_random(&g->rngstate);
uint32_t rh = pcg32_random(&g->rngstate);
double rd = ldexp(double(rl | (uint64_t(rh) << 32)), -64);
lua_pushnumber(L, rd); // number between 0 and 1
break;
}
case 1:
{ // only upper limit
int u = luaL_checkinteger(L, 1);
luaL_argcheck(L, 1 <= u, 1, "interval is empty");
uint64_t x = uint64_t(u) * pcg32_random(&g->rngstate);
int r = int(1 + (x >> 32));
lua_pushinteger(L, r); // int between 1 and `u'
break;
}
case 2:
{ // lower and upper limits
int l = luaL_checkinteger(L, 1);
int u = luaL_checkinteger(L, 2);
luaL_argcheck(L, l <= u, 2, "interval is empty");
uint32_t ul = uint32_t(u) - uint32_t(l);
luaL_argcheck(L, ul < UINT_MAX, 2, "interval is too large"); // -INT_MIN..INT_MAX interval can result in integer overflow
uint64_t x = uint64_t(ul + 1) * pcg32_random(&g->rngstate);
int r = int(l + (x >> 32));
lua_pushinteger(L, r); // int between `l' and `u'
break;
}
default:
luaL_error(L, "wrong number of arguments");
}
return 1;
}
static int math_randomseed(lua_State* L)
{
int seed = luaL_checkinteger(L, 1);
pcg32_seed(&L->global->rngstate, seed);
return 0;
}
static const unsigned char kPerlin[512] = {151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99,
37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174,
20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41,
55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86,
164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17,
182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110,
79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14,
239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24,
72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247,
120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74,
165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65,
25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52,
217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112,
104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199,
106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61,
156, 180};
static float fade(float t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
static float math_lerp(float t, float a, float b)
{
return a + t * (b - a);
}
static float grad(unsigned char hash, float x, float y, float z)
{
unsigned char h = hash & 15;
float u = (h < 8) ? x : y;
float v = (h < 4) ? y : (h == 12 || h == 14) ? x : z;
return (h & 1 ? -u : u) + (h & 2 ? -v : v);
}
static float perlin(float x, float y, float z)
{
float xflr = floorf(x);
float yflr = floorf(y);
float zflr = floorf(z);
int xi = int(xflr) & 255;
int yi = int(yflr) & 255;
int zi = int(zflr) & 255;
float xf = x - xflr;
float yf = y - yflr;
float zf = z - zflr;
float u = fade(xf);
float v = fade(yf);
float w = fade(zf);
const unsigned char* p = kPerlin;
int a = p[xi] + yi;
int aa = p[a] + zi;
int ab = p[a + 1] + zi;
int b = p[xi + 1] + yi;
int ba = p[b] + zi;
int bb = p[b + 1] + zi;
return math_lerp(w,
math_lerp(v, math_lerp(u, grad(p[aa], xf, yf, zf), grad(p[ba], xf - 1, yf, zf)),
math_lerp(u, grad(p[ab], xf, yf - 1, zf), grad(p[bb], xf - 1, yf - 1, zf))),
math_lerp(v, math_lerp(u, grad(p[aa + 1], xf, yf, zf - 1), grad(p[ba + 1], xf - 1, yf, zf - 1)),
math_lerp(u, grad(p[ab + 1], xf, yf - 1, zf - 1), grad(p[bb + 1], xf - 1, yf - 1, zf - 1))));
}
static int math_noise(lua_State* L)
{
double x = luaL_checknumber(L, 1);
double y = luaL_optnumber(L, 2, 0.0);
double z = luaL_optnumber(L, 3, 0.0);
double r = perlin((float)x, (float)y, (float)z);
lua_pushnumber(L, r);
return 1;
}
static int math_clamp(lua_State* L)
{
double v = luaL_checknumber(L, 1);
double min = luaL_checknumber(L, 2);
double max = luaL_checknumber(L, 3);
luaL_argcheck(L, min <= max, 3, "max must be greater than or equal to min");
double r = v < min ? min : v;
r = r > max ? max : r;
lua_pushnumber(L, r);
return 1;
}
static int math_sign(lua_State* L)
{
double v = luaL_checknumber(L, 1);
lua_pushnumber(L, v > 0.0 ? 1.0 : v < 0.0 ? -1.0 : 0.0);
return 1;
}
static int math_round(lua_State* L)
{
lua_pushnumber(L, round(luaL_checknumber(L, 1)));
return 1;
}
static const luaL_Reg mathlib[] = {
{"abs", math_abs},
{"acos", math_acos},
{"asin", math_asin},
{"atan2", math_atan2},
{"atan", math_atan},
{"ceil", math_ceil},
{"cosh", math_cosh},
{"cos", math_cos},
{"deg", math_deg},
{"exp", math_exp},
{"floor", math_floor},
{"fmod", math_fmod},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10},
{"log", math_log},
{"max", math_max},
{"min", math_min},
{"modf", math_modf},
{"pow", math_pow},
{"rad", math_rad},
{"random", math_random},
{"randomseed", math_randomseed},
{"sinh", math_sinh},
{"sin", math_sin},
{"sqrt", math_sqrt},
{"tanh", math_tanh},
{"tan", math_tan},
{"noise", math_noise},
{"clamp", math_clamp},
{"sign", math_sign},
{"round", math_round},
{NULL, NULL},
};
/*
** Open math library
*/
int luaopen_math(lua_State* L)
{
uint64_t seed = uintptr_t(L);
seed ^= time(NULL);
seed ^= clock();
pcg32_seed(&L->global->rngstate, seed);
luaL_register(L, LUA_MATHLIBNAME, mathlib);
lua_pushnumber(L, PI);
lua_setfield(L, -2, "pi");
lua_pushnumber(L, HUGE_VAL);
lua_setfield(L, -2, "huge");
return 1;
}