be86d1338a
Signed-off-by: Abdulkadir Furkan Şanlı <me@abdulocra.cy>
433 lines
9.2 KiB
C++
433 lines
9.2 KiB
C++
#include "bank.h"
|
|
#include <cmath>
|
|
#include <iostream>
|
|
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;
|
|
}
|