Designing Splitwise
    
    Requirements
    
        - The system should allow users to create accounts and manage their profile information.
 
        - Users should be able to create groups and add other users to the groups.
 
        - Users should be able to add expenses within a group, specifying the amount, description, and participants.
 
        - The system should automatically split the expenses among the participants based on their share.
 
        - Users should be able to view their individual balances with other users and settle up the balances.
 
        - The system should support different split methods, such as equal split, percentage split, and exact amounts.
 
        - Users should be able to view their transaction history and group expenses.
 
        - The system should handle concurrent transactions and ensure data consistency.
 
    
    
    Design Components:
    
        - The User class represents a user in the Splitwise system, with properties such as ID, name, email, and a map to store balances with other users.
 
        - The Group class represents a group in Splitwise, containing a list of member users and a list of expenses.
 
        - The Expense class represents an expense within a group, with properties such as ID, amount, description, the user who paid, and a list of splits.
 
        - The Split class is an abstract class representing the split of an expense. It is extended by EqualSplit, PercentSplit, and ExactSplit classes to handle different split methods.
 
        - The Transaction class represents a transaction between two users, with properties such as ID, sender, receiver, and amount.
 
        - The SplitwiseService class is the main class that manages the Splitwise system. It follows the Singleton pattern to ensure only one instance of the service exists.
 
        - The SplitwiseService class provides methods for adding users, groups, and expenses, splitting expenses, updating balances, settling balances, and creating transactions.
 
        - Multi-threading is achieved using concurrent data structures such as ConcurrentHashMap and CopyOnWriteArrayList to handle concurrent access to shared resources.
 
        - The SplitwiseDemo class demonstrates the usage of the Splitwise system by creating users, a group, adding an expense, settling balances, and printing user balances.
 
    
    
    Code Implementation:
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
// Abstract class for different split methods
abstract class Split {
    private User user;
    private double amount;
    public Split(User user) {
        this.user = user;
    }
    public User getUser() {
        return user;
    }
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amount) {
        this.amount = amount;
    }
}
// Equal Split
class EqualSplit extends Split {
    public EqualSplit(User user) {
        super(user);
    }
}
// Percent Split
class PercentSplit extends Split {
    private double percentage;
    public PercentSplit(User user, double percentage) {
        super(user);
        this.percentage = percentage;
    }
    public double getPercentage() {
        return percentage;
    }
}
// Exact Split
class ExactSplit extends Split {
    public ExactSplit(User user) {
        super(user);
    }
}
// User class
class User {
    private String id;
    private String name;
    private String email;
    private Map<String, Double> balances;
    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.balances = new ConcurrentHashMap<>();
    }
    public String getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public Map<String, Double> getBalances() {
        return balances;
    }
}
// Expense class
class Expense {
    private String id;
    private double amount;
    private String description;
    private User paidBy;
    private List<Split> splits;
    public Expense(String id, double amount, String description, User paidBy, List<Split> splits) {
        this.id = id;
        this.amount = amount;
        this.description = description;
        this.paidBy = paidBy;
        this.splits = splits;
    }
    public double getAmount() {
        return amount;
    }
    public List<Split> getSplits() {
        return splits;
    }
    public User getPaidBy() {
        return paidBy;
    }
}
// Group class
class Group {
    private String id;
    private String name;
    private List<User> members;
    private List<Expense> expenses;
    public Group(String id, String name) {
        this.id = id;
        this.name = name;
        this.members = new CopyOnWriteArrayList<>();
        this.expenses = new CopyOnWriteArrayList<>();
    }
    public List<User> getMembers() {
        return members;
    }
    public List<Expense> getExpenses() {
        return expenses;
    }
    public void addMember(User user) {
        members.add(user);
    }
    public void addExpense(Expense expense) {
        expenses.add(expense);
    }
}
// Splitwise Service
class SplitwiseService {
    private static SplitwiseService instance;
    private Map<String, User> users;
    private Map<String, Group> groups;
    private SplitwiseService() {
        users = new ConcurrentHashMap<>();
        groups = new ConcurrentHashMap<>();
    }
    public static SplitwiseService getInstance() {
        if (instance == null) {
            synchronized (SplitwiseService.class) {
                if (instance == null) {
                    instance = new SplitwiseService();
                }
            }
        }
        return instance;
    }
    public User addUser(String id, String name, String email) {
        User user = new User(id, name, email);
        users.put(id, user);
        return user;
    }
    public Group addGroup(String id, String name) {
        Group group = new Group(id, name);
        groups.put(id, group);
        return group;
    }
    public void addExpense(Group group, Expense expense) {
        group.addExpense(expense);
        updateBalances(expense);
    }
    private void updateBalances(Expense expense) {
        User paidBy = expense.getPaidBy();
        double amount = expense.getAmount();
        List<Split> splits = expense.getSplits();
        for (Split split : splits) {
            User owedBy = split.getUser();
            double splitAmount = split.getAmount();
            paidBy.getBalances().put(owedBy.getId(),
                    paidBy.getBalances().getOrDefault(owedBy.getId(), 0.0) + splitAmount);
            owedBy.getBalances().put(paidBy.getId(),
                    owedBy.getBalances().getOrDefault(paidBy.getId(), 0.0) - splitAmount);
        }
    }
    public void settleBalance(User user1, User user2) {
        double balance = user1.getBalances().getOrDefault(user2.getId(), 0.0);
        user1.getBalances().put(user2.getId(), 0.0);
        user2.getBalances().put(user1.getId(), 0.0);
        System.out.println("Settled balance of " + balance + " between " + user1.getName() + " and " + user2.getName());
    }
    public void printBalances(User user) {
        System.out.println("Balances for " + user.getName() + ":");
        for (Map.Entry<String, Double> entry : user.getBalances().entrySet()) {
            System.out.println("  Owes " + entry.getKey() + ": " + entry.getValue());
        }
    }
}
// Demo Class
public class SplitwiseDemo {
    public static void main(String[] args) {
        SplitwiseService service = SplitwiseService.getInstance();
        User u1 = service.addUser("1", "Alice", "alice@example.com");
        User u2 = service.addUser("2", "Bob", "bob@example.com");
        User u3 = service.addUser("3", "Charlie", "charlie@example.com");
        Group group = service.addGroup("g1", "Friends");
        group.addMember(u1);
        group.addMember(u2);
        group.addMember(u3);
        List<Split> splits = Arrays.asList(
                new EqualSplit(u1),
                new EqualSplit(u2),
                new EqualSplit(u3)
        );
        splits.forEach(split -> split.setAmount(100.0 / splits.size()));
        Expense expense = new Expense("e1", 100.0, "Dinner", u1, splits);
        service.addExpense(group, expense);
        service.printBalances(u1);
        service.printBalances(u2);
        service.printBalances(u3);
        service.settleBalance(u2, u1);
    }
}
    Input:
    
        - Alice pays $100 for dinner, split equally among Alice, Bob, and Charlie.
 
        - Balances are printed for all users.
 
        - Bob settles balance with Alice.
 
    
    Output:
    
Balances for Alice:
  Owes Bob: 33.33333333333333
  Owes Charlie: 33.33333333333333
Balances for Bob:
  Owes Alice: -33.33333333333333
Balances for Charlie:
  Owes Alice: -33.33333333333333
Settled balance of 33.33333333333333 between Bob and Alice
    
    
    Explanation of the Code:
    
        - User: Represents users with unique IDs and balances.
 
        - Group: Represents a group with members and expenses.
 
        - Expense: Represents an expense with splits among participants.
 
        - Split: Abstract base class for different split methods.
 
        - SplitwiseService: Singleton managing the entire Splitwise logic.
 
    
    
    Design Considerations:
    
        - Thread-safe implementation with ConcurrentHashMap and CopyOnWriteArrayList.
 
        - Extensibility with support for different split types.
 
        - Single responsibility principle applied across classes.
 
    
    
    Improvements:
    
        - Support for percentages and exact split methods in the demo.
 
        - Enhanced UI for better user experience.