Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r94818:4db575509642 Date: 2018-07-06 18:25 +0200 http://bitbucket.org/pypy/pypy/changeset/4db575509642/
Log: hg merge default diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -48,6 +48,7 @@ 'int_lshift': 'interp_intop.int_lshift', 'int_rshift': 'interp_intop.int_rshift', 'uint_rshift': 'interp_intop.uint_rshift', + 'int_mulmod': 'interp_intop.int_mulmod', } diff --git a/pypy/module/__pypy__/interp_intop.py b/pypy/module/__pypy__/interp_intop.py --- a/pypy/module/__pypy__/interp_intop.py +++ b/pypy/module/__pypy__/interp_intop.py @@ -2,7 +2,7 @@ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib.rarithmetic import r_uint, intmask -from rpython.rlib.rarithmetic import int_c_div, int_c_mod +from rpython.rlib.rarithmetic import int_c_div, int_c_mod, mulmod from rpython.rlib import jit @@ -39,3 +39,7 @@ n = r_uint(n) x = llop.uint_rshift(lltype.Unsigned, n, m) return space.newint(intmask(x)) + +@unwrap_spec(a=int, b=int, c=int) +def int_mulmod(space, a, b, c): + return space.newint(mulmod(a, b, c)) diff --git a/pypy/module/__pypy__/test/test_intop.py b/pypy/module/__pypy__/test/test_intop.py --- a/pypy/module/__pypy__/test/test_intop.py +++ b/pypy/module/__pypy__/test/test_intop.py @@ -102,3 +102,7 @@ assert intop.uint_rshift(-1, 1) == sys.maxsize assert intop.uint_rshift(-1, bits-2) == 3 assert intop.uint_rshift(-1, bits-1) == 1 + + def test_mulmod(self): + from __pypy__ import intop + assert intop.int_mulmod(9373891, 9832739, 2**31-1) == 1025488209 diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -371,20 +371,23 @@ return wrapint(space, a) -@jit.look_inside_iff(lambda space, iv, iw, iz: - jit.isconstant(iw) and jit.isconstant(iz)) def _pow(space, iv, iw, iz): """Helper for pow""" - if iw < 0: - if iz != 0: - raise oefmt(space.w_ValueError, - "pow() 2nd argument cannot be negative when 3rd " - "argument specified") + if iz == 0: + return _pow_nomod(iv, iw) + else: + return _pow_mod(space, iv, iw, iz) + +@jit.look_inside_iff(lambda iv, iw: jit.isconstant(iw)) +def _pow_nomod(iv, iw): + if iw <= 0: + if iw == 0: + return 1 # bounce it, since it always returns float raise ValueError temp = iv ix = 1 - while iw > 0: + while True: if iw & 1: try: ix = ovfcheck(ix * temp) @@ -397,12 +400,40 @@ temp = ovfcheck(temp * temp) # Square the value of temp except OverflowError: raise - if iz: - # If we did a multiplication, perform a modulo - ix %= iz - temp %= iz - if iz: - ix %= iz + return ix + +@jit.look_inside_iff(lambda space, iv, iw, iz: + jit.isconstant(iw) and jit.isconstant(iz)) +def _pow_mod(space, iv, iw, iz): + from rpython.rlib.rarithmetic import mulmod + + if iw <= 0: + if iw == 0: + return 1 % iz # != 1, for iz == 1 or iz < 0 + raise oefmt(space.w_ValueError, + "pow() 2nd argument cannot be negative when 3rd " + "argument specified") + if iz < 0: + try: + iz = ovfcheck(-iz) + except OverflowError: + raise + iz_negative = True + else: + iz_negative = False + + temp = iv + ix = 1 + while True: + if iw & 1: + ix = mulmod(ix, temp, iz) + iw >>= 1 # Shift exponent down by 1 bit + if iw == 0: + break + temp = mulmod(temp, temp, iz) + + if iz_negative and ix > 0: + ix -= iz return ix diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -1,7 +1,8 @@ # encoding: utf-8 import sys +from pypy.interpreter.error import OperationError from pypy.objspace.std import intobject as iobj -from rpython.rlib.rarithmetic import r_uint, is_valid_int +from rpython.rlib.rarithmetic import r_uint, is_valid_int, intmask from rpython.rlib.rbigint import rbigint class TestW_IntObject: @@ -169,6 +170,63 @@ assert space.isinstance_w(v, space.w_int) assert space.bigint_w(v).eq(rbigint.fromlong(pow(10, 20))) + try: + from hypothesis import given, strategies, example + except ImportError: + pass + else: + @given( + a=strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint), + b=strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint), + c=strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint)) + @example(0, 0, -sys.maxint-1) + @example(0, 1, -sys.maxint-1) + @example(1, 0, -sys.maxint-1) + def test_hypot_pow(self, a, b, c): + if c == 0: + return + # + # "pow(a, b, c)": if b < 0, should get an app-level ValueError. + # Otherwise, should always work except if c == -maxint-1 + if b < 0: + expected = "app-level ValueError" + elif b > 0 and c == -sys.maxint-1: + expected = OverflowError + else: + expected = pow(a, b, c) + + try: + result = iobj._pow(self.space, a, b, c) + except OperationError as e: + assert ('ValueError: pow() 2nd argument cannot be negative ' + 'when 3rd argument specified' == e.errorstr(self.space)) + result = "app-level ValueError" + except OverflowError: + result = OverflowError + assert result == expected + + @given( + a=strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint), + b=strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint)) + def test_hypot_pow_nomod(self, a, b): + # "a ** b": detect overflows and ValueErrors + if b < 0: + expected = ValueError + elif b > 128 and not (-1 <= a <= 1): + expected = OverflowError + else: + expected = a ** b + if expected != intmask(expected): + expected = OverflowError + + try: + result = iobj._pow(self.space, a, b, 0) + except ValueError: + result = ValueError + except OverflowError: + result = OverflowError + assert result == expected + def test_neg(self): space = self.space x = 42 diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -840,6 +840,31 @@ return z +@specialize.memo() +def check_support_int128(): + from rpython.rtyper.lltypesystem import rffi + return hasattr(rffi, '__INT128_T') + +def mulmod(a, b, c): + """Computes (a * b) % c. + Assumes c > 0, and returns a nonnegative result. + """ + assert c > 0 + if LONG_BIT < LONGLONG_BIT: + a = r_longlong(a) + b = r_longlong(b) + return intmask((a * b) % c) + elif check_support_int128(): + a = r_longlonglong(a) + b = r_longlonglong(b) + return intmask((a * b) % c) + else: + from rpython.rlib.rbigint import rbigint + a = rbigint.fromlong(a) + b = rbigint.fromlong(b) + return a.mul(b).int_mod(c).toint() + + # String parsing support # --------------------------- diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -1,6 +1,7 @@ from rpython.rlib.rarithmetic import LONG_BIT, intmask, longlongmask, r_uint, r_ulonglong from rpython.rlib.rarithmetic import ovfcheck, r_longlong, widen from rpython.rlib.rarithmetic import most_neg_value_of_same_type +from rpython.rlib.rarithmetic import check_support_int128 from rpython.rlib.rstring import StringBuilder from rpython.rlib.debug import make_sure_not_resized, check_regular_int from rpython.rlib.objectmodel import we_are_translated, specialize, not_rpython @@ -10,7 +11,7 @@ import math, sys -SUPPORT_INT128 = hasattr(rffi, '__INT128_T') +SUPPORT_INT128 = check_support_int128() BYTEORDER = sys.byteorder # note about digit sizes: diff --git a/rpython/rlib/test/test_rarithmetic.py b/rpython/rlib/test/test_rarithmetic.py --- a/rpython/rlib/test/test_rarithmetic.py +++ b/rpython/rlib/test/test_rarithmetic.py @@ -1,5 +1,6 @@ from rpython.rtyper.test.tool import BaseRtypingTest from rpython.rtyper.test.test_llinterp import interpret +from rpython.rlib import rarithmetic from rpython.rlib.rarithmetic import * from rpython.rlib.rstring import ParseStringError, ParseStringOverflowError from hypothesis import given, strategies, assume @@ -731,3 +732,17 @@ py.test.raises(OverflowError, ovfcheck_int32_sub, 2**30, -2**30) assert ovfcheck_int32_mul(-2**16, 2**15) == -2**31 py.test.raises(OverflowError, ovfcheck_int32_mul, -2**16, -2**15) + +@given(strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint), + strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint), + strategies.integers(min_value=1, max_value=sys.maxint)) +def test_mulmod(a, b, c): + assert mulmod(a, b, c) == (a * b) % c + # + import rpython.rlib.rbigint # import before patching check_support_int128 + prev = rarithmetic.check_support_int128 + try: + rarithmetic.check_support_int128 = lambda: False + assert mulmod(a, b, c) == (a * b) % c + finally: + rarithmetic.check_support_int128 = prev diff --git a/rpython/translator/c/test/test_typed.py b/rpython/translator/c/test/test_typed.py --- a/rpython/translator/c/test/test_typed.py +++ b/rpython/translator/c/test/test_typed.py @@ -962,3 +962,12 @@ f = self.getcompiled(func, [int]) res = f(2) assert res == 1 # and not 2 + + def test_mulmod(self): + from rpython.rlib.rarithmetic import mulmod + + def func(a, b, c): + return mulmod(a, b, c) + f = self.getcompiled(func, [int, int, int]) + res = f(1192871273, 1837632879, 2001286281) + assert res == 1573897320 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit