Projects

 

FAQ: How to submit programming assignments and projects?

 

 

Project 1 (Due October 26 24:00)

Polynomial class

What This Assignment Is About

This project is borrowed from Prof. David Martin’s Computing III class given in Fall 2003. It is designed to help you gain experience with class implementation and encapsulation.   

What You Are To Do

Define and implement a class that manipulates polynomials of one variable (x).  Four our purpose, a polynomial is a list of terms, each consisting of a coefficient of type double and a nonnegative integer power.  The degree of a polynomial is defined to be the highest power that has a nonzero coefficient.  (In the degenerate case where there is no nonzero coefficient, the degree is also zero.)  For example, the degree of "x^3 + 7.5x + 3" is 3, while the degree of the (constant) polynomial "5.2" is 0, for 5.2 can be written as 5.2x^0.  The polynomial x^3 + 7.5x + 3 can be represented as the array of coefficients [1, 0, 7.5, 3] or [3, 7.5, 0, 1], depending on which direction you want to count. The 2nd convention has the nice property that if you put the coefficients into an array, then coef[i] is the power of x^i in the polynomial, and so is recommended.

To help make things easier for you, let us first implement the following three operations:

1.      Main Constructors.   Provide a constructor that takes a character string as input (such as "x^3 + 7.5x + 3") and constructs the polynomial appropriately (see below for more information about this).  Provide a default constructor as well.

2.      Destructor.  Be sure to release any memory you allocated in your constructor.

3.      Printing function.  Write a member function that prints out the polynomial in a friendly format by looking at the polynomial's internal representation.  This may be the trickiest function in the whole program-- not conceptually hard, but tricky, because of the quirky rules on how output is supposed to look.  Your function should generate the polynomial strings that we're used to seeing:

a.      If the coefficient is zero, then you shouldn't show the term at all.  But if all of the coefficients are zero, then the entire polynomial should show as "0".

b.      If the coefficient is 1 or -1, you shouldn't show it (e.g., "- x^2" instead of "- 1x^2").

c.       If the power is 1 then you shouldn't show the power (e.g., "35x" instead of "35x^1").

d.      If the power is zero then you shouldn't show the x factor (e.g., "-18" instead of "-18x^0").  

Start with a simpler version that ignores all of these rules, just to make sure you are storing the right numbers in your data structure.  Here are some more requirements for print().  These two rules constitute a general recipe for making your objects able to conveniently print themselves.

      1. Write your function so that you can call it like this:

        poly p("x^2-3x");
        p.print(cout);

        This means that your member function must take a stream variable as input, and print to whatever stream it's given.  The type of output stream variables like cout and cerr is "ostream", and such streams must always be passed by reference -- your program won't compile otherwise.  So inside your print function, instead of sending output to cout, send it to whatever you named your ostream& parameter.  It follows that your function should rely on operations appropriate for cout and not stdout.  In particular, don't use printf in this function.
      2. After you have the print(ostream&) member function working, overload the "<<" operator so that instead of writing p.print(cout) as above, you can also print the object with the familiar syntax:

        cout << "The polynomial is " << p << endl;

        The way to do this is to put this function prototype in your poly.h file after the end of the poly class definition:

        // Enable printing the poly to the given ostream, as in:
        // cout << p;

        std::ostream& operator<<(std::ostream& stream_arg, const poly& p);

        And in your poly.cc (or poly.cpp) file, implement this (global) function exactly as written here.  Don't try to make it a member function.

        std::ostream& operator<<(std::ostream& stream_arg, const poly& p) {
           p.print(stream_arg);
           return stream_arg;
        }

        The name of this function is "operator<<". 
         

After you have finished these three operations and your code is working, continue to implement the following operations:

4.      Copy constructor.

5.      Assignment (the overloaded operator=).

6.      Computing the multiplication of two polynomials.

7.      Computing the addition of two polynomials.

8.      Computing the subtraction of two polynomials.

9.      Computing the derivative of one polynomial.

10. Evaluating a polynomial when the free variable is bound to a given number.  For example, the polynomial x^3 + 7.5x + 3 has the value 26 when x=2.  The constant polynomial 5.2 (also known as 5.2x^0) has the value 5.2 no matter what value is given to x.

11.  Returning the degree of a polynomial.  

12.  Comparing two polynomials for equality.  (That is, whether they both represent the same polynomial value, not whether they are both in fact the same object at the same place in memory.)

How to Parse a Polynomial

Use the functions provided here in polyparse.h and polyparse.cc.  See the documentation in polyparse.h.  The main function uses some C++ features we haven’t talked about yet, namely, standard template library (STL) data structures.  But you should be able to use it now without completely understanding how it works.  By the way, in later courses you will learn more general techniques for parsing input.  The function here was written specifically for this program.

Further Requirements and Considerations 

1.      You must not impose an arbitrary limit on the degrees of polynomials.  If I want to create a polynomial of degree 50,000, and there is enough memory, then you should support it.  This implies that you should use a dynamically allocated array to store your coefficients (for an example how to use copy constructors, see the Namelist examples at pages 129-135).  Be sure to implement your copy constructor, operator=, and destructor so that you do not make any errors such as leaking memory or freeing the same pointer twice.

