Skip to content

Commit 182062d

Browse files
bizzfitchcclauss
authored andcommitted
Adding deterministic miller rabin primality test (#1453)
* Adding deterministic miller rabin primality test * Moved to ciphers directory and renamed for clarity. Changed docstring to add test
1 parent a2a3ca6 commit 182062d

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

ciphers/deterministic_miller_rabin.py

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Created by Nathan Damon, @bizzfitch on github
2+
>>> test_miller_rabin()
3+
"""
4+
5+
6+
def miller_rabin(n, allow_probable=False):
7+
"""Deterministic Miller-Rabin algorithm for primes ~< 3.32e24.
8+
9+
Uses numerical analysis results to return whether or not the passed number
10+
is prime. If the passed number is above the upper limit, and
11+
allow_probable is True, then a return value of True indicates that n is
12+
probably prime. This test does not allow False negatives- a return value
13+
of False is ALWAYS composite.
14+
15+
Parameters
16+
----------
17+
n : int
18+
The integer to be tested. Since we usually care if a number is prime,
19+
n < 2 returns False instead of raising a ValueError.
20+
allow_probable: bool, default False
21+
Whether or not to test n above the upper bound of the deterministic test.
22+
23+
Raises
24+
------
25+
ValueError
26+
27+
Reference
28+
---------
29+
https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
30+
"""
31+
if n == 2:
32+
return True
33+
if not n % 2 or n < 2:
34+
return False
35+
if n > 5 and n % 10 not in (1, 3, 7, 9): # can quickly check last digit
36+
return False
37+
if n > 3_317_044_064_679_887_385_961_981 and not allow_probable:
38+
raise ValueError(
39+
"Warning: upper bound of deterministic test is exceeded. "
40+
"Pass allow_probable=True to allow probabilistic test. "
41+
"A return value of True indicates a probable prime."
42+
)
43+
# array bounds provided by analysis
44+
bounds = [2_047,
45+
1_373_653,
46+
25_326_001,
47+
3_215_031_751,
48+
2_152_302_898_747,
49+
3_474_749_660_383,
50+
341_550_071_728_321,
51+
1,
52+
3_825_123_056_546_413_051,
53+
1,
54+
1,
55+
318_665_857_834_031_151_167_461,
56+
3_317_044_064_679_887_385_961_981]
57+
58+
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
59+
for idx, _p in enumerate(bounds, 1):
60+
if n < _p:
61+
# then we have our last prime to check
62+
plist = primes[:idx]
63+
break
64+
d, s = n - 1, 0
65+
# break up n -1 into a power of 2 (s) and
66+
# remaining odd component
67+
# essentially, solve for d * 2 ** s == n - 1
68+
while d % 2 == 0:
69+
d //= 2
70+
s += 1
71+
for prime in plist:
72+
pr = False
73+
for r in range(s):
74+
m = pow(prime, d * 2 ** r, n)
75+
# see article for analysis explanation for m
76+
if (r == 0 and m == 1) or ((m + 1) % n == 0):
77+
pr = True
78+
# this loop will not determine compositeness
79+
break
80+
if pr:
81+
continue
82+
# if pr is False, then the above loop never evaluated to true,
83+
# and the n MUST be composite
84+
return False
85+
return True
86+
87+
88+
def test_miller_rabin():
89+
"""Testing a nontrivial (ends in 1, 3, 7, 9) composite
90+
and a prime in each range.
91+
"""
92+
assert not miller_rabin(561)
93+
assert miller_rabin(563)
94+
# 2047
95+
96+
assert not miller_rabin(838_201)
97+
assert miller_rabin(838_207)
98+
# 1_373_653
99+
100+
assert not miller_rabin(17_316_001)
101+
assert miller_rabin(17_316_017)
102+
# 25_326_001
103+
104+
assert not miller_rabin(3_078_386_641)
105+
assert miller_rabin(3_078_386_653)
106+
# 3_215_031_751
107+
108+
assert not miller_rabin(1_713_045_574_801)
109+
assert miller_rabin(1_713_045_574_819)
110+
# 2_152_302_898_747
111+
112+
assert not miller_rabin(2_779_799_728_307)
113+
assert miller_rabin(2_779_799_728_327)
114+
# 3_474_749_660_383
115+
116+
assert not miller_rabin(113_850_023_909_441)
117+
assert miller_rabin(113_850_023_909_527)
118+
# 341_550_071_728_321
119+
120+
assert not miller_rabin(1_275_041_018_848_804_351)
121+
assert miller_rabin(1_275_041_018_848_804_391)
122+
# 3_825_123_056_546_413_051
123+
124+
assert not miller_rabin(79_666_464_458_507_787_791_867)
125+
assert miller_rabin(79_666_464_458_507_787_791_951)
126+
# 318_665_857_834_031_151_167_461
127+
128+
assert not miller_rabin(552_840_677_446_647_897_660_333)
129+
assert miller_rabin(552_840_677_446_647_897_660_359)
130+
# 3_317_044_064_679_887_385_961_981
131+
# upper limit for probabilistic test
132+
133+
134+
if __name__ == '__main__':
135+
test_miller_rabin()

0 commit comments

Comments
 (0)