"======================================================================
|
|   Class Fraction 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 David Duke.
| Slightly modified by Steve Byrne and Paolo Bonzini.
|
| 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.  
|
 ======================================================================"


Number subclass: #Fraction
	instanceVariableNames: 'numerator denominator'
	classVariableNames: 'Zero One'
	poolDictionaries: ''
	category: 'Language-Data types'!

Fraction comment:
' I represent rational numbers in the form (p/q) where p and q are integers.
The arithmetic operations  *, +, -, /,  on fractions, all return a reduced 
fraction.'!



!Fraction class methodsFor: 'converting'!

coerce: aNumber
    "Answer aNumber converted to a Fraction"
    ^aNumber asFraction
! !


!Fraction class methodsFor: 'instance creation'!

initialize
    "Initialize the receiver's class variables"
    Zero := self numerator: 0 denominator: 1.
    One := self numerator: 1 denominator: 1.
!

numerator: nInteger denominator: dInteger 
    " Answer a new instance of fraction (nInteger/dInteger)"
 
    ^self new setNumerator: nInteger setDenominator: dInteger 
! !


!Fraction methodsFor: 'accessing'!

denominator
    "Answer the receiver's denominator"
    ^denominator!

numerator
    "Answer the receiver's numerator"
    ^numerator 

! !



!Fraction methodsFor: 'arithmetic'!

