20-CS-122-001 Computer Science II Spring 2012
Rational Number class

Virtual functions, classes, inheritance, lists, queues, stacks, applications

Prev     Next     Lectures     rational.cc     rational.h     rationaltest.cc     rattest.cc

#include <string.h>
#include <stdlib.h>
#include <iostream>
#include "rational.h"
using namespace std;

// As late as October 2007 there is no provision for negative numbers or
// negative results.
BigInt *gcd (BigInt *m, BigInt *n) {
   BigInt *r;
   if (n->lessThan(m)) r = m->mod(n); else r = n->mod(m);
   while (!r->equals(new BigInt("0"))) {
      if (n->lessThan(m)) {
         m = n;  
         n = r;  
      } else {
         n = m;  
         m = r;  
      }
      if (n->lessThan(m)) r = m->mod(n); 
      else r = n->mod(m);
   }
   if (n->lessThan(m)) return n; else return m;
}

ostream & operator << (ostream &out, Rational &f) {
   if (!f.denom->equals(new BigInt("1")))
      out << f.num << "/" << f.denom;
   else
      out << f.num;
   return out;
}

ostream & operator << (ostream &out, Rational *f) {
   if (f == NULL) return out;
   if (!f->denom->equals(new BigInt("1")))
      out << f->num << "/" << f->denom;
   else
      out << f->num;
   return out;
}

istream & operator >> (istream &in, Rational &f) {
   f.read(in);
   return in;
}

void Rational::read(istream &in) {
   char num[1024], denom[1024], c[1024];
   in >> num >> c;
   if (!strcmp(c,"/")) in >> denom;
   else strcpy(denom,c);

   BigInt *g = NULL;
   BigInt *numerator = new BigInt(num);
   BigInt *denominator = new BigInt(denom);

   if (denominator->equals(new BigInt("0"))) {
      cerr << " Rational: denominator is 0\n";
      exit(1);
   }
   if (numerator->equals(new BigInt("0"))) {  
      this->num = new BigInt("0"); 
      this->denom = new BigInt("1"); 
      return;  
   }
   g = gcd(numerator, denominator);
   this->num = numerator->divide(g);
   this->denom = denominator->divide(g);
}

Rational &Rational::operator*(Rational &f) {
   return *new Rational(f.num->multiply(num), 
                        f.denom->multiply(denom));
}

Rational &Rational::operator*(Rational *f) {
   return *new Rational(f->num->multiply(num), 
                        f->denom->multiply(denom));
}

Rational *Rational::multiply(Rational *f) {
   return new Rational(f->num->multiply(num), 
                       f->denom->multiply(denom));
}

Rational &Rational::operator/(Rational &f) {
   return *new Rational(f.denom->multiply(num), 
                        f.num->multiply(denom));
}

Rational &Rational::operator/(Rational *f) {
   return *new Rational(f->denom->multiply(num), 
                        f->num->multiply(denom));
}

Rational *Rational::divide(Rational *f) {
   return new Rational(f->denom->multiply(num),
                        f->num->multiply(denom));
}

Rational &Rational::operator/(int t) {
   return *new Rational(num, denom->multiply(new BigInt(t)));
}

Rational &Rational::operator+(Rational &f) {
   BigInt *n = f.num->multiply(denom)->add(f.denom->multiply(num));
   BigInt *d = f.denom->multiply(denom);
   return *new Rational(n,d); 
}

Rational &Rational::operator+(Rational *f) {
   BigInt *n = f->num->multiply(denom)->add(f->denom->multiply(num));
   BigInt *d = f->denom->multiply(denom);
   return *new Rational(n,d); 
}

Rational *Rational::add(Rational *f) {
   BigInt *n = f->num->multiply(denom)->add(f->denom->multiply(num));
   BigInt *d = f->denom->multiply(denom);
   return new Rational(n,d); 
}        

Rational &Rational::operator-(Rational &f) {
   BigInt *n = f.denom->multiply(num)->subtract(f.num->multiply(denom));
   BigInt *d = f.denom->multiply(denom);
   return *new Rational(n,d);
}

Rational &Rational::operator-(Rational *f) {
   BigInt *n = f->denom->multiply(num)->subtract(f->num->multiply(denom));
   BigInt *d = f->denom->multiply(denom);
   return *new Rational(n,d);
}

Rational *Rational::subtract(Rational *f) {
   BigInt *n = f->denom->multiply(num)->subtract(f->num->multiply(denom));
   BigInt *d = f->denom->multiply(denom);
   return new Rational(n,d);
}

Rational &Rational::operator=(Rational &f) {
   num = f.num;
   denom = f.denom;
   return *this;
}

Rational &Rational::operator*(BigInt *x) {
   return *new Rational(num->multiply(x), denom);
}

