#include "bank.h" #include #include using namespace std; double Client::earn (const double amount) { underMattress += amount; return amount; } double Client::getMattress () { return underMattress; } auto Bank::countEmployees () { return employees.size (); } auto Bank::countClients () { return clients.size (); } bool Bank::openAccount (Client &x) { if (employees.size () * CLIENT_EMPLOYEE <= clients.size ()) return false; // Insufficient employee capacity. for (auto i : x.accounts) if (i == this) return false; clientRecord newClient; newClient.id = x.id; clients.push_back (newClient); x.accounts.push_back (this); return true; } bool Bank::closeAccount (Client &x) { for (auto i = clients.begin (); i != clients.end (); i++) if (i->id == x.id) { if (i->loans.size () != 0) return false; // Can't close account with outstanding loans. else if (this->withdraw (x, this->getCurrent (x)) == -3) return false; // Bank can't pay for withdrawal and wasn't bailed out. else { clients.erase (i); for (auto j = x.accounts.begin (); j != x.accounts.end (); j++) if (*j == this) { auto position = j - x.accounts.begin (); x.accounts.erase (x.accounts.begin () + position); return true; } } } return false; // Account doesn't exist. } // Only used in ~Client void Bank::closeAccountNoBail (Client &x) { for (auto i = clients.begin (); i != clients.end (); i++) if (i->id == x.id) { clients.erase (i); return; } } Client::~Client () { for (auto i : accounts) { i->closeAccountNoBail (*this); } } bool Bank::hire (Employee &x) { if (x.employer != nullptr) return false; employees.push_back (&x); x.employer = this; return true; } bool Bank::fire (Employee &x) { for (auto i = employees.begin (); i != employees.end (); i++) if (*i == &x) { employees.erase (i); x.employer = nullptr; return true; } return false; // Can't fire unemployed. } // Deposit money from under the mattress, return amount. double Bank::deposit (Client &x, const double amount) { if (amount == 0) return 0; if (amount < 0) return 0; // Can't be negative. if (x.underMattress < amount) return 0; for (auto i = clients.begin (); i != clients.end (); i++) if (i->id == x.id) { x.underMattress -= amount; i->current += amount; current += amount; return amount; } return -1; // No such account. } // Withdraw from current account, if bank doesn't have amount in current, will // attempt to withdraw required amount from Gov. If unable, will attempt // bailout. double Bank::withdraw (Client &x, const double amount) { if (amount == 0) return 0; if (amount < 0) return 0; // Can't be negative. for (auto i = clients.begin (); i != clients.end (); i++) if (i->id == x.id) { if (i->current < amount) return 0; if (current < amount) { if (this->centralBank->reserveWithdraw (*this, ceil (amount - current)) < 0) { if (!this->centralBank->bailout (*this, ceil (amount - current))) return -3; // Bank unable to pay, wasn't bailed out. } } i->current -= amount; x.underMattress += amount; current -= amount; return amount; } return -1; // No such account. } double Bank::getCurrent () { return current; } double Bank::getCreditable () { return creditable; } double Bank::getCredited () { return credited; } double Bank::getCurrent (const Client &x) { for (auto i : clients) if (i.id == x.id) return i.current; cout << "Client doesn't have an account at bank." << endl; return 0; } bool Gov::associate (Bank *const x) { for (auto i = banks.begin (); i != banks.end (); i++) if (*i == x) return false; // Can't associate twice. x->centralBank = this; banks.push_back (x); return true; } double Gov::getReserve () { return reserve; } double Gov::reserveDeposit (Bank &x, const double amount) { if (amount == 0) return 0; if (amount < 0) return 0; // Can't be negative. if (x.current < amount) return 0; x.current -= amount; reserve += amount; x.creditable += amount * FACTOR; return amount; } double Gov::reserveWithdraw (Bank &x, const double amount) { if (amount == 0) return 0; if (amount < 0) return 0; // Can't be negative. if (x.creditable < (amount * FACTOR)) return -3; // Bank can't repay. x.creditable -= amount * FACTOR; reserve -= amount; x.current += amount; return amount; } // Functions for simulating giving and receiving back loans (no interest). Used // for testing until loans are implemented. bool Bank::loan (const double amount) { if (amount == 0) return 0; if (creditable < amount) return 0; creditable -= amount; credited += amount; return amount; } bool Bank::unloan (const double amount) { if (amount == 0) return 0; if (credited < amount) return 0; creditable += amount; credited -= amount; return amount; } // Gov bailout for Bank's unfulfillable Client withdrawals. 50% chance, seeded // with time since last bailout. bool Gov::bailout (Bank &x, const double amount) { auto now = time (0); srand (now - lastBailout); if (rand () > (RAND_MAX / 2)) { lastBailout = now; x.current += amount; // Just like magic. return true; } return false; } Employee::~Employee () { if (employer != nullptr) { employer->fire (*this); } } int main () { Client abdul ("abdul"); abdul.earn (666); abdul.earn (10); if (abdul.getMattress () != 676) exit (1); Gov fed; Bank boa (&fed); if (fed.associate (&boa)) exit (1); // Can't associate twice. if (boa.openAccount (abdul)) exit (1); // Over employee capacity. Employee dude ("dude"); Employee gal ("gal"); boa.hire (dude); if (boa.countEmployees () != 1) exit (1); boa.fire (gal); // Can't fire non-employed. if (boa.countEmployees () != 1) exit (1); boa.fire (dude); if (boa.countEmployees () != 0) exit (1); boa.hire (gal); boa.hire (dude); boa.hire (dude); // Can't hire employed. if (boa.countEmployees () != 2) exit (1); if (boa.openAccount (abdul) != 1) exit (1); if (boa.openAccount (abdul) != 0) exit (1); // Can't open existing client account. if (boa.countClients () != 1) exit (1); if (abdul.getMattress () != 676) exit (1); boa.deposit (abdul, 500); if (boa.getCurrent (abdul) != 500) exit (1); if (abdul.getMattress () != 176) exit (1); if (boa.getCurrent () != 500) exit (1); boa.withdraw (abdul, 30); Client jane ("jane"); jane.earn (1000); boa.openAccount (jane); boa.deposit (jane, 500); if (boa.getCurrent (abdul) != 470) exit (1); if (abdul.getMattress () != 206) exit (1); if (boa.getCurrent (jane) != 500) exit (1); if (jane.getMattress () != 500) exit (1); if (boa.getCurrent () != 970) exit (1); Client joe ("joe"), bob ("bob"), frank ("frank"); boa.openAccount (joe); boa.openAccount (bob); boa.openAccount (frank); // Not added (5th client). if (boa.countClients () != 4) exit (1); // Over employee capacity. boa.closeAccount (abdul); // Success if (boa.countClients () != 3) exit (1); if (boa.getCurrent () != 500) exit (1); if (abdul.getMattress () != 676) exit (1); fed.reserveDeposit (boa, 250); if (fed.getReserve () != 250) exit (1); if (boa.getCreditable () != 1500) exit (1); fed.reserveWithdraw (boa, 125); fed.reserveWithdraw (boa, 1000); // Can't repay. if (fed.getReserve () != 125) exit (1); if (boa.getCreditable () != 750) exit (1); boa.loan (250); bool bailed = boa.closeAccount (jane); // Bank can't pay, Gov may bail out. if (bailed == 0 // Not bailed out. && (jane.getMattress () != 500 || boa.getCreditable () != 500 || boa.getCredited () != 250 || boa.getCurrent () != 375)) exit (1); if (bailed == 1 // Bailed out. && (jane.getMattress () != 1000 || boa.getCreditable () != 500 || boa.getCredited () != 250 || boa.getCurrent () != 0)) exit (1); joe.earn (1000); boa.deposit (joe, 1000); fed.reserveDeposit (boa, 500); boa.loan (100); boa.withdraw (joe, 50); // Test withdrawal bank can pay. if (joe.getMattress () != 50) exit (1); boa.withdraw ( joe, 500); // Test withdrawal bank can pay by withdrawing from reserves. if (joe.getMattress () != 550) exit (1); { // Test ~Employee Employee tempemp ("underwood"); boa.hire (tempemp); } if (boa.countEmployees () != 2) exit (1); { Client tempclient ("guy"); boa.openAccount (tempclient); } if ((boa.countClients () != 2 && bailed == 1) || (boa.countClients () != 3 && bailed == 0)) exit (1); // Run until both 0 and 1 are seen, in order to test both code paths for // bailout. cout << "Bailed out: " << bailed << ", all others successful." << endl; return 0; }