Math Unit
 

UNIT Mathunit;
INTERFACE

TYPE
PolyPtr = ^Polynom;
RootStack = ^EachRoot;
Complex = RECORD
Re : DOUBLE;
Im : DOUBLE;
END;
Polynom = RECORD
Link : PolyPtr;
Coeff : Complex;
END;
EachRoot = RECORD
Link : RootStack;
Coeff : Complex;
END;

FUNCTION Floor(X : DOUBLE) : DOUBLE; (* The next lowest integer *)
FUNCTION Ceiling(X : DOUBLE) : DOUBLE; (* The next highest integer *)
FUNCTION Log10(X : DOUBLE) : DOUBLE; (* Base 10 log of x *)
FUNCTION Exp10(X : DOUBLE) : DOUBLE; (* 10 raised to the x power *)
FUNCTION PwrXY(X, Y : DOUBLE) : DOUBLE; (* raise x to the power y *)
FUNCTION QDBV(Volts : DOUBLE) : DOUBLE; (* convert from volts to dB *)
FUNCTION QDBW(Watts : DOUBLE) : DOUBLE; (* convert from watts to dB *)
FUNCTION QWATTS(DB : DOUBLE) : DOUBLE; (* convert from dB to watts *)
FUNCTION QVOLTS(DB : DOUBLE) : DOUBLE; (* convert from dB to volts *)
FUNCTION SINH(X : DOUBLE) : DOUBLE; (* hyperbolic sine *)
FUNCTION COSH(X : DOUBLE) : DOUBLE; (* hyperbolic cosine *)
FUNCTION TANH(X : DOUBLE) : DOUBLE; (* hyperbolic tangent *)
FUNCTION ISINH(X : DOUBLE) : DOUBLE; (* arc hyperbolic sine *)
FUNCTION ICOSH(X : DOUBLE) : DOUBLE; (* arc hyperbolic cosine *)
FUNCTION ITANH(X : DOUBLE) : DOUBLE; (* arc hyperbolic tangent *)
FUNCTION ARCSIN(X : DOUBLE) : DOUBLE; (* arc sine using TP arctan function *)
FUNCTION ARCCOS(X : DOUBLE) : DOUBLE; (* inverse cosine using TP arctan *)
FUNCTION ATAN2(X, Y : DOUBLE) : DOUBLE; (* arctan function with quadrant check
X is real axis value or denominator, Y is imaginary axis value or numerator *)
FUNCTION TAN(X : DOUBLE) : DOUBLE; (* tangent of X *)
FUNCTION GAUSS (Mean, StdDev : DOUBLE) : DOUBLE; (* gaussian random number *)
FUNCTION RESIDUE(Radix, Number : DOUBLE) : DOUBLE; (* remainder of number/radix
*)
FUNCTION MINIMUM(A, B : DOUBLE) : DOUBLE; (* the minimum of a and b *)
FUNCTION MAXIMUM(A, B : DOUBLE) : DOUBLE; (* the maximum of a and b *)
PROCEDURE Cadd(C, D : Complex;
VAR Result : Complex); (* add two complex numbers *)
PROCEDURE Cconj(A : Complex;
VAR Result : Complex); (* complex conjugate *)
PROCEDURE Cdiv(Num, Denom : Complex;
VAR Result : Complex); (* complex division *)
PROCEDURE Cinv(A : Complex;
VAR Result : Complex); (* complex inverse *)
FUNCTION Cmag(C : Complex) : DOUBLE; (* magnitude of a complex number *)
PROCEDURE Cmake(A, B : DOUBLE;
VAR Result : Complex); (* form a complex number *)
PROCEDURE Cmult(C, D : Complex;
VAR Result : Complex); (* multiply two complex numbers *)
PROCEDURE Csub(C, D : Complex;
VAR Result : Complex); (* subtract D from C complex number
*)
PROCEDURE Cexp(A : Complex;
VAR Result : Complex); (* exponential of a complex number *)
PROCEDURE Csqr(A : Complex;
VAR Result : Complex); (* square of a complex number *)
PROCEDURE Csqrt(A : Complex;
VAR Result : Complex); (* sqrt of a complex number *)
PROCEDURE Cln(A : Complex;
VAR Result : Complex); (* natural log of complex A *)
PROCEDURE CpwrXY(X, Y : Complex;
VAR Result : Complex); (* raise X to the Y power *)
PROCEDURE Csinh(A : Complex;
VAR Result : Complex); (* hyperbolic sine of complex A *)
PROCEDURE Ccosh(A : Complex;
VAR Result : Complex); (* hyperbolic cosine of complex A *)
PROCEDURE Ctanh(A : Complex;
VAR Result : Complex); (* hyperbolic tangent of complex A *)
PROCEDURE Csin(A : Complex;
VAR Result : Complex); (* sine of complex A *)
PROCEDURE Ccos(A : Complex;
VAR Result : Complex); (* cosine of complex A *)
PROCEDURE Ctan(A : Complex;
VAR Result : Complex); (* tangent of complex A *)
PROCEDURE Carcsin(A : Complex;
VAR Result : Complex); (* inverse sine of complex A *)
PROCEDURE Carccos(A : Complex;
VAR Result : Complex); (* inverse cosine of complex A *)
PROCEDURE Carctan(A : Complex;
VAR Result : Complex); (* inverse tangent of complex A *)
PROCEDURE PolyAssign(X : PolyPtr; (* generate new polynomial at Y with *)
VAR Y : PolyPtr); (* same coefficients as poly at X *)
PROCEDURE PolyClear(VAR Ptr : PolyPtr); (* remove a polynomial *)
PROCEDURE PolyEval(X : PolyPtr; (* evaluate polynomial X in S *)
S : Complex; (* at complex value S *)
VAR Result : Complex); (* assign to Result *)
PROCEDURE PolyForm(A : RootStack; (* form a polynomial B from the
roots of rootstack A *)
VAR B : PolyPtr);
PROCEDURE PolyMult(Xx, Yy : PolyPtr;
VAR Z : PolyPtr); (* multiply two polynomials *)
PROCEDURE PolyNegate(X : PolyPtr);
FUNCTION PolyOrder(Z : PolyPtr) : BYTE; (* order of a polynomial *)
PROCEDURE PolyPower(I : BYTE; (* raise a polynomial X to power I *)
X : PolyPtr;
VAR Y : PolyPtr);
PROCEDURE PolyPrint(X : PolyPtr); (* writeln for polynomial *)
PROCEDURE PolyScale(X : PolyPtr; (* multiply polynomial X by *)
Scalar : Complex); (* complex number Scalar *)
PROCEDURE PolyUnary(X : PolyPtr); (* make the polynomial X unary *)
(* i.e. the leading coef = 1 *)
PROCEDURE RootPush(R : Complex; (* add root R to a rootstack L *)
VAR L : RootStack);
PROCEDURE RootPop(VAR L : RootStack; (* get root from a rootstack L *)
VAR R : Complex);
PROCEDURE RootClear(VAR L : RootStack); (* clear a rootstack L *)
PROCEDURE RootRotate(N : BYTE;VAR L : RootStack);(* rotate rootstack L by N *)
(* so that last moves toward 1st *)
PROCEDURE RootCopy(S : RootStack; (* copy a rootstack from S to D *)
VAR D: RootStack);

