Skip to content

Commit 110501c

Browse files
authored
implement say exercise (exercism#1769) fix exercism#1752
1 parent bef52bb commit 110501c

File tree

8 files changed

+350
-0
lines changed

8 files changed

+350
-0
lines changed

config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,16 @@
271271
"strings"
272272
]
273273
},
274+
{
275+
"slug": "say",
276+
"uuid": "3c76a983-e689-4d82-8f1b-6d52f3c5434c",
277+
"core": false,
278+
"unlocked_by": "difference-of-squares",
279+
"difficulty": 3,
280+
"topics": [
281+
"integers"
282+
]
283+
},
274284
{
275285
"slug": "micro-blog",
276286
"uuid": "8295ae71-5c0e-49d0-bbe9-9b43a85bf2dd",
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import java.util.ArrayList;
2+
import java.util.Arrays;
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
6+
public class Say {
7+
8+
private static List<Magnitude> supportedMagnitudes = Arrays.asList(
9+
Magnitude.BILLIONS, Magnitude.MILLIONS,
10+
Magnitude.THOUSANDS, Magnitude.ONES);
11+
12+
private enum Magnitude {
13+
TRILLIONS(1_000_000_000_000L, "trillion"),
14+
BILLIONS(1_000_000_000, "billion"),
15+
MILLIONS(1_000_000, "million"),
16+
THOUSANDS(1_000, "thousand"),
17+
ONES(1, "one");
18+
19+
private long number;
20+
private String inEnglish;
21+
22+
private Magnitude(long number, String inEnglish) {
23+
this.number = number;
24+
this.inEnglish = inEnglish;
25+
}
26+
27+
public long getNumber() {
28+
return number;
29+
}
30+
31+
public String inEnglish() {
32+
return inEnglish;
33+
}
34+
}
35+
36+
public String say(long number) {
37+
if (number < 0 || number >= Magnitude.TRILLIONS.getNumber()) {
38+
throw new IllegalArgumentException("input out of range");
39+
}
40+
List<Part> parts = new ArrayList<Say.Part>();
41+
for (Magnitude magnitude: supportedMagnitudes) {
42+
double quoziente = Math.floor(number / magnitude.getNumber());
43+
Part part = new Part((int) quoziente, magnitude);
44+
if (part.getDigits() > 0 || (part.getMagnitude().equals(Magnitude.ONES) && parts.size() == 0)) {
45+
parts.add(part);
46+
}
47+
number = number % magnitude.getNumber();
48+
}
49+
50+
return String.join(" ", parts.stream().map(part -> part.say()).collect(Collectors.toList()));
51+
}
52+
53+
private static class Part {
54+
private int digits;
55+
private Magnitude magnitude;
56+
57+
public Part(int digits, Magnitude magnitude) {
58+
this.digits = digits;
59+
this.magnitude = magnitude;
60+
}
61+
62+
private static String[] untilTwenty = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eigth",
63+
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "senventeen", "eithteen",
64+
"nineteen" };
65+
66+
public int getDigits() {
67+
return digits;
68+
}
69+
70+
public Magnitude getMagnitude() {
71+
return magnitude;
72+
}
73+
74+
private String convertTens(long digit) {
75+
switch ((int) digit) {
76+
case 2:
77+
return "twenty";
78+
case 3:
79+
return "thirty";
80+
case 4:
81+
return "forty";
82+
case 5:
83+
return "fifty";
84+
case 6:
85+
return "sixty";
86+
case 7:
87+
return "seventy";
88+
case 8:
89+
return "eighty";
90+
case 9:
91+
return "ninety";
92+
default:
93+
throw new IllegalArgumentException("not supported value");
94+
}
95+
}
96+
97+
private String say() {
98+
StringBuffer stringBuffer = new StringBuffer();
99+
int hundreds = (int) digits / 100;
100+
int tens = (int) digits % 100;
101+
if (hundreds > 0) {
102+
stringBuffer.append(untilTwenty[hundreds]).append(" hundred");
103+
}
104+
105+
if (stringBuffer.length() > 0) {
106+
stringBuffer.append(" ");
107+
}
108+
109+
if (tens >= 20) {
110+
stringBuffer.append(convertTens(tens / 10));
111+
if (tens % 10 > 0) {
112+
stringBuffer.append("-").append(untilTwenty[tens % 10]);
113+
}
114+
} else {
115+
stringBuffer.append(untilTwenty[tens]);
116+
}
117+
118+
if (!magnitude.equals(Magnitude.ONES)) {
119+
stringBuffer.append(" ").append(magnitude.inEnglish());
120+
}
121+
122+
String message = stringBuffer.toString().replace(" zero", "");
123+
return message;
124+
}
125+
}
126+
}
127+

exercises/say/.meta/version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.2.0

