Currently we define polynomials over doubles which are approximations of real numbers from mathematics. However, from the mathematical point of view the underlying structure for a polynomial is a ring. The polynomial requires the operations "*" and "+" to be defined for the underlying type. Real numbers fulfill the axioms of a ring but much more general objects do likewise. So it would be nice if our code for polynomials worked for arbitrary types with a "*" and "+" defined. Exactly this is possible with "templates" which are parametric types. What is a template or parametric type? Consider the following function declaration:
1 double f(double x);It declares a function getting a double and returning a double. x is called a variable because it is variable. The type double however is not variable. Templates are variable types. Defining a grammar which allows to define variable types is not really trivial (think about it). In C++ the parametric type is declared before its actual usage in a variable declaration:
1 template <class T> T f(T x);This tells the compiler: Attention, T is going to be a parametric type. So this line reads: There is a function f getting any type of argument returning the same type of argument. If f is now called say with a string it will return a string. Nevertheless, the whole template thing is much more flexible than can be described in this short tutorial. Analogously to the template function declaration just shown we may declare a template class. We shall apply this to our Polynomial class:
 1 #include <iostream>     // use the iostream library (for input and output)
 2 #include <vector>       // use the STL type vector (a container type)
 3 
 4 using namespace std;    // add the namespace "std" to standard scope resolution
 5 
 6 //-------------------------------------------------------------
 7 // declaration:
 8 template <class T>
 9 class Polynomial : public vector<T> {   // declare a new type "Polynomial" which inheritas from a vector of Ts
10 public: // the following is publicly visibel
11     // constructor
12     Polynomial(int n);  // gets an integer as degree
13 
14     // function evaluation
15     T operator () (T x) const;
16 
17     // declare addition operator
18     Polynomial operator + (const Polynomial & p) const;
19     
20 };  // the class declaration of "Polynomial" ends here
21 
22 // define output operator
23 template <class T>
24 ostream & operator << (ostream & o, const Polynomial<T> & p);
25 
26 //-------------------------------------------------------------
27 // implementation:
28 template <class T>
29 Polynomial<T>::Polynomial(int n) :
30     vector<T>(n+1, 0)   // allocate space for the coefficients and initialize them to 0
31 {}  // nothing else to do
32 
33 template <class T>
34 T Polynomial<T>::operator () (T x) const
35 {
36 T   xx = 1; // temporary for powers of x
37 T   y = 0;  // will contain the result
38     for ( unsigned int i=0 ; i<this->size() ; ++i )
39     {
40         y += (*this)[i] * xx;   // add coefficient * x^n
41         xx *= x;    // next power
42     }
43     return y;   // return result
44 }
45 
46 
47 template <class T>
48 Polynomial<T> Polynomial<T>::operator + (const Polynomial<T> & p) const
49 {
50 Polynomial<T>   r(*this);   // copy left polynomial into result
51     for ( unsigned int i=0 ; i<p.size() ; ++i ) // loop over right polynomial
52         if ( i<this->size() )   // check if coefficient index exist in result polysnomial
53             r[i] += p[i];   // yes: add coefficients from right polynomial
54         else
55             r.push_back(p[i]);  // no: append coefficients from right polynomial
56     return r;   // return result
57 }
58 
59 template <class T>
60 ostream & operator << (ostream & o, const Polynomial<T> & p)
61 {
62     for ( int i=p.size()-1 ; i>=0 ; i-- )
63     {
64         if ( p[i]!=0 )
65         {
66             if ( i!=p.size()-1 )
67                 o << " + ";
68             o << p[i];
69             if ( i>0 )
70                 o << "*x^" << i;
71         }
72     }
73     return o;
74 }
75 
76 
77 int main()  
78 {
79 Polynomial<double>  p(4);   // create a polynomial of Ts of 4th degree
80     p[0] = 1;   // 1*x^0
81     p[4] = 2;   // 2*x^4
82 
83 Polynomial<double>  q(2);   // create a polynomial of Ts of 2nd degree
84     q[0] = 1;   // 1*x^0
85     q[1] = 2;   // 2*x^1
86     q[2] = 3;   // 3*x^2
87 
88     cout << "p = " << p << endl;    // print it
89     cout << "q = " << q << endl;    // print it
90     cout << "p(3.4) = " << p(3.4) << endl;  // evaluate and print it
91     cout << "q(3.4) = " << q(3.4) << endl;  // evaluate and print it
92     cout << "p(3)+q(3) = " << p(3)+q(3) << endl;
93     cout << "p+q = " << p+q << endl;
94     cout << "(p+q)(3) = " << (p+q)(3) << endl;
95     return 0;
96 }
Download Full Source
This Polynomial will work exactly as before but is now pretty general. Consequently, we may do things like the following:
1 Polynomial<Matrix<double> > p(3); 2 Matrix<Polynomial<double> > q(3);The first line generates a polynomial of matrices of doubles. The second line generates a matrix of polynomials of doubles. This is expressive power! There is no runtime penalty. Think about what you would have to do to achieve this with FORTRAN! You might say: "I never use polynomials of matrices." You may be right. But if you write a quantum chemistry program package you could certainly use something like the following:
1 Basis<SlaterDeterminant<SpinOrbital> >