Population

Polynomial

    A.First Edition

    This is my first edition of a simple assignment of C++. And I hesitate for some time to decide whether to add it.

    B.The problem

Implement the class Polynomial declared below. You are responsible to define
each member function as follows (do not change their prototypes):
1. A Constructor that takes one integer argument as the Polynomial order.
Remember that the order of a Polynomial is the greatest exponent, and
that there is a zero order term. Hence, there should be (order+1) terms.
The order of the Polynomial is stored in a private data member.
The Polynomial coefficients are stored in a dynamically created array of
floats pointed to by the data member polyPtr.
You chose whether you want to store the Polynomial coefficients in
ascending or descending order (just comment on which one you use).
2. A Constructor that takes an array of Polynomial coefficients, and its order.
3. A copy Constructor.
4. A Destructor to clean up dynamically created memory.
OVERLOADED OPERATORS
5. Overload the stream extraction operator (<<) to output the the polynomial in
descending order. Don't print the terms with coefficients of zero.
As an example, a third order Polynomial should be printed in the form
3.23x^3 + 1.2x^2 + 9.97x + 5.09
6. Overload the assignment operator.
7. Overload operators '+', '-', and '*'. These operators should return a
Polynomial being the sum, difference and product or two polynomials.
8. Overload the subscript operator []. Use this operator to read and
modify the coefficients of a Polynomial.
Finally, write a very comprehensive driver program. A good test program should:
- Test every member function
- Test extreme cases such as:
assigning one Polynomial to itself ( P1 = P1 ) ;
adding/subtracting/multiplying one Polynomial to itself
add/subtract/multiply/assign two different order Polynomial
test zero order Polynomials
Hand in your code including the driver program and the OUTPUT!!!
กก
C.The idea of program

    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 think old data should be preserved in case assignment failed,
    and old data should be restored.
    4. The term of order is stored ascendly.
    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 cannot display anything.

    D.The major functions

    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};

    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 showMessage(ErrorType errorType);
    	void errorHandler(ErrorType errorType);
    	bool initialize(int Order);
    	void uninitialize();
    	bool arithOperation(const float* ptr, const int size, ArithType arithType=Assignment);
    	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 assumes the highest term is always non-zero, but
    //in case of subtraction, it may be incorrect!!!!
    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 dependend 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];
    	}
    }

    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
    {
    	int newOrder = order>P.order?order:P.order;
    	Polynomial Result;
    	if (Result.initialize(newOrder))
    	{
    		if (Result.arithOperation(this->polyPtr, this->order+1))//default is assignment
    		{
    			if (Result.arithOperation(P.polyPtr, P.order+1, arithType))
    			{
    				return Result;
    			}
    		}
    	}
    	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:
    				polyPtr[i] *=ptr[i];
    				break;
    			case Assignment:
    				polyPtr[i] =ptr[i];
    				break;
    			}
    		}
    		return true;
    	}
    }

    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};
    	//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:

    test default constructor, and P should be order of 0 with one term initialized to 0:
    order of P:0
    all element of P is:
    0
    test constructor with float array as input, now Q should be:
    7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3

    Test friend function and term of 0 will not
    be showed--term of order of 3 is 0 and will not be showed:
    7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3

    now test copy constructor, R will be initialized same as Q

    now Q is
    7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3

    now R should be same as Q
    7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3

    now test conversion constructor, R should be initialized to order of 4
    by R = 4, and all term is initialized to 0 and R is:
    0

    now test friend function, since all term is 0,only one 0 will be printed for R
    0

    now test operator [], try to change order of 3 from 0 to 1
    before change Q is
    7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3

    now change term 3 to 1

    now Q is
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    now test operator +,-,*,=
    R = Q
    before assignment
    R is order of 4 with all term equal 0
    0

    Q is
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    nowR=Q and R is
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    R+Q
    15.2x^5 + 13x^4 + 2x^3 + 8.6x^2 + 6.4x + 2.6

    R-Q
    0

    R*Q
    57.76x^5 + 42.25x^4 + 1x^3 + 18.49x^2 + 10.24x + 1.69

    now test extreme cases
    self-assignment: R=R which is not allowed
    Self-assignment not allowed
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    adding/subtracting/multiplying one Polynomial to itself
    cout<<(R+R)
    15.2x^5 + 13x^4 + 2x^3 + 8.6x^2 + 6.4x + 2.6

    cout<<(R*R)
    57.76x^5 + 42.25x^4 + 1x^3 + 18.49x^2 + 10.24x + 1.69

    cout<<(R-R), as all term is 0, only one 0 will be showed,0

    add/subtract/multiply/assign two different order Polynomial
    Q is:
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    S is:
    5x^3 + 6.7x^2 + 4.5x + 2.3

    P+S: 7.6x^5 + 6.5x^4 + 6x^3 + 11x^2 + 7.7x + 3.6

    P-S: 7.6x^5 + 6.5x^4 + -4x^3 + -2.4x^2 + -1.3x + -1

    P*S: 7.6x^5 + 6.5x^4 + 5x^3 + 28.81x^2 + 14.4x + 2.99

    test zero order Polynomials: T is created by default
    So T will be only one 0
    0

    test negative polynomials: Polynomial U(-3);
    Order cannot be negative number!

    test input of invalid array for constructor and it should display error message
    Invalid array input

    and such object will be initialized to order=-1, polyPtr =NULL so nothing will be displayed

    test constructor with valid array, but negative order:Polynomial W(f1, -3)
    Order cannot be negative number!

    and such object will be initialized to order=-1, polyPtr =NULL so nothing will be displayed

    now test SAFE assignment: if an object encounter exception during creation
    its array will be initialized to NULL pointer, when assigning this object to
    other object, it will surely fail. However, the assigned object should at least
    keep its own original data, this is called safe assignment. Now assign Q with
    this empty object V
    now V should be completely empty (order is even NOT 0! so nothing should be displayed at all):

    before assignment Q is:
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    now assign Q=V which will fail for sure as order of V is -1
    and an error message will indicate failure of assignment
    Order cannot be negative number!

    Q should be the same
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    now test if subscript exceeds limit
    now Q is:
    7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3

    attempt to access Q[9]:
    Subscipt exceeds order limit!