Complex.st
author Claus Gittinger <cg@exept.de>
Thu, 09 Jul 1998 10:12:39 +0200
changeset 3633 89f1f31527f4
parent 1944 9fb0b642d2ca
child 3637 2b4d733c9a01
permissions -rw-r--r--
avoid a send if the result of an arithmetic op is a real (i.e. imaginary is 0).

ArithmeticValue subclass:#Complex
	instanceVariableNames:'real imaginary'
	classVariableNames:''
	poolDictionaries:''
	category:'Magnitude-Numbers'
!

Complex comment:'This is an implementation of complex numbers.  A complex number has real and
 imaginary parts which must be manipulated simultaneously in any numeric processing.
  Complex numbers can be used in many of the same places that regular numbers
 can be used with one major exception of comparisons, since complex numbers cannot
 be directly compared for size (except through lengths of vectors (see absolute
 value)).

Instance variables:
    real        <Number> the part of the number which can be expressed as a Real number
    imaginary   <Number> the part of the number which, in terms of how the number behaves,
			 has been multiplied by ''i'' (-1 sqrt)

Author: Kurt Hebel (hebel@uinova.cerl.uiuc.edu)'!

!Complex class methodsFor:'instance creation'!

fromReal: aNumber
	"Create a new complex number from the given real number."
	^ self basicNew setReal: aNumber setImaginary: 0
!

real: u imaginary: v
	"Create a new complex number with the given real and imaginary parts.  If the
	 imaginary part is zero, return the real part of the number."
	^v = 0 ifTrue: [u]
	       ifFalse: [self basicNew setReal: u setImaginary: v]
! !

!Complex class methodsFor:'constants access'!

unity
	"Answer the value which allows, for any given arithmetic value, the following to be true

	 aNumber * aNumber class unity = aNumber

	This must be true regardless of how a given subclass chooses to define #*"

	^self fromReal: 1
!

zero
	"Answer the value which allows, for any given arithmetic value, the following to be true

		aNumber + aNumber class zero = aNumber

	This must be true regardless of how a given subclass chooses to define #+"

	^self fromReal: 0
! !

!Complex class methodsFor:'exception handling'!

trapImaginary: aBlock
	"Complex trapImaginary: [-27 sqrt]"

	| send |

	^Number domainErrorSignal handle: [ :ex |
	    send := ex parameter.
	    (send selector = #sqrt or: [send selector = #sqrtTruncated]) ifTrue: [
		send receiver: send receiver asComplex.
		ex proceedWith: send value
	    ] ifFalse: [
		ex reject
	    ]
	] do: aBlock
! !

!Complex methodsFor:'accessing'!

imaginary
	"Return the imaginary part of the complex number."
	^ imaginary
!

real
	"Return the real part of the complex number."
	^ real
! !

!Complex methodsFor:'arithmetic'!

* aNumber 
        "Return the product of the receiver and the argument."

        | u v r i |

        aNumber isComplex ifTrue:[
            u := aNumber real.
            v := aNumber imaginary.
            r := (real * u) - (imaginary * v).
            i  := (real * v) + (imaginary * u).
            i = 0 ifTrue:[ ^ r ].
            ^ Complex real:r imaginary:i
        ].
        ^ self retry: #* coercing: aNumber

    "Modified: / 8.7.1998 / 12:12:37 / cg"
!

+ aNumber 
        "Return the sum of the receiver and the argument."

        | r i |

        aNumber isComplex ifTrue: [
            r := aNumber real + real.
            i := aNumber imaginary + imaginary.
            i = 0 ifTrue:[ ^ r ].
            ^ Complex real:r imaginary:i
        ].
        ^ self retry: #+ coercing: aNumber

    "Modified: / 8.7.1998 / 12:15:42 / cg"
!

- aNumber
        "Return the difference of the receiver and the argument."

        | r i |

        aNumber isComplex ifTrue: [
            r := real - aNumber real.
            i := imaginary - aNumber imaginary.
            i = 0 ifTrue:[ ^ r ].
            ^ Complex real:r imaginary:i.
        ].
        ^ self retry: #- coercing: aNumber

    "Modified: / 8.7.1998 / 12:15:38 / cg"
!

/ aNumber 
        "Return the quotient of the receiver and the argument."

        | denom u v r i |

        aNumber isComplex ifTrue:[ 
            u := aNumber real.
            v := aNumber imaginary.
            denom := u * u + (v * v).
            r := u * real + (v * imaginary) / denom.
            i := u * imaginary - (v * real) / denom.
            i = 0 ifTrue:[ ^ r ].
            ^ Complex real:r imaginary:i
        ].
        ^ self retry: #/ coercing: aNumber

    "Modified: / 8.7.1998 / 12:15:34 / cg"
