#include <iostream.h>

// Class definition for ivec - an integer vector object.
//
// Don't confuse this with a C++ vectors (STL).
// this is just a sample class that might make
// sense to students and allows us the chance to
// play with things like overloading operators
// 

// The general idea is that an ivec is an ordered
// list of ints. We create this new "type" and support
// operations like addition and subtraction by overloading
// the + and - C++ operators.



class ivec {
private:
  int *nums;		// this is where we store the ints
  int n;		// this is how many we have

public:

  // default constructor
  ivec(void) {

    cout << "Default constructor called" << endl;
    // initialize the pointer and size
    nums = NULL;
    n=0;
  }


  // constructor that allows us to initialize from
  // an array of int. Note that we don't simply
  // set this object to point to the array, we 
  // make our own copy if the ints.
  // a is an array of num ints

  ivec( int a[], int num ) {

    cout << "constructor called with an array" << endl;

    // allocate memory for our copy
    nums = new int[num];
    
    // now copy each value
    for (int i=0;i<num;i++)
      nums[i] = a[i];

    // and record how many we have
    n = num;
  }


  // copy constructor. This is called when copies are made of
  // objects of type ivec, for example when one is passed as
  // a parameter to a function. We have to overload it or the
  // copy will be a memberwise copy - in this case the copy
  // will be made of the pointer nums, not of the array itself.
  //
  // this copy constructor forces a copy of the entire array
  
  ivec( ivec &v) {

    cout << "copy constructor called " << endl;

    // find out how big v is
    n = v.size();

    // allocate memory for our copy
    nums = new int[v.size()];
    
    // now copy each value
    for (int i=0;i<n;i++)
      nums[i] = v[i];
  }

  // destructor - must de-allocate the array of ints!

  ~ivec( void ) {
    if (nums !=NULL)
      delete[] nums;
  }

  // empty the array.
  void empty(void) {
    if (nums != NULL)
      delete[] nums;
    n=0;
  }

  // n is a private data member, but we want to eb able to
  // find out how large a ivec is outside of the class methods,
  // so we need to provide a method for this

  int size(void) {
    return(n);
  }


  // a method that allows us to add a new value at the end of 
  // the list - we have to allocate new memory
  // (and this isn't the most efficient way to do it!)

  int append( int x ) {
    int *newlist = new int[n+1];
    
    for (int i=0;i<n;i++)
      newlist[i] = nums[i];

    newlist[n] = x;

    if (nums)
      delete[] nums;

    nums = newlist;
    n++;
    return(x);
  }

  // overload the array element access operator!
  // now we can treat the object like an array!

  int &operator[](int i) {
    // error checking!
    if ((i>=0) && (i<n))
      return(nums[i]);
    else {
      cerr << "Error accessing vector element - invalid subscript " << i << endl;
      exit(1);
    }
  }

  // overload the assignment operator. We have to do this for the
  // same reason we defined our own copy constructor - if we don't
  // we end up with 2 ivecs that share an array of int. Changing the
  // values in one would mess up the other (or deleting one!).

  const ivec &operator=( ivec &v) {

    // make sure we are not assigning to ourself!
    if ( &v != this ) {
      
      cout << "Assignment operator called" << endl;

      // clear out anything we already have.
      empty();

      // find out how big v is
      n = v.size();

      // allocate memory for our copy
      nums = new int[n];

      // now copy each value
      for (int i=0;i<n;i++)
      nums[i] = v[i];
    }
    return(*this);
  }



  // --------------------------------
  // overload the + operator

  ivec &operator+(ivec &v) {
    int i;

    // add corresponding elements, assume 0s fill the shorter one.
    if (size() < v.size()) {
      // fill us with 0s at the end
      for (i=size(); i< v.size(); i++) 
	append(0);
    }

    i=0;
    while ((i<size()) && (i<v.size())) {
      nums[i] += v[i];
      i++;
    }
    return(*this);
  }

  // --------------------------------
  // overload the - operator

  ivec &operator-(ivec &v) {
    int i;

    // add corresponding elements, assume 0s fill the shorter one.
    if (size() < v.size()) {
      // fill us with 0s at the end
      for (i=size(); i< v.size(); i++) 
	append(0);
    }

    i=0;
    while ((i<size()) && (i<v.size())) {
      nums[i] -= v[i];
      i++;
    }
    return(*this);
  }

  // --------------------------------
  // overload the == comparison operator

  bool operator ==(ivec &v) const {
    
    if ( n != v.size() )	
      return(false);

    // now check each element

    for (int i=0;i<n;i++)
      if (nums[i] != v[i]) 
	return(false);

    return(true);

  }

  // --------------------------------
  // overload the < comparison operator
  //
  //  check as many elements as both have (the smaller number)

  bool operator <(ivec &v) const {
    
    for (int i=0;(i<n) && (i<v.size());i++)
      if (nums[i] > v[i]) 
	return(false);

    return(true);

  }


  




  // operator overload! This method defines what should happen if
  // an ivec object is sent to an output stream using the "<<" operator
  // not that the prototype is defined here, but the actual 
  // function is defined somewhere outside of this class definition
  // (at global scope), this is necessary for these methods (the right
  // operand is the class object, operator overload methods can only
  // be class members if the class object is the left operand of 
  // a binary operation... Check out chapter 8 in the book for the details...

  friend ostream &operator<<( ostream &, const ivec& );

  friend istream &operator>>( istream &, ivec& );


};



