This module provides conversion to and from foreign numerals to native numerical values. It's partially redundant with Module:roman numerals.
Numeral system | "From" function | "To" function |
---|---|---|
Roman numerals | from_Roman
|
to_Roman
|
Armenian numerals | from_Armenian
|
— |
Hebrew numerals | from_Hebrew
|
to_Hebrew
|
Indian numerals | from_Indian
|
to_Indian
|
Each function may be called either from a template (see below) or from a module. "From" functions return native numerical values (of 'number'
type), while "to" functions return strings.
Example:
{{#invoke:foreign numerals|to_Roman|2764}}
gives
MMDCCLXIV
And the reverse function:
{{#invoke:foreign numerals|from_Roman|MMDCCLXIV}}
gives
2764
This module serves as a backend to the templates {{R2A}}
and {{A2R}}
.
local u = require("Module:string utilities").char
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 = u(0x305)
local double_overline = u(0x33F)
-- Add overline and double-overline numerals. The table needs to be in reverse order, so do double overline first.
for _, v in ipairs(to_Roman_tab) do
local number, symbol = unpack(v)
if number > 1 then
table.insert(new_to_Roman_tab, { number * 1000000, (symbol:gsub(".", "%1" .. double_overline)) })
end
end
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)) })
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)
else
-- accept strings for use by ], which is invoked from ]
-- with the number in string format to allow for very large numbers
numeral = tonumber(numeral)
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 cp in mw.ustring.gcodepoint(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
-- Indian numerals
function export.from_Indian(numeral)
if type(numeral) == 'table' then
value = numeral.args
else
value = numeral
end
text = mw.ustring.gsub(
tostring(value),
'.',
{
= '0',
= '1',
= '2',
= '3',
= '4',
= '5',
= '6',
= '7',
= '8',
= '9',
}
)
return text
end
function export.to_Indian(numeral)
if type(numeral) == 'table' then
value = numeral.args
else
value = numeral
end
text = mw.ustring.gsub(
tostring(value),
'.',
{
= '०',
= '१',
= '२',
= '३',
= '४',
= '५',
= '६',
= '७',
= '८',
= '९',
}
)
return text
end
return export