CONST
Cone : Complex = (Re : 1.0; Im : 0.0);
Czero: Complex = (Re : 0.0; Im : 0.0);
Ci : Complex = (Re : 0.0; Im : 1.0);

IMPLEMENTATION
VAR
Ln10 : DOUBLE;

FUNCTION Floor (X : DOUBLE) : DOUBLE; (* The next lowest integer *)

(* note that INT will not work when X is a negative number *)
BEGIN
IF X >= 0.0 THEN
Floor := Int(X)
ELSE (* Use int for positive x *)
IF Frac(X) = 0.0 THEN
Floor := X
ELSE (* no shift on exact integer *)
Floor := - Int(1 - X) (* Round away from zero *)
END; (* Floor *)

FUNCTION Ceiling (X : DOUBLE) : DOUBLE; (* The next highest integer *)
BEGIN
IF X <= 0.0 THEN
Ceiling := Int(X)
ELSE (* Use int for negative x *)
IF Frac(X) = 0.0 THEN
Ceiling := X
ELSE (* no shift on exact integer *)
Ceiling := 1 - Int(- X) (* Shift x to negative *)
END; (* Ceiling *)

FUNCTION Log10 (X : DOUBLE) : DOUBLE; (* Base 10 log of x *)
BEGIN (* Ln(10) supplied for speed *)
Log10 := Ln(X) / Ln10 (* Easily derived *)
END; (* Log10 *)

FUNCTION Exp10 (X : DOUBLE) : DOUBLE; (* 10 raised to the x power *)
BEGIN (* Ln(10) supplied for speed *)
Exp10 := Exp(X * Ln10) (* easily derived *)
END; (* Exp10 *)

FUNCTION PwrXY (X, Y : DOUBLE) : DOUBLE; (* raise x to the power y *)
BEGIN
IF (Y = 0.0) THEN
PwrXY := 1.0
ELSE
IF (X <= 0.0) AND (Frac(Y) = 0.0) THEN
IF (Frac(Y / 2)) = 0.0 THEN
PwrXY := Exp(Y * Ln(Abs(X)))
ELSE
PwrXY := - Exp(Y * Ln(Abs(X)))
ELSE
PwrXY := Exp(Y * Ln(X));
END; (* PwrXY *)

FUNCTION QDBV (Volts : DOUBLE) : DOUBLE; (* convert from volts to dB *)
BEGIN
QDBV := 20.0 * Log10(Volts)
END; (* QDBV *)

FUNCTION QDBW (Watts : DOUBLE) : DOUBLE; (* convert from watts to dB *)
BEGIN
QDBW := 10.0 * Log10(Watts)
END; (* QDBW *)