2.      You must define your member functions to be const whenever it makes sense.  (This is generally sound advice, but people tend to overlook this when first writing classes, so I'm making it a prominent requirement here.) 

3.      You may use operator overloading (chapter 6) to name your functions whenever it seems appropriate.  For example it's more natural to write "a = a*b-c" than "a = a.multiply(b).subtract(c)".  Note that you automatically get the standard operator precedences when you use operator overloading.  But you do not have to use additional operator overloading, other than for assignment (operator=).

4.      Remember: encapsulate.  Hide as much implementation detail as possible from the client/user of the class.  The .h file should contain all that the user needs to know about the objects.  Be very careful to document all of the requirements you are imposing on the users of the polynomial class. 

5.      Minimize the number of different ways to change a polynomial.  Is there a good reason for any member function (other than assignment) to alter the polynomial that is being called?  Probably not, but if you have a good reason, your documentation should make it very clear.

6.      You must submit (at least) three source files: your polynomial class's .h file (poly.h), its .cc (or .cpp) implementation file (poly.cc), and a main program .cc/.cpp file (main.cc) showing test cases that exercise the program requirements.  Compiling multiple source files is relly no harder than compiling a single source file.  You give the .cc (or .cpp) names to the compiler (but never the .h names -- they are only ever consulted as a result of reading in a .cc file) and off it goes.

g++ -Wall -g -o poly main.cc poly.cc polyparse.cc

7.      Test your program thoroughly.  If done properly, your program should produce the same results as shown below.  But beware that the example code below does not test all of the required operations.

Example

Here is some sample code.  Your syntax may vary from this, but these are the sorts of things your class ought to support:

poly a("X^3 - 7.5x - 3");
cout << "a = " << a << endl;

poly b = a.multiply(a).subtract(a);
cout << "b = a*a - a = " << b << endl;

poly c = b.derivative();
cout << "c = db/dx = " << c << endl;

poly d = c.multiply(c);
d = d.multiply(d);

cout << "d = c**4 = " << d << endl;

cout << "Now enter polynomials, one per line. Type the EOF ";
cout << "character when done." << endl;

char line[100];
while (cin.getline(line, sizeof line)) {
  const poly p(line);
  cout << "The parsed polynomial is: " << p << endl;
}

Sample output:

a = x^3 - 7.5x - 3
b = a*a - a = x^6 - 15x^4 - 7x^3 + 56.25x^2 + 52.5x + 12
c = db/dx = 6x^5 - 60x^3 - 21x^2 + 112.5x + 52.5
d = c**4 = 1296x^20 - 51840x^18 - 18144x^17 + 874800x^16 + 589680x^15 - 8.00474e+06x^14 - 7.8246e+06x^13 + 4.24724e+07x^12 + 5.44932e+07x^11 - 1.28656e+08x^10 - 2.13697e+08x^9 + 1.94625e+08x^8 + 4.69348e+08x^7 - 4.97095e+07x^6 - 5.21605e+08x^5 - 2.23225e+08x^4 + 1.86135e+08x^3 + 1.97148e+08x^2 + 6.51164e+07x + 7.59691e+06
Now enter polynomials, one per line. Type the EOF character when done.
10x^4 -2x^2 +1
The parsed polynomial is: 10x^4 - 2x^2 + 1
300.7 x^3 - x - 3x -x
The parsed polynomial is: 300.7x^3 - 5x

 

Extra Credit

If you are feeling ambitious, you may also like to work on implementing a function that performs polynomial division.  This is somewhat challenging, and is not necessarily recommended as a way to make up for lost points-- it is  just an interesting problem.

              5x^3      +  x
          __________________________________
2x^2 - 1  |  10x^5      - 3x^3     + x + 2
   subtract  10x^5      - 5x^3
          _____________________
                          2x^3     + x + 2
 
           subtract       2x^3     - x
                       ___________________
                                    2x + 2

In this example, the polynomial 10x^5 - 3x^3 + x + 2 (the dividend) is divided by 2x^2 - 1 (the divisor), yielding the quotient 5x^3 + x and the remainder 2x+2.  Your division function should be called upon the dividend and produce the quotient and remainder as outputs, as in this prototype:

  void poly::divide(const poly &divisor, 
                    poly& quotientOut, 
                    poly& remainderOut) const;

Your function does not have to produce a long-division graphic like that shown above. That was created by hand!

Suppose that a user has polynomials named "dividend" and "divisor" and executes the following sequence:

  poly quotient;
  poly remainder;
  dividend.divide(divisor, quotient, remainder);

Then the resulting quotient and remainder should satisfy these two constraints, which define what it means to divide polynomials:

·         dividend == quotient.multiply(divisor).add(remainder)

·         remainder.degree() < divisor.degree()

The degree comparison is what tells your algorithm that it  has  produced enough of the quotient and can stop dividing. 

If you want to attempt this function, then do a few examples by hand first so that you get the hang of it.

What You Are To Hand In

Submit your source code and a README file. Your source code must contain sufficient documentation. Put additional comments for us into your README file. 

How You Will Be Graded

Criteria

Possible
Points

Program Integrity

·    required operations are implemented

·    submitted executable program runs properly

·    submitted source files compile properly

·    no memory leaks or double frees

·    no internal data or functions are improperly exposed

·    member functions declared const when possible

·    test cases are reasonable and illustrative

90

Documentation and Formatting 

·    all requirements to use the objects are clearly documented in the .h file

·    code has adequate in-line comments

·    code has adequate whitespace for readability

·    code is properly and consistently indented

·    variables are documented

·    documentation is meaningful and does not merely echo code

10

Extra Credit

·    division function works in general case

10