Module:Roman numeral

From para.wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Roman numeral/doc

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", "ⅰ"}, {"V", "ⅴ"}, {"X", "ⅹ"},
	{"L", "ⅼ"}, {"C", "ⅽ"}, {"D", "ⅾ"},
	{"M", "ⅿ"}
}
local UU = {
	-- Upper Unicode
	{"I", "Ⅰ"}, {"V", "Ⅴ"}, {"X", "Ⅹ"},
	{"L", "Ⅼ"}, {"C", "Ⅽ"}, {"D", "Ⅾ"},
	{"M", "Ⅿ"}
}

local LC = {
	-- Lower Combining
	{"VIII", "ⅷ"}, {"III", "ⅲ"},
	{"XII", "ⅻ"}, {"VII", "ⅶ"}, {"II", "ⅱ"},
	{"IV", "ⅳ"}, {"IX", "ⅸ"},
	{"VI", "ⅵ"}, {"XI", "ⅺ"},
	
	{"I", "ⅰ"}, {"V", "ⅴ"}, {"X", "ⅹ"}, 
	{"L", "ⅼ"}, {"C", "ⅽ"}, {"D", "ⅾ"},
	{"M", "ⅿ"}
}
local UC = {
	-- Upper Combining
	{"VIII", "Ⅷ"}, {"III", "Ⅲ"},
	{"XII", "Ⅻ"}, {"VII", "Ⅶ"}, {"II", "Ⅱ"},
	{"IV", "Ⅳ"}, {"IX",	"Ⅸ"},
	{"VI", "Ⅵ"}, {"XI", "Ⅺ"},
	
	{"I", "Ⅰ"}, {"V", "Ⅴ"}, {"X", "Ⅹ"},
	{"L", "Ⅼ"}, {"C", "Ⅽ"}, {"D", "Ⅾ"},
	{"M", "Ⅿ"}
}

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