Polynomial-Revised
A. Second Edition
This is my second edition of a simple assignment of C++. And I hesitate for some time to decide whether to add it.
There is a serious mistake in first edition which is pointed out by Mr. Yun Ke and I am grateful for this! You
see, it is rather a stupid mistake: I forgot how to do multiply for polynomial. The exponents should be added and
the coefficients should be multiplied. The biggest problem is that I tried in a couple of hours to try to combine
multiply operation with addition and subtraction and assignment. Finally I realized that it is not a good idea to
make a uniform code for different operations like "*" and "+","-".... Maybe this is the nature of THE OPERATION!
See, they are in different "ORDER" which I mean that "MULTIPLICITY" is a high "order" operation than "addition"
and "subtraction". Therefore they may not suit for same algorithm. (Just disregard my nonsense.)
Originally I tried to make as little modification as possible. Finally I have to add a new "doMulti" function which operates
completely different from all other operations. That's it.
1. bool Polynomial::initialize(int Order)
This method takes care of two kinds of error:
a) Input parameter Order is negative;
b) Dynamic allocating of memory failed.
2. const Polynomial& Polynomial::operator=(const Polynomial& P)
This method I insist to store the old pointer and old order in case assignment fails, the old data will not be
destroyed!
3. ostream& operator<<(ostream& in, const Polynomial& P)
This friend function has an assumption that the highest order term must be NON-Zero term! Then all non-zero
consecutive term will be correctly expressed.
4. const Polynomial& Polynomial::operator=(const Polynomial& P)
a) First of all self-assignment not allowed.
b) Dynamic allocation only needed when order are not same.
c) You only need to delete old pointer after you succeed in copying data and of course you allocate new memory.
Please note the case that the order of two objects are same, don't delete!
d) In all cases of failure, original data should remain unchanged!
E.Further improvement
1. Are you kidding? For such a trivial program?
polynomial.h
#ifndef POLYNOMIAL_H
#define POLYNOMIAL_H
#include <iostream>
using std::ostream ;
//this makes error handling easier
enum ErrorType
{UnableAllocateMemory, OrderNonPositive, InvalidArray, SelfAssignmentNotAllowed,
SubscriptExceedOrder};
//these are flags for all arithmatic ops, including assignment op
enum ArithType
{Addition, Subtraction, Multiply, Assignment};
class Polynomial {
// STREAM EXTRACTION OPERATOR
friend ostream& operator<<( ostream&, const Polynomial& ) ;
public:
// CONSTRUCTOR - Creates Polynomial of order O
Polynomial( int Order = 0 ) ;
// CONSTRUCTOR - Creates Polynomial of order O Initializing the
// coefficients to the coef array
Polynomial( float coef[], int Order = 0 ) ;
// COPY CONSTRUCTOR - Copies the polynomial into "this"
Polynomial( const Polynomial& P ) ;
// DESTRUCTOR - Clean up dynamically created data
~Polynomial() ;
const Polynomial& operator=( const Polynomial& ) ;
Polynomial operator+( const Polynomial& ) const ;
Polynomial operator-( const Polynomial& ) const ;
Polynomial operator*( const Polynomial& ) const ;
// You're lucky I didn't make you do a DIVIDE function...
// ... Maybe I'll save it for the FINAL :)
// SUBSCRIPT OVERLOAD - Use this operator to change any of the
// coefficients after the Polynomial has been created.
float& operator[]( unsigned int sub ) ;
int getOrder(){return order;}
private:
void doMulti(const Polynomial& P1, const Polynomial& P2);
//Polynomial singleMul(float coef, int shiftBy);
//void times(float coef);
//void shift(int shiftBy);
void showMessage(ErrorType errorType);//simply show all error msg
void errorHandler(ErrorType errorType);//here goes all error handling
bool initialize(int Order);//this handles dynamic mem allocation and test order input
void uninitialize();
bool arithOperation(const float* ptr, const int size, ArithType arithType=Assignment);
//I have to make this const, as it is the return value of a const function--+,-,*
Polynomial doArithOp(const Polynomial& P, ArithType) const;
int order ; // Polynomial Order
// polyPtr will point to an array of polynomial coefficients
// Don't forget that a polynomial of Order N has N+1 terms
// Polynomial is created "dynamically" (i.e. use "new")
float* polyPtr ;
} ;
#endif
กก
polynomial.cpp
/*****************************************************
Course: COEN244
Assignment No.: 3
Program: Polynomial
Name: Qingzhe Huang
ID: 5037735
Major features:
1. I defined a series of enum type ErrorType to enable error handling within one function.
As I observe that different constructor and other member functions repeatedly
require these error handlers.
2. I defined a series of enum type ArithType to enable operator overloading of +,-*,= to
be implemented through one same functions as they are almost same operations.
3. During assignment operation, I use a kind of SAFE ASSIGNMENT. I think old data should be
preserved in case assignment failed, and old data should be restored.
4. The term of order is stored ascendingly.
5. Many utility methods are defined to maximumly reduce repeating codes.
6. Since 0 term should not be displayed, for an object created by default constructor will
be initialized to all 0's. So, friend function cout<<P can only display one 0.
*******************************************************/
#include "Polynomial.h"
using namespace std;
//I don't know why compiler complains when I place this in .h file!!!
char* errorMessage[5] =
{"Unable to allocate memory!",
"Order cannot be negative number!",
"Invalid array input",
"Self-assignment not allowed",
"Subscipt exceeds order limit!"};
//My idea is to display the first term and all non-zero term will be displayed with
//"+" sign at beginning. This does not assume the highest term is always non-zero.
//And if all term are 0's, at least one 0 will be printed.
ostream& operator<<(ostream& in, const Polynomial& P)
{
bool first=true;
for (int i=P.order; i>=0; i--)
{
if (P.polyPtr[i]!=0)//only show non-zero term
{
//only print the first non-zero term
if (first)//the first non-zero term is print without "+" sign
{
in<<P.polyPtr[i]<<"x^"<<i;
first = false;
}
else
{
if (i>1)
{
//all consecutive term has + at beginning
in<<" + "<<P.polyPtr[i]<<"x^"<<i;
}
else
{
if (i==1)
{
in<<" + "<<P.polyPtr[i]<<"x";
}
else
{
in<<" + "<<P.polyPtr[i]; //the last term is constant term
}
}
}
}
else
{
if (first && i==0)//if all term are 0, at least print one 0
{
in<<0;
}
}
}
cout<<endl;
return in;
}
Polynomial::Polynomial(int Order)
{
initialize(Order);
}
//in ascending order does it store
Polynomial::Polynomial(const Polynomial& P)
{
if (initialize(P.order))//test if allocate memory is ok
{
if (!arithOperation(P.polyPtr, P.order+1))//default is assginment
{
uninitialize();
}
}
}
Polynomial::Polynomial(float coef[], int Order)
{
//only do the assignment after initializing succeedingly
if (initialize(Order))
{
if (!arithOperation(coef, order+1))
{
uninitialize();//in case assignment failed, ptr should be cleared to NULL
}
}
}
Polynomial::~Polynomial()
{
uninitialize();
}
//this utility method can be used for different constructors
bool Polynomial::initialize(int Order)
{
if (Order>=0)
{
order = Order;
polyPtr = new float[Order+1];
if (polyPtr==NULL)
{
errorHandler(UnableAllocateMemory);
//we must make sure user won't use this array by mistake!
//casue maybe some method is dependent on checking the order to see
//if array is usable. So in this case I make order =-1;
}
else
{
//do we need to initialize all elements of array? I guess so.
for (int i=0; i<=order; i++)
{
polyPtr[i] = 0;
}
return true;
}
}
else
{
errorHandler(OrderNonPositive);
}
return false;
}
float& Polynomial::operator [](unsigned int sub)
{
if (sub>order+1)
{
errorHandler(SubscriptExceedOrder);
return polyPtr[0]; //I don't know if we can have better choice
}
else
{
return polyPtr[sub];
}
}
//these arithmatic operations have very similar implementations.
Polynomial Polynomial::operator *(const Polynomial& P) const
{
return doArithOp(P, Multiply);
}
Polynomial Polynomial::operator+(const Polynomial& P) const
{
return doArithOp(P, Addition);
}
Polynomial Polynomial::operator -(const Polynomial& P) const
{
return doArithOp(P, Subtraction);
}
//this utility method make sure to initialize result to correct order for arithmatic op
//as return value must be a const for the return value of operator +,-,*, I have to make
//it a const function, and this is very ackward!!!
Polynomial Polynomial::doArithOp(const Polynomial& P, ArithType arithType) const
{
//modified
//int newOrder = order>P.order?order:P.order;
int newOrder;
Polynomial Result;
if (arithType==Multiply)
{
newOrder= order+P.order;
}
else
{
newOrder= order>P.order?order:P.order;
}
//modified
if (Result.initialize(newOrder))
{
if (arithType==Multiply)
{
Result.doMulti(*this, P);
}
else
{
if (Result.arithOperation(this->polyPtr, this->order+1))//default is assignment
{
Result.arithOperation(P.polyPtr, P.order+1, arithType);
}
}
}
return Result;
//always return an object, this is different with my original design
//return NULL;
}
//I am using a kind of SAFE assignment. If there is any exception during dynamic allocation
//or copying data, the original data will not be destroyed, on the contrary, it will be safely
//restored. So, even there is a failure in assignment, the object will remain the original
//state, not with a lost-memory pointer.
const Polynomial& Polynomial::operator=(const Polynomial& P)
{
if (this!=&P)//see if assign to itself
{
float* oldPtr = polyPtr;//should restore ptr in case copy fails
int oldOrder = order; //store old order to restore if assign fails
if (order!=P.order)//only do resize if size is different
{
if (initialize(P.order))//see if any exception during mem allocation
{
if (arithOperation(P.polyPtr, P.order+1))
{
delete[] oldPtr;//succeed then destroy old ptr
return *this;
}
}
}
else
{
if (arithOperation(P.polyPtr, P.order+1))//default is copy or assignment
{
return *this;
}
}
//in all other cases, old data need be restored
polyPtr = oldPtr;
order = oldOrder;
}
else
{
errorHandler(SelfAssignmentNotAllowed);
}
return *this;
}
void Polynomial::uninitialize()
{
delete [] polyPtr;
polyPtr = NULL;
order =-1; //as even order is 0, it does not mean 0 element in polyPtr,
//order =0 means there is one element and polyPtr is not NULL
}
//I put all error handle method together to make it easy to maintain
void Polynomial::errorHandler(ErrorType errorType)
{
//in case there is error, I will restore all intialized status
//make pointer NULL, counter 0
switch (errorType)
{
case UnableAllocateMemory:
showMessage(UnableAllocateMemory);
order =-1;
break;
case OrderNonPositive:
showMessage(OrderNonPositive);
polyPtr =NULL;
order = -1;
break;
case InvalidArray:
showMessage(InvalidArray);
break;
case SelfAssignmentNotAllowed: //this is a minor error, only message showed
showMessage(SelfAssignmentNotAllowed);
break;
case SubscriptExceedOrder:
showMessage(SubscriptExceedOrder);//showmessage is enough for this minor error.
break;
}
}
//this utility method saves the repeating code
void Polynomial::showMessage(ErrorType errorType)
{
cout<<errorMessage[errorType]<<endl;
}
//the caller of arithOperation will make sure size no exceed limit
//default arithType is assignment
bool Polynomial::arithOperation(const float* ptr, const int size, ArithType arithType)
{
if (ptr==NULL)
{
errorHandler(InvalidArray);
return false;
}
else
{
for (int i=0; i<size; i++)
{
//in ascending order
switch (arithType)
{
case Addition:
polyPtr[i] +=ptr[i];
break;
case Subtraction:
polyPtr[i] -=ptr[i];
break;
/*
case Multiply:
this->operator +(Result.singleMul(ptr[i], i));
//polyPtr[i] *=ptr[i];
break;
*/
case Assignment://this is the default argument
polyPtr[i] =ptr[i];
break;
}
}
return true;
}
}
/*
//this two internal method is specially for "multiply"
//"multiply" can be divided into many simple multiplying by a single "polynomial":
//the "single" polynomial means that with single "coefficient" of some "order"
//for example (a1+a2*x+a3*x^2+a4*x^3...an*x^(n-1)) * (b*x^k)
//where a1,a2...an is coeffient from first "normal" polynomial
//and "b" is coefficient from the "single" polynomial with order of "k"
//Then the "mulitply" is simply "shift by k" and "multiply coefficient b"
/*
Polynomial Polynomial::singleMul(float coef, int shiftBy)
{
Polynomial Result(*this);
Result.shift(shiftBy);
Result.times(coef);
return Result;
}
void Polynomial::times(float coef)
{
for (int i=0; i<=order; i++)
{
polyPtr[i]*=coef;
}
}
//see above
void Polynomial::shift(int shiftBy)
{
int index=order;
if (shiftBy<=0)
{
return;//do nothing
}
while (index>=0)
{
if (index-shiftBy>=0)
{
polyPtr[index]=polyPtr[index-shiftBy];
}
else
{
polyPtr[index]=0;//to make it zero
}
index--;
}
}
*/
void Polynomial::doMulti(const Polynomial& P1, const Polynomial& P2)
{
for (int i=0; i<=P1.order; i++)
{
for (int j=0; j<=P2.order; j++)
{
polyPtr[i+j] += P1.polyPtr[i]*P2.polyPtr[j];
}
}
}
กก
driver.cpp
#include <iostream>
#include "Polynomial.h"
using namespace std;
int main()
{
float f1[] = {1.3,3.2, 4.3, 0, 6.5,7.6};
float f2[] = {2.3, 4.5, 6.7, 5};
Polynomial P(f1,5);
Polynomial Q(f2, 3);
cout<<P*Q<<endl;
/*
//test default constructor
Polynomial P;
cout<<"test default constructor, "
<<"and P should be order of 0 with one term initialized to 0:\n";
cout<<"order of P:"<<P.getOrder()<<endl;
cout<<"all element of P is:\n"<<P;
//test constructor with float array as input
cout<<"test constructor with float array as input, now Q should be:\n";
Polynomial Q(f1, 5);
cout<<Q;
cout<<"\nTest friend function and term of 0 will not\n"
<<"be showed--term of order of 3 is 0 and will not be showed:\n"<<Q<<endl;
//test copy constructor
cout<<"now test copy constructor, R will be initialized same as Q\n";
Polynomial R(Q);
cout<<"\nnow Q is\n"<<Q;
cout<<"\nnow R should be same as Q\n"<<R;
//test conversion constructor
cout<<"\nnow test conversion constructor, R should be initialized to order of 4\n"
<<"by R = 4, and all term is initialized to 0 and R is:\n"<<(R = 4);
//test friend function, since all term is 0, nothing is printed
cout<<"\nnow test friend function, since all term is 0,"
<<"only one 0 will be printed for R\n"<<R;
//test []operator
cout<<"\nnow test operator [], try to change order of 3 from 0 to 1\n";
cout<<"before change Q is\n"<<Q;
cout<<"\nnow change term 3 to 1\n";
Q[3] = 1;
cout<<"\nnow Q is\n"<<Q;
//test operator +,-,*,=
cout<<"\nnow test operator +,-,*,=\n";
cout<<"R = Q\n";
cout<<"before assignment\n";
cout<<"R is order of 4 with all term equal 0\n"<<R;
cout<<"\nQ is\n"<<Q;
cout<<"\nnowR=Q and R is\n"<<(R = Q);
cout<<"\n R+Q\n"<<(R+Q);
cout<<"\n R-Q\n"<<(R-Q);
cout<<"\n R*Q\n"<<(R*Q);
//now test extreme cases:
cout<<"\nnow test extreme cases\n";
cout<<"self-assignment: R=R which is not allowed\n";
cout<<(R =R);
cout<<"\nadding/subtracting/multiplying one Polynomial to itself\n";
cout<<"cout<<(R+R)\n"<<(R+R);
cout<<"\ncout<<(R*R)\n"<<(R*R);
cout<<"\ncout<<(R-R), as all term is 0, only one 0 will be showed,"
<<(R-R);
//add/subtract/multiply/assign two different order Polynomial\n
cout<<"\nadd/subtract/multiply/assign two different order Polynomial\n";
Polynomial S(f2, 3);
cout<<"Q is:\n"<<Q;
cout<<"\nS is:\n"<<S;
cout<<"\nP+S: "<<(Q+S);
cout<<"\nP-S: "<<(Q-S);
cout<<"\nP*S: "<<(Q*S);
//test zero order Polynomials
cout<<"\ntest zero order Polynomials: T is created by default\n";
Polynomial T;
cout<<"So T will be only one 0\n"<<T;
//test negative polynomials
cout<<"\ntest negative polynomials: Polynomial U(-3);\n";
Polynomial U(-3);
//test assignment for an invalid array of constructor;
cout<<"\ntest input of invalid array for constructor and it "
<<"should display error message\n";
float* invalidArray = NULL;
Polynomial V(invalidArray, 4);
cout<<"\nand such object will be initialized to order=-1, polyPtr =NULL "
<<"so nothing will be displayed\n"<<V;
//test constructor with valid array, but negative order
cout<<"\ntest constructor with valid array, "
<<"but negative order:Polynomial W(f1, -3) \n";
Polynomial W(f1, -3);
cout<<"\nand such object will be initialized to order=-1, polyPtr =NULL "
<<"so nothing will be displayed\n"<<W;
//now test SAFE assignment
cout<<"\nnow test SAFE assignment: if an object encounter exception during creation\n"
<<"its array will be initialized to NULL pointer, when assigning this object to\n"
<<"other object, it will surely fail. However, the assigned object should at least\n "
<<"keep its own original data, this is called safe assignment. Now assign Q with\n "
<<"this empty object V\n";
cout<<"now V should be completely empty (order is even NOT 0! so nothing should"
<<" be displayed at all):\n"<<V<<endl;
cout<<"before assignment Q is:\n"<<Q<<endl;
cout<<"now assign Q=V which will fail for sure as order of V is -1\n"
<<"and an error message will indicate failure of assignment\n";
Q = V;
cout<<"\nQ should be the same\n"<<Q<<endl;
//test if subscript exceeds limit
cout<<"now test if subscript exceeds limit\n";
cout<<"now Q is:\n"<<Q<<endl;
cout<<"\nattempt to access Q[9]:\n";
Q[9] = 50;
cout<<endl;
*/
return 0;
}
Running result of program:
38x^8 + 83.42x^7 + 77.75x^6 + 68.23x^5 + 59.76x^4 + 47.29x^3 + 33x^2 + 13.21x + 2.99 Press any key to continue