header
//---------------------------------------------------------
// File: RationalTestProg.cpp
//
// Purpose: Test the rational class member functions and
//          operators.
//
// D. Searls
// Asbury College
// Feb 2002
//---------------------------------------------------------


#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;


class rational
{
private:

    long n;  // numerator
    long d;  // denominator
    
public:

    // Class constructors
    
    //---------------------------------------------------------
    // Default constructor creates a new rational with a value
    // of 0/1.
    //---------------------------------------------------------
    rational()
    {
        n = 0;
        d = 1;
    }
    
    //---------------------------------------------------------
    // Initialization constructor creates a new rational with
    // a value of numer/denom.
    //
    // Precondition: denom != 0
    //
    // In Parameters: numer, denom
    //---------------------------------------------------------
    rational(long numer, long denom)
    {
        init(numer, denom);
    }
    
    //---------------------------------------------------------
    // Copy constructor creates a new rational that has the
    // same value as r.
    //
    // In Parameter: r
    //---------------------------------------------------------
    rational(const rational& r)
    {
        n = r.n;
        d = r.d;
    }
    
    // Member functions
    
    //---------------------------------------------------------
    // Reduce a rational to lowest terms
    //
    // This function is not part of the public interface.
    //---------------------------------------------------------
    void reduce()
    {
        long gcd = abs(n);
        long y = abs(d);
        long temp;
        
        if (d == 0)
        {
            return;
        }
    
        // Make sure gcd is initalized as larger of two values
    
        if (gcd < y)
        {
            temp = gcd;
            gcd = y;
            y = temp;
        }
    
        // Find the gcd
    
        while (y != 0)
        {
            temp = gcd % y;
            gcd = y;
            y = temp;
        }
    
        // Reduce the fraction
    
        n = n / gcd;
        d = d / gcd;
    
        // Make sure denominator is positive
    
        if (d < 0)  // In a reduced fraction, denominator > 0
        {
            d = -d;
            n = -n;
        }
    }
    
    //---------------------------------------------------------
    // Convert a rational to the nearest integer value and
    // return that value
    //---------------------------------------------------------
    long toInt() const
    {
        bool isNegative;
    	double num;
    	long intValue;
    	
    	isNegative = (n < 0);
    	num = double(n)/double(d);
    	if (isNegative) {
    		num = -num;
    	}
    	intValue = long(num + 0.5);
    	if (isNegative) {
    		intValue = -intValue;
    	}
    	return intValue;
    }
    
    //---------------------------------------------------------
    // Convert a rational to a decimal value and return it
    //---------------------------------------------------------
    double toReal() const
    {
        return double(n)/double(d);
    }
    
    //---------------------------------------------------------
    // Initalize this rational to the value numer/denom.
    //
    // Precondition: denom != 0
    //
    // In Parameters: numer, denom
    //---------------------------------------------------------
    void init(long numer, long denom)
    {
        n = numer;
        d = denom;
        reduce();
    }
    
    //---------------------------------------------------------
    // Return sum of two rationals (this + r)
    //---------------------------------------------------------
    rational add(rational r) const
    {
        rational sum;
        if (d == r.d)
        {
            sum.init(n + r.n, d);
        }
        else
        {
            sum.init(n*r.d + d*r.n, d*r.d);
        }
    	sum.reduce();
        return sum;
    }
    
    //---------------------------------------------------------
    // Return difference of two rationals (this - r)
    //---------------------------------------------------------
    rational sub(rational r) const
    {
        rational diff(n*r.d - d*r.n, d*r.d);
    	diff.reduce();
        return diff;
    }
    
    //---------------------------------------------------------
    // Return product of two rationals (this*r)
    //---------------------------------------------------------
    rational mul(rational r) const
    {
        rational prod(n*r.n, d*r.d);
        prod.reduce();
    	return prod;
    }
    
    //---------------------------------------------------------
    // Return quotient of two rationals (this/r)
    //---------------------------------------------------------
    rational div(rational r) const
    {
        rational quotient(n*r.d, d*r.n);
        quotient.reduce();
    	return quotient;
    }
    
    //---------------------------------------------------------
    // Assign the value of the rational parameter to this
    // rational and return that value as the return value of
    // this function.
    //---------------------------------------------------------
    rational assign(rational r)
    {
        n = r.n;
        d = r.d;
    	return r;
    }
    
