Fraction.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 15 Jun 2009 20:55:05 +0100
branchjv
changeset 17711 39faaaf888b4
parent 11734 7c2cc71aabc9
child 17732 a1892eeca6c0
permissions -rw-r--r--
Added branch jv

"
 COPYRIGHT (c) 1989 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.
"
"{ Package: 'stx:libbasic' }"

Number subclass:#Fraction
	instanceVariableNames:'numerator denominator'
	classVariableNames:'FractionOne FractionZero PrintWholeNumbers'
	poolDictionaries:''
	category:'Magnitude-Numbers'
!

!Fraction class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1989 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.
"
!

documentation
"
    Instances of Fraction represent fractional numbers consisting of
    a numerator and denominator. Both are themselfes arbitrary precision
    integers. 
    Fractions are usually created by dividing Integers using / (for exact division).
    Notice, that all operations on fractions reduce their result; this means, that
    the result of a fraction-operation may return an integer.
    Aka:
        (1 / 7) * 7   ->  1  (not 0.99999999...)

    Mixed mode arithmetic:
        fraction op fraction    -> fraction/integer
        fraction op fix         -> fix; scale is fix's scale
        fraction op integer     -> fraction/integer
        fraction op float       -> float


    [classVariables:]
        PrintWholeNumbers       Booolean        experimental: 
                                                controls how fractions which are greater than 1 are printed.
                                                if true, print them as a sum of an integral and the fractional part. 
                                                (Large ones are easier to read this way)
                                                     (17/3) printString  -> '(5+(2/3))'  
                                                for now, the default is false, for backward compatibility

    [author:]
        Claus Gittinger

    [see also:]
        Number
        FixedPoint Float ShortFloat LongFloat Integer Complex
"
! !

!Fraction class methodsFor:'instance creation'!

new
    "create and return a new fraction with value 0"

    ^ self numerator:0 denominator:1
!

numerator:num denominator:den
    "create and return a new fraction with numerator num and denominator den.
     Notice: stc inlines this message if sent to the global named Fraction."

    |newFraction|

%{  /* NOCONTEXT */

    /* this check allows subclassing .. */
    if (self == Fraction) {
        if (__bothSmallInteger(num, den)) {
            if (den != __mkSmallInteger(0)) {
                if (__CanDoQuickAlignedNew(sizeof(struct __Fraction))) {    /* OBJECT ALLOCATION */
                    OBJ newFraction;
                    int spc;
                    INT iDen;

                    __qCheckedAlignedNew(newFraction, sizeof(struct __Fraction));
                    __InstPtr(newFraction)->o_class = self;
                    __qSTORE(newFraction, self);
                    iDen = __intVal(den);
                    if (iDen != 0) {
                        if (iDen < 0) {
                            __FractionInstPtr(newFraction)->f_numerator = __mkSmallInteger(- __intVal(num));
                            __FractionInstPtr(newFraction)->f_denominator = __mkSmallInteger(- iDen);
                        } else {
                            __FractionInstPtr(newFraction)->f_numerator = num;
                            __FractionInstPtr(newFraction)->f_denominator = den;
                        }
                        if (num == __mkSmallInteger(1)) {
                            /* no need to reduce */
                            RETURN ( newFraction );
                        }
                    }
                }
            }
        }
    }
%}.
    den = 0 ifTrue:[
        ^ ZeroDivide raiseRequestWith:thisContext.
    ].
    newFraction isNil ifTrue:[
        newFraction := self basicNew setNumerator:num denominator:den.
    ].
    ^ newFraction reduced

    "
     Fraction numerator:1 denominator:3
     Fraction numerator:2 denominator:6

     Fraction numerator:1 denominator:0
     Fraction numerator:2 denominator:0
    "
! !

!Fraction class methodsFor:'class initialization'!

initialize
    FractionZero isNil ifTrue:[
        FractionZero := self numerator:0 denominator:1.
        FractionOne := self numerator:1 denominator:1
    ]
! !

!Fraction class methodsFor:'constants'!

pi
    "return an approximation of the constant pi as Fraction.
     The approx. returned here has an error smaller than representable by float instances"

    ^ self 
        numerator:314159265358979323846264343
        denominator:100000000000000000000000000

