[comp.lang.pascal] Complex arithmetic

4526P%NAVPGS.BITNET@CUNYVM.CUNY.EDU (LT Scott A. Norton, USN) (01/12/88)

As an electrical engineer, I do a lot of complex math.  My current
complex arithmetic package for Pascal is a bit cumbersome, and I wonder
if anyone can see any way to clean it up.

My first gripe is that Pascal doesn't provide, or let me define, infix
arithmetic operators for complex numbers.  Fortran has complex math
built in, and Ada lets you define "+", for example, as an operator
for user-defined types.  I can't see any way around this, so my
complex math is based on functions, with the result of a LISP-like
next of parenthesis.  add( times( a,b ), times( c,d ) );

The other restriction is the fact that a function can not return a
record.  So I have to return pointers to records.  The records
themselves I have to allocate with new() inside the function.  Then,
to prevent the growth of garbage, I needed some way of disposing of these
allocated records.  My solution was that every complex math function
disposes of its arguments.  Finally, I needed conversion routines
to transform complex variables into allocated records, and an assignment
procedure to store results in variables.

Is there any cleaner way to implement complex numbers in Pascal?  My
only other idea was to mark() the heap, but that is not standard Pascal.

The declarations for my complex numbers follows.  If you can see a way
to improve them, please let me know.  Conversely, if you want too use
them, feel free.  Since they were developed by a federal employee( me ),
they are public domain.
-------------------------------------------------------------------
const pi = 3.14159265358979;
type
     complex = record
        re, im : real;
        end; { record }
     cxptr = @complex;

{ These operations return pointers to complex numbers.  To prevent
garbage accumulation, they dispose of their arguments, which are
also @complex.  }

function v( a : complex ) : cxptr;  { 'value' function.  Converts  }
                                    { complex to cxptr.            }
     var result : cxptr;
     begin
        new(result);
        result@ := a;
        v := result;
        end; { v }

function cmplx(a,b : real ) : cxptr;  { a + b*j }
     var result : cxptr;
     begin
        new(result);
        result@.re := a;  result@.im := b;
        cmplx := result;
        end; { cmplx }

procedure assign( var a : complex; b : cxptr ); { Assign a := b@ }
     begin
        a := b@;
        dispose( b );
        end; { assign }

function plus (a,b : cxptr) : cxptr;
 external; function plus;
     var result : cxptr;
     begin
        new(result);
        result@ .re := a@ .re + b@ .re;
        result@ .im := a@ .im + b@ .im;
        plus := result;
        dispose (a); dispose (b);
     end; { plus }

function minus(a,b : cxptr) : cxptr;
 external; function minus;
     var result : cxptr;
     begin
        new(result);
        result@ .re := a@ .re - b@ .re;
        result@ .im := a@ .im - b@ .im;
        minus := result;
        dispose (a); dispose (b);
     end; { minus }

function times (a,b : cxptr) : cxptr;
 external; function times;
     var result : cxptr;
     begin
        new(result);
        result@ .re := a@ .re * b@ .re - a@ .im * b@ .im;
        result@ .im := a@ .im * b@ .re + a@ .re * b@ .im;
        times := result;
        dispose (a); dispose (b);
     end; { times }

function magsq (a : cxptr) : real;
 external; function magsq;
         { Calculates the magnitude squared of the argument a }
     begin
        magsq := sqr(a@.re) + sqr(a@.im);
     end; { magsq }

function divide (a,b : cxptr) : cxptr;
 external; function divide;
     var result : cxptr;
         m : real;
     begin
        m := 1/ magsq( b );
        new(result);
        result@ .re := ( a@.re * b@.re + a@.im * b@.im ) * m;
        result@ .im := ( a@.im * b@.re - a@.re * b@.im ) * m;
        divide := result;
        dispose (a); dispose (b);
     end; { divide }

function scale (a : real; b : cxptr) : cxptr;
 external; function scale;
     var result : cxptr;
     begin
        new(result);
        result@ .re := a * b@ .re;
        result@ .im := a * b@ .im;
        scale := result;
        dispose (b);
     end; { scale }

function rplus (a : cxptr; b : real) : cxptr;   { complex + real }
 external; function rplus;
     var result : cxptr;
     begin
        new(result);
        result@ .re := a@ .re + b;
        result@ .im := a@ .im;
        rplus := result;
        dispose (a);
     end; { rplus }

function jexp (a : real) : cxptr;     { e ** (j a) }
 external; function jexp;
     var result : cxptr;
     begin
        new(result);
        result@ .re := cos(a);
        result@ .im := sin(a);
        jexp := result;
        end; { jexp }

function angle (x : cxptr) : real;  { -270 <= angle < 90 degrees }
 external; function angle;
     var a : real;
     begin
        if x@.re = 0 then if x@.im > 0 then a:= -270 else a:= -90
                     else a:= arctan( x@.im / x@.re ) * 180 / pi;
        if x@.re < 0 then a := a - 180;
        angle := a;
     end; { angle }

