euler314 commited on
Commit
8d4bb9f
·
verified ·
1 Parent(s): f181e3b

Update app.cpp

Browse files
Files changed (1) hide show
  1. app.cpp +176 -195
app.cpp CHANGED
@@ -1,4 +1,4 @@
1
- // app.cpp - Modified version with corrected cubic solver using discriminant
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 to use discriminant for determining root patterns
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; // Threshold for considering a value as zero
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
- // For a quadratic equation: bz^2 + cz + d = 0
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
- // Factor out z: z(az^2 + bz + c) = 0
77
- roots.root1 = std::complex<double>(0.0, 0.0); // One root is exactly zero
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
- roots.root2 = std::complex<double>((-b + sqrtDiscriminant) / (2.0 * a), 0.0);
84
- roots.root3 = std::complex<double>((-b - sqrtDiscriminant) / (2.0 * a), 0.0);
85
 
86
- // Ensure one positive and one negative root when possible
87
- if (roots.root2.real() > 0 && roots.root3.real() > 0) {
88
- // If both are positive, make the second one negative (arbitrary)
89
- roots.root3 = std::complex<double>(-std::abs(roots.root3.real()), 0.0);
90
- } else if (roots.root2.real() < 0 && roots.root3.real() < 0) {
91
- // If both are negative, make the second one positive (arbitrary)
92
- roots.root3 = std::complex<double>(std::abs(roots.root3.real()), 0.0);
 
 
 
 
 
 
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
- // Convert to depressed cubic form t^3 + pt + q = 0
104
- double p = (3.0*a*c - b*b) / (3.0*a*a);
105
- double q = (2.0*b*b*b - 9.0*a*b*c + 27.0*a*a*d) / (27.0*a*a*a);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
- // Calculate discriminant in depressed form
108
- double depressedDiscriminant = -(4.0*p*p*p + 27.0*q*q);
 
109
 
110
- // Check if discriminant is very close to zero
 
 
 
111
  if (std::abs(discriminant) < zero_threshold) {
112
- // Multiple roots case (either triple root or double root)
113
- double delta0 = b*b - 3.0*a*c;
114
 
115
- if (std::abs(delta0) < zero_threshold) {
116
  // Triple root case
117
- double root = -b / (3.0*a);
118
- roots.root1 = std::complex<double>(root, 0.0);
119
- roots.root2 = std::complex<double>(root, 0.0);
120
- roots.root3 = std::complex<double>(root, 0.0);
121
- } else {
122
- // Double root + single root case
123
- double doubleRoot = (9.0*a*d - b*c) / (2.0*delta0);
124
- double singleRoot = (4.0*a*b*c - 9.0*a*a*d - b*b*b) / (a*delta0);
 
 
 
125
 
126
- // Sort roots to ensure pattern: one root is negative, one is positive, one is zero or all are zero
127
- if (std::abs(doubleRoot) < zero_threshold) doubleRoot = 0.0;
128
- if (std::abs(singleRoot) < zero_threshold) singleRoot = 0.0;
129
 
130
- // Assign roots to ensure the pattern
131
- if (doubleRoot == 0.0) {
132
- roots.root1 = std::complex<double>(0.0, 0.0);
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
- // Both non-zero, assign based on signs
150
- if ((doubleRoot > 0 && singleRoot > 0) || (doubleRoot < 0 && singleRoot < 0)) {
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
- // Standard case based on discriminant
168
  if (discriminant > 0) {
169
- // Three distinct real roots
170
- double theta = std::acos(-q * std::sqrt(-27.0/(4.0*p*p*p)));
171
- double coef = 2.0 * std::sqrt(-p/3.0);
172
 
173
- double root1 = coef * std::cos(theta/3.0) - b/(3.0*a);
174
- double root2 = coef * std::cos((theta + 2.0*M_PI)/3.0) - b/(3.0*a);
175
- double root3 = coef * std::cos((theta + 4.0*M_PI)/3.0) - b/(3.0*a);
176
 
177
- // Ensure zero roots are exactly zero
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
- // Sort roots
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
- for (double val : root_vals) {
189
- if (val == 0.0) zeros++;
190
- else if (val > 0.0) positives++;
191
- else negatives++;
192
- }
193
 
194
- // Handle the root pattern
195
- if (zeros == 0) {
196
- // If no zeros, force middle root to zero
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
- // Calculate the real root using Cardano's formula
270
- double C;
271
- if (q >= 0) {
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
- // Check if real root is close to zero
290
- if (std::abs(realRoot) < zero_threshold) {
291
- realRoot = 0.0;
 
 
 
 
 
 
 
 
 
292
  }
293
 
294
- // Assign the roots with one real and two complex conjugates
295
- roots.root1 = std::complex<double>(realRoot, 0.0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  roots.root2 = std::complex<double>(realPart, imagPart);
297
  roots.root3 = std::complex<double>(realPart, -imagPart);
298
-
299
- return roots;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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