-
-
Save kennethcalamay/85f1076500178c54fc76a5f8d6c61b8d to your computer and use it in GitHub Desktop.
Numbers to words, version 2, in Elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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