* aNumber 
     "Multiply two numbers and answer the result."

     (aNumber generality = self generality)
	 ifTrue: [^(Fraction numerator: numerator * aNumber numerator
			     denominator: denominator * aNumber denominator) reduce]
	 ifFalse: [^self retry: #* coercing: aNumber]
!

+ aNumber 
     "Sum two numbers and answer the result."

     (aNumber generality = self generality)
	 ifTrue: [^(Fraction numerator: (numerator * aNumber denominator)
					+ (aNumber numerator * denominator) 
			     denominator: denominator * aNumber denominator) reduce]
	 ifFalse: [^self retry: #+ coercing: aNumber]
!

- aNumber
     "Subtract aNumber from the receiver and answer the result."

     (aNumber generality = self generality)
	 ifTrue: [^self + (aNumber negated)]
	 ifFalse: [^self retry: #- coercing: aNumber]
!

/ aNumber 
     "Divide the receiver by aNumber and answer the result."

     (aNumber generality = self generality)
	 ifTrue: [^(Fraction numerator: numerator * aNumber denominator
			     denominator: denominator * aNumber numerator) reduce]
	 ifFalse: [^self retry: #/ coercing: aNumber]
!

// aNumber
     "Return the integer quotient of dividing the receiver by aNumber with
      truncation towards negative infinity."
     ^(self / aNumber) floor 
!

\\ aNumber
    "Return the remainder from dividing the receiver by aNumber, (using //)."
    ^(self - (self // aNumber * aNumber))
!

estimatedLog
    "Answer an estimate of (self abs floorLog: 10)"
    ^numerator estimateLog - denominator estimateLog
!

negated
    "Return the negated of the receiver"
    ^Fraction numerator: numerator negated denominator: denominator
!

reciprocal
    "Return the reciprocal of the receiver"
    denominator < 0
	ifTrue: [^Fraction numerator: denominator negated 
			   denominator: numerator negated ]
	ifFalse: [^Fraction numerator: denominator denominator: numerator]

! !



!Fraction methodsFor: 'coercing'!

zero
    "Coerce 0 to the receiver's class"
    ^Zero
!

unity
    "Coerce 1 to the receiver's class"
    ^One
!

coerce: aNumber 
    "Coerce aNumber to the receiver's class"
    ^aNumber asFraction
!

generality
    "Return the receiver's generality"
    ^300
!

truncated
    "Truncate the receiver and return the truncated result"
    ^numerator quo: denominator
! !



!Fraction methodsFor: 'comparing'!

< arg 
     "Test if the receiver is less than arg."

     (arg generality = self generality)
	 ifTrue: [^arg denominator * numerator < (denominator * arg numerator)]
	 ifFalse: [^self retry: #< coercing: arg]
!

<= arg 
     "Test if the receiver is less than or equal to arg."

     (arg generality = self generality)
	 ifTrue: [^arg denominator * numerator <= (denominator * arg numerator)]
	 ifFalse: [^self retry: #<= coercing: arg]
!

> arg 
     "Test if the receiver is more than arg."

     (arg generality = self generality)
	ifTrue: [^arg denominator * numerator > (denominator * arg numerator)]
	ifFalse: [^self retry: #> coercing: arg]
!

>= arg 
     "Test if the receiver is greater than or equal to arg."

     (arg generality = self generality)
	 ifTrue: [^arg denominator * numerator >= (denominator * arg numerator)]
	 ifFalse: [^self retry: #>= coercing: arg]
!

= arg 
     "Test if the receiver equals arg."

     (arg isKindOf: Number)
	 ifFalse: [^false].

     (arg generality = self generality)
	 ifTrue: [^arg denominator * numerator = (denominator * arg numerator)]
	 ifFalse: [^self retry: #= coercing: arg]!

hash
    "Answer an hash value for the receiver"
    ^numerator + denominator + denominator
! !


!Fraction methodsFor: 'testing'!

isRational
    "Answer whether the receiver is rational - true"
    ^true
! !



!Fraction methodsFor: 'converting'!

asFloat
    "Answer the receiver converted to a Float"
    | n d shift sign |
    n := numerator.
    d := denominator.
    sign := n sign * d sign.

    "Avoid answering NaNs and infinite values.
     1e1800 asFloat / (1e1799 + 1) asFloat = NaN, but
     (1e1800 / (1e1799 + 1)) asFloat must be 10."
    shift := (Float largest exponent - numerator highBit).
    shift := shift min: (Float largest exponent - denominator highBit).
    shift < 0 ifTrue: [
	"Lose some more precision, but we MUST avoid infinites and NaNs!"
	shift := shift - 10. n := n bitShift: shift. d := d bitShift: shift ].

    d = 0 ifTrue: [
	^sign > 0 ifTrue: [ Float infinity ] ifFalse: [ Float negativeInfinity ] ].
    n = 0 ifTrue: [
	^sign > 0 ifTrue: [ 0.0 ] ifFalse: [ Float negativeInfinity reciprocal ] ].
    ^n asFloat / d asFloat
!

asFraction
    "Answer the receiver converted to a Fraction"
    ^self 
! !



!Fraction methodsFor: 'printing'!

printOn: aStream
    "Print a representation of the receiver on aStream"
    " Fractions print as (numerator/denominator) eg (3/4) ."

    aStream nextPut: $(; 
	print: numerator;
	nextPut: $/;
	print: denominator;
	nextPut: $) 
!

storeOn: aStream
    "Store Smalltalk code compiling to the receiver on aStream"
    aStream nextPutAll: '(Fraction numerator: ';
	store: numerator;
	nextPutAll: ' denominator: ';
	store: numerator ;
	nextPut: $)
! !



!Fraction methodsFor: 'private'!

reduce
    "Reduce the fraction"
    | gcd |
    numerator = 1 ifTrue: [^self].
    denominator = 1 ifTrue: [^numerator].
    numerator = 0 ifTrue: [^0].
    numerator = denominator ifTrue: [^1].
    gcd := numerator gcd: denominator.
    gcd = 1 ifTrue: [^self].
    denominator = gcd ifTrue: [^numerator quo: gcd].
    numerator := numerator quo: gcd.
    denominator := denominator quo: gcd.
    ^self
!

setNumerator: numInteger setDenominator: denInteger 
    "Set the fraction's numerator and denominator"
    denInteger = 0
	ifTrue: [^self error: 'The denominator can not be zero'].
    denInteger < 0
	ifTrue: [numerator := numInteger negated.
		 denominator := denInteger negated ]
	ifFalse: [numerator := numInteger.
		  denominator := denInteger]
! !


!Fraction methodsFor: 'optimized cases'!

negated
    "Return the receiver, with its sign changed."
    ^Fraction
	numerator: 0 - numerator
	denominator: denominator
!

raisedToInteger: anInteger 
    "Return self raised to the anInteger-th power."
    "No need to reduce"
    ^Fraction
	numerator: (numerator raisedToInteger: anInteger)
	denominator: (denominator raisedToInteger: anInteger)
!

reciprocal
    "Return the reciprocal of the receiver."
    ^Fraction
	numerator: denominator
	denominator: numerator
!

squared
    "Return the square of the receiver."
    ^Fraction
	numerator: numerator squared
	denominator: denominator squared
! !