function csqrt(a : cxptr) : cxptr;   { Complex square root }
 external; function csqrt;
 { This function is based on an algorithm describes in
   Numerical Recipes by William H. Press, et al, 1986, Cambridge
   University Press. }
     const tiny = 1e-15;
     var result : cxptr;
         Q, R : real;
     begin
        new(result);
        with a@ do begin
           if ( abs(re)<tiny ) and ( abs(im)<tiny )
             then begin
              result@.re := 0; result@.im := 0;
              end
            else begin
              if abs(re)>abs(im)
               then R := abs(re) * sqrt( 1 + sqr( im / re ) )
               else R := abs(im) * sqrt( 1 + sqr( re / im ) );
              Q := abs(re) + R;
              if re > 0
                then begin
                 result@.re :=sqrt(0.5*Q);
                 result@.im :=abs( im ) * sqrt(0.5/Q);
                 end { then }
                else begin
                 result@.re :=abs( im ) * sqrt(0.5/Q);
                 result@.im :=sqrt(0.5*Q);
                 end; { else }
              if im < 0
                then result@.im := -result@.im;
              end; { else ( csqrt non-zero ) }
           end; { with }
        csqrt := result;
        dispose (a);
     end; { csqrt }

function power( a : cxptr; b : real ) : cxptr;
{ Raises a to the power b  }
   begin { function power }
      power := scale( exp( 0.5 * b * ln(magsq( a )) ),
                      jexp( b * angle( a ) ) );
      end; { function power }
----------------------------------------------------------------------
LT Scott A. Norton, USN     | From Internet, if you need a gateway, use
Naval Postgraduate School   |    4526p%navpgs.bitnet@jade.berkeley.edu
Monterey, CA 93943-5018     | or 4526p%navpgs.bitnet@ucscc.ucsc.edu
4526P@NavPGS.BITNET         | The WISCVM gateway closed 15 Dec 87. )

shankar@hpclscu.HP.COM (Shankar Unni) (01/14/88)

/ hpclscu:comp.lang.pascal / 4526P%NAVPGS.BITNET@CUNYVM.CUNY.EDU (LT Scott A. Norton, USN) /  6:42 pm  Jan 11, 1988 /

> My first gripe is that Pascal doesn't provide, or let me define, infix
> arithmetic operators for complex numbers.  Fortran has complex math
> built in, and Ada lets you define "+", for example, as an operator
> for user-defined types.

This is one among several big hits against Pascal, at least as it is defined
by the ANSI folks. Lots of other features (seperate compilation, type
coercion, pointer manipulation, extensibility, etc) are also missing.

There is an evolving standard for Extended Pascal (also by the ANSI committee)
which has most of these features, and a whole lot more (to the point where it
looks like a union of Pascal, FORTRAN and COBOL (shudder!!)). But the most
important "feature", i.e. extensibility of the language (ability to define
and overload operators, add "predefines" (inline procedures)) is still absent.

C++ (or objective C) is much better in this regard, giving the programmer the
ability to define classes of objects, and overloaded functions and operators
(a lot like ADA) to generate inline code for these types. As a starter, the
compiler comes with a few such types implemented as add-on include files, to 
give the programmer a good example to base his/her modules on. One of these,
of course, is a "complex" module.

I realise, of course, that this is no consolation to you. As Pascal stands
today (or even as it stands tomorrow, for that matter), you may have to define
"LISP-like" functions to do your stuff. BTW, most Pascals allow users to
return structures as function return values, so at least you should be able to
get rid of most of the new/dispose stuff: simply define functions like:

     function add (var c1, c2 : complex) : complex;
     ...

-----------------------------------------------------------------------------
Shankar Unni                                   allegra)
Unix Mail: shankar@hpda.hp.com            UUCP: ucbvax)!hplabs!hpda!shankar
                                                   sun)
(AT&T: (408) 447-5797)                          decwrl)
-----------------------------------------------------------------------------

bobdi@omepd (Bob Dietrich) (01/15/88)

I realize this is not an immediate help to you, but the draft proposed
Extended Pascal Standard has complex numbers built-in as a "simple" type
(e.g., infix operators are supported), along with functions operating on
complex numbers. You can also have parameters and functions of type complex,
as with any data type. Incidently, Extended Pascal allows functions to be of
most any structured type (except, of course, files, since they are not an
assignable type).

				Bob Dietrich
				Intel Corporation, Hillsboro, Oregon
				(503) 696-2092 (messages x2111)
		usenet:		tektronix!ogcvax!omepd!ihf1!bobd
		  or		tektronix!psu-cs!omepd!ihf1!bobd
		  or		ihnp4!verdix!omepd!ihf1!bobd