home *** CD-ROM | disk | FTP | other *** search
- /*
- * solve.c -- solve equations
- *
- * The larger part of this code (the sturm sequence code) is from Art, the
- * raytracer from the rendering package Vort. It was written by David G
- * Hook, Bernie Kirby and Peter McAree all the credits should go to them.
- * You can reach them via <echidna@munnari.oz.au> The solve_quartic and
- * solve_cubic are from Graphics Gems I "roots3and4.c", by Jochen
- * Schwarze.
- */
-
- /*
- * Ok...
- *
- * It's not my code, but i'll try explain what this is all about.
- *
- * This module solves polynomial equations, or equations that look like:
- *
- * a_n x^n + a_(n-1) x^(n-1) + .. + a_0 = 0
- *
- * n is called the order of the equation, is denoted by "ord" in the code.
- * Note that any solution p(s) = 0 to the equation also is a root of the
- * polynomial: if p(s) = 0 is a solution, then the polynomial can be
- * written as p(x) = q(x) (x-s), with degree(q) = degree(p) - 1.
- *
- * In this application we are only interested in so called "simple roots", ie
- * if s is a root ( p(s) = 0 and p(x) = q(x) (x-s) ), then q(s) != 0.
- *
- * This has numerical reason: take (for example) p(x) := (x-1)^2. p clearly
- * has a double root: p(1) = 0. But if we would start calculating,
- * intermediary results would look like it came from p(x) = (x-1)^2 +
- * 1e-20, which has no solutions. So double roots can be hard to detect.
- *
- * If n <= 3, then an "exact" solution is possible. (Even with n == 4, an out
- * of the box solution is possible. However, it is numerically unstable.
- * has been proved that for n > 4, no general solution method exists.
- *
- * If n >= 4, we have to resort to numerical techniques. The first attempt
- * used was equation_solver(), which has been #undef'd ^H^H^Hdeleted,
- * since it is 3x slower for quartics than sturm sequences.
- *
- * It works like this: let us assume p(t) = 0 is the equation we want to
- * solve.
- *
- * 1. if degree <= 3, then solve exactly
- *
- * 2. if degree > 3, then take derivative, (recursive) find it's roots with
- * equation_solver().
- *
- * These roots of the derivative are the extrema of p(t). We then calculate
- * these extrema. Additionally, p(0) and p(LARGE) are boundary extrema.
- * Any zero of p would lie between two extrema with different sign, and
- * (vice versa) Between two extrema with different signs there always is a
- * zero. We can find such a zero with binary chop.
- *
- * The sturm sequence uses also this property: A sturm sequence can be used
- * to detect sign changes of p(x) for x in the interval [a,b).
- *
- * A sturm sequence is a sequence of polynomials,
- *
- * p_0, p_1, ... p_m
- *
- * because it's a sturm seq, these polynomials have special properties. From
- * these properties, it can be deduced that the number of sign changes in
- * p_0(x) in the interval [a,b) is w(a) - w(b). Here, w(c) means the
- * number of sign changes in
- *
- * p_0(c), p_1(c), .. p_m(c)
- *
- * A sturm sequence can easily be constructed using polynome division. For
- * details: see J. Stoer, R. Bulirsch "Introduction to Numerical Analysis"
- * pages 281 and further.
- *
- * The number of sign changes between a and b equals the number of roots
- * between a and b. So we could use binary chop too: after we've divided
- * [a,b) into [a,c) and [c,b) we can check the number of signchanges in
- * [a,c) and [c,b), until the number of signchanges is one, and then we're
- * sure that one root is sitting in the interval.
- */
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
- /*
- * a coefficient smaller than SMALL_ENOUGH is considered to be zero (0.0).
- */
-
- /* epsilon surrounding for near zero values */
- #define EQN_EPS 1e-9
-
- /* cubic root. */
- #define cbrt(x) ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \
- ((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0))
-
- #define MAXP2 32
-
- /* max number of iterations */
- #define MAXIT 800
-
- /* smallest relative error we want */
- #define RELERROR 1.0e-10
-
-
- PRIVATE int solve_cubic(double *coeffs, double *roots);
-
- PRIVATE int solve_rt_linear(double *coeff, double *roots);
- PRIVATE int solve_rt_quartic(double *coef, double *roots);
- PRIVATE int solve_rt_cubic(double *coeffs, double *roots);
- PRIVATE int solve_rt_quadric(double *coeffs, double *roots);
-
-
- PRIVATE int buildsturm(spoly *sseq);
- PRIVATE int numchanges(int np, spoly *sseq, double a);
- PRIVATE int numroots(int np, spoly *sseq, int *atneg, int *atpos);
- PRIVATE int modp(spoly *u, spoly *v, spoly *r);
- PRIVATE int modrf(int ord, double *coef, register double a, double b, double *val);
- PRIVATE int evalatb(int np, register spoly *sseq, register double b);
- PRIVATE int evalata(register int np, register spoly *sseq, register double a);
- PRIVATE int evalat0(register int np, register spoly *sseq);
- PRIVATE int sturm_solve(spoly *thepoly, double *roots, bool chkall);
- PRIVATE void sbisect(int np, spoly *sseq, double min, double max, int atmin, int atmax, double *roots);
- PRIVATE int csgsbisect(int np, spoly *sseq, double min, double max, int atmin, int atmax, double *roots);
-
- /***************************************************************************
- * ENTRY POINT: solve polynomial, return number of nonnegative roots *
- ***************************************************************************/
-
- PUBLIC int
- solve_rt_poly(spoly *thepoly, double *roots, bool chkall)
- {
- int r;
-
- /* avoid degenerate cases. */
- poly_clean(thepoly);
-
-
- /* special cases can be handled much faster: */
- switch (thepoly->ord) {
- case 4:
- return solve_rt_quartic(thepoly->coef, roots);
- case 2:
- return solve_rt_quadric(thepoly->coef, roots);
- case 3:
- return solve_rt_cubic(thepoly->coef, roots);
- case 1:
- return solve_rt_linear(thepoly->coef, roots);
- case 0:
- return 0;
- }
-
- r = sturm_solve(thepoly, roots, chkall);
-
- return r;
- }
-
- PUBLIC int
- sturm_solve_rt_poly(spoly *thepoly, double *roots, bool chkall)
- {
- int r;
-
- /* not really necessary for these polys. */
- if (thepoly->ord <= 2)
- return solve_rt_poly(thepoly, roots, chkall);
-
- /* avoid degenerate cases. */
- poly_clean(thepoly);
-
- while (thepoly->ord > 0 && thepoly->coef[thepoly->ord] == 0.0)
- thepoly->ord--;
-
- r = sturm_solve(thepoly, roots, chkall);
- return r;
- }
-
- /*
- * Polysolver, based on Eric's Sturm sequence polysolver (function below
- * was taken from torus.c
- */
- PRIVATE int
- sturm_solve(spoly *thepoly, double *roots, bool chkall)
- {
- int np, /* number of polys in the sturm sequence */
- numroots,
- nroots,
- atmin,
- atinf;
-
- spoly sseq[MAX_ORDER]; /* structure to hold the sequence */
-
- register int its,
- i;
- register float min,
- max;
-
- /* copy polynomial */
- for (i = 0; i <= thepoly->ord; i++) {
- sseq[0].coef[i] = thepoly->coef[i];
- }
-
- sseq[0].ord = thepoly->ord;
-
- /* build it's sturm sequence. */
- np = buildsturm(sseq);
-
- /* find sign changes in the sequence at 0 */
- atmin = evalat0(np, sseq);
-
- /* find sign changes in the sequence at infinity */
- atinf = evalatb(np, sseq, 0.0);
-
- /*
- * the number of sign changes of thepoly in [a,b] is atmin - atinf.
- * This is the important thing of Sturm seqs
- */
- nroots = atmin - atinf;
-
- /* no roots which are bigger than 0 */
- if (nroots == 0) {
- return 0;
- }
- /* hmm ... I wonder why. */
- atmin = evalata(np, sseq, tolerance);
-
- nroots = atmin - atinf;
-
- if (nroots == 0) {
- return 0;
- }
- min = tolerance;
- max = min + 2.0;
-
- /*
- * find the range where all those signchanges occur, by doubling the
- * interval size.
- */
- for (its = 0; its < MAXP2; its++) {
- atinf = evalatb(np, sseq, max);
-
- numroots = atmin - atinf;
-
- if (numroots == nroots) /* [min, max] covers all signchanges,
- * therefore it covers all zero's */
- break;
-
- max *= 2.0;
- }
-
-
- if (nroots == 1 || !chkall) {
- /* only one root, (or we're only interested in the smallest) */
- sbisect(np, sseq, min, max, atmin, atinf, roots);
- return 1;
-
- } else { /* we'll need this if CSG is implemented. */
- if (csgsbisect(np, sseq, min, max, atmin, atinf, roots) != nroots)
- assert(FALSE);
- return nroots;
- }
- }
-
-
- /*
- * evalat0
- *
- * calculate the sign changes in the sturm sequence in smat at 0
- */
- PRIVATE int
- evalat0(register int np, register spoly *sseq)
- {
- register int at0;
- register spoly *s,
- *ls;
-
- at0 = 0;
-
- ls = sseq;
- for (s = sseq + 1; s <= sseq + np; s++) {
- if (ls->coef[0] * s->coef[0] < 0 || (ls->coef[0] == 0.0 && s->coef[0] != 0.0))
- at0++;
- ls = s;
- }
-
- return (at0);
- }
-
- /*
- * evalata
- *
- * calculate the sign changes in the sturm sequence in smat at min value a
- */
- PRIVATE int
- evalata(register int np, register spoly *sseq, register double a)
- {
- register int ata;
- register double f,
- lf;
- register spoly *s;
-
- ata = 0;
-
- lf = poly_eval(a, sseq[0].ord, sseq[0].coef);
-
- for (s = sseq + 1; s <= sseq + np; s++) {
- f = poly_eval(a, s->ord, s->coef);
- if (lf * f < 0 || lf == 0.0)
- ata++;
- lf = f;
- }
-
- return (ata);
- }
-
- /*
- * evalatb
- *
- * calculate the sign changes in the sturm sequence in smat at max value b
- */
- PRIVATE int
- evalatb(int np, register spoly *sseq, register double b)
- {
- int atb;
- register double f,
- lf;
- register spoly *s,
- *ls;
-
- atb = 0;
-
- if (b == 0.0) {
- ls = sseq;
- for (s = sseq + 1; s <= sseq + np; s++) {
- if (ls->coef[ls->ord] * s->coef[s->ord] < 0 || (ls->coef[ls->ord] == 0.0 && s->coef[s->ord] != 0.0))
- atb++;
- ls = s;
- }
- } else {
- lf = poly_eval(b, sseq[0].ord, sseq[0].coef);
- for (s = sseq + 1; s <= sseq + np; s++) {
- f = poly_eval(b, s->ord, s->coef);
- if (lf * f < 0 || lf == 0.0)
- atb++;
- lf = f;
- }
- }
-
- return (atb);
- }
-
-
- /*
- * modrf
- *
- * uses the modified regula-falsi method to evaluate the root in interval
- * [a,b] of the polynomial described in coef. The root is returned is
- * returned in *val. The routine returns zero if it can't converge.
- */
- PRIVATE int
- modrf(int ord, double *coef, register double a, double b, double *val)
- {
- register int its;
- register double fa,
- fb,
- x,
- fx,
- lfx;
- register double *fp,
- *scoef,
- *ecoef;
-
- scoef = coef;
- ecoef = &coef[ord];
-
- fb = fa = *ecoef;
- for (fp = ecoef - 1; fp >= scoef; fp--) {
- fa = a * fa + *fp;
- fb = b * fb + *fp;
- }
-
- /*
- * if there is no sign difference the method won't work
- */
- if (fa * fb > 0.0)
- return (0);
-
- if (fabs(fa) < RELERROR) {
- *val = a;
- return (1);
- }
- if (fabs(fb) < RELERROR) {
- *val = b;
- return (1);
- }
- lfx = fa;
-
- for (its = 0; its < MAXIT; its++) {
-
- x = (fb * a - fa * b) / (fb - fa);
-
- fx = *ecoef;
- for (fp = ecoef - 1; fp >= scoef; fp--)
- fx = x * fx + *fp;
-
- if (fabs(x) > RELERROR) {
- if (fabs(fx / x) < RELERROR) {
- *val = x;
- return (1);
- }
- } else if (fabs(fx) < RELERROR) {
- *val = x;
- return (1);
- }
- if ((fa * fx) < 0) {
- b = x;
- fb = fx;
- if ((lfx * fx) > 0)
- fa /= 2;
- } else {
- a = x;
- fa = fx;
- if ((lfx * fx) > 0)
- fb /= 2;
- }
-
- lfx = fx;
- }
-
- if (fabs(fx) > ALG_TOLERANCE) {
- #ifdef DEBUG
- if (debug_options & DEBUGRUNTIME)
- warning("modrf overflow %f %f %f\n", a, b, fx);
- #endif
- }
- *val = x;
-
- return (1);
- }
-
- /*
- * sbisect
- *
- * uses a bisection based on the sturm sequence for the polynomial described
- * in sseq to isolate the lowest positve root of the polynomial in the
- * bounds min and max. The root is returned in *roots.
- */
- PRIVATE void
- sbisect(int np, spoly *sseq, double min, double max, int atmin, int atmax, double *roots)
- {
- register double mid;
- register int n1,
- its,
- atmid;
-
-
- for (its = 0; its < MAXIT; its++) {
-
- mid = (min + max) / 2;
-
- atmid = numchanges(np, sseq, mid);
-
- n1 = atmin - atmid;
-
- if (n1 == 1) {
- /*
- * first try a less expensive technique.
- */
- if (modrf(sseq->ord, sseq->coef, min, mid, &roots[0]))
- return;
-
- /*
- * if we get here we have to evaluate the root the hard way by
- * using the Sturm sequence. It also means the root is
- * repeated an even number of times.
- */
- for (its = 0; its < MAXIT; its++) {
- mid = (min + max) / 2;
-
- atmid = numchanges(np, sseq, mid);
-
- if (fabs(mid) > RELERROR) {
- if (fabs((max - min) / mid) < RELERROR) {
- roots[0] = mid;
- return;
- }
- } else if (fabs(max - min) < RELERROR) {
- roots[0] = mid;
- return;
- }
- if ((atmin - atmid) == 0)
- min = mid;
- else
- max = mid;
- }
-
- #ifdef DEBUG
- if (its == MAXIT && (max - min) > ALG_TOLERANCE &&
- (debug_options & DEBUGRUNTIME)) {
- warning("sbisect: overflow min %f max %f diff %e\n", min, max, max - min);
- }
- #endif
- roots[0] = mid;
-
- return;
- } else if (n1 == 0)
- min = mid;
- else
- max = mid;
-
- }
-
- roots[0] = min; /* multiple roots close together */
- #ifdef DEBUG
- if (its == MAXIT && (max - min) > ALG_TOLERANCE && (debug_options & DEBUGRUNTIME))
- warning("sbisect: overflow min %f max %f diff %e\n", min, max, max - min);
- #endif
- }
-
- /*
- * csgsbisect
- *
- * uses a bisection based on the sturm sequence for the polynomial described
- * in sseq to find the positive roots in the bounds min to max, the roots
- * are returned in the roots array in order of magnitude.
- */
- PRIVATE int
- csgsbisect(int np, spoly *sseq, double min, double max, int atmin, int atmax, double *roots)
- {
- double mid;
-
- int n1,
- n2,
- its,
- atmid,
- nroot,
- nr;
-
- /* assume the worst */
- if ((nroot = atmin - atmax) == 1) {
-
- /*
- * first try a less expensive technique.
- */
- if (modrf(sseq->ord, sseq->coef, min, max, &roots[0])) {
- return 1;
- }
- /*
- * if we get here we have to evaluate the root the hard way by
- * using the Sturm sequence. It also means the root is repeated an
- * even number of times.
- */
- for (its = 0; its < MAXIT; its++) {
- mid = (min + max) / 2;
-
- atmid = numchanges(np, sseq, mid);
-
- if (fabs(mid) > RELERROR) {
- if (fabs((max - min) / mid) < RELERROR)
- break;
- } else if (fabs(max - min) < RELERROR)
- break;
-
- if ((atmin - atmid) == 0)
- min = mid;
- else
- max = mid;
- }
-
- #ifdef DEBUG
- if (its == MAXIT && (debug_options & DEBUGRUNTIME))
- warning("csgsbisect: overflow min %f max %f diff %e\n", min, max, max - min);
-
- #endif
- roots[1] = roots[0] = mid;
- return 2;
- }
- /*
- * more than one root in the interval, we have to bisect...
- */
- nr = 0;
- for (its = 0; its < MAXIT; its++) {
-
- mid = (min + max) / 2;
-
- atmid = numchanges(np, sseq, mid);
-
- n1 = atmin - atmid;
- n2 = atmid - atmax;
-
- if (n1 != 0 && n2 != 0) {
- nr += csgsbisect(np, sseq, min, mid, atmin, atmid, roots);
- nr += csgsbisect(np, sseq, mid, max, atmid, atmax, &roots[n1]);
- break;
- }
- if (n1 == 0)
- min = mid;
- else
- max = mid;
- }
-
- if (its == MAXIT) {
- int i;
-
- #ifdef DEBUG
- if (debug_options & DEBUGRUNTIME) {
- warning("csgsbisect: roots too close together\n");
- warning("csgsbisect: overflow min %f max %f diff %e nroot %d n1 %d n2 %d\n", min, max, max - min, nroot, n1, n2);
- }
- #endif
-
- n1 = atmin - atmax;
- nr += n1;
- for (i = 0; i < n1; i++)
- roots[i] = mid;
- }
- return nr;
- }
-
- /*
- * modp
- *
- * calculates the modulus of u(x) / v(x) leaving it in r, it returns 0 if
- * r(x) is a constant.
- *
- * note: this function assumes the leading coefficient of v is 1 or -1
- */
- PRIVATE int
- modp(spoly *u, spoly *v, spoly *r)
- {
- int k,
- j;
- double *nr,
- *end,
- *uc;
-
- nr = r->coef;
- end = &u->coef[u->ord];
-
- uc = u->coef;
- while (uc <= end)
- *nr++ = *uc++;
-
- if (v->coef[v->ord] < 0.0) {
-
- for (k = u->ord - v->ord - 1; k >= 0; k -= 2)
- r->coef[k] = -r->coef[k];
-
- for (k = u->ord - v->ord; k >= 0; k--)
- for (j = v->ord + k - 1; j >= k; j--)
- r->coef[j] = -r->coef[j] - r->coef[v->ord + k] * v->coef[j - k];
- } else {
- for (k = u->ord - v->ord; k >= 0; k--)
- for (j = v->ord + k - 1; j >= k; j--)
- r->coef[j] -= r->coef[v->ord + k] * v->coef[j - k];
- }
-
- k = v->ord - 1;
- while (k >= 0 && r->coef[k] == 0.0)
- k--;
-
- r->ord = (k < 0) ? 0 : k;
-
- return (r->ord);
- }
-
- /*
- * buildsturm
- *
- * build up a sturm sequence for a polynomial in smat, returning the number
- * of polynomials in the sequence
- */
- PRIVATE int
- buildsturm(spoly *sseq)
- {
- int i,
- ord;
- double f,
- *fp,
- *fc;
- spoly *sp;
-
- ord = sseq[0].ord;
- sseq[1].ord = ord - 1;
-
- /*
- * calculate the derivative and normalise the leading coefficient.
- */
- f = fabs(sseq[0].coef[ord] * ord);
- fp = sseq[1].coef;
- fc = sseq[0].coef + 1;
- for (i = 1; i <= ord; i++)
- *fp++ = *fc++ * i / f;
-
- /*
- * construct the rest of the Sturm sequence
- */
- for (sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++) {
-
- /*
- * reverse the sign and normalise
- */
- f = -fabs(sp->coef[sp->ord]);
- for (fp = &sp->coef[sp->ord]; fp >= sp->coef; fp--)
- *fp /= f;
- }
-
- sp->coef[0] = -sp->coef[0]; /* reverse the sign */
-
- return (sp - sseq);
- }
-
- /*
- * numroots
- *
- * return the number of distinct real roots of the polynomial described in
- * sseq.
- */
- PRIVATE int
- numroots(int np,
- spoly *sseq,
- int *atneg, int *atpos)
- {
- int atposinf,
- atneginf;
- spoly *s;
- double f,
- lf;
-
- atposinf = atneginf = 0;
-
- /*
- * changes at positve infinity
- */
- lf = sseq[0].coef[sseq[0].ord];
-
- for (s = sseq + 1; s <= sseq + np; s++) {
- f = s->coef[s->ord];
- if (lf == 0.0 || lf * f < 0)
- atposinf++;
- lf = f;
- }
-
- /*
- * changes at negative infinity
- */
- if (sseq[0].ord & 1)
- lf = -sseq[0].coef[sseq[0].ord];
- else
- lf = sseq[0].coef[sseq[0].ord];
-
- for (s = sseq + 1; s <= sseq + np; s++) {
- if (s->ord & 1)
- f = -s->coef[s->ord];
- else
- f = s->coef[s->ord];
- if (lf == 0.0 || lf * f < 0)
- atneginf++;
- lf = f;
- }
-
- *atneg = atneginf;
- *atpos = atposinf;
-
- return (atneginf - atposinf);
- }
-
- /*
- * numchanges
- *
- * return the number of sign changes in the Sturm sequence in sseq at the
- * value a.
- */
- PRIVATE int
- numchanges(int np,
- spoly *sseq,
- double a)
- {
- int changes;
- double f,
- lf;
- spoly *s;
-
- changes = 0;
-
- lf = poly_eval(a, sseq[0].ord, sseq[0].coef);
-
- for (s = sseq + 1; s <= sseq + np; s++) {
- f = poly_eval(a, s->ord, s->coef);
- if (lf == 0.0 || lf * f < 0)
- changes++;
- lf = f;
- }
-
- return (changes);
- }
-
-
- /* solve linear equation */
- PRIVATE int
- solve_rt_linear(double *coeff, double *roots)
- {
- if ((*roots = -coeff[0] / coeff[1]) > tolerance)
- return 1;
- else
- return 0;
- }
-
- /*
- * SPECIAL ROOTFINDERS: quadric, cubic and quartic.
- *
- * written by Jochen Schwarze and me from Graphics Gems I, by Jochen
- * Schwarze, (schwarze@isa.de) quadratic: both by Jochen Schwarze, from
- * graphics gems cubic: one from me, and one by Jochen Schwarze quartic:
- * by Jochen Schwarze.
- *
- *
- * Here is the original comment header:
- *
- * Roots3And4.c
- *
- * Utility functions to find cubic and quartic roots, coefficients are passed
- * like this:
- *
- * c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
- *
- * The functions return the number of non-complex roots and put the values
- * into the s array.
- *
- * Author: Jochen Schwarze (schwarze@isa.de)
- *
- * Jan 26, 1990 Version for Graphics Gems Oct 11, 1990 Fixed sign
- * problem for negative q's in SolveQuartic (reported by Mark Podlipec),
- * Old-style function definitions, IsZero() as a macro Nov 23, 1990 Some
- * systems do not declare acos() and cbrt() in <math.h>, though the
- * functions exist in the library. If large coefficients are used, EQN_EPS
- * should be reduced considerably (e.g. to 1E-30), results will be correct
- * but multiple roots might be reported more than once.
- */
-
-
-
-
- /*
- * all roots of quadratic eqn.
- */
- int
- solve_quadric(double c[3], double s[2])
- {
- double p,
- q,
- D;
-
- /* normal form: x^2 + px + q = 0 */
-
- p = c[1] / (2 * c[2]);
- q = c[0] / c[2];
-
- D = p * p - q;
-
- if (D < 0) {
- return 0;
- } else {
- D = sqrt(D);
-
- s[0] = D - p;
- s[1] = -D - p;
- return 2;
- }
- }
-
- /*
- * solve quadric, return roots sorted and bigger than 0 double roots are
- * returned double, so CSG still works.
- */
- int
- solve_rt_quadric(double *coeffs, double *roots)
- {
- double p,
- q,
- D,
- r1,
- r2;
-
-
- /*
- * pre: coeffs[2] != 0
- */
-
- p = coeffs[1] / (2 * coeffs[2]);
- q = coeffs[0] / coeffs[2];
-
- D = p * p - q;
-
- if (D < 0) {
- return 0;
- }
- /*
- * don't check for double roots. If there are any, we still need both
- * of them.
- */
-
- D = sqrt(D);
- r1 = D - p;
- r2 = -D - p;
-
- if (r1 > r2) {
- D = r2;
- r2 = r1;
- r1 = D;
- }
- /* r2 is maximum. If r2 < epsilon, then shake it */
- if (r2 < tolerance)
- return 0;
-
- /* r2 > tolerance, but is r1 > tolerance? */
- if (r1 < tolerance) {
- *roots = r2;
- return 1;
- } else {
- *roots++ = r1;
- *roots = r2;
- return 2;
- }
-
- }
-
- /*
- * solve an equation using Cardano's method. Numerically unstable with
- * double roots.
- *
- * How does it work?
- *
- * Suppose we have an cubic equation
- *
- * ax^3+ bx^2 + cx + d = 0
- *
- * After division by a, we get
- *
- * x^3 + b' x^2 + c' x + d' = 0
- *
- * After substitution x = y -b'/3. We get
- *
- * y^3 = 3py - 2q (1)
- *
- * For certain p and q. This is the big Cardano trick: we could solve the
- * equation if we would find u and v such that
- *
- * u^3 + v^3 = -2q and uv = p (2)
- *
- * since (u+v)^3 = u^3 + 3u^2v + 3v^2u + v^3. = 3(u+v) uv + u^3 + v^3
- *
- * and y would be y = u+v. (2) turns out to be a quadratic equation, and it
- * gives us one solution for (1). If (2) has no solution (discriminant
- * 4q^2 - 4p^3 < 0), an other method has to used. We can safely assume
- * that y = 2sqrt(p) cos(theta), and (1) turns out to be
- *
- * cos(3 theta) = -q/(p*(sqrt(p)))
- *
- * This gives us 3 solutions.
- */
-
-
- /*
- * this one is for RT applications, it returns number of roots bigger than
- * EPSILON, and the roots itself sorted in an array.
- */
-
- #ifdef UNDEFINED
- PRIVATE int
- solve_rt_cubic(double *coeffs, double *roots)
- {
- double tcoeffs[4],
- a,
- lin,
- cons;
- int noroots,
- i;
-
- int n,
- j;
- double r[4];
-
- n = solve_quartic(coeffs, r);
- qsort(r, n, sizeof(double), dcomp);
-
- j = 0;
- for (i = 0; i < n; i++)
- if (r[i] > tolerance)
- roots[j++] = r[i];
- return j;
-
-
-
- a = coeffs[3];
- for (i = 0; i < 4; i++)
- tcoeffs[i] = coeffs[i] / a;
-
- a = tcoeffs[2];
-
- lin = -sqr(a) / 3.0 + tcoeffs[1];
- cons = (a * a * a * 2) / 27.0 - a * tcoeffs[1] / 3.0 + tcoeffs[0];
-
- {
- double p,
- q,
- u,
- v,
- d;
-
- p = -lin / 3;
- q = cons / 2;
-
- d = 4 * q * q - 4 * p * p * p;
-
- if (d >= 0) {
- d = sqrt(d);
- u = (-2 * q + d) / 2;
- v = (-2 * q - d) / 2;
-
- u = cbrt(u);
- v = cbrt(v);
-
- *roots = u + v - a / 3;
-
- if (*roots > tolerance)
- return 1;
- else
- return 0;
- } else { /* discr < 0, use Vi\`ete's method */
- double r,
- *rp,
- rt[3];
-
- noroots = 0;
- rp = rt;
-
- a = a / 3;
-
- d = acos(-q / (p * sqrt(p))) / 3;
- p = 2 * sqrt(p);
-
- if ((r = p * cos(d) - a) > tolerance)
- *rp++ = r;
-
- d += 2 * M_PI / 3;
- if ((r = p * cos(d) - a) > tolerance)
- *rp++ = r;
-
- d += 2 * M_PI / 3;
-
- if ((r = p * cos(d) - a) > tolerance)
- *rp++ = r;
-
-
- /*
- * calculation is done. Sort the roots. Smallest must be
- * first.
- */
-
- noroots = rp - rt;
-
- if (noroots <= 1) {
- *roots = rt[0];
- return noroots;
- }
- if (rt[0] > rt[1]) {
- r = rt[0];
- rt[0] = rt[1];
- rt[1] = r;
- }
- if (noroots == 2) {
- *roots++ = rt[0];
- *roots++ = rt[1];
- return 2;
- }
- if (rt[0] > rt[2]) {
- r = rt[0];
- rt[0] = rt[2];
- rt[2] = r;
- }
- /* now, rt[0] is the smallest */
- if (rt[1] > rt[2]) {
- r = rt[1];
- rt[1] = rt[2];
- rt[2] = r;
- }
- /* copy the roots */
- *roots++ = rt[0];
- *roots++ = rt[1];
- *roots++ = rt[2];
-
- return 3;
- }
- }
- }
-
- #endif
-
- /*
- * solve cubic, same method as above. Don't sort roots; return all roots.
- */
- PRIVATE int
- solve_cubic(double c[4], double s[3])
- {
- int i,
- num;
- double sub;
- double A,
- B,
- C;
- double sq_A,
- p,
- q;
- double cb_p,
- D;
-
- /* normal form: x^3 + Ax^2 + Bx + C = 0 */
-
- A = c[2] / c[3];
- B = c[1] / c[3];
- C = c[0] / c[3];
-
- /*
- * substitute x = y - A/3 to eliminate quadric term: x^3 +px + q = 0
- */
-
- sq_A = A * A;
- p = 1.0 / 3 * (-1.0 / 3 * sq_A + B);
- q = 1.0 / 2 * (2.0 / 27 * A * sq_A - 1.0 / 3 * A * B + C);
-
- /* use Cardano's formula */
-
- cb_p = p * p * p;
- D = q * q + cb_p;
-
- if (ISZERO(D)) {
- if (ISZERO(q)) { /* one triple solution */
- s[0] = 0;
- num = 1;
- } else { /* one single and one double solution */
- double u = cbrt(-q);
-
- s[0] = 2 * u;
- s[1] = -u;
- num = 2;
- }
- } else if (D < 0) { /* Casus irreducibilis: three real
- * solutions */
- double phi = 1.0 / 3 * acos(-q / sqrt(-cb_p));
- double t = 2 * sqrt(-p);
-
- s[0] = t * cos(phi);
- s[1] = -t * cos(phi + M_PI / 3);
- s[2] = -t * cos(phi - M_PI / 3);
- num = 3;
- } else { /* one real solution */
- double sqrt_D = sqrt(D);
- double u = cbrt(sqrt_D - q);
- double v = -cbrt(sqrt_D + q);
-
- s[0] = u + v;
- num = 1;
- }
-
- /* resubstitute */
-
- sub = 1.0 / 3 * A;
-
- for (i = 0; i < num; ++i)
- s[i] -= sub;
-
- return num;
- }
-
- /*
- * solve a quartic eqn. Ferrari was the first to think of this. (I believe
- * or was it Descartes?
- */
-
- PRIVATE int
- solve_quartic(double c[5], double s[4])
- {
- double coeffs[4];
- double z,
- u,
- v,
- sub;
- double A,
- B,
- C,
- D;
- double sq_A,
- p,
- q,
- r;
- int i,
- num;
-
- /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
-
- A = c[3] / c[4];
- B = c[2] / c[4];
- C = c[1] / c[4];
- D = c[0] / c[4];
-
- /*
- * substitute x = y - A/4 to eliminate cubic term: x^4 + px^2 + qx + r
- * = 0
- */
-
- sq_A = A * A;
- p = -3.0 / 8 * sq_A + B;
- q = 1.0 / 8 * sq_A * A - 1.0 / 2 * A * B + C;
- r = -3.0 / 256 * sq_A * sq_A + 1.0 / 16 * sq_A * B - 1.0 / 4 * A * C + D;
-
- if (ISZERO(r)) {
- /* no absolute term: y(y^3 + py + q) = 0 */
-
- coeffs[0] = q;
- coeffs[1] = p;
- coeffs[2] = 0;
- coeffs[3] = 1;
-
- num = solve_cubic(coeffs, s);
-
- s[num++] = 0;
- } else {
- /* solve the resolvent cubic ... */
-
- coeffs[0] = 1.0 / 2 * r * p - 1.0 / 8 * q * q;
- coeffs[1] = -r;
- coeffs[2] = -1.0 / 2 * p;
- coeffs[3] = 1;
-
- (void) solve_cubic(coeffs, s);
-
- /* ... and take the one real solution ... */
-
- z = s[0];
-
- /* ... to build two quadric equations */
-
- u = z * z - r;
- v = 2 * z - p;
-
- if (ISZERO(u))
- u = 0;
- else if (u > 0)
- u = sqrt(u);
- else
- return 0;
-
- if (ISZERO(v))
- v = 0;
- else if (v > 0)
- v = sqrt(v);
- else
- return 0;
-
- coeffs[0] = z - u;
- coeffs[1] = q < 0 ? -v : v;
- coeffs[2] = 1;
-
- num = solve_quadric(coeffs, s);
-
- coeffs[0] = z + u;
- coeffs[1] = q < 0 ? v : -v;
- coeffs[2] = 1;
-
- num += solve_quadric(coeffs, s + num);
- }
-
- /* resubstitute */
-
- sub = 1.0 / 4 * A;
-
- for (i = 0; i < num; ++i)
- s[i] -= sub;
-
- return num;
- }
-
-
- int
- dcomp(const void *p, const void *q)
- {
- double a,
- b;
-
- a = *(double *) p;
- b = *(double *) q;
- if (a < b)
- return -1;
- if (a > b)
- return +1;
- return 0;
- }
-
- PRIVATE int
- solve_rt_quartic(double *coef, double *roots)
- {
- int n,
- i,
- j;
- double r[4];
-
- n = solve_quartic(coef, r);
- qsort(r, n, sizeof(double), dcomp);
-
- j = 0;
- for (i = 0; i < n; i++)
- if (r[i] > tolerance)
- roots[j++] = r[i];
- return j;
- }
-
- PRIVATE int
- solve_rt_cubic(double *coeffs, double *roots)
- {
- int i;
-
- int n,
- j;
- double r[4];
-
- n = solve_cubic(coeffs, r);
- qsort(r, n, sizeof(double), dcomp);
-
- j = 0;
- for (i = 0; i < n; i++)
- if (r[i] > tolerance)
- roots[j++] = r[i];
- return j;
- }
-
- /*
- * the debugging routines are still here, if you need them.
- */
- #ifdef DEBUG
-
- /* print solutions, and check wether they are solutions */
- PUBLIC void
- print_sols(double *s, int nosols, spoly p)
- {
-
- int i;
-
- if (nosols <= 0) {
- printf("no solutions\n");
- return;
- }
- printf("%d solution(s)\n", nosols);
-
- for (i = 0; i < nosols; i++)
- printf("%d: p(%lf) = %lf\n", i + 1, s[i], poly_eval(s[i], p.ord, p.coef));
-
- printf("\n");
- }
-
- #endif
-