File:
poly.cpp
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include "poly.h"
using namespace std;
/*
* Remove leading zeros, but so as to empty the list.
*/
void Polynomial::normalize()
{
// Remove zeros from the end, except don't make the vector empty.
while(m_coeff.size() > 1 && m_coeff.back() == 0.0)
m_coeff.pop_back();
}
/*
* Evaluate at x. The trick is
* 9x^5 + 8x^4 + 7x^3 + 5x^2 + 2x + 3 ==
* ((((((9)x + 8)x + 7)x + 5)x + 2)x + 3)
* Each successive assignment of val is the value of a set of parentheses,
* working from inside out. Initialize to highest coef (9 in the example),
* then work through the other levels in the loop. For the example, the first
* iteration will multiply 9 by x and add 8. The next multiplies that sum
* by x again, and adds 7, etc.
*/
double Polynomial::f(double x) const
{
// Scan through the array _backwards_ (high order to low), computing
// as indicated above. We initialize ret to the high coeff, then
// loop through the others, multiplying x by the previous sum and
// adding the coeff.
vector<double>::const_reverse_iterator i = m_coeff.rbegin();
double val = *i++;
for(; i != m_coeff.rend(); i++) {
val = val * x + *i;
}
return val;
}
/*
* Return the first derivative.
*/
Polynomial Polynomial::dx() const
{
vector<double> newvec;
if(m_coeff.size() <= 1) { // Should never be 0. Using <= is paranoia.
// If the poly is only a constant, df is 0.
newvec.push_back(0.0);
} else {
// The reserve is for efficiency only. It makes sure the
// object reserves all needed memory ahead of time, rather
// than having to grow the storage as needed, guessing each
// amount. It does not change the size, or any attribute
// viewable through the public interface. This method still
// works fine if the reserve call is removed.
newvec.reserve(m_coeff.size()-1);
// Push the coefficients on the new array.
int exp = 1;
for(auto i = m_coeff.begin() + 1; i != m_coeff.end(); ++i)
newvec.push_back(*i * exp++);
}
// Return a Polynomial object containg the array we just built.
return Polynomial(newvec);
}
/*
* Return a string representation.
*/
void Polynomial::print(ostream &strm) const
{
int exp = order(); // Exponent of the term.
bool first = true; // First time through the loop
// This loop goes through the vector backwards (rbegin() and rend()
// return reverse iterators), so from highest to lowest exponent.
for(auto i = m_coeff.rbegin(); i != m_coeff.rend(); ++i, --exp) {
// If the coefficient is zero, we skip it entirely.
if(*i == 0.0) continue;
// Get the coefficient, and clean up the format a bit.
// If the coeff is 1.0, just represent it as the
// empty string, unless it's the constant term.
string coeff = "";
if(*i != 1.0 || exp == 0) {
// Get the coefficient as a string (removing the sign).
coeff = to_string(abs(*i));
// There are some shorter ways to do this, but
// not that I want to explain just yet. This
// is a good string exercise, though. If there
// is a decimal place (we expect so), we strip
// all trailing zeros, and the decimal point also
// if it is the last after zero removal.
if(coeff.find('.') != string::npos) {
while(coeff.back() == '0')
coeff.pop_back();
if(coeff.back() == '.')
coeff.pop_back();
// Note: I can't run .back or .pop_back unless
// I know the string isn't empty. But I do know
// that, since it contains at least a decimal.
// Otherwise, I would add a coeff.size() > 0
// test first.
}
}
// Figure out what to put before the term.
if(first) {
// Enters on first term, but we don't want it back
// here again.
first = false;
// This is the first term printed. Doesn't need
// anything before it, but if it's negative,
// we need to say so (we stripped the sign from
// the coefficient string).
if(*i < 0.0) strm << "-";
} else {
// Not the first. Put a + or - operator, based
// on sign of the number.
if(*i < 0.0) strm << " - ";
else strm << " + ";
}
// Output the coefficient, then if the power exceeds 0,
// put the x that we're multiplying by, and if the exponent
// is more than 1, output that.
strm << coeff;
if(exp > 0) strm << "x";
if(exp > 1) strm << "^" << exp;
}
// If all the terms were zero, we skipped them all. That
// won't quite do.
if(first) strm << "0";
}
This file contains the bodies of larger methods from
the the polynomial class
Notice that when methods are defined here, they are physically
located outside the class definition. For that reason, each
method is prefixed by the class name, for instance
Polynomial::normalize. This tells the compiler that
the name belongs to the class, even though it's not inside it.
There is no hard and fast rule about which
methods should stay inside the class definition and which should
be moved to the .cpp file, but generally methods larger
than one or two lines should be in .cpp.