#include "rationalnumber.h"

#include <iostream>
#include <cstdlib>					// exit
using namespace std;

static struct {
    int gcd, con, copy, des, assn, rel, add, sub, mul, div, in, out;
} stats;						// implicitly initialized to 0

static int gcd( int a, int b ) {
    for ( ;; ) {
	int r = a % b;
      if ( r == 0 ) break;
	a = b;
	b = r;
    } // for
    stats.gcd += 1;
    return b;
} // gcd

void Rationalnumber::statistics() {
    cerr
	<< "gcd:"  << stats.gcd  << '\t'
	<< "con:"  << stats.con  << '\t'
	<< "copy:" << stats.copy << '\t'
	<< "des:"  << stats.des  << '\t'
	<< "assn:" << stats.assn << '\t'
	<< "rel:"  << stats.rel  << '\t'
	<< "add:"  << stats.add  << '\t'
	<< "sub:"  << stats.sub  << '\t'
	<< "mul:"  << stats.mul  << '\t'
	<< "div:"  << stats.div  << '\t'
	<< "in:"   << stats.in   << '\t'
	<< "out:"  << stats.out
	<< endl;
} // Rationalnumber::statistics

bool Rationalnumber::eq( Rationalnumber &r ) {
    return num * r.denom == denom * r.num;
} // Rationalnumber::Rationalnumber::eq

bool Rationalnumber::lt( Rationalnumber &r ) {
    //int temp1 = denom * r.denom, temp2 = num * r.denom, temp3 = denom * r.num;
    //return temp1 > 0 && temp2 < temp3 || temp1 < 0 && temp2 > temp3;
    // if denominator is always > 0, only this check is necessary
    return num * r.denom < denom * r.num;
} // Rationalnumber::Rationalnumber::lt

void Rationalnumber::common1( int n, int d ) {
    num = n;
    denom = d;
    stats.con += 1;
} // Rationalnumber::common1

int Rationalnumber::common2( int &n, int &d ) {
    if ( d == 0 ) {
	cerr << "Invalid rational number construction: denominator cannot be equal to 0." << endl;
	exit( EXIT_FAILURE );
    } // exit
    if ( d < 0 ) { d = -d; n = -n; }			// move sign to numerator
    return gcd( abs( n ), d );				// simplify
} // Rationalnumber::common2

Rationalnumber::Rationalnumber() {
    common1( 0, 1 );
} // Rationalnumber::Rationalnumber

Rationalnumber::Rationalnumber( int n ) {
    common1( n, 1 );
} // Rationalnumber::Rationalnumber

Rationalnumber::Rationalnumber( int n, int d ) {
    int temp = common2( n, d );
    common1( n / temp, d / temp );
} // Rationalnumber::Rationalnumber

Rationalnumber::Rationalnumber( const Rationalnumber &c ) {
    num = c.num;
    denom = c.denom;
    stats.copy += 1;
} // Rationalnumber::Rationalnumber

Rationalnumber::~Rationalnumber() {
    stats.des += 1;
} // Rationalnumber::~Rationalnumber

Rationalnumber &Rationalnumber::operator=( const Rationalnumber &r ) {
    num = r.num;
    denom = r.denom;
    stats.assn += 1;
    return *this;
} // Rationalnumber::operator=

int Rationalnumber::numerator() const {
    return num;
} // Rationalnumber::numerator

int Rationalnumber::numerator( int n ) {
    int prev = num;
    int temp = gcd( abs( n ), denom );			// simplify
    num = n / temp;
    denom = denom / temp;
    return prev;
} // Rationalnumber::numerator
		   
int Rationalnumber::denominator() const {
    return denom;
} // Rationalnumber::denominator

int Rationalnumber::denominator( int d ) {
    int prev = denom;
    int temp = common2( num, d );
    num = num / temp;
    denom = d / temp;
    return prev;
} // Rationalnumber::denominator

bool operator==( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return l.eq( r );
} // operator==

bool operator!=( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return ! ( l.eq( r ) );
} // operator!=

bool operator<( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return l.lt( r );
} // operator<

bool operator<=( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return l.lt( r ) || l.eq( r );
} // operator<=

bool operator>( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return ! ( l.lt( r ) || l.eq( r ) );
} // operator>

bool operator>=( Rationalnumber l, Rationalnumber r ) {
    stats.rel += 1;
    return ! ( l.lt( r ) );
} // operator>=

Rationalnumber Rationalnumber::operator-() {
    return Rationalnumber( -num, denom );
} // Rationalnumber::operator-

Rationalnumber operator+( Rationalnumber l, Rationalnumber r ) {
    stats.add += 1;
    if ( l.denom == r.denom ) {				// special case
	return Rationalnumber( l.num + r.num, l.denom );
    } else {
	return Rationalnumber( l.num * r.denom + l.denom * r.num, l.denom * r.denom );
    } // if
} // operator+

Rationalnumber operator-( Rationalnumber l, Rationalnumber r ) {
    stats.sub += 1;
    if ( l.denom == r.denom ) {				// special case
	return Rationalnumber( l.num - r.num, l.denom );
    } else {
	return Rationalnumber( l.num * r.denom - l.denom * r.num, l.denom * r.denom );
    } // if
} // operator-

Rationalnumber operator*( Rationalnumber l, Rationalnumber r ) {
    stats.mul += 1;
    return Rationalnumber( l.num * r.num, l.denom * r.denom );
} // operator*

Rationalnumber operator/( Rationalnumber l, Rationalnumber r ) {
    stats.div += 1;
    if ( r.num < 0 ) { r.num = -r.num; r.denom = -r.denom; }
    return Rationalnumber( l.num * r.denom, l.denom * r.num );
} // operator/

istream &operator>>( istream &is, Rationalnumber &r ) {
    stats.in += 1;
    is >> r.num >> r.denom;
    int temp = Rationalnumber::common2( r.num, r.denom );
    r.num /= temp;
    r.denom /= temp;
    return is;
} // operator>>

ostream &operator<<( ostream &os, Rationalnumber r ) {
    stats.out += 1;
    return os << r.num << "/" << r.denom;
} // operator<<

// Local Variables: //
// compile-command: "make rationalnumber" //
// End: //
