[Back to MATH SWAG index]  [Back to Main SWAG index]  [Original]

{$N+,E+} {Use math coprocessor, if any, emulate otherwise.}
UNIT ComplexOps;  { see demo at end }

 {This UNIT provides complex arithmetic and transcendental functions.

  (C) Copyright 1990, 1992, Earl F. Glynn, Overland Park, KS.  Compuserve 73257,3527.
  All rights reserved.  This program may be freely distributed only for
  non-commercial use.

  Some ideas in this UNIT were borrowed from "A Pascal Tool for Complex
  Numbers", Journal of Pascal, Ada, & Modula-2, May/June 1985, pp. 23-29.
  Many complex formulas were taken from Chapter 4, "Handbook of Mathematical
  Functions" (Ninth Printing), Abramowitz and Stegun (editors), Dover, 1972.}

INTERFACE

  TYPE
    RealType    = DOUBLE;
    ComplexForm = (polar,rectangular);
    Complex =
      RECORD
        CASE form:  ComplexForm OF
          rectangular:  (x,y    :  RealType);  {z = x + y*i}
          polar      :  (r,theta:  RealType);  {z = r*CIS(theta)}
      END;                 {where CIS(theta) = COS(theta) + SIN(theta)*i}
                           {      theta = -PI..PI (in canonical form)}

  CONST
    MaxTerm    : BYTE     = 35;
    EpsilonSqr : RealType = 1.0E-20;
    Infinity   : RealType = 1.0E25;    {virtual infinity}

                             {complex number definition/conversion/output}
  PROCEDURE CConvert (VAR z:  Complex; f:  ComplexForm);
  PROCEDURE CSet (VAR z:  Complex; a,b:  RealType; f:  ComplexForm);
  FUNCTION  CStr (z:  Complex; w,d:  BYTE; f:  ComplexForm):  STRING;

                             {complex arithmetic}
  PROCEDURE CAdd  (VAR z:  Complex; a,b:  Complex);    {z = a + b}
  PROCEDURE CDiv  (VAR z:  Complex; a,b:  Complex);    {z = a / b}
  PROCEDURE CMult (VAR z:  Complex; a,b:  Complex);    {z = a * b}
  PROCEDURE CSub  (VAR z:  Complex; a,b:  Complex);    {z = a - b}
  PROCEDURE CNeg  (VAR z:  Complex; a  :  Complex);    {z = -a   }

                             {complex natural log, exponential}
  PROCEDURE CLn   (VAR fn :  Complex; z:  Complex);   {fn  = ln(z) }
  PROCEDURE CExp  (VAR z  :  Complex; a:  Complex);   {z   = exp(a)}
  PROCEDURE CPwr  (VAR z  :  Complex; a,b:  Complex); {z   = a^b   }

                             {complex trig functions}
  PROCEDURE CCos  (VAR z:  Complex; a:  Complex);     {z   = cos(a)}
  PROCEDURE CSin  (VAR z:  Complex; a:  Complex);     {z   = sin(a)}
  PROCEDURE CTan  (VAR z:  Complex; a:  Complex);     {z   = tan(a)}

  PROCEDURE CSec  (VAR z:  Complex; a:  Complex);     {z   = sec(a)}
  PROCEDURE CCsc  (VAR z:  Complex; a:  Complex);     {z   = csc(a)}
  PROCEDURE CCot  (VAR z:  Complex; a:  Complex);     {z   = cot(a)}

                             {complex hyperbolic functions}
  PROCEDURE CCosh (VAR z:  Complex; a:  Complex);     {z   = cosh(a)}
  PROCEDURE CSinh (VAR z:  Complex; a:  Complex);     {z   = sinh(a)}
  PROCEDURE CTanh (VAR z:  Complex; a:  Complex);     {z   = tanh(a)}

  PROCEDURE CSech (VAR z:  Complex; a:  Complex);     {z   = sech(a)}
  PROCEDURE CCsch (VAR z:  Complex; a:  Complex);     {z   = csch(a)}
  PROCEDURE CCoth (VAR z:  Complex; a:  Complex);     {z   = coth(a)}

                             {miscellaneous complex functions}
  FUNCTION  CAbs (z:  Complex):  RealType;                 {CAbs = |z|}
  FUNCTION  CAbsSqr (z:  Complex):  RealType;           {CAbsSqr = |z|^2}
  PROCEDURE CIntPwr (VAR z:  Complex; a:  Complex; n:  INTEGER); {z = a^n}
  PROCEDURE CRealPwr (VAR z:  Complex; a:  Complex; x:  RealType); {z = a^x}
  PROCEDURE CConjugate (VAR z:  Complex; a:  Complex);        {z = a*}
  PROCEDURE CSqrt (VAR z:  Complex; a:  Complex);      {z = SQRT(a)}
  PROCEDURE CRoot (VAR z:  Complex; a:  Complex; k,n:  WORD); {z = a^(1/n)}

                             {complex Bessel functions of order zero}
  PROCEDURE CI0   (VAR sum:  Complex; z:  Complex);  {sum = I0(z)}
  PROCEDURE CJ0   (VAR sum:  Complex; z:  Complex);  {sum = J0(z)}

  PROCEDURE CLnGamma (VAR z:  Complex; a:  Complex);
  PROCEDURE CGamma   (VAR z:  Complex; a:  Complex);

                                  {treat "fuzz" of real numbers}
  PROCEDURE CDeFuzz (VAR z:  Complex);
  FUNCTION  DeFuzz (x:  RealType):  RealType;
  PROCEDURE SetFuzz (value:  RealType);

                                  {miscellaneous}
  FUNCTION FixAngle (theta:  RealType):  RealType;    {-PI < theta <= PI}
  FUNCTION Pwr (x,y:  RealType):  RealType;    {Pwr = x^y}
  FUNCTION Log10 (x:  RealType):  RealType;
  FUNCTION LongMod (l1,l2:  LongInt):  LongInt;
  FUNCTION Cosh (x:  RealType):  RealType;
  FUNCTION Sinh (x:  RealType):  RealType;

IMPLEMENTATION

  VAR
    fuzz     :  RealType;
    Cone     :  Complex;
    Cinfinity:  Complex;
    Czero    :  Complex;
    hLnPI    :  RealType;
    hLn2PI   :  RealType;
    ln2      :  RealType;

                             {complex number definition/conversion/output}
  PROCEDURE CConvert (VAR z:  Complex; f:  ComplexForm);
    VAR a:  Complex;
  BEGIN
    IF   z.form = f
    THEN CDeFuzz (z)
    ELSE BEGIN
      CASE z.form OF
        polar:            {polar-to-rectangular conversion}
          BEGIN
            a.form := rectangular;
            a.x := z.r * COS(z.theta);
            a.y := z.r * SIN(z.theta)
          END;
        rectangular:      {rectangular-to-polar conversion}
          BEGIN
            a.form := polar;
            IF   DeFuzz(z.x) = 0.0
            THEN BEGIN
              IF   DeFuzz(z.y) = 0.0
              THEN BEGIN
                a.r     := 0.0;
                a.theta := 0.0
              END
              ELSE
                IF   z.y > 0.0
                THEN BEGIN
                  a.r := z.y;
                  a.theta := 0.5*PI
                END
                ELSE BEGIN
                  a.r := -z.y;
                  a.theta := -0.5*PI
                END
            END
            ELSE BEGIN
              a.r := CAbs(z);
              a.theta := ARCTAN(z.y/z.x);   {4th/1st quadrant -PI/2..PI/2}
              IF   z.x < 0.0                {2nd/3rd quadrants}
              THEN
                IF   z.y >= 0.0
                THEN a.theta :=  PI + a.theta {2nd quadrant:  PI/2..PI}
                ELSE a.theta := -PI + a.theta {3rd quadrant: -PI..-PI/2}
            END
          END;
      END;
      CDeFuzz (a);
      z := a
    END
  END {CConvert};

  PROCEDURE CSet (VAR z:  Complex; a,b:  RealType; f:  ComplexForm);
  BEGIN
    z.form := f;
    CASE f OF
      polar:
        BEGIN
          z.r := a;
          z.theta := b
        END;
      rectangular:
        BEGIN
          z.x := a;
          z.y := b
        END;
    END
  END {CSet};

  FUNCTION  CStr (z:  Complex; w,d:  BYTE; f:  ComplexForm):  STRING;
    VAR s1,s2:  STRING;
  BEGIN
    CConvert (z,f);
    CASE f OF
      polar:
        BEGIN
          Str (z.r:w:d, s1);
          Str (z.theta:w:d, s2);
          CStr := s1+'*CIS('+s2+')'
        END;
      rectangular:
        BEGIN
          Str (z.x:w:d, s1);
          Str (ABS(z.y):w:d, s2);
          IF   z.y >= 0
          THEN CStr := s1+' +'+s2+'i'
          ELSE CStr := s1+' -'+s2+'i'
        END
    END
  END {CStr};

                                  {complex arithmetic}
  PROCEDURE CAdd  (VAR z:  Complex; a,b:  Complex);    {z = a + b}
  BEGIN                                               {complex addition}
    CConvert (a,rectangular);
    CConvert (b,rectangular);
    z.form := rectangular;
    z.x := a.x + b.x;   {real part}
    z.y := a.y + b.y;   {imaginary part}
  END {CAdd};

  PROCEDURE CDiv  (VAR z:  Complex; a,b:  Complex);    {z = a / b}
    VAR temp:  RealType;
  BEGIN
    CConvert (b,a.form);    {arbitrarily convert one to type of other}
    z.form := a.form;
    CASE a.form OF
      polar:
        BEGIN
          z.r := a.r / b.r;
          z.theta := FixAngle(a.theta - b.theta)
        END;
      rectangular:
        BEGIN
          temp := SQR(b.x) + SQR(b.y);
          z.x := (a.x*b.x + a.y*b.y) / temp;
          z.y := (a.y*b.x - a.x*b.y) / temp
        END
    END
  END {CDiv};

  PROCEDURE CMult (VAR z:  Complex; a,b:  Complex);    {z = a * b}
  BEGIN
    CConvert (b,a.form);    {arbitrarily convert one to type of other}
    z.form := a.form;
    CASE a.form OF
      polar:
        BEGIN
          z.r := a.r * b.r;
          z.theta := FixAngle(a.theta + b.theta)
        END;
      rectangular:
        BEGIN
          z.x := a.x*b.x - a.y*b.y;
          z.y := a.x*b.y + a.y*b.x
        END
    END
  END {CMult};

  PROCEDURE CSub  (VAR z:  Complex; a,b:  Complex);    {z = a - b}
  BEGIN                                               {complex subtraction}
    CConvert (a,rectangular);
    CConvert (b,rectangular);
    z.form := rectangular;
    z.x := a.x - b.x;   {real part}
    z.y := a.y - b.y;   {imaginary part}
  END {CSub};

  PROCEDURE CNeg  (VAR z:  Complex; a  :  Complex);    {z = -a   }
  BEGIN
    z.form := a.form;
    CASE a.form OF
      polar:
        BEGIN
          z.r := a.r;
          z.theta := FixAngle(a.theta + PI)
        END;
      rectangular:
        BEGIN
          z.x := -a.x;
          z.y := -a.y
        END
    END
  END {CNeg};
                                  {complex natural log, exponential}
  PROCEDURE CLn (VAR fn:  Complex; z:  Complex);  {fn  = ln(z)}
  BEGIN  {Abramowitz formula 4.1.2 on p. 67}
    CConvert (z,polar);
    fn.form := rectangular;
    fn.x := LN(z.r);
    fn.y := FixAngle(z.theta)
  END {CLn};  {principal value only}

  PROCEDURE CExp  (VAR z  :  Complex; a:  Complex);   {z   = exp(a)}
    VAR
      temp:  RealType;
  BEGIN  {Euler's Formula:  Abramowitz formula 4.3.47 on p. 74}
    CConvert (a,rectangular);
    temp := EXP(a.x);
    CSet (z, temp*COS(a.y),temp*SIN(a.y), rectangular)
  END {CExp};

  PROCEDURE CPwr  (VAR z  :  Complex; a,b:  Complex); {z   = a^b   }
    VAR
      blna,lna:  Complex;
  BEGIN  {Abramowitz formula 4.2.7 on p. 69}
    CDeFuzz (a);
    CDeFuzz (b);
    IF   CAbsSqr(a) = 0.0
    THEN
      IF    (CAbsSqr(b) = 0.0)
      THEN  z := Cone                   {lim a^a = 1 as a -> 0}
      ELSE  z := Czero                  {0^b = 0, b <> 0}
    ELSE BEGIN
      CLn (lna,a);
      CMult (blna,b,lna);
      CExp (z, blna)
    END
  END {CPwr};
                                  {complex trig functions}
  PROCEDURE CCos  (VAR z:  Complex; a:  Complex);     {z   = cos(a)}
  BEGIN  {Abramowitz formula 4.3.56 on p. 74}
    CConvert (a,rectangular);
    CSet (z, COS(a.x)*COSH(a.y), -SIN(a.x)*SINH(a.y), rectangular)
  END {CCos};

  PROCEDURE CSin  (VAR z:  Complex; a:  Complex);     {z   = sin(a)}
  BEGIN  {Abramowitz formula 4.3.55 on p. 74}
    CConvert (a,rectangular);
    CSet (z, SIN(a.x)*COSH(a.y), COS(a.x)*SINH(a.y), rectangular)
  END {CSin};

  PROCEDURE CTan  (VAR z:  Complex; a:  Complex);     {z   = tan(a)}
    VAR
      temp:  RealType;
  BEGIN  {Abramowitz formula 4.3.57 on p. 74}
    CConvert (a,rectangular);
    temp := COS(2.0*a.x) + COSH(2.0*a.y);
    IF   DeFuzz(temp) <> 0.0
    THEN BEGIN
      CSet (z,SIN(2.0*a.x)/temp,SINH(2.0*a.y)/temp,rectangular)
    END
    ELSE z := Cinfinity
  END {CTan};

  PROCEDURE CSec  (VAR z:  Complex; a:  Complex);     {z   = sec(a)}
    VAR
      temp:  Complex;
  BEGIN  {Abramowitz formula 4.3.5 on p. 72}
    CCos (temp, a);
    IF   DeFuzz( Cabs(temp) ) <> 0.0
    THEN CDiv (z, Cone,temp)
    ELSE z := Cinfinity
  END {CSec};

  PROCEDURE CCsc  (VAR z:  Complex; a:  Complex);     {z   = csc(a)}
    VAR
      temp:  Complex;
  BEGIN  {Abramowitz formula 4.3.4 on p. 72}
    CSin (temp, a);
    IF   DeFuzz( Cabs(temp) ) <> 0.0
    THEN CDiv (z, Cone,temp)
    ELSE z := Cinfinity
  END {CCsc};

  PROCEDURE CCot  (VAR z:  Complex; a:  Complex);     {z   = cot(a)}
    VAR
      temp:  RealType;
  BEGIN  {Abramowitz formula 4.3.58 on p. 74}
    CConvert (a,rectangular);
    temp := COSH(2.0*a.y) - COS(2.0*a.x);
    IF   DeFuzz(temp) <> 0.0
    THEN BEGIN
      CSet (z,SIN(2.0*a.x)/temp,-SINH(2.0*a.y)/temp,rectangular)
    END
    ELSE z := Cinfinity
  END {CCot};

                                  {complex hyperbolic functions}
  PROCEDURE CCosh (VAR z:  Complex; a:  Complex);     {z   = cosh(a)}
  BEGIN  {Abramowitz formula 4.5.50 on p. 84}
    CConvert (a,rectangular);
    CSet (z, COSH(a.x)*COS(a.y), SINH(a.x)*SIN(a.y), rectangular)
  END {CCosh};

  PROCEDURE CSinh (VAR z:  Complex; a:  Complex);     {z   = sinh(a)}
  BEGIN  {Abramowitz formula 4.5.49 on p.84}
    CConvert (a,rectangular);
    CSet (z, SINH(a.x)*COS(a.y), COSH(a.x)*SIN(a.y), rectangular)
  END {CSinh};

  PROCEDURE CTanh (VAR z:  Complex; a:  Complex);     {z   = tanh(a)}
    VAR
      temp:  RealType;
  BEGIN  {Abramowitz formula 4.5.51 on p. 84}
    CConvert (a,rectangular);
    temp := COSH(2.0*a.x) + COS(2.0*a.y);
    IF   DeFuzz(temp) <> 0.0
    THEN BEGIN
      CSet (z,SINH(2.0*a.x)/temp,SIN(2.0*a.y)/temp,rectangular)
    END
    ELSE z := Cinfinity
  END {CTanh};

  PROCEDURE CSech (VAR z:  Complex; a:  Complex);     {z   = sech(a)}
    VAR
      temp:  Complex;
  BEGIN  {Abramowitz formula 4.5.5 on p. 83}
    CCosh (temp, a);
    IF   DeFuzz( Cabs(temp) ) <> 0.0
    THEN CDiv (z, Cone,temp)
    ELSE z := Cinfinity
  END {CSec};

  PROCEDURE CCsch (VAR z:  Complex; a:  Complex);     {z   = csch(a)}
    VAR
      temp:  Complex;
  BEGIN  {Abramowitz formula 4.5.4 on p. 83}
    CSinh (temp, a);
    IF   DeFuzz( Cabs(temp) ) <> 0.0
    THEN CDiv (z, Cone,temp)
    ELSE z := Cinfinity
  END {CCsch};

  PROCEDURE CCoth (VAR z:  Complex; a:  Complex);     {z   = coth(a)}
    VAR
      temp:  RealType;
  BEGIN  {Abramowitz formula 4.5.52 on p. 84}
    CConvert (a,rectangular);
    temp := COSH(2.0*a.x) - COS(2.0*a.y);
    IF   DeFuzz(temp) <> 0.0
    THEN BEGIN
      CSet (z,SINH(2.0*a.x)/temp,-SIN(2.0*a.y)/temp,rectangular)
    END
    ELSE z := Cinfinity
  END {CCoth};

                                  {miscellaneous complex functions}
  FUNCTION CAbs (z:  Complex):  RealType;                  {CAbs = |z|}
  BEGIN
    CASE z.form OF
      rectangular:  CAbs := SQRT( SQR(z.x) + SQR(z.y) );
      polar:        CAbs := z.r
    END
  END {CAbs};

  FUNCTION CAbsSqr (z:  Complex):  RealType;            {CAbsSqr = |z|^2}
  BEGIN
    CASE z.form OF
      rectangular:  CAbsSqr := SQR(z.x) + SQR(z.y);
      polar:        CAbsSqr := SQR(z.r)
    END
  END {CAbsSqr};

  PROCEDURE CIntPwr (VAR z:  Complex; a:  Complex; n:  INTEGER); {z = a^n}
    {CIntPwr directly applies DeMoivre's theorem to calculate
     an integer power of a complex number.  The formula holds
     for both positive and negative values of 'n'.}
  BEGIN
    IF   CAbsSqr(a) = 0.0
    THEN
      IF    n = 0
      THEN  z := Cone                   {lim a^a = 1 as a -> 0}
      ELSE  z := Czero                  {0^n = 0, except for 0^0=1}
    ELSE BEGIN
      CConvert (a,polar);
      z.form := polar;
      z.r := Pwr(a.r,n);
      z.theta := FixAngle(n*a.theta)
    END
  END {CIntPwr};

  PROCEDURE CRealPwr (VAR z:  Complex; a:  Complex; x:  RealType); {z = a^x}
  BEGIN
    IF   CAbsSqr(a) = 0.0
    THEN
      IF    Defuzz(x) = 0.0
      THEN  z := Cone                   {lim a^a = 1 as a -> 0}
      ELSE  z := Czero                  {0^n = 0, except for 0^0=1}
    ELSE BEGIN
      CConvert (a,polar);
      z.form := polar;
      z.r := Pwr(a.r,x);
      z.theta := FixAngle(x*a.theta)
    END
  END {CRealPwr};

  PROCEDURE CConjugate (VAR z:  Complex; a:  Complex);      {z = a*}
  BEGIN
    z.form := a.form;
    CASE a.form OF
      polar:
        BEGIN
          z.r := a.r;
          z.theta := FixAngle(-a.theta)
        END;
      rectangular:
        BEGIN
          z.x := a.x;
          z.y := -a.y
        END
    END
  END {CConjugate};

  PROCEDURE CSqrt (VAR z:  Complex; a:  Complex);      {z = SQRT(a)}
  BEGIN
    CRoot (z, a, 0,2)  {return only one of the two values}
  END {CSqrt};
                                           {z = a^(1/n), n > 0}
  PROCEDURE CRoot (VAR z:  Complex; a:  Complex; k,n:  WORD);
    {CRoot can calculate all 'n' roots of 'a' by varying 'k' from 0..n-1.}
    {This is another application of DeMoivre's theorem.  See CIntPwr.}
  BEGIN
    IF   CAbs(a) = 0.0
    THEN z := Czero                   {0^z = 0, except 0^0 is undefined}
    ELSE BEGIN
      CConvert (a,polar);
      z.form := polar;
      z.r := Pwr(a.r,1.0/n);
      z.theta := FixAngle((a.theta + k*2.0*PI)/n)
    END
  END {CRoot};

                             {complex Bessel functions of order zero}
  PROCEDURE CI0   (VAR sum:  Complex; z:  Complex);  {sum = I0(z)}
    {I0(z) = ä ( (­z^2)^k / (k!)^2 ), k=0,1,2,...,ì}
    VAR
      i      :  BYTE;
      SizeSqr:  RealType;
      term   :  Complex;
      zSQR25 :  Complex;
  BEGIN
    CConvert (z,rectangular);
    sum := Cone;                       {term 0}
    Cmult (zSQR25, z,z);
    zSQR25.x := 0.25 * zSQR25.x;
    zSQR25.y := 0.25 * zSQR25.y;       {­z^2}
    term := zSQR25;
    CAdd (sum, sum,zSQR25);            {term 1}
    i := 1;
    REPEAT
      CMult (term,zSQR25,term);
      INC (i);
      term.x := term.x / SQR(i);
      term.y := term.y / SQR(i);
      CAdd (sum, sum,term);       {sum := sum + term}
      SizeSqr := SQR(term.x) + SQR(term.y)
    UNTIL (i > MaxTerm) OR (SizeSqr < EpsilonSqr)
  END {CI0};

  PROCEDURE CJ0   (VAR sum:  Complex; z:  Complex);  {sum = J0(z)}
    {J0(z) = ä ( (-1)^k * (­z^2)^k / (k!)^2 ), k=0,1,2,...,ì}
    VAR
      addflag:  BOOLEAN;
      i      :  BYTE;
      SizeSqr:  RealType;
      term   :  Complex;
      zSQR25 :  Complex;
  BEGIN
    CConvert (z,rectangular);
    sum := Cone;                       {term 0}
    Cmult (zSQR25, z,z);
    zSQR25.x := 0.25 * zSQR25.x;
    zSQR25.y := 0.25 * zSQR25.y;       {­z^2}
    term := zSQR25;
    CSub (sum, sum,zSQR25);            {term 1}
    addflag := FALSE;
    i := 1;
    REPEAT
      CMult (term,zSQR25,term);
      INC (i);
      addflag := NOT addflag;
      term.x := term.x / SQR(i);
      term.y := term.y / SQR(i);
      IF   addflag
      THEN CAdd (sum, sum,term)        {sum := sum + term}
      ELSE CSub (sum, sum,term);       {sum := sum - term}
      SizeSqr := SQR(term.x) + SQR(term.y)
    UNTIL (i > MaxTerm) OR (SizeSqr < EpsilonSqr)
  END {CJ0};

  PROCEDURE CApproxLnGamma (VAR sum:  Complex; z:  Complex);
    {This is the approximation used in the National Bureau of
     Standards "Table of the Gamma Function for Complex Arguments,"
     Applied Mathematics Series 34, 1954.  The NBS table was created
     using this approximation over the area  9 ó Re(z) ó 10 and
     0 ó Im(z) ó 10.  Other table values were computed using the
     relationship ln â(z+1) = ln z + ln â(z).}
    CONST
      c:  ARRAY[1..8] OF RealType
       =  (1/12, -1/360, 1/1260, -1/1680, 1/1188, -691/360360,
           1/156, -3617/122400);
    VAR
      i     :  WORD;
      powers:  ARRAY[1..8] OF Complex;
      temp1 :  Complex;
      temp2 :  Complex;
  BEGIN
    CConvert (z,rectangular);
    CLn  (temp1,z);                              {ln(z}
    CSet (temp2, z.x-0.5, z.y, rectangular);     {z - 0.5}
    CMult (sum, temp1,temp2);                    {(z - 0.5)*ln(z)}
    CSub (sum, sum,z);                           {(z - 0.5)*ln(z) - z}
    sum.x := sum.x + hLn2PI;

    temp1 := Cone;
    CDiv (powers[1], temp1, z);                  {z^-1}
    CMult (temp2, powers[1],powers[1]);          {z^-2}
    FOR i := 2 TO 8 DO
      CMult (powers[i], powers[i-1],temp2);
    FOR i := 8 DOWNTO 1 DO BEGIN
      CSet (temp1, c[i]*powers[i].x, c[i]*powers[i].y, rectangular);
      CAdd (sum, sum,temp1);
    END
  END {CApproxLnGamma};

  PROCEDURE CLnGamma (VAR z:  Complex; a:  Complex);
    VAR
      lna :  Complex;
      temp:  Complex;
  BEGIN
    CConvert (a, rectangular);

    IF   (a.x <= 0.0) AND (DeFuzz(a.y) = 0.0)
    THEN
      IF   DeFuzz(INT(a.x-1E-8) - a.x) = 0.0     {negative integer?}
      THEN BEGIN
        z := Cinfinity;
        EXIT
      END;

    IF   a.y < 0.0                     {3rd or 4th quadrant?}
    THEN BEGIN
      CConjugate (a, a);
      CLnGamma (z, a);                 {try again in 1st or 2nd quadrant}
      CConjugate (z, z)                {left this out!  1/3/91}
    END
    ELSE BEGIN
      IF   a.x < 9.0                   {"left" of NBS table range}
      THEN BEGIN
        CLn (lna, a);
        CSet (a, a.x+1.0, a.y, rectangular);
        CLnGamma (temp,a);
        CSub (z, temp,lna)
      END
      ELSE CApproxLnGamma (z,a)  {NBS table range:  9 ó Re(z) ó 10}
    END
  END {CLnGamma};

  PROCEDURE CGamma (VAR z:  Complex; a:  Complex);
    VAR
      lnz:  Complex;
  BEGIN
    CLnGamma (lnz,a);
    IF   lnz.x >= 75.0       {arbitrary cutoff for infinity}
    THEN z := Cinfinity
    ELSE
      IF   lnz.x < -200.0
      THEN z := Czero        {avoid underflow}
      ELSE CExp (z, lnz);
  END {CGamma};

                                  {treat "fuzz" of real numbers}
  PROCEDURE CDeFuzz (VAR z:  Complex);
  BEGIN
    CASE z.form OF
      rectangular:
        BEGIN
          z.x := DeFuzz(z.x);
          z.y := DeFuzz(z.y);
        END;
      polar:
        BEGIN
          z.r := DeFuzz(z.r);
          IF   z.r = 0.0
          THEN z.theta := 0.0     {canonical form when radius is zero}
          ELSE z.theta := DeFuzz(z.theta)
        END
    END
  END {CDeFuzz};

  FUNCTION  DeFuzz (x:  RealType):  RealType;
  BEGIN
    IF   ABS(x) < fuzz
    THEN Defuzz := 0
    ELSE Defuzz := x
  END {Defuzz};

  PROCEDURE SetFuzz (value:  RealType);
  BEGIN
    fuzz := value
  END {SetFuzz};

                                  {miscellaneous}
  FUNCTION FixAngle (theta:  RealType):  RealType;    {-PI < theta <= PI}
  BEGIN
    WHILE theta > PI DO
      theta := theta - 2.0*PI;
    WHILE theta <= -PI DO
      theta := theta + 2.0*PI;
    FixAngle := DeFuzz(theta)
  END {FixAngle};

  FUNCTION Pwr (x,y:  RealType):  RealType;        {Pwr = x^y}
  BEGIN
    IF   DeFuzz(x) = 0.0
    THEN
      IF   DeFuzz(y) = 0.0
      THEN Pwr := 1.0    {0^0 = 1 (i.e., lim x^x = 1 as x -> 0)}
      ELSE Pwr := 0.0
    ELSE Pwr := EXP( LN(x)*y )
  END {Pwr};

  FUNCTION Log10 (x:  RealType):  RealType;
  BEGIN
    Log10 := LN(x) / LN(10)
  END {Log10};

  FUNCTION LongMod (l1,l2:  LongInt):  LongInt;
  BEGIN
    LongMod := l1 - l2*(l1 DIV l2)
  END {LongMod};

  FUNCTION Cosh (x:  RealType):  RealType;
  BEGIN
    Cosh := 0.5*( EXP(x) + EXP(-x) )
  END {Cosh};

  FUNCTION Sinh (x:  RealType):  RealType;
  BEGIN
    Sinh := 0.5*( EXP(x) - EXP(-x) )
  END {Sinh};

BEGIN
  SetFuzz (1.0E-12);
  CSet ( Cone, 1.0, 0.0, rectangular);
  CSet (Czero, 0.0, 0.0, rectangular);
  CSet (Cinfinity, Infinity, Infinity, rectangular);
  hLnPI := 0.5*(LN(PI));
  hLn2PI := 0.5*(LN(2.0*PI));
  ln2 := LN(2.0)
END.

{-------------  DEMO ----  CUT HERE ------ }
{$N+,E+}
PROGRAM cdemo;

 {This PROGRAM demonstrates the use of the ComplexOps UNIT.

  (C) Copyright 1990, 1992, Earl F. Glynn, Overland Park, KS.  Compuserve 73257,3527.
  All rights reserved.  This program may be freely distributed only for
  non-commercial use.}


  USES ComplexOps;

  VAR
    a      :  ARRAY[1..22] OF Complex;
    csave  :  ARRAY[1..22] OF Complex;
    k,m    :  WORD;
    n      :  INTEGER;
    x,y    :  RealType;
    z,z1,z2:  Complex;

BEGIN

  WRITELN ('Demo ComplexOPs PROCEDUREs and FUNCTIONs');
  WRITELN;
  WRITELN ('  Notes:  1.  CIS(w) = COS(w) + i*SIN(w), w = -PI..PI');
  WRITELN ('          2.  z = x + i*y');
  WRITELN;
  WRITELN;

  CSet (a[ 1],  0.0,  0.0, rectangular);
  CSet (a[ 2],  0.5,  0.5, rectangular);
  CSet (a[ 3], -0.5,  0.5, rectangular);
  CSet (a[ 4], -0.5, -0.5, rectangular);
  CSet (a[ 5],  0.5, -0.5, rectangular);
  CSet (a[ 6],  1.0,  0.0, rectangular);
  CSet (a[ 7],  1.0,  1.0, rectangular);
  CSet (a[ 8],  0.0,  1.0, rectangular);
  CSet (a[ 9], -1.0,  1.0, rectangular);
  CSet (a[10], -1.0,  0.0, rectangular);
  CSet (a[11], -1.0, -1.0, rectangular);
  CSet (a[12],  0.0, -1.0, rectangular);
  CSet (a[13],  1.0, -1.0, rectangular);
  CSet (a[14],   5.,   0., rectangular);
  CSet (a[15],   5.,   3., rectangular);
  CSet (a[16],   0.,   3., rectangular);
  CSet (a[17],  -5.,   3., rectangular);
  CSet (a[18],  -5.,   0., rectangular);
  CSet (a[19],  -5.,  -3., rectangular);
  CSet (a[20],   0.,  -3., rectangular);
  CSet (a[21],  -5.,  -3., rectangular);
  CSet (a[22], -20.,  20., rectangular);

  WRITELN ('Complex number definition/conversion/output:  CSet/CConvert/CStr');
  WRITELN;
  WRITELN ('   z rectangular':25,'z polar':28);
  WRITELN ('     ---------------------------   ',
    '-----------------------------');
  FOR k := 1 TO 21 DO
    WRITELN (k:3,'  ',CStr(a[k],12,8,rectangular),'  ',
                     CStr(a[k],12,8,polar));
  WRITELN;
  WRITELN;

  WRITELN ('Complex arithmetic:  CAdd, CSub, CMult, CDiv');
  WRITELN;

  CSet (z1,  1, 1, rectangular);
  WRITELN ('Let z1 = ':12,CStr(z1,8,3,rectangular):20,' or ',
                      CStr(z1,8,3,polar));
  CSet (z2, SQRT(3), -1, rectangular);
  WRITELN ('z2 = ':12,CStr(z2,8,3,rectangular):20,' or ',
                      CStr(z2,8,3,polar));
  WRITELN;

  CAdd  (z,z1,z2);
  WRITELN ('z1 + z2 = ':12,CStr(z,8,3,rectangular):20,' or ',
                           CStr(z,8,3,polar));

  CSub  (z,z1,z2);
  WRITELN ('z1 - z2 = ':12,CStr(z,8,3,rectangular):20,' or ',
                           CStr(z,8,3,polar));

  CMult (z,z1,z2);
  WRITELN ('z1 * z2 = ':12,CStr(z,8,3,rectangular):20,' or ',
                           CStr(z,8,3,polar));

  CDiv  (z,z1,z2);
  WRITELN ('z1 / z2 = ':12,CStr(z,8,3,rectangular):20,' or ',
                           CStr(z,8,3,polar));
  WRITELN;
  WRITELN;

  WRITELN ('Complex natural logarithm:  CLn = LN(z)');
  WRITELN;
  WRITELN ('  Notes:  1.  LN(z) is multivalued.');
  WRITELN (' ':9,' 2.  Any multiple of 2*PI*i could be added to/',
    'subtracted from LN(z).');
  WRITELN (' ':9,' 3.  LN(1)=0; LN(-1)=PI*i; LN(+/-i)=+/-0.5*PI*i.');
  WRITELN;
  WRITELN ('LN(z)':35);
  WRITELN ('z':11,'rectangular':27,'EXP( LN(z) ) = z':32);
  WRITELN ('     ------------  ---------------------------  ',
    '---------------------------');
  FOR k := 1 TO 22 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    IF   CAbs(a[k]) = 0.0
    THEN WRITELN ('undefined':18)
    ELSE BEGIN
      CLn (z,a[k]);
      CExp (z1,z);
      WRITELN (CStr(z,12,9,rectangular),'  ',CStr(z1,12,9,rectangular))
    END
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex exponential:  CExp = EXP(z)');
  WRITELN;
  WRITELN ('EXP(z)':35);
  WRITELN ('z':11,'rectangular':27,'LN( EXP(z) ) = z':32);
  WRITELN ('     ------------  ---------------------------  ',
    '---------------------------');
  FOR k := 1 TO 22 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CExp (z,a[k]);
    CLn (z1,z);
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),'  ',CStr(z1,12,m,rectangular))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex power:  CPwr = z1^z2');
  WRITELN;
  WRITELN ('z^(-1+i)':36,'z^(-1+i)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  CSet (z1, -1,1, rectangular);
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    IF   CAbs(a[k]) = 0.0
    THEN WRITELN ('undefined':18)
    ELSE BEGIN
      CPwr (z,a[k],z1);
      WRITELN (CStr(z,12,9,rectangular),' ',CStr(z,12,9,polar))
    END
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex cosine:  CCos = COS(z)');
  WRITELN;
  WRITELN ('COS(z)':35,'COS(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CCos (z,a[k]);
    CIntPwr (csave[k], z,2);  {save COS^2}
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex sine:  CSin = SIN(z)');
  WRITELN;
  WRITELN ('SIN(z)':35);
  WRITELN ('z':11,'rectangular':27,'SIN^2(z)+COS^2(z)=1':32);
  WRITELN ('     ------------  ---------------------------  ',
    '---------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CSin (z,a[k]);
    CIntPwr (z1, z,2);      {SIN^2}
    CAdd (z1, z1,csave[k]); {SIN^2 + COS^2}
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),'  ',CStr(z1,12,9,rectangular))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex tangent:  CTan = TAN(z)');
  WRITELN;
  WRITELN ('TAN(z)':35,'TAN(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CTan (z,a[k]);
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex hyperbolic cosine:  CCosh = COSH(z)');
  WRITELN;
  WRITELN ('COSH(z)':36,'COSH(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CCosh (z,a[k]);
    CIntPwr (csave[k], z,2);  {save COSH^2}
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex hyperbolic sine:  CSinh = SINH(z)');
  WRITELN;
  WRITELN ('SINH(z)':36);
  WRITELN ('z':11,'rectangular':27,'COSH^2(z)-SINH^2(z)=1':34);
  WRITELN ('     ------------  ---------------------------  ',
    '---------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CSinh (z,a[k]);
    CIntPwr (z1, z,2);      {SINH^2}
    CSub (z1, csave[k],z1); {COSH^2 - SINH^2}
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),'  ',CStr(z1,12,9,rectangular))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex hyperbolic tangent:  CTanh = TANH(z)');
  WRITELN;
  WRITELN ('TANH(z)':36,'TANH(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CTanh (z,a[k]);
    IF   CAbs(z) > 10.0
    THEN m := 4
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Absolute value of complex number:  CAbs = ABS(z)');
  WRITELN;
  WRITELN ('z':11,'ABS(z)':17);
  WRITELN ('     ------------  ------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITELN (k:3,' ',CStr(a[k],5,1,rectangular),'  ',CAbs(a[k]):12:9)
  END;
  WRITELN;

  WRITELN ('Complex integer power:  CIntPwr = z^n  ',
    '(using DeMoivre''s Theorem)');
  WRITELN;
  WRITELN ('z^3':34,'z^3':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    IF   CAbs(a[k]) = 0.0
    THEN WRITELN ('undefined':18)
    ELSE BEGIN
      CIntPwr (z,a[k],3);
      IF   CAbs(z) > 10.0
      THEN m := 7
      ELSE m := 9;
      WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
    END
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex conjugate:  CConjugate = z*');
  WRITELN;
  WRITELN ('z*':35,'z*':29);
  WRITELN ('z':11,'rectangular':28,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CConjugate (z,a[k]);
    WRITELN (CStr(z,12,8,rectangular),' ',CStr(z,12,8,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex square root:  CSqrt = SQRT(z)');
  WRITELN;
  WRITELN ('SQRT(z)':36,'SQRT(z)':28);
  WRITELN ('z':11,'root 1':25,'root 2':28);
  WRITELN ('     ------------  ---------------------------  ',
    '---------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CSqrt (z,a[k]);       {same as CRoot (z,a[k],0,2)}
    CRoot (z1,a[k],1,2);
    WRITELN (CStr(z,12,9,rectangular),'  ',CStr(z1,12,9,rectangular))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('The three cube roots of -1+i:  (-1+i)^(1/3)');
  WRITELN ('(See Schaum''s Outline Series "Complex Variables", 1964, ',
    'p. 18, problem 29.)');
  WRITELN;
  WRITELN ('z^(1/3)':35,'z^(1/3)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  CSet (z1, -1,1, rectangular);
  FOR k := 0 TO 2 DO BEGIN
    WRITE (k:3,' ',CStr(z1,5,1,rectangular),'  ');
    CRoot (z,z1,k,3);
    WRITELN (CStr(z,12,9,rectangular),' ',CStr(z,12,9,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex Bessel function:  CI0 = I0(z)');
  WRITELN;
  WRITELN ('I0(z)':36,'I0(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CI0 (z,a[k]);
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Complex Bessel function:  CJ0 = J0(z)');
  WRITELN;
  WRITELN ('J0(z)':36,'J0(z)':29);
  WRITELN ('z':11,'rectangular':27,'polar':26);
  WRITELN ('     ------------  ---------------------------  ',
    '-----------------------------');
  FOR k := 1 TO 21 DO BEGIN
    WRITE (k:3,' ',CStr(a[k],5,1,rectangular),'  ');
    CJ0 (z,a[k]);
    IF   CAbs(z) > 10.0
    THEN m := 7
    ELSE m := 9;
    WRITELN (CStr(z,12,m,rectangular),' ',CStr(z,12,m,polar))
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Removing "Fuzz" from real numbers for zero test:');
  WRITELN;  {Note:  CStr calls CConvert that calls CDefuzz}
  CSet (z, -3.21E-14,7.65E-14, rectangular);
  WRITELN ('  Before:  ',z.x:18:15,' +',z.y:18:15,'i');
  CDeFuzz (z);
  WRITELN ('   After:  ',CStr(z,18,15,rectangular));
  WRITELN;
  CSet (z, -3.21E-14,PI, polar);
  WRITELN ('  Before:  ',z.r:18:15,'*CIS(',z.theta:18:15,')');
  CDeFuzz (z);
  WRITELN ('   After:  ',CStr(z,18,15,polar));
  WRITELN;
  WRITELN;

  WRITELN ('Miscellaneous:  FixAngle -- keep angle in interval (-PI..PI)');
  WRITELN;

  WRITELN ('     radians FixAngle');
  WRITELN ('    -------- --------');
  FOR n := -4 TO 8 DO BEGIN
    x := n*PI/2.0;
    y := FixAngle(x);
    WRITELN (n:3,' ',x:8:5,' ',y:8:5)
  END;
  WRITELN;
  WRITELN;

  WRITELN ('Real power function:  Pwr = x^y');
  WRITELN;
  WRITELN ('        x        y         x^y');
  WRITELN ('    -------- -------- ------------');
  WRITELN (' ':4,2.1:8:5,' ',-2.5:8:5,Pwr(2.1,-2.5):12:9);
  WRITELN (' ':4,2.1:8:5,' ', 2.5:8:5,Pwr(2.1, 2.5):12:9);
  WRITELN (' ':4,1.4:8:5,' ', 8.9:8:5,Pwr(1.2, 8.9):12:9);
  WRITELN (' ':4,0.0:8:5,' ', 2.0:8:5,Pwr(0.0, 2.0):12:9);
  WRITELN (' ':4,4.2:8:5,' ', 0.0:8:5,Pwr(4.2, 0.0):12:9);
  WRITELN;

END {cdemo}.

[Back to MATH SWAG index]  [Back to Main SWAG index]  [Original]