*Version of June, 2005*

- Introduction
- Classes
- Constructors
- Member functions
- Global functions
- Conversions
- Binary functions
- Input and output
- Global variables, functions
- Mandelbrot set example

This is a rather more extensive complex number library than the one provided
in the standard library. It provides complex, imaginary and polar classes and a
representation of the *i* or *j* of complex analysis. It does not use
templates. As in my other libraries, this library is based on a floating point
type *Real*. This must be *typedef*ed to be *double*, *float*
or a class defined by the user (this is done in the file include.h).

**This library is not going to replace the one in the standard library
- so use this one only if you need its additional facilities or you
really don't want to use the standard one.**

As usual with my libraries, there are no restrictions on the use of this library. If you distribute it, you should note that it is available essentially for free on the Internet.

Use is at your own risk. I take no responsibility for errors and problems or losses you may incur through the use of this library. But please report errors.

See the documentation for newmat11
for setting options in the file *include.h* .

Go to the download page for the library.

Author: Robert Davies; robert** at **statsresearch.co.nz.

Available from http://www.robertnz.net

The following files are included in this package

cx.h | header file for the complex number library |

cx.cpp | function bodies |

cx_polar.cpp | function bodies for polar class |

boolean.h | simulation of the standard Boolean type |

myexcept.h | header for the exceptions simulator |

myexcept.cpp | bodies for the exceptions simulator |

include.h | options header file (see documentation in newmat10) |

cxtest.h | test program header file |

cxtest.cpp | test program main function |

cxtest1.cpp | test unary functions |

cxtest2.cpp | test binary functions |

cxtest.txt | output from the test program |

array1.h | simple array class used by test program |

mandel.cpp | Mandelbrot set example |

mandel.txt | Output from mandel.cpp |

cx_targ.txt | target file for use with genmake |

cx_b55.mak | make file for Borland 5.5 compiler |

cx_b56.mak | make file for Borland 5.6 compiler |

cx_m6.mak | make file for VC++ 6, 7 compilers |

cx_ow.mak | make file for Open Watcom compiler |

cx_gnu.mak | make file for Gnu compiler |

cx_cc.mak | make file for CC compiler |

cx_i8.mak | make file for Intel 8.1 compiler for Windows |

cx_il8.mak | make file for Intel 8.1 compiler for Linux |

cx.htm | this file |

rbd.css | Style sheet for cx.htm |

polar.gif | diagram of polar coordinates |

mandel.gif | Mandelbrot set example |

I have tested this program on the Borland 5.5,5.6; MS VC++ 6,7; Gnu 3.3, 3.4;
Intel 8. The *test* program uses *templates* so you will not be able
to run the test program on compilers that don't support templates. I
couldn't get the templates in the test program to compile with the Sun CC
compiler. The program didn't compile with the Open Watcom (1.3) compiler.

I include make files for the test programs for several compilers - see the file list. Make files for some other compilers can be generated with my genmake utility.

If the program is working correctly the output from the test program will end with two lines similar to

maximum from cxtest1: 3.10082e-14 maximum from cxtest2: 7.10543e-15The exact values will, most likely, differ. These values are from the Borland 5.02 compiler.

For older compilers that don't support *bool* variables you need to edit * include.h* to
enable my simulated Booleans (comment out the statement *#define bool_LIB 0*).

You will need to #include files * include.h* and * cx.h* in your programs that use this
package. Edit * include.h* to determine whether exceptions are to be used,
simulated or disabled. If you use the simulated exceptions you should turn off
the exception capability of a compiler that does support exceptions. If you want to use namespace activate the *use_namespace* definition. This
library uses the namespace name RBD_COMPLEX.

May, 2000 | Initial release |

July, 2001 | Improve compatibility with my other libraries, include sum_square() function |

class |
description |
user |

class CX | complex | yes |

class Imag | imaginary | yes |

class ImaginaryUnit | the i or j of complex analysis |
yes |

class Polar | polar | yes |

class ConvertFromReal | prevent implicit conversion | no |

class Quadrant | mod 4 arithmetic | no |

Class *CX* is the main complex class. I have chosen the name *CX*
so as not to conflict with other complex classes. For example, using *complex*
will clash with the standard library.

Class *Imag* defines imaginary variables. If you have a complex variable
that will always be imaginary (the real part is always zero), then use the Imag
class to save computer time and make the program clearer.

Class *ImaginaryUnit* is used to represent *i* = sqrt(-1). I use
this to define * _I_* as a global variable to represent *i*.

Class *Polar* represents a complex number in polar coordinates.

I use class *ConvertFromReal* is used to prevent implicit conversions from *Real*
to *Imag*. Modern C++ compilers use the keyword *explicit* to prevent
implicit conversion, but, at present I don't want to assume all compilers
implement *explicit*.

Class *Quadrant* is used by class *Polar* in the representation of
the angle part of the polar coordinate.

The angle in the polar coordinates is represented by *theta* which is no
greater than *pi*/4 in absolute value and represents the angle from one of
the axes. Quadrant shows which axis. A *char* value in a Quadrant object
can take on one of the values 0, 1, 2 or 3 corresponding to the positive real
axis, the positive imaginary axis, the negative real axis or the negative
imaginary axis respectively. This representation means that one doesn't lose
accuracy when converting from complex coordinates to polar coordinates for
complex numbers very close to the real or imaginary axes.

CX() | constructor, don't set values |

CX(Real x, Real y) | set real part to x, imaginary part to y |

CX(Real x) | set real part to x, imaginary part to 0 |

CX(Imag y) | set real part to 0, imaginary part to y |

CX(ImaginaryUnit) | set real part to 0, imaginary part to 1 |

CX(const CX& z) | copy constructor |

CX(const Polar& p) | convert polar value p to complex |

Imag() | constructor, don't set values |

Imag(ConvertFromReal x) | set real part to x. ConvertFromReal stops implicit
conversions |

Imag(const Imag& z) | copy constructor |

Imag(ImaginaryUnit) | set value to i |

Polar() | constructor, don't set values |

Polar(Real r, Real theta) | set polar coordinates to r, theta after
normalising |

Polar(Real x) | set polar coordinates to x, 0 or -x, pi |

Polar(Imag y) | set polar coordinates to y, pi/2 or -y,-pi/2 |

Polar(ImaginaryUnit) | set polar coordinates to 1, pi/2 |

Polar(const CX& z) | convert complex z to polar |

Polar(const Polar& p) | copy constructor |

Function |
Description |
Return types with this class |
|||

CX |
Imag |
_I_ |
Polar |
||

real() const | real part | Real | Real | Real | |

real() | ref. to real part | Real& | |||

imag() const | imaginary part | Real | Real | Real | |

imag() | ref. to imaginary part | Real& | Real& | ||

imag_as_imag() const | imaginary part as Imag | Imag | Imag | Imag | |

conj() const | conjugate | CX | Imag | Polar | |

cabs() const | absolute value | Real | Real | Real | |

cabs() | ref. to absolute value | Real& | |||

arg() const | argument | Real | Real | Real | |

sum_square() const | square of cabs | Real | Real | Real | |

operator+() const |
+ prefix op. |
CX | Imag | _I_ | Polar |

operator-() const |
- prefix op. |
CX | Imag | Imag | Polar |

theta() const | theta | Real | |||

theta() | ref. to theta | Real& | |||

quadrant() const | quadrant | Quadrant | |||

quadrant() | ref. to quadrant | Quadrant& | |||

assert_is_valid() const AssertIsValid() const |
check Polar valid | void |

For example:

CX z = 3.0 + 4.0 * _I_; cout << z.real() << ", " << z.imag() << endl;

outputs 3.0, 4.0 .

The declaration of *z* could have been replaced with any of the
following:

CX z(3.0, 4.0); CX z; z.real() = 3.0; z.imag() = 4.0; CX z = 3.0 + Imag(4.0); Polar p(5.0, atan(4.0 / 3.0)); CX z = p;

Note that there are two versions of *z.real()* and *z.imag()* for *CX*
variable, *z*. The non-*const* version is used when *z* hasn't
been declared as *const*. This version can be used on the left-hand side of
an = sign. The same applies for *p.cabs()*, *p.theta()* and *p.quadrant()*
for *Polar* variable *p*.

It might seem more natural for *z.imag()* to return a variable of type *Imag*
rather than *Real*. However, this would be unusual for those used to the
standard library or Fortran. I include another function *z.imag_as_imag()*
which does return the imaginary part as type *Imag*. Nevertheless, there is
a potential source of error, here, if *z.imag()* is used when *z.imag_as_imag()*
was intended.

*sum_square()* is the *norm* function in the standard. It isn't a
norm in the mathematical sense so *you* wouldn't call it *norm* would you?

*assert_is_valid* checks that a Polar variable is valid. An exception is
thrown if the value is invalid; for example the absolute value of the member
variable *Theta* is greater than *pi*/4. This should not be possible unless a
Polar object is uninitialised or invalid values have been entered with the *cabs*, *theta* or *quadrant*
functions.

Function |
Description |
Return types with this class |
|||

CX |
Imag |
_I_ |
Polar |
||

real(.) | real part | Real | Real | Real | |

imag(.) | imaginary part | Real | Real | Real | |

imag_as_imag(.) | imaginary part as Imag | Imag | Imag | Imag | |

conj(.) | conjugate | CX | Imag | Polar | |

cabs(.) | absolute value | Real | Real | Real | |

fabs(.) | absolute value | Real | Real | Real | |

norm1(.) | fabs(real) + fabs(imag) | Real | |||

arg(.) | argument | Real | Real | Real | |

exp(.) | exponential | CX | CX | ||

log(.) | natural log | CX | CX | CX | |

polar_exp(.) | exponential | Polar | Polar | ||

sqrt(.) | square root | CX | CX | Polar | |

square(.) | square | CX | Real | Polar | |

sum_square(.) | square of cabs | Real | Real | Real | |

sin(.) | sin trig. function | CX | Imag | ||

cos(.) | cos trig. function | CX | Real | ||

tan(.) | tan trig. function | CX | Imag | ||

sinh(.) | sinh hyperbolic fn. | CX | Imag | ||

cosh(.) | cosh hyperbolic fn. | CX | Real | ||

tanh(.) | tanh hyperbolic fn. | CX | Imag |

For example:

CX z = 3.0 + 4.0 * _I_; cout << real(z) << ", " << imag(z) << endl;

outputs 3.0, 4.0 .

Only the *const* versions of *real(z)* and *imag(z)* are
provided. The comments about the member functions *.imag()* and *.imag_as_imag()*
also apply here. Note the differences between *imag(z)*, *imag_as_imag(z)*
and *Imag(z)*.

CX z = 3.0 + 4.0 * _I_; Real a = imag(z); // returns a = 4 Imag b = imag_as_imag(z); // returns b = 4iImag c = Imag(z); // error, can't convert CX to Imag

It is important not to confuse *Imag* and *imag* or *Real* and
*real*.

The functions *fabs* and *cabs* are alternative names for the same
function.

The function *norm1* returns the sum of the absolute values of the real
and imaginary parts. This is faster than *cabs* and may be sufficient for
deciding, for example, whether the absolute value of a complex number is less than some bound.

The function *polar_exp* takes the *exp* of a complex number and
returns the result in polar coordinates.

=, conversions |
To |
||||

From |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
Yes | Yes | |||

Real |
Yes | (Yes) | Yes | ||

Imag |
Yes | Yes | Yes | ||

_I_ |
Yes | Yes | Yes | ||

Polar |
Yes | Yes |

The only conversions allowed are ones that do not lose information (apart
from round-off error). For example, you cannot convert *Polar* to *Imag*.

I return void from the = operation so you can't do *a* = *b* = *c*;
.

+, - |
Second Argument |
||||

First Argument |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
CX | CX | CX | CX | CX |

Real |
CX | (Real) | Imag | Imag | CX |

Imag |
CX | Imag | Imag | Imag | CX |

_I_ |
CX | Imag | Imag | Imag, Real | CX |

Polar |
CX | CX | CX | CX | CX |

When additions or subtractions involve * Polar* the Polar number is converted to
*
CX* and a * CX* addition or subtraction performed.

*, / |
Second Argument |
||||

First Argument |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
CX | CX | CX | CX | illegal |

Real |
CX | (Real) | Imag | Imag | Polar |

Imag |
CX | Imag | Real | Real | Polar |

_I_ |
CX | Imag | Real | Real | Polar |

Polar |
illegal |
Polar | Polar | Polar | Polar |

Multiplication of a *CX* variable with a *Polar* variable will
result in a compile error. Either convert the *CX* variable to *Polar*,
if you want a *Polar* result or the *Polar* variable to *CX* if
you want a *CX* result.

+=, -= |
Argument |
||||

Class |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
Yes | Yes | Yes | Yes | Yes |

Real |
(Yes) | ||||

Imag |
Yes | Yes | |||

_I_ |
|||||

Polar |
Yes | Yes | Yes | Yes | Yes |

*=, /= |
Argument |
||||

Class |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
Yes | Yes | Yes | Yes | Yes |

Real |
(Yes) | ||||

Imag |
Yes | ||||

_I_ |
|||||

Polar |
Yes | Yes | Yes | Yes | Yes |

==, != |
Second Argument |
||||

First Argument |
CX |
Real |
Imag |
_I_ |
Polar |

CX |
bool | bool | bool | bool | illegal |

Real |
bool | (bool) | bool | false |
bool |

Imag |
bool | bool | bool | bool | bool |

_I_ |
bool | false |
bool | true |
bool |

Polar |
illegal |
bool | bool | bool | bool |

Remember that, because of round-off error, testing equality of floating point variables is not very useful unless the variables involved have values that can be represented exactly as binary numbers or might be equal because one is a copy of the other.

pow |
Second Argument |
||||

First Argument |
CX |
Real, int |
Imag |
_I_ |
Polar |

CX |
CX | CX | CX | ||

Real |
CX | (Real) | CX | ||

Imag |
CX | CX | CX | ||

_I_ |
|||||

Polar |
Polar | Polar | Polar |

If *y* is of type *int* and in the range -12 to 12 then *pow(x,y)* is
calculated by direct multiplication. In all other cases it is calculated with *exp(y *
log(x))*.

Value of *pow(x,y)* if *x* is zero:

x |
y |
pow(x,y) |

0 | int and negative |
throw exception |

0 | int and non-negative |
0 |

0 | not int and non-positive real part |
throw exception |

0 | not int and positive real part |
0 |

Make sure you understand what you are doing if you use *pow(x,y)* with
complex *y*.

I have not defined input and output functions.

Real pi, pi_times_2, pi_over_2, pi_over_4 Real ipow(Real x, int n)

ImaginaryUnit _I_

The variables *pi*, *pi_times_2*, *pi_over_2*, * pi_over_4*
have the obvious values.

The global variable *_I_* is the representation of *i* = sqrt(-1).

The function *ipow* is *pow* with integer second argument, *n*.
If *n* is in the range -12 to 12 the value is calculated by multiplication.
An version of *pow* with integer argument is included in the new standard
but is not in all compilers.

The example calculates what is essentially the boundary of the Mandelbrot set
using contour integration. Details are given in the program file. The program
outputs the coordinates of the points on the boundary. Here is an
Excel scatter plot of the points. The program is mostly a demonstration of the *CX*
class but there is a little use of the *Imag* class and *_I_*. A copy
of the output from the program is in mandel.txt. Your output will probably
differ because of the formatting of the output and round-off error.