    //---------------------------------------------------------
    // Compare two rationals
    //
    // If (this < r)
    //     return a negative value
    // else if (this == r)
    //     return zero
    // else
    //     return a positive value
    //---------------------------------------------------------
    long compare(rational r) const
    {
        return (n*r.d - d*r.n);
    }
    
    //---------------------------------------------------------
    // Input a rational value from the keyboard as "n/d" where
    // n and d are integer values.
    //
    // It is assumed that the denominator will not be zero.
    //---------------------------------------------------------
    void input()
    {
        char slash;
    
        cin >> n;     // Read the numerator
        cin >> slash; // Read the divide symbol
        cin >> d;     // Read the denominator
        reduce();
    }
    
    //---------------------------------------------------------
    // Output a rational value to cout as "n/d" where
    // n and d are integer values. If the denominator is zero,
    // the text "NaR" (Not a Rational) will be displayed.
    //---------------------------------------------------------
    void output() const
    {
        if (d == 0)
        {
            cout << "NaR";
        }
        else
        {
            cout << n << '/' << d;
        }
    }
};

//---------------------------------------------------------
//               T E S T    P R O G R A M 
//---------------------------------------------------------

int main()
{
    rational r1;
    rational r2(6,8);
    rational r3(r2);

    // Verify constructors and output member function

    cout << "Default Constructor:        r1 = "; r1.output(); cout << endl;
	cout << "You SHOULD get:             r1 = 0/1\n\n";
    cout << "Initialization Constructor: r2 = "; r2.output(); cout << endl;
	cout << "You SHOULD get:             r2 = 3/4\n\n";
    cout << "Copy Constructor:           r3 = "; r3.output(); cout << endl;
	cout << "You SHOULD get:             r3 = 3/4\n\n";

    // Test Input

    cout << "Enter a rational in the form a/b: ";
    r3.input();
    cout << "You entered: "; r3.output(); cout << endl << endl;

    // Test arithmetic operators

    r1 = r2.add(r3);
    r2.output(); cout << " + "; r3.output(); cout << " = "; r1.output();
	cout << endl;

    r1 = r2.sub(r3);
    r2.output(); cout << " - "; r3.output(); cout << " = "; r1.output();
	cout << endl;

    r1 = r2.mul(r3);
    r2.output(); cout << " * "; r3.output(); cout << " = "; r1.output();
	cout << endl;

    r1 = r2.div(r3);
    r2.output(); cout << " / "; r3.output(); cout << " = "; r1.output();
	cout << endl << endl;

    // Test init

    r1.init(34,20);
	cout << "After r1.init(34,20)   r1 = "; r1.output();
	cout << endl;
	cout << "You SHOULD have gotten r1 = 17/10\n";

    r1.init(12,-31);
	cout << "After r1.init(12,-31)  r1 = "; r1.output();
	cout << endl;
	cout << "You SHOULD have gotten r1 = -12/31\n\n";

    // Test assignment

	r3.assign(r2.assign(r1));
    cout << "After r3.assign(r2.assign(r1))  r2 = "; r2.output();
	cout << endl;
	cout << "                                r3 = "; r3.output();
	cout << endl;
	cout << "Both r2 and r3 SHOULD equal r1: r1 = -12/31\n\n";

    // Test compare

    r1.init(1, 2);
	r2.init(1, 3);
    r1.output(); cout << ".compare("; r2.output(); cout << ") = " << r1.compare(r2) << endl;
    r2.output(); cout << ".compare("; r1.output(); cout << ") = " << r2.compare(r1) << endl;
    r1.output(); cout << ".compare("; r1.output(); cout << ") = " << r1.compare(r1) << endl;
	cout << endl;

    // Test conversion functions

    cout << fixed << setprecision(10);
	r1.init(9, 5);
	r2.init(-100, 3);
    r1.output(); cout << " to the nearest integer is " << r1.toInt() << endl;
    r1.output(); cout << " as a real is " << r1.toReal() << endl;
    r2.output(); cout << " to the nearest integer is " << r2.toInt() << endl;
    r2.output(); cout << " as a real is " << r2.toReal() << endl;
    cout << endl;
    

    //r1.reduce();          // Trying to access a private member function
    //r1.n = 3;             // or private data members results in compile
    //r1.d = 4;             // errors.

    return 0;
}