-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCashFlow.cpp
More file actions
165 lines (137 loc) · 4.7 KB
/
CashFlow.cpp
File metadata and controls
165 lines (137 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "CashFlow.h"
#include "Utils.h"
#include <algorithm>
#include <cmath>
#include <fstream>
#include <queue>
namespace {
struct HeapNode {
double amount;
int person;
bool operator<(const HeapNode& other) const {
if (std::fabs(amount - other.amount) > EPSILON) {
return amount < other.amount;
}
return person > other.person;
}
};
// Pushes positive balances into creditors and negative balances into debtors.
void buildHeaps(const std::vector<double>& balances,
std::priority_queue<HeapNode>& creditors,
std::priority_queue<HeapNode>& debtors) {
for (int person = 0; person < static_cast<int>(balances.size()); ++person) {
if (balances[person] > EPSILON) {
creditors.push({balances[person], person});
} else if (balances[person] < -EPSILON) {
debtors.push({-balances[person], person});
}
}
}
}
int CashFlowSystem::getOrCreatePerson(const std::string& name) {
auto it = personToId.find(name);
if (it != personToId.end()) {
return it->second;
}
int id = static_cast<int>(people.size());
people.push_back(name);
personToId[name] = id;
return id;
}
bool CashFlowSystem::addTransaction(const std::string& from, const std::string& to, double amount) {
if (from == to || amount <= EPSILON) {
return false;
}
Transaction transaction{getOrCreatePerson(from), getOrCreatePerson(to), amount};
transactions.push_back(transaction);
undoStack.push(transaction);
return true;
}
bool CashFlowSystem::undoLastTransaction() {
if (undoStack.empty() || transactions.empty()) {
return false;
}
undoStack.pop();
transactions.pop_back();
return true;
}
void CashFlowSystem::reset() {
people.clear();
personToId.clear();
transactions.clear();
while (!undoStack.empty()) {
undoStack.pop();
}
}
std::vector<double> CashFlowSystem::calculateNetBalances() const {
std::vector<double> balances(people.size(), 0.0);
for (const Transaction& transaction : transactions) {
balances[transaction.from] -= transaction.amount;
balances[transaction.to] += transaction.amount;
}
return balances;
}
SettlementResult CashFlowSystem::minimizeTransactions() const {
std::priority_queue<HeapNode> creditors;
std::priority_queue<HeapNode> debtors;
buildHeaps(calculateNetBalances(), creditors, debtors);
SettlementResult result;
while (!creditors.empty() && !debtors.empty()) {
HeapNode creditor = creditors.top();
HeapNode debtor = debtors.top();
creditors.pop();
debtors.pop();
double settledAmount = std::min(creditor.amount, debtor.amount);
double creditorRemaining = creditor.amount - settledAmount;
double debtorRemaining = debtor.amount - settledAmount;
result.settlements.push_back({debtor.person, creditor.person, settledAmount});
result.steps.push_back({
debtor.person,
creditor.person,
debtor.amount,
creditor.amount,
settledAmount,
debtorRemaining,
creditorRemaining
});
if (creditorRemaining > EPSILON) {
creditors.push({creditorRemaining, creditor.person});
}
if (debtorRemaining > EPSILON) {
debtors.push({debtorRemaining, debtor.person});
}
}
return result;
}
bool CashFlowSystem::exportSettlements(const std::string& filePath) const {
std::ofstream out(filePath);
if (!out) {
return false;
}
SettlementResult result = minimizeTransactions();
out << "Cash Flow Minimization - Final Settlements\n";
out << "Participants: " << people.size() << "\n";
out << "Original transactions: " << transactions.size() << "\n";
out << "Optimized transactions: " << result.settlements.size() << "\n\n";
if (result.settlements.empty()) {
out << "No settlements needed. Everyone is already balanced.\n";
return true;
}
for (const Settlement& settlement : result.settlements) {
out << people[settlement.from] << " pays " << people[settlement.to]
<< " : " << Utils::formatMoney(settlement.amount) << "\n";
}
return true;
}
const std::vector<std::string>& CashFlowSystem::getPeople() const {
return people;
}
const std::vector<Transaction>& CashFlowSystem::getTransactions() const {
return transactions;
}
int CashFlowSystem::transactionCount() const {
return static_cast<int>(transactions.size());
}
int CashFlowSystem::participantCount() const {
return static_cast<int>(people.size());
}