QuarticRealPolynomial.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import CubicRealPolynomial from "./CubicRealPolynomial.js";
  2. import DeveloperError from "./DeveloperError.js";
  3. import CesiumMath from "./Math.js";
  4. import QuadraticRealPolynomial from "./QuadraticRealPolynomial.js";
  5. /**
  6. * Defines functions for 4th order polynomial functions of one variable with only real coefficients.
  7. *
  8. * @namespace QuarticRealPolynomial
  9. */
  10. const QuarticRealPolynomial = {};
  11. /**
  12. * Provides the discriminant of the quartic equation from the supplied coefficients.
  13. *
  14. * @param {number} a The coefficient of the 4th order monomial.
  15. * @param {number} b The coefficient of the 3rd order monomial.
  16. * @param {number} c The coefficient of the 2nd order monomial.
  17. * @param {number} d The coefficient of the 1st order monomial.
  18. * @param {number} e The coefficient of the 0th order monomial.
  19. * @returns {number} The value of the discriminant.
  20. */
  21. QuarticRealPolynomial.computeDiscriminant = function (a, b, c, d, e) {
  22. //>>includeStart('debug', pragmas.debug);
  23. if (typeof a !== "number") {
  24. throw new DeveloperError("a is a required number.");
  25. }
  26. if (typeof b !== "number") {
  27. throw new DeveloperError("b is a required number.");
  28. }
  29. if (typeof c !== "number") {
  30. throw new DeveloperError("c is a required number.");
  31. }
  32. if (typeof d !== "number") {
  33. throw new DeveloperError("d is a required number.");
  34. }
  35. if (typeof e !== "number") {
  36. throw new DeveloperError("e is a required number.");
  37. }
  38. //>>includeEnd('debug');
  39. const a2 = a * a;
  40. const a3 = a2 * a;
  41. const b2 = b * b;
  42. const b3 = b2 * b;
  43. const c2 = c * c;
  44. const c3 = c2 * c;
  45. const d2 = d * d;
  46. const d3 = d2 * d;
  47. const e2 = e * e;
  48. const e3 = e2 * e;
  49. const discriminant =
  50. b2 * c2 * d2 -
  51. 4.0 * b3 * d3 -
  52. 4.0 * a * c3 * d2 +
  53. 18 * a * b * c * d3 -
  54. 27.0 * a2 * d2 * d2 +
  55. 256.0 * a3 * e3 +
  56. e *
  57. (18.0 * b3 * c * d -
  58. 4.0 * b2 * c3 +
  59. 16.0 * a * c2 * c2 -
  60. 80.0 * a * b * c2 * d -
  61. 6.0 * a * b2 * d2 +
  62. 144.0 * a2 * c * d2) +
  63. e2 *
  64. (144.0 * a * b2 * c -
  65. 27.0 * b2 * b2 -
  66. 128.0 * a2 * c2 -
  67. 192.0 * a2 * b * d);
  68. return discriminant;
  69. };
  70. function original(a3, a2, a1, a0) {
  71. const a3Squared = a3 * a3;
  72. const p = a2 - (3.0 * a3Squared) / 8.0;
  73. const q = a1 - (a2 * a3) / 2.0 + (a3Squared * a3) / 8.0;
  74. const r =
  75. a0 -
  76. (a1 * a3) / 4.0 +
  77. (a2 * a3Squared) / 16.0 -
  78. (3.0 * a3Squared * a3Squared) / 256.0;
  79. // Find the roots of the cubic equations: h^6 + 2 p h^4 + (p^2 - 4 r) h^2 - q^2 = 0.
  80. const cubicRoots = CubicRealPolynomial.computeRealRoots(
  81. 1.0,
  82. 2.0 * p,
  83. p * p - 4.0 * r,
  84. -q * q
  85. );
  86. if (cubicRoots.length > 0) {
  87. const temp = -a3 / 4.0;
  88. // Use the largest positive root.
  89. const hSquared = cubicRoots[cubicRoots.length - 1];
  90. if (Math.abs(hSquared) < CesiumMath.EPSILON14) {
  91. // y^4 + p y^2 + r = 0.
  92. const roots = QuadraticRealPolynomial.computeRealRoots(1.0, p, r);
  93. if (roots.length === 2) {
  94. const root0 = roots[0];
  95. const root1 = roots[1];
  96. let y;
  97. if (root0 >= 0.0 && root1 >= 0.0) {
  98. const y0 = Math.sqrt(root0);
  99. const y1 = Math.sqrt(root1);
  100. return [temp - y1, temp - y0, temp + y0, temp + y1];
  101. } else if (root0 >= 0.0 && root1 < 0.0) {
  102. y = Math.sqrt(root0);
  103. return [temp - y, temp + y];
  104. } else if (root0 < 0.0 && root1 >= 0.0) {
  105. y = Math.sqrt(root1);
  106. return [temp - y, temp + y];
  107. }
  108. }
  109. return [];
  110. } else if (hSquared > 0.0) {
  111. const h = Math.sqrt(hSquared);
  112. const m = (p + hSquared - q / h) / 2.0;
  113. const n = (p + hSquared + q / h) / 2.0;
  114. // Now solve the two quadratic factors: (y^2 + h y + m)(y^2 - h y + n);
  115. const roots1 = QuadraticRealPolynomial.computeRealRoots(1.0, h, m);
  116. const roots2 = QuadraticRealPolynomial.computeRealRoots(1.0, -h, n);
  117. if (roots1.length !== 0) {
  118. roots1[0] += temp;
  119. roots1[1] += temp;
  120. if (roots2.length !== 0) {
  121. roots2[0] += temp;
  122. roots2[1] += temp;
  123. if (roots1[1] <= roots2[0]) {
  124. return [roots1[0], roots1[1], roots2[0], roots2[1]];
  125. } else if (roots2[1] <= roots1[0]) {
  126. return [roots2[0], roots2[1], roots1[0], roots1[1]];
  127. } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) {
  128. return [roots2[0], roots1[0], roots1[1], roots2[1]];
  129. } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) {
  130. return [roots1[0], roots2[0], roots2[1], roots1[1]];
  131. } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) {
  132. return [roots2[0], roots1[0], roots2[1], roots1[1]];
  133. }
  134. return [roots1[0], roots2[0], roots1[1], roots2[1]];
  135. }
  136. return roots1;
  137. }
  138. if (roots2.length !== 0) {
  139. roots2[0] += temp;
  140. roots2[1] += temp;
  141. return roots2;
  142. }
  143. return [];
  144. }
  145. }
  146. return [];
  147. }
  148. function neumark(a3, a2, a1, a0) {
  149. const a1Squared = a1 * a1;
  150. const a2Squared = a2 * a2;
  151. const a3Squared = a3 * a3;
  152. const p = -2.0 * a2;
  153. const q = a1 * a3 + a2Squared - 4.0 * a0;
  154. const r = a3Squared * a0 - a1 * a2 * a3 + a1Squared;
  155. const cubicRoots = CubicRealPolynomial.computeRealRoots(1.0, p, q, r);
  156. if (cubicRoots.length > 0) {
  157. // Use the most positive root
  158. const y = cubicRoots[0];
  159. const temp = a2 - y;
  160. const tempSquared = temp * temp;
  161. const g1 = a3 / 2.0;
  162. const h1 = temp / 2.0;
  163. const m = tempSquared - 4.0 * a0;
  164. const mError = tempSquared + 4.0 * Math.abs(a0);
  165. const n = a3Squared - 4.0 * y;
  166. const nError = a3Squared + 4.0 * Math.abs(y);
  167. let g2;
  168. let h2;
  169. if (y < 0.0 || m * nError < n * mError) {
  170. const squareRootOfN = Math.sqrt(n);
  171. g2 = squareRootOfN / 2.0;
  172. h2 = squareRootOfN === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfN;
  173. } else {
  174. const squareRootOfM = Math.sqrt(m);
  175. g2 = squareRootOfM === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfM;
  176. h2 = squareRootOfM / 2.0;
  177. }
  178. let G;
  179. let g;
  180. if (g1 === 0.0 && g2 === 0.0) {
  181. G = 0.0;
  182. g = 0.0;
  183. } else if (CesiumMath.sign(g1) === CesiumMath.sign(g2)) {
  184. G = g1 + g2;
  185. g = y / G;
  186. } else {
  187. g = g1 - g2;
  188. G = y / g;
  189. }
  190. let H;
  191. let h;
  192. if (h1 === 0.0 && h2 === 0.0) {
  193. H = 0.0;
  194. h = 0.0;
  195. } else if (CesiumMath.sign(h1) === CesiumMath.sign(h2)) {
  196. H = h1 + h2;
  197. h = a0 / H;
  198. } else {
  199. h = h1 - h2;
  200. H = a0 / h;
  201. }
  202. // Now solve the two quadratic factors: (y^2 + G y + H)(y^2 + g y + h);
  203. const roots1 = QuadraticRealPolynomial.computeRealRoots(1.0, G, H);
  204. const roots2 = QuadraticRealPolynomial.computeRealRoots(1.0, g, h);
  205. if (roots1.length !== 0) {
  206. if (roots2.length !== 0) {
  207. if (roots1[1] <= roots2[0]) {
  208. return [roots1[0], roots1[1], roots2[0], roots2[1]];
  209. } else if (roots2[1] <= roots1[0]) {
  210. return [roots2[0], roots2[1], roots1[0], roots1[1]];
  211. } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) {
  212. return [roots2[0], roots1[0], roots1[1], roots2[1]];
  213. } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) {
  214. return [roots1[0], roots2[0], roots2[1], roots1[1]];
  215. } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) {
  216. return [roots2[0], roots1[0], roots2[1], roots1[1]];
  217. }
  218. return [roots1[0], roots2[0], roots1[1], roots2[1]];
  219. }
  220. return roots1;
  221. }
  222. if (roots2.length !== 0) {
  223. return roots2;
  224. }
  225. }
  226. return [];
  227. }
  228. /**
  229. * Provides the real valued roots of the quartic polynomial with the provided coefficients.
  230. *
  231. * @param {number} a The coefficient of the 4th order monomial.
  232. * @param {number} b The coefficient of the 3rd order monomial.
  233. * @param {number} c The coefficient of the 2nd order monomial.
  234. * @param {number} d The coefficient of the 1st order monomial.
  235. * @param {number} e The coefficient of the 0th order monomial.
  236. * @returns {number[]} The real valued roots.
  237. */
  238. QuarticRealPolynomial.computeRealRoots = function (a, b, c, d, e) {
  239. //>>includeStart('debug', pragmas.debug);
  240. if (typeof a !== "number") {
  241. throw new DeveloperError("a is a required number.");
  242. }
  243. if (typeof b !== "number") {
  244. throw new DeveloperError("b is a required number.");
  245. }
  246. if (typeof c !== "number") {
  247. throw new DeveloperError("c is a required number.");
  248. }
  249. if (typeof d !== "number") {
  250. throw new DeveloperError("d is a required number.");
  251. }
  252. if (typeof e !== "number") {
  253. throw new DeveloperError("e is a required number.");
  254. }
  255. //>>includeEnd('debug');
  256. if (Math.abs(a) < CesiumMath.EPSILON15) {
  257. return CubicRealPolynomial.computeRealRoots(b, c, d, e);
  258. }
  259. const a3 = b / a;
  260. const a2 = c / a;
  261. const a1 = d / a;
  262. const a0 = e / a;
  263. let k = a3 < 0.0 ? 1 : 0;
  264. k += a2 < 0.0 ? k + 1 : k;
  265. k += a1 < 0.0 ? k + 1 : k;
  266. k += a0 < 0.0 ? k + 1 : k;
  267. switch (k) {
  268. case 0:
  269. return original(a3, a2, a1, a0);
  270. case 1:
  271. return neumark(a3, a2, a1, a0);
  272. case 2:
  273. return neumark(a3, a2, a1, a0);
  274. case 3:
  275. return original(a3, a2, a1, a0);
  276. case 4:
  277. return original(a3, a2, a1, a0);
  278. case 5:
  279. return neumark(a3, a2, a1, a0);
  280. case 6:
  281. return original(a3, a2, a1, a0);
  282. case 7:
  283. return original(a3, a2, a1, a0);
  284. case 8:
  285. return neumark(a3, a2, a1, a0);
  286. case 9:
  287. return original(a3, a2, a1, a0);
  288. case 10:
  289. return original(a3, a2, a1, a0);
  290. case 11:
  291. return neumark(a3, a2, a1, a0);
  292. case 12:
  293. return original(a3, a2, a1, a0);
  294. case 13:
  295. return original(a3, a2, a1, a0);
  296. case 14:
  297. return original(a3, a2, a1, a0);
  298. case 15:
  299. return original(a3, a2, a1, a0);
  300. default:
  301. return undefined;
  302. }
  303. };
  304. export default QuarticRealPolynomial;