Created
January 7, 2026 21:24
-
-
Save light-bringer/7dd93a57548bd5ba80291b28334efaa8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import defaultdict | |
| from decimal import Decimal, ROUND_HALF_UP | |
| # ----------------------------- | |
| # Configuration | |
| # ----------------------------- | |
| PEOPLE = ["Buro", "Tablu", "Shreshtha", "Meghna", "Sayani"] | |
| # All expenses: (amount, paid_by) | |
| EXPENSES = [ | |
| # 26th | |
| (2962, "Buro"), | |
| (3300, "Buro"), | |
| (913, "Tablu"), | |
| (4500, "Tablu"), | |
| # 27th | |
| (750, "Buro"), | |
| (6600, "Buro"), | |
| (1420, "Buro"), | |
| # 28th | |
| (730, "Buro"), | |
| (1180, "Buro"), | |
| (4050, "Tablu"), | |
| (10000, "Buro"), | |
| (1420, "Tablu"), | |
| # 29th | |
| (1250, "Tablu"), | |
| (3870, "Buro"), | |
| (2870, "Buro"), | |
| (550, "Buro"), | |
| (4050, "Tablu"), | |
| (4065, "Tablu"), | |
| # 30th | |
| (1065, "Buro"), | |
| (550, "Tablu"), | |
| (2000, "Buro"), | |
| (250, "Shreshtha"), | |
| (250, "Tablu"), | |
| # 31st | |
| (4000, "Buro"), | |
| (850, "Buro"), | |
| (250, "Buro"), | |
| (640, "Tablu"), | |
| (3817, "Buro"), | |
| (3200, "Tablu"), | |
| # 1st | |
| (5000, "Buro"), | |
| (2599, "Tablu"), | |
| (565, "Tablu"), | |
| (1310, "Tablu"), | |
| (1695, "Buro"), | |
| ] | |
| COLLECTOR = "Tablu" | |
| REPRESENTED_BY_COLLECTOR = ["Tablu", "Meghna", "Sayani"] | |
| # ----------------------------- | |
| # Helpers | |
| # ----------------------------- | |
| TWOPLACES = Decimal("0.01") | |
| def D(value): | |
| return Decimal(str(value)).quantize(TWOPLACES, ROUND_HALF_UP) | |
| # ----------------------------- | |
| # Splitwise Engine | |
| # ----------------------------- | |
| class SplitwiseEngine: | |
| def __init__(self, people): | |
| self.people = people | |
| self.balance = defaultdict(Decimal) | |
| def add_expense(self, amount, paid_by): | |
| amount = D(amount) | |
| split = (amount / len(self.people)).quantize(TWOPLACES) | |
| for p in self.people: | |
| self.balance[p] -= split | |
| self.balance[paid_by] += amount | |
| def get_balances(self): | |
| return {p: self.balance[p].quantize(TWOPLACES) for p in self.people} | |
| def settle_with_collector(self, collector, represented): | |
| settlements = [] | |
| # Step 1: represented people pay collector | |
| for p in represented: | |
| if self.balance[p] < 0: | |
| amt = -self.balance[p] | |
| settlements.append(f"{p} pays {collector}: {amt:.2f}") | |
| self.balance[collector] += amt | |
| self.balance[p] = D(0) | |
| # Step 2: remaining debtors and creditors | |
| creditors = { | |
| p: amt for p, amt in self.balance.items() | |
| if amt > 0 and p != collector | |
| } | |
| debtors = { | |
| p: -amt for p, amt in self.balance.items() | |
| if amt < 0 | |
| } | |
| # Direct settlements (non-represented debtors) | |
| for d, d_amt in debtors.items(): | |
| for c in list(creditors): | |
| if d_amt <= 0: | |
| break | |
| pay = min(d_amt, creditors[c]) | |
| settlements.append(f"{d} pays {c}: {pay:.2f}") | |
| d_amt -= pay | |
| creditors[c] -= pay | |
| self.balance[c] -= pay | |
| # Step 3: collector pays remaining creditors | |
| for c, amt in creditors.items(): | |
| if amt > 0: | |
| settlements.append(f"{collector} pays {c}: {amt:.2f}") | |
| self.balance[collector] -= amt | |
| self.balance[c] = D(0) | |
| self.balance[collector] = D(0) | |
| return settlements | |
| # ----------------------------- | |
| # Main Execution | |
| # ----------------------------- | |
| def main(): | |
| engine = SplitwiseEngine(PEOPLE) | |
| for amount, payer in EXPENSES: | |
| engine.add_expense(amount, payer) | |
| print("================================") | |
| print("TOTAL BALANCES") | |
| print("================================") | |
| balances = engine.get_balances() | |
| for p in PEOPLE: | |
| print(f"{p:<10}: {balances[p]:>10.2f}") | |
| print("\n================================") | |
| print("FINAL SETTLEMENT") | |
| print("================================") | |
| settlements = engine.settle_with_collector( | |
| collector=COLLECTOR, | |
| represented=REPRESENTED_BY_COLLECTOR | |
| ) | |
| for s in settlements: | |
| print(s) | |
| print("\n================================") | |
| print("ALL SETTLED ✔") | |
| print("================================") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment