From 3feef61742facb3cbf53f9df1cb89062452597f2 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 11 Feb 2008 06:19:17 +0000 Subject: Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60569,60571-60572,60574,60576-60583,60585-60586,60589,60591,60594-60595,60597-60598,60600-60601,60606-60612,60615,60617,60619-60621,60623-60625,60627-60629,60631,60633,60635,60647,60650,60652,60654,60656,60658-60659,60664-60666,60668-60670,60672,60676,60678,60680-60683,60685-60686,60688,60690,60692-60694,60697-60706,60708-60712,60714-60724 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60701 | georg.brandl | 2008-02-09 22:36:15 +0100 (Sat, 09 Feb 2008) | 2 lines Needs only 2.4 now. ........ r60702 | georg.brandl | 2008-02-09 22:38:54 +0100 (Sat, 09 Feb 2008) | 2 lines Docs are rst now. ........ r60703 | georg.brandl | 2008-02-09 23:00:00 +0100 (Sat, 09 Feb 2008) | 2 lines Fix link. ........ r60704 | georg.brandl | 2008-02-10 00:09:25 +0100 (Sun, 10 Feb 2008) | 2 lines Fix for newest doctools. ........ r60709 | raymond.hettinger | 2008-02-10 08:21:09 +0100 (Sun, 10 Feb 2008) | 1 line Clarify that decimal also supports fixed-point arithmetic. ........ r60710 | nick.coghlan | 2008-02-10 08:32:52 +0100 (Sun, 10 Feb 2008) | 1 line Add missing NEWS entry for r60695 ........ r60712 | mark.dickinson | 2008-02-10 15:58:38 +0100 (Sun, 10 Feb 2008) | 3 lines Turn classmethods into staticmethods, and avoid calling the constructor of subclasses of Rational. (See discussion in issue #1682.) ........ r60715 | mark.dickinson | 2008-02-10 16:19:58 +0100 (Sun, 10 Feb 2008) | 2 lines Typos in decimal comment and documentation ........ r60716 | skip.montanaro | 2008-02-10 16:31:54 +0100 (Sun, 10 Feb 2008) | 2 lines Get the saying right. ;-) ........ r60717 | skip.montanaro | 2008-02-10 16:32:16 +0100 (Sun, 10 Feb 2008) | 2 lines whoops - revert ........ r60718 | mark.dickinson | 2008-02-10 20:23:36 +0100 (Sun, 10 Feb 2008) | 2 lines Remove reference to Rational ........ r60719 | raymond.hettinger | 2008-02-10 21:35:16 +0100 (Sun, 10 Feb 2008) | 1 line Complete an open todo on pickletools -- add a pickle optimizer. ........ r60721 | mark.dickinson | 2008-02-10 22:29:51 +0100 (Sun, 10 Feb 2008) | 3 lines Rename rational.Rational to fractions.Fraction, to avoid name clash with numbers.Rational. See issue #1682 for related discussion. ........ r60722 | christian.heimes | 2008-02-11 03:26:22 +0100 (Mon, 11 Feb 2008) | 1 line The test requires the network resource ........ r60723 | mark.dickinson | 2008-02-11 04:11:55 +0100 (Mon, 11 Feb 2008) | 3 lines Put an extra space into the repr of a Fraction: Fraction(1, 2) instead of Fraction(1,2). ........ --- Demo/classes/README | 1 - Doc/README.txt | 4 +- Doc/conf.py | 22 +- Doc/extending/windows.rst | 2 +- Doc/library/decimal.rst | 22 +- Doc/library/fractions.rst | 75 ++++++ Doc/library/numbers.rst | 10 +- Doc/library/pickletools.rst | 7 + Doc/library/rational.rst | 75 ------ Doc/whatsnew/2.6.rst | 22 +- Lib/decimal.py | 2 +- Lib/fractions.py | 535 ++++++++++++++++++++++++++++++++++++++++++ Lib/pickletools.py | 31 ++- Lib/rational.py | 536 ------------------------------------------- Lib/test/test_builtin.py | 4 +- Lib/test/test_fractions.py | 427 ++++++++++++++++++++++++++++++++++ Lib/test/test_rational.py | 427 ---------------------------------- Modules/_collectionsmodule.c | 2 + 18 files changed, 1124 insertions(+), 1080 deletions(-) create mode 100644 Doc/library/fractions.rst delete mode 100644 Doc/library/rational.rst create mode 100755 Lib/fractions.py delete mode 100755 Lib/rational.py create mode 100644 Lib/test/test_fractions.py delete mode 100644 Lib/test/test_rational.py diff --git a/Demo/classes/README b/Demo/classes/README index 1d41f6a..e5bc289 100644 --- a/Demo/classes/README +++ b/Demo/classes/README @@ -4,7 +4,6 @@ Complex.py Complex numbers Dates.py Date manipulation package by Tim Peters Dbm.py Wrapper around built-in dbm, supporting arbitrary values Range.py Example of a generator: re-implement built-in range() -Rat.py Rational numbers Rev.py Yield the reverse of a sequence Vec.py A simple vector class bitvec.py A bit-vector class by Jan-Hein B\"uhrman diff --git a/Doc/README.txt b/Doc/README.txt index a93542f..202a1ce 100644 --- a/Doc/README.txt +++ b/Doc/README.txt @@ -14,7 +14,7 @@ those familiar with the previous docs written in LaTeX. Building the docs ================= -You need to install Python 2.5.1 or higher (but Python 3.0 is not supported yet); +You need to install Python 2.4 or higher (but Python 3.0 is not supported yet); the toolset used to build the docs are written in Python. The toolset used to build the documentation is called *Sphinx*, it is not included in this tree, but maintained separately in the Python Subversion repository. Also @@ -55,7 +55,7 @@ Available make targets are: * "latex", which builds LaTeX source files that can be run with "pdflatex" to produce PDF documents. - + * "linkcheck", which checks all external references to see whether they are broken, redirected or malformed, and outputs this information to stdout as well as a plain-text (.txt) file. diff --git a/Doc/conf.py b/Doc/conf.py index 273c76c..1c8dd71 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -38,17 +38,17 @@ today = '' today_fmt = '%B %d, %Y' # List of files that shouldn't be included in the build. -unused_files = [ - 'whatsnew/2.0.rst', - 'whatsnew/2.1.rst', - 'whatsnew/2.2.rst', - 'whatsnew/2.3.rst', - 'whatsnew/2.4.rst', - 'whatsnew/2.5.rst', - 'whatsnew/2.6.rst', - 'maclib/scrap.rst', - 'library/xmllib.rst', - 'library/xml.etree.rst', +unused_docs = [ + 'whatsnew/2.0', + 'whatsnew/2.1', + 'whatsnew/2.2', + 'whatsnew/2.3', + 'whatsnew/2.4', + 'whatsnew/2.5', + 'whatsnew/2.6', + 'maclib/scrap', + 'library/xmllib', + 'library/xml.etree', ] # Relative filename of the reference count data file. diff --git a/Doc/extending/windows.rst b/Doc/extending/windows.rst index a34ba2b..a0782a7 100644 --- a/Doc/extending/windows.rst +++ b/Doc/extending/windows.rst @@ -179,7 +179,7 @@ and add the following to the module initialization function:: MyObject_Type.ob_type = &PyType_Type; -Refer to section 3 of the `Python FAQ `_ for +Refer to section 3 of the `Python FAQ `_ for details on why you must do this. diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index f6b96b2..422436f 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -1,6 +1,6 @@ -:mod:`decimal` --- Decimal floating point arithmetic -==================================================== +:mod:`decimal` --- Decimal fixed point and floating point arithmetic +==================================================================== .. module:: decimal :synopsis: Implementation of the General Decimal Arithmetic Specification. @@ -16,6 +16,11 @@ The :mod:`decimal` module provides support for decimal floating point arithmetic. It offers several advantages over the :class:`float` datatype: +* Decimal "is based on a floating-point model which was designed with people + in mind, and necessarily has a paramount guiding principle -- computers must + provide an arithmetic that works in the same way as the arithmetic that + people learn at school." -- excerpt from the decimal arithmetic specification. + * Decimal numbers can be represented exactly. In contrast, numbers like :const:`1.1` do not have an exact representation in binary floating point. End users typically would not expect :const:`1.1` to display as @@ -25,7 +30,7 @@ arithmetic. It offers several advantages over the :class:`float` datatype: + 0.1 + 0.1 - 0.3`` is exactly equal to zero. In binary floating point, the result is :const:`5.5511151231257827e-017`. While near to zero, the differences prevent reliable equality testing and differences can accumulate. For this - reason, decimal would be preferred in accounting applications which have strict + reason, decimal is preferred in accounting applications which have strict equality invariants. * The decimal module incorporates a notion of significant places so that ``1.30 @@ -50,6 +55,13 @@ arithmetic. It offers several advantages over the :class:`float` datatype: standards. While the built-in float type exposes only a modest portion of its capabilities, the decimal module exposes all required parts of the standard. When needed, the programmer has full control over rounding and signal handling. + This includes an option to enforce exact arithmetic by using exceptions + to block any inexact operations. + +* The decimal module was designed to support "without prejudice, both exact + unrounded decimal arithmetic (sometimes called fixed-point arithmetic) + and rounded floating-point arithmetic." -- excerpt from the decimal + arithmetic specification. The module design is centered around three concepts: the decimal number, the context for arithmetic, and signals. @@ -832,7 +844,7 @@ described below. In addition, the module provides three pre-made contexts: :const:`ROUND_HALF_EVEN`. All flags are cleared. No traps are enabled (so that exceptions are not raised during computations). - Because the trapped are disabled, this context is useful for applications that + Because the traps are disabled, this context is useful for applications that prefer to have result value of :const:`NaN` or :const:`Infinity` instead of raising exceptions. This allows an application to complete a run in the presence of conditions that would otherwise halt the program. @@ -1245,7 +1257,7 @@ quiet or signaling :const:`NaN` always returns :const:`False` (even when doing :const:`True`. An attempt to compare two Decimals using any of the ``<``, ``<=``, ``>`` or ``>=`` operators will raise the :exc:`InvalidOperation` signal if either operand is a :const:`NaN`, and return :const:`False` if this signal is -trapped. Note that the General Decimal Arithmetic specification does not +not trapped. Note that the General Decimal Arithmetic specification does not specify the behavior of direct comparisons; these rules for comparisons involving a :const:`NaN` were taken from the IEEE 854 standard (see Table 3 in section 5.7). To ensure strict standards-compliance, use the :meth:`compare` diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst new file mode 100644 index 0000000..5f30caf --- /dev/null +++ b/Doc/library/fractions.rst @@ -0,0 +1,75 @@ + +:mod:`fractions` --- Rational numbers +==================================== + +.. module:: fractions + :synopsis: Rational numbers. +.. moduleauthor:: Jeffrey Yasskin +.. sectionauthor:: Jeffrey Yasskin +.. versionadded:: 2.6 + + +The :mod:`fractions` module defines an immutable, infinite-precision +Rational number class. + + +.. class:: Fraction(numerator=0, denominator=1) + Fraction(other_fraction) + Fraction(string) + + The first version requires that *numerator* and *denominator* are + instances of :class:`numbers.Integral` and returns a new + ``Fraction`` representing ``numerator/denominator``. If + *denominator* is :const:`0`, raises a :exc:`ZeroDivisionError`. The + second version requires that *other_fraction* is an instance of + :class:`numbers.Fraction` and returns an instance of + :class:`Rational` with the same value. The third version expects a + string of the form ``[-+]?[0-9]+(/[0-9]+)?``, optionally surrounded + by spaces. + + Implements all of the methods and operations from + :class:`numbers.Rational` and is immutable and hashable. + + +.. method:: Fraction.from_float(flt) + + This classmethod constructs a :class:`Fraction` representing the + exact value of *flt*, which must be a :class:`float`. Beware that + ``Fraction.from_float(0.3)`` is not the same value as ``Rational(3, + 10)`` + + +.. method:: Fraction.from_decimal(dec) + + This classmethod constructs a :class:`Fraction` representing the + exact value of *dec*, which must be a + :class:`decimal.Decimal`. + + +.. method:: Fraction.__floor__() + + Returns the greatest :class:`int` ``<= self``. Will be accessible + through :func:`math.floor` in Py3k. + + +.. method:: Fraction.__ceil__() + + Returns the least :class:`int` ``>= self``. Will be accessible + through :func:`math.ceil` in Py3k. + + +.. method:: Fraction.__round__() + Fraction.__round__(ndigits) + + The first version returns the nearest :class:`int` to ``self``, + rounding half to even. The second version rounds ``self`` to the + nearest multiple of ``Fraction(1, 10**ndigits)`` (logically, if + ``ndigits`` is negative), again rounding half toward even. Will be + accessible through :func:`round` in Py3k. + + +.. seealso:: + + Module :mod:`numbers` + The abstract base classes making up the numeric tower. + diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index 1d543c8..d78595e 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -104,7 +104,7 @@ Notes for type implementors Implementors should be careful to make equal numbers equal and hash them to the same values. This may be subtle if there are two different -extensions of the real numbers. For example, :class:`rational.Rational` +extensions of the real numbers. For example, :class:`fractions.Fraction` implements :func:`hash` as follows:: def __hash__(self): @@ -199,11 +199,11 @@ in :class:`complex`, and both :meth:`__radd__` s land there, so ``a+b Because most of the operations on any given type will be very similar, it can be useful to define a helper function which generates the forward and reverse instances of any given operator. For example, -:class:`rational.Rational` uses:: +:class:`fractions.Fraction` uses:: def _operator_fallbacks(monomorphic_operator, fallback_operator): def forward(a, b): - if isinstance(b, (int, long, Rational)): + if isinstance(b, (int, long, Fraction)): return monomorphic_operator(a, b) elif isinstance(b, float): return fallback_operator(float(a), b) @@ -215,7 +215,7 @@ forward and reverse instances of any given operator. For example, forward.__doc__ = monomorphic_operator.__doc__ def reverse(b, a): - if isinstance(a, RationalAbc): + if isinstance(a, Rational): # Includes ints. return monomorphic_operator(a, b) elif isinstance(a, numbers.Real): @@ -231,7 +231,7 @@ forward and reverse instances of any given operator. For example, def _add(a, b): """a + b""" - return Rational(a.numerator * b.denominator + + return Fraction(a.numerator * b.denominator + b.numerator * a.denominator, a.denominator * b.denominator) diff --git a/Doc/library/pickletools.rst b/Doc/library/pickletools.rst index 3fc38ff..3dc06ac 100644 --- a/Doc/library/pickletools.rst +++ b/Doc/library/pickletools.rst @@ -33,3 +33,10 @@ probably won't find the :mod:`pickletools` module relevant. the opcode's argument; *pos* is the position at which this opcode is located. *pickle* can be a string or a file-like object. +.. function:: optimize(picklestring) + + Returns a new equivalent pickle string after eliminating unused ``PUT`` + opcodes. The optimized pickle is shorter, takes less transmission time, + requires less storage space, and unpickles more efficiently. + + .. versionadded:: 2.6 diff --git a/Doc/library/rational.rst b/Doc/library/rational.rst deleted file mode 100644 index 8ed702f..0000000 --- a/Doc/library/rational.rst +++ /dev/null @@ -1,75 +0,0 @@ - -:mod:`rational` --- Rational numbers -==================================== - -.. module:: rational - :synopsis: Rational numbers. -.. moduleauthor:: Jeffrey Yasskin -.. sectionauthor:: Jeffrey Yasskin -.. versionadded:: 2.6 - - -The :mod:`rational` module defines an immutable, infinite-precision -Rational number class. - - -.. class:: Rational(numerator=0, denominator=1) - Rational(other_rational) - Rational(string) - - The first version requires that *numerator* and *denominator* are - instances of :class:`numbers.Integral` and returns a new - ``Rational`` representing ``numerator/denominator``. If - *denominator* is :const:`0`, raises a :exc:`ZeroDivisionError`. The - second version requires that *other_rational* is an instance of - :class:`numbers.Rational` and returns an instance of - :class:`Rational` with the same value. The third version expects a - string of the form ``[-+]?[0-9]+(/[0-9]+)?``, optionally surrounded - by spaces. - - Implements all of the methods and operations from - :class:`numbers.Rational` and is immutable and hashable. - - -.. method:: Rational.from_float(flt) - - This classmethod constructs a :class:`Rational` representing the - exact value of *flt*, which must be a :class:`float`. Beware that - ``Rational.from_float(0.3)`` is not the same value as ``Rational(3, - 10)`` - - -.. method:: Rational.from_decimal(dec) - - This classmethod constructs a :class:`Rational` representing the - exact value of *dec*, which must be a - :class:`decimal.Decimal`. - - -.. method:: Rational.__floor__() - - Returns the greatest :class:`int` ``<= self``. Will be accessible - through :func:`math.floor` in Py3k. - - -.. method:: Rational.__ceil__() - - Returns the least :class:`int` ``>= self``. Will be accessible - through :func:`math.ceil` in Py3k. - - -.. method:: Rational.__round__() - Rational.__round__(ndigits) - - The first version returns the nearest :class:`int` to ``self``, - rounding half to even. The second version rounds ``self`` to the - nearest multiple of ``Rational(1, 10**ndigits)`` (logically, if - ``ndigits`` is negative), again rounding half toward even. Will be - accessible through :func:`round` in Py3k. - - -.. seealso:: - - Module :mod:`numbers` - The abstract base classes making up the numeric tower. - diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index cbc8b8f..d37c5ac 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -578,8 +578,8 @@ and comparisons. :class:`Rational` numbers derive from :class:`Real`, have :attr:`numerator` and :attr:`denominator` properties, and can be -converted to floats. Python 2.6 adds a simple rational-number class -in the :mod:`rational` module. +converted to floats. Python 2.6 adds a simple rational-number class, +:class:`Fraction`, in the :mod:`fractions` module. :class:`Integral` numbers derive from :class:`Rational`, and can be shifted left and right with ``<<`` and ``>>``, @@ -598,29 +598,29 @@ one, :func:`trunc`, that's been backported to Python 2.6. -The Rational Module +The Fraction Module -------------------------------------------------- To fill out the hierarchy of numeric types, a rational-number class -has been added as the :mod:`rational` module. Rational numbers are +has been added as the :mod:`fractions` module. Rational numbers are represented as a fraction; rational numbers can exactly represent numbers such as two-thirds that floating-point numbers can only approximate. -The :class:`Rational` constructor takes two :class:`Integral` values +The :class:`Fraction` constructor takes two :class:`Integral` values that will be the numerator and denominator of the resulting fraction. :: - >>> from rational import Rational - >>> a = Rational(2, 3) - >>> b = Rational(2, 5) + >>> from fractions import Fraction + >>> a = Fraction(2, 3) + >>> b = Fraction(2, 5) >>> float(a), float(b) (0.66666666666666663, 0.40000000000000002) >>> a+b - rational.Rational(16,15) + Fraction(16, 15) >>> a/b - rational.Rational(5,3) + Fraction(5, 3) -The :mod:`rational` module is based upon an implementation by Sjoerd +The :mod:`fractions` module is based upon an implementation by Sjoerd Mullender that was in Python's :file:`Demo/classes/` directory for a long time. This implementation was significantly updated by Jeffrey Yaskin. diff --git a/Lib/decimal.py b/Lib/decimal.py index 55faf99..873f7c0 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -802,7 +802,7 @@ class Decimal(_numbers.Real, _numbers.Inexact): # != comparisons involving a NaN always return True # <, >, <= and >= comparisons involving a (quiet or signaling) # NaN signal InvalidOperation, and return False if the - # InvalidOperation is trapped. + # InvalidOperation is not trapped. # # This behavior is designed to conform as closely as possible to # that specified by IEEE 754. diff --git a/Lib/fractions.py b/Lib/fractions.py new file mode 100755 index 0000000..25b9f02 --- /dev/null +++ b/Lib/fractions.py @@ -0,0 +1,535 @@ +# Originally contributed by Sjoerd Mullender. +# Significantly modified by Jeffrey Yasskin . + +"""Fraction, infinite-precision, real numbers.""" + +import math +import numbers +import operator +import re + +__all__ = ["Fraction"] + + + +def gcd(a, b): + """Calculate the Greatest Common Divisor of a and b. + + Unless b==0, the result will have the same sign as b (so that when + b is divided by it, the result comes out positive). + """ + while b: + a, b = b, a%b + return a + + +_RATIONAL_FORMAT = re.compile(r""" + \A\s* # optional whitespace at the start, then + (?P[-+]?) # an optional sign, then + (?=\d|\.\d) # lookahead for digit or .digit + (?P\d*) # numerator (possibly empty) + (?: # followed by an optional + /(?P\d+) # / and denominator + | # or + \.(?P\d*) # decimal point and fractional part + )? + \s*\Z # and optional whitespace to finish +""", re.VERBOSE) + + +class Fraction(numbers.Rational): + """This class implements rational numbers. + + Fraction(8, 6) will produce a rational number equivalent to + 4/3. Both arguments must be Integral. The numerator defaults to 0 + and the denominator defaults to 1 so that Fraction(3) == 3 and + Fraction() == 0. + + Fraction can also be constructed from strings of the form + '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces. + + """ + + __slots__ = ('_numerator', '_denominator') + + # We're immutable, so use __new__ not __init__ + def __new__(cls, numerator=0, denominator=1): + """Constructs a Rational. + + Takes a string like '3/2' or '1.5', another Rational, or a + numerator/denominator pair. + + """ + self = super(Fraction, cls).__new__(cls) + + if denominator == 1: + if isinstance(numerator, str): + # Handle construction from strings. + input = numerator + m = _RATIONAL_FORMAT.match(input) + if m is None: + raise ValueError('Invalid literal for Fraction: ' + input) + numerator = m.group('num') + decimal = m.group('decimal') + if decimal: + # The literal is a decimal number. + numerator = int(numerator + decimal) + denominator = 10**len(decimal) + else: + # The literal is an integer or fraction. + numerator = int(numerator) + # Default denominator to 1. + denominator = int(m.group('denom') or 1) + + if m.group('sign') == '-': + numerator = -numerator + + elif (not isinstance(numerator, numbers.Integral) and + isinstance(numerator, numbers.Rational)): + # Handle copies from other rationals. + other_rational = numerator + numerator = other_rational.numerator + denominator = other_rational.denominator + + if (not isinstance(numerator, numbers.Integral) or + not isinstance(denominator, numbers.Integral)): + raise TypeError("Fraction(%(numerator)s, %(denominator)s):" + " Both arguments must be integral." % locals()) + + if denominator == 0: + raise ZeroDivisionError('Fraction(%s, 0)' % numerator) + + g = gcd(numerator, denominator) + self._numerator = int(numerator // g) + self._denominator = int(denominator // g) + return self + + @classmethod + def from_float(cls, f): + """Converts a finite float to a rational number, exactly. + + Beware that Fraction.from_float(0.3) != Fraction(3, 10). + + """ + if not isinstance(f, float): + raise TypeError("%s.from_float() only takes floats, not %r (%s)" % + (cls.__name__, f, type(f).__name__)) + if math.isnan(f) or math.isinf(f): + raise TypeError("Cannot convert %r to %s." % (f, cls.__name__)) + return cls(*f.as_integer_ratio()) + + @classmethod + def from_decimal(cls, dec): + """Converts a finite Decimal instance to a rational number, exactly.""" + from decimal import Decimal + if not isinstance(dec, Decimal): + raise TypeError( + "%s.from_decimal() only takes Decimals, not %r (%s)" % + (cls.__name__, dec, type(dec).__name__)) + if not dec.is_finite(): + # Catches infinities and nans. + raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__)) + sign, digits, exp = dec.as_tuple() + digits = int(''.join(map(str, digits))) + if sign: + digits = -digits + if exp >= 0: + return cls(digits * 10 ** exp) + else: + return cls(digits, 10 ** -exp) + + @classmethod + def from_continued_fraction(cls, seq): + 'Build a Fraction from a continued fraction expessed as a sequence' + n, d = 1, 0 + for e in reversed(seq): + n, d = d, n + n += e * d + return cls(n, d) if seq else cls(0) + + def as_continued_fraction(self): + 'Return continued fraction expressed as a list' + n = self.numerator + d = self.denominator + cf = [] + while d: + e = int(n // d) + cf.append(e) + n -= e * d + n, d = d, n + return cf + + def approximate(self, max_denominator): + 'Best rational approximation with a denominator <= max_denominator' + # XXX First cut at algorithm + # Still needs rounding rules as specified at + # http://en.wikipedia.org/wiki/Continued_fraction + if self.denominator <= max_denominator: + return self + cf = self.as_continued_fraction() + result = Fraction(0) + for i in range(1, len(cf)): + new = self.from_continued_fraction(cf[:i]) + if new.denominator > max_denominator: + break + result = new + return result + + @property + def numerator(a): + return a._numerator + + @property + def denominator(a): + return a._denominator + + def __repr__(self): + """repr(self)""" + return ('Fraction(%r,%r)' % (self.numerator, self.denominator)) + + def __str__(self): + """str(self)""" + if self.denominator == 1: + return str(self.numerator) + else: + return '%s/%s' % (self.numerator, self.denominator) + + def _operator_fallbacks(monomorphic_operator, fallback_operator): + """Generates forward and reverse operators given a purely-rational + operator and a function from the operator module. + + Use this like: + __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op) + + In general, we want to implement the arithmetic operations so + that mixed-mode operations either call an implementation whose + author knew about the types of both arguments, or convert both + to the nearest built in type and do the operation there. In + Fraction, that means that we define __add__ and __radd__ as: + + def __add__(self, other): + # Both types have numerators/denominator attributes, + # so do the operation directly + if isinstance(other, (int, Fraction)): + return Fraction(self.numerator * other.denominator + + other.numerator * self.denominator, + self.denominator * other.denominator) + # float and complex don't have those operations, but we + # know about those types, so special case them. + elif isinstance(other, float): + return float(self) + other + elif isinstance(other, complex): + return complex(self) + other + # Let the other type take over. + return NotImplemented + + def __radd__(self, other): + # radd handles more types than add because there's + # nothing left to fall back to. + if isinstance(other, numbers.Rational): + return Fraction(self.numerator * other.denominator + + other.numerator * self.denominator, + self.denominator * other.denominator) + elif isinstance(other, Real): + return float(other) + float(self) + elif isinstance(other, Complex): + return complex(other) + complex(self) + return NotImplemented + + + There are 5 different cases for a mixed-type addition on + Fraction. I'll refer to all of the above code that doesn't + refer to Fraction, float, or complex as "boilerplate". 'r' + will be an instance of Fraction, which is a subtype of + Rational (r : Fraction <: Rational), and b : B <: + Complex. The first three involve 'r + b': + + 1. If B <: Fraction, int, float, or complex, we handle + that specially, and all is well. + 2. If Fraction falls back to the boilerplate code, and it + were to return a value from __add__, we'd miss the + possibility that B defines a more intelligent __radd__, + so the boilerplate should return NotImplemented from + __add__. In particular, we don't handle Rational + here, even though we could get an exact answer, in case + the other type wants to do something special. + 3. If B <: Fraction, Python tries B.__radd__ before + Fraction.__add__. This is ok, because it was + implemented with knowledge of Fraction, so it can + handle those instances before delegating to Real or + Complex. + + The next two situations describe 'b + r'. We assume that b + didn't know about Fraction in its implementation, and that it + uses similar boilerplate code: + + 4. If B <: Rational, then __radd_ converts both to the + builtin rational type (hey look, that's us) and + proceeds. + 5. Otherwise, __radd__ tries to find the nearest common + base ABC, and fall back to its builtin type. Since this + class doesn't subclass a concrete type, there's no + implementation to fall back to, so we need to try as + hard as possible to return an actual value, or the user + will get a TypeError. + + """ + def forward(a, b): + if isinstance(b, (int, Fraction)): + return monomorphic_operator(a, b) + elif isinstance(b, float): + return fallback_operator(float(a), b) + elif isinstance(b, complex): + return fallback_operator(complex(a), b) + else: + return NotImplemented + forward.__name__ = '__' + fallback_operator.__name__ + '__' + forward.__doc__ = monomorphic_operator.__doc__ + + def reverse(b, a): + if isinstance(a, numbers.Rational): + # Includes ints. + return monomorphic_operator(a, b) + elif isinstance(a, numbers.Real): + return fallback_operator(float(a), float(b)) + elif isinstance(a, numbers.Complex): + return fallback_operator(complex(a), complex(b)) + else: + return NotImplemented + reverse.__name__ = '__r' + fallback_operator.__name__ + '__' + reverse.__doc__ = monomorphic_operator.__doc__ + + return forward, reverse + + def _add(a, b): + """a + b""" + return Fraction(a.numerator * b.denominator + + b.numerator * a.denominator, + a.denominator * b.denominator) + + __add__, __radd__ = _operator_fallbacks(_add, operator.add) + + def _sub(a, b): + """a - b""" + return Fraction(a.numerator * b.denominator - + b.numerator * a.denominator, + a.denominator * b.denominator) + + __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub) + + def _mul(a, b): + """a * b""" + return Fraction(a.numerator * b.numerator, a.denominator * b.denominator) + + __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) + + def _div(a, b): + """a / b""" + return Fraction(a.numerator * b.denominator, + a.denominator * b.numerator) + + __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) + + def __floordiv__(a, b): + """a // b""" + return math.floor(a / b) + + def __rfloordiv__(b, a): + """a // b""" + return math.floor(a / b) + + def __mod__(a, b): + """a % b""" + div = a // b + return a - b * div + + def __rmod__(b, a): + """a % b""" + div = a // b + return a - b * div + + def __pow__(a, b): + """a ** b + + If b is not an integer, the result will be a float or complex + since roots are generally irrational. If b is an integer, the + result will be rational. + + """ + if isinstance(b, numbers.Rational): + if b.denominator == 1: + power = b.numerator + if power >= 0: + return Fraction(a.numerator ** power, + a.denominator ** power) + else: + return Fraction(a.denominator ** -power, + a.numerator ** -power) + else: + # A fractional power will generally produce an + # irrational number. + return float(a) ** float(b) + else: + return float(a) ** b + + def __rpow__(b, a): + """a ** b""" + if b.denominator == 1 and b.numerator >= 0: + # If a is an int, keep it that way if possible. + return a ** b.numerator + + if isinstance(a, numbers.Rational): + return Fraction(a.numerator, a.denominator) ** b + + if b.denominator == 1: + return a ** b.numerator + + return a ** float(b) + + def __pos__(a): + """+a: Coerces a subclass instance to Fraction""" + return Fraction(a.numerator, a.denominator) + + def __neg__(a): + """-a""" + return Fraction(-a.numerator, a.denominator) + + def __abs__(a): + """abs(a)""" + return Fraction(abs(a.numerator), a.denominator) + + def __trunc__(a): + """trunc(a)""" + if a.numerator < 0: + return -(-a.numerator // a.denominator) + else: + return a.numerator // a.denominator + + def __floor__(a): + """Will be math.floor(a) in 3.0.""" + return a.numerator // a.denominator + + def __ceil__(a): + """Will be math.ceil(a) in 3.0.""" + # The negations cleverly convince floordiv to return the ceiling. + return -(-a.numerator // a.denominator) + + def __round__(self, ndigits=None): + """Will be round(self, ndigits) in 3.0. + + Rounds half toward even. + """ + if ndigits is None: + floor, remainder = divmod(self.numerator, self.denominator) + if remainder * 2 < self.denominator: + return floor + elif remainder * 2 > self.denominator: + return floor + 1 + # Deal with the half case: + elif floor % 2 == 0: + return floor + else: + return floor + 1 + shift = 10**abs(ndigits) + # See _operator_fallbacks.forward to check that the results of + # these operations will always be Fraction and therefore have + # round(). + if ndigits > 0: + return Fraction(round(self * shift), shift) + else: + return Fraction(round(self / shift) * shift) + + def __hash__(self): + """hash(self) + + Tricky because values that are exactly representable as a + float must have the same hash as that float. + + """ + # XXX since this method is expensive, consider caching the result + if self.denominator == 1: + # Get integers right. + return hash(self.numerator) + # Expensive check, but definitely correct. + if self == float(self): + return hash(float(self)) + else: + # Use tuple's hash to avoid a high collision rate on + # simple fractions. + return hash((self.numerator, self.denominator)) + + def __eq__(a, b): + """a == b""" + if isinstance(b, numbers.Rational): + return (a.numerator == b.numerator and + a.denominator == b.denominator) + if isinstance(b, numbers.Complex) and b.imag == 0: + b = b.real + if isinstance(b, float): + return a == a.from_float(b) + else: + # XXX: If b.__eq__ is implemented like this method, it may + # give the wrong answer after float(a) changes a's + # value. Better ways of doing this are welcome. + return float(a) == b + + def _subtractAndCompareToZero(a, b, op): + """Helper function for comparison operators. + + Subtracts b from a, exactly if possible, and compares the + result with 0 using op, in such a way that the comparison + won't recurse. If the difference raises a TypeError, returns + NotImplemented instead. + + """ + if isinstance(b, numbers.Complex) and b.imag == 0: + b = b.real + if isinstance(b, float): + b = a.from_float(b) + try: + # XXX: If b <: Real but not <: Rational, this is likely + # to fall back to a float. If the actual values differ by + # less than MIN_FLOAT, this could falsely call them equal, + # which would make <= inconsistent with ==. Better ways of + # doing this are welcome. + diff = a - b + except TypeError: + return NotImplemented + if isinstance(diff, numbers.Rational): + return op(diff.numerator, 0) + return op(diff, 0) + + def __lt__(a, b): + """a < b""" + return a._subtractAndCompareToZero(b, operator.lt) + + def __gt__(a, b): + """a > b""" + return a._subtractAndCompareToZero(b, operator.gt) + + def __le__(a, b): + """a <= b""" + return a._subtractAndCompareToZero(b, operator.le) + + def __ge__(a, b): + """a >= b""" + return a._subtractAndCompareToZero(b, operator.ge) + + def __bool__(a): + """a != 0""" + return a.numerator != 0 + + # support for pickling, copy, and deepcopy + + def __reduce__(self): + return (self.__class__, (str(self),)) + + def __copy__(self): + if type(self) == Fraction: + return self # I'm immutable; therefore I am my own clone + return self.__class__(self.numerator, self.denominator) + + def __deepcopy__(self, memo): + if type(self) == Fraction: + return self # My components are also immutable + return self.__class__(self.numerator, self.denominator) diff --git a/Lib/pickletools.py b/Lib/pickletools.py index 0665cd0..1b6967a 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -14,9 +14,7 @@ import codecs import pickle import re -__all__ = ['dis', - 'genops', - ] +__all__ = ['dis', 'genops', 'optimize'] bytes_types = pickle.bytes_types @@ -1836,6 +1834,33 @@ def genops(pickle): break ############################################################################## +# A pickle optimizer. + +def optimize(p): + 'Optimize a pickle string by removing unused PUT opcodes' + gets = set() # set of args used by a GET opcode + puts = [] # (arg, startpos, stoppos) for the PUT opcodes + prevpos = None # set to pos if previous opcode was a PUT + for opcode, arg, pos in genops(p): + if prevpos is not None: + puts.append((prevarg, prevpos, pos)) + prevpos = None + if 'PUT' in opcode.name: + prevarg, prevpos = arg, pos + elif 'GET' in opcode.name: + gets.add(arg) + + # Copy the pickle string except for PUTS without a corresponding GET + s = [] + i = 0 + for arg, start, stop in puts: + j = stop if (arg in gets) else start + s.append(p[i:j]) + i = stop + s.append(p[i:]) + return ''.join(s) + +############################################################################## # A symbolic pickle disassembler. def dis(pickle, out=None, memo=None, indentlevel=4): diff --git a/Lib/rational.py b/Lib/rational.py deleted file mode 100755 index 6002964..0000000 --- a/Lib/rational.py +++ /dev/null @@ -1,536 +0,0 @@ -# Originally contributed by Sjoerd Mullender. -# Significantly modified by Jeffrey Yasskin . - -"""Rational, infinite-precision, real numbers.""" - -import math -import numbers -import operator -import re - -__all__ = ["Rational"] - -RationalAbc = numbers.Rational - - -def gcd(a, b): - """Calculate the Greatest Common Divisor of a and b. - - Unless b==0, the result will have the same sign as b (so that when - b is divided by it, the result comes out positive). - """ - while b: - a, b = b, a%b - return a - - -_RATIONAL_FORMAT = re.compile(r""" - \A\s* # optional whitespace at the start, then - (?P[-+]?) # an optional sign, then - (?=\d|\.\d) # lookahead for digit or .digit - (?P\d*) # numerator (possibly empty) - (?: # followed by an optional - /(?P\d+) # / and denominator - | # or - \.(?P\d*) # decimal point and fractional part - )? - \s*\Z # and optional whitespace to finish -""", re.VERBOSE) - - -class Rational(RationalAbc): - """This class implements rational numbers. - - Rational(8, 6) will produce a rational number equivalent to - 4/3. Both arguments must be Integral. The numerator defaults to 0 - and the denominator defaults to 1 so that Rational(3) == 3 and - Rational() == 0. - - Rationals can also be constructed from strings of the form - '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces. - - """ - - __slots__ = ('_numerator', '_denominator') - - # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=1): - """Constructs a Rational. - - Takes a string like '3/2' or '1.5', another Rational, or a - numerator/denominator pair. - - """ - self = super(Rational, cls).__new__(cls) - - if denominator == 1: - if isinstance(numerator, str): - # Handle construction from strings. - input = numerator - m = _RATIONAL_FORMAT.match(input) - if m is None: - raise ValueError('Invalid literal for Rational: ' + input) - numerator = m.group('num') - decimal = m.group('decimal') - if decimal: - # The literal is a decimal number. - numerator = int(numerator + decimal) - denominator = 10**len(decimal) - else: - # The literal is an integer or fraction. - numerator = int(numerator) - # Default denominator to 1. - denominator = int(m.group('denom') or 1) - - if m.group('sign') == '-': - numerator = -numerator - - elif (not isinstance(numerator, numbers.Integral) and - isinstance(numerator, RationalAbc)): - # Handle copies from other rationals. - other_rational = numerator - numerator = other_rational.numerator - denominator = other_rational.denominator - - if (not isinstance(numerator, numbers.Integral) or - not isinstance(denominator, numbers.Integral)): - raise TypeError("Rational(%(numerator)s, %(denominator)s):" - " Both arguments must be integral." % locals()) - - if denominator == 0: - raise ZeroDivisionError('Rational(%s, 0)' % numerator) - - g = gcd(numerator, denominator) - self._numerator = int(numerator // g) - self._denominator = int(denominator // g) - return self - - @classmethod - def from_float(cls, f): - """Converts a finite float to a rational number, exactly. - - Beware that Rational.from_float(0.3) != Rational(3, 10). - - """ - if not isinstance(f, float): - raise TypeError("%s.from_float() only takes floats, not %r (%s)" % - (cls.__name__, f, type(f).__name__)) - if math.isnan(f) or math.isinf(f): - raise TypeError("Cannot convert %r to %s." % (f, cls.__name__)) - return cls(*f.as_integer_ratio()) - - @classmethod - def from_decimal(cls, dec): - """Converts a finite Decimal instance to a rational number, exactly.""" - from decimal import Decimal - if not isinstance(dec, Decimal): - raise TypeError( - "%s.from_decimal() only takes Decimals, not %r (%s)" % - (cls.__name__, dec, type(dec).__name__)) - if not dec.is_finite(): - # Catches infinities and nans. - raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__)) - sign, digits, exp = dec.as_tuple() - digits = int(''.join(map(str, digits))) - if sign: - digits = -digits - if exp >= 0: - return cls(digits * 10 ** exp) - else: - return cls(digits, 10 ** -exp) - - @classmethod - def from_continued_fraction(cls, seq): - 'Build a Rational from a continued fraction expessed as a sequence' - n, d = 1, 0 - for e in reversed(seq): - n, d = d, n - n += e * d - return cls(n, d) if seq else cls(0) - - def as_continued_fraction(self): - 'Return continued fraction expressed as a list' - n = self.numerator - d = self.denominator - cf = [] - while d: - e = int(n // d) - cf.append(e) - n -= e * d - n, d = d, n - return cf - - def approximate(self, max_denominator): - 'Best rational approximation with a denominator <= max_denominator' - # XXX First cut at algorithm - # Still needs rounding rules as specified at - # http://en.wikipedia.org/wiki/Continued_fraction - if self.denominator <= max_denominator: - return self - cf = self.as_continued_fraction() - result = Rational(0) - for i in range(1, len(cf)): - new = self.from_continued_fraction(cf[:i]) - if new.denominator > max_denominator: - break - result = new - return result - - @property - def numerator(a): - return a._numerator - - @property - def denominator(a): - return a._denominator - - def __repr__(self): - """repr(self)""" - return ('Rational(%r,%r)' % (self.numerator, self.denominator)) - - def __str__(self): - """str(self)""" - if self.denominator == 1: - return str(self.numerator) - else: - return '%s/%s' % (self.numerator, self.denominator) - - def _operator_fallbacks(monomorphic_operator, fallback_operator): - """Generates forward and reverse operators given a purely-rational - operator and a function from the operator module. - - Use this like: - __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op) - - In general, we want to implement the arithmetic operations so - that mixed-mode operations either call an implementation whose - author knew about the types of both arguments, or convert both - to the nearest built in type and do the operation there. In - Rational, that means that we define __add__ and __radd__ as: - - def __add__(self, other): - # Both types have numerators/denominator attributes, - # so do the operation directly - if isinstance(other, (int, Rational)): - return Rational(self.numerator * other.denominator + - other.numerator * self.denominator, - self.denominator * other.denominator) - # float and complex don't have those operations, but we - # know about those types, so special case them. - elif isinstance(other, float): - return float(self) + other - elif isinstance(other, complex): - return complex(self) + other - # Let the other type take over. - return NotImplemented - - def __radd__(self, other): - # radd handles more types than add because there's - # nothing left to fall back to. - if isinstance(other, RationalAbc): - return Rational(self.numerator * other.denominator + - other.numerator * self.denominator, - self.denominator * other.denominator) - elif isinstance(other, Real): - return float(other) + float(self) - elif isinstance(other, Complex): - return complex(other) + complex(self) - return NotImplemented - - - There are 5 different cases for a mixed-type addition on - Rational. I'll refer to all of the above code that doesn't - refer to Rational, float, or complex as "boilerplate". 'r' - will be an instance of Rational, which is a subtype of - RationalAbc (r : Rational <: RationalAbc), and b : B <: - Complex. The first three involve 'r + b': - - 1. If B <: Rational, int, float, or complex, we handle - that specially, and all is well. - 2. If Rational falls back to the boilerplate code, and it - were to return a value from __add__, we'd miss the - possibility that B defines a more intelligent __radd__, - so the boilerplate should return NotImplemented from - __add__. In particular, we don't handle RationalAbc - here, even though we could get an exact answer, in case - the other type wants to do something special. - 3. If B <: Rational, Python tries B.__radd__ before - Rational.__add__. This is ok, because it was - implemented with knowledge of Rational, so it can - handle those instances before delegating to Real or - Complex. - - The next two situations describe 'b + r'. We assume that b - didn't know about Rational in its implementation, and that it - uses similar boilerplate code: - - 4. If B <: RationalAbc, then __radd_ converts both to the - builtin rational type (hey look, that's us) and - proceeds. - 5. Otherwise, __radd__ tries to find the nearest common - base ABC, and fall back to its builtin type. Since this - class doesn't subclass a concrete type, there's no - implementation to fall back to, so we need to try as - hard as possible to return an actual value, or the user - will get a TypeError. - - """ - def forward(a, b): - if isinstance(b, (int, Rational)): - return monomorphic_operator(a, b) - elif isinstance(b, float): - return fallback_operator(float(a), b) - elif isinstance(b, complex): - return fallback_operator(complex(a), b) - else: - return NotImplemented - forward.__name__ = '__' + fallback_operator.__name__ + '__' - forward.__doc__ = monomorphic_operator.__doc__ - - def reverse(b, a): - if isinstance(a, RationalAbc): - # Includes ints. - return monomorphic_operator(a, b) - elif isinstance(a, numbers.Real): - return fallback_operator(float(a), float(b)) - elif isinstance(a, numbers.Complex): - return fallback_operator(complex(a), complex(b)) - else: - return NotImplemented - reverse.__name__ = '__r' + fallback_operator.__name__ + '__' - reverse.__doc__ = monomorphic_operator.__doc__ - - return forward, reverse - - def _add(a, b): - """a + b""" - return Rational(a.numerator * b.denominator + - b.numerator * a.denominator, - a.denominator * b.denominator) - - __add__, __radd__ = _operator_fallbacks(_add, operator.add) - - def _sub(a, b): - """a - b""" - return Rational(a.numerator * b.denominator - - b.numerator * a.denominator, - a.denominator * b.denominator) - - __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub) - - def _mul(a, b): - """a * b""" - return Rational(a.numerator * b.numerator, a.denominator * b.denominator) - - __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) - - def _div(a, b): - """a / b""" - return Rational(a.numerator * b.denominator, - a.denominator * b.numerator) - - __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) - - def __floordiv__(a, b): - """a // b""" - return math.floor(a / b) - - def __rfloordiv__(b, a): - """a // b""" - return math.floor(a / b) - - def __mod__(a, b): - """a % b""" - div = a // b - return a - b * div - - def __rmod__(b, a): - """a % b""" - div = a // b - return a - b * div - - def __pow__(a, b): - """a ** b - - If b is not an integer, the result will be a float or complex - since roots are generally irrational. If b is an integer, the - result will be rational. - - """ - if isinstance(b, RationalAbc): - if b.denominator == 1: - power = b.numerator - if power >= 0: - return Rational(a.numerator ** power, - a.denominator ** power) - else: - return Rational(a.denominator ** -power, - a.numerator ** -power) - else: - # A fractional power will generally produce an - # irrational number. - return float(a) ** float(b) - else: - return float(a) ** b - - def __rpow__(b, a): - """a ** b""" - if b.denominator == 1 and b.numerator >= 0: - # If a is an int, keep it that way if possible. - return a ** b.numerator - - if isinstance(a, RationalAbc): - return Rational(a.numerator, a.denominator) ** b - - if b.denominator == 1: - return a ** b.numerator - - return a ** float(b) - - def __pos__(a): - """+a: Coerces a subclass instance to Rational""" - return Rational(a.numerator, a.denominator) - - def __neg__(a): - """-a""" - return Rational(-a.numerator, a.denominator) - - def __abs__(a): - """abs(a)""" - return Rational(abs(a.numerator), a.denominator) - - def __trunc__(a): - """trunc(a)""" - if a.numerator < 0: - return -(-a.numerator // a.denominator) - else: - return a.numerator // a.denominator - - def __floor__(a): - """Will be math.floor(a) in 3.0.""" - return a.numerator // a.denominator - - def __ceil__(a): - """Will be math.ceil(a) in 3.0.""" - # The negations cleverly convince floordiv to return the ceiling. - return -(-a.numerator // a.denominator) - - def __round__(self, ndigits=None): - """Will be round(self, ndigits) in 3.0. - - Rounds half toward even. - """ - if ndigits is None: - floor, remainder = divmod(self.numerator, self.denominator) - if remainder * 2 < self.denominator: - return floor - elif remainder * 2 > self.denominator: - return floor + 1 - # Deal with the half case: - elif floor % 2 == 0: - return floor - else: - return floor + 1 - shift = 10**abs(ndigits) - # See _operator_fallbacks.forward to check that the results of - # these operations will always be Rational and therefore have - # round(). - if ndigits > 0: - return Rational(round(self * shift), shift) - else: - return Rational(round(self / shift) * shift) - - def __hash__(self): - """hash(self) - - Tricky because values that are exactly representable as a - float must have the same hash as that float. - - """ - # XXX since this method is expensive, consider caching the result - if self.denominator == 1: - # Get integers right. - return hash(self.numerator) - # Expensive check, but definitely correct. - if self == float(self): - return hash(float(self)) - else: - # Use tuple's hash to avoid a high collision rate on - # simple fractions. - return hash((self.numerator, self.denominator)) - - def __eq__(a, b): - """a == b""" - if isinstance(b, RationalAbc): - return (a.numerator == b.numerator and - a.denominator == b.denominator) - if isinstance(b, numbers.Complex) and b.imag == 0: - b = b.real - if isinstance(b, float): - return a == a.from_float(b) - else: - # XXX: If b.__eq__ is implemented like this method, it may - # give the wrong answer after float(a) changes a's - # value. Better ways of doing this are welcome. - return float(a) == b - - def _subtractAndCompareToZero(a, b, op): - """Helper function for comparison operators. - - Subtracts b from a, exactly if possible, and compares the - result with 0 using op, in such a way that the comparison - won't recurse. If the difference raises a TypeError, returns - NotImplemented instead. - - """ - if isinstance(b, numbers.Complex) and b.imag == 0: - b = b.real - if isinstance(b, float): - b = a.from_float(b) - try: - # XXX: If b <: Real but not <: RationalAbc, this is likely - # to fall back to a float. If the actual values differ by - # less than MIN_FLOAT, this could falsely call them equal, - # which would make <= inconsistent with ==. Better ways of - # doing this are welcome. - diff = a - b - except TypeError: - return NotImplemented - if isinstance(diff, RationalAbc): - return op(diff.numerator, 0) - return op(diff, 0) - - def __lt__(a, b): - """a < b""" - return a._subtractAndCompareToZero(b, operator.lt) - - def __gt__(a, b): - """a > b""" - return a._subtractAndCompareToZero(b, operator.gt) - - def __le__(a, b): - """a <= b""" - return a._subtractAndCompareToZero(b, operator.le) - - def __ge__(a, b): - """a >= b""" - return a._subtractAndCompareToZero(b, operator.ge) - - def __bool__(a): - """a != 0""" - return a.numerator != 0 - - # support for pickling, copy, and deepcopy - - def __reduce__(self): - return (self.__class__, (str(self),)) - - def __copy__(self): - if type(self) == Rational: - return self # I'm immutable; therefore I am my own clone - return self.__class__(self.numerator, self.denominator) - - def __deepcopy__(self, memo): - if type(self) == Rational: - return self # My components are also immutable - return self.__class__(self.numerator, self.denominator) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b17133a..f781db3 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -5,7 +5,7 @@ from test.test_support import fcmp, TESTFN, unlink, run_unittest, \ run_with_locale from operator import neg -import sys, warnings, random, collections, io, rational +import sys, warnings, random, collections, io, rational, fractions warnings.filterwarnings("ignore", "hex../oct.. of negative int", FutureWarning, __name__) warnings.filterwarnings("ignore", "integer argument expected", @@ -607,7 +607,7 @@ class BuiltinTest(unittest.TestCase): n, d = f.as_integer_ratio() self.assertEqual(float(n).__truediv__(d), f) - R = rational.Rational + R = fractions.Fraction self.assertEqual(R(0, 1), R(*float(0.0).as_integer_ratio())) self.assertEqual(R(5, 2), diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py new file mode 100644 index 0000000..00fd549 --- /dev/null +++ b/Lib/test/test_fractions.py @@ -0,0 +1,427 @@ +"""Tests for Lib/fractions.py.""" + +from decimal import Decimal +from test.test_support import run_unittest, verbose +import math +import operator +import fractions +import unittest +from copy import copy, deepcopy +from pickle import dumps, loads +R = fractions.Fraction +gcd = fractions.gcd + + +class GcdTest(unittest.TestCase): + + def testMisc(self): + self.assertEquals(0, gcd(0, 0)) + self.assertEquals(1, gcd(1, 0)) + self.assertEquals(-1, gcd(-1, 0)) + self.assertEquals(1, gcd(0, 1)) + self.assertEquals(-1, gcd(0, -1)) + self.assertEquals(1, gcd(7, 1)) + self.assertEquals(-1, gcd(7, -1)) + self.assertEquals(1, gcd(-23, 15)) + self.assertEquals(12, gcd(120, 84)) + self.assertEquals(-12, gcd(84, -120)) + + +def _components(r): + return (r.numerator, r.denominator) + + +class FractionTest(unittest.TestCase): + + def assertTypedEquals(self, expected, actual): + """Asserts that both the types and values are the same.""" + self.assertEquals(type(expected), type(actual)) + self.assertEquals(expected, actual) + + def assertRaisesMessage(self, exc_type, message, + callable, *args, **kwargs): + """Asserts that callable(*args, **kwargs) raises exc_type(message).""" + try: + callable(*args, **kwargs) + except exc_type as e: + self.assertEquals(message, str(e)) + else: + self.fail("%s not raised" % exc_type.__name__) + + def testInit(self): + self.assertEquals((0, 1), _components(R())) + self.assertEquals((7, 1), _components(R(7))) + self.assertEquals((7, 3), _components(R(R(7, 3)))) + + self.assertEquals((-1, 1), _components(R(-1, 1))) + self.assertEquals((-1, 1), _components(R(1, -1))) + self.assertEquals((1, 1), _components(R(-2, -2))) + self.assertEquals((1, 2), _components(R(5, 10))) + self.assertEquals((7, 15), _components(R(7, 15))) + self.assertEquals((10**23, 1), _components(R(10**23))) + + self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)", + R, 12, 0) + self.assertRaises(TypeError, R, 1.5) + self.assertRaises(TypeError, R, 1.5 + 3j) + + self.assertRaises(TypeError, R, R(1, 2), 3) + self.assertRaises(TypeError, R, "3/2", 3) + + def testFromString(self): + self.assertEquals((5, 1), _components(R("5"))) + self.assertEquals((3, 2), _components(R("3/2"))) + self.assertEquals((3, 2), _components(R(" \n +3/2"))) + self.assertEquals((-3, 2), _components(R("-3/2 "))) + self.assertEquals((3, 2), _components(R(" 03/02 \n "))) + self.assertEquals((3, 2), _components(R(" 03/02 \n "))) + self.assertEquals((16, 5), _components(R(" 3.2 "))) + self.assertEquals((-16, 5), _components(R(" -3.2 "))) + self.assertEquals((-3, 1), _components(R(" -3. "))) + self.assertEquals((3, 5), _components(R(" .6 "))) + + self.assertRaisesMessage( + ZeroDivisionError, "Fraction(3, 0)", + R, "3/0") + self.assertRaisesMessage( + ValueError, "Invalid literal for Fraction: 3/", + R, "3/") + self.assertRaisesMessage( + ValueError, "Invalid literal for Fraction: 3 /2", + R, "3 /2") + self.assertRaisesMessage( + # Denominators don't need a sign. + ValueError, "Invalid literal for Fraction: 3/+2", + R, "3/+2") + self.assertRaisesMessage( + # Imitate float's parsing. + ValueError, "Invalid literal for Fraction: + 3/2", + R, "+ 3/2") + self.assertRaisesMessage( + # Avoid treating '.' as a regex special character. + ValueError, "Invalid literal for Fraction: 3a2", + R, "3a2") + self.assertRaisesMessage( + # Only parse ordinary decimals, not scientific form. + ValueError, "Invalid literal for Fraction: 3.2e4", + R, "3.2e4") + self.assertRaisesMessage( + # Don't accept combinations of decimals and rationals. + ValueError, "Invalid literal for Fraction: 3/7.2", + R, "3/7.2") + self.assertRaisesMessage( + # Don't accept combinations of decimals and rationals. + ValueError, "Invalid literal for Fraction: 3.2/7", + R, "3.2/7") + self.assertRaisesMessage( + # Allow 3. and .3, but not . + ValueError, "Invalid literal for Fraction: .", + R, ".") + + def testImmutable(self): + r = R(7, 3) + r.__init__(2, 15) + self.assertEquals((7, 3), _components(r)) + + self.assertRaises(AttributeError, setattr, r, 'numerator', 12) + self.assertRaises(AttributeError, setattr, r, 'denominator', 6) + self.assertEquals((7, 3), _components(r)) + + # But if you _really_ need to: + r._numerator = 4 + r._denominator = 2 + self.assertEquals((4, 2), _components(r)) + # Which breaks some important operations: + self.assertNotEquals(R(4, 2), r) + + def testFromFloat(self): + self.assertRaisesMessage( + TypeError, "Fraction.from_float() only takes floats, not 3 (int)", + R.from_float, 3) + + self.assertEquals((0, 1), _components(R.from_float(-0.0))) + self.assertEquals((10, 1), _components(R.from_float(10.0))) + self.assertEquals((-5, 2), _components(R.from_float(-2.5))) + self.assertEquals((99999999999999991611392, 1), + _components(R.from_float(1e23))) + self.assertEquals(float(10**23), float(R.from_float(1e23))) + self.assertEquals((3602879701896397, 1125899906842624), + _components(R.from_float(3.2))) + self.assertEquals(3.2, float(R.from_float(3.2))) + + inf = 1e1000 + nan = inf - inf + self.assertRaisesMessage( + TypeError, "Cannot convert inf to Fraction.", + R.from_float, inf) + self.assertRaisesMessage( + TypeError, "Cannot convert -inf to Fraction.", + R.from_float, -inf) + self.assertRaisesMessage( + TypeError, "Cannot convert nan to Fraction.", + R.from_float, nan) + + def testFromDecimal(self): + self.assertRaisesMessage( + TypeError, + "Fraction.from_decimal() only takes Decimals, not 3 (int)", + R.from_decimal, 3) + self.assertEquals(R(0), R.from_decimal(Decimal("-0"))) + self.assertEquals(R(5, 10), R.from_decimal(Decimal("0.5"))) + self.assertEquals(R(5, 1000), R.from_decimal(Decimal("5e-3"))) + self.assertEquals(R(5000), R.from_decimal(Decimal("5e3"))) + self.assertEquals(1 - R(1, 10**30), + R.from_decimal(Decimal("0." + "9" * 30))) + + self.assertRaisesMessage( + TypeError, "Cannot convert Infinity to Fraction.", + R.from_decimal, Decimal("inf")) + self.assertRaisesMessage( + TypeError, "Cannot convert -Infinity to Fraction.", + R.from_decimal, Decimal("-inf")) + self.assertRaisesMessage( + TypeError, "Cannot convert NaN to Fraction.", + R.from_decimal, Decimal("nan")) + self.assertRaisesMessage( + TypeError, "Cannot convert sNaN to Fraction.", + R.from_decimal, Decimal("snan")) + + def testFromContinuedFraction(self): + self.assertRaises(TypeError, R.from_continued_fraction, None) + phi = R.from_continued_fraction([1]*100) + self.assertEquals(round(phi - (1 + 5 ** 0.5) / 2, 10), 0.0) + + minusphi = R.from_continued_fraction([-1]*100) + self.assertEquals(round(minusphi + (1 + 5 ** 0.5) / 2, 10), 0.0) + + self.assertEquals(R.from_continued_fraction([0]), R(0)) + self.assertEquals(R.from_continued_fraction([]), R(0)) + + def testAsContinuedFraction(self): + self.assertEqual(R.from_float(math.pi).as_continued_fraction()[:15], + [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3]) + self.assertEqual(R.from_float(-math.pi).as_continued_fraction()[:16], + [-4, 1, 6, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3]) + self.assertEqual(R(0).as_continued_fraction(), [0]) + + def testApproximateFrom(self): + self.assertEqual(R.from_float(math.pi).approximate(10000), R(355, 113)) + self.assertEqual(R.from_float(-math.pi).approximate(10000), R(-355, 113)) + self.assertEqual(R.from_float(0.0).approximate(10000), R(0)) + + def testConversions(self): + self.assertTypedEquals(-1, math.trunc(R(-11, 10))) + self.assertTypedEquals(-2, math.floor(R(-11, 10))) + self.assertTypedEquals(-1, math.ceil(R(-11, 10))) + self.assertTypedEquals(-1, math.ceil(R(-10, 10))) + self.assertTypedEquals(-1, int(R(-11, 10))) + + self.assertTypedEquals(0, round(R(-1, 10))) + self.assertTypedEquals(0, round(R(-5, 10))) + self.assertTypedEquals(-2, round(R(-15, 10))) + self.assertTypedEquals(-1, round(R(-7, 10))) + + self.assertEquals(False, bool(R(0, 1))) + self.assertEquals(True, bool(R(3, 2))) + self.assertTypedEquals(0.1, float(R(1, 10))) + + # Check that __float__ isn't implemented by converting the + # numerator and denominator to float before dividing. + self.assertRaises(OverflowError, float, int('2'*400+'7')) + self.assertAlmostEquals(2.0/3, + float(R(int('2'*400+'7'), int('3'*400+'1')))) + + self.assertTypedEquals(0.1+0j, complex(R(1,10))) + + def testRound(self): + self.assertTypedEquals(R(-200), round(R(-150), -2)) + self.assertTypedEquals(R(-200), round(R(-250), -2)) + self.assertTypedEquals(R(30), round(R(26), -1)) + self.assertTypedEquals(R(-2, 10), round(R(-15, 100), 1)) + self.assertTypedEquals(R(-2, 10), round(R(-25, 100), 1)) + + + def testArithmetic(self): + self.assertEquals(R(1, 2), R(1, 10) + R(2, 5)) + self.assertEquals(R(-3, 10), R(1, 10) - R(2, 5)) + self.assertEquals(R(1, 25), R(1, 10) * R(2, 5)) + self.assertEquals(R(1, 4), R(1, 10) / R(2, 5)) + self.assertTypedEquals(2, R(9, 10) // R(2, 5)) + self.assertTypedEquals(10**23, R(10**23, 1) // R(1)) + self.assertEquals(R(2, 3), R(-7, 3) % R(3, 2)) + self.assertEquals(R(8, 27), R(2, 3) ** R(3)) + self.assertEquals(R(27, 8), R(2, 3) ** R(-3)) + self.assertTypedEquals(2.0, R(4) ** R(1, 2)) + z = pow(R(-1), R(1, 2)) + self.assertAlmostEquals(z.real, 0) + self.assertEquals(z.imag, 1) + + def testMixedArithmetic(self): + self.assertTypedEquals(R(11, 10), R(1, 10) + 1) + self.assertTypedEquals(1.1, R(1, 10) + 1.0) + self.assertTypedEquals(1.1 + 0j, R(1, 10) + (1.0 + 0j)) + self.assertTypedEquals(R(11, 10), 1 + R(1, 10)) + self.assertTypedEquals(1.1, 1.0 + R(1, 10)) + self.assertTypedEquals(1.1 + 0j, (1.0 + 0j) + R(1, 10)) + + self.assertTypedEquals(R(-9, 10), R(1, 10) - 1) + self.assertTypedEquals(-0.9, R(1, 10) - 1.0) + self.assertTypedEquals(-0.9 + 0j, R(1, 10) - (1.0 + 0j)) + self.assertTypedEquals(R(9, 10), 1 - R(1, 10)) + self.assertTypedEquals(0.9, 1.0 - R(1, 10)) + self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - R(1, 10)) + + self.assertTypedEquals(R(1, 10), R(1, 10) * 1) + self.assertTypedEquals(0.1, R(1, 10) * 1.0) + self.assertTypedEquals(0.1 + 0j, R(1, 10) * (1.0 + 0j)) + self.assertTypedEquals(R(1, 10), 1 * R(1, 10)) + self.assertTypedEquals(0.1, 1.0 * R(1, 10)) + self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * R(1, 10)) + + self.assertTypedEquals(R(1, 10), R(1, 10) / 1) + self.assertTypedEquals(0.1, R(1, 10) / 1.0) + self.assertTypedEquals(0.1 + 0j, R(1, 10) / (1.0 + 0j)) + self.assertTypedEquals(R(10, 1), 1 / R(1, 10)) + self.assertTypedEquals(10.0, 1.0 / R(1, 10)) + self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / R(1, 10)) + + self.assertTypedEquals(0, R(1, 10) // 1) + self.assertTypedEquals(0, R(1, 10) // 1.0) + self.assertTypedEquals(10, 1 // R(1, 10)) + self.assertTypedEquals(10**23, 10**22 // R(1, 10)) + self.assertTypedEquals(10, 1.0 // R(1, 10)) + + self.assertTypedEquals(R(1, 10), R(1, 10) % 1) + self.assertTypedEquals(0.1, R(1, 10) % 1.0) + self.assertTypedEquals(R(0, 1), 1 % R(1, 10)) + self.assertTypedEquals(0.0, 1.0 % R(1, 10)) + + # No need for divmod since we don't override it. + + # ** has more interesting conversion rules. + self.assertTypedEquals(R(100, 1), R(1, 10) ** -2) + self.assertTypedEquals(R(100, 1), R(10, 1) ** 2) + self.assertTypedEquals(0.1, R(1, 10) ** 1.0) + self.assertTypedEquals(0.1 + 0j, R(1, 10) ** (1.0 + 0j)) + self.assertTypedEquals(4 , 2 ** R(2, 1)) + z = pow(-1, R(1, 2)) + self.assertAlmostEquals(0, z.real) + self.assertEquals(1, z.imag) + self.assertTypedEquals(R(1, 4) , 2 ** R(-2, 1)) + self.assertTypedEquals(2.0 , 4 ** R(1, 2)) + self.assertTypedEquals(0.25, 2.0 ** R(-2, 1)) + self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** R(1, 10)) + + def testMixingWithDecimal(self): + # Decimal refuses mixed comparisons. + self.assertRaisesMessage( + TypeError, + "unsupported operand type(s) for +: 'Fraction' and 'Decimal'", + operator.add, R(3,11), Decimal('3.1415926')) + self.assertNotEquals(R(5, 2), Decimal('2.5')) + + def testComparisons(self): + self.assertTrue(R(1, 2) < R(2, 3)) + self.assertFalse(R(1, 2) < R(1, 2)) + self.assertTrue(R(1, 2) <= R(2, 3)) + self.assertTrue(R(1, 2) <= R(1, 2)) + self.assertFalse(R(2, 3) <= R(1, 2)) + self.assertTrue(R(1, 2) == R(1, 2)) + self.assertFalse(R(1, 2) == R(1, 3)) + self.assertFalse(R(1, 2) != R(1, 2)) + self.assertTrue(R(1, 2) != R(1, 3)) + + def testMixedLess(self): + self.assertTrue(2 < R(5, 2)) + self.assertFalse(2 < R(4, 2)) + self.assertTrue(R(5, 2) < 3) + self.assertFalse(R(4, 2) < 2) + + self.assertTrue(R(1, 2) < 0.6) + self.assertFalse(R(1, 2) < 0.4) + self.assertTrue(0.4 < R(1, 2)) + self.assertFalse(0.5 < R(1, 2)) + + def testMixedLessEqual(self): + self.assertTrue(0.5 <= R(1, 2)) + self.assertFalse(0.6 <= R(1, 2)) + self.assertTrue(R(1, 2) <= 0.5) + self.assertFalse(R(1, 2) <= 0.4) + self.assertTrue(2 <= R(4, 2)) + self.assertFalse(2 <= R(3, 2)) + self.assertTrue(R(4, 2) <= 2) + self.assertFalse(R(5, 2) <= 2) + + def testBigFloatComparisons(self): + # Because 10**23 can't be represented exactly as a float: + self.assertFalse(R(10**23) == float(10**23)) + # The first test demonstrates why these are important. + self.assertFalse(1e23 < float(R(math.trunc(1e23) + 1))) + self.assertTrue(1e23 < R(math.trunc(1e23) + 1)) + self.assertFalse(1e23 <= R(math.trunc(1e23) - 1)) + self.assertTrue(1e23 > R(math.trunc(1e23) - 1)) + self.assertFalse(1e23 >= R(math.trunc(1e23) + 1)) + + def testBigComplexComparisons(self): + self.assertFalse(R(10**23) == complex(10**23)) + self.assertTrue(R(10**23) > complex(10**23)) + self.assertFalse(R(10**23) <= complex(10**23)) + + def testMixedEqual(self): + self.assertTrue(0.5 == R(1, 2)) + self.assertFalse(0.6 == R(1, 2)) + self.assertTrue(R(1, 2) == 0.5) + self.assertFalse(R(1, 2) == 0.4) + self.assertTrue(2 == R(4, 2)) + self.assertFalse(2 == R(3, 2)) + self.assertTrue(R(4, 2) == 2) + self.assertFalse(R(5, 2) == 2) + + def testStringification(self): + self.assertEquals("Fraction(7,3)", repr(R(7, 3))) + self.assertEquals("7/3", str(R(7, 3))) + self.assertEquals("7", str(R(7, 1))) + + def testHash(self): + self.assertEquals(hash(2.5), hash(R(5, 2))) + self.assertEquals(hash(10**50), hash(R(10**50))) + self.assertNotEquals(hash(float(10**23)), hash(R(10**23))) + + def testApproximatePi(self): + # Algorithm borrowed from + # http://docs.python.org/lib/decimal-recipes.html + three = R(3) + lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24 + while abs(s - lasts) > R(1, 10**9): + lasts = s + n, na = n+na, na+8 + d, da = d+da, da+32 + t = (t * n) / d + s += t + self.assertAlmostEquals(math.pi, s) + + def testApproximateCos1(self): + # Algorithm borrowed from + # http://docs.python.org/lib/decimal-recipes.html + x = R(1) + i, lasts, s, fact, num, sign = 0, 0, R(1), 1, 1, 1 + while abs(s - lasts) > R(1, 10**9): + lasts = s + i += 2 + fact *= i * (i-1) + num *= x * x + sign *= -1 + s += num / fact * sign + self.assertAlmostEquals(math.cos(1), s) + + def test_copy_deepcopy_pickle(self): + r = R(13, 7) + self.assertEqual(r, loads(dumps(r))) + self.assertEqual(id(r), id(copy(r))) + self.assertEqual(id(r), id(deepcopy(r))) + +def test_main(): + run_unittest(FractionTest, GcdTest) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_rational.py b/Lib/test/test_rational.py deleted file mode 100644 index 92d8a14..0000000 --- a/Lib/test/test_rational.py +++ /dev/null @@ -1,427 +0,0 @@ -"""Tests for Lib/rational.py.""" - -from decimal import Decimal -from test.test_support import run_unittest, verbose -import math -import operator -import rational -import unittest -from copy import copy, deepcopy -from pickle import dumps, loads -R = rational.Rational -gcd = rational.gcd - - -class GcdTest(unittest.TestCase): - - def testMisc(self): - self.assertEquals(0, gcd(0, 0)) - self.assertEquals(1, gcd(1, 0)) - self.assertEquals(-1, gcd(-1, 0)) - self.assertEquals(1, gcd(0, 1)) - self.assertEquals(-1, gcd(0, -1)) - self.assertEquals(1, gcd(7, 1)) - self.assertEquals(-1, gcd(7, -1)) - self.assertEquals(1, gcd(-23, 15)) - self.assertEquals(12, gcd(120, 84)) - self.assertEquals(-12, gcd(84, -120)) - - -def _components(r): - return (r.numerator, r.denominator) - - -class RationalTest(unittest.TestCase): - - def assertTypedEquals(self, expected, actual): - """Asserts that both the types and values are the same.""" - self.assertEquals(type(expected), type(actual)) - self.assertEquals(expected, actual) - - def assertRaisesMessage(self, exc_type, message, - callable, *args, **kwargs): - """Asserts that callable(*args, **kwargs) raises exc_type(message).""" - try: - callable(*args, **kwargs) - except exc_type as e: - self.assertEquals(message, str(e)) - else: - self.fail("%s not raised" % exc_type.__name__) - - def testInit(self): - self.assertEquals((0, 1), _components(R())) - self.assertEquals((7, 1), _components(R(7))) - self.assertEquals((7, 3), _components(R(R(7, 3)))) - - self.assertEquals((-1, 1), _components(R(-1, 1))) - self.assertEquals((-1, 1), _components(R(1, -1))) - self.assertEquals((1, 1), _components(R(-2, -2))) - self.assertEquals((1, 2), _components(R(5, 10))) - self.assertEquals((7, 15), _components(R(7, 15))) - self.assertEquals((10**23, 1), _components(R(10**23))) - - self.assertRaisesMessage(ZeroDivisionError, "Rational(12, 0)", - R, 12, 0) - self.assertRaises(TypeError, R, 1.5) - self.assertRaises(TypeError, R, 1.5 + 3j) - - self.assertRaises(TypeError, R, R(1, 2), 3) - self.assertRaises(TypeError, R, "3/2", 3) - - def testFromString(self): - self.assertEquals((5, 1), _components(R("5"))) - self.assertEquals((3, 2), _components(R("3/2"))) - self.assertEquals((3, 2), _components(R(" \n +3/2"))) - self.assertEquals((-3, 2), _components(R("-3/2 "))) - self.assertEquals((3, 2), _components(R(" 03/02 \n "))) - self.assertEquals((3, 2), _components(R(" 03/02 \n "))) - self.assertEquals((16, 5), _components(R(" 3.2 "))) - self.assertEquals((-16, 5), _components(R(" -3.2 "))) - self.assertEquals((-3, 1), _components(R(" -3. "))) - self.assertEquals((3, 5), _components(R(" .6 "))) - - self.assertRaisesMessage( - ZeroDivisionError, "Rational(3, 0)", - R, "3/0") - self.assertRaisesMessage( - ValueError, "Invalid literal for Rational: 3/", - R, "3/") - self.assertRaisesMessage( - ValueError, "Invalid literal for Rational: 3 /2", - R, "3 /2") - self.assertRaisesMessage( - # Denominators don't need a sign. - ValueError, "Invalid literal for Rational: 3/+2", - R, "3/+2") - self.assertRaisesMessage( - # Imitate float's parsing. - ValueError, "Invalid literal for Rational: + 3/2", - R, "+ 3/2") - self.assertRaisesMessage( - # Avoid treating '.' as a regex special character. - ValueError, "Invalid literal for Rational: 3a2", - R, "3a2") - self.assertRaisesMessage( - # Only parse ordinary decimals, not scientific form. - ValueError, "Invalid literal for Rational: 3.2e4", - R, "3.2e4") - self.assertRaisesMessage( - # Don't accept combinations of decimals and rationals. - ValueError, "Invalid literal for Rational: 3/7.2", - R, "3/7.2") - self.assertRaisesMessage( - # Don't accept combinations of decimals and rationals. - ValueError, "Invalid literal for Rational: 3.2/7", - R, "3.2/7") - self.assertRaisesMessage( - # Allow 3. and .3, but not . - ValueError, "Invalid literal for Rational: .", - R, ".") - - def testImmutable(self): - r = R(7, 3) - r.__init__(2, 15) - self.assertEquals((7, 3), _components(r)) - - self.assertRaises(AttributeError, setattr, r, 'numerator', 12) - self.assertRaises(AttributeError, setattr, r, 'denominator', 6) - self.assertEquals((7, 3), _components(r)) - - # But if you _really_ need to: - r._numerator = 4 - r._denominator = 2 - self.assertEquals((4, 2), _components(r)) - # Which breaks some important operations: - self.assertNotEquals(R(4, 2), r) - - def testFromFloat(self): - self.assertRaisesMessage( - TypeError, "Rational.from_float() only takes floats, not 3 (int)", - R.from_float, 3) - - self.assertEquals((0, 1), _components(R.from_float(-0.0))) - self.assertEquals((10, 1), _components(R.from_float(10.0))) - self.assertEquals((-5, 2), _components(R.from_float(-2.5))) - self.assertEquals((99999999999999991611392, 1), - _components(R.from_float(1e23))) - self.assertEquals(float(10**23), float(R.from_float(1e23))) - self.assertEquals((3602879701896397, 1125899906842624), - _components(R.from_float(3.2))) - self.assertEquals(3.2, float(R.from_float(3.2))) - - inf = 1e1000 - nan = inf - inf - self.assertRaisesMessage( - TypeError, "Cannot convert inf to Rational.", - R.from_float, inf) - self.assertRaisesMessage( - TypeError, "Cannot convert -inf to Rational.", - R.from_float, -inf) - self.assertRaisesMessage( - TypeError, "Cannot convert nan to Rational.", - R.from_float, nan) - - def testFromDecimal(self): - self.assertRaisesMessage( - TypeError, - "Rational.from_decimal() only takes Decimals, not 3 (int)", - R.from_decimal, 3) - self.assertEquals(R(0), R.from_decimal(Decimal("-0"))) - self.assertEquals(R(5, 10), R.from_decimal(Decimal("0.5"))) - self.assertEquals(R(5, 1000), R.from_decimal(Decimal("5e-3"))) - self.assertEquals(R(5000), R.from_decimal(Decimal("5e3"))) - self.assertEquals(1 - R(1, 10**30), - R.from_decimal(Decimal("0." + "9" * 30))) - - self.assertRaisesMessage( - TypeError, "Cannot convert Infinity to Rational.", - R.from_decimal, Decimal("inf")) - self.assertRaisesMessage( - TypeError, "Cannot convert -Infinity to Rational.", - R.from_decimal, Decimal("-inf")) - self.assertRaisesMessage( - TypeError, "Cannot convert NaN to Rational.", - R.from_decimal, Decimal("nan")) - self.assertRaisesMessage( - TypeError, "Cannot convert sNaN to Rational.", - R.from_decimal, Decimal("snan")) - - def testFromContinuedFraction(self): - self.assertRaises(TypeError, R.from_continued_fraction, None) - phi = R.from_continued_fraction([1]*100) - self.assertEquals(round(phi - (1 + 5 ** 0.5) / 2, 10), 0.0) - - minusphi = R.from_continued_fraction([-1]*100) - self.assertEquals(round(minusphi + (1 + 5 ** 0.5) / 2, 10), 0.0) - - self.assertEquals(R.from_continued_fraction([0]), R(0)) - self.assertEquals(R.from_continued_fraction([]), R(0)) - - def testAsContinuedFraction(self): - self.assertEqual(R.from_float(math.pi).as_continued_fraction()[:15], - [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3]) - self.assertEqual(R.from_float(-math.pi).as_continued_fraction()[:16], - [-4, 1, 6, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3]) - self.assertEqual(R(0).as_continued_fraction(), [0]) - - def testApproximateFrom(self): - self.assertEqual(R.from_float(math.pi).approximate(10000), R(355, 113)) - self.assertEqual(R.from_float(-math.pi).approximate(10000), R(-355, 113)) - self.assertEqual(R.from_float(0.0).approximate(10000), R(0)) - - def testConversions(self): - self.assertTypedEquals(-1, math.trunc(R(-11, 10))) - self.assertTypedEquals(-2, math.floor(R(-11, 10))) - self.assertTypedEquals(-1, math.ceil(R(-11, 10))) - self.assertTypedEquals(-1, math.ceil(R(-10, 10))) - self.assertTypedEquals(-1, int(R(-11, 10))) - - self.assertTypedEquals(0, round(R(-1, 10))) - self.assertTypedEquals(0, round(R(-5, 10))) - self.assertTypedEquals(-2, round(R(-15, 10))) - self.assertTypedEquals(-1, round(R(-7, 10))) - - self.assertEquals(False, bool(R(0, 1))) - self.assertEquals(True, bool(R(3, 2))) - self.assertTypedEquals(0.1, float(R(1, 10))) - - # Check that __float__ isn't implemented by converting the - # numerator and denominator to float before dividing. - self.assertRaises(OverflowError, float, int('2'*400+'7')) - self.assertAlmostEquals(2.0/3, - float(R(int('2'*400+'7'), int('3'*400+'1')))) - - self.assertTypedEquals(0.1+0j, complex(R(1,10))) - - def testRound(self): - self.assertTypedEquals(R(-200), round(R(-150), -2)) - self.assertTypedEquals(R(-200), round(R(-250), -2)) - self.assertTypedEquals(R(30), round(R(26), -1)) - self.assertTypedEquals(R(-2, 10), round(R(-15, 100), 1)) - self.assertTypedEquals(R(-2, 10), round(R(-25, 100), 1)) - - - def testArithmetic(self): - self.assertEquals(R(1, 2), R(1, 10) + R(2, 5)) - self.assertEquals(R(-3, 10), R(1, 10) - R(2, 5)) - self.assertEquals(R(1, 25), R(1, 10) * R(2, 5)) - self.assertEquals(R(1, 4), R(1, 10) / R(2, 5)) - self.assertTypedEquals(2, R(9, 10) // R(2, 5)) - self.assertTypedEquals(10**23, R(10**23, 1) // R(1)) - self.assertEquals(R(2, 3), R(-7, 3) % R(3, 2)) - self.assertEquals(R(8, 27), R(2, 3) ** R(3)) - self.assertEquals(R(27, 8), R(2, 3) ** R(-3)) - self.assertTypedEquals(2.0, R(4) ** R(1, 2)) - z = pow(R(-1), R(1, 2)) - self.assertAlmostEquals(z.real, 0) - self.assertEquals(z.imag, 1) - - def testMixedArithmetic(self): - self.assertTypedEquals(R(11, 10), R(1, 10) + 1) - self.assertTypedEquals(1.1, R(1, 10) + 1.0) - self.assertTypedEquals(1.1 + 0j, R(1, 10) + (1.0 + 0j)) - self.assertTypedEquals(R(11, 10), 1 + R(1, 10)) - self.assertTypedEquals(1.1, 1.0 + R(1, 10)) - self.assertTypedEquals(1.1 + 0j, (1.0 + 0j) + R(1, 10)) - - self.assertTypedEquals(R(-9, 10), R(1, 10) - 1) - self.assertTypedEquals(-0.9, R(1, 10) - 1.0) - self.assertTypedEquals(-0.9 + 0j, R(1, 10) - (1.0 + 0j)) - self.assertTypedEquals(R(9, 10), 1 - R(1, 10)) - self.assertTypedEquals(0.9, 1.0 - R(1, 10)) - self.assertTypedEquals(0.9 + 0j, (1.0 + 0j) - R(1, 10)) - - self.assertTypedEquals(R(1, 10), R(1, 10) * 1) - self.assertTypedEquals(0.1, R(1, 10) * 1.0) - self.assertTypedEquals(0.1 + 0j, R(1, 10) * (1.0 + 0j)) - self.assertTypedEquals(R(1, 10), 1 * R(1, 10)) - self.assertTypedEquals(0.1, 1.0 * R(1, 10)) - self.assertTypedEquals(0.1 + 0j, (1.0 + 0j) * R(1, 10)) - - self.assertTypedEquals(R(1, 10), R(1, 10) / 1) - self.assertTypedEquals(0.1, R(1, 10) / 1.0) - self.assertTypedEquals(0.1 + 0j, R(1, 10) / (1.0 + 0j)) - self.assertTypedEquals(R(10, 1), 1 / R(1, 10)) - self.assertTypedEquals(10.0, 1.0 / R(1, 10)) - self.assertTypedEquals(10.0 + 0j, (1.0 + 0j) / R(1, 10)) - - self.assertTypedEquals(0, R(1, 10) // 1) - self.assertTypedEquals(0, R(1, 10) // 1.0) - self.assertTypedEquals(10, 1 // R(1, 10)) - self.assertTypedEquals(10**23, 10**22 // R(1, 10)) - self.assertTypedEquals(10, 1.0 // R(1, 10)) - - self.assertTypedEquals(R(1, 10), R(1, 10) % 1) - self.assertTypedEquals(0.1, R(1, 10) % 1.0) - self.assertTypedEquals(R(0, 1), 1 % R(1, 10)) - self.assertTypedEquals(0.0, 1.0 % R(1, 10)) - - # No need for divmod since we don't override it. - - # ** has more interesting conversion rules. - self.assertTypedEquals(R(100, 1), R(1, 10) ** -2) - self.assertTypedEquals(R(100, 1), R(10, 1) ** 2) - self.assertTypedEquals(0.1, R(1, 10) ** 1.0) - self.assertTypedEquals(0.1 + 0j, R(1, 10) ** (1.0 + 0j)) - self.assertTypedEquals(4 , 2 ** R(2, 1)) - z = pow(-1, R(1, 2)) - self.assertAlmostEquals(0, z.real) - self.assertEquals(1, z.imag) - self.assertTypedEquals(R(1, 4) , 2 ** R(-2, 1)) - self.assertTypedEquals(2.0 , 4 ** R(1, 2)) - self.assertTypedEquals(0.25, 2.0 ** R(-2, 1)) - self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** R(1, 10)) - - def testMixingWithDecimal(self): - # Decimal refuses mixed comparisons. - self.assertRaisesMessage( - TypeError, - "unsupported operand type(s) for +: 'Rational' and 'Decimal'", - operator.add, R(3,11), Decimal('3.1415926')) - self.assertNotEquals(R(5, 2), Decimal('2.5')) - - def testComparisons(self): - self.assertTrue(R(1, 2) < R(2, 3)) - self.assertFalse(R(1, 2) < R(1, 2)) - self.assertTrue(R(1, 2) <= R(2, 3)) - self.assertTrue(R(1, 2) <= R(1, 2)) - self.assertFalse(R(2, 3) <= R(1, 2)) - self.assertTrue(R(1, 2) == R(1, 2)) - self.assertFalse(R(1, 2) == R(1, 3)) - self.assertFalse(R(1, 2) != R(1, 2)) - self.assertTrue(R(1, 2) != R(1, 3)) - - def testMixedLess(self): - self.assertTrue(2 < R(5, 2)) - self.assertFalse(2 < R(4, 2)) - self.assertTrue(R(5, 2) < 3) - self.assertFalse(R(4, 2) < 2) - - self.assertTrue(R(1, 2) < 0.6) - self.assertFalse(R(1, 2) < 0.4) - self.assertTrue(0.4 < R(1, 2)) - self.assertFalse(0.5 < R(1, 2)) - - def testMixedLessEqual(self): - self.assertTrue(0.5 <= R(1, 2)) - self.assertFalse(0.6 <= R(1, 2)) - self.assertTrue(R(1, 2) <= 0.5) - self.assertFalse(R(1, 2) <= 0.4) - self.assertTrue(2 <= R(4, 2)) - self.assertFalse(2 <= R(3, 2)) - self.assertTrue(R(4, 2) <= 2) - self.assertFalse(R(5, 2) <= 2) - - def testBigFloatComparisons(self): - # Because 10**23 can't be represented exactly as a float: - self.assertFalse(R(10**23) == float(10**23)) - # The first test demonstrates why these are important. - self.assertFalse(1e23 < float(R(math.trunc(1e23) + 1))) - self.assertTrue(1e23 < R(math.trunc(1e23) + 1)) - self.assertFalse(1e23 <= R(math.trunc(1e23) - 1)) - self.assertTrue(1e23 > R(math.trunc(1e23) - 1)) - self.assertFalse(1e23 >= R(math.trunc(1e23) + 1)) - - def testBigComplexComparisons(self): - self.assertFalse(R(10**23) == complex(10**23)) - self.assertTrue(R(10**23) > complex(10**23)) - self.assertFalse(R(10**23) <= complex(10**23)) - - def testMixedEqual(self): - self.assertTrue(0.5 == R(1, 2)) - self.assertFalse(0.6 == R(1, 2)) - self.assertTrue(R(1, 2) == 0.5) - self.assertFalse(R(1, 2) == 0.4) - self.assertTrue(2 == R(4, 2)) - self.assertFalse(2 == R(3, 2)) - self.assertTrue(R(4, 2) == 2) - self.assertFalse(R(5, 2) == 2) - - def testStringification(self): - self.assertEquals("Rational(7,3)", repr(R(7, 3))) - self.assertEquals("7/3", str(R(7, 3))) - self.assertEquals("7", str(R(7, 1))) - - def testHash(self): - self.assertEquals(hash(2.5), hash(R(5, 2))) - self.assertEquals(hash(10**50), hash(R(10**50))) - self.assertNotEquals(hash(float(10**23)), hash(R(10**23))) - - def testApproximatePi(self): - # Algorithm borrowed from - # http://docs.python.org/lib/decimal-recipes.html - three = R(3) - lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24 - while abs(s - lasts) > R(1, 10**9): - lasts = s - n, na = n+na, na+8 - d, da = d+da, da+32 - t = (t * n) / d - s += t - self.assertAlmostEquals(math.pi, s) - - def testApproximateCos1(self): - # Algorithm borrowed from - # http://docs.python.org/lib/decimal-recipes.html - x = R(1) - i, lasts, s, fact, num, sign = 0, 0, R(1), 1, 1, 1 - while abs(s - lasts) > R(1, 10**9): - lasts = s - i += 2 - fact *= i * (i-1) - num *= x * x - sign *= -1 - s += num / fact * sign - self.assertAlmostEquals(math.cos(1), s) - - def test_copy_deepcopy_pickle(self): - r = R(13, 7) - self.assertEqual(r, loads(dumps(r))) - self.assertEqual(id(r), id(copy(r))) - self.assertEqual(id(r), id(deepcopy(r))) - -def test_main(): - run_unittest(RationalTest, GcdTest) - -if __name__ == '__main__': - test_main() diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 87d744d..74b3ea2 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1182,6 +1182,8 @@ defdict_reduce(defdictobject *dd) static PyMethodDef defdict_methods[] = { {"__missing__", (PyCFunction)defdict_missing, METH_O, defdict_missing_doc}, + {"copy", (PyCFunction)defdict_copy, METH_NOARGS, + defdict_copy_doc}, {"__copy__", (PyCFunction)defdict_copy, METH_NOARGS, defdict_copy_doc}, {"__reduce__", (PyCFunction)defdict_reduce, METH_NOARGS, -- cgit v0.12