defmodule Say do @in_english_19 %{ 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" } @in_english_100 %{ 2 => "twenty", 3 => "thirty", 4 => "forty", 5 => "fifty", 6 => "sixty", 7 => "seventy", 8 => "eighty", 9 => "ninety" } @doc """ Translate a positive integer into English. """ @spec in_english(integer) :: {atom, String.t()} def in_english(number) when number < 0 or number > 999_999_999_999, do: {:error, "number is out of range"} def in_english(0), do: {:ok, "zero"} def in_english(number) do {:ok, number |> Integer.digits() |> Enum.reverse() |> Enum.chunk_every(3) |> Enum.with_index(fn element, index -> in_english_part(element, index) end) |> Enum.reverse() |> List.flatten() |> Enum.reject(&is_nil/1) |> Enum.join(" ") } end @spec in_english_part([integer], integer) :: String.t() defp in_english_part([0, 0, 0], _index), do: [] defp in_english_part(element, 3), do: [do_in_english_part(element) | ["billion"]] defp in_english_part(element, 2), do: [do_in_english_part(element) | ["million"]] defp in_english_part(element, 1), do: [do_in_english_part(element) | ["thousand"]] defp in_english_part(element, 0), do: [do_in_english_part(element)] defp do_in_english_part([d1, d2, d3]) when d3 != 0, do: [@in_english_19[d3], "hundred" | [do_in_english_part([d1, d2])]] defp do_in_english_part([d1, d2, 0]), do: do_in_english_part([d1, d2]) defp do_in_english_part([d1, d2]) when d2 < 2, do: @in_english_19[Integer.undigits([d2, d1])] defp do_in_english_part([0, d2]) when d2 > 1, do: @in_english_100[d2] defp do_in_english_part([d1, d2]) when d2 > 1, do: @in_english_100[d2] <> "-" <> @in_english_19[d1] defp do_in_english_part([d1]), do: @in_english_19[d1] end