I wrote a quick program to implement the to_en (to_english) version as
well. It supports using and and commas -- the and has a significant
effect on the lowest collated odd number returned. As the brute force
solution was rather brutish (I let it run for two nights without
reaching the correct solution), I came up with an "optimized"
solution. Below is the output and below that the code - the output is
too wide -- sorry, but the code is still properly formatted
Patrick
-------------- Output
-------------------------------------------------------------------
Using both commas and 'and'
eight billion and eighty-five
Repeat without 'and' or comma
eight billion eight hundred eight million eight hundred eight thousand
eight hundred eighty-five
-------------- Code
----------------------------------------------------------------------
class Integer
DIVISORS = [[100, ''],
[10, 'hundred'],
[1000, 'thousand'],
[1000, 'million'],
[1000, 'billion'],
[1000, 'trillion'],
[1000, 'quadrillion'],
[1000, 'quintillion'],
[1000, 'sextillion'],
[1000, 'septillion'],
[1000, 'octillion'],
[1000, 'nonillion'],
[1000, 'decillion'],
[1000, 'undecillion'],
[1000, 'duodecillion'],
[1000, 'tredecillion'],
[1000, 'quattuordecillion'],
[1000, 'quindecillion'],
[1000, 'sexdecillion'],
[1000, 'septendecillion'],
[1000, 'octodecillion'],
[1000, 'novemdecillion'],
[1000, 'vigintillion'],
[1000, 'unvigintillion'],
[1000, 'duovigintillion'],
[1000, 'trevigintillion'],
[1000, 'quattuorvigintillion'],
[1000, 'quinvigintillion'],
[1000, 'sexvigintillion'],
[1000, 'septvigintillion'],
[1000, 'octovigintillion'],
[1000, 'novemvigintillion'],
[1000, 'trigintillion'],
[1000, 'untrigintillion'],
[1000, 'duotrigintillion'],
[1000, 'tretrigintillion'],
[1000, 'quattuortrigintillion'],
[1000, 'quintrigintillion'],
[1000, 'sextrigintillion'],
[1000, 'septtrigintillion'],
[1000, 'octotrigintillion'],
[1000, 'novemtrigintillion'],
[1000, 'quadragintillion'],
[1000, 'unquadragintillion'],
[1000, 'duoquadragintillion'],
[1000, 'trequadragintillion'],
[1000, 'quattuorquadragintillion'],
[1000, 'quinquadragintillion'],
[1000, 'sexquadragintillion'],
[1000, 'septquadragintillion'],
[1000, 'octoquadragintillion'],
[1000, 'novemquadragintillion'],
[1000, 'quinquagintillion'],
[1000, 'unquinquagintillion'],
[1000, 'duoquinquagintillion'],
[1000, 'trequinquagintillion'],
[1000, 'quattuorquinquagintillion'],
[1000, 'quinquinquagintillion'],
[1000, 'sexquinquagintillion'],
[1000, 'septquinquagintillion'],
[1000, 'octoquinquagintillion'],
[1000, 'novemquinquagintillion'],
[1000, 'sexagintillion'],
[1000, 'unsexagintillion'],
[1000, 'duosexagintillion'],
[1000, 'tresexagintillion'],
[1000, 'quattuorsexagintillion'],
[1000, 'quinsexagintillion'],
[1000, 'sexsexagintillion'],
[1000, 'septsexagintillion'],
[1000, 'octosexagintillion'],
[1000, 'novemsexagintillion'],
[1000, 'septuagintillion'],
[1000, 'unseptuagintillion'],
[1000, 'duoseptuagintillion'],
[1000, 'treseptuagintillion'],
[1000, 'quattuorseptuagintillion'],
[1000, 'quinseptuagintillion'],
[1000, 'sexseptuagintillion'],
[1000, 'septseptuagintillion'],
[1000, 'octoseptuagintillion'],
[1000, 'novemseptuagintillion'],
[1000, 'octogintillion'],
[1000, 'unoctogintillion'],
[1000, 'duooctogintillion'],
[1000, 'treoctogintillion'],
[1000, 'quattuoroctogintillion'],
[1000, 'quinoctogintillion'],
[1000, 'sexoctogintillion'],
[1000, 'septoctogintillion'],
[1000, 'octooctogintillion'],
[1000, 'novemoctogintillion'],
[1000, 'nonagintillion'],
[1000, 'unnonagintillion'],
[1000, 'duononagintillion'],
[1000, 'trenonagintillion'],
[1000, 'quattuornonagintillion'],
[1000, 'quinnonagintillion'],
[1000, 'sexnonagintillion'],
[1000, 'septnonagintillion'],
[1000, 'octononagintillion'],
[1000, 'novemnonagintillion'],
[1000, 'centillion'],
]
ENGLISH_MAP = {
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',
20 => 'twenty',
30 => 'thirty',
40 => 'forty',
50 => 'fifty',
60 => 'sixty',
70 => 'seventy',
80 => 'eighty',
90 => 'ninety',
}
ENGLISH_TEXT = {
:and => 'and',
:comma => ',',
:zero => 'zero',
:minus => 'minus'
}
def Integer.insert_and?(number, result, recurse)
number > 0 &&
result.empty? &&
!recurse && !ENGLISH_TEXT[:and].empty?
end
def Integer.insert_comma?(index, result)
(index >= 2) &&
(
!ENGLISH_TEXT[:and].empty? &&
!result.empty? &&
!result[/^#{Regexp.escape(ENGLISH_TEXT[:and])}/]
) ||
(
ENGLISH_TEXT[:and].empty? &&
result[/\s\S+$/]
)
end
def Integer.to_en(number, recurse=false)
result = ''
DIVISORS.each_with_index do |(divisor, name), index|
break unless number > 0
remainder = number % divisor
number /= divisor
next if (remainder == 0)
# check for special case 1100...2000
# if (1 == number && (1..9) === remainder && index == 1 && !recurse)
# check for special case 1100...1999 skipping [2-9]0..
if ((1..9) === number && (1..9) === remainder && index == 1 && !recurse)
remainder += number * 10
number = 0
end
part = ''
case remainder
when 1..19
part = ENGLISH_MAP[remainder]
when 20..99
units = remainder % 10
part = ENGLISH_MAP[remainder - units]
part += '-' + ENGLISH_MAP[units] if (units != 0)
else
# Recurse
part = Integer.to_en(remainder, true)
end
# the following section is complicated by supporting
# commas and ands
full_part = ''
if insert_and?(number, result, recurse)
full_part += ENGLISH_TEXT[:and] + ' '
end
full_part += part
full_part += ' ' + name unless name.empty?
if insert_comma?(index, result)
full_part += ENGLISH_TEXT[:comma]
end
full_part += ' ' + result unless result.empty?
result = full_part
end
result
end
def to_en
Integer.to_en(self)
end
end
# assumptions based upon US number to words algorithm
# - the first digit of the number in question must begin with
# the lowest number name alphabetically (this is a pretty good
# prune all by itself
#
# - the number in question must be a member of the lowest
# divisor name (alphabetically)
#
# we know the number must be eight somethings
#number = EnglishNumber::ENGLISH_MAP.values.sort
#puts number.first
# ok so we must be in the eight billions
#names = EnglishNumber:

IVISORS.map { |d,name| name }
#names = names.reject { |n| n.size == 0 }
#names = names.sort
#puts names.first
# now we re-apply the same rules as above, but
# not forgetting to "odd" rule (meaning we cannot
# use eight on the final digit
# now we know the last digit will be a five
#number = EnglishNumber::ENGLISH_MAP.to_a.reject { |k,n| k % 2 == 0 }
#number = number.map { |k,name| name }
#number.sort
#puts number.first
# So putting it all together we check all entries in
# our heavily pruned tree of choices
# numbers ending in 5 and having a combination of
# 0 and 8's
def try_some_numbers
lowest = 'zzzz'
number = 5
prefix = 0
while number < 10_000_000_000
number = (prefix.to_s(2).gsub(/1/, '8') + '5').to_i
name = number.to_en
if name < lowest
lowest = name
end
prefix += 1
end
puts lowest
end
puts "Using both commas and 'and'"
try_some_numbers
puts "\nRepeat without 'and' or comma"
Integer::ENGLISH_TEXT[:comma] = ''
Integer::ENGLISH_TEXT[:and] = ''
try_some_numbers