ArithVal.st
author claus
Fri, 16 Jul 1993 11:39:45 +0200
changeset 1 a27a279701f8
child 3 24d81bf47225
permissions -rw-r--r--
Initial revision

"
 COPYRIGHT (c) 1993 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Magnitude subclass:#ArithmeticValue
       instanceVariableNames:''
       classVariableNames:'DivisionByZeroSignal DomainErrorSignal
                           OverflowSignal UnderflowSignal
			   AnyArithmeticSignal'
       poolDictionaries:''
       category:'Magnitude-Numbers'
!

ArithmeticValue comment:'

COPYRIGHT (c) 1993 by Claus Gittinger
              All Rights Reserved

ArithmeticValue is an abstract superclass for all things responding to
arithmetic messages. It was inserted into the hierarchy, to allow things
like matrices, functions etc. share the arithmetic methods defined here.

(In the old hierarchy these had to be Numbers to do that 
 - which is not quite correct)

%W% %E%
'!

!ArithmeticValue class methodsFor:'initialization' !

initialize
    "setup the signals"

    DomainErrorSignal := (Signal new) mayProceed:false.
    DomainErrorSignal notifierString:'domain error'.

    DivisionByZeroSignal := (Signal new) mayProceed:false.
    DivisionByZeroSignal notifierString:'division by zero'.

    OverflowSignal := (Signal new) mayProceed:false.
    OverflowSignal notifierString:'overflow'.

    UnderflowSignal := (Signal new) mayProceed:false.
    UnderflowSignal notifierString:'underflow'.

    AnyArithmeticSignal := SignalSet with:DomainErrorSignal
				     with:DivisionByZeroSignal
				     with:OverflowSignal
				     with:UnderflowSignal.
! !

!ArithmeticValue class methodsFor:'signal access' !

domainErrorSignal
    "return the signal which is raised on math errors
     (such as log of 0 etc.)"

    ^ DomainErrorSignal
!

divisionByZeroSignal
    "return the signal which is raised on division by zero"

    ^ DivisionByZeroSignal
!

overflowSignal
    "return the signal which is raised on overflow conditions (in floats)"

    ^ OverflowSignal
!

underflowSignal
    "return the signal which is raised on underflow conditions (in floats)"

    ^ UnderflowSignal
!

anyArithmeticSignal
    "return a signalSet with all possible arithmetic signals"

    ^ AnyArithmeticSignal
! !

!ArithmeticValue methodsFor:'converting' !

degreesToRadians
    "interpreting the receiver as radians, return the degrees"

    ^ self asFloat degreesToRadians
!

radiansToDegrees
    "interpreting the receiver as degrees, return the radians"

    ^ self asFloat radiansToDegrees
!

asInteger
    "return an integer with same value - might truncate"

    ^ self truncated
!

asFloat
    "return a float with same value"

   ^ self subclassResponsibility
!

asFraction
    "return a fraction with same value"

   ^ self subclassResponsibility
!

coerce:aNumber
    "convert aNumber into an instance of the receivers class and return it."

    ^ self subclassResponsibility
!

generality
    "return a number giving the receivers generality, that number is
     used to convert one of the arguments in a mixed expression. 
     The generality has to be defined in subclasses,
     such that gen(a) > gen(b) iff, conversion of b into a's class 
     does not cut precision. For example, Integer has 40, Float has 80,
     meaning that if we convert a Float to an Integer, some precision may
     be lost. The generality is used by ArithmeticValue>>retry:cuercing:"
      
    ^ self subclassResponsibility
!