FUNCTION QWATTS (DB : DOUBLE) : DOUBLE; (* convert from dB to watts *)
BEGIN
QWATTS := Exp10(DB / 10.0);
END; (* QWATTS *)

FUNCTION QVOLTS (DB : DOUBLE) : DOUBLE; (* convert from dB to volts *)
BEGIN
QVOLTS := Exp10(DB / 20.0);
END; (* QVOLTS *)

FUNCTION SINH (X : DOUBLE) : DOUBLE; (* hyperbolic sine *)
BEGIN
SINH := 0.5 * (Exp(X) - Exp(- X))
END; (* SINH *)

FUNCTION COSH (X : DOUBLE) : DOUBLE; (* hyperbolic cosine *)
BEGIN
COSH := 0.5 * (Exp(X) + Exp(- X))
END; (* COSH *)

FUNCTION TANH (X : DOUBLE) : DOUBLE; (* hyperbolic tangent *)
BEGIN
X := Exp(2.0 * X);
TANH := (X - 1.0) / (X + 1.0)
END; (* TANH *)

FUNCTION ISINH (X : DOUBLE) : DOUBLE; (* arc hyperbolic sine *)
BEGIN
ISINH := Ln(Sqrt(1.0 + X * X) + X)
END; (* ISINH *)

FUNCTION ICOSH (X : DOUBLE) : DOUBLE; (* arc hyperbolic cosine *)
BEGIN
ICOSH := Ln(X + Sqrt(X * X - 1.0))
END; (* ICOSH *)

FUNCTION ITANH (X : DOUBLE) : DOUBLE; (* arc hyperbolic tangent *)
BEGIN
ITANH := Ln((1.0 + X) / (1.0 - X))
END; (* ITANH *)

FUNCTION ARCSIN (X : DOUBLE) : DOUBLE; (* arc sine using TP arctan function *)
BEGIN (* answer returned in radians *)
IF X = 1.0 THEN
ARCSIN := Pi / 2.0
ELSE
IF X = - 1.0 THEN
ARCSIN := Pi / - 2.0
ELSE
ARCSIN := Arctan(X / Sqrt(1.0 - Sqr(X)))
END; (* ARCSIN *)

FUNCTION ARCCOS (X : DOUBLE) : DOUBLE; (* inverse cosine using TP arctan *)
BEGIN (* answer returned in radians *)
IF X = 0.0 THEN
ARCCOS := Pi / 2.0
ELSE
IF X < 0.0 THEN
ARCCOS := Pi - Arctan(Sqrt(1.0 - Sqr(X)) / Abs(X))
ELSE
ARCCOS := Arctan(Sqrt(1.0 - Sqr(X)) / Abs(X))
END; (* ARCCOS *)

FUNCTION ATAN2(X, Y : DOUBLE) : DOUBLE; (* arctan function with quadrant check
X is real axis value or denominator, Y is imaginary axis value or numerator *)
BEGIN (* answer returned in radians *)
IF Y <> 0.0 THEN
IF X <> 0.0 THEN (* point not on an axis *)
IF X > 0.0 THEN (* 1st or 4th quadrant use std arctan *)
ATAN2 := Arctan(Y / X)
ELSE
IF Y > 0.0 THEN (* 2nd quadrant *)
ATAN2 := Pi + Arctan(Y / X)
ELSE
ATAN2 := Arctan(Y / X) - Pi (* 3rd quadrant *)
ELSE (* point on the Y axis *)
IF Y >= 0.0 THEN
ATAN2 := Pi / 2.0 (* positive Y axis *)
ELSE
ATAN2 := - Pi / 2.0 (* negative Y axis *)
ELSE (* point on the X axis *)
IF X >= 0.0 THEN
ATAN2 := 0.0 (* positive X axis *)
ELSE
ATAN2 := - Pi (* negative X axis *)
END; (* ATAN2 *)

FUNCTION TAN (X : DOUBLE) : DOUBLE; (* tangent of X *)
BEGIN
TAN := Sin(X) / Cos(X)
END; (* TAN *)

FUNCTION GAUSS (Mean, StdDev : DOUBLE) : DOUBLE; (* gaussian random number *)
VAR
I : BYTE; (* index for loop *)
T : DOUBLE; (* temporary variable *)

BEGIN (* Based on the central limit theorem *)
T := - 6.0; (* maximum deviation is 6 sigma, remove
the mean first *)
FOR I := 1 TO 12 DO
T := T + Random; (* 12 uniform over 0 to 1 *)
GAUSS := Mean + T * StdDev (* adjust mean and standard deviation *)
END; (* GAUSS *)

FUNCTION RESIDUE (Radix, Number : DOUBLE) : DOUBLE; (* remainder of
radix/number *)

(* uses APL residue definition *)
BEGIN
RESIDUE := Number - Radix * Floor(Number / Radix)
END; (* RESIDUE *)

FUNCTION MINIMUM (A, B : DOUBLE) : DOUBLE; (* the minimum of a and b *)
BEGIN
IF A < B THEN
MINIMUM := A
ELSE
MINIMUM := B
END; (* MINIMUM *)

