File indexing completed on 2025-01-18 09:40:40
0001
0002
0003
0004
0005 #ifndef BOOST_MATH_TOOLS_CUBIC_ROOTS_HPP
0006 #define BOOST_MATH_TOOLS_CUBIC_ROOTS_HPP
0007 #include <algorithm>
0008 #include <array>
0009 #include <boost/math/special_functions/sign.hpp>
0010 #include <boost/math/tools/roots.hpp>
0011
0012 namespace boost::math::tools {
0013
0014
0015
0016
0017
0018
0019
0020 template <typename Real>
0021 std::array<Real, 3> cubic_roots(Real a, Real b, Real c, Real d) {
0022 using std::abs;
0023 using std::acos;
0024 using std::cbrt;
0025 using std::cos;
0026 using std::fma;
0027 using std::sqrt;
0028 std::array<Real, 3> roots = {std::numeric_limits<Real>::quiet_NaN(),
0029 std::numeric_limits<Real>::quiet_NaN(),
0030 std::numeric_limits<Real>::quiet_NaN()};
0031 if (a == 0) {
0032
0033 if (b == 0) {
0034
0035 if (c == 0) {
0036 if (d != 0) {
0037
0038 return roots;
0039 }
0040 roots[0] = 0;
0041 roots[1] = 0;
0042 roots[2] = 0;
0043 return roots;
0044 }
0045 roots[0] = -d / c;
0046 return roots;
0047 }
0048 auto [x0, x1] = quadratic_roots(b, c, d);
0049 roots[0] = x0;
0050 roots[1] = x1;
0051 return roots;
0052 }
0053 if (d == 0) {
0054 auto [x0, x1] = quadratic_roots(a, b, c);
0055 roots[0] = x0;
0056 roots[1] = x1;
0057 roots[2] = 0;
0058 std::sort(roots.begin(), roots.end());
0059 return roots;
0060 }
0061 Real p = b / a;
0062 Real q = c / a;
0063 Real r = d / a;
0064 Real Q = (p * p - 3 * q) / 9;
0065 Real R = (2 * p * p * p - 9 * p * q + 27 * r) / 54;
0066 if (R * R < Q * Q * Q) {
0067 Real rtQ = sqrt(Q);
0068 Real theta = acos(R / (Q * rtQ)) / 3;
0069 Real st = sin(theta);
0070 Real ct = cos(theta);
0071 roots[0] = -2 * rtQ * ct - p / 3;
0072 roots[1] = -rtQ * (-ct + sqrt(Real(3)) * st) - p / 3;
0073 roots[2] = rtQ * (ct + sqrt(Real(3)) * st) - p / 3;
0074 } else {
0075
0076
0077
0078
0079
0080
0081
0082 Real arg = R * R - Q * Q * Q;
0083 Real A = (R >= 0 ? -1 : 1) * cbrt(abs(R) + sqrt(arg));
0084 Real B = 0;
0085 if (A != 0) {
0086 B = Q / A;
0087 }
0088 roots[0] = A + B - p / 3;
0089
0090
0091
0092 if (A == B || arg == 0) {
0093 roots[1] = -A - p / 3;
0094 roots[2] = -A - p / 3;
0095 }
0096 }
0097
0098 for (auto &r : roots) {
0099
0100
0101
0102
0103 Real f = fma(a, r, b);
0104 f = fma(f, r, c);
0105 f = fma(f, r, d);
0106 Real df = fma(3 * a, r, 2 * b);
0107 df = fma(df, r, c);
0108 if (df != 0) {
0109 Real d2f = fma(6 * a, r, 2 * b);
0110 Real denom = 2 * df * df - f * d2f;
0111 if (denom != 0) {
0112 r -= 2 * f * df / denom;
0113 } else {
0114 r -= f / df;
0115 }
0116 }
0117 }
0118 std::sort(roots.begin(), roots.end());
0119 return roots;
0120 }
0121
0122
0123
0124
0125
0126 template <typename Real>
0127 std::array<Real, 2> cubic_root_residual(Real a, Real b, Real c, Real d,
0128 Real root) {
0129 using std::abs;
0130 using std::fma;
0131 std::array<Real, 2> out;
0132 Real residual = fma(a, root, b);
0133 residual = fma(residual, root, c);
0134 residual = fma(residual, root, d);
0135
0136 out[0] = residual;
0137
0138
0139
0140
0141
0142
0143 root = abs(root);
0144 Real expected_residual = fma(4 * abs(a), root, 3 * abs(b));
0145 expected_residual = fma(expected_residual, root, 2 * abs(c));
0146 expected_residual = fma(expected_residual, root, abs(d));
0147 out[1] = expected_residual * std::numeric_limits<Real>::epsilon();
0148 return out;
0149 }
0150
0151
0152
0153 template <typename Real>
0154 Real cubic_root_condition_number(Real a, Real b, Real c, Real d, Real root) {
0155 using std::abs;
0156 using std::fma;
0157
0158
0159 if (root == static_cast<Real>(0)) {
0160 return std::numeric_limits<Real>::infinity();
0161 }
0162
0163 Real numerator = fma(abs(a), abs(root), abs(b));
0164 numerator = fma(numerator, abs(root), abs(c));
0165 numerator = fma(numerator, abs(root), abs(d));
0166 Real denominator = fma(3 * a, root, 2 * b);
0167 denominator = fma(denominator, root, c);
0168 if (denominator == static_cast<Real>(0)) {
0169 return std::numeric_limits<Real>::infinity();
0170 }
0171 denominator *= root;
0172 return numerator / abs(denominator);
0173 }
0174
0175 }
0176 #endif