Theoretica
A C++ numerical and automatic mathematical library
polynomial.h
Go to the documentation of this file.
1 
5 
6 #ifndef THEORETICA_POLYNOMIAL_H
7 #define THEORETICA_POLYNOMIAL_H
8 
9 #ifndef THEORETICA_NO_PRINT
10 #include <sstream>
11 #include <ostream>
12 #endif
13 
14 #include "../core/real_analysis.h"
15 #include "../algebra/vec.h"
16 #include "../complex/complex.h"
17 #include "../complex/complex_analysis.h"
18 
19 
20 namespace theoretica {
21 
24  template<typename Type = real>
25  class polynomial {
26  public:
27  std::vector<Type> coeff;
28 
30  polynomial() : coeff() {}
31 
33  polynomial(Type a) : coeff({a}) {}
34 
36  polynomial(const std::vector<Type>& c) : coeff(c) {}
37 
39  polynomial(std::initializer_list<Type> l) : coeff(l) {}
40 
43 
44 
46  inline Type& at(int i) {
47  return coeff[i];
48  }
49 
50 
52  inline Type get(int i) const {
53  return coeff[i];
54  }
55 
56 
58  inline const Type& operator[](unsigned int i) const {
59  return coeff[i];
60  }
61 
62 
64  inline Type& operator[](unsigned int i) {
65  return coeff[i];
66  }
67 
68 
70  template<typename EvalType = Type>
71  inline EvalType eval(EvalType x) const {
72 
73  if(!coeff.size())
74  return 0;
75 
76  EvalType sum = 0;
77 
78  // Evaluate using Horner's method
79  for (unsigned int i = 0; i < coeff.size(); ++i)
80  sum = coeff[coeff.size() - i - 1] + x * sum;
81 
82  // TO-DO Compare performance using fma x86 Assembly instruction
83  // sum = fma(x, sum, coeff[coeff.size() - i - 1]);
84 
85  return sum;
86  }
87 
88 
90  template<typename EvalType = Type>
91  inline EvalType operator()(EvalType x) const {
92  return eval(x);
93  }
94 
95 
98  inline unsigned int find_order() const {
99 
100  for (int i = coeff.size() - 1; i >= 0; --i) {
101  if(abs(coeff[i]) < MACH_EPSILON)
102  return i;
103  }
104 
105  return 0;
106  }
107 
108 
110  inline void trim() {
111 
112  for (unsigned int i = coeff.size() - 1; i >= 0; --i) {
113  if(abs(coeff[i]) > MACH_EPSILON)
114  break;
115 
116  coeff.pop_back();
117  }
118  }
119 
120 
122  inline size_t size() const {
123  return coeff.size();
124  }
125 
126 
128  inline polynomial operator+(const polynomial& p) const {
129 
130  polynomial r;
131  r.coeff.resize(max(coeff.size(), p.size()));
132 
133  unsigned int i = 0;
134 
135  for (; i < min(coeff.size(), p.size()); ++i)
136  r[i] = coeff[i] + p[i];
137 
138  if (coeff.size() > p.size())
139  for (; i < coeff.size(); ++i)
140  r[i] = coeff[i];
141  else
142  for (; i < p.size(); ++i)
143  r[i] = p[i];
144 
145  return r;
146  }
147 
148 
150  inline polynomial operator-(const polynomial& p) const {
151 
152  polynomial r;
153  r.coeff.resize(max(coeff.size(), p.size()));
154 
155  unsigned int i = 0;
156 
157  for (; i < min(r.size(), p.size()); ++i)
158  r[i] = coeff[i] - p.get(i);
159 
160  if (coeff.size() > p.size())
161  for (; i < coeff.size(); ++i)
162  r[i] = coeff[i];
163  else
164  for (; i < p.size(); ++i)
165  r[i] = -p[i];
166 
167  return r;
168  }
169 
170 
172  inline polynomial operator*(const polynomial& p) const {
173 
174  polynomial r = polynomial();
175  r.coeff.resize(this->size() + p.size() - 1);
176 
177  for (unsigned int i = 0; i < size(); ++i) {
178  for (unsigned int j = 0; j < p.size(); ++j) {
179  r[i + j] += coeff[i] * p.get(j);
180  }
181  }
182 
183  return r;
184  }
185 
186 
188  inline polynomial operator/(const polynomial& d) const {
189 
190  const unsigned int d_order = d.find_order();
191  const unsigned int this_order = find_order();
192 
193  if(d_order == 0 && d.get(0) == 0) {
194  TH_MATH_ERROR("polynomial::operator/", d.get(0), DIV_BY_ZERO);
195  return polynomial(nan());
196  }
197 
198  // Remainder
199  polynomial r = *this;
200 
201  // Quotient
202  polynomial q = polynomial();
203  unsigned int i = 0;
204 
205  while(i < this_order) {
206 
207  // Compute only once the degree of the polynomial
208  const unsigned int r_order = r.find_order();
209 
210  // Stop execution if the division is complete
211  // (when the remainder is 0 or has lower degree)
212  if((r_order == 0 && (abs(r.get(0)) < MACH_EPSILON)) || r_order < d_order)
213  break;
214 
215  // Simple division between highest degree terms
217  r.get(r_order) / d.get(d_order),
218  r_order - d_order);
219 
220  // Add monomial to quotient and subtract the
221  // monomial times the dividend from the remainder
222  q += t;
223  r -= t * d;
224 
225  i++;
226  }
227 
228  // The algorithm has stopped iterating after a number
229  // of the dividend's degree counts
230  if(i == this_order) {
231  TH_MATH_ERROR("polynomial::operator/", i, NO_ALGO_CONVERGENCE);
232  return polynomial(nan());
233  }
234 
235  return q;
236  }
237 
238 
240  inline polynomial operator*(Type a) const {
241 
242  polynomial r = polynomial(*this);
243 
244  for (unsigned int i = 0; i < size(); ++i)
245  r.coeff[i] *= a;
246 
247  return r;
248  }
249 
250 
252  inline polynomial operator/(Type a) const {
253 
254  if(abs(a) < MACH_EPSILON) {
255  TH_MATH_ERROR("polynomial::operator/", a, DIV_BY_ZERO);
256  }
257 
258  polynomial r = polynomial(*this);
259 
260  for (unsigned int i = 0; i < size(); ++i)
261  r.coeff[i] /= a;
262 
263  return r;
264  }
265 
266 
268  inline polynomial& operator+=(const polynomial& p) {
269 
270  // Make room for the new coefficients
271  if(coeff.size() < p.size())
272  coeff.resize(p.size(), Type(0));
273 
274  for (unsigned int i = 0; i < min(size(), p.size()); ++i)
275  coeff[i] += p.get(i);
276 
277  return *this;
278  }
279 
280 
282  inline polynomial& operator-=(const polynomial& p) {
283 
284  // Make room for the new coefficients
285  if(coeff.size() < p.size())
286  coeff.resize(p.size(), Type(0));
287 
288  for (unsigned int i = 0; i < min(coeff.size(), p.size()); ++i)
289  coeff[i] -= p.get(i);
290 
291  return *this;
292  }
293 
294 
296  inline polynomial& operator*=(const polynomial& p) {
297 
298  polynomial r = polynomial();
299  r.coeff.resize(this->size() + p.size() - 1, Type(0));
300 
301  for (unsigned int i = 0; i < coeff.size(); ++i)
302  for (unsigned int j = 0; j < p.size(); ++j)
303  r[i + j] += coeff[i] * p.get(j);
304 
305  *this = r;
306  return *this;
307  }
308 
309 
311  inline polynomial& operator*=(Type a) {
312 
313  for (unsigned int i = 0; i < coeff.size(); ++i)
314  coeff[i] *= a;
315 
316  return *this;
317  }
318 
319 
321  inline polynomial& operator/=(const polynomial& a) {
322  return (*this = (*this / a));
323  }
324 
325 
327  inline polynomial& operator/=(Type a) {
328 
329  if(abs(a) < MACH_EPSILON) {
330  TH_MATH_ERROR("polynomial::operator/=", a, DIV_BY_ZERO);
331  return *this;
332  }
333 
334  for (unsigned int i = 0; i < coeff.size(); ++i)
335  coeff[i] /= a;
336 
337  return *this;
338  }
339 
340 
342  inline bool operator==(const polynomial& other) const {
343 
344  const unsigned int n = min(other.size(), this->size());
345  for (unsigned int i = 0; i < n; ++i) {
346  if(abs(other.coeff[i] - coeff[i]) >= MACH_EPSILON)
347  return false;
348  }
349 
350  if(size() > other.size()) {
351  for (unsigned int i = 0; i < size(); ++i) {
352  if(abs(coeff[i]) >= MACH_EPSILON)
353  return false;
354  }
355  } else {
356  for (unsigned int i = 0; i < other.size(); ++i) {
357  if(abs(other.coeff[i]) >= MACH_EPSILON)
358  return false;
359  }
360  }
361 
362  return true;
363  }
364 
365 
368  inline auto begin() {
369  return coeff.begin();
370  }
371 
372 
375  inline auto end() {
376  return coeff.end();
377  }
378 
379 
382  inline auto cbegin() {
383  return coeff.cbegin();
384  }
385 
386 
389  inline auto cend() {
390  return coeff.cend();
391  }
392 
393 
395  inline vec<complex<>, 2> quadratic_roots() const {
396 
397  const int order = find_order();
398 
399  // Check that the polynomial is quadratic
400  if(order != 2) {
401  TH_MATH_ERROR("quadratic_roots", order, IMPOSSIBLE_OPERATION);
402  return vec<complex<>, 2>({nan(), nan()});
403  }
404 
405  const Type p = coeff[1] / coeff[2];
406  const Type q = coeff[0] / coeff[2];
407 
408  // Case when 0 is a root
409  if(abs(q) < MACH_EPSILON)
410  return {complex<>(-p), complex<>(0)};
411 
412  complex<> z1, z2;
413 
414  // Use Vieta's theorem to avoid catastrophic cancellation
415  if(abs(p) > 1) {
416 
417  z1 = -sgn(p) * (abs(p) / 2.0
418  + abs(p) * sqrt(complex<>(0.25 - (q / p) / p)));
419  z2 = q / z1;
420 
421  } else {
422 
423  const complex<> s = sqrt(complex<>(0.25 * square(p) - q));
424  z1 = -p / 2.0 + s;
425  z2 = -p / 2.0 - s;
426  }
427 
428  return {z1, z2};
429  }
430 
431 
434  const std::vector<Type>& roots) {
435 
436  polynomial<Type> P = {1};
437 
438  // P = product((x - r_i))
439  for (unsigned int i = 0; i < roots.size(); ++i)
440  P *= polynomial<Type>({roots[i] * -1, 1});
441 
442  return P;
443  }
444 
445 
447  inline static polynomial<Type> monomial(Type c, unsigned int order) {
448 
449  polynomial m;
450  m.coeff = std::vector<Type>(order + 1, Type(0));
451  m.coeff[order] = c;
452  return m;
453  }
454 
455 
456  // Friend operators to enable equations of the form
457  // (Type) op. (polynomial<Type>)
458 
459  inline friend polynomial<Type> operator+(
460  Type r, const polynomial<Type>& z) {
461 
462  return z + polynomial(r);
463  }
464 
465  inline friend polynomial<Type> operator-(
466  Type r, const polynomial<Type>& z) {
467 
468  return (z * -1) + polynomial(r);
469  }
470 
471  inline friend polynomial<Type> operator*(
472  Type r, const polynomial<Type>& z) {
473 
474  return z * r;
475  }
476 
477 
478 #ifndef THEORETICA_NO_PRINT
479 
482  const std::string& unknown = "x",
483  const std::string& exponentiation = "^") const {
484 
485  std::stringstream res;
486  bool flag = false;
487  const int sz = coeff.size();
488 
489  for (int i = sz - 1; i >= 0; --i) {
490 
491  if(abs(coeff[i]) < MACH_EPSILON)
492  continue;
493 
494  res << (coeff[i] >= 0 ? "+ " : "- ");
495  res << abs(coeff[i]);
496 
497  if(i) {
498  res << "*" << unknown << exponentiation << i;
499  res << " ";
500  }
501 
502  flag = true;
503  }
504 
505  if(!flag)
506  res << "0";
507 
508  return res.str();
509  }
510 
511 
513  inline operator std::string() {
514  return to_string();
515  }
516 
517 
520  friend std::ostream& operator<<(std::ostream& out, const polynomial& obj) {
521  return out << obj.to_string();
522  }
523 
524 #endif
525 
526 
527  };
528 
529 }
530 
531 #endif
Complex number in algebraic form .
Definition: complex.h:26
A polynomial of arbitrary order.
Definition: polynomial.h:25
polynomial & operator-=(const polynomial &p)
Subtract a polynomial from this one.
Definition: polynomial.h:282
~polynomial()
Default destructor.
Definition: polynomial.h:42
polynomial & operator*=(Type a)
Multiply a polynomial by a scalar value.
Definition: polynomial.h:311
auto begin()
Get an iterator for the first coefficient of the polynomial.
Definition: polynomial.h:368
polynomial & operator+=(const polynomial &p)
Sum a polynomial to this one.
Definition: polynomial.h:268
polynomial(std::initializer_list< Type > l)
Initialize from an std::initializer_list.
Definition: polynomial.h:39
polynomial & operator/=(const polynomial &a)
Multiply a polynomial by a scalar value.
Definition: polynomial.h:321
polynomial(const std::vector< Type > &c)
Initialize from an std::vector.
Definition: polynomial.h:36
auto cbegin()
Get a const iterator for the first coefficient of the polynomial.
Definition: polynomial.h:382
const Type & operator[](unsigned int i) const
Return the nth order coefficient.
Definition: polynomial.h:58
static polynomial< Type > monomial(Type c, unsigned int order)
Returns a monomial of the given degree and coefficient.
Definition: polynomial.h:447
EvalType eval(EvalType x) const
Evaluate the polynomial using x as variable.
Definition: polynomial.h:71
polynomial operator/(const polynomial &d) const
Polynomial division.
Definition: polynomial.h:188
void trim()
Remove trailing zero coefficients.
Definition: polynomial.h:110
Type get(int i) const
Get the i-th by value.
Definition: polynomial.h:52
EvalType operator()(EvalType x) const
Evaluate the polynomial using x as variable.
Definition: polynomial.h:91
Type & operator[](unsigned int i)
Return the nth order coefficient.
Definition: polynomial.h:64
polynomial & operator/=(Type a)
Divide a polynomial by a scalar value.
Definition: polynomial.h:327
polynomial(Type a)
Initialize as a constant.
Definition: polynomial.h:33
auto end()
Get an iterator for the one plus last coefficient of the polynomial.
Definition: polynomial.h:375
bool operator==(const polynomial &other) const
Check whether two polynomials are equal.
Definition: polynomial.h:342
polynomial operator*(const polynomial &p) const
Multiply two polynomials.
Definition: polynomial.h:172
Type & at(int i)
Access i-th coefficient.
Definition: polynomial.h:46
polynomial()
Initialize as an empty polynomial.
Definition: polynomial.h:30
friend std::ostream & operator<<(std::ostream &out, const polynomial &obj)
Stream the polynomial in string representation to an output stream (std::ostream)
Definition: polynomial.h:520
unsigned int find_order() const
Find the true order of the polynomial (ignoring trailing null coefficients)
Definition: polynomial.h:98
vec< complex<>, 2 > quadratic_roots() const
Compute the roots of a quadratic polynomial.
Definition: polynomial.h:395
auto cend()
Get a const iterator for the one plus last coefficient of the polynomial.
Definition: polynomial.h:389
size_t size() const
Get the number of coefficients.
Definition: polynomial.h:122
static polynomial< Type > from_roots(const std::vector< Type > &roots)
Construct a polynomial from its roots.
Definition: polynomial.h:433
polynomial operator+(const polynomial &p) const
Sum two polynomials.
Definition: polynomial.h:128
polynomial operator-(const polynomial &p) const
Subtract a polynomial from another.
Definition: polynomial.h:150
polynomial operator/(Type a) const
Divide a polynomial by a scalar.
Definition: polynomial.h:252
std::string to_string(const std::string &unknown="x", const std::string &exponentiation="^") const
Convert the polynomial to string representation.
Definition: polynomial.h:481
polynomial & operator*=(const polynomial &p)
Multiply two polynomials.
Definition: polynomial.h:296
polynomial operator*(Type a) const
Multiply a polynomial by a scalar.
Definition: polynomial.h:240
A statically allocated N-dimensional vector with elements of the given type.
Definition: vec.h:88
#define TH_MATH_ERROR(F_NAME, VALUE, EXCEPTION)
TH_MATH_ERROR is a macro which throws exceptions or modifies errno (depending on which compiling opti...
Definition: error.h:219
std::string string(size_t length)
Generate a random string made of human-readable ASCII characters.
Definition: random.h:102
Main namespace of the library which contains all functions and objects.
Definition: algebra.h:27
auto min(const Vector &X)
Finds the minimum value inside a dataset.
Definition: dataset.h:351
dual2 sqrt(dual2 x)
Compute the square root of a second order dual number.
Definition: dual2_functions.h:54
dual2 abs(dual2 x)
Compute the absolute value of a second order dual number.
Definition: dual2_functions.h:183
auto sum(const Vector &X)
Compute the sum of a vector of real values using pairwise summation to reduce round-off error.
Definition: dataset.h:219
auto max(const Vector &X)
Finds the maximum value inside a dataset.
Definition: dataset.h:330
std::vector< real > roots(RealFunction f, real a, real b, real tolerance=OPTIMIZATION_TOL, real steps=10)
Find the roots of a function inside a given interval.
Definition: roots.h:462
constexpr real MACH_EPSILON
Machine epsilon for the real type.
Definition: constants.h:197
int sgn(real x)
Return the sign of x (1 if positive, -1 if negative, 0 if null)
Definition: real_analysis.h:259
dual2 square(dual2 x)
Return the square of a second order dual number.
Definition: dual2_functions.h:23
real nan()
Return a quiet NaN number in floating point representation.
Definition: error.h:54