Skip to content

Commit 518cfae

Browse files
committed
test_numbertheory: test gcd
move sanity checks for gcd() to unittest add hypothesis tests for gcd
1 parent fa9dded commit 518cfae

File tree

1 file changed

+96
-6
lines changed

1 file changed

+96
-6
lines changed

src/ecdsa/test_numbertheory.py

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import operator
12
from six import print_
3+
from functools import reduce
4+
import operator
25
try:
36
import unittest2 as unittest
47
except ImportError:
@@ -22,11 +25,6 @@ def test_numbertheory():
2225
# p = modular_exp(2, -2, 3)
2326
# p = square_root_mod_prime(2, 3)
2427

25-
print_("Testing gcd...")
26-
assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5
27-
assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5
28-
assert gcd(3) == 3
29-
3028
print_("Testing lcm...")
3129
assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7
3230
assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7
@@ -95,7 +93,7 @@ def st_primes(draw, *args, **kwargs):
9593
kwargs["min_value"] = 1
9694
prime = draw(st.sampled_from(smallprimes) |
9795
st.integers(*args, **kwargs)
98-
.filter(lambda x: is_prime(x)))
96+
.filter(is_prime))
9997
return prime
10098

10199

@@ -107,6 +105,65 @@ def st_num_square_prime(draw):
107105
return sq, prime
108106

109107

108+
@st.composite
109+
def st_comp_with_com_fac(draw):
110+
"""
111+
Strategy that returns lists of numbers, all having a common factor.
112+
"""
113+
primes = draw(st.lists(st_primes(max_value=2**512), min_size=1,
114+
max_size=10))
115+
# select random prime(s) that will make the common factor of composites
116+
com_fac_primes = draw(st.lists(st.sampled_from(primes),
117+
min_size=1, max_size=20))
118+
com_fac = reduce(operator.mul, com_fac_primes, 1)
119+
120+
# select at most 20 lists (returned numbers),
121+
# each having at most 30 primes (factors) including none (then the number
122+
# will be 1)
123+
comp_primes = draw(
124+
st.integers(min_value=1, max_value=20).
125+
flatmap(lambda n: st.lists(st.lists(st.sampled_from(primes),
126+
max_size=30),
127+
min_size=1, max_size=n)))
128+
129+
return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes]
130+
131+
132+
@st.composite
133+
def st_comp_no_com_fac(draw):
134+
"""
135+
Strategy that returns lists of numbers that don't have a common factor.
136+
"""
137+
primes = draw(st.lists(st_primes(max_value=2**512),
138+
min_size=2, max_size=10, unique=True))
139+
# first select the primes that will create the uncommon factor
140+
# between returned numbers
141+
uncom_fac_primes = draw(st.lists(
142+
st.sampled_from(primes),
143+
min_size=1, max_size=len(primes)-1, unique=True))
144+
uncom_fac = reduce(operator.mul, uncom_fac_primes, 1)
145+
146+
# then build composites from leftover primes
147+
leftover_primes = [i for i in primes if i not in uncom_fac_primes]
148+
149+
assert leftover_primes
150+
assert uncom_fac_primes
151+
152+
# select at most 20 lists, each having at most 30 primes
153+
# selected from the leftover_primes list
154+
number_primes = draw(
155+
st.integers(min_value=1, max_value=20).
156+
flatmap(lambda n: st.lists(st.lists(st.sampled_from(leftover_primes),
157+
max_size=30),
158+
min_size=1, max_size=n)))
159+
160+
numbers = [reduce(operator.mul, nums, 1) for nums in number_primes]
161+
162+
insert_at = draw(st.integers(min_value=0, max_value=len(numbers)))
163+
numbers.insert(insert_at, uncom_fac)
164+
return numbers
165+
166+
110167
HYP_SETTINGS = {}
111168
if HC_PRESENT:
112169
HYP_SETTINGS['suppress_health_check']=[HealthCheck.filter_too_much,
@@ -116,6 +173,39 @@ def st_num_square_prime(draw):
116173

117174

118175
class TestNumbertheory(unittest.TestCase):
176+
def test_gcd(self):
177+
assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5
178+
assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5
179+
assert gcd(3) == 3
180+
181+
@unittest.skipUnless(HC_PRESENT,
182+
"Hypothesis 2.0.0 can't be made tolerant of hard to "
183+
"meet requirements (like `is_prime()`)")
184+
@settings(**HYP_SETTINGS)
185+
@given(st_comp_with_com_fac())
186+
def test_gcd_with_com_factor(self, numbers):
187+
n = gcd(numbers)
188+
assert 1 in numbers or n != 1
189+
for i in numbers:
190+
assert i % n == 0
191+
192+
@unittest.skipUnless(HC_PRESENT,
193+
"Hypothesis 2.0.0 can't be made tolerant of hard to "
194+
"meet requirements (like `is_prime()`)")
195+
@settings(**HYP_SETTINGS)
196+
@given(st_comp_no_com_fac())
197+
def test_gcd_with_uncom_factor(self, numbers):
198+
n = gcd(numbers)
199+
assert n == 1
200+
201+
@given(st.lists(st.integers(min_value=1, max_value=2**8192),
202+
min_size=1, max_size=20))
203+
def test_gcd_with_random_numbers(self, numbers):
204+
n = gcd(numbers)
205+
for i in numbers:
206+
# check that at least it's a divider
207+
assert i % n == 0
208+
119209
@unittest.skipUnless(HC_PRESENT,
120210
"Hypothesis 2.0.0 can't be made tolerant of hard to "
121211
"meet requirements (like `is_prime()`)")

0 commit comments

Comments
 (0)