36#include "irplib_polynomial.h"
39#include "irplib_utils.h"
59static double irplib_polynomial_eval_2_max(
double,
66static double irplib_polynomial_eval_3_max(
double,
77irplib_polynomial_solve_1d_2(
double,
double,
double,
double *,
double *);
78static cpl_boolean irplib_polynomial_solve_1d_3(
double,
88static void irplib_polynomial_solve_1d_31(
double,
95static void irplib_polynomial_solve_1d_32(
double,
103static void irplib_polynomial_solve_1d_3r(
double,
111static void irplib_polynomial_solve_1d_3c(
double,
123static cpl_error_code irplib_polynomial_solve_1d_4(
double,
135irplib_polynomial_solve_1d_zero(cpl_polynomial *,
137 cpl_size *) CPL_ATTR_NONNULL;
140irplib_polynomial_solve_1d_nonzero(cpl_polynomial *,
142 cpl_size *) CPL_ATTR_NONNULL;
145irplib_polynomial_divide_1d_root(cpl_polynomial *,
double,
double *);
148irplib_polynomial_solve_1d_guess(
const cpl_polynomial *,
149 double *) CPL_ATTR_NONNULL;
151#ifdef IRPLIB_POLYNOMIAL_GUESS_ANASOL
152static double irplib_polynomial_depress_1d(cpl_polynomial *);
187irplib_polynomial_solve_1d_all(
const cpl_polynomial *self,
191 cpl_error_code error;
192 const cpl_size degree = cpl_polynomial_get_degree(self);
195 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
196 cpl_ensure_code(roots != NULL, CPL_ERROR_NULL_INPUT);
197 cpl_ensure_code(preal != NULL, CPL_ERROR_NULL_INPUT);
198 cpl_ensure_code(cpl_polynomial_get_dimension(self) == 1,
199 CPL_ERROR_INVALID_TYPE);
200 cpl_ensure_code(degree > 0, CPL_ERROR_DATA_NOT_FOUND);
201 cpl_ensure_code(degree == cpl_vector_get_size(roots),
202 CPL_ERROR_INCOMPATIBLE_INPUT);
206 p = cpl_polynomial_duplicate(self);
208 error = irplib_polynomial_solve_1d_zero(p, roots, preal);
210 if (!error && *preal < degree) {
214 const cpl_boolean dosort = *preal > 0 || degree - *preal > 4;
216 assert(cpl_polynomial_get_degree(p) + *preal == degree);
218 error = irplib_polynomial_solve_1d_nonzero(p, roots, preal);
220 if (!error && dosort) {
222 cpl_vector_wrap(*preal, cpl_vector_get_data(roots));
223 cpl_vector_sort(reals, CPL_SORT_ASCENDING);
224 (void)cpl_vector_unwrap(reals);
228 cpl_polynomial_delete(p);
230 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
248irplib_polynomial_solve_1d_zero(cpl_polynomial *self,
253 const cpl_size degree = cpl_polynomial_get_degree(self);
256 for (nzero = 0; nzero < degree; nzero++) {
257 if (cpl_polynomial_get_coeff(self, &nzero) != 0.0)
263 for (; i <= degree - nzero; i++) {
264 const cpl_size icopy = i + nzero;
265 const double value = cpl_polynomial_get_coeff(self, &icopy);
267 if (cpl_polynomial_set_coeff(self, &i, value))
268 return cpl_error_set_where(cpl_func);
270 for (; i <= degree; i++) {
271 if (cpl_polynomial_set_coeff(self, &i, 0.0))
272 return cpl_error_set_where(cpl_func);
273 cpl_vector_set(roots, (*preal)++, 0.0);
277 return CPL_ERROR_NONE;
297irplib_polynomial_solve_1d_nonzero(cpl_polynomial *self,
301 cpl_error_code error = CPL_ERROR_NONE;
302 const cpl_size ncoeffs = 1 + cpl_polynomial_get_degree(self);
305 cpl_ensure_code(ncoeffs > 1, CPL_ERROR_DATA_NOT_FOUND);
306 cpl_ensure_code(*preal >= 0, CPL_ERROR_ILLEGAL_INPUT);
307 cpl_ensure_code(ncoeffs + *preal == 1 + cpl_vector_get_size(roots),
308 CPL_ERROR_INCOMPATIBLE_INPUT);
312 const cpl_size i1 = 1;
313 const cpl_size i0 = 0;
314 const double p1 = cpl_polynomial_get_coeff(self, &i1);
315 const double p0 = cpl_polynomial_get_coeff(self, &i0);
319 cpl_vector_set(roots, (*preal)++, -p0 / p1);
323 const cpl_size i2 = 2;
324 const cpl_size i1 = 1;
325 const cpl_size i0 = 0;
326 const double p2 = cpl_polynomial_get_coeff(self, &i2);
327 const double p1 = cpl_polynomial_get_coeff(self, &i1);
328 const double p0 = cpl_polynomial_get_coeff(self, &i0);
333 if (irplib_polynomial_solve_1d_2(p2, p1, p0, &x1, &x2)) {
335 cpl_vector_set(roots, (*preal), x1);
336 cpl_vector_set(roots, (*preal) + 1, x2);
339 cpl_vector_set(roots, (*preal)++, x1);
340 cpl_vector_set(roots, (*preal)++, x2);
345 const cpl_size i3 = 3;
346 const cpl_size i2 = 2;
347 const cpl_size i1 = 1;
348 const cpl_size i0 = 0;
349 const double p3 = cpl_polynomial_get_coeff(self, &i3);
350 const double p2 = cpl_polynomial_get_coeff(self, &i2);
351 const double p1 = cpl_polynomial_get_coeff(self, &i1);
352 const double p0 = cpl_polynomial_get_coeff(self, &i0);
357 if (irplib_polynomial_solve_1d_3(p3, p2, p1, p0, &x1, &x2, &x3,
359 cpl_vector_set(roots, (*preal)++, x1);
361 cpl_vector_set(roots, (*preal), x2);
362 cpl_vector_set(roots, (*preal) + 1, x3);
365 cpl_vector_set(roots, (*preal)++, x1);
366 cpl_vector_set(roots, (*preal)++, x2);
367 cpl_vector_set(roots, (*preal)++, x3);
372 const cpl_size i4 = 4;
373 const cpl_size i3 = 3;
374 const cpl_size i2 = 2;
375 const cpl_size i1 = 1;
376 const cpl_size i0 = 0;
377 const double p4 = cpl_polynomial_get_coeff(self, &i4);
378 const double p3 = cpl_polynomial_get_coeff(self, &i3);
379 const double p2 = cpl_polynomial_get_coeff(self, &i2);
380 const double p1 = cpl_polynomial_get_coeff(self, &i1);
381 const double p0 = cpl_polynomial_get_coeff(self, &i0);
382 double x1, x2, x3, x4;
387 error = irplib_polynomial_solve_1d_4(p4, p3, p2, p1, p0, &nreal,
390 cpl_vector_set(roots, (*preal), x1);
391 cpl_vector_set(roots, (*preal) + 1, x2);
392 cpl_vector_set(roots, (*preal) + 2, x3);
393 cpl_vector_set(roots, (*preal) + 3, x4);
404 error = irplib_polynomial_solve_1d_guess(self, &root);
407 cpl_vector_set(roots, (*preal)++, root);
409 irplib_polynomial_divide_1d_root(self, root, NULL);
411 error = irplib_polynomial_solve_1d_nonzero(self, roots, preal);
418 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
433irplib_polynomial_solve_1d_guess(
const cpl_polynomial *self,
double *proot)
435 cpl_errorstate prestate = cpl_errorstate_get();
436 cpl_error_code error = CPL_ERROR_NONE;
437 const cpl_size degree = cpl_polynomial_get_degree(self);
438 const cpl_size ncand = 5;
441 size_t ipos = 0, ineg = 0;
442 double rpos[1], rneg[1];
443 cpl_boolean do_bisect = CPL_FALSE;
450 for (itry = 0;; itry++) {
454 const double pn0 = cpl_polynomial_get_coeff(self, °ree);
455 const cpl_size n1 = degree - 1;
456 const double pn1 = cpl_polynomial_get_coeff(self, &n1);
461 rmean = -pn1 / (pn0 * (double)degree);
463 rcand[icand++] = rmean;
470 const cpl_size i0 = 0;
471 const double c0 = cpl_polynomial_get_coeff(self, &i0);
476 rmean = pow(fabs(c0), 1.0 / (
double)degree);
478 rcand[icand++] = rmean;
485 const cpl_size i0 = 0;
486 const double c0 = cpl_polynomial_get_coeff(self, &i0);
487 const double pn0 = cpl_polynomial_get_coeff(self, °ree);
488 const cpl_size n1 = degree - 1;
489 const double pn1 = cpl_polynomial_get_coeff(self, &n1);
494 rmean = -pn1 / (pn0 * (double)degree);
496 rcand[icand++] = rmean + c0;
497 rcand[icand++] = rmean - c0;
502#ifdef IRPLIB_POLYNOMIAL_GUESS_ANASOL
505 cpl_polynomial *copy = cpl_polynomial_duplicate(self);
506 const cpl_size i0 = 0;
507 const double rmean = irplib_polynomial_depress_1d(copy);
508 const double c0 = cpl_polynomial_get_coeff(copy, &i0);
509 const double radius = pow(fabs(c0), 1.0 / (
double)degree);
511 rcand[icand++] = rmean + radius;
513 rcand[icand++] = rmean - radius;
515 cpl_polynomial_delete(copy);
524 if (ipos > 0 && ineg > 0) {
525 rcand[icand++] = 0.5 * (rpos[0] + rneg[0]);
526 do_bisect = CPL_TRUE;
533 double root = rcand[--icand];
534 const double resid = cpl_polynomial_eval_1d(self, root, &grad);
536#ifdef IRPLIB_POLYNOMIAL_DEBUG
538 cpl_msg_warning(cpl_func,
"RETRY(%d)=%g, degree=%d, r=%g, d=%g",
539 (
int)itry, root, (
int)degree, resid, grad);
542 error = cpl_polynomial_solve_1d(self, root, proot, 1);
544 cpl_errorstate_set(prestate);
549 *(resid > 0.0 ? rpos : rneg) = root;
557 else if (ineg == 0) {
561 if (ipos == 0 || ineg == 0) {
562 const double resid2 =
563 cpl_polynomial_eval_1d(self, *proot, &grad);
566 rpos[ipos++] = *proot;
568 else if (ineg == 0) {
569 rneg[ineg++] = *proot;
579 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
598irplib_polynomial_solve_1d_2(
double p2,
605 sqrt(p1 * p1 < 4.0 * p2 * p0 ? 4.0 * p2 * p0 - p1 * p1
606 : p1 * p1 - 4.0 * p2 * p0);
607 cpl_boolean is_complex = CPL_FALSE;
608 double x1 = -0.5 * p1 / p2;
612 double res0 = irplib_polynomial_eval_2_max(p2, p1, p0, CPL_FALSE, x1, x1);
624 x1 = -0.5 * (p1 + sqrtD);
627 x1 = -0.5 * (p1 - sqrtD);
634 res = irplib_polynomial_eval_2_max(p2, p1, p0, CPL_FALSE, x1, x2);
651 x2 = 0.5 * sqrtD / fabs(p2);
653 res = irplib_polynomial_eval_2_max(p2, p1, p0, CPL_TRUE, x1, x2);
658 is_complex = CPL_TRUE;
680irplib_polynomial_eval_2_max(
double p2,
690 res = fabs(p0 + x1 * (p1 + x1 * p2) - p2 * x2 * x2);
693 const double r1 = fabs(p0 + x1 * (p1 + x1 * p2));
694 const double r2 = fabs(p0 + x2 * (p1 + x2 * p2));
696 res = r1 > r2 ? r1 : r2;
720irplib_polynomial_eval_3_max(
double p3,
729 const double r1 = fabs(p0 + x1 * (p1 + x1 * (p2 + x1 * p3)));
733 const double r2 = fabs(p0 + x2 * (p1 + x2 * (p2 + x2 * p3)) -
734 x3 * x3 * (3.0 * p3 * x2 + p2));
736 res = r1 > r2 ? r1 : r2;
739 const double r2 = fabs(p0 + x2 * (p1 + x2 * (p2 + x2 * p3)));
740 const double r3 = fabs(p0 + x3 * (p1 + x3 * (p2 + x3 * p3)));
741 res = r1 > r2 ? (r1 > r3 ? r1 : r3) : (r2 > r3 ? r2 : r3);
771irplib_polynomial_solve_1d_3(
double p3,
781 cpl_boolean is_complex = CPL_FALSE;
782 const double a = p2 / p3;
783 const double b = p1 / p3;
784 const double c = p0 / p3;
786 const double q = (a * a - 3.0 * b);
787 const double r = (a * (2.0 * a * a - 9.0 * b) + 27.0 * c);
789 const double Q = q / 9.0;
790 const double R = r / 54.0;
792 const double Q3 = Q * Q * Q;
793 const double R2 = R * R;
798 double xx1 = DBL_MAX;
799 double xx2 = DBL_MAX;
800 double xx3 = DBL_MAX;
802 double res = DBL_MAX;
803 cpl_boolean is_first = CPL_TRUE;
825 if ((R2 >= Q3 && R != 0.0) || R2 > Q3) {
826 cpl_boolean is_c = CPL_FALSE;
829 irplib_polynomial_solve_1d_3c(a, c, Q, Q3, R, R2, &x1, &x2, &x3, &is_c,
834 irplib_polynomial_eval_3_max(p3, p2, p1, p0, is_c, x1, x2, x3);
836 is_first = CPL_FALSE;
840 if (!is_c && pdbl2 != NULL)
845 if (Q > 0.0 && fabs(R / (Q * sqrt(Q))) <= 1.0) {
853 irplib_polynomial_solve_1d_3r(a, c, Q, R, &xx1, &xx2, &xx3);
855 resx = irplib_polynomial_eval_3_max(p3, p2, p1, p0, CPL_FALSE, xx1, xx2,
858 if (is_first || (dbl2 ? resx < res : resx <= res)) {
859 is_first = CPL_FALSE;
868 is_complex = CPL_FALSE;
873 cpl_boolean dbl1 = CPL_FALSE;
877 irplib_polynomial_solve_1d_32(a, c, Q, &xx1, &xx2, &xx3, &dbl2);
879 resx = irplib_polynomial_eval_3_max(p3, p2, p1, p0, CPL_FALSE, xx1, xx2,
886 if (is_first || resx <= res) {
896 is_complex = CPL_FALSE;
903 irplib_polynomial_solve_1d_31(a, Q, &xx1, &xx2, &xx3, &dbl1);
905 resx = irplib_polynomial_eval_3_max(p3, p2, p1, p0, CPL_FALSE, xx1, xx2,
918 is_complex = CPL_FALSE;
922 if (px2 != NULL && px3 != NULL) {
927 else if (is_complex) {
953irplib_polynomial_solve_1d_31(
double a,
960 const double sqrtQ = sqrt(Q);
964 x2 = x1 = -sqrtQ - a / 3.0;
965 x3 = 2.0 * sqrtQ - a / 3.0;
993irplib_polynomial_solve_1d_32(
double a,
1001 const double sqrtQ = sqrt(Q);
1003 double x1 = DBL_MAX;
1004 double x2 = DBL_MAX;
1005 double x3 = DBL_MAX;
1009 x1 = -2.0 * sqrtQ - a / 3.0;
1012 x3 = x2 = -a < x1 ? -sqrt(fabs(c / x1)) : sqrt(fabs(c / x1));
1018 x3 = x2 = sqrtQ - a / 3.0;
1019 x1 = -c / (x2 * x2);
1059irplib_polynomial_solve_1d_3c(
double a,
1079 const double sgnR = (R >= 0 ? 1.0 : -1.0);
1080 const double A = -sgnR * pow(fabs(R) + sqrt(R2 - Q3), 1.0 / 3.0);
1081 const double B = Q / A;
1083 double x1 = DBL_MAX;
1084 double x2 = DBL_MAX;
1085 double x3 = DBL_MAX;
1086 cpl_boolean is_complex = CPL_FALSE;
1088 if ((A > -B && a > 0.0) || (A < -B && a < 0.0)) {
1092 x2 = -0.5 * (A + B) - a / 3.0;
1094 x3 = 0.5 * CPL_MATH_SQRT3 * fabs(A - B);
1096 x1 = -c / (x2 * x2 + x3 * x3);
1100 x1 = A + B - a / 3.0;
1102 x3 = 0.5 * CPL_MATH_SQRT3 * fabs(A - B);
1106 x2 = -0.5 * (A + B) - a / 3.0;
1109 x2 = -a < x1 ? -sqrt(fabs(c / x1)) : sqrt(fabs(c / x1));
1115 is_complex = CPL_TRUE;
1128 *pis_c = is_complex;
1150irplib_polynomial_solve_1d_3r(
double a,
1158 const double sqrtQ = sqrt(Q);
1159 const double theta = acos(R / (Q * sqrtQ));
1165#define TR1 (-2.0 * sqrtQ * cos(theta / 3.0))
1166#define TR2 (-2.0 * sqrtQ * cos((theta - CPL_MATH_2PI) / 3.0))
1167#define TR3 (-2.0 * sqrtQ * cos((theta + CPL_MATH_2PI) / 3.0))
1176 double x1 = DBL_MAX;
1177 double x2 = DBL_MAX;
1178 double x3 = DBL_MAX;
1182 if (TR2 > 0.0 && (TR2 + TR3) > 2.0 * a) {
1185 x2 = -c / (x1 * x3);
1193 x3 = -c / (x1 * x2);
1198 if (TR2 < 0.0 && (TR1 + TR2) > 2.0 * a) {
1200 x2 = -c / (x1 * x3);
1204 x1 = -c / (x2 * x3);
1223 x1 = x2 = 0.5 * (x1 + x2);
1233 x3 = x2 = 0.5 * (x2 + x3);
1262static cpl_error_code
1263irplib_polynomial_solve_1d_4(
double p4,
1275 const double a = (p2 - 0.375 * p3 * p3 / p4) / p4;
1276 const double b = (p1 - 0.5 * (p2 - 0.25 * p3 * p3 / p4) * p3 / p4) / p4;
1278 (p0 - 0.25 * (p1 - 0.25 * (p2 - 0.1875 * p3 * p3 / p4) * p3 / p4) * p3 /
1282 double x1 = DBL_MAX;
1283 double x2 = DBL_MAX;
1284 double x3 = DBL_MAX;
1285 double x4 = DBL_MAX;
1287 assert(preal != NULL);
1288 assert(px1 != NULL);
1289 assert(px2 != NULL);
1290 assert(px3 != NULL);
1291 assert(px4 != NULL);
1299 cpl_boolean dbl1, dbl2;
1300 const cpl_boolean is_real =
1301 !irplib_polynomial_solve_1d_3(1.0, 0.0, a, b, &x1, &x3, &x4, &dbl1,
1304 x1 -= 0.25 * p3 / p4;
1305 x2 = -0.25 * p3 / p4;
1306 x3 -= 0.25 * p3 / p4;
1314 x4 -= 0.25 * p3 / p4;
1317 IRPLIB_SWAP_DOUBLE(x2, x3);
1336 IRPLIB_SWAP_DOUBLE(x1, x2);
1343 else if (b == 0.0) {
1346 const cpl_boolean is_complex =
1347 irplib_polynomial_solve_1d_2(1.0, a, c, &u1, &u2);
1351 const double norm = sqrt(u1 * u1 + u2 * u2);
1352 const double v1 = sqrt(0.5 * (norm + u1));
1353 const double v2 = u2 / sqrt(2.0 * (norm + u1));
1356 x1 = -0.25 * p3 / p4 - v1;
1357 x3 = -0.25 * p3 / p4 + v1;
1363 else if (u1 >= 0.0) {
1365 const double sv1 = sqrt(u1);
1366 const double sv2 = sqrt(u2);
1371 x1 = -0.25 * p3 / p4 - sv2;
1372 x2 = -0.25 * p3 / p4 - sv1;
1373 x3 = -0.25 * p3 / p4 + sv1;
1374 x4 = -0.25 * p3 / p4 + sv2;
1376 else if (u2 < 0.0) {
1378 const double sv1 = sqrt(-u2);
1379 const double sv2 = sqrt(-u1);
1384 x1 = x3 = -0.25 * p3 / p4;
1391 const double sv1 = sqrt(-u1);
1392 const double sv2 = sqrt(u2);
1397 x1 = -0.25 * p3 / p4 - sv2;
1398 x2 = -0.25 * p3 / p4 + sv2;
1400 x3 = -0.25 * p3 / p4;
1406 const double q2 = -a;
1407 const double q1 = -4.0 * c;
1408 const double q0 = 4.0 * a * c - b * b;
1409 double u1, sqrtd, sqrtrd;
1410 double z1, z2, z3, z4;
1412 cpl_boolean is_complex1, is_complex2;
1416 (void)irplib_polynomial_solve_1d_3(1.0, q2, q1, q0, &u1, NULL, NULL,
1422 sqrtd = sqrt(u1 - a);
1424 sqrtrd = 0.5 * b / sqrtd;
1426 is_complex1 = irplib_polynomial_solve_1d_2(1.0, sqrtd,
1427 0.5 * u1 - sqrtrd, &z1, &z2);
1429 is_complex2 = irplib_polynomial_solve_1d_2(1.0, -sqrtd,
1430 0.5 * u1 + sqrtrd, &z3, &z4);
1432 z1 -= 0.25 * p3 / p4;
1433 z3 -= 0.25 * p3 / p4;
1435 z2 -= 0.25 * p3 / p4;
1437 z4 -= 0.25 * p3 / p4;
1439 if (!is_complex1 && is_complex2) {
1446 else if (is_complex1 && !is_complex2) {
1453 else if (is_complex1 && is_complex2) {
1456 if (z1 < z3 || (z1 == z3 && z2 <= z4)) {
1478 else if (z4 <= z1) {
1504 return CPL_ERROR_NONE;
1507#ifdef IRPLIB_POLYNOMIAL_GUESS_ANASOL
1518irplib_polynomial_depress_1d(cpl_polynomial *self)
1520 const cpl_size degree = cpl_polynomial_get_degree(self);
1521 const cpl_size nc1 = degree - 1;
1522 const double an = cpl_polynomial_get_coeff(self, °ree);
1523 const double an1 = cpl_polynomial_get_coeff(self, &nc1);
1524 const double rmean = an != 0.0 ? -an1 / (an * (double)degree) : 0.0;
1527 cpl_ensure(degree > 0, CPL_ERROR_DATA_NOT_FOUND, 0.0);
1532 cpl_polynomial_shift_1d(self, 0, rmean);
1536 for (cpl_size i = 0; i < nc1; i++) {
1537 const double ai = cpl_polynomial_get_coeff(self, &i) / an;
1538 cpl_polynomial_set_coeff(self, &i, ai);
1541 cpl_polynomial_set_coeff(self, &nc1, 0.0);
1542 cpl_polynomial_set_coeff(self, °ree, 1.0);
1564static cpl_error_code
1565irplib_polynomial_divide_1d_root(cpl_polynomial *p,
double r,
double *pres)
1567 const cpl_size n = cpl_polynomial_get_degree(p);
1572 cpl_ensure_code(p != NULL, CPL_ERROR_NULL_INPUT);
1573 cpl_ensure_code(n > 0, CPL_ERROR_DATA_NOT_FOUND);
1575 sum = cpl_polynomial_get_coeff(p, &n);
1576 cpl_polynomial_set_coeff(p, &n, 0.0);
1578 for (i = n - 1; i >= 0; i--) {
1579 const double coeff = cpl_polynomial_get_coeff(p, &i);
1581 cpl_polynomial_set_coeff(p, &i, sum);
1583 sum = coeff + r * sum;
1589 return CPL_ERROR_NONE;