-- Module for converting between different representations of numbers. See talk page for user documentation.
-- For unit tests see: Module:ConvertNumeric/testcases
-- When editing, preview with: Module_talk:ConvertNumeric/testcases
-- First, edit Module:ConvertNumeric/sandbox, then preview with Module_talk:ConvertNumeric/sandbox/testcases  
local ones_position = {
[0] = 'சுழியம்',
[1] = 'ஒன்று',
[2] = 'இரண்டு',
[3] = 'மூன்று',
[4] = 'நான்கு',
[5] = 'ஐந்து',
[6] = 'ஆறு',
[7] = 'ஏழு',
[8] = 'எட்டு',
[9] = 'ஒன்பது',
[10] = 'பத்து',
[11] = 'eleven',
[12] = 'twelve',
[13] = 'thirteen',
[14] = 'fourteen',
[15] = 'fifteen',
[16] = 'sixteen',
[17] = 'seventeen',
[18] = 'eighteen',
[19] = 'nineteen'
}  
local ones_position_ord = {
[0] = 'zeroth',
[1] = 'முதலாம்',
[2] = 'இரண்டாம்',
[3] = 'மூன்றாம்',
[4] = 'நான்காம்',
[5] = 'ஐந்தாம்',
[6] = 'ஆறாம்',
[7] = 'ஏழாம்',
[8] = 'எட்டாம்',
[9] = 'ஒன்பதாம்',
[10] = 'பத்தாம்',
[11] = 'பதினோராம்',
[12] = 'பன்னிரண்டாம்',
[13] = 'பதின்மூன்றாம்',
[14] = 'பதினான்காம்',
[15] = 'பதினைந்தாம்',
[16] = 'பதினாறாம்',
[17] = 'பதினேழாம்',
[18] = 'பதினெட்டாம்',
[19] = 'பத்தொன்பதாம்'
}  
local ones_position_plural = {
[0] = 'zeros',
[1] = 'ones',
[2] = 'twos',
[3] = 'threes',
[4] = 'fours',
[5] = 'fives',
[6] = 'sixes',
[7] = 'sevens',
[8] = 'eights',
[9] = 'nines',
[10] = 'tens',
[11] = 'elevens',
[12] = 'twelves',
[13] = 'thirteens',
[14] = 'fourteens',
[15] = 'fifteens',
[16] = 'sixteens',
[17] = 'seventeens',
[18] = 'eighteens',
[19] = 'nineteens'
}  
local tens_position = {
[2] = 'twenty',
[3] = 'thirty',
[4] = 'forty',
[5] = 'fifty',
[6] = 'sixty',
[7] = 'seventy',
[8] = 'eighty',
[9] = 'ninety'
}  
local tens_position_ord = {
[2] = 'twentieth',
[3] = 'thirtieth',
[4] = 'fortieth',
[5] = 'fiftieth',
[6] = 'sixtieth',
[7] = 'seventieth',
[8] = 'eightieth',
[9] = 'ninetieth'
}  
local tens_position_plural = {
[2] = 'twenties',
[3] = 'thirties',
[4] = 'forties',
[5] = 'fifties',
[6] = 'sixties',
[7] = 'seventies',
[8] = 'eighties',
[9] = 'nineties'
}  
local groups = {
[1] = 'thousand',
[2] = 'million',
[3] = 'billion',
[4] = 'trillion',
[5] = 'quadrillion',
[6] = 'quintillion',
[7] = 'sextillion',
[8] = 'septillion',
[9] = 'octillion',
[10] = 'nonillion',
[11] = 'decillion',
[12] = 'undecillion',
[13] = 'duodecillion',
[14] = 'tredecillion',
[15] = 'quattuordecillion',
[16] = 'quindecillion',
[17] = 'sexdecillion',
[18] = 'septendecillion',
[19] = 'octodecillion',
[20] = 'novemdecillion',
[21] = 'vigintillion',
[22] = 'unvigintillion',
[23] = 'duovigintillion',
[24] = 'tresvigintillion',
[25] = 'quattuorvigintillion',
[26] = 'quinquavigintillion',
[27] = 'sesvigintillion',
[28] = 'septemvigintillion',
[29] = 'octovigintillion',
[30] = 'novemvigintillion',
[31] = 'trigintillion',
[32] = 'untrigintillion',
[33] = 'duotrigintillion',
[34] = 'trestrigintillion',
[35] = 'quattuortrigintillion',
[36] = 'quinquatrigintillion',
[37] = 'sestrigintillion',
[38] = 'septentrigintillion',
[39] = 'octotrigintillion',
[40] = 'noventrigintillion',
[41] = 'quadragintillion',
[51] = 'quinquagintillion',
[61] = 'sexagintillion',
[71] = 'septuagintillion',
[81] = 'octogintillion',
[91] = 'nonagintillion',
[101] = 'centillion',
[102] = 'uncentillion',
[103] = 'duocentillion',
[104] = 'trescentillion',
[111] = 'decicentillion',
[112] = 'undecicentillion',
[121] = 'viginticentillion',
[122] = 'unviginticentillion',
[131] = 'trigintacentillion',
[141] = 'quadragintacentillion',
[151] = 'quinquagintacentillion',
[161] = 'sexagintacentillion',
[171] = 'septuagintacentillion',
[181] = 'octogintacentillion',
[191] = 'nonagintacentillion',
[201] = 'ducentillion',
[301] = 'trecentillion',
[401] = 'quadringentillion',
[501] = 'quingentillion',
[601] = 'sescentillion',
[701] = 'septingentillion',
[801] = 'octingentillion',
[901] = 'nongentillion',
[1001] = 'millinillion',
}  
local roman_numerals = {
I = 1,
V = 5,
X = 10,
L = 50,
C = 100,
D = 500,
M = 1000
}  
local engord_tens_end = {
['twentieth'] = 20,
['thirtieth'] = 30,
['fortieth'] = 40,
['fiftieth'] = 50,
['sixtieth'] = 60,
['seventieth'] = 70,
['eightieth'] = 80,
['ninetieth'] = 90,
}  
local eng_tens_cont = {
['twenty'] = 20,
['thirty'] = 30,
['forty'] = 40,
['fifty'] = 50,
['sixty'] = 60,
['seventy'] = 70,
['eighty'] = 80,
['ninety'] = 90,
}  
-- Converts a given valid roman numeral (and some invalid roman numerals) to a number. Returns { -1, errorstring } on error.
local function roman_to_numeral(roman)
if type(roman) ~= "string" then return -1, "roman numeral not a string" end
local rev = roman:reverse
local raising = true
local last = 0
local result = 0
for i = 1, #rev do
local c = rev:sub(i, i)
local next = roman_numerals[c]
if next == nil then return -1, "roman numeral contains illegal character " .. c end
if next > last then
result = result + next
raising = true
elseif next < last then
result = result - next
raising = false
elseif raising then
result = result + next
else
result = result - next
end
last = next
end
return result
end  
-- Converts a given integer between 0 and 100 to English text (e.g. 47 -> forty-seven).
local function numeral_to_english_less_100(num, ordinal, plural, zero)
local terminal_ones, terminal_tens
if ordinal then
terminal_ones = ones_position_ord
terminal_tens = tens_position_ord
elseif plural then
terminal_ones = ones_position_plural
terminal_tens = tens_position_plural
else
terminal_ones = ones_position
terminal_tens = tens_position
end  
if num == 0 and zero ~= nil then
return zero
elseif num < 20 then
return terminal_ones[num]
elseif num % 10 == 0 then
return terminal_tens[num / 10]
else
return tens_position[math.floor(num / 10)] .. '-' .. terminal_ones[num % 10]
end
end  
local function standard_suffix(ordinal, plural)
if ordinal then return 'th' end
if plural then return 's' end
return ''
end  
-- Converts a given integer (in string form) between 0 and 1000 to English text (e.g. 47 -> forty-seven).
local function numeral_to_english_less_1000(num, use_and, ordinal, plural, zero)
num = tonumber(num)
if num < 100 then
return numeral_to_english_less_100(num, ordinal, plural, zero)
elseif num % 100 == 0 then
return ones_position[num/100] .. ' hundred' .. standard_suffix(ordinal, plural)
else
return ones_position[math.floor(num/100)] .. ' hundred ' .. (use_and and 'and ' or '') .. numeral_to_english_less_100(num % 100, ordinal, plural, zero)
end
end  
-- Converts an ordinal in English text from 'zeroth' to 'ninety-ninth' inclusive to a number [0–99], else -1.
local function english_to_ordinal(english)
local eng = string.lower(english or '')  
local engord_lt20 = {} -- ones_position_ord{} keys & values swapped
for k, v in pairs( ones_position_ord ) do
engord_lt20[v] = k
end  
if engord_lt20[eng] then
return engord_lt20[eng] -- e.g. first -> 1
elseif engord_tens_end[eng] then
return engord_tens_end[eng] -- e.g. ninetieth -> 90
else
local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$')
if tens and ones then
local tens_cont = eng_tens_cont[tens]
local ones_end = engord_lt20[ones]
if tens_cont and ones_end then
return tens_cont + ones_end -- e.g. ninety-ninth -> 99
end
end
end
return -1 -- Failed
end  
-- Converts a number in English text from 'zero' to 'ninety-nine' inclusive to a number [0–99], else -1.
local function english_to_numeral(english)
local eng = string.lower(english or '')  
local eng_lt20 = { ['single'] = 1 } -- ones_position{} keys & values swapped
for k, v in pairs( ones_position ) do
eng_lt20[v] = k
end  
if eng_lt20[eng] then
return eng_lt20[eng] -- e.g. one -> 1
elseif eng_tens_cont[eng] then
return eng_tens_cont[eng] -- e.g. ninety -> 90
else
local tens, ones = string.match(eng, '^([a-z]+)[%s%-]+([a-z]+)$')
if tens and ones then
local tens_cont = eng_tens_cont[tens]
local ones_end = eng_lt20[ones]
if tens_cont and ones_end then
return tens_cont + ones_end -- e.g. ninety-nine -> 99
end
end
end
return -1 -- Failed
end  
-- Converts a number expressed as a string in scientific notation to a string in standard decimal notation