"/    ^ self 
"/        numerator:  314159265358979323846264338327950288419716939937510582097494459
"/        denominator:100000000000000000000000000000000000000000000000000000000000000

    "
     Fraction pi         
     Fraction pi asFloat
     Float pi            
    "

    "Modified: 5.11.1996 / 11:11:44 / cg"
!

pi_approximation
    "return an approximation of the constant pi as Fraction.
     The approx. returned is good for 6 valid digits and has an error of less than -2.67-07.
     The value might be useful to avoid floating point numbers in graphic rendering code,
     where 6 digits of precision are usually good enough."

    ^ self 
        numerator:355
        denominator:113

    "
     Fraction pi         
     Fraction pi asFloat
     Float pi - Fraction pi_approximation asFloat            
    "
!

unity
    "return the neutral element for multiplication (1 / 1)"

    ^ FractionOne

    "Modified: 18.7.1996 / 12:26:06 / cg"
!

zero
    "return the neutral element for addition (0 / 1)"

    ^ FractionZero

    "Modified: 18.7.1996 / 12:26:12 / cg"
! !

!Fraction class methodsFor:'queries'!

isBuiltInClass
    "return true if this class is known by the run-time-system.
     Here, true is returned for myself, false for subclasses."

    ^ self == Fraction

    "Modified: 23.4.1996 / 15:59:10 / cg"
! !

!Fraction methodsFor:'accessing'!

denominator
    "return the denominator"

    ^ denominator
!

numerator
    "return the numerator"

    ^ numerator
! !

!Fraction methodsFor:'arithmetic'!

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

    "/ notice:
    "/ the following code handles some common cases,
    "/ and exists as an optimization, to speed up those cases.
    "/ also notice, that checks for those cases must be inlinable without
    "/ a message send; otherwise double-dispatch is just as fast.
    "/
    "/ Conceptionally, (and for most other argument types),
    "/ mixed arithmetic is implemented by double dispatching
    "/ (see the message send at the bottom)

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ self class 
                numerator:(numerator * aNumber)
                denominator:denominator
    ].
    (aNumber isMemberOf:Float) ifTrue:[
        ^ (numerator * aNumber) / denominator
    ].

    ^ aNumber productFromFraction:self

    "Modified: 28.7.1997 / 19:09:23 / cg"
!

+ aNumber
    "return the sum of the receiver and the argument, aNumber"

    "/ notice:
    "/ the following code handles some common cases,
    "/ and exists as an optimization, to speed up those cases.
    "/ also notice, that checks for those cases must be inlinable without
    "/ a message send; otherwise double-dispatch is just as fast.
    "/
    "/ Conceptionally, (and for most other argument types),
    "/ mixed arithmetic is implemented by double dispatching
    "/ (see the message send at the bottom)

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ self class 
            numerator:(numerator + (denominator * aNumber))
            denominator:denominator
    ].
    (aNumber isMemberOf:Float) ifTrue:[
        ^ (numerator asFloat / denominator asFloat) + aNumber
    ].

    ^ aNumber sumFromFraction:self

    "Modified: 28.7.1997 / 19:09:16 / cg"
!

- aNumber
    "return the difference of the receiver and the argument, aNumber"

    "/ notice:
    "/ the following code handles some common cases,
    "/ and exists as an optimization, to speed up those cases.
    "/ also notice, that checks for those cases must be inlinable without
    "/ a message send; otherwise double-dispatch is just as fast.
    "/
    "/ Conceptionally, (and for most other argument types),
    "/ mixed arithmetic is implemented by double dispatching
    "/ (see the message send at the bottom)

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ self class 
                numerator:(numerator - (denominator * aNumber))
                denominator:denominator
    ].
    (aNumber isMemberOf:Float) ifTrue:[
        ^ (numerator asFloat / denominator asFloat) - aNumber
    ].

    ^ aNumber differenceFromFraction:self

    "
     (1/3) - (1/9)      
     (1/9) - (1/3)      
     (999/1000) - (1/1000)      
     (999/1000) - (1/1000000)      
     (999000/1000000) - (1/1000000)      
    "

    "Modified: 28.7.1997 / 19:09:11 / cg"
!