!

abs
	"Return the magnitude (or absolute value) of the complex number."

	^ (real * real + (imaginary * imaginary)) sqrt
!

conjugated
	"Return the complex conjugate of this complex number."

	^ Complex real: real imaginary: imaginary negated
! !

!Complex methodsFor:'coercing'!

coerce: aNumber

	^aNumber asComplex
!

generality

	^150
! !

!Complex methodsFor:'comparing'!

< aNumber
	^Number
		raise: #unorderedSignal
		receiver: self
		selector: #<
		arg: aNumber
		errorString: 'Complex numbers are not well ordered'!

= aNumber
	^ (aNumber real = real) and:[aNumber imaginary = imaginary]
!

hash
	"Hash is implemented because equals is implemented."

	^ real hash
! !

!Complex methodsFor:'converting'!

asComplex

	^self
!

asFloat

	imaginary = 0 ifTrue: [^real asFloat].
	^Number
		raise: #coercionErrorSignal
		receiver: self
		selector: #asFloat
		errorString: 'Can''t coerce an instance of Complex to a Float'
!

asInteger

	imaginary = 0 ifTrue: [^real asInteger].
	^Number
		raise: #coercionErrorSignal
		receiver: self
		selector: #asInteger
		errorString: 'Can''t coerce an instance of Complex to an Integer'
!

asPoint
	"Return the complex number as a point."
	^ real @ imaginary
!

reduceGeneralityIfPossible
	"Answer the receiver transformed to a lower generality, if such a 
	transformation is possible without losing information. If not, answer 
	the receiver"

	imaginary isZero
		ifTrue: [^real]
		ifFalse: [^self]
! !

!Complex methodsFor:'double dispatching'!

differenceFromFloat: argument
	^ argument asComplex - self
!

differenceFromFraction: argument
	^ argument asComplex - self
!

differenceFromInteger: argument
	^ argument asComplex - self
!

productFromFloat: argument
	^ argument asComplex * self
!

productFromFraction: argument
	^ argument asComplex * self
!

productFromInteger: argument
	^ argument asComplex * self
!

quotientFromFloat: argument
	^ argument asComplex / self
!

quotientFromFraction: argument
	^ argument asComplex / self
!

quotientFromInteger: argument
	^ argument asComplex / self
!

sumFromFloat: argument
	^ argument asComplex + self
!

sumFromFraction: argument
	^ argument asComplex + self
!

sumFromInteger: argument
	^ argument asComplex + self
! !

!Complex methodsFor:'mathematical functions'!

angle
	"Return the radian angle for this Complex number."

	real < 0 ifTrue: [
	    imaginary < 0 ifTrue: [
		^ (imaginary / real) arcTan - Float pi
	    ].
	    ^ Float pi + (imaginary / real) arcTan
	].
	^ (imaginary / real) arcTan
!

exp
	"Return the complex exponential of the receiver."

	^ imaginary cos % imaginary sin * real exp
!

sqrt
	"Return the square root of the receiver"

	| u v |
	(imaginary = 0 and: [real >= 0]) ifTrue: [^real sqrt].
	v := (self abs - real / 2) sqrt.
	u := imaginary / 2 / v.
	^Complex real: u imaginary: v

	"-4 asComplex sqrt"
	"-4 asComplex sqrt squared"
! !

!Complex methodsFor:'printing'!

printOn: aStream
	aStream nextPut: $(.
	real storeOn: aStream.
	aStream nextPutAll: '%'.
	imaginary storeOn: aStream.
	aStream nextPut: $).
!

printString
	^ '(' , real printString, '%', imaginary printString, ')'
!

storeOn: aStream
	aStream nextPut: $(.
	real storeOn: aStream.
	aStream nextPutAll: '%'.
	imaginary storeOn: aStream.
	aStream nextPut: $).
! !

!Complex methodsFor:'private'!

setReal: u setImaginary: v
	real := u.
	imaginary := v.
! !

!Complex methodsFor:'testing'!

isComplex

	^true
!

isReal
	"Return true if this Complex number has a zero imaginary part."
	^ imaginary = 0
!

isZero
	"Answer whether 'self = self class zero'.  We can't use #= because
	#= is defined in terms of #isZero"

	^real isZero and: [imaginary isZero]
!

sign

	^Complex real: real sign imaginary: imaginary sign
! !