Jump to content

Module:Sandbox/Squc/Roman

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Squc (talk | contribs) at 13:04, 23 July 2013. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- Module to convert Roman numerals and reject invalid numerals
 
local p={}
 
local function todec ( rnum )

    local err = ""
    if rnum == "" then return -1, ("Input is empty") end
    if type(rnum) ~= "string" then return -1, ("Input is not a string") end
    rnum = rnum:upper()
    rnum = rnum:match"^%s*(.*%S)"
    local space = rnum:gsub("[^ \t\r\n\v\f]","")
    if #space ~= 0 then
        err = (err .. "Whitespace found in middle of string, ")
        rnum:match"[^ \t\r\n\v\f]"
    end
    local extra = rnum:gsub("[MDCLXVI]","")
    if #extra ~= 0 then return -1, ("Extra characters " .. extra .. " found") end
 
    local value = {
        I = 1,
        V = 5,
        X = 10,
        L = 50,
        C = 100,
        D = 500,
        M = 1000
    }
    local symbol = {}
    for i,v in ipairs(value) do symbol[v] = i end -- reverse value table
    local prev, curr, cu1d, run, num = 0,0,0,0,0
    local psym, csym = "","" -- previous symbol, current symbol

    for i = 1, #rnum do
        csym = rnum:sub(i, i)
        local curr = value[ csym ]
        cu1d = tostring( curr ):sub( 1, 1 )  -- first digit of curr (1 or 5)
        if curr < prev or prev == 0 then

            num = num + prev*run
            run = 1

        elseif curr == prev then
 
            if cu1d == "1" then
                if run == 4 then                            -- e.g. "XXXXX" for 50, "L" suggested
                    err=(err.."More than four " .. csym .. " in a row, suggestion: " .. symbol[ curr*5 ] .. "? - Char " ..tostring(i)..", ")
                elseif run == 0 then                        -- e.g. occurs after curr > prev (below) e.g. "XCC"
                    err=(err.."Repeat after subtraction - " .. rnum:sub(i-2, i-2) .. psym .. csym .. " - Char " ..tostring(i)..", ")
                else
                    run = run + 1
                end
            elseif cu1d == "5" then                         -- e.g. "VV" for 10, "X" suggested
                err=(err..psym .. " cannot be with another ".. csym ..", suggestion: " .. symbol[curr*2] .. "? - Char " ..tostring(i)..", ")
            else return -1, ("Unknown error 1") end
 
        elseif curr > prev then
 
            if curr > prev * 10 then                        -- e.g. "XM" or "IL"
                err=(err..csym .. " cannot follow " .. psym .. " (Subtraction can only be within the same digit) Char " ..tostring(i)..", ")
            elseif tostring( prev ):sub(1, 1) == "5" then   -- e.g. "LC" for 50
                err=(err..csym .. " cannot follow " .. psym .. " (Cannot subtract from " .. tostring(prev) .. ") Char " ..tostring(i)..", ")
            elseif run > 2 then                             -- e.g. "XXXL" for 20
                err=(err.."Number of" .. psym .. " before " .. csym .. " must be at most two - Char " ..tostring(i)..", ")
            else 
                num = num - prev*run + curr
                run = 0
            end
 
        else return -1, ("Unknown error 2") end
 
        prev = curr
        psym = csym
    end
    num = num + prev*run
    
    if err ~= "" then err = err:sub(1, -3) end
    return num, err
end

function p.todecimald( frame )
    return todec( frame )
end

function p.todecimalf( frame )
    local input = frame.args[1]
    local num, err = todec( input )
    if num ~= -1 then return num
    else
        if err ~= "" then error(err)
        else error("Unknown error 3") end
    end
end

function p.todecimal( frame )
    local input = frame.args[1]
    local num, err = todec( input )
    if err == "" then
        if num ~= -1 then return num
        else error("Unknown error 3") end
    else
        error(err)
    end
end

return p