/ aNumber
    "return the quotient of the receiver and the argument, aNumber"

    "/ notice:
    "/ the following code handles some common cases,
    "/ and exists as an optimization, to speed up those cases.
    "/ also notice, that checks for those cases must be inlinable without
    "/ a message send; otherwise double-dispatch is just as fast.
    "/
    "/ Conceptionally, (and for most other argument types),
    "/ mixed arithmetic is implemented by double dispatching
    "/ (see the message send at the bottom)

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ self class 
                numerator:numerator
                denominator:(denominator * aNumber)
    ].
    (aNumber isMemberOf:Float) ifTrue:[
        ^ numerator / (denominator * aNumber)
    ].

    ^ aNumber quotientFromFraction:self

    "Modified: 28.7.1997 / 19:09:06 / cg"
!

// aNumber
    "return the integer quotient of dividing the receiver by aNumber with
     truncation towards negative infinity."

    ^ (numerator * aNumber denominator) // (denominator * aNumber numerator)

    "
     0.5 // 1
     -0.5 // 1
     (1/2) // 1  = 0 ifFalse:[self halt].
     (-1/2) // 1 = -1 ifFalse:[self halt].
    "

    "Modified: / 5.11.1996 / 11:47:14 / cg"
    "Modified: / 13.2.1998 / 09:15:35 / stefan"
!

negated
    "optional - could use inherited method ..."

    ^ self class 
        numerator:(numerator negated)
        denominator:denominator

    "Modified: 5.11.1996 / 10:29:11 / cg"
!

reciprocal
    "optional - could use inherited method ..."

    numerator == 1 ifTrue:[^ denominator].
    ^ self class 
        numerator:denominator
        denominator:numerator

    "Modified: 5.11.1996 / 10:29:22 / cg"
! !

!Fraction methodsFor:'coercing & converting'!

asFixedPoint
    "return the receiver as fixedPoint number.
     Q: what should the scale be here ?"

    ^ FixedPoint numerator:numerator denominator:denominator scale:2

    "
     (1/2) asFixedPoint
    "

    "Created: 5.11.1996 / 15:15:54 / cg"
!

asFixedPoint:scale
    "return the receiver as fixedPoint number, with the given number
     of post-decimal-point digits."

    ^ FixedPoint numerator:numerator denominator:denominator scale:scale

    "
     (1/2) asFixedPoint:2 
     (1/3) asFixedPoint:2 
     (1/3) asFixedPoint:5 
     (2/3) asFixedPoint:2 
     (2/3) asFixedPoint:5 
    "

    "Created: 5.11.1996 / 15:15:54 / cg"
    "Modified: 10.1.1997 / 19:54:50 / cg"
!

asFloat
    "return a float with (approximately) my value.
     Since floats have a limited precision, you usually loose bits when doing this."

    |num den numShift denShift bits rslt|

    (numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
        ^ (numerator asFloat) / (denominator asFloat)
    ].

    "Do it the hard way: reduce magnitude and undo reduction on the quotient"

    bits := Float precision * 2.    "number of bits to preserve (conservative)"
    num := numerator abs.
    numShift := bits - num highBit. "(num highBit - bits) negated"
    numShift < 0 ifTrue:[num := num bitShift:numShift] ifFalse:[numShift := 0].

    den :=  denominator.
    denShift := bits - den highBit. "(den highBit - bits) negated"
    denShift < 0 ifTrue:[den := den bitShift:denShift] ifFalse:[denShift := 0].

    rslt := (num asFloat / den asFloat) * (2 raisedToInteger:denShift-numShift).
    numerator negative ifTrue:[ ^ rslt negated ].
    ^ rslt.

    " 
      (5/9) asFloat
      (-5/9) asFloat
      (500000000000/900000000000) asFloat
      (-500000000000/900000000000) asFloat
      (500000000000/9) asFloat
      (5/900000000000) asFloat
      89012345678901234567 asFloat / 123456789123456789 asFloat
      (89012345678901234567 / 123456789123456789) asFloat

      (
       180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
        /
       180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
      ) asFloat

      180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
         asFloat /
      180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
         asFloat
    "
!

asFraction
    "return the receiver as fraction - thats itself"

    ^ self
!

asInteger
    "return an integer with my value - will usually truncate"

    ^ numerator // denominator
!

