 # rapidfireworks's solution

## to Say in the Elixir Track

Published at Jun 09 2020 · 0 comments
Instructions
Test suite
Solution

Given a number from 0 to 999,999,999,999, spell out that number in English.

## Step 1

Handle the basic case of 0 through 99.

If the input to the program is `22`, then the output should be `'twenty-two'`.

Your program should complain loudly if given a number outside the blessed range.

Some good test cases for this program are:

• 0
• 14
• 50
• 98
• -1
• 100

### Extension

If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud. If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`.

## Step 2

Implement breaking a number up into chunks of thousands.

So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0.

The program must also report any values that are out of range.

## Step 3

Now handle inserting the appropriate scale word between those chunks.

So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`

The program must also report any values that are out of range. It's fine to stop at "trillion".

## Step 4

Put it all together to get nothing but plain English.

`12345` should give `twelve thousand three hundred forty-five`.

The program must also report any values that are out of range.

### Extensions

Use and (correctly) when spelling out the number in English:

• 14 becomes "fourteen".
• 100 becomes "one hundred".
• 120 becomes "one hundred and twenty".
• 1002 becomes "one thousand and two".
• 1323 becomes "one thousand three hundred and twenty-three".

## Running tests

Execute the tests with:

``````\$ mix test
``````

### Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

## Source

A variation on JavaRanch CattleDrive, exercise 4a http://www.javaranch.com/say.jsp

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### say_test.exs

``````defmodule SayTest do
use ExUnit.Case

# @tag :pending
test "zero" do
assert Say.in_english(0) == {:ok, "zero"}
end

@tag :pending
test "one" do
assert Say.in_english(1) == {:ok, "one"}
end

@tag :pending
test "fourteen" do
assert Say.in_english(14) == {:ok, "fourteen"}
end

@tag :pending
test "twenty" do
assert Say.in_english(20) == {:ok, "twenty"}
end

@tag :pending
test "twenty-two" do
assert Say.in_english(22) == {:ok, "twenty-two"}
end

@tag :pending
test "one hundred" do
assert Say.in_english(100) == {:ok, "one hundred"}
end

@tag :pending
test "one hundred twenty-three" do
assert Say.in_english(123) == {:ok, "one hundred twenty-three"}
end

@tag :pending
test "one thousand" do
assert Say.in_english(1_000) == {:ok, "one thousand"}
end

@tag :pending
test "one thousand two hundred thirty-four" do
assert Say.in_english(1_234) == {:ok, "one thousand two hundred thirty-four"}
end

@tag :pending
test "one million" do
assert Say.in_english(1_000_000) == {:ok, "one million"}
end

@tag :pending
test "one million two thousand three hundred forty-five" do
assert Say.in_english(1_002_345) == {:ok, "one million two thousand three hundred forty-five"}
end

@tag :pending
test "one billion" do
assert Say.in_english(1_000_000_000) == {:ok, "one billion"}
end

@tag :pending
test "a big number" do
assert Say.in_english(987_654_321_123) ==
{:ok,
"nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three"}
end

@tag :pending
test "numbers below zero are out of range" do
assert Say.in_english(-1) == {:error, "number is out of range"}
end

@tag :pending
test "numbers above 999,999,999,999 are out of range" do
assert Say.in_english(1_000_000_000_000) == {:error, "number is out of range"}
end
end``````

### test_helper.exs

``````ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)``````
``````defmodule Say do
@doc """
Translate a positive integer into English.
"""
@spec in_english(integer) :: {atom, String.t()}
def in_english(number) when number in 0..999_999_999_999, do: {:ok, say(number)}
def in_english(_number), do: {:error, "number is out of range"}

defp say(number), do: say(number, 0) |> Enum.join()

defp say(number, exponent) do
quotient = div(number, 1000)
remainder = rem(number, 1000)

case {quotient > 0, remainder > 0} do
{true, true} -> [say(quotient, exponent + 1), " ", convert(remainder), unit(exponent)]
{true, false} -> [say(quotient, exponent + 1)]
_ -> [convert(remainder), unit(exponent)]
end
end

defp convert(number) do
quotient = div(number, 100)
remainder = rem(number, 100)

convert(quotient, div(remainder, 10), rem(remainder, 10))
end

defp convert(0, 0, 0), do: "zero"
defp convert(0, 0, 1), do: "one"
defp convert(0, 0, 2), do: "two"
defp convert(0, 0, 3), do: "three"
defp convert(0, 0, 4), do: "four"
defp convert(0, 0, 5), do: "five"
defp convert(0, 0, 6), do: "six"
defp convert(0, 0, 7), do: "seven"
defp convert(0, 0, 8), do: "eight"
defp convert(0, 0, 9), do: "nine"
defp convert(0, 1, 0), do: "ten"
defp convert(0, 1, 1), do: "eleven"
defp convert(0, 1, 2), do: "twelve"
defp convert(0, 1, 3), do: "thirteen"
defp convert(0, 1, 4), do: "fourteen"
defp convert(0, 1, 5), do: "fifteen"
defp convert(0, 1, 6), do: "sixteen"
defp convert(0, 1, 7), do: "seventeen"
defp convert(0, 1, 8), do: "eighteen"
defp convert(0, 1, 9), do: "ninteen"
defp convert(0, 2, 0), do: "twenty"
defp convert(0, 3, 0), do: "thirty"
defp convert(0, 4, 0), do: "forty"
defp convert(0, 5, 0), do: "fifty"
defp convert(0, 6, 0), do: "sixty"
defp convert(0, 7, 0), do: "seventy"
defp convert(0, 8, 0), do: "eighty"
defp convert(0, 9, 0), do: "ninty"
defp convert(0, t, o), do: [convert(0, t, 0), "-", convert(0, 0, o)]
defp convert(h, 0, 0), do: [convert(0, 0, h), " hundred"]
defp convert(h, t, o), do: [convert(0, 0, h), " hundred ", convert(0, t, o)]

defp unit(0), do: ""
defp unit(1), do: " thousand"
defp unit(2), do: " million"
defp unit(3), do: " billion"
defp unit(4), do: " trillion"
end``````