[comp.lang.pascal] Turbo 5.0 Complex ADT

parker@mars.njit.edu (Bruce Parker) (07/17/89)

Given the current discussion concerning the implementation of complex numbers
as ADTs in Turbo 5.5 Pascal, I thought the following code might be useful
to the discussion.  I have used this code as an example of an ADT in my
classes.  My feeling lately is that it is too complex, if you'll pardon
the pun, to be introduced early on in a class where ADTs are to introduced
and implemented, such as a data structures class.

My orientation is that of having taught Modula-2 for a few years, so my first
inclination was to break each ADT into two files: an interface file
(complex.int) which is included into an implementation file (complex.pas).
In constructing a library, the interface file would be available along with
the compiled object code for the ADT unit.

The entire program, including the test driver, can be compiled by typing

	tpc testcomp /m

Any comments are welcome, as I am both a software engineering and Turbo
Pascal novice.  I'd also be happy to explain or discuss any part of this.

Bruce Parker
305 Weston Hall					(201) 596-3369
Computer and Information Science Department 	parker@mars.njit.edu
New Jersey Institute of Technology
Newark, New Jersey  07102

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./complex.int`
then
echo "writing ./complex.int"
cat > ./complex.int << '\End\Of\Shar\'
(*
 * ELEMENTS     T
 *
 * DOMAIN       Complex Numbers, limited by finiteness of floating point
 *              number representation.
 *
 * STRUCTURE    Opaque
 *
 *)

TYPE
  ErrorCode = (         (* Programming errors *)
    DivisionByZero,     (* attempt to divide by zero *)
    LogarithmOfZero,    (* attempt to take the logarithm of zero *)
    Undefined,          (* undefined value, e.g., Argument( 0 ) *)
    NoError
    );

PROCEDURE Create(
(* out *)       VAR z   : T
        );
(*
 * MODIFIES     allocates z
 *)

PROCEDURE Destroy(
(* in/out *)    VAR z   : T
          );
(*
 * MODIFIES     deallocates z
 *)

PROCEDURE Assign(
(* in/out *)    VAR z	: T;
(* in *)        x       : REAL;
(* in *)        y       : REAL
        );
(*
 * REQUIRES     Defined( z )
 * MODIFIES     z's real and imaginary parts are set to x and y, respectively.
 *)

FUNCTION RealPart(
(* in *)        z       : T
        ): REAL;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns real part of complex number z
 *)

FUNCTION ImaginaryPart(
(* in *)        z       : T
        ): REAL;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns imaginary part of complex number z
 *)

FUNCTION Modulus(
(* in *)        z       : T
        ): REAL;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns modulus of complex number z
 *)

FUNCTION Argument(
(* in *)        z       : T;
(* out *) VAR   result  : REAL
       ): ErrorCode;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      sets result to complex sin of z
 * EXCEPTIONS   Undefined ( z ~ 0 )
 *)

FUNCTION Conjugate(
(* in *)        z       : T
        ): T;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns complex conjugate of z
 *)

FUNCTION Add(
(* in *)        x       : T;
(* in *)        y       : T
        ): T;
(*
 * REQUIRES     Defined( x ), Defined( y ), Assigned( x ), Assigned( y )
 * EFFECTS      returns complex sum of complex numbers x and y
 *)

FUNCTION Multiply(
(* in *)        x       : T;
(* in *)        y       : T
        ): T;
(*
 * REQUIRES     Defined( x ), Defined( y ), Assigned( x ), Assigned( y )
 * EFFECTS      returns complex product of complex numbers x and y
 *)

FUNCTION Divide(
(* in *)        x       : T;
(* in *)        y       : T;
(* out *) VAR   result  : T
        ): ErrorCode;
(*
 * REQUIRES     Defined( x ), Defined( y ), Assigned( x ), Assigned( y )
 * EFFECTS      sets result to complex quotient of complex numbers x and y
 * EXCEPTIONS   DivisionByZero
 *)

FUNCTION CExp(
(* in *)        z       : T
        ): T;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns complex exponential of complex number z
 *)

FUNCTION CLog(
(* in *)        z       : T;
(* out *) VAR   result  : T
        ): ErrorCode;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      sets result to complex logarithm of complex number z
 * EXCEPTIONS   LogarithmOfZero
 *)

FUNCTION Power(
(* in *)        x       : T;
(* in *)        y       : T;
(* out *) VAR   result  : T
        ): ErrorCode;
(*
 * REQUIRES     Defined( x ), Defined( y ), Assigned( x ), Assigned( y )
 * EFFECTS      sets result to complex number x raised to
 *                power of complex number y
 * EXCEPTIONS   LogarithmOfZero ( tries to compute Log( x ) )
 *)

FUNCTION CCos(
(* in *)        z       : T
        ): T;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns complex cosine of complex number z
 *)

FUNCTION CSin(
(* in *)        z       : T
        ): T;
(*
 * REQUIRES     Defined( z ) and Assigned( z )
 * EFFECTS      returns complex sin of complex number z
 *)
\End\Of\Shar\
else
  echo "will not over write ./complex.int"
fi
if `test ! -s ./complex.pas`
then
echo "writing ./complex.pas"
cat > ./complex.pas << '\End\Of\Shar\'
UNIT Complex;

INTERFACE

TYPE
  ComplexRecord = RECORD
    RealPart            : REAL;
    ImaginaryPart       : REAL;
  END;
  T		= ^ComplexRecord;

{$I Complex.int}

IMPLEMENTATION

(*
 * This module implements complex numbers as an abstract data type.
 * Each complex number is represented pointer to a record containing
 * a ( real, imaginary ) pair.  Disposal is left to the memory manager.
 *)

CONST
  (*
   * A feable attempt to handle "near-zero" values.
   * In truth, this is hardware dependent.  Relevant code should
   * respond to hardware exceptions, instead of second guessing
   * the hardware dependencies.  On most of 32-bit machines this
   * will be too conservative.  For small machines, this may not
   * be enough ( <-- boo! hiss! )
   *)
  Epsilon               = 1.0e-10;

PROCEDURE Create(
(* out *)	VAR z	: T
        );
BEGIN
  NEW( z );
END; (* Create *)

PROCEDURE Destroy(
(* in/out *)	VAR z	: T
        );
BEGIN
  DISPOSE( z );
END; (* Destroy *)

PROCEDURE Assign(
(* out *)	VAR z	: T;
(* in *) 	x	: REAL;
(* in *)	y	: REAL
        );
(* Assigns the parameters x and y as the real and imaginary parts,
   respectively, of the complex number z. *)
(* assumptions: z has already been created. *)
BEGIN
  z^.RealPart := x;
  z^.ImaginaryPart := y
END; (* Assign *)

FUNCTION RealPart(
(* in *)	z      : T
        ): REAL;
BEGIN
  RealPart := z^.RealPart
END; (* RealPart *)

FUNCTION ImaginaryPart(
(* in *)	z      : T
        ): REAL;
BEGIN
  ImaginaryPart := z^.ImaginaryPart
END; (* ImaginaryPart *)

FUNCTION Modulus(
(* in *)	z      : T
        ): REAL;
BEGIN
  Modulus := SQRT( z^.RealPart * z^.RealPart
		  + z^.ImaginaryPart * z^.ImaginaryPart )
END; (* Modulus *)

FUNCTION Argument(
(* in *)         z      : T;
(* out *) VAR    result : REAL
        ): ErrorCode;
BEGIN
  IF Modulus( z ) < Epsilon THEN
    Argument := Undefined
  ELSE BEGIN
    Argument := NoError;
    IF ABS( z^.ImaginaryPart ) < Epsilon THEN
      result := 0.0
    ELSE
      result := ARCTAN( z^.RealPart / z^.ImaginaryPart )
  END
END; (* Argument *)

FUNCTION Conjugate(
(* in *)	z      : T
        ): T;
VAR
  ConjugateZ    : T;
BEGIN
  Create( ConjugateZ );
  Assign( ConjugateZ, z^.RealPart, - z^.ImaginaryPart );
  Conjugate := ConjugateZ
END; (* Conjugate *)

FUNCTION Add(
(* in *)	x      : T;
(* in *)	y      : T
        ): T;
VAR
  sum   : T;
BEGIN
  Create( sum );
  Assign( sum, x^.RealPart + y^.RealPart,
               x^.ImaginaryPart + y^.ImaginaryPart );
  Add := sum
END; (* Add *)

FUNCTION Multiply(
(* in *)	x      : T;
(* in *)	y      : T
        ): T;
VAR
  product       : T;
BEGIN
  Create( product );
  Assign( product, x^.RealPart * y^.RealPart
                        - x^.ImaginaryPart * y^.ImaginaryPart,
                   x^.RealPart * y^.ImaginaryPart
                        + x^.ImaginaryPart * y^.RealPart );
  Multiply := product
END; (* Multiply *)

FUNCTION Reciprocal(
(* in *)         z      : T;
(* out *) VAR    result : T
        ): ErrorCode;
(* This function is used in computing the quotient of two complex numbers. *)
VAR
  reciprocalZ           : T;
  modulusSquared        : REAL;
BEGIN
  NEW( reciprocalZ );
  modulusSquared := z^.RealPart * z^.RealPart +
                    z^.ImaginaryPart * z^.ImaginaryPart;

  (* Ugh!  This test for division by zero should be hardware dependent. *)

  IF modulusSquared > Epsilon THEN BEGIN
    reciprocalZ^.RealPart := z^.RealPart / modulusSquared;
    reciprocalZ^.ImaginaryPart := - z^.ImaginaryPart / modulusSquared;
    result := reciprocalZ;
    Reciprocal := NoError
  END ELSE
    Reciprocal := DivisionByZero
END; (* Reciprocal *)

FUNCTION Divide(
(* in *)	    x      : T;
(* in *)	    y      : T;
(* out *) VAR result : T
        ): ErrorCode;
VAR
  reciprocalY   : T;
  exception     : ErrorCode;
BEGIN
  exception := Reciprocal( y, reciprocalY );
  Divide := exception;
  IF exception = NoError THEN
    result := Multiply( x, reciprocalY )
END; (* Divide *)

FUNCTION CExp(
(* in *)	z      : T
        ): T;
VAR
  expZ    : T;
BEGIN
  Create( expZ );
  Assign( expZ, EXP( z^.RealPart ) * COS( z^.ImaginaryPart ),
                EXP( z^.RealPart ) * SIN( z^.ImaginaryPart ) );
  CExp := expZ
END; (* CExp *)

FUNCTION CLog(
(* in *)	    z      : T;
(* out *) VAR result : T
        ): ErrorCode;
VAR
  logZ      : T;
  exception : ErrorCode;
  argumentZ : REAL;
BEGIN
  IF Modulus( z ) > Epsilon THEN BEGIN
    exception := Argument( z, argumentZ );
    CLog := exception;
    IF exception = NoError THEN BEGIN
      Create( logZ );
      Assign( logZ, LN( Modulus( z ) ), argumentZ );
      result := logZ
    END
  END ELSE
    CLog := LogarithmOfZero
END; (* Log *)

FUNCTION Power(
(* in *)      x      : T;
(* in *)      y      : T;
(* out *) VAR result : T
        ): ErrorCode;
VAR
  logX      : T;
  exception : ErrorCode;
BEGIN
  exception := CLog( x, logX );
  IF exception = NoError THEN
    result := CExp( Multiply( y, logX ) );
  Power := exception
END; (* Power *)

FUNCTION Cosh(
(* in *)      x : REAL
      ) : REAL;
BEGIN
  Cosh := ( EXP( x ) + EXP( -x ) ) / 2.0
END; (* Cosh *)

FUNCTION Sinh(
(* in *)      x : REAL
      ) : REAL;
BEGIN
  Sinh := ( EXP( x ) - EXP( -x ) ) / 2.0
END; (* Sinh *)

FUNCTION CSin(
(* in *)	z      : T
        ): T;
VAR
  sinZ    : T;
BEGIN
  Create( sinZ );
  Assign( sinZ, SIN( z^.RealPart ) * Cosh( z^.ImaginaryPart ),
                COS( z^.RealPart ) * Sinh( z^.ImaginaryPart ) );
  CSin := sinZ
END; (* CSin *)

FUNCTION CCos(
(* in *)	z      : T
        ): T;
VAR
  cosZ  : T;
BEGIN
  Create( cosZ );
  Assign( cosZ, COS( z^.RealPart ) * Cosh( z^.ImaginaryPart ),
                - SIN( z^.RealPart ) * Sinh( z^.ImaginaryPart ) );
  CCos := cosZ
END; (* CCos *)

END. (* Complex *)
\End\Of\Shar\
else
  echo "will not over write ./complex.pas"
fi
if `test ! -s ./testcomp.pas`
then
echo "writing ./testcomp.pas"
cat > ./testcomp.pas << '\End\Of\Shar\'
PROGRAM TestComplex( input, output );

(* A driver for testing the complex number dictionary module.
 * The program is self-documenting.  It has on-line documentation
 * and somewhat reasonable error messages.
 *)

USES
  Complex;

TYPE
  FormatType      = ( cartesian, polar ); (* input/output format
                                             for complex numbers. *)
  CommandType     = ( format, conjugate, add, multiply, divide, exp, power,
        log, cosine, sine, help, invalid, quit );
  CommandSet      = SET OF CommandType;

VAR
  command       : CommandType;
  complexFormat : FormatType;
  exception     : Complex.ErrorCode;
  x1            : REAL;      (* first component of first complex number *)
  x2            : REAL;      (* second component of first complex number *)
  y1            : REAL;      (* first component of second complex number *)
  y2            : REAL;      (* second component of second complex number *)
  result        : Complex.T; (* complex result of Complex function *)
  done          : BOOLEAN;
  sc            : REAL;

PROCEDURE ReadString(
(* out *) VAR   text    : STRING
        );
(*
 * REQUIRES     OPEN( Input )
 * MODIFIES     alters file pointer to position after next non-blank string
 * EFFECTS      sets text to next non-blank string of characters
 *)
VAR
  nextChar      : CHAR;
  whichChar     : INTEGER;
BEGIN
  text := '';

  (* deblank the input *)
  nextChar := ' ';
  WHILE NOT EOLN( Input ) AND ( nextChar = ' ' ) DO
    Read( Input, nextChar );

  text[ 1 ] := nextChar;
  whichChar := 1;
  WHILE ( NOT EOLN( Input ) ) AND ( nextChar <> ' ' ) DO BEGIN
    Read( Input, nextChar );
    IF nextChar <> ' ' THEN BEGIN
      whichChar := whichChar + 1;
      text[ whichChar ] := nextChar
    END
  END;
  text[ 0 ] := CHR( whichChar )
END; (* ReadString *)

PROCEDURE WriteString(
(* in *)        text    : STRING
        );
(*
 * REQUIRES     OPEN( Output )
 * MODIFIES     outputs the given non-blank string of characters to Output
 *)
VAR
  whichChar	: INTEGER;
BEGIN
  FOR whichChar := 1 TO LENGTH( text ) DO
    Write( output, text[ whichChar ] )
END; (* WriteString *)

FUNCTION GetCommand : CommandType;
(*
 * REQUIRES    Open( Input )
 * MODIFIES    alters file pointer to point to position following
 *             the current command
 * EFFECTS     returns CommandType of current command.
 *)
VAR
  inputString   : STRING;
BEGIN
  ReadString( inputString );
  IF ( inputString = 'quit' ) OR ( inputString = 'q' ) THEN
    GetCommand := quit
  ELSE IF ( inputString = 'help' ) OR ( inputString = '?' ) THEN
    GetCommand := help
  ELSE IF ( inputString = 'format' ) OR ( inputString = 'f' ) THEN
    GetCommand := format
  ELSE IF ( inputString = 'conjugate' ) OR ( inputString = 'C' ) THEN
    GetCommand := conjugate
  ELSE IF ( inputString = 'add' ) OR ( inputString = '+' ) THEN
    GetCommand := add
  ELSE IF ( inputString = 'multiply' ) OR ( inputString = '*' ) THEN
    GetCommand := multiply
  ELSE IF ( inputString = 'divide' ) OR ( inputString = '/' ) THEN
    GetCommand := divide
  ELSE IF ( inputString = 'exp' ) OR ( inputString = 'e' ) THEN
    GetCommand := exp
  ELSE IF ( inputString = 'power' ) OR ( inputString = '^' ) THEN
    GetCommand := power
  ELSE IF ( inputString = 'log' ) OR ( inputString = 'l' ) THEN
    GetCommand := log
  ELSE IF ( inputString = 'cos' ) OR ( inputString = 'c' ) THEN
    GetCommand := cosine
  ELSE IF ( inputString = 'sin' ) OR ( inputString = 's' ) THEN
    GetCommand := sine
  ELSE
    GetCommand := invalid
END; (* GetCommand *)

PROCEDURE PrintHelpMessage;
(*
 * MODIFIES     prints help message on Output
 *)
BEGIN
  WriteLn( output, '                  Command' );
  WriteLn( output, 'Command            Alias   Synopsis' );
  WriteLn( output, '-------           -------  --------' );
  WriteLn( output, 'add <z1> <z2>        +     Add <z1> and <z2>' );
  WriteLn( output, 'conjugate <z>        C     Form complex conjugate of <z>' );
  WriteLn( output, 'cos <z>              c     Cosine of <z>' );
  WriteLn( output, 'divide <z1> <z2>     /     Divide <z1> by <z2>');
  WriteLn( output, 'exp <z>              e     Raise e to the <z>th power' );
  WriteLn( output, 'help                 ?     Print this message' );
  WriteLn( output, 'format [xy|polar|?]  f     Set the complex number format' );
  WriteLn( output, '                             xy or cartesian for real-imaginary pair' );
  WriteLn( output, '                             polar for polar coordinates' );
  WriteLn( output, '                             ? to display current format' );
  WriteLn( output, 'log <z>              l     Logarithm of <z>' );
  WriteLn( output, 'multiply <z1> <z2>   *     Multiply <z1> by <z2>' );
  WriteLn( output, 'power <z1> <z2>      ^     Raise <z1> to the <z2>th power' );
  WriteLn( output, 'quit                 q     Quit' );
  WriteLn( output, 'sin <z>              s     Sine of <z>' )
END; (* PrintHelpMessage *)

FUNCTION SetValue(
(* in *) x             : REAL;
(* in *) y             : REAL;
(* in *) complexFormat : FormatType
   ) : Complex.T;
(*
 * EFFECTS      Assign the parameters x and y to a new complex number
 *              and return y.  The parameters are interpreted according
 *              to the current format, i.e., cartesian or polar coordinates.
 *)
VAR
  z : Complex.T;
BEGIN
  Complex.Create( z );
  CASE complexFormat OF
    cartesian : Complex.Assign( z, x, y );
    polar     : Complex.Assign( z, x * COS( y ), x * SIN( y ) )
  END;
  SetValue := z
END; (* SetValue *)

FUNCTION FirstComponent(
(* in *) z             : Complex.T;
(* in *) complexFormat : FormatType
      ) : REAL;
(*
 * REQUIRES     Created( z ), Assigned( z )
 * EFFECTS      return the first co-ordinate of the given complex number,
 *              translating as necessary according to the current format.
 *)
BEGIN
  CASE complexFormat OF
    cartesian   : FirstComponent := Complex.RealPart( z );
    polar       : FirstComponent := Complex.Modulus( z )
  END
END; (* FirstComponent *)

FUNCTION SecondComponent(
(* in *)        z             : Complex.T;
(* in *)        complexFormat : FormatType;
(* out *) VAR   result        : REAL
      ) : Complex.ErrorCode;
(*
 * REQUIRES     Created( z ), Assigned( z )
 * MODIFIES     sets 'result' to the second co-ordinate of the given
 *              complex number, translating as necessary according
 *              to the current format.
 *)
BEGIN
  CASE complexFormat OF
    cartesian :
      BEGIN
        SecondComponent := Complex.NoError;
        result := Complex.ImaginaryPart( z )
      END;
    polar :
      SecondComponent := Complex.Argument( z, result )
  END
END; (* SecondComponent *)

PROCEDURE PrintErrorMessage(
(* in *)        exception   : Complex.ErrorCode
        );
(*
 * MODIFIES     converts Complex.ErrorCode to string and prints it
 *              on Output
 *)
BEGIN
  CASE exception OF
    Complex.DivisionByZero  : WriteLn( Output, 'divide by zero' );
    Complex.LogarithmOfZero : WriteLn( Output, 'logarithm of zero' );
    Complex.Undefined       : WriteLn( Output, 'undefined' )
  END
END; (* PrintErrorMessage *)

PROCEDURE SetFormat(
(* in/out *) VAR complexFormat : FormatType
          );
(*
 * REQUIRES     Open( Input )
 * EFFECTS      Decodes and records new format for complex numbers,
 *              or prints current format if queried with "?".
 *              Input file pointer will point to blank following non-blank
 *              string.
 * EXCEPTIONS   Error messages printed if argument is invalid.
 *)
VAR
  newFormat     : STRING;
  valueCount    : INTEGER;
BEGIN
  ReadString( newFormat );
  IF ( newFormat = 'cartesian' ) OR ( newFormat = 'xy' ) THEN
    complexFormat := cartesian
  ELSE IF newFormat = 'polar' THEN
    complexFormat := polar
  ELSE IF newFormat = '?' THEN BEGIN
    Write( Output, 'current format is ' );
    CASE complexFormat OF
      cartesian : WriteLn( Output, 'cartesian' );
      polar     : WriteLn( Output, 'polar' )
    END
  END ELSE
    WriteLn( Output, 'invalid format specification' )
END; (* SetFormat *)

BEGIN
  WriteLn( output, 'Complex Number Test Module' );
  WriteLn( output, '--------------------------' );
  WriteLn( output, 'Enter ? for help.' );
  Write( output, '> ' );
  complexFormat := cartesian;
  done := FALSE;
  WHILE ( NOT EOF( input ) ) AND ( NOT done ) DO BEGIN
    exception := Complex.NoError;
    command := GetCommand;
    (*
     * Handle parameterless commands
     *)
    IF command IN [ format, help, invalid, quit ] THEN
      CASE command OF
        format :
          SetFormat( complexFormat );
        help :
          PrintHelpMessage;
        invalid :
          WriteLn( Output, 'invalid command' );
        quit :
          done := TRUE
      END
    (*
     * Handle commands with parameters
     *)
    ELSE BEGIN
      Read( Input, x1, x2 );
      (*
       * Handle commands with second complex number parameter
       *)
      IF command IN [ add, divide, multiply, power ] THEN
        Read( Input, y1, y2 );

      CASE command OF
        add :
          result := Complex.Add( SetValue( x1, x2, complexFormat ),
                                 SetValue( y1, y2, complexFormat ) );
        conjugate :
          result := Complex.Conjugate( SetValue( x1, x2, complexFormat ) );
        cosine :
          result := Complex.CCos( SetValue( x1, x2, complexFormat ) );
        divide :
          exception := Complex.Divide( SetValue( x1, x2, complexFormat ),
                        SetValue( y1, y2, complexFormat ), result );
        exp :
          result := Complex.CExp( SetValue( x1, x2, complexFormat ) );
        log :
          exception := Complex.CLog( SetValue( x1, x2, complexFormat ),
                        result );
        multiply :
          result := Complex.Multiply( SetValue( x1, x2, complexFormat ),
                        SetValue( y1, y2, complexFormat ) );
        power :
          exception := Complex.Power( SetValue( x1, x2, complexFormat ),
                        SetValue( y1, y2, complexFormat ), result );
        sine :
          result := Complex.CSin( SetValue( x1, x2, complexFormat ) )
      END;
      IF exception = Complex.NoError THEN BEGIN
        Write( Output, FirstComponent( result, complexFormat ), ' ' );
        IF SecondComponent( result, complexFormat, sc ) = Complex.NoError THEN
          WriteLn( Output, sc )
        ELSE
          WriteLn( Output, ' undefined' )
      END ELSE
        PrintErrorMessage( exception )
    END;
    IF NOT done THEN BEGIN
      ReadLn( input );
      Write( output, '> ' )
    END
  END;
  IF EOF( input ) THEN
    WriteLn( output )
END.
\End\Of\Shar\
else
  echo "will not over write ./testcomp.pas"
fi
echo "Finished archive 1 of 1"
exit
Bruce Parker
305 Weston Hall					(201) 596-3369
Computer and Information Science Department 	parker@mars.njit.edu
New Jersey Institute of Technology