blob: 66ce3bf415577118f949aca7b3d4eaa9e43b6b00 [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;
caryclark@google.come7bd5f42012-12-13 19:47:53 +000049 double D = p * p - q;
caryclark@google.coma461ff02012-10-11 12:54:23 +000050 if (D < 0) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +000051 if (approximately_positive_squared(D)) {
52 D = 0;
53 } else {
54 return 0;
55 }
caryclark@google.coma461ff02012-10-11 12:54:23 +000056 }
57 double sqrt_D = sqrt(D);
58 if (approximately_less_than_zero(sqrt_D)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +000059 s[0] = -p;
60 return 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +000061 }
caryclark@google.coma461ff02012-10-11 12:54:23 +000062 s[0] = sqrt_D - p;
63 s[1] = -sqrt_D - p;
64 return 2;
caryclark@google.com235f56a2012-09-14 14:19:30 +000065}
66
caryclark@google.comd1688742012-09-18 20:08:37 +000067#define USE_GEMS 0
68#if USE_GEMS
caryclark@google.com235f56a2012-09-14 14:19:30 +000069// unlike cubicRoots in CubicUtilities.cpp, this does not discard
70// real roots <= 0 or >= 1
71static int cubicRootsX(const double A, const double B, const double C,
72 const double D, double s[3]) {
73 int num;
74 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
75 const double invA = 1 / A;
76 const double a = B * invA;
77 const double b = C * invA;
78 const double c = D * invA;
79 /* substitute x = y - a/3 to eliminate quadric term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000080 x^3 +px + q = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +000081 const double a2 = a * a;
82 const double Q = (-a2 + b * 3) / 9;
83 const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
84 /* use Cardano's formula */
85 const double Q3 = Q * Q * Q;
86 const double R2plusQ3 = R * R + Q3;
87 if (approximately_zero(R2plusQ3)) {
88 if (approximately_zero(R)) {/* one triple solution */
89 s[0] = 0;
90 num = 1;
91 } else { /* one single and one double solution */
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000092
caryclark@google.com235f56a2012-09-14 14:19:30 +000093 double u = cube_root(-R);
94 s[0] = 2 * u;
95 s[1] = -u;
96 num = 2;
97 }
98 }
99 else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
caryclark@google.comd1688742012-09-18 20:08:37 +0000100 const double theta = acos(-R / sqrt(-Q3)) / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000101 const double _2RootQ = 2 * sqrt(-Q);
102 s[0] = _2RootQ * cos(theta);
103 s[1] = -_2RootQ * cos(theta + PI / 3);
104 s[2] = -_2RootQ * cos(theta - PI / 3);
105 num = 3;
106 } else { /* one real solution */
107 const double sqrt_D = sqrt(R2plusQ3);
108 const double u = cube_root(sqrt_D - R);
109 const double v = -cube_root(sqrt_D + R);
110 s[0] = u + v;
111 num = 1;
112 }
113 /* resubstitute */
caryclark@google.comd1688742012-09-18 20:08:37 +0000114 const double sub = a / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000115 for (int i = 0; i < num; ++i) {
116 s[i] -= sub;
117 }
118 return num;
119}
caryclark@google.comd1688742012-09-18 20:08:37 +0000120#else
121
122static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
123 if (approximately_zero(A)) { // we're just a quadratic
124 return quadraticRootsX(B, C, D, s);
125 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000126 if (approximately_zero(D)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000127 int num = quadraticRootsX(A, B, C, s);
128 for (int i = 0; i < num; ++i) {
129 if (approximately_zero(s[i])) {
130 return num;
131 }
132 }
133 s[num++] = 0;
134 return num;
135 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000136 if (approximately_zero(A + B + C + D)) { // 1 is one root
137 int num = quadraticRootsX(A, A + B, -D, s);
138 for (int i = 0; i < num; ++i) {
139 if (approximately_equal(s[i], 1)) {
140 return num;
141 }
142 }
143 s[num++] = 1;
144 return num;
145 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000146 double a, b, c;
147 {
148 double invA = 1 / A;
149 a = B * invA;
150 b = C * invA;
151 c = D * invA;
152 }
153 double a2 = a * a;
154 double Q = (a2 - b * 3) / 9;
155 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
156 double Q3 = Q * Q * Q;
157 double R2MinusQ3 = R * R - Q3;
158 double adiv3 = a / 3;
159 double r;
160 double* roots = s;
161
caryclark@google.com0b7da432012-10-31 19:00:20 +0000162 if (approximately_zero_squared(R2MinusQ3)) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000163 if (approximately_zero(R)) {/* one triple solution */
164 *roots++ = -adiv3;
165 } else { /* one single and one double solution */
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000166
caryclark@google.comd1688742012-09-18 20:08:37 +0000167 double u = cube_root(-R);
168 *roots++ = 2 * u - adiv3;
169 *roots++ = -u - adiv3;
170 }
171 }
172 else if (R2MinusQ3 < 0) // we have 3 real roots
173 {
174 double theta = acos(R / sqrt(Q3));
175 double neg2RootQ = -2 * sqrt(Q);
176
177 r = neg2RootQ * cos(theta / 3) - adiv3;
178 *roots++ = r;
179
180 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
181 *roots++ = r;
182
183 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
184 *roots++ = r;
185 }
186 else // we have 1 real root
187 {
188 double A = fabs(R) + sqrt(R2MinusQ3);
189 A = cube_root(A);
190 if (R > 0) {
191 A = -A;
192 }
193 if (A != 0) {
194 A += Q / A;
195 }
196 r = A - adiv3;
197 *roots++ = r;
198 }
199 return (int)(roots - s);
200}
201#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000202
203int quarticRoots(const double A, const double B, const double C, const double D,
204 const double E, double s[4]) {
205 if (approximately_zero(A)) {
206 if (approximately_zero(B)) {
207 return quadraticRootsX(C, D, E, s);
208 }
209 return cubicRootsX(B, C, D, E, s);
210 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000211 int num;
caryclark@google.comd1688742012-09-18 20:08:37 +0000212 int i;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000213 if (approximately_zero(E)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000214 num = cubicRootsX(A, B, C, D, s);
215 for (i = 0; i < num; ++i) {
216 if (approximately_zero(s[i])) {
217 return num;
218 }
219 }
220 s[num++] = 0;
221 return num;
222 }
caryclark@google.com0b7da432012-10-31 19:00:20 +0000223 if (approximately_zero_squared(A + B + C + D + E)) { // 1 is one root
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000224 num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
225 for (i = 0; i < num; ++i) {
226 if (approximately_equal(s[i], 1)) {
227 return num;
228 }
229 }
230 s[num++] = 1;
231 return num;
232 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000233 double u, v;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000234 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
235 const double invA = 1 / A;
236 const double a = B * invA;
237 const double b = C * invA;
238 const double c = D * invA;
239 const double d = E * invA;
240 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000241 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000242 const double a2 = a * a;
243 const double p = -3 * a2 / 8 + b;
244 const double q = a2 * a / 8 - a * b / 2 + c;
245 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
246 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000247 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000248 num = cubicRootsX(1, 0, p, q, s);
249 s[num++] = 0;
250 } else {
251 /* solve the resolvent cubic ... */
252 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
253 /* ... and take the one real solution ... */
254 const double z = s[0];
255 /* ... to build two quadric equations */
256 u = z * z - r;
257 v = 2 * z - p;
258 if (approximately_zero(u)) {
259 u = 0;
260 } else if (u > 0) {
261 u = sqrt(u);
262 } else {
263 return 0;
264 }
265 if (approximately_zero(v)) {
266 v = 0;
267 } else if (v > 0) {
268 v = sqrt(v);
269 } else {
270 return 0;
271 }
272 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
273 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
274 }
275 // eliminate duplicates
caryclark@google.com235f56a2012-09-14 14:19:30 +0000276 for (i = 0; i < num - 1; ++i) {
277 for (int j = i + 1; j < num; ) {
278 if (approximately_equal(s[i], s[j])) {
279 if (j < --num) {
280 s[j] = s[num];
281 }
282 } else {
283 ++j;
284 }
285 }
286 }
287 /* resubstitute */
288 const double sub = a / 4;
289 for (i = 0; i < num; ++i) {
290 s[i] -= sub;
291 }
292 return num;
293}
294
295