cgv
Loading...
Searching...
No Matches
solve_polynom.cxx
1#include "solve_polynom.h"
2#include <math.h>
3/*
4 * Roots3And4.c
5 *
6 * Utility functions to find cubic and quartic roots,
7 * coefficients are passed like this:
8 *
9 * c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
10 *
11 * The functions return the number of non-complex roots and
12 * put the values into the s array.
13 *
14 * Author: Jochen Schwarze (schwarze@isa.de)
15 *
16 * Jan 26, 1990 Version for Graphics Gems
17 * Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic
18 * (reported by Mark Podlipec),
19 * Old-style function definitions,
20 * IsZero() as a macro
21 * Nov 23, 1990 Some systems do not declare acos() and cbrt() in
22 * <math.h>, though the functions exist in the library.
23 * If large coefficients are used, EQN_EPS should be
24 * reduced considerably (e.g. to 1E-30), results will be
25 * correct but multiple roots might be reported more
26 * than once.
27 */
28
29#ifndef M_PI
30#define M_PI 3.14159265358979323846
31#endif
32
33namespace cgv{
34 namespace math{
35
36
37
38/* epsilon surrounding for near zero values */
39
40#define EQN_EPS 1e-9
41#define IsZero(x) ((x) > -EQN_EPS && (x) < EQN_EPS)
42#define cbrt(x) ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \
43 ((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0))
44
45int solve_linear(double c[2],double s[1])
46{
47 if(IsZero(c[1]))
48 return 0;
49 s[0]=-c[0]/c[1];
50 return 1;
51}
52
53int solve_quadric(double c[3], double s[2], bool replicate_multiple_solutions)
54{
55 double p, q, D;
56
57 /* normal form: x^2 + px + q = 0 */
58
59 if (IsZero(c[2]))
60 return solve_linear(c, s);
61
62 p = c[ 1 ] / (2 * c[ 2 ]);
63 q = c[ 0 ] / c[ 2 ];
64
65 D = p * p - q;
66
67 if (IsZero(D)) {
68 s[ 0 ] = - p;
69 if (replicate_multiple_solutions) {
70 s[1] = s[0];
71 return 2;
72 }
73 return 1;
74 }
75 else if (D < 0) {
76 return 0;
77 }
78 else {
79 double sqrt_D = sqrt(D);
80 s[ 0 ] = sqrt_D - p;
81 s[ 1 ] = - sqrt_D - p;
82 return 2;
83 }
84}
85
86
87int solve_cubic(double c[4], double s[3], bool replicate_multiple_solutions)
88{
89 int i, num;
90 double sub;
91 double A, B, C;
92 double sq_A, p, q;
93 double cb_p, D;
94
95 /* normal form: x^3 + Ax^2 + Bx + C = 0 */
96 if(c[3] == 0)
97 return solve_quadric(c,s,replicate_multiple_solutions);
98
99 A = c[ 2 ] / c[ 3 ];
100 B = c[ 1 ] / c[ 3 ];
101 C = c[ 0 ] / c[ 3 ];
102
103 /* substitute x = y - A/3 to eliminate quadric term:
104 x^3 +px + q = 0 */
105
106 sq_A = A * A;
107 p = 1.0/3 * (- 1.0/3 * sq_A + B);
108 q = 1.0/2 * (2.0/27 * A * sq_A - 1.0/3 * A * B + C);
109
110 /* use Cardano's formula */
111
112 cb_p = p * p * p;
113 D = q * q + cb_p;
114
115 if (IsZero(D)) {
116 if (IsZero(q)) /* one triple solution */ {
117 s[ 0 ] = 0;
118 if (replicate_multiple_solutions) {
119 s[1] = s[2] = s[0];
120 num = 3;
121 }
122 else
123 num = 1;
124 }
125 else /* one single and one double solution */ {
126 double u = cbrt(-q);
127 s[ 0 ] = 2 * u;
128 s[ 1 ] = - u;
129 if (replicate_multiple_solutions) {
130 s[2] = s[1];
131 num = 3;
132 }
133 else
134 num = 2;
135 }
136 }
137 else if (D < 0) /* Casus irreducibilis: three real solutions */ {
138 double phi = 1.0/3 * acos(-q / sqrt(-cb_p));
139 double t = 2 * sqrt(-p);
140
141 s[ 0 ] = t * cos(phi);
142 s[ 1 ] = - t * cos(phi + M_PI / 3);
143 s[ 2 ] = - t * cos(phi - M_PI / 3);
144 num = 3;
145 }
146 else /* one real solution */ {
147 double sqrt_D = sqrt(D);
148 double u = cbrt(sqrt_D - q);
149 double v = - cbrt(sqrt_D + q);
150
151 s[ 0 ] = u + v;
152 num = 1;
153 }
154
155 /* resubstitute */
156 sub = 1.0/3 * A;
157 for (i = 0; i < num; ++i) s[ i ] -= sub;
158 return num;
159}
160
161
162int solve_quartic(double c[5], double s[4])
163{
164 double coeffs[ 4 ];
165 double z, u, v, sub;
166 double A, B, C, D;
167 double sq_A, p, q, r;
168 int i, num;
169
170 /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
171
172 A = c[ 3 ] / c[ 4 ];
173 B = c[ 2 ] / c[ 4 ];
174 C = c[ 1 ] / c[ 4 ];
175 D = c[ 0 ] / c[ 4 ];
176
177 /* substitute x = y - A/4 to eliminate cubic term:
178 x^4 + px^2 + qx + r = 0 */
179
180 sq_A = A * A;
181 p = - 3.0/8 * sq_A + B;
182 q = 1.0/8 * sq_A * A - 1.0/2 * A * B + C;
183 r = - 3.0/256*sq_A*sq_A + 1.0/16*sq_A*B - 1.0/4*A*C + D;
184
185 if (IsZero(r))
186 {
187 /* no absolute term: y(y^3 + py + q) = 0 */
188
189 coeffs[ 0 ] = q;
190 coeffs[ 1 ] = p;
191 coeffs[ 2 ] = 0;
192 coeffs[ 3 ] = 1;
193
194 num = solve_cubic(coeffs, s);
195
196 s[ num++ ] = 0;
197 }
198 else
199 {
200 /* solve the resolvent cubic ... */
201
202 coeffs[ 0 ] = 1.0/2 * r * p - 1.0/8 * q * q;
203 coeffs[ 1 ] = - r;
204 coeffs[ 2 ] = - 1.0/2 * p;
205 coeffs[ 3 ] = 1;
206
207 solve_cubic(coeffs, s);
208
209 /* ... and take the one real solution ... */
210
211 z = s[ 0 ];
212
213 /* ... to build two quadric equations */
214
215 u = z * z - r;
216 v = 2 * z - p;
217
218 if (IsZero(u))
219 u = 0;
220 else if (u > 0)
221 u = sqrt(u);
222 else
223 return 0;
224
225 if (IsZero(v))
226 v = 0;
227 else if (v > 0)
228 v = sqrt(v);
229 else
230 return 0;
231
232 coeffs[ 0 ] = z - u;
233 coeffs[ 1 ] = q < 0 ? -v : v;
234 coeffs[ 2 ] = 1;
235
236 num = solve_quadric(coeffs, s);
237
238 coeffs[ 0 ]= z + u;
239 coeffs[ 1 ] = q < 0 ? v : -v;
240 coeffs[ 2 ] = 1;
241
242 num += solve_quadric(coeffs, s + num);
243 }
244
245 /* resubstitute */
246
247 sub = 1.0/4 * A;
248 for (i = 0; i < num; ++i) s[ i ] -= sub;
249 return num;
250}
251
252}
253}
the cgv namespace
Definition print.h:11