"======================================================================
|
|   Number Method Definitions
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Steve Byrne.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class 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 the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================"


Magnitude subclass: #Number
	  instanceVariableNames: ''
	  classVariableNames: ''
	  poolDictionaries: ''
	  category: 'Language-Data types'
!

Number comment: 
'I am an abstract class that provides operations on numbers, both floating
point and integer.  I provide some generic predicates, and supply the 
implicit type coercing code for binary operations.' !


!Number class methodsFor: 'converting'!

coerce: aNumber
    "Answer aNumber - whatever class it belongs to, it is good"
    ^aNumber
!

readFrom: aStream
    "Answer the number read from the rest of aStream, converted to an
     instance of the receiver. If the receiver is number, the class of the
     result is undefined -- but the result is good."
    | value ch scale |
    value := 0.
    scale := (aStream peekFor: $-) ifTrue: [ -1 ] ifFalse: [ 1 ].

    [ aStream atEnd ifTrue: [^self coerce: value].
      (ch := aStream next) isDigit ] whileTrue: [
	 value := ch digitValue * scale + (value * 10).
    ].
    ch = $. ifFalse: [aStream skip: -1. ^value].

    [ aStream atEnd ifTrue: [^self coerce: value].
      (ch := aStream next) isDigit ] whileTrue: [
	 scale := scale / 10.
	 value := ch digitValue * scale + value.
    ].

    aStream skip: -1.
    ^self coerce: value
! !


!Number class methodsFor: 'testing'!

isImmediate
    "Answer whether, if x is an instance of the receiver, x copy == x"
    ^true
! !


!Number methodsFor: 'copying'!

shallowCopy
    "Return the receiver - it's an immediate (immutable) object"
    ^self
!

deepCopy
    "Return the receiver - it's an immediate (immutable) object"
    ^self
! !


!Number methodsFor: 'converting'!

degreesToRadians
    "Convert the receiver to radians"
    ^self asFloat / 57.295779513082320876846364344191
!

radiansToDegrees
    "Convert the receiver from radians to degrees"
    ^self asFloat * 57.295779513082320876846364344191
!

coerce: aNumber
    "Answer aNumber - whatever class it belongs to, it is good"
    ^aNumber
!

zero
    "Coerce 0 to the receiver's class. The default implementation works,
     but is inefficient"
    ^self coerce: 0
!

unity
    "Coerce 1 to the receiver's class. The default implementation works,
     but is inefficient"
    ^self coerce: 1
!

generality
    "Answer the receiver's generality"
    self subclassResponsibility
!

