Tatooine
polynomial.h
Go to the documentation of this file.
1#ifndef TATOOINE_POLYNOMIAL_H
2#define TATOOINE_POLYNOMIAL_H
3//==============================================================================
5#include <tatooine/tensor.h>
7
8#include <ostream>
9#include <type_traits>
10//==============================================================================
11namespace tatooine {
12//==============================================================================
13template <typename Real, std::size_t Degree>
14struct polynomial {
15 //----------------------------------------------------------------------------
16 // typedefs
17 //----------------------------------------------------------------------------
18 public:
19 using real_type = Real;
20 //----------------------------------------------------------------------------
21 // static methods
22 //----------------------------------------------------------------------------
23 public:
24 static constexpr std::size_t degree() { return Degree; }
25 //----------------------------------------------------------------------------
26 // members
27 //----------------------------------------------------------------------------
28 private:
29 std::array<Real, Degree + 1> m_coefficients;
30
31 //----------------------------------------------------------------------------
32 // ctors
33 //----------------------------------------------------------------------------
34 public:
35 constexpr polynomial() : m_coefficients{std::array<Real, Degree + 1>{}} {}
36 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
37 constexpr polynomial(polynomial const& other) = default;
38 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
39 constexpr polynomial(polynomial&& other) noexcept = default;
40 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41 constexpr auto operator=(polynomial const& other) -> polynomial& = default;
42 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
43 constexpr auto operator=(polynomial&& other) noexcept
44 -> polynomial& = default;
45 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46 constexpr polynomial(std::array<Real, Degree + 1> const& coeffs)
47 : m_coefficients{coeffs} {}
48 //----------------------------------------------------------------------------
49 template <typename OtherReal, std::size_t OtherDegree>
50 requires(OtherDegree <= Degree)
52 : m_coefficients{make_array<Degree + 1>(Real(0))} {
53 for (std::size_t i = 0; i < OtherDegree + 1; ++i) {
54 m_coefficients[i] = other.coefficient(i);
55 }
56 }
57 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58 constexpr polynomial(std::array<Real, Degree + 1>&& coeffs)
59 : m_coefficients{std::move(coeffs)} {}
60 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61 template <arithmetic... Coeffs>
62 constexpr polynomial(arithmetic auto const... coeffs) requires(
63 sizeof...(coeffs) == Degree + 1)
64
65 : m_coefficients{static_cast<Real>(coeffs)...} {}
66 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
67 template <arithmetic OtherReal>
69 : m_coefficients{make_array<Real>(coeffs.data())} {
70 }
71 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
72 template <arithmetic OtherReal>
73 constexpr polynomial(std::array<OtherReal, Degree + 1> const& coeffs)
74 : m_coefficients{make_array<Real>(coeffs)} {
75 }
76
77 //----------------------------------------------------------------------------
78 // methods
79 //----------------------------------------------------------------------------
80 public:
82 constexpr auto evaluate(arithmetic auto const x) const {
83 Real y = 0;
84 Real acc = 1;
85 for (std::size_t i = 0; i < Degree + 1; ++i) {
86 y += acc * m_coefficients[i];
87 acc *= static_cast<Real>(x);
88 }
89 return y;
90 }
91 //----------------------------------------------------------------------------
93 constexpr auto operator()(arithmetic auto const x) const { return evaluate(x); }
94 //----------------------------------------------------------------------------
95 auto c(std::size_t i) const -> auto const& { return m_coefficients[i]; }
96 auto c(std::size_t i) -> auto& { return m_coefficients[i]; }
97 //----------------------------------------------------------------------------
98 auto coefficient(std::size_t i) const -> auto const& { return m_coefficients[i]; }
99 auto coefficient(std::size_t i) -> auto const& { return m_coefficients[i]; }
100 //----------------------------------------------------------------------------
101 auto coefficients() const -> auto const& { return m_coefficients; }
102 auto coefficients() -> auto const& { return m_coefficients; }
103 //----------------------------------------------------------------------------
104 constexpr auto set_coefficients(std::array<Real, Degree + 1> const& coeffs)
105 -> void {
106 m_coefficients = coeffs;
107 }
108 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109 constexpr auto set_coefficients(std::array<Real, Degree + 1>&& coeffs)
110 -> void {
111 m_coefficients = std::move(coeffs);
112 }
113 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114 template <arithmetic OtherReal>
115 constexpr auto set_coefficients(
116 std::array<OtherReal, Degree + 1> const& coeffs) -> void {
117 m_coefficients = make_array<Real>(coeffs);
118 }
119 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120
121 constexpr auto set_coefficients(arithmetic auto const... coeffs)
122 -> void requires(sizeof...(coeffs) == Degree + 1) {
123 m_coefficients = std::array<Real, Degree + 1>{static_cast<Real>(coeffs)...};
124 }
125 //----------------------------------------------------------------------------
126 private:
127 template <std::size_t... Is>
128 constexpr auto diff(std::index_sequence<Is...> /*seq*/) const {
129 return polynomial<Real, Degree - 1>{(m_coefficients[Is + 1] * (Is + 1))...};
130 }
131 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
132 public:
133 constexpr auto diff() const {
134 if constexpr (Degree >= 1) {
135 return diff(std::make_index_sequence<Degree>{});
136 } else {
137 return polynomial<Real, 0>{0};
138 }
139 }
140 auto print(std::ostream& out, std::string const& x) const -> std::ostream& {
141 out << c(0);
142 if (Degree >= 1) {
143 if (c(1) != 0) {
144 if (c(1) == 1) {
145 out << " + " << x;
146 } else if (c(1) == -1) {
147 out << " - " << x;
148 } else {
149 out << " + " << c(1) << " * " << x;
150 }
151 }
152 }
153 for (std::size_t i = 2; i < Degree + 1; ++i) {
154 if (c(i) != 0) {
155 if (c(i) == 1) {
156 out << " + " << x << "^" << i;
157 } else if (c(i) == -1) {
158 out << " - " << x << "^" << i;
159 } else {
160 out << " + " << c(i) << " * " << x << "^" << i;
161 }
162 }
163 }
164 return out;
165 }
166};
167//------------------------------------------------------------------------------
168// deduction guides
169//------------------------------------------------------------------------------
170template <typename... Coeffs>
171polynomial(Coeffs... coeffs)
172 -> polynomial<common_type<Coeffs...>, sizeof...(Coeffs) - 1>;
173template <typename Real, std::size_t N>
174polynomial(tensor<Real, N> const&) -> polynomial<Real, N - 1>;
175//------------------------------------------------------------------------------
176// diff
177//------------------------------------------------------------------------------
178template <typename Real, std::size_t Degree>
179constexpr auto diff(polynomial<Real, Degree> const& f) {
180 return f.diff();
181}
182//------------------------------------------------------------------------------
183// solve
184//------------------------------------------------------------------------------
186template <typename Real>
187auto solve(polynomial<Real, 1> const& p) -> std::vector<Real> {
188 if (p.c(1) == 0) {
189 return {};
190 }
191 return {-p.c(0) / p.c(1)};
192}
193//------------------------------------------------------------------------------
195template <typename Real>
196auto solve(polynomial<Real, 2> const& p) -> std::vector<Real> {
197 auto const a = p.c(0);
198 auto const b = p.c(1);
199 auto const c = p.c(2);
200 if (c == 0) {
201 return solve(polynomial{a, b});
202 }
203
204 auto const discr = b * b - 4 * a * c;
205 if (discr < 0) {
206 return {};
207 } else if (std::abs(discr) < 1e-10) {
208 return {-b / (2 * c)};
209 }
210 std::vector<Real> solutions;
211 solutions.reserve(2);
212 Real q =
213 (b > 0) ? -0.5 * (b + std::sqrt(discr)) : -0.5 * (b - std::sqrt(discr));
214 solutions.push_back(q / c);
215 solutions.push_back(a / q);
216 std::swap(solutions[0], solutions[1]);
217 return solutions;
218}
219//------------------------------------------------------------------------------
223template <typename Real>
225 if (f.c(3) == 0) {
226 return solve(polynomial{f.c(0), f.c(1), f.c(2)});
227 }
228 std::vector<Real> solutions;
229 Real sub;
230 Real A, B, C;
231 Real sq_A, p, q;
232 Real cb_p, D;
233
234 // normal form: x^3 + Ax^2 + Bx + C = 0
235 A = f.c(2) / f.c(3);
236 B = f.c(1) / f.c(3);
237 C = f.c(0) / f.c(3);
238
239 // substitute x = y - A/3 to eliminate quadric term: x^3 +px + q = 0
240 sq_A = A * A;
241 p = Real(1) / 3 * (-Real(1) / 3 * sq_A + B);
242 q = Real(1) / 2 * (Real(2) / 27 * A * sq_A - Real(1) / 3 * A * B + C);
243
244 // use Cardano's formula
245 cb_p = p * p * p;
246 D = q * q + cb_p;
247
248 constexpr Real eps = 1e-30;
249 constexpr auto is_zero = [](auto const x) {
250 return ((x) > -eps && (x) < eps);
251 };
252 if (is_zero(D)) {
253 if (is_zero(q)) { // one triple solution
254 solutions = {0};
255 } else { // one single and one double solution
256 Real u = std::cbrt(-q);
257 solutions = {2 * u, -u};
258 }
259 } else if (D < 0) { // Casus irreducibilis: three real solutions
260 Real phi = Real(1) / 3 * std::acos(-q / std::sqrt(-cb_p));
261 Real t = 2 * std::sqrt(-p);
262
263 solutions = {t * std::cos(phi), -t * std::cos(phi + M_PI / 3),
264 -t * std::cos(phi - M_PI / 3)};
265 } else { // one real solution
266 Real sqrt_D = std::sqrt(D);
267 Real u = std::cbrt(sqrt_D - q);
268 Real v = -std::cbrt(sqrt_D + q);
269
270 solutions = {u + v};
271 }
272
273 // resubstitute
274 sub = Real(1) / 3 * A;
275
276 for (auto& s : solutions) {
277 s -= sub;
278 }
279
280 std::sort(begin(solutions), end(solutions));
281 return solutions;
282}
283//------------------------------------------------------------------------------
287template <typename Real>
288auto solve(polynomial<Real, 4> const& f) -> std::vector<Real> {
289 if (f.c(4) == 0) {
290 return solve(polynomial{f.c(0), f.c(1), f.c(2), f.c(3)});
291 }
292
293 std::vector<Real> solutions;
294
295 // normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0
296 auto const A = f.c(3) / f.c(4);
297 auto const B = f.c(2) / f.c(4);
298 auto const C = f.c(1) / f.c(4);
299 auto const D = f.c(0) / f.c(4);
300
301 // substitute x = y - A/4 to eliminate cubic term: x^4 + px^2 + qx + r = 0
302 auto const sq_A = A * A;
303 auto const p = -Real(3) / 8 * sq_A + B;
304 auto const q = Real(1) / 8 * sq_A * A - Real(1) / 2 * A * B + C;
305 auto const r = -Real(3) / 256 * sq_A * sq_A + Real(1) / 16 * sq_A * B -
306 Real(1) / 4 * A * C + D;
307
308 constexpr Real eps = 0;
309 constexpr auto is_zero = [](auto const x) {
310 return ((x) > -eps && (x) < eps);
311 };
312 if (is_zero(r)) {
313 // no absolute term: y(y^3 + py + q) = 0
314 solutions = solve(polynomial{p, q, 0, 1});
315 if (std::find(begin(solutions), end(solutions), 0) == end(solutions)) {
316 solutions.push_back(0);
317 }
318 } else {
319 // solve the resolvent cubic and take the one real solution...
320 auto const z = solve(polynomial{Real(1) / 2 * r * p - Real(1) / 8 * q * q,
321 -r, -Real(1) / 2 * p, 1})
322 .front();
323
324 // ... to build two quadric equations
325 auto u = z * z - r;
326 auto v = 2 * z - p;
327
328 if (is_zero(u)) {
329 u = 0;
330 } else if (u > 0) {
331 u = std::sqrt(u);
332 } else {
333 return {};
334 }
335
336 if (is_zero(v)) {
337 v = 0;
338 } else if (v > 0) {
339 v = std::sqrt(v);
340 } else {
341 return {};
342 }
343
344 solutions = solve(polynomial{z - u, q < 0 ? -v : v, 1});
345
346 auto const other_solutions = solve(polynomial{z + u, q < 0 ? v : -v, 1});
347 std::copy(begin(other_solutions), end(other_solutions),
348 std::back_inserter(solutions));
349 }
350
351 // resubstitute
352 auto const sub = Real(1) / 4 * A;
353
354 for (auto& s : solutions) {
355 s -= sub;
356 }
357
358 std::sort(begin(solutions), end(solutions));
359 solutions.erase(std::unique(begin(solutions), end(solutions)),
360 end(solutions));
361 return solutions;
362}
363//------------------------------------------------------------------------------
364// I/O
365//------------------------------------------------------------------------------
366template <typename Real, std::size_t Degree>
367auto& operator<<(std::ostream& out, polynomial<Real, Degree> const& f) {
368 return f.print(out, "x");
369}
370//------------------------------------------------------------------------------
371// type_traits
372//------------------------------------------------------------------------------
373template <typename T>
374struct is_polynomial_impl : std::false_type {};
375// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
376template <typename Real, std::size_t Degree>
377struct is_polynomial_impl<polynomial<Real, Degree>> : std::true_type {};
378// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
379template <typename T>
381//==============================================================================
382} // namespace tatooine
383//==============================================================================
384#endif
Definition: concepts.h:33
Definition: algorithm.h:6
auto operator<<(std::ostream &out, linspace< Real > const &l) -> auto &
Definition: linspace.h:165
typename common_type_impl< Ts... >::type common_type
Definition: common_type.h:23
auto begin(Range &&range)
Definition: iterator_facade.h:318
auto end(Range &&range)
Definition: iterator_facade.h:322
constexpr auto make_array()
Definition: make_array.h:22
auto solve(polynomial< Real, 1 > const &p) -> std::vector< Real >
solve a + b*x
Definition: polynomial.h:187
static constexpr bool is_polynomial
Definition: polynomial.h:380
constexpr auto diff(polynomial< Real, Degree > const &f)
Definition: polynomial.h:179
Definition: polynomial.h:374
Definition: polynomial.h:14
constexpr auto diff() const
Definition: polynomial.h:133
constexpr auto set_coefficients(std::array< Real, Degree+1 > &&coeffs) -> void
Definition: polynomial.h:109
constexpr polynomial(polynomial< OtherReal, OtherDegree > const &other)
Definition: polynomial.h:51
constexpr polynomial(tensor< OtherReal, Degree+1 > const &coeffs)
Definition: polynomial.h:68
constexpr polynomial(polynomial const &other)=default
auto print(std::ostream &out, std::string const &x) const -> std::ostream &
Definition: polynomial.h:140
Real real_type
Definition: polynomial.h:19
constexpr polynomial(std::array< Real, Degree+1 > &&coeffs)
Definition: polynomial.h:58
constexpr polynomial(arithmetic auto const ... coeffs)
Definition: polynomial.h:62
auto coefficient(std::size_t i) -> auto const &
Definition: polynomial.h:99
constexpr polynomial(polynomial &&other) noexcept=default
auto coefficient(std::size_t i) const -> auto const &
Definition: polynomial.h:98
auto coefficients() -> auto const &
Definition: polynomial.h:102
constexpr polynomial()
Definition: polynomial.h:35
static constexpr std::size_t degree()
Definition: polynomial.h:24
constexpr auto set_coefficients(arithmetic auto const ... coeffs) -> void requires(sizeof...(coeffs)==Degree+1)
Definition: polynomial.h:121
auto c(std::size_t i) const -> auto const &
Definition: polynomial.h:95
constexpr polynomial(std::array< OtherReal, Degree+1 > const &coeffs)
Definition: polynomial.h:73
auto c(std::size_t i) -> auto &
Definition: polynomial.h:96
constexpr auto set_coefficients(std::array< Real, Degree+1 > const &coeffs) -> void
Definition: polynomial.h:104
constexpr polynomial(std::array< Real, Degree+1 > const &coeffs)
Definition: polynomial.h:46
constexpr auto set_coefficients(std::array< OtherReal, Degree+1 > const &coeffs) -> void
Definition: polynomial.h:115
constexpr auto diff(std::index_sequence< Is... >) const
Definition: polynomial.h:128
constexpr auto operator=(polynomial &&other) noexcept -> polynomial &=default
std::array< Real, Degree+1 > m_coefficients
Definition: polynomial.h:29
constexpr auto operator=(polynomial const &other) -> polynomial &=default
constexpr auto operator()(arithmetic auto const x) const
evaluates c0 * x^0 + c1 * x^1 + ... + c{N-1} * x^{N-1}
Definition: polynomial.h:93
auto coefficients() const -> auto const &
Definition: polynomial.h:101
constexpr auto evaluate(arithmetic auto const x) const
evaluates c0 * x^0 + c1 * x^1 + ... + c{N-1} * x^{N-1}
Definition: polynomial.h:82
Definition: tensor.h:17