Documentation for this module may be created at Module:CurveTable/doc

local p = {}

-- Parses a string of the format `x,y` with numerical `x` and `y` into an array with named numbers `x` and `y`.
function parsePoint(string)
    local stringArray = mw.text.split(string, ",")

    local point = {}
    point["x"] = tonumber(stringArray[1])
    point["y"] = tonumber(stringArray[2])

    return point
end

-- Rounds `value` according to the type of rounding indicated by `mode`.
function round(mode, value)
        if mode == "ceil"    then return math.ceil(value)
    elseif mode == "floor"   then return math.floor(value)
    elseif mode == "trunc"   then return value >= 0 and math.floor(value - 0.5) or math.ceil(value + 0.5)
    elseif mode == "nearest" then return math.floor(value + 0.5)
                             else return value
    end
end

-- Interpolates (or extrapolates) the x-coordinate `c` based on points `a` and `b`.
function interpolatePoint(a, b, c)
    return a["y"] + (c - a["x"]) * (b["y"] - a["y"]) / (b["x"] - a["x"])
end

-- Interpolates (or extrapolates) the x-coordinate `target` in the curve table `curveTable`.
function interpolate(curveTable, target)
    if #curveTable == 1 then
        -- One point in curve table; interpret as horizontal line
        return parsePoint(curveTable[1])["y"]
    end

    -- Find the first point that is greater than `target`
    for i = 1, #curveTable, 1 do
        local point = parsePoint(curveTable[i])

        if target == point["x"] then
            -- `target` is on a point in the curve table; no interpolation/extrapolation required
            return point["y"]
        elseif target < point["x"] then
            if i == 1 then
                -- `target` comes before the first point; extrapolate from first two points in table
                return interpolatePoint(parsePoint(curveTable[1]), parsePoint(curveTable[2]), target)
            else
                -- `target` is in between two points; interpolate between current and previous point
                return interpolatePoint(parsePoint(curveTable[i - 1]), parsePoint(curveTable[i]), target)
            end
        end
    end

    -- `target` is after last point. Extrapolate from last two points in table
    return interpolatePoint(parsePoint(curveTable[#curveTable - 1]), parsePoint(curveTable[#curveTable]), target)
end

-- See documentation at [[Template:CurveTable]].
function p.evaluate(frame)
    local curveTable = mw.text.split(frame.args[1], " ")
    if #curveTable == 0 then
        -- Empty curve table
        return "Not enough points in curve table."
    end
    
    local target1 = tonumber(frame.args[2])
    return round(frame.args["round"], interpolate(curveTable, target1))
end

return p
Community content is available under CC-BY-SA unless otherwise noted.