/* ******** ell_cubic(): ** ** finds real roots of x^3 + A*x^2 + B*x + C. ** ** records the found real roots in the given root array. ** ** returns information about the roots according to ellCubicRoot enum, ** the set the following values in given root[] array: ** ellCubicRootSingle: root[0], root[1] == root[2] == AIR_NAN ** ellCubicRootTriple: root[0] == root[1] == root[2] ** ellCubicRootSingleDouble: single root[0]; double root[1] == root[2] ** or double root[0] == root[1], single root[2] ** ellCubicRootThree: root[0], root[1], root[2] ** ** The values stored in root[] are, in a change from the past, sorted ** in descending order! No need to sort them any more! ** ** This does NOT use biff */ int ell_cubic(double root[3], double A, double B, double C, int newton) { char me[]="ell_cubic"; double epsilon = 1.0E-11, AA, Q, R, QQQ, D, sqrt_D, der, u, v, x, theta, t, sub; sub = A/3.0; AA = A*A; Q = (AA/3.0 - B)/3.0; R = (-2.0*A*AA/27.0 + A*B/3.0 - C)/2.0; QQQ = Q*Q*Q; D = R*R - QQQ; if (D < -epsilon) { /* three distinct roots- this is the most common case, it has been tested the most, its code should go first */ theta = acos(R/sqrt(QQQ))/3.0; t = 2*sqrt(Q); /* yes, these are sorted, because the C definition of acos says that it returns values in in [0, pi] */ root[0] = t*cos(theta) - sub; root[1] = t*cos(theta - 2*AIR_PI/3.0) - sub; root[2] = t*cos(theta + 2*AIR_PI/3.0) - sub; /* if (!AIR_EXISTS(root[0])) { fprintf(stderr, "%s: %g %g %g --> nan!!!\n", me, A, B, C); } */ return ell_cubic_root_three; } else if (D > epsilon) { double nr, fnr; /* one real solution, except maybe also a "rescued" double root */ sqrt_D = sqrt(D); u = airCbrt(sqrt_D+R); v = -airCbrt(sqrt_D-R); x = u+v - sub; if (!newton) { root[0] = x; root[1] = root[2] = AIR_NAN; return ell_cubic_root_single; } /* else refine x, the known root, with newton-raphson, so as to get the most accurate possible calculation for nr, the possible new root */ x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der); nr = -(A + x)/2.0; fnr = ((nr + A)*nr + B)*nr + C; /* the polynomial evaluated at nr */ /* if (ell_debug) { fprintf(stderr, "%s: root = %g -> %g, nr=% 20.15f\n" " fnr=% 20.15f\n", me, x, (((x + A)*x + B)*x + C), nr, fnr); } */ if (fnr < -epsilon || fnr > epsilon) { root[0] = x; root[1] = root[2] = AIR_NAN; return ell_cubic_root_single; } else { if (ell_debug) { fprintf(stderr, "%s: rescued double root:% 20.15f\n", me, nr); } if (x > nr) { root[0] = x; root[1] = nr; root[2] = nr; } else { root[0] = nr; root[1] = nr; root[2] = x; } return ell_cubic_root_single_double; } } else { /* else D is in the interval [-epsilon, +epsilon] */ if (R < -epsilon || epsilon < R) { /* one double root and one single root */ u = airCbrt(R); if (u > 0) { root[0] = 2*u - sub; root[1] = -u - sub; root[2] = -u - sub; } else { root[0] = -u - sub; root[1] = -u - sub; root[2] = 2*u - sub; } return ell_cubic_root_single_double; } else { /* one triple root */ root[0] = root[1] = root[2] = -sub; return ell_cubic_root_triple; } } /* shouldn't ever get here */ /* return ell_cubic_root_unknown; */ }
float dyeLcbrt(float t) { return AIR_CAST(float, (t > 0.008856 ? airCbrt(t) : 7.787*t + 16.0/116.0)); }
int evals_evecs(double eval[3], double evec[9], const double _M00, const double _M01, const double _M02, const double _M11, const double _M12, const double _M22) { double r0[3], r1[3], r2[3], crs[3], len, dot; double mean, norm, rnorm, Q, R, QQQ, D, theta, M00, M01, M02, M11, M12, M22; double epsilon = 1.0E-12; int roots; /* copy the given matrix elements */ M00 = _M00; M01 = _M01; M02 = _M02; M11 = _M11; M12 = _M12; M22 = _M22; /* ** subtract out the eigenvalue mean (will add back to evals later); ** helps with numerical stability */ mean = (M00 + M11 + M22)/3.0; M00 -= mean; M11 -= mean; M22 -= mean; /* ** divide out L2 norm of eigenvalues (will multiply back later); ** this too seems to help with stability */ norm = sqrt(M00*M00 + 2*M01*M01 + 2*M02*M02 + M11*M11 + 2*M12*M12 + M22*M22); rnorm = norm ? 1.0/norm : 1.0; M00 *= rnorm; M01 *= rnorm; M02 *= rnorm; M11 *= rnorm; M12 *= rnorm; M22 *= rnorm; /* this code is a mix of prior Teem code and ideas from Eberly's "Eigensystems for 3 x 3 Symmetric Matrices (Revisited)" */ Q = (M01*M01 + M02*M02 + M12*M12 - M00*M11 - M00*M22 - M11*M22)/3.0; QQQ = Q*Q*Q; R = (M00*M11*M22 + M02*(2*M01*M12 - M02*M11) - M00*M12*M12 - M01*M01*M22)/2.0; D = QQQ - R*R; if (D > epsilon) { /* three distinct roots- this is the most common case */ double mm, ss, cc; theta = atan2(sqrt(D), R)/3.0; mm = sqrt(Q); ss = sin(theta); cc = cos(theta); eval[0] = 2*mm*cc; eval[1] = mm*(-cc + sqrt(3.0)*ss); eval[2] = mm*(-cc - sqrt(3.0)*ss); roots = ROOT_THREE; /* else D is near enough to zero */ } else if (R < -epsilon || epsilon < R) { double U; /* one double root and one single root */ U = airCbrt(R); /* cube root function */ if (U > 0) { eval[0] = 2*U; eval[1] = -U; eval[2] = -U; } else { eval[0] = -U; eval[1] = -U; eval[2] = 2*U; } roots = ROOT_SINGLE_DOUBLE; } else { /* a triple root! */ eval[0] = eval[1] = eval[2] = 0.0; roots = ROOT_TRIPLE; } /* r0, r1, r2 are the vectors we manipulate to find the nullspaces of M - lambda*I */ VEC_SET(r0, 0.0, M01, M02); VEC_SET(r1, M01, 0.0, M12); VEC_SET(r2, M02, M12, 0.0); if (ROOT_THREE == roots) { r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace1(evec+0, r0, r1, r2); r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1]; nullspace1(evec+3, r0, r1, r2); r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2]; nullspace1(evec+6, r0, r1, r2); } else if (ROOT_SINGLE_DOUBLE == roots) { if (eval[1] == eval[2]) { /* one big (eval[0]) , two small (eval[1,2]) */ r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace1(evec+0, r0, r1, r2); r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1]; nullspace2(evec+3, evec+6, r0, r1, r2); } else { /* two big (eval[0,1]), one small (eval[2]) */ r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0]; nullspace2(evec+0, evec+3, r0, r1, r2); r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2]; nullspace1(evec+6, r0, r1, r2); } } else { /* ROOT_TRIPLE == roots; use any basis for eigenvectors */ VEC_SET(evec+0, 1, 0, 0); VEC_SET(evec+3, 0, 1, 0); VEC_SET(evec+6, 0, 0, 1); } /* we always make sure its really orthonormal; keeping fixed the eigenvector associated with the largest-magnitude eigenvalue */ if (ABS(eval[0]) > ABS(eval[2])) { /* normalize evec+0 but don't move it */ VEC_NORM(evec+0, len); dot = VEC_DOT(evec+0, evec+3); VEC_SCL_SUB(evec+3, dot, evec+0); VEC_NORM(evec+3, len); dot = VEC_DOT(evec+0, evec+6); VEC_SCL_SUB(evec+6, dot, evec+0); dot = VEC_DOT(evec+3, evec+6); VEC_SCL_SUB(evec+6, dot, evec+3); VEC_NORM(evec+6, len); } else { /* normalize evec+6 but don't move it */ VEC_NORM(evec+6, len); dot = VEC_DOT(evec+6, evec+3); VEC_SCL_SUB(evec+3, dot, evec+6); VEC_NORM(evec+3, len); dot = VEC_DOT(evec+3, evec+0); VEC_SCL_SUB(evec+0, dot, evec+3); dot = VEC_DOT(evec+6, evec+0); VEC_SCL_SUB(evec+0, dot, evec+6); VEC_NORM(evec+0, len); } /* to be nice, make it right-handed */ VEC_CROSS(crs, evec+0, evec+3); if (0 > VEC_DOT(crs, evec+6)) { VEC_SCL(evec+6, -1); } /* multiply back by eigenvalue L2 norm */ eval[0] /= rnorm; eval[1] /= rnorm; eval[2] /= rnorm; /* add back in the eigenvalue mean */ eval[0] += mean; eval[1] += mean; eval[2] += mean; return roots; }
static double _nrrdUnaryOpCbrt(double a) {return airCbrt(a);}