Source code for py3nt.numbers.integer
"""Integers"""
import numpy as np
from sympy.ntheory import pollard_rho
from py3nt.defaults import MAX_LOGN_FACTORIZATION_LIMIT
[docs]
class Integer(int):
"""
Integer class
Methods
-------
multiply_modular:
Modular multiplication of current integer with another integer.
pollard_rho_factor:
Find a divisor of current integer using Pollard's rho factor algorithm.
"""
[docs]
def multiply_modular(self, other: int, modulus: int) -> int:
"""
Calculate ``self*other%modulus``.
This remainder will always be non-negative.
If negative integers are provided, they will be converted to positive first.
Parameters
----------
other : ``int``
Multiplier.
modulus : ``int``
Modulo used for multiplcation.
Returns
-------
``int``
Multiplication of ``self`` and ``other`` modulo ``modulus``.
"""
remainder = 0
cur = self % modulus
other %= modulus
while other > 0:
if (other & 1) == 1:
remainder += cur
if remainder > modulus:
remainder -= modulus
other >>= 1
cur <<= 1
if cur > modulus:
cur -= modulus
remainder %= modulus
return remainder
[docs]
def kthroot(self, k: int) -> int:
r"""
Parameters
----------
k : ``int``
A positive integer.
Returns
-------
``int``
A positive integer :math:`r` such that :math:`r^{k}\leq n<r^{k+1}`.
"""
high = 1
while pow(high, k) < self:
high <<= 1
low = high >> 1
while high - low > 1:
mid = (low + high) >> 1
cur = pow(mid, k)
if cur < self:
low = mid
elif self < cur:
high = mid
else:
return mid
if pow(high, k) == self:
return high
else:
return low
def __pow__(self, exponent: int, modulus=None):
if not modulus:
return pow(int(self), int(exponent))
return pow(int(self), int(exponent), int(modulus))
[docs]
def pollard_rho_factor(self, a: int, c: int, max_iter: int = 5) -> int:
"""
Find a factor of ``n`` greater than 1 using Pollard's rho factorization.
Use :math:`f(x) = x^2+c`
Parameters
----------
a : ``int``
Initial value of ``x``.
c : ``int``
Constant in the polynomial.
max_iter : ``int``, optional
Maximum number of iteration to find a non-trivial divisor, by default 5
Returns
-------
``int``
A non-trivial divisor of ``n`` if ``n`` is not a prime.
Raises
------
``ValueError``
If ``n`` can be factorized using classical sieve
or is larger than the biggest number.
"""
if (self % 2) == 0:
return 2
return pollard_rho(n=self, s=a, a=c, retries=max_iter)
[docs]
def brent_pollard_rho_factor(self) -> int:
"""Brent's optimization of Pollard's rho algorithm.
Returns
-------
``int``
A non-trivial factor of :math:`n` when :math:`n` is not a prime.
"""
if (self & 1) == 0:
return 2
if self < MAX_LOGN_FACTORIZATION_LIMIT:
return self.pollard_rho_factor(a=2, c=1, max_iter=10)
y = np.random.randint(low=2, high=int(np.minimum(self - 1, (1 << 30))))
c = np.random.randint(low=2, high=int(np.minimum(self - 1, (1 << 30))))
m = np.random.randint(low=2, high=int(np.minimum(self - 1, (1 << 30))))
divisor, r, q = 1, 1, 1
while divisor == 1:
x = y
for _ in range(r):
y = (Integer(y).multiply_modular(other=y, modulus=self) + c) % self
k = 0
while k < r and divisor == 1:
ys = y
for _ in range(min(m, r - k)):
y = ((y * y) % self + c) % self
q = q * (abs(x - y)) % self
divisor = np.gcd(q, self)
k = k + m
r <<= 1
if divisor == self:
while True:
ys = ((ys * ys) % self + c) % self
divisor = np.gcd(abs(x - ys), self)
if divisor > 1:
break
return divisor