มอดูล:Roman
เอกสารการใช้งานสำหรับมอดูลนี้อาจสร้างขึ้นที่ มอดูล:Roman/doc
require('strict')
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
local function error_message(message)
message = message or 'Error in [[Module:Roman]]'
local cat
local page = mw.title.getCurrentTitle()
local demoPages = {
['Roman'] = true,
['Saferoman'] = true,
['Roman-to-arabic'] = true
}
if (page.nsText == 'Template' or page.nsText == 'Module') and demoPages[page.rootText] then
cat = ''
else
cat = '[[Category:Errors reported by Module Roman]]'
end
return require('Module:Error')['error']({['message'] = message .. cat})
end
-- Roman-to-Arabic
local function _toArabic(roman)
-- strip non-roman numerals and convert to uppercase for parsing
roman = string.gsub(roman, '[^IVXLCDMivxlcdm]', ''):upper()
local numHash = { ["M"] = 1000,
["D"] = 500, ["C"] = 100,
["L"] = 50, ["X"] = 10,
["V"] = 5, ["I"] = 1 }
local total = 0
local i = 1
local strlen = roman:len()
-- Get last char separately. If i >= strlen, this loop's i+1 is out of bounds.
while i < strlen do
local thisChar = numHash[string.sub(roman, i, i)]
local nextChar = numHash[string.sub(roman, i + 1, i + 1)]
-- e.g. IX is 10 minus 1, and XL is 50 minus 10
if thisChar < nextChar then
total = total + ( nextChar - thisChar )
i = i + 2 -- consumed 2 (this + next)
else
total = total + thisChar
i = i + 1 -- consumed 1
end
end
-- leftover from i, i+1 loop above
if i <= strlen then
total = total + numHash[string.sub(roman, i, i)]
end
return total
end
function p._toArabic(args)
local numeral = args[1]
if numeral == nil then
return error_message('Error in [[Module:Roman]]: first parameter is nil')
else
return _toArabic(numeral)
end
end
function p.toArabic(frame)
return p._toArabic(getArgs(frame))
end
-- Arabic-to-Roman
local function _toRoman(arabic, lc, usej)
local arabic_digits = {
[3] = math.floor(arabic/1000),
[2] = math.floor((arabic % 1000)/100),
[1] = math.floor((arabic % 100)/10),
[0] = math.floor(arabic % 10)
}
local roman_units = {
[1] = 'I',
[5] = 'V',
[10] = 'X',
[50] = 'L',
[100] = 'C',
[500] = 'D',
[1000] = 'M',
[5000] = 'V',
[10000] = 'X'
}
local function roman_digit(digit, place)
local digit_values = {
[0] = '',
[1] = roman_units[place],
[2] = roman_units[place] .. roman_units[place],
[3] = roman_units[place] .. roman_units[place] .. roman_units[place],
[4] = roman_units[place] .. roman_units[5 * place],
[5] = roman_units[5 * place],
[6] = roman_units[5 * place] .. roman_units[place],
[7] = roman_units[5 * place] .. roman_units[place] .. roman_units[place],
[8] = roman_units[5 * place] .. roman_units[place] .. roman_units[place] .. roman_units[place],
[9] = roman_units[place] .. roman_units[10 * place]
}
return digit_values[digit]
end
local roman_digits = {
roman_digit(arabic_digits[3], 10^3),
roman_digit(arabic_digits[2], 10^2),
roman_digit(arabic_digits[1], 10^1),
roman_digit(arabic_digits[0], 10^0)
}
local roman = table.concat(roman_digits)
if lc then
roman = string.lower(roman)
if usej and string.sub(roman, -1) == 'i' then
roman = string.sub(roman, 1, -2) .. 'j'
end
end
return roman
end
function p._toRoman(args)
local numeral = tonumber(args[1])
if args[1] == nil then
return error_message('Error in [[Module:Roman]]: first parameter is nil')
elseif numeral == nil then
return error_message('Error in [[Module:Roman]]: ' .. args[1] .. ' is not a number')
elseif numeral <= 0 or numeral >= 4000 or numeral ~= math.floor(numeral) then
return error_message('Error in [[Module:Roman]]: ' .. numeral .. ' is not an integer in the range 1–3999')
end
local lc = yesno(args.lc) or false
local usej = yesno(args.usej) or false
return _toRoman(numeral, lc, usej)
end
function p.toRoman(frame)
return p._toRoman(getArgs(frame))
end
-- Saferoman
local function substring_is_integer(str, i, j)
i = tonumber(i)
j = tonumber(j)
if not str or not i or not j or i ~= math.floor(i) or j ~= math.floor(j) then
return nil
end
local n = tonumber(string.sub(str, i, j))
return n and n == math.floor(n)
end
function p._saferoman(args)
if args == nil or args[1] == nil then
return ''
end
local lc = args.lc
local usej = args.usej
local numstr = args[1]
local i = 1
local num
local vin
local ret = ''
while (i <= string.len(numstr)) do
-- If char is a number, get substring that's a number and convert to roman. Otherwise, move on.
if substring_is_integer(numstr, i, i) then
local j = i
while j <= string.len(numstr) and substring_is_integer(numstr, i, j) do
j = j + 1
end
num = tonumber(string.sub(numstr, i, j - 1))
if num == 0 then
ret = ret .. 'N'
elseif num > 3999 then
ret = ret .. '<span style="text-decoration:overline;">'
vin = math.floor(num / 1000)
num = (num - (vin * 1000))
while (vin > 3000) do
ret = ret .. 'M'
vin = vin - 1000
end
ret = ret .. _toRoman(vin, lc, usej)
ret = ret .. '</span>'
end
if num > 0 then
ret = ret .. _toRoman(num, lc, usej)
end
i = j
else
ret = ret .. string.sub(numstr, i, i)
i = i + 1
end
end
return ret
end
function p.saferoman(frame)
return p._saferoman(getArgs(frame))
end
return p