Rational &Rational::operator++() {  // preincrement
   num->add(denom);
   return *this;
}

Rational &Rational::operator++(int n) {  // postincrement
   Rational *frac = new Rational(num, denom);
   operator++();
   return *frac;
}

bool Rational::operator<(Rational &numb) {
   if ((numb.num)->sign && !(this->num)->sign) return false;
   if (!(numb.num)->sign && (this->num)->sign)  return true;
   if (equals(&numb)) return false;

   BigInt *numb_div = (numb.num)->divide(numb.denom);
   BigInt *numb_mod = (numb.num)->mod(numb.denom);
   BigInt *this_div = num->divide(denom);
   BigInt *this_mod = num->mod(denom);
   if (!(numb.num)->sign && !(this->num)->sign) {
      if (this_div->absoluteLessThan(numb_div)) return true;
      else if ((numb_div)->absoluteLessThan(this_div)) return false;
      else if ((this_mod)->absoluteLessThan(numb_mod)) return true;
      else return false;
   } else {
      if (this_div->absoluteLessThan(numb_div)) return false;
      else if ((numb_div)->absoluteLessThan(this_div)) return true;
      else if ((this_mod)->absoluteLessThan(numb_mod)) return false;
      else return true;
   }
}

Rational::Rational(BigInt *n) {
   num = n;
   denom = new BigInt(1);
}

Rational::Rational(int n) {
   num = new BigInt(n);
   denom = new BigInt(1);
}

Rational::Rational(int n, int d) {
   if (d == 0) {
      cerr << " Rational: denominator is 0\n";
      exit(1);
   }
   if (d < 0) n = -n;
   num = new BigInt(n);
   denom = new BigInt(d);
   bool save_sign = num->sign;
   num->sign = false;
   BigInt *g = gcd(num, denom);
   num = num->divide(g);
   denom = denom->divide(g);
   num->sign = save_sign;
}

Rational::Rational(BigInt *n, BigInt *d) {
   if (d->sign) { n->sign = !n->sign; d->sign = false; }
   BigInt *g;
   num = n;
   denom = d;

   if (denom->equals(new BigInt("0"))) {
      cerr << " Rational: denominator is 0\n";
      exit(1);
   }
   if (denom->equals(new BigInt("1"))) return;

   if (denom->equals(num)) {
      num = new BigInt("1");
      denom = new BigInt("1");
      return;
   }
      
   if (num->equals(new BigInt("0"))) {  
      num = new BigInt("0"); 
      denom = new BigInt("1"); 
      return;  
   }
   bool save_sign = num->sign;
   num->sign = false;
   g = gcd(num, denom);
   num = num->divide(g);
   denom = denom->divide(g);
   num->sign = save_sign;
}

char *Rational::show(int digits) {
   // For detailed comments see 'truncate' below
   char *result = new char[digits+10];
   int index = 0;
   BigInt *n = num;
   BigInt *d = denom;
   if (d->lessThan(n)) {
      BigInt *whole = n->divide(d);
      cout << whole << ".";
      strcat(result,whole->value());
      index = strlen(result);
      n = n->mod(d);
      digits -= strlen(whole->value());
   } else {
      cout << "0.";
      result[index++] = '0';
      result[index++] = '.';
      result[index] = 0;
   }

   if (n->equals(new BigInt("0"))) {
      cout << "0\n";
      result[index++] = '0';
      result[index] = 0;
      return result;
   }

   for (int i=0 ; i < digits && !n->equals(new BigInt("0")) ; i++) {
      n = n->multiply(new BigInt("10"));
      while (n->lessThan(d) && i < digits) {
         n = n->multiply(new BigInt("10"));
         cout << "0";
         result[index++] = '0';
         result[index] = 0;
         i++;
      }
      if (i >= digits) break;
      BigInt *p = n->divide(d);
      cout << p;
      strcat(result,p->value());
      index = strlen(result);
      n = n->mod(d);
   }
   cout << "\n";
   return result;
}