exercises/say/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Say
2+
3+
Given a number from 0 to 999,999,999,999, spell out that number in English.
4+
5+
## Step 1
6+
7+
Handle the basic case of 0 through 99.
8+
9+
If the input to the program is `22`, then the output should be
10+
`'twenty-two'`.
11+
12+
Your program should complain loudly if given a number outside the
13+
blessed range.
14+
15+
Some good test cases for this program are:
16+
17+
- 0
18+
- 14
19+
- 50
20+
- 98
21+
- -1
22+
- 100
23+
24+
### Extension
25+
26+
If you're on a Mac, shell out to Mac OS X's `say` program to talk out
27+
loud. If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`.
28+
29+
## Step 2
30+
31+
Implement breaking a number up into chunks of thousands.
32+
33+
So `1234567890` should yield a list like 1, 234, 567, and 890, while the
34+
far simpler `1000` should yield just 1 and 0.
35+
36+
The program must also report any values that are out of range.
37+
38+
## Step 3
39+
40+
Now handle inserting the appropriate scale word between those chunks.
41+
42+
So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`
43+
44+
The program must also report any values that are out of range. It's
45+
fine to stop at "trillion".
46+
47+
## Step 4
48+
49+
Put it all together to get nothing but plain English.
50+
51+
`12345` should give `twelve thousand three hundred forty-five`.
52+
53+
The program must also report any values that are out of range.
54+
55+
### Extensions
56+
57+
Use _and_ (correctly) when spelling out the number in English:
58+
59+
- 14 becomes "fourteen".
60+
- 100 becomes "one hundred".
61+
- 120 becomes "one hundred and twenty".
62+
- 1002 becomes "one thousand and two".
63+
- 1323 becomes "one thousand three hundred and twenty-three".
64+
65+
## Setup
66+
67+
Go through the setup instructions for Java to install the necessary
68+
dependencies:
69+
70+
[https://exercism.io/tracks/java/installation](https://exercism.io/tracks/java/installation)
71+
72+
# Running the tests
73+
74+
You can run all the tests for an exercise by entering the following in your
75+
terminal:
76+
77+
```sh
78+
$ gradle test
79+
```
80+
81+
> Use `gradlew.bat` if you're on Windows
82+
83+
In the test suites all tests but the first have been skipped.
84+
85+
Once you get a test passing, you can enable the next one by removing the
86+
`@Ignore("Remove to run test")` annotation.
87+
88+
## Source
89+
90+
A variation on JavaRanch CattleDrive, exercise 4a [http://www.javaranch.com/say.jsp](http://www.javaranch.com/say.jsp)
91+
92+
## Submitting Incomplete Solutions
93+
It's possible to submit an incomplete solution so you can see how others have
94+
completed the exercise.

exercises/say/build.gradle

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apply plugin: "java"
2+
apply plugin: "eclipse"
3+
apply plugin: "idea"
4+
5+
repositories {
6+
mavenCentral()
7+
}
8+
9+
dependencies {
10+
testCompile "junit:junit:4.12"
11+
}
12+
13+
test {
14+
testLogging {
15+
exceptionFormat = 'full'
16+
events = ["passed", "failed", "skipped"]
17+
}
18+
}

exercises/say/src/main/java/.keep

Whitespace-only changes.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import org.junit.Test;
2+
import org.junit.Ignore;
3+
4+
import static org.junit.Assert.assertEquals;
5+
6+
public class SayTest {
7+
8+
private Say say = new Say();
9+
10+
@Test
11+
public void zero() {
12+
assertEquals("zero", say.say(0));
13+
}
14+
15+
@Ignore("Remove to run test")
16+
@Test
17+
public void one() {
18+
assertEquals("one", say.say(1));
19+
}
20+
21+
@Ignore("Remove to run test")
22+
@Test
23+
public void fourteen() {
24+
assertEquals("fourteen", say.say(14));
25+
}
26+
27+
@Ignore("Remove to run test")
28+
@Test
29+
public void twenty() {
30+
assertEquals("twenty", say.say(20));
31+
}
32+
33+
@Ignore("Remove to run test")
34+
@Test
35+
public void twentyTwo() {
36+
assertEquals("twenty-two", say.say(22));
37+
}
38+
39+
@Ignore("Remove to run test")
40+
@Test
41+
public void oneHundred() {
42+
assertEquals("one hundred", say.say(100));
43+
}
44+
45+
@Ignore("Remove to run test")
46+
@Test
47+
public void oneHundredTwentyThree() {
48+
assertEquals("one hundred twenty-three", say.say(123));
49+
}
50+
51+
@Ignore("Remove to run test")
52+
@Test
53+
public void oneThousand() {
54+
assertEquals("one thousand", say.say(1_000));
55+
}
56+
57+
@Ignore("Remove to run test")
58+
@Test
59+
public void oneThousandTwoHundredThirtyFour() {
60+
assertEquals("one thousand two hundred thirty-four", say.say(1_234));
61+
}
62+
63+
@Ignore("Remove to run test")
64+
@Test
65+
public void oneMillion() {
66+
assertEquals("one million", say.say(1_000_000));
67+
}
68+
69+
@Ignore("Remove to run test")
70+
@Test
71+
public void oneMillionTwoThousandThreeHundredFortyFive() {
72+
assertEquals("one million two thousand three hundred forty-five", say.say(1_002_345));
73+
}
74+
75+
@Ignore("Remove to run test")
76+
@Test
77+
public void oneBillion() {
78+
assertEquals("one billion", say.say(1_000_000_000));
79+
}
80+
81+
@Ignore("Remove to run test")
82+
@Test
83+
public void nineHundredEightySevenBillionSixHundredFiftyFourThreeHundredTwentyOneThousandOneHundredTwentyThree() {
84+
assertEquals("nine hundred eighty-seven billion six hundred fifty-four million" +
85+
" three hundred twenty-one thousand one hundred twenty-three", say.say(987_654_321_123L));
86+
}
87+
88+
@Ignore("Remove to run test")
89+
@Test(expected = IllegalArgumentException.class)
90+
public void illegalNegativeNumber() {
91+
say.say(-1);
92+
}
93+
94+
@Ignore("Remove to run test")
95+
@Test(expected = IllegalArgumentException.class)
96+
public void illegalTooBigNumber() {
97+
say.say(1_000_000_000_000L);
98+
}
99+
}

exercises/settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ include 'roman-numerals'
9090
include 'run-length-encoding'
9191
include 'rotational-cipher'
9292
include 'saddle-points'
93+
include 'say'
9394
include 'satellite'
9495
include 'scrabble-score'
9596
include 'secret-handshake'

0 commit comments

Comments
 (0)