Skip to content

Commit

Permalink
Added string_indexing.lua and words_to_numbers.lua to the distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
nickgammon committed Dec 3, 2010
1 parent 88177f4 commit 82b0097
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 0 deletions.
4 changes: 4 additions & 0 deletions install/mushclient.nsi
Expand Up @@ -364,9 +364,11 @@ SetOverwrite ifnewer
File "..\lua\serialize.lua"
File "..\lua\show_loaded.lua"
File "..\lua\strict.lua"
File "..\lua\string_indexing.lua"
File "..\lua\tprint.lua"
File "..\lua\var.lua"
File "..\lua\wait.lua"
File "..\lua\words_to_numbers.lua"
File "..\luacom\luacom5.lua"

; LuaSocket
Expand Down Expand Up @@ -579,9 +581,11 @@ Section Uninstall
Delete "$INSTDIR\lua\serialize.lua"
Delete "$INSTDIR\lua\show_loaded.lua"
Delete "$INSTDIR\lua\strict.lua"
Delete "$INSTDIR\lua\string_indexing.lua"
Delete "$INSTDIR\lua\tprint.lua"
Delete "$INSTDIR\lua\var.lua"
Delete "$INSTDIR\lua\wait.lua"
Delete "$INSTDIR\lua\words_to_numbers.lua"

Delete "$INSTDIR\lua\socket.lua"
Delete "$INSTDIR\lua\ltn12.lua"
Expand Down
24 changes: 24 additions & 0 deletions lua/string_indexing.lua
@@ -0,0 +1,24 @@
--[[
string_indexing.lua
If you install this, then you can index into strings, like this:
require "string_indexing"
a = "nick"
print (a [3]) --> c
--]]
getmetatable ("").__index = function (str, i)
if (type (i) == "number") then
return string.sub (str, i, i) -- index into str
end -- if
return string [i] -- fallback (eg. string.match)
end -- function
298 changes: 298 additions & 0 deletions lua/words_to_numbers.lua
@@ -0,0 +1,298 @@
-- Convert a number to words
-- Author: Nick Gammon
-- Date: 18th March 2010

-- See: http://www.gammon.com.au/forum/?id=10155

-- Examples of use:
-- words = convert_numbers_to_words ("94921277802687490518")
-- number = convert_words_to_numbers ("one hundred eight thousand three hundred nine")

-- Both functions return nil and an error message so you can check for failure,
-- or assert, eg. words = assert (convert_numbers_to_words ("2687490518"))

-- Units, must be in inverse order!
-- The trailing space is required as the space between words

local inverse_units = {
"vigintillion ", -- 10^63
"novemdecillion ", -- 10^60
"octodecillion ", -- 10^57
"septendecillion ", -- 10^54
"sexdecillion ", -- 10^51
"quindecillion ", -- 10^48
"quattuordecillion ",-- 10^45
"tredecillion ", -- 10^42
"duodecillion ", -- 10^39
"undecillion ", -- 10^36
"decillion ", -- 10^33
"nonillion ", -- 10^30
"octillion ", -- 10^27
"septillion ", -- 10^24
"sextillion ", -- 10^21
"quintillion ", -- 10^18
"quadrillion ", -- 10^15
"trillion ", -- 10^12
"billion ", -- 10^9
"million ", -- 10^6
"thousand ", -- 10^3
} -- inverse_units

local inverse_numbers = {
"one ",
"two ",
"three ",
"four ",
"five ",
"six ",
"seven ",
"eight ",
"nine ",
"ten ",
"eleven ",
"twelve ",
"thirteen ",
"fourteen ",
"fifteen ",
"sixteen ",
"seventeen ",
"eighteen ",
"nineteen ",
"twenty ",
[30] = "thirty ",
[40] = "forty ",
[50] = "fifty ",
[60] = "sixty ",
[70] = "seventy ",
[80] = "eighty ",
[90] = "ninety ",
} -- inverse_numbers

local function convert_up_to_999 (n)

if n <= 0 then
return ""
end -- if zero

local hundreds = math.floor (n / 100)
local tens = math.floor (n % 100)
local result = ""

-- if over 99 we need to say x hundred
if hundreds > 0 then

result = inverse_numbers [hundreds] .. "hundred "
if tens == 0 then
return result
end -- if only a digit in the hundreds column

-- to have "and" between things like "hundred and ten"
-- uncomment the next line
-- result = result .. "and "

end -- if

-- up to twenty it is then just five hundred (and) fifteen
if tens <= 20 then
return result .. inverse_numbers [tens]
end -- if

-- otherwise we need: thirty (something)
result = result .. inverse_numbers [math.floor (tens / 10) * 10]

-- get final digit (eg. thirty four)
local digits = math.floor (n % 10)

-- to put a hyphen between things like "forty-two"
-- uncomment the WITH HYPHEN line and
-- comment out the NO HYPHEN line

if digits > 0 then
result = result .. inverse_numbers [digits] -- NO HYPHEN
-- result = string.sub (result, 1, -2) .. "-" .. inverse_numbers [digits] -- WITH HYPHEN
end -- if

return result

end -- convert_up_to_999

-- convert a number to words
-- See: http://www.gammon.com.au/forum/?id=10155

function convert_numbers_to_words (n)
local s = tostring (n)

