Saltar para o conteúdo

Módulo:Road data/util

Origem: Wikipédia, a enciclopédia livre.
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 target[key] == nil then
            target[key] = 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 = arr[key]
        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