File indexing completed on 2026-09-28 09:20:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathRoot_Trig_HeaderFile
0015 #define _MathRoot_Trig_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <MathPoly_Quadratic.hxx>
0021 #include <MathPoly_Quartic.hxx>
0022
0023 #include <cmath>
0024 #include <algorithm>
0025
0026 namespace MathRoot
0027 {
0028 using namespace MathUtils;
0029
0030
0031 struct TrigResult
0032 {
0033 MathUtils::Status Status = MathUtils::Status::NotConverged;
0034 std::array<double, 4> Roots = {0.0, 0.0, 0.0, 0.0};
0035 int NbRoots = 0;
0036 bool InfiniteRoots = false;
0037
0038 bool IsDone() const { return Status == MathUtils::Status::OK; }
0039
0040 explicit operator bool() const { return IsDone(); }
0041 };
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061 inline TrigResult Trigonometric(double theA,
0062 double theB,
0063 double theC,
0064 double theD,
0065 double theE,
0066 double theInfBound = 0.0,
0067 double theSupBound = THE_2PI,
0068 double theEps = 1.5e-12)
0069 {
0070 TrigResult aResult;
0071 aResult.Status = MathUtils::Status::OK;
0072
0073
0074 double aMyBorneInf, aDelta, aMod;
0075 if (theInfBound <= std::numeric_limits<double>::lowest() / 2.0
0076 && theSupBound >= std::numeric_limits<double>::max() / 2.0)
0077 {
0078 aMyBorneInf = 0.0;
0079 aDelta = THE_2PI;
0080 aMod = 0.0;
0081 }
0082 else if (theSupBound >= std::numeric_limits<double>::max() / 2.0)
0083 {
0084 aMyBorneInf = theInfBound;
0085 aDelta = THE_2PI;
0086 aMod = aMyBorneInf / THE_2PI;
0087 }
0088 else if (theInfBound <= std::numeric_limits<double>::lowest() / 2.0)
0089 {
0090 aMyBorneInf = theSupBound - THE_2PI;
0091 aDelta = THE_2PI;
0092 aMod = aMyBorneInf / THE_2PI;
0093 }
0094 else
0095 {
0096 aMyBorneInf = theInfBound;
0097 aDelta = theSupBound - theInfBound;
0098 aMod = theInfBound / THE_2PI;
0099 if (aDelta > THE_2PI)
0100 {
0101 aDelta = THE_2PI;
0102 }
0103 }
0104
0105 std::array<double, 4> aZer = {0.0, 0.0, 0.0, 0.0};
0106 size_t aNZer = 0;
0107 const double aDelta_Eps = std::numeric_limits<double>::epsilon() * std::abs(aDelta);
0108
0109
0110 if (std::abs(theA) <= theEps && std::abs(theB) <= theEps)
0111 {
0112 if (std::abs(theC) <= theEps)
0113 {
0114 if (std::abs(theD) <= theEps)
0115 {
0116 if (std::abs(theE) <= theEps)
0117 {
0118 aResult.InfiniteRoots = true;
0119 return aResult;
0120 }
0121 else
0122 {
0123 aResult.NbRoots = 0;
0124 return aResult;
0125 }
0126 }
0127 else
0128 {
0129
0130 double aVal = -theE / theD;
0131 if (std::abs(aVal) > 1.0)
0132 {
0133 aResult.NbRoots = 0;
0134 return aResult;
0135 }
0136
0137 aZer[0] = std::asin(aVal);
0138 aZer[1] = THE_PI - aZer[0];
0139 aNZer = 2;
0140
0141
0142 for (size_t i = 0; i < aNZer; ++i)
0143 {
0144 if (aZer[i] <= -theEps)
0145 {
0146 aZer[i] = THE_2PI - std::abs(aZer[i]);
0147 }
0148 aZer[i] += std::trunc(aMod) * THE_2PI;
0149 double aX = aZer[i] - aMyBorneInf;
0150 if (aX >= -aDelta_Eps && aX <= aDelta + aDelta_Eps)
0151 {
0152 aResult.Roots[aResult.NbRoots++] = aZer[i];
0153 }
0154 }
0155 return aResult;
0156 }
0157 }
0158 else if (std::abs(theD) <= theEps)
0159 {
0160
0161 double aVal = -theE / theC;
0162 if (std::abs(aVal) > 1.0)
0163 {
0164 aResult.NbRoots = 0;
0165 return aResult;
0166 }
0167
0168 double aPrincipal = std::acos(aVal);
0169 aZer[0] = aPrincipal;
0170 aZer[1] = -aPrincipal;
0171 aNZer = 2;
0172
0173
0174 for (size_t i = 0; i < aNZer; ++i)
0175 {
0176 double aAngle = aZer[i];
0177
0178 double aK = std::floor((theInfBound - aAngle) / THE_2PI);
0179 aAngle += (aK + 1) * THE_2PI;
0180
0181
0182 for (int aPeriod = 0; aPeriod < 2; ++aPeriod)
0183 {
0184 double aTestAngle = aAngle + aPeriod * THE_2PI;
0185 if (aTestAngle >= theInfBound - aDelta_Eps && aTestAngle <= theSupBound + aDelta_Eps)
0186 {
0187
0188 bool aDup = false;
0189 for (int k = 0; k < aResult.NbRoots; ++k)
0190 {
0191 if (std::abs(aTestAngle - aResult.Roots[k]) < theEps)
0192 {
0193 aDup = true;
0194 break;
0195 }
0196 }
0197 if (!aDup && aResult.NbRoots < 4)
0198 {
0199 aResult.Roots[aResult.NbRoots++] = aTestAngle;
0200 }
0201 }
0202 }
0203 }
0204 return aResult;
0205 }
0206 else
0207 {
0208
0209
0210 double aAA = theE - theC;
0211 double aBB = 2.0 * theD;
0212 double aCC = theE + theC;
0213
0214 MathPoly::PolyResult aPoly = MathPoly::Quadratic(aAA, aBB, aCC);
0215 if (!aPoly.IsDone())
0216 {
0217 aResult.Status = aPoly.Status;
0218 return aResult;
0219 }
0220 if (aPoly.Status == MathUtils::Status::InfiniteSolutions)
0221 {
0222 aResult.InfiniteRoots = true;
0223 return aResult;
0224 }
0225
0226 aNZer = aPoly.NbRoots;
0227 for (size_t i = 0; i < aNZer; ++i)
0228 {
0229 aZer[i] = aPoly.Roots[i];
0230 }
0231 }
0232 }
0233 else
0234 {
0235
0236 if (std::abs(theA) <= theEps && std::abs(theE) <= theEps)
0237 {
0238 if (std::abs(theC) <= theEps)
0239 {
0240
0241 aZer[0] = 0.0;
0242 aZer[1] = THE_PI;
0243 aNZer = 2;
0244
0245 double aVal = -theD / (theB * 2.0);
0246 if (std::abs(aVal) <= 1.0 + 1.0e-10)
0247 {
0248 if (aVal >= 1.0)
0249 {
0250 aZer[2] = 0.0;
0251 aZer[3] = 0.0;
0252 }
0253 else if (aVal <= -1.0)
0254 {
0255 aZer[2] = THE_PI;
0256 aZer[3] = THE_PI;
0257 }
0258 else
0259 {
0260 aZer[2] = std::acos(aVal);
0261 aZer[3] = THE_2PI - aZer[2];
0262 }
0263 aNZer = 4;
0264 }
0265
0266 for (size_t i = 0; i < aNZer; ++i)
0267 {
0268 if (aZer[i] <= aMyBorneInf - theEps)
0269 {
0270 aZer[i] += THE_2PI;
0271 }
0272 aZer[i] += std::trunc(aMod) * THE_2PI;
0273 double aX = aZer[i] - aMyBorneInf;
0274 if (aX >= -1.0e-10 && aX <= aDelta + 1.0e-10)
0275 {
0276 aZer[i] = std::max(theInfBound, std::min(theSupBound, aZer[i]));
0277 aResult.Roots[aResult.NbRoots++] = aZer[i];
0278 }
0279 }
0280 return aResult;
0281 }
0282 if (std::abs(theD) <= theEps)
0283 {
0284
0285 aZer[0] = THE_PI / 2.0;
0286 aZer[1] = THE_PI * 3.0 / 2.0;
0287 aNZer = 2;
0288
0289 double aVal = -theC / (theB * 2.0);
0290 if (std::abs(aVal) <= 1.0 + 1.0e-10)
0291 {
0292 if (aVal >= 1.0)
0293 {
0294 aZer[2] = THE_PI / 2.0;
0295 aZer[3] = THE_PI / 2.0;
0296 }
0297 else if (aVal <= -1.0)
0298 {
0299 aZer[2] = THE_PI * 3.0 / 2.0;
0300 aZer[3] = THE_PI * 3.0 / 2.0;
0301 }
0302 else
0303 {
0304 aZer[2] = std::asin(aVal);
0305 aZer[3] = THE_PI - aZer[2];
0306 }
0307 aNZer = 4;
0308 }
0309
0310 for (size_t i = 0; i < aNZer; ++i)
0311 {
0312 if (aZer[i] <= aMyBorneInf - theEps)
0313 {
0314 aZer[i] += THE_2PI;
0315 }
0316 aZer[i] += std::trunc(aMod) * THE_2PI;
0317 double aX = aZer[i] - aMyBorneInf;
0318 if (aX >= -1.0e-10 && aX <= aDelta + 1.0e-10)
0319 {
0320 aZer[i] = std::max(theInfBound, std::min(theSupBound, aZer[i]));
0321 aResult.Roots[aResult.NbRoots++] = aZer[i];
0322 }
0323 }
0324 return aResult;
0325 }
0326 }
0327
0328
0329
0330
0331 double ko0 = theA - theC + theE;
0332 double ko1 = 2.0 * theD - 4.0 * theB;
0333 double ko2 = 2.0 * theE - 2.0 * theA;
0334 double ko3 = 4.0 * theB + 2.0 * theD;
0335 double ko4 = theA + theC + theE;
0336
0337 MathPoly::PolyResult aPoly = MathPoly::Quartic(ko0, ko1, ko2, ko3, ko4);
0338 if (!aPoly.IsDone())
0339 {
0340 if (aPoly.Status == MathUtils::Status::InfiniteSolutions)
0341 {
0342 aResult.InfiniteRoots = true;
0343 }
0344 else
0345 {
0346 aResult.Status = aPoly.Status;
0347 }
0348 return aResult;
0349 }
0350
0351
0352 aNZer = std::min<size_t>(aPoly.NbRoots, aZer.size());
0353 for (size_t i = 0; i < aNZer; ++i)
0354 {
0355 aZer[i] = aPoly.Roots[i];
0356 }
0357
0358
0359 std::sort(aZer.begin(), aZer.begin() + aNZer);
0360 }
0361
0362
0363 for (size_t i = 0; i < aNZer; ++i)
0364 {
0365 double aTeta = 2.0 * std::atan(aZer[i]);
0366 if (aZer[i] <= -theEps)
0367 {
0368 aTeta = THE_2PI - std::abs(aTeta);
0369 }
0370 aTeta += std::trunc(aMod) * THE_2PI;
0371 if (aTeta - aMyBorneInf < 0.0)
0372 {
0373 aTeta += THE_2PI;
0374 }
0375
0376 double aX = aTeta - aMyBorneInf;
0377 if (aX >= -aDelta_Eps && aX <= aDelta + aDelta_Eps)
0378 {
0379
0380 auto aRefineRoot = [&](double theX) -> double {
0381 constexpr int THE_MAX_ITER = 20;
0382 constexpr double THE_TOL = 1.0e-14;
0383
0384 for (int anIter = 0; anIter < THE_MAX_ITER; ++anIter)
0385 {
0386 double aCos = std::cos(theX);
0387 double aSin = std::sin(theX);
0388 double aCos2 = aCos * aCos;
0389 double aSin2 = aSin * aSin;
0390 double aCS = aCos * aSin;
0391
0392 double aF = theA * aCos2 + 2.0 * theB * aCS + theC * aCos + theD * aSin + theE;
0393 double aDF = -2.0 * theA * aCS + 2.0 * theB * (aCos2 - aSin2) - theC * aSin + theD * aCos;
0394
0395
0396 if (std::abs(aF) < 1.0e-15)
0397 {
0398 break;
0399 }
0400
0401 double aDelta;
0402 if (std::abs(aDF) < 1.0e-10 * (std::abs(aF) + 1.0))
0403 {
0404
0405
0406 double aD2F =
0407 -2.0 * theA * (aCos2 - aSin2) - 4.0 * theB * aCS - theC * aCos - theD * aSin;
0408 double aDenom = 2.0 * aDF * aDF - aF * aD2F;
0409 if (std::abs(aDenom) < 1.0e-30)
0410 {
0411
0412 break;
0413 }
0414 aDelta = 2.0 * aF * aDF / aDenom;
0415 }
0416 else
0417 {
0418
0419 aDelta = aF / aDF;
0420 }
0421
0422
0423 constexpr double THE_MAX_STEP = 0.5;
0424 if (std::abs(aDelta) > THE_MAX_STEP)
0425 {
0426 aDelta = (aDelta > 0) ? THE_MAX_STEP : -THE_MAX_STEP;
0427 }
0428
0429 theX -= aDelta;
0430
0431 if (std::abs(aDelta) < THE_TOL)
0432 {
0433 break;
0434 }
0435 }
0436 return theX;
0437 };
0438
0439 double aTetaRefined = aRefineRoot(aTeta);
0440
0441
0442 double aDeltaNewton = std::abs(aTetaRefined - aTeta);
0443 double aSupmInfs100 = (theSupBound - theInfBound) * 0.01;
0444 if (aDeltaNewton <= aSupmInfs100)
0445 {
0446 aTeta = aTetaRefined;
0447 }
0448
0449
0450 bool aFound = false;
0451 for (int k = 0; k < aResult.NbRoots; ++k)
0452 {
0453 if (std::abs(aTeta - aResult.Roots[k]) < theEps)
0454 {
0455 aFound = true;
0456 break;
0457 }
0458 }
0459
0460 if (!aFound && aResult.NbRoots < 4)
0461 {
0462
0463 int aPos = aResult.NbRoots;
0464 for (int k = 0; k < aResult.NbRoots; ++k)
0465 {
0466 if (aTeta < aResult.Roots[k])
0467 {
0468 aPos = k;
0469 break;
0470 }
0471 }
0472 for (int k = aResult.NbRoots; k > aPos; --k)
0473 {
0474 aResult.Roots[k] = aResult.Roots[k - 1];
0475 }
0476 aResult.Roots[aPos] = aTeta;
0477 aResult.NbRoots++;
0478 }
0479 }
0480 }
0481
0482
0483 if (aResult.NbRoots < 4 && std::abs(theA - theC + theE) <= theEps)
0484 {
0485 double aTeta = THE_PI + std::trunc(aMod) * THE_2PI;
0486 double aX = aTeta - aMyBorneInf;
0487 if (aX >= -aDelta_Eps && aX <= aDelta + aDelta_Eps)
0488 {
0489 bool aFound = false;
0490 for (int k = 0; k < aResult.NbRoots; ++k)
0491 {
0492 if (std::abs(aTeta - aResult.Roots[k]) <= theEps)
0493 {
0494 aFound = true;
0495 break;
0496 }
0497 }
0498 if (!aFound)
0499 {
0500 int aPos = aResult.NbRoots;
0501 for (int k = 0; k < aResult.NbRoots; ++k)
0502 {
0503 if (aTeta < aResult.Roots[k])
0504 {
0505 aPos = k;
0506 break;
0507 }
0508 }
0509 for (int k = aResult.NbRoots; k > aPos; --k)
0510 {
0511 aResult.Roots[k] = aResult.Roots[k - 1];
0512 }
0513 aResult.Roots[aPos] = aTeta;
0514 aResult.NbRoots++;
0515 }
0516 }
0517 }
0518
0519 return aResult;
0520 }
0521
0522
0523
0524
0525
0526
0527
0528
0529 inline TrigResult TrigonometricLinear(double theD,
0530 double theE,
0531 double theInfBound = 0.0,
0532 double theSupBound = THE_2PI)
0533 {
0534 return Trigonometric(0.0, 0.0, 0.0, theD, theE, theInfBound, theSupBound);
0535 }
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545 inline TrigResult TrigonometricCDE(double theC,
0546 double theD,
0547 double theE,
0548 double theInfBound = 0.0,
0549 double theSupBound = THE_2PI)
0550 {
0551 return Trigonometric(0.0, 0.0, theC, theD, theE, theInfBound, theSupBound);
0552 }
0553
0554 }
0555
0556 #endif