68 lines
1.7 KiB
Python
68 lines
1.7 KiB
Python
|
|
from math import gcd
|
||
|
|
|
||
|
|
'''
|
||
|
|
Euler's Totient (Phi) Function
|
||
|
|
'''
|
||
|
|
def totient(n: int) -> int:
|
||
|
|
phi = int(n > 1 and n)
|
||
|
|
for p in range(2, int(n ** .5) + 1):
|
||
|
|
if not n % p:
|
||
|
|
phi -= phi // p
|
||
|
|
while not n % p:
|
||
|
|
n //= p
|
||
|
|
#if n is > 1 it means it is prime
|
||
|
|
if n > 1: phi -= phi // n
|
||
|
|
return phi
|
||
|
|
|
||
|
|
'''
|
||
|
|
Tests the primality of an integer using its totient.
|
||
|
|
NOTE: If totient(n) has already been calculated
|
||
|
|
then pass it as the optional phi parameter.
|
||
|
|
'''
|
||
|
|
def is_prime(n: int, phi: int = None) -> bool:
|
||
|
|
return n - 1 == (phi if phi is not None else totient(n))
|
||
|
|
|
||
|
|
'''
|
||
|
|
Prime number generator function.
|
||
|
|
Returns the tuple (p, phi(p)) where p is prime
|
||
|
|
and phi is Euler's totient function.
|
||
|
|
'''
|
||
|
|
def prime_gen(yield_phi: bool = False) -> int | tuple[int, int]:
|
||
|
|
n = 1
|
||
|
|
while True:
|
||
|
|
n += 1
|
||
|
|
phi = totient(n)
|
||
|
|
if is_prime(n, phi=phi):
|
||
|
|
if yield_phi:
|
||
|
|
yield (n, phi)
|
||
|
|
else:
|
||
|
|
yield n
|
||
|
|
|
||
|
|
'''
|
||
|
|
Returns the prime factorisation of a number.
|
||
|
|
Returns a list of tuples (p, m) where p is
|
||
|
|
a prime factor and m is its multiplicity.
|
||
|
|
NOTE: uses a trial division algorithm
|
||
|
|
'''
|
||
|
|
def prime_factors(n: int) -> list[tuple[int, int]]:
|
||
|
|
phi = totient(n)
|
||
|
|
if is_prime(n, phi=phi):
|
||
|
|
return [(n, 1)]
|
||
|
|
factors = []
|
||
|
|
for p in prime_gen(yield_phi=False):
|
||
|
|
if p >= n:
|
||
|
|
break
|
||
|
|
# check if divisor
|
||
|
|
multiplicity = 0
|
||
|
|
while n % p == 0:
|
||
|
|
n //= p
|
||
|
|
multiplicity += 1
|
||
|
|
if multiplicity:
|
||
|
|
factors.append((p, multiplicity))
|
||
|
|
if is_prime(n):
|
||
|
|
break
|
||
|
|
if n != 1:
|
||
|
|
factors.append((n, 1))
|
||
|
|
return factors
|
||
|
|
|