Skip to content

Commit 70c7a07

Browse files
authored
Merge pull request #6 from LimZiJia/testing
Add test cases
2 parents d631ac1 + b9babb7 commit 70c7a07

File tree

8 files changed

+168
-32
lines changed

8 files changed

+168
-32
lines changed

data/transactions.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
a 40.10
2+
b 40.10
3+
c 10.00
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package balancer.logic.command;
22

33
import java.io.IOException;
4+
import java.text.DecimalFormat;
45

56
import balancer.storage.Storage;
67

@@ -12,24 +13,25 @@ public class AddCommand extends Command {
1213
public static final String COMMAND_WORD = "add";
1314
private static final String ADD_COMMAND_SUCCESS = "'s transaction has been successfully added!";
1415
private final String name;
15-
private final int amount;
16+
private final double amount;
1617

1718
/**
1819
* Constructs an {@code AddCommand} with the specified name and amount.
1920
*
2021
* @param name the name of the individual involved in the transaction.
2122
* @param amount the amount of the transaction.
2223
*/
23-
public AddCommand(String name, int amount) {
24+
public AddCommand(String name, double amount) {
2425
this.name = name;
2526
this.amount = amount;
2627
}
2728

2829
@Override
2930
public CommandResult execute(Storage storage) throws IOException {
31+
DecimalFormat df = new DecimalFormat("0.00");
3032
storage.addTransaction(name, amount);
3133
storage.save();
32-
return new CommandResult(String.format("%s%s %s has now contributed $%.2f",
33-
name, ADD_COMMAND_SUCCESS, name, storage.getAmount(name)));
34+
return new CommandResult(String.format("%s%s %s has now contributed $%s",
35+
name, ADD_COMMAND_SUCCESS, name, df.format(storage.getAmount(name))));
3436
}
3537
}

src/main/java/balancer/logic/command/CalculateCommand.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package balancer.logic.command;
22

3+
import java.text.DecimalFormat;
34
import java.util.ArrayList;
45
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.stream.Collectors;
58

69
import balancer.storage.Storage;
710
import balancer.storage.Transaction;
@@ -14,14 +17,15 @@
1417
*/
1518
public class CalculateCommand extends Command {
1619
public static final String COMMAND_WORD = "calculate";
20+
public static final String NO_TRANSACTIONS_REPLY = "All good! No transactions required!";
1721

1822
@Override
1923
public CommandResult execute(Storage storage) {
2024
HashMap<String, Transaction> transactions = storage.getTransactions();
2125

2226
// Remove all people that contributed exactly the average amount and transforms the amounts to their difference
2327
// from the average. +ve means then are owed money and -ve means that they owe money.
24-
ArrayList<Transaction> processed = preprocess(transactions);
28+
List<Transaction> processed = preprocess(transactions);
2529

2630
// Run the greedy algorithm to get the minimum number of transactions
2731
String result = greedy(processed);
@@ -38,24 +42,19 @@ public CommandResult execute(Storage storage) {
3842
* @param hashmap Taken straight from the storage. Maps names to {@code Transaction}.
3943
* @return An {@code ArrayList<Transaction>} of the processed {@code HashMap}.
4044
*/
41-
private ArrayList<Transaction> preprocess(HashMap<String, Transaction> hashmap) {
42-
ArrayList<Transaction> current = new ArrayList<>(hashmap.values());
43-
float total = 0;
45+
public List<Transaction> preprocess(HashMap<String, Transaction> hashmap) {
46+
List<Transaction> current = new ArrayList<>(hashmap.values());
47+
double total = 0;
4448
for (Transaction t: current) {
4549
total += t.getAmount();
4650
}
47-
float average = total / current.size();
51+
double average = total / current.size();
4852

4953
// Changing transactions to be difference from average
5054
current.replaceAll(x -> x.update(-average));
5155

5256
// Removing all people who have to do nothing (people who are not owed or owe any money)
53-
current.forEach(x -> {
54-
int index = current.indexOf(x);
55-
if (x.isEmpty()) {
56-
current.remove(index);
57-
}
58-
});
57+
current = current.stream().filter(t -> !t.isEmpty()).collect(Collectors.toList());
5958

6059
return current;
6160
}
@@ -68,9 +67,12 @@ private ArrayList<Transaction> preprocess(HashMap<String, Transaction> hashmap)
6867
* @return The String representation of the list of transactions that need to occur in the form of
6968
* {@code person1 has to pay person2 $X.XX}.
7069
*/
71-
private String greedy(ArrayList<Transaction> processed) {
70+
public String greedy(List<Transaction> processed) {
71+
DecimalFormat df = new DecimalFormat("0.00");
7272
StringBuilder sb = new StringBuilder();
7373
TransactionComparator comparator = new TransactionComparator();
74+
int numberOfTransactions = 0;
75+
7476
while (!processed.isEmpty()) {
7577
// Sort the list
7678
processed.sort(comparator);
@@ -80,9 +82,10 @@ private String greedy(ArrayList<Transaction> processed) {
8082
Transaction biggestReceiver = processed.get(processed.size() - 1);
8183

8284
// Make the largest possible transaction from biggestGiver to biggestReceiver
83-
float amountToTransfer = Math.min(-biggestGiver.getAmount(), biggestReceiver.getAmount());
84-
sb.append(String.format("%s has to pay %s $%.2f\n",
85-
biggestGiver.getPerson(), biggestReceiver.getPerson(), amountToTransfer));
85+
double amountToTransfer = Math.min(-biggestGiver.getAmount(), biggestReceiver.getAmount());
86+
sb.append(String.format("%s has to pay %s $%s\n",
87+
biggestGiver.getPerson(), biggestReceiver.getPerson(), df.format(amountToTransfer)));
88+
numberOfTransactions++;
8689

8790
// Update transactions and transaction list
8891
biggestGiver.update(amountToTransfer);
@@ -99,6 +102,8 @@ private String greedy(ArrayList<Transaction> processed) {
99102
}
100103
}
101104

102-
return sb.toString();
105+
return sb.length() == 0
106+
? NO_TRANSACTIONS_REPLY
107+
: sb.append(String.format("\nNumber of transactions: %d", numberOfTransactions)).toString();
103108
}
104109
}

src/main/java/balancer/logic/parser/AddParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public AddCommand parse(String userInput) throws ParserException {
1515
}
1616
String[] arguments = userInput.split(" ");
1717
String name = arguments[0];
18-
int amount = Integer.parseInt(arguments[1]);
18+
double amount = Double.parseDouble(arguments[1]);
1919
return new AddCommand(name, amount);
2020
}
2121
}

src/main/java/balancer/storage/Storage.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.ArrayList;
88
import java.util.HashMap;
99
import java.util.List;
10+
import java.util.Map;
1011
import java.util.stream.Collectors;
1112

1213
/**
@@ -34,7 +35,7 @@ public Storage(String dir, String name) {
3435
* @param name Name of the person to add or update.
3536
* @param amount The initial or update transaction amount.
3637
*/
37-
public void addTransaction(String name, int amount) {
38+
public void addTransaction(String name, double amount) {
3839
Transaction current = transactions.get(name);
3940
if (current == null) {
4041
current = new Transaction(name, amount);
@@ -45,10 +46,17 @@ public void addTransaction(String name, int amount) {
4546
}
4647

4748
public HashMap<String, Transaction> getTransactions() {
48-
return this.transactions;
49+
// Deep copy so that this.transactions cannot be altered
50+
HashMap<String, Transaction> copy = new HashMap<>();
51+
for (Map.Entry<String, Transaction> entry : this.transactions.entrySet()) {
52+
String person = entry.getValue().getPerson();
53+
double amount = entry.getValue().getAmount();
54+
copy.put(entry.getKey(), new Transaction(person, amount));
55+
}
56+
return copy;
4957
}
5058

51-
public float getAmount(String name) {
59+
public double getAmount(String name) {
5260
return transactions.get(name).getAmount();
5361
}
5462

src/main/java/balancer/storage/Transaction.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
package balancer.storage;
22

3+
import java.text.DecimalFormat;
4+
35
/**
46
* Represents a transaction.
57
*/
68
public class Transaction {
79
private String person;
8-
private float amount;
10+
private double amount;
911

1012
/**
1113
* Constructs a transaction.
1214
*
1315
* @param person Name of the person involved in the transaction.
1416
* @param amount Amount of the transaction.
1517
*/
16-
public Transaction(String person, float amount) {
18+
public Transaction(String person, double amount) {
1719
this.person = person;
1820
this.amount = amount;
1921
}
@@ -24,7 +26,7 @@ public Transaction(String person, float amount) {
2426
* @param amount Amount to update. Can be negative.
2527
* @return Updated {@code Transaction}.
2628
*/
27-
public Transaction update(float amount) {
29+
public Transaction update(double amount) {
2830
this.amount += amount;
2931
return this;
3032
}
@@ -33,7 +35,7 @@ public String getPerson() {
3335
return person;
3436
}
3537

36-
public float getAmount() {
38+
public double getAmount() {
3739
return amount;
3840
}
3941

@@ -43,7 +45,7 @@ public float getAmount() {
4345
* @return Ture if amount is 0.
4446
*/
4547
public boolean isEmpty() {
46-
float epsilon = 0.00001f; // Had to choose an arbitrarily small value due to float equality being bad.
48+
double epsilon = 0.00001; // Had to choose an arbitrarily small value due to double equality being bad.
4749
return Math.abs(amount) < epsilon;
4850
}
4951

@@ -59,14 +61,15 @@ public static Transaction savedStringToTransaction(String savedString) {
5961
throw new UnsupportedOperationException();
6062
} else {
6163
String newName = split[0];
62-
float newAmount = Float.parseFloat(split[1]);
64+
double newAmount = Double.parseDouble(split[1]);
6365
return new Transaction(newName, newAmount);
6466
}
6567
}
6668

6769
@Override
6870
public String toString() {
69-
return String.format("%s %.2f", person, amount);
71+
DecimalFormat df = new DecimalFormat("0.00");
72+
return String.format("%s %s", person, df.format(amount));
7073
}
7174

7275
}

src/main/java/balancer/storage/TransactionComparator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
public class TransactionComparator implements Comparator<Transaction> {
99
@Override
1010
public int compare(Transaction t1, Transaction t2) {
11-
return Float.compare(t1.getAmount(), t2.getAmount());
11+
return Double.compare(t1.getAmount(), t2.getAmount());
1212
}
1313
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package balancer.logic.command;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.HashMap;
6+
import java.util.List;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
import balancer.storage.Transaction;
11+
12+
class CalculateCommandTest {
13+
private CalculateCommand cc = new CalculateCommand();
14+
15+
@Test
16+
public void onePersonPayTwoPersons() {
17+
HashMap<String, Transaction> transactions = new HashMap<>();
18+
transactions.put("Alice", new Transaction("Alice", 40));
19+
transactions.put("Bob", new Transaction("Bob", 40));
20+
transactions.put("Charlie", new Transaction("Charlie", 10));
21+
List<Transaction> transactionList = cc.preprocess(transactions);
22+
23+
String expected = "Charlie has to pay Alice $10.00\n"
24+
+ "Charlie has to pay Bob $10.00\n"
25+
+ "\n"
26+
+ "Number of transactions: 2";
27+
String actual = cc.greedy(transactionList);
28+
assertEquals(expected, actual);
29+
}
30+
31+
@Test
32+
public void onePersonPayTwoPersons2dp() {
33+
HashMap<String, Transaction> transactions = new HashMap<>();
34+
transactions.put("Alice", new Transaction("Alice", 40.105));
35+
transactions.put("Bob", new Transaction("Bob", 40.105));
36+
transactions.put("Charlie", new Transaction("Charlie", 10));
37+
List<Transaction> transactionList = cc.preprocess(transactions);
38+
39+
String expected = "Charlie has to pay Alice $10.04\n"
40+
+ "Charlie has to pay Bob $10.03\n"
41+
+ "\n"
42+
+ "Number of transactions: 2";
43+
String actual = cc.greedy(transactionList);
44+
assertEquals(expected, actual);
45+
}
46+
47+
@Test
48+
public void fourPersons1() {
49+
HashMap<String, Transaction> transactions = new HashMap<>();
50+
transactions.put("Alice", new Transaction("Alice", 10));
51+
transactions.put("Bob", new Transaction("Bob", 20));
52+
transactions.put("Charlie", new Transaction("Charlie", 0));
53+
transactions.put("Don", new Transaction("Don", 10));
54+
List<Transaction> transactionList = cc.preprocess(transactions);
55+
56+
String expected = "Charlie has to pay Bob $10.00\n"
57+
+ "\n"
58+
+ "Number of transactions: 1";
59+
String actual = cc.greedy(transactionList);
60+
assertEquals(expected, actual);
61+
}
62+
63+
@Test
64+
public void fourPersons2() {
65+
HashMap<String, Transaction> transactions = new HashMap<>();
66+
transactions.put("Alice", new Transaction("Alice", 40));
67+
transactions.put("Bob", new Transaction("Bob", 40));
68+
transactions.put("Charlie", new Transaction("Charlie", 10));
69+
transactions.put("Don", new Transaction("Don", 10));
70+
List<Transaction> transactionList = cc.preprocess(transactions);
71+
72+
String expected = "Don has to pay Alice $15.00\n"
73+
+ "Charlie has to pay Bob $15.00\n"
74+
+ "\n"
75+
+ "Number of transactions: 2";
76+
String actual = cc.greedy(transactionList);
77+
assertEquals(expected, actual);
78+
}
79+
80+
@Test
81+
public void fourPersonsWithOneSignificantlyMmore() {
82+
HashMap<String, Transaction> transactions = new HashMap<>();
83+
transactions.put("Alice", new Transaction("Alice", 200));
84+
transactions.put("Bob", new Transaction("Bob", 80));
85+
transactions.put("Charlie", new Transaction("Charlie", 50));
86+
transactions.put("Don", new Transaction("Don", 20));
87+
List<Transaction> transactionList = cc.preprocess(transactions);
88+
89+
String expected = "Don has to pay Alice $67.50\n"
90+
+ "Charlie has to pay Alice $37.50\n"
91+
+ "Bob has to pay Alice $7.50\n"
92+
+ "\n"
93+
+ "Number of transactions: 3";
94+
String actual = cc.greedy(transactionList);
95+
assertEquals(expected, actual);
96+
}
97+
98+
@Test
99+
public void fourPersonsWithTwoSignificantlyMore() {
100+
HashMap<String, Transaction> transactions = new HashMap<>();
101+
transactions.put("Alice", new Transaction("Alice", 160));
102+
transactions.put("Bob", new Transaction("Bob", 120));
103+
transactions.put("Charlie", new Transaction("Charlie", 50));
104+
transactions.put("Don", new Transaction("Don", 20));
105+
List<Transaction> transactionList = cc.preprocess(transactions);
106+
107+
String expected = "Don has to pay Alice $67.50\n"
108+
+ "Charlie has to pay Bob $32.50\n"
109+
+ "Charlie has to pay Alice $5.00\n"
110+
+ "\n"
111+
+ "Number of transactions: 3";
112+
String actual = cc.greedy(transactionList);
113+
assertEquals(expected, actual);
114+
}
115+
}

0 commit comments

Comments
 (0)