Open main menu

Module:roman numerals

All tests passed. (refresh)

Text Expected Actual
test_arabic_to_roman:
Passed 1 I I
Passed 2 II II
Passed 3 III III
Passed 4 IV IV
Passed 5 V V
Passed 6 VI VI
Passed 7 VII VII
Passed 8 VIII VIII
Passed 9 IX IX
Passed 10 X X
Passed 944 CMXLIV CMXLIV
Passed 2848 MMDCCCXLVIII MMDCCCXLVIII
Passed 3999 MMMCMXCIX MMMCMXCIX
Text Expected Actual
test_roman_to_arabic:
Passed I 1 1
Passed II 2 2
Passed III 3 3
Passed IV 4 4
Passed V 5 5
Passed VI 6 6
Passed VII 7 7
Passed VIII 8 8
Passed IX 9 9
Passed X 10 10
Passed MCCXLI 1241 1241
Passed CMXCIX 999 999
Passed MMMCMXCIX 3999 3999

local roman_dict = {M = 1000, D = 500, C = 100, L = 50, X = 10, V = 5, I = 1}
local repeatable = {M = true, C = true, X = true, I = true}
local roman_table =  {'M',  'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'}
local arabic_table = {1000, 900,  500, 400,  100, 90,   50,  40,   10,  9,    5,   4,    1  }

local export = {}

local function nil_or_error(msg, no_error)
	if no_error then
		return nil
	else
		error(msg, 2)
	end
end

function export.arabic_to_roman(arabic, no_error, to_lower)
	if arabic == '' or arabic == nil then return nil end
	
	arabic = mw.ustring.gsub(arabic, ",", "")
	
	local j = tonumber(arabic)
	
	if j == nil then return nil end
	
	if j <= 0 or j > 3999 then
		return nil_or_error("Out of valid range", no_error)
	end

	local result = {}
	local a, count
	
	for i, r in ipairs(roman_table) do
		a = arabic_table[i]
		count = math.floor(j / a)
		table.insert(result, string.rep(r, count))
		j = j % a
		if j <= 0 then break end
	end
	
	local out = table.concat(result)
	if to_lower then out = mw.ustring.lower(out) end
	
	return out
end

function export.roman_to_arabic(roman, no_error)
	if roman == '' or roman == nil then
		return nil
	else
		roman = mw.ustring.upper(roman)
	end
	
	if not mw.ustring.match(roman, "^[MDCLXVI]+$") then
		return nil_or_error("Illegal Roman numeral format", no_error)
	end
	
	local result = 0
	
	local i = 1
	local s1, s2, c2
	local length = #roman
	
	while i <= length do
		s1 = roman_dict[roman:sub(i, i)]
		if s1 == nil then
			return nil_or_error("Unrecognized character in input", no_error)
		elseif (i + 1) <= length then
			c2 = roman:sub(i + 1, i + 1)
			s2 = roman_dict[c2]
			if s2 == nil then 
				return nil_or_error("Unrecognized character in input", no_error)
			elseif s1 >= s2 then
				if s1 == s2 and not repeatable[c2] then
					return nil_or_error("Illegal Roman numeral format: “" .. c2 .. "” may not appear adjacent to itself", no_error)
				end
				result = result + s1
			else
				result = result + s2 - s1
				i = i + 1
			end
		else
			result = result + s1
		end
		i = i + 1
	end
	
	return result
	
end

function export.roman_to_arabic_t(frame)
	local params = {
		[1] = {},
		['no_error'] = {type = 'boolean'},
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	
	return export.roman_to_arabic(args[1], args.no_error)
end

function export.arabic_to_roman_t(frame)
	local params = {
		[1] = {},
		['lower'] = {type = 'boolean'},
		['no_error'] = {type = 'boolean'},
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	
	return export.arabic_to_roman(args[1], args.no_error, args.lower)
end

return export