Project 1 (Due
October 26 24:00)
Polynomial class
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.
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.
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.)
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.
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.
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
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.
Submit your source code and a
README file. Your source code must contain sufficient
documentation. Put additional
comments for us into your README file.
|
Criteria |
Possible |
|
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 |