Permanently protected module
From Wikipedia, the free encyclopedia

local util = {}



local insert = table.insert

local concat = table.concat

local format = mw.ustring.format



---

-- Add all entries in `arr` into `target`.

-- An error is raised if `overwrite` is not true

-- and any key in `arr` is already in `target`.

function util.addAll(target, arr, overwrite)

	if type(target) ~= "table" then

		error("target is not a table")

	end

	for key,value in pairs(arr) do

		if overwrite or targetkey == nil then

			targetkey = value

		else

			error("Duplicate key: " .. tostring(key))

		end

	end

end



local function comp(e1, e2)

	local t1 = type(e1)

	local t2 = type(e2)

	if t1 ~= t2 then return t1 < t2 end

	if t1 == "function" then

		error("Unexpected function type")

	end

	return e1 < e2

end



local arrayToStringAux

arrayToStringAux = function(arr, indent)

	if type(arr) ~= "table" then

		error("arr is not a table")

	end

	if type(indent) ~= "number" then

		error("indent is not a number")

	end

	local result = {}

	local keys = {}

	for key in pairs(arr) do insert(keys, key) end

	table.sort(keys, comp)

	for _,key in ipairs(keys) do

		local value = arrkey

		local keyPrint

		if type(key) == "string" then

			keyPrint = format("\"%s\"", key)

		else

			keyPrint = tostring(key)

		end

		local valuePrint

		if type(value) == "table" then

			valuePrint = format("{\n%s\n%s}",

				arrayToStringAux(value, indent + 4),

				string.rep(" ", indent))

		elseif type(value) == "string" then

			valuePrint = format("\"%s\"", value)

		else

			valuePrint = tostring(value)

		end

		insert(result, format("%s[%s] = %s",

			string.rep(" ", indent),

			keyPrint,

			valuePrint))

	end

	return concat(result, ", \n")

end



--- Return a string representation of `arr`.

function util.arrayToString(arr, indent)

	return arrayToStringAux(arr, indent or 0)

end



local function convert(distance, multiplier, desiredPrec)

	if type(distance) ~= "string" then

		error("distance is not a string")

	end

	if type(multiplier) ~= "number" then

		error("multiplier is not a number")

	end

	-- Import math functions.

	local math = require "Module:Math"

	-- This function returns the precision of a given string representing a number.

	local precision = math._precision

	-- This function returns the order of magnitude of a given string representing a number.

	local order = math._order

	-- This function rounds a given number to the given number of digits.

	local round = math._precision_format



	local prec = desiredPrec or precision(distance)

	if not desiredPrec then

		local ord = order(distance)

		-- Adjust precision based on multiplier, as done in {{convert}}.

		prec = prec - order(multiplier / 0.2)

	end



	local converted = distance * multiplier

	local magnitude = order(converted)

	if prec <= -magnitude then

		-- Ensure the result has at least two significant digits.

		prec = -magnitude + 1

	end

	return round(converted, prec)

end



--[[-

Convert length specified in one unit (mi or km) to length in the other unit.

@param #map<#string, #string> lengths

	a map from unit to distance (as a string) in that unit;

	may contain entry `prec` indicating desired conversion precision

@param #string blank text to be used if length is unspecified

@return #table a table containing the conversion result:

	orig = source unit;

	comp = target unit;

	mi = length in miles;

	ft = converted length in feet;

	km = length in kilometers;

	m = converted length in meters;

	error = error message, if any

]]

function util.convertLengths(lengths, blank)

	-- Import math functions.

	local math = require "Module:Math"

	-- In Lua, storing functions locally results in more efficient execution.

	-- This function rounds a given number to the given number of digits.

	local round = math._precision_format

	-- This function returns the precision of a given string representing a number.

	local precision = math._precision



	local kmPerMile = 1.609344

	local ftPerMile = 5280

	-- The length in kilometers as passed to the function.

	local km = lengths.km

	-- The length in miles as passed to the function.

	local mi = lengths.mi

	-- Precision for the converted length.

	local prec = lengths.prec

	local errMsg = {}

	-- Sanitize inputs.

	local km_ = tonumber(km)

	if km and not km_ then

		insert(errMsg, util.err("km is not a number"))

	end

	local mi_ = tonumber(mi)

	if mi and not mi_ then

		insert(errMsg, util.err("mi is not a number"))

	end

	local prec_ = tonumber(prec)

	if prec and not prec_ then

		insert(errMsg, util.err("prec is not a number"))

	end

	prec = prec_



	local ft

	local m

	local orig = "mi"

	local comp = "km"

	if mi and km then

		insert(errMsg, util.err("Both mi and km are specified"))

	elseif mi then

		-- Length in miles was passed.

		if mi_ then

			-- If `mi` is indeed a number, compute and round the length in kilometers.

			km = convert(mi, kmPerMile, prec)

			m = convert(mi, kmPerMile * 1000, prec)

			-- format mi (insert separators as in 1,000)

			mi = round(mi_, precision(mi))

		else

			-- `mi` is not a number.

			km = blank

			m = blank

		end

	elseif km then

		-- Length in kilometers was passed.

		-- Swap units.

		orig, comp = comp, orig

		if km_ then

			-- If `km` is indeed a number, compute and round the length in miles.

			mi = convert(km, 1 / kmPerMile, prec)

			ft = convert(km, ftPerMile / kmPerMile, prec)

			-- format km (insert separators as in 1,000)

			km = round(km_, precision(km))

		else

			-- `km` is not a number.

			mi = blank

			ft = blank

		end

	else

		mi = blank

		ft = blank

		km = blank

		m = blank

	end

	local error = concat(errMsg)

	if error == "" then error = nil end

	return {mi = mi, ft = ft, km = km, m = m, orig = orig, comp = comp,

		error = error}

end



--- Generates wikitext error messages.

function util.err(msg)

	if msg == nil then

		error("Unspecified error message")

	end

	return format('<strong class="error">Error: %s</strong>', msg)

end



return util