info-sather
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

1.3b7 CPX and CPXD bugfixes


From: Darren J Wilkinson
Subject: 1.3b7 CPX and CPXD bugfixes
Date: Mon, 19 Mar 2001 09:29:38 +0000

Dear Keith and Co, 
  I've downloaded and installed 1.3 beta 7, and it seems to work OK
generally. However, within the Numeric section of the Required
library, the complex number classes, CPX and CPXD are still a bit of a
mess. It's just a hunch, but I'm guessing that complex numbers aren't
Keith's forte! ;-) Anyway, I've fixed up a couple of bugs in the
routines, and I've re-written most of the pre and post conditions, as
most of them were buggy. I've also attached some test classes (which I
actually emailed to Keith a year ago, but maybe they got lost). I'm
not certain that everything is exactly right, but things are a lot
better than they were! Keep up the good work everyone. Cheers,

-- 
Darren Wilkinson      Email:  address@hidden
                        WWW:  http://www.btinternet.com/~darrenjwilkinson/
------------------------->  GNU Sather - sourcefile  <-------------------------
-- Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand        --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute  and/or modify it under the terms of the GNU Library General --
-- Public  License (LGPL)  as published  by the  Free  Software  Foundation; --
-- either version 2 of the license, or (at your option) any later version.   --
-- This  library  is distributed  in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       --
-- The license text is also available from:  Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     --
-------------->  Please email comments to <address@hidden>  <--------------

partial class CPX{STP < $REAL{STP}, ATP} is

--        This class implements the mathematical notion of a complex number
--   within the constraints of the parameter type.

--        Some of the algorithms are taken from:
--
--        Press, Flannery, Teukolsky, and Vettering, "Numerical Recipes in C",
--        2nd ed, CUP, 1993.
--
--        Some of the choices of branch cut were chosen to be consistent with:
--
--        Guy L. Steele, "Common Lisp, The Language", 2nd ed, Digital 1990

--         Version 1.1 Dec 2000.   Copyright K Hopper, U of Waikato

--                          Development History
--                          -------------------

--        Date           Who By         Detail
--        ----           ------         ------

--        8 Aug 97        kh       Original from Sather 1.1 Dist
--        7 Dec 00        kh       included is_lt based on magnitude.
--       18 Mar 01        djw      Fixed bug in div, added routine
--                                 is_similar, and fixed/added numerous 
--                                 pre and post conditions


include COMPARABLE ;
include BINARY ;
include COMPLEX_STR{STP} ;

-- include CPX_FUNCTIONS ;                   -- include if desired!

const negatable : BOOL := true ;

attr re,
     im : STP ;                              -- Real and imaginary parts.


stub log : SAME ;

--        This routine returns the complex logarithm of self.


create_real(
           val : STP
           ) : SAME is

--        This routine creates a complex number which has a zero imaginary
--   component.

     return create(val,STP::create(0.0))
end ;


build(
      cursor : BIN_CURSOR
      ) : SAME

     pre ~void(cursor)
               and ~cursor.is_done

     post true

     is

--        This routine returns the complex number contained in the indicated
--   string at the current position.

     return create(STP::build(cursor),STP::build(cursor))
end ;


create(
      val : CARD
      ) : SAME is

--        This version of create produces one which has an integral real part
--   but zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
      val : FIELD
      ) : SAME is

--        This version of create produces one which has an integral real part
--   but zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
      val : INT
      ) : SAME is

--        This version of create produces one which has an integral real part
--   but zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
      val : INTI
      ) : SAME

     pre true

     post (result.re = STP::create(val))
          and (result.im = STP::zero)

     is

--        This version of create produces one which has an integral real part
--   but zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
      val : RAT
      ) : SAME

     pre true

     post (result.re = STP::create(val))
          and (result.im = STP::zero)

     is

--        This version of create produces one which has an integral real part
--   but zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
      val : FLT
      ) : SAME is

--        This routine creates a complex number with a real part val and
--   zero imaginary part.

     return create_real(STP::create(val))
