Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kennethcalamay/85f1076500178c54fc76a5f8d6c61b8d to your computer and use it in GitHub Desktop.
Save kennethcalamay/85f1076500178c54fc76a5f8d6c61b8d to your computer and use it in GitHub Desktop.
Numbers to words, version 2, in Elixir
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
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
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
# run this inline suite with "elixir #{__ENV__.file} test"
if System.argv |> List.first == "test" do
ExUnit.start
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)
end)
end
test "negative" do
assert "negative one thousand one" == NumbersToWords.parse(-1001)
end
test "tens digits to words" do
assert "twelve" == NumbersToWords.parse(12)
assert "eighteen" == NumbersToWords.parse(18)
assert "twenty three" == NumbersToWords.parse(23)
assert "fifty six" == NumbersToWords.parse(56)
assert "sixty nine" == NumbersToWords.parse(69)
assert "ninety five" == NumbersToWords.parse(95)
end
test "hundreds numbers to words" do
assert "one hundred three" == NumbersToWords.parse(103)
assert "five hundred twelve" == NumbersToWords.parse(512)
assert "three hundred" == NumbersToWords.parse(300)
end
test "thousands numbers to words" do
assert "four thousand twenty three" == NumbersToWords.parse(4023)
end
test "hundred thousand numbers to words" do
assert "three hundred thousand four" == NumbersToWords.parse(300004)
end
test "quadrillion baby" do
assert "three quadrillion" == NumbersToWords.parse(3000000000000000)
assert "three quadrillion one" == NumbersToWords.parse(3000000000000001)
end
# showoff...
test "negative quintillion with interspersed digits" do
assert "negative sixty nine quintillion one billion six hundred ninety million one" == NumbersToWords.parse(-69000000001690000001)
end
test "running out of available words raises" do
assert_raise ArgumentError, "Dude. That number is too long. I don't know how to say it.", fn ->
NumbersToWords.parse(100000000000000000000000000000000000000000000000000)
end
end
test "unknown characters raise" do
assert_raise ArgumentError, "r is not an integer", fn -> NumbersToWords.parse("r") end
assert_raise ArgumentError, "1r5 is not an integer", fn -> NumbersToWords.parse("1r5") end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment