// testRational.cc
// P. Conrad, for CISC181, Spring 2006

#include <iostream>
using std::cout;
using std::endl;

#include <cstdlib> // for srand(), rand()

#include "rational.h"


// return a non zero integer between min and max (inclusive)

int randomNonZeroInteger(int min = -10, int max = 10)
{
  int sizeOfRange = max - min + 1; // how many numbers are in the range
  int randomNumber; 

  do // compute a random number until we get one that is non zero
    {
      // give us a number between 0 and sizeOfRange-1
      randomNumber = rand() % sizeOfRange;
      
      // shift the number to the range desired
      randomNumber -= min;

      
    } while (randomNumber == 0);  // stop when randomNumber is NON-zero.

  return randomNumber;

}

void constructorTest(int num, int denom, int expectedNum, int expectedDenom)
{
  // construct an object

  Rational_C f(num, denom);

  // report the test

  cout << " constructorTest for num=" << num << " denom=" << denom 
       << " expectedNum=" << expectedNum 
       << " expectedDenom=" << expectedDenom;

  // see if we got the expected result

  if ( f.getNum() == expectedNum && f.getDenom() == expectedDenom)
    cout << "\tPassed" << endl;
  else
    cout << "\tFailed" << endl;
     
}

void mutatorTest(int num, int denom, int expectedNum, int expectedDenom)
{

  // report the test

  cout << " mutatorTest1 for num=" << num << " denom=" << denom 
       << " expectedNum=" << expectedNum 
       << " expectedDenom=" << expectedDenom;

  // mutator test: 
  // First construct an object with default constructor, and then 
  // use the supplied values.

  Rational_C f;

  f.setNum(num);
  f.setDenom(denom);

  // Then construct an object with random non zero values, and then
  // set using the supplied values

  Rational_C g(randomNonZeroInteger(), randomNonZeroInteger());
  g.setNum(num * g.getDenom());
  g.setDenom(denom);

  // see if we got the expected result

  if ( f.getNum() == expectedNum && f.getDenom() == expectedDenom &&
       g.getNum() == expectedNum && g.getDenom() == expectedDenom)
    cout << "\tPassed" << endl;
  else
    cout << "\tFailed" << endl;
     
}

void noArgConstructorTest(int num, int denom, 
			  int expectedNum, int expectedDenom)
{
  // report the test

  cout << " noArgConstructorTest for num=" << num 
       << " denom=" << denom 
       << " expectedNum=" << expectedNum 
       << " expectedDenom=" << expectedDenom;

  // Construct one object with default constructor.  We'll test that
  // it has 1 for both numerator and denominator

  Rational_C one;

  // construct two objects with default constructor;
  // set numerator on one, and denominator on the other.
  // should get num/1 for first one, and 1/denom for second one.
  
  Rational_C f; f.setNum(num); // set only num (no reduction)
  Rational_C g; g.setDenom(denom); // negative denom should make num -1
  Rational_C h; h.setNum(num); h.setDenom(denom); // try setting both
  
#ifdef DEBUG
  std::cout 
    << "\n"
    << "one.getNum()=" << one.getNum() 
    << " one.getDenom=" << one.getDenom() << "\n"
    << "f.getNum()=" << f.getNum() << " f.getDenom=" << f.getDenom() << "\n"
    << "g.getNum()=" << g.getNum() << " g.getDenom=" << g.getDenom() << "\n"
    << "h.getNum()=" << h.getNum() << " h.getDenom=" << h.getDenom() << endl;

#endif

  // see if we got the expected result
  
  if ( (one.getNum() == 1) && (one.getDenom() == 1) &&
       (f.getNum() == num) && (f.getDenom() == 1) &&
       (g.getNum() == ((denom<0) ? -1: 1)) && (g.getDenom() == abs(denom)) &&
       (h.getNum() == expectedNum) && (h.getDenom() == expectedDenom) )
    cout << "\tPassed" << endl;
  else
    cout << "\tFailed" << endl;
  
}


int main(void)
{
  // Seed a random number generator. 
  // Only call srand once at the start of the program!

  srand(time(0)); 

  cout << "Constructor Tests (constructor with 2 args)" << endl;
  
  constructorTest(1,2,1,2);
  constructorTest(2,4,1,2);
  constructorTest(-2,-4,1,2);
  constructorTest(2,-4,-1,2); // make sure denominator is always positive
  constructorTest(5,10,1,2);
  constructorTest(10,15,2,3);
  constructorTest(100,150,2,3);
  constructorTest(150,100,3,2);

  // @@@ ADD AT LEAST 3 MORE TESTS OF YOUR OWN

  cout << "Mutator tests" << endl;

  mutatorTest(1,2,1,2);
  mutatorTest(2,4,1,2);
  mutatorTest(-2,-4,1,2);
  mutatorTest(2,-4,-1,2); // make sure denominator is always positive
  mutatorTest(5,10,1,2);
  mutatorTest(10,15,2,3);
  mutatorTest(100,150,2,3);
  mutatorTest(150,100,3,2);
  
  cout << "No-arg constructor tests" << endl;

  noArgConstructorTest(1,2,1,2);
  noArgConstructorTest(2,4,1,2);
  noArgConstructorTest(-2,-4,1,2);
  noArgConstructorTest(2,-4,-1,2); // make sure denom is always positive
  noArgConstructorTest(5,10,1,2);
  noArgConstructorTest(10,15,2,3);
  noArgConstructorTest(100,150,2,3);
  noArgConstructorTest(150,100,3,2);

  return 0;

}