asLargeFloat
    "return a large float with (approximately) my value"

    ^ (numerator asLargeFloat) / (denominator asLargeFloat)

    "
      (5/9) asLargeFloat
      (500000000000/900000000000) asLargeFloat
      (500000000000/9) asLargeFloat
    "
!

asLargeInteger
    "return an integer with my value - will usually truncate"

    ^ self asInteger asLargeInteger
!

asLongFloat
    "return a long float with (approximately) my value"

    |num den numShift denShift numBits rslt|

    (numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
        ^ (numerator asLongFloat) / (denominator asLongFloat)
    ].

    "Do it the hard way: reduce magnitude and undo reduction on the quotient"

    numBits := LongFloat precision * 2.    "number of bits to preserve (conservative)"
    num := numerator abs.
    numShift := numBits - num highBit. "(num highBit - bits) negated"
    numShift < 0 ifTrue:[
        num := num bitShift:numShift
    ] ifFalse:[
        numShift := 0
    ].

    den :=  denominator.
    denShift := numBits - den highBit. "(den highBit - bits) negated"
    denShift < 0 ifTrue:[
        den := den bitShift:denShift
    ] ifFalse:[
        denShift := 0
    ].

    rslt := (num asLongFloat / den asLongFloat) * (2 raisedToInteger:denShift-numShift).
    numerator negative ifTrue:[ ^ rslt negated ].
    ^ rslt.

    " 
      (5/9) asLongFloat                        
      (-5/9) asLongFloat   
      (Fraction basicNew setNumerator:500000000000 denominator:900000000000) asLongFloat = (5/9) asLongFloat
      (Fraction basicNew setNumerator:500000000001 denominator:900000000000) asLongFloat = (5/9) asLongFloat
      (500000000001/900000000000) asLongFloat  
      (-500000000001/900000000000) asLongFloat 
      (500000000001/900000000000) asLongFloat = (5/9) asLongFloat

      (500000000000/9) asLongFloat             
      (5/900000000000) asLongFloat     
      89012345678901234567 asFloat / 123456789123456789 asLongFloat 
      (89012345678901234567 / 123456789123456789) asLongFloat        
      (-89012345678901234567 / 123456789123456789) asLongFloat       

      (
       180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
        /
       180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
      ) asLongFloat    

      180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
         asLongFloat /
      180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
         asLongFloat
    "
!

asShortFloat
    "return a short float with (approximately) my value"

    (numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
        ^ (numerator asShortFloat) / (denominator asShortFloat)
    ].

    ^ self asFloat asShortFloat

    "
      (5/9) asShortFloat
      (500000000000/900000000000) asShortFloat
      (500000000000/9) asShortFloat
    "
!

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

    ^ aNumber asFraction
!

generality
    "return the generality value - see ArithmeticValue>>retry:coercing:"

    ^ 60
! !

!Fraction methodsFor:'comparing'!

< aNumber
    "return true if the receiver is less
     than aNumber, false otherwise."

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ numerator < (denominator * aNumber)
    ].
    ^ aNumber lessFromFraction:self

    "Modified: 5.11.1996 / 10:30:52 / cg"
!

= aNumber
    "return true, if the argument represents the same numeric value
     as the receiver, false otherwise"

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        (denominator == 1) ifFalse:[
            ^ numerator = (aNumber * denominator)
        ].
        ^ numerator = aNumber
    ].
    ^ aNumber equalFromFraction:self

    "Modified: / 7.7.1998 / 17:17:07 / cg"
!

> aNumber
    "return true if the receiver is greater
     than aNumber, false otherwise."
    "optional - could use inherited method ..."

    (aNumber isMemberOf:SmallInteger) ifTrue:[
        ^ numerator > (denominator * aNumber)
    ].
    ^ aNumber < self
!

hash
    "return a number for hashing; redefined, since fractions compare
     by numeric value (i.e. (9/3) = 3), therefore (9/3) hash must be the same
     as 3 hash."

    (denominator = 1) ifTrue:[^ numerator hash].

    ^ self asFloat hash

    "
     3 hash           
     (9/3) hash       
     3.0 hash         
     (1/2) hash       
     (1/4) hash       
     0.0 hash         
     0.5 hash         
     0.25 hash         
     0.4 hash         
    "
!

