2021-06-11 20:47:29 +02:00
|
|
|
-- Localize globals
|
2022-07-10 15:36:54 +02:00
|
|
|
local assert, math, math_floor, minetest, modlib_table_reverse, os, string_char, select, setmetatable, table_insert, table_concat
|
|
|
|
= assert, math, math.floor, minetest, modlib.table.reverse, os, string.char, select, setmetatable, table.insert, table.concat
|
2021-06-11 20:47:29 +02:00
|
|
|
|
2021-08-16 20:07:23 +02:00
|
|
|
local inf = math.huge
|
|
|
|
|
2021-06-17 19:45:08 +02:00
|
|
|
-- Set environment
|
|
|
|
local _ENV = {}
|
|
|
|
setfenv(1, _ENV)
|
|
|
|
|
2021-12-19 14:59:05 +01:00
|
|
|
-- TODO might be too invasive
|
2020-04-21 15:04:07 +02:00
|
|
|
-- Make random random
|
2021-03-04 12:18:26 +01:00
|
|
|
math.randomseed(minetest and minetest.get_us_time() or os.time() + os.clock())
|
2020-12-19 14:41:03 +01:00
|
|
|
for _ = 1, 100 do math.random() end
|
|
|
|
|
2021-07-18 15:11:11 +02:00
|
|
|
negative_nan = 0/0
|
|
|
|
positive_nan = negative_nan ^ 1
|
|
|
|
|
2021-12-19 14:59:05 +01:00
|
|
|
function sign(number)
|
|
|
|
if number ~= number then return number end -- nan
|
|
|
|
if number == 0 then return 0 end
|
|
|
|
if number < 0 then return -1 end
|
|
|
|
if number > 0 then return 1 end
|
|
|
|
end
|
|
|
|
|
2022-05-10 19:44:27 +02:00
|
|
|
function clamp(number, min, max)
|
|
|
|
return math.min(math.max(number, min), max)
|
|
|
|
end
|
|
|
|
|
2022-07-10 15:36:54 +02:00
|
|
|
-- Random integer from 0 to 2^53 - 1 (inclusive)
|
|
|
|
local function _randint()
|
|
|
|
return math.random(0, 2^27 - 1) * 2^26 + math.random(0, 2^26 - 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Random float from 0 to 1 (exclusive)
|
|
|
|
local function _randfloat()
|
|
|
|
return _randint() / (2^53)
|
|
|
|
end
|
|
|
|
|
|
|
|
--+ Increased randomness float random without overflows
|
|
|
|
--+ `random()`: Random number from `0` to `1` (exclusive)
|
|
|
|
--+ `random(max)`: Random number from `0` to `max` (exclusive)
|
|
|
|
--+ `random(min, max)`: Random number from `min` to `max` (exclusive)
|
|
|
|
function random(...)
|
|
|
|
local n = select("#", ...)
|
|
|
|
if n == 0 then
|
|
|
|
return _randfloat()
|
|
|
|
end if n == 1 then
|
|
|
|
local max = ...
|
|
|
|
return _randfloat() * max
|
|
|
|
end do assert(n == 2)
|
|
|
|
local min, max = ...
|
|
|
|
return min + (max - min) * _randfloat()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Increased randomness integer random
|
|
|
|
--+ `randint()`: Random integer from `0` to `2^53 - 1` (inclusive)
|
|
|
|
--+ `randint(max)`: Random integer from `0` to `max` (inclusive)
|
|
|
|
--+ `randint(min, max)`: Random integer from `min` to `max` (inclusive)
|
|
|
|
function randint(...)
|
|
|
|
local n = select("#", ...)
|
|
|
|
if n == 0 then
|
|
|
|
return _randint()
|
|
|
|
end if n == 1 then
|
|
|
|
local max = ...
|
|
|
|
return math.floor(_randfloat() * max + 0.5)
|
|
|
|
end do assert(n == 2)
|
|
|
|
local min, max = ...
|
|
|
|
return min + math.floor(_randfloat() * (max - min) + 0.5)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 14:16:42 +01:00
|
|
|
log = setmetatable({}, {
|
|
|
|
__index = function(self, base)
|
|
|
|
local div = math.log(base)
|
|
|
|
local function base_log(number)
|
|
|
|
return math.log(number) / div
|
|
|
|
end
|
|
|
|
self[base] = base_log
|
|
|
|
return base_log
|
|
|
|
end,
|
|
|
|
__call = function(_, number, base)
|
|
|
|
if not base then
|
|
|
|
return math.log(number)
|
|
|
|
end
|
|
|
|
return math.log(number) / math.log(base)
|
2022-01-17 11:58:16 +01:00
|
|
|
end
|
2022-01-17 14:16:42 +01:00
|
|
|
})
|
2022-01-17 11:58:16 +01:00
|
|
|
|
2022-01-07 18:31:15 +01:00
|
|
|
-- one-based mod
|
|
|
|
function onemod(number, modulus)
|
|
|
|
return ((number - 1) % modulus) + 1
|
|
|
|
end
|
|
|
|
|
2020-12-19 14:41:03 +01:00
|
|
|
function round(number, steps)
|
|
|
|
steps = steps or 1
|
2021-08-08 23:29:43 +02:00
|
|
|
return math_floor(number * steps + 0.5) / steps
|
2020-02-09 01:39:54 +01:00
|
|
|
end
|
2020-12-19 14:41:03 +01:00
|
|
|
|
2020-03-23 20:20:43 +01:00
|
|
|
local c0 = ("0"):byte()
|
|
|
|
local cA = ("A"):byte()
|
2020-12-19 14:41:03 +01:00
|
|
|
|
2020-03-23 20:20:43 +01:00
|
|
|
function default_digit_function(digit)
|
2021-08-08 23:29:43 +02:00
|
|
|
if digit <= 9 then return string_char(c0 + digit) end
|
|
|
|
return string_char(cA + digit - 10)
|
2020-03-23 20:20:43 +01:00
|
|
|
end
|
2020-12-19 14:41:03 +01:00
|
|
|
|
2020-03-23 20:20:43 +01:00
|
|
|
default_precision = 10
|
2020-12-19 14:41:03 +01:00
|
|
|
|
|
|
|
-- See https://github.com/appgurueu/Luon/blob/master/index.js#L724
|
2020-03-23 20:20:43 +01:00
|
|
|
function tostring(number, base, digit_function, precision)
|
2021-08-16 20:07:23 +02:00
|
|
|
if number ~= number then
|
|
|
|
return "nan"
|
|
|
|
end
|
|
|
|
if number == inf then
|
|
|
|
return "inf"
|
|
|
|
end
|
|
|
|
if number == -inf then
|
|
|
|
return "-inf"
|
|
|
|
end
|
2020-12-19 14:41:03 +01:00
|
|
|
digit_function = digit_function or default_digit_function
|
|
|
|
precision = precision or default_precision
|
|
|
|
local out = {}
|
|
|
|
if number < 0 then
|
2021-08-08 23:29:43 +02:00
|
|
|
table_insert(out, "-")
|
2020-12-19 14:41:03 +01:00
|
|
|
number = -number
|
|
|
|
end
|
2021-08-08 23:29:43 +02:00
|
|
|
-- Rounding
|
2020-12-19 14:41:03 +01:00
|
|
|
number = number + base ^ -precision / 2
|
|
|
|
local digit
|
|
|
|
while number >= base do
|
2021-08-08 23:29:43 +02:00
|
|
|
digit = math_floor(number % base)
|
|
|
|
table_insert(out, digit_function(digit))
|
2021-08-08 22:37:47 +02:00
|
|
|
number = (number - digit) / base
|
2020-12-19 14:41:03 +01:00
|
|
|
end
|
2021-08-08 23:29:43 +02:00
|
|
|
digit = math_floor(number)
|
|
|
|
table_insert(out, digit_function(digit))
|
|
|
|
modlib_table_reverse(out)
|
2020-12-19 14:41:03 +01:00
|
|
|
number = number % 1
|
|
|
|
if number ~= 0 and number >= base ^ -precision then
|
2021-08-08 23:29:43 +02:00
|
|
|
table_insert(out, ".")
|
2021-08-08 22:37:47 +02:00
|
|
|
while precision >= 0 and number >= base ^ -precision do
|
2020-12-19 14:41:03 +01:00
|
|
|
number = number * base
|
2021-08-08 23:29:43 +02:00
|
|
|
digit = math_floor(number % base)
|
|
|
|
table_insert(out, digit_function(digit))
|
2020-12-19 14:41:03 +01:00
|
|
|
number = number - digit
|
|
|
|
precision = precision - 1
|
|
|
|
end
|
|
|
|
end
|
2021-08-08 23:29:43 +02:00
|
|
|
return table_concat(out)
|
2021-03-04 12:19:08 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
-- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround#polyfill
|
2023-02-26 15:39:37 +01:00
|
|
|
-- Rounds a 64-bit float to a 32-bit float;
|
|
|
|
-- if the closest 32-bit float is out of bounds,
|
|
|
|
-- the appropriate infinity is returned.
|
2021-03-04 12:19:08 +01:00
|
|
|
function fround(number)
|
|
|
|
if number == 0 or number ~= number then
|
|
|
|
return number
|
|
|
|
end
|
|
|
|
local sign = 1
|
|
|
|
if number < 0 then
|
|
|
|
sign = -1
|
|
|
|
number = -number
|
|
|
|
end
|
2023-02-26 15:39:37 +01:00
|
|
|
local _, exp = math.frexp(number)
|
|
|
|
exp = exp - 1 -- we want 2^exponent >= number > 2^(exponent-1)
|
2021-03-30 18:06:58 +02:00
|
|
|
local powexp = 2 ^ math.max(-126, math.min(exp, 127))
|
2023-02-26 15:39:37 +01:00
|
|
|
local leading = exp <= -127 and 0 or 1 -- subnormal number?
|
|
|
|
local mantissa = math.floor((number / powexp - leading) * 0x800000 + 0.5)
|
|
|
|
if
|
|
|
|
mantissa > 0x800000 -- doesn't fit in mantissa
|
|
|
|
or (exp >= 127 and mantissa == 0x800000) -- fits if the exponent can be increased
|
|
|
|
then
|
2021-08-16 20:07:23 +02:00
|
|
|
return sign * inf
|
2021-03-04 12:19:08 +01:00
|
|
|
end
|
2023-02-26 15:39:37 +01:00
|
|
|
return sign * powexp * (leading + mantissa / 0x800000)
|
2021-06-17 19:45:08 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Export environment
|
2022-05-10 19:44:27 +02:00
|
|
|
return _ENV
|