 # numbers to words
 
+# Original implementation:
+
+# defmodule NumbersToWords do
+
+#   @places ~w[ thousand million billion trillion quadrillion quintillion sextillion septillion octillion nonillion decillion ]
+
+#   # the default entry point, when the argument is an int and not a binary string
+#   def parse(int) when is_integer(int) do
+#     parse(Integer.to_string(int)) |> String.replace(~r/\s+/, " ") |> String.rstrip
+#   end
+
+#   # nought case
+#   def parse(""), do: ""
+
+#   # negative integer case
+#   def parse(<<?-, the_rest::binary>>), do: "negative " <> parse(the_rest)
+
+#   # ones
+#   def parse("0"), do: "zero"
+#   def parse("1"), do: "one"
+#   def parse("2"), do: "two"
+#   def parse("3"), do: "three"
+#   def parse("4"), do: "four"
+#   def parse("5"), do: "five"
+#   def parse("6"), do: "six"
+#   def parse("7"), do: "seven"
+#   def parse("8"), do: "eight"
+#   def parse("9"), do: "nine"
+
+#   # tens
+#   def parse("00"), do: ""
+#   def parse("10"), do: "ten"
+#   def parse("11"), do: "eleven"
+#   def parse("12"), do: "twelve"
+#   def parse("13"), do: "thirteen"
+#   def parse("14"), do: "fourteen"
+#   def parse("15"), do: "fifteen"
+#   def parse("16"), do: "sixteen"
+#   def parse("17"), do: "seventeen"
+#   def parse("18"), do: "eighteen"
+#   def parse("19"), do: "nineteen"
+#   def parse("20"), do: "twenty"
+
+#   # tens, finally with a pattern
+#   def parse(<<?2, n::utf8>>), do: "twenty #{parse(<<n>>)}"
+#   def parse("30"), do: "thirty"
+#   def parse(<<?3, n::utf8>>), do: "thirty #{parse(<<n>>)}"
+#   def parse("40"), do: "forty"
+#   def parse(<<?4, n::utf8>>), do: "forty #{parse(<<n>>)}"
+#   def parse("50"), do: "fifty"
+#   def parse(<<?5, n::utf8>>), do: "fifty #{parse(<<n>>)}"
+#   def parse("60"), do: "sixty"
+#   def parse(<<?6, n::utf8>>), do: "sixty #{parse(<<n>>)}"
+#   def parse("70"), do: "seventy"
+#   def parse(<<?7, n::utf8>>), do: "seventy #{parse(<<n>>)}"
+#   def parse("80"), do: "eighty"
+#   def parse(<<?8, n::utf8>>), do: "eighty #{parse(<<n>>)}"
+#   def parse("90"), do: "ninety"
+#   def parse(<<?9, n::utf8>>), do: "ninety #{parse(<<n>>)}"
+
+#   # tens with a leading zero... drop it
+#   def parse(<<?0, n::utf8>>), do: parse(<<n>>)
+
+#   # hundreds with a leading zero... drop it
+#   def parse(<<?0, tens::utf8, ones::utf8>>), do: parse(<<tens, ones>>)
+
+#   # regular hundreds
+#   def parse(<<hundreds::utf8, tens::utf8, ones::utf8>>), do: "#{parse(<<hundreds>>)} hundred #{parse(<<tens, ones>>)}"
+
+#   #### Thousands and up. now we get fancy ####
+
+#   # General case for splitting up digits 4 or longer, start an "every third digit" state and pass back
+#   # Since all binaries fall through to here that also haven't already matched, we have to validate too
+#   def parse(<<long_binary::binary>>) when is_binary(long_binary) do
+#     if validate?(long_binary) do
+#       parse(<<long_binary::binary>>, @places)
+#     else
+#       raise ArgumentError, message: "Unknown digit(s): #{long_binary}"
+#     end
+#   end
+
+#   # End case where we run out of words. Oops.
+#   def parse(_, []) do
+#     raise ArgumentError, message: "Dude. That number is too long. I don't know how to say it."
+#   end
+
+#   # End cases (length is 3 or less). no more to parse, drop "every third digit" state, fallback to digit-trio parsing
+#   def parse(<<ones::utf8>>, _), do: parse(<<ones::utf8>>)
+#   def parse(<<tens::utf8, ones::utf8>>, _), do: parse(<<tens::utf8, ones::utf8>>)
+#   def parse(<<hundreds::utf8, tens::utf8, ones::utf8>>, _), do: parse(<<hundreds::utf8, tens::utf8, ones::utf8>>)
+
+#   # thousands and up here... recursive call... this is most of the magic
+#   def parse(<<long_binary::binary>>, [illion | rest]) do
+#     { further_digits, these_digits } = String.split_at(long_binary, -3)
+#     if String.match?(further_digits, ~r/0{3}$/) do
+#       # we have to skip the trio join word if the next trio of parseable digits to the left is all zeroes, basically
+#       # Otherwise you'd get stuff like "six trillion billion million thousand"
+#       parse(<<further_digits::binary>>, rest) <> parse(<<these_digits::binary>>)
+#     else
+#       parse(<<further_digits::binary>>, rest) <> " #{illion} " <> parse(<<these_digits::binary>>)
+#     end
+#   end
+
+#   # numeric string validation
+#   defp validate?(str) do
+#     String.match?(str, ~r/^-?[0-9]+$/)
+#   end
+
+# end
+
+# New implementation using divs and mods:
+
+defmodule MathThatShouldHaveBeenInStockElixirAlready do
+  def int_pow10(num, 0), do: num
+  def int_pow10(num, pow) when pow > 0, do: int_pow10(10 * num, pow - 1)
+end
+
 defmodule NumbersToWords do
 
