// fraction.h
// ===================
// Kole Kantner
// CAE Homework #5
// Fraction class


// The Program fraction declares a class of type Fraction that contains two
// private members numer and denom corresponding to numerator and denominator.
//
// The following public member functions are declared to operate on the
// class Fraction:
//   default and initializer constructors;
//   algebraic operators +, -, * and /;
//   conversion to decimal equivalent;
//   simplification;
//   output to write fractions in form a/b;
//   equality test.
//
// The main section declares several fractions and tests the member functions.

// Class declaration

class Fraction { 
public: 
  Fraction (); 
  Fraction (int num, int den);
  double float_equiv ();
  void simplify();
  void write();
  int operator== (Fraction frac);
  Fraction& operator= (Fraction rhs);
  Fraction operator+ (Fraction frac);
  Fraction operator- (Fraction frac); 
  Fraction operator* (Fraction frac);
  Fraction operator/ (Fraction frac);
private:
  int numer;
  int denom;
}; 


ostream& operator<< (ostream& out, Fraction frac);
istream& operator>> (istream& in, Fraction& frac);


// declaration for a greatest common divisor function. 
// useful for simplification. 
int find_gcd (int a, int b); 


// Definition of Member Functions

// Constructors

Fraction::Fraction()  // Default constructor
{
  numer = 0; 
  denom = 1;
}

Fraction::Fraction (int num, int den)  // Constructor with initialization
{
  numer = num;
  denom = den; 
}


// Miscelaneous Member Functions: output, type conversion, simplification
// and test for equality.

void Fraction::write ()  // Write a fraction in the form a/b
{
  if (denom == 1)
    cout << numer;         // Write the fraction a/1 as a
  else
    cout << numer << "/" << denom; 
}

double Fraction::float_equiv()  // Convert a fraction to its decimal equivalent
{
  return(double(numer) / double(denom));
}

// Simplify the fraction by dividing both numerator and denominator by the gcd
void Fraction::simplify()  
{
  int fraction_gcd = find_gcd(numer, denom);
  numer = numer / fraction_gcd;
  denom = denom / fraction_gcd;

  if (denom < 0) { 
    numer = - numer;  // For negative fractions make the numerator negative
    denom = - denom;  // so the "-" leads the fraction when written.
  }
}

// Test for equality of two fractions.
// Simplify fractions so that equality can be tested by equality of
// numerators and denominators.
// Use temporary fractions frac1 and frac2 so simplification doesn't modify
// original fractions.
int Fraction::operator== (Fraction frac) 
{
  Fraction frac1(numer, denom);
  Fraction frac2(frac.numer, frac.denom);
  frac1.simplify();
  frac2.simplify();
  return((frac1.numer == frac2.numer) && (frac1.denom == frac2.denom));
}

Fraction& Fraction::operator= (Fraction rhs) {
  numer = rhs.numer;
  denom = rhs.denom;
  return *this;
}

ostream& operator<< (ostream& out, Fraction frac) {
  if (frac.denom == 1)
    out << frac.numer;         // Write the fraction a/1 as a
  else
    out << frac.numer << "/" << frac.denom;
  return out;
}

ostream& operator>> (istream& in, Fraction& frac) {
  cout << "\n Input fraction numerator and denominator:";
  in >> frac.numer;
  in >> frac.denom;
  return in;
}


// Algebraic operator Member Functions.

// The results of the following fraction operators +, -, * and / are all
// simplified after the operation is performed to aid in readability, and
// to reduce the size of the individual numbers to avoid overflow.
 
// In order to perform addition of two fractions, both denominators must
// be the same.  Since the result will be simplified anyway, an easy way to
// make both denominators identical is to multiply each fraction's numerator
// and denominator by the other fraction's original denominator.
Fraction Fraction::operator+ (Fraction frac)  // Add two fractions
{
  int num_result = (numer * frac.denom) + (denom * frac.numer);
  int den_result = denom * frac.denom;
  Fraction result(num_result, den_result);
  result.simplify();
  return(result);
}

Fraction Fraction::operator- (Fraction frac)  // Take the difference
{
  int num_result = (numer * frac.denom) - (denom * frac.numer);
  int den_result = denom * frac.denom;
  Fraction result(num_result, den_result);
  result.simplify();
  return(result);
}

Fraction Fraction::operator* (Fraction frac)  // Multiply two fractions
{
  int num_result = numer * frac.numer;
  int den_result = denom * frac.denom;
  Fraction result(num_result, den_result);
  result.simplify();
  return(result);
}

// Find the quotient of two fractions.  A divide-by-zero error will result
// if the second fraction is zero, as regular division does.
// To avoid truncation errors, do division by multiplying the numerator
// fraction by the inverted denominator fraction.
Fraction Fraction::operator/ (Fraction frac) 
{
  int num_result = numer * frac.denom;
  int den_result = denom * frac.numer;
  Fraction result(num_result, den_result);
  result.simplify();
  return(result);
}



// Utilities 

// The function find_gcd determines the greatest common divisor of two
// variables a and b using Euclid's algorithm.
// Function find_gcd requires that b not equal zero.
//
// Explanation of Euclid's algorithm:
// Initially, a = numerator and b = denominator.  Suppose that, at the
// beginning of an iteration, a and b have certain values, and let a' and b'
// be their values at the beginning of the next iteration.  Euclid's
// algorithm works by assigning b to a' and  a % b  to  b'.  The loop
// repeats until b is zero, at which time a is the greatest common divisor.
int find_gcd (int a, int b)
{
  int remainder;

  while (b != 0) {
    remainder = a % b;
    a = b;               // a' gets b
    b = remainder;       // b' gets a % b
  }
  return(a);
}