sameFractionValueAs:aNumber
    "return true, if the argument represents the same numeric value
     as the receiver, false otherwise"

    |rSelf rNum|

    rSelf := self reduced.
    rNum := aNumber reduced.
    rSelf denominator = rNum denominator ifTrue:[
        ^ rSelf numerator = rNum numerator
    ].
    ^ false
! !

!Fraction methodsFor:'double dispatching'!

differenceFromFixedPoint:aFixedPoint
    |n d otherDenominator otherNumerator|

    otherDenominator := aFixedPoint denominator.
    otherNumerator := aFixedPoint numerator.

    "save a multiplication if possible"
    otherDenominator == denominator ifTrue:[
        n := otherNumerator - numerator. 
        d := otherDenominator.
    ] ifFalse:[
        n := (otherNumerator * denominator) - (numerator * otherDenominator).
        d := otherDenominator * denominator.
    ].
    ^ aFixedPoint class 
        numerator:n
        denominator:d
        scale:(aFixedPoint scale)

    "
     ((1/3) asFixedPoint:2) - (1/3)        
     ((1/3) asFixedPoint:2) - (2/3) 
    "
!

differenceFromFloat:aFloat
    "sent when a float does not know how to subtract the receiver, a fraction"

    ^ (aFloat * denominator - numerator) / denominator
!

differenceFromFraction:aFraction
    |n d otherDenominator otherNumerator|

    otherDenominator := aFraction denominator.
    otherNumerator := aFraction numerator.

    "save a multiplication if possible"
    otherDenominator == denominator ifTrue:[
        n := otherNumerator - numerator. 
        d := otherDenominator.
    ] ifFalse:[
        n := (otherNumerator * denominator) - (numerator * otherDenominator).
        d := otherDenominator * denominator.
    ].
    ^ aFraction class 
        numerator:n
        denominator:d

    "
     ((1/3) asFixedPoint:2) - (1/3)        
     ((1/3) asFixedPoint:2) - (2/3) 
    "
!

differenceFromInteger:anInteger
    "sent when an integer does not know how to subtract the receiver, a fraction"

    ^ self class 
        numerator:((anInteger * denominator) - numerator)
        denominator:denominator

    "Modified: 28.7.1997 / 19:08:53 / cg"
!

equalFromFraction:aFraction
    denominator = aFraction denominator ifFalse:[
        ^ false   " must always be reduced "
        "/ ^ (numerator * aFraction denominator) = (aFraction numerator * denominator)
    ].
    ^ numerator = aFraction numerator
!

equalFromInteger:anInteger
    "sent when an integer does not know how to compare to the receiver, a fraction"

    "as I am always reduced, this test should not be required.
     However, it is here for subclasses and to allow comparing unnormalized fractions,
     which might be encountered internally"

    denominator = 1 ifFalse:[
        ^ numerator = (anInteger * denominator)
    ].
    ^ numerator = anInteger

    "
     1 = (1 asFixedPoint:1)
     (1 asFixedPoint:1) = 1
    "
!

lessEqFromInteger:anInteger
    "sent when an integer does not know how to compare to the receiver, a fraction"

    ^ (denominator * anInteger) <= numerator
!

lessFromFraction:aFraction
    "sent when a fraction does not know how to compare to the receiver"

    |n d|

    d := aFraction denominator.
    n := aFraction numerator.

    "/ save a multiplication if possible
    d == denominator ifTrue:[
        ^ n < numerator
    ].
    ^ (denominator * n) < (numerator * d)  
!

lessFromInteger:anInteger
    "sent when an integer does not know how to compare to the receiver, a fraction"

    ^ (denominator * anInteger) < numerator
!

productFromFixedPoint:aFixedPoint
    ^ aFixedPoint class 
        numerator:(aFixedPoint numerator * numerator) 
        denominator:(aFixedPoint denominator * denominator)
        scale:(aFixedPoint scale)

    "
     ((1/3) asFixedPoint:2) * 2       
     ((1/3) asFixedPoint:2) * (1/2) 
     ((1/3) asFixedPoint:2) * (3/2) 
    "
!

productFromFloat:aFloat
    "sent when a float does not know how to multiply the receiver, a fraction"

    ^ aFloat * numerator / denominator
!

