blob: b2e73cba50d6b6d4ebd45765bdf14a45368172db [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
caryclark@google.comd1688742012-09-18 20:08:37 +000064#define USE_GEMS 0
65#if USE_GEMS
caryclark@google.com235f56a2012-09-14 14:19:30 +000066// unlike cubicRoots in CubicUtilities.cpp, this does not discard
67// real roots <= 0 or >= 1
68static int cubicRootsX(const double A, const double B, const double C,
69 const double D, double s[3]) {
70 int num;
71 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
72 const double invA = 1 / A;
73 const double a = B * invA;
74 const double b = C * invA;
75 const double c = D * invA;
76 /* substitute x = y - a/3 to eliminate quadric term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000077 x^3 +px + q = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +000078 const double a2 = a * a;
79 const double Q = (-a2 + b * 3) / 9;
80 const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
81 /* use Cardano's formula */
82 const double Q3 = Q * Q * Q;
83 const double R2plusQ3 = R * R + Q3;
84 if (approximately_zero(R2plusQ3)) {
85 if (approximately_zero(R)) {/* one triple solution */
86 s[0] = 0;
87 num = 1;
88 } else { /* one single and one double solution */
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000089
caryclark@google.com235f56a2012-09-14 14:19:30 +000090 double u = cube_root(-R);
91 s[0] = 2 * u;
92 s[1] = -u;
93 num = 2;
94 }
95 }
96 else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
caryclark@google.comd1688742012-09-18 20:08:37 +000097 const double theta = acos(-R / sqrt(-Q3)) / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +000098 const double _2RootQ = 2 * sqrt(-Q);
99 s[0] = _2RootQ * cos(theta);
100 s[1] = -_2RootQ * cos(theta + PI / 3);
101 s[2] = -_2RootQ * cos(theta - PI / 3);
102 num = 3;
103 } else { /* one real solution */
104 const double sqrt_D = sqrt(R2plusQ3);
105 const double u = cube_root(sqrt_D - R);
106 const double v = -cube_root(sqrt_D + R);
107 s[0] = u + v;
108 num = 1;
109 }
110 /* resubstitute */
caryclark@google.comd1688742012-09-18 20:08:37 +0000111 const double sub = a / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000112 for (int i = 0; i < num; ++i) {
113 s[i] -= sub;
114 }
115 return num;
116}
caryclark@google.comd1688742012-09-18 20:08:37 +0000117#else
118
119static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
120 if (approximately_zero(A)) { // we're just a quadratic
121 return quadraticRootsX(B, C, D, s);
122 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000123 if (approximately_zero(D)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000124 int num = quadraticRootsX(A, B, C, s);
125 for (int i = 0; i < num; ++i) {
126 if (approximately_zero(s[i])) {
127 return num;
128 }
129 }
130 s[num++] = 0;
131 return num;
132 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000133 if (approximately_zero(A + B + C + D)) { // 1 is one root
134 int num = quadraticRootsX(A, A + B, -D, s);
135 for (int i = 0; i < num; ++i) {
136 if (approximately_equal(s[i], 1)) {
137 return num;
138 }
139 }
140 s[num++] = 1;
141 return num;
142 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000143 double a, b, c;
144 {
145 double invA = 1 / A;
146 a = B * invA;
147 b = C * invA;
148 c = D * invA;
149 }
150 double a2 = a * a;
151 double Q = (a2 - b * 3) / 9;
152 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
153 double Q3 = Q * Q * Q;
154 double R2MinusQ3 = R * R - Q3;
155 double adiv3 = a / 3;
156 double r;
157 double* roots = s;
158
159 if (R2MinusQ3 > -FLT_EPSILON / 10 && R2MinusQ3 < FLT_EPSILON / 10 ) {
160 if (approximately_zero(R)) {/* one triple solution */
161 *roots++ = -adiv3;
162 } else { /* one single and one double solution */
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000163
caryclark@google.comd1688742012-09-18 20:08:37 +0000164 double u = cube_root(-R);
165 *roots++ = 2 * u - adiv3;
166 *roots++ = -u - adiv3;
167 }
168 }
169 else if (R2MinusQ3 < 0) // we have 3 real roots
170 {
171 double theta = acos(R / sqrt(Q3));
172 double neg2RootQ = -2 * sqrt(Q);
173
174 r = neg2RootQ * cos(theta / 3) - adiv3;
175 *roots++ = r;
176
177 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
178 *roots++ = r;
179
180 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
181 *roots++ = r;
182 }
183 else // we have 1 real root
184 {
185 double A = fabs(R) + sqrt(R2MinusQ3);
186 A = cube_root(A);
187 if (R > 0) {
188 A = -A;
189 }
190 if (A != 0) {
191 A += Q / A;
192 }
193 r = A - adiv3;
194 *roots++ = r;
195 }
196 return (int)(roots - s);
197}
198#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000199
200int quarticRoots(const double A, const double B, const double C, const double D,
201 const double E, double s[4]) {
202 if (approximately_zero(A)) {
203 if (approximately_zero(B)) {
204 return quadraticRootsX(C, D, E, s);
205 }
206 return cubicRootsX(B, C, D, E, s);
207 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000208 int num;
caryclark@google.comd1688742012-09-18 20:08:37 +0000209 int i;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000210 if (approximately_zero(E)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000211 num = cubicRootsX(A, B, C, D, s);
212 for (i = 0; i < num; ++i) {
213 if (approximately_zero(s[i])) {
214 return num;
215 }
216 }
217 s[num++] = 0;
218 return num;
219 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000220 if (approximately_zero(A + B + C + D + E)) { // 1 is one root
221 num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
222 for (i = 0; i < num; ++i) {
223 if (approximately_equal(s[i], 1)) {
224 return num;
225 }
226 }
227 s[num++] = 1;
228 return num;
229 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000230 double u, v;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000231 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
232 const double invA = 1 / A;
233 const double a = B * invA;
234 const double b = C * invA;
235 const double c = D * invA;
236 const double d = E * invA;
237 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000238 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000239 const double a2 = a * a;
240 const double p = -3 * a2 / 8 + b;
241 const double q = a2 * a / 8 - a * b / 2 + c;
242 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
243 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000244 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000245 num = cubicRootsX(1, 0, p, q, s);
246 s[num++] = 0;
247 } else {
248 /* solve the resolvent cubic ... */
249 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
250 /* ... and take the one real solution ... */
251 const double z = s[0];
252 /* ... to build two quadric equations */
253 u = z * z - r;
254 v = 2 * z - p;
255 if (approximately_zero(u)) {
256 u = 0;
257 } else if (u > 0) {
258 u = sqrt(u);
259 } else {
260 return 0;
261 }
262 if (approximately_zero(v)) {
263 v = 0;
264 } else if (v > 0) {
265 v = sqrt(v);
266 } else {
267 return 0;
268 }
269 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
270 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
271 }
272 // eliminate duplicates
caryclark@google.com235f56a2012-09-14 14:19:30 +0000273 for (i = 0; i < num - 1; ++i) {
274 for (int j = i + 1; j < num; ) {
275 if (approximately_equal(s[i], s[j])) {
276 if (j < --num) {
277 s[j] = s[num];
278 }
279 } else {
280 ++j;
281 }
282 }
283 }
284 /* resubstitute */
285 const double sub = a / 4;
286 for (i = 0; i < num; ++i) {
287 s[i] -= sub;
288 }
289 return num;
290}
291
292