diff options
author | Tim Peters <tim.peters@gmail.com> | 2022-01-02 13:18:20 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-02 13:18:20 -0600 |
commit | 863729e9c6f599286f98ec37c8716e982c4ca9dd (patch) | |
tree | 8a993db961e57ce1208ed309bd40f92038517870 /Lib/test/test_pow.py | |
parent | ce4d25f3cd0a1c6e65b64015140fb5e1397c8ac5 (diff) | |
download | cpython-863729e9c6f599286f98ec37c8716e982c4ca9dd.tar.gz cpython-863729e9c6f599286f98ec37c8716e982c4ca9dd.zip |
bpo-46218: Change long_pow() to sliding window algorithm (GH-30319)
* bpo-46218: Change long_pow() to sliding window algorithm
The primary motivation is to eliminate long_pow's reliance on that the number of bits in a long "digit" is a multiple of 5. Now it no longer cares how many bits are in a digit.
But the sliding window approach also allows cutting the precomputed table of small powers in half, which reduces initialization overhead enough that the approach pays off for smaller exponents too. Depending on exponent bit patterns, a sliding window may also be able to save some bigint multiplies (sometimes when at least 5 consecutive exponent bits are 0, regardless of their starting bit position modulo 5).
Note: boosting the window width to 6 didn't work well overall. It give marginal speed improvements for huge exponents, but the increased overhead (the small-power table needs twice as many entries) made it a loss for smaller exponents.
Co-authored-by: Oleg Iarygin <dralife@yandex.ru>
Diffstat (limited to 'Lib/test/test_pow.py')
-rw-r--r-- | Lib/test/test_pow.py | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/Lib/test/test_pow.py b/Lib/test/test_pow.py index 660ff80bbf5..5cea9ceb20f 100644 --- a/Lib/test/test_pow.py +++ b/Lib/test/test_pow.py @@ -93,6 +93,28 @@ class PowTest(unittest.TestCase): pow(int(i),j,k) ) + def test_big_exp(self): + import random + self.assertEqual(pow(2, 50000), 1 << 50000) + # Randomized modular tests, checking the identities + # a**(b1 + b2) == a**b1 * a**b2 + # a**(b1 * b2) == (a**b1)**b2 + prime = 1000000000039 # for speed, relatively small prime modulus + for i in range(10): + a = random.randrange(1000, 1000000) + bpower = random.randrange(1000, 50000) + b = random.randrange(1 << (bpower - 1), 1 << bpower) + b1 = random.randrange(1, b) + b2 = b - b1 + got1 = pow(a, b, prime) + got2 = pow(a, b1, prime) * pow(a, b2, prime) % prime + if got1 != got2: + self.fail(f"{a=:x} {b1=:x} {b2=:x} {got1=:x} {got2=:x}") + got3 = pow(a, b1 * b2, prime) + got4 = pow(pow(a, b1, prime), b2, prime) + if got3 != got4: + self.fail(f"{a=:x} {b1=:x} {b2=:x} {got3=:x} {got4=:x}") + def test_bug643260(self): class TestRpow: def __rpow__(self, other): |