Here naverokê

Modul:math

Ji Wîkîferheng

Documentation for this module may be created at Modul:math/belge

local export = {}

local format = string.format
local is_integer -- defined as export.is_integer below
local match = string.match
local select = select
local tonumber = tonumber
local tostring = tostring
local type = type

--[==[
An extended version of {tonumber()}, which attempts to convert `x` to a number. Like {tonumber()}, it will convert from base 10 by default, and the optional parameter `base` can be used to specify a different base between 2 and 36, with the letters {A-Z} (case-insensitive) representing additional digits beyond {0-9}. When strings contain hexadecimal notation (e.g. {"0x100"}), base 16 is used as the default instead, but this is overridden if `base` is set to anything other than 16.

This function differs from {tonumber()} in the following ways:
* If `finite_real` is set, then the function will only return finite real numbers; inputs which would normally produce ±infinity or NaN will instead produce {nil}.
* If `no_prefix` is set, then strings which start with {"0x"} will not be interpreted as containing hexadecimal notation, resulting in {nil}.
* If `base` is explicitly set to {10}, then strings in hexadecimal notation will always return {nil}. This fixes a bug in {tonumber()}, which treats {base=10} the same as {base} being unset, causing base 16 to be used if `x` contains hexadecimal notation (e.g. {tonumber("0x10", 10)} returns {16}, whereas {tonumber_extended("0x10", 10)} returns {nil}).]==]
function export.tonumber_extended(x, base, finite_real, no_prefix)
	-- TODO: tonumber() maxes out at 2^64 if the base is anything other than 10.
	-- TODO: support binary (0b) and octal (0o) prefixes.
	local n = tonumber(x, base)
	return not (
		n == nil or
		finite_real and n - n ~= 0 or ( -- ±infinity and NaN fail here
			n ~= x and -- no point using match() on number inputs
			(base == 10 or no_prefix and (base == nil or base == 16)) and
			match(x, "^%s*[+-]?0[xX]()")
		)
	) and n or nil
end

--[==[
Returns {true} if `x` is a finite real number, or {false} if not. If `x` is a string, it will not be coerced to a number before this check is performed.]==]
function export.is_finite_real_number(x)
	return x and type(x) == "number" and x - x == 0 -- ±infinity and NaN don't give 0
end

--[==[
Returns {true} if `x` is an integer, or {false} if not. If `x` is a string, it will not be coerced to a number before this check is performed.]==]
function export.is_integer(x)
	return x and type(x) == "number" and x % 1 == 0 -- ±infinity and NaN also fail here
end
is_integer = export.is_integer

--[==[
Returns {true} if `x` is a positive integer (or zero if the `include_0` flag is set), or {false} if not. If `x` is a string, it will not be coerced to a number before this check is performed.]==]
function export.is_positive_integer(x, include_0)
	return is_integer(x) and (x > 0 or include_0 and x == 0) or false
end

--[==[
Returns the base-10 logarithm of `x`.

This function should be used instead of {math.log10}, which is deprecated and may stop working if Scribunto is updated to a more recent Lua version.]==]
function export.log10(x) -- Structured like this so that module documentation works.
	local log10 = math.log10
	if log10 ~= nil then
		return log10
	end
	local log = math.log
	return log(10, 10) == 1 and function(x) -- Lua 5.2
		return log(x, 10)
	end or function(x) -- Lua 5.1
		return log(x) * 0.43429448190325182765112891891660508229439700580367 -- log10(e)
	end
end
export.log10 = export.log10() -- Sets the actual returned function.

local function integer_error(x, param, func_name)
	local type_x = type(x)
	error(format(
		"bad argument #%d to '%s' (integer expected, got %s)",
		param, func_name, type_x == "number" and tostring(x) or type_x
	), 3)
end

--[==[
Converts a decimal number to hexadecimal. If `include_prefix` is set, the returned number will include the 0x prefix.]==]
function export.to_hex(dec, include_prefix)
	dec = tonumber(dec) or dec
	if not is_integer(dec, true) then
		integer_error(dec, 1, "to_hex")
	end
	local neg = dec < 0
	if neg then
		dec = -dec
	end
	-- Inputs >= 2^64 cause string.format to return "0".
	if dec >= 0x1p64 then
		error("integer overflow in 'to_hex': cannot convert inputs with a magnitude greater than or equal to 2^64 (18446744073709551616)", 2)
	end
	-- string.format treats hex numbers as unsigned, so any sign must be added manually.
	return format("%s%s%X", neg and "-" or "", include_prefix and "0x" or "", dec)
end

--[==[
Returns the greatest common divisor of an arbitrary number of input numbers.]==]
function export.gcd(x, ...)
	x = tonumber(x) or x
	if not is_integer(x) then
		integer_error(x, 1, "gcd")
	end
	local q, args_len, integers = ..., select("#", ...)
	-- Compute p_1 = gcd(n_1, n_2), p_2 = gcd(p_1, n_3), ... i.e. compute GCD by Euclid's algorithm for the current result and the next number.
	for i = 2, args_len + 1 do
		q = tonumber(q) or q
		if not is_integer(q) then
			integer_error(q, i, "gcd")
		elseif x ~= 1 then -- If x is 1, validate remaining inputs.
			-- GCD of two integers x, q with Euclid's algorithm.
			while q ~= 0 do
				x, q = q, x % q
			end
		end
		if i <= args_len then
			-- Only create a table if absolutely necessary, as it's inefficient.
			if i == 2 then
				integers = {...}
			end
			q = integers[i]
		end
	end
	return x < 0 and -x or x
end

--[==[
Returns the least common multiple of an arbitrary number of input numbers.]==]
function export.lcm(x, ...)
	x = tonumber(x) or x
	if not is_integer(x) then
		integer_error(x, 1, "lcm")
	end
	local q, args_len, integers = ..., select("#", ...)
	-- Compute the product of all inputs as p and GCD as x.
	for i = 2, args_len + 1 do
		q = tonumber(q) or q
		if not is_integer(q) then
			integer_error(q, i, "lcm")
		elseif x ~= 0 then  -- If x is 0, validate remaining inputs.
			-- Compute the product.
			local p = x * q
			-- GCD of two integers x, q with Euclid's algorithm.
			while q ~= 0 do
				x, q = q, x % q
			end
			-- Divide product by the GCD to get new LCM.
			x = p / x
		end
		if i <= args_len then
			-- Only create a table if absolutely necessary, as it's inefficient.
			if i == 2 then
				integers = {...}
			end
			q = integers[i]
		end
	end
	return x < 0 and -x or x
end

return export