Module:RomanNumeral

-- -- original source: https://gist.github.com/efrederickson/4080372 local getArgs = require('Module:Arguments').getArgs

-- Symbol  Value I        1 V        5 X        10 L        50 C        100 D        500 M        1000 If a lesser number comes before a greater number (e.g. IX), then the lesser number is subtracted from the greater number (IX -> 9, 900 -> CM) So, Symbol   Value IV       4 IX       9 XL       40 XC       90 CD       400 CM       900 LM       950 VX is actually valid as 5, along with other irregularities, such as IIIIIV for 8 Copyright (C) 2012 LoDC

local map = { I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000, } local numbers = { 1, 5, 10, 50, 100, 500, 1000 } local chars = { "I", "V", "X", "L", "C", "D", "M" }

local p = {}

function p._toRomanNumerals(s) --s = tostring(s) s = tonumber(s) if not s or s ~= s then error"Unable to convert to number" end if s == math.huge then error"Unable to convert infinity" end s = math.floor(s) if s <= 0 then return s end local ret = "" for i = #numbers, 1, -1 do       local num = numbers[i] while s - num >= 0 and s > 0 do           ret = ret .. chars[i] s = s - num end for j = 1, i - 1 do           local n2 = numbers[j] if s - (num - n2) >= 0 and s 0 and num - n2 ~= n2 then ret = ret .. chars[j] .. chars[i] s = s - (num - n2) break end end end return ret end

-- invoke function p.toRomanNumerals(frame) local args = getArgs(frame) local num = args[1]; local min = tonumber(args["min"]) or 1; local max = tonumber(args["max"]) or 9999999; -- if already a roman numeral, convert to number if string.match(num:upper, "^[IVXLCDM]+$") then num = p._toNumber(num) end num = tonumber(num) if tonumber(num) then if num >= min and num <= max then return p._toRomanNumerals(num) else -- if we set a min/max and the number doesn't fall between it then it's invalid return "[out of range]" end end return "INVALID INPUT" end

function p._toNumber(s) s = tostring(s):upper local ret = 0 local i = 1 if s:match('^%d-$') then return s   end while i <= s:len do   --for i = 1, s:len do        local c = s:sub(i, i)        if c ~= " " then -- allow spaces local m = map[c] or error("Unknown Roman Numeral '" .. c .. "'") local next = s:sub(i + 1, i + 1) local nextm = map[next] if next and nextm then if nextm > m then -- if string[i] < string[i + 1] then result += string[i + 1] - string[i] -- This is used instead of programming in IV = 4, IX = 9, etc, because it is               -- more flexible and possibly more efficient ret = ret + (nextm - m)                   i = i + 1 else ret = ret + m               end else ret = ret + m           end end i = i + 1 end return ret end

return p