Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added string_indexing.lua and words_to_numbers.lua to the distribution
- Loading branch information
1 parent
88177f4
commit 82b0097
Showing
3 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |