ArithVal.st
changeset 1 a27a279701f8
child 3 24d81bf47225
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ArithVal.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,613 @@
+"
+ 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
+! !