ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# martinsvalin's solution

## to Say in the Elixir Track

Published at Jul 13 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
@max 1_000_000_000_000

@doc """
Translate a positive integer into English.
"""
@spec in_english(integer) :: {atom, String.t()}
def in_english(n) when n in 0..(@max - 1), do: {:ok, say(n)}
def in_english(_), do: {:error, "number is out of range"}

defp say(0), do: "zero"
defp say(1), do: "one"
defp say(2), do: "two"
defp say(3), do: "three"
defp say(4), do: "four"
defp say(5), do: "five"
defp say(6), do: "six"
defp say(7), do: "seven"
defp say(8), do: "eight"
defp say(9), do: "nine"
defp say(10), do: "ten"
defp say(11), do: "eleven"
defp say(12), do: "twelve"
defp say(13), do: "thirteen"
defp say(14), do: "fourteen"
defp say(15), do: "fifteen"
defp say(16), do: "sixteen"
defp say(17), do: "seventeen"
defp say(18), do: "eighteen"
defp say(19), do: "nineteen"
defp say(20), do: "twenty"
defp say(30), do: "thirty"
defp say(40), do: "forty"
defp say(50), do: "fifty"
defp say(60), do: "sixty"
defp say(70), do: "seventy"
defp say(80), do: "eighty"
defp say(90), do: "ninety"

defp say(n) when n < 100 do
say(n - rem(n, 10)) <> "-" <> say(rem(n, 10))
end

@hundred 100
@thousand 1000
@million 1_000_000
@billion 1_000_000_000
defp say(n) when n < @thousand, do: big(n, @hundred, " hundred")
defp say(n) when n < @million, do: big(n, @thousand, " thousand")
defp say(n) when n < @billion, do: big(n, @million, " million")
defp say(n) when n < @max, do: big(n, @billion, " billion")

defp big(n, unit, string) do
case rem(n, unit) do
0 -> say(div(n, unit)) <> string
small -> say(n - small) <> " " <> say(small)
end
end
end``````