local export = {}
-- Roman numerals
local from_Roman_tab = {
M = 1000; CM = 900; D = 500; CD = 400;
C = 100; XC = 90; L = 50; XL = 40;
X = 10; IX = 9; V = 5; IV = 4;
I = 1;
}
local to_Roman_tab = {
{ 1000, "M" }; { 900, "CM" }; { 500, "D" }; { 400, "CD" };
{ 100, "C" }; { 90, "XC" }; { 50, "L" }; { 40, "XL" };
{ 10, "X" }; { 9, "IX" }; { 5, "V" }; { 4, "IV" };
{ 1, "I" };
}
local new_to_Roman_tab = {}
local overline = mw.ustring.char(0x305)
local double_overline = mw.ustring.char(0x33F)
for _, v in ipairs(to_Roman_tab) do
local number, symbol = unpack(v)
if number > 1 then
table.insert(new_to_Roman_tab, { number * 1000, (symbol:gsub(".", "%1" .. overline)) })
table.insert(new_to_Roman_tab, { number * 1000000, (symbol:gsub(".", "%1" .. double_overline)) })
end
end
for _, v in ipairs(to_Roman_tab) do
table.insert(new_to_Roman_tab, v)
end
to_Roman_tab = new_to_Roman_tab
function export.to_Roman(numeral)
if type(numeral) == 'table' then
numeral = tonumber(numeral.args)
end
local output = {}
for _, item in ipairs(to_Roman_tab) do
local limit, letter = item, item
while numeral >= limit do
table.insert(output, letter)
numeral = numeral - limit
end
end
return table.concat(output)
end
function export.from_Roman(numeral)
if type(numeral) == 'table' then
numeral = numeral.args
end
if tonumber(numeral) then
return tonumber(numeral)
end
local accum = 0
-- shame on Lua for having no regex alternations...
while numeral ~= "" do
local l2, l1 = numeral:sub(1, 2), numeral:sub(1, 1)
if from_Roman_tab then
accum = accum + from_Roman_tab
numeral = numeral:sub(3)
elseif from_Roman_tab then
accum = accum + from_Roman_tab
numeral = numeral:sub(2)
else
return nil
end
end
return accum
end
-- Armenian numerals
local from_Armenian_tab = {
= 1; = 2; = 3; = 4; = 5; = 6; = 7; = 8; = 9;
= 10; = 20; = 30; = 40; = 50; = 60; = 70; = 80; = 90;
= 100; = 200; = 300; = 400; = 500; = 600; = 700; = 800; = 900;
= 1000; = 2000; = 3000; = 4000; = 5000; = 6000; = 7000; = 8000; = 9000;
}
function export.from_Armenian(numeral)
if type(numeral) == 'table' then
numeral = numeral.args
end
if tonumber(numeral) then
return tonumber(numeral)
end
local accum = 0
for char in mw.ustring.gmatch(numeral, ".") do
local value = from_Armenian_tab
if value then
accum = accum + value
else
return nil
end
end
return accum
end
-- Hebrew numerals
local from_Hebrew_tab = {
= 1,
= 2,
= 3,
= 4,
= 5,
= 6,
= 7,
= 8,
= 9,
= 10,
= 20,
= 20,
= 30,
= 40,
= 40,
= 50,
= 50,
= 60,
= 70,
= 80,
= 80,
= 90,
= 90,
= 100,
= 200,
= 300,
= 400,
}
local to_Hebrew_ones = { = '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט'}
local to_Hebrew_tens = { = '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ'}
local to_Hebrew_hundreds = { = '', 'ק', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק'}
local to_Hebrew_special = {
= 'טו',
= 'טז',
}
-- This only works for numbers such that 0 < value < 1000, because beyond that the logic gets complicated
function export.from_Hebrew(numeral)
if type(numeral) == 'table' then
numeral = numeral.args
end
if tonumber(numeral) then
return tonumber(numeral)
end
local value = 0
for c in mw.ustring.gmatch(numeral, '') do
value = value + (from_Hebrew_tab or 0)
end
if value == 0 then
return nil
end
return value
end
-- This only works for numbers such that 0 < value < 1000, because beyond that the logic gets complicated
function export.to_Hebrew(value, use_gershayim)
if type(value) == 'table' then
use_gershayim = value.args ~= '' and value.args
value = value.args
end
if type(value) ~= 'number' then
if tonumber(value) then
value = tonumber(value)
else
return nil
end
end
if value <= 0 or value >= 1000 then
return nil
end
local tens_and_ones = value % 100
local hundreds = to_Hebrew_hundreds
if to_Hebrew_special then
tens_and_ones = to_Hebrew_special
else
local ones = tens_and_ones % 10
local tens = (tens_and_ones - ones) / 10
tens_and_ones = to_Hebrew_tens .. to_Hebrew_ones
end
local numeral = hundreds .. tens_and_ones
if use_gershayim then
if mw.ustring.match(numeral, '^.$') then
numeral = numeral .. '׳'
else
numeral = mw.ustring.gsub(numeral, '.$', '״%0')
end
end
return numeral
end
-- Decimal numerals
-- Generated from the names of code points with General Category Nd
-- and name ending in DIGIT ZERO
-- in http://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
-- by removing the DIGIT ZERO suffix, lowercasing, replacing a hyphen or space
-- with an underscore, and replacing DIGIT ZERO with latin:
--[[
local lines = {}
for hex, name in unicode_data:gmatch "%f(+);(+);Nd" do
local code_point = tonumber(hex, 16)
local script_name
if name == "DIGIT ZERO" then
script_name = "latin"
else
script_name = name:gsub(" DIGIT ZERO$", ""):lower():gsub("", "_")
end
table.insert(lines, "\t" .. script_name .. ' = "' .. mw.ustring.char(code_point) .. '",')
end
table.sort(lines)
print(table.concat(lines, "\n"))
--]]
local decimal_zero_characters = {
adlam = "𞥐",
ahom = "𑜰",
arabic_indic = "٠",
balinese = "᭐",
bengali = "০",
bhaiksuki = "𑱐",
brahmi = "𑁦",
chakma = "𑄶",
cham = "꩐",
devanagari = "०",
dives_akuru = "𑥐",
extended_arabic_indic = "۰",
fullwidth = "0",
gujarati = "૦",
gunjala_gondi = "𑶠",
gurmukhi = "੦",
hanifi_rohingya = "𐴰",
javanese = "꧐",
kannada = "೦",
kayah_li = "꤀",
khmer = "០",
khudawadi = "𑋰",
lao = "໐",
latin = "0",
lepcha = "᱀",
limbu = "᥆",
malayalam = "൦",
masaram_gondi = "𑵐",
mathematical_bold = "𝟎",
mathematical_double_struck = "𝟘",
mathematical_monospace = "𝟶",
mathematical_sans_serif = "𝟢",
mathematical_sans_serif_bold = "𝟬",
meetei_mayek = "꯰",
modi = "𑙐",
mongolian = "᠐",
mro = "𖩠",
myanmar = "၀",
myanmar_shan = "႐",
myanmar_tai_laing = "꧰",
new_tai_lue = "᧐",
newa = "𑑐",
nko = "߀",
nyiakeng_puachue_hmong = "𞅀",
ol_chiki = "᱐",
oriya = "୦",
osmanya = "𐒠",
pahawh_hmong = "𖭐",
saurashtra = "꣐",
segmented = "🯰",
sharada = "𑇐",
sinhala_lith = "෦",
sora_sompeng = "𑃰",
sundanese = "᮰",
tai_tham_hora = "᪀",
tai_tham_tham = "᪐",
takri = "𑛀",
tamil = "௦",
telugu = "౦",
thai = "๐",
tibetan = "༠",
tirhuta = "𑓐",
vai = "꘠",
wancho = "𞋰",
warang_citi = "𑣠",
}
function export.convert_decimal(from, to, number)
if type(from) == "table" then
local frame = from
local args = assert(frame.args)
from, to, number = args, args, args
end
assert(from and to and number)
local from_zero, to_zero =
assert(decimal_zero_characters),
assert(decimal_zero_characters)
local get_code = mw.ustring.codepoint
local get_char = mw.ustring.char
local from_zero_code, to_zero_code = get_code(from_zero), get_code(to_zero)
local converted = mw.ustring.gsub(
number,
"",
function(digit)
return get_char(get_code(digit) - from_zero_code + to_zero_code)
end)
return converted
end
-- Indian numerals
function export.from_Indian(numeral)
if type(numeral) == 'table' then
value = numeral.args
else
value = numeral
end
return export.convert_decimal("devanagari", "latin", value)
end
function export.to_Indian(numeral)
if type(numeral) == 'table' then
value = numeral.args
else
value = numeral
end
return export.convert_decimal("latin", "devanagari", numeral)
end
return export