// Divide numerator by denominator and return an equivalent new Rational 
// number with a power of 10 in the denominator and a numerator that 
// contains roughly a number of digits equal to the argument 'digits'.
Rational &Rational::truncate(int digits) {
   // Don't do anything if the current rational number has a small numerator
   if (digits < 2 || strlen(num->value()) < (size_t)digits) 
     return *new Rational(new BigInt(num->value()),new BigInt(denom->value()));

   BigInt *n = num;
   BigInt *d = denom;
   char *num1 = NULL, *denom1 = NULL;
   int index = 0;

   // Strip off the part of the number to the left of the radix point
   if (d->lessThan(n)) {
      BigInt *whole = n->divide(d);
      num1 = new char[strlen(whole->value())+digits+10];
      strcpy(num1, whole->value());
      index = strlen(whole->value());
      n = n->mod(d);
      digits -= strlen(whole->value());
   }

   // At this point 'digits' is the number of digits of the number that
   // remain to be filled, 'n' is the fractional part of the number, and
   // 'index' is the current point in the 'num1' array that is waiting for
   // the next digit.  Eventually, the 'num1' array will hold the truncated
   // digits of the number.  If there is no fractional part, just build
   // the rational number now and return it.
   if (n->equals(new BigInt("0")))
      return *new Rational(new BigInt(num1), new BigInt("1"));

   // Repeatedly multiply the fractional part ('n') by 10 until it is 
   // greater than the denominator.  Then divide to get another digit
   // for 'num1'.  All extra times 10 multiplies add '0' to num1 as well.
   // Stop if the number of digits is exceeded.  The variable 'count' 
   // contains the number of decimal places in 'num1' ('num1' is an 
   // integer and count shows where the radix point goes in 'num1').
   int count = 0;
   for (int i=0 ; i < digits && !n->equals(new BigInt("0")) ; i++) {
      n = n->multiply(new BigInt("10"));
      while (n->lessThan(d) && i < digits) {
         n = n->multiply(new BigInt("10")); // An extra times 10 multiply
         num1[index++] = '0';
         num1[index] = 0;
         count++;
         i++;
      }
      num1[index++] = (n->divide(d))->value()[0];
      num1[index] = 0;
      count++;
      n = n->mod(d);
   }

   // At this point 'num1' has the numerator, the denominator is next
   // constructed as a power of 10.
   denom1 = new char[count+10];
   denom1[0] = '1'; 
   for (int i=1 ; i <= count ; i++) denom1[i] = '0';
   denom1[count+1] = 0;

   return *new Rational(new BigInt(num1), new BigInt(denom1));
}

Rational *Rational::truncate_p(int digits) {
   // Don't do anything if the current rational number has a small numerator
   if (digits < 2 || strlen(num->value()) < (size_t)digits) 
     return new Rational(new BigInt(num->value()),new BigInt(denom->value()));

   BigInt *n = num;
   BigInt *d = denom;
   char *num1 = NULL, *denom1 = NULL;
   int index = 0;

   // Strip off the part of the number to the left of the radix point
   if (d->lessThan(n)) {
      BigInt *whole = n->divide(d);
      num1 = new char[strlen(whole->value())+digits+10];
      strcpy(num1, whole->value());
      index = strlen(whole->value());
      n = n->mod(d);
      digits -= strlen(whole->value());
   }

   // At this point 'digits' is the number of digits of the number that
   // remain to be filled, 'n' is the fractional part of the number, and
   // 'index' is the current point in the 'num1' array that is waiting for
   // the next digit.  Eventually, the 'num1' array will hold the truncated
   // digits of the number.  If there is no fractional part, just build
   // the rational number now and return it.
   if (n->equals(new BigInt("0")))
      return new Rational(new BigInt(num1), new BigInt("1"));

   // Repeatedly multiply the fractional part ('n') by 10 until it is 
   // greater than the denominator.  Then divide to get another digit
   // for 'num1'.  All extra times 10 multiplies add '0' to num1 as well.
   // Stop if the number of digits is exceeded.  The variable 'count' 
   // contains the number of decimal places in 'num1' ('num1' is an 
   // integer and count shows where the radix point goes in 'num1').
   int count = 0;
   for (int i=0 ; i < digits && !n->equals(new BigInt("0")) ; i++) {
      n = n->multiply(new BigInt("10"));
      while (n->lessThan(d) && i < digits) {
         n = n->multiply(new BigInt("10")); // An extra times 10 multiply
         num1[index++] = '0';
         num1[index] = 0;
         count++;
         i++;
      }
      num1[index++] = (n->divide(d))->value()[0];
      num1[index] = 0;
      count++;
      n = n->mod(d);
   }

   // At this point 'num1' has the numerator, the denominator is next
   // constructed as a power of 10.
   denom1 = new char[count+10];
   denom1[0] = '1'; 
   for (int i=1 ; i <= count ; i++) denom1[i] = '0';
   denom1[count+1] = 0;

   return new Rational(new BigInt(num1), new BigInt(denom1));
}

bool Rational::equals(Rational *obj) {
   char *n = (obj->num)->value();
   char *d = (obj->denom)->value();
   if (!strcmp(n,num->value()) && !strcmp(d,denom->value())) return true;
   return false;
}

int Rational::sizeOf() {
   if (num->sizeOf() < denom->sizeOf()) return denom->sizeOf();
   else return num->sizeOf();
}

Rational::~Rational() {
   delete num;
   delete denom;
}