Spaces:
Sleeping
Sleeping
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
|