retry: aSymbol coercing: aNumber
    "Coerce the number (between the receiver and aNumber) which has the lowest
     generality to the other's number class, and retry calling aSymbol."

    | selfGen aNumGen |
    (aSymbol == #= or: [ aSymbol == #~= ])
    	ifTrue: [ (aNumber isKindOf: Number) 
		      "Return false for =, true for ~="
		      ifFalse: [ ^aSymbol ~~ #= ] ].
    selfGen := self generality.
    aNumGen := aNumber generality.
    selfGen > aNumGen
    	ifTrue: [ ^self perform: aSymbol with: (self coerce: aNumber) ].
    selfGen < aNumGen
    	ifTrue: [ ^(aNumber coerce: self) perform: aSymbol with: aNumber ].

    ^self error: 'retry:coercing: called with arguments of the same generality' 
!

asFloat
    self subclassResponsibility
! !



!Number methodsFor: 'arithmetic'!

+ aNumber
    "Sum the receiver and aNumber, answer the result"
    self subclassResponsibility
!

- aNumber
    "Subtract aNumber from the receiver, answer the result"
    self subclassResponsibility
!

* aNumber
    "Subtract the receiver and aNumber, answer the result"
    self subclassResponsibility
!

/ aNumber
    "Divide the receiver by aNumber, answer the result (no loss of
     precision)"
    self subclassResponsibility
!

// aNumber
    "Divide the receiver by aNumber, answer the result truncated towards
     -infinity"
    self subclassResponsibility
!

\\ aNumber
    "Divide the receiver by aNumber truncating towards -infinity, answer
     the remainder"
    self subclassResponsibility
!

quo: aNumber
    "Return the integer quotient of dividing the receiver by aNumber with
    truncation towards zero."
    ^(self / aNumber) truncated
!

rem: aNumber
    "Return the remainder of dividing the receiver by aNumber with
    truncation towards zero."
    ^self - ((self quo: aNumber) * aNumber)
! !



!Number methodsFor: 'truncation and round off'!

asInteger
    "Answer the receiver, truncated towards zero"
    ^self truncated
!

truncated
    "Answer the receiver, truncated towards zero"
    ^self subclassResponsibility
!

truncateTo: aNumber
    "Answer the receiver, truncated towards zero to a multiple
     of aNumber"
    ^(self / aNumber) truncated * aNumber
!

rounded
    "Returns the integer nearest the receiver"
    ^(self + 0.5) floor
!

roundTo: aNumber
    "Answer the receiver, truncated to the nearest multiple
     of aNumber"
    ^(self / aNumber) rounded * aNumber
! !



!Number methodsFor: 'testing'!

isRational
    "Answer whether the receiver is rational - false by default"
    ^false
!

isNumber
    ^true
!

negative
    "Answer whether the receiver is < 0"
    ^self < self zero
!

positive
    "Answer whether the receiver is >= 0"
    ^self >= self zero
!

strictlyPositive
    "Answer whether the receiver is > 0"
    ^self > self zero
!

sign
    "Returns the sign of the receiver."
    self < self zero ifTrue: [ ^-1 ].
    self > self zero ifTrue: [ ^1 ].
    ^0
!

even
    "Returns true if self is divisible by 2"
    ^self truncated even
!

odd
    "Returns true if self is not divisible by 2"
    ^self truncated odd
! !



!Number methodsFor: 'misc math'!

squared
    "Answer the square of the receiver"
    ^self * self
!

abs
    "Answer the absolute value of the receiver"
    ^self > self zero
	ifTrue: [ self ]
	ifFalse: [ self negated ]
!

negated
    "Answer the negated of the receiver"
    ^self zero - self
!

sin
    "return the sine of the receiver"
    ^self asFloat sin
!

cos
    "return the cosine of the receiver"
    ^self asFloat cos
!

tan
    "return the tangent of the receiver"
    ^self asFloat tan
!

arcSin
    "return the arc sine of the receiver"
    ^self asFloat arcSin
!

arcCos
    "return the arc cosine of the receiver"
    ^self asFloat arcCos
!

arcTan
    "return the arc tangent of the receiver"
    ^self asFloat arcTan
!

sqrt
    "return the square root of the receiver"
    ^self asFloat sqrt
!

exp
    "return e raised to the receiver"
    ^self asFloat exp
!

ln
    "return log base e of the receiver"
    ^self asFloat ln
!

estimatedLog
    "Answer an estimate of (self abs floorLog: 10). This method
     should be overridden by subclasses, but Number's implementation
     does not raise errors - simply, it gives a correct result, so
     it is slow."
    ^self abs floorLog: 10
!

log
    "return log base aNumber of the receiver"
    ^self asFloat ln / Float ln10
!

log: aNumber
    "return log base aNumber of the receiver"
    ^self asFloat ln / aNumber asFloat ln
!

floorLog: radix
    "return (self log: radix) floor. Optimized to answer an integer."

    | me answer |

    self < self zero ifTrue: [
       ^self error: 'cannot extract logarithm of a negative number'
    ].
    radix <= radix unity ifTrue: [
	(radix <= radix zero) ifTrue: [ ^self error: 'bad radix' ].
	(radix = radix unity) ifTrue: [ ^self error: 'bad radix' ].
	^(self floorLog: radix reciprocal) negated
    ].

    me := self.
    self < self unity
	ifTrue: [
	    answer := -1.
	    [   me := me * radix. me < me unity ] whileTrue: [
		answer := answer - 1
	    ]
	]
	ifFalse: [
	    answer := 0.
	    [   me > radix ] whileTrue: [
		me := me / radix.
		answer := answer + 1
	    ]
	].

   ^answer
!

raisedTo: aNumber
    "Return self raised to aNumber power"

    ^aNumber isInteger
	ifTrue: [ self raisedToInteger: aNumber ]
	ifFalse: [ self asFloat raisedTo: aNumber asFloat ]
!

raisedToInteger: anInteger
    "Return self raised to the anInteger-th power"
    | result y n |

    "Some special cases first"
    anInteger isInteger ifFalse: [ ^self error: 'bad argument type' ].
    anInteger < 0 ifTrue: [ ^self reciprocal raisedToInteger: 0 - anInteger ].
    anInteger = 0 ifTrue: [ ^1 ].
    anInteger = 1 ifTrue: [ ^self ].

    result := self unity.
    y := self.
    n := anInteger.
    [   (n bitAnd: 1) == 1 ifTrue: [ result := result * y ].
	n := n bitShift: -1. n > 0
    ]   whileTrue: [ y := y * y ].

    ^result
! !



!Number methodsFor: 'truncation and round off'!

floor
    "Return the integer nearest the receiver toward negative infinity."

    | selfTruncated |
    selfTruncated := self truncated.
    "If positive, truncation to zero is what we want."
    self >= self zero ifTrue: [^selfTruncated].

    "Must be negative."
    self = (self coerce: selfTruncated)
	ifTrue: [^selfTruncated]
	ifFalse: [^selfTruncated - 1]
! !


!Number methodsFor: 'arithmetic'!

reciprocal
    "Return the reciprocal of the receiver"
    self = self zero
	ifTrue: [self error: 'can not return the reciprocal of zero']
	ifFalse: [^self unity / self]
! !



!Number methodsFor: 'Intervals & iterators'!

to: stop
    "Return an interval going from the receiver to stop by 1"
    ^Interval from: self to: stop
!

to: stop by: step
    "Return an interval going from the receiver to stop with the given step"
    ^Interval from: self to: stop by: step
!

to: stop by: step do: aBlock
    "Evaluate aBlock for each value in the interval going from the receiver
     to stop with the given step. Compiled in-line for integer literal steps,
     and for one-argument aBlocks without temporaries, and therefore
     not overridable."
    | i |
    i := self.
    step > step zero
	ifTrue: [
    	    [ i <= stop ]
	    	whileTrue: [ aBlock value: i.
		    	     i := i + step ]
    	]
	ifFalse: [
    	    [ i >= stop ]
	    	whileTrue: [ aBlock value: i.
		    	     i := i + step ]
	].
    ^stop
!

to: stop do: aBlock
    "Evaluate aBlock for each value in the interval going from the receiver
     to stop by 1. Compiled in-line for one-argument aBlocks without
     temporaries, and therefore not overridable."
    | i |
    i := self.
    [ i <= stop ]
    	whileTrue: [ aBlock value: i.
	    	     i := i + self unity ]
! !