FUNCTION MAXIMUM (A, B : DOUBLE) : DOUBLE; (* the maximum of a and b *)
BEGIN
IF A < B THEN
MAXIMUM := B
ELSE
MAXIMUM := A
END; (* MAXIMUM *)

PROCEDURE Cmult(C, D : Complex;
VAR Result : Complex); (* multiply two complex numbers *)
BEGIN
Result.Re := C.Re * D.Re - C.Im * D.Im; (* real part *)
Result.Im := C.Re * D.Im + C.Im * D.Re; (* imaginary part *)
END;

PROCEDURE Cadd(C, D : Complex;
VAR Result : Complex); (* add two complex numbers *)
BEGIN
Result.Re := C.Re + D.Re; (* real part *)
Result.Im := C.Im + D.Im; (* imaginary part *)
END;

PROCEDURE Csub(C, D : Complex;
VAR Result : Complex); (* subtract D from C complex number
*)
BEGIN
Result.Re := C.Re - D.Re; (* real part *)
Result.Im := C.Im - D.Im; (* imaginary part *)
END;

FUNCTION Cmag (C : Complex) : DOUBLE; (* magnitude of a complex number *)
BEGIN
Cmag := Sqrt(Sqr(C.Re) + Sqr(C.Im));
END;

PROCEDURE Cmake(A, B : DOUBLE;
VAR Result : Complex); (* form a complex number *)
BEGIN
Result.Re := A;
Result.Im := B;
END;

PROCEDURE Cconj(A : Complex;
VAR Result : Complex); (* complex conjugate *)
BEGIN
Result.Re := A.Re;
Result.Im := - A.Im;
END;

PROCEDURE Cexp(A : Complex;
VAR Result : Complex); (* exponential of a complex number *)
VAR
Magnitude : DOUBLE;

BEGIN (* exp(real+j imag)=exp(real)exp(j imag) *)
Magnitude := Exp(A.Re);
Result.Re := Magnitude * Cos(A.Im); (* Eulers equation *)
Result.Im := Magnitude * Sin(A.Im);
END;

PROCEDURE Csqr(A : Complex;
VAR Result : Complex); (* square of a complex number *)
BEGIN (* sqr(real + j imag) *)
Result.Re := Sqr(A.Re) - Sqr(A.Im);
Result.Im := 2.0 * A.Re * A.Im;
END;

PROCEDURE Csqrt(A : Complex;
VAR Result : Complex); (* sqrt of a complex number *)
VAR
Magnitude, Phase : DOUBLE; (* A to be written as mag*exp(j phase) *)

