| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- import operator
- from functools import reduce
- import sys
- try:
- import unittest2 as unittest
- except ImportError:
- import unittest
- import hypothesis.strategies as st
- import pytest
- from hypothesis import given, settings, example
- try:
- from hypothesis import HealthCheck
- HC_PRESENT = True
- except ImportError: # pragma: no cover
- HC_PRESENT = False
- from .numbertheory import (
- SquareRootError,
- JacobiError,
- factorization,
- gcd,
- lcm,
- jacobi,
- inverse_mod,
- is_prime,
- next_prime,
- smallprimes,
- square_root_mod_prime,
- )
- try:
- from gmpy2 import mpz
- except ImportError:
- try:
- from gmpy import mpz
- except ImportError:
- def mpz(x):
- return x
- BIGPRIMES = (
- 999671,
- 999683,
- 999721,
- 999727,
- 999749,
- 999763,
- 999769,
- 999773,
- 999809,
- 999853,
- 999863,
- 999883,
- 999907,
- 999917,
- 999931,
- 999953,
- 999959,
- 999961,
- 999979,
- 999983,
- )
- @pytest.mark.parametrize(
- "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])]
- )
- def test_next_prime(prime, next_p):
- assert next_prime(prime) == next_p
- @pytest.mark.parametrize("val", [-1, 0, 1])
- def test_next_prime_with_nums_less_2(val):
- assert next_prime(val) == 2
- @pytest.mark.slow
- @pytest.mark.parametrize("prime", smallprimes)
- def test_square_root_mod_prime_for_small_primes(prime):
- squares = set()
- for num in range(0, 1 + prime // 2):
- sq = num * num % prime
- squares.add(sq)
- root = square_root_mod_prime(sq, prime)
- # tested for real with TestNumbertheory.test_square_root_mod_prime
- assert root * root % prime == sq
- for nonsquare in range(0, prime):
- if nonsquare in squares:
- continue
- with pytest.raises(SquareRootError):
- square_root_mod_prime(nonsquare, prime)
- def test_square_root_mod_prime_for_2():
- a = square_root_mod_prime(1, 2)
- assert a == 1
- def test_square_root_mod_prime_for_small_prime():
- root = square_root_mod_prime(98**2 % 101, 101)
- assert root * root % 101 == 9
- def test_square_root_mod_prime_for_p_congruent_5():
- p = 13
- assert p % 8 == 5
- root = square_root_mod_prime(3, p)
- assert root * root % p == 3
- def test_square_root_mod_prime_for_p_congruent_5_large_d():
- p = 29
- assert p % 8 == 5
- root = square_root_mod_prime(4, p)
- assert root * root % p == 4
- class TestSquareRootModPrime(unittest.TestCase):
- def test_power_of_2_p(self):
- with self.assertRaises(JacobiError):
- square_root_mod_prime(12, 32)
- def test_no_square(self):
- with self.assertRaises(SquareRootError) as e:
- square_root_mod_prime(12, 31)
- self.assertIn("no square root", str(e.exception))
- def test_non_prime(self):
- with self.assertRaises(SquareRootError) as e:
- square_root_mod_prime(12, 33)
- self.assertIn("p is not prime", str(e.exception))
- def test_non_prime_with_negative(self):
- with self.assertRaises(SquareRootError) as e:
- square_root_mod_prime(697 - 1, 697)
- self.assertIn("p is not prime", str(e.exception))
- @st.composite
- def st_two_nums_rel_prime(draw):
- # 521-bit is the biggest curve we operate on, use 1024 for a bit
- # of breathing space
- mod = draw(st.integers(min_value=2, max_value=2**1024))
- num = draw(
- st.integers(min_value=1, max_value=mod - 1).filter(
- lambda x: gcd(x, mod) == 1
- )
- )
- return num, mod
- @st.composite
- def st_primes(draw, *args, **kwargs):
- if "min_value" not in kwargs: # pragma: no branch
- kwargs["min_value"] = 1
- prime = draw(
- st.sampled_from(smallprimes)
- | st.integers(*args, **kwargs).filter(is_prime)
- )
- return prime
- @st.composite
- def st_num_square_prime(draw):
- prime = draw(st_primes(max_value=2**1024))
- num = draw(st.integers(min_value=0, max_value=1 + prime // 2))
- sq = num * num % prime
- return sq, prime
- @st.composite
- def st_comp_with_com_fac(draw):
- """
- Strategy that returns lists of numbers, all having a common factor.
- """
- primes = draw(
- st.lists(st_primes(max_value=2**512), min_size=1, max_size=10)
- )
- # select random prime(s) that will make the common factor of composites
- com_fac_primes = draw(
- st.lists(st.sampled_from(primes), min_size=1, max_size=20)
- )
- com_fac = reduce(operator.mul, com_fac_primes, 1)
- # select at most 20 lists (returned numbers),
- # each having at most 30 primes (factors) including none (then the number
- # will be 1)
- comp_primes = draw( # pragma: no branch
- st.integers(min_value=1, max_value=20).flatmap(
- lambda n: st.lists(
- st.lists(st.sampled_from(primes), max_size=30),
- min_size=1,
- max_size=n,
- )
- )
- )
- return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes]
- @st.composite
- def st_comp_no_com_fac(draw):
- """
- Strategy that returns lists of numbers that don't have a common factor.
- """
- primes = draw(
- st.lists(
- st_primes(max_value=2**512), min_size=2, max_size=10, unique=True
- )
- )
- # first select the primes that will create the uncommon factor
- # between returned numbers
- uncom_fac_primes = draw(
- st.lists(
- st.sampled_from(primes),
- min_size=1,
- max_size=len(primes) - 1,
- unique=True,
- )
- )
- uncom_fac = reduce(operator.mul, uncom_fac_primes, 1)
- # then build composites from leftover primes
- leftover_primes = [i for i in primes if i not in uncom_fac_primes]
- assert leftover_primes
- assert uncom_fac_primes
- # select at most 20 lists, each having at most 30 primes
- # selected from the leftover_primes list
- number_primes = draw( # pragma: no branch
- st.integers(min_value=1, max_value=20).flatmap(
- lambda n: st.lists(
- st.lists(st.sampled_from(leftover_primes), max_size=30),
- min_size=1,
- max_size=n,
- )
- )
- )
- numbers = [reduce(operator.mul, nums, 1) for nums in number_primes]
- insert_at = draw(st.integers(min_value=0, max_value=len(numbers)))
- numbers.insert(insert_at, uncom_fac)
- return numbers
- HYP_SETTINGS = {}
- if HC_PRESENT: # pragma: no branch
- HYP_SETTINGS["suppress_health_check"] = [
- HealthCheck.filter_too_much,
- HealthCheck.too_slow,
- ]
- # the factorization() sometimes takes a long time to finish
- HYP_SETTINGS["deadline"] = 5000
- if "--fast" in sys.argv: # pragma: no cover
- HYP_SETTINGS["max_examples"] = 20
- HYP_SLOW_SETTINGS = dict(HYP_SETTINGS)
- if "--fast" in sys.argv: # pragma: no cover
- HYP_SLOW_SETTINGS["max_examples"] = 1
- else:
- HYP_SLOW_SETTINGS["max_examples"] = 20
- class TestIsPrime(unittest.TestCase):
- def test_very_small_prime(self):
- assert is_prime(23)
- def test_very_small_composite(self):
- assert not is_prime(22)
- def test_small_prime(self):
- assert is_prime(123456791)
- def test_special_composite(self):
- assert not is_prime(10261)
- def test_medium_prime_1(self):
- # nextPrime[2^256]
- assert is_prime(2**256 + 0x129)
- def test_medium_prime_2(self):
- # nextPrime(2^256+0x129)
- assert is_prime(2**256 + 0x12D)
- def test_medium_trivial_composite(self):
- assert not is_prime(2**256 + 0x130)
- def test_medium_non_trivial_composite(self):
- assert not is_prime(2**256 + 0x12F)
- def test_large_prime(self):
- # nextPrime[2^2048]
- assert is_prime(mpz(2) ** 2048 + 0x3D5)
- def test_pseudoprime_base_19(self):
- assert not is_prime(1543267864443420616877677640751301)
- def test_pseudoprime_base_300(self):
- # F. Arnault "Constructing Carmichael Numbers Which Are Strong
- # Pseudoprimes to Several Bases". Journal of Symbolic
- # Computation. 20 (2): 151-161. doi:10.1006/jsco.1995.1042.
- # Section 4.4 Large Example (a pseudoprime to all bases up to
- # 300)
- p = int(
- "29 674 495 668 685 510 550 154 174 642 905 332 730 "
- "771 991 799 853 043 350 995 075 531 276 838 753 171 "
- "770 199 594 238 596 428 121 188 033 664 754 218 345 "
- "562 493 168 782 883".replace(" ", "")
- )
- assert is_prime(p)
- for _ in range(10):
- if not is_prime(p * (313 * (p - 1) + 1) * (353 * (p - 1) + 1)):
- break
- else:
- assert False, "composite not detected"
- class TestNumbertheory(unittest.TestCase):
- def test_gcd(self):
- assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5
- assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5
- assert gcd(3) == 3
- @unittest.skipUnless(
- HC_PRESENT,
- "Hypothesis 2.0.0 can't be made tolerant of hard to "
- "meet requirements (like `is_prime()`), the test "
- "case times-out on it",
- )
- @settings(**HYP_SLOW_SETTINGS)
- @example([877 * 1151, 877 * 1009])
- @given(st_comp_with_com_fac())
- def test_gcd_with_com_factor(self, numbers):
- n = gcd(numbers)
- assert 1 in numbers or n != 1
- for i in numbers:
- assert i % n == 0
- @unittest.skipUnless(
- HC_PRESENT,
- "Hypothesis 2.0.0 can't be made tolerant of hard to "
- "meet requirements (like `is_prime()`), the test "
- "case times-out on it",
- )
- @settings(**HYP_SLOW_SETTINGS)
- @example([1151, 1069, 1009])
- @given(st_comp_no_com_fac())
- def test_gcd_with_uncom_factor(self, numbers):
- n = gcd(numbers)
- assert n == 1
- @settings(**HYP_SLOW_SETTINGS)
- @given(
- st.lists(
- st.integers(min_value=1, max_value=2**8192),
- min_size=1,
- max_size=20,
- )
- )
- def test_gcd_with_random_numbers(self, numbers):
- n = gcd(numbers)
- for i in numbers:
- # check that at least it's a divider
- assert i % n == 0
- def test_lcm(self):
- assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7
- assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7
- assert lcm(3) == 3
- @settings(**HYP_SLOW_SETTINGS)
- @given(
- st.lists(
- st.integers(min_value=1, max_value=2**8192),
- min_size=1,
- max_size=20,
- )
- )
- def test_lcm_with_random_numbers(self, numbers):
- n = lcm(numbers)
- for i in numbers:
- assert n % i == 0
- @unittest.skipUnless(
- HC_PRESENT,
- "Hypothesis 2.0.0 can't be made tolerant of hard to "
- "meet requirements (like `is_prime()`), the test "
- "case times-out on it",
- )
- @settings(**HYP_SLOW_SETTINGS)
- @given(st_num_square_prime())
- def test_square_root_mod_prime(self, vals):
- square, prime = vals
- calc = square_root_mod_prime(square, prime)
- assert calc * calc % prime == square
- @pytest.mark.slow
- @settings(**HYP_SLOW_SETTINGS)
- @given(st.integers(min_value=1, max_value=10**12))
- @example(265399 * 1526929)
- @example(373297**2 * 553991)
- def test_factorization(self, num):
- factors = factorization(num)
- mult = 1
- for i in factors:
- mult *= i[0] ** i[1]
- assert mult == num
- def test_factorisation_smallprimes(self):
- exp = 101 * 103
- assert 101 in smallprimes
- assert 103 in smallprimes
- factors = factorization(exp)
- mult = 1
- for i in factors:
- mult *= i[0] ** i[1]
- assert mult == exp
- def test_factorisation_not_smallprimes(self):
- exp = 1231 * 1237
- assert 1231 not in smallprimes
- assert 1237 not in smallprimes
- factors = factorization(exp)
- mult = 1
- for i in factors:
- mult *= i[0] ** i[1]
- assert mult == exp
- def test_jacobi_with_zero(self):
- assert jacobi(0, 3) == 0
- def test_jacobi_with_one(self):
- assert jacobi(1, 3) == 1
- @settings(**HYP_SLOW_SETTINGS)
- @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2))
- def test_jacobi(self, mod):
- mod = mpz(mod)
- if is_prime(mod):
- squares = set()
- for root in range(1, mod):
- root = mpz(root)
- assert jacobi(root * root, mod) == 1
- squares.add(root * root % mod)
- for i in range(1, mod):
- if i not in squares:
- i = mpz(i)
- assert jacobi(i, mod) == -1
- else:
- factors = factorization(mod)
- for a in range(1, mod):
- c = 1
- for i in factors:
- c *= jacobi(a, i[0]) ** i[1]
- assert c == jacobi(a, mod)
- @settings(**HYP_SLOW_SETTINGS)
- @given(st_two_nums_rel_prime())
- def test_inverse_mod(self, nums):
- num, mod = nums
- inv = inverse_mod(num, mod)
- assert 0 < inv < mod
- assert num * inv % mod == 1
- def test_inverse_mod_with_zero(self):
- assert 0 == inverse_mod(0, 11)
|