@@ -380,32 +380,139 @@ def reverse(b, a):
380380
381381 return forward , reverse
382382
383+ # Rational arithmetic algorithms: Knuth, TAOCP, Volume 2, 4.5.1.
384+ #
385+ # Assume input fractions a and b are normalized.
386+ #
387+ # 1) Consider addition/subtraction.
388+ #
389+ # Let g = gcd(da, db). Then
390+ #
391+ # na nb na*db ± nb*da
392+ # a ± b == -- ± -- == ------------- ==
393+ # da db da*db
394+ #
395+ # na*(db//g) ± nb*(da//g) t
396+ # == ----------------------- == -
397+ # (da*db)//g d
398+ #
399+ # Now, if g > 1, we're working with smaller integers.
400+ #
401+ # Note, that t, (da//g) and (db//g) are pairwise coprime.
402+ #
403+ # Indeed, (da//g) and (db//g) share no common factors (they were
404+ # removed) and da is coprime with na (since input fractions are
405+ # normalized), hence (da//g) and na are coprime. By symmetry,
406+ # (db//g) and nb are coprime too. Then,
407+ #
408+ # gcd(t, da//g) == gcd(na*(db//g), da//g) == 1
409+ # gcd(t, db//g) == gcd(nb*(da//g), db//g) == 1
410+ #
411+ # Above allows us optimize reduction of the result to lowest
412+ # terms. Indeed,
413+ #
414+ # g2 = gcd(t, d) == gcd(t, (da//g)*(db//g)*g) == gcd(t, g)
415+ #
416+ # t//g2 t//g2
417+ # a ± b == ----------------------- == ----------------
418+ # (da//g)*(db//g)*(g//g2) (da//g)*(db//g2)
419+ #
420+ # is a normalized fraction. This is useful because the unnormalized
421+ # denominator d could be much larger than g.
422+ #
423+ # We should special-case g == 1 (and g2 == 1), since 60.8% of
424+ # randomly-chosen integers are coprime:
425+ # https://en.wikipedia.org/wiki/Coprime_integers#Probability_of_coprimality
426+ # Note, that g2 == 1 always for fractions, obtained from floats: here
427+ # g is a power of 2 and the unnormalized numerator t is an odd integer.
428+ #
429+ # 2) Consider multiplication
430+ #
431+ # Let g1 = gcd(na, db) and g2 = gcd(nb, da), then
432+ #
433+ # na*nb na*nb (na//g1)*(nb//g2)
434+ # a*b == ----- == ----- == -----------------
435+ # da*db db*da (db//g1)*(da//g2)
436+ #
437+ # Note, that after divisions we're multiplying smaller integers.
438+ #
439+ # Also, the resulting fraction is normalized, because each of
440+ # two factors in the numerator is coprime to each of the two factors
441+ # in the denominator.
442+ #
443+ # Indeed, pick (na//g1). It's coprime with (da//g2), because input
444+ # fractions are normalized. It's also coprime with (db//g1), because
445+ # common factors are removed by g1 == gcd(na, db).
446+ #
447+ # As for addition/subtraction, we should special-case g1 == 1
448+ # and g2 == 1 for same reason. That happens also for multiplying
449+ # rationals, obtained from floats.
450+
383451 def _add (a , b ):
384452 """a + b"""
385- da , db = a .denominator , b .denominator
386- return Fraction (a .numerator * db + b .numerator * da ,
387- da * db )
453+ na , da = a .numerator , a .denominator
454+ nb , db = b .numerator , b .denominator
455+ g = math .gcd (da , db )
456+ if g == 1 :
457+ return Fraction (na * db + da * nb , da * db , _normalize = False )
458+ s = da // g
459+ t = na * (db // g ) + nb * s
460+ g2 = math .gcd (t , g )
461+ if g2 == 1 :
462+ return Fraction (t , s * db , _normalize = False )
463+ return Fraction (t // g2 , s * (db // g2 ), _normalize = False )
388464
389465 __add__ , __radd__ = _operator_fallbacks (_add , operator .add )
390466
391467 def _sub (a , b ):
392468 """a - b"""
393- da , db = a .denominator , b .denominator
394- return Fraction (a .numerator * db - b .numerator * da ,
395- da * db )
469+ na , da = a .numerator , a .denominator
470+ nb , db = b .numerator , b .denominator
471+ g = math .gcd (da , db )
472+ if g == 1 :
473+ return Fraction (na * db - da * nb , da * db , _normalize = False )
474+ s = da // g
475+ t = na * (db // g ) - nb * s
476+ g2 = math .gcd (t , g )
477+ if g2 == 1 :
478+ return Fraction (t , s * db , _normalize = False )
479+ return Fraction (t // g2 , s * (db // g2 ), _normalize = False )
396480
397481 __sub__ , __rsub__ = _operator_fallbacks (_sub , operator .sub )
398482
399483 def _mul (a , b ):
400484 """a * b"""
401- return Fraction (a .numerator * b .numerator , a .denominator * b .denominator )
485+ na , da = a .numerator , a .denominator
486+ nb , db = b .numerator , b .denominator
487+ g1 = math .gcd (na , db )
488+ if g1 > 1 :
489+ na //= g1
490+ db //= g1
491+ g2 = math .gcd (nb , da )
492+ if g2 > 1 :
493+ nb //= g2
494+ da //= g2
495+ return Fraction (na * nb , db * da , _normalize = False )
402496
403497 __mul__ , __rmul__ = _operator_fallbacks (_mul , operator .mul )
404498
405499 def _div (a , b ):
406500 """a / b"""
407- return Fraction (a .numerator * b .denominator ,
408- a .denominator * b .numerator )
501+ # Same as _mul(), with inversed b.
502+ na , da = a .numerator , a .denominator
503+ nb , db = b .numerator , b .denominator
504+ g1 = math .gcd (na , nb )
505+ if g1 > 1 :
506+ na //= g1
507+ nb //= g1
508+ g2 = math .gcd (db , da )
509+ if g2 > 1 :
510+ da //= g2
511+ db //= g2
512+ n , d = na * db , nb * da
513+ if d < 0 :
514+ n , d = - n , - d
515+ return Fraction (n , d , _normalize = False )
409516
410517 __truediv__ , __rtruediv__ = _operator_fallbacks (_div , operator .truediv )
411518
0 commit comments