Jump to content

Module:math

From Wiktionary, the free dictionary

Various mathematical functions.

Detailed documentation

export.tonumber_finite_real

function export.tonumber_finite_real(n)

A version of tonumber which only returns finite real numbers. Inputs which would return infinity or NaN from tonumber return nil.

export.is_finite_real_number

function export.is_finite_real_number(n)

Returns true if the given value is a finite real number, or false if not.

export.is_integer

function export.is_integer(n)

Returns true if the given value is an integer, or false if not.

export.is_positive_integer

function export.is_positive_integer(n, include_0)

Returns true if the given value is a positive integer (or zero if the include_0 flag is set), or false if not.

export.log10

function export.log10(x)

Returns the base-10 logarithm of x.

Should be used instead of math.log10, which is deprecated and may stop working if Scribunto is updated to a more recent Lua version.

export.to_hex

function export.to_hex(dec)

Converts a decimal number to hexadecimal.

export.gcd

function export.gcd(n, ...)

Returns the greatest common divisor of an arbitrary number of input numbers.

export.lcm

function export.lcm(n, ...)

Returns the least common multiple of an arbitrary number of input numbers.


local export = {}

local format = string.format
local is_integer -- defined as export.is_integer below
local log = math.log
local log10 = math.log10
local select = select
local tonumber = tonumber
local tostring = tostring
local type = type

--[==[
A version of {tonumber} which only returns finite real numbers. Inputs which would return infinity or {NaN} from {tonumber} return {nil}.]==]
function export.tonumber_finite_real(n)
	n = tonumber(n)
	return n and n - n == 0 and n or nil
end

--[==[
Returns {true} if the given value is a finite real number, or {false} if not.]==]
function export.is_finite_real_number(n)
	return n and type(n) == "number" and n - n == 0 -- INF, -INF and NAN fail here.
end

--[==[
Returns {true} if the given value is an integer, or {false} if not.]==]
function export.is_integer(n)
	return n and type(n) == "number" and n % 1 == 0 -- INF, -INF and NAN also fail here.
end
is_integer = export.is_integer

--[==[
Returns {true} if the given value is a positive integer (or zero if the `include_0` flag is set), or {false} if not.]==]
function export.is_positive_integer(n, include_0)
	return is_integer(n) and (n > 0 or include_0 and n == 0) or false
end

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

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)
	if log10 == nil then
		if log(10, 10) == 1 then -- Lua 5.2
			function log10(x)
				return log(x, 10)
			end
		else
			function log10(x)
				return log(x) * 0.43429448190325182765112891891660508229439700580367 -- log10(e)
			end
		end
	end
	export.log10 = log10
end
export.log10() -- Sets the actual returned function. (Note: structured like this so that module documentation works.)

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.]==]
function export.to_hex(dec)
	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 >= 18446744073709551616 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%X", neg and "-" or "", dec)
end

--[==[
Returns the greatest common divisor of an arbitrary number of input numbers.]==]
function export.gcd(n, ...)
	n = tonumber(n) or n
	if not is_integer(n) then
		integer_error(n, 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 n ~= 1 then -- If n is 1, validate remaining inputs.
			-- GCD of two integers n, q with Euclid's algorithm.
			while q ~= 0 do
				n, q = q, n % 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 n < 0 and -n or n
end

--[==[
Returns the least common multiple of an arbitrary number of input numbers.]==]
function export.lcm(n, ...)
	n = tonumber(n) or n
	if not is_integer(n) then
		integer_error(n, 1, "lcm")
	end
	local q, args_len, integers = ..., select("#", ...)
	-- Compute the product of all inputs as p and GCD as n.
	for i = 2, args_len + 1 do
		q = tonumber(q) or q
		if not is_integer(q) then
			integer_error(q, i, "lcm")
		elseif n ~= 0 then  -- If n is 0, validate remaining inputs.
			-- Compute the product.
			local p = n * q
			-- GCD of two integers n, q with Euclid's algorithm.
			while q ~= 0 do
				n, q = q, n % q
			end
			-- Divide product by the GCD to get new LCM.
			n = p / n
		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 n < 0 and -n or n
end

return export