blob: 7a95b2468c337ec16e0f99eacc5adea8cde52a95 [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;
caryclark@google.coma461ff02012-10-11 12:54:23 +000050 if (D < 0) {
51 return 0;
52 }
53 double sqrt_D = sqrt(D);
54 if (approximately_less_than_zero(sqrt_D)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +000055 s[0] = -p;
56 return 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +000057 }
caryclark@google.coma461ff02012-10-11 12:54:23 +000058 s[0] = sqrt_D - p;
59 s[1] = -sqrt_D - p;
60 return 2;
caryclark@google.com235f56a2012-09-14 14:19:30 +000061}
62
caryclark@google.comd1688742012-09-18 20:08:37 +000063#define USE_GEMS 0
64#if USE_GEMS
caryclark@google.com235f56a2012-09-14 14:19:30 +000065// unlike cubicRoots in CubicUtilities.cpp, this does not discard
66// real roots <= 0 or >= 1
67static int cubicRootsX(const double A, const double B, const double C,
68 const double D, double s[3]) {
69 int num;
70 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
71 const double invA = 1 / A;
72 const double a = B * invA;
73 const double b = C * invA;
74 const double c = D * invA;
75 /* substitute x = y - a/3 to eliminate quadric term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000076 x^3 +px + q = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +000077 const double a2 = a * a;
78 const double Q = (-a2 + b * 3) / 9;
79 const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
80 /* use Cardano's formula */
81 const double Q3 = Q * Q * Q;
82 const double R2plusQ3 = R * R + Q3;
83 if (approximately_zero(R2plusQ3)) {
84 if (approximately_zero(R)) {/* one triple solution */
85 s[0] = 0;
86 num = 1;
87 } else { /* one single and one double solution */
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000088
caryclark@google.com235f56a2012-09-14 14:19:30 +000089 double u = cube_root(-R);
90 s[0] = 2 * u;
91 s[1] = -u;
92 num = 2;
93 }
94 }
95 else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
caryclark@google.comd1688742012-09-18 20:08:37 +000096 const double theta = acos(-R / sqrt(-Q3)) / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +000097 const double _2RootQ = 2 * sqrt(-Q);
98 s[0] = _2RootQ * cos(theta);
99 s[1] = -_2RootQ * cos(theta + PI / 3);
100 s[2] = -_2RootQ * cos(theta - PI / 3);
101 num = 3;
102 } else { /* one real solution */
103 const double sqrt_D = sqrt(R2plusQ3);
104 const double u = cube_root(sqrt_D - R);
105 const double v = -cube_root(sqrt_D + R);
106 s[0] = u + v;
107 num = 1;
108 }
109 /* resubstitute */
caryclark@google.comd1688742012-09-18 20:08:37 +0000110 const double sub = a / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000111 for (int i = 0; i < num; ++i) {
112 s[i] -= sub;
113 }
114 return num;
115}
caryclark@google.comd1688742012-09-18 20:08:37 +0000116#else
117
118static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
119 if (approximately_zero(A)) { // we're just a quadratic
120 return quadraticRootsX(B, C, D, s);
121 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000122 if (approximately_zero(D)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000123 int num = quadraticRootsX(A, B, C, s);
124 for (int i = 0; i < num; ++i) {
125 if (approximately_zero(s[i])) {
126 return num;
127 }
128 }
129 s[num++] = 0;
130 return num;
131 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000132 if (approximately_zero(A + B + C + D)) { // 1 is one root
133 int num = quadraticRootsX(A, A + B, -D, s);
134 for (int i = 0; i < num; ++i) {
135 if (approximately_equal(s[i], 1)) {
136 return num;
137 }
138 }
139 s[num++] = 1;
140 return num;
141 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000142 double a, b, c;
143 {
144 double invA = 1 / A;
145 a = B * invA;
146 b = C * invA;
147 c = D * invA;
148 }
149 double a2 = a * a;
150 double Q = (a2 - b * 3) / 9;
151 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
152 double Q3 = Q * Q * Q;
153 double R2MinusQ3 = R * R - Q3;
154 double adiv3 = a / 3;
155 double r;
156 double* roots = s;
157
caryclark@google.com0b7da432012-10-31 19:00:20 +0000158 if (approximately_zero_squared(R2MinusQ3)) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000159 if (approximately_zero(R)) {/* one triple solution */
160 *roots++ = -adiv3;
161 } else { /* one single and one double solution */
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000162
caryclark@google.comd1688742012-09-18 20:08:37 +0000163 double u = cube_root(-R);
164 *roots++ = 2 * u - adiv3;
165 *roots++ = -u - adiv3;
166 }
167 }
168 else if (R2MinusQ3 < 0) // we have 3 real roots
169 {
170 double theta = acos(R / sqrt(Q3));
171 double neg2RootQ = -2 * sqrt(Q);
172
173 r = neg2RootQ * cos(theta / 3) - adiv3;
174 *roots++ = r;
175
176 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
177 *roots++ = r;
178
179 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
180 *roots++ = r;
181 }
182 else // we have 1 real root
183 {
184 double A = fabs(R) + sqrt(R2MinusQ3);
185 A = cube_root(A);
186 if (R > 0) {
187 A = -A;
188 }
189 if (A != 0) {
190 A += Q / A;
191 }
192 r = A - adiv3;
193 *roots++ = r;
194 }
195 return (int)(roots - s);
196}
197#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000198
199int quarticRoots(const double A, const double B, const double C, const double D,
200 const double E, double s[4]) {
201 if (approximately_zero(A)) {
202 if (approximately_zero(B)) {
203 return quadraticRootsX(C, D, E, s);
204 }
205 return cubicRootsX(B, C, D, E, s);
206 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000207 int num;
caryclark@google.comd1688742012-09-18 20:08:37 +0000208 int i;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000209 if (approximately_zero(E)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000210 num = cubicRootsX(A, B, C, D, s);
211 for (i = 0; i < num; ++i) {
212 if (approximately_zero(s[i])) {
213 return num;
214 }
215 }
216 s[num++] = 0;
217 return num;
218 }
caryclark@google.com0b7da432012-10-31 19:00:20 +0000219 if (approximately_zero_squared(A + B + C + D + E)) { // 1 is one root
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000220 num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
221 for (i = 0; i < num; ++i) {
222 if (approximately_equal(s[i], 1)) {
223 return num;
224 }
225 }
226 s[num++] = 1;
227 return num;
228 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000229 double u, v;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000230 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
231 const double invA = 1 / A;
232 const double a = B * invA;
233 const double b = C * invA;
234 const double c = D * invA;
235 const double d = E * invA;
236 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000237 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000238 const double a2 = a * a;
239 const double p = -3 * a2 / 8 + b;
240 const double q = a2 * a / 8 - a * b / 2 + c;
241 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
242 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000243 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000244 num = cubicRootsX(1, 0, p, q, s);
245 s[num++] = 0;
246 } else {
247 /* solve the resolvent cubic ... */
248 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
249 /* ... and take the one real solution ... */
250 const double z = s[0];
251 /* ... to build two quadric equations */
252 u = z * z - r;
253 v = 2 * z - p;
254 if (approximately_zero(u)) {
255 u = 0;
256 } else if (u > 0) {
257 u = sqrt(u);
258 } else {
259 return 0;
260 }
261 if (approximately_zero(v)) {
262 v = 0;
263 } else if (v > 0) {
264 v = sqrt(v);
265 } else {
266 return 0;
267 }
268 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
269 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
270 }
271 // eliminate duplicates
caryclark@google.com235f56a2012-09-14 14:19:30 +0000272 for (i = 0; i < num - 1; ++i) {
273 for (int j = i + 1; j < num; ) {
274 if (approximately_equal(s[i], s[j])) {
275 if (j < --num) {
276 s[j] = s[num];
277 }
278 } else {
279 ++j;
280 }
281 }
282 }
283 /* resubstitute */
284 const double sub = a / 4;
285 for (i = 0; i < num; ++i) {
286 s[i] -= sub;
287 }
288 return num;
289}
290
291