124 lines
2.7 KiB
Python
124 lines
2.7 KiB
Python
"""Integer primality (deterministic Miller-Rabin for n < 2^64) and factorization
|
|
(Pollard's rho with Brent cycle detection + deterministic Miller-Rabin).
|
|
"""
|
|
import math
|
|
import random
|
|
|
|
|
|
_SMALL_PRIMES = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37)
|
|
|
|
|
|
def is_prime(n: int) -> bool:
|
|
if n < 2:
|
|
return False
|
|
for p in _SMALL_PRIMES:
|
|
if n == p:
|
|
return True
|
|
if n % p == 0:
|
|
return False
|
|
d = n - 1
|
|
s = 0
|
|
while d % 2 == 0:
|
|
d //= 2
|
|
s += 1
|
|
for a in (2, 3, 5, 7, 11, 13, 17):
|
|
if a % n == 0:
|
|
continue
|
|
x = pow(a, d, n)
|
|
if x == 1 or x == n - 1:
|
|
continue
|
|
composite = True
|
|
for _ in range(s - 1):
|
|
x = x * x % n
|
|
if x == n - 1:
|
|
composite = False
|
|
break
|
|
if composite:
|
|
return False
|
|
return True
|
|
|
|
|
|
def _pollard_brent(n: int) -> int:
|
|
if n % 2 == 0:
|
|
return 2
|
|
if n % 3 == 0:
|
|
return 3
|
|
while True:
|
|
c = random.randrange(1, n)
|
|
f = lambda x: (x * x + c) % n
|
|
y, m, g, r, q = random.randrange(2, n), 1, 1, 1, 1
|
|
while g == 1:
|
|
x = y
|
|
for _ in range(r):
|
|
y = f(y)
|
|
k = 0
|
|
while k < r and g == 1:
|
|
ys = y
|
|
for _ in range(min(m, r - k)):
|
|
y = f(y)
|
|
diff = x - y
|
|
if diff < 0:
|
|
diff = -diff
|
|
q = q * diff % n
|
|
g = math.gcd(q, n)
|
|
k += m
|
|
r *= 2
|
|
m *= 2
|
|
if g == n:
|
|
while True:
|
|
ys = f(ys)
|
|
diff = x - ys
|
|
if diff < 0:
|
|
diff = -diff
|
|
g = math.gcd(diff, n)
|
|
if g != 1:
|
|
break
|
|
if g != n:
|
|
return g
|
|
|
|
|
|
def factor(n: int) -> list[int]:
|
|
if n < 0:
|
|
return factor(-n)
|
|
if n < 2:
|
|
return []
|
|
n = abs(n)
|
|
factors = []
|
|
|
|
def _factor(m):
|
|
if m == 1:
|
|
return
|
|
if is_prime(m):
|
|
factors.append(m)
|
|
return
|
|
d = _pollard_brent(m)
|
|
_factor(d)
|
|
_factor(m // d)
|
|
|
|
for p in _SMALL_PRIMES:
|
|
while n % p == 0:
|
|
factors.append(p)
|
|
n //= p
|
|
if n > 1:
|
|
_factor(n)
|
|
factors.sort()
|
|
return factors
|
|
|
|
|
|
def totient(n: int) -> int:
|
|
if n < 1:
|
|
raise ValueError("n must be >= 1")
|
|
if n == 1:
|
|
return 1
|
|
fs = factor(n)
|
|
result = n
|
|
for p in set(fs):
|
|
result -= result // p
|
|
return result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
for arg in sys.argv[1:]:
|
|
n = int(arg)
|
|
print(n, is_prime(n), factor(n)) |