what it says in the title, might work with number-extension systems if
- they use metamethods
- tostring gives the full number and not a abbreviation
local module = {}
function numberInRange(n, mn, mx)
return (n>=mn) and (n<=mx)
end
function splitStringEveryN(str, n)
local result = {}
local len = #str
for i = 1, len, n do
table.insert(result, string.sub(str, i, i + n - 1))
end
return result
end
function reverseTable(t)
local n = #t
for i = 1, math.floor(n / 2) do
t[i], t[n - i + 1] = t[n - i + 1], t[i]
end
return t
end
function singleDigitToWord(n)
local numbers = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"}
if n == 0 then
return "zero"
elseif n < 0 then
n = math.abs(n)
return "Negative "..numbers[n]
else
return numbers[n]
end
end
function doubleDigitToWord(n)
local isNegative = (n<0)
local n = math.abs(n)
local check = tostring(n)
if string.len(check) == 1 then
return singleDigitToWord(n)
end
local tenCases = {"Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}
local otherNumbers = {"", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}
local FirstDigit = tonumber(string.sub(tostring(n), 1, 1))
local SecondDigit = tonumber(string.sub(tostring(n), 2, 2))
if isNegative then
for i, v in ipairs(tenCases) do
tenCases[i] = "Negative "..v
end
for i, v in ipairs(otherNumbers) do
otherNumbers[i] = "Negative "..v
end
end
if FirstDigit == 1 then
-- special case for ten to make the rest of the code cleaner
if SecondDigit == 0 then
if isNegative then
return "Negative ten"
else
return "ten"
end
else
return tenCases[SecondDigit]
end
else
if SecondDigit == 0 then
return otherNumbers[FirstDigit]
else
return otherNumbers[FirstDigit].."-"..singleDigitToWord(SecondDigit)
end
end
end
function tripleDigitToWord(n)
local isNegative = (n<0)
n = math.abs(n)
local FirstDigit = tonumber(string.sub(tostring(n), 1, 1))
local TheRest = tonumber(string.sub(tostring(n), 2, 3))
local finalResult = singleDigitToWord(FirstDigit).." Hundred"
if TheRest ~= 0 then
finalResult = finalResult.." "..doubleDigitToWord(TheRest)
end
if isNegative then
finalResult = "Negative "..finalResult
end
return finalResult
end
function aboveTripleDigitsToWord(n, stringmode)
-- infinitely extendable! probably also works with any system that expands numbers in lua, as long as they use metamethods
local largeNumbers = {"", "Thousand", "Million", "Billion", "Trillion", "Quadrillion", "Quintillion", "Sextillion", "Septillion", "Octillio", "Nonillion", "Decillion", "Undecillion", "Duodecillion", "Tredecillion", "Quattuordecillion", "Quindecillion", "Sexdecillion", "Septendecillion", "Octodecillion", "Novemdecillion", "Vigintillion", "Unvigintillion"}
local isNegative
if stringmode then
isNegative = (string.sub(n, 1, 1)=="-")
if isNegative then
n = string.sub(n, 2, string.len(n))
end
else
isNegative = (n<0)
n = tostring(math.abs(n))
end
local NLength = string.len(n)
local howmanytoadd = (NLength%3)
if howmanytoadd == 1 then
howmanytoadd = howmanytoadd+1
elseif howmanytoadd == 2 then
howmanytoadd = howmanytoadd-1
end
-- make the amount of digits in the number divisible by 3
for i = 1, howmanytoadd do
n = "0"..n
end
NLength = string.len(n)
local NSpilt = reverseTable(splitStringEveryN(n, 3))
local finalTable = {}
for i, v in ipairs(NSpilt) do
v = tonumber(v)
local result = belowThreeDigitsToWord(v)
if result ~= 0 then
if i == 1 then
table.insert(finalTable, result)
else
result = result.." "..largeNumbers[i]
table.insert(finalTable, result)
end
end
end
finalTable = reverseTable(finalTable)
local returnal = table.concat(finalTable, " ")
if stringmode and isNegative then
returnal = "Negative "..returnal
end
return returnal
end
function module:numberToWord(n, titlecase)
local stringmode = false
if type(n) == "string" then
stringmode = true
end
local returnal = nil
-- math.floor but for... strings :scream:
if stringmode then
local decimalplace = string.find(n, ".")
n = string.sub(n, 1, decimalplace-1)
else
n = math.floor(n)
end
local NLength = string.len(tostring(n))
if stringmode then
if string.sub(n, 1, 1) == "-" then
NLength -= 1
end
end
if NLength <= 3 and n ~= math.huge then
returnal = belowThreeDigitsToWord(n)
elseif numberInRange(NLength, 4, 67) and n ~= math.huge then
returnal = aboveTripleDigitsToWord(n, stringmode)
else
return "NUMBER TOO BIG"
end
if not titlecase then
returnal = string.lower(returnal)
end
return returnal
end
return module