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) {
return 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 *d = f.denom->multiply(denom);
return *new Rational(n,d);
}

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

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
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;
}
```