Skip to content

Commit 2ea76ab

Browse files
authored
Merge branch 'master' into list-ops
2 parents 7bd1529 + bd611c3 commit 2ea76ab

16 files changed

Lines changed: 322 additions & 53 deletions

File tree

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ addons:
1818
apt:
1919
packages:
2020
- tree
21+
2122
before_install:
2223
- rvm install 2.2.5
2324
- rvm use 2.2.5
25+
- bin/build-jq.sh # we want jq 1.5 features; not avail through pkg repo.
26+
27+
before_script:
28+
- export PATH=$TRAVIS_BUILD_DIR/bin:$PATH # ensure our tools are prefered over included ones.
2429

2530
script:
2631
- bin/unit-tests.sh

bin/build-jq.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
pushd bin
5+
curl --location https://github.com/stedolan/jq/releases/download/jq-1.5/jq-1.5.tar.gz >jq-1.5.tar.gz
6+
tar xvf jq-1.5.tar.gz
7+
cd jq-1.5
8+
./configure --disable-maintainer-mode && make
9+
mv jq ..
10+
popd
11+

config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"secret-handshake",
6464
"flatten-array",
6565
"perfect-numbers",
66+
"change",
6667
"list-ops"
6768
],
6869
"exercises": [
@@ -361,6 +362,11 @@
361362
"difficulty": 1,
362363
"topics": []
363364
},
365+
{
366+
"slug": "change",
367+
"difficulty": 1,
368+
"topics": []
369+
},
364370
{
365371
"slug": "list-ops",
366372
"difficulty": 1,

exercises/allergies/src/test/java/AllergiesTest.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import java.util.Arrays;
55
import java.util.List;
6+
import java.util.Collections;
67

78
import static org.junit.Assert.assertEquals;
89

@@ -103,7 +104,7 @@ public void noAllergies() {
103104
@Test
104105
public void isAllergicToJustEggs() {
105106
Allergies allergies = new Allergies(1);
106-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{Allergen.EGGS});
107+
List<Allergen> expectedAllergens = Collections.singletonList(Allergen.EGGS);
107108

108109
assertEquals(expectedAllergens, allergies.getList());
109110
}
@@ -112,7 +113,7 @@ public void isAllergicToJustEggs() {
112113
@Test
113114
public void isAllergicToJustPeanuts() {
114115
Allergies allergies = new Allergies(2);
115-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{Allergen.PEANUTS});
116+
List<Allergen> expectedAllergens = Collections.singletonList(Allergen.PEANUTS);
116117

117118
assertEquals(expectedAllergens, allergies.getList());
118119
}
@@ -121,7 +122,7 @@ public void isAllergicToJustPeanuts() {
121122
@Test
122123
public void isAllergicToJustStrawberries() {
123124
Allergies allergies = new Allergies(8);
124-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{Allergen.STRAWBERRIES});
125+
List<Allergen> expectedAllergens = Collections.singletonList(Allergen.STRAWBERRIES);
125126

126127
assertEquals(expectedAllergens, allergies.getList());
127128
}
@@ -130,10 +131,10 @@ public void isAllergicToJustStrawberries() {
130131
@Test
131132
public void isAllergicToEggsAndPeanuts() {
132133
Allergies allergies = new Allergies(3);
133-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{
134+
List<Allergen> expectedAllergens = Arrays.asList(
134135
Allergen.EGGS,
135136
Allergen.PEANUTS
136-
});
137+
);
137138

138139
assertEquals(expectedAllergens, allergies.getList());
139140
}
@@ -142,10 +143,10 @@ public void isAllergicToEggsAndPeanuts() {
142143
@Test
143144
public void isAllergicToEggsAndShellfish() {
144145
Allergies allergies = new Allergies(5);
145-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{
146+
List<Allergen> expectedAllergens = Arrays.asList(
146147
Allergen.EGGS,
147148
Allergen.SHELLFISH
148-
});
149+
);
149150

150151
assertEquals(expectedAllergens, allergies.getList());
151152
}
@@ -154,13 +155,13 @@ public void isAllergicToEggsAndShellfish() {
154155
@Test
155156
public void isAllergicToLotsOfStuff() {
156157
Allergies allergies = new Allergies(248);
157-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{
158+
List<Allergen> expectedAllergens = Arrays.asList(
158159
Allergen.STRAWBERRIES,
159160
Allergen.TOMATOES,
160161
Allergen.CHOCOLATE,
161162
Allergen.POLLEN,
162163
Allergen.CATS
163-
});
164+
);
164165

165166
assertEquals(expectedAllergens, allergies.getList());
166167
}
@@ -169,7 +170,7 @@ public void isAllergicToLotsOfStuff() {
169170
@Test
170171
public void isAllergicToEverything() {
171172
Allergies allergies = new Allergies(255);
172-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{
173+
List<Allergen> expectedAllergens = Arrays.asList(
173174
Allergen.EGGS,
174175
Allergen.PEANUTS,
175176
Allergen.SHELLFISH,
@@ -178,7 +179,7 @@ public void isAllergicToEverything() {
178179
Allergen.CHOCOLATE,
179180
Allergen.POLLEN,
180181
Allergen.CATS
181-
});
182+
);
182183

183184
assertEquals(expectedAllergens, allergies.getList());
184185
}
@@ -187,15 +188,15 @@ public void isAllergicToEverything() {
187188
@Test
188189
public void ignoreNonAllergenScoreParts() {
189190
Allergies allergies = new Allergies(509);
190-
List<Allergen> expectedAllergens = Arrays.asList(new Allergen[]{
191+
List<Allergen> expectedAllergens = Arrays.asList(
191192
Allergen.EGGS,
192193
Allergen.SHELLFISH,
193194
Allergen.STRAWBERRIES,
194195
Allergen.TOMATOES,
195196
Allergen.CHOCOLATE,
196197
Allergen.POLLEN,
197198
Allergen.CATS
198-
});
199+
);
199200

200201
assertEquals(expectedAllergens, allergies.getList());
201202
}

exercises/change/build.gradle

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
test {
13+
testLogging {
14+
exceptionFormat = 'full'
15+
events = ["passed", "failed", "skipped"]
16+
}
17+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import java.util.*;
2+
import java.util.stream.Collectors;
3+
4+
import static java.util.Comparator.comparingInt;
5+
6+
final class ChangeCalculator {
7+
8+
private final List<Integer> currencyCoins;
9+
10+
ChangeCalculator(final List<Integer> currencyCoins) {
11+
this.currencyCoins = currencyCoins;
12+
Collections.sort(currencyCoins);
13+
}
14+
15+
List<Integer> computeMostEfficientChange(final int grandTotal) {
16+
if (grandTotal < 0) {
17+
throw new IllegalArgumentException("Negative totals are not allowed.");
18+
}
19+
20+
final Map<Integer, List<Integer>> minimalCoinsMap = new HashMap<>();
21+
minimalCoinsMap.put(0, new ArrayList<>());
22+
23+
for (int total = 1; total <= grandTotal; total++) {
24+
final int localTotal = total;
25+
26+
final List<Integer> minimalCoins = getCoinsNoLargerThan(total)
27+
.stream()
28+
.map(coin -> {
29+
final List<Integer> minimalRemainderCoins = minimalCoinsMap.get(localTotal - coin);
30+
return minimalRemainderCoins != null ? prepend(coin, minimalRemainderCoins) : null;
31+
})
32+
.filter(Objects::nonNull)
33+
.sorted(comparingInt(List::size))
34+
.findFirst()
35+
.orElse(null);
36+
37+
minimalCoinsMap.put(localTotal, minimalCoins);
38+
}
39+
40+
final List<Integer> resultCandidate = minimalCoinsMap.get(grandTotal);
41+
42+
if (resultCandidate == null) {
43+
throw new IllegalArgumentException(
44+
"The total " + grandTotal + " cannot be represented in the given currency.");
45+
}
46+
47+
return resultCandidate;
48+
}
49+
50+
private List<Integer> getCoinsNoLargerThan(final int threshold) {
51+
return currencyCoins.stream()
52+
.filter(coin -> coin <= threshold)
53+
.collect(Collectors.toList());
54+
}
55+
56+
private List<Integer> prepend(final int integer, final List<Integer> integers) {
57+
final List<Integer> result = new ArrayList<>();
58+
result.add(integer);
59+
result.addAll(integers);
60+
return result;
61+
}
62+
63+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
final class ChangeCalculator {
2+
3+
4+
5+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import org.junit.Ignore;
2+
import org.junit.Rule;
3+
import org.junit.Test;
4+
import org.junit.rules.ExpectedException;
5+
6+
import static java.util.Arrays.asList;
7+
import static java.util.Collections.emptyList;
8+
import static java.util.Collections.singletonList;
9+
import static org.junit.Assert.assertEquals;
10+
11+
public final class ChangeCalculatorTest {
12+
13+
/*
14+
* See https://github.com/junit-team/junit4/wiki/Rules for information on JUnit Rules in general and
15+
* ExpectedExceptions in particular.
16+
*/
17+
@Rule
18+
public ExpectedException expectedException = ExpectedException.none();
19+
20+
@Test
21+
public void testChangeThatCanBeGivenInASingleCoin() {
22+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100));
23+
24+
assertEquals(
25+
singletonList(25),
26+
changeCalculator.computeMostEfficientChange(25));
27+
}
28+
29+
@Ignore
30+
@Test
31+
public void testChangeThatMustBeGivenInMultipleCoins() {
32+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100));
33+
34+
assertEquals(
35+
asList(5, 10),
36+
changeCalculator.computeMostEfficientChange(15));
37+
}
38+
39+
@Ignore
40+
@Test
41+
// https://en.wikipedia.org/wiki/Change-making_problem#Greedy_method
42+
public void testLilliputianCurrencyForWhichGreedyAlgorithmFails() {
43+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 4, 15, 20, 50));
44+
45+
assertEquals(
46+
asList(4, 4, 15),
47+
changeCalculator.computeMostEfficientChange(23));
48+
}
49+
50+
@Ignore
51+
@Test
52+
// https://en.wikipedia.org/wiki/Change-making_problem#Greedy_method
53+
public void testLowerElbonianCurrencyForWhichGreedyAlgorithmFails() {
54+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25));
55+
56+
assertEquals(
57+
asList(21, 21, 21),
58+
changeCalculator.computeMostEfficientChange(63));
59+
}
60+
61+
@Ignore
62+
@Test
63+
public void testLargeAmountOfChange() {
64+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5, 10, 20, 50, 100));
65+
66+
assertEquals(
67+
asList(2, 2, 5, 20, 20, 50, 100, 100, 100, 100, 100, 100, 100, 100, 100),
68+
changeCalculator.computeMostEfficientChange(999));
69+
}
70+
71+
@Ignore
72+
@Test
73+
public void testZeroChange() {
74+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25));
75+
76+
assertEquals(
77+
emptyList(),
78+
changeCalculator.computeMostEfficientChange(0));
79+
}
80+
81+
@Ignore
82+
@Test
83+
public void testChangeLessThanSmallestCoinInCurrencyCannotBeRepresented() {
84+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10));
85+
86+
expectedException.expect(IllegalArgumentException.class);
87+
expectedException.expectMessage("The total 3 cannot be represented in the given currency.");
88+
89+
changeCalculator.computeMostEfficientChange(3);
90+
}
91+
92+
@Ignore
93+
@Test
94+
public void testChangeLargerThanAllCoinsInCurrencyThatCannotBeRepresented() {
95+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10));
96+
97+
expectedException.expect(IllegalArgumentException.class);
98+
expectedException.expectMessage("The total 94 cannot be represented in the given currency.");
99+
100+
changeCalculator.computeMostEfficientChange(94);
101+
}
102+
103+
@Ignore
104+
@Test
105+
public void testNegativeChangeIsRejected() {
106+
ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5));
107+
108+
expectedException.expect(IllegalArgumentException.class);
109+
expectedException.expectMessage("Negative totals are not allowed.");
110+
111+
changeCalculator.computeMostEfficientChange(-5);
112+
}
113+
114+
}

0 commit comments

Comments
 (0)