end ;


create(
       val : FLTD
       ) : SAME

     pre (STP::maxval.fltd >= val)
          and (val >= -STP::maxval.fltd)

     post true

     is

--        This routine creates a complex number with a real part val and
--   zero imaginary part.

     return create_real(STP::create(val))
end ;


zero : SAME is

--        This routine provides a complex zero value.

     return create(STP::zero,STP::zero)
end ;


one : SAME is

--        This routine provides a complex number with unit real part and zero
--   imaginary part.

     return create(STP::one,STP::zero)
end ;


maxval : SAME is

--        This routine creates a complex number which has the maximum
--   representable real and imaginary parts.

     return create(STP::maxval,STP::maxval)
end ;


minval : SAME is

--        This routine creates a complex number which has the minimum
--   representable real and imaginary parts.

     return create(STP::minval,STP::minval)
end ;


nil : SAME is

--        This predicate returns a nil complex value.

     return create(re.nil,im.nil)
end ;


private absolute : STP

     pre true

     post create(result.square).is_similar(create(self.magnitude_squared))

     is

--        This private routine returns the absolute magnitude of self which is
--   calculated using the algorithm in 'Numerical Recipes in C' p949.

     loc_re : STP := re.abs ;
     loc_im : STP := im.abs ;
     temp : STP ;

     if loc_re = STP::zero then
          return loc_im
     elsif loc_im = STP::zero then
          return loc_re
     elsif loc_re > loc_im then
          temp := loc_im / loc_re ;
          return loc_re * (STP::one + temp * temp).sqrt
     else
          temp := loc_re / loc_im ;
          return loc_im * (STP::one + temp * temp).sqrt
     end
end ;


abs : SAME

     pre true

     post (result.re = absolute)
          and (result.im = STP::zero)

     is

--        This routine returns the absolute value of self.   It is here to
--   conform to the interface of $NFE.

     return create_real(absolute)
end ;


magnitude : STP

     pre true

     post result = absolute

     is

--        This routine returns the absolute magnitude of self.

     return absolute
end ;


magnitude_squared : STP

      pre (re / STP::maxval)*re + (im / STP::maxval)*im < STP::one
      
      post true
      
     is

--        This routine returns the square of the absolute magnitude of self.

     return re * re + im * im
end ;


conjugate : SAME

     pre true

     post (self*result).is_similar(create(self.magnitude_squared))

     is

--        This routine returns the complex conjugate of self.

     return create(re,-im)
end ;


is_eq(
     other : SAME
     ) : BOOL is

--        This predicate returns true if and only if self and other have
--   identical values of real and imaginary parts.

     return re = other.re
                and im = other.im
end ;


is_lt(
     other : SAME
     ) : BOOL is

--        This predicate returns true if and only if the magnitude of self is
--   less than that of other, otherwise false

     return magnitude < other.magnitude
end ;


is_nil : BOOL is

--        This predicate returns true if either component of the number is nil.

     return re.is_nil
               or im.is_nil
end ;


is_neg : BOOL is

--        This routine returns true if and only if both components are negative.

     return (re < STP::zero)
               and (im < STP::zero)
end ;


is_zero : BOOL is

--        This routine returns true if and only if both components are zero.

     return (re = STP::zero)
               and (im = STP::zero)
end ;


is_pos : BOOL is

--        This routine returns true if and only if both components are positive.

     return (re > STP::zero)
               and (im > STP::zero)
end ;


is_within(
          radius : STP,
          other : SAME
          ) : BOOL is

--        This predicate returns true if and only if self is within the given
--   radius of other.

     return (self - other).magnitude_squared <= radius*radius
end ;

is_similar(
           other : SAME
           ) : BOOL is
      
      tol::=STP::epsilon.sqrt;
      return is_within(tol,other);
end ; 

sign : NUM_SIGNS is