productFromFraction:aFraction
    ^ aFraction class 
        numerator:(aFraction numerator * numerator) 
        denominator:(aFraction denominator * denominator)

    "
     ((1/3) asFixedPoint:2) * 2       
     ((1/3) asFixedPoint:2) * (1/2) 
     ((1/3) asFixedPoint:2) * (3/2) 
    "
!

productFromInteger:anInteger
    "sent when an integer does not know how to multiply the receiver, a fraction"

    ^ self class 
        numerator:(anInteger * numerator)
        denominator:denominator

    "Modified: 28.7.1997 / 19:06:22 / cg"
!

quotientFromFixedPoint:aFixedPoint
    "Return the quotient of the argument, aFixedPoint and the receiver.
     Sent when aFixedPoint does not know how to divide by the receiver."

    ^ aFixedPoint class 
        numerator:(aFixedPoint numerator * denominator) 
        denominator:(aFixedPoint denominator * numerator)
        scale:(aFixedPoint scale)

    "
     ((1/3) asFixedPoint:2) / 2       
     ((1/3) asFixedPoint:2) / (1/2) 
    "
!

quotientFromFloat:aFloat
    "Return the quotient of the argument, aFloat and the receiver.
     Sent when aFloat does not know how to divide by the receiver."

    ^ (aFloat * denominator) / numerator
!

quotientFromFraction:aFraction
    "Return the quotient of the argument, aFraction and the receiver.
     Sent when aFraction does not know how to divide by the receiver."

    ^ aFraction class 
        numerator:(aFraction numerator * denominator) 
        denominator:(aFraction denominator * numerator)

    "
     (1/3) / (1/2) 
     (1/3) / (3/2) 
    "
!

quotientFromInteger:anInteger
    "Return the quotient of the argument, anInteger and the receiver.
     Sent when anInteger does not know how to divide by the receiver."

    ^ self class 
        numerator:(anInteger * denominator)
        denominator:numerator

    "Modified: 28.7.1997 / 19:08:46 / cg"
!

sumFromFixedPoint:aFixedPoint
    |n d otherDenominator otherNumerator|

    otherDenominator := aFixedPoint denominator.
    otherNumerator := aFixedPoint numerator.

    "save a multiplication if possible"
    otherDenominator == denominator ifTrue:[
        n := otherNumerator + numerator. 
        d := otherDenominator.
    ] ifFalse:[
        n := (otherNumerator * denominator) + (numerator * otherDenominator).
        d := otherDenominator * denominator.
    ].
    ^ aFixedPoint class 
        numerator:n
        denominator:d
        scale:(aFixedPoint scale)

    "
     ((1/3) asFixedPoint:2) + (1/3)        
     ((1/3) asFixedPoint:2) + (2/3) 
    "
!

sumFromFloat:aFloat
    "sent when a float does not know how to add the receiver, a fraction"

    ^ (aFloat * denominator + numerator) / denominator
!

sumFromFraction:aFraction
    |n d otherDenominator otherNumerator|

    otherDenominator := aFraction denominator.
    otherNumerator := aFraction numerator.

    "save a multiplication if possible"
    otherDenominator == denominator ifTrue:[
        n := otherNumerator + numerator. 
        d := otherDenominator.
    ] ifFalse:[
        n := (otherNumerator * denominator) + (numerator * otherDenominator).
        d := otherDenominator * denominator.
    ].
    ^ aFraction class 
        numerator:n
        denominator:d

    "
     (1/3) + (1/3)        
     (1/3) + (2/3) 
    "
!

sumFromInteger:anInteger
    "sent when an integer does not know how to add the receiver, a fraction"

    ^ self class 
        numerator:(numerator + (anInteger * denominator))
        denominator:denominator

    "Modified: 28.7.1997 / 19:08:40 / cg"
! !

!Fraction methodsFor:'printing & storing'!

