Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
#################################################################################
2
#
3
# (c) Copyright 2011 R. Andrew Ohana
4
#
5
# This file is part of PSAGE
6
#
7
# PSAGE is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# PSAGE is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
#################################################################################
21
22
"""
23
Fast Cython code to choose a canonical minimal model.
24
"""
25
26
include 'stdsage.pxi'
27
include 'cdefs.pxi'
28
29
from sage.schemes.elliptic_curves.constructor import EllipticCurve
30
from sage.rings.integer cimport Integer
31
from libc.stdint cimport int64_t, uint64_t
32
33
cdef void f_map64(int64_t *o):
34
cdef bint s
35
cdef uint64_t x[2],y[2],g[3],f[3],h[3],t
36
o[2] = 1
37
o[3] = 0
38
s = (o[0] < 0L)
39
# to simplify boolean checks
40
o[0] *= 1-2*s
41
o[1] *= 1-2*s
42
x[1] = o[0] >> 32
43
x[0] = o[0]&0xFFFFFFFF
44
# to avoid multiplying by negative ints
45
y[1] = (1-2*(o[1]<0L))*o[1] >> 32
46
y[0] = ((1-2*(o[1]<0L))*o[1])&0xFFFFFFFF
47
# f = o[0]*o[1]
48
f[0] = x[0]*y[0]
49
t = x[0]*y[1] + x[1]*y[0] + (f[0]>>32)
50
f[1] = x[1]*y[1] + (t>>32)
51
f[0] = (f[0]&0xFFFFFFFF) + (t<<32)
52
# fix sign if needed
53
if o[1] < 0L:
54
f[0] = -f[0]
55
f[1] = -f[1]-(f[0]>0)
56
f[2] = -1
57
else:
58
f[2] = 0
59
# g = o[0]*o[0]
60
g[0] = x[0]*x[0]
61
t = ((x[0]*x[1])<<1)+(g[0]>>32)
62
g[1] = x[1]*x[1] + (t>>32)
63
g[0] = (g[0]&0xFFFFFFFF) + (t<<32)
64
# g *= 36
65
g[2] = 9*(g[1]>>4)>>58
66
g[1] = 36*g[1] + (9*(g[0]>>4)>>58)
67
g[0] *= 36
68
# h = o[1]*o[1]
69
h[0] = y[0]*y[0]
70
t = ((y[0]*y[1])<<1)+(h[0]>>32)
71
h[1] = y[1]*y[1] + (t>>32)
72
h[0] = (h[0]&0xFFFFFFFF) + (t<<32)
73
# h *= 180
74
h[2] = 45*(h[1]>>6)>>56
75
h[1] = 180*h[1] + (45*(h[0]>>6)>>56)
76
h[0] *= 180
77
# g += h
78
g[2] += h[2]
79
g[1] += h[1]
80
g[0] += h[0]
81
g[1] += g[0] < h[0]
82
g[2] += g[1] < h[1] or (g[1] == h[1] and g[0] < h[0])
83
# h = 161*f
84
h[2] = 161*f[2] + (161*(f[1]>>8)>>56)
85
h[1] = 161*f[1] + (161*(f[0]>>8)>>56)
86
h[0] = 161*f[0]
87
# f *= 2
88
f[2] = (f[2]<<1) + (f[1]>>63)
89
f[1] = (f[1]<<1) + (f[0]>>63)
90
f[0] <<= 1
91
# f = 2*o[0]*o[1]
92
if o[1] > 0L:
93
# g = -g
94
g[0] = -g[0]
95
g[1] = -g[1]-(g[0]>0)
96
g[2] = -g[2]-(g[1]>0 or g[0]>0)
97
# g += h
98
g[2] += h[2]
99
g[1] += h[1]
100
g[0] += h[0]
101
g[1] += g[0] < h[0]
102
g[2] += g[1] < h[1] or (g[1] == h[1] and g[0] < h[0])
103
# g = 161*o[0]*o[1]-36*o[0]*o[0]-180*o[1]*o[1]
104
while g[2] < 0x8000000000000000L:
105
t = 161*o[0]-360*o[1]
106
o[1] = 161*o[1]-72*o[0]
107
o[0] = t
108
# f = 644*g-f
109
h[2] = 644*g[2] + (161*(g[1]>>8)>>54)
110
h[1] = 644*g[1] + (161*(g[0]>>8)>>54)
111
h[0] = 644*g[0]
112
f[2] = h[2]-f[2]
113
f[1] = h[1]-f[1]
114
f[0] = h[0]-f[0]
115
f[1] -= f[0] > h[0]
116
f[2] -= f[1] > h[1] or (f[1] == h[1] and f[0] > h[0])
117
# g = 161*f-g
118
h[2] = 161*f[2] + (161*(f[1]>>8)>>56)
119
h[1] = 161*f[1] + (161*(f[0]>>8)>>56)
120
h[0] = 161*f[0]
121
g[2] = h[2]-g[2]
122
g[1] = h[1]-g[1]
123
g[0] = h[0]-g[0]
124
g[1] -= g[0] > h[0]
125
g[2] -= g[1] > h[1] or (g[1] == h[1] and g[0] > h[0])
126
o[3] += o[2]
127
o[2] = o[3]-o[2]
128
elif o[1] < 0L:
129
# g += h
130
g[2] += h[2]
131
g[1] += h[1]
132
g[0] += h[0]
133
g[1] += g[0] < h[0]
134
g[2] += g[1] < h[1] or (g[1] == h[1] and g[0] < h[0])
135
# g = 161*o[0]*o[1]+36*o[0]*o[0]+180*o[1]*o[1]
136
while g[2] >= 0x8000000000000000L:
137
t = 161*o[0]+360*o[1]
138
o[1] = 72*o[0]+161*o[1]
139
o[0] = t
140
# f = 644*g-f
141
h[2] = 644*g[2] + (161*(g[1]>>8)>>54)
142
h[1] = 644*g[1] + (161*(g[0]>>8)>>54)
143
h[0] = 644*g[0]
144
f[2] = h[2]-f[2]
145
f[1] = h[1]-f[1]
146
f[0] = h[0]-f[0]
147
f[1] -= f[0] > h[0]
148
f[2] -= f[1] > h[1] or (f[1] == h[1] and f[0] > h[0])
149
# g = 161*f-g
150
h[2] = 161*f[2] + (161*(f[1]>>8)>>56)
151
h[1] = 161*f[1] + (161*(f[0]>>8)>>56)
152
h[0] = 161*f[0]
153
g[2] = h[2]-g[2]
154
g[1] = h[1]-g[1]
155
g[0] = h[0]-g[0]
156
g[1] -= g[0] > h[0]
157
g[2] -= g[1] > h[1] or (g[1] == h[1] and g[0] > h[0])
158
o[2] = o[3]-o[2]
159
o[3] -= o[2]
160
o[0] = o[0]-o[1]>>1
161
if not o[0]%13 and not o[1]%8 and o[0]/13 == -(o[1]/8):
162
t = o[0]/13
163
o[0] = 5*t
164
o[1] = 8*t
165
o[2] = o[3]-o[2]
166
o[3] -= o[2]
167
elif not o[0]%29 and not o[1]%18 and o[0]/29 == -(o[1]/18):
168
t = o[1]/18
169
o[0] = 11*t
170
o[1] = 18*t
171
o[2] = o[3]-o[2]
172
o[3] -= o[2]
173
o[0] *= 1-2*s
174
o[1] *= 1-2*s
175
176
cdef void f_mapC(mpz_t rop1, mpz_t rop2, mpz_t rop3, mpz_t rop4, mpz_t op1, mpz_t op2):
177
"""
178
Let `G` be the function
179
`G(\delta) = 2*a*a + 2*a*b + 3*b*b,`
180
where `\delta = a+b*\gamma`, then the output are the unique values such that
181
`G(rop1+rop2*\gamma) <= G(v^12 * (rop1+rop2*\gamma))`
182
for any unit `v`, and
183
`op1+op2*\gamma = (rop3+rop4*\gamma)^12 * (rop1+rop2*\gamma).`
184
The implementation transforms to a new space, then iteratively finds these
185
values by storing either
186
`G(\gamma^12*\delta) - G(\delta)`
187
or
188
`G(\gamma^-12*\delta) - G(\delta)`
189
and terminating when this difference is positive, and transforming back.
190
"""
191
cdef bint s
192
cdef int64_t o[4]
193
cdef mpz_t f,g,t
194
mpz_add(rop1,op1,op1)
195
mpz_add(rop1,rop1,op2)
196
if not mpz_sgn(rop1):
197
mpz_set(rop1, op1)
198
mpz_set(rop2, op2)
199
mpz_set_si(rop3, 1)
200
mpz_set_si(rop4, 0)
201
return
202
mpz_set(rop2,op2)
203
if mpz_sizeinbase(rop1, 2) < (sizeof(o[0])<<3):
204
if mpz_sizeinbase(rop2, 2) < (sizeof(o[1])<<3):
205
mpz_export(&o[0], NULL, -1, sizeof(o[0]), -1, 0, rop1)
206
mpz_export(&o[1], NULL, -1, sizeof(o[1]), -1, 0, rop2)
207
o[0] *= mpz_sgn(rop1)
208
o[1] *= mpz_sgn(rop2)
209
f_map64(o)
210
mpz_set_si(rop1,o[0])
211
mpz_set_si(rop2,o[1])
212
mpz_set_si(rop3,o[2])
213
mpz_set_si(rop4,o[3])
214
return
215
mpz_set_si(rop3,1)
216
mpz_set_si(rop4,0)
217
s = mpz_sgn(rop1) < 0
218
# to simplify boolean checks
219
if s:
220
mpz_neg(rop1,rop1)
221
mpz_neg(rop2,rop2)
222
mpz_init(f); mpz_init(g); mpz_init(t)
223
224
# g = 36*rop1*rop1+180*rop2*rop2
225
mpz_mul(f,rop2,rop2)
226
mpz_mul(g,rop1,rop1)
227
mpz_mul_si(g,g,36)
228
mpz_addmul_ui(g,f,180)
229
230
mpz_mul(f,rop1,rop2)
231
if mpz_sgn(rop2) > 0:
232
# g = 36*rop1*rop1-161*rop1*rop2+180*rop2*rop2
233
mpz_submul_ui(g,f,161)
234
mpz_mul_si(f,f,2)
235
while mpz_sgn(g) < 0:
236
# rop1+rop2*g /= g^12
237
mpz_mul_si(t,rop1,161)
238
mpz_submul_ui(t,rop2,360)
239
mpz_mul_si(rop2,rop2,161)
240
mpz_submul_ui(rop2,rop1,72)
241
mpz_set(rop1,t)
242
243
# fix f and g for new rop1+rop2*g
244
mpz_addmul_ui(f,g,644)
245
mpz_neg(f,f)
246
mpz_addmul_ui(g,f,161)
247
mpz_neg(g,g)
248
249
# rop3+rop4*g *= g
250
mpz_add(rop4,rop3,rop4)
251
mpz_sub(rop3,rop4,rop3)
252
elif mpz_sgn(rop2) < 0:
253
# g = 36*rop1*rop1+161*rop1*rop2+180*rop2*rop2
254
mpz_addmul_ui(g,f,161)
255
mpz_mul_si(f,f,2)
256
while mpz_sgn(g) < 0:
257
# rop1+rop2*g *= g^12
258
mpz_mul_si(t,rop1,161)
259
mpz_addmul_ui(t,rop2,360)
260
mpz_mul_si(rop2,rop2,161)
261
mpz_addmul_ui(rop2,rop1,72)
262
mpz_set(rop1,t)
263
264
# fix f and g for new rop1+rop2*g
265
mpz_submul_ui(f,g,644)
266
mpz_neg(f,f)
267
mpz_submul_ui(g,f,161)
268
mpz_neg(g,g)
269
270
# rop3+rop4*g /= g
271
mpz_sub(rop3,rop4,rop3)
272
mpz_sub(rop4,rop4,rop3)
273
# fix sign if needed
274
mpz_clear(f)
275
mpz_sub(rop1,rop1,rop2)
276
mpz_divexact_ui(rop1,rop1,2ul)
277
if not mpz_mod_ui(g, rop1, 13ul) and not mpz_mod_ui(t, rop2, 8ul):
278
mpz_divexact_ui(t, rop1, 13ul)
279
mpz_divexact_ui(g, rop2, 11ul)
280
mpz_neg(g,g)
281
if not mpz_cmp(t,g):
282
mpz_mul_ui(rop1, t, 5ul)
283
mpz_mul_ui(rop2, t, 8ul)
284
mpz_sub(rop3,rop4,rop3)
285
mpz_sub(rop4,rop4,rop3)
286
elif not mpz_mod_ui(g, rop1, 29ul) and not mpz_mod_ui(t, rop2, 18ul):
287
mpz_divexact_ui(g, rop1, 29ul)
288
mpz_divexact_ui(t, rop2, 18ul)
289
mpz_neg(t,t)
290
if not mpz_cmp(t,g):
291
mpz_mul_ui(rop1, g, 11ul)
292
mpz_mul_ui(rop2, g, 18ul)
293
mpz_sub(rop3,rop4,rop3)
294
mpz_sub(rop4,rop4,rop3)
295
mpz_clear(g); mpz_clear(t)
296
if s:
297
mpz_neg(rop1,rop1)
298
mpz_neg(rop2,rop2)
299
300
cpdef f_map(Integer a, Integer b):
301
"""
302
Computes 'cannonical' representitives for the equivalence relation
303
`\lambda / \mu \in U_F^{12},`
304
on the ring of integers for `F=Q(\sqrt{5})`.
305
306
INPUT:
307
308
- ``a+b*\gamma`` -- an element of the ring of integers of `F`
309
310
OUTPUT:
311
312
A 'cannonical' element `\delta*u^{-12}` of the maximal order of `F` and
313
the unit `u`
314
315
EXAMPLES::
316
317
sage: from psage.ellcurve.minmodel.sqrt5 import f_map
318
sage: K.<a> = NumberField(x^2-x-1)
319
sage: E = EllipticCurve(K,[a,a+1,a+2,a+3,a+4])
320
sage: olddelta = E.discriminant()
321
sage: olddeltapair = (Integer(olddelta[0]),Integer(olddelta[1]))
322
sage: temp = f_map(olddeltapair[0],olddeltapair[1])
323
sage: temp[0] == olddeltapair[0] and temp[1] == olddeltapair[1]
324
True
325
sage: F = E.change_weierstrass_model([a^5,0,0,0])
326
sage: delta = F.discriminant()
327
sage: deltapair = (Integer(delta[0]),Integer(delta[1]))
328
sage: temp = f_map(deltapair[0],deltapair[1])
329
sage: temp[0] == deltapair[0] and temp[1] == deltapair[1]
330
False
331
sage: temp[0] == olddeltapair[0] and temp[1] == olddeltapair[1]
332
True
333
sage: F.change_weierstrass_model([temp[2]+a*temp[3],0,0,0]) == E
334
True
335
336
"""
337
cdef Integer c,d,e,f
338
c = <Integer>PY_NEW(Integer)
339
d = <Integer>PY_NEW(Integer)
340
e = <Integer>PY_NEW(Integer)
341
f = <Integer>PY_NEW(Integer)
342
f_mapC(c.value, d.value, e.value, f.value, a.value, b.value)
343
return c,d,e,f
344
345
def canonical_model(E):
346
"""
347
Given a global minimal model E over Q(sqrt5) returns a canonical global
348
minimal model.
349
350
EXAMPLES::
351
352
sage: from psage.ellcurve.minmodel.sqrt5 import canonical_model
353
sage: K.<a> = NumberField(x**2-x-1)
354
sage: E = EllipticCurve(K,[a,a-1,a,-1,-a+1])
355
sage: F = canonical_model(E)
356
sage: E == F
357
False
358
sage: E.is_isomorphic(F)
359
True
360
sage: F == canonical_model(F)
361
True
362
"""
363
try:
364
K = E.base_field()
365
if K.disc() != 5:
366
raise TypeError, "E must be over Q(sqrt(5))"
367
D1, D2 = E.discriminant()
368
a1,a2,a3,a4,a6 = E.a_invariants()
369
except AttributeError:
370
raise TypeError, "E must be an elliptic curve"
371
cdef Integer u1, u2
372
u1 = Integer(D1)
373
u2 = Integer(D2)
374
cdef mpz_t t1,t2
375
mpz_init(t1); mpz_init(t2)
376
f_mapC(t1, u1.value, t2, u2.value, u1.value, u2.value)
377
mpz_add(u1.value, t2, u2.value)
378
mpz_mul(t1, u1.value, t2)
379
mpz_submul(t1, u2.value, u2.value)
380
if mpz_sgn(t1) > 0:
381
mpz_neg(u2.value, u2.value)
382
else:
383
mpz_neg(u1.value, u1.value)
384
mpz_clear(t1); mpz_clear(t2)
385
u = K(u1)+K.gen(0)*K(u2)
386
a1,a2,a3,a4,a6 = u*a1,u*u*a2,u*u*u*a3,a4*u**4,a6*u**6
387
P2 = K.ideal(2)
388
P3 = K.ideal(3)
389
a1p = a1.mod(P2)
390
s = (a1p - a1)/K(2)
391
sa1 = s*a1
392
a2p = (a2 - sa1 - s**2).mod(P3)
393
r = (a2p - a2 + sa1 + s**2)/K(3)
394
ra1p = r*a1p
395
a3p = (a3+ra1p).mod(P2)
396
t = r*s + (a3p - a3 - ra1p)/K(2)
397
a4p = a4 - s*a3 + 2*r*a2 - (t+r*s)*a1 + 3*r**2 - 2*s*t
398
a6p = a6 + r*a4 + r**2*a2 + r**3 - t*a3 - t**2 - r*t*a1
399
return EllipticCurve(K, [a1p, a2p, a3p, a4p, a6p])
400
401
cpdef canonical_model_c_invariants(Integer a, Integer b, Integer c, Integer d):
402
"""
403
Given the pair (c4,c6) of a global minimal model, canonical_model returns
404
a 'cannonical' representation of the curve in terms of (c4,c6).
405
406
INPUT:
407
408
- ``c4=a+b*\gamma`` -- an element of the ring of integers of `F=Q(\sqrt{5})`
409
- ``c6=c+d*\gamma`` -- an element of the ring of integers of `F`
410
411
OUTPUT:
412
413
A 'cannonical' model of a curve in terms of (c4,c6)
414
415
EXAMPLES::
416
417
sage: K.<a> = NumberField(x^2-x-1)
418
sage: E = EllipticCurve(K,[a,a+1,a+2,a+3,a+4])
419
sage: F = E.change_weierstrass_model(a,a+1,a+2,a+3)
420
sage: E.c_invariants() == F.c_invariants()
421
False
422
sage: Ecs = [Integer(i) for i in E.c4()]
423
sage: Ecs += [Integer(i) for i in E.c6()]
424
sage: Fcs = [Integer(i) for i in F.c4()]
425
sage: Fcs += [Integer(i) for i in F.c6()]
426
sage: from psage.ellcurve.minmodel.sqrt5 import canonical_model_c_invariants
427
sage: canonical_model_c_invariants(Ecs[0],Ecs[1],Ecs[2],Ecs[3])
428
(-118, -45, -3001, 116)
429
sage: canonical_model_c_invariants(Fcs[0],Fcs[1],Fcs[2],Fcs[3])
430
(-118, -45, -3001, 116)
431
432
433
"""
434
cdef Integer x,y,z,w
435
cdef mpz_t p,q
436
x = <Integer>PY_NEW(Integer)
437
y = <Integer>PY_NEW(Integer)
438
z = <Integer>PY_NEW(Integer)
439
w = <Integer>PY_NEW(Integer)
440
mpz_init(p)
441
mpz_init(q)
442
443
# p+q*g = (a+b*g)^3
444
mpz_mul(x.value,a.value,a.value)
445
mpz_mul(y.value,b.value,b.value)
446
mpz_mul(p,y.value,a.value)
447
mpz_mul_si(p,p,3)
448
mpz_mul(q,x.value,b.value)
449
mpz_mul_si(q,q,3)
450
mpz_add(q,q,p)
451
mpz_addmul(p,x.value,a.value)
452
mpz_mul(y.value,y.value,b.value)
453
mpz_add(p,p,y.value)
454
mpz_addmul_ui(q,y.value,2)
455
456
457
# compute discriminant
458
459
# p+q*g -= (c+d*g)^2
460
mpz_submul(p,c.value,c.value)
461
mpz_mul(x.value,c.value,d.value)
462
mpz_submul_ui(q,x.value,2)
463
mpz_mul(x.value,d.value,d.value)
464
mpz_sub(p,p,x.value)
465
mpz_sub(q,q,x.value)
466
467
# p+q*g /= 1728
468
mpz_divexact_ui(p,p,1728)
469
mpz_divexact_ui(q,q,1728)
470
471
472
# find the needed transformation, and x+y*g to corresponding unit
473
f_mapC(p,q,x.value,y.value,p,q)
474
475
476
# compute the transform
477
478
# p+q*g = (x+y*g)^-2
479
mpz_add(x.value,x.value,y.value)
480
mpz_mul(p,y.value,y.value)
481
mpz_mul(q,x.value,y.value)
482
mpz_mul_si(q,q,2)
483
mpz_sub(q,q,p)
484
mpz_addmul(p,x.value,x.value)
485
mpz_neg(q,q)
486
487
# x+y*g = (p+q*g)^3
488
mpz_mul(z.value,p,p)
489
mpz_mul(w.value,q,q)
490
mpz_mul(x.value,w.value,p)
491
mpz_mul_si(x.value,x.value,3)
492
mpz_mul(y.value,z.value,q)
493
mpz_mul_si(y.value,y.value,3)
494
mpz_add(y.value,y.value,x.value)
495
mpz_addmul(x.value,z.value,p)
496
mpz_mul(w.value,w.value,q)
497
mpz_add(x.value,x.value,w.value)
498
mpz_addmul_ui(y.value,w.value,2)
499
500
# z+w*g = (x+y*g)*(c+d*g)
501
mpz_mul(z.value,y.value,d.value)
502
mpz_mul(w.value,x.value,d.value)
503
mpz_addmul(w.value,y.value,c.value)
504
mpz_add(w.value,w.value,z.value)
505
mpz_addmul(z.value,x.value,c.value)
506
507
# p+q*g *= p+q*g
508
mpz_mul(x.value,p,q)
509
mpz_mul(q,q,q)
510
mpz_mul(p,p,p)
511
mpz_add(p,p,q)
512
mpz_addmul_ui(q,x.value,2)
513
514
# x+y*g = (p+q*g)*(a+b*g)
515
mpz_mul(x.value,q,b.value)
516
mpz_mul(y.value,p,b.value)
517
mpz_addmul(y.value,q,a.value)
518
mpz_add(y.value,y.value,x.value)
519
mpz_addmul(x.value,p,a.value)
520
521
mpz_clear(p)
522
mpz_clear(q)
523
524
return x,y,z,w
525
526