--        This routine returns the sign of self which is negative if either
--   component is negative, zero if both are zero - otherwise positive..

     if (re < STP::zero)
               or (im < STP::zero) then
          return NUM_SIGNS::Negative
     elsif self = zero then
          return NUM_SIGNS::Zero
     else
          return NUM_SIGNS::Positive
     end
end ;


plus(
     other : SAME
     ) : SAME

      pre ( ((re / STP::maxval) + (other.re / STP::maxval) < STP::one)
           and ((re / STP::maxval) + (other.re / STP::maxval) > -STP::one)
           and ((im / STP::maxval) + (other.im / STP::maxval) < STP::one)
           and ((im / STP::maxval) + (other.im / STP::maxval) > -STP::one) ) 
      
      post self.is_similar(result-other)
      
     is

--      This routine returns the sum of self and other.

     return create(re + other.re,im + other.im)
end ;


minus(
      other : SAME
      ) : SAME

     pre ((self.re.sign = other.re.sign)
               or ((STP::maxval - self.re.abs) >= other.re.abs))
          and ((self.im.sign = other.im.sign)
               or ((STP::maxval - self.im.abs) >= other.im.abs))

      post true 
      
     is

--        This routine returns the complex difference of subtracting other from
--   self.

     return create(re - other.re,im - other.im)
end ;


negate : SAME

     pre true

     post zero.is_similar(self+result)
      
     is

--        This routine returns the additive inverse of self.

     return create(-re,-im)
end ;


times(
     other : SAME
     ) : SAME

      pre ( 
           ((re / STP::maxval)*other.re - (im / STP::maxval)*other.im 
            < STP::one) and 
           ((re / STP::maxval)*other.re - (im / STP::maxval)*other.im 
            > -STP::one) and 
           ((re / STP::maxval)*other.im + (im / STP::maxval)*other.re 
            < STP::one) and 
           ((re / STP::maxval)*other.im + (im / STP::maxval)*other.re 
            > -STP::one)
           )

      post true
      
     is

--        This routine returns the complex product of self and other.

     return create(re * other.re - im * other.im,
                                re * other.im + im * other.re)
end ;


div(
    other : SAME
    ) : SAME

     pre true

      post self.is_similar(result*other)
      
     is

--        This routine returns the result of complex division of self by other.

     denom,
     res : STP ;

     if other.re.abs >= other.im.abs then
          res := other.im/other.re ;
          denom := other.re + res * other.im ;
          res := res / denom ;               -- to make sure no overflow!
          return create((re/denom) + (res * im),(im/denom) - (res * re))
     else
          res := other.re/other.im ;
          denom := other.im + res * other.re ;
          res := res / denom ;               -- to make sure no overflow!
          return create((im/denom) + (res * re), (res * im) - (re/denom))
     end
end ;


mod(
    other : SAME
    ) : SAME is

--        This routine returns the remainder of the result of dividing self by
--   other.  This is zero for a complex number.

        return create(0)
end ;


times(
      factor : STP
      ) : SAME

     pre (((factor.abs > STP::one)
                    and ((STP::maxval / factor.abs) <= re.abs))
               or ((STP::maxval * factor.abs) >= re.abs))
          and (((factor.abs > STP::one)
                    and ((STP::maxval / factor.abs) <= im.abs))
               or ((STP::maxval * factor.abs) >= im.abs))

     post result.is_similar(self*create(factor))

     is

--        This routine scales both real and imaginary components of self by
--   the given factor.

     return create(re * factor,im * factor)
end ;


div(
    divisor : STP
    ) : SAME

     pre (divisor.abs >= STP::one)
          or (((divisor.abs * STP::maxval) <= re.abs)
               and ((divisor.abs * STP::maxval) <= im.abs))

     post result.is_similar(self/create(divisor))

     is

--        This routine divides both components of self by the given divisor.

     return create(re / divisor, im /divisor)
end ;


pow(
     other : SAME
     ) : SAME

     pre ~((re = STP::zero)
                and (im = STP::zero))

     is

--        This routine returns the result of raising self to the power of other.

     return (log * other).exp
end ;


