diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0da2c5c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+docs/
+docs/
diff --git a/input.txt b/input.txt
new file mode 100644
index 0000000..47870ef
--- /dev/null
+++ b/input.txt
@@ -0,0 +1,2 @@
+EXPENSE u4 1200 4 u1 u2 u3 u4 PERCENT 20 20 20 20 20
+SHOW u1
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f664ae2
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,33 @@
+
+
+ 4.0.0
+
+ com.arpit
+ mock-machine-coding-2
+ 1.0-SNAPSHOT
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.10
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/SplitWiseRunner.java b/src/main/java/com/SplitWiseRunner.java
new file mode 100644
index 0000000..c5d0565
--- /dev/null
+++ b/src/main/java/com/SplitWiseRunner.java
@@ -0,0 +1,17 @@
+package com;
+
+import com.core.InputProcessor;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class SplitWiseRunner {
+
+ public static void main(String[] args) throws IOException {
+ BufferedReader br = new BufferedReader(new FileReader("input.txt"));
+ br.lines().forEach(new InputProcessor());
+ }
+}
diff --git a/src/main/java/com/core/InputProcessor.java b/src/main/java/com/core/InputProcessor.java
new file mode 100644
index 0000000..559eecf
--- /dev/null
+++ b/src/main/java/com/core/InputProcessor.java
@@ -0,0 +1,32 @@
+package com.core;
+
+import com.dao.ExpenseDao;
+import com.dao.impl.InMemoryExpenseDao;
+import com.factory.SplitStrategyFactory;
+import com.models.Transaction;
+import com.split.SplitStrategy;
+import java.util.function.Consumer;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class InputProcessor implements Consumer {
+
+ private ExpenseDao expenseDao;
+
+ public InputProcessor() {
+ expenseDao = InMemoryExpenseDao.getInstance();
+ }
+
+ @Override
+ public void accept(String inputLine) {
+ if (!inputLine.contains("SHOW")) {
+ SplitStrategy splitStrategy = SplitStrategyFactory.provide(inputLine);
+ Transaction transaction = splitStrategy.split(inputLine);
+ expenseDao.addTransaction(transaction);
+ } else {
+ String[] showForUser = inputLine.split("\\s");
+ System.out.println(expenseDao.showUserBalance(showForUser[1]));
+ }
+ }
+}
diff --git a/src/main/java/com/dao/ExpenseDao.java b/src/main/java/com/dao/ExpenseDao.java
new file mode 100644
index 0000000..dede7b1
--- /dev/null
+++ b/src/main/java/com/dao/ExpenseDao.java
@@ -0,0 +1,10 @@
+package com.dao;
+
+import com.models.Transaction;
+
+public interface ExpenseDao {
+
+ void addTransaction(Transaction transaction);
+
+ String showUserBalance(String userId);
+}
diff --git a/src/main/java/com/dao/impl/InMemoryExpenseDao.java b/src/main/java/com/dao/impl/InMemoryExpenseDao.java
new file mode 100644
index 0000000..b5ff7e8
--- /dev/null
+++ b/src/main/java/com/dao/impl/InMemoryExpenseDao.java
@@ -0,0 +1,49 @@
+package com.dao.impl;
+
+import com.dao.ExpenseDao;
+import com.models.Borrow;
+import com.models.Transaction;
+import com.models.UserBalance;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class InMemoryExpenseDao implements ExpenseDao {
+
+ private static final InMemoryExpenseDao IN_MEMORY_EXPENSE_DAO = new InMemoryExpenseDao();
+
+ private Map userBalanceData = new HashMap<>();
+
+ private InMemoryExpenseDao() { }
+
+ public static ExpenseDao getInstance() {
+ return IN_MEMORY_EXPENSE_DAO;
+ }
+
+ @Override
+ public void addTransaction(Transaction transaction) {
+ List borrowList = transaction.getBorrowList();
+ for (Borrow borrow : borrowList) {
+ UserBalance lenderBalanceData = userBalanceData.getOrDefault(borrow.getLender(), new UserBalance(borrow.getLender()));
+ UserBalance borrowerBalanceData = userBalanceData.getOrDefault(borrow.getBorrower(), new UserBalance(borrow.getBorrower()));
+ lenderBalanceData.receiveFrom(borrow.getBorrower(), borrow.getAmount());
+ borrowerBalanceData.payTo(borrow.getLender(), borrow.getAmount());
+ userBalanceData.putIfAbsent(lenderBalanceData.getUserId(), lenderBalanceData);
+ userBalanceData.putIfAbsent(borrowerBalanceData.getUserId(), borrowerBalanceData);
+ }
+ }
+
+ @Override
+ public String showUserBalance(String userId) {
+ UserBalance userBalance = userBalanceData.get(userId);
+ List debitList = userBalance.getAllPayTo();
+ List creditList = userBalance.getAllReceiveFrom();
+ StringBuilder sbr = new StringBuilder();
+ debitList.forEach(borrow -> sbr.append(borrow.formattedOutput()).append("\n"));
+ creditList.forEach(borrow -> sbr.append(borrow.formattedOutput()).append("\n"));
+ return sbr.toString();
+ }
+}
diff --git a/src/main/java/com/factory/SplitStrategyFactory.java b/src/main/java/com/factory/SplitStrategyFactory.java
new file mode 100644
index 0000000..c03a278
--- /dev/null
+++ b/src/main/java/com/factory/SplitStrategyFactory.java
@@ -0,0 +1,32 @@
+package com.factory;
+
+import com.split.SplitStrategy;
+import com.split.impl.EqualSplitStrategy;
+import com.split.impl.ExactSplitStrategy;
+import com.split.impl.PercentSplitStrategy;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class SplitStrategyFactory {
+ private static final SplitStrategy EQUAL_SPLIT_STRATEGY;
+ private static final SplitStrategy PERCENT_SPLIT_STRATEGY;
+ private static final SplitStrategy EXACT_SPLIT_STRATEGY;
+
+ static {
+ EQUAL_SPLIT_STRATEGY = new EqualSplitStrategy();
+ EXACT_SPLIT_STRATEGY = new ExactSplitStrategy();
+ PERCENT_SPLIT_STRATEGY = new PercentSplitStrategy();
+ }
+
+ public static SplitStrategy provide(String inputLine) {
+ if (inputLine.contains("EXACT")) {
+ return EXACT_SPLIT_STRATEGY;
+ } else if (inputLine.contains("EQUAL")) {
+ return EQUAL_SPLIT_STRATEGY;
+ } else if (inputLine.contains("PERCENT")) {
+ return PERCENT_SPLIT_STRATEGY;
+ }
+ throw new IllegalArgumentException("Invalid Input");
+ }
+}
diff --git a/src/main/java/com/models/Borrow.java b/src/main/java/com/models/Borrow.java
new file mode 100644
index 0000000..8cac6bd
--- /dev/null
+++ b/src/main/java/com/models/Borrow.java
@@ -0,0 +1,20 @@
+package com.models;
+
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+@Builder
+@Getter
+public class Borrow {
+
+ private String borrower;
+ private String lender;
+ private double amount;
+
+ public String formattedOutput() {
+ return String.format("%s Owes %s : %f", borrower, lender, amount);
+ }
+}
diff --git a/src/main/java/com/models/Transaction.java b/src/main/java/com/models/Transaction.java
new file mode 100644
index 0000000..8333757
--- /dev/null
+++ b/src/main/java/com/models/Transaction.java
@@ -0,0 +1,20 @@
+package com.models;
+
+import java.util.List;
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+@Builder
+@Getter
+public class Transaction {
+
+ private String spentBy;
+ private List borrowList;
+
+ public String getSpentBy() {
+ return spentBy;
+ }
+}
diff --git a/src/main/java/com/models/UserBalance.java b/src/main/java/com/models/UserBalance.java
new file mode 100644
index 0000000..e13002d
--- /dev/null
+++ b/src/main/java/com/models/UserBalance.java
@@ -0,0 +1,66 @@
+package com.models;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+
+public class UserBalance {
+
+ @Getter
+ private String userId;
+ private Map payTo;
+ private Map receiveFrom;
+
+ public UserBalance(String userId) {
+ this.userId = userId;
+ payTo = new HashMap<>();
+ receiveFrom = new HashMap<>();
+ }
+
+ public void receiveFrom(String userId, double amount) {
+ receiveFrom.merge(userId, amount, (oldValue, newValue) -> oldValue + amount);
+ settle(userId);
+ }
+
+ public void payTo(String userId, double amount) {
+ payTo.merge(userId, amount, (oldValue, newValue) -> oldValue + amount);
+ settle(userId);
+ }
+
+ private void settle(String externalUserId) {
+ Double payToUser = payTo.getOrDefault(externalUserId, (double) 0);
+ Double receiveFromUser = receiveFrom.getOrDefault(externalUserId, (double) 0);
+ if (payToUser > receiveFromUser) {
+ payTo.put(externalUserId, payToUser - receiveFromUser);
+ receiveFrom.remove(externalUserId);
+ } else if (payToUser < receiveFromUser) {
+ receiveFrom.put(externalUserId, receiveFromUser - payToUser);
+ payTo.remove(externalUserId);
+ } else {
+ receiveFrom.remove(externalUserId);
+ payTo.remove(externalUserId);
+ }
+ }
+
+ public List getAllReceiveFrom() {
+ List allReceiveFrom = new ArrayList<>();
+ receiveFrom.forEach((key, value) -> {
+ allReceiveFrom.add(Borrow.builder().lender(userId).borrower(key).amount(value).build());
+ });
+ return allReceiveFrom;
+ }
+
+ public List getAllPayTo() {
+ List allPayTo = new ArrayList<>();
+ payTo.forEach((key, value) -> {
+ allPayTo.add(Borrow.builder().lender(key).borrower(userId).amount(value).build());
+ });
+ return allPayTo;
+ }
+}
diff --git a/src/main/java/com/split/SplitStrategy.java b/src/main/java/com/split/SplitStrategy.java
new file mode 100644
index 0000000..94294f6
--- /dev/null
+++ b/src/main/java/com/split/SplitStrategy.java
@@ -0,0 +1,8 @@
+package com.split;
+
+import com.models.Transaction;
+
+public interface SplitStrategy {
+
+ Transaction split(String inputLine);
+}
diff --git a/src/main/java/com/split/impl/EqualSplitStrategy.java b/src/main/java/com/split/impl/EqualSplitStrategy.java
new file mode 100644
index 0000000..4e30188
--- /dev/null
+++ b/src/main/java/com/split/impl/EqualSplitStrategy.java
@@ -0,0 +1,15 @@
+package com.split.impl;
+
+import com.models.Transaction;
+import com.split.SplitStrategy;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class EqualSplitStrategy implements SplitStrategy {
+
+ @Override
+ public Transaction split(String inputLine) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/split/impl/ExactSplitStrategy.java b/src/main/java/com/split/impl/ExactSplitStrategy.java
new file mode 100644
index 0000000..1169ac7
--- /dev/null
+++ b/src/main/java/com/split/impl/ExactSplitStrategy.java
@@ -0,0 +1,16 @@
+package com.split.impl;
+
+import com.models.Transaction;
+import com.split.SplitStrategy;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class ExactSplitStrategy implements SplitStrategy {
+
+ @Override
+ public Transaction split(String inputLine) {
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/split/impl/PercentSplitStrategy.java b/src/main/java/com/split/impl/PercentSplitStrategy.java
new file mode 100644
index 0000000..be84535
--- /dev/null
+++ b/src/main/java/com/split/impl/PercentSplitStrategy.java
@@ -0,0 +1,56 @@
+package com.split.impl;
+
+import com.models.Borrow;
+import com.models.Transaction;
+import com.split.SplitStrategy;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by bugkiller on 21/09/19.
+ */
+public class PercentSplitStrategy implements SplitStrategy {
+
+ private static DecimalFormat decimalFormat = new DecimalFormat(".##");
+
+ public PercentSplitStrategy() {
+ decimalFormat.setRoundingMode(RoundingMode.UP);
+ }
+
+ @Override
+ public Transaction split(String inputLine) {
+ String split[] = inputLine.split("PERCENT");
+ String spenderUsersAndAmount[] = split[0].split("\\s");
+ String shareList[] = split[1].split("\\s");
+ validateShareList(shareList);
+ String spender = spenderUsersAndAmount[1];
+ double amount = Double.parseDouble(spenderUsersAndAmount[2]);
+ List borrowList = new ArrayList<>();
+ for (int i = 4, j = 1; i < spenderUsersAndAmount.length; i++) {
+ double share = Double.parseDouble(shareList[j]);
+ double borrowAmount = amount * (share / 100);
+ borrowList.add(Borrow.builder()
+ .lender(spender)
+ .borrower(spenderUsersAndAmount[i])
+ .amount(borrowAmount)
+ .build());
+ }
+ return Transaction.builder()
+ .borrowList(borrowList)
+ .spentBy(spender)
+ .build();
+ }
+
+ private void validateShareList(String[] shareList) {
+ double percentSum = 0;
+ for (int i = 1; i < shareList.length; i++) {
+ String percent = shareList[i];
+ percentSum += Double.parseDouble(percent);
+ }
+ if (percentSum != 100) {
+ throw new IllegalArgumentException("Percent don't add up to 100");
+ }
+ }
+}