-  @places ~w[ thousand million billion trillion quadrillion quintillion sextillion septillion octillion nonillion decillion ]
-
-  # the default entry point, when the argument is an int and not a binary string
-  def parse(int) when is_integer(int) do
-    parse(Integer.to_string(int)) |> String.replace("  ", " ") |> String.rstrip
+  alias MathThatShouldHaveBeenInStockElixirAlready, as: Math
+
+  def parse(0), do: "zero"
+  def parse(number) when is_integer(number) do
+    to_word(number)
+    |> List.flatten
+    |> Enum.filter(&(&1))
+    |> Enum.join(" ")
   end
-
-  # nought case
-  def parse(""), do: ""
-
-  # negative integer case
-  def parse(<<?-, the_rest::binary>>), do: "negative " <> parse(the_rest)
-
-  # ones
-  def parse("0"), do: "zero"
-  def parse("1"), do: "one"
-  def parse("2"), do: "two"
-  def parse("3"), do: "three"
-  def parse("4"), do: "four"
-  def parse("5"), do: "five"
-  def parse("6"), do: "six"
-  def parse("7"), do: "seven"
-  def parse("8"), do: "eight"
-  def parse("9"), do: "nine"
-
-  # tens
-  def parse("00"), do: ""
-  def parse("10"), do: "ten"
-  def parse("11"), do: "eleven"
-  def parse("12"), do: "twelve"
-  def parse("13"), do: "thirteen"
-  def parse("14"), do: "fourteen"
-  def parse("15"), do: "fifteen"
-  def parse("16"), do: "sixteen"
-  def parse("17"), do: "seventeen"
-  def parse("18"), do: "eighteen"
-  def parse("19"), do: "nineteen"
-  def parse("20"), do: "twenty"
-
-  # tens, finally with a pattern
-  def parse(<<?2, n::utf8>>), do: "twenty #{parse(<<n>>)}"
-  def parse("30"), do: "thirty"
-  def parse(<<?3, n::utf8>>), do: "thirty #{parse(<<n>>)}"
-  def parse("40"), do: "forty"
-  def parse(<<?4, n::utf8>>), do: "forty #{parse(<<n>>)}"
-  def parse("50"), do: "fifty"
-  def parse(<<?5, n::utf8>>), do: "fifty #{parse(<<n>>)}"
-  def parse("60"), do: "sixty"
-  def parse(<<?6, n::utf8>>), do: "sixty #{parse(<<n>>)}"
-  def parse("70"), do: "seventy"
-  def parse(<<?7, n::utf8>>), do: "seventy #{parse(<<n>>)}"
-  def parse("80"), do: "eighty"
-  def parse(<<?8, n::utf8>>), do: "eighty #{parse(<<n>>)}"
-  def parse("90"), do: "ninety"
-  def parse(<<?9, n::utf8>>), do: "ninety #{parse(<<n>>)}"
-
-  # tens with a leading zero... drop it
-  def parse(<<?0, n::utf8>>), do: parse(<<n>>)
-
-  # hundreds with a leading zero... drop it
-  def parse(<<?0, tens::utf8, ones::utf8>>), do: parse(<<tens, ones>>)
-
-  # regular hundreds
-  def parse(<<hundreds::utf8, tens::utf8, ones::utf8>>), do: "#{parse(<<hundreds>>)} hundred #{parse(<<tens, ones>>)}"
-
-  #### Thousands and up. now we get fancy ####
-
-  # General case for splitting up digits 4 or longer, start an "every third digit" state and pass back
-  # Since all binaries fall through to here that also haven't already matched, we have to validate too
-  def parse(<<long_binary::binary>>) when is_binary(long_binary) do
-    if validate?(long_binary) do
-      parse(<<long_binary::binary>>, @places)
-    else
-      raise ArgumentError, message: "Unknown digit(s): #{long_binary}"
-    end
-  end
-
-  # End case where we run out of words. Oops.
-  def parse(_, []) do
-    raise ArgumentError, message: "Dude. That number is too long. I don't know how to say it."
-  end
-
-  # End cases (length is 3 or less). no more to parse, drop "every third digit" state, fallback to digit-trio parsing
-  def parse(<<ones::utf8>>, _), do: parse(<<ones::utf8>>)
-  def parse(<<tens::utf8, ones::utf8>>, _), do: parse(<<tens::utf8, ones::utf8>>)
-  def parse(<<hundreds::utf8, tens::utf8, ones::utf8>>, _), do: parse(<<hundreds::utf8, tens::utf8, ones::utf8>>)
-
-  # thousands and up here... recursive call... this is most of the magic
-  def parse(<<long_binary::binary>>, [illion | rest]) do
-    { further_digits, these_digits } = String.split_at(long_binary, -3)
-    if String.match?(further_digits, ~r/0{3}$/) do
-      # we have to skip the trio join word if the next trio of parseable digits to the left is all zeroes, basically
-      # Otherwise you'd get stuff like "six trillion billion million thousand"
-      parse(<<further_digits::binary>>, rest) <> parse(<<these_digits::binary>>)
-    else
-      parse(<<further_digits::binary>>, rest) <> " #{illion} " <> parse(<<these_digits::binary>>)
-    end
-  end
-
-  # numeric string validation
-  defp validate?(str) do
-    String.match?(str, ~r/^-?[0-9]+$/)
-  end
-
+  def parse(unknown), do: raise(ArgumentError, message: "#{unknown} is not an integer")
+
+  defp to_word(0),  do: [nil]
+  defp to_word(1),  do: ["one"]
+  defp to_word(2),  do: ["two"]
+  defp to_word(3),  do: ["three"]
+  defp to_word(4),  do: ["four"]
+  defp to_word(5),  do: ["five"]
+  defp to_word(6),  do: ["six"]
+  defp to_word(7),  do: ["seven"]
+  defp to_word(8),  do: ["eight"]
+  defp to_word(9),  do: ["nine"]
+  defp to_word(10), do: ["ten"]
+  defp to_word(11), do: ["eleven"]
+  defp to_word(12), do: ["twelve"]
+  defp to_word(13), do: ["thirteen"]
+  defp to_word(14), do: ["fourteen"]
+  defp to_word(15), do: ["fifteen"]
+  defp to_word(16), do: ["sixteen"]
+  defp to_word(17), do: ["seventeen"]
+  defp to_word(18), do: ["eighteen"]
+  defp to_word(19), do: ["nineteen"]
+  defp to_word(20), do: ["twenty"]
+  defp to_word(30), do: ["thirty"]
+  defp to_word(40), do: ["forty"]
+  defp to_word(50), do: ["fifty"]
+  defp to_word(60), do: ["sixty"]
+  defp to_word(70), do: ["seventy"]
+  defp to_word(80), do: ["eighty"]
+  defp to_word(90), do: ["ninety"]
+  defp to_word(n) when n < 0, do: ["negative", to_word(-n)]
+  defp to_word(n) when n < 100, do: [to_word(div(n,10)*10), to_word(rem(n, 10))]
+  defp to_word(n) when n < 1_000, do: [to_word(div(n,100)), "hundred", to_word(rem(n, 100))]
+  ~w[ thousand million billion trillion quadrillion quintillion sextillion septillion octillion nonillion decillion ]
+  |> Enum.zip(2..13)
+  |> Enum.each(
+    fn {illion, factor} ->
+      defp to_word(n) when n < unquote(Math.int_pow10(1,factor*3)) do
+        [to_word(div(n,unquote(Math.int_pow10(1,(factor-1)*3)))), unquote(illion), to_word(rem(n,unquote(Math.int_pow10(1,(factor-1)*3))))]
+      end
+    end
+  )
+
+  defp to_word(_), do: raise(ArgumentError, message: "Dude. That number is too long. I don't know how to say it.")
 end
 
 

    end

    test "unknown characters raise" do
      assert_raise ArgumentError, "Unknown digit(s): r", fn -> NumbersToWords.parse("r") end
      assert_raise ArgumentError, "Unknown digit(s): r5", fn -> NumbersToWords.parse("1r5") end
    end

  end

  defmodule NumbersToWordsTest do
    use ExUnit.Case, async: true

    test "digits to words" do
      %{
        zero: 0, 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
      }
      |>
      Enum.each(fn {word, num} ->
        assert to_string(word) == NumbersToWords.parse(num)
