# Module:Roman

-- This module implements .  

local p = {}  

-- This function implements the template.  
local function overline(s)  
return mw.ustring.format( '<span>%s</span>', s )  
end  

-- Gets the Roman numerals for a given numeral table. Returns both the string of  
-- numerals and the value of the number after it is finished being processed.  
local function getLetters(num, t)  
local ret = {}  
for _, v in ipairs(t) do  
local val, letter = unpack(v)  
while num >= val do  
num = num - val  
table.insert(ret, letter)  
end  
end  

return table.concat(ret), num  
end  

-- The main control flow of the module.  
local function _main(args)  
-- Get input and exit displaying nothing if the input is empty.  
if args[1] == nil then return end  
local num = tonumber(args[1])  
if not num or num < 0 or num == math.huge then  
error('Invalid number ' .. args[1], 2)  
elseif num == 0 then  
return 'N'  
end  

-- Return a message for numbers too big to be expressed in Roman numerals.  
if num >= 5000000 then  
return args[2] or 'N/A'  
end  

local ret = ''  
-- Find the Roman numerals for the large part of numbers.  
-- 23 April 2016 - tweaked to >= 4000 to accept big Roman 'IV'  
-- The if statement is not strictly necessary, but makes the algorithm  
-- more efficient for smaller numbers.  
if num >= 4000 then  
local bigRomans = {  
{ 1000000, 'M' },  
{ 900000, 'CM' }, { 500000, 'D' }, { 400000, 'CD' }, { 100000, 'C' },  
{ 90000, 'XC' }, { 50000, 'L' }, { 40000, 'XL' }, { 10000, 'X' },  
{ 9000, 'IX' }, { 5000, 'V' }, { 4000, 'IV' },  
}  
local bigLetters  
bigLetters, num = getLetters(num, bigRomans)  
ret = overline(bigLetters)  
end  

-- Find the Roman numerals for numbers less than the big Roman threshold.  
local smallRomans = {  
{ 1000, 'M' },  
{ 900, 'CM' }, { 500, 'D' }, { 400, 'CD' }, { 100, 'C' },  
{ 90, 'XC' }, { 50, 'L' }, { 40, 'XL' }, { 10, 'X' },  
{ 9, 'IX' }, { 5, 'V' }, { 4, 'IV' }, { 1, 'I' }  
}  
local smallLetters = getLetters( num, smallRomans )  
ret = ret .. smallLetters  

if args.fraction == 'yes' then  
-- Find the Roman numerals for the fractional parts of numbers.  
-- If num is not a whole number, add half of 1/1728 (the smallest unit) to equate to rounding.  
-- Ensure we're not less than the smallest unit or larger than 1 - smallest unit  
-- to avoid getting two "half" symbols or no symbols at all  
num = num - math.floor(num)  
if num ~= 0 then  
num = math.max(1.1/1728, math.min(1727.1/1728, num + 1/3456))  
end  
local fractionalRomans = {  
{ 1/2, 'S' }, { 5/12, "**:**•**:**" }, { 1/3, "**::**" },  
{ 1/4, "**:**•" }, { 1/6, "**:**" }, { 1/12, '•' },  
{ 1/24, 'Є' }, { 1/36, 'ƧƧ' }, { 1/48, 'Ɔ' }, { 1/72, 'Ƨ' }, { 1/144, '
Ƨ
' },  
{ 1/288, '℈' }, { 1/1728, '»' },  
}  
local fractionalLetters = getLetters(num, fractionalRomans)  

ret = ret .. fractionalLetters  
end  

return ret  
end  

function p.main  
-- If called via #invoke, use the args passed into the invoking  
-- template, or the args passed to #invoke if any exist. Otherwise  
-- assume args are being passed directly in from the debug console  
-- or from another Lua module.  
local origArgs  
if == mw.getCurrentFrame then  
origArgs = :getParent.args  
for k, v in pairs(.args) do  
origArgs = .args  
break  
end  
else  
origArgs =  
end  
-- Trim whitespace and remove blank arguments.  
local args = {}  
for k, v in pairs(origArgs) do  
if type( v ) == 'string' then  
v = mw.text.trim(v)  
end  
if v ~= '' then  
args[k] = v  
end  
end  

-- exit if not given anything  
if args == nil or args == {} then return end  
-- Given mathematical expression, simplify to a number  
if type(args[1]) == 'string' then  
args[1] = mw.ext.ParserFunctions.expr(args[1])  
end  
return _main(args)  
end  

return p