Initially, it is probably easiest to understand complex numbers pictorially: Just as the real numbers can be understood as the coordinates, relative to an arbitrarily selected origin, of the points on a line, so the complex numbers can be understood as the coordinates of the points on a plane, with the first real number as the abscissa and the multiple of the imaginary number as the ordinate. The origin is 0.0+0.0i; horizontal distances are real multiples of the real unit, 1.0, and vertical distances are real multiples the imaginary unit. Since any point can be uniquely identified by its (signed) horizontal and vertical distances from the origin, this gives a way of using real numbers to identify complex numbers.
This ``rectangular'' coordinate system is the most common way of identifying points on a plane, and is also the most common way to identify complex numbers. The abscissa and the ordinate are then referred to as the real part and the imaginary part of the complex number. In both cases, however, a system of polar coordinates is often a useful alternative. In polar coordinates, a point is identified by its magnitude -- that is, its absolute distance from the origin, as a non-negative real number -- and its phase -- that is, the angle (measured as a real number of radians) from the non-negative part of the x-axis to a ray starting at the origin and passing through the point.
In this system, there are several ways to denote the same point; if we want to require unique representation, we must stipulate that the phase be in a certain range, say from -pi (exclusive) to pi (inclusive), and select an arbitrary value from this range, say 0.0, as the phase of the origin. A polar-coordinate numeral for a complex number will consist of a numeral for its magnitude (the distance from the origin of the corresponding point), the symbol @, and a numeral for its phase. In polar representation, then, 0.0+0.0i is 0.0@0.0 and -3.7+18.93i turns out to be approximately 19.288206241121@1.763819774188. (The latter equation is approximate because the true magnitude and phase are both irrational numbers.)
Of course, since in the implementation most reals are approximated, the same is true of most complex numbers. The complex numbers that can be represented exactly form an irregular lattice of points on the plane, each serving as the representative for the points in a rectangular region surrounding it. As in the case of reals, it is usual to treat this approximation strategy as a generally acknowledged precondition on the use of operations on complex numbers; but, as in the case of reals, this is a frequent source of programming errors.
The only complex number for which it seems worthwhile to provide a special numeral is i itself, conceived as 0.0+1.0i:
i, the imaginary unit, as a complex number.
The following operations belong to my proposed interface for the complex-number data type.
To begin with, there should be ways of constructing a complex number from its coordinates, either rectangular or polar:
make-rectangular
Inputs: real-part and imaginary-part, both real
numbers.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is equal to the sum of
real-part and the product of imaginary-part and
i.
make-polar
Inputs: magnitude and phase, both real numbers.
Output: result, a complex number.
Precondition: magnitude is not negative.
Postconditions: result has a magnitude equal to
magnitude. If the magnitude of result is
0.0, then its phase is also 0.0; otherwise, its phase is in
the range from -pi (exclusive) to pi (inclusive), and differs
from phase by a multiple of twice pi.
As a special case of make-rectangular, it is often handy to have a function that supplies an imaginary part of 0.0:
real-to-complex-number
Input: real-part, a real number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the sum of real-part and
0.0 times the imaginary unit.
Similarly, as a special case of make-polar, it is often handy to have a function that returns a complex number of unit magnitude with a given phase:
unit-magnitude
Input: phase, a real number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result has a magnitude of 1.0 and a
phase in the range from -pi (exclusive) to pi (inclusive),
differing from phase by a multiple of twice pi.
A related operation is ``normalizing'' a given complex number to obtain one that has the same phase but a unit magnitude. If an exception is made for the origin, which really has an indeterminate phase, the operation is often called signum:
signum
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postconditions: If operand is 0.0+0.0i, then the
magnitude of result is 0.0 and its phase is also
0.0; otherwise, the magnitude of result is 1.0
and its phase is the phase of operand.
We should be able to recover the coordinates -- either kind of coordinates -- from any given complex number:
real-part
Input: operand, a complex number.
Output: result, a real number.
Preconditions: none.
Postcondition: result is the real part of
operand.
imaginary-part
Input: operand, a complex number.
Output: result, a real number.
Preconditions: none.
Postcondition: result is the imaginary part of
operand.
magnitude
Input: operand, a complex number.
Output: result, a real number.
Preconditions: none.
Postcondition: result is the magnitude of
operand.
phase
Input: operand, a complex number.
Output: result, a real number.
Preconditions: none.
Postcondition: result is the phase of operand.
It is greater than -pi and less than or equal to pi.
Testing whether a given complex number is 0.0+0.0i is a common operation:
zero
Input: operand, a complex number.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand
is 0.0+0.0i and false if it is not.
Now for the basic arithmetic operations:
negate
Input: negand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: The sum of negand and result is
0.0+0.0i.
The conjugate of a given complex number z is the complex number that has the same real part as z but a negated imaginary part.
conjugate
Input: operand, a real number.
Output: result, a real number.
Preconditions: none.
Postconditions: The real part of result is the real part of
operand. The sum of the imaginary part of result
and the imaginary part of operand is 0.0.
add
Inputs: augend and addend, both complex
numbers.
Output: sum, a complex number.
Preconditions: none.
Postcondition: sum is the sum of augend and
addend.
subtract
Inputs: minuend and subtrahend, both complex
numbers.
Output: difference, a complex number.
Preconditions: none.
Postcondition: minuend is the sum of difference
and subtrahend.
multiply
Inputs: multiplicand and multiplier, both
complex numbers.
Output: product, a complex number.
Preconditions: none.
Postcondition: product is the product of
multiplicand and multiplier.
divide
Inputs: dividend and divisor, both complex
numbers.
Output: quotient, a complex number.
Precondition: divisor is not 0.0+0.0i.
Postconditions: dividend is the product of
quotient and divisor.
exponential
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the result of raising e+0.0i
to the power operand.
natural-logarithm
Input: operand, a complex number.
Output: result, a complex number.
Precondition: operand is not 0.0+0.0i.
Postcondition: operand is the result of raising e+0.0i to
the power result. The imaginary part of result
is greater than -pi and less than or equal to pi.
raise
Inputs: base and exponent, both complex
numbers.
Output: power, a complex number.
Precondition: Either base is not 0.0+0.0i or
exponent is 0.0+0.0i or the real part of
exponent is greater than 0.0.
Postconditions: power is the result of raising
base to the power of exponent.
If both base and exponent are 0.0+0.0i,
power is 1.0+0.0i; if base is
0.0+0.0i and the real part of exponent is positive, then
power is 0.0+0.0i.
logarithm
Inputs: base and power, both complex numbers.
Output: exponent, a complex number.
Preconditions: Neither base and exponent is
0.0+0.0i. base is not 1.0+0.0i.
Postconditions: power is the result of raising
base to the power of exponent.
The next three operations are special cases of the preceding ones that occur frequently enough to be treated separately:
reciprocal
Input: operand, a complex number.
Output: result, a complex number.
Precondition: operand is not 0.0+0.0i
Postcondition: The product of operand and result
is 1.0+0.0i.
square
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the result of multiplying
operand by itself.
square-root
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the result of raising
operand to the power 0.5.
Trigonometric functions can be extended to complex numbers by considering their expansions as sums of infinite series:
sine
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the sine of operand.
cosine
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postcondition: result is the cosine of
operand.
tangent
Input: operand, a complex number.
Output: result, a complex number.
Precondition: Either the imaginary part of operand is not
0.0 or the result of dividing operand by half of
pi is not an odd integer.
Postcondition: result is the tangent of operand.
arc-sine
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: none.
Postconditions: operand is the sine of result.
The real part of result is greater than or equal to half of
the negative of pi and less than or equal to half of pi.
If the imaginary part of result is positive, its real part is
less than half of pi. If the imaginary part of result
is negative, its real part is greater than half of the negative of
pi.
arc-cosine
Input: operand, a complex number.
Output: result, a complex number.
Precondition: none.
Postconditions: operand is the cosine of result.
The real part of result is non-negative and less than or equal
to pi. If the imaginary part of result is positive,
its real part is less than pi. If the imaginary part of
result is negative, its real part is positive.
arc-tangent
Input: operand, a complex number.
Output: result, a complex number.
Preconditions: Either the real part of operand is not
0.0 or the absolute value of its imaginary part is not
1.0.
Postconditions: operand is the tangent of result.
result is greater than or equal to the negative of half of
pi and less than or equal to half of pi. If the imaginary
part of result is negative, its real part is less than half of
pi. If the imaginary part of result is positive, its
real part is greater than the negative of half of pi.
Since complex numbers are not linearly ordered, only one comparison operation is really needed:
equal
Inputs: left-operand and right-operand, both
complex numbers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if the operands are the
same complex number, false if they are different complex numbers.
Finally, the input and output operations:
read
Input: source, a data source (e.g., a file, the keyboard, a
device).
Outputs: legend, a complex number, and success, a
Boolean.
Preconditions: none.
Postcondition: Either some representation of a complex number has been
extracted from source and legend is that complex
number,or an input error of some kind has occurred and success
is false.
write
Inputs: target, a data sink (e.g., a file, a window, a
device), and scribend, a complex number.
Outputs: none.
Preconditions: none.
Postcondition: A representation of scribend has been appended
to target.
ComplexNumber data type and the functions and procedures that
are exported by this module and then use them as if they were predefined
by the language.
The ComplexNumbers module uses the following non-standard
features of HP Pascal:
end keyword.
export section, containing
type definitions and the headers of functions and procedures to be
exported, and an implement section, containing local
definitions and the bodies of the exported functions and procedures.
ComplexNumber type, which is a structured type.
ComplexNumbers from other modules, using HP Pascal's
import declaration. The HP Pascal compiler option
search is used to indicate the location of one of the
imported modules.
Assert procedure is used to enforce the
preconditions for the various functions. The assert_halt
compiler option is used to ensure that the program will halt if a
precondition is violated.
otherwise is used to pick up
unexpected alternatives in case statements.
ComplexNumber
record has three fields. The first field, Tag, indicates
whether the coordinates from which the complex number was constructed were
rectangular or polar; the value of this field is fixed when the complex
number is first constructed and never changed thereafter. If the
Tag is Rectangular, the other two fields are
named RealPart and ImaginaryPart and contain the
complex number's rectangular coordinates; if Tag is
Polar, the other two fields are named Magnitude
and Phase and contain the polar coordinates instead. In the
latter case, Magnitude is always a non-negative real number,
Phase is always in the range from -Pi (exclusive)
to Pi (inclusive), and if Magnitude is zero, so
is Phase.
The fact that ComplexNumber is a record type and the names of
the fields that it uses are visible to the application programmer; the
field identifiers are exported along with the type. This means that
application programmers can, in principle, change the Tag
field of an existing complex number, assign negative values to
Magnitude, access the (non-existent) RealPart
field of a complex number in which Tag is Polar,
and commit other, similar misdemeanors. All of these actions violate the
abstraction of the data type, and users of the module should be warned
against them: The only operations that application programmers should
perform on ComplexNumber values are procedure and function
calls and whole-record assignments.
One could make it impossible for application programmers to commit these
misdemeanors by making ComplexNumber an opaque data
type -- one in which the internal structure is not exported.
Unfortunately, under HP Pascal, the only way to accomplish this is to make
ComplexNumber a pointer type; a pointer type can be exported
without exporting its base type. This would mean that storage for complex
numbers would have to be explicitly allocated and, worse, explicitly
deallocated. Since HP Pascal does not have a built-in ``garbage
collector'' (a storage-recycling system), programming with complex numbers
would as a result be much more complicated and much less robust.
Each of the functions that takes an argument of type
ComplexNumber checks it for validity before operating on it,
as a partial protection against meddling by the application programmer; but
this does not prevent meddling that merely replaces the value stored in a
ComplexNumber record with a different value.
The ComplexNumbers module contains several procedures that are
not exported; the scope of such a procedure begins at the point of
declaration and ends at the end of the module. The
ComplexNumberExceptionHandler procedure prints out an
appropriate error message whenever a precondition is violated. The
ValidComplexNumber procedure checks to make sure that a given
ComplexNumber record contains a correctly constructed value.
The ToRectangular and ToPolar functions take a
complex number that may be represented in either kind of coordinates and
returns an (approximately) equal complex number in the specified
representation. The ToPhaseRange function similarly converts
a given real number, representing a phase, into the equivalent value in the
range from -Pi (exclusive) to Pi (inclusive).
Here are some additional notes on the coding of particular functions and procedures:
The constant i is implemented as a function I of no
arguments that simply constructs and returns the appropriate
ComplexNumber value. HP Pascal provides a mechanism for
defining structured constants, so it would also be possible to write
const I = ComplexNumber [Tag: Rectangular, RealPart: 0.0, ImaginaryPart: 1.0];and it is arguable that this would be a better way to meet the specification.
Most of the arithmetic functions on complex numbers begin by coercing their operands to whichever form, rectangular or polar, allows the operation to be coded more easily. When calls to these functions are nested in an expression, it can happen that any or all of the computed intermediate values are coerced to the other form by the next function out, with a loss of precision each time. Application programmers who are concerned about speed and/or precision should inspect the implementation code carefully to see where these coercions occur.
The coding for the arithmetic operations is based on the following identities. In difficult cases, I was guided by the discussion of complex numbers in section 12.5 of Guy L. Steele, Jr.'s Common Lisp: the language, second edition (n.p.: Digital Press, 1990).
(a + bi) - (c + di) = (a - c) + (b - d)i
(r @ theta)(s @ alpha) = rs @ (theta + alpha)
(r @ theta) / (s @ alpha) = (r / s) @ (theta - alpha)
e ^ (a + bi) = (e ^ a) @ b
ln (r @ theta) = (ln r) + theta i
z ^ y = e ^ (y ln z)
log (base y) z = (ln z) / (ln y)
sin z = ((e ^ iz) - (e ^ -iz)) / 2i
cos z = ((e ^ iz) + (e ^ -iz)) / 2
tan z = (sin z) / (cos z)
Arcsin z = -i ln (iz + sqrt (1 - z^2))
Arccos z = -i ln (z + i sqrt (1 - z^2))
Arctan z = (ln (1 + iz) + ln (1 - iz)) / 2i
ReadComplexNumber procedure recognizes only
rectangular-coordinate numerals for complex numbers; adding
polar-coordinate numerals is left as an exercise for the reader.
Two output procedures are provided: WriteComplexNumber writes
the rectangular-coordinate numeral, and
WriteComplexNumberAsPolar the polar-coordinate one.
module ComplexNumbers;
export
type
ComplexNumberRepresentation = (Rectangular, Polar);
ComplexNumber = record
case Tag: ComplexNumberRepresentation of
Rectangular:
(RealPart: Real; ImaginaryPart: Real);
Polar:
(Magnitude: Real; Phase: Real)
end;
function I: ComplexNumber;
function MakeRectangularComplexNumber (RealPart: Real;
ImaginaryPart: Real): ComplexNumber;
function MakePolarComplexNumber (Magnitude: Real; Phase: Real):
ComplexNumber;
function RealToComplexNumber (RealPart: Real): ComplexNumber;
function UnitMagnitudeComplexNumber (Phase: Real): ComplexNumber;
function SignumOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function RealPartOfComplexNumber (Operand: ComplexNumber): Real;
function ImaginaryPartOfComplexNumber (Operand: ComplexNumber): Real;
function MagnitudeOfComplexNumber (Operand: ComplexNumber): Real;
function PhaseOfComplexNumber (Operand: ComplexNumber): Real;
function ZeroComplexNumber (Operand: ComplexNumber): Boolean;
function NegateComplexNumber (Negand: ComplexNumber): ComplexNumber;
function ConjugateComplexNumber (Conjugand: ComplexNumber):
ComplexNumber;
function AddComplexNumber (Augend, Addend: ComplexNumber):
ComplexNumber;
function SubtractComplexNumber (Minuend, Subtrahend: ComplexNumber):
ComplexNumber;
function MultiplyComplexNumber (Multiplicand, Multiplier: ComplexNumber):
ComplexNumber;
function DivideComplexNumber (Dividend, Divisor: ComplexNumber):
ComplexNumber;
function ExponentialOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
function NaturalLogarithmOfComplexNumber (Power: ComplexNumber):
ComplexNumber;
function RaiseComplexNumber (Base, Exponent: ComplexNumber):
ComplexNumber;
function LogarithmOfComplexNumber (Base, Power: ComplexNumber):
ComplexNumber;
function ReciprocalOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
function SquareOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function SquareRootOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
function SineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function CosineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function TangentOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function ArcSineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function ArcCosineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
function ArcTangentOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
function EqualComplexNumber (LeftOperand, RightOperand: ComplexNumber):
Boolean;
procedure ReadComplexNumber (var Source: Text;
var Legend: ComplexNumber; var Success: Boolean);
procedure WriteComplexNumber (var Target: Text; Scribend: ComplexNumber);
procedure WriteComplexNumberAsPolar (var Target: Text;
Scribend: ComplexNumber);
implement
$search 'reals.o'$
import Reals, StdErr;
$assert_halt on$
const
FirstExceptionCode = 1;
InvalidComplexNumberException = 1;
NegativeMagnitudeException = 2;
DivideDomainException = 3;
NaturalLogarithmDomainException = 4;
RaiseDomainException = 5;
LogarithmDomainException = 6;
ReciprocalDomainException = 7;
TangentDomainException = 8;
ArcTangentDomainException = 9;
ReadComplexNumberException = 10;
ExceptionException = 11;
LastExceptionCode = 11;
procedure ComplexNumberExceptionHandler (ExceptionCode: Integer);
begin
if (ExceptionCode < FirstExceptionCode) or
(LastExceptionCode < ExceptionCode) then
ExceptionCode := ExceptionException;
WriteLn (StdErr, 'Exception #', ExceptionCode : 1,
' in module ComplexNumbers:');
case ExceptionCode of
InvalidComplexNumberException:
WriteLn (StdErr, 'One of the arguments to a function in ',
'ComplexNumbers was an incorrectly constructed complex ',
'number.');
NegativeMagnitudeException:
WriteLn (StdErr, 'The Magnitude argument to the ',
'MakePolarComplexNumber function was negative.');
DivideDomainException:
WriteLn (StdErr, 'The Divisor argument to the ',
'DivideComplexNumber function was zero.');
NaturalLogarithmDomainException:
WriteLn (StdErr, 'The argument to the ',
'NaturalLogarithmOfComplexNumber function was zero.');
RaiseDomainException:
WriteLn (StdErr, 'The arguments to the RaiseComplexNumber function ',
'were not in its domain.');
LogarithmDomainException:
WriteLn (StdErr, 'The arguments to the LogarithmOfComplexNumber ',
'function were not in its domain.');
ReciprocalDomainException:
WriteLn (StdErr, 'The argument to the ReciprocalOfComplexNumber ',
'function was zero.');
TangentDomainException:
WriteLn (StdErr, 'The arguments to the TangentOfComplexNumber ',
'function were not in its domain.');
ArcTangentDomainException:
WriteLn (StdErr, 'The arguments to the ArcTangentOfComplexNumber ',
'function were not in its domain.');
ReadComplexNumberException:
WriteLn (StdErr, 'The ReadComplexNumber procedure read in an ',
'incorrectly constructed complex number.');
ExceptionException:
WriteLn (StdErr, 'The ComplexNumberExceptionHandler function ',
'received an unknown exception code.')
end
end;
function ValidComplexNumber (Candidate: ComplexNumber): Boolean;
begin
case Candidate.Tag of
Rectangular:
ValidComplexNumber := True;
Polar:
ValidComplexNumber := Positive (Candidate.Magnitude) or
(Zero (Candidate.Magnitude) and Zero (Candidate.Phase));
otherwise
ValidComplexNumber := False
end
end;
function ToRectangular (Operand: ComplexNumber): ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Result.Tag := Rectangular;
case Operand.Tag of
Rectangular:
Result := Operand;
Polar:
begin
Result.RealPart := Operand.Magnitude * Cos (Operand.Phase);
Result.ImaginaryPart := Operand.Magnitude * Sin (Operand.Phase)
end
end;
ToRectangular := Result
end;
function ToPhaseRange (ProposedPhase: Real): Real;
var
Reduced: Real;
begin
if (-Pi < ProposedPhase) and (ProposedPhase <= Pi) then
ToPhaseRange := ProposedPhase
else if -Pi = ProposedPhase then
ToPhaseRange := Pi
else begin
Reduced := Modulo (ProposedPhase, Twice (Pi));
if Reduced <= Pi then
ToPhaseRange := Reduced
else
ToPhaseRange := Reduced - Twice (Pi)
end
end;
function ToPolar (Operand: ComplexNumber): ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Result.Tag := Polar;
case Operand.Tag of
Rectangular:
begin
Result.Magnitude :=
Sqrt (Sqr (Operand.RealPart) + Sqr (Operand.ImaginaryPart));
if Zero (Operand.ImaginaryPart) then begin
if Negative (Operand.RealPart) then
Result.Phase := Pi
else
Result.Phase := 0.0
end
else
Result.Phase := RatioArcTangent (Operand.ImaginaryPart,
Operand.RealPart)
end;
Polar:
Result := Operand
end;
ToPolar := Result
end;
function I: ComplexNumber;
var
Result: ComplexNumber;
begin
Result.Tag := Rectangular;
Result.RealPart := 0.0;
Result.ImaginaryPart := 1.0;
I := Result
end;
function MakeRectangularComplexNumber (RealPart: Real;
ImaginaryPart: Real): ComplexNumber;
var
Result: ComplexNumber;
begin
Result.Tag := Rectangular;
Result.RealPart := RealPart;
Result.ImaginaryPart := ImaginaryPart;
MakeRectangularComplexNumber := Result
end;
function MakePolarComplexNumber (Magnitude: Real; Phase: Real):
ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (not Negative (Magnitude), NegativeMagnitudeException,
ComplexNumberExceptionHandler);
Result.Tag := Polar;
if Zero (Magnitude) then begin
Result.Magnitude := 0.0;
Result.Phase := 0.0
end
else begin
Result.Magnitude := Magnitude;
Result.Phase := ToPhaseRange (Phase)
end;
MakePolarComplexNumber := Result
end;
function RealToComplexNumber (RealPart: Real):ComplexNumber;
var
Result: ComplexNumber;
begin
Result.Tag := Rectangular;
Result.RealPart := RealPart;
Result.ImaginaryPart := 0.0;
RealToComplexNumber := Result
end;
function UnitMagnitudeComplexNumber (Phase: Real): ComplexNumber;
var
Result: ComplexNumber;
begin
Result.Tag := Polar;
Result.Magnitude := 1.0;
Result.Phase := ToPhaseRange (Phase);
UnitMagnitudeComplexNumber := Result
end;
function SignumOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Result := ToPolar (Operand);
if not Zero (Result.Magnitude) then
Result.Magnitude := 1.0;
SignumOfComplexNumber := Result
end;
function RealPartOfComplexNumber (Operand: ComplexNumber): Real;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
case Operand.Tag of
Rectangular:
RealPartOfComplexNumber := Operand.RealPart;
Polar:
RealPartOfComplexNumber := Operand.Magnitude * Cos (Operand.Phase)
end
end;
function ImaginaryPartOfComplexNumber (Operand: ComplexNumber): Real;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
case Operand.Tag of
Rectangular:
ImaginaryPartOfComplexNumber := Operand.ImaginaryPart;
Polar:
ImaginaryPartOfComplexNumber :=
Operand.Magnitude * Sin (Operand.Phase)
end
end;
function MagnitudeOfComplexNumber (Operand: ComplexNumber): Real;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
case Operand.Tag of
Rectangular:
MagnitudeOfComplexNumber :=
Sqrt (Sqr (Operand.RealPart) + Sqr (Operand.ImaginaryPart));
Polar:
MagnitudeOfComplexNumber := Operand.Magnitude
end
end;
function PhaseOfComplexNumber (Operand: ComplexNumber): Real;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
case Operand.Tag of
Rectangular:
if Zero (Operand.ImaginaryPart) then begin
if Negative (Operand.RealPart) then
PhaseOfComplexNumber := Pi
else
PhaseOfComplexNumber := 0.0
end
else
PhaseOfComplexNumber := RatioArcTangent (Operand.ImaginaryPart,
Operand.RealPart);
Polar:
PhaseOfComplexNumber := Operand.Phase
end
end;
function ZeroComplexNumber (Operand: ComplexNumber): Boolean;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
case Operand.Tag of
Rectangular:
ZeroComplexNumber :=
Zero (Operand.RealPart) and Zero (Operand.ImaginaryPart);
Polar:
ZeroComplexNumber := Zero (Operand.Magnitude)
end
end;
function NegateComplexNumber (Negand: ComplexNumber): ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Negand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Result.Tag := Negand.Tag;
case Negand.Tag of
Rectangular:
begin
Result.RealPart := -Negand.RealPart;
Result.ImaginaryPart := -Negand.ImaginaryPart
end;
Polar:
begin
Result.Magnitude := Negand.Magnitude;
if Zero (Negand.Magnitude) then
Result.Phase := 0.0
else if Positive (Negand.Phase) then
Result.Phase := Negand.Phase - Pi
else
Result.Phase := Negand.Phase + Pi
end
end;
NegateComplexNumber := Result
end;
function ConjugateComplexNumber (Conjugand: ComplexNumber):
ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Conjugand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Result.Tag := Conjugand.Tag;
case Conjugand.Tag of
Rectangular:
begin
Result.RealPart := Conjugand.RealPart;
Result.ImaginaryPart := -Conjugand.ImaginaryPart
end;
Polar:
begin
Result.Magnitude := Conjugand.Magnitude;
if Conjugand.Phase = Pi then
Result.Phase := Pi
else
Result.Phase := -Conjugand.Phase
end
end;
ConjugateComplexNumber := Result
end;
function AddComplexNumber (Augend, Addend: ComplexNumber):
ComplexNumber;
var
RectangularAugend, RectangularAddend: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Augend) and ValidComplexNumber (Addend),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
RectangularAugend := ToRectangular (Augend);
RectangularAddend := ToRectangular (Addend);
Result.Tag := Rectangular;
Result.RealPart :=
RectangularAugend.RealPart + RectangularAddend.RealPart;
Result.ImaginaryPart :=
RectangularAugend.ImaginaryPart + RectangularAddend.ImaginaryPart;
AddComplexNumber := Result;
end;
function SubtractComplexNumber (Minuend, Subtrahend: ComplexNumber):
ComplexNumber;
var
RectangularMinuend, RectangularSubtrahend: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Minuend) and ValidComplexNumber (Subtrahend),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
RectangularMinuend := ToRectangular (Minuend);
RectangularSubtrahend := ToRectangular (Subtrahend);
Result.Tag := Rectangular;
Result.RealPart :=
RectangularMinuend.RealPart - RectangularSubtrahend.RealPart;
Result.ImaginaryPart := RectangularMinuend.ImaginaryPart -
RectangularSubtrahend.ImaginaryPart;
SubtractComplexNumber := Result;
end;
function MultiplyComplexNumber (Multiplicand, Multiplier: ComplexNumber):
ComplexNumber;
var
PolarMultiplicand, PolarMultiplier: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Multiplicand) and
ValidComplexNumber (Multiplier),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
PolarMultiplicand := ToPolar (Multiplicand);
PolarMultiplier := ToPolar (Multiplier);
Result.Tag := Polar;
Result.Magnitude :=
PolarMultiplicand.Magnitude * PolarMultiplier.Magnitude;
if Zero (Result.Magnitude) then
Result.Phase := 0.0
else
Result.Phase :=
ToPhaseRange (PolarMultiplicand.Phase + PolarMultiplier.Phase);
MultiplyComplexNumber := Result
end;
function DivideComplexNumber (Dividend, Divisor: ComplexNumber):
ComplexNumber;
var
PolarDividend, PolarDivisor: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Dividend) and ValidComplexNumber (Divisor),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
Assert (not ZeroComplexNumber (Divisor), DivideDomainException,
ComplexNumberExceptionHandler);
PolarDividend := ToPolar (Dividend);
PolarDivisor := ToPolar (Divisor);
Result.Tag := Polar;
Result.Magnitude := PolarDividend.Magnitude / PolarDivisor.Magnitude;
if Zero (Result.Magnitude) then
Result.Phase := 0.0
else
Result.Phase :=
ToPhaseRange (PolarDividend.Phase - PolarDivisor.Phase);
DivideComplexNumber := Result
end;
function ExponentialOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
var
RectangularOperand: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
RectangularOperand := ToRectangular (Operand);
Result.Tag := Polar;
Result.Magnitude := Exp (RectangularOperand.RealPart);
Result.Phase := ToPhaseRange (RectangularOperand.ImaginaryPart);
ExponentialOfComplexNumber := Result
end;
function NaturalLogarithmOfComplexNumber (Power: ComplexNumber):
ComplexNumber;
var
PolarPower: ComplexNumber;
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Power), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Assert (not ZeroComplexNumber (Power), NaturalLogarithmDomainException,
ComplexNumberExceptionHandler);
PolarPower := ToPolar (Power);
Result.Tag := Rectangular;
Result.RealPart := Ln (PolarPower.Magnitude);
Result.ImaginaryPart := PolarPower.Phase;
NaturalLogarithmOfComplexNumber := Result
end;
function RaiseComplexNumber (Base, Exponent: ComplexNumber):
ComplexNumber;
begin
Assert (ValidComplexNumber (Base) and ValidComplexNumber (Exponent),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
Assert (not ZeroComplexNumber (Base) or ZeroComplexNumber (Exponent) or
Positive (RealPartOfComplexNumber (Exponent)),
RaiseDomainException, ComplexNumberExceptionHandler);
if ZeroComplexNumber (Base) then begin
if ZeroComplexNumber (Exponent) then
RaiseComplexNumber := RealToComplexNumber (1.0)
else
RaiseComplexNumber := RealToComplexNumber (0.0)
end
else
RaiseComplexNumber :=
ExponentialOfComplexNumber (
MultiplyComplexNumber (Exponent,
NaturalLogarithmOfComplexNumber (Base)))
end;
function LogarithmOfComplexNumber (Base, Power: ComplexNumber):
ComplexNumber;
begin
Assert (ValidComplexNumber (Base) and ValidComplexNumber (Power),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
Assert (not ZeroComplexNumber (Power) and
not ZeroComplexNumber (Base) and
((RealPartOfComplexNumber (Base) <> 1.0) or
(ImaginaryPartOfComplexNumber (Base) <> 0.0)),
LogarithmDomainException, ComplexNumberExceptionHandler);
LogarithmOfComplexNumber :=
DivideComplexNumber (NaturalLogarithmOfComplexNumber (Power),
NaturalLogarithmOfComplexNumber (Base))
end;
function ReciprocalOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
var
Result: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Assert (not ZeroComplexNumber (Operand), ReciprocalDomainException,
ComplexNumberExceptionHandler);
Result := ToPolar (Operand);
Result.Magnitude := Reciprocal (Result.Magnitude);
ReciprocalOfComplexNumber := Result
end;
function SquareOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
SquareOfComplexNumber := MultiplyComplexNumber (Operand, Operand)
end;
function SquareRootOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
var
Log: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
if ZeroComplexNumber (Operand) then
SquareRootOfComplexNumber := Operand
else begin
Log := NaturalLogarithmOfComplexNumber (Operand);
Log.RealPart := Half (Log.RealPart);
Log.ImaginaryPart := Half (Log.ImaginaryPart);
SquareRootOfComplexNumber := ExponentialOfComplexNumber (Log)
end
end;
function SineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
var
ITimesOperand: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
ITimesOperand := MultiplyComplexNumber (I, Operand);
SineOfComplexNumber :=
DivideComplexNumber (
SubtractComplexNumber (
ExponentialOfComplexNumber (ITimesOperand),
ExponentialOfComplexNumber (NegateComplexNumber (ITimesOperand))),
MakeRectangularComplexNumber (0.0, 2.0))
end;
function CosineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
var
ITimesOperand: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
ITimesOperand := MultiplyComplexNumber (I, Operand);
CosineOfComplexNumber :=
DivideComplexNumber (
AddComplexNumber (
ExponentialOfComplexNumber (ITimesOperand),
ExponentialOfComplexNumber (NegateComplexNumber (ITimesOperand))),
RealToComplexNumber (2.0))
end;
function TangentOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Assert (not Zero (ImaginaryPartOfComplexNumber (Operand)) or
not Zero (Modulo (
RealPartOfComplexNumber (Operand) - Half (Pi),
Pi)),
TangentDomainException, ComplexNumberExceptionHandler);
TangentOfComplexNumber :=
DivideComplexNumber (SineOfComplexNumber (Operand),
CosineOfComplexNumber (Operand))
end;
function ArcSineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
ArcSineOfComplexNumber :=
MultiplyComplexNumber (
MakeRectangularComplexNumber (0.0, -1.0),
NaturalLogarithmOfComplexNumber (
AddComplexNumber (
MultiplyComplexNumber (I, Operand),
SquareRootOfComplexNumber (
SubtractComplexNumber (
RealToComplexNumber (1.0),
SquareOfComplexNumber (Operand))))))
end;
function ArcCosineOfComplexNumber (Operand: ComplexNumber): ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
ArcCosineOfComplexNumber :=
MultiplyComplexNumber (
MakeRectangularComplexNumber (0.0, -1.0),
NaturalLogarithmOfComplexNumber (
AddComplexNumber (
Operand,
MultiplyComplexNumber (
I,
SquareRootOfComplexNumber (
SubtractComplexNumber (
RealToComplexNumber (1.0),
SquareOfComplexNumber (Operand)))))))
end;
function ArcTangentOfComplexNumber (Operand: ComplexNumber):
ComplexNumber;
var
RealUnit: ComplexNumber;
ITimesOperand: ComplexNumber;
begin
Assert (ValidComplexNumber (Operand), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
Assert (not Zero (RealPartOfComplexNumber (Operand)) or
(Abs (ImaginaryPartOfComplexNumber (Operand)) <> 1.0),
ArcTangentDomainException, ComplexNumberExceptionHandler);
RealUnit := RealToComplexNumber (1.0);
ITimesOperand := MultiplyComplexNumber (I, Operand);
ArcTangentOfComplexNumber :=
DivideComplexNumber (
SubtractComplexNumber (
NaturalLogarithmOfComplexNumber (
AddComplexNumber (RealUnit, ITimesOperand)),
NaturalLogarithmOfComplexNumber (
SubtractComplexNumber (RealUnit, ITimesOperand))),
MakeRectangularComplexNumber (0.0, 2.0))
end;
function EqualComplexNumber (LeftOperand, RightOperand: ComplexNumber):
Boolean;
var
RectangularLeftOperand, RectangularRightOperand: ComplexNumber;
begin
Assert (ValidComplexNumber (LeftOperand) and
ValidComplexNumber (RightOperand),
InvalidComplexNumberException, ComplexNumberExceptionHandler);
RectangularLeftOperand := ToRectangular (LeftOperand);
RectangularRightOperand := ToRectangular (RightOperand);
EqualComplexNumber :=
(RectangularLeftOperand.RealPart =
RectangularRightOperand.RealPart) and
(RectangularLeftOperand.ImaginaryPart =
RectangularRightOperand.ImaginaryPart)
end;
procedure ReadComplexNumber (var Source: Text;
var Legend: ComplexNumber; var Success: Boolean);
label 99;
type
Sign = (Negative, NonNegative);
var
FirstSign, SecondSign: Sign;
FirstReal, SecondReal: Real;
procedure SkipWhiteSpace (var Source: Text);
var
Continue: Boolean;
begin
Continue := True;
while Continue do
if EOF (Source) then
Continue := False
else if Source^ <= ' ' then
Get (Source)
else
Continue := False
end;
begin
SkipWhiteSpace (Source);
if EOF (Source) then begin { no numeral left in file }
Success := False;
goto 99
end;
if Source^ = '-' then
FirstSign := Negative
else
FirstSign := NonNegative;
if Source^ in ['-', '+'] then begin
Get (Source);
if EOF (Source) then begin { file ended after initial sign }
Success := False;
goto 99
end
end;
if Source^ in ['i', 'I'] then begin { +i, -i, or i }
Get (Source);
if FirstSign = Negative then
Legend := MakeRectangularComplexNumber (0.0, -1.0)
else
Legend := MakeRectangularComplexNumber (0.0, 1.0)
end
else begin { After the sign, there should be a numeral. }
if not (Source^ in ['0' .. '9']) then begin { There wasn't. }
Success := False;
goto 99
end;
Read (Source, FirstReal);
if EOF (Source) then begin { file ended after real part }
if FirstSign = Negative then
Legend := MakeRectangularComplexNumber (-FirstReal, 0.0)
else
Legend := MakeRectangularComplexNumber (FirstReal, 0.0)
end
else if Source^ in ['i', 'I'] then begin { imaginary number }
Get (Source);
if FirstSign = Negative then
Legend := MakeRectangularComplexNumber (0.0, -FirstReal)
else
Legend := MakeRectangularComplexNumber (0.0, FirstReal)
end
else begin
Legend.Tag := Rectangular;
if FirstSign = Negative then
Legend.RealPart := -FirstReal
else
Legend.RealPart := FirstReal;
if Source^ in ['+', '-'] then begin
if Source^ = '-' then
SecondSign := Negative
else
SecondSign := NonNegative;
Get (Source);
if EOF (Source) then begin { file ended after sign of imag. part }
Success := False;
goto 99
end;
if Source^ in ['i', 'I'] then begin { imag. part is +i or -i }
Get (Source);
if SecondSign = Negative then
Legend.ImaginaryPart := -1.0
else
Legend.ImaginaryPart := 1.0
end
else begin { After the sign, there should be a numeral. }
if not (Source^ in ['0' .. '9']) then begin { There wasn't. }
Success := False;
goto 99
end;
Read (Source, SecondReal);
if EOF (Source) then begin { file ended without i in imag. part }
Success := False;
goto 99
end;
if not (Source^ in ['i', 'I']) then begin { no i in imag. part }
Success := False;
goto 99
end;
Get (Source);
if SecondSign = Negative then
Legend.ImaginaryPart := -SecondReal
else
Legend.ImaginaryPart := SecondReal
end
end
else { nothing useful after real part }
Legend.ImaginaryPart := 0.0
end
end;
Success := True;
Assert (ValidComplexNumber (Legend), ReadComplexNumberException,
ComplexNumberExceptionHandler);
99:
end;
procedure WriteComplexNumber (var Target: Text; Scribend: ComplexNumber);
var
RectangularScribend: ComplexNumber;
AbsoluteValueOfImaginaryPart: Real;
begin
Assert (ValidComplexNumber (Scribend), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
RectangularScribend := ToRectangular (Scribend);
if Zero (RectangularScribend.ImaginaryPart) then
Write (Target, Scribend.RealPart : 1 : 6)
else if Zero (RectangularScribend.RealPart) then begin
if RectangularScribend.ImaginaryPart = 1.0 then
Write (Target, 'i')
else if RectangularScribend.ImaginaryPart = -1.0 then
Write (Target, '-i')
else
Write (Target, RectangularScribend.ImaginaryPart : 1 : 6, 'i')
end
else begin
Write (Target, RectangularScribend.RealPart : 1 : 6);
if Positive (RectangularScribend.ImaginaryPart) then
Write (Target, '+')
else
Write (Target, '-');
AbsoluteValueOfImaginaryPart :=
Abs (RectangularScribend.ImaginaryPart);
if AbsoluteValueOfImaginaryPart <> 1.0 then
Write (Target, AbsoluteValueOfImaginaryPart : 1 : 6);
Write (Target, 'i')
end
end;
procedure WriteComplexNumberAsPolar (var Target: Text;
Scribend: ComplexNumber);
var
PolarScribend: ComplexNumber;
begin
Assert (ValidComplexNumber (Scribend), InvalidComplexNumberException,
ComplexNumberExceptionHandler);
PolarScribend := ToPolar (Scribend);
Write (Target, PolarScribend.Magnitude : 1 : 6, '@',
PolarScribend.Phase : 1 : 6)
end;
end.
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/courses/fundamentals/complex-numbers.html