ImathRoots.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. //
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Copyright Contributors to the OpenEXR Project.
  4. //
  5. //
  6. // Functions to solve linear, quadratic or cubic equations
  7. //
  8. // Note: It is possible that an equation has real solutions, but that
  9. // the solutions (or some intermediate result) are not representable.
  10. // In this case, either some of the solutions returned are invalid
  11. // (nan or infinity), or, if floating-point exceptions have been
  12. // enabled, an exception is thrown.
  13. //
  14. #ifndef INCLUDED_IMATHROOTS_H
  15. #define INCLUDED_IMATHROOTS_H
  16. #include "ImathMath.h"
  17. #include "ImathNamespace.h"
  18. #include <complex>
  19. /// @cond Doxygen_Suppress
  20. #ifdef __CUDACC__
  21. # include <thrust/complex.h>
  22. # define COMPLEX_NAMESPACE thrust
  23. #else
  24. # define COMPLEX_NAMESPACE std
  25. #endif
  26. /// @endcond
  27. IMATH_INTERNAL_NAMESPACE_HEADER_ENTER
  28. ///
  29. /// Solve for x in the linear equation:
  30. ///
  31. /// a * x + b == 0
  32. ///
  33. /// @return 1 if the equation has a solution, 0 if there is no
  34. /// solution, and -1 if all real numbers are solutions.
  35. template <class T> IMATH_HOSTDEVICE IMATH_CONSTEXPR14 int solveLinear (T a, T b, T& x);
  36. ///
  37. /// Solve for x in the quadratic equation:
  38. ///
  39. /// a * x*x + b * x + c == 0
  40. ///
  41. /// @return 2 if the equation has two solutions, 1 if the equation has
  42. /// a single solution, 0 if there is no solution, and -1 if all real
  43. /// numbers are solutions.
  44. template <class T> IMATH_HOSTDEVICE IMATH_CONSTEXPR14 int solveQuadratic (T a, T b, T c, T x[2]);
  45. template <class T>
  46. ///
  47. /// Solve for x in the normalized cubic equation:
  48. ///
  49. /// x*x*x + r * x*x + s * x + t == 0
  50. ///
  51. /// The equation is solved using Cardano's Formula; even though only
  52. /// real solutions are produced, some intermediate results are complex
  53. /// (std::complex<T>).
  54. ///
  55. /// @return 0 if there is no solution, and -1 if all real
  56. /// numbers are solutions, otherwise return the number of solutions.
  57. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 int solveNormalizedCubic (T r, T s, T t, T x[3]);
  58. ///
  59. /// Solve for x in the cubic equation:
  60. ///
  61. /// a * x*x*x + b * x*x + c * x + d == 0
  62. ///
  63. /// The equation is solved using Cardano's Formula; even though only
  64. /// real solutions are produced, some intermediate results are complex
  65. /// (std::complex<T>).
  66. ///
  67. /// @return 0 if there is no solution, and -1 if all real
  68. /// numbers are solutions, otherwise return the number of solutions.
  69. template <class T> IMATH_HOSTDEVICE IMATH_CONSTEXPR14 int solveCubic (T a, T b, T c, T d, T x[3]);
  70. //---------------
  71. // Implementation
  72. //---------------
  73. template <class T>
  74. IMATH_CONSTEXPR14 int
  75. solveLinear (T a, T b, T& x)
  76. {
  77. if (a != 0)
  78. {
  79. x = -b / a;
  80. return 1;
  81. }
  82. else if (b != 0)
  83. {
  84. return 0;
  85. }
  86. else
  87. {
  88. return -1;
  89. }
  90. }
  91. template <class T>
  92. IMATH_CONSTEXPR14 int
  93. solveQuadratic (T a, T b, T c, T x[2])
  94. {
  95. if (a == 0)
  96. {
  97. return solveLinear (b, c, x[0]);
  98. }
  99. else
  100. {
  101. T D = b * b - 4 * a * c;
  102. if (D > 0)
  103. {
  104. T s = std::sqrt (D);
  105. T q = -(b + (b > 0 ? 1 : -1) * s) / T (2);
  106. x[0] = q / a;
  107. x[1] = c / q;
  108. return 2;
  109. }
  110. if (D == 0)
  111. {
  112. x[0] = -b / (2 * a);
  113. return 1;
  114. }
  115. else
  116. {
  117. return 0;
  118. }
  119. }
  120. }
  121. template <class T>
  122. IMATH_CONSTEXPR14 int
  123. solveNormalizedCubic (T r, T s, T t, T x[3])
  124. {
  125. T p = (3 * s - r * r) / 3;
  126. T q = 2 * r * r * r / 27 - r * s / 3 + t;
  127. T p3 = p / 3;
  128. T q2 = q / 2;
  129. T D = p3 * p3 * p3 + q2 * q2;
  130. if (D == 0 && p3 == 0)
  131. {
  132. x[0] = -r / 3;
  133. x[1] = -r / 3;
  134. x[2] = -r / 3;
  135. return 1;
  136. }
  137. if (D > 0)
  138. {
  139. auto real_root = [] (T a, T x) -> T {
  140. T sign = std::copysign(T(1), a);
  141. return sign * std::pow (sign * a, T (1) / x);
  142. };
  143. T u = real_root (-q / 2 + std::sqrt (D), 3);
  144. T v = -p / (T (3) * u);
  145. x[0] = u + v - r / 3;
  146. return 1;
  147. }
  148. namespace CN = COMPLEX_NAMESPACE;
  149. CN::complex<T> u = CN::pow (-q / 2 + CN::sqrt (CN::complex<T> (D)), T (1) / T (3));
  150. CN::complex<T> v = -p / (T (3) * u);
  151. const T sqrt3 = T (1.73205080756887729352744634150587); // enough digits
  152. // for long double
  153. CN::complex<T> y0 (u + v);
  154. CN::complex<T> y1 (-(u + v) / T (2) + (u - v) / T (2) * CN::complex<T> (0, sqrt3));
  155. CN::complex<T> y2 (-(u + v) / T (2) - (u - v) / T (2) * CN::complex<T> (0, sqrt3));
  156. if (D == 0)
  157. {
  158. x[0] = y0.real() - r / 3;
  159. x[1] = y1.real() - r / 3;
  160. return 2;
  161. }
  162. else
  163. {
  164. x[0] = y0.real() - r / 3;
  165. x[1] = y1.real() - r / 3;
  166. x[2] = y2.real() - r / 3;
  167. return 3;
  168. }
  169. }
  170. template <class T>
  171. IMATH_CONSTEXPR14 int
  172. solveCubic (T a, T b, T c, T d, T x[3])
  173. {
  174. if (a == 0)
  175. {
  176. return solveQuadratic (b, c, d, x);
  177. }
  178. else
  179. {
  180. return solveNormalizedCubic (b / a, c / a, d / a, x);
  181. }
  182. }
  183. IMATH_INTERNAL_NAMESPACE_HEADER_EXIT
  184. #endif // INCLUDED_IMATHROOTS_H