Module:Roman

-- 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.

local p = {}

local convertRomanToArabic, convertRomanHTMLToArabic, convertArabicToRoman, convertArabicToRomanHTML, outputError -- ======================================= -- === 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:

- 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 = ' ' local overline_end = ' '

-- 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 = ' Roman Module Error: ' .. error_str .. ' '   error_str = '' .. error_str return error_str end

return p