blob: 8e3664bef545c50fe9b5be777476acbf348560e7 [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 }
123 if (approximately_zero(D)) {
124 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 }
133 double a, b, c;
134 {
135 double invA = 1 / A;
136 a = B * invA;
137 b = C * invA;
138 c = D * invA;
139 }
140 double a2 = a * a;
141 double Q = (a2 - b * 3) / 9;
142 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
143 double Q3 = Q * Q * Q;
144 double R2MinusQ3 = R * R - Q3;
145 double adiv3 = a / 3;
146 double r;
147 double* roots = s;
148
149 if (R2MinusQ3 > -FLT_EPSILON / 10 && R2MinusQ3 < FLT_EPSILON / 10 ) {
150 if (approximately_zero(R)) {/* one triple solution */
151 *roots++ = -adiv3;
152 } else { /* one single and one double solution */
153
154 double u = cube_root(-R);
155 *roots++ = 2 * u - adiv3;
156 *roots++ = -u - adiv3;
157 }
158 }
159 else if (R2MinusQ3 < 0) // we have 3 real roots
160 {
161 double theta = acos(R / sqrt(Q3));
162 double neg2RootQ = -2 * sqrt(Q);
163
164 r = neg2RootQ * cos(theta / 3) - adiv3;
165 *roots++ = r;
166
167 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
168 *roots++ = r;
169
170 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
171 *roots++ = r;
172 }
173 else // we have 1 real root
174 {
175 double A = fabs(R) + sqrt(R2MinusQ3);
176 A = cube_root(A);
177 if (R > 0) {
178 A = -A;
179 }
180 if (A != 0) {
181 A += Q / A;
182 }
183 r = A - adiv3;
184 *roots++ = r;
185 }
186 return (int)(roots - s);
187}
188#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000189
190int quarticRoots(const double A, const double B, const double C, const double D,
191 const double E, double s[4]) {
192 if (approximately_zero(A)) {
193 if (approximately_zero(B)) {
194 return quadraticRootsX(C, D, E, s);
195 }
196 return cubicRootsX(B, C, D, E, s);
197 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000198 int num;
caryclark@google.comd1688742012-09-18 20:08:37 +0000199 int i;
200 if (approximately_zero(E)) {
201 num = cubicRootsX(A, B, C, D, s);
202 for (i = 0; i < num; ++i) {
203 if (approximately_zero(s[i])) {
204 return num;
205 }
206 }
207 s[num++] = 0;
208 return num;
209 }
210 double u, v;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000211 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
212 const double invA = 1 / A;
213 const double a = B * invA;
214 const double b = C * invA;
215 const double c = D * invA;
216 const double d = E * invA;
217 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000218 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000219 const double a2 = a * a;
220 const double p = -3 * a2 / 8 + b;
221 const double q = a2 * a / 8 - a * b / 2 + c;
222 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
223 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000224 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000225 num = cubicRootsX(1, 0, p, q, s);
226 s[num++] = 0;
227 } else {
228 /* solve the resolvent cubic ... */
229 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
230 /* ... and take the one real solution ... */
231 const double z = s[0];
232 /* ... to build two quadric equations */
233 u = z * z - r;
234 v = 2 * z - p;
235 if (approximately_zero(u)) {
236 u = 0;
237 } else if (u > 0) {
238 u = sqrt(u);
239 } else {
240 return 0;
241 }
242 if (approximately_zero(v)) {
243 v = 0;
244 } else if (v > 0) {
245 v = sqrt(v);
246 } else {
247 return 0;
248 }
249 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
250 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
251 }
252 // eliminate duplicates
caryclark@google.com235f56a2012-09-14 14:19:30 +0000253 for (i = 0; i < num - 1; ++i) {
254 for (int j = i + 1; j < num; ) {
255 if (approximately_equal(s[i], s[j])) {
256 if (j < --num) {
257 s[j] = s[num];
258 }
259 } else {
260 ++j;
261 }
262 }
263 }
264 /* resubstitute */
265 const double sub = a / 4;
266 for (i = 0; i < num; ++i) {
267 s[i] -= sub;
268 }
269 return num;
270}
271
272