def pollard_pm1(n, B=10, a=2, retries=0, seed=1234): """ Use Pollard's p-1 method to try to extract a nontrivial factor of ``n``. Either a divisor (perhaps composite) or ``None`` is returned. The value of ``a`` is the base that is used in the test gcd(a**M - 1, n). The default is 2. If ``retries`` > 0 then if no factor is found after the first attempt, a new ``a`` will be generated randomly (using the ``seed``) and the process repeated. Note: the value of M is lcm(1..B) = reduce(ilcm, range(2, B + 1)). A search is made for factors next to even numbers having a power smoothness less than ``B``. Choosing a larger B increases the likelihood of finding a larger factor but takes longer. Whether a factor of n is found or not depends on ``a`` and the power smoothness of the even mumber just less than the factor p (hence the name p - 1). Although some discussion of what constitutes a good ``a`` some descriptions are hard to interpret. At the modular.math site referenced below it is stated that if gcd(a**M - 1, n) = N then a**M % q**r is 1 for every prime power divisor of N. But consider the following: >>> from sympy.ntheory.factor_ import smoothness_p, pollard_pm1 >>> n=257*1009 >>> smoothness_p(n) (-1, [(257, (1, 2, 256)), (1009, (1, 7, 16))])
def test_smoothness_and_smoothness_p(): assert smoothness(1) == (1, 1) assert smoothness(2 ** 4 * 3 ** 2) == (3, 16) assert smoothness_p(10431, m=1) == (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) assert smoothness_p(10431) == (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) assert smoothness_p(10431, power=1) == (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) assert ( smoothness_p(21477639576571, visual=1) == "p**i=4410317**1 has p-1 B=1787, B-pow=1787\n" + "p**i=4869863**1 has p-1 B=2434931, B-pow=2434931" )
def test_smoothness_and_smoothness_p(): assert smoothness(1) == (1, 1) assert smoothness(2**4*3**2) == (3, 16) assert smoothness_p(10431, m=1) == \ (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) assert smoothness_p(10431) == \ (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) assert smoothness_p(10431, power=1) == \ (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) assert smoothness_p(21477639576571, visual=1) == \ 'p**i=4410317**1 has p-1 B=1787, B-pow=1787\n' + \ 'p**i=4869863**1 has p-1 B=2434931, B-pow=2434931'
def smoothness_p(n, m=-1, power=0, visual=None): """ Return a list of [m, (p, (M, sm(p + m), psm(p + m)))...] where: 1. p**M is the base-p divisor of n 2. sm(p + m) is the smoothness of p + m (m = -1 by default) 3. psm(p + m) is the power smoothness of p + m The list is sorted according to smoothness (default) or by power smoothness if power=1. The smoothness of the numbers to the left (m = -1) or right (m = 1) of a factor govern the results that are obtained from the p +/- 1 type factoring methods. >>> from sympy.ntheory.factor_ import smoothness_p, factorint >>> smoothness_p(10431, m=1) (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) >>> smoothness_p(10431) (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) >>> smoothness_p(10431, power=1) (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) If visual=True then an annotated string will be returned: >>> print(smoothness_p(21477639576571, visual=1)) p**i=4410317**1 has p-1 B=1787, B-pow=1787 p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 This string can also be generated directly from a factorization dictionary and vice versa: >>> factorint(17*9) {3: 2, 17: 1} >>> smoothness_p(_) 'p**i=3**2 has p-1 B=2, B-pow=2\\np**i=17**1 has p-1 B=2, B-pow=16' >>> smoothness_p(_) {3: 2, 17: 1} The table of the output logic is: ====== ====== ======= ======= | Visual ------ ---------------------- Input True False other ====== ====== ======= ======= dict str tuple str str str tuple dict tuple str tuple str n str tuple tuple mul str tuple tuple ====== ====== ======= ======= See Also ======== factorint, smoothness """ from sympy.utilities import flatten # visual must be True, False or other (stored as None) if visual in (1, 0): visual = bool(visual) elif visual not in (True, False): visual = None if type(n) is str: if visual: return n d = {} for li in n.splitlines(): k, v = [int(i) for i in li.split('has')[0].split('=')[1].split('**')] d[k] = v if visual is not True and visual is not False: return d return smoothness_p(d, visual=False) elif type(n) is not tuple: facs = factorint(n, visual=False) if power: k = -1 else: k = 1 if type(n) is not tuple: rv = (m, sorted([(f, tuple([M] + list(smoothness(f + m)))) for f, M in [i for i in facs.items()]], key=lambda x: (x[1][k], x[0]))) else: rv = n if visual is False or (visual is not True) and (type(n) in [int, Mul]): return rv lines = [] for dat in rv[1]: dat = flatten(dat) dat.insert(2, m) lines.append('p**i=%i**%i has p%+i B=%i, B-pow=%i' % tuple(dat)) return '\n'.join(lines)