ZERO_TO_TWENTY = {
0 => nil,
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
6 => "six",
7 => "seven",
8 => "eight",
9 => "nine",
10 => "ten",
11 => "eleven",
12 => "twelve",
13 => "thirteen",
14 => "fourteen",
15 => "fifteen",
16 => "sixteen",
17 => "seventeen",
18 => "eighteen",
19 => "nineteen"
}.freeze
TENS = {
2 => "twenty",
3 => "thirty",
4 => "forty",
5 => "fifty",
6 => "sixty",
7 => "seventy",
8 => "eighty",
9 => "ninety"
}.freeze
NUMBER_SCALE = [
nil,
"thousand",
"million",
"billion",
"trillion"
].freeze
def in_english(number)
number = number.to_i
raise RangeError if number < 0 or number > 999_999_999_999
return "zero" if number.zero?
chunks = []
number, chunks[chunks.count] = number.divmod(1000) while number > 0
strings = chunks.map do |number|
string = []
hundreds, remainder = number.divmod(100)
string << "#{ZERO_TO_TWENTY[hundreds]} hundred" if hundreds > 0
tens, units = remainder < 20 ? [nil, remainder] : remainder.divmod(10)
string << [ TENS[tens], ZERO_TO_TWENTY[units] ].compact.join("-")
string.join(" ")
end
strings.zip(NUMBER_SCALE).reverse.flatten.compact.join(" ")
end
# =========
number = 1_123_456_089
p in_english(number)
WkVST19UT19UV0VOVFkgPSB7CiAgMCAgPT4gbmlsLAogIDEgID0+ICJvbmUiLAogIDIgID0+ICJ0d28iLAogIDMgID0+ICJ0aHJlZSIsCiAgNCAgPT4gImZvdXIiLAogIDUgID0+ICJmaXZlIiwKICA2ICA9PiAic2l4IiwKICA3ICA9PiAic2V2ZW4iLAogIDggID0+ICJlaWdodCIsCiAgOSAgPT4gIm5pbmUiLAogIDEwID0+ICJ0ZW4iLAogIDExID0+ICJlbGV2ZW4iLAogIDEyID0+ICJ0d2VsdmUiLAogIDEzID0+ICJ0aGlydGVlbiIsCiAgMTQgPT4gImZvdXJ0ZWVuIiwKICAxNSA9PiAiZmlmdGVlbiIsCiAgMTYgPT4gInNpeHRlZW4iLAogIDE3ID0+ICJzZXZlbnRlZW4iLAogIDE4ID0+ICJlaWdodGVlbiIsCiAgMTkgPT4gIm5pbmV0ZWVuIgp9LmZyZWV6ZQoKVEVOUyA9IHsKICAyID0+ICJ0d2VudHkiLAogIDMgPT4gInRoaXJ0eSIsCiAgNCA9PiAiZm9ydHkiLAogIDUgPT4gImZpZnR5IiwKICA2ID0+ICJzaXh0eSIsCiAgNyA9PiAic2V2ZW50eSIsCiAgOCA9PiAiZWlnaHR5IiwKICA5ID0+ICJuaW5ldHkiCn0uZnJlZXplCgpOVU1CRVJfU0NBTEUgPSBbCiAgbmlsLAogICJ0aG91c2FuZCIsCiAgIm1pbGxpb24iLAogICJiaWxsaW9uIiwKICAidHJpbGxpb24iCl0uZnJlZXplCgpkZWYgaW5fZW5nbGlzaChudW1iZXIpCiAgbnVtYmVyID0gbnVtYmVyLnRvX2kKICByYWlzZSBSYW5nZUVycm9yIGlmIG51bWJlciA8IDAgb3IgbnVtYmVyID4gOTk5Xzk5OV85OTlfOTk5CiAgcmV0dXJuICJ6ZXJvIiBpZiBudW1iZXIuemVybz8KICAKICBjaHVua3MgPSBbXQogIG51bWJlciwgY2h1bmtzW2NodW5rcy5jb3VudF0gPSBudW1iZXIuZGl2bW9kKDEwMDApIHdoaWxlIG51bWJlciA+IDAKCiAgc3RyaW5ncyA9IGNodW5rcy5tYXAgZG8gfG51bWJlcnwKICAgIHN0cmluZyA9IFtdCiAgICBodW5kcmVkcywgcmVtYWluZGVyID0gbnVtYmVyLmRpdm1vZCgxMDApCiAgICBzdHJpbmcgPDwgIiN7WkVST19UT19UV0VOVFlbaHVuZHJlZHNdfSBodW5kcmVkIiBpZiBodW5kcmVkcyA+IDAKICAKICAgIHRlbnMsIHVuaXRzID0gcmVtYWluZGVyIDwgMjAgPyBbbmlsLCByZW1haW5kZXJdIDogcmVtYWluZGVyLmRpdm1vZCgxMCkKICAgIHN0cmluZyA8PCBbIFRFTlNbdGVuc10sIFpFUk9fVE9fVFdFTlRZW3VuaXRzXSBdLmNvbXBhY3Quam9pbigiLSIpCiAgCiAgICBzdHJpbmcuam9pbigiICIpCiAgZW5kCiAgCiAgc3RyaW5ncy56aXAoTlVNQkVSX1NDQUxFKS5yZXZlcnNlLmZsYXR0ZW4uY29tcGFjdC5qb2luKCIgIikKZW5kCgojID09PT09PT09PQoKbnVtYmVyID0gMV8xMjNfNDU2XzA4OQpwIGluX2VuZ2xpc2gobnVtYmVyKQ==