Module:Roman
Appearance
Documentation for this module may be created at Module:Roman/doc
--[[
This module converts Arabic numerals into Roman numerals. It currently works for any
whole number between 0 and 4999999.
Please do not modify this code without applying the changes first at Module:Roman/sandbox and testing
at Module:Roman/sandbox/testcases and Module talk:Roman/sandbox/testcases.
Authors and maintainers:
* User:RP88
]]
local p = {}
-- =======================================
-- === Public Functions ==================
-- =======================================
--[[
Numeral
This function converts an Arabic numeral into a Roman numeral. It works for values between
0 and 4999999. The output string may contain HTML tags. Arabic numeral zero is output as
an empty string.
Usage:
{{#invoke:Roman|Numeral|<value>}}
{{#invoke:Roman|Numeral}} - uses the caller's parameters
Parameters
1: Value to convert into a Roman numeral. Must be at least 0 and less than 5,000,000.
Error Handling:
If the input does not look like it contains a number or the number is outside of the
supported range an error message is returned.
]]
function p.Numeral(frame)
-- if no argument provided than check parent template/module args
local args = frame.args
if args[1]==nil then
args = frame:getParent().args
end
return p._Numeral(args[1])
end
--[[
_Numeral
This function returns a string containing the input value formatted as a Roman numeral. It works for values between
0 and 4999999. The output string may contain HTML tags.
Parameters
input: integer or string containing value to convert into a Roman numeral
Error Handling:
If the input does not look like it contains a number or the number is outside of the
supported range an error message is returned.
]]
function p._Numeral(input)
local output = ''
if input then
local value = tonumber(input)
if value and (value >= 0) and (value < 5000000) then
output = convertArabicToRomanHTML(value)
else
output = outputError("unsupported value")
end
else
output = outputError("missing value")
end
return output
end
--[[
isRoman
Tests if the input is a valid Roman numeral. Returns true if so, false if not. For the
purposes of this function, the empty string is not a Roman numeral.
Parameters
s: string to test if it is a valid Roman numeral
Error Handling:
If the input is not a valid Roman numeral this function returns false.
]]
function p.isRoman(s)
return s and (s ~= '') and (p.toArabic(s) ~= 0)
end
--[[
toArabic
This function converts a Roman numeral into an Arabic numeral. It works for values between
0 and 4999999. The empty string is converted to zero.
Parameters
roman: string containing value to convert into an Arabic numeral
Error Handling:
If the input is not a valid Roman numeral this function returns zero.
]]
function p.toArabic(roman)
local result = 0
if roman and (type(roman)=='string') and (roman ~= '') then
roman = mw.ustring.lower(mw.text.trim(roman))
result = convertRomanHTMLToArabic(roman)
end
return result
end
-- =======================================
-- === Private Functions =================
-- =======================================
local overline_start = '<span style="text-decoration:overline;">'
local overline_end = '</span>'
--[[
This function returns a string containing the input value formatted as a Roman numeral. It works for values between
0 and 4999999. The result string may contain HTML tags.
]]
function convertArabicToRomanHTML(value)
local result = ''
if (value < 5000) then
result = convertArabicToRoman(value)
else
local low_value
if (math.floor(value) % 5000) >= 4000 then
low_value = math.floor(value) % 1000;
else
low_value = math.floor(value) % 5000;
end
local high_value = math.floor((value - low_value) / 1000)
local low_roman = convertArabicToRoman(low_value)
local high_roman = convertArabicToRoman(high_value)
result = overline_start .. high_roman .. overline_end .. low_roman
end
return result
end
--[[
This function returns a string containing the input value formatted as a Roman numeral. It works for values between
0 and 4999. The result string will be a simple alphabetic string.
]]
function convertArabicToRoman(value)
local thousands = {'', 'M', 'MM', 'MMM', 'MMMM'}
local hundreds = {'', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'}
local tens = {'', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'}
local ones = {'', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'}
local index
local result = ''
if ((value >= 0) and (value < 5000)) then
index = (math.floor(value / 1000) % 5) + 1
result = result .. thousands[index]
index = (math.floor(value / 100) % 10) + 1
result = result .. hundreds[index]
index = (math.floor(value / 10) % 10) + 1
result = result .. tens[index]
index = (math.floor(value) % 10) + 1
result = result .. ones[index]
end
return result
end
--[[
This function converts a string containing a Roman numeral to an integer. It works for values between
0 and 4999999. The input string may contain HTML tags.
]]
function convertRomanHTMLToArabic(roman)
local result = 0
if mw.ustring.find(roman, "^[mdclxvi]+$") ~= nil then
result = convertRomanToArabic(roman)
else
local overline_start_len = mw.ustring.len(overline_start)
if mw.ustring.sub(roman, 1, overline_start_len) == overline_start then
local end_tag_start, end_tag_end = mw.ustring.find(roman, overline_end, overline_start_len, true)
if end_tag_start ~= nil then
local roman_high = mw.ustring.sub(roman, overline_start_len + 1, end_tag_start - 1)
local roman_low = mw.ustring.sub(roman, end_tag_end + 1, mw.ustring.len(roman)) or ''
if (mw.ustring.find(roman_high, "^[mdclxvi]+$") ~= nil) and (mw.ustring.find(roman_low, "^[mdclxvi]*$") ~= nil) then
result = convertRomanToArabic(roman_high) * 1000 + convertRomanToArabic(roman_low)
end
end
end
end
return result
end
--[[
This function converts a string containing a Roman numeral to an integer. It works for values between
0 and 4999.
]]
function convertRomanToArabic(roman)
local romanDecimals = {m = 1000, d = 500, c = 100, l = 50, x = 10, v = 5, i = 1}
local prevRomanDecimal = 0
local result = 0
for i = mw.ustring.len(roman), 1, -1 do
local c = mw.ustring.sub(roman, i, i)
local currentRomanDecimal = romanDecimals[c]
if currentRomanDecimal == nil then
return 0
end
if prevRomanDecimal > currentRomanDecimal then
result = result - currentRomanDecimal
else
result = result + currentRomanDecimal
end
prevRomanDecimal = currentRomanDecimal
end
return result
end
--[[
Helper function to handle error messages.
]]
function outputError(error_str)
local error_str = '<strong class="error">Roman Module Error: ' .. error_str .. '</strong>';
error_str = '[[Category:Errors reported by Module Roman]]' .. error_str;
return error_str;
end
return p