blob: e0ec2b0f29f9dad9a515cb6c4bcc8e14b7f20b12 [file] [log] [blame]
caryclark@google.com235f56a2012-09-14 14:19:30 +00001// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
2/*
3 * Roots3And4.c
4 *
5 * Utility functions to find cubic and quartic roots,
6 * coefficients are passed like this:
7 *
8 * c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
9 *
10 * The functions return the number of non-complex roots and
11 * put the values into the s array.
12 *
13 * Author: Jochen Schwarze (schwarze@isa.de)
14 *
15 * Jan 26, 1990 Version for Graphics Gems
16 * Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000017 * (reported by Mark Podlipec),
18 * Old-style function definitions,
19 * IsZero() as a macro
caryclark@google.com235f56a2012-09-14 14:19:30 +000020 * Nov 23, 1990 Some systems do not declare acos() and cbrt() in
21 * <math.h>, though the functions exist in the library.
22 * If large coefficients are used, EQN_EPS should be
23 * reduced considerably (e.g. to 1E-30), results will be
24 * correct but multiple roots might be reported more
25 * than once.
26 */
27
28#include <math.h>
29#include "CubicUtilities.h"
30#include "QuarticRoot.h"
31
32const double PI = 4 * atan(1);
33
34// unlike quadraticRoots in QuadraticUtilities.cpp, this does not discard
35// real roots <= 0 or >= 1
36static int quadraticRootsX(const double A, const double B, const double C,
37 double s[2]) {
38 if (approximately_zero(A)) {
39 if (approximately_zero(B)) {
40 s[0] = 0;
41 return C == 0;
42 }
43 s[0] = -C / B;
44 return 1;
45 }
46 /* normal form: x^2 + px + q = 0 */
47 const double p = B / (2 * A);
48 const double q = C / A;
49 const double D = p * p - q;
50 if (approximately_zero(D)) {
51 s[0] = -p;
52 return 1;
53 } else if (D < 0) {
54 return 0;
55 } else {
56 assert(D > 0);
57 double sqrt_D = sqrt(D);
58 s[0] = sqrt_D - p;
59 s[1] = -sqrt_D - p;
60 return 2;
61 }
62}
63
64// unlike cubicRoots in CubicUtilities.cpp, this does not discard
65// real roots <= 0 or >= 1
66static int cubicRootsX(const double A, const double B, const double C,
67 const double D, double s[3]) {
68 int num;
69 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
70 const double invA = 1 / A;
71 const double a = B * invA;
72 const double b = C * invA;
73 const double c = D * invA;
74 /* substitute x = y - a/3 to eliminate quadric term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000075 x^3 +px + q = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +000076 const double a2 = a * a;
77 const double Q = (-a2 + b * 3) / 9;
78 const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
79 /* use Cardano's formula */
80 const double Q3 = Q * Q * Q;
81 const double R2plusQ3 = R * R + Q3;
82 if (approximately_zero(R2plusQ3)) {
83 if (approximately_zero(R)) {/* one triple solution */
84 s[0] = 0;
85 num = 1;
86 } else { /* one single and one double solution */
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000087
caryclark@google.com235f56a2012-09-14 14:19:30 +000088 double u = cube_root(-R);
89 s[0] = 2 * u;
90 s[1] = -u;
91 num = 2;
92 }
93 }
94 else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
95 const double theta = 1.0/3 * acos(-R / sqrt(-Q3));
96 const double _2RootQ = 2 * sqrt(-Q);
97 s[0] = _2RootQ * cos(theta);
98 s[1] = -_2RootQ * cos(theta + PI / 3);
99 s[2] = -_2RootQ * cos(theta - PI / 3);
100 num = 3;
101 } else { /* one real solution */
102 const double sqrt_D = sqrt(R2plusQ3);
103 const double u = cube_root(sqrt_D - R);
104 const double v = -cube_root(sqrt_D + R);
105 s[0] = u + v;
106 num = 1;
107 }
108 /* resubstitute */
109 const double sub = 1.0/3 * a;
110 for (int i = 0; i < num; ++i) {
111 s[i] -= sub;
112 }
113 return num;
114}
115
116int quarticRoots(const double A, const double B, const double C, const double D,
117 const double E, double s[4]) {
118 if (approximately_zero(A)) {
119 if (approximately_zero(B)) {
120 return quadraticRootsX(C, D, E, s);
121 }
122 return cubicRootsX(B, C, D, E, s);
123 }
124 double u, v;
125 int num;
126 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
127 const double invA = 1 / A;
128 const double a = B * invA;
129 const double b = C * invA;
130 const double c = D * invA;
131 const double d = E * invA;
132 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000133 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000134 const double a2 = a * a;
135 const double p = -3 * a2 / 8 + b;
136 const double q = a2 * a / 8 - a * b / 2 + c;
137 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
138 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000139 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000140 num = cubicRootsX(1, 0, p, q, s);
141 s[num++] = 0;
142 } else {
143 /* solve the resolvent cubic ... */
144 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
145 /* ... and take the one real solution ... */
146 const double z = s[0];
147 /* ... to build two quadric equations */
148 u = z * z - r;
149 v = 2 * z - p;
150 if (approximately_zero(u)) {
151 u = 0;
152 } else if (u > 0) {
153 u = sqrt(u);
154 } else {
155 return 0;
156 }
157 if (approximately_zero(v)) {
158 v = 0;
159 } else if (v > 0) {
160 v = sqrt(v);
161 } else {
162 return 0;
163 }
164 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
165 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
166 }
167 // eliminate duplicates
168 int i;
169 for (i = 0; i < num - 1; ++i) {
170 for (int j = i + 1; j < num; ) {
171 if (approximately_equal(s[i], s[j])) {
172 if (j < --num) {
173 s[j] = s[num];
174 }
175 } else {
176 ++j;
177 }
178 }
179 }
180 /* resubstitute */
181 const double sub = a / 4;
182 for (i = 0; i < num; ++i) {
183 s[i] -= sub;
184 }
185 return num;
186}
187
188