retry:aSymbol coercing:aNumber
    "arithmetic represented by the binary operator, aSymbol,
    could not be performed with the receiver and the argument, aNumber, 
    because of the differences in representation.  
    Coerce either the receiver or the argument, depending on which has higher 
    generality, and try again.  
    If the operation is compare for same value (=), return false if
    the argument is not a Number. 
    If the generalities are the same, create an error message, since this
    means that a subclass has not been fully implemented."

    |myGenerality otherGenerality|

    (aSymbol == #=) ifTrue:[
        (aNumber respondsTo:#generality) ifFalse:[^ false]
    ] ifFalse:[
        (aNumber respondsTo:#generality) ifFalse:[
            self error:'retry:coercing: argument is not a number'.
            ^ self
        ]
    ].
    myGenerality := self generality.
    otherGenerality := aNumber generality.
    (myGenerality > otherGenerality) ifTrue:[
        ^ self perform:aSymbol with:(self coerce:aNumber)
    ].
    (myGenerality < otherGenerality) ifTrue:[
        ^ (aNumber coerce:self) perform:aSymbol with:aNumber
    ].
    self error:'retry:coercing: oops - same generality'
! !

!ArithmeticValue methodsFor:'queries' !

respondsToArithmetic
    "return true, if the receiver responds to arithmetic messages"

    ^ true
! !

!ArithmeticValue methodsFor:'arithmetic' !

+ something
    "return the sum of the receiver and the argument"

    ^ self subclassResponsibility
!

- something
    "return the difference of the receiver and the argument"

    ^ self subclassResponsibility
!

* something
    "return the product of the receiver and the argument"

    ^ self subclassResponsibility
!

/ something
    "return the quotient of the receiver and the argument"

    ^ self subclassResponsibility
!

// something
    "return the integer quotient of the receiver and the argument"

    ^ (self / something) floor
!

\\ something
    "return the integer modulu of the receiver and the argument"

    ^ self - ((self // something) * something)
!

quo:something
    "Return the integer quotient of dividing the receiver by the argument
     with truncation towards zero."

    ^ (self / something) truncated
!

rem:something
    "Return the integer remainder of dividing the receiver by the argument
     with truncation towards zero.
     The remainder has the same sign as the receiver."

    ^ self - ((self quo:something) * something)
!

abs
    "return the absolute value of the receiver"

    (self negative) ifTrue:[^ self negated].
    ^ self
!

negated
    "return the receiver negated"

    ^ self class zero - self
!

reciprocal
    "return the receivers reciprocal"

    ^ self class unity / self
! !

!ArithmeticValue methodsFor:'comparing'!

>= something
    "return true, if the argument is less or equal than the receiver"

    ^ (self < something) not
!

> something
    "return true, if the argument is less than the receiver"

    ^ something < self
!

<= something
    "return true, if the argument is greater or equal than the receiver"

    ^ (something < self) not
!

< something
    "return true, if the argument is greater than the receiver"

    ^ self subclassResponsibility
!

compare:arg ifLess:lessBlock ifEqual:equalBlock ifGreater:greaterBlock
    "three-way compare - thanks to Self for this idea.
     Can be redefined in subclasses to do it with a single comparison if
     comparison is expensive."

    self < arg ifTrue:[
        ^ lessBlock value
    ].
    self = arg ifTrue:[
        ^ equalBlock value
    ].
    ^ greaterBlock value
! !

!ArithmeticValue methodsFor:'truncation and rounding'!

ceiling
    "return the integer nearest the receiver towards positive infinity."

    |anInteger|

    anInteger := self // 1.       "truncates towards negative infinity"
    anInteger = self ifTrue:[^ anInteger].
    ^ anInteger + 1
!

floor
    "return the receiver truncated towards negative infinity"

    ^ self // 1
!

truncated
    "return the receiver truncated towards zero"

    ^ self floor asInteger
!

truncateTo:aNumber
    "return the receiver truncated to multiples of aNumber"

    ^ ((self / aNumber) floor * aNumber) asInteger
!

rounded
    "return the integer nearest the receiver"

    ^ (self + 0.5) floor asInteger
!

roundTo:aNumber
    "return the receiver rounded to multiples of aNumber"

    ^ (self / aNumber) rounded * aNumber
! !

!ArithmeticValue methodsFor:'double dispatching'!

sumFromInteger:anInteger
    "the receiver does not know how to add an integer -
     retry the operation by coercing to higher generality"

    ^ anInteger retry:#+ coercing:self
!

sumFromFloat:aFloat
    "the receiver does not know how to add a float -
     retry the operation by coercing to higher generality"

    ^ aFloat retry:#+ coercing:self
!

sumFromFraction:aFraction
    "the receiver does not know how to add a fraction -
     retry the operation by coercing to higher generality"

    ^ aFraction retry:#+ coercing:self
!

differenceFromInteger:anInteger
    "the receiver does not know how to subtract from an integer -
     retry the operation by coercing to higher generality"

    ^ anInteger retry:#- coercing:self
!

differenceFromFloat:aFloat
    "the receiver does not know how to subtract from a float -
     retry the operation by coercing to higher generality"

    ^ aFloat retry:#- coercing:self
!

differenceFromFraction:aFraction
    "the receiver does not know how to subtract from a fraction -
     retry the operation by coercing to higher generality"

    ^ aFraction retry:#- coercing:self
!

productFromInteger:anInteger
    "the receiver does not know how to multiply an integer -
     retry the operation by coercing to higher generality"

    ^ anInteger retry:#* coercing:self
!

productFromFloat:aFloat
    "the receiver does not know how to multiply a float -
     retry the operation by coercing to higher generality"

    ^ aFloat retry:#* coercing:self
!

productFromFraction:aFraction
    "the receiver does not know how to multiply a fraction -
     retry the operation by coercing to higher generality"

    ^ aFraction retry:#* coercing:self
!

quotientFromInteger:anInteger
    "the receiver does not know how to divide an integer -
     retry the operation by coercing to higher generality"

    ^ anInteger retry:#/ coercing:self
!

quotientFromFloat:aFloat
    "the receiver does not know how to divide a float -
     retry the operation by coercing to higher generality"

    ^ aFloat retry:#/ coercing:self
!

quotientFromFraction:aFraction
    "the receiver does not know how to divide a fraction -
     retry the operation by coercing to higher generality"

    ^ aFraction retry:#/ coercing:self
!

lessFromInteger:anInteger
    "the receiver does not know how to compare to an integer -
     retry the operation by coercing to higher generality"

    ^ anInteger retry:#< coercing:self
!

lessFromFloat:aFloat
    "the receiver does not know how to compare to a float -
     retry the operation by coercing to higher generality"

    ^ aFloat retry:#< coercing:self
!

lessFromFraction:aFraction
    "the receiver does not know how to compare to a fraction -
     retry the operation by coercing to higher generality"

    ^ aFraction retry:#< coercing:self
! !

!ArithmeticValue methodsFor:'misc math'!

squared
    "return receiver * receiver"

    ^ self * self
!

exp
    "return e ^ receiver"

    ^ self asFloat exp
!

ln
    "return the natural logarithm of the receiver"

    ^ self asFloat ln
!

log
    "return log base 10 of the receiver"

    ^ self log:10
!

log:aNumber
    "return log base aNumber of the receiver"

    ^ self ln / aNumber ln
!

sqrt
    "return the square root of the receiver"

    ^ self asFloat sqrt
!

floorLog:radix
    "return the logarithm truncated as an integer"

    ^ (self log:radix) floor
!

raisedTo:aNumber
    "return the receiver raised to aNumber"

    aNumber = 0 ifTrue:[^ 1].
    aNumber = 1 ifTrue:[^ self].
    aNumber isInteger ifTrue:[
        ^ self raisedToInteger:aNumber
    ].
    ^ self asFloat raisedTo:aNumber
!

raisedToInteger:anInteger
    "return the receiver raised to anInteger"

    |count result|

    result := self coerce:1.
    count := anInteger abs.
    count timesRepeat:[result := result * self].
    (anInteger < 0) ifTrue:[
        ^ 1 / result
    ].
    ^ result
! !

!ArithmeticValue methodsFor:'trigonometric'!

sin
    "return the sine of the receiver (interpreted as radians)"

    ^ self asFloat sin
!

cos
    "return the cosine of the receiver (interpreted as radians)"

    ^ self asFloat cos
!

tan
    "return the tangens of the receiver (interpreted as radians)"

    ^ self asFloat tan
!

arcCos
    "return the arccosine of the receiver (in radians)"

    ^ self asFloat arcCos
!

arcSin
    "return the arcsine of the receiver (in radians)"

    ^ self asFloat arcSin
!

arcTan
    "return the arctangens of the receiver (in radians)"

    ^ self asFloat arcTan
! !

!ArithmeticValue methodsFor:'error handling'!

divideByZeroError
    "report a division by zero error"

    DivisionByZeroSignal raise
    "self error:'division by zero'"
! !

!ArithmeticValue methodsFor:'testing'!

negative
    "return true, if the receiver is < 0"

    " this would lead to infinite recursion ...
    ^ (self < 0)
    "
    ^ self subclassResponsibility
!

positive
    "return true, if the receiver is >= 0"

    ^ self negative not
!

strictlyPositive
    "return true, if the receiver is > 0"

    ^ (self > 0)
!

sign
    "return the sign of the receiver"

    (self < 0) ifTrue:[^ -1].
    (self > 0) ifTrue:[^ 1].
    ^ 0
!

even
    "return true if the receiver is divisible by 2"

    ^ self truncated asInteger even
!

odd
    "return true if the receiver is not divisible by 2"

    ^ self even not
!

denominator
    "return the denominator of the receiver"

    ^ 1
!

numerator
    "return the numerator of the receiver."

    ^ self
! !