printOn:aStream
    "append a printed representation of the receiver to the
     argument, aStream"

    |t|

    PrintWholeNumbers == true ifTrue:[
        "/ experimental: print fractions which are greater than 1 as a sum of
        "/ an integral and the fractional part. They are easier to read this way.
        numerator > denominator ifTrue:[
            aStream nextPut:$(.
            t := numerator // denominator.
            t printOn:aStream.
            aStream nextPutAll:'+('.
            (numerator - (t*denominator)) printOn:aStream.
            aStream nextPut:$/.
            denominator printOn:aStream.
            aStream nextPutAll:'))'.
            ^ self
        ].
    ].

    aStream nextPut:$(.
    numerator printOn:aStream.
    aStream nextPut:$/.
    denominator printOn:aStream.
    aStream nextPut:$)

    "Modified: / 31.7.2002 / 09:56:41 / cg"
! !

!Fraction methodsFor:'private'!

reduced
    "reduce the receiver; divide the numerator and denominator by their
     greatest common divisor; if the result is integral, return an Integer.
     Otherwise, return the normalized receiver.
     CAVEAT: bad name; should be called reduce, as it has a side effect
     (i.e. this is destructive wrt. the instance values)."

    |gcd den|

    den := denominator.
    den < 0 ifTrue:[
        numerator := numerator negated.
        den := denominator := den negated.
    ].

    den == 1 ifTrue:[^ numerator].
    numerator == 1 ifTrue:[^ self].
    numerator == 0 ifTrue:[^ 0].

    gcd := numerator gcd:den.
    (gcd ~~ 1) ifTrue:[
        gcd < 0 ifTrue:[
             gcd := gcd negated.
        ].
        numerator := numerator // gcd.
        denominator := den := den // gcd.
        (den == 1) ifTrue:[^ numerator].
    ].
    ^ self
!

setNumerator:num denominator:den
    "set both numerator and denominator"

    numerator := num.
    denominator := den
! !

!Fraction methodsFor:'testing'!

isFraction
    "return true, if the receiver is some kind of fraction;
     true is returned here - the method is redefined from Object."

    ^ true
!

isLiteral
    "return true, if the receiver can be used as a literal constant in ST syntax
     (i.e. can be used in constant arrays)"

    ^ true

!

negative
    "return true if the receiver is negative"

    (numerator < 0) ifTrue:[
        ^ (denominator < 0) not
    ].
    ^ (denominator < 0)
! !

!Fraction methodsFor:'truncation & rounding'!

fractionPart
    "extract the after-decimal fraction part,
     such that (self truncated + self fractionPart) = self"

    numerator abs < denominator abs ifTrue:[
        ^ self
    ].
    ^ (numerator rem: denominator) / denominator

    "
     (3/2) fractionPart + (3/2) truncated    
     (-3/2) fractionPart + (-3/2) truncated    

     (3/2) fractionPart     
     (-3/2) fractionPart     
     (3/2) asFloat fractionPart     
     (-3/2) asFloat fractionPart     
     (2/3) fractionPart          
     ((3/2)*(15/4)) fractionPart   
     ((2/3)*(4/15)) fractionPart   
    "

    "Modified: / 5.11.2001 / 17:55:25 / cg"
!

integerPart
    "extract the pre-decimal integer part."

    numerator abs < denominator abs ifTrue:[
        ^ 0
    ].
    ^ super integerPart

    "
     (3/2) integerPart        
     (-3/2) integerPart        
     (2/3) integerPart           
     ((3/2)*(15/4)) integerPart   
     ((2/3)*(4/15)) integerPart   
    "

    "Modified: / 5.11.2001 / 17:55:01 / cg"
!

rounded
    "return the receiver rounded to the nearest integer as integer"

    "/ mhmh - what about -(1/2)

    |t|

    self negative ifTrue:[
        t := self - (1/2)
    ] ifFalse:[
        t := self + (1/2)
    ].
    ^ t truncated.

    "
     (1/3) rounded           
     (1/3) negated rounded     
     (1/2) rounded           
     (1/2) negated rounded   
     0.5 rounded  
     -0.5 rounded 
     (2/3) rounded             
     (2/3) negated rounded     
    "

    "Modified: 5.11.1996 / 11:32:32 / cg"
!

truncated
    "return the receiver truncated towards zero as Integer"

    ^ numerator quo: denominator

    "
     (3/2) truncated     
     (3/2) negated truncated  
    "

    "Modified: 5.11.1996 / 12:18:46 / cg"
! !

!Fraction methodsFor:'visiting'!

acceptVisitor:aVisitor with:aParameter

    ^ aVisitor visitFraction:self with:aParameter
! !

!Fraction class methodsFor:'documentation'!

version
    ^ '$Id: Fraction.st 10448 2009-06-14 16:10:51Z vranyj1 $'
! !

Fraction initialize!