# giga64's solution

## to Say in the Swift Track

Published at Jul 13 2018 · 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.

## 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".

## Setup

Go through the project setup instructions for Xcode using Swift:

http://exercism.io/languages/swift

## 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.

### LinuxMain.swift

``````import XCTest
@testable import SayTests

XCTMain([
testCase(SayTests.allTests),
])``````

### SayTests.swift

``````import XCTest
@testable import Say

class SayTests: XCTestCase {

func testZero() {
XCTAssertEqual("zero", Say.say(0))
}

func testOne() {
XCTAssertEqual("one", Say.say(1))
}

func testFourteen() {
XCTAssertEqual("fourteen", Say.say(14))
}

func testTwenty() {
XCTAssertEqual("twenty", Say.say(20))
}

func testTwentyTwo() {
XCTAssertEqual("twenty-two", Say.say(22))
}

func testOneHundred() {
XCTAssertEqual("one hundred", Say.say(100))
}

func testOneHundredTwentyThree() {
XCTAssertEqual("one hundred twenty-three", Say.say(123))
}

func testOneThousand() {
XCTAssertEqual("one thousand", Say.say(1_000))
}

func testOneThousandTwoHundredThirtyFour() {
XCTAssertEqual("one thousand two hundred thirty-four", Say.say(1_234))
}

func testOneMillion() {
XCTAssertEqual("one million", Say.say(1_000_000))
}

func testOneMillionTwoThousandThreeHundredFortyFive() {
XCTAssertEqual("one million two thousand three hundred forty-five", Say.say(1_002_345))
}

func testOneBillion() {
XCTAssertEqual("one billion", Say.say(1_000_000_000))
}

func testABigNumber() {
XCTAssertEqual("nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three", Say.say(987_654_321_123))
}

func testNumbersBelowZeroAreOutOfRange() {
XCTAssertNil(Say.say(-1))
}

func testNumbersAbove999999999999AreOutOfRange() {
XCTAssertNil(Say.say(1_000_000_000_000))
}

static var allTests: [(String, (SayTests) -> () throws -> Void)] {
return [
("testZero", testZero),
("testOne", testOne),
("testFourteen", testFourteen),
("testTwenty", testTwenty),
("testTwentyTwo", testTwentyTwo),
("testOneHundred", testOneHundred),
("testOneHundredTwentyThree", testOneHundredTwentyThree),
("testOneThousand", testOneThousand),
("testOneThousandTwoHundredThirtyFour", testOneThousandTwoHundredThirtyFour),
("testOneMillion", testOneMillion),
("testOneMillionTwoThousandThreeHundredFortyFive", testOneMillionTwoThousandThreeHundredFortyFive),
("testOneBillion", testOneBillion),
("testABigNumber", testABigNumber),
("testNumbersBelowZeroAreOutOfRange", testNumbersBelowZeroAreOutOfRange),
("testNumbersAbove999999999999AreOutOfRange", testNumbersAbove999999999999AreOutOfRange),
]
}
}``````
``````class Say {
static let NUM_STRINGS = [
0: "zero",
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",
20: "twenty",
30: "thirty",
40: "forty",
50: "fifty",
60: "sixty",
70: "seventy",
80: "eighty",
90: "ninty",
]

static let GROUP_STRINGS = ["thousand", "million", "billion"]

static func say(_ number: Int) -> String? {
if number < 0 || number >= 1_000_000_000_000 {return nil}
if number == 0 {return "zero"}

var n = number
var groupCount = -1
var words: [String] = []
while n >= 1000 {
if n % 1000 != 0 {
if groupCount >= 0 {words.insert(GROUP_STRINGS[groupCount], at: 0)}
let threeDigit = spellThreeDigit(n % 1000)
words.insert(contentsOf: threeDigit, at: 0)
}
n /= 1000
groupCount += 1
}
if groupCount >= 0 {words.insert(GROUP_STRINGS[groupCount], at: 0)}
words.insert(contentsOf: spellThreeDigit(n), at: 0)
return words.joined(separator: " ")
}

private static func spellThreeDigit(_ number: Int) -> [String] {
if number < 100 {
let word = spellBelowHundred(number)
return word == "" ? [] : [word]
}
var words = [NUM_STRINGS[number / 100]!, "hundred"]
let below = spellBelowHundred(number % 100)
if below != "" {words.append(below)}
return words
}

private static func spellBelowHundred(_ number: Int, ignoreZero ignore: Bool = true) -> String {
if number == 0 && ignore {return ""}
let lower = number % 10;
if lower == 0 || number <= 20 {
return NUM_STRINGS[number]!
} else {
var word = NUM_STRINGS[number / 10 * 10]!
word.append("-")
word.append(NUM_STRINGS[lower]!)
return word
}
}
}``````