1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
/*
* Roots3And4.c
*
* Utility functions to find cubic and quartic roots.
*
* Coefficients are passed like this:
*
* c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
*
* The functions return the number of non-complex roots and
* put the values into the s array.
*
* Author: Jochen Schwarze (schwarze@isa.de)
*
* Jan 26, 1990 Version for Graphics Gems
* Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
* (reported by Mark Podlipec),
* Old-style function definitions,
* IsZero() as a macro
* Nov 23, 1990 Some systems do not declare acos() and cbrt() in
* <math.h>, though the functions exist in the library.
* If large coefficients are used, EQN_EPS should be
* reduced considerably (e.g. to 1E-30), results will be
* correct but multiple roots might be reported more
* than once.
* Apr 18, 2018 Reformat for inclusing in ArgyllCMS - GWG
*
* The authors and the publisher hold no copyright restrictions
* on any of these files; this source code is public domain, and
* is freely available to the entire computer graphics community
* for study, use, and modification. We do request that the
* comment at the top of each file, identifying the original
* author and its original publication in the book Graphics
* Gems, be retained in all programs that use these files.
*
*/
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* epsilon surrounding for near zero values */
#define EQN_EPS 1e-9
#define IsZero(x) ((x) > -EQN_EPS && (x) < EQN_EPS)
#define CBRT(x) ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \
((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0))
int SolveQuadric(double c[3], double s[2]) {
double p, q, D;
/* normal form: x^2 + px + q = 0 */
p = c[1] / (2 * c[2]);
q = c[0] / c[2];
D = p * p - q;
if (IsZero(D)) {
s[ 0 ] = - p;
return 1;
} else if (D < 0) {
return 0;
} else /* if (D > 0) */ {
double sqrt_D = sqrt(D);
s[0] = sqrt_D - p;
s[1] = - sqrt_D - p;
return 2;
}
}
int SolveCubic(double c[4], double s[3]) {
int i, num;
double sub;
double A, B, C;
double sq_A, p, q;
double cb_p, D;
/* normal form: x^3 + Ax^2 + Bx + C = 0 */
A = c[2] / c[3];
B = c[1] / c[3];
C = c[0] / c[3];
/* substitute x = y - A/3 to eliminate quadric term: */
/* x^3 +px + q = 0 */
sq_A = A * A;
p = 1.0/3 * (-1.0/3 * sq_A + B);
q = 1.0/2 * (2.0/27 * A * sq_A - 1.0/3 * A * B + C);
/* use Cardano's formula */
cb_p = p * p * p;
D = q * q + cb_p;
if (IsZero(D)) {
if (IsZero(q)) { /* one triple solution */
s[0] = 0.0;
num = 1;
} else { /* one single and one double solution */
double u = CBRT(-q);
s[0] = 2.0 * u;
s[1] = - u;
num = 2;
}
} else if (D < 0) { /* Casus irreducibilis: three real solutions */
double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
double t = 2 * sqrt(-p);
s[0] = t * cos(phi);
s[1] = -t * cos(phi + M_PI / 3.0);
s[2] = -t * cos(phi - M_PI / 3.0);
num = 3;
} else { /* one real solution */
double sqrt_D = sqrt(D);
double u = CBRT(sqrt_D - q);
double v = -CBRT(sqrt_D + q);
s[0] = u + v;
num = 1;
}
/* resubstitute */
sub = 1.0/3.0 * A;
for (i = 0; i < num; i++)
s[i] -= sub;
return num;
}
int SolveQuartic(double c[5], double s[4]) {
double coeffs[4];
double z, u, v, sub;
double A, B, C, D;
double sq_A, p, q, r;
int i, num;
/* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
A = c[3] / c[4];
B = c[2] / c[4];
C = c[1] / c[4];
D = c[0] / c[4];
/* substitute x = y - A/4 to eliminate cubic term:
x^4 + px^2 + qx + r = 0 */
sq_A = A * A;
p = - 3.0/8 * sq_A + B;
q = 1.0/8 * sq_A * A - 1.0/2 * A * B + C;
r = - 3.0/256*sq_A*sq_A + 1.0/16*sq_A*B - 1.0/4*A*C + D;
if (IsZero(r)) {
/* no absolute term: y(y^3 + py + q) = 0 */
coeffs[0] = q;
coeffs[1] = p;
coeffs[2] = 0;
coeffs[3] = 1.0;
num = SolveCubic(coeffs, s);
s[num++] = 0;
} else {
/* solve the resolvent cubic ... */
coeffs[0] = 1.0/2.0 * r * p - 1.0/8.0 * q * q;
coeffs[1] = - r;
coeffs[2] = - 1.0/2.0 * p;
coeffs[3] = 1.0;
(void) SolveCubic(coeffs, s);
/* ... and take the one real solution ... */
z = s[0];
/* ... to build two quadric equations */
u = z * z - r;
v = 2 * z - p;
if (IsZero(u))
u = 0;
else if (u > 0)
u = sqrt(u);
else
return 0;
if (IsZero(v))
v = 0;
else if (v > 0)
v = sqrt(v);
else
return 0;
coeffs[0] = z - u;
coeffs[1] = q < 0 ? -v : v;
coeffs[2] = 1.0;
num = SolveQuadric(coeffs, s);
coeffs[0]= z + u;
coeffs[1] = q < 0 ? v : -v;
coeffs[2] = 1.0;
num += SolveQuadric(coeffs, s + num);
}
/* resubstitute */
sub = 1.0/4.0 * A;
for (i = 0; i < num; i++)
s[i] -= sub;
return num;
}
|