Module:Roman numeral

require "string"

-- Args: value, type, case -- Value is a number -- Type is one of 'ascii', 'unicode', or 'combining' -- Case is one of 'upper' or 'lower'

local p = {}

-- 1 2 3 4  5   6   7 -- I V X  L  C   D   M -- 1 5 10 50 100 500 1000 local LA = { -- Lower Ascii {"I", 'i'}, {"V", 'v'}, {"X", 'x'}, {"L", 'l'}, {"C", 'c'}, {"D", 'd'}, {"M", 'm'} } local UA = {} -- Upper Ascii, special case no need for replacements

local LU = { -- Lower Unicode {"I", "&#x2170;"}, {"V", "&#x2174;"}, {"X", "&#x2179;"}, {"L", "&#x217C;"}, {"C", "&#x217D;"}, {"D", "&#x217E;"}, {"M", "&#x217F;"} } local UU = { -- Upper Unicode {"I", "&#x2160;"}, {"V", "&#x2164;"}, {"X", "&#x2169;"}, {"L", "&#x216C;"}, {"C", "&#x216D;"}, {"D", "&#x216E;"}, {"M", "&#x216F;"} }

local LC = { -- Lower Combining {"VIII", "&#x2177;"}, {"III", "&#x2172;"}, {"XII", "&#x217B;"}, {"VII", "&#x2176;"}, {"II", "&#x2171;"}, {"IV", "&#x2173;"}, {"IX", "&#x2178;"}, {"VI", "&#x2175;"}, {"XI", "&#x217A;"}, {"I", "&#x2170;"}, {"V", "&#x2174;"}, {"X", "&#x2179;"}, {"L", "&#x217C;"}, {"C", "&#x217D;"}, {"D", "&#x217E;"}, {"M", "&#x217F;"} } local UC = { -- Upper Combining {"VIII", "&#x2167;"}, {"III", "&#x2162;"}, {"XII", "&#x216B;"}, {"VII", "&#x2166;"}, {"II", "&#x2161;"}, {"IV", "&#x2163;"}, {"IX",	"&#x2168;"}, {"VI", "&#x2165;"}, {"XI", "&#x216A;"}, {"I", "&#x2160;"}, {"V", "&#x2164;"}, {"X", "&#x2169;"}, {"L", "&#x216C;"}, {"C", "&#x216D;"}, {"D", "&#x216E;"}, {"M", "&#x216F;"} }

function p.exec(frame) local args -- If called via #invoke, use the args passed into the invoking template. -- Otherwise, for testing purposes, assume args are being passed directly in. if frame == mw.getCurrentFrame then args = frame:getParent.args else args = frame end local value = tonumber(args[1]) if value == nil then return '0' end local neg = false if value == 0 then return '0'; elseif value < 0 then neg = true value = -value end local rn = ''

while value >= 1000 do rn = rn .. string.rep(numeral("M"), value/1000) value = value % 1000 end while value ~= 0 do 	if value >= 500 then if value < 900 then rn = rn .. string.rep("D", value/500) value = value % 500 else rn = rn .. "CM" value = value % 100 end elseif value >= 100 then if value < 400 then rn = rn .. string.rep("C", value/100) value = value % 100 else rn = rn .. "CD" value = value % 100 end elseif value >= 50 then if value < 90 then rn = rn .. string.rep("L", value/50) value = value % 50 else rn = rn .. "XC" value = value % 10 end elseif value >= 10 then if value < 40 then rn = rn .. string.rep("X", value/10) value = value % 10 else rn = rn .. "XL" value = value % 10 end elseif value >= 5 then if value < 9 then rn = rn .. string.rep("V", value/5) value = value % 5 else rn = rn .. "IX" break end else if value < 4 then rn = rn .. string.rep("I", value) else rn = rn .. "IV" end break end end

local d if args[2] == 'ascii' then if args[3] == 'lower' then d = LA 	else d = UA 	end elseif args[2] == 'unicode' then if args[3] == 'lower' then d = LU		else d = UU		end else if args[3] == 'lower' then d = LC		else d = UC		end end if d ~= UA then for _, kv in ipairs(d) do			rn = rn:gsub(kv[1], kv[2]) end end if neg then return '-' .. rn else return rn end end

return p