1#ifndef PBAT_MATH_POLYNOMIAL_ROOTS_H
2#define PBAT_MATH_POLYNOMIAL_ROOTS_H
21#include "pbat/warning/Push.h"
22#include "pbat/warning/OldStyleCast.h"
23#include "pbat/warning/SignConversion.h"
35#if !defined(CY_NO_INTRIN_H) && !defined(CY_NO_EMMINTRIN_H) && !defined(CY_NO_IMMINTRIN_H)
36 #include <immintrin.h>
39namespace pbat::math::polynomial::detail {
66#ifndef _CY_CORE_H_INCLUDED_
67#define _CY_CORE_H_INCLUDED_
71#ifndef _CY_CORE_MEMCPY_LIMIT
72 #define _CY_CORE_MEMCPY_LIMIT 256
82#if defined(__INTEL_COMPILER)
83 #define _CY_COMPILER_INTEL __INTEL_COMPILER
84 #define _CY_COMPILER_VER_MEETS(msc, gcc, clang, intel) _CY_COMPILER_INTEL >= intel
85 #define _CY_COMPILER_VER_BELOW(msc, gcc, clang, intel) _CY_COMPILER_INTEL < intel
86#elif defined(__clang__)
87 #define _CY_COMPILER_CLANG \
88 (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
89 #define _CY_COMPILER_VER_MEETS(msc, gcc, clang, intel) _CY_COMPILER_CLANG >= clang
90 #define _CY_COMPILER_VER_BELOW(msc, gcc, clang, intel) _CY_COMPILER_CLANG < clang
91#elif defined(_MSC_VER)
92 #define _CY_COMPILER_MSC _MSC_VER
93 #define _CY_COMPILER_VER_MEETS(msc, gcc, clang, intel) _CY_COMPILER_MSC >= msc
94 #define _CY_COMPILER_VER_BELOW(msc, gcc, clang, intel) _CY_COMPILER_MSC < msc
96 #define _CY_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
97 #define _CY_COMPILER_VER_MEETS(msc, gcc, clang, intel) _CY_COMPILER_GCC >= gcc
98 #define _CY_COMPILER_VER_BELOW(msc, gcc, clang, intel) _CY_COMPILER_GCC < gcc
100 #define _CY_COMPILER_UNKNOWN
101 #define _CY_COMPILER_VER_MEETS(msc, gcc, clang, intel) false
102 #define _CY_COMPILER_VER_BELOW(msc, gcc, clang, intel) false
106#ifndef __cpp_constexpr
107 #if _CY_COMPILER_VER_MEETS(1900, 40600, 30100, 1310)
108 #define __cpp_constexpr
115#if _CY_COMPILER_VER_BELOW(1600, 40600, 20900, 1210)
124 template <
class C,
class T>
125 operator T C::*()
const
131 void operator&()
const {}
133static _cy_nullptr_t
nullptr;
137#define _CY_TEMPLATE_ALIAS_UNPACK(...) __VA_ARGS__
138#if _CY_COMPILER_VER_BELOW(1800, 40700, 30000, 1210)
139 #define _CY_TEMPLATE_ALIAS(template_name, template_equivalent) \
140 class template_name : public _CY_TEMPLATE_ALIAS_UNPACK template_equivalent \
144 #define _CY_TEMPLATE_ALIAS(template_name, template_equivalent) \
145 using template_name = _CY_TEMPLATE_ALIAS_UNPACK template_equivalent
149#if _CY_COMPILER_VER_MEETS(1700, 50000, 30400, 1300)
150 #define _cy_std_is_trivially_copyable 1
154#if defined(__INTEL_COMPILER)
156#elif defined(__clang__)
157 #define restrict __restrict__
158#elif defined(_MSC_VER)
159 #define restrict __restrict
161 #define restrict __restrict__
167#if _CY_COMPILER_VER_BELOW(1900, 40800, 30000, 1500)
168 #if defined(_MSC_VER)
169 #define alignas(alignment_size) __declspec(align(alignment_size))
171 #define alignas(alignment_size) __attribute__((aligned(alignment_size)))
176#if _CY_COMPILER_VER_BELOW(1700, 40700, 20900, 1210)
178 #if defined(_MSC_VER)
186#if _CY_COMPILER_VER_BELOW(1900, 60000, 20500, 1800)
187 #define static_assert(condition, message) assert(condition&& message)
191#ifndef __cpp_unrestricted_unions
192 #if _CY_COMPILER_VER_MEETS(1900, 40600, 30000, 1400)
193 #define __cpp_unrestricted_unions
199#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
200 #define CY_NODISCARD [[nodiscard]]
206#if _CY_COMPILER_VER_MEETS(1800, 40400, 30000, 1200)
207 #define CY_CLASS_FUNCTION_DEFAULT = default;
208 #define CY_CLASS_FUNCTION_DELETE = delete;
210 #define CY_CLASS_FUNCTION_DEFAULT \
213 #define CY_CLASS_FUNCTION_DELETE \
215 static_assert(false, "Calling deleted method."); \
220#if defined(__INTEL_COMPILER) || defined(__clang__) || defined(__GNUC__)
222 default: __builtin_unreachable()
223#elif defined(_MSC_VER)
227 #define nodefault default:
236 #define _CY_IVDEP __pragma(loop(ivdep))
238#elif defined __GNUC__
239 #if _CY_GCC_VER >= 40900
240 #define _CY_IVDEP _Pragma("GCC ivdep");
242#elif defined __clang__
243 #if _CY_CLANG_VER >= 30500
244 #define _CY_IVDEP _Pragma("clang loop vectorize(enable) interleave(enable)");
255#define _CY_IVDEP_FOR _CY_IVDEP for
261#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
262 #define _CY_CRT_SECURE_NO_WARNINGS __pragma(warning(push)) __pragma(warning(disable : 4996))
263 #define _CY_CRT_SECURE_RESUME_WARNINGS __pragma(warning(pop))
265 #define _CY_CRT_SECURE_NO_WARNINGS
266 #define _CY_CRT_SECURE_RESUME_WARNINGS
276CY_NODISCARD
inline T Max(T v1, T v2)
278 return v1 >= v2 ? v1 : v2;
281CY_NODISCARD
inline T Min(T v1, T v2)
283 return v1 <= v2 ? v1 : v2;
286CY_NODISCARD
inline T Max(T v1, T v2, T v3)
288 return Max(Max(v1, v2), v3);
291CY_NODISCARD
inline T Min(T v1, T v2, T v3)
293 return Min(Min(v1, v2), v3);
296CY_NODISCARD
inline T Max(T v1, T v2, T v3, T
const v4)
298 return Max(Max(v1, v2), Max(v3, v4));
301CY_NODISCARD
inline T Min(T v1, T v2, T v3, T
const v4)
303 return Min(Min(v1, v2), Min(v3, v4));
306CY_NODISCARD
inline T Clamp(T v, T minVal = T(0), T maxVal = T(1))
308 return Min(maxVal, Max(minVal, v));
312CY_NODISCARD
inline T ACosSafe(T v)
314 return (T)std::acos(Clamp(v, T(-1), T(1)));
317CY_NODISCARD
inline T ASinSafe(T v)
319 return (T)std::asin(Clamp(v, T(-1), T(1)));
322CY_NODISCARD
inline T Sqrt(T v)
324 return (T)std::sqrt(v);
327CY_NODISCARD
inline T SqrtSafe(T v)
329 return (T)std::sqrt(Max(v, T(0)));
334[[maybe_unused]] CY_NODISCARD
inline float Sqrt<float>(
float v)
336 return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ps1(v)));
339[[maybe_unused]] CY_NODISCARD
inline float SqrtSafe<float>(
float v)
341 return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ps1(Max(v, 0.0f))));
344[[maybe_unused]] CY_NODISCARD
inline double Sqrt<double>(
double v)
346 __m128d t = _mm_set1_pd(v);
347 return _mm_cvtsd_f64(_mm_sqrt_sd(t, t));
350[[maybe_unused]] CY_NODISCARD
inline double SqrtSafe<double>(
double v)
352 __m128d t = _mm_set1_pd(Max(v, 0.0));
353 return _mm_cvtsd_f64(_mm_sqrt_sd(t, t));
358constexpr inline T Pi()
360 return T(3.141592653589793238462643383279502884197169);
364CY_NODISCARD
inline bool IsFinite(T v)
366 return std::numeric_limits<T>::is_integer || std::isfinite(v);
374inline void MemCopy(T* restrict dest, T
const* restrict src,
size_t count)
376#ifdef _cy_std_is_trivially_copyable
377 if (std::is_trivially_copyable<T>())
379 memcpy(dest, src, (count) *
sizeof(T));
383 for (
size_t i = 0; i < count; ++i)
387template <
typename T,
typename S>
388inline void MemConvert(T* restrict dest, S
const* restrict src,
size_t count)
390 for (
size_t i = 0; i < count; ++i)
391 dest[i] =
reinterpret_cast<T
>(src[i]);
395inline void MemClear(T* dest,
size_t count)
397 memset(dest, 0, count *
sizeof(T));
401inline void SwapBytes(T& v1, T& v2)
404 memcpy(&t, &v1,
sizeof(T));
405 memcpy(&v1, &v2,
sizeof(T));
406 memcpy(&v2, &t,
sizeof(T));
409inline void Swap(T& v1, T& v2)
411 if (std::is_trivially_copyable<T>::value)
425template <
bool ascending,
typename T>
426inline void Sort2(T& r0, T& r1, T
const& v0, T
const& v1)
440template <
bool ascending,
typename T>
441inline void Sort2(T r[2], T
const v[2])
443 r[1 - ascending] = Min(v[0], v[1]);
444 r[ascending] = Max(v[0], v[1]);
447template <
bool ascending,
typename T>
448void Sort3(T& r0, T& r1, T& r2, T
const& v0, T
const& v1, T
const& v2)
452 T n2x01 = Min(v2, x01);
453 r1 = Max(n01, n2x01);
456 r0 = Min(n2x01, n01);
462 r2 = Min(n2x01, n01);
466template <
bool ascending,
typename T>
467void Sort3(T r[3], T
const v[3])
469 T n01 = Min(v[0], v[1]);
470 T x01 = Max(v[0], v[1]);
471 T n2x01 = Min(v[2], x01);
472 T r0 = Min(n2x01, n01);
473 T r1 = Max(n01, n2x01);
474 T r2 = Max(x01, v[2]);
489template <
bool ascending,
typename T>
490inline void Sort4(T& r0, T& r1, T& r2, T& r3, T
const& v0, T
const& v1, T
const& v2, T
const& v3)
496 T x02 = Max(n23, n01);
497 T n13 = Min(x01, x23);
514template <
bool ascending,
typename T>
515inline void Sort4(T r[4], T
const v[4])
517 T n01 = Min(v[0], v[1]);
518 T x01 = Max(v[0], v[1]);
519 T n23 = Min(v[2], v[3]);
520 T x23 = Max(v[2], v[3]);
521 T x02 = Max(n23, n01);
522 T n13 = Min(x01, x23);
523 T r0 = Min(n01, n23);
524 T r1 = Min(x02, n13);
525 T r2 = Max(n13, x02);
526 T r3 = Max(x23, x01);
574#ifndef _CY_POLYNOMIAL_H_INCLUDED_
575#define _CY_POLYNOMIAL_H_INCLUDED_
595template <
int N,
typename ftype>
596inline ftype PolynomialEval(ftype
const coef[N + 1], ftype x)
599 for (
int i = N - 1; i >= 0; --i)
612template <
int N,
typename ftype>
613inline ftype PolynomialEvalWithDeriv(ftype& derivativeValue, ftype
const coef[N + 1], ftype x)
622 ftype p = coef[N] * x + coef[N - 1];
624 for (
int i = N - 2; i >= 0; --i)
629 derivativeValue = dp;
640template <
int N,
typename ftype>
641inline void PolynomialDerivative(ftype deriv[N], ftype
const coef[N + 1])
644 for (
int i = 2; i <= N; ++i)
645 deriv[i - 1] = i * coef[i];
661template <
int N,
typename ftype>
662inline void PolynomialDeflate(ftype defPoly[N], ftype
const coef[N + 1], ftype root)
664 defPoly[N - 1] = coef[N];
665 for (
int i = N - 1; i > 0; --i)
666 defPoly[i - 1] = coef[i] + root * defPoly[i];
681template <
int N,
typename ftype>
682inline void PolynomialInflate(ftype infPoly[N + 2], ftype
const coef[N + 1], ftype root)
684 infPoly[N + 1] = coef[N];
685 for (
int i = N - 1; i >= 0; --i)
686 infPoly[i + 1] = coef[i] - root * infPoly[i + 1];
687 infPoly[0] = -root * coef[0];
715template <
typename ftype>
716constexpr ftype PolynomialDefaultError()
721[[maybe_unused]]
constexpr float PolynomialDefaultError<float>()
726[[maybe_unused]]
constexpr double PolynomialDefaultError<double>()
733class RootFinderNewton;
735#define _CY_POLY_TEMPLATE_N \
739 bool boundError = false, \
740 typename RootFinder = RootFinderNewton> \
742#define _CY_POLY_TEMPLATE_R \
743 template <typename ftype, bool boundError = false, typename RootFinder = RootFinderNewton> \
745#define _CY_POLY_TEMPLATE_A \
746 template <typename ftype> \
748#define _CY_POLY_TEMPLATE_D \
749 template <typename ftype> \
752#define _CY_POLY_TEMPLATE_NC \
756 bool boundError = false, \
757 typename RootFinder = RootFinderNewton, \
758 typename RootCallback> \
760#define _CY_POLY_TEMPLATE_RC \
763 bool boundError = false, \
764 typename RootFinder = RootFinderNewton, \
765 typename RootCallback> \
767#define _CY_POLY_TEMPLATE_AC \
768 template <typename ftype, typename RootCallback> \
775_CY_POLY_TEMPLATE_N
int PolynomialRoots(
777 ftype
const coef[N + 1],
780 ftype xError = PolynomialDefaultError<ftype>());
781_CY_POLY_TEMPLATE_R
int CubicRoots(
786 ftype xError = PolynomialDefaultError<ftype>());
787_CY_POLY_TEMPLATE_A
int QuadraticRoots(ftype roots[2], ftype
const coef[3], ftype xMin, ftype xMax);
788_CY_POLY_TEMPLATE_D
int LinearRoot(ftype& root, ftype
const coef[2], ftype xMin, ftype xMax);
793_CY_POLY_TEMPLATE_N
int PolynomialRoots(
795 ftype
const coef[N + 1],
796 ftype xError = PolynomialDefaultError<ftype>());
797_CY_POLY_TEMPLATE_R
int
798CubicRoots(ftype roots[3], ftype
const coef[4], ftype xError = PolynomialDefaultError<ftype>());
799_CY_POLY_TEMPLATE_A
int QuadraticRoots(ftype roots[2], ftype
const coef[3]);
800_CY_POLY_TEMPLATE_D
int LinearRoot(ftype& root, ftype
const coef[2]);
806_CY_POLY_TEMPLATE_N
bool PolynomialFirstRoot(
808 ftype
const coef[N + 1],
811 ftype xError = PolynomialDefaultError<ftype>());
812_CY_POLY_TEMPLATE_R
bool CubicFirstRoot(
817 ftype xError = PolynomialDefaultError<ftype>());
818_CY_POLY_TEMPLATE_A
bool
819QuadraticFirstRoot(ftype& root, ftype
const coef[3], ftype xMin, ftype xMax);
824_CY_POLY_TEMPLATE_N
bool PolynomialFirstRoot(
826 ftype
const coef[N + 1],
827 ftype xError = PolynomialDefaultError<ftype>());
828_CY_POLY_TEMPLATE_R
bool
829CubicFirstRoot(ftype& root, ftype
const coef[4], ftype xError = PolynomialDefaultError<ftype>());
830_CY_POLY_TEMPLATE_A
bool QuadraticFirstRoot(ftype& root, ftype
const coef[3]);
835_CY_POLY_TEMPLATE_N
bool PolynomialHasRoot(
836 ftype
const coef[N + 1],
839 ftype xError = PolynomialDefaultError<ftype>());
840_CY_POLY_TEMPLATE_A
bool CubicHasRoot(ftype
const coef[4], ftype xMin, ftype xMax);
841_CY_POLY_TEMPLATE_A
bool QuadraticHasRoot(ftype
const coef[3], ftype xMin, ftype xMax);
846_CY_POLY_TEMPLATE_N
bool
847PolynomialHasRoot(ftype
const coef[N + 1], ftype xError = PolynomialDefaultError<ftype>());
848_CY_POLY_TEMPLATE_A
bool CubicHasRoot(ftype
const coef[4])
852_CY_POLY_TEMPLATE_A
bool QuadraticHasRoot(ftype
const coef[3])
854 return coef[1] * coef[1] - ftype(4) * coef[0] * coef[2] >= 0;
867_CY_POLY_TEMPLATE_NC
bool PolynomialForEachRoot(
868 RootCallback callback,
869 ftype
const coef[N + 1],
872 ftype xError = PolynomialDefaultError<ftype>());
873_CY_POLY_TEMPLATE_RC
bool CubicForEachRoot(
874 RootCallback callback,
878 ftype xError = PolynomialDefaultError<ftype>());
879_CY_POLY_TEMPLATE_AC
bool
880QuadraticForEachRoot(RootCallback callback, ftype
const coef[3], ftype xMin, ftype xMax);
892_CY_POLY_TEMPLATE_NC
bool PolynomialForEachRoot(
893 RootCallback callback,
894 ftype
const coef[N + 1],
895 ftype xError = PolynomialDefaultError<ftype>());
896_CY_POLY_TEMPLATE_RC
bool CubicForEachRoot(
897 RootCallback callback,
899 ftype xError = PolynomialDefaultError<ftype>());
900_CY_POLY_TEMPLATE_AC
bool QuadraticForEachRoot(RootCallback callback, ftype
const coef[3]);
906#define _CY_POLY_TEMPLATE_B \
907 template <bool boundError = false> \
909#define _CY_POLY_TEMPLATE_BC template <bool boundError = false, typename RootCallback>
920template <
typename ftype,
int N>
926 CY_NODISCARD ftype
const& operator[](
int i)
const
930 CY_NODISCARD ftype& operator[](
int i)
934 CY_NODISCARD ftype operator()(ftype x)
const
939 CY_NODISCARD ftype Eval(ftype x)
const
941 return PolynomialEval<N, ftype>(coef, x);
943 CY_NODISCARD ftype EvalWithDeriv(ftype& deriv, ftype x)
const
945 return PolynomialEvalWithDeriv<N, ftype>(deriv, coef, x);
948 CY_NODISCARD Polynomial<ftype, N> operator+(Polynomial<ftype, N>
const& p)
const
950 Polynomial<ftype, N> r;
951 for (
int i = 0; i <= N; ++i)
952 r[i] = coef[i] + p[i];
955 CY_NODISCARD Polynomial<ftype, N> operator-(Polynomial<ftype, N>
const& p)
const
957 Polynomial<ftype, N> r;
958 for (
int i = 0; i <= N; ++i)
959 r[i] = coef[i] - p[i];
962 CY_NODISCARD Polynomial<ftype, N> operator*(ftype s)
const
964 Polynomial<ftype, N> r;
965 for (
int i = 0; i <= N; ++i)
969 void operator+=(Polynomial<ftype, N>
const& p)
971 for (
int i = 0; i <= N; ++i)
974 void operator-=(Polynomial<ftype, N>
const& p)
976 for (
int i = 0; i <= N; ++i)
979 void operator*=(ftype s)
981 for (
int i = 0; i <= N; ++i)
987 Polynomial<ftype, N + M> operator*(Polynomial<ftype, M>
const& p)
const
989 Polynomial<ftype, N + M> r;
990 for (
int i = 0; i <= N + M; ++i)
992 for (
int i = 0; i <= N; ++i)
994 for (
int j = 0; j <= M; ++j)
996 r[i + j] += coef[i] * p[j];
1003 CY_NODISCARD Polynomial<ftype, 2 * N> Squared()
const
1005 Polynomial<ftype, 2 * N> r;
1006 for (
int i = 0; i <= 2 * N; ++i)
1008 for (
int i = 0; i <= N; ++i)
1010 r[2 * i] += coef[i] * coef[i];
1011 for (
int j = i + 1; j <= N; ++j)
1013 r[i + j] += 2 * coef[i] * coef[j];
1019 CY_NODISCARD Polynomial<ftype, N - 1> Derivative()
const
1021 Polynomial<ftype, N - 1> d;
1022 PolynomialDerivative<N, ftype>(d.coef, coef);
1025 CY_NODISCARD Polynomial<ftype, N - 1> Deflate(ftype root)
const
1027 Polynomial<ftype, N - 1> p;
1028 PolynomialDeflate<N, ftype>(p.coef, coef, root);
1031 CY_NODISCARD Polynomial<ftype, N + 1> Inflate(ftype root)
const
1033 Polynomial<ftype, N + 1> p;
1034 PolynomialInflate<N, ftype>(p.coef, coef, root);
1038 _CY_POLY_TEMPLATE_B
int Roots(ftype roots[N], ftype xError = DefaultError())
const
1040 return PolynomialRoots<N, ftype, boundError>(roots, coef, xError);
1042 _CY_POLY_TEMPLATE_B
bool FirstRoot(ftype& root, ftype xError = DefaultError())
const
1044 return PolynomialFirstRoot<N, ftype, boundError>(root, coef, xError);
1046 _CY_POLY_TEMPLATE_B
bool HasRoot(ftype xError = DefaultError())
const
1048 return PolynomialHasRoot<N, ftype, boundError>(coef, xError);
1050 _CY_POLY_TEMPLATE_BC
void ForEachRoot(RootCallback c, ftype xError = DefaultError())
const
1052 return PolynomialForEachRoot<N, ftype, boundError>(c, coef, xError);
1055 _CY_POLY_TEMPLATE_B
int
1056 Roots(ftype roots[N], ftype xMin, ftype xMax, ftype xError = DefaultError())
const
1058 return PolynomialRoots<N, ftype, boundError>(roots, coef, xMin, xMax, xError);
1061 _CY_POLY_TEMPLATE_B
bool
1062 FirstRoot(ftype& root, ftype xMin, ftype xMax, ftype xError = DefaultError())
const
1064 return PolynomialFirstRoot<N, ftype, boundError>(root, coef, xMin, xMax, xError);
1067 _CY_POLY_TEMPLATE_B
bool HasRoot(ftype xMin, ftype xMax, ftype xError = DefaultError())
const
1069 return PolynomialHasRoot<N, ftype, boundError>(coef, xMin, xMax, xError);
1071 _CY_POLY_TEMPLATE_BC
void
1072 ForEachRoot(RootCallback c, ftype xMin, ftype xMax, ftype xError = DefaultError())
const
1074 return PolynomialForEachRoot<N, ftype, boundError>(c, coef, xMin, xMax, xError);
1078 CY_NODISCARD
bool IsFinite()
const
1080 for (
int i = 0; i <= N; ++i)
1081 if (!cy::IsFinite(coef[i]))
1087 static constexpr ftype DefaultError()
1089 return PolynomialDefaultError<ftype>();
1099template <
typename T,
typename S>
1100inline T MultSign(T v, S sign)
1102 return v * (
sign < 0 ? T(-1) : T(1));
1104template <
typename T,
typename S>
1105inline bool IsDifferentSign(T a, S b)
1107 return (a < 0) != (b < 0);
1123class RootFinderNewton
1126 template <
int N,
typename ftype,
bool boundError = false>
1127 static inline ftype FindClosed(
1128 ftype
const coef[N + 1],
1129 ftype
const deriv[N],
1136 template <
int N,
typename ftype,
bool boundError = false>
1137 static inline ftype FindOpen(
1138 ftype
const coef[N + 1],
1139 ftype
const deriv[N],
1141 template <
int N,
typename ftype,
bool boundError = false>
1142 static inline ftype FindOpenMin(
1143 ftype
const coef[N + 1],
1144 ftype
const deriv[N],
1149 template <
int N,
typename ftype,
bool boundError = false>
1150 static inline ftype FindOpenMax(
1151 ftype
const coef[N + 1],
1152 ftype
const deriv[N],
1158 template <
int N,
typename ftype,
bool boundError,
bool openMin>
1159 static inline ftype FindOpen(
1160 ftype
const coef[N + 1],
1161 ftype
const deriv[N],
1178template <
int N,
typename ftype,
bool boundError>
1179inline ftype RootFinderNewton::FindClosed(
1180 ftype
const coef[N + 1],
1181 ftype
const deriv[N],
1185 [[maybe_unused]] ftype y1,
1188 ftype ep2 = 2 * xError;
1189 ftype xr = (x0 + x1) / 2;
1193 if constexpr (N <= 3)
1196 for (
int safetyCounter = 0; safetyCounter < 16; ++safetyCounter)
1199 xr - PolynomialEval<N, ftype>(coef, xr) / PolynomialEval<2, ftype>(deriv, xr);
1200 xn = Clamp(xn, x0, x1);
1201 if (std::abs(xr - xn) <= xError)
1209 ftype yr = PolynomialEval<N, ftype>(coef, xr);
1215 int side = IsDifferentSign(y0, yr);
1220 ftype dy = PolynomialEval<N - 1, ftype>(deriv, xr);
1223 if (xn > xb0 && xn < xb1)
1225 ftype stepsize = std::abs(xr - xn);
1227 if (stepsize > xError)
1229 yr = PolynomialEval<N, ftype>(coef, xr);
1233 if constexpr (boundError)
1238 xs = std::nextafter(side ? xb1 : xb0, side ? xb0 : xb1);
1242 xs = xn - MultSign(xError, side - 1);
1244 xs = std::nextafter(side ? xb1 : xb0, side ? xb0 : xb1);
1246 ftype ys = PolynomialEval<N, ftype>(coef, xs);
1247 int s = IsDifferentSign(y0, ys);
1259 xr = (xb0 + xb1) / 2;
1260 if (xr == xb0 || xr == xb1 || xb1 - xb0 <= ep2)
1262 if constexpr (boundError)
1266 ftype xm = side ? xb0 : xb1;
1267 ftype ym = PolynomialEval<N, ftype>(coef, xm);
1268 if (std::abs(ym) < std::abs(yr))
1274 yr = PolynomialEval<N, ftype>(coef, xr);
1287template <
int N,
typename ftype,
bool boundError>
1288inline ftype RootFinderNewton::FindOpen(ftype
const coef[N + 1], ftype
const deriv[N], ftype xError)
1292 "RootFinderNewton::FindOpen only works for polynomials with odd degrees.");
1294 const ftype yr = coef[0];
1295 if (IsDifferentSign(coef[N], yr))
1297 return FindOpenMax<N, ftype, boundError>(coef, deriv, xr, yr, xError);
1301 return FindOpenMin<N, ftype, boundError>(coef, deriv, xr, yr, xError);
1308template <
int N,
typename ftype,
bool boundError>
1309inline ftype RootFinderNewton::FindOpenMin(
1310 ftype
const coef[N + 1],
1311 ftype
const deriv[N],
1316 return FindOpen<N, ftype, boundError, true>(coef, deriv, x1, y1, x1 - ftype(1), xError);
1322template <
int N,
typename ftype,
bool boundError>
1323inline ftype RootFinderNewton::FindOpenMax(
1324 ftype
const coef[N + 1],
1325 ftype
const deriv[N],
1330 return FindOpen<N, ftype, boundError, false>(coef, deriv, x0, y0, x0 + ftype(1), xError);
1336template <
int N,
typename ftype,
bool boundError,
bool openMin>
1337inline ftype RootFinderNewton::FindOpen(
1338 ftype
const coef[N + 1],
1339 ftype
const deriv[N],
1345 ftype delta = ftype(1);
1346 ftype yr = PolynomialEval<N, ftype>(coef, xr);
1348 bool otherside = IsDifferentSign(ym, yr);
1354 if constexpr (openMin)
1356 return FindClosed<N, ftype, boundError>(coef, deriv, xr, xm, yr, ym, xError);
1360 return FindClosed<N, ftype, boundError>(coef, deriv, xm, xr, ym, yr, xError);
1368 ftype dy = PolynomialEval<N - 1, ftype>(deriv, xr);
1371 ftype dif = openMin ? xr - xn : xn - xr;
1372 if (dif <= 0 && std::isfinite(xn))
1379 ftype xs = xn - MultSign(xError, -ftype(openMin));
1380 ftype ys = PolynomialEval<N, ftype>(coef, xs);
1381 bool s = IsDifferentSign(ym, ys);
1391 xr = openMin ? xr - delta : xr + delta;
1394 yr = PolynomialEval<N, ftype>(coef, xr);
1395 otherside = IsDifferentSign(ym, yr);
1411template <
typename ftype>
1412inline int LinearRoot(ftype& root, ftype
const coef[2], ftype x0, ftype x1)
1414 if (coef[1] != ftype(0))
1416 ftype r = -coef[0] / coef[1];
1418 return (r >= x0 && r <= x1);
1422 root = (x0 + x1) / 2;
1423 return coef[0] == 0;
1427template <
typename ftype>
1428inline int LinearRoot(ftype& root, ftype
const coef[2])
1430 root = -coef[0] / coef[1];
1431 return coef[1] != 0;
1438template <
typename ftype>
1439inline int QuadraticRoots(ftype roots[2], ftype
const coef[3])
1441 const ftype c = coef[0];
1442 const ftype b = coef[1];
1443 const ftype a = coef[2];
1444 const ftype delta = b * b - 4 * a * c;
1447 const ftype d = Sqrt(delta);
1448 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1449 const ftype rv0 = q / a;
1450 const ftype rv1 = c / q;
1451 roots[0] = Min(rv0, rv1);
1452 roots[1] = Max(rv0, rv1);
1457 roots[0] = ftype(-0.5) * b / a;
1461template <
typename ftype>
1462inline int QuadraticRoots(ftype roots[2], ftype
const coef[3], ftype x0, ftype x1)
1464 const ftype c = coef[0];
1465 const ftype b = coef[1];
1466 const ftype a = coef[2];
1467 const ftype delta = b * b - 4 * a * c;
1470 const ftype d = Sqrt(delta);
1471 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1472 const ftype rv0 = q / a;
1473 const ftype rv1 = c / q;
1474 const ftype r0 = Min(rv0, rv1);
1475 const ftype r1 = Max(rv0, rv1);
1476 int r0i = (r0 >= x0) & (r0 <= x1);
1477 int r1i = (r1 >= x0) & (r1 <= x1);
1484 const ftype r0 = ftype(-0.5) * b / a;
1486 return (r0 >= x0) & (r0 <= x1);
1492template <
typename RootCallback>
1493inline int QuadraticRoots([[maybe_unused]]
float roots[2],
float const* coef, RootCallback callback)
1496 __m128 _0abc = _mm_load_ps(coef);
1497 __m128 _02a2b2c = _mm_add_ps(_0abc, _0abc);
1498 __m128 _2a2c_bb = _mm_shuffle_ps(_0abc, _02a2b2c, _MM_SHUFFLE(2, 0, 1, 1));
1499 __m128 _2c2a_bb = _mm_shuffle_ps(_0abc, _02a2b2c, _MM_SHUFFLE(0, 2, 1, 1));
1500 __m128 _4ac_b2 = _mm_mul_ps(_2a2c_bb, _2c2a_bb);
1501 __m128 _4ac = _mm_shuffle_ps(_4ac_b2, _4ac_b2, _MM_SHUFFLE(2, 2, 2, 2));
1502 if (_mm_comigt_ss(_4ac_b2, _4ac))
1504 __m128 delta = _mm_sub_ps(_4ac_b2, _4ac);
1505 __m128 sqrtd = _mm_sqrt_ss(delta);
1506 __m128 signb = _mm_set_ps(-0.0f, -0.0f, -0.0f, -0.0f);
1507 __m128 db = _mm_xor_ps(sqrtd, _mm_and_ps(_2a2c_bb, signb));
1508 __m128 b_db = _mm_add_ss(_2a2c_bb, db);
1509 __m128 _2q = _mm_xor_ps(b_db, signb);
1510 __m128 _2c_2q = _mm_shuffle_ps(_2q, _02a2b2c, _MM_SHUFFLE(0, 0, 0, 0));
1511 __m128 _2q_2a = _mm_shuffle_ps(_02a2b2c, _2q, _MM_SHUFFLE(0, 0, 2, 2));
1512 __m128 rv = _mm_div_ps(_2c_2q, _2q_2a);
1513 __m128 r0 = _mm_min_ps(rv, _mm_shuffle_ps(rv, rv, _MM_SHUFFLE(3, 2, 1, 2)));
1514 __m128 r = _mm_max_ps(r0, _mm_shuffle_ps(r0, r0, _MM_SHUFFLE(3, 2, 2, 0)));
1517 else if (_mm_comilt_ss(_4ac_b2, _4ac))
1519 __m128 r = _mm_div_ps(_2a2c_bb, _mm_shuffle_ps(_2a2c_bb, _2a2c_bb, _MM_SHUFFLE(1, 0, 3, 3)));
1520 return callback(r) * (coef[2] != 0);
1524[[maybe_unused]]
inline int QuadraticRoots<float>(
float roots[2],
float const* coef)
1526 return QuadraticRoots(roots, coef, [&](__m128 r) {
1527 roots[0] = _mm_cvtss_f32(r);
1528 roots[1] = _mm_cvtss_f32(_mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 2, 0, 1)));
1534[[maybe_unused]]
inline int QuadraticRoots<float>(
float roots[2],
float const* coef,
float x0,
float x1)
1536 return QuadraticRoots(roots, coef, [&](__m128 r) {
1537 __m128 range = _mm_set_ps(x1, x1, x0, x0);
1538 __m128 minT = _mm_cmpge_ps(r, range);
1539 __m128 maxT = _mm_cmple_ps(r, _mm_shuffle_ps(range, range, _MM_SHUFFLE(3, 2, 2, 2)));
1541 _mm_and_ps(minT, _mm_shuffle_ps(maxT, _mm_setzero_ps(), _MM_SHUFFLE(3, 2, 1, 0)));
1542 __m128 rr = _mm_blendv_ps(_mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 2, 0, 1)), r, valid);
1543 roots[0] = _mm_cvtss_f32(rr);
1544 roots[1] = _mm_cvtss_f32(_mm_shuffle_ps(rr, rr, _MM_SHUFFLE(3, 2, 0, 1)));
1545 return _mm_popcnt_u32(_mm_movemask_ps(valid));
1552template <
typename ftype>
1553inline bool QuadraticFirstRoot(ftype& root, ftype
const coef[3], ftype x0, ftype x1)
1555 const ftype c = coef[0];
1556 const ftype b = coef[1];
1557 const ftype a = coef[2];
1558 const ftype delta = b * b - 4 * a * c;
1561 const ftype d = Sqrt(delta);
1562 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1563 const ftype rv0 = q / a;
1564 const ftype rv1 = c / q;
1565 const ftype r0 = Min(rv0, rv1);
1573 const ftype r1 = Max(rv0, rv1);
1575 return (r1 >= x0) & (r1 <= x1);
1583template <
typename ftype>
1584inline bool QuadraticFirstRoot(ftype& root, ftype
const coef[3])
1586 const ftype c = coef[0];
1587 const ftype b = coef[1];
1588 const ftype a = coef[2];
1589 const ftype delta = b * b - 4 * a * c;
1592 const ftype d = Sqrt(delta);
1593 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1594 const ftype rv0 = q / a;
1595 const ftype rv1 = c / q;
1596 root = Min(rv0, rv1);
1604template <
typename ftype>
1605inline bool QuadraticHasRoot(ftype
const coef[3], ftype x0, ftype x1)
1607 const ftype c = coef[0];
1608 const ftype b = coef[1];
1609 const ftype a = coef[2];
1610 const ftype delta = b * b - 4 * a * c;
1613 const ftype d = Sqrt(delta);
1614 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1615 const ftype rv0 = q / a;
1616 const ftype rv1 = c / q;
1617 const ftype r0 = Min(rv0, rv1);
1618 const ftype r1 = Max(rv0, rv1);
1619 if (r0 >= x0 && r0 <= x1)
1621 if (r1 >= x0 && r1 <= x1)
1629template <
typename ftype,
typename RootCallback>
1630inline bool QuadraticForEachRoot(RootCallback callback, ftype
const coef[3], ftype x0, ftype x1)
1632 const ftype c = coef[0];
1633 const ftype b = coef[1];
1634 const ftype a = coef[2];
1635 const ftype delta = b * b - 4 * a * c;
1638 const ftype d = Sqrt(delta);
1639 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1640 const ftype rv0 = q / a;
1641 const ftype rv1 = c / q;
1642 const ftype r0 = Min(rv0, rv1);
1643 const ftype r1 = Max(rv0, rv1);
1644 if (r0 >= x0 && r0 <= x1)
1647 if (r1 >= x0 && r1 <= x1)
1648 return callback(r1);
1655template <
typename ftype,
typename RootCallback>
1656inline bool QuadraticForEachRoot(RootCallback callback, ftype
const coef[3])
1658 const ftype c = coef[0];
1659 const ftype b = coef[1];
1660 const ftype a = coef[2];
1661 const ftype delta = b * b - 4 * a * c;
1664 const ftype d = Sqrt(delta);
1665 const ftype q = ftype(-0.5) * (b + MultSign(d, b));
1666 const ftype rv0 = q / a;
1667 const ftype rv1 = c / q;
1668 const ftype r0 = Min(rv0, rv1);
1669 const ftype r1 = Max(rv0, rv1);
1672 return callback(r1);
1681template <
typename ftype,
bool boundError,
typename RootFinder>
1682inline int CubicRoots(ftype roots[3], ftype
const coef[4], ftype x0, ftype x1, ftype xError)
1684 const ftype y0 = PolynomialEval<3, ftype>(coef, x0);
1685 const ftype y1 = PolynomialEval<3, ftype>(coef, x1);
1687 const ftype a = coef[3] * 3;
1688 const ftype b_2 = coef[2];
1689 const ftype c = coef[1];
1691 const ftype deriv[4] = {c, 2 * b_2, a, 0};
1693 const ftype delta_4 = b_2 * b_2 - a * c;
1697 const ftype d_2 = Sqrt(delta_4);
1698 const ftype q = -(b_2 + MultSign(d_2, b_2));
1701 const ftype xa = Min(rv0, rv1);
1702 const ftype xb = Max(rv0, rv1);
1704 if (IsDifferentSign(y0, y1))
1706 if (xa >= x1 || xb <= x0 || (xa <= x0 && xb >= x1))
1708 roots[0] = RootFinder::template FindClosed<3, ftype, boundError>(
1721 if ((xa >= x1 || xb <= x0) || (xa <= x0 && xb >= x1))
1728 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
1729 if (IsDifferentSign(y0, ya))
1731 roots[0] = RootFinder::template FindClosed<3, ftype, boundError>(
1739 if constexpr (!boundError)
1741 if (IsDifferentSign(ya, y1) ||
1742 (xb < x1 && IsDifferentSign(ya, PolynomialEval<3, ftype>(coef, xb))))
1745 PolynomialDeflate<3>(defPoly, coef, roots[0]);
1746 return QuadraticRoots(roots + 1, defPoly, xa, x1) + 1;
1756 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
1757 if (IsDifferentSign(ya, yb))
1759 roots[!boundError ? 0 : numRoots++] =
1760 RootFinder::template FindClosed<3, ftype, boundError>(
1768 if constexpr (!boundError)
1770 if (IsDifferentSign(yb, y1))
1773 PolynomialDeflate<3>(defPoly, coef, roots[0]);
1774 return QuadraticRoots(roots + 1, defPoly, xb, x1) + 1;
1780 if (IsDifferentSign(yb, y1))
1782 roots[!boundError ? 0 : numRoots++] =
1783 RootFinder::template FindClosed<3, ftype, boundError>(
1791 if constexpr (!boundError)
1797 if (IsDifferentSign(ya, y1))
1799 roots[!boundError ? 0 : numRoots++] =
1800 RootFinder::template FindClosed<3, ftype, boundError>(
1815 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
1816 if (IsDifferentSign(y0, yb))
1818 roots[0] = RootFinder::template FindClosed<3, ftype, boundError>(
1826 if constexpr (!boundError)
1828 if (IsDifferentSign(yb, y1))
1831 PolynomialDeflate<3>(defPoly, coef, roots[0]);
1832 return QuadraticRoots(roots + 1, defPoly, xb, x1) + 1;
1840 if (IsDifferentSign(yb, y1))
1842 roots[!boundError ? 0 : numRoots++] =
1843 RootFinder::template FindClosed<3, ftype, boundError>(
1851 if constexpr (!boundError)
1859 if (IsDifferentSign(y0, y1))
1861 roots[0] = RootFinder::template FindClosed<3, ftype, boundError>(
1877template <
typename ftype,
bool boundError,
typename RootFinder>
1878inline int CubicRoots(ftype roots[3], ftype
const coef[4], ftype xError)
1882 const ftype a = coef[3] * 3;
1883 const ftype b_2 = coef[2];
1884 const ftype c = coef[1];
1886 const ftype deriv[4] = {c, 2 * b_2, a, 0};
1888 const ftype delta_4 = b_2 * b_2 - a * c;
1892 const ftype d_2 = Sqrt(delta_4);
1893 const ftype q = -(b_2 + MultSign(d_2, b_2));
1894 const ftype rv0 = q / a;
1895 const ftype rv1 = c / q;
1896 const ftype xa = Min(rv0, rv1);
1897 const ftype xb = Max(rv0, rv1);
1899 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
1900 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
1902 if (!IsDifferentSign(coef[3], ya))
1904 roots[0] = RootFinder::template FindOpenMin<3, ftype, boundError>(
1910 if constexpr (!boundError)
1912 if (IsDifferentSign(ya, yb))
1915 PolynomialDeflate<3>(defPoly, coef, roots[0]);
1916 return QuadraticRoots(roots + 1, defPoly) + 1;
1921 if (IsDifferentSign(ya, yb))
1923 roots[1] = RootFinder::template FindClosed<3, ftype, boundError>(
1931 roots[2] = RootFinder::template FindOpenMax<3, ftype, boundError>(
1943 roots[0] = RootFinder::template FindOpenMax<3, ftype, boundError>(
1954 ftype x_inf = -b_2 / a;
1955 ftype y_inf = PolynomialEval<3, ftype>(coef, x_inf);
1956 if (IsDifferentSign(coef[3], y_inf))
1958 roots[0] = RootFinder::template FindOpenMax<3, ftype, boundError>(
1967 roots[0] = RootFinder::template FindOpenMin<3, ftype, boundError>(
1978 return QuadraticRoots<ftype>(roots, coef);
1983template <
typename ftype,
bool boundError,
typename RootFinder>
1984inline bool CubicFirstRoot(ftype& root, ftype
const coef[4], ftype x0, ftype x1, ftype xError)
1986 const ftype y0 = PolynomialEval<3, ftype>(coef, x0);
1987 const ftype y1 = PolynomialEval<3, ftype>(coef, x1);
1989 const ftype a = coef[3] * 3;
1990 const ftype b_2 = coef[2];
1991 const ftype c = coef[1];
1993 const ftype deriv[4] = {c, 2 * b_2, a, 0};
1995 const ftype delta_4 = b_2 * b_2 - a * c;
1999 const ftype d_2 = Sqrt(delta_4);
2000 const ftype q = -(b_2 + MultSign(d_2, b_2));
2001 const ftype rv0 = q / a;
2002 const ftype rv1 = c / q;
2003 const ftype xa = Min(rv0, rv1);
2004 const ftype xb = Max(rv0, rv1);
2006 if (IsDifferentSign(y0, y1))
2008 if (xa >= x1 || xb <= x0 || (xa <= x0 && xb >= x1))
2010 root = RootFinder::template FindClosed<3, ftype, boundError>(
2023 if ((xa >= x1 || xb <= x0) || (xa <= x0 && xb >= x1))
2029 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
2030 if (IsDifferentSign(y0, ya))
2032 root = RootFinder::template FindClosed<3, ftype, boundError>(
2044 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2045 if (IsDifferentSign(ya, yb))
2047 root = RootFinder::template FindClosed<3, ftype, boundError>(
2057 if (IsDifferentSign(yb, y1))
2059 root = RootFinder::template FindClosed<3, ftype, boundError>(
2072 if (IsDifferentSign(ya, y1))
2074 root = RootFinder::template FindClosed<3, ftype, boundError>(
2088 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2089 if (IsDifferentSign(y0, yb))
2091 root = RootFinder::template FindClosed<3, ftype, boundError>(
2101 if (IsDifferentSign(yb, y1))
2103 root = RootFinder::template FindClosed<3, ftype, boundError>(
2117 if (IsDifferentSign(y0, y1))
2119 root = RootFinder::template FindClosed<3, ftype, boundError>(
2135template <
typename ftype,
bool boundError,
typename RootFinder>
2136inline bool CubicFirstRoot(ftype& root, ftype
const coef[4], ftype xError)
2140 const ftype a = coef[3] * 3;
2141 const ftype b_2 = coef[2];
2142 const ftype c = coef[1];
2144 const ftype deriv[4] = {c, 2 * b_2, a, 0};
2146 const ftype delta_4 = b_2 * b_2 - a * c;
2150 const ftype d_2 = Sqrt(delta_4);
2151 const ftype q = -(b_2 + MultSign(d_2, b_2));
2152 const ftype rv0 = q / a;
2153 const ftype rv1 = c / q;
2154 const ftype xa = Min(rv0, rv1);
2155 const ftype xb = Max(rv0, rv1);
2157 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
2158 if (!IsDifferentSign(coef[3], ya))
2160 root = RootFinder::template FindOpenMin<3, ftype, boundError>(
2169 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2170 root = RootFinder::template FindOpenMax<3, ftype, boundError>(
2180 ftype x_inf = -b_2 / a;
2181 ftype y_inf = PolynomialEval<3, ftype>(coef, x_inf);
2182 if (IsDifferentSign(coef[3], y_inf))
2184 root = RootFinder::template FindOpenMax<3, ftype, boundError>(
2193 root = RootFinder::template FindOpenMin<3, ftype, boundError>(
2204 return QuadraticFirstRoot<ftype>(root, coef);
2209template <
typename ftype>
2210inline bool CubicHasRoot(ftype
const coef[4], ftype x0, ftype x1)
2212 const ftype y0 = PolynomialEval<3, ftype>(coef, x0);
2213 const ftype y1 = PolynomialEval<3, ftype>(coef, x1);
2214 if (IsDifferentSign(y0, y1))
2217 const ftype a = coef[3] * 3;
2218 const ftype b_2 = coef[2];
2219 const ftype c = coef[1];
2221 const ftype delta_4 = b_2 * b_2 - a * c;
2225 const ftype d_2 = Sqrt(delta_4);
2226 const ftype q = -(b_2 + MultSign(d_2, b_2));
2227 const ftype rv0 = q / a;
2228 const ftype rv1 = c / q;
2229 const ftype xa = Min(rv0, rv1);
2230 const ftype xb = Max(rv0, rv1);
2232 if ((xa >= x1 || xb <= x0) || (xa <= x0 && xb >= x1))
2237 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
2238 if (IsDifferentSign(y0, ya))
2242 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2243 if (IsDifferentSign(y0, yb))
2249 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2250 if (IsDifferentSign(y0, yb))
2259template <
typename ftype,
bool boundError,
typename RootFinder,
typename RootCallback>
2261CubicForEachRoot(RootCallback callback, ftype
const coef[4], ftype x0, ftype x1, ftype xError)
2263 const ftype y0 = PolynomialEval<3, ftype>(coef, x0);
2264 const ftype y1 = PolynomialEval<3, ftype>(coef, x1);
2266 const ftype a = coef[3] * 3;
2267 const ftype b_2 = coef[2];
2268 const ftype c = coef[1];
2270 const ftype deriv[4] = {c, 2 * b_2, a, 0};
2272 const ftype delta_4 = b_2 * b_2 - a * c;
2276 const ftype d_2 = Sqrt(delta_4);
2277 const ftype t = -(b_2 + MultSign(d_2, b_2));
2278 const ftype rv0 = t / a;
2279 const ftype rv1 = c / t;
2280 const ftype xa = Min(rv0, rv1);
2281 const ftype xb = Max(rv0, rv1);
2283 if (xa >= x1 || xb <= x0)
2285 if (IsDifferentSign(y0, y1))
2287 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2298 else if (xa <= x0 && xb >= x1)
2300 if (IsDifferentSign(y0, y1))
2302 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2315 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
2316 if (IsDifferentSign(y0, ya))
2318 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2330 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2331 if (IsDifferentSign(ya, yb))
2333 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2343 if (IsDifferentSign(yb, y1))
2345 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2358 if (IsDifferentSign(ya, y1))
2360 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2374 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2375 if (IsDifferentSign(y0, yb))
2377 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2387 if (IsDifferentSign(yb, y1))
2389 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2403 if (IsDifferentSign(y0, y1))
2405 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2421template <
typename ftype,
bool boundError,
typename RootFinder,
typename RootCallback>
2422inline bool CubicForEachRoot(RootCallback callback, ftype
const coef[4], ftype xError)
2426 const ftype a = coef[3] * 3;
2427 const ftype b_2 = coef[2];
2428 const ftype c = coef[1];
2430 const ftype deriv[4] = {c, 2 * b_2, a, 0};
2432 const ftype delta_4 = b_2 * b_2 - a * c;
2436 const ftype d_2 = Sqrt(delta_4);
2437 const ftype q = -(b_2 + MultSign(d_2, b_2));
2438 const ftype rv0 = q / a;
2439 const ftype rv1 = c / q;
2440 const ftype xa = Min(rv0, rv1);
2441 const ftype xb = Max(rv0, rv1);
2443 const ftype ya = PolynomialEval<3, ftype>(coef, xa);
2444 const ftype yb = PolynomialEval<3, ftype>(coef, xb);
2446 if (!IsDifferentSign(coef[3], ya))
2449 root = RootFinder::template FindOpenMin<3, ftype, boundError>(
2457 if constexpr (!boundError)
2459 if (IsDifferentSign(ya, yb))
2462 PolynomialDeflate<3>(defPoly, coef, root);
2463 return QuadraticForEachRoot(callback, defPoly);
2468 if (IsDifferentSign(ya, yb))
2470 if (callback(RootFinder::template FindClosed<3, ftype, boundError>(
2479 if (callback(RootFinder::template FindOpenMax<3, ftype, boundError>(
2492 if (callback(RootFinder::template FindOpenMax<3, ftype, boundError>(
2503 ftype x_inf = -b_2 / a;
2504 ftype y_inf = PolynomialEval<3, ftype>(coef, x_inf);
2505 if (IsDifferentSign(coef[3], y_inf))
2507 if (callback(RootFinder::template FindOpenMax<3, ftype, boundError>(
2517 if (callback(RootFinder::template FindOpenMin<3, ftype, boundError>(
2529 return QuadraticForEachRoot<ftype>(callback, coef);
2536template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2538PolynomialRoots(ftype roots[N], ftype
const coef[N + 1], ftype x0, ftype x1, ftype xError)
2540 if constexpr (N == 1)
2541 return LinearRoot<ftype>(*roots, coef, x0, x1);
2542 else if constexpr (N == 2)
2543 return QuadraticRoots<ftype>(roots, coef, x0, x1);
2544 else if constexpr (N == 3)
2545 return CubicRoots<ftype, boundError, RootFinder>(roots, coef, x0, x1, xError);
2546 else if (coef[N] == 0)
2547 return PolynomialRoots<N - 1, ftype, boundError, RootFinder>(roots, coef, x0, x1, xError);
2550 ftype y0 = PolynomialEval<N, ftype>(coef, x0);
2552 PolynomialDerivative<N, ftype>(deriv, coef);
2553 ftype derivRoots[N - 1];
2554 int nd = PolynomialRoots<N - 1, ftype, boundError, RootFinder>(
2560 ftype x[N + 1] = {x0};
2561 ftype y[N + 1] = {y0};
2562 for (
int i = 0; i < nd; ++i)
2564 x[i + 1] = derivRoots[i];
2565 y[i + 1] = PolynomialEval<N, ftype>(coef, derivRoots[i]);
2568 y[nd + 1] = PolynomialEval<N, ftype>(coef, x1);
2570 for (
int i = 0; i <= nd; ++i)
2572 if (IsDifferentSign(y[i], y[i + 1]))
2574 roots[nr++] = RootFinder::template FindClosed<N, ftype, boundError>(
2590template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2591inline int PolynomialRoots(ftype roots[N], ftype
const coef[N + 1], ftype xError)
2593 if constexpr (N == 1)
2594 return LinearRoot<ftype>(*roots, coef);
2595 else if constexpr (N == 2)
2596 return QuadraticRoots<ftype>(roots, coef);
2597 else if constexpr (N == 3)
2598 return CubicRoots<ftype, boundError, RootFinder>(roots, coef, xError);
2599 else if (coef[N] == 0)
2600 return PolynomialRoots<N - 1, ftype, boundError, RootFinder>(roots, coef, xError);
2604 PolynomialDerivative<N, ftype>(deriv, coef);
2605 ftype derivRoots[N - 1];
2606 int nd = PolynomialRoots<N - 1, ftype, boundError, RootFinder>(derivRoots, deriv, xError);
2607 if ((N & 1) || ((N & 1) == 0 && nd > 0))
2610 ftype xa = derivRoots[0];
2611 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2612 if (IsDifferentSign(coef[N], ya) != (N & 1))
2614 roots[0] = RootFinder::template FindOpenMin<N, ftype, boundError>(
2622 for (
int i = 1; i < nd; ++i)
2624 ftype xb = derivRoots[i];
2625 ftype yb = PolynomialEval<N, ftype>(coef, xb);
2626 if (IsDifferentSign(ya, yb))
2628 roots[nr++] = RootFinder::template FindClosed<N, ftype, boundError>(
2640 if (IsDifferentSign(coef[N], ya))
2642 roots[nr++] = RootFinder::template FindOpenMax<N, ftype, boundError>(
2653 if constexpr (N & 1)
2655 roots[0] = RootFinder::template FindOpen<N, ftype, boundError>(coef, deriv, xError);
2666template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2668PolynomialFirstRoot(ftype& root, ftype
const coef[N + 1], ftype x0, ftype x1, ftype xError)
2670 if constexpr (N == 1)
2671 return LinearRoot<ftype>(root, coef, x0, x1);
2672 if constexpr (N == 2)
2673 return QuadraticFirstRoot<ftype>(root, coef, x0, x1);
2674 else if constexpr (N == 3)
2675 return CubicFirstRoot<ftype, boundError, RootFinder>(root, coef, x0, x1, xError);
2676 else if (coef[N] == 0)
2677 return PolynomialFirstRoot<N - 1, ftype, boundError, RootFinder>(
2685 ftype y0 = PolynomialEval<N, ftype>(coef, x0);
2687 PolynomialDerivative<N, ftype>(deriv, coef);
2688 bool done = PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2690 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2691 if (IsDifferentSign(y0, ya))
2693 root = RootFinder::template FindClosed<N, ftype, boundError>(
2715 ftype y1 = PolynomialEval<N, ftype>(coef, x1);
2716 if (IsDifferentSign(y0, y1))
2718 root = RootFinder::template FindClosed<N, ftype, boundError>(
2736template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2737inline bool PolynomialFirstRoot(ftype& root, ftype
const coef[N + 1], ftype xError)
2739 if constexpr (N == 1)
2740 return LinearRoot<ftype>(root, coef);
2741 if constexpr (N == 2)
2742 return QuadraticFirstRoot<ftype>(root, coef);
2743 else if constexpr (N == 3)
2744 return CubicFirstRoot<ftype, boundError, RootFinder>(root, coef, xError);
2745 else if (coef[N] == 0)
2746 return PolynomialFirstRoot<N - 1, ftype, boundError, RootFinder>(root, coef, xError);
2749 ftype x0 = -std::numeric_limits<ftype>::infinity();
2750 ftype y0 = (((N & 1) == 0) ^ (coef[N] < 0)) ? std::numeric_limits<ftype>::infinity() :
2751 -std::numeric_limits<ftype>::infinity();
2752 bool firstInterval =
true;
2754 PolynomialDerivative<N, ftype>(deriv, coef);
2755 bool done = PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2757 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2758 if (IsDifferentSign(y0, ya))
2762 root = RootFinder::template FindOpenMin<N, ftype, boundError>(
2771 root = RootFinder::template FindClosed<N, ftype, boundError>(
2782 firstInterval =
false;
2793 if constexpr ((N & 1) == 1)
2797 root = RootFinder::template FindOpen<N, ftype, boundError>(coef, deriv, xError);
2801 root = RootFinder::template FindOpenMax<N, ftype, boundError>(
2818template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2819inline bool PolynomialHasRoot(ftype
const coef[N + 1], ftype x0, ftype x1, ftype xError)
2821 if constexpr (N == 2)
2822 return QuadraticHasRoot<ftype>(coef, x0, x1);
2823 else if constexpr (N == 3)
2824 return CubicHasRoot<ftype>(coef, x0, x1);
2825 else if (coef[N] == 0)
2826 return PolynomialHasRoot<N - 1, ftype, boundError, RootFinder>(coef, x0, x1, xError);
2829 ftype y0 = PolynomialEval<N, ftype>(coef, x0);
2830 ftype y1 = PolynomialEval<N, ftype>(coef, x1);
2831 if (IsDifferentSign(y0, y1))
2833 [[maybe_unused]]
bool foundRoot =
false;
2835 PolynomialDerivative<N, ftype>(deriv, coef);
2836 return PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2838 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2839 return (IsDifferentSign(y0, ya));
2850template <
int N,
typename ftype,
bool boundError,
typename RootFinder>
2851inline bool PolynomialHasRoot(ftype
const coef[N + 1], ftype xError)
2853 if constexpr (N == 2)
2854 return QuadraticHasRoot<ftype>(coef);
2855 else if constexpr (N == 3)
2856 return CubicHasRoot<ftype>(coef);
2857 else if (coef[N] == 0)
2858 return PolynomialHasRoot<N - 1, ftype, boundError, RootFinder>(coef);
2859 else if constexpr ((N & 1) == 1)
2863 ftype y0 = (coef[N] < 0) ? -std::numeric_limits<ftype>::infinity() :
2864 std::numeric_limits<ftype>::infinity();
2866 PolynomialDerivative<N, ftype>(deriv, coef);
2867 return PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2869 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2870 if (IsDifferentSign(y0, ya))
2881template <
int N,
typename ftype,
bool boundError,
typename RootFinder,
typename RootCallback>
2882inline bool PolynomialForEachRoot(
2883 RootCallback callback,
2884 ftype
const coef[N + 1],
2889 if constexpr (N == 2)
2890 return QuadraticForEachRoot<ftype, RootCallback>(callback, coef, x0, x1);
2891 else if constexpr (N == 3)
2892 return CubicForEachRoot<ftype, boundError, RootFinder, RootCallback>(
2898 else if (coef[N] == 0)
2899 return PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder, RootCallback>(
2907 ftype y0 = PolynomialEval<N, ftype>(coef, x0);
2909 PolynomialDerivative<N, ftype>(deriv, coef);
2910 bool done = PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2912 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2913 if (IsDifferentSign(y0, ya))
2916 root = RootFinder::template FindClosed<N, ftype, boundError>(
2939 ftype y1 = PolynomialEval<N, ftype>(coef, x1);
2940 if (IsDifferentSign(y0, y1))
2942 return callback(RootFinder::template FindClosed<N, ftype, boundError>(
2958template <
int N,
typename ftype,
bool boundError,
typename RootFinder,
typename RootCallback>
2959inline bool PolynomialForEachRoot(RootCallback callback, ftype
const coef[N + 1], ftype xError)
2961 if constexpr (N == 2)
2962 return QuadraticForEachRoot<ftype, RootCallback>(callback, coef);
2963 else if constexpr (N == 3)
2964 return CubicForEachRoot<ftype, boundError, RootFinder, RootCallback>(
2968 else if (coef[N] == 0)
2969 return PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder, RootCallback>(
2975 ftype x0 = -std::numeric_limits<ftype>::infinity();
2976 ftype y0 = (((N & 1) == 0) ^ (coef[N] < 0)) ? std::numeric_limits<ftype>::infinity() :
2977 -std::numeric_limits<ftype>::infinity();
2978 bool firstInterval =
true;
2980 PolynomialDerivative<N, ftype>(deriv, coef);
2981 bool done = PolynomialForEachRoot<N - 1, ftype, boundError, RootFinder>(
2983 ftype ya = PolynomialEval<N, ftype>(coef, xa);
2984 if (IsDifferentSign(y0, ya))
2989 root = RootFinder::template FindOpenMin<N, ftype, boundError>(
2998 root = RootFinder::template FindClosed<N, ftype, boundError>(
3010 firstInterval =
false;
3021 if (IsDifferentSign(y0, coef[N]))
3024 if constexpr ((N & 1) == 1)
3028 root = RootFinder::template FindOpen<N, ftype, boundError>(
3035 root = RootFinder::template FindOpenMax<N, ftype, boundError>(
3045 root = RootFinder::template FindOpenMax<N, ftype, boundError>(
3052 return callback(root);
3068#include "pbat/warning/Pop.h"
3075namespace pbat::math::polynomial {
3078template <
class T, auto N>
3093template <auto N,
class TScalar = Scalar>
3095 std::array<TScalar, N + 1>
const& coeffs,
3096 TScalar min = std::numeric_limits<TScalar>::lowest(),
3097 TScalar max = std::numeric_limits<TScalar>::max())
3099 return detail::cy::PolynomialHasRoot<N, TScalar, true>(
3117template <auto N,
class TScalar = Scalar>
3119 std::array<TScalar, N + 1>
const& coeffs,
3120 TScalar min = std::numeric_limits<TScalar>::lowest(),
3121 TScalar max = std::numeric_limits<TScalar>::max())
3123 std::array<TScalar, N> roots;
3124 roots.fill(std::numeric_limits<TScalar>::quiet_NaN());
3125 [[maybe_unused]]
int const nRoots = detail::cy::PolynomialRoots<N, TScalar, true>(
3152template <auto N,
class FOnRoot,
class TScalar = Scalar>
3155 std::array<TScalar, N + 1>
const& coeffs,
3156 TScalar min = std::numeric_limits<TScalar>::lowest(),
3157 TScalar max = std::numeric_limits<TScalar>::max())
3159 return detail::cy::PolynomialForEachRoot<N, TScalar, true>(
3160 std::forward<FOnRoot>(fOnRoot),
T[N] CArray
C-style array alias.
Definition Roots.h:3079
TScalar sign(TScalar x)
Sign function, i.e. .
Definition Primitive.h:32
std::array< TScalar, N > Roots(std::array< TScalar, N+1 > const &coeffs, TScalar min=std::numeric_limits< TScalar >::lowest(), TScalar max=std::numeric_limits< TScalar >::max())
Computes all real roots of a degree N polynomial in the range [min,max].
Definition Roots.h:3118
bool HasRoot(std::array< TScalar, N+1 > const &coeffs, TScalar min=std::numeric_limits< TScalar >::lowest(), TScalar max=std::numeric_limits< TScalar >::max())
Check if a polynomial has a root in the range [min,max].
Definition Roots.h:3094
bool ForEachRoot(FOnRoot &&fOnRoot, std::array< TScalar, N+1 > const &coeffs, TScalar min=std::numeric_limits< TScalar >::lowest(), TScalar max=std::numeric_limits< TScalar >::max())
Computes the each real root of a degree N polynomial in the range [min,max] in increasing order.
Definition Roots.h:3153