home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / numana01.zip / SRC / POLY.MOD < prev    next >
Text File  |  1996-07-31  |  29KB  |  846 lines

  1. IMPLEMENTATION MODULE Poly;
  2.  
  3.         (********************************************************)
  4.         (*                                                      *)
  5.         (*              Polynomial arithmetic                   *)
  6.         (*      This version uses a vector representation       *)
  7.         (*                                                      *)
  8.         (*  Programmer:         P. Moylan                       *)
  9.         (*  Last edited:        31 July 1996                    *)
  10.         (*  Status:             Working                         *)
  11.         (*                                                      *)
  12.         (********************************************************)
  13.  
  14. FROM SYSTEM IMPORT
  15.     (* proc *)  ADR;
  16.  
  17. FROM Storage IMPORT
  18.     (* proc *)  ALLOCATE, DEALLOCATE;
  19.  
  20. FROM LongComplexMath IMPORT
  21.     (* proc *)  abs, conj, sqrt, scalarMult;
  22.  
  23. IMPORT MiscM2;
  24. FROM MiscM2 IMPORT
  25.     (* proc *)  PressAnyKey, WriteString, WriteLn, Error, Sqrt,
  26.                 LongRealToString;
  27.  
  28. (************************************************************************)
  29.  
  30. CONST small = 1.0E-12;
  31.  
  32. TYPE
  33.     power = [0..8190];
  34.  
  35.     (* We represent a polynomial as a vector of coefficients, with      *)
  36.     (* the zero-degree term coming first.                               *)
  37.  
  38.     CoeffPointer = POINTER TO ARRAY power OF CoeffType;
  39.     Polynomial = POINTER TO
  40.                     RECORD
  41.                         degree: CARDINAL;
  42.                         pcoeffs: CoeffPointer;
  43.                     END (*RECORD*);
  44.  
  45. (************************************************************************)
  46. (*
  47. PROCEDURE Checkpoint (message: ARRAY OF CHAR);
  48.  
  49.     BEGIN
  50.         WriteString (message);  WriteLn;
  51.         PressAnyKey;
  52.     END Checkpoint;
  53. *)
  54. (************************************************************************)
  55. (*                CREATING AND DESTROYING POLYNOMIALS                   *)
  56. (************************************************************************)
  57.  
  58. PROCEDURE Init (VAR (*OUT*) P: Polynomial);
  59.  
  60.     (* This should be the first operation performed on P, since this    *)
  61.     (* module needs to keep track of which polynomials have already had *)
  62.     (* space allocated for them.  It creates the zero polynomial.       *)
  63.  
  64.     BEGIN
  65.         P := NIL;
  66.     END Init;
  67.  
  68. (************************************************************************)
  69.  
  70. PROCEDURE Reduce (VAR (*INOUT*) P: Polynomial);
  71.  
  72.     (* If possible, reduces the degree of P by removing negligible      *)
  73.     (* higher-order terms.                                              *)
  74.  
  75.     VAR N: power;  bytecount: CARDINAL;
  76.         newcoeffs: CoeffPointer;
  77.  
  78.     BEGIN
  79.         IF P <> NIL THEN
  80.             N := P^.degree;
  81.             WHILE (N > 0) AND (ABS(P^.pcoeffs^[N]) < small) DO
  82.                 DEC (N);
  83.             END (*WHILE*);
  84.             IF (N = 0) AND (ABS(P^.pcoeffs^[0]) < small) THEN
  85.                 DEALLOCATE (P^.pcoeffs, (P^.degree+1)*SIZE(CoeffType));
  86.                 DISPOSE (P);
  87.             ELSIF N < P^.degree THEN
  88.                 bytecount := (N+1)*SIZE(CoeffType);
  89.                 ALLOCATE (newcoeffs, bytecount);
  90.                 MiscM2.BlockCopy (P^.pcoeffs, newcoeffs, bytecount);
  91.                 DEALLOCATE (P^.pcoeffs, (P^.degree+1)*SIZE(CoeffType));
  92.                 P^.pcoeffs := newcoeffs;
  93.                 P^.degree := N;
  94.             END (*IF*);
  95.         END (*IF*);
  96.     END Reduce;
  97.  
  98. (************************************************************************)
  99.  
  100. PROCEDURE Make (N: power): Polynomial;
  101.  
  102.     (* Creates a polynomial of degree N.  The caller still has to       *)
  103.     (* fill in the coefficient values.                                  *)
  104.  
  105.     VAR result: Polynomial;
  106.  
  107.     BEGIN
  108.         NEW (result);
  109.         WITH result^ DO
  110.             degree := N;
  111.             ALLOCATE (pcoeffs, (N+1)*SIZE(CoeffType));
  112.         END (*WITH*);
  113.         RETURN result;
  114.     END Make;
  115.  
  116. (************************************************************************)
  117.  
  118. PROCEDURE Assign (VAR (*INOUT*) P: Polynomial;
  119.                                 coeffs: ARRAY OF CoeffType);
  120.  
  121.     (* Creates a polynomial with specified coefficients.  The previous  *)
  122.     (* value, if any, is lost.  The coefficients are specified from     *)
  123.     (* low to high degree; for example, the coefficient set specified   *)
  124.     (* by the array (1.0, 2.0, 3.0) gives the second-degree polynomial  *)
  125.     (* 1.0 + 2.0*x + 3.0*x^2.                                           *)
  126.  
  127.     BEGIN
  128.         Destroy (P);
  129.         P := Make (HIGH(coeffs));
  130.         WITH P^ DO
  131.             MiscM2.BlockCopy (ADR(coeffs), pcoeffs, (degree+1)*SIZE(CoeffType));
  132.         END (*WITH*);
  133.         Reduce (P);
  134.     END Assign;
  135.  
  136. (************************************************************************)
  137.  
  138. PROCEDURE Destroy (VAR (*INOUT*) P: Polynomial);
  139.  
  140.     (* Deallocates the space occupied by P.  P is still considered to   *)
  141.     (* exist, and its value is the zero polynomial.                     *)
  142.  
  143.     BEGIN
  144.         IF P <> NIL THEN
  145.             DEALLOCATE (P^.pcoeffs, (P^.degree+1)*SIZE(CoeffType));
  146.             DISPOSE (P);
  147.         END (*IF*);
  148.     END Destroy;
  149.  
  150. (************************************************************************)
  151. (*                      SOME INTERNAL UTILITIES                         *)
  152. (************************************************************************)
  153.  
  154. PROCEDURE Copy (P: Polynomial): Polynomial;
  155.  
  156.     (* Creates a copy of a polynomial. *)
  157.  
  158.     VAR result: Polynomial;
  159.  
  160.     BEGIN
  161.         result := NIL;
  162.         IF P <> NIL THEN
  163.             result := Make (P^.degree);
  164.             WITH result^ DO
  165.                 MiscM2.BlockCopy (P^.pcoeffs, pcoeffs,
  166.                                 (degree+1)*SIZE(CoeffType));
  167.             END (*WITH*);
  168.         END (*IF*);
  169.         RETURN result;
  170.     END Copy;
  171.  
  172. (************************************************************************)
  173. (*                        THE BASIC OPERATIONS                          *)
  174. (************************************************************************)
  175.  
  176. PROCEDURE Degree (P: Polynomial): INTEGER;
  177.  
  178.     (* Returns the degree of P, i.e. the power of the most significant  *)
  179.     (* term.  The degree of a constant is 0, but the degree of the      *)
  180.     (* constant 0.0 is defined to be -1.                                *)
  181.  
  182.     BEGIN
  183.         IF P = NIL THEN RETURN -1
  184.         ELSE RETURN P^.degree
  185.         END (*IF*);
  186.     END Degree;
  187.  
  188. (************************************************************************)
  189.  
  190. PROCEDURE Add (A, B: Polynomial;  VAR (*INOUT*) C: Polynomial);
  191.  
  192.     (* Computes C := A + B.  Note that we don't copy the result to C    *)
  193.     (* until the end of the computation, in case C is an alias for      *)
  194.     (* one of A or B.                                                   *)
  195.  
  196.     VAR result: Polynomial;  j: power;
  197.  
  198.     BEGIN
  199.         IF Degree (B) > Degree (A) THEN
  200.             result := B;  B := A;  A := result;
  201.         END (*IF*);
  202.         result := Copy (A);
  203.         IF B <> NIL THEN
  204.             FOR j := 0 TO B^.degree DO
  205.                 result^.pcoeffs^[j] := result^.pcoeffs^[j] + B^.pcoeffs^[j];
  206.             END (*FOR*);
  207.         END (*IF*);
  208.         Reduce (result);
  209.         Destroy (C);  C := result;
  210.     END Add;
  211.  
  212. (************************************************************************)
  213.  
  214. PROCEDURE Negate (P: Polynomial);
  215.  
  216.     (* P := -P.  This is an in-place operation, i.e. the original       *)
  217.     (* value of P is overwritten.                                       *)
  218.  
  219.     VAR j: power;
  220.  
  221.     BEGIN
  222.         IF P <> NIL THEN
  223.             FOR j := 0 TO P^.degree DO
  224.                 P^.pcoeffs^[j] := -P^.pcoeffs^[j];
  225.             END (*FOR*);
  226.         END (*IF*);
  227.     END Negate;
  228.  
  229. (************************************************************************)
  230.  
  231. PROCEDURE Sub (A, B: Polynomial;  VAR (*INOUT*) C: Polynomial);
  232.  
  233.     (* Computes C := A - B. *)
  234.  
  235.     VAR temp: Polynomial;
  236.  
  237.     BEGIN
  238.         temp := Copy (B);  Negate (temp);
  239.         Add (A, temp, C);
  240.         Destroy (temp);
  241.     END Sub;
  242.  
  243. (************************************************************************)
  244.  
  245. PROCEDURE Mul (A, B: Polynomial;  VAR (*INOUT*) C: Polynomial);
  246.  
  247.     (* Computes C := A*B. *)
  248.  
  249.     VAR result: Polynomial;  j, k, M, N, first, last: power;
  250.         sum: CoeffType;
  251.  
  252.     BEGIN
  253.         IF (A = NIL) OR (B = NIL) THEN
  254.             result := NIL;
  255.         ELSE
  256.             M := A^.degree;  N := B^.degree;
  257.             result := Make (M+N);
  258.             FOR k := 0 TO M+N DO
  259.                 sum := 0.0;
  260.                 IF k < N THEN first := 0 ELSE first := k-N END(*IF*);
  261.                 IF k > M THEN last := M ELSE last := k END(*IF*);
  262.                 FOR j := first TO last DO
  263.                     sum := sum + A^.pcoeffs^[j] * B^.pcoeffs^[k-j];
  264.                 END (*FOR*);
  265.                 result^.pcoeffs^[k] := sum;
  266.             END (*FOR*);
  267.         END (*IF*);
  268.         Destroy (C);  C := result;
  269.     END Mul;
  270.  
  271. (************************************************************************)
  272.  
  273. PROCEDURE Div (A, B: Polynomial;  VAR (*INOUT*) Q, R: Polynomial);
  274.  
  275.     (* Computes A/B.  On return the quotient is Q and the       *)
  276.     (* remainder is R.                                          *)
  277.  
  278.     VAR quot, rem: Polynomial;  j, k: power;
  279.         scale: CoeffType;
  280.  
  281.     BEGIN
  282.         IF B = NIL THEN
  283.             Error ("Division by zero");  RETURN;
  284.         END (*IF*);
  285.         rem := Copy(A);
  286.         IF Degree(A) < Degree(B) THEN
  287.             quot := NIL;
  288.         ELSE
  289.             quot := Make (A^.degree - B^.degree);
  290.             FOR j := quot^.degree TO 0 BY -1 DO
  291.                 scale := rem^.pcoeffs^[j+B^.degree] / B^.pcoeffs^[B^.degree];
  292.                 quot^.pcoeffs^[j] := scale;
  293.                 FOR k := 0 TO B^.degree-1 DO
  294.                     rem^.pcoeffs^[j+k] := rem^.pcoeffs^[j+k]
  295.                                                 - scale * B^.pcoeffs^[k];
  296.                 END (*FOR*);
  297.                 rem^.pcoeffs^[j+B^.degree] := 0.0;
  298.             END (*FOR*);
  299.         END (*IF*);
  300.         Reduce (quot);  Reduce (rem);
  301.         Destroy (Q);  Q := quot;
  302.         Destroy (R);  R := rem;
  303.     END Div;
  304.  
  305. (************************************************************************)
  306. (*              EVALUATING A POLYNOMIAL AT A GIVEN POINT                *)
  307. (************************************************************************)
  308.  
  309. PROCEDURE EvalR (P: Polynomial;  x: LONGREAL): LONGREAL;
  310.  
  311.     (* Returns P(x), for real x. *)
  312.  
  313.     VAR sum: LONGREAL;  j: power;
  314.  
  315.     BEGIN
  316.         sum := 0.0;
  317.         IF P <> NIL THEN
  318.             FOR j := P^.degree TO 0 BY -1 DO
  319.                 sum := P^.pcoeffs^[j] + x * sum;
  320.             END (*FOR*);
  321.         END (*IF*);
  322.         RETURN sum;
  323.     END EvalR;
  324.  
  325. (************************************************************************)
  326.  
  327. PROCEDURE Eval (P: Polynomial;  x: LONGCOMPLEX): LONGCOMPLEX;
  328.  
  329.     (* Returns P(x), for complex x. *)
  330.  
  331.     VAR sum: LONGCOMPLEX;  j: power;
  332.  
  333.     BEGIN
  334.         sum := CMPLX (0.0, 0.0);
  335.         IF P <> NIL THEN
  336.             FOR j := P^.degree TO 0 BY -1 DO
  337.                 sum := CMPLX (P^.pcoeffs^[j], 0.0) + x*sum;
  338.             END (*FOR*);
  339.         END (*IF*);
  340.         RETURN sum;
  341.     END Eval;
  342.  
  343. (************************************************************************)
  344.  
  345. PROCEDURE EvalD (P: Polynomial;  x: LONGCOMPLEX;
  346.                                 VAR (*OUT*) value, derivative: LONGCOMPLEX);
  347.  
  348.     (* Returns P(x) and the derivative of P at x. *)
  349.  
  350.     VAR j: power;  cj, jval: LONGCOMPLEX;
  351.  
  352.     BEGIN
  353.         value := CMPLX (0.0, 0.0);
  354.         derivative := value;
  355.         IF P <> NIL THEN
  356.             FOR j := P^.degree TO 1 BY -1 DO
  357.                 cj := CMPLX (P^.pcoeffs^[j], 0.0);
  358.                 jval := CMPLX (VAL(CoeffType, j), 0.0);
  359.                 value := cj + x*value;
  360.                 derivative := jval*cj + x*derivative;
  361.             END (*FOR*);
  362.             value := CMPLX (P^.pcoeffs^[0], 0.0) + x*value;
  363.         END (*IF*);
  364.     END EvalD;
  365.  
  366. (************************************************************************)
  367. (*                      ROOTS OF POLYNOMIALS                            *)
  368. (************************************************************************)
  369.  
  370. PROCEDURE Newton (P: Polynomial;  VAR (*INOUT*) root: LONGCOMPLEX);
  371.  
  372.     (* Improves an initial guess at a root of the equation P(x) = 0 by  *)
  373.     (* Newton's method.  We assume that the input value of root is      *)
  374.     (* close enough to make Newton's method appropriate.                *)
  375.  
  376.     VAR val, deriv, step: LONGCOMPLEX;
  377.  
  378.     BEGIN
  379.         LOOP
  380.             EvalD (P, root, val, deriv);
  381.             IF abs(deriv) < small THEN
  382.                 (* We're not going to converge from here *)
  383.                 EXIT (*LOOP*);
  384.             END (*IF*);
  385.             step := val/deriv;
  386.             IF abs(step) < small THEN
  387.                 (* We've converged. *)
  388.                 EXIT (*LOOP*);
  389.             END (*IF*);
  390.             root := root - step;
  391.         END (*LOOP*);
  392.     END Newton;
  393.  
  394. (************************************************************************)
  395.  
  396. PROCEDURE CxMueller (P: Polynomial;  x0, x1: LONGCOMPLEX;
  397.                                         VAR (*INOUT*) root: LONGCOMPLEX);
  398.  
  399.     (* A version of Mu"ller's algorithm (see below) using complex       *)
  400.     (* arithmetic.  The starting points are x0,x1,root, and the final   *)
  401.     (* result is root.                                                  *)
  402.  
  403.     VAR y0, y1, y2: LONGCOMPLEX;
  404.         a, b, prevstep, step, step1, step2, olddelta, delta: LONGCOMPLEX;
  405.  
  406.     BEGIN
  407.         prevstep := x1 - x0;
  408.         step := root - x1;
  409.         y0 := Eval (P, x0);
  410.         y1 := Eval (P, x1);
  411.         olddelta := (y1-y0)/prevstep;
  412.         LOOP
  413.             y2 := Eval (P, root);
  414.             delta := (y2-y1)/step;
  415.             a := (delta-olddelta)/(prevstep+step);
  416.             b := delta + step*a;
  417.             prevstep := step;
  418.  
  419.             step := sqrt (b*b - scalarMult(4.0,a)*y2);
  420.             step1 := b + step;
  421.             step2 := b - step;
  422.             IF abs(step1) >= abs(step2) THEN
  423.                 step := step1;
  424.             ELSE
  425.                 step := step2;
  426.             END (*IF*);
  427.             IF abs(step) < small THEN
  428.                 EXIT (*LOOP*);
  429.             END (*IF*);
  430.             step := scalarMult(-2.0,y2) / step;
  431.             IF abs(step) < small THEN
  432.                 EXIT (*LOOP*);
  433.             END (*IF*);
  434.             root := root + step;
  435.             y1 := y2;  olddelta := delta;
  436.         END (*LOOP*);
  437.     END CxMueller;
  438.  
  439. (************************************************************************)
  440.  
  441. PROCEDURE Mueller (P: Polynomial;  VAR (*OUT*) root: LONGCOMPLEX);
  442.  
  443.     (* Finds one root of the equation P(x) = 0 by Mu"llers method.      *)
  444.  
  445.     (* Method: we fit a quadratic to three approximations x0, x1, x2.   *)
  446.     (* (Initially these can be very poor approximations).  Then we      *)
  447.     (* throw away x0 and take one root of the quadratic as the third    *)
  448.     (* approximation.  We use real arithmetic for as long as possible,  *)
  449.     (* and switch to complex arithmetic only when it's unavoidable.     *)
  450.  
  451.     CONST Initialx0 = -1.0;
  452.           Initialx1 = 0.0;
  453.           Initialx2 = 1.0;
  454.  
  455.     VAR x0, x1, x2, y0, y1, y2: LONGREAL;
  456.         a, b, discr, prevstep, step, olddelta, delta: LONGREAL;
  457.  
  458.     BEGIN
  459.         (* First eliminate a pathological case: a root at the origin.   *)
  460.  
  461.         IF ABS(P^.pcoeffs^[0]) < small THEN
  462.             root := CMPLX (0.0, 0.0);  RETURN;
  463.         END (*IF*);
  464.  
  465.         x0 := Initialx0;
  466.         x1 := Initialx1;
  467.         x2 := Initialx2;
  468.  
  469.         y1 := EvalR (P, Initialx1);
  470.  
  471.         (* Another troublesome case is where P(x) has the same value    *)
  472.         (* at all three points.  The following loop is designed to      *)
  473.         (* avoid that situation.                                        *)
  474.  
  475.         LOOP
  476.             y0 := EvalR (P, x0);
  477.             IF ABS(y1-y0) > small THEN EXIT(*LOOP*) END(*IF*);
  478.             x0 := 2.0*x0;
  479.         END (*LOOP*);
  480.  
  481.         step := Initialx2 - Initialx1;
  482.         prevstep := Initialx1 - x0;
  483.         olddelta := (y1-y0)/prevstep;
  484.  
  485.         (* Now for the main loop. *)
  486.  
  487.         LOOP
  488.             y2 := EvalR (P, x2);
  489.             delta := (y2-y1)/step;
  490.             a := (delta - olddelta)/(prevstep+step);
  491.             b := delta + step*a;
  492.             prevstep := step;
  493.  
  494.             (* We've fitted the quadratic                               *)
  495.             (*          y = a(x-x2)^2 + b(x-x2) + y2                    *)
  496.             (* to the three sample points.  Now solve this for y = 0.   *)
  497.             (* We choose the solution closest to x2.                    *)
  498.  
  499.             discr := b*b - 4.0*a*y2;
  500.             IF discr < 0.0 THEN
  501.                 (* Need to go to a complex solution. *)
  502.                 EXIT (*LOOP*);
  503.             END (*IF*);
  504.             IF b >= 0.0 THEN
  505.                 step := b + Sqrt(discr);
  506.             ELSE
  507.                 step := b - Sqrt(discr);
  508.             END (*IF*);
  509.             IF ABS(step) < small THEN
  510.                 (* Either we've converged (if y2 is small), or the      *)
  511.                 (* calculation is about to blow up.                     *)
  512.                 EXIT (*LOOP*);
  513.             END (*IF*);
  514.             step := -2.0*y2/step;
  515.             IF ABS(step) < small THEN
  516.                 (* We've converged. *)
  517.                 EXIT (*LOOP*);
  518.             END (*IF*);
  519.  
  520.             x1 := x2;  x2 := x2 + step;
  521.             y1 := y2;  olddelta := delta;
  522.  
  523.         END (*LOOP*);
  524.  
  525.         IF discr >= 0.0 THEN
  526.  
  527.             (* We've found a real root. *)
  528.  
  529.             root := CMPLX (x2, 0.0);
  530.  
  531.         ELSE
  532.             (* We have to continue the calculation using complex        *)
  533.             (* arithmetic.                                              *)
  534.  
  535.             root := CMPLX(x2,0.0) - CMPLX(2.0*y2,0.0) / CMPLX(b, Sqrt(-discr));
  536.             CxMueller (P, CMPLX(x1,0.0), CMPLX(x2,0.0), root);
  537.  
  538.         END (*IF*);
  539.  
  540.     END Mueller;
  541.  
  542. (************************************************************************)
  543.  
  544. PROCEDURE SolveQuadratic (P: Polynomial;
  545.                                 VAR (*OUT*) roots: ARRAY OF LONGCOMPLEX);
  546.  
  547.     (* Finds both solutions to the quadratic equation P(x) = 0.  This   *)
  548.     (* procedure should be called only if P has degree 2.               *)
  549.  
  550.     VAR a, b, c, discr: LONGREAL;
  551.  
  552.     BEGIN
  553.         a := P^.pcoeffs^[2];
  554.         b := 0.5 * P^.pcoeffs^[1];
  555.         c := P^.pcoeffs^[0];
  556.         discr := b*b - a*c;
  557.         IF discr < 0.0 THEN
  558.  
  559.             (* Complex roots. *)
  560.  
  561.             roots[0] := CMPLX (-b/a, Sqrt(-discr) / a);
  562.             roots[1] := conj (roots[0]);
  563.  
  564.         ELSE
  565.  
  566.             (* A pair of real roots.  For best accuracy we compute the  *)
  567.             (* larger root first, and get the other from the fact that  *)
  568.             (* the product of the roots is c/a.                         *)
  569.  
  570.             IF b < 0.0 THEN
  571.                 discr := (-b + Sqrt(discr))/a;
  572.             ELSE
  573.                 discr := (-b - Sqrt(discr))/a;
  574.             END (*IF*);
  575.             roots[0] := CMPLX (discr, 0.0);
  576.             roots[1] := CMPLX (c/discr/a, 0.0);
  577.         END (*IF*);
  578.  
  579.     END SolveQuadratic;
  580.  
  581. (************************************************************************)
  582.  
  583. PROCEDURE FindApproxRoots (P: Polynomial;
  584.                                 VAR (*OUT*) roots: ARRAY OF LONGCOMPLEX);
  585.  
  586.     (* Finds all (we hope) the solutions to P(x) = 0.  The results are  *)
  587.     (* not guaranteed to be especially accurate; the caller should      *)
  588.     (* probably refine the solutions.                                   *)
  589.     (* Assumption: P <> NIL.                                            *)
  590.  
  591.     VAR factor, quot, rem: Polynomial;
  592.         oneroot: LONGCOMPLEX;  re, im: LONGREAL;
  593.  
  594.     BEGIN
  595.         IF P^.degree = 1 THEN
  596.             roots[0] := CMPLX ( -P^.pcoeffs^[0]/P^.pcoeffs^[1], 0.0);
  597.         ELSIF P^.degree = 2 THEN
  598.             SolveQuadratic (P, roots);
  599.         ELSE
  600.             Init (quot);  Init (rem);
  601.  
  602.             (* Degree of P is greater than 2.  First find one root      *)
  603.             (* by Mu"ller's method.                                     *)
  604.  
  605.             (* Checkpoint ("FindApproxRoots, before Mueller call"); *)
  606.  
  607.             Mueller (P, oneroot);
  608.  
  609.             (* Checkpoint ("FindApproxRoots, after Mueller call"); *)
  610.  
  611.             IF ABS(IM(oneroot)) < small THEN
  612.  
  613.                 (* One real root found. *)
  614.  
  615.                 oneroot := CMPLX (RE(oneroot), 0.0);
  616.                 roots[P^.degree-1] := oneroot;
  617.                 factor := Make(1);
  618.                 factor^.pcoeffs^[0] := -RE(oneroot);
  619.                 factor^.pcoeffs^[1] := 1.0;
  620.             ELSE
  621.                 (* Complex conjugate pair found. *)
  622.  
  623.                 roots[P^.degree-1] := oneroot;
  624.                 roots[P^.degree-2] := conj(oneroot);
  625.                 factor := Make(2);
  626.                 re := RE (oneroot);
  627.                 im := IM (oneroot);
  628.                 WITH factor^ DO
  629.                     pcoeffs^[0] := re*re + im*im;
  630.                     pcoeffs^[1] := -2.0*re;
  631.                     pcoeffs^[2] := 1.0;
  632.                 END (*WITH*);
  633.             END (*IF*);
  634.  
  635.             (* Checkpoint ("FindApproxRoots, before deflation"); *)
  636.  
  637.             (* Deflate the polynomial, and find the other roots. *)
  638.  
  639.             Div (P, factor, quot, rem);
  640.             Destroy (factor);  Destroy (rem);
  641.             FindApproxRoots (quot, roots);
  642.             Destroy (quot);
  643.  
  644.         END (*IF*);
  645.  
  646.     END FindApproxRoots;
  647.  
  648. (************************************************************************)
  649.  
  650. PROCEDURE FindRoots (P: Polynomial;  VAR (*OUT*) roots: ARRAY OF LONGCOMPLEX);
  651.  
  652.     (* Finds all (we hope) the solutions to P(x) = 0. *)
  653.  
  654.     VAR j: power;
  655.  
  656.     BEGIN
  657.         IF P <> NIL THEN
  658.  
  659.             FindApproxRoots (P, roots);
  660.  
  661.             (* The polynomial deflations can create a lot of    *)
  662.             (* cumulative rounding error; so now we go back and *)
  663.             (* improve the estimates, working directly from the *)
  664.             (* original polynomial.                             *)
  665.  
  666.             FOR j := 0 TO P^.degree-1 DO
  667.                 Newton (P, roots[j]);
  668.             END (*FOR*);
  669.  
  670.         END (*IF*);
  671.  
  672.     END FindRoots;
  673.  
  674. (************************************************************************)
  675. (*                          SCREEN OUTPUT                               *)
  676. (************************************************************************)
  677.  
  678. PROCEDURE Write (P: Polynomial;  places, linesize: CARDINAL);
  679.  
  680.     (* Writes P to the screen, where each coefficient is allowed to be  *)
  681.     (* up to "places" characters wide, and "linesize" is the number of  *)
  682.     (* characters allowed before we have to wrap onto a new line.       *)
  683.  
  684.     CONST Nul = CHR(0);  Space = ' ';
  685.  
  686.     TYPE subscript = [0..8191];
  687.          WhichHalf = (upper, lower);
  688.  
  689.     VAR buffer: POINTER TO ARRAY WhichHalf,subscript OF CHAR;
  690.         nextloc, checkpoint: subscript;
  691.  
  692.     (********************************************************************)
  693.  
  694.     PROCEDURE FlushBuffer;
  695.  
  696.         (* Writes out the buffer contents to the current line.  If the  *)
  697.         (* buffer contains characters beyond the checkpoint, we put out *)
  698.         (* everything up the checkpoint, and reshuffle the buffer       *)
  699.         (* contents so that it's now loaded with what has to go out on  *)
  700.         (* the next line.                                               *)
  701.  
  702.         VAR j: WhichHalf;  k: subscript;  temp: CHAR;
  703.  
  704.         BEGIN
  705.             temp := "?";        (* to suppress a compiler warning *)
  706.             FOR j := upper TO lower DO
  707.                 IF checkpoint < linesize THEN
  708.                     temp := buffer^[j][checkpoint];
  709.                     buffer^[j][checkpoint] := Nul;
  710.                 END (*IF*);
  711.                 WriteString (buffer^[j]);
  712.                 IF checkpoint < nextloc THEN
  713.                     buffer^[j][0] := temp;
  714.                     FOR k := 1 TO nextloc-checkpoint-1 DO
  715.                         buffer^[j][k] := buffer^[j][k+checkpoint];
  716.                     END (*FOR*);
  717.                 END (*IF*);
  718.                 WriteLn;
  719.             END (*FOR*);
  720.             DEC (nextloc, checkpoint);  checkpoint := 0;
  721.         END FlushBuffer;
  722.  
  723.     (********************************************************************)
  724.  
  725.     PROCEDURE PutChar (hilo: WhichHalf;  ch: CHAR);
  726.  
  727.         (* Appends ch to either the upper or lower half of the buffer.  *)
  728.         (* Corresponding positions in the other half are space-filled.  *)
  729.  
  730.         BEGIN
  731.             IF nextloc >= linesize THEN
  732.                 FlushBuffer;
  733.             END (*IF*);
  734.             buffer^[hilo,nextloc] := ch;
  735.             buffer^[VAL(WhichHalf,1-ORD(hilo)), nextloc] := Space;
  736.             INC (nextloc);
  737.         END PutChar;
  738.  
  739.     (********************************************************************)
  740.  
  741.     PROCEDURE PutString (hilo: WhichHalf;  str: ARRAY OF CHAR;  from: CARDINAL);
  742.  
  743.         (* Appends str[from..] to either the upper or lower half of the *)
  744.         (* buffer.                                                      *)
  745.         (* Corresponding positions in the other half are space-filled.  *)
  746.  
  747.         VAR j: subscript;
  748.  
  749.         BEGIN
  750.             j := from;
  751.             WHILE (j <= HIGH(str)) AND (str[j] <> Nul) DO
  752.                 PutChar (hilo, str[j]);  INC (j);
  753.             END (*WHILE*);
  754.         END PutString;
  755.  
  756.     (********************************************************************)
  757.  
  758.     PROCEDURE PutPower (value: power);
  759.  
  760.         (* Appends a number to the upper half of the buffer.    *)
  761.  
  762.         BEGIN
  763.             IF value > 9 THEN
  764.                 PutPower (value DIV 10);
  765.             END (*IF*);
  766.             PutChar (upper, CHR (ORD("0") + value MOD 10));
  767.         END PutPower;
  768.  
  769.     (********************************************************************)
  770.  
  771.     PROCEDURE PutCoeff (value: CoeffType);
  772.  
  773.         (* Appends a number to the lower half of the buffer.    *)
  774.  
  775.         VAR localbuff: ARRAY [0..79] OF CHAR;  start: CARDINAL;
  776.  
  777.         BEGIN
  778.             LongRealToString (value, localbuff, places);
  779.             start := 0;
  780.             WHILE localbuff[start] = Space DO INC(start) END(*WHILE*);
  781.             PutString (lower, localbuff, start);
  782.         END PutCoeff;
  783.  
  784.     (********************************************************************)
  785.  
  786.     PROCEDURE PutTerm (coeff: CoeffType;  power: CARDINAL;  leading: BOOLEAN);
  787.  
  788.         (* Appends a term coeff*X^power to the buffer.  The final       *)
  789.         (* parameter specifies whether to suppress a leading "+" sign.  *)
  790.  
  791.         BEGIN
  792.             PutChar (lower, Space);
  793.             IF coeff < 0.0 THEN
  794.                 PutChar (lower, '-');  coeff := -coeff;
  795.             ELSIF NOT leading THEN
  796.                 PutChar (lower, '+');
  797.             END (*IF*);
  798.             PutChar (lower, Space);
  799.             IF (power = 0) OR (ABS(coeff - 1.0) >= small) THEN
  800.                 PutCoeff (coeff);
  801.             END (*IF*);
  802.             IF power > 0 THEN
  803.                 PutChar (lower, "X");
  804.                 IF power > 1 THEN
  805.                     PutPower (power);
  806.                 END (*IF*);
  807.             END (*IF*);
  808.             checkpoint := nextloc;
  809.         END PutTerm;
  810.  
  811.     (********************************************************************)
  812.  
  813.     VAR j: power;
  814.  
  815.     BEGIN       (* Body of procedure Write *)
  816.         ALLOCATE (buffer, 2*linesize);
  817.         nextloc := 0;  checkpoint := 0;
  818.         IF P = NIL THEN
  819.             PutTerm (0.0, 0, TRUE);
  820.         ELSE
  821.  
  822.             (* The leading coefficient requires special treatment,      *)
  823.             (* so that we can avoid putting out a leading '+' sign.     *)
  824.  
  825.             PutTerm (P^.pcoeffs^[P^.degree], P^.degree, TRUE);
  826.             FOR j := P^.degree-1 TO 0 BY -1 DO
  827.                 IF ABS(P^.pcoeffs^[j]) >= small THEN
  828.                     PutTerm (P^.pcoeffs^[j], j, FALSE);
  829.                 END (*IF*);
  830.             END (*FOR*);
  831.         END (*IF*);
  832.  
  833.         (* Write out whatever is still left in the buffer. *)
  834.  
  835.         IF nextloc > 0 THEN
  836.             FlushBuffer;
  837.         END (*IF*);
  838.         DEALLOCATE (buffer, 2*linesize);
  839.  
  840.     END Write;
  841.  
  842. (************************************************************************)
  843.  
  844. END Poly.
  845.  
  846.