////////////////////////////////////////////////////////////////////
// An abstract data type for Money values                         //
// Roger Hartley, Spring 2000                                     //
// Values of type Money are represented as two integers for the   //
// whole number part and the fractional part.                     //
// e.g. $123.45 is represented as 123 and 45                      //
// Each value also stores its currency symbol (default '$') for   //
// printing purposes.                                             //
// Necessary operations include creation (construction), addition,//
// multiplication, comparisons and division to produce a ratio.   //
////////////////////////////////////////////////////////////////////

#include <iostream>
#include <values.h>  // contains definition of MININT
using namespace std;

class Money {
private:
  long whole;
  unsigned char fraction;
  char prefix;
  double Convert() const;
public:
  Money(char = '$');
  Money(double, char = '$');
  Money(long, short, char = '$');
  void Insert(ostream&) const;
  void Extract(istream&);
  Money Add(const Money&) const;
  Money Mult(double) const;
  Money Mult(int) const;
  bool Equal(const Money&) const;
  bool Less(const Money&) const;
  bool Greater(const Money&) const;
  double Ratio(const Money&) const;
};

// internal arithmetic is done with doubles
double Money::Convert() const {
  if (whole < 0)
    return (double)whole - fraction / 100.0;
  else
    return (double)whole + fraction / 100.0;
}

Money::Money(char c) {
  whole = fraction = 0;
  prefix = c;
}

Money::Money(double m, char c) {
  // construct a Money object from a double
  double t = m;
  if (m < 0)
    t = -t;
  // the 0.5 adjusts for possible rounding problems
  long amt = (long)(t * 100 + 0.5);
  whole = amt / 100;
  if (m < 0)
    whole = -whole;
  fraction = amt % 100;
  prefix = c;
}

Money::Money(long w, short f, char c) {
  // construct a Money object from whole and fractional parts
  whole = w;
  fraction = f;
  prefix = c;
}

void Money::Insert(ostream& sout) const {
  if (whole < 0) {
    sout << '(' << prefix;
    // since negative values are supported through a negative
    // value for whole, there is problem with a zero value, 
    // so store "-ve zero" as MININT
    if (whole == MININT)
      sout << 0;
    else
      sout << -whole;
  }
  else {
    sout << prefix << whole;
  }
  sout << '.';
  sout.width(2);
  sout.fill('0');
  sout << (int)fraction;
  if (whole < 0)
    sout << ')';
}

void Money::Extract(istream& sin) {
  char c;
  sin >> c;
  if (c == '(')
    sin >> prefix;
  else
    prefix = c;
  sin >> whole;
  if (c == '(') {
    // see comment in Insert for MININT
    if (whole == 0)
      whole = MININT;
    else
      whole = -whole;
  }
  sin >> c;
  int f;
  sin >> f;
  fraction = (unsigned char)f;
  if (c == '(')
    sin >> c;
}

Money Money::Add(const Money& m) const {
  // add two Money values
  double amt = Convert() + m.Convert();
  return Money(amt);	
}

Money Money::Mult(double f) const {
  // multiply a Money value by a double
  double amt = Convert() * f;
  return Money(amt);
}

Money Money::Mult(int f) const {
  // multiply a Money value by an integer
  double amt = Convert() * f;
  return Money(amt);
}

double Money::Ratio(const Money& m) const {
  // calculate the ratio of two Money values
  return Convert() / m.Convert();
}

bool Money::Equal(const Money& m) const {
  // test the equality of two Money values
  return whole == m.whole && fraction == m.fraction && prefix == m.prefix;
}

bool Money::Less(const Money& m) const {
  // test whether one Money value is less than another
  if (prefix != m.prefix)
    return false;
  return Convert() < m.Convert();
}

bool Money::Greater(const Money& m) const {
  // test whether one Money value is greater than another
  if (prefix != m.prefix)
    return false;
  return Convert() > m.Convert();
}

ostream& operator<<(ostream& sout, const Money& m) {
  m.Insert(sout);
  return sout;
}

istream& operator>>(istream& sin, Money& m) {
  m.Extract(sin);
  return sin;
}

Money operator+(const Money& m1, const Money& m2) {
  return m1.Add(m2);
}

Money operator*(const Money& m, double f) {
  return m.Mult(f);
}

Money operator*(const Money& m, int f) {
  return m.Mult(f);
}

double operator/(const Money& m1, const Money& m2) {
  return m1.Ratio(m2);
}

bool operator==(const Money& m1, const Money& m2) {
  return m1.Equal(m2);
}

bool operator>(const Money& m1, const Money& m2) {
  return m1.Greater(m2);
}

bool operator<(const Money& m1, const Money& m2) {
  return m1.Less(m2);
}


int main() {
  // object creation
  Money m1, m2(123.45), m3(234.56);
    
  // input and output
  cout << "m1 is " << m1 << endl;
  cin >> m1; // make sure this reads the value $345.67
  cout << "m1 is " << m1 <<endl;
  cout << "m2 is " << m2 <<endl;
  cout << "m3 is " << m3 <<endl;
  
  // arithmetic 
  m1 = m2 + m3 * 2;
  cout << "m1 is now " << m1 << endl;
  // division
  double ratio = m3 / m2;
  cout << "ratio of m3 and m2 is " << ratio << endl;
  
  // comparision
  if (m3 > m2)
    cout << "m3 is bigger than m2" << endl;
  else
    cout << "m2 is bigger than m3" << endl;
  
  return 0;
}

