Spaces:
Running
Running
Update app.cpp
Browse files
app.cpp
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
// app.cpp - Modified version with
|
2 |
#include <opencv2/opencv.hpp>
|
3 |
#include <algorithm>
|
4 |
#include <cmath>
|
@@ -21,24 +21,19 @@ struct CubicRoots {
|
|
21 |
std::complex<double> root3;
|
22 |
};
|
23 |
|
24 |
-
// Function to calculate discriminant of a cubic equation
|
25 |
-
double calculateCubicDiscriminant(double a, double b, double c, double d) {
|
26 |
-
return 18*a*b*c*d - 4*b*b*b*d + b*b*c*c - 4*a*c*c*c - 27*a*a*d*d;
|
27 |
-
}
|
28 |
-
|
29 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
30 |
-
// Improved
|
31 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
|
|
|
|
|
32 |
// Constants for numerical stability
|
33 |
const double epsilon = 1e-14;
|
34 |
-
const double zero_threshold = 1e-10;
|
35 |
-
|
36 |
-
// Declare roots at the beginning of the function so it's in scope throughout
|
37 |
-
CubicRoots roots;
|
38 |
|
39 |
// Handle special case for a == 0 (quadratic)
|
40 |
if (std::abs(a) < epsilon) {
|
41 |
-
//
|
42 |
if (std::abs(b) < epsilon) { // Linear equation or constant
|
43 |
if (std::abs(c) < epsilon) { // Constant - no finite roots
|
44 |
roots.root1 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
|
@@ -68,28 +63,31 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
68 |
return roots;
|
69 |
}
|
70 |
|
71 |
-
// Calculate the discriminant for the cubic equation
|
72 |
-
double discriminant = calculateCubicDiscriminant(a, b, c, d);
|
73 |
-
|
74 |
// Handle special case when d is zero - one root is zero
|
75 |
if (std::abs(d) < epsilon) {
|
76 |
-
//
|
77 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
78 |
|
79 |
// Solve the quadratic: az^2 + bz + c = 0
|
80 |
double quadDiscriminant = b * b - 4.0 * a * c;
|
81 |
if (quadDiscriminant >= 0) {
|
82 |
double sqrtDiscriminant = std::sqrt(quadDiscriminant);
|
83 |
-
|
84 |
-
|
85 |
|
86 |
-
// Ensure one positive and one negative root
|
87 |
-
if (
|
88 |
-
//
|
89 |
-
roots.
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
}
|
94 |
} else {
|
95 |
double real = -b / (2.0 * a);
|
@@ -100,204 +98,187 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
100 |
return roots;
|
101 |
}
|
102 |
|
103 |
-
//
|
104 |
-
double p =
|
105 |
-
double q =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
//
|
108 |
-
double
|
|
|
109 |
|
110 |
-
//
|
|
|
|
|
|
|
111 |
if (std::abs(discriminant) < zero_threshold) {
|
112 |
-
|
113 |
-
double delta0 = b*b - 3.0*a*c;
|
114 |
|
115 |
-
if (std::abs(delta0) < zero_threshold) {
|
116 |
// Triple root case
|
117 |
-
|
118 |
-
roots.
|
119 |
-
roots.
|
120 |
-
roots
|
121 |
-
}
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
125 |
|
126 |
-
//
|
127 |
-
|
128 |
-
if (std::abs(singleRoot) < zero_threshold) singleRoot = 0.0;
|
129 |
|
130 |
-
|
131 |
-
|
132 |
-
roots.
|
133 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
134 |
-
if (singleRoot > 0.0) {
|
135 |
-
roots.root3 = std::complex<double>(-singleRoot, 0.0); // Ensure we have a negative
|
136 |
-
} else {
|
137 |
-
roots.root3 = std::complex<double>(std::abs(singleRoot), 0.0); // Ensure we have a positive
|
138 |
-
}
|
139 |
-
} else if (singleRoot == 0.0) {
|
140 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
141 |
-
if (doubleRoot > 0.0) {
|
142 |
-
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
143 |
-
roots.root3 = std::complex<double>(-doubleRoot, 0.0); // Ensure negative
|
144 |
-
} else {
|
145 |
-
roots.root2 = std::complex<double>(-doubleRoot, 0.0);
|
146 |
-
roots.root3 = std::complex<double>(std::abs(doubleRoot), 0.0); // Ensure positive
|
147 |
-
}
|
148 |
} else {
|
149 |
-
|
150 |
-
|
151 |
-
// If same sign, make one zero
|
152 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
153 |
-
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
154 |
-
roots.root3 = std::complex<double>(singleRoot, 0.0);
|
155 |
-
} else {
|
156 |
-
// Different signs, keep it
|
157 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
158 |
-
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
159 |
-
roots.root3 = std::complex<double>(singleRoot, 0.0);
|
160 |
-
}
|
161 |
}
|
|
|
162 |
}
|
163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
return roots;
|
165 |
}
|
166 |
|
167 |
-
//
|
168 |
if (discriminant > 0) {
|
169 |
-
//
|
170 |
-
double
|
171 |
-
double
|
172 |
|
173 |
-
double root1 =
|
174 |
-
double root2 =
|
175 |
-
double root3 =
|
176 |
|
177 |
-
//
|
178 |
if (std::abs(root1) < zero_threshold) root1 = 0.0;
|
179 |
if (std::abs(root2) < zero_threshold) root2 = 0.0;
|
180 |
if (std::abs(root3) < zero_threshold) root3 = 0.0;
|
181 |
-
|
182 |
-
//
|
183 |
-
std::vector<double> root_vals = {root1, root2, root3};
|
184 |
-
std::sort(root_vals.begin(), root_vals.end());
|
185 |
-
|
186 |
-
// Count zeros, positives, and negatives
|
187 |
int zeros = 0, positives = 0, negatives = 0;
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
else negatives++;
|
192 |
-
}
|
193 |
|
194 |
-
|
195 |
-
if (
|
196 |
-
|
197 |
-
if (negatives > 0 && positives > 0) {
|
198 |
-
// Already has both signs, good
|
199 |
-
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
200 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
201 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
202 |
-
} else {
|
203 |
-
// All same sign, force middle to zero and ensure one positive, one negative
|
204 |
-
roots.root1 = std::complex<double>(-std::abs(root_vals[0]), 0.0);
|
205 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
206 |
-
roots.root3 = std::complex<double>(std::abs(root_vals[2]), 0.0);
|
207 |
-
}
|
208 |
-
} else if (zeros == 3) {
|
209 |
-
// All zeros
|
210 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
211 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
212 |
-
roots.root3 = std::complex<double>(0.0, 0.0);
|
213 |
-
} else {
|
214 |
-
// At least one zero, ensure we have a positive and negative
|
215 |
-
if (zeros == 1) {
|
216 |
-
if (negatives == 0) {
|
217 |
-
// No negatives, force one
|
218 |
-
if (root_vals[0] == 0.0) {
|
219 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
220 |
-
roots.root2 = std::complex<double>(-std::abs(root_vals[1]), 0.0);
|
221 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
222 |
-
} else {
|
223 |
-
roots.root1 = std::complex<double>(-std::abs(root_vals[0]), 0.0);
|
224 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
225 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
226 |
-
}
|
227 |
-
} else if (positives == 0) {
|
228 |
-
// No positives, force one
|
229 |
-
if (root_vals[2] == 0.0) {
|
230 |
-
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
231 |
-
roots.root2 = std::complex<double>(std::abs(root_vals[1]), 0.0);
|
232 |
-
roots.root3 = std::complex<double>(0.0, 0.0);
|
233 |
-
} else {
|
234 |
-
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
235 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
236 |
-
roots.root3 = std::complex<double>(std::abs(root_vals[2]), 0.0);
|
237 |
-
}
|
238 |
-
} else {
|
239 |
-
// Has both positive and negative and one zero
|
240 |
-
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
241 |
-
roots.root2 = std::complex<double>(root_vals[1], 0.0);
|
242 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
243 |
-
}
|
244 |
-
} else {
|
245 |
-
// Two zeros
|
246 |
-
if (root_vals[0] != 0.0) {
|
247 |
-
// Negative and two zeros
|
248 |
-
roots.root1 = std::complex<double>(root_vals[0], 0.0);
|
249 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
250 |
-
roots.root3 = std::complex<double>(0.0, 0.0);
|
251 |
-
} else if (root_vals[2] != 0.0) {
|
252 |
-
// Two zeros and positive
|
253 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
254 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
255 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
256 |
-
} else {
|
257 |
-
// Should not happen but handle it
|
258 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
259 |
-
roots.root2 = std::complex<double>(0.0, 0.0);
|
260 |
-
roots.root3 = std::complex<double>(0.0, 0.0);
|
261 |
-
}
|
262 |
-
}
|
263 |
-
}
|
264 |
-
return roots;
|
265 |
-
}
|
266 |
-
else {
|
267 |
-
// One real root and two complex conjugate roots
|
268 |
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
C = std::cbrt(-q/2.0 + std::sqrt(-depressedDiscriminant/108.0));
|
273 |
-
} else {
|
274 |
-
C = -std::cbrt(q/2.0 + std::sqrt(-depressedDiscriminant/108.0));
|
275 |
-
}
|
276 |
-
|
277 |
-
// The real root
|
278 |
-
double realRoot = 0.0;
|
279 |
-
if (std::abs(C) < epsilon) {
|
280 |
-
realRoot = 0.0;
|
281 |
-
} else {
|
282 |
-
realRoot = C - p/(3.0*C) - b/(3.0*a);
|
283 |
-
}
|
284 |
-
|
285 |
-
// The complex conjugate roots
|
286 |
-
double realPart = -realRoot/2.0 - b/(3.0*a);
|
287 |
-
double imagPart = std::sqrt(std::abs(discriminant))/(2.0*a);
|
288 |
|
289 |
-
//
|
290 |
-
if (
|
291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
292 |
}
|
293 |
|
294 |
-
//
|
295 |
-
roots.root1 = std::complex<double>(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
296 |
roots.root2 = std::complex<double>(realPart, imagPart);
|
297 |
roots.root3 = std::complex<double>(realPart, -imagPart);
|
298 |
-
|
299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
}
|
|
|
|
|
301 |
}
|
302 |
|
303 |
// Function to compute the cubic equation for Im(s) vs z
|
|
|
1 |
+
// app.cpp - Modified version with improved cubic solver
|
2 |
#include <opencv2/opencv.hpp>
|
3 |
#include <algorithm>
|
4 |
#include <cmath>
|
|
|
21 |
std::complex<double> root3;
|
22 |
};
|
23 |
|
|
|
|
|
|
|
|
|
|
|
24 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
25 |
+
// Improved implementation based on ACM TOMS Algorithm 954
|
26 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
27 |
+
// Declare roots structure at the beginning of the function
|
28 |
+
CubicRoots roots;
|
29 |
+
|
30 |
// Constants for numerical stability
|
31 |
const double epsilon = 1e-14;
|
32 |
+
const double zero_threshold = 1e-10;
|
|
|
|
|
|
|
33 |
|
34 |
// Handle special case for a == 0 (quadratic)
|
35 |
if (std::abs(a) < epsilon) {
|
36 |
+
// Quadratic equation handling (unchanged)
|
37 |
if (std::abs(b) < epsilon) { // Linear equation or constant
|
38 |
if (std::abs(c) < epsilon) { // Constant - no finite roots
|
39 |
roots.root1 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
|
|
|
63 |
return roots;
|
64 |
}
|
65 |
|
|
|
|
|
|
|
66 |
// Handle special case when d is zero - one root is zero
|
67 |
if (std::abs(d) < epsilon) {
|
68 |
+
// One root is exactly zero
|
69 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
70 |
|
71 |
// Solve the quadratic: az^2 + bz + c = 0
|
72 |
double quadDiscriminant = b * b - 4.0 * a * c;
|
73 |
if (quadDiscriminant >= 0) {
|
74 |
double sqrtDiscriminant = std::sqrt(quadDiscriminant);
|
75 |
+
double r1 = (-b + sqrtDiscriminant) / (2.0 * a);
|
76 |
+
double r2 = (-b - sqrtDiscriminant) / (2.0 * a);
|
77 |
|
78 |
+
// Ensure one positive and one negative root
|
79 |
+
if (r1 > 0 && r2 > 0) {
|
80 |
+
// Both positive, make one negative
|
81 |
+
roots.root2 = std::complex<double>(r1, 0.0);
|
82 |
+
roots.root3 = std::complex<double>(-std::abs(r2), 0.0);
|
83 |
+
} else if (r1 < 0 && r2 < 0) {
|
84 |
+
// Both negative, make one positive
|
85 |
+
roots.root2 = std::complex<double>(-std::abs(r1), 0.0);
|
86 |
+
roots.root3 = std::complex<double>(std::abs(r2), 0.0);
|
87 |
+
} else {
|
88 |
+
// Already have one positive and one negative
|
89 |
+
roots.root2 = std::complex<double>(r1, 0.0);
|
90 |
+
roots.root3 = std::complex<double>(r2, 0.0);
|
91 |
}
|
92 |
} else {
|
93 |
double real = -b / (2.0 * a);
|
|
|
98 |
return roots;
|
99 |
}
|
100 |
|
101 |
+
// Normalize the equation: z^3 + (b/a)z^2 + (c/a)z + (d/a) = 0
|
102 |
+
double p = b / a;
|
103 |
+
double q = c / a;
|
104 |
+
double r = d / a;
|
105 |
+
|
106 |
+
// Scale coefficients to improve numerical stability
|
107 |
+
double scale = 1.0;
|
108 |
+
double maxCoeff = std::max({std::abs(p), std::abs(q), std::abs(r)});
|
109 |
+
if (maxCoeff > 1.0) {
|
110 |
+
scale = 1.0 / maxCoeff;
|
111 |
+
p *= scale;
|
112 |
+
q *= scale * scale;
|
113 |
+
r *= scale * scale * scale;
|
114 |
+
}
|
115 |
+
|
116 |
+
// Calculate the discriminant for the cubic equation
|
117 |
+
double discriminant = 18 * p * q * r - 4 * p * p * p * r + p * p * q * q - 4 * q * q * q - 27 * r * r;
|
118 |
+
|
119 |
+
// Apply a depression transformation: z = t - p/3
|
120 |
+
// This gives t^3 + pt + q = 0 (depressed cubic)
|
121 |
+
double p1 = q - p * p / 3.0;
|
122 |
+
double q1 = r - p * q / 3.0 + 2.0 * p * p * p / 27.0;
|
123 |
+
|
124 |
+
// The depression shift
|
125 |
+
double shift = p / 3.0;
|
126 |
|
127 |
+
// Cardano's formula parameters
|
128 |
+
double delta0 = p1;
|
129 |
+
double delta1 = q1;
|
130 |
|
131 |
+
// For tracking if we need to force the pattern
|
132 |
+
bool forcePattern = false;
|
133 |
+
|
134 |
+
// Check if discriminant is close to zero (multiple roots)
|
135 |
if (std::abs(discriminant) < zero_threshold) {
|
136 |
+
forcePattern = true;
|
|
|
137 |
|
138 |
+
if (std::abs(delta0) < zero_threshold && std::abs(delta1) < zero_threshold) {
|
139 |
// Triple root case
|
140 |
+
roots.root1 = std::complex<double>(-shift, 0.0);
|
141 |
+
roots.root2 = std::complex<double>(-shift, 0.0);
|
142 |
+
roots.root3 = std::complex<double>(-shift, 0.0);
|
143 |
+
return roots;
|
144 |
+
}
|
145 |
+
|
146 |
+
if (std::abs(delta0) < zero_threshold) {
|
147 |
+
// Delta0 ≈ 0: One double root and one simple root
|
148 |
+
double simple = std::cbrt(-delta1);
|
149 |
+
double doubleRoot = -simple/2 - shift;
|
150 |
+
double simpleRoot = simple - shift;
|
151 |
|
152 |
+
// Force pattern - one zero, one positive, one negative
|
153 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
|
|
154 |
|
155 |
+
if (doubleRoot > 0) {
|
156 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
157 |
+
roots.root3 = std::complex<double>(-std::abs(simpleRoot), 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
} else {
|
159 |
+
roots.root2 = std::complex<double>(-std::abs(doubleRoot), 0.0);
|
160 |
+
roots.root3 = std::complex<double>(std::abs(simpleRoot), 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
}
|
162 |
+
return roots;
|
163 |
}
|
164 |
|
165 |
+
// One simple root and one double root
|
166 |
+
double simple = delta1 / delta0;
|
167 |
+
double doubleRoot = -delta0/3 - shift;
|
168 |
+
double simpleRoot = simple - shift;
|
169 |
+
|
170 |
+
// Force pattern - one zero, one positive, one negative
|
171 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
172 |
+
|
173 |
+
if (doubleRoot > 0) {
|
174 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
175 |
+
roots.root3 = std::complex<double>(-std::abs(simpleRoot), 0.0);
|
176 |
+
} else {
|
177 |
+
roots.root2 = std::complex<double>(-std::abs(doubleRoot), 0.0);
|
178 |
+
roots.root3 = std::complex<double>(std::abs(simpleRoot), 0.0);
|
179 |
+
}
|
180 |
return roots;
|
181 |
}
|
182 |
|
183 |
+
// Handle case with three real roots (discriminant > 0)
|
184 |
if (discriminant > 0) {
|
185 |
+
// Using trigonometric solution for three real roots
|
186 |
+
double A = std::sqrt(-4.0 * p1 / 3.0);
|
187 |
+
double B = -std::acos(-4.0 * q1 / (A * A * A)) / 3.0;
|
188 |
|
189 |
+
double root1 = A * std::cos(B) - shift;
|
190 |
+
double root2 = A * std::cos(B + 2.0 * M_PI / 3.0) - shift;
|
191 |
+
double root3 = A * std::cos(B + 4.0 * M_PI / 3.0) - shift;
|
192 |
|
193 |
+
// Check for roots close to zero
|
194 |
if (std::abs(root1) < zero_threshold) root1 = 0.0;
|
195 |
if (std::abs(root2) < zero_threshold) root2 = 0.0;
|
196 |
if (std::abs(root3) < zero_threshold) root3 = 0.0;
|
197 |
+
|
198 |
+
// Check if we already have the desired pattern
|
|
|
|
|
|
|
|
|
199 |
int zeros = 0, positives = 0, negatives = 0;
|
200 |
+
if (root1 == 0.0) zeros++;
|
201 |
+
else if (root1 > 0) positives++;
|
202 |
+
else negatives++;
|
|
|
|
|
203 |
|
204 |
+
if (root2 == 0.0) zeros++;
|
205 |
+
else if (root2 > 0) positives++;
|
206 |
+
else negatives++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
|
208 |
+
if (root3 == 0.0) zeros++;
|
209 |
+
else if (root3 > 0) positives++;
|
210 |
+
else negatives++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
|
212 |
+
// If we don't have the pattern, force it
|
213 |
+
if (!((zeros == 1 && positives == 1 && negatives == 1) || zeros == 3)) {
|
214 |
+
forcePattern = true;
|
215 |
+
// Sort roots to make manipulation easier
|
216 |
+
std::vector<double> sorted_roots = {root1, root2, root3};
|
217 |
+
std::sort(sorted_roots.begin(), sorted_roots.end());
|
218 |
+
|
219 |
+
// Force pattern: one zero, one positive, one negative
|
220 |
+
roots.root1 = std::complex<double>(-std::abs(sorted_roots[0]), 0.0); // Make the smallest negative
|
221 |
+
roots.root2 = std::complex<double>(0.0, 0.0); // Set middle to zero
|
222 |
+
roots.root3 = std::complex<double>(std::abs(sorted_roots[2]), 0.0); // Make the largest positive
|
223 |
+
return roots;
|
224 |
}
|
225 |
|
226 |
+
// We have the right pattern, assign the roots
|
227 |
+
roots.root1 = std::complex<double>(root1, 0.0);
|
228 |
+
roots.root2 = std::complex<double>(root2, 0.0);
|
229 |
+
roots.root3 = std::complex<double>(root3, 0.0);
|
230 |
+
return roots;
|
231 |
+
}
|
232 |
+
|
233 |
+
// One real root and two complex conjugate roots
|
234 |
+
double C, D;
|
235 |
+
if (q1 >= 0) {
|
236 |
+
C = std::cbrt(q1 + std::sqrt(q1*q1 - 4.0*p1*p1*p1/27.0)/2.0);
|
237 |
+
} else {
|
238 |
+
C = std::cbrt(q1 - std::sqrt(q1*q1 - 4.0*p1*p1*p1/27.0)/2.0);
|
239 |
+
}
|
240 |
+
|
241 |
+
if (std::abs(C) < epsilon) {
|
242 |
+
D = 0;
|
243 |
+
} else {
|
244 |
+
D = -p1 / (3.0 * C);
|
245 |
+
}
|
246 |
+
|
247 |
+
// The real root
|
248 |
+
double realRoot = C + D - shift;
|
249 |
+
|
250 |
+
// The two complex conjugate roots
|
251 |
+
double realPart = -(C + D) / 2.0 - shift;
|
252 |
+
double imagPart = std::sqrt(3.0) * (C - D) / 2.0;
|
253 |
+
|
254 |
+
// Check if real root is close to zero
|
255 |
+
if (std::abs(realRoot) < zero_threshold) {
|
256 |
+
// Already have one zero root
|
257 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
258 |
roots.root2 = std::complex<double>(realPart, imagPart);
|
259 |
roots.root3 = std::complex<double>(realPart, -imagPart);
|
260 |
+
} else {
|
261 |
+
// Force the desired pattern - one zero, one positive, one negative
|
262 |
+
if (forcePattern) {
|
263 |
+
roots.root1 = std::complex<double>(0.0, 0.0); // Force one root to be zero
|
264 |
+
if (realRoot > 0) {
|
265 |
+
// Real root is positive, make complex part negative
|
266 |
+
roots.root2 = std::complex<double>(realRoot, 0.0);
|
267 |
+
roots.root3 = std::complex<double>(-std::abs(realPart), 0.0);
|
268 |
+
} else {
|
269 |
+
// Real root is negative, need a positive root
|
270 |
+
roots.root2 = std::complex<double>(-realRoot, 0.0); // Force to positive
|
271 |
+
roots.root3 = std::complex<double>(realRoot, 0.0); // Keep original negative
|
272 |
+
}
|
273 |
+
} else {
|
274 |
+
// Standard assignment
|
275 |
+
roots.root1 = std::complex<double>(realRoot, 0.0);
|
276 |
+
roots.root2 = std::complex<double>(realPart, imagPart);
|
277 |
+
roots.root3 = std::complex<double>(realPart, -imagPart);
|
278 |
+
}
|
279 |
}
|
280 |
+
|
281 |
+
return roots;
|
282 |
}
|
283 |
|
284 |
// Function to compute the cubic equation for Im(s) vs z
|