Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/schemes/elliptic_curves/descent_two_isogeny.pyx
4158 views
1
"""
2
Descent on elliptic curves over QQ with a 2-isogeny.
3
"""
4
5
#*****************************************************************************
6
# Copyright (C) 2009 Robert L. Miller <[email protected]>
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
# http://www.gnu.org/licenses/
10
#*****************************************************************************
11
12
from sage.rings.all import ZZ
13
from sage.rings.polynomial.polynomial_ring import polygen
14
cdef object x_ZZ = polygen(ZZ)
15
from sage.rings.polynomial.real_roots import real_roots
16
from sage.rings.arith import prime_divisors
17
from sage.misc.all import walltime, cputime
18
from sage.libs.pari.gen import pari
19
from sage.all import ntl
20
21
from sage.rings.integer cimport Integer
22
23
include "../../ext/cdefs.pxi"
24
include "../../ext/interrupt.pxi"
25
include "../../libs/flint/fmpz_poly.pxi"
26
27
from sage.libs.flint.zmod_poly cimport *, zmod_poly_t
28
from sage.libs.flint.long_extras cimport *, factor_t
29
from sage.libs.ratpoints cimport ratpoints_mpz_exists_only
30
31
cdef int N_RES_CLASSES_BSD = 10
32
33
cdef unsigned long ui0 = <unsigned long>0
34
cdef unsigned long ui1 = <unsigned long>1
35
cdef unsigned long ui2 = <unsigned long>2
36
cdef unsigned long ui3 = <unsigned long>3
37
cdef unsigned long ui4 = <unsigned long>4
38
cdef unsigned long ui8 = <unsigned long>8
39
40
cdef unsigned long valuation(mpz_t a, mpz_t p):
41
"""
42
Return the number of times p divides a.
43
"""
44
cdef mpz_t aa
45
cdef unsigned long v
46
mpz_init(aa)
47
v = mpz_remove(aa,a,p)
48
mpz_clear(aa)
49
return v
50
51
def test_valuation(a, p):
52
"""
53
Doctest function for cdef long valuation(mpz_t, mpz_t).
54
55
EXAMPLE::
56
57
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import test_valuation as tv
58
sage: for i in [1..20]:
59
... print '%10s'%factor(i), tv(i,2), tv(i,3), tv(i,5)
60
1 0 0 0
61
2 1 0 0
62
3 0 1 0
63
2^2 2 0 0
64
5 0 0 1
65
2 * 3 1 1 0
66
7 0 0 0
67
2^3 3 0 0
68
3^2 0 2 0
69
2 * 5 1 0 1
70
11 0 0 0
71
2^2 * 3 2 1 0
72
13 0 0 0
73
2 * 7 1 0 0
74
3 * 5 0 1 1
75
2^4 4 0 0
76
17 0 0 0
77
2 * 3^2 1 2 0
78
19 0 0 0
79
2^2 * 5 2 0 1
80
81
"""
82
cdef Integer A = Integer(a)
83
cdef Integer P = Integer(p)
84
return valuation(A.value, P.value)
85
86
cdef int padic_square(mpz_t a, mpz_t p):
87
"""
88
Test if a is a p-adic square.
89
"""
90
cdef unsigned long v
91
cdef mpz_t aa
92
cdef int result
93
94
if mpz_sgn(a) == 0: return 1
95
96
v = valuation(a,p)
97
if v&(ui1): return 0
98
99
mpz_init_set(aa,a)
100
while v:
101
v -= 1
102
mpz_divexact(aa, aa, p)
103
if mpz_cmp_ui(p, ui2)==0:
104
result = ( mpz_fdiv_ui(aa,ui8)==ui1 )
105
else:
106
result = ( mpz_legendre(aa,p)==1 )
107
mpz_clear(aa)
108
return result
109
110
def test_padic_square(a, p):
111
"""
112
Doctest function for cdef int padic_square(mpz_t, unsigned long).
113
114
EXAMPLE::
115
116
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import test_padic_square as ps
117
sage: for i in [1..300]:
118
... for p in prime_range(100):
119
... if not Qp(p)(i).is_square()==bool(ps(i,p)):
120
... print i, p
121
122
"""
123
cdef Integer A = Integer(a)
124
cdef Integer P = Integer(p)
125
return padic_square(A.value, P.value)
126
127
cdef int lemma6(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e,
128
mpz_t x, mpz_t p, unsigned long nu):
129
"""
130
Implements Lemma 6 of BSD's "Notes on elliptic curves, I" for odd p.
131
132
Returns -1 for insoluble, 0 for undecided, +1 for soluble.
133
"""
134
cdef mpz_t g_of_x, g_prime_of_x
135
cdef unsigned long lambd, mu
136
cdef int result = -1
137
138
mpz_init(g_of_x)
139
mpz_mul(g_of_x, a, x)
140
mpz_add(g_of_x, g_of_x, b)
141
mpz_mul(g_of_x, g_of_x, x)
142
mpz_add(g_of_x, g_of_x, c)
143
mpz_mul(g_of_x, g_of_x, x)
144
mpz_add(g_of_x, g_of_x, d)
145
mpz_mul(g_of_x, g_of_x, x)
146
mpz_add(g_of_x, g_of_x, e)
147
148
if padic_square(g_of_x, p):
149
mpz_clear(g_of_x)
150
return +1 # soluble
151
152
mpz_init_set(g_prime_of_x, x)
153
mpz_mul(g_prime_of_x, a, x)
154
mpz_mul_ui(g_prime_of_x, g_prime_of_x, ui4)
155
mpz_addmul_ui(g_prime_of_x, b, ui3)
156
mpz_mul(g_prime_of_x, g_prime_of_x, x)
157
mpz_addmul_ui(g_prime_of_x, c, ui2)
158
mpz_mul(g_prime_of_x, g_prime_of_x, x)
159
mpz_add(g_prime_of_x, g_prime_of_x, d)
160
161
lambd = valuation(g_of_x, p)
162
if mpz_sgn(g_prime_of_x)==0:
163
if lambd >= 2*nu: result = 0 # undecided
164
else:
165
mu = valuation(g_prime_of_x, p)
166
if lambd > 2*mu: result = +1 # soluble
167
elif lambd >= 2*nu and mu >= nu: result = 0 # undecided
168
169
mpz_clear(g_prime_of_x)
170
mpz_clear(g_of_x)
171
return result
172
173
cdef int lemma7(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e,
174
mpz_t x, mpz_t p, unsigned long nu):
175
"""
176
Implements Lemma 7 of BSD's "Notes on elliptic curves, I" for p=2.
177
178
Returns -1 for insoluble, 0 for undecided, +1 for soluble.
179
"""
180
cdef mpz_t g_of_x, g_prime_of_x, g_of_x_odd_part
181
cdef unsigned long lambd, mu, g_of_x_odd_part_mod_4
182
cdef int result = -1
183
184
mpz_init(g_of_x)
185
mpz_mul(g_of_x, a, x)
186
mpz_add(g_of_x, g_of_x, b)
187
mpz_mul(g_of_x, g_of_x, x)
188
mpz_add(g_of_x, g_of_x, c)
189
mpz_mul(g_of_x, g_of_x, x)
190
mpz_add(g_of_x, g_of_x, d)
191
mpz_mul(g_of_x, g_of_x, x)
192
mpz_add(g_of_x, g_of_x, e)
193
194
if padic_square(g_of_x, p):
195
mpz_clear(g_of_x)
196
return +1 # soluble
197
198
mpz_init_set(g_prime_of_x, x)
199
mpz_mul(g_prime_of_x, a, x)
200
mpz_mul_ui(g_prime_of_x, g_prime_of_x, ui4)
201
mpz_addmul_ui(g_prime_of_x, b, ui3)
202
mpz_mul(g_prime_of_x, g_prime_of_x, x)
203
mpz_addmul_ui(g_prime_of_x, c, ui2)
204
mpz_mul(g_prime_of_x, g_prime_of_x, x)
205
mpz_add(g_prime_of_x, g_prime_of_x, d)
206
207
lambd = valuation(g_of_x, p)
208
mpz_init_set(g_of_x_odd_part, g_of_x)
209
while mpz_even_p(g_of_x_odd_part):
210
mpz_divexact_ui(g_of_x_odd_part, g_of_x_odd_part, ui2)
211
g_of_x_odd_part_mod_4 = mpz_fdiv_ui(g_of_x_odd_part, ui4)
212
if mpz_sgn(g_prime_of_x)==0:
213
if lambd >= 2*nu: result = 0 # undecided
214
elif lambd == 2*nu-2 and g_of_x_odd_part_mod_4==1:
215
result = 0 # undecided
216
else:
217
mu = valuation(g_prime_of_x, p)
218
if lambd > 2*mu: result = +1 # soluble
219
elif nu > mu:
220
if lambd >= mu+nu: result = +1 # soluble
221
elif lambd+1 == mu+nu and lambd&ui1==0:
222
result = +1 # soluble
223
elif lambd+2 == mu+nu and lambd&ui1==0 and g_of_x_odd_part_mod_4==1:
224
result = +1 # soluble
225
else: # nu <= mu
226
if lambd >= 2*nu: result = 0 # undecided
227
elif lambd+2 == 2*nu and g_of_x_odd_part_mod_4==1:
228
result = 0 # undecided
229
230
mpz_clear(g_prime_of_x)
231
mpz_clear(g_of_x)
232
return result
233
234
cdef int Zp_soluble_BSD(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e,
235
mpz_t x_k, mpz_t p, unsigned long k):
236
"""
237
Uses the approach of BSD's "Notes on elliptic curves, I" to test for
238
solubility of y^2 == ax^4 + bx^3 + cx^2 + dx + e over Zp, with
239
x=x_k (mod p^k).
240
"""
241
# returns solubility of y^2 = ax^4 + bx^3 + cx^2 + dx + e
242
# in Zp with x=x_k (mod p^k)
243
cdef int code
244
cdef unsigned long t
245
cdef mpz_t s
246
247
if mpz_cmp_ui(p, ui2)==0:
248
code = lemma7(a,b,c,d,e,x_k,p,k)
249
else:
250
code = lemma6(a,b,c,d,e,x_k,p,k)
251
if code == 1:
252
return 1
253
if code == -1:
254
return 0
255
256
# now code == 0
257
t = 0
258
mpz_init(s)
259
while code == 0 and mpz_cmp_ui(p, t) > 0 and t < N_RES_CLASSES_BSD:
260
mpz_pow_ui(s, p, k)
261
mpz_mul_ui(s, s, t)
262
mpz_add(s, s, x_k)
263
code = Zp_soluble_BSD(a,b,c,d,e,s,p,k+1)
264
t += 1
265
mpz_clear(s)
266
return code
267
268
cdef bint Zp_soluble_siksek(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e,
269
mpz_t pp, unsigned long pp_ui,
270
zmod_poly_factor_t f_factzn, zmod_poly_t f,
271
fmpz_poly_t f1, fmpz_poly_t linear,
272
double pp_ui_inv):
273
"""
274
Uses the approach of Algorithm 5.3.1 of Siksek's thesis to test for
275
solubility of y^2 == ax^4 + bx^3 + cx^2 + dx + e over Zp.
276
"""
277
cdef unsigned long v_min, v
278
cdef unsigned long roots[4]
279
cdef int i, j, has_roots, has_single_roots
280
cdef bint result
281
282
cdef mpz_t aa, bb, cc, dd, ee
283
cdef mpz_t aaa, bbb, ccc, ddd, eee
284
cdef unsigned long qq
285
cdef unsigned long rr, ss
286
cdef mpz_t tt
287
288
# Step 0: divide out all common p from the quartic
289
v_min = valuation(a, pp)
290
if mpz_cmp_ui(b, ui0) != 0:
291
v = valuation(b, pp)
292
if v < v_min: v_min = v
293
if mpz_cmp_ui(c, ui0) != 0:
294
v = valuation(c, pp)
295
if v < v_min: v_min = v
296
if mpz_cmp_ui(d, ui0) != 0:
297
v = valuation(d, pp)
298
if v < v_min: v_min = v
299
if mpz_cmp_ui(e, ui0) != 0:
300
v = valuation(e, pp)
301
if v < v_min: v_min = v
302
for 0 <= v < v_min:
303
mpz_divexact(a, a, pp)
304
mpz_divexact(b, b, pp)
305
mpz_divexact(c, c, pp)
306
mpz_divexact(d, d, pp)
307
mpz_divexact(e, e, pp)
308
309
if not v_min%2:
310
# Step I in Alg. 5.3.1 of Siksek's thesis
311
zmod_poly_set_coeff_ui(f, 0, mpz_fdiv_ui(e, pp_ui))
312
zmod_poly_set_coeff_ui(f, 1, mpz_fdiv_ui(d, pp_ui))
313
zmod_poly_set_coeff_ui(f, 2, mpz_fdiv_ui(c, pp_ui))
314
zmod_poly_set_coeff_ui(f, 3, mpz_fdiv_ui(b, pp_ui))
315
zmod_poly_set_coeff_ui(f, 4, mpz_fdiv_ui(a, pp_ui))
316
317
result = 0
318
(<zmod_poly_factor_struct *>f_factzn)[0].num_factors = 0 # reset data struct
319
qq = zmod_poly_factor(f_factzn, f)
320
for i from 0 <= i < f_factzn.num_factors:
321
if f_factzn.exponents[i]&1:
322
result = 1
323
break
324
if result == 0 and z_legendre_precomp(qq, pp_ui, pp_ui_inv) == 1:
325
result = 1
326
if result:
327
return 1
328
329
zmod_poly_zero(f)
330
zmod_poly_set_coeff_ui(f, 0, ui1)
331
for i from 0 <= i < f_factzn.num_factors:
332
for j from 0 <= j < (f_factzn.exponents[i]>>1):
333
zmod_poly_mul(f, f, f_factzn.factors[i])
334
335
(<zmod_poly_factor_struct *>f_factzn)[0].num_factors = 0 # reset data struct
336
zmod_poly_factor(f_factzn, f)
337
has_roots = 0
338
j = 0
339
for i from 0 <= i < f_factzn.num_factors:
340
if zmod_poly_degree(f_factzn.factors[i]) == 1 and 0 != zmod_poly_get_coeff_ui(f_factzn.factors[i], 1):
341
has_roots = 1
342
roots[j] = pp_ui - zmod_poly_get_coeff_ui(f_factzn.factors[i], 0)
343
j += 1
344
if not has_roots:
345
return 0
346
347
i = zmod_poly_degree(f)
348
mpz_init(aaa)
349
mpz_init(bbb)
350
mpz_init(ccc)
351
mpz_init(ddd)
352
mpz_init(eee)
353
354
if i == 0: # g == 1
355
mpz_set(aaa, a)
356
mpz_set(bbb, b)
357
mpz_set(ccc, c)
358
mpz_set(ddd, d)
359
mpz_sub_ui(eee, e, qq)
360
elif i == 1: # g == x + rr
361
mpz_set(aaa, a)
362
mpz_set(bbb, b)
363
mpz_sub_ui(ccc, c, qq)
364
rr = zmod_poly_get_coeff_ui(f, 0)
365
ss = rr*qq
366
mpz_set(ddd,d)
367
mpz_sub_ui(ddd, ddd, ss*ui2)
368
mpz_set(eee,e)
369
mpz_sub_ui(eee, eee, ss*rr)
370
elif i == 2: # g == x^2 + rr*x + ss
371
mpz_sub_ui(aaa, a, qq)
372
rr = zmod_poly_get_coeff_ui(f, 1)
373
mpz_init(tt)
374
mpz_set_ui(tt, rr*qq)
375
mpz_set(bbb,b)
376
mpz_submul_ui(bbb, tt, ui2)
377
mpz_set(ccc,c)
378
mpz_submul_ui(ccc, tt, rr)
379
ss = zmod_poly_get_coeff_ui(f, 0)
380
mpz_set_ui(tt, ss*qq)
381
mpz_set(eee,e)
382
mpz_submul_ui(eee, tt, ss)
383
mpz_mul_ui(tt, tt, ui2)
384
mpz_sub(ccc, ccc, tt)
385
mpz_set(ddd,d)
386
mpz_submul_ui(ddd, tt, rr)
387
mpz_clear(tt)
388
mpz_divexact(aaa, aaa, pp)
389
mpz_divexact(bbb, bbb, pp)
390
mpz_divexact(ccc, ccc, pp)
391
mpz_divexact(ddd, ddd, pp)
392
mpz_divexact(eee, eee, pp)
393
# now aaa,bbb,ccc,ddd,eee represents h(x)
394
395
result = 0
396
mpz_init(tt)
397
for i from 0 <= i < j:
398
mpz_mul_ui(tt, aaa, roots[i])
399
mpz_add(tt, tt, bbb)
400
mpz_mul_ui(tt, tt, roots[i])
401
mpz_add(tt, tt, ccc)
402
mpz_mul_ui(tt, tt, roots[i])
403
mpz_add(tt, tt, ddd)
404
mpz_mul_ui(tt, tt, roots[i])
405
mpz_add(tt, tt, eee)
406
# tt == h(r) mod p
407
mpz_mod(tt, tt, pp)
408
if mpz_sgn(tt) == 0:
409
fmpz_poly_zero(f1)
410
fmpz_poly_zero(linear)
411
fmpz_poly_set_coeff_mpz(f1, 0, e)
412
fmpz_poly_set_coeff_mpz(f1, 1, d)
413
fmpz_poly_set_coeff_mpz(f1, 2, c)
414
fmpz_poly_set_coeff_mpz(f1, 3, b)
415
fmpz_poly_set_coeff_mpz(f1, 4, a)
416
fmpz_poly_set_coeff_ui(linear, 0, roots[i])
417
fmpz_poly_set_coeff_mpz(linear, 1, pp)
418
fmpz_poly_compose(f1, f1, linear)
419
fmpz_poly_scalar_div_ui(f1, f1, pp_ui)
420
fmpz_poly_scalar_div_ui(f1, f1, pp_ui)
421
mpz_init(aa)
422
mpz_init(bb)
423
mpz_init(cc)
424
mpz_init(dd)
425
mpz_init(ee)
426
fmpz_poly_get_coeff_mpz(aa, f1, 4)
427
fmpz_poly_get_coeff_mpz(bb, f1, 3)
428
fmpz_poly_get_coeff_mpz(cc, f1, 2)
429
fmpz_poly_get_coeff_mpz(dd, f1, 1)
430
fmpz_poly_get_coeff_mpz(ee, f1, 0)
431
result = Zp_soluble_siksek(aa, bb, cc, dd, ee, pp, pp_ui, f_factzn, f, f1, linear, pp_ui_inv)
432
mpz_clear(aa)
433
mpz_clear(bb)
434
mpz_clear(cc)
435
mpz_clear(dd)
436
mpz_clear(ee)
437
if result == 1:
438
break
439
mpz_clear(aaa)
440
mpz_clear(bbb)
441
mpz_clear(ccc)
442
mpz_clear(ddd)
443
mpz_clear(eee)
444
mpz_clear(tt)
445
return result
446
else:
447
# Step II in Alg. 5.3.1 of Siksek's thesis
448
zmod_poly_set_coeff_ui(f, 0, mpz_fdiv_ui(e, pp_ui))
449
zmod_poly_set_coeff_ui(f, 1, mpz_fdiv_ui(d, pp_ui))
450
zmod_poly_set_coeff_ui(f, 2, mpz_fdiv_ui(c, pp_ui))
451
zmod_poly_set_coeff_ui(f, 3, mpz_fdiv_ui(b, pp_ui))
452
zmod_poly_set_coeff_ui(f, 4, mpz_fdiv_ui(a, pp_ui))
453
(<zmod_poly_factor_struct *>f_factzn)[0].num_factors = 0 # reset data struct
454
zmod_poly_factor(f_factzn, f)
455
has_roots = 0
456
has_single_roots = 0
457
j = 0
458
for i from 0 <= i < f_factzn.num_factors:
459
if zmod_poly_degree(f_factzn.factors[i]) == 1 and 0 != zmod_poly_get_coeff_ui(f_factzn.factors[i], 1):
460
has_roots = 1
461
if f_factzn.exponents[i] == 1:
462
has_single_roots = 1
463
break
464
roots[j] = pp_ui - zmod_poly_get_coeff_ui(f_factzn.factors[i], 0)
465
j += 1
466
467
if not has_roots: return 0
468
if has_single_roots: return 1
469
470
result = 0
471
if j > 0:
472
mpz_init(aa)
473
mpz_init(bb)
474
mpz_init(cc)
475
mpz_init(dd)
476
mpz_init(ee)
477
for i from 0 <= i < j:
478
fmpz_poly_zero(f1)
479
fmpz_poly_zero(linear)
480
fmpz_poly_set_coeff_mpz(f1, 0, e)
481
fmpz_poly_set_coeff_mpz(f1, 1, d)
482
fmpz_poly_set_coeff_mpz(f1, 2, c)
483
fmpz_poly_set_coeff_mpz(f1, 3, b)
484
fmpz_poly_set_coeff_mpz(f1, 4, a)
485
fmpz_poly_set_coeff_ui(linear, 0, roots[i])
486
fmpz_poly_set_coeff_mpz(linear, 1, pp)
487
fmpz_poly_compose(f1, f1, linear)
488
fmpz_poly_scalar_div_ui(f1, f1, pp_ui)
489
fmpz_poly_get_coeff_mpz(aa, f1, 4)
490
fmpz_poly_get_coeff_mpz(bb, f1, 3)
491
fmpz_poly_get_coeff_mpz(cc, f1, 2)
492
fmpz_poly_get_coeff_mpz(dd, f1, 1)
493
fmpz_poly_get_coeff_mpz(ee, f1, 0)
494
result = Zp_soluble_siksek(aa, bb, cc, dd, ee, pp, pp_ui, f_factzn, f, f1, linear, pp_ui_inv)
495
if result == 1:
496
break
497
if j > 0:
498
mpz_clear(aa)
499
mpz_clear(bb)
500
mpz_clear(cc)
501
mpz_clear(dd)
502
mpz_clear(ee)
503
return result
504
505
cdef bint Zp_soluble_siksek_large_p(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, mpz_t pp,
506
fmpz_poly_t f1, fmpz_poly_t linear):
507
"""
508
Uses the approach of Algorithm 5.3.1 of Siksek's thesis to test for
509
solubility of y^2 == ax^4 + bx^3 + cx^2 + dx + e over Zp.
510
"""
511
cdef unsigned long v_min, v
512
cdef mpz_t roots[4]
513
cdef int i, j, has_roots, has_single_roots
514
cdef bint result
515
516
cdef mpz_t aa, bb, cc, dd, ee
517
cdef mpz_t aaa, bbb, ccc, ddd, eee
518
cdef mpz_t qq, rr, ss, tt
519
cdef Integer A,B,C,D,E,P
520
521
# Step 0: divide out all common p from the quartic
522
v_min = valuation(a, pp)
523
if mpz_cmp_ui(b, ui0) != 0:
524
v = valuation(b, pp)
525
if v < v_min: v_min = v
526
if mpz_cmp_ui(c, ui0) != 0:
527
v = valuation(c, pp)
528
if v < v_min: v_min = v
529
if mpz_cmp_ui(d, ui0) != 0:
530
v = valuation(d, pp)
531
if v < v_min: v_min = v
532
if mpz_cmp_ui(e, ui0) != 0:
533
v = valuation(e, pp)
534
if v < v_min: v_min = v
535
for 0 <= v < v_min:
536
mpz_divexact(a, a, pp)
537
mpz_divexact(b, b, pp)
538
mpz_divexact(c, c, pp)
539
mpz_divexact(d, d, pp)
540
mpz_divexact(e, e, pp)
541
542
if not v_min%2:
543
# Step I in Alg. 5.3.1 of Siksek's thesis
544
A = Integer(0); B = Integer(0); C = Integer(0); D = Integer(0); E = Integer(0); P = Integer(0)
545
mpz_set(A.value, a); mpz_set(B.value, b); mpz_set(C.value, c); mpz_set(D.value, d); mpz_set(E.value, e); mpz_set(P.value, pp)
546
f = ntl.ZZ_pX([E,D,C,B,A], P)
547
f /= ntl.ZZ_pX([A], P) # now f is monic, and we are done with A,B,C,D,E
548
mpz_set(qq, A.value) # qq is the leading coefficient of the polynomial
549
f_factzn = f.factor()
550
result = 0
551
for factor, exponent in f_factzn:
552
if exponent&1:
553
result = 1
554
break
555
if result == 0 and mpz_legendre(qq, pp) == 1:
556
result = 1
557
if result:
558
return 1
559
560
f = ntl.ZZ_pX([1], P)
561
for factor, exponent in f_factzn:
562
for j from 0 <= j < (exponent/2):
563
f *= factor
564
565
f /= f.leading_coefficient()
566
f_factzn = f.factor()
567
568
has_roots = 0
569
j = 0
570
for factor, exponent in f_factzn:
571
if factor.degree() == 1:
572
has_roots = 1
573
A = P - Integer(factor[0])
574
mpz_set(roots[j], A.value)
575
j += 1
576
if not has_roots:
577
return 0
578
579
i = f.degree()
580
mpz_init(aaa)
581
mpz_init(bbb)
582
mpz_init(ccc)
583
mpz_init(ddd)
584
mpz_init(eee)
585
586
if i == 0: # g == 1
587
mpz_set(aaa, a)
588
mpz_set(bbb, b)
589
mpz_set(ccc, c)
590
mpz_set(ddd, d)
591
mpz_sub(eee, e, qq)
592
elif i == 1: # g == x + rr
593
mpz_set(aaa, a)
594
mpz_set(bbb, b)
595
mpz_sub(ccc, c, qq)
596
A = Integer(f[0])
597
mpz_set(rr, A.value)
598
mpz_mul(ss, rr, qq)
599
mpz_set(ddd,d)
600
mpz_sub(ddd, ddd, ss)
601
mpz_sub(ddd, ddd, ss)
602
mpz_set(eee,e)
603
mpz_mul(ss, ss, rr)
604
mpz_sub(eee, eee, ss)
605
mpz_divexact(ss, ss, rr)
606
elif i == 2: # g == x^2 + rr*x + ss
607
mpz_sub(aaa, a, qq)
608
A = Integer(f[1])
609
mpz_set(rr, A.value)
610
mpz_init(tt)
611
mpz_mul(tt, rr, qq)
612
mpz_set(bbb,b)
613
mpz_submul_ui(bbb, tt, ui2)
614
mpz_set(ccc,c)
615
mpz_submul(ccc, tt, rr)
616
A = Integer(f[0])
617
mpz_set(ss, A.value)
618
mpz_mul(tt, ss, qq)
619
mpz_set(eee,e)
620
mpz_submul(eee, tt, ss)
621
mpz_mul_ui(tt, tt, ui2)
622
mpz_sub(ccc, ccc, tt)
623
mpz_set(ddd,d)
624
mpz_submul(ddd, tt, rr)
625
mpz_clear(tt)
626
mpz_divexact(aaa, aaa, pp)
627
mpz_divexact(bbb, bbb, pp)
628
mpz_divexact(ccc, ccc, pp)
629
mpz_divexact(ddd, ddd, pp)
630
mpz_divexact(eee, eee, pp)
631
# now aaa,bbb,ccc,ddd,eee represents h(x)
632
633
result = 0
634
mpz_init(tt)
635
for i from 0 <= i < j:
636
mpz_mul(tt, aaa, roots[i])
637
mpz_add(tt, tt, bbb)
638
mpz_mul(tt, tt, roots[i])
639
mpz_add(tt, tt, ccc)
640
mpz_mul(tt, tt, roots[i])
641
mpz_add(tt, tt, ddd)
642
mpz_mul(tt, tt, roots[i])
643
mpz_add(tt, tt, eee)
644
# tt == h(r) mod p
645
mpz_mod(tt, tt, pp)
646
if mpz_sgn(tt) == 0:
647
fmpz_poly_zero(f1)
648
fmpz_poly_zero(linear)
649
fmpz_poly_set_coeff_mpz(f1, 0, e)
650
fmpz_poly_set_coeff_mpz(f1, 1, d)
651
fmpz_poly_set_coeff_mpz(f1, 2, c)
652
fmpz_poly_set_coeff_mpz(f1, 3, b)
653
fmpz_poly_set_coeff_mpz(f1, 4, a)
654
fmpz_poly_set_coeff_mpz(linear, 0, roots[i])
655
fmpz_poly_set_coeff_mpz(linear, 1, pp)
656
fmpz_poly_compose(f1, f1, linear)
657
fmpz_poly_scalar_div_mpz(f1, f1, pp)
658
fmpz_poly_scalar_div_mpz(f1, f1, pp)
659
660
mpz_init(aa)
661
mpz_init(bb)
662
mpz_init(cc)
663
mpz_init(dd)
664
mpz_init(ee)
665
fmpz_poly_get_coeff_mpz(aa, f1, 4)
666
fmpz_poly_get_coeff_mpz(bb, f1, 3)
667
fmpz_poly_get_coeff_mpz(cc, f1, 2)
668
fmpz_poly_get_coeff_mpz(dd, f1, 1)
669
fmpz_poly_get_coeff_mpz(ee, f1, 0)
670
result = Zp_soluble_siksek_large_p(aa, bb, cc, dd, ee, pp, f1, linear)
671
mpz_clear(aa)
672
mpz_clear(bb)
673
mpz_clear(cc)
674
mpz_clear(dd)
675
mpz_clear(ee)
676
if result == 1:
677
break
678
mpz_clear(aaa)
679
mpz_clear(bbb)
680
mpz_clear(ccc)
681
mpz_clear(ddd)
682
mpz_clear(eee)
683
mpz_clear(tt)
684
return result
685
else:
686
# Step II in Alg. 5.3.1 of Siksek's thesis
687
A = Integer(0); B = Integer(0); C = Integer(0); D = Integer(0); E = Integer(0); P = Integer(0)
688
mpz_set(A.value, a); mpz_set(B.value, b); mpz_set(C.value, c); mpz_set(D.value, d); mpz_set(E.value, e); mpz_set(P.value, pp)
689
f = ntl.ZZ_pX([E,D,C,B,A], P)
690
f /= ntl.ZZ_pX([A], P) # now f is monic
691
f_factzn = f.factor()
692
693
has_roots = 0
694
has_single_roots = 0
695
j = 0
696
for factor, exponent in f_factzn:
697
if factor.degree() == 1:
698
has_roots = 1
699
if exponent == 1:
700
has_single_roots = 1
701
break
702
A = P - Integer(factor[0])
703
mpz_set(roots[j], A.value)
704
j += 1
705
706
if not has_roots: return 0
707
if has_single_roots: return 1
708
709
result = 0
710
if j > 0:
711
mpz_init(aa)
712
mpz_init(bb)
713
mpz_init(cc)
714
mpz_init(dd)
715
mpz_init(ee)
716
for i from 0 <= i < j:
717
fmpz_poly_zero(f1)
718
fmpz_poly_zero(linear)
719
fmpz_poly_set_coeff_mpz(f1, 0, e)
720
fmpz_poly_set_coeff_mpz(f1, 1, d)
721
fmpz_poly_set_coeff_mpz(f1, 2, c)
722
fmpz_poly_set_coeff_mpz(f1, 3, b)
723
fmpz_poly_set_coeff_mpz(f1, 4, a)
724
fmpz_poly_set_coeff_mpz(linear, 0, roots[i])
725
fmpz_poly_set_coeff_mpz(linear, 1, pp)
726
fmpz_poly_compose(f1, f1, linear)
727
fmpz_poly_scalar_div_mpz(f1, f1, pp)
728
fmpz_poly_get_coeff_mpz(aa, f1, 4)
729
fmpz_poly_get_coeff_mpz(bb, f1, 3)
730
fmpz_poly_get_coeff_mpz(cc, f1, 2)
731
fmpz_poly_get_coeff_mpz(dd, f1, 1)
732
fmpz_poly_get_coeff_mpz(ee, f1, 0)
733
result = Zp_soluble_siksek_large_p(aa, bb, cc, dd, ee, pp, f1, linear)
734
if result == 1:
735
break
736
if j > 0:
737
mpz_clear(aa)
738
mpz_clear(bb)
739
mpz_clear(cc)
740
mpz_clear(dd)
741
mpz_clear(ee)
742
return result
743
744
cdef bint Qp_soluble_siksek(mpz_t A, mpz_t B, mpz_t C, mpz_t D, mpz_t E,
745
mpz_t p, unsigned long P,
746
zmod_poly_factor_t f_factzn, fmpz_poly_t f1,
747
fmpz_poly_t linear):
748
"""
749
Uses Samir Siksek's thesis results to determine whether the quartic is
750
locally soluble at p.
751
"""
752
cdef int result = 0
753
cdef mpz_t a,b,c,d,e
754
cdef zmod_poly_t f
755
cdef double pp_ui_inv = z_precompute_inverse(P)
756
zmod_poly_init(f, P)
757
758
mpz_init_set(a,A)
759
mpz_init_set(b,B)
760
mpz_init_set(c,C)
761
mpz_init_set(d,D)
762
mpz_init_set(e,E)
763
764
if Zp_soluble_siksek(a,b,c,d,e,p,P,f_factzn, f, f1, linear, pp_ui_inv):
765
result = 1
766
else:
767
mpz_set(a,A)
768
mpz_set(b,B)
769
mpz_set(c,C)
770
mpz_set(d,D)
771
mpz_set(e,E)
772
if Zp_soluble_siksek(e,d,c,b,a,p,P,f_factzn, f, f1, linear, pp_ui_inv):
773
result = 1
774
775
mpz_clear(a)
776
mpz_clear(b)
777
mpz_clear(c)
778
mpz_clear(d)
779
mpz_clear(e)
780
zmod_poly_clear(f)
781
return result
782
783
cdef bint Qp_soluble_siksek_large_p(mpz_t A, mpz_t B, mpz_t C, mpz_t D, mpz_t E,
784
mpz_t p, fmpz_poly_t f1, fmpz_poly_t linear):
785
"""
786
Uses Samir Siksek's thesis results to determine whether the quartic is
787
locally soluble at p, when p is bigger than wordsize, and we can't use
788
FLINT.
789
"""
790
cdef int result = 0
791
cdef mpz_t a,b,c,d,e
792
793
mpz_init_set(a,A)
794
mpz_init_set(b,B)
795
mpz_init_set(c,C)
796
mpz_init_set(d,D)
797
mpz_init_set(e,E)
798
799
if Zp_soluble_siksek_large_p(a,b,c,d,e,p,f1,linear):
800
result = 1
801
else:
802
mpz_set(a,A)
803
mpz_set(b,B)
804
mpz_set(c,C)
805
mpz_set(d,D)
806
mpz_set(e,E)
807
if Zp_soluble_siksek_large_p(e,d,c,b,a,p,f1,linear):
808
result = 1
809
810
mpz_clear(a)
811
mpz_clear(b)
812
mpz_clear(c)
813
mpz_clear(d)
814
mpz_clear(e)
815
return result
816
817
cdef bint Qp_soluble_BSD(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, mpz_t p):
818
"""
819
Uses the original test of Birch and Swinnerton-Dyer to test for local
820
solubility of the quartic at p.
821
"""
822
cdef mpz_t zero
823
cdef int result = 0
824
mpz_init_set_ui(zero, ui0)
825
if Zp_soluble_BSD(a,b,c,d,e,zero,p,0):
826
result = 1
827
elif Zp_soluble_BSD(e,d,c,b,a,zero,p,1):
828
result = 1
829
mpz_clear(zero)
830
return result
831
832
cdef bint Qp_soluble(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e, mpz_t p):
833
"""
834
Try the BSD approach for a few residue classes and if no solution is found,
835
switch to Siksek to try to prove insolubility.
836
"""
837
cdef int bsd_sol, sik_sol
838
cdef unsigned long pp
839
cdef fmpz_poly_t f1, linear
840
cdef zmod_poly_factor_t f_factzn
841
bsd_sol = Qp_soluble_BSD(a, b, c, d, e, p)
842
if mpz_cmp_ui(p,N_RES_CLASSES_BSD)>0 and not bsd_sol:
843
fmpz_poly_init(f1)
844
fmpz_poly_init(linear)
845
if mpz_fits_ulong_p(p):
846
zmod_poly_factor_init(f_factzn)
847
pp = mpz_get_ui(p)
848
sik_sol = Qp_soluble_siksek(a,b,c,d,e,p,pp,f_factzn,f1,linear)
849
zmod_poly_factor_clear(f_factzn)
850
else:
851
sik_sol = Qp_soluble_siksek_large_p(a,b,c,d,e,p,f1,linear)
852
fmpz_poly_clear(f1)
853
fmpz_poly_clear(linear)
854
else:
855
sik_sol = bsd_sol
856
return sik_sol
857
858
def test_qpls(a,b,c,d,e,p):
859
"""
860
Testing function for Qp_soluble.
861
862
EXAMPLE:
863
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import test_qpls as tq
864
sage: tq(1,2,3,4,5,7)
865
1
866
867
"""
868
cdef Integer A,B,C,D,E,P
869
cdef int i, result
870
cdef mpz_t aa,bb,cc,dd,ee,pp
871
A=Integer(a); B=Integer(b); C=Integer(c); D=Integer(d); E=Integer(e); P=Integer(p)
872
mpz_init_set(aa, A.value)
873
mpz_init_set(bb, B.value)
874
mpz_init_set(cc, C.value)
875
mpz_init_set(dd, D.value)
876
mpz_init_set(ee, E.value)
877
mpz_init_set(pp, P.value)
878
result = Qp_soluble(aa, bb, cc, dd, ee, pp)
879
mpz_clear(aa)
880
mpz_clear(bb)
881
mpz_clear(cc)
882
mpz_clear(dd)
883
mpz_clear(ee)
884
mpz_clear(pp)
885
return result
886
887
cdef int everywhere_locally_soluble(mpz_t a, mpz_t b, mpz_t c, mpz_t d, mpz_t e) except -1:
888
"""
889
Returns whether the quartic has local solutions at all primes p.
890
"""
891
cdef Integer A,B,C,D,E,Delta,p
892
cdef mpz_t mpz_2
893
A=Integer(0); B=Integer(0); C=Integer(0); D=Integer(0); E=Integer(0)
894
mpz_set(A.value, a); mpz_set(B.value, b); mpz_set(C.value, c); mpz_set(D.value, d); mpz_set(E.value, e);
895
f = (((A*x_ZZ + B)*x_ZZ + C)*x_ZZ + D)*x_ZZ + E
896
897
# RR soluble:
898
if mpz_sgn(a)!=1:
899
if len(real_roots(f)) == 0:
900
return 0
901
902
# Q2 soluble:
903
mpz_init_set_ui(mpz_2, ui2)
904
if not Qp_soluble(a,b,c,d,e,mpz_2):
905
mpz_clear(mpz_2)
906
return 0
907
mpz_clear(mpz_2)
908
909
# Odd finite primes
910
Delta = f.discriminant()
911
for p in prime_divisors(Delta):
912
if p == 2: continue
913
if not Qp_soluble(a,b,c,d,e,p.value): return 0
914
915
return 1
916
917
def test_els(a,b,c,d,e):
918
"""
919
Doctest function for cdef int everywhere_locally_soluble(mpz_t, mpz_t, mpz_t, mpz_t, mpz_t).
920
921
EXAMPLE::
922
923
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import test_els
924
sage: from sage.libs.ratpoints import ratpoints
925
sage: for _ in range(1000):
926
... a,b,c,d,e = randint(1,1000), randint(1,1000), randint(1,1000), randint(1,1000), randint(1,1000)
927
... if len(ratpoints([e,d,c,b,a], 1000)) > 0:
928
... try:
929
... if not test_els(a,b,c,d,e):
930
... print "This never happened", a,b,c,d,e
931
... except ValueError:
932
... continue
933
934
"""
935
cdef Integer A,B,C,D,E,Delta
936
A=Integer(a); B=Integer(b); C=Integer(c); D=Integer(d); E=Integer(e)
937
return everywhere_locally_soluble(A.value, B.value, C.value, D.value, E.value)
938
939
cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len,
940
int global_limit_small, int global_limit_large,
941
int verbosity, bint selmer_only, mpz_t n1, mpz_t n2) except -1:
942
"""
943
Count the number of els/gls quartic 2-covers of E.
944
"""
945
cdef unsigned long n_primes, i
946
cdef bint found_global_points, els, check_negs, verbose = (verbosity > 4)
947
cdef Integer a_Int, c_Int, e_Int
948
cdef mpz_t c_sq_mpz, d_prime_mpz
949
cdef mpz_t n_divisors, j
950
cdef mpz_t *coeffs_ratp
951
952
953
mpz_init(c_sq_mpz)
954
mpz_mul(c_sq_mpz, c_mpz, c_mpz)
955
mpz_init_set(d_prime_mpz, c_sq_mpz)
956
mpz_submul_ui(d_prime_mpz, d_mpz, ui4)
957
check_negs = 0
958
if mpz_sgn(d_prime_mpz) > 0:
959
if mpz_sgn(c_mpz) >= 0 or mpz_cmp(c_sq_mpz, d_prime_mpz) <= 0:
960
check_negs = 1
961
mpz_clear(c_sq_mpz)
962
mpz_clear(d_prime_mpz)
963
964
965
# Set up coefficient array, and static variables
966
cdef mpz_t *coeffs = <mpz_t *> sage_malloc(5 * sizeof(mpz_t))
967
for i from 0 <= i <= 4:
968
mpz_init(coeffs[i])
969
mpz_set_ui(coeffs[1], ui0) #
970
mpz_set(coeffs[2], c_mpz) # These never change
971
mpz_set_ui(coeffs[3], ui0) #
972
973
if not selmer_only:
974
# allocate space for ratpoints
975
coeffs_ratp = <mpz_t *> sage_malloc(5 * sizeof(mpz_t))
976
for i from 0 <= i <= 4:
977
mpz_init(coeffs_ratp[i])
978
979
# Get prime divisors, and put them in an mpz_t array
980
# (this block, by setting check_negs, takes care of
981
# local solubility over RR)
982
cdef mpz_t *p_div_d_mpz = <mpz_t *> sage_malloc((p_list_len+1) * sizeof(mpz_t))
983
n_primes = 0
984
for i from 0 <= i < p_list_len:
985
if mpz_divisible_p(d_mpz, p_list[i]):
986
mpz_init(p_div_d_mpz[n_primes])
987
mpz_set(p_div_d_mpz[n_primes], p_list[i])
988
n_primes += 1
989
if check_negs:
990
mpz_init(p_div_d_mpz[n_primes])
991
mpz_set_si(p_div_d_mpz[n_primes], -1)
992
n_primes += 1
993
mpz_init_set_ui(n_divisors, ui1)
994
mpz_mul_2exp(n_divisors, n_divisors, n_primes)
995
# if verbosity > 3:
996
# print '\nDivisors of d which may lead to RR-soluble quartics:', p_div_d
997
998
mpz_init_set_ui(j, ui0)
999
if not selmer_only:
1000
mpz_set_ui(n1, ui0)
1001
mpz_set_ui(n2, ui0)
1002
while mpz_cmp(j, n_divisors) < 0:
1003
mpz_set_ui(coeffs[4], ui1)
1004
for i from 0 <= i < n_primes:
1005
if mpz_tstbit(j, i):
1006
mpz_mul(coeffs[4], coeffs[4], p_div_d_mpz[i])
1007
if verbosity > 3:
1008
a_Int = Integer(0); mpz_set(a_Int.value, coeffs[4])
1009
print '\nSquarefree divisor:', a_Int
1010
mpz_divexact(coeffs[0], d_mpz, coeffs[4])
1011
found_global_points = 0
1012
if not selmer_only:
1013
if verbose:
1014
print "\nCalling ratpoints for small point search"
1015
for i from 0 <= i <= 4:
1016
mpz_set(coeffs_ratp[i], coeffs[i])
1017
sig_on()
1018
found_global_points = ratpoints_mpz_exists_only(coeffs_ratp, global_limit_small, 4, verbose)
1019
sig_off()
1020
if found_global_points:
1021
if verbosity > 2:
1022
a_Int = Integer(0); mpz_set(a_Int.value, coeffs[4])
1023
c_Int = Integer(0); mpz_set(c_Int.value, coeffs[2])
1024
e_Int = Integer(0); mpz_set(e_Int.value, coeffs[0])
1025
print 'Found small global point, quartic (%d,%d,%d,%d,%d)'%(a_Int,0,c_Int,0,e_Int)
1026
mpz_add_ui(n1, n1, ui1)
1027
mpz_add_ui(n2, n2, ui1)
1028
if verbose:
1029
print "\nDone calling ratpoints for small point search"
1030
if not found_global_points:
1031
# Test whether the quartic is everywhere locally soluble:
1032
els = 1
1033
for i from 0 <= i < p_list_len:
1034
if not Qp_soluble(coeffs[4], coeffs[3], coeffs[2], coeffs[1], coeffs[0], p_list[i]):
1035
els = 0
1036
break
1037
if els:
1038
if verbosity > 2:
1039
a_Int = Integer(0); mpz_set(a_Int.value, coeffs[4])
1040
c_Int = Integer(0); mpz_set(c_Int.value, coeffs[2])
1041
e_Int = Integer(0); mpz_set(e_Int.value, coeffs[0])
1042
print 'ELS without small global points, quartic (%d,%d,%d,%d,%d)'%(a_Int,0,c_Int,0,e_Int)
1043
mpz_add_ui(n2, n2, ui1)
1044
if not selmer_only:
1045
if verbose:
1046
print "\nCalling ratpoints for large point search"
1047
for i from 0 <= i <= 4:
1048
mpz_set(coeffs_ratp[i], coeffs[i])
1049
sig_on()
1050
found_global_points = ratpoints_mpz_exists_only(coeffs_ratp, global_limit_large, 4, verbose)
1051
sig_off()
1052
if found_global_points:
1053
if verbosity > 2:
1054
print ' -- Found large global point.'
1055
mpz_add_ui(n1, n1, ui1)
1056
if verbose:
1057
print "\nDone calling ratpoints for large point search"
1058
mpz_add_ui(j, j, ui1)
1059
if not selmer_only:
1060
for i from 0 <= i <= 4:
1061
mpz_clear(coeffs_ratp[i])
1062
sage_free(coeffs_ratp)
1063
mpz_clear(j)
1064
for i from 0 <= i < n_primes:
1065
mpz_clear(p_div_d_mpz[i])
1066
sage_free(p_div_d_mpz)
1067
mpz_clear(n_divisors)
1068
for i from 0 <= i <= 4:
1069
mpz_clear(coeffs[i])
1070
sage_free(coeffs)
1071
return 0
1072
1073
def two_descent_by_two_isogeny(E,
1074
int global_limit_small = 10,
1075
int global_limit_large = 10000,
1076
int verbosity = 0,
1077
bint selmer_only = 0, bint proof = 1):
1078
"""
1079
Given an elliptic curve E with a two-isogeny phi : E --> E' and dual isogeny
1080
phi', runs a two-isogeny descent on E, returning n1, n2, n1' and n2'. Here
1081
n1 is the number of quartic covers found with a rational point, and n2 is
1082
the number which are ELS.
1083
1084
EXAMPLES::
1085
1086
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import two_descent_by_two_isogeny
1087
sage: E = EllipticCurve('14a')
1088
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny(E)
1089
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1090
0
1091
sage: E = EllipticCurve('65a')
1092
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny(E)
1093
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1094
1
1095
sage: x,y = var('x,y')
1096
sage: E = EllipticCurve(y^2 == x^3 + x^2 - 25*x + 39)
1097
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny(E)
1098
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1099
2
1100
sage: E = EllipticCurve(y^2 + x*y + y == x^3 - 131*x + 558)
1101
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny(E)
1102
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1103
3
1104
1105
Using the verbosity option::
1106
1107
sage: E = EllipticCurve('14a')
1108
sage: two_descent_by_two_isogeny(E, verbosity=1)
1109
2-isogeny
1110
Results:
1111
2 <= #E(Q)/phi'(E'(Q)) <= 2
1112
2 <= #E'(Q)/phi(E(Q)) <= 2
1113
#Sel^(phi')(E'/Q) = 2
1114
#Sel^(phi)(E/Q) = 2
1115
1 <= #Sha(E'/Q)[phi'] <= 1
1116
1 <= #Sha(E/Q)[phi] <= 1
1117
1 <= #Sha(E/Q)[2], #Sha(E'/Q)[2] <= 1
1118
0 <= rank of E(Q) = rank of E'(Q) <= 0
1119
(2, 2, 2, 2)
1120
1121
Handling curves whose discriminants involve larger than wordsize primes::
1122
1123
sage: E = EllipticCurve('14a')
1124
sage: E = E.quadratic_twist(next_prime(10^20))
1125
sage: E
1126
Elliptic Curve defined by y^2 = x^3 + x^2 + 716666666666666667225666666666666666775672*x - 391925925925925926384240370370370370549019837037037037060249356 over Rational Field
1127
sage: E.discriminant().factor()
1128
-1 * 2^18 * 7^3 * 100000000000000000039^6
1129
sage: log(100000000000000000039.0, 2.0)
1130
66.438...
1131
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny(E)
1132
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1133
0
1134
1135
TESTS:
1136
1137
Here we contrive an example to demonstrate that a keyboard interrupt
1138
is caught. Here we let `E` be the smallest optimal curve with two-torsion
1139
and nontrivial `Sha[2]`. This ensures that the two-descent will be looking
1140
for rational points which do not exist, and by setting global_limit_large
1141
to a very high bound, it will still be working when we simulate a ``CTRL-C``::
1142
1143
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import two_descent_by_two_isogeny
1144
sage: import sage.tests.interrupt
1145
sage: E = EllipticCurve('960d'); E
1146
Elliptic Curve defined by y^2 = x^3 - x^2 - 900*x - 10098 over Rational Field
1147
sage: E.sha().an()
1148
4
1149
sage: try:
1150
... sage.tests.interrupt.interrupt_after_delay(1000)
1151
... two_descent_by_two_isogeny(E, global_limit_large=10^8)
1152
... except KeyboardInterrupt:
1153
... print "Caught!"
1154
Caught!
1155
"""
1156
cdef Integer a1, a2, a3, a4, a6, s2, s4, s6
1157
cdef Integer c, d, x0
1158
cdef list x_list
1159
assert E.torsion_order()%2==0, 'Need rational two-torsion for isogeny descent.'
1160
if verbosity > 0:
1161
print '\n2-isogeny'
1162
if verbosity > 1:
1163
print '\nchanging coordinates'
1164
a1 = Integer(E.a1())
1165
a2 = Integer(E.a2())
1166
a3 = Integer(E.a3())
1167
a4 = Integer(E.a4())
1168
a6 = Integer(E.a6())
1169
if a1==0 and a3==0:
1170
s2=a2; s4=a4; s6=a6
1171
else:
1172
s2=a1*a1+4*a2; s4=8*(a1*a3+2*a4); s6=16*(a3*a3+4*a6)
1173
f = ((x_ZZ + s2)*x_ZZ + s4)*x_ZZ + s6
1174
x_list = f.roots() # over ZZ -- use FLINT directly?
1175
x0 = x_list[0][0]
1176
c = 3*x0+s2; d = (c+s2)*x0+s4
1177
return two_descent_by_two_isogeny_work(c, d,
1178
global_limit_small, global_limit_large, verbosity, selmer_only, proof)
1179
1180
def two_descent_by_two_isogeny_work(Integer c, Integer d,
1181
int global_limit_small = 10, int global_limit_large = 10000,
1182
int verbosity = 0, bint selmer_only = 0, bint proof = 1):
1183
"""
1184
Do all the work in doing a two-isogeny descent.
1185
1186
EXAMPLES::
1187
1188
sage: from sage.schemes.elliptic_curves.descent_two_isogeny import two_descent_by_two_isogeny_work
1189
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny_work(13,128)
1190
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1191
0
1192
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny_work(1,-16)
1193
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1194
1
1195
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny_work(10,8)
1196
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1197
2
1198
sage: n1, n2, n1_prime, n2_prime = two_descent_by_two_isogeny_work(85,320)
1199
sage: log(n1,2) + log(n1_prime,2) - 2 # the rank
1200
3
1201
1202
"""
1203
cdef mpz_t c_mpz, d_mpz, c_prime_mpz, d_prime_mpz
1204
cdef mpz_t *p_list_mpz
1205
cdef unsigned long i, j, p, p_list_len
1206
cdef Integer P, n1, n2, n1_prime, n2_prime, c_prime, d_prime
1207
cdef object PO
1208
cdef bint found, too_big, d_neg, d_prime_neg
1209
cdef factor_t fact
1210
cdef list primes
1211
mpz_init_set(c_mpz, c.value) #
1212
mpz_init_set(d_mpz, d.value) #
1213
mpz_init(c_prime_mpz) #
1214
mpz_init(d_prime_mpz) #
1215
mpz_mul_si(c_prime_mpz, c_mpz, -2)
1216
mpz_mul(d_prime_mpz, c_mpz, c_mpz)
1217
mpz_submul_ui(d_prime_mpz, d_mpz, ui4)
1218
1219
d_neg = 0
1220
d_prime_neg = 0
1221
if mpz_sgn(d_mpz) < 0:
1222
d_neg = 1
1223
mpz_neg(d_mpz, d_mpz)
1224
if mpz_sgn(d_prime_mpz) < 0:
1225
d_prime_neg = 1
1226
mpz_neg(d_prime_mpz, d_prime_mpz)
1227
if mpz_fits_ulong_p(d_mpz) and mpz_fits_ulong_p(d_prime_mpz):
1228
# Factor very quickly using FLINT.
1229
p_list_mpz = <mpz_t *> sage_malloc(20 * sizeof(mpz_t))
1230
mpz_init_set_ui(p_list_mpz[0], ui2)
1231
p_list_len = 1
1232
z_factor(&fact, mpz_get_ui(d_mpz), proof)
1233
for i from 0 <= i < fact.num:
1234
p = fact.p[i]
1235
if p != ui2:
1236
mpz_init_set_ui(p_list_mpz[p_list_len], p)
1237
p_list_len += 1
1238
z_factor(&fact, mpz_get_ui(d_prime_mpz), proof)
1239
for i from 0 <= i < fact.num:
1240
p = fact.p[i]
1241
found = 0
1242
for j from 0 <= j < p_list_len:
1243
if mpz_cmp_ui(p_list_mpz[j], p)==0:
1244
found = 1
1245
break
1246
if not found:
1247
mpz_init_set_ui(p_list_mpz[p_list_len], p)
1248
p_list_len += 1
1249
else:
1250
# Factor more slowly using Pari via Python.
1251
d = Integer(0)
1252
mpz_set(d.value, d_mpz)
1253
primes = list(pari(d).factor()[0])
1254
d_prime = Integer(0)
1255
mpz_set(d_prime.value, d_prime_mpz)
1256
for PO in pari(d_prime).factor()[0]:
1257
if PO not in primes:
1258
primes.append(PO)
1259
P = Integer(2)
1260
if P not in primes: primes.append(P)
1261
p_list_len = len(primes)
1262
p_list_mpz = <mpz_t *> sage_malloc(p_list_len * sizeof(mpz_t))
1263
for i from 0 <= i < p_list_len:
1264
P = Integer(primes[i])
1265
mpz_init_set(p_list_mpz[i], P.value)
1266
if d_neg:
1267
mpz_neg(d_mpz, d_mpz)
1268
if d_prime_neg:
1269
mpz_neg(d_prime_mpz, d_prime_mpz)
1270
1271
if verbosity > 1:
1272
c_prime = -2*c
1273
d_prime = c*c-4*d
1274
print '\nnew curve is y^2 == x( x^2 + (%d)x + (%d) )'%(int(c),int(d))
1275
print 'new isogenous curve is' + \
1276
' y^2 == x( x^2 + (%d)x + (%d) )'%(int(c_prime),int(d_prime))
1277
1278
n1 = Integer(0); n2 = Integer(0)
1279
n1_prime = Integer(0); n2_prime = Integer(0)
1280
count(c.value, d.value, p_list_mpz, p_list_len,
1281
global_limit_small, global_limit_large, verbosity, selmer_only,
1282
n1.value, n2.value)
1283
count(c_prime_mpz, d_prime_mpz, p_list_mpz, p_list_len,
1284
global_limit_small, global_limit_large, verbosity, selmer_only,
1285
n1_prime.value, n2_prime.value)
1286
1287
for i from 0 <= i < p_list_len:
1288
mpz_clear(p_list_mpz[i])
1289
sage_free(p_list_mpz)
1290
1291
if verbosity > 0:
1292
print "\nResults:"
1293
print n1, "<= #E(Q)/phi'(E'(Q)) <=", n2
1294
print n1_prime, "<= #E'(Q)/phi(E(Q)) <=", n2_prime
1295
print "#Sel^(phi')(E'/Q) =", n2
1296
print "#Sel^(phi)(E/Q) =", n2_prime
1297
print "1 <= #Sha(E'/Q)[phi'] <=", n2/n1
1298
print "1 <= #Sha(E/Q)[phi] <=", n2_prime/n1_prime
1299
print "1 <= #Sha(E/Q)[2], #Sha(E'/Q)[2] <=", (n2_prime/n1_prime)*(n2/n1)
1300
a = Integer(n1*n1_prime).log(Integer(2))
1301
e = Integer(n2*n2_prime).log(Integer(2))
1302
print a - 2, "<= rank of E(Q) = rank of E'(Q) <=", e - 2
1303
1304
return n1, n2, n1_prime, n2_prime
1305
1306
1307
1308