/
quadraticsieve.py
166 lines (146 loc) · 6.15 KB
/
quadraticsieve.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import getopt
import os
import math
import sys
import time
import mpmath
import numpy as np
from nzmath.arith1 import modsqrt
from sympy import sieve
from sympy.matrices import SparseMatrix
def legendre(a,p): # i know i saw this as a library function in nzmath
return pow(a, (p - 1) // 2, p) % p
def gcd(a, b):#this is probably implemented somewhere too
while a != 0:
a, b = b % a, a
return b
def get_factor_base(n, primes):#change this to a function of three arguments, namely smoothness
return [prime for prime in filter(lambda x: x < n, primes) if 1 == legendre(n, prime)]
def is_smooth(x, base):#i don't know why this is here. it was in the paper, but never got used.
for y in base:
if x == 1:
return True
factor_out(x,base)
return x == 1
def partition_ranges(number, num_partitions):
sieve_start = floor(sqrt(number))
sieve_end = math.ceil(sqrt(number*2))
partition_size = (sieve_end-sieve_start)/num_partitions
partitions = []
for x in range(num_partitions):
partitions.append(range(int(sieve_start + partition_size * x), int(sieve_start + partition_size*(x+1))))
return partitions
def calculate_start(number):
return int(math.sqrt(number))
def calculate_end(number):
return int(math.sqrt(2*number))
def factor_out(number, p):
assert number != 0
while number % p == 0:
number = number // p
return number
def generate_smooth(number, factor_base):
result = []
start = calculate_start(number)
end = calculate_end(number)
sieve = np.array([x**2-number for x in range(start,end)],
copy = False,
order = 'C')
xs = []
ys = []
for factor in factor_base:
roots = []
if 2 == factor:
roots = [modsqrt(number, factor)]
# ring formed by mod 2 only has one root
else:
first_root = modsqrt(number, factor)
roots = [first_root, factor - first_root]
for root in roots:
s = math.ceil(start / factor) * factor - start
for ind in [s-root,s+root]:
index = int(ind) # for python2
while index < end - start:
if 0 <= index:
sieve[index] = factor_out(sieve[index], factor)
if sieve[index] == 1:
xs.append(index+start)
ys.append((index+start)**2-number)
if len(xs) > len(factor_base) + 30:
return (xs, ys)
index = index + factor
return (xs,ys)
def generate_exponent_vector(composite, factor_base):
vector = []
for factor in factor_base:
count = 0
while composite % factor == 0:
composite = composite // factor
count = count + 1
vector.append(count)
return np.asarray(vector)
def generate_exponent_vector_m(composite, factor_base, base):#i dont know if numpy arrays help here
return np.asarray([x % base for x in generate_exponent_vector(composite, factor_base)])
def is_square(x):
x2 = int(math.sqrt(x))
if x2*x2 == x:
return True
else:
return False
def printT(start):
print("")
def qsieve(number):
primelimit = 1000
cores = 1
primes = [p for p in sieve.primerange(2, primelimit)]
factor_base = get_factor_base(number, primes)
xs,ys = generate_smooth(number, factor_base)
exponents = [generate_exponent_vector_m(y, factor_base, 2) for y in ys] # makes me miss haskell
sols = SparseMatrix(exponents).transpose().nullspace()
sol_found = False
for sol in sols:
a = 1
b = 1
z = sol.transpose()
for i,x in enumerate(z):
if 1 == (x % 2):
a *= xs[i]
b *= ys[i]
if (a ** 2 % number == b % number):
f1 = gcd(a + int(math.sqrt(b)),number)
if f1 == 1:
f1 = gcd(a - int(math.sqrt(b)),number)
f2 = number // f1
if 1 not in [f1,f2]: # if the trivial solution hasn't been found:
print("%d, %d %d" % (number, f1, f2))
return True
print("No solution found for", number)
return False
def main(argv):
primelimit = 1000
number = 223612903572099253
cores = 1
try:
opts, args = getopt.getopt(argv,"hs:p:c:",["semiprime=","primelimit=","cores="])
except getopt.GetoptError:
print("quadraticsieve.py -s <semiprime> -p <primelimit> -c <cores>") # well, i wound up not using two of those args
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print("quadraticsieve.py -s <semiprime> -p <primelimit> -c <cores>")
sys.exit()
elif opt in ("-s", "--semiprime"):
number = int(arg)
elif opt in ("-p", "--primelimit"):
primelimit = int(arg)
elif opt in ("-c", "--cores"):
cores = int(arg)
if is_square(number):
root = math.sqrt(number)
print("%d, %d %d" % (number, root, root))
printT(start)
return True
qsieve(number)
return False
if __name__ == "__main__":
main(sys.argv[1:])