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 10:33, 22 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={}

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

local function todec ( rnum )

    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 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, dec = 0
    local psym, csym = "" -- previous symbol, current symbol
    
    for i = 1, #rnum do
        csym = rnum:sub(i, i)
        local curr = value[symbol]
        cu1d = tostring( value ):sub( 1, 1 )  -- first digit of curr (1 or 5)
        if curr < prev or prev == 0 then
            
            run = 1
            num = num + prev*run
            
        elseif curr == prev then
            
            if cu1d == "1" then
                if run == 4 then                            -- e.g. "XXXXX" for 50, "L" suggested
                    return -1,("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"
                    return -1,("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
                return -1,(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"
                return -1,(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
                return -1,(csym .. " cannot follow " .. psym .. " (Cannot subtract from " .. tostring(prev) .. ") Char " .. tostring(i))
            elseif run > 2 then                             -- e.g. "XXXL" for 20
                return -1,("Number of" .. psym .. " before " .. csym .. " must be at most two - Char " .. tostring(i))
            else 
                num = num - prev*run + curr
                curr = 0
            end
        
        else return -1,("Unknown error 2") end
        
        prev = curr
        psym = csym
    end
    num = num + prev*run
    return num
end

return p