File indexing completed on 2026-09-28 09:20:51
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathPoly_Laguerre_HeaderFile
0015 #define _MathPoly_Laguerre_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Core.hxx>
0019 #include <MathPoly_Quartic.hxx>
0020
0021 #include <array>
0022 #include <cmath>
0023 #include <complex>
0024 #include <algorithm>
0025
0026
0027 namespace MathPoly
0028 {
0029 using namespace MathUtils;
0030
0031
0032 constexpr int THE_MAX_POLY_DEGREE = 20;
0033
0034
0035 struct GeneralPolyResult
0036 {
0037 MathUtils::Status Status = MathUtils::Status::NotConverged;
0038 std::array<double, THE_MAX_POLY_DEGREE> Roots = {};
0039 std::array<std::complex<double>, THE_MAX_POLY_DEGREE> ComplexRoots = {};
0040 size_t NbRoots = 0;
0041 size_t NbComplexRoots = 0;
0042
0043 bool IsDone() const { return Status == MathUtils::Status::OK; }
0044
0045 explicit operator bool() const { return IsDone(); }
0046 };
0047
0048 namespace detail
0049 {
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 inline void EvaluatePolynomialWithDerivatives(const double* theCoeffs,
0060 int theDegree,
0061 std::complex<double> theX,
0062 std::complex<double>& theP,
0063 std::complex<double>& theDP,
0064 std::complex<double>& theD2P)
0065 {
0066
0067 theP = std::complex<double>(theCoeffs[theDegree], 0.0);
0068 theDP = std::complex<double>(0.0, 0.0);
0069 theD2P = std::complex<double>(0.0, 0.0);
0070
0071 for (int i = theDegree - 1; i >= 0; --i)
0072 {
0073 theD2P = theD2P * theX + theDP;
0074 theDP = theDP * theX + theP;
0075 theP = theP * theX + std::complex<double>(theCoeffs[i], 0.0);
0076 }
0077 theD2P *= 2.0;
0078 }
0079
0080
0081
0082
0083
0084
0085
0086
0087 inline std::complex<double> LaguerreIteration(const double* theCoeffs,
0088 int theDegree,
0089 std::complex<double> theX0,
0090 double theTol,
0091 int theMaxIter)
0092 {
0093 std::complex<double> aX = theX0;
0094 const double aN = static_cast<double>(theDegree);
0095
0096 for (int anIter = 0; anIter < theMaxIter; ++anIter)
0097 {
0098 std::complex<double> aP, aDP, aD2P;
0099 EvaluatePolynomialWithDerivatives(theCoeffs, theDegree, aX, aP, aDP, aD2P);
0100
0101 const double aAbsP = std::abs(aP);
0102 if (aAbsP < theTol)
0103 {
0104 return aX;
0105 }
0106
0107
0108 std::complex<double> aG = aDP / aP;
0109 std::complex<double> aH = aG * aG - aD2P / aP;
0110 std::complex<double> aSq = std::sqrt((aN - 1.0) * (aN * aH - aG * aG));
0111
0112
0113 std::complex<double> aDenom1 = aG + aSq;
0114 std::complex<double> aDenom2 = aG - aSq;
0115 std::complex<double> aDenom = (std::abs(aDenom1) > std::abs(aDenom2)) ? aDenom1 : aDenom2;
0116
0117 std::complex<double> aDelta;
0118 if (std::abs(aDenom) < MathUtils::THE_ZERO_TOL)
0119 {
0120
0121 if (std::abs(aDP) < MathUtils::THE_ZERO_TOL)
0122 {
0123 aDelta = std::complex<double>(1.0 + std::abs(aX), 0.0);
0124 }
0125 else
0126 {
0127 aDelta = aP / aDP;
0128 }
0129 }
0130 else
0131 {
0132 aDelta = std::complex<double>(aN, 0.0) / aDenom;
0133 }
0134
0135 aX -= aDelta;
0136
0137
0138 if (std::abs(aDelta) < theTol * (1.0 + std::abs(aX)))
0139 {
0140 return aX;
0141 }
0142 }
0143
0144 return aX;
0145 }
0146
0147
0148
0149
0150
0151
0152 inline void DeflateReal(double* theCoeffs, int& theDegree, double theRoot)
0153 {
0154
0155 double aCarry = theCoeffs[theDegree];
0156 for (int i = theDegree - 1; i >= 0; --i)
0157 {
0158 double aTemp = theCoeffs[i];
0159 theCoeffs[i] = aCarry;
0160 aCarry = aTemp + aCarry * theRoot;
0161 }
0162 --theDegree;
0163 }
0164
0165
0166
0167
0168
0169
0170 inline void DeflateComplex(double* theCoeffs, int& theDegree, std::complex<double> theRoot)
0171 {
0172
0173 const double aRe = theRoot.real();
0174 const double aIm = theRoot.imag();
0175 const double aB = -2.0 * aRe;
0176 const double aC = aRe * aRe + aIm * aIm;
0177
0178
0179
0180
0181
0182
0183 std::array<double, THE_MAX_POLY_DEGREE + 1> aQuotient;
0184 aQuotient.fill(0.0);
0185
0186
0187 aQuotient[theDegree - 2] = theCoeffs[theDegree];
0188 if (theDegree >= 3)
0189 {
0190 aQuotient[theDegree - 3] = theCoeffs[theDegree - 1] - aB * aQuotient[theDegree - 2];
0191 }
0192
0193 for (int i = theDegree - 4; i >= 0; --i)
0194 {
0195 aQuotient[i] = theCoeffs[i + 2] - aB * aQuotient[i + 1] - aC * aQuotient[i + 2];
0196 }
0197
0198
0199 for (int i = 0; i <= theDegree - 2; ++i)
0200 {
0201 theCoeffs[i] = aQuotient[i];
0202 }
0203 theDegree -= 2;
0204 }
0205
0206
0207 inline double RefineRealRoot(const double* theOrigCoeffs, int theOrigDegree, double theRoot)
0208 {
0209 constexpr int THE_MAX_ITER = 10;
0210 constexpr double THE_TOL = 1.0e-14;
0211
0212 double aX = theRoot;
0213 for (int anIter = 0; anIter < THE_MAX_ITER; ++anIter)
0214 {
0215
0216 double aP = theOrigCoeffs[theOrigDegree];
0217 double aDP = 0.0;
0218 for (int i = theOrigDegree - 1; i >= 0; --i)
0219 {
0220 aDP = aDP * aX + aP;
0221 aP = aP * aX + theOrigCoeffs[i];
0222 }
0223
0224 if (std::abs(aDP) < MathUtils::THE_ZERO_TOL)
0225 {
0226 break;
0227 }
0228
0229 const double aDelta = aP / aDP;
0230 aX -= aDelta;
0231
0232 if (std::abs(aDelta) < THE_TOL * (1.0 + std::abs(aX)))
0233 {
0234 break;
0235 }
0236 }
0237 return aX;
0238 }
0239
0240 }
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256 inline GeneralPolyResult Laguerre(const double* theCoeffs, int theDegree, double theTol = 1.0e-12)
0257 {
0258 GeneralPolyResult aResult;
0259
0260
0261 if (theDegree < 1 || theDegree > THE_MAX_POLY_DEGREE)
0262 {
0263 aResult.Status = MathUtils::Status::InvalidInput;
0264 return aResult;
0265 }
0266
0267
0268 if (std::abs(theCoeffs[theDegree]) < MathUtils::THE_ZERO_TOL)
0269 {
0270 aResult.Status = MathUtils::Status::InvalidInput;
0271 return aResult;
0272 }
0273
0274
0275 std::array<double, THE_MAX_POLY_DEGREE + 1> aWorkCoeffs;
0276 for (int i = 0; i <= theDegree; ++i)
0277 {
0278 aWorkCoeffs[i] = theCoeffs[i];
0279 }
0280
0281
0282 std::array<double, THE_MAX_POLY_DEGREE + 1> aOrigCoeffs;
0283 for (int i = 0; i <= theDegree; ++i)
0284 {
0285 aOrigCoeffs[i] = theCoeffs[i];
0286 }
0287
0288 int aDeg = theDegree;
0289
0290
0291 int aStartIdx = 0;
0292 while (aDeg > 0)
0293 {
0294
0295
0296 std::array<std::complex<double>, 4> aStartPoints = {std::complex<double>(0.0, 0.1),
0297 std::complex<double>(1.0, 0.5),
0298 std::complex<double>(-0.5, 0.3),
0299 std::complex<double>(0.5, -0.3)};
0300
0301 std::complex<double> aX0 = aStartPoints[aStartIdx % 4];
0302 ++aStartIdx;
0303
0304
0305 std::complex<double> aRoot =
0306 detail::LaguerreIteration(aWorkCoeffs.data(), aDeg, aX0, theTol, 100);
0307
0308
0309 const double aImagPart = std::abs(aRoot.imag());
0310 const double aRealPart = std::abs(aRoot.real());
0311 const double aScale = std::max(1.0, aRealPart);
0312
0313 if (aImagPart < theTol * aScale)
0314 {
0315
0316 double aRealRoot = aRoot.real();
0317
0318
0319 aRealRoot = detail::RefineRealRoot(aOrigCoeffs.data(), theDegree, aRealRoot);
0320
0321 aResult.Roots[aResult.NbRoots++] = aRealRoot;
0322
0323
0324 detail::DeflateReal(aWorkCoeffs.data(), aDeg, aRealRoot);
0325 }
0326 else
0327 {
0328
0329 aResult.ComplexRoots[aResult.NbComplexRoots++] = aRoot;
0330 aResult.ComplexRoots[aResult.NbComplexRoots++] = std::conj(aRoot);
0331
0332
0333 detail::DeflateComplex(aWorkCoeffs.data(), aDeg, aRoot);
0334 }
0335 }
0336
0337
0338 std::sort(aResult.Roots.begin(), aResult.Roots.begin() + aResult.NbRoots);
0339
0340
0341 if (aResult.NbRoots > 1)
0342 {
0343 size_t aNewCount = 1;
0344 for (size_t i = 1; i < aResult.NbRoots; ++i)
0345 {
0346 if (std::abs(aResult.Roots[i] - aResult.Roots[aNewCount - 1]) > theTol)
0347 {
0348 aResult.Roots[aNewCount++] = aResult.Roots[i];
0349 }
0350 }
0351 aResult.NbRoots = aNewCount;
0352 }
0353
0354 aResult.Status = MathUtils::Status::OK;
0355 return aResult;
0356 }
0357
0358
0359
0360
0361
0362
0363 inline GeneralPolyResult LaguerreN(const double* theCoeffs, size_t theSize, double theTol = 1.0e-12)
0364 {
0365 if (theSize < 2)
0366 {
0367 GeneralPolyResult aResult;
0368 aResult.Status = MathUtils::Status::InvalidInput;
0369 return aResult;
0370 }
0371 return Laguerre(theCoeffs, static_cast<int>(theSize - 1), theTol);
0372 }
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383 inline MathUtils::PolyResult Sextic(double theA,
0384 double theB,
0385 double theC,
0386 double theD,
0387 double theE,
0388 double theF,
0389 double theG)
0390 {
0391 MathUtils::PolyResult aResult;
0392
0393
0394 if (MathUtils::IsZero(theA))
0395 {
0396
0397 double aCoeffs[6] = {theG, theF, theE, theD, theC, theB};
0398 auto aGenResult = Laguerre(aCoeffs, 5);
0399 if (!aGenResult.IsDone())
0400 {
0401 aResult.Status = aGenResult.Status;
0402 return aResult;
0403 }
0404 aResult.Status = MathUtils::Status::OK;
0405 aResult.NbRoots = std::min(aGenResult.NbRoots, size_t(4));
0406 for (size_t i = 0; i < aResult.NbRoots; ++i)
0407 {
0408 aResult.Roots[i] = aGenResult.Roots[i];
0409 }
0410 return aResult;
0411 }
0412
0413 double aCoeffs[7] = {theG, theF, theE, theD, theC, theB, theA};
0414 auto aGenResult = Laguerre(aCoeffs, 6);
0415
0416 if (!aGenResult.IsDone())
0417 {
0418 aResult.Status = aGenResult.Status;
0419 return aResult;
0420 }
0421
0422 aResult.Status = MathUtils::Status::OK;
0423 aResult.NbRoots = std::min(aGenResult.NbRoots, size_t(4));
0424 for (size_t i = 0; i < aResult.NbRoots; ++i)
0425 {
0426 aResult.Roots[i] = aGenResult.Roots[i];
0427 }
0428
0429 return aResult;
0430 }
0431
0432
0433
0434
0435
0436
0437
0438
0439
0440 inline MathUtils::PolyResult Quintic(double theA,
0441 double theB,
0442 double theC,
0443 double theD,
0444 double theE,
0445 double theF)
0446 {
0447 MathUtils::PolyResult aResult;
0448
0449 if (MathUtils::IsZero(theA))
0450 {
0451
0452 return Quartic(theB, theC, theD, theE, theF);
0453 }
0454
0455 double aCoeffs[6] = {theF, theE, theD, theC, theB, theA};
0456 auto aGenResult = Laguerre(aCoeffs, 5);
0457
0458 if (!aGenResult.IsDone())
0459 {
0460 aResult.Status = aGenResult.Status;
0461 return aResult;
0462 }
0463
0464 aResult.Status = MathUtils::Status::OK;
0465 aResult.NbRoots = std::min(aGenResult.NbRoots, size_t(4));
0466 for (size_t i = 0; i < aResult.NbRoots; ++i)
0467 {
0468 aResult.Roots[i] = aGenResult.Roots[i];
0469 }
0470
0471 return aResult;
0472 }
0473
0474
0475
0476
0477
0478 inline GeneralPolyResult Octic(const double theCoeffs[9])
0479 {
0480 return Laguerre(theCoeffs, 8);
0481 }
0482
0483 }
0484
0485 #endif