Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/interfaces/qsieve.py
4048 views
1
"""
2
Interface to Bill Hart's Quadratic Sieve
3
"""
4
5
import os
6
7
cygwin = os.uname()[0][:6]=="CYGWIN"
8
9
import sage.rings.integer
10
11
from sage.misc.all import tmp_dir
12
13
_tmp_dir = False
14
def tmpdir():
15
global _tmp_dir
16
if _tmp_dir:
17
return
18
X = tmp_dir('qsieve')
19
os.environ['TMPDIR'] = X
20
_tmp_dir = True
21
22
def qsieve(n, block=True, time=False, verbose=False):
23
r"""
24
Run Hart's quadratic sieve and return the distinct proper factors
25
of the integer n that it finds.
26
27
CONDITIONS:
28
The conditions for the quadratic sieve to work are as follows:
29
30
\begin{enumerate}
31
\item No small factors
32
\item Not a perfect power
33
\item Not prime
34
\end{enumerate}
35
36
If any of these fails, the sieve will also.
37
38
39
INPUT:
40
n -- an integer with at least 40 digits
41
block -- (default: True) if True, you must wait until the
42
sieve computation is complete before using Sage further.
43
If False, Sage will run while the sieve computation
44
runs in parallel. If q is the returned object, use
45
q.quit() to terminate a running factorization.
46
time -- (default: False) if True, time the command using
47
the UNIX "time" command (which you might have to install).
48
verbose -- (default: False) if True, print out verbose
49
logging information about what happened during
50
the Sieve run (for non-blocking Sieve, verbose information
51
is always available via the log() method.)
52
53
OUTPUT:
54
list -- a list of the distinct proper factors of n found
55
str -- the time in cpu seconds that the computation took, as given
56
by the command line time command. (If time is False,
57
this is always an empty string.)
58
59
EXAMPLES:
60
sage: k = 19; n = next_prime(10^k)*next_prime(10^(k+1))
61
sage: factor(n) # (currently) uses PARI
62
10000000000000000051 * 100000000000000000039
63
sage: v, t = qsieve(n, time=True) # uses the sieve (optional: time doesn't work on cygwin)
64
sage: v # optional
65
[10000000000000000051, 100000000000000000039]
66
sage: t # random and optional
67
'0.36 real 0.19 user 0.00 sys'
68
"""
69
Z = sage.rings.integer.Integer
70
n = Z(n)
71
if len(str(n)) < 40:
72
raise ValueError, "n must have at least 40 digits"
73
if block:
74
return qsieve_block(n, time, verbose)
75
else:
76
return qsieve_nonblock(n, time)
77
78
def qsieve_block(n, time, verbose=False):
79
"""
80
Compute the factorization of n using Hart's quadratic Sieve
81
blocking until complete.
82
"""
83
if time:
84
if cygwin:
85
raise ValueError, "qsieve time not supported on Cygwin"
86
t = 'time '
87
else:
88
t = ''
89
tmpdir()
90
out = os.popen('echo "%s" | %s QuadraticSieve 2>&1'%(n,t)).read()
91
z = data_to_list(out, n, time=time)
92
if verbose:
93
print z[-1]
94
return z[:2]
95
96
def data_to_list(out, n, time):
97
"""
98
Convert output of Hart's sieve and n to a list and time.
99
100
INPUT:
101
out -- snapshot of text output of Hart's QuadraticSieve program
102
n -- the integer being factored
103
104
OUTPUT:
105
list -- proper factors found so far
106
str -- time information
107
"""
108
i = out.find('FACTORS:')
109
if i == -1:
110
return [], '', out # whole thing
111
else:
112
verbose = out[:i]
113
out = out[i+len('FACTORS:')+1:].strip()
114
if time:
115
w = out.split('\n')
116
for i in range(len(w)):
117
if 'user' in w[i]:
118
break
119
if i < len(w):
120
t = w[i].strip()
121
out = '\n'.join([w[j] for j in range(i)])
122
else:
123
t = ''
124
else:
125
t = ''
126
Z = sage.rings.integer.Integer
127
v = out.split()
128
v = list(set([Z(m) for m in v if Z(m) != n]))
129
v.sort()
130
return v, t, verbose
131
132
133
import pexpect
134
import cleaner
135
class qsieve_nonblock:
136
"""
137
A non-blocking version of Hart's quadratic sieve.
138
139
The sieve starts running when you create the object, but you can
140
still use Sage in parallel:
141
142
sage: k = 19; n = next_prime(10^k)*next_prime(10^(k+1))
143
sage: q = qsieve(n, block=False, time=True) # optional -- requires time command
144
sage: q # random output # optional
145
Proper factors so far: []
146
sage: q # random output # optional
147
([10000000000000000051, 100000000000000000039], '0.21')
148
sage: q.list() # random output # optional
149
[10000000000000000051, 100000000000000000039]
150
sage: q.time() # random output (optional -- requires time command)
151
'0.21'
152
153
sage: q = qsieve(next_prime(10^20)*next_prime(10^21), block=False)
154
sage: q # random output
155
Proper factors so far: [100000000000000000039, 1000000000000000000117]
156
sage: q # random output
157
[100000000000000000039, 1000000000000000000117]
158
"""
159
def __init__(self, n, time):
160
self._n = n
161
if time:
162
if cygwin:
163
raise ValueError, "qsieve time not supported on Cygwin"
164
cmd = 'time QuadraticSieve'
165
else:
166
cmd = 'QuadraticSieve'
167
tmpdir()
168
self._p = pexpect.spawn(cmd)
169
cleaner.cleaner(self._p.pid, 'QuadraticSieve')
170
self._p.sendline(str(self._n)+'\n\n\n')
171
self._done = False
172
self._out = ''
173
self._time = ''
174
self._do_time = time
175
176
def n(self):
177
"""
178
Return the integer that is being factored.
179
"""
180
return self._n
181
182
def pid(self):
183
"""
184
Return the PIN id of the QuadraticSieve process (actually
185
of the time process that spawns the sieve process).
186
"""
187
return self._p.pid
188
189
def done(self):
190
"""
191
Return True if the sieve process has completed.
192
"""
193
return self._done
194
195
def __repr__(self):
196
"""
197
Return a text representation of self.
198
"""
199
if self._done:
200
if hasattr(self, '_killed') and self._killed:
201
return "Factorization was terminated early."
202
v = data_to_list(self._get(), self._n, self._do_time)
203
if self._do_time:
204
return str(v[:2])
205
else:
206
return str(v[0])
207
else:
208
return 'Proper factors so far: %s'%self.list()
209
210
def cputime(self):
211
"""
212
Return the time in seconds (as a string) that it took to
213
factor n, or return '?' if the factorization has not
214
completed or the time is unknown.
215
"""
216
if not self._do_time:
217
raise ValueError, "you have to start the sieve with the option time=True in order to get timing information"
218
try:
219
return data_to_list(self._get(), self._n, self._do_time)[1]
220
except IndexError:
221
return '?'
222
time = cputime
223
224
def log(self):
225
"""
226
Return all output of running the sieve so far.
227
"""
228
return self._get()
229
230
def __getitem__(self, i):
231
"""
232
Return the i-th factor (in sorted order) found so far.
233
"""
234
return self.list()[i]
235
236
def __len__(self):
237
"""
238
Return the number of factors found so far. If q is the
239
Sieve object, type len(q) to see the number of factors.
240
"""
241
return len(self.list())
242
243
def list(self):
244
"""
245
Return a list of the factors found so far, as Sage
246
integers.
247
"""
248
try:
249
return data_to_list(self._get(), self._n, self._do_time)[0]
250
except IndexError:
251
return []
252
253
def quit(self):
254
"""
255
Terminate the QuadraticSieve process, in case you want
256
to give up on computing this factorization.
257
258
EXAMPLES:
259
sage: n = next_prime(2^110)*next_prime(2^100)
260
sage: qs = qsieve(n, block=False)
261
sage: qs
262
Proper factors so far: []
263
sage: qs.quit()
264
sage: qs
265
Factorization was terminated early.
266
"""
267
pid = self.pid()
268
os.killpg(int(pid),9)
269
#self._p.close()
270
self._killed = True
271
self._done = True
272
273
def _get(self, timeout=0.1):
274
"""
275
Used internally to get information about what has been
276
computed so far.
277
"""
278
if self._done:
279
return self._out
280
e = self._p
281
try:
282
e.expect('xxx', timeout=timeout)
283
except pexpect.TIMEOUT, msg:
284
pass
285
except pexpect.EOF, msg:
286
pass
287
self._done = True
288
self._p.close()
289
self._out += e.before
290
return self._out
291
292
293
294
295