reciprocal : SAME

     pre true

     post one.is_similar(self*result)

     is

--        This routine returns the multiplicative inverse of self.

     denom,
     res : STP ;

     if re.abs >= im.abs then
          res := im/re ;
          denom := re + res * im ;
          return create(STP::one/denom,-res/denom)
     else
          res := re/im ;
          denom := im + res * re ;
          return create(res/denom,(-STP::one)/denom)
     end
end ;


exp : SAME is

--        This routine returns the complex exponential `e^self'.

     real_part : STP := re.exp ;
     phase : ATP := ATP::radians(im) ;
     return create(real_part * phase.cos,real_part * phase.sin)
end ;


sqrt : SAME

     pre true

     post self.is_similar(result.square)
      
     is

--        This routine returns the complex square root of self.   The algorithm
--   is taken from 'Numerical Recipes in C' p949, choosing the branch cut by
--
--        e^((log z)/2)

     if re = STP::create(STP::zero)          -- zero is special case.
               and im = STP::create(STP::zero) then
          return create(STP::create(STP::zero),STP::create(STP::zero))
     end ;

     loc_re : STP := re.abs ;
     loc_im : STP := im.abs ;
     trial_val : STP ;
     loc_half : STP := STP::one / (STP::one + STP::one) ;

     if loc_re >= loc_im then
          tmp : STP := loc_im / loc_re ;
          trial_val := loc_re.sqrt * (loc_half * STP::one) +
                                  (STP::one + tmp * tmp).sqrt.sqrt
     else
          tmp : STP := loc_re / loc_im ;
          trial_val := loc_im.sqrt * (loc_half *
                              (tmp + (STP::one + tmp * tmp).sqrt)).sqrt
     end ;

     loc_two : STP := STP::one + STP::one ;

     if re >= STP::zero then
          return create(trial_val,im / (loc_two * trial_val))
     elsif im >= STP::zero then
          return create(im / (loc_two * trial_val),trial_val)
     else
          return create(-im / (loc_two * trial_val),-trial_val)
     end
end ;


cube_root : SAME

     pre true

     post self.is_similar(result.cube)

     is

--        This routine returns the complex cube root of self using a preliminary
--   algorithm.

     loc_three : STP := STP::one + STP::one + STP::one ;
     return self.pow(create_real(STP::one/loc_three))
end ;


square : SAME

--     pre (STP::maxval / re.square < im.square)

     post result.is_similar(self.pow(one+one))

     is

--        This routine returns the square of self.

     return self * self
end ;


cube : SAME

     pre (STP::maxval / (re.square * re) < (im.square * im))

     post result.is_similar(self.pow(one+one+one))

     is

--        This routine returns the cube of self.

     return self * self * self
end ;


binstr : BINSTR

     pre true

     post build(result.cursor) = self

     is

--        This routine returns a binary representation of self.

      return re.binstr + im.binstr
   end ;


end ; -- CPX{T}

-------------------------------------------------------------------------------

immutable class CPX < $COMPLEX{FLT,CPX}, $OPTION, $FLT_FMT is

--        This class implements the class of complex numbers which have real
--   components (of FLT class).

--         Version 1.0 Aug 97.   Copyright K Hopper, U of Waikato

--                          Development History
--                          -------------------

--        Date           Who By         Detail
--        ----           ------         ------

--        8 Aug 97        kh       Original from Sather 1.1 Dist
--       18 Mar 01        djw      Fixed bug in log, and added
--                                 post condition to log.
   
include CPX{FLT,ANGLE} ;


create(
      real,
      imaginary : FLT
      ) : SAME is

--        This routine creates a complex number with a real part `re' and
--   imaginary part `im'.

     me : SAME ;
     return me.re(real).im(imaginary)
end ;


log : CPX 

      post self.is_similar(result.exp)
      
      is
      
--        This routine returns the complex logarithm of self.  The chosen
--   branch is
--
--     log |self| + i phase(self).  See Steele p302.

     phase : ANGLE := ANGLE::atan2(im,re) ;
     magnitude : FLT := (re * re + im * im).sqrt.log ;
     return create(magnitude , phase.radians)
end ;


end ; --  CPX
------------------------->  GNU Sather - sourcefile  <-------------------------
-- Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand        --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute  and/or modify it under the terms of the GNU Library General --
-- Public  License (LGPL)  as published  by the  Free  Software  Foundation; --
-- either version 2 of the license, or (at your option) any later version.   --
-- This  library  is distributed  in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       --
-- The license text is also available from:  Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     --
-------------->  Please email comments to <address@hidden>  <--------------

immutable class CPXD < $COMPLEX{FLTD, CPXD}, $FLT_FMT is

--        This class implements the mathematical notion of a 'double length'
--   complex number within the constraints of the parameter type.

--   Some of the algorithms are taken from:
--   Press, Flannery, Teukolsky, and Vettering, "Numerical Recipes in C",
--   second edition, Cambridge University Press, 1993.
--
--   Some of the choices of branch cut were chosen to be consistent with:
--   Guy L. Steele, "Common Lisp, The Language", second edition,
--   1990, Digital Press.

--         Version 1.1 Oct 98.   Copyright K Hopper, U of Waikato

--                          Development History
--                          -------------------

--        Date           Who By         Detail
--        ----           ------         ------

--      25 Aug 97        kh       Original from Sather 1.1 Dist
--       2 Oct 98        kh       Factored out formatting - see CPX{?}
--      18 Mar 01        djw      Fixed bugs as for CPX
   
include CPX{FLTD,ANGLED} ;


create(
      real,
      imaginary : FLTD
      ) : SAME is

--        This routine creates a complex number with a real part `re' and
--   imaginary part `im'.

     me : SAME ;
     return me.re(real).im(imaginary)
end ;


log : CPXD 

      post self.is_similar(result.exp)
      
        is
      
--        This routine returns the complex logarithm of self.  The chosen
--   branch is
--
--     log |self| + i phase(self).  See Steele p302.

     phase : ANGLED := ANGLED::atan2(im,re) ;
     magnitude : FLTD := (re * re + im * im).sqrt.log ;
     return create(magnitude , phase.radians)
end ;

end ; -- CPXD
class TEST_CPX is

--        This class carries out tests on the complex number class CPX.  Note
--   that it is not portable for simplicity in building a local library.

--         Version 1.0 Nov 97.   Copyright K Hopper, U of Waikato

--                          Development History
--                          -------------------

--        Date           Who By         Detail
--        ----           ------         ------

--       21 Nov 97        kh       Original from Sather distribution
--       3  Apr 00        djw      Extended to cover more cases

include TEST ;
    
main is
     class_name("CPX") ;
     
      first : CPX := CPX::create(3.0,4.0) ;
      second : CPX := CPX::create(5.0,7.0) ;
      third : CPX := CPX::create(8.0,11.0) ;
      sum : CPX := first + second ;
      test("plus",sum.str,third.str) ;
      
      fourth : CPX := CPX::create(-2.0,-3.0) ;
      diff : CPX := first - second ;
      test("minus",diff.str,fourth.str) ;
      
      neg : CPX := first + (second.negate) ;
      test("negate",neg.str,fourth.str) ;
      
      prod : CPX := first * second ;
      fifth : CPX := CPX::create(-13.0,41.0) ;
      test("times",prod.str,fifth.str) ;
      
      div : CPX := second / first ;
      sixth : CPX := CPX::create(1.72,0.04) ;
      test("divide",div.str,sixth.str) ;
      
      recip : CPX := second * (first.reciprocal) ;
      test("reciprocal",recip.str,sixth.str) ;
      
      dfirst : CPX := CPX::create(4.0,5.0) ;
      dsecond : CPX := dfirst.conjugate ;
      test("conjugate",dsecond.str,CPX::create(4.0,-5.0).str) ;
      
      dpow : CPX := dfirst.pow(CPX::create(2.0)) ;
      dthird : CPX := CPX::create(-9.0,40.0) ;
      test("pow",dpow.re.str(8)+" "+dpow.im.str(6),
           dthird.re.str(8)+" "+dthird.im.str(6)) ;     
      
      dsqr : CPX := dfirst.square ;
      test("square",dsqr.str,dthird.str) ; 
      
      dsqrt : CPX := dsqr.sqrt ;
      test("sqrt",dsqrt.re.str(8)+" "+dsqrt.im.str(8),
             dfirst.re.str(8)+" "+dfirst.im.str(8)) ;
      
      dmag : FLT := dfirst.magnitude_squared ;
      test("magnitude_squared",dmag.str,41.0.str) ;
      
      dmagg : FLT := dfirst.magnitude ;
      test("magnitude",dmagg.str(8),41.0.sqrt.str(8)) ;
      
      
      finish
   end ;
   
end ; --  TEST_CPX
class TEST_CPXD is

--        This class carries out tests on the complex number class CPXD.  Note
--   that it is not portable for simplicity in building a local library.

--         Version 1.0 Nov 97.   Copyright K Hopper, U of Waikato

--                          Development History
--                          -------------------

--        Date           Who By         Detail
--        ----           ------         ------

--       21 Nov 97        kh       Original from Sather distribution
--       3  Apr 00        djw      Extended to cover more cases

include TEST ;
    
main is
     class_name("CPXD") ;
     
      first : CPXD := CPXD::create(3.0d,4.0d) ;
      second : CPXD := CPXD::create(5.0d,7.0d) ;
      third : CPXD := CPXD::create(8.0d,11.0d) ;
      sum : CPXD := first + second ;
      test("plus",sum.str,third.str) ;
      
      fourth : CPXD := CPXD::create(-2.0d,-3.0d) ;
      diff : CPXD := first - second ;
      test("minus",diff.str,fourth.str) ;
      
      neg : CPXD := first + (second.negate) ;
      test("negate",neg.str,fourth.str) ;
      
      prod : CPXD := first * second ;
      fifth : CPXD := CPXD::create(-13.0d,41.0d) ;
      test("times",prod.str,fifth.str) ;
      
      div : CPXD := second / first ;
      sixth : CPXD := CPXD::create(1.72d,0.04d) ;
      test("divide",div.re.str(8)+" "+div.im.str(8),
           sixth.re.str(8)+" "+sixth.im.str(8)) ;
      
      recip : CPXD := second * (first.reciprocal) ;
      test("reciprocal",recip.re.str(8)+" "+recip.im.str(8),
           sixth.re.str(8)+" "+sixth.im.str(8)) ;
      
      dfirst : CPXD := CPXD::create(4.0d,5.0d) ;
      dsecond : CPXD := dfirst.conjugate ;
      test("conjugate",dsecond.str,CPXD::create(4.0d,-5.0d).str) ;
      
      dpow : CPXD := dfirst.pow(CPXD::create(2.0d)) ;
      dthird : CPXD := CPXD::create(-9.0d,40.0d) ;
      test("pow",dpow.re.str(8)+" "+dpow.im.str(8),
           dthird.re.str(8)+" "+dthird.im.str(8)) ;     
      
      dsqr : CPXD := dfirst.square ;
      test("square",dsqr.str,dthird.str) ; 
      
      dsqrt : CPXD := dsqr.sqrt ;
      test("sqrt",dsqrt.re.str(8)+" "+dsqrt.im.str(8),
             dfirst.re.str(8)+" "+dfirst.im.str(8)) ;
      
      dmag : FLTD := dfirst.magnitude_squared ;
      test("magnitude_squared",dmag.str,41.0d.str) ;
      
      dmagg : FLTD := dfirst.magnitude ;
      test("magnitude",dmagg.str(8),41.0d.sqrt.str(8)) ;
      
      
      finish
   end ;
   
end ; --  TEST_CPXD



reply via email to

[Prev in Thread] Current Thread [Next in Thread]