File indexing completed on 2024-12-15 04:01:13

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "polynomial.hpp"
0008 #include "math.hpp"
0009 
0010 
0011 namespace math = glaxnimate::math;
0012 
0013 
0014 // Returns the real cube root of a value
0015 static double cuberoot(double v)
0016 {
0017     if ( v < 0 )
0018         return -math::pow(-v, 1./3);
0019     return math::pow(v, 1./3);
0020 }
0021 
0022 std::vector<double> glaxnimate::math::cubic_roots(double a, double b, double c, double d)
0023 {
0024 
0025     // If a is 0, it's a quadratic
0026     if ( qFuzzyIsNull(a) )
0027         return quadratic_roots(b, c, d);
0028 
0029     // Cardano's algorithm.
0030     b /= a;
0031     c /= a;
0032     d /= a;
0033 
0034     double p = (3*c - b * b) / 3;
0035     double p3 = p / 3;
0036     double q = (2 * b*b*b - 9 * b * c + 27 * d) / 27;
0037     double q2 = q / 2;
0038     double discriminant = q2 * q2 + p3 * p3 * p3;
0039 
0040     // and some variables we're going to use later on:
0041 
0042     // 3 real roots:
0043     if ( discriminant < 0)
0044     {
0045         double mp3  = -p / 3;
0046         double r = math::sqrt(mp3*mp3*mp3);
0047         double t = -q / (2*r);
0048         double cosphi = t < -1 ? -1 : t > 1 ? 1 : t;
0049         double phi  = math::acos(cosphi);
0050         double crtr = cuberoot(r);
0051         double t1   = 2 * crtr;
0052         double root1 = t1 * math::cos(phi / 3) - b / 3;
0053         double root2 = t1 * math::cos((phi + 2 * math::pi) / 3) - b / 3;
0054         double root3 = t1 * math::cos((phi + 4 * math::pi) / 3) - b / 3;
0055         return {root1, root2, root3};
0056     }
0057 
0058     // 2 real roots
0059     if ( qFuzzyIsNull(discriminant) )
0060     {
0061         double u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2);
0062         double root1 = 2*u1 - b / 3;
0063         double root2 = -u1 - b / 3;
0064         return {root1, root2};
0065     }
0066 
0067     // 1 real root, 2 complex roots
0068     double sd = math::sqrt(discriminant);
0069     double u1 = cuberoot(sd - q2);
0070     double v1 = cuberoot(sd + q2);
0071     return {u1 - v1 - b / 3};
0072 }
0073 
0074 std::vector<double> glaxnimate::math::quadratic_roots(double a, double b, double c)
0075 {
0076     // linear
0077     if ( qFuzzyIsNull(a) )
0078     {
0079         if ( qFuzzyIsNull(b) )
0080             return {};
0081 
0082         return {-c / b};
0083     }
0084 
0085     double s = b * b - 4 * a * c;
0086 
0087     // Complex roots
0088     if ( s < 0 )
0089         return {};
0090 
0091     double single_root = -b / (2 * a);
0092 
0093     // 1 root
0094     if ( qFuzzyIsNull(s) )
0095         return {single_root};
0096 
0097     double delta = math::sqrt(s) / (2 * a);
0098 
0099     // 2 roots
0100     return {single_root - delta, single_root + delta};
0101 }