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