blob: 86ea7a63fde9a9d6d015796bd7c9fe233864dc14 [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"
caryclark@google.com73ca6242013-01-17 21:02:47 +000030#include "QuadraticUtilities.h"
caryclark@google.com235f56a2012-09-14 14:19:30 +000031#include "QuarticRoot.h"
32
caryclark@google.com73ca6242013-01-17 21:02:47 +000033#if SK_DEBUG
34#define QUARTIC_DEBUG 1
35#else
36#define QUARTIC_DEBUG 0
37#endif
38
caryclark@google.com235f56a2012-09-14 14:19:30 +000039const double PI = 4 * atan(1);
40
41// unlike quadraticRoots in QuadraticUtilities.cpp, this does not discard
42// real roots <= 0 or >= 1
caryclark@google.com73ca6242013-01-17 21:02:47 +000043int quadraticRootsX(const double A, const double B, const double C,
caryclark@google.com235f56a2012-09-14 14:19:30 +000044 double s[2]) {
45 if (approximately_zero(A)) {
46 if (approximately_zero(B)) {
47 s[0] = 0;
48 return C == 0;
49 }
50 s[0] = -C / B;
51 return 1;
52 }
53 /* normal form: x^2 + px + q = 0 */
54 const double p = B / (2 * A);
55 const double q = C / A;
caryclark@google.come7bd5f42012-12-13 19:47:53 +000056 double D = p * p - q;
caryclark@google.coma461ff02012-10-11 12:54:23 +000057 if (D < 0) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +000058 if (approximately_positive_squared(D)) {
59 D = 0;
60 } else {
61 return 0;
62 }
caryclark@google.coma461ff02012-10-11 12:54:23 +000063 }
64 double sqrt_D = sqrt(D);
65 if (approximately_less_than_zero(sqrt_D)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +000066 s[0] = -p;
67 return 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +000068 }
caryclark@google.coma461ff02012-10-11 12:54:23 +000069 s[0] = sqrt_D - p;
70 s[1] = -sqrt_D - p;
71 return 2;
caryclark@google.com235f56a2012-09-14 14:19:30 +000072}
73
caryclark@google.comd1688742012-09-18 20:08:37 +000074#define USE_GEMS 0
75#if USE_GEMS
caryclark@google.com235f56a2012-09-14 14:19:30 +000076// unlike cubicRoots in CubicUtilities.cpp, this does not discard
77// real roots <= 0 or >= 1
caryclark@google.com73ca6242013-01-17 21:02:47 +000078int cubicRootsX(const double A, const double B, const double C,
caryclark@google.com235f56a2012-09-14 14:19:30 +000079 const double D, double s[3]) {
80 int num;
81 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
82 const double invA = 1 / A;
83 const double a = B * invA;
84 const double b = C * invA;
85 const double c = D * invA;
86 /* substitute x = y - a/3 to eliminate quadric term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000087 x^3 +px + q = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +000088 const double a2 = a * a;
89 const double Q = (-a2 + b * 3) / 9;
90 const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
91 /* use Cardano's formula */
92 const double Q3 = Q * Q * Q;
93 const double R2plusQ3 = R * R + Q3;
94 if (approximately_zero(R2plusQ3)) {
95 if (approximately_zero(R)) {/* one triple solution */
96 s[0] = 0;
97 num = 1;
98 } else { /* one single and one double solution */
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000099
caryclark@google.com235f56a2012-09-14 14:19:30 +0000100 double u = cube_root(-R);
101 s[0] = 2 * u;
102 s[1] = -u;
103 num = 2;
104 }
105 }
106 else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
caryclark@google.comd1688742012-09-18 20:08:37 +0000107 const double theta = acos(-R / sqrt(-Q3)) / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000108 const double _2RootQ = 2 * sqrt(-Q);
109 s[0] = _2RootQ * cos(theta);
110 s[1] = -_2RootQ * cos(theta + PI / 3);
111 s[2] = -_2RootQ * cos(theta - PI / 3);
112 num = 3;
113 } else { /* one real solution */
114 const double sqrt_D = sqrt(R2plusQ3);
115 const double u = cube_root(sqrt_D - R);
116 const double v = -cube_root(sqrt_D + R);
117 s[0] = u + v;
118 num = 1;
119 }
120 /* resubstitute */
caryclark@google.comd1688742012-09-18 20:08:37 +0000121 const double sub = a / 3;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000122 for (int i = 0; i < num; ++i) {
123 s[i] -= sub;
124 }
125 return num;
126}
caryclark@google.comd1688742012-09-18 20:08:37 +0000127#else
128
caryclark@google.com73ca6242013-01-17 21:02:47 +0000129int cubicRootsX(double A, double B, double C, double D, double s[3]) {
130#if QUARTIC_DEBUG
131 // create a string mathematica understands
132 char str[1024];
133 bzero(str, sizeof(str));
134 sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D);
135#endif
caryclark@google.comd1688742012-09-18 20:08:37 +0000136 if (approximately_zero(A)) { // we're just a quadratic
137 return quadraticRootsX(B, C, D, s);
138 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000139 if (approximately_zero(D)) { // 0 is one root
caryclark@google.comd1688742012-09-18 20:08:37 +0000140 int num = quadraticRootsX(A, B, C, s);
141 for (int i = 0; i < num; ++i) {
142 if (approximately_zero(s[i])) {
143 return num;
144 }
145 }
146 s[num++] = 0;
147 return num;
148 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000149 if (approximately_zero(A + B + C + D)) { // 1 is one root
150 int num = quadraticRootsX(A, A + B, -D, s);
151 for (int i = 0; i < num; ++i) {
152 if (approximately_equal(s[i], 1)) {
153 return num;
154 }
155 }
156 s[num++] = 1;
157 return num;
158 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000159 double a, b, c;
160 {
161 double invA = 1 / A;
162 a = B * invA;
163 b = C * invA;
164 c = D * invA;
165 }
166 double a2 = a * a;
167 double Q = (a2 - b * 3) / 9;
168 double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
169 double Q3 = Q * Q * Q;
170 double R2MinusQ3 = R * R - Q3;
171 double adiv3 = a / 3;
172 double r;
173 double* roots = s;
174
caryclark@google.com0b7da432012-10-31 19:00:20 +0000175 if (approximately_zero_squared(R2MinusQ3)) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000176 if (approximately_zero(R)) {/* one triple solution */
177 *roots++ = -adiv3;
178 } else { /* one single and one double solution */
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000179
caryclark@google.comd1688742012-09-18 20:08:37 +0000180 double u = cube_root(-R);
181 *roots++ = 2 * u - adiv3;
182 *roots++ = -u - adiv3;
183 }
184 }
185 else if (R2MinusQ3 < 0) // we have 3 real roots
186 {
187 double theta = acos(R / sqrt(Q3));
188 double neg2RootQ = -2 * sqrt(Q);
189
190 r = neg2RootQ * cos(theta / 3) - adiv3;
191 *roots++ = r;
192
193 r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
194 *roots++ = r;
195
196 r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
197 *roots++ = r;
198 }
199 else // we have 1 real root
200 {
201 double A = fabs(R) + sqrt(R2MinusQ3);
202 A = cube_root(A);
203 if (R > 0) {
204 A = -A;
205 }
206 if (A != 0) {
207 A += Q / A;
208 }
209 r = A - adiv3;
210 *roots++ = r;
211 }
212 return (int)(roots - s);
213}
214#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +0000215
216int quarticRoots(const double A, const double B, const double C, const double D,
217 const double E, double s[4]) {
caryclark@google.comd1688742012-09-18 20:08:37 +0000218 double u, v;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000219 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
220 const double invA = 1 / A;
221 const double a = B * invA;
222 const double b = C * invA;
223 const double c = D * invA;
224 const double d = E * invA;
225 /* substitute x = y - a/4 to eliminate cubic term:
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000226 x^4 + px^2 + qx + r = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000227 const double a2 = a * a;
228 const double p = -3 * a2 / 8 + b;
229 const double q = a2 * a / 8 - a * b / 2 + c;
230 const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000231 int num;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000232 if (approximately_zero(r)) {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000233 /* no absolute term: y(y^3 + py + q) = 0 */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000234 num = cubicRootsX(1, 0, p, q, s);
235 s[num++] = 0;
236 } else {
237 /* solve the resolvent cubic ... */
238 (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000239 /* ... and take one real solution ... */
caryclark@google.com235f56a2012-09-14 14:19:30 +0000240 const double z = s[0];
241 /* ... to build two quadric equations */
242 u = z * z - r;
243 v = 2 * z - p;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000244 if (approximately_zero_squared(u)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000245 u = 0;
246 } else if (u > 0) {
247 u = sqrt(u);
248 } else {
249 return 0;
250 }
caryclark@google.com73ca6242013-01-17 21:02:47 +0000251 if (approximately_zero_squared(v)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000252 v = 0;
253 } else if (v > 0) {
254 v = sqrt(v);
255 } else {
256 return 0;
257 }
258 num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
259 num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
260 }
261 // eliminate duplicates
caryclark@google.com73ca6242013-01-17 21:02:47 +0000262 for (int i = 0; i < num - 1; ++i) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000263 for (int j = i + 1; j < num; ) {
264 if (approximately_equal(s[i], s[j])) {
265 if (j < --num) {
266 s[j] = s[num];
267 }
268 } else {
269 ++j;
270 }
271 }
272 }
273 /* resubstitute */
274 const double sub = a / 4;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000275 for (int i = 0; i < num; ++i) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000276 s[i] -= sub;
277 }
278 return num;
279}