Skip to content

Instantly share code, notes, and snippets.

@Mikoangelo
Created January 21, 2010 00:29
Show Gist options
  • Save Mikoangelo/282465 to your computer and use it in GitHub Desktop.
Save Mikoangelo/282465 to your computer and use it in GitHub Desktop.
NUMBERS = {
:a => 1,
:one => 1,
:two => 2,
:three => 3,
:four => 4,
:five => 5,
:six => 6,
:seven => 7,
:eight => 8,
:nine => 9,
:ten => 10,
:eleven => 11,
:twelve => 12,
:thirteen => 13,
:fourteen => 14,
:fifteen => 15,
:sixteen => 16,
:seventeen => 17,
:eighteen => 18,
:nineteen => 19,
:twenty => 20,
:thirty => 30,
:forty => 40,
:fifty => 50,
:sixty => 60,
:seventy => 70,
:eighty => 80,
:ninety => 90,
:hundred => 100,
:thousand => 1000,
:million => 1000000,
:billion => 1000000000
}
# Decimas are stuff like million, hundred, thousand.
# Also hundred thousand and hundred million.
DECIMAS = [3,6,9].map { |n| 10 ** n }
DECIMAS.concat [100] + DECIMAS.map { |n| n * 100 }
def DECIMAS.highest num
select{ |d| d <= num }.max
end
NUMBERS.each do |meth, val|
# Kernel.send :define_method instead of Kernel.define_method because it's
# private. Truth be told, the |after| should be |*after| (and after=after[0]
# afterwards) to avoid a warning about arity. The whole powernesting-of-
# single-argument-methods thing kinda ruins the concept, though ;)
Kernel.send :define_method, meth do |after = nil|
ret = if after
after, is_decima = after if Array === after # See the `ret = [ret, ...] ...` line
highest_decima = DECIMAS.highest after
# if highest_decima < val, we are just adding digits (i.e. a "two-digit" method name)
if highest_decima.nil? or highest_decima < val
val + after
else
# This comment is too small to hold my elegant explanation for this.
val * highest_decima + after - ((val < 10 or is_decima) ? highest_decima : 0)
end
else
val
end
# Here's a little hack. Since there's no way of differentiating `million`
# and `one million` (as in `twenty [one] million`), `million` returns the
# value in an array just to distinguish it. It doesn't break the use since
# decimas are never supposed to be used at the start of a number nesting
# (use `one` or `a`). The :is_decima isn't needed per se, but hey;
# shits'n'giggles!
ret = [ret, :is_decima] if DECIMAS.include? val
ret
end
end
nineteen billion eight hundred six million five hundred forty three thousand two hundred ten # => 19806543210
a thousand three # => 1003
twenty one thousand three # => 21003
twenty(thousand(three)) # => 20003
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment