123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- #ifndef BOOST_MATH_TOOLS_QUARTIC_ROOTS_HPP
- #define BOOST_MATH_TOOLS_QUARTIC_ROOTS_HPP
- #include <array>
- #include <cmath>
- #include <boost/math/tools/cubic_roots.hpp>
- namespace boost::math::tools {
- namespace detail {
- template<typename Real>
- bool comparator(Real r1, Real r2) {
- using std::isnan;
- if (isnan(r1)) { return false; }
- if (isnan(r2)) { return true; }
- return r1 < r2;
- }
- template<typename Real>
- std::array<Real, 4> polish_and_sort(Real a, Real b, Real c, Real d, Real e, std::array<Real, 4>& roots) {
-
- using std::fma;
- using std::abs;
- for (auto &r : roots) {
- Real df = fma(4*a, r, 3*b);
- df = fma(df, r, 2*c);
- df = fma(df, r, d);
- Real d2f = fma(12*a, r, 6*b);
- d2f = fma(d2f, r, 2*c);
- Real f = fma(a, r, b);
- f = fma(f,r,c);
- f = fma(f,r,d);
- f = fma(f,r,e);
- Real denom = 2*df*df - f*d2f;
- if (abs(denom) > (std::numeric_limits<Real>::min)())
- {
- r -= 2*f*df/denom;
- }
- }
- std::sort(roots.begin(), roots.end(), detail::comparator<Real>);
- return roots;
- }
- }
- template<typename Real>
- std::array<Real, 4> quartic_roots(Real a, Real b, Real c, Real d, Real e) {
- using std::abs;
- using std::sqrt;
- auto nan = std::numeric_limits<Real>::quiet_NaN();
- std::array<Real, 4> roots{nan, nan, nan, nan};
- if (abs(a) <= (std::numeric_limits<Real>::min)()) {
- auto cbrts = cubic_roots(b, c, d, e);
- roots[0] = cbrts[0];
- roots[1] = cbrts[1];
- roots[2] = cbrts[2];
- if (b == 0 && c == 0 && d == 0 && e == 0) {
- roots[3] = 0;
- }
- return detail::polish_and_sort(a, b, c, d, e, roots);
- }
- if (abs(e) <= (std::numeric_limits<Real>::min)()) {
- auto v = cubic_roots(a, b, c, d);
- roots[0] = v[0];
- roots[1] = v[1];
- roots[2] = v[2];
- roots[3] = 0;
- return detail::polish_and_sort(a, b, c, d, e, roots);
- }
-
- Real A = b/a;
- Real B = c/a;
- Real C = d/a;
- Real D = e/a;
- Real Asq = A*A;
-
-
-
- Real p = B - 3*Asq/8;
- Real q = C - A*B/2 + Asq*A/8;
- Real r = D - A*C/4 + Asq*B/16 - 3*Asq*Asq/256;
- if (abs(r) <= (std::numeric_limits<Real>::min)()) {
- auto [r1, r2, r3] = cubic_roots(Real(1), Real(0), p, q);
- r1 -= A/4;
- r2 -= A/4;
- r3 -= A/4;
- roots[0] = r1;
- roots[1] = r2;
- roots[2] = r3;
- roots[3] = -A/4;
- return detail::polish_and_sort(a, b, c, d, e, roots);
- }
-
- if (abs(q) <= (std::numeric_limits<Real>::min)()) {
- auto [r1, r2] = quadratic_roots(Real(1), p, r);
- if (r1 >= 0) {
- Real rtr = sqrt(r1);
- roots[0] = rtr - A/4;
- roots[1] = -rtr - A/4;
- }
- if (r2 >= 0) {
- Real rtr = sqrt(r2);
- roots[2] = rtr - A/4;
- roots[3] = -rtr - A/4;
- }
- return detail::polish_and_sort(a, b, c, d, e, roots);
- }
-
-
-
-
-
-
-
- auto z_roots = cubic_roots(Real(1), 2*p, p*p - 4*r, -q*q);
-
-
- Real largest_root = std::numeric_limits<Real>::lowest();
- for (auto z : z_roots) {
- if (z > largest_root) {
- largest_root = z;
- }
- }
-
- if (largest_root <= 0) {
- return roots;
- }
- Real s = sqrt(largest_root);
-
- Real v = (p + s*s + q/s)/2;
- Real u = v - q/s;
-
- auto [root0, root1] = quadratic_roots(Real(1), s, u);
-
- auto [root2, root3] = quadratic_roots(Real(1), -s, v);
- roots[0] = root0;
- roots[1] = root1;
- roots[2] = root2;
- roots[3] = root3;
- for (auto& r : roots) {
- r -= A/4;
- }
- return detail::polish_and_sort(a, b, c, d, e, roots);
- }
- }
- #endif
|