Module:Utils: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Bug fixing
Fix for clamp. Improve documentation
 
(74 intermediate revisions by 7 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


-- evaluate input on error use default
local get_args = require("Module:Arguments").getArgs
local function eval_num_arg(input, def_value)
local yesno = require("Module:Yesno")
 
-- TODO??? Enforce rule for prefixing module-callable functions with an
-- underscore, regardless of whether a wrapper is needed or can be made????
 
-- Trim a string (remove leading and trailing, but not interior, whitespace)
function p.trim(s)
  return s:match("^%s*(.-)%s*$")
end
 
-- Wrapper function for template access to [[Module:Yesno]]
function p._yesno(frame)
return yesno(frame.args["input"], frame.args["default"])
end
 
-- Clamp function
-- Underscore-prefixed in case a wrapper for a template is ever needed
function p._clamp(value, min_value, max_value)
return math.min(math.max(value, min_value), max_value)
end
 
-- Check if a table contains the given value
-- Accepts an optional comparison function that accepts two tables, returning
-- true if value and tbl[i] have the same value.
function p.table_contains(tbl, value, compare_func)
if compare_func ~= nil then
-- Use compare function
for i = 1, #tbl do
if compare_func(value, tbl[i]) then
return true
end
end
else
-- No compare function
for i = 1, #tbl do
if value == tbl[i] then
return true
end
end
end
return false
end
 
-- Return the first index with the given value (or nil if not found)
-- Accepts an optional comparison function that accepts two tables, returning
-- true if value and v have the same value.
function p.index_of(array, value, compare_func)
if compare_func ~= nil then
-- Use compare function
for i, v in ipairs(array) do
if compare_func(v, value) then
return i
end
end
else
-- No compare function
for i, v in ipairs(array) do
if v == value then
return i
end
end
end
return nil
end
 
-- Check whether the input is a non-empty string
function p.value_provided(s)
return type(s) == "string" and #s > 0
end
 
-- Parse a ratio string like "3/2" into a number
-- Use default on error; cannot be used with {{#invoke:}}
function p.eval_num_arg(input, def_value)
local result = input
local result = input
if type(input) ~= 'number' then
if type(input) ~= "number" then
result = def_value
result = def_value
if type(input) == 'string' then
if type(input) == "string" then
input = input:match("^%s*(.-)%s*$")
-- Check for fraction notation
if string.len(input) > 0 then
if input:match("/") == "/" then
local numerator, denominator = input:match("^%s*([0-9]+)[/?]([0-9]+)%s*$")
result = (tonumber(numerator) or def_value) / (tonumber(denominator) or 1)
else
result = tonumber(input)
result = tonumber(input)
end
end
Line 16: Line 91:
end
end


-- return logarithm base b of x
-- Return logarithm base b of x
function p.log(frame)
function p.log(frame)
-- x defaults to 0 if omitted
local args = get_args(frame)
local x = eval_num_arg(frame.args[1], 0)
return p._log(args[1], args[2])
-- b defaults to 2 ("octave") if omitted
end
local b = eval_num_arg(frame.args[2], 2)
 
return math.log(x)/math.log(b)
local LN_2 = math.log(2)
-- Return logarithm base 2 of x
function p.log2(x)
return math.log(x) / LN_2
end
end


-- return x rounded to a precision of p decimal places
function p._log(x, b)
-- x defaults to 0
x = p.eval_num_arg(x, 0)
-- b defaults to 2 ("octave")
b = p.eval_num_arg(b, 2)
return math.log(x) / math.log(b)
end
 
-- Return greatest common divisor of a and b
function p.gcd(frame)
local args = get_args(frame)
return p._gcd(args[1], args[2])
end
 
function p._gcd(a, b)
if b ~= 0 then
return p._gcd(b, a % b)
else
return math.abs(a)
end
end
 
-- Return x rounded to places decimal places
function p.round_dec(frame)
local args = get_args(frame)
return p._round_dec(args[1], args[2])
end
 
function p._round_dec(x, places)
-- x defaults to 0
x = p.eval_num_arg(x, 0)
-- places defaults to 0
places = p.eval_num_arg(places, 0)
return math.floor(x * 10 ^ places + 0.5) / 10 ^ places
end
 
-- Return x rounded to a precision of prec significant figures
function p.round(frame)
function p.round(frame)
-- x defaults to 0 if omitted
local args = get_args(frame)
local x = eval_num_arg(frame.args[1], 0)
return p._round(args[1], args[2])
-- prec defaults to 3 if omitted
local prec = eval_num_arg(frame.args[2], 3)
return math.floor(x*10^prec+0.5)/10^prec
end
end
function p._round(x, prec)
-- x defaults to 0
x = p.eval_num_arg(x, 0)
-- prec defaults to 6
prec = p.eval_num_arg(prec, 6)
if x == 0 then
return 0
else
return p._round_dec(x, prec - math.floor(p._log(math.abs(x), 10)) - 1)
end
end
-- Cached list of primes for is_prime
local primes_cache = {
[0] = false,
[1] = false,
}
-- Returns true if integer n is prime; cannot be used with {{#invoke:}}
function p.is_prime(n)
local cached = primes_cache[n]
if cached ~= nil then
return cached
end
for i = 2, math.sqrt(n) do
if n % i == 0 then
primes_cache[n] = false
return false
end
end
primes_cache[n] = true
return true
end
-- Returns prime factorization of integer n > 1; cannot be used with {{#invoke:}}
-- Note: the order of keys is not specified for Lua tables
function p.prime_factorization_raw(n)
local factors = {}
local m = n
for i = 2, math.sqrt(n) + 1 do
while m % i == 0 do
factors[i] = factors[i] or 0
factors[i] = factors[i] + 1
m = m / i
end
if m == 1 then
break
end
end
if m > 1 then
factors[m] = factors[m] or 1
end
return factors
end
-- Returns prime factorization of integer n > 2 (with wiki markup for exponents)
function p.prime_factorization(frame)
local args = get_args(frame)
return p._prime_factorization(p.eval_num_arg(args[1], 12)) -- default to 12
end
function p._prime_factorization(n)
if n <= 1 then
return "n/a"
end
local factors, powers = {}, {}
local new_number = n
for i = 2, n do
if p.is_prime(i) then
if new_number % i == 0 then
factors[#factors + 1] = i
powers[#factors] = 0
while new_number % i == 0 do
powers[#factors] = powers[#factors] + 1
new_number = new_number / i
end
if powers[#factors] > 1 then
powers[#factors] = factors[#factors] .. "<sup>" .. powers[#factors] .. "</sup>"
else
powers[#factors] = factors[#factors]
end
end
end
if new_number == 1 then
break
end
end
return table.concat(powers, " × ")
end
-- Returns signum(x); cannot be used with {{#invoke:}}
function p.signum(x)
if type(x) ~= "number" then
return 0
end
if x > 0 then
return 1
end
if x < 0 then
return -1
end
return 0
end
-- Returns the next Young diagram of the same size or nil; cannot be used with {{#invoke:}}
-- Modifies the input table
function p.next_young_diagram(d)
if #d == 0 then
return nil
end
local i_from = nil
local size = 0
for i = #d, 1, -1 do
if d[i] > 1 then
i_from = i
break
end
size = size + d[i]
end
if i_from == nil then
return nil
end
d[i_from] = d[i_from] - 1
size = size + 1
-- repacking the tail
local max_d = d[i_from]
for i = i_from + 1, #d + 1 do
if size >= max_d then
d[i] = max_d
size = size - max_d
elseif size > 0 then
d[i] = size
size = 0
else
d[i] = nil
end
end
return d
end
-- stylua: ignore
p.primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
  101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
  211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271}


return p
return p

Latest revision as of 10:14, 12 December 2025

Module documentation[view] [edit] [history] [purge]
This module primarily serves as a library for other modules and has no corresponding template.

This module provides several mathematical functions which are likely to be used frequently on the Xenharmonic Wiki.

Namely, the functions in this module can be called from other modules by using require("Module:Utils") and calling the desired functions.

For pairs of functions with names that differ by a single leading underscore (for example, gcd and _gcd), the function with an underscore is meant to be called by other modules, and the function without an underscore can be used with #invoke.


Introspection summary for Module:Utils 
Functions provided (22)
Line Function Params
10 trim (s)
15 _yesno (invokable) (frame)
21 _clamp (value, min_value, max_value)
28 table_contains (tbl, value, compare_func)
50 index_of (array, value, compare_func)
70 value_provided (s)
76 eval_num_arg (input, def_value)
94 log (invokable) (frame)
101 log2 (x)
105 _log (x, b)
114 gcd (invokable) (frame)
119 _gcd (a, b)
128 round_dec (invokable) (frame)
133 _round_dec (x, places)
142 round (invokable) (frame)
147 _round (x, prec)
166 is_prime (n)
183 prime_factorization_raw (n)
203 prime_factorization (invokable) (frame)
208 _prime_factorization (n)
238 signum (x)
253 next_young_diagram (d)
Lua modules required (2)
Variable Module Functions used
get_args Module:Arguments getArgs
yesno Module:Yesno yesno

No function descriptions were provided. The Lua code may have further information.

Functions

trim(s)
Removes leading and trailing whitespaces (but not interior ones) from a string.
_yesno(frame)
Wrapper function for Module: Yesno, for use with templates. (Module: Yesno does not have a corresponding template.)
_clamp(value, min_value, max_value)
Returns value such that it is between min_value and max_value.
table_contains(tbl, value, compare_func) *
Checks whether table tbl contains value and returns true (if found) or false if not found. Passing compare_func is optional; By default, value is a numeric value or a string; to check for other values, such as ratios defined by Module: Rational, a comparison function compare_func must be passed in.
index_of(array, value, compare_func) *
Returns the first index with the given value (or nil if not found). Passing compare_func is optional. By default, value is a numeric value or a string; to check for other values, such as ratios defined by Module:Rational, a comparison function compare_func must be passed in.
value_provided(s) *
Checks whether s is a non-empty string.
eval_num_arg(input, def_value) *
Evaluates input as a number, fraction notation supported but not other expressions; uses def_value on error.
log(x, b)
Returns the logarithm base b of x. Parameter b defaults to base 2 (octave) if it is omitted.
log2(x)
Returns the base-2 logarithm of x.
gcd(a, b)
Returns the greatest common divisor of a and b.
round_dec(x, places)
Returns x rounded to a precision of places decimal places. Parameter places defaults to 0 if it is omitted.
round(x, prec)
Returns x rounded to a precision of prec significant figures. Parameter prec defaults to 6 if it is omitted.
is_prime(n)*
Returns true if the given integer n is a prime number.
prime_factorization(n)
Returns the prime factorization of the given integer n using the exponential form (in wikitext).
prime_factorization_raw(n) *
Returns a table encoding the prime factorization of n.
signum(x) *
Returns 1 for positive numbers, −1 for negative ones, and 0 for zero and non-integer inputs.
next_young_diagram(d)
Returns the next Young diagram of the same size; the first one is [N], the last one is [1, 1, …, 1]. After the last one, nil is returned. The input table is modified.

* These functions are designed to be used by other modules only; they cannot be called with {{#invoke: }}.



local p = {}

local get_args = require("Module:Arguments").getArgs
local yesno = require("Module:Yesno")

-- TODO??? Enforce rule for prefixing module-callable functions with an
-- underscore, regardless of whether a wrapper is needed or can be made????

-- Trim a string (remove leading and trailing, but not interior, whitespace)
function p.trim(s)
   return s:match("^%s*(.-)%s*$")
end

-- Wrapper function for template access to [[Module:Yesno]]
function p._yesno(frame)
	return yesno(frame.args["input"], frame.args["default"])
end

-- Clamp function
-- Underscore-prefixed in case a wrapper for a template is ever needed
function p._clamp(value, min_value, max_value)
	return math.min(math.max(value, min_value), max_value)
end

-- Check if a table contains the given value
-- Accepts an optional comparison function that accepts two tables, returning
-- true if value and tbl[i] have the same value.
function p.table_contains(tbl, value, compare_func)
	if compare_func ~= nil then
		-- Use compare function
		for i = 1, #tbl do
			if compare_func(value, tbl[i]) then
				return true
			end
		end
	else
		-- No compare function
		for i = 1, #tbl do
			if value == tbl[i] then
				return true
			end
		end
	end
	return false
end

-- Return the first index with the given value (or nil if not found)
-- Accepts an optional comparison function that accepts two tables, returning
-- true if value and v have the same value.
function p.index_of(array, value, compare_func)
	if compare_func ~= nil then
		-- Use compare function
		for i, v in ipairs(array) do
			if compare_func(v, value) then
				return i
			end
		end
	else
		-- No compare function
		for i, v in ipairs(array) do
			if v == value then
				return i
			end
		end
	end
	return nil
end

-- Check whether the input is a non-empty string
function p.value_provided(s)
	return type(s) == "string" and #s > 0
end

-- Parse a ratio string like "3/2" into a number
-- Use default on error; cannot be used with {{#invoke:}}
function p.eval_num_arg(input, def_value)
	local result = input
	if type(input) ~= "number" then
		result = def_value
		if type(input) == "string" then
			-- Check for fraction notation
			if input:match("/") == "/" then
				local numerator, denominator = input:match("^%s*([0-9]+)[/?]([0-9]+)%s*$")
				result = (tonumber(numerator) or def_value) / (tonumber(denominator) or 1)
			else
				result = tonumber(input)
			end
		end
	end
	return result
end

-- Return logarithm base b of x
function p.log(frame)
	local args = get_args(frame)
	return p._log(args[1], args[2])
end

local LN_2 = math.log(2)
-- Return logarithm base 2 of x
function p.log2(x)
	return math.log(x) / LN_2
end

function p._log(x, b)
	-- x defaults to 0
	x = p.eval_num_arg(x, 0)
	-- b defaults to 2 ("octave")
	b = p.eval_num_arg(b, 2)
	return math.log(x) / math.log(b)
end

-- Return greatest common divisor of a and b
function p.gcd(frame)
	local args = get_args(frame)
	return p._gcd(args[1], args[2])
end

function p._gcd(a, b)
	if b ~= 0 then
		return p._gcd(b, a % b)
	else
		return math.abs(a)
	end
end

-- Return x rounded to places decimal places
function p.round_dec(frame)
	local args = get_args(frame)
	return p._round_dec(args[1], args[2])
end

function p._round_dec(x, places)
	-- x defaults to 0
	x = p.eval_num_arg(x, 0)
	-- places defaults to 0
	places = p.eval_num_arg(places, 0)
	return math.floor(x * 10 ^ places + 0.5) / 10 ^ places
end

-- Return x rounded to a precision of prec significant figures
function p.round(frame)
	local args = get_args(frame)
	return p._round(args[1], args[2])
end

function p._round(x, prec)
	-- x defaults to 0
	x = p.eval_num_arg(x, 0)
	-- prec defaults to 6
	prec = p.eval_num_arg(prec, 6)
	if x == 0 then
		return 0
	else
		return p._round_dec(x, prec - math.floor(p._log(math.abs(x), 10)) - 1)
	end
end

-- Cached list of primes for is_prime
local primes_cache = {
	[0] = false,
	[1] = false,
}

-- Returns true if integer n is prime; cannot be used with {{#invoke:}}
function p.is_prime(n)
	local cached = primes_cache[n]
	if cached ~= nil then
		return cached
	end
	for i = 2, math.sqrt(n) do
		if n % i == 0 then
			primes_cache[n] = false
			return false
		end
	end
	primes_cache[n] = true
	return true
end

-- Returns prime factorization of integer n > 1; cannot be used with {{#invoke:}}
-- Note: the order of keys is not specified for Lua tables
function p.prime_factorization_raw(n)
	local factors = {}
	local m = n
	for i = 2, math.sqrt(n) + 1 do
		while m % i == 0 do
			factors[i] = factors[i] or 0
			factors[i] = factors[i] + 1
			m = m / i
		end
		if m == 1 then
			break
		end
	end
	if m > 1 then
		factors[m] = factors[m] or 1
	end
	return factors
end

-- Returns prime factorization of integer n > 2 (with wiki markup for exponents)
function p.prime_factorization(frame)
	local args = get_args(frame)
	return p._prime_factorization(p.eval_num_arg(args[1], 12)) -- default to 12
end

function p._prime_factorization(n)
	if n <= 1 then
		return "n/a"
	end
	local factors, powers = {}, {}
	local new_number = n
	for i = 2, n do
		if p.is_prime(i) then
			if new_number % i == 0 then
				factors[#factors + 1] = i
				powers[#factors] = 0
				while new_number % i == 0 do
					powers[#factors] = powers[#factors] + 1
					new_number = new_number / i
				end
				if powers[#factors] > 1 then
					powers[#factors] = factors[#factors] .. "<sup>" .. powers[#factors] .. "</sup>"
				else
					powers[#factors] = factors[#factors]
				end
			end
		end
		if new_number == 1 then
			break
		end
	end
	return table.concat(powers, " × ")
end

-- Returns signum(x); cannot be used with {{#invoke:}}
function p.signum(x)
	if type(x) ~= "number" then
		return 0
	end
	if x > 0 then
		return 1
	end
	if x < 0 then
		return -1
	end
	return 0
end

-- Returns the next Young diagram of the same size or nil; cannot be used with {{#invoke:}}
-- Modifies the input table
function p.next_young_diagram(d)
	if #d == 0 then
		return nil
	end
	local i_from = nil
	local size = 0
	for i = #d, 1, -1 do
		if d[i] > 1 then
			i_from = i
			break
		end
		size = size + d[i]
	end
	if i_from == nil then
		return nil
	end
	d[i_from] = d[i_from] - 1
	size = size + 1
	-- repacking the tail
	local max_d = d[i_from]
	for i = i_from + 1, #d + 1 do
		if size >= max_d then
			d[i] = max_d
			size = size - max_d
		elseif size > 0 then
			d[i] = size
			size = 0
		else
			d[i] = nil
		end
	end
	return d
end

-- stylua: ignore
p.primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
			   101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
			   211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271}

return p