program alg028;
{  MULLER'S METHOD

   To find a solution to f(x) = 0 given three approximations x0, x1
   and x2:

   INPUT:  x0,x1,x2; tolerance TOL; maximum number of iterations NO.

   OUTPUT: approximate solution p or message of failure.

   This implementation allows for a switch to complex arithmetic.
   The coefficients are stored in the vector A, so the dimension
   of A may have to be changed.                                        }
const
   ZERO = 1.0e-20;
var
   A : array[1..50] of real;
   ZR,ZI,GR,GI,F,X : array[1..4] of real;
   CH1R,CH1I,H : array [1..3] of real;
   CDEL1R,CDEL1I,DEL1 : array[1..2] of real;
   CDELR,CDELI,CBR,CBI,CDR,CDI,CER,CEI : real;
   DEL,B,D,E,TOL : real;
   QR,QI,ER,EI,FR,FI : real;
   FLAG,N,M,I,J,K,ISW,KK : integer;
   OK : boolean;
   NAME : string [ 14 ];
   INP, OUP : text;
   AA : char;
procedure CADD ( A,B,C,D : real; var E,F : real );
   begin
      E := A + C;
      F := B + D
   end;
procedure CMULT ( A,B,C,D : real; var E,F : real );
   begin
      E := ( A * C ) - ( B * D );
      F := A * D + B * C
   end;
procedure CDIV ( A,B,C,D : real; var E,F : real );
   var
      G : real;
   begin
      G := C * C + D * D;
      E := ( A * C + B * D ) / G;
      F := ( B * C - A * D ) / G
   end;
function CABS ( A,B : real ) : real;
   var
      C : real;
   begin
      C := sqrt(A * A + B * B);
      CABS := C
   end;
 procedure CSQRT ( A,B : real; var C,D : real );
   const
      ZERO = 1.0E-20;
   var
      G,R,T,HP : real;
   begin
      HP := 0.5*pi;
      if ( abs( A ) <= ZERO ) then
         begin
            if ( abs( B ) <= ZERO ) then
               begin
                  R := 0.0;
                  T := 0.0
               end
            else
               begin
                  T := HP;
                  if ( B < 0.0 ) then T := -T;
                  R := abs( B )
               end
         end
      else
         begin
            R :=  sqrt(A * A + B * B) ;
            if (abs(B) < ZERO) then
               begin
                  T := 0.0;
                  if (A < 0.0) then T := pi
               end
            else
               begin
                  T := arctan( B / A );
                  if (A < 0.0) then T := T + pi
               end
         end;
      G := sqrt( R );
      C := G * cos( 0.5 * T );
      D := G * sin( 0.5 * T )
   end;
procedure CSUB ( A,B,C,D : real; var E,F : real );
   begin
      E := A - C;
      F := B - D
   end;
procedure INPUT;
   begin
      OK := false;
      while ( not OK ) do
         begin
            writeln ('Choice of input method: ');
            writeln ('1. Input entry by entry from keyboard ');
            writeln ('2. Input data from a text file ');
            writeln ('Choose 1 or 2 please ');
            readln ( FLAG );
            if ( FLAG = 1 ) or ( FLAG = 2 ) then OK := true
         end;
      case FLAG of
         1 : begin
                OK := false;
                while ( not OK ) do
                   begin
                      writeln('Input the degree n of the polynomial');
                      readln(N);
                      if ( N > 0 ) then
                         begin
                            OK := true;
                            write('Input the coefficients of the');
                            writeln(' polynomial in ascending order');
                            writeln('of exponent at the prompt');
                            N := N+1;
                            for I := 1 to N do
                               begin
                                  J := I-1;
                                  writeln('Input A( ',J,' )');
                                  readln(A[I])
                               end
                         end
                      else writeln(' n must be a positive integer.');
                   end;
             end;
         2 : begin
                writeln('Is there a text file containing the coefficients');
                writeln('in ascending order of exponent?');
                writeln ('Enter Y or N ');
                readln ( AA );
                if ( AA = 'Y' ) or ( AA = 'y' ) then
                   begin
                      OK := true;
                      write ('Input the file name in the form - ');
                      writeln ('drive:name.ext ');
                      writeln ('for example:   A:DATA.DTA ');
                      readln ( NAME );
                      assign ( INP, NAME );
                      reset ( INP );
                      OK := false;
                      while ( not OK ) do
                         begin
                            writeln ('Input n');
                            readln ( N );
                            if ( N > 0 ) then
                               begin
                                  OK := true;
                                  N := N+1;
                                  for I := 1 to N do
                                     read ( INP,A[I]);
                                  close ( INP )
                               end
                            else writeln ('Number must be a positive integer ')
                         end
                   end
                else
                   begin
                      writeln ('Please create the input file.');
                      writeln ('The program will end so the input file can ');
                      writeln ('be created. ');
                      OK := false
                   end
             end
      end;
      if OK then
         begin
            OK := false;
            while ( not OK ) do
               begin
                  writeln ('Input tolerance ');
                  readln ( TOL );
                  if (TOL <= 0.0) then
                     writeln ('Tolerance must be positive ')
                  else OK := true
               end;
            OK := false;
            while ( not OK ) do
               begin
                  write('Input maximum number of iterations ');
                  writeln('- no decimal point ');
                  readln ( M );
                  if ( M <= 0 ) then
                     writeln ('Must be positive integer ')
                  else OK := true
               end;
            writeln('Input the first of three starting values');
            readln(X[1]);
            writeln('Input the second of three starting values');
            readln(X[2]);
            writeln('Input the third starting value');
            readln(X[3]);
         end;
   end;
procedure OUTPUT;
   begin
      writeln ('Select output destination ');
      writeln ('1. Screen ');
      writeln ('2. Text file ');
      writeln ('Enter 1 or 2 ');
      readln ( FLAG );
      if ( FLAG = 2 ) then
         begin
            write ('Input the file name in the form - ');
            writeln ('drive:name.ext ');
            writeln ('for example:   A:OUTPUT.DTA ');
            readln ( NAME );
            assign ( OUP, NAME )
         end
      else assign ( OUP, 'CON');
      rewrite ( OUP );
      writeln(OUP,'MULLERS METHOD');
      writeln(OUP,'The output is i, approximation x(i), f(x(i))');
      writeln(OUP);
   end;
begin
   writeln('This is Mullers Method.');
   INPUT;
   if OK then
     begin
        OUTPUT;
{  Evaluate F using Horner's Method and save in the vector F           }
        for I := 1 to 3 do
           begin
              F[I] := A[N];
              for J := 2 to N do
                 begin
                    K := N-J+1;
                    F[I] := F[I]*X[I]+A[K]
                 end
           end;
{  Variable ISW is used to note a switch to complex arithmetic
    ISW=0 means real arithmetic, and ISW=1 means complex arithmetic    }
        ISW := 0;
{  STEP 1                                                              }
        H[1] := X[2]-X[1];
        H[2] := X[3]-X[2];
        DEL1[1] := (F[2]-F[1])/H[1];
        DEL1[2] := (F[3]-F[2])/H[2];
        DEL := (DEL1[2]-DEL1[1])/(H[2]+H[1]);
        I := 3;
        OK := true;
{  STEP 2                                                              }
        while ((I <= M) and (OK)) do
           begin
{        STEPS 3-7 for real arithmetic                                 }
              if (ISW = 0) then
                 begin
{              STEP 3                                                  }
                    B := DEL1[2]+H[2]*DEL;
                    D := B*B-4.0*F[3]*DEL;
{              test to see if need complex arithmetic                  }
                    if (D >= 0.0) then
                       begin
{                   real arithmetic - test to see if straight line     }
                          if (abs(DEL) <= ZERO) then
                             begin
{                          straight line - test if horizontal line     }
                                   if (abs(DEL1[2]) <= ZERO) then
                                      begin
                                         writeln('Horizontal Line');
                                         OK := false
                                      end
                                   else
                                      begin
{                                straight line but not horizontal      }
                                         X[4] := (F[3]-DEL1[2]*X[3])
                                              /DEL1[2];
                                         H[3] := X[4]-X[3]
                                      end
                             end
                          else
                             begin
{                          not straight line                           }
                                D := sqrt(D);
{                          STEP 4                                      }
                                E := B+D;
                                if (abs(B-D) > abs(E)) then E := B-D;
{                          STEP 5                                      }
                                H[3] := -2.0*F[3]/E;
                                X[4] := X[3]+H[3]
                             end;
                          if (OK) then
                             begin
{                          evaluate f(x(2))=F[4] by Horner's method    }
                                F[4] := A[N];
                                for J := 2 to N do
                                   begin
                                      K := N-J+1;
                                      F[4] := F[4]*X[4]+A[K]
                                   end;
                                writeln(OUP,I,X[4],F[4]);
{                          STEP 6                                      }
                                if (abs(H[3]) < TOL) then
                                   begin
                                      writeln(OUP);
                                      writeln(OUP,'Method Succeeds');
                                      write(OUP,'Approximation is within ');
                                      writeln(OUP,TOL);
                                      writeln(OUP,'in ',I,' iterations');
                                      OK := false
                                   end
                                else
{                             STEP 7                                   }
                                   begin
                                      for J := 1 to 2 do
                                         begin
                                            H[J] := H[J+1];
                                            X[J] := X[J+1];
                                            F[J] := F[J+1]
                                         end;
                                      X[3] := X[4];
                                      F[3] := F[4];
                                      DEL1[1] := DEL1[2];
                                      DEL1[2] := (F[3]-F[2])/H[2];
                                      DEL := (DEL1[2]-DEL1[1])
                                           /(H[2]+H[1])
                                   end
                             end
                       end
                    else
                       begin
{                    switch to complex arithmetic                      }
                          ISW := 1;
                          for J := 1 to 3 do
                             begin
                                ZR[J] := X[J]; ZI[J] := 0.0;
                                GR[J] := F[J]; GI[J] := 0.0
                             end;
                          for J := 1 to 2 do
                             begin
                                CDEL1R[J] := DEL1[J]; CDEL1I[J] := 0.0;
                                CH1R[J] := H[J]; CH1I[J] := 0.0
                             end;
                          CDELR := DEL; CDELI := 0.0
                       end
                 end;
              if ((ISW = 1) and (OK)) then
{               STEPS 3-7 complex arithmetic                           }
                 begin
{              test if straight line                                   }
                    if (CABS(CDELR,CDELI) <= ZERO) then
                       begin
{                   straight line - test if horizontal line            }
                          if (CABS(CDEL1R[1],CDEL1I[1]) <= ZERO) then
                             begin
                                writeln('horizontal line - complex');
                                OK := false
                             end
                          else
{                       straight line but not horizontal               }
                             begin
                                writeln('line - not horizontal');
                                CMULT(CDEL1R[2],CDEL1I[2],
                                      ZR[3],ZI[3],ER,EI);
                                CSUB(GR[4],GI[4],ER,EI,FR,FI);
                                CDIV(FR,FI,CDEL1R[2],
                                     CDEL1I[2],ZR[4],ZI[4]);
                                CSUB(ZR[4],ZI[4],ZR[3],ZI[3],
                                     CH1R[3],CH1I[3])
                             end
                       end
                    else
{                not straight line                                     }
                       begin
{                   STEP 3                                             }
                          CMULT(CH1R[2],CH1I[2],CDELR,CDELI,ER,EI);
                          CADD(CDEL1R[2],CDEL1I[2],ER,EI,CBR,CBI);
                          CMULT(GR[3],GI[3],CDELR,CDELI,ER,EI);
                          CMULT(ER,EI,4.0,0.0,FR,FI);
                          QR := CBR; QI := CBI;
                          CMULT(CBR,CBI,QR,QI,ER,EI);
                          CSUB(ER,EI,FR,FI,CDR,CDI);
                          CSQRT(CDR,CDI,FR,FI);
{                    STEP 4                                            }
                          CDR := FR; CDI := FI;
                          CADD(CBR,CBI,CDR,CDI,CER,CEI);
                          CSUB(CBR,CBI,CDR,CDI,FR,FI);
                          if (CABS(FR,FI) > CABS(CER,CEI)) then
                              CSUB(CBR,CBI,CDR,CDI,CER,CEI);
{                    STEP 5                                            }
                          CDIV(GR[3],GI[3],CER,CEI,ER,EI);
                          CMULT(ER,EI,-2.0,0.0,CH1R[3],CH1I[3]);
                          CADD(ZR[3],ZI[3],CH1R[3],CH1I[3],ZR[4],ZI[4])
                       end;
                    if (OK) then
                       begin
{                    evaluate f(x(i))=f(4) by Horner's method          }
                          GR[4]:= A[N]; GI[4] := 0.0;
                          for J := 2 to N do
                             begin
                                K := N-J+1;
                                CMULT(GR[4],GI[4],ZR[4],ZI[4],ER,EI);
                                GR[4] := ER+A[K];
                                GI[4] := EI
                             end;
                          writeln(OUP,i:4,' ',ZR[4]:14:8,' ',ZI[4]:14:8,' ',
                                  GR[4]:14:8,' ',GI[4]:14:8);
{                       STEP 6                                         }
                          if (CABS(CH1R[3],CH1I[3]) <= TOL) then
                             begin
                                writeln(OUP);
                                writeln(OUP,'Method Succeeds');
                                writeln(OUP,'Approximation is within ',TOL);
                                writeln(OUP,'in ',I,' iterations');
                                OK := false
                             end
                          else
{                       STEP 7                                         }
                             begin
                                for J := 1 to 2 do
                                   begin
                                      CH1R[J] := CH1R[J+1];
                                      CH1I[J] := CH1I[J+1];
                                      ZR[J] := ZR[J+1];
                                      ZI[J] := ZI[J+1];
                                      GR[J] := GR[J+1];
                                      GI[J] := GI[J+1]
                                   end;
                                ZR[3] := ZR[4];
                                ZI[3] := ZI[4];
                                GR[3] := GR[4];
                                GI[3] := GI[4];
                                CDEL1R[1] := CDEL1R[2];
                                CDEL1I[1] := CDEL1I[2];
                                CSUB(GR[3],GI[3],GR[2],GI[2],ER,EI);
                                CDIV(ER,EI,CH1R[2],CH1I[2],
                                     CDEL1R[2],CDEL1I[2]);
                                CSUB(CDEL1R[2],CDEL1I[2],
                                     CDEL1R[1],CDEL1I[1],ER,EI);
                                CADD(CH1R[2],CH1I[2],CH1R[1],
                                     CH1I[1],FR,FI);
                                CDIV(ER,EI,FR,FI,CDELR,CDELI)
                             end
                       end
                 end;
{         STEP 7 CONTINUED                                             }
              I := I + 1
           end;
{    STEP 8                                                            }
        if ( I > M ) and OK then writeln(OUP,'Method failed');
        close(OUP)
     end
end.