-- preliminary sanity checks
local c = string.match (s, "%D")
if c then
return nil, "Non-numeric digit '" .. c .. "' in number"
end -- if

if #s == 0 then
return nil, "No number supplied"
elseif #s > 66 then
return nil, "Number too big to convert to words"
end -- if

-- make multiple of 3
while #s % 3 > 0 do
s = "0" .. s
end -- while

local result = ""
local start = #inverse_units - (#s / 3) + 2

for i = start, #inverse_units do
local group = tonumber (string.sub (s, 1, 3))
if group > 0 then
result = result .. convert_up_to_999 (group) .. inverse_units [i]
end -- if not zero
s = string.sub (s, 4)
end -- for

result = result .. convert_up_to_999 (tonumber (s))

if result == "" then
result = "zero"
end -- if

return (string.gsub (result, " +$", "")) -- trim trailing spaces

end -- convert_numbers_to_words

-- Convert words to a number
-- Author: Nick Gammon
-- Date: 18th March 2010

-- Does NOT handle decimal places (eg. four point six)

local numbers = {
zero = bc.number (0),
one = bc.number (1),
two = bc.number (2),
three = bc.number (3),
four = bc.number (4),
five = bc.number (5),
six = bc.number (6),
seven = bc.number (7),
eight = bc.number (8),
nine = bc.number (9),
ten = bc.number (10),
eleven = bc.number (11),
twelve = bc.number (12),
thirteen = bc.number (13),
fourteen = bc.number (14),
fifteen = bc.number (15),
sixteen = bc.number (16),
seventeen = bc.number (17),
eighteen = bc.number (18),
nineteen = bc.number (19),
twenty = bc.number (20),
thirty = bc.number (30),
forty = bc.number (40),
fifty = bc.number (50),
sixty = bc.number (60),
seventy = bc.number (70),
eighty = bc.number (80),
ninety = bc.number (90),
} -- numbers

local units = {
hundred = bc.number ("100"),
thousand = bc.number ("1" .. string.rep ("0", 3)),
million = bc.number ("1" .. string.rep ("0", 6)),
billion = bc.number ("1" .. string.rep ("0", 9)),
trillion = bc.number ("1" .. string.rep ("0", 12)),
quadrillion = bc.number ("1" .. string.rep ("0", 15)),
quintillion = bc.number ("1" .. string.rep ("0", 18)),
sextillion = bc.number ("1" .. string.rep ("0", 21)),
septillion = bc.number ("1" .. string.rep ("0", 24)),
octillion = bc.number ("1" .. string.rep ("0", 27)),
nonillion = bc.number ("1" .. string.rep ("0", 30)),
decillion = bc.number ("1" .. string.rep ("0", 33)),
undecillion = bc.number ("1" .. string.rep ("0", 36)),
duodecillion = bc.number ("1" .. string.rep ("0", 39)),
tredecillion = bc.number ("1" .. string.rep ("0", 42)),
quattuordecillion = bc.number ("1" .. string.rep ("0", 45)),
quindecillion = bc.number ("1" .. string.rep ("0", 48)),
sexdecillion = bc.number ("1" .. string.rep ("0", 51)),
septendecillion = bc.number ("1" .. string.rep ("0", 54)),
octodecillion = bc.number ("1" .. string.rep ("0", 57)),
novemdecillion = bc.number ("1" .. string.rep ("0", 60)),
vigintillion = bc.number ("1" .. string.rep ("0", 63)),
} -- units

-- convert a number in words to a numeric form
-- See: http://www.gammon.com.au/forum/?id=10155
-- Thanks to David Haley

function convert_words_to_numbers (s)

local stack = {}
local previous_type

for word in string.gmatch (s:lower (), "[%a%d]+") do
if word ~= "and" then -- skip "and" (like "hundred and fifty two")
local top = #stack

-- If the current word is a number (English or numeric),
-- and the previous word was also a number, pop the previous number
-- from the stack and push the addition of the two numbers.
-- Otherwise, push the new number.

local number = tonumber (word) -- try for numeric (eg. 22 thousand)

if number then
number = bc.number (number) -- turn into "big number"
else
number = numbers [word]
end -- if a number-word "like: twenty"

if number then
if previous_type == "number" then -- eg. forty three
local previous_number = table.remove (stack, top) -- get the forty
number = number + previous_number -- add three
end -- if
table.insert (stack, number)
previous_type = "number"
else

-- If the current word is a unit, multiply the number on the top of the stack by the unit's magnitude.
local unit = units [word]
if not unit then
return nil, "Unexpected word: " .. word
end -- not unit
previous_type = "unit"

-- It is an error to get a unit before a number.

if top == 0 then
return nil, "Cannot have unit before a number: " .. word
end -- starts of with something like "thousand"

-- pop until we get something larger on the stack
local interim_result = bc.number (0)
while top > 0 and stack [top] < unit do
interim_result = interim_result + table.remove (stack, top)
top = #stack
end -- while
table.insert (stack, interim_result * unit)

end -- if number or not
end -- if 'and'

end -- for each word

if #stack == 0 then
return nil, "No number found"
end -- nothing

-- When the input has been parsed, sum all numbers on the stack.

local result = bc.number (0)
for _, item in ipairs (stack) do
result = result + item
end -- for

return result
end -- function convert_words_to_numbers

0 comments on commit 82b0097

Please sign in to comment.