#include <iostream>
#include <vector>

using namespace std;

// The following Monomial and Polynomial classes are abstract template
// classes because we want to be able to choose the leading coefficient
// from any field k.

template<class T>
class Monomial{
public:
  int n;                 // number of variables
  T coef;                // coefficient
  vector<int> exponents; // one exponent for each variable

  // Constructor
  Monomial(int n): n(n){
    // Create a vector with n slots each initially filled with 0
    exponents = vector<int>(n, 0);
  }

  // Overload the output operator << so we can easily print Monomial objects
  friend std::ostream& operator<<(std::ostream& os, Monomial<T> &m){
    os << m.coef << '(';
    for(unsigned int i=0; i<m.exponents.size(); ++i){
      os << m.exponents[i];
      if(i < (m.exponents.size()-1) )
	os << ',';
    }
    os << ')';
    return os;
  }
};

template<class T>
class Polynomial{
public:
  vector< Monomial<T> > monomials;

  // Constructor
  Polynomial(const Monomial<T> &m){
    monomials.push_back(m);
  }

  // Overload the output operator << so we can easily print Polynomial objects
  friend std::ostream& operator<<(std::ostream& os, Polynomial<T> &p){
    for(unsigned int i=0; i<p.monomials.size(); ++i){
      os << p.monomials[i];
      if(i < (p.monomials.size()-1) )
	os << " + ";
    }
    
    return os;
  }
};


int main() {

  // Create a constant to keep track of the number of variables
  const int NUM_VARS = 3;

  // Instantiate three Monomial objects:
  // coefficients are over the field of reals which in C++
  // is approximated by the "double" type, and each monomial
  // has NUM_VARS exponents.

  Monomial<double> m1(NUM_VARS), m2(NUM_VARS), m3(NUM_VARS);
  
  // Fill the objects with random data
  m1.coef = 4.5;
  m1.exponents[0] = 1;
  m1.exponents[1] = 2;
  m1.exponents[2] = 3;

  m2.coef = -3;
  m2.exponents[0] = 0;
  m2.exponents[1] = 2;
  m2.exponents[2] = 1;

  m3.coef = 1.0/2.0;
  m3.exponents[0] = 0;
  m3.exponents[1] = 0;
  m3.exponents[2] = 2;

  // Instantiate a Polynomial object where each term/monomial
  // in the polynomial has coefficients over the field of reals.
  Polynomial<double> p(m1);

  // Add two more terms/monomials to the back end of the polynomial
  p.monomials.push_back(m2);
  p.monomials.push_back(m3);

  // Print our polynomial to stdout (i.e. the terminal)
  cout << p << endl;

  return (0);
}