BEGIN (* solve sqrt(mag)*exp(j .5*phase *)
Phase := 0.5 * Atan2(A.Re, A.Im);
Magnitude := Sqrt(Sqrt(Sqr(A.Re) + Sqr(A.Im)));
Result.Re := Magnitude * Cos(Phase); (* Eulers equation *)
Result.Im := Magnitude * Sin(Phase);
END;

PROCEDURE Cln(A : Complex;
VAR Result : Complex); (* natural log of complex A *)
BEGIN (* A to be written as mag*exp(j phase) *)
Result.Re := 0.5 * Ln(Sqr(A.Re) + Sqr(A.Im));
Result.Im := Atan2(A.Re, A.Im);
END;

PROCEDURE CpwrXY(X, Y : Complex;
VAR Result : Complex); (* raise X to the Y power *)
VAR
Temp : Complex;

BEGIN
IF (X.Re = 0.0) AND (X.Im = 0.0) THEN (* avoid taking log of 0 *)
BEGIN
Result.Re := 0.0;
Result.Im := 0.0;
END
ELSE
BEGIN
Cln(X,Temp);
Cmult(Y,Temp,Temp);
Cexp(Temp,Result);
END
END;

PROCEDURE Csinh(A : Complex;
VAR Result : Complex); (* hyperbolic sine of complex A *)
BEGIN
Result.Re := Cos(A.Im) * Sinh(A.Re);
Result.Im := Sin(A.Im) * Cosh(A.Re);
END;

PROCEDURE Ccosh(A : Complex;
VAR Result : Complex); (* hyperbolic cosine of complex A *)
BEGIN
Result.Re := Cos(A.Im) * Cosh(A.Re);
Result.Im := Sin(A.Im) * Sinh(A.Re);
END;

PROCEDURE Ctanh(A : Complex;
VAR Result : Complex); (* hyperbolic tangent of complex A *)
VAR
Denom : DOUBLE;

BEGIN
Denom := Cos(2.0 * A.Im) + Cosh(2.0 * A.Re);
Result.Re := Sinh(2.0 * A.Re) / Denom;
Result.Im := Sin(2.0 * A.Im) / Denom;
END;

PROCEDURE Csin(A : Complex;
VAR Result : Complex); (* sine of complex A *)
BEGIN
Result.Re := Sin(A.Re) * Cosh(A.Im);
Result.Im := Cos(A.Re) * Sinh(A.Im);
END;

PROCEDURE Ccos(A : Complex;
VAR Result : Complex); (* cosine of complex A *)
BEGIN
Result.Re := Cos(A.Re) * Cosh(A.Im);
Result.Im := - Sin(A.Re) * Sinh(A.Im);
END;

PROCEDURE Ctan(A : Complex;
VAR Result : Complex); (* tangent of complex A *)
VAR
Denom : DOUBLE;

BEGIN
Denom := Cos(2.0 * A.Re) + Cosh(2.0 * A.Im);
Result.Re := Sin(2.0 * A.Re) / Denom;
Result.Im := Sinh(2.0 * A.Im) / Denom;
END;

PROCEDURE Cinv(A : Complex;
VAR Result : Complex); (* complex inverse *)
VAR
Scalar : DOUBLE;

BEGIN
Scalar := Sqr(A.Re) + Sqr(A.Im);
Result.Re := A.Re / Scalar;
Result.Im := - A.Im / Scalar;
END;

PROCEDURE Cdiv(Num, Denom : Complex;
VAR Result : Complex); (* complex division *)

(* returns Num/Denom *)
VAR
Scalar : DOUBLE;

BEGIN

(****************************************************************************)
(* try to avoid overflow by normalizing the denominator *)
(****************************************************************************)
IF (Abs(Denom.Re) > Abs(Denom.Im)) THEN
BEGIN
Scalar := Denom.Re;
Denom.Im := - Denom.Im / Scalar;
(* Denom.Re := 1.0; is implied *)
Scalar := (Sqr(Denom.Im) + 1.0) * Scalar;
Result.Re := (Num.Re - Num.Im * Denom.Im) / Scalar;
Result.Im := (Num.Re * Denom.Im + Num.Im) / Scalar;
END
ELSE
BEGIN
Scalar := Denom.Im;
Denom.Re := Denom.Re / Scalar;
(* Denom.Im := -1.0; is implied *)
Scalar := (Sqr(Denom.Re) + 1.0) * Scalar;
Result.Re := (Num.Re * Denom.Re + Num.Im) / Scalar;
Result.Im := (Num.Im * Denom.Re - Num.Re) / Scalar;
END;
END;

PROCEDURE Carcsin(A : Complex;
VAR Result : Complex); (* inverse sine of complex A *)
VAR
Temp : Complex;

BEGIN
Csqr(A, Temp);
Csub(Cone, Temp, Temp);
Csqrt(Temp, Temp);
Cmult(Ci, A, A);
Csub(Temp, A, Temp);
Cln(Temp, Temp);
Cmult(Ci, Temp, Result)
END;

PROCEDURE Carccos(A : Complex;
VAR Result : Complex); (* inverse cosine of complex A *)
VAR
Temp : Complex;

BEGIN
Csqr(A, Temp);
Csub(Temp, Cone, Temp);
Csqrt(Temp, Temp);
Csub(A, Temp, Temp);
Cln(Temp, Temp);
Cmult(Ci, Temp, Result)
END;

PROCEDURE Carctan(A : Complex;
VAR Result : Complex); (* inverse tangent of complex A *)
VAR
Temp : DOUBLE;

BEGIN
Temp := Sqr(A.Re);
Result.Re := 0.5 * Atan2((1.0 - Temp - Sqr(A.Im)),2.0 * A.Re);
Result.Im := 0.25 * Ln((Sqr(1.0 + A.Im) + Temp) / (Sqr(1.0 - A.Im) + Temp));
END;

PROCEDURE PolyClear(VAR Ptr : PolyPtr); (* remove a polynomial *)
VAR
Tempptr : PolyPtr;

BEGIN
WHILE Ptr <> NIL DO (* for all polynomial coefficients *)
BEGIN
Tempptr := Ptr^.Link; (* store link to the next coefficient *)
Dispose(Ptr); (* free memory at current coefficient *)
Ptr := Tempptr; (* go to next coefficient *)
END;
END;

PROCEDURE PolyNext(VAR Ptr : PolyPtr); (* new linked list element *)
BEGIN
New(Ptr); (* get memory space for a coefficient *)
Ptr^.Coeff := Czero; (* set the coefficient to zero *)
Ptr^.Link := NIL; (* next element in the list is
nonexistant *)
END;

FUNCTION PolyOrder (Z : PolyPtr) : BYTE; (* order of a polynomial *)
VAR
OrderCtr : BYTE; (* maximum order is 255 *)

BEGIN
OrderCtr := 0; (* return 0 for polynomial with one
element *)
WHILE Z^.Link <> NIL DO (* last element in list ? *)
BEGIN
Inc(OrderCtr); (* count number of times through loop *)
Z := Z^.Link; (* go to next coefficient *)
END;
PolyOrder := OrderCtr;
END;

PROCEDURE PolyNew(N : BYTE; (* create a zero polynomial of length N>0
*)
VAR Z : PolyPtr); (* ** Z must be an existing polynomial **
*)
VAR
I : BYTE; (* maximum order is 255 *)
Ztemp : PolyPtr; (* to move through coefficient list *)

BEGIN
PolyClear(Z); (* free existing polynomial location *)
PolyNext(Z); (* get zeroth coefficient *)
Ztemp := Z; (* Z stays at first element of list *)
FOR I := 1 TO N DO (* add other coefficients to the list *)
BEGIN
PolyNext(Ztemp^.Link);
Ztemp := Ztemp^.Link;
END;
END;

PROCEDURE PolyAssign(X : PolyPtr; (* generate new polynomial at Y with *)
VAR Y : PolyPtr); (* same coefficients as poly at X *)
VAR
I : BYTE; (* length of polynomial X *)
Ytemp, YtStart : PolyPtr; (* to move through the list *)
(* maximum order is 255 *)

BEGIN
IF X <> NIL THEN
BEGIN
I := PolyOrder(X); (* order of X *)
Ytemp := NIL; (* initialize Ytemp *)
PolyNew(I, Ytemp); (* get new poly of same order *)
YtStart := Ytemp; (* remember first element of list *)
WHILE X <> NIL DO (* go through X *)
BEGIN
Ytemp^.Coeff := X^.Coeff; (* assign X coef to Y coef *)
Ytemp := Ytemp^.Link; (* locate next element of Y *)
X := X^.Link; (* locate next element of X *)
END;
PolyClear(Y); (* in case PolyAssign(P1,P1) *)
Y := YtStart;
END
ELSE PolyClear(Y);
END;

PROCEDURE Root_Poly(Root : Complex; (* form polynomial S-Root *)
VAR Result : PolyPtr);
VAR
ResultTemp : PolyPtr; (* to move through two element list *)

BEGIN
PolyNew(1, Result); (* generate two element list *)
ResultTemp := Result; (* keep Result at 1st element of list *)
ResultTemp^.Coeff.Im := - Root.Im; (* zeroth coefficient *)
ResultTemp^.Coeff.Re := - Root.Re;
ResultTemp := ResultTemp^.Link; (* move to S*1 coefficient *)
ResultTemp^.Coeff := Cone; (* set it equal to 1 + j0 *)
END;

PROCEDURE PolyMult(Xx, Yy : PolyPtr;
VAR Z : PolyPtr);
VAR
X, Y, Ypt, Zpt, Zptsave : PolyPtr;
Result : Complex;
I : BYTE; (* maximum order is 255 *)

BEGIN
X := NIL; (* don't give PolyAssign trash *)
Y := NIL; (* don't give PolyAssign trash *)
PolyAssign(Xx, X); (* copy Xx, in case PolyMult(A,A,A) *)
PolyAssign(Yy, Y); (* copy Yy, in case PolyMult(A,A,A) *)
I := PolyOrder(X) + PolyOrder(Y); (* resultant polynomial order *)
PolyClear(Z); (* release existing Z *)
PolyNew(I, Z); (* make a list of length I *)
Zptsave := Z; (* keep Z at start of the list *)
WHILE X <> NIL DO (* for each element of x *)
BEGIN
Zpt := Zptsave; (* remember start of list 2nd loop *)
Ypt := Y; (* remember start of list 2nd loop *)
WHILE Ypt <> NIL DO (* 2nd loop goes over elements of Y *)
BEGIN (* scale Y polynomial by X coeff *)
Cmult(X^.Coeff, Ypt^.Coeff, Result);
Cadd(Result, Zpt^.Coeff, Zpt^.Coeff);
Ypt := Ypt^.Link; (* next element in Y *)
Zpt := Zpt^.Link; (* next element in Z *)
END;
Zptsave := Zptsave^.Link; (* begin at next higher element in Z *)
X := X^.Link; (* by multiplying by next X element *)
END;
PolyClear(X); (* release X storage *)
PolyClear(Y); (* release Y storage *)
END;

PROCEDURE PolyForm(A : RootStack; (* form a polynomial B from the
roots of rootstack A *)
VAR B : PolyPtr);
VAR
Tply : PolyPtr; (* will hold the 1st order polynomial *)
DupRoots : RootStack; (* get a duplicate rootStack *)
Troot : Complex;

BEGIN
PolyClear(B); (* erase B *)
IF A <> NIL THEN
BEGIN
DupRoots := NIL; (* initialize DupRoots *)
RootCopy(A,DupRoots); (* don't destroy the contents of A *)
RootPop(DupRoots,Troot); (* get first root *)
Root_Poly(Troot,B); (* form 1st order poly *)
Tply := NIL; (* initialize Tply *)
WHILE DupRoots <> NIL DO (* for other each root *)
BEGIN
RootPop(DupRoots,Troot);
Root_Poly(Troot,Tply); (* form 1st order poly *)
PolyMult(Tply,B,B); (* multiply by current B *)
END;
PolyClear(Tply); (* free temporary polynomial *)
RootClear(DupRoots); (* free temporary rootlist *)
END;
END;

PROCEDURE PolyPrint(X : PolyPtr);
BEGIN
WHILE X <> NIL DO
BEGIN
Writeln(X^.Coeff.Re, X^.Coeff.Im);
X := X^.Link;
END;
END;

PROCEDURE PolyPower(I : BYTE; (* raise a polynomial X to power I *)
X : PolyPtr;
VAR Y : PolyPtr); (* assign to polynomial Y *)
VAR
N : BYTE;
Xtemp : PolyPtr;

BEGIN
IF I = 0 THEN (* expression to the 0 power is 1 *)
BEGIN
PolyClear(Y);
PolyNext(Y);
Y^.Coeff.Re := 1.0;
END
ELSE (* not 0 power *)
BEGIN
Xtemp := NIL; (* initialize Xtemp *)
PolyAssign(X, Xtemp); (* in case called PolyPwr(3,A,A) *)
PolyAssign(Xtemp, Y); (* first power *)
N := 1;
WHILE N < I DO (* continuing powers *)
BEGIN
PolyMult(Xtemp, Y, Y); (* multiply by X *)
INC(N); (* current power *)
END; (* WHILE *)
PolyClear(Xtemp);
END (* IF *)
END;

PROCEDURE PolyEval(X : PolyPtr; (* evaluate polynomial X in S *)
S : Complex; (* at complex value S *)
VAR Result : Complex); (* assign to Result *)
VAR
Tempr, Temps : Complex;

BEGIN
Temps := S; (* in generating powers of S *)
IF X <> NIL THEN (* any coefficients in X *)
BEGIN
Result := X^.Coeff; (* add the constant of the polynomial *)
X := X^.Link; (* go to S*1 coefficient *)
WHILE X <> NIL DO (* continue for each coefficient *)
BEGIN
Cmult(X^.Coeff, Temps, Tempr); (* multiply by S*n *)
Cadd(Result, Tempr, Result); (* add to running sum *)
Cmult(S, Temps, Temps); (* form S*(n+1) *)
X := X^.Link; (* next order *)
END; (* while *)
END;
END;

PROCEDURE PolyAdd(X, Y : PolyPtr; (* add polynomials X and Y *)
VAR Z : PolyPtr); (* assign to polynomial Z *)
VAR
Xtemp, Ytemp, Tptr1, Tptr2, Tptr3 : PolyPtr;

BEGIN
Xtemp := NIL; (* Initialize undefined polynomials *)
Ytemp := NIL;
PolyAssign(X, Xtemp); (* temporary working polynomials *)
PolyAssign(Y, Ytemp);
PolyClear(Z); (* in case of PolyAdd(P1,P2,P2) *)
IF PolyOrder(Xtemp) > PolyOrder(Ytemp) THEN (* want to add the smaller to
larger *)
BEGIN (* Z will be same order of the larger *)
Tptr1 := Xtemp;
Tptr2 := Ytemp;
END
ELSE
BEGIN
Tptr1 := Ytemp;
Tptr2 := Xtemp;
END;
PolyAssign(Tptr1, Z); (* Z is 0 + the larger *)
Tptr3 := Z; (* keep Z at start of polynomial *)
WHILE Tptr2 <> NIL DO (* for each coeff of the smaller *)
BEGIN
Cadd(Tptr3^.Coeff, Tptr2^.Coeff, Tptr3^.Coeff); (* Z + smaller *)
Tptr3 := Tptr3^.Link; (* next Z coef *)
Tptr2 := Tptr2^.Link; (* next smaller coef *)
END;
PolyClear(Xtemp); (* free Xtemp and Ytemp storage *)
PolyClear(Ytemp);
END;

PROCEDURE PolyNegate(X : PolyPtr); (* change poly in S to poly in -S *)
VAR
Temp : Complex; (* to be 1+j0 or -1+j0 *)

BEGIN
Temp := Cone; (* initially 1+j0 *)
WHILE X <> NIL DO (* for each coefficient *)
BEGIN
Cmult(X^.Coeff, Temp, X^.Coeff); (* multiply by 1 or -1 *)
Temp.Re := - Temp.Re; (* change 1 to -1 or -1 to 1 *)
X := X^.Link; (* next coefficient *)
END;
END;

PROCEDURE PolyScale(X : PolyPtr; (* multiply polynomial X by *)
Scalar : Complex); (* complex number Scalar *)
BEGIN
WHILE X <> NIL DO (* go through each element of X *)
BEGIN
Cmult(Scalar, X^.Coeff, X^.Coeff); (* scale the coefficient *)
X := X^.Link; (* locate next element of X *)
END;
END;

PROCEDURE PolyUnary(X : PolyPtr); (* make the polynomial X unary *)
(* i.e. the leading coef = 1 *)
VAR
Xtemp : PolyPtr;
Scalar : Complex;

BEGIN
Xtemp := X; (* remember the start of the list *)
WHILE X <> NIL DO (* go through each element of X *)
BEGIN (* to locate last element *)
Scalar := X^.Coeff; (* looking for the last coefficient *)
X := X^.Link; (* locate next element of X *)
END; (* while *)
Cinv(Scalar, Scalar); (* inverse of last element of X *)
PolyScale(Xtemp, Scalar); (* will make last element = 1.0 *)
END;

PROCEDURE PolyDivide(Num, Denom : PolyPtr; (* Synthetic division *)
VAR Quotient, Remainder: PolyPtr);
VAR
TempN, TempD, TempQ, TempR : PolyPtr;
OrderNum, OrderDenom, OrderQuo : BYTE;
LeadingCoef, LeadingCoefD : Complex;
BEGIN
OrderNum := PolyOrder(Num);
OrderDenom := PolyOrder(Denom);
IF OrderNum > OrderDenom THEN
BEGIN
TempN := NIL; (* initialize temporary numerator *)
TempD := NIL; (* initialize temporary denominator *)
PolyAssign(Num,TempN); (* in case PolyDivide(A,B,A,B) *)
PolyAssign(Denom,TempD); (* in case PolyDivide(A,B,A,B) *)
WHILE TempN <> NIL DO (* find the leading coef of the num *)
BEGIN
LeadingCoef := TempN^.Coeff;
TempN := TempN^.Link;
END;
WHILE TempD <> NIL DO (* find the leading coef of the denom *)
BEGIN
LeadingCoefD:= TempD^.Coeff;
TempD := TempD^.Link;
END;
Cdiv(LeadingCoef,LeadingCoefD,LeadingCoef); (* quotient leading coef *)
TempQ := NIL; (* initialize temporary quotient *)
PolyClear(Quotient);
PolyNew((OrderNum-OrderDenom),Quotient);
TempR := NIL; (* initialize temporary remainder *)

PolyClear(Quotient);
PolyClear(Remainder);

END
ELSE (* denominator cannot divide into numerator *)
BEGIN
PolyAssign(Num,Remainder);
PolyClear(Quotient);
END;
END;

PROCEDURE RootPush(R : Complex; (* add root R to a rootstack L *)
VAR L : RootStack);
VAR
NewSpace : RootStack;
BEGIN
New(NewSpace); (* get memory space for new root *)
NewSpace^.Coeff := R; (* set the coefficient to R *)
NewSpace^.Link := L; (* next element in the list is L *)
L := NewSpace; (* new top of stack *)
END;

PROCEDURE RootPop(VAR L : RootStack; (* get root from a rootstack L *)
VAR R : Complex);
VAR
Ltemp : RootStack;
BEGIN
IF L <> NIL THEN
BEGIN
R := L^.Coeff; (* assign R from first stack element *)
Ltemp := L; (* remember top stack element location *)
L := L^.Link; (* move L to second member of stack *)
Dispose(Ltemp); (* free memory at old top of stack *)
END;
END;

PROCEDURE RootClear(VAR L : RootStack); (* clear a rootstack L *)
VAR
Dummy : Complex;
BEGIN
WHILE L <> NIL DO RootPop(L,Dummy); (* pop until empty *)
END;

PROCEDURE RootRotate(N : BYTE;VAR L : RootStack);(* rotate rootstack L by N *)
(* so that last moves toward 1st *)
VAR
MarkTop, Temp : RootStack;
Count : Byte;
BEGIN
IF (L <> NIL) AND (N <> 0) THEN
BEGIN (* make the stack a ring *)
MarkTop := L; (* link to top of L *)
While L <> NIL DO (* search to the end of the list *)
Begin
Temp := L; (* Previous element *)
L := L^.Link;
END;
Temp^.Link := MarkTop; (* wrap the last element to the first *)
Temp := MarkTop; (* move temp back to top of the list *)

(* in the following we locate the bottom of the list which is currently *)
(* linked to the first element of the list. Set the link at the bottom to *)
(* NIL, and its link (being the top) to L *)

FOR Count := 1 to N DO (* will locate bottom of list *)
Temp := Temp^.Link;
L := Temp^.Link; (* pointer to the top of list *)
Temp^.Link := NIL; (* split the ring *)
END;
END;

PROCEDURE RootCopy(S : RootStack; (* copy a rootstack from S to D *)
VAR D: RootStack);
VAR
Dtemp, DtempPrev : RootStack;
BEGIN
If S <> D THEN (* if = then exit *)
BEGIN
IF D <> NIL THEN RootClear(D); (* clear existing stack *)
IF S <> NIL THEN
BEGIN
New(Dtemp); (* first element *)
D := Dtemp; (* is at top of new stack *)
D^.Coeff := S^.Coeff; (* copy the first root *)
D^.Link := NIL; (* don't know if another element *)
S := S^.Link; (* move to second coeff *)
While S <> NIL DO
BEGIN
writeln('rootcopy');
DtempPrev := Dtemp;
New(Dtemp);
Dtemp^.Coeff := S^.Coeff; (* copy the root *)
DtempPrev^.Link := Dtemp; (* tie to previous stack element *)
Dtemp^.Link := NIL;
S := S^.Link;
END; (* while *)
END; (* if *)
END; (* if *)
END; (* RootCopy *)
BEGIN
Randomize;
Ln10 := Ln(10.0)
END.

(c) Shilpa Sayura Foundation 2006-2017