Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Testing latest pari + WASM + node.js... and it works?! Wow.

28479 views
License: GPL3
ubuntu2004
1
/* Copyright (C) 2000-2005 The PARI group.
2
3
This file is part of the PARI/GP package.
4
5
PARI/GP is free software; you can redistribute it and/or modify it under the
6
terms of the GNU General Public License as published by the Free Software
7
Foundation; either version 2 of the License, or (at your option) any later
8
version. It is distributed in the hope that it will be useful, but WITHOUT
9
ANY WARRANTY WHATSOEVER.
10
11
Check the License for details. You should have received a copy of it, along
12
with the package; see the file 'COPYING'. If not, write to the Free Software
13
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14
#include "pari.h"
15
#include "paripriv.h"
16
/*******************************************************************/
17
/* */
18
/* QUADRATIC POLYNOMIAL ASSOCIATED TO A DISCRIMINANT */
19
/* */
20
/*******************************************************************/
21
22
void
23
check_quaddisc(GEN x, long *s, long *pr, const char *f)
24
{
25
long r;
26
if (typ(x) != t_INT) pari_err_TYPE(f,x);
27
*s = signe(x);
28
if (Z_issquare(x)) pari_err_DOMAIN(f,"issquare(disc)","=", gen_1,x);
29
r = mod4(x); if (*s < 0 && r) r = 4 - r;
30
if (r > 1) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
31
if (pr) *pr = r;
32
}
33
void
34
check_quaddisc_real(GEN x, long *r, const char *f)
35
{
36
long sx; check_quaddisc(x, &sx, r, f);
37
if (sx < 0) pari_err_DOMAIN(f, "disc","<",gen_0,x);
38
}
39
void
40
check_quaddisc_imag(GEN x, long *r, const char *f)
41
{
42
long sx; check_quaddisc(x, &sx, r, f);
43
if (sx > 0) pari_err_DOMAIN(f, "disc",">",gen_0,x);
44
}
45
46
/* X^2 + b X + c is the canonical quadratic t_POL of discriminant D.
47
* Dodd is nonzero iff D is odd */
48
static void
49
quadpoly_bc(GEN D, long Dodd, GEN *b, GEN *c)
50
{
51
if (Dodd)
52
{
53
pari_sp av = avma;
54
*b = gen_m1;
55
*c = gerepileuptoint(av, shifti(subui(1,D), -2));
56
}
57
else
58
{
59
*b = gen_0;
60
*c = shifti(D,-2); togglesign(*c);
61
}
62
}
63
/* X^2 - X - (D-1)/4 or X^2 - D/4 */
64
static GEN
65
quadpoly_ii(GEN D, long Dmod4)
66
{
67
GEN b, c, y = cgetg(5,t_POL);
68
y[1] = evalsigne(1) | evalvarn(0);
69
quadpoly_bc(D, Dmod4, &b,&c);
70
gel(y,2) = c;
71
gel(y,3) = b;
72
gel(y,4) = gen_1; return y;
73
}
74
GEN
75
quadpoly(GEN D)
76
{
77
long s, Dmod4;
78
check_quaddisc(D, &s, &Dmod4, "quadpoly");
79
return quadpoly_ii(D, Dmod4);
80
}
81
GEN /* no checks */
82
quadpoly_i(GEN D) { return quadpoly_ii(D, Mod4(D)); }
83
84
GEN
85
quadpoly0(GEN x, long v)
86
{
87
GEN T = quadpoly(x);
88
if (v > 0) setvarn(T, v);
89
return T;
90
}
91
92
GEN
93
quadgen(GEN x)
94
{ retmkquad(quadpoly(x), gen_0, gen_1); }
95
96
GEN
97
quadgen0(GEN x, long v)
98
{
99
if (v==-1) v = fetch_user_var("w");
100
retmkquad(quadpoly0(x, v), gen_0, gen_1);
101
}
102
103
/***********************************************************************/
104
/** **/
105
/** BINARY QUADRATIC FORMS **/
106
/** **/
107
/***********************************************************************/
108
static int
109
is_qfi(GEN q) { return typ(q)==t_QFB && qfb_is_qfi(q); }
110
111
static GEN
112
check_qfbext(const char *fun, GEN x)
113
{
114
long t = typ(x);
115
if (t == t_QFB) return x;
116
if (t == t_VEC && lg(x)==3)
117
{
118
GEN q = gel(x,1);
119
if (!is_qfi(q) && typ(gel(x,2))==t_REAL) return q;
120
}
121
pari_err_TYPE(fun, x);
122
return NULL;/* LCOV_EXCL_LINE */
123
}
124
125
static GEN
126
qfb3(GEN x, GEN y, GEN z)
127
{ retmkqfb(icopy(x), icopy(y), icopy(z), qfb_disc3(x,y,z)); }
128
129
static int
130
qfb_equal(GEN x, GEN y)
131
{
132
return equalii(gel(x,1),gel(y,1))
133
&& equalii(gel(x,2),gel(y,2))
134
&& equalii(gel(x,3),gel(y,3));
135
}
136
137
/* valid for t_QFB, qfr3, qfr5; shallow */
138
static GEN
139
qfb_inv(GEN x) {
140
GEN z = shallowcopy(x);
141
gel(z,2) = negi(gel(z,2));
142
return z;
143
}
144
/* valid for t_QFB, gerepile-safe */
145
static GEN
146
qfbinv(GEN x)
147
{ retmkqfb(icopy(gel(x,1)),negi(gel(x,2)),icopy(gel(x,3)), icopy(gel(x,4))); }
148
149
GEN
150
Qfb0(GEN a, GEN b, GEN c)
151
{
152
GEN q, D;
153
if (!b)
154
{
155
if (c) pari_err_TYPE("Qfb",c);
156
if (typ(a) != t_VEC || lg(a) != 4) pari_err_TYPE("Qfb",a);
157
b = gel(a,2);
158
c = gel(a,3);
159
a = gel(a,1);
160
}
161
else if (!c)
162
pari_err_TYPE("Qfb",b);
163
if (typ(a)!=t_INT) pari_err_TYPE("Qfb",a);
164
if (typ(b)!=t_INT) pari_err_TYPE("Qfb",b);
165
if (typ(c)!=t_INT) pari_err_TYPE("Qfb",c);
166
q = qfb3(a, b, c); D = qfb_disc(q);
167
if (signe(D) < 0)
168
{ if (signe(a) < 0) pari_err_IMPL("negative definite t_QFB"); }
169
else if (Z_issquare(D)) pari_err_DOMAIN("Qfb","issquare(disc)","=", gen_1,q);
170
return q;
171
}
172
173
/* composition */
174
static void
175
qfb_sqr(GEN z, GEN x)
176
{
177
GEN c, d1, x2, v1, v2, c3, m, p1, r;
178
179
d1 = bezout(gel(x,2),gel(x,1),&x2, NULL); /* usually 1 */
180
c = gel(x,3);
181
m = mulii(c,x2);
182
if (equali1(d1))
183
v1 = v2 = gel(x,1);
184
else
185
{
186
v1 = diviiexact(gel(x,1),d1);
187
v2 = mulii(v1, gcdii(d1,c)); /* = v1 iff x primitive */
188
c = mulii(c, d1);
189
}
190
togglesign(m);
191
r = modii(m,v2);
192
p1 = mulii(r, v1);
193
c3 = addii(c, mulii(r,addii(gel(x,2),p1)));
194
gel(z,1) = mulii(v1,v2);
195
gel(z,2) = addii(gel(x,2), shifti(p1,1));
196
gel(z,3) = diviiexact(c3,v2);
197
}
198
/* z <- x * y */
199
static void
200
qfb_comp(GEN z, GEN x, GEN y)
201
{
202
GEN n, c, d, y1, v1, v2, c3, m, p1, r;
203
204
if (x == y) { qfb_sqr(z,x); return; }
205
n = shifti(subii(gel(y,2),gel(x,2)), -1);
206
v1 = gel(x,1);
207
v2 = gel(y,1);
208
c = gel(y,3);
209
d = bezout(v2,v1,&y1,NULL);
210
if (equali1(d))
211
m = mulii(y1,n);
212
else
213
{
214
GEN s = subii(gel(y,2), n);
215
GEN x2, y2, d1 = bezout(s,d,&x2,&y2); /* x2 s + y2 (x1 v1 + y1 v2) = d1 */
216
if (!equali1(d1))
217
{
218
v1 = diviiexact(v1,d1);
219
v2 = diviiexact(v2,d1); /* gcd = 1 iff x or y primitive */
220
v1 = mulii(v1, gcdii(c,gcdii(gel(x,3),gcdii(d1,n))));
221
c = mulii(c, d1);
222
}
223
m = addii(mulii(mulii(y1,y2),n), mulii(gel(y,3),x2));
224
}
225
togglesign(m);
226
r = modii(m, v1);
227
p1 = mulii(r, v2);
228
c3 = addii(c, mulii(r,addii(gel(y,2),p1)));
229
gel(z,1) = mulii(v1,v2);
230
gel(z,2) = addii(gel(y,2), shifti(p1,1));
231
gel(z,3) = diviiexact(c3,v1);
232
}
233
234
/* not meant to be efficient */
235
static GEN
236
qfb_comp_gen(GEN x, GEN y)
237
{
238
GEN d1 = qfb_disc(x), d2 = qfb_disc(y);
239
GEN a1 = gel(x,1), b1 = gel(x,2), c1 = gel(x,3), n1;
240
GEN a2 = gel(y,1), b2 = gel(y,2), c2 = gel(y,3), n2;
241
GEN cx = content(x), cy = content(y), A, B, C, D, U, m, m2;
242
243
if (!is_pm1(cx))
244
{
245
a1 = diviiexact(a1, cx); b1 = diviiexact(b1, cx);
246
c1 = diviiexact(c1, cx); d1 = diviiexact(d1, sqri(cx));
247
}
248
if (!is_pm1(cy))
249
{
250
a2 = diviiexact(a2, cy); c2 = diviiexact(c2, cy);
251
b2 = diviiexact(b2, cy); d2 = diviiexact(d2, sqri(cy));
252
}
253
D = gcdii(d1, d2); if (signe(d1) < 0) setsigne(D, -1);
254
if (!Z_issquareall(diviiexact(d1, D), &n1) ||
255
!Z_issquareall(diviiexact(d2, D), &n2)) return NULL;
256
A = mulii(a1, n2);
257
B = mulii(a2, n1);
258
C = shifti(addii(mulii(b1, n2), mulii(b2, n1)), -1);
259
U = ZV_extgcd(mkvec3(A, B, C));
260
m = gel(U,1); U = gmael(U,2,3);
261
A = mulii(diviiexact(mulii(a1,b2),m), gel(U,1));
262
B = mulii(diviiexact(mulii(a2,b1),m), gel(U,2));
263
C = addii(mulii(b1,b2), mulii(D, mulii(n1,n2)));
264
C = mulii(diviiexact(shifti(C,-1), m), gel(U,3));
265
B = addii(A, addii(B, C));
266
m2 = sqri(m);
267
A = diviiexact(mulii(a1, a2), m2);
268
C = diviiexact(shifti(subii(sqri(B),D), -2), A);
269
cx = mulii(cx, cy);
270
if (!is_pm1(cx))
271
{
272
A = mulii(A, cx); B = mulii(B, cx);
273
C = mulii(C, cx); D = mulii(D, sqri(cx));
274
}
275
return mkqfb(A, B, C, D);
276
}
277
278
static GEN redimag_av(pari_sp av, GEN q);
279
static GEN
280
qficomp0(GEN x, GEN y, int raw)
281
{
282
pari_sp av = avma;
283
GEN z = cgetg(5,t_QFB);
284
gel(z,4) = gel(x,4);
285
qfb_comp(z, x,y);
286
if (raw) return gerepilecopy(av,z);
287
return redimag_av(av, z);
288
}
289
static GEN redreal(GEN x);
290
static GEN
291
qfrcomp0(GEN x, GEN y, int raw)
292
{
293
pari_sp av = avma;
294
GEN dx = NULL, dy = NULL;
295
GEN z = cgetg(5,t_QFB);
296
if (typ(x)==t_VEC) { dx = gel(x,2); x = gel(x,1); }
297
if (typ(y)==t_VEC) { dy = gel(y,2); y = gel(y,1); }
298
gel(z,4) = gel(x,4);
299
qfb_comp(z, x,y);
300
if (dx) z = mkvec2(z, dy? addrr(dx, dy): dx); else if (dy) z = mkvec2(z, dy);
301
if (!raw) z = redreal(z);
302
return gerepilecopy(av, z);
303
}
304
/* same discriminant, no distance, no checks */
305
GEN
306
qfbcomp_i(GEN x, GEN y)
307
{ return qfb_is_qfi(x)? qficomp0(x,y,0): qfrcomp0(x,y,0); }
308
GEN
309
qfbcomp(GEN x, GEN y)
310
{
311
GEN qx = check_qfbext("qfbcomp", x);
312
GEN qy = check_qfbext("qfbcomp", y);
313
if (!equalii(gel(qx,4),gel(qy,4)))
314
{
315
pari_sp av = avma;
316
GEN z = qfb_comp_gen(qx, qy);
317
if (typ(x) == t_VEC || typ(y) == t_VEC)
318
pari_err_IMPL("Shanks's distance in general composition");
319
if (!z) pari_err_OP("*",x,y);
320
return gerepileupto(av, qfbred(z));
321
}
322
return qfb_is_qfi(qx)? qficomp0(x,y,0): qfrcomp0(x,y,0);
323
}
324
/* same discriminant, no distance, no checks */
325
GEN
326
qfbcompraw_i(GEN x, GEN y)
327
{ return qfb_is_qfi(x)? qficomp0(x,y,1): qfrcomp0(x,y,1); }
328
GEN
329
qfbcompraw(GEN x, GEN y)
330
{
331
GEN qx = check_qfbext("qfbcompraw", x);
332
GEN qy = check_qfbext("qfbcompraw", y);
333
if (!equalii(gel(qx,4),gel(qy,4)))
334
{
335
pari_sp av = avma;
336
GEN z = qfb_comp_gen(qx, qy);
337
if (typ(x) == t_VEC || typ(y) == t_VEC)
338
pari_err_IMPL("Shanks's distance in general composition");
339
if (!z) pari_err_OP("qfbcompraw",x,y);
340
return gerepilecopy(av, z);
341
}
342
if (!equalii(gel(qx,4),gel(qy,4))) pari_err_OP("qfbcompraw",x,y);
343
return qfb_is_qfi(qx)? qficomp0(x,y,1): qfrcomp0(x,y,1);
344
}
345
346
static GEN
347
qfisqr0(GEN x, long raw)
348
{
349
pari_sp av = avma;
350
GEN z = cgetg(5,t_QFB);
351
gel(z,4) = gel(x,4);
352
qfb_sqr(z,x);
353
if (raw) return gerepilecopy(av,z);
354
return redimag_av(av, z);
355
}
356
static GEN
357
qfrsqr0(GEN x, long raw)
358
{
359
pari_sp av = avma;
360
GEN z = cgetg(5,t_QFB);
361
gel(z,4) = gel(x,4);
362
qfb_sqr(z,x);
363
if (!raw) z = redreal(z);
364
return gerepilecopy(av, z);
365
}
366
/* same discriminant, no distance, no checks */
367
GEN
368
qfbsqr_i(GEN x)
369
{ return qfb_is_qfi(x)? qfisqr0(x,0): qfrsqr0(x,0); }
370
GEN
371
qfbsqr(GEN x)
372
{
373
GEN qx = check_qfbext("qfbsqr", x);
374
return qfb_is_qfi(qx)? qfisqr0(x,0): qfrsqr0(x,0);
375
}
376
377
static GEN
378
qfr_1_by_disc(GEN D)
379
{
380
GEN y = cgetg(5,t_QFB), isqrtD;
381
pari_sp av = avma;
382
long r;
383
384
check_quaddisc_real(D, &r, "qfr_1_by_disc");
385
gel(y,1) = gen_1; isqrtD = sqrti(D);
386
if ((r & 1) != mod2(isqrtD)) /* we know isqrtD > 0 */
387
isqrtD = gerepileuptoint(av, subiu(isqrtD,1));
388
gel(y,2) = isqrtD; av = avma;
389
gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(isqrtD), D),-2));
390
gel(y,4) = icopy(D); return y;
391
}
392
393
static GEN
394
qfr_disc(GEN x)
395
{ return qfb_disc(typ(x)==t_VEC ? gel(x,1): x); }
396
397
static GEN
398
qfr_1(GEN x)
399
{ return qfr_1_by_disc(qfr_disc(x)); }
400
401
static void
402
qfr_1_fill(GEN y, struct qfr_data *S)
403
{
404
pari_sp av = avma;
405
GEN y2 = S->isqrtD;
406
gel(y,1) = gen_1;
407
if (mod2(S->D) != mod2(y2)) y2 = subiu(y,1);
408
gel(y,2) = y2; av = avma;
409
gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(y2), S->D),-2));
410
}
411
static GEN
412
qfr5_1(struct qfr_data *S, long prec)
413
{
414
GEN y = cgetg(6, t_VEC);
415
qfr_1_fill(y, S);
416
gel(y,4) = gen_0;
417
gel(y,5) = real_1(prec); return y;
418
}
419
static GEN
420
qfr3_1(struct qfr_data *S)
421
{
422
GEN y = cgetg(4, t_VEC);
423
qfr_1_fill(y, S); return y;
424
}
425
426
/* Assume D < 0 is the discriminant of a t_QFB */
427
static GEN
428
qfi_1_by_disc(GEN D)
429
{
430
GEN b,c, y = cgetg(5,t_QFB);
431
quadpoly_bc(D, mod2(D), &b,&c);
432
if (b == gen_m1) b = gen_1;
433
gel(y,1) = gen_1;
434
gel(y,2) = b;
435
gel(y,3) = c;
436
gel(y,4) = icopy(D); return y;
437
}
438
static GEN
439
qfi_1(GEN x)
440
{
441
if (typ(x) != t_QFB) pari_err_TYPE("qfi_1",x);
442
return qfi_1_by_disc(qfb_disc(x));
443
}
444
445
GEN
446
qfb_1(GEN x) { return qfb_is_qfi(x) ? qfi_1(x): qfr_1(x); }
447
448
static GEN
449
_qfimul(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,0); }
450
static GEN
451
_qfisqr(void *E, GEN x) { (void) E; return qficomp0(x,x,0); }
452
static GEN
453
_qfimulraw(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,1); }
454
static GEN
455
_qfisqrraw(void *E, GEN x) { (void) E; return qficomp0(x,x,1); }
456
457
static GEN
458
qfipowraw(GEN x, long n)
459
{
460
pari_sp av = avma;
461
GEN y;
462
if (!n) return qfi_1(x);
463
if (n== 1) return gcopy(x);
464
if (n==-1) { x = gcopy(x); togglesign(gel(x,2)); return x; }
465
if (n < 0) x = qfb_inv(x);
466
y = gen_powu(x, labs(n), NULL, &_qfisqrraw, &_qfimulraw);
467
return gerepilecopy(av,y);
468
}
469
470
static GEN
471
qfipow(GEN x, GEN n)
472
{
473
pari_sp av = avma;
474
GEN y;
475
long s = signe(n);
476
if (!s) return qfi_1(x);
477
if (s < 0) x = qfb_inv(x);
478
y = gen_pow(qfbred_i(x), n, NULL, &_qfisqr, &_qfimul);
479
return gerepilecopy(av,y);
480
}
481
482
static long
483
parteucl(GEN L, GEN *d, GEN *v3, GEN *v, GEN *v2)
484
{
485
long z;
486
*v = gen_0; *v2 = gen_1;
487
for (z=0; abscmpii(*v3,L) > 0; z++)
488
{
489
GEN t3, t2 = subii(*v, mulii(truedvmdii(*d,*v3,&t3),*v2));
490
*v = *v2; *d = *v3; *v2 = t2; *v3 = t3;
491
}
492
return z;
493
}
494
495
/* composition: Shanks' NUCOMP & NUDUPL */
496
/* L = floor((|d|/4)^(1/4)) */
497
GEN
498
nucomp(GEN x, GEN y, GEN L)
499
{
500
pari_sp av = avma;
501
long z;
502
GEN a, a1, a2, b2, b, d, d1, g, n, p1, q1, q2, s, u, u1, v, v2, v3, Q;
503
504
if (x==y) return nudupl(x,L);
505
if (!is_qfi(x)) pari_err_TYPE("nucomp",x);
506
if (!is_qfi(y)) pari_err_TYPE("nucomp",y);
507
508
if (abscmpii(gel(x,1),gel(y,1)) < 0) swap(x, y);
509
s = shifti(addii(gel(x,2),gel(y,2)), -1);
510
n = subii(gel(y,2), s);
511
a1 = gel(x,1);
512
a2 = gel(y,1); d = bezout(a2,a1,&u,&v);
513
if (equali1(d)) { a = negi(mulii(u,n)); d1 = d; }
514
else if (dvdii(s,d)) /* d | s */
515
{
516
a = negi(mulii(u,n)); d1 = d;
517
a1 = diviiexact(a1, d1);
518
a2 = diviiexact(a2, d1);
519
s = diviiexact(s, d1);
520
}
521
else
522
{
523
GEN p2, l;
524
d1 = bezout(s,d,&u1,NULL);
525
if (!equali1(d1))
526
{
527
a1 = diviiexact(a1,d1);
528
a2 = diviiexact(a2,d1);
529
s = diviiexact(s,d1);
530
d = diviiexact(d,d1);
531
}
532
p1 = remii(gel(x,3),d);
533
p2 = remii(gel(y,3),d);
534
l = modii(mulii(negi(u1), addii(mulii(u,p1),mulii(v,p2))), d);
535
a = subii(mulii(l,diviiexact(a1,d)), mulii(u,diviiexact(n,d)));
536
}
537
a = modii(a,a1); p1 = subii(a,a1); if (abscmpii(a,p1) > 0) a = p1;
538
d = a1; v3 = a; z = parteucl(L, &d,&v3, &v,&v2);
539
Q = cgetg(5,t_QFB);
540
if (!z) {
541
g = diviiexact(addii(mulii(v3,s),gel(y,3)), d);
542
b = a2;
543
b2 = gel(y,2);
544
v2 = d1;
545
gel(Q,1) = mulii(d,b);
546
} else {
547
GEN e, q3, q4;
548
if (z&1) { v3 = negi(v3); v2 = negi(v2); }
549
b = diviiexact(addii(mulii(a2,d), mulii(n,v)), a1);
550
e = diviiexact(addii(mulii(s,d),mulii(gel(y,3),v)), a1);
551
q3 = mulii(e,v2);
552
q4 = subii(q3,s);
553
b2 = addii(q3,q4);
554
g = diviiexact(q4,v);
555
if (!equali1(d1)) { v2 = mulii(d1,v2); v = mulii(d1,v); b2 = mulii(d1,b2); }
556
gel(Q,1) = addii(mulii(d,b), mulii(e,v));
557
}
558
q1 = mulii(b, v3);
559
q2 = addii(q1,n);
560
gel(Q,2) = addii(b2, z? addii(q1,q2): shifti(q1, 1));
561
gel(Q,3) = addii(mulii(v3,diviiexact(q2,d)), mulii(g,v2));
562
gel(Q,4) = gel(x,4);
563
return redimag_av(av, Q);
564
}
565
566
GEN
567
nudupl(GEN x, GEN L)
568
{
569
pari_sp av = avma;
570
long z;
571
GEN u, v, d, d1, p1, a, b, c, a2, b2, c2, Q, v2, v3, g;
572
573
if (!is_qfi(x)) pari_err_TYPE("nudupl",x);
574
a = gel(x,1);
575
b = gel(x,2);
576
d1 = bezout(b,a, &u,NULL);
577
if (!equali1(d1))
578
{
579
a = diviiexact(a, d1);
580
b = diviiexact(b, d1);
581
}
582
c = modii(negi(mulii(u,gel(x,3))), a);
583
p1 = subii(c,a); if (abscmpii(c,p1) > 0) c = p1;
584
d = a; v3 = c; z = parteucl(L, &d,&v3, &v,&v2);
585
a2 = sqri(d);
586
c2 = sqri(v3);
587
Q = cgetg(5,t_QFB);
588
if (!z) {
589
g = diviiexact(addii(mulii(v3,b),gel(x,3)), d);
590
b2 = gel(x,2);
591
v2 = d1;
592
gel(Q,1) = a2;
593
} else {
594
GEN e;
595
if (z&1) { v = negi(v); d = negi(d); }
596
e = diviiexact(addii(mulii(gel(x,3),v), mulii(b,d)), a);
597
g = diviiexact(subii(mulii(e,v2), b), v);
598
b2 = addii(mulii(e,v2), mulii(v,g));
599
if (!equali1(d1)) { b2 = mulii(d1,b2); v = mulii(d1,v); v2 = mulii(d1,v2); }
600
gel(Q,1) = addii(a2, mulii(e,v));
601
}
602
gel(Q,2) = addii(b2, subii(sqri(addii(d,v3)), addii(a2,c2)));
603
gel(Q,3) = addii(c2, mulii(g,v2));
604
gel(Q,4) = gel(x,4);
605
return redimag_av(av, Q);
606
}
607
608
static GEN
609
mul_nucomp(void *l, GEN x, GEN y) { return nucomp(x, y, (GEN)l); }
610
static GEN
611
mul_nudupl(void *l, GEN x) { return nudupl(x, (GEN)l); }
612
GEN
613
nupow(GEN x, GEN n, GEN L)
614
{
615
pari_sp av;
616
GEN y, D;
617
618
if (typ(n) != t_INT) pari_err_TYPE("nupow",n);
619
if (!is_qfi(x)) pari_err_TYPE("nupow",x);
620
if (gequal1(n)) return gcopy(x);
621
av = avma;
622
D = qfb_disc(x);
623
y = qfi_1_by_disc(D);
624
if (!signe(n)) return y;
625
if (!L) L = sqrtnint(absi_shallow(D), 4);
626
y = gen_pow_i(x, n, (void*)L, &mul_nudupl, &mul_nucomp);
627
if (signe(n) < 0
628
&& !absequalii(gel(y,1),gel(y,2))
629
&& !absequalii(gel(y,1),gel(y,3))) togglesign(gel(y,2));
630
return gerepilecopy(av, y);
631
}
632
633
/* Reduction */
634
635
/* assume a > 0. Write b = q*2a + r, with -a < r <= a */
636
static GEN
637
dvmdii_round(GEN b, GEN a, GEN *r)
638
{
639
GEN a2 = shifti(a, 1), q = dvmdii(b, a2, r);
640
if (signe(b) >= 0) {
641
if (abscmpii(*r, a) > 0) { q = addiu(q, 1); *r = subii(*r, a2); }
642
} else { /* r <= 0 */
643
if (abscmpii(*r, a) >= 0){ q = subiu(q, 1); *r = addii(*r, a2); }
644
}
645
return q;
646
}
647
/* Assume 0 < a <= LONG_MAX. Ensure no overflow */
648
static long
649
dvmdsu_round(long b, ulong a, long *r)
650
{
651
ulong a2 = a << 1, q, ub, ur;
652
if (b >= 0) {
653
ub = b;
654
q = ub / a2;
655
ur = ub % a2;
656
if (ur > a) { ur -= a; q++; *r = (long)ur; *r -= (long)a; }
657
else *r = (long)ur;
658
return (long)q;
659
} else { /* r <= 0 */
660
ub = (ulong)-b; /* |b| */
661
q = ub / a2;
662
ur = ub % a2;
663
if (ur >= a) { ur -= a; q++; *r = (long)ur; *r = (long)a - *r; }
664
else *r = -(long)ur;
665
return -(long)q;
666
}
667
}
668
/* reduce b mod 2*a. Update b,c */
669
static void
670
REDB(GEN a, GEN *b, GEN *c)
671
{
672
GEN r, q = dvmdii_round(*b, a, &r);
673
if (!signe(q)) return;
674
*c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
675
*b = r;
676
}
677
/* Assume a > 0. Reduce b mod 2*a. Update b,c */
678
static void
679
sREDB(ulong a, long *b, ulong *c)
680
{
681
long r, q;
682
ulong uz;
683
if (a > LONG_MAX) return; /* b already reduced */
684
q = dvmdsu_round(*b, a, &r);
685
if (q == 0) return;
686
/* Final (a,r,c2) satisfies |r| <= |b| hence c2 <= c, c2 = c - q*z,
687
* where z = (b+r) / 2, representable as long, has the same sign as q. */
688
if (*b < 0)
689
{ /* uz = -z >= 0, q < 0 */
690
if (r >= 0) /* different signs=>no overflow, exact division */
691
uz = (ulong)-((*b + r)>>1);
692
else
693
{
694
ulong ub = (ulong)-*b, ur = (ulong)-r;
695
uz = (ub + ur) >> 1;
696
}
697
*c -= (-q) * uz; /* c -= qz */
698
}
699
else
700
{ /* uz = z >= 0, q > 0 */
701
if (r <= 0)
702
uz = (*b + r)>>1;
703
else
704
{
705
ulong ub = (ulong)*b, ur = (ulong)r;
706
uz = ((ub + ur) >> 1);
707
}
708
*c -= q * uz; /* c -= qz */
709
}
710
*b = r;
711
}
712
static void
713
REDBU(GEN a, GEN *b, GEN *c, GEN u1, GEN *u2)
714
{ /* REDB(a,b,c) */
715
GEN r, q = dvmdii_round(*b, a, &r);
716
*c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
717
*b = r;
718
*u2 = subii(*u2, mulii(q, u1));
719
}
720
721
/* q t_QFB, return reduced representative and set base change U in Sl2(Z) */
722
GEN
723
redimagsl2(GEN q, GEN *U)
724
{
725
pari_sp av = avma;
726
GEN z, u1,u2,v1,v2,Q;
727
GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);
728
long cmp;
729
u1 = gen_1; u2 = gen_0;
730
cmp = abscmpii(a, b);
731
if (cmp < 0)
732
REDBU(a,&b,&c, u1,&u2);
733
else if (cmp == 0 && signe(b) < 0)
734
{ /* b = -a */
735
b = negi(b);
736
u2 = gen_1;
737
}
738
for(;;)
739
{
740
cmp = abscmpii(a, c); if (cmp <= 0) break;
741
swap(a,c); b = negi(b);
742
z = u1; u1 = u2; u2 = negi(z);
743
REDBU(a,&b,&c, u1,&u2);
744
if (gc_needed(av, 1)) {
745
if (DEBUGMEM>1) pari_warn(warnmem, "redimagsl2");
746
gerepileall(av, 5, &a,&b,&c, &u1,&u2);
747
}
748
}
749
if (cmp == 0 && signe(b) < 0)
750
{
751
b = negi(b);
752
z = u1; u1 = u2; u2 = negi(z);
753
}
754
/* Let q = (A,B,C). q o [u1,u2; v1,v2] = Q implies
755
* [v1,v2] = (1/C) [(b-B)/2 u1 - a u2, c u1 - (b+B)/2 u2] */
756
z = shifti(subii(b, gel(q,2)), -1);
757
v1 = subii(mulii(z, u1), mulii(a, u2)); v1 = diviiexact(v1, gel(q,3));
758
z = subii(z, b);
759
v2 = addii(mulii(z, u2), mulii(c, u1)); v2 = diviiexact(v2, gel(q,3));
760
*U = mkmat2(mkcol2(u1,v1), mkcol2(u2,v2));
761
Q = lg(q)==5 ? mkqfb(a,b,c,gel(q,4)): mkvec3(a,b,c);
762
gerepileall(av, 2, &Q, U);
763
return Q;
764
}
765
766
static GEN
767
setq_b0(ulong a, ulong c, GEN D)
768
{ retmkqfb(utoipos(a), gen_0, utoipos(c), icopy(D)); }
769
/* assume |sb| = 1 */
770
static GEN
771
setq(ulong a, ulong b, ulong c, long sb, GEN D)
772
{ retmkqfb(utoipos(a), sb==1? utoipos(b): utoineg(b), utoipos(c), icopy(D)); }
773
/* 0 < a, c < 2^BIL, b = 0 */
774
static GEN
775
redimag_1_b0(ulong a, ulong c, GEN D)
776
{ return (a <= c)? setq_b0(a, c, D): setq_b0(c, a, D); }
777
778
/* 0 < a, c < 2^BIL: single word affair */
779
static GEN
780
redimag_1(pari_sp av, GEN a, GEN b, GEN c, GEN D)
781
{
782
ulong ua, ub, uc;
783
long sb;
784
for(;;)
785
{ /* at most twice */
786
long lb = lgefint(b); /* <= 3 after first loop */
787
if (lb == 2) return redimag_1_b0(a[2],c[2], D);
788
if (lb == 3 && uel(b,2) <= (ulong)LONG_MAX) break;
789
REDB(a,&b,&c);
790
if (uel(a,2) <= uel(c,2))
791
{ /* lg(b) <= 3 but may be too large for itos */
792
long s = signe(b);
793
set_avma(av);
794
if (!s) return redimag_1_b0(a[2], c[2], D);
795
if (a[2] == c[2]) s = 1;
796
return setq(a[2], b[2], c[2], s, D);
797
}
798
swap(a,c); b = negi(b);
799
}
800
/* b != 0 */
801
set_avma(av);
802
ua = a[2];
803
ub = sb = b[2]; if (signe(b) < 0) sb = -sb;
804
uc = c[2];
805
if (ua < ub)
806
sREDB(ua, &sb, &uc);
807
else if (ua == ub && sb < 0) sb = (long)ub;
808
while(ua > uc)
809
{
810
lswap(ua,uc); sb = -sb;
811
sREDB(ua, &sb, &uc);
812
}
813
if (!sb) return setq_b0(ua, uc, D);
814
else
815
{
816
long s = 1;
817
if (sb < 0)
818
{
819
sb = -sb;
820
if (ua != uc) s = -1;
821
}
822
return setq(ua, sb, uc, s, D);
823
}
824
}
825
826
static GEN
827
redimag_av(pari_sp av, GEN q)
828
{
829
GEN a = gel(q,1), b = gel(q,2), c = gel(q,3), D = gel(q,4);
830
long cmp, lc = lgefint(c);
831
832
if (lgefint(a) == 3 && lc == 3) return redimag_1(av, a, b, c, D);
833
cmp = abscmpii(a, b);
834
if (cmp < 0)
835
REDB(a,&b,&c);
836
else if (cmp == 0 && signe(b) < 0)
837
b = negi(b);
838
for(;;)
839
{
840
cmp = abscmpii(a, c); if (cmp <= 0) break;
841
lc = lgefint(a); /* lg(future c): we swap a & c next */
842
if (lc == 3) return redimag_1(av, a, b, c, D);
843
swap(a,c); b = negi(b); /* apply rho */
844
REDB(a,&b,&c);
845
}
846
if (cmp == 0 && signe(b) < 0) b = negi(b);
847
return gerepilecopy(av, mkqfb(a, b, c, D));
848
}
849
static GEN
850
redimag(GEN q) { return redimag_av(avma, q); }
851
852
static GEN
853
rhoimag(GEN x)
854
{
855
pari_sp av = avma;
856
GEN a = gel(x,1), b = gel(x,2), c = gel(x,3);
857
int fl = abscmpii(a, c);
858
if (fl <= 0)
859
{
860
int fg = abscmpii(a, b);
861
if (fg >= 0)
862
{
863
x = gcopy(x);
864
if ((!fl || !fg) && signe(gel(x,2)) < 0) setsigne(gel(x,2), 1);
865
return x;
866
}
867
}
868
swap(a,c); b = negi(b);
869
REDB(a, &b, &c);
870
return gerepilecopy(av, mkqfb(a,b,c, qfb_disc(x)));
871
}
872
873
/* qfr3 / qfr5 */
874
875
/* t_QFB are unusable: D, sqrtD, isqrtD are recomputed all the time and the
876
* logarithmic Shanks's distance is costly and hard to control.
877
* qfr3 / qfr5 routines take a container of t_INTs (e.g a t_VEC) as argument,
878
* at least 3 (resp. 5) components [it is a feature that they do not check the
879
* precise type or length of the input]. They return a vector of length 3
880
* (resp. 5). A qfr3 [a,b,c] contains the form coeffs, in a qfr5 [a,b,c, e,d]
881
* the t_INT e is a binary exponent, d a t_REAL, coding the distance in
882
* multiplicative form: the true distance is obtained from qfr5_dist.
883
* All other qfr routines are obsolete (inefficient) wrappers */
884
885
/* static functions are not stack-clean. Unless mentionned otherwise, public
886
* functions are. */
887
888
#define EMAX 22
889
static void
890
fix_expo(GEN x)
891
{
892
if (expo(gel(x,5)) >= (1L << EMAX)) {
893
gel(x,4) = addiu(gel(x,4), 1);
894
shiftr_inplace(gel(x,5), - (1L << EMAX));
895
}
896
}
897
898
/* (1/2) log (d * 2^{e * 2^EMAX}). Not stack clean if e != 0 */
899
GEN
900
qfr5_dist(GEN e, GEN d, long prec)
901
{
902
GEN t = logr_abs(d);
903
if (signe(e)) {
904
GEN u = mulir(e, mplog2(prec));
905
shiftr_inplace(u, EMAX); t = addrr(t, u);
906
}
907
shiftr_inplace(t, -1); return t;
908
}
909
910
static void
911
rho_get_BC(GEN *B, GEN *C, GEN b, GEN c, struct qfr_data *S)
912
{
913
GEN t, u;
914
u = shifti(c,1);
915
t = (abscmpii(S->isqrtD,c) >= 0)? S->isqrtD: c;
916
u = remii(addii_sign(t,1, b,signe(b)), u);
917
*B = addii_sign(t, 1, u, -signe(u)); /* |t| - (|t|+b) % |2c| */
918
if (*B == gen_0)
919
{ u = shifti(S->D, -2); setsigne(u, -1); }
920
else
921
u = shifti(addii_sign(sqri(*B),1, S->D,-1), -2);
922
*C = diviiexact(u, c); /* = (B^2-D)/4c */
923
}
924
/* Not stack-clean */
925
GEN
926
qfr3_rho(GEN x, struct qfr_data *S)
927
{
928
GEN B, C, b = gel(x,2), c = gel(x,3);
929
rho_get_BC(&B, &C, b, c, S);
930
return mkvec3(c,B,C);
931
}
932
/* Not stack-clean */
933
GEN
934
qfr5_rho(GEN x, struct qfr_data *S)
935
{
936
GEN B, C, y, b = gel(x,2), c = gel(x,3);
937
long sb = signe(b);
938
939
rho_get_BC(&B, &C, b, c, S);
940
y = mkvec5(c,B,C, gel(x,4), gel(x,5));
941
if (sb) {
942
GEN t = subii(sqri(b), S->D);
943
if (sb < 0)
944
t = divir(t, sqrr(subir(b,S->sqrtD)));
945
else
946
t = divri(sqrr(addir(b,S->sqrtD)), t);
947
/* t = (b + sqrt(D)) / (b - sqrt(D)), evaluated stably */
948
gel(y,5) = mulrr(t, gel(y,5)); fix_expo(y);
949
}
950
return y;
951
}
952
953
/* Not stack-clean */
954
GEN
955
qfr_to_qfr5(GEN x, long prec)
956
{ return mkvec5(gel(x,1),gel(x,2),gel(x,3),gen_0,real_1(prec)); }
957
958
/* d0 = initial distance, x = [a,b,c, expo(d), d], d = exp(2*distance) */
959
GEN
960
qfr5_to_qfr(GEN x, GEN D, GEN d0)
961
{
962
if (d0)
963
{
964
GEN n = gel(x,4), d = absr(gel(x,5));
965
if (signe(n))
966
{
967
n = addis(shifti(n, EMAX), expo(d));
968
setexpo(d, 0); d = logr_abs(d);
969
if (signe(n)) d = addrr(d, mulir(n, mplog2(lg(d0))));
970
shiftr_inplace(d, -1);
971
d0 = addrr(d0, d);
972
}
973
else if (!gequal1(d)) /* avoid loss of precision */
974
{
975
d = logr_abs(d);
976
shiftr_inplace(d, -1);
977
d0 = addrr(d0, d);
978
}
979
}
980
x = qfr3_to_qfr(x, D);
981
return d0 ? mkvec2(x,d0): x;
982
}
983
984
/* Not stack-clean */
985
GEN
986
qfr3_to_qfr(GEN x, GEN d) { retmkqfb(gel(x,1), gel(x,2), gel(x,3), d); }
987
988
static int
989
ab_isreduced(GEN a, GEN b, GEN isqrtD)
990
{
991
if (signe(b) > 0 && abscmpii(b, isqrtD) <= 0)
992
{
993
GEN t = addii_sign(isqrtD,1, shifti(a,1),-1);
994
long l = abscmpii(b, t); /* compare |b| and |floor(sqrt(D)) - |2a|| */
995
if (l > 0 || (l == 0 && signe(t) < 0)) return 1;
996
}
997
return 0;
998
}
999
1000
INLINE int
1001
qfr_isreduced(GEN x, GEN isqrtD)
1002
{
1003
return ab_isreduced(gel(x,1),gel(x,2),isqrtD);
1004
}
1005
1006
/* Not stack-clean */
1007
GEN
1008
qfr5_red(GEN x, struct qfr_data *S)
1009
{
1010
pari_sp av = avma;
1011
while (!qfr_isreduced(x, S->isqrtD))
1012
{
1013
x = qfr5_rho(x, S);
1014
if (gc_needed(av,2))
1015
{
1016
if (DEBUGMEM>1) pari_warn(warnmem,"qfr5_red");
1017
x = gerepilecopy(av, x);
1018
}
1019
}
1020
return x;
1021
}
1022
/* Not stack-clean */
1023
GEN
1024
qfr3_red(GEN x, struct qfr_data *S)
1025
{
1026
pari_sp av = avma;
1027
while (!qfr_isreduced(x, S->isqrtD))
1028
{
1029
x = qfr3_rho(x, S);
1030
if (gc_needed(av,2))
1031
{
1032
if (DEBUGMEM>1) pari_warn(warnmem,"qfr3_red");
1033
x = gerepilecopy(av, x);
1034
}
1035
}
1036
return x;
1037
}
1038
1039
void
1040
qfr_data_init(GEN D, long prec, struct qfr_data *S)
1041
{
1042
S->D = D;
1043
S->sqrtD = sqrtr(itor(S->D,prec));
1044
S->isqrtD = truncr(S->sqrtD);
1045
}
1046
1047
static GEN
1048
qfr5_init(GEN x, GEN d, struct qfr_data *S)
1049
{
1050
long prec = realprec(d), l = -expo(d);
1051
if (l < BITS_IN_LONG) l = BITS_IN_LONG;
1052
prec = maxss(prec, nbits2prec(l));
1053
S->D = qfb_disc(x);
1054
x = qfr_to_qfr5(x,prec);
1055
if (!S->sqrtD) S->sqrtD = sqrtr(itor(S->D,prec));
1056
else if (typ(S->sqrtD) != t_REAL) pari_err_TYPE("qfr_init",S->sqrtD);
1057
1058
if (!S->isqrtD)
1059
{
1060
pari_sp av=avma;
1061
long e;
1062
S->isqrtD = gcvtoi(S->sqrtD,&e);
1063
if (e>-2) { set_avma(av); S->isqrtD = sqrti(S->D); }
1064
}
1065
else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
1066
return x;
1067
}
1068
static GEN
1069
qfr3_init(GEN x, struct qfr_data *S)
1070
{
1071
S->D = qfb_disc(x);
1072
if (!S->isqrtD) S->isqrtD = sqrti(S->D);
1073
else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
1074
return x;
1075
}
1076
1077
#define qf_NOD 2
1078
#define qf_STEP 1
1079
1080
static GEN
1081
redreal_i(GEN x, long flag, GEN isqrtD, GEN sqrtD)
1082
{
1083
struct qfr_data S;
1084
GEN d = NULL, y;
1085
if (typ(x)==t_VEC) { d = gel(x,2); x = gel(x,1); } else flag |= qf_NOD;
1086
S.sqrtD = sqrtD;
1087
S.isqrtD = isqrtD;
1088
y = (flag & qf_NOD)? qfr3_init(x, &S): qfr5_init(x, d, &S);
1089
switch(flag) {
1090
case 0: y = qfr5_red(y,&S); break;
1091
case qf_NOD: y = qfr3_red(y,&S); break;
1092
case qf_STEP: y = qfr5_rho(y,&S); break;
1093
case qf_STEP|qf_NOD: y = qfr3_rho(y,&S); break;
1094
default: pari_err_FLAG("qfbred");
1095
}
1096
return qfr5_to_qfr(y, qfb_disc(x), d);
1097
}
1098
static GEN
1099
redreal(GEN x) { return redreal_i(x,0,NULL,NULL); }
1100
1101
GEN
1102
qfbred0(GEN x, long flag, GEN isqrtD, GEN sqrtD)
1103
{
1104
pari_sp av;
1105
GEN q = check_qfbext("qfbred",x);
1106
if (qfb_is_qfi(q)) return (flag & qf_STEP)? rhoimag(x): redimag(x);
1107
if (typ(x)==t_QFB) flag |= qf_NOD;
1108
else flag &= ~qf_NOD;
1109
av = avma;
1110
return gerepilecopy(av, redreal_i(x,flag,isqrtD,sqrtD));
1111
}
1112
/* t_QFB */
1113
GEN
1114
qfbred_i(GEN x) { return qfb_is_qfi(x)? redimag(x): redreal(x); }
1115
GEN
1116
qfbred(GEN x) { return qfbred0(x, 0, NULL, NULL); }
1117
1118
/* Not stack-clean */
1119
GEN
1120
qfr5_compraw(GEN x, GEN y)
1121
{
1122
GEN z = cgetg(6,t_VEC); qfb_comp(z,x,y);
1123
if (x == y)
1124
{
1125
gel(z,4) = shifti(gel(x,4),1);
1126
gel(z,5) = sqrr(gel(x,5));
1127
}
1128
else
1129
{
1130
gel(z,4) = addii(gel(x,4),gel(y,4));
1131
gel(z,5) = mulrr(gel(x,5),gel(y,5));
1132
}
1133
fix_expo(z); return z;
1134
}
1135
GEN
1136
qfr5_comp(GEN x, GEN y, struct qfr_data *S)
1137
{ return qfr5_red(qfr5_compraw(x, y), S); }
1138
/* Not stack-clean */
1139
GEN
1140
qfr3_compraw(GEN x, GEN y)
1141
{
1142
GEN z = cgetg(4,t_VEC); qfb_comp(z,x,y);
1143
return z;
1144
}
1145
GEN
1146
qfr3_comp(GEN x, GEN y, struct qfr_data *S)
1147
{ return qfr3_red(qfr3_compraw(x,y), S); }
1148
1149
/* m > 0. Not stack-clean */
1150
static GEN
1151
qfr5_powraw(GEN x, long m)
1152
{
1153
GEN y = NULL;
1154
for (; m; m >>= 1)
1155
{
1156
if (m&1) y = y? qfr5_compraw(y,x): x;
1157
if (m == 1) break;
1158
x = qfr5_compraw(x,x);
1159
}
1160
return y;
1161
}
1162
1163
/* return x^n. Not stack-clean */
1164
GEN
1165
qfr5_pow(GEN x, GEN n, struct qfr_data *S)
1166
{
1167
GEN y = NULL;
1168
long i, m, s = signe(n);
1169
if (!s) return qfr5_1(S, lg(gel(x,5)));
1170
if (s < 0) x = qfb_inv(x);
1171
for (i=lgefint(n)-1; i>1; i--)
1172
{
1173
m = n[i];
1174
for (; m; m>>=1)
1175
{
1176
if (m&1) y = y? qfr5_comp(y,x,S): x;
1177
if (m == 1 && i == 2) break;
1178
x = qfr5_comp(x,x,S);
1179
}
1180
}
1181
return y;
1182
}
1183
/* m > 0; return x^m. Not stack-clean */
1184
static GEN
1185
qfr3_powraw(GEN x, long m)
1186
{
1187
GEN y = NULL;
1188
for (; m; m>>=1)
1189
{
1190
if (m&1) y = y? qfr3_compraw(y,x): x;
1191
if (m == 1) break;
1192
x = qfr3_compraw(x,x);
1193
}
1194
return y;
1195
}
1196
/* return x^n. Not stack-clean */
1197
GEN
1198
qfr3_pow(GEN x, GEN n, struct qfr_data *S)
1199
{
1200
GEN y = NULL;
1201
long i, m, s = signe(n);
1202
if (!s) return qfr3_1(S);
1203
if (s < 0) x = qfb_inv(x);
1204
for (i=lgefint(n)-1; i>1; i--)
1205
{
1206
m = n[i];
1207
for (; m; m>>=1)
1208
{
1209
if (m&1) y = y? qfr3_comp(y,x,S): x;
1210
if (m == 1 && i == 2) break;
1211
x = qfr3_comp(x,x,S);
1212
}
1213
}
1214
return y;
1215
}
1216
1217
static GEN
1218
qfrinvraw(GEN x)
1219
{
1220
if (typ(x) == t_VEC) retmkvec2(qfbinv(gel(x,1)), negr(gel(x,2)));
1221
return qfbinv(x);
1222
}
1223
static GEN
1224
qfrpowraw(GEN x, long n)
1225
{
1226
struct qfr_data S = { NULL, NULL, NULL };
1227
pari_sp av = avma;
1228
if (!n) return qfr_1(x);
1229
if (n==1) return gcopy(x);
1230
if (n==-1) return qfrinvraw(x);
1231
if (typ(x)==t_QFB)
1232
{
1233
GEN D = qfb_disc(x);
1234
if (n < 0) { x = qfb_inv(x); n = -n; }
1235
x = qfr3_powraw(x, n);
1236
x = qfr3_to_qfr(x, D);
1237
}
1238
else
1239
{
1240
GEN d0 = gel(x,2);
1241
x = gel(x,1);
1242
if (n < 0) { x = qfb_inv(x); n = -n; }
1243
x = qfr5_init(x, d0, &S);
1244
if (labs(n) != 1) x = qfr5_powraw(x, n);
1245
x = qfr5_to_qfr(x, S.D, mulrs(d0,n));
1246
}
1247
return gerepilecopy(av, x);
1248
}
1249
static GEN
1250
qfrpow(GEN x, GEN n)
1251
{
1252
struct qfr_data S = { NULL, NULL, NULL };
1253
long s = signe(n);
1254
pari_sp av = avma;
1255
if (!s) return qfr_1(x);
1256
if (typ(x)==t_QFB)
1257
{
1258
if (s < 0) x = qfb_inv(x);
1259
x = qfr3_init(x, &S);
1260
x = is_pm1(n)? qfr3_red(x, &S): qfr3_pow(x, n, &S);
1261
x = qfr3_to_qfr(x, S.D);
1262
}
1263
else
1264
{
1265
GEN d0 = gel(x,2);
1266
x = gel(x,1);
1267
if (s < 0) x = qfb_inv(x);
1268
x = qfr5_init(x, d0, &S);
1269
x = is_pm1(n)? qfr5_red(x, &S): qfr5_pow(x, n, &S);
1270
x = qfr5_to_qfr(x, S.D, mulri(d0,n));
1271
}
1272
return gerepilecopy(av, x);
1273
}
1274
GEN
1275
qfbpowraw(GEN x, long n)
1276
{
1277
GEN q = check_qfbext("qfbpowraw",x);
1278
return qfb_is_qfi(q)? qfipowraw(x,n): qfrpowraw(x,n);
1279
}
1280
/* same discriminant, no distance, no checks */
1281
GEN
1282
qfbpow_i(GEN x, GEN n) { return qfb_is_qfi(x)? qfipow(x,n): qfrpow(x,n); }
1283
GEN
1284
qfbpow(GEN x, GEN n)
1285
{
1286
GEN q = check_qfbext("qfbpow",x);
1287
return qfb_is_qfi(q)? qfipow(x,n): qfrpow(x,n);
1288
}
1289
1290
/* Prime forms attached to prime ideals of degree 1 */
1291
1292
/* assume x != 0 a t_INT, p > 0
1293
* Return a t_QFB, but discriminant sign is not checked: can be used for
1294
* real forms as well */
1295
GEN
1296
primeform_u(GEN x, ulong p)
1297
{
1298
GEN c, y = cgetg(5, t_QFB);
1299
pari_sp av = avma;
1300
ulong b;
1301
long s;
1302
1303
s = mod8(x); if (signe(x) < 0 && s) s = 8-s;
1304
/* 2 or 3 mod 4 */
1305
if (s & 2) pari_err_DOMAIN("primeform", "disc % 4", ">",gen_1, x);
1306
if (p == 2) {
1307
switch(s) {
1308
case 0: b = 0; break;
1309
case 1: b = 1; break;
1310
case 4: b = 2; break;
1311
default: pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
1312
b = 0; /* -Wall */
1313
}
1314
c = shifti(subsi(s,x), -3);
1315
} else {
1316
b = Fl_sqrt(umodiu(x,p), p);
1317
if (b == ~0UL) pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
1318
/* mod(b) != mod2(x) ? */
1319
if ((b ^ s) & 1) b = p - b;
1320
c = diviuexact(shifti(subii(sqru(b), x), -2), p);
1321
}
1322
gel(y,3) = gerepileuptoint(av, c);
1323
gel(y,4) = icopy(x);
1324
gel(y,2) = utoi(b);
1325
gel(y,1) = utoipos(p); return y;
1326
}
1327
1328
/* special case: p = 1 return unit form */
1329
GEN
1330
primeform(GEN x, GEN p)
1331
{
1332
const char *f = "primeform";
1333
pari_sp av;
1334
long s, sx = signe(x), sp = signe(p);
1335
GEN y, b, absp;
1336
1337
if (typ(x) != t_INT) pari_err_TYPE(f,x);
1338
if (typ(p) != t_INT) pari_err_TYPE(f,p);
1339
if (!sp) pari_err_DOMAIN(f,"p","=",gen_0,p);
1340
if (!sx) pari_err_DOMAIN(f,"D","=",gen_0,x);
1341
if (lgefint(p) == 3)
1342
{
1343
ulong pp = p[2];
1344
if (pp == 1) {
1345
if (sx < 0) {
1346
long r;
1347
if (sp < 0) pari_err_IMPL("negative definite t_QFB");
1348
r = mod4(x);
1349
if (r && r != 3) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
1350
return qfi_1_by_disc(x);
1351
}
1352
y = qfr_1_by_disc(x);
1353
if (sp < 0) { gel(y,1) = gen_m1; togglesign(gel(y,3)); }
1354
return y;
1355
}
1356
y = primeform_u(x, pp);
1357
if (sx < 0) {
1358
if (sp < 0) pari_err_IMPL("negative definite t_QFB");
1359
return y;
1360
}
1361
if (sp < 0) { togglesign(gel(y,1)); togglesign(gel(y,3)); }
1362
return gcopy( qfr3_to_qfr(y, x) );
1363
}
1364
s = mod8(x);
1365
if (sx < 0)
1366
{
1367
if (sp < 0) pari_err_IMPL("negative definite t_QFB");
1368
if (s) s = 8-s;
1369
}
1370
y = cgetg(5, t_QFB);
1371
/* 2 or 3 mod 4 */
1372
if (s & 2) pari_err_DOMAIN(f, "disc % 4", ">",gen_1, x);
1373
absp = absi_shallow(p); av = avma;
1374
b = Fp_sqrt(x, absp); if (!b) pari_err_SQRTN(f, mkintmod(x,absp));
1375
s &= 1; /* s = x mod 2 */
1376
/* mod(b) != mod2(x) ? [Warning: we may have b == 0] */
1377
if ((!signe(b) && s) || mod2(b) != s) b = gerepileuptoint(av, subii(absp,b));
1378
1379
av = avma;
1380
gel(y,3) = gerepileuptoint(av, diviiexact(shifti(subii(sqri(b), x), -2), p));
1381
gel(y,4) = icopy(x);
1382
gel(y,2) = b;
1383
gel(y,1) = icopy(p);
1384
return y;
1385
}
1386
1387
static GEN
1388
normforms(GEN D, GEN fa)
1389
{
1390
long i, j, k, lB, aN, sa;
1391
GEN a, L, V, B, N, N2;
1392
int D_odd = mpodd(D);
1393
a = typ(fa) == t_INT ? fa: typ(fa) == t_VEC? gel(fa,1): factorback(fa);
1394
sa = signe(a);
1395
if (sa==0 || (signe(D)<0 && sa<0)) return NULL;
1396
V = D_odd? Zn_quad_roots(fa, gen_1, shifti(subsi(1, D), -2))
1397
: Zn_quad_roots(fa, gen_0, negi(shifti(D, -2)));
1398
if (!V) return NULL;
1399
N = gel(V,1); B = gel(V,2); lB = lg(B);
1400
N2 = shifti(N,1);
1401
aN = itou(diviiexact(a, N)); /* |a|/N */
1402
L = cgetg((lB-1)*aN+1, t_VEC);
1403
for (k = 1, i = 1; i < lB; i++)
1404
{
1405
GEN b = shifti(gel(B,i), 1), c, C;
1406
if (D_odd) b = addiu(b , 1);
1407
c = diviiexact(shifti(subii(sqri(b), D), -2), a);
1408
for (j = 0;; b = addii(b, N2))
1409
{
1410
gel(L, k++) = mkqfb(a, b, c, D);
1411
if (++j == aN) break;
1412
C = addii(b, N); if (aN > 1) C = diviuexact(C, aN);
1413
c = sa > 0? addii(c, C): subii(c, C);
1414
}
1415
}
1416
return L;
1417
}
1418
1419
/* Let M and N in SL2(Z), return (N*M^-1)[,1] */
1420
static GEN
1421
SL2_div_mul_e1(GEN N, GEN M)
1422
{
1423
GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);
1424
GEN p = subii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));
1425
GEN q = subii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));
1426
return mkvec2(p,q);
1427
}
1428
1429
/* Let M and N in SL2(Z), return (N*[1,0;0,-1]*M^-1)[,1] */
1430
static GEN
1431
SL2_swap_div_mul_e1(GEN N, GEN M)
1432
{
1433
GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);
1434
GEN p = addii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));
1435
GEN q = addii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));
1436
return mkvec2(p,q);
1437
}
1438
1439
/* Test equality modulo GL2 of two reduced forms */
1440
static int
1441
GL2_qfb_equal(GEN a, GEN b)
1442
{
1443
return equalii(gel(a,1),gel(b,1))
1444
&& absequalii(gel(a,2),gel(b,2))
1445
&& equalii(gel(a,3),gel(b,3));
1446
}
1447
1448
static GEN
1449
qfisolve_normform(GEN Q, GEN P)
1450
{
1451
GEN a = gel(Q,1), N = gel(Q,2);
1452
GEN M, b = redimagsl2(P, &M);
1453
if (!GL2_qfb_equal(a,b) || signe(gel(a,2))!=signe(gel(b,2)))
1454
return NULL;
1455
return SL2_div_mul_e1(N,M);
1456
}
1457
1458
static GEN
1459
qfbsolve_cornacchia(GEN c, GEN p, int swap)
1460
{
1461
pari_sp av = avma;
1462
GEN M, N;
1463
if (kronecker(negi(c), p) < 0 || !cornacchia(c, p, &M,&N))
1464
{ set_avma(av); return gen_0; }
1465
return gerepilecopy(av, swap? mkvec2(N,M): mkvec2(M,N));
1466
}
1467
1468
GEN
1469
qfisolvep(GEN Q, GEN p)
1470
{
1471
GEN M, N, x,y, a,b,c, d;
1472
pari_sp av = avma;
1473
if (!signe(gel(Q,2)))
1474
{
1475
a = gel(Q,1);
1476
c = gel(Q,3); /* if principal form, use faster cornacchia */
1477
if (equali1(a)) return qfbsolve_cornacchia(c, p, 0);
1478
if (equali1(c)) return qfbsolve_cornacchia(a, p, 1);
1479
}
1480
d = qfb_disc(Q); if (kronecker(d,p) < 0) return gen_0;
1481
a = redimagsl2(Q, &N);
1482
if (equali1(gel(a,1))) /* principal form */
1483
{
1484
long r;
1485
if (!signe(gel(a,2)))
1486
{
1487
a = qfbsolve_cornacchia(gel(a,3), p, 0);
1488
if (a == gen_0) { set_avma(av); return gen_0; }
1489
a = ZM_ZC_mul(N, a);
1490
a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */
1491
return gerepileupto(av, a);
1492
}
1493
/* x^2 + xy + ((1-d)/4)y^2 = p <==> (2x + y)^2 - d y^2 = 4p */
1494
if (!cornacchia2(negi(d), p, &x, &y)) { set_avma(av); return gen_0; }
1495
x = divis_rem(subii(x,y), 2, &r); if (r) { set_avma(av); return gen_0; }
1496
a = ZM_ZC_mul(N, mkvec2(x,y));
1497
a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */
1498
return gerepileupto(av, a);
1499
}
1500
b = redimagsl2(primeform(d, p), &M);
1501
if (!GL2_qfb_equal(a,b)) { set_avma(av); return gen_0; }
1502
if (signe(gel(a,2))==signe(gel(b,2)))
1503
x = SL2_div_mul_e1(N,M);
1504
else
1505
x = SL2_swap_div_mul_e1(N,M);
1506
return gerepilecopy(av, x);
1507
}
1508
1509
static GEN
1510
redrealsl2step(GEN A, GEN rd)
1511
{
1512
GEN N, V = gel(A,1), M = gel(A,2);
1513
GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);
1514
GEN C = absi_shallow(c);
1515
GEN t = addii(b, gmax_shallow(rd, C));
1516
GEN r, q = truedvmdii(t, shifti(C,1), &r);
1517
b = subii(t, addii(r,b));
1518
a = c;
1519
c = truedivii(subii(sqri(b), d), shifti(c,2));
1520
if (signe(a) < 0) togglesign(q);
1521
N = mkmat2(gel(M,2),
1522
mkcol2(subii(mulii(q, gcoeff(M, 1, 2)), gcoeff(M, 1, 1)),
1523
subii(mulii(q, gcoeff(M, 2, 2)), gcoeff(M, 2, 1))));
1524
return mkvec2(mkqfb(a,b,c,gel(V,4)), N);
1525
}
1526
1527
static GEN
1528
redrealsl2(GEN V, GEN rd)
1529
{
1530
pari_sp ltop = avma;
1531
GEN M, u1, u2, v1, v2;
1532
GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);
1533
u1 = v2 = gen_1; v1 = u2 = gen_0;
1534
while (!ab_isreduced(a,b,rd))
1535
{
1536
GEN C = mpabs_shallow(c);
1537
GEN t = addii(b, gmax_shallow(rd,C));
1538
GEN r, q = truedvmdii(t, shifti(C,1), &r);
1539
b = subii(t, addii(r,b));
1540
a = c;
1541
c = truedivii(subii(sqri(b), d), shifti(c,2));
1542
if (signe(a) < 0) togglesign(q);
1543
r = u1; u1 = v1; v1 = subii(mulii(q, v1), r);
1544
r = u2; u2 = v2; v2 = subii(mulii(q, v2), r);
1545
if (gc_needed(ltop, 1))
1546
{
1547
if (DEBUGMEM>1) pari_warn(warnmem,"redrealsl2");
1548
gerepileall(ltop, 7, &a,&b,&c,&u1,&u2,&v1,&v2);
1549
}
1550
}
1551
M = mkmat2(mkcol2(u1,u2), mkcol2(v1,v2));
1552
return gerepilecopy(ltop, mkvec2(lg(V)==5?mkqfb(a,b,c,gel(V,4))
1553
:mkvec3(a,b,c), M));
1554
}
1555
1556
GEN
1557
qfbredsl2(GEN q, GEN isD)
1558
{
1559
pari_sp av;
1560
if (typ(q) != t_QFB) pari_err_TYPE("qfbredsl2",q);
1561
if (qfb_is_qfi(q))
1562
{
1563
GEN v = cgetg(3,t_VEC);
1564
if (isD) pari_err_TYPE("qfbredsl2", isD);
1565
gel(v,1) = redimagsl2(q, &gel(v,2)); return v;
1566
}
1567
av = avma;
1568
if (!isD) isD = sqrti(qfb_disc(q));
1569
else if (typ(isD) != t_INT) pari_err_TYPE("qfbredsl2",isD);
1570
return gerepileupto(av, redrealsl2(q, isD));
1571
}
1572
1573
static GEN
1574
qfrsolve_normform(GEN N, GEN Ps, GEN rd)
1575
{
1576
pari_sp av = avma, btop;
1577
GEN M = N, P = redrealsl2(Ps, rd), Q = P;
1578
1579
btop = avma;
1580
for(;;)
1581
{
1582
if (qfb_equal(gel(M,1), gel(P,1)))
1583
return gerepilecopy(av, SL2_div_mul_e1(gel(M,2),gel(P,2)));
1584
if (qfb_equal(gel(N,1), gel(Q,1)))
1585
return gerepilecopy(av, SL2_div_mul_e1(gel(N,2),gel(Q,2)));
1586
M = redrealsl2step(M, rd);
1587
if (qfb_equal(gel(M,1), gel(N,1))) return gc_NULL(av);
1588
Q = redrealsl2step(Q, rd);
1589
if (qfb_equal(gel(P,1), gel(Q,1))) return gc_NULL(av);
1590
if (gc_needed(btop, 1)) gerepileall(btop, 2, &M, &Q);
1591
}
1592
}
1593
1594
GEN
1595
qfrsolvep(GEN Q, GEN p)
1596
{
1597
pari_sp av = avma;
1598
GEN N, x, rd, d = qfb_disc(Q);
1599
1600
if (kronecker(d, p) < 0) return gc_const(av, gen_0);
1601
rd = sqrti(d);
1602
N = redrealsl2(Q, rd);
1603
x = qfrsolve_normform(N, primeform(d, p), rd);
1604
return x? gerepileupto(av, x): gc_const(av, gen_0);
1605
}
1606
1607
static GEN
1608
qfsolve_normform(GEN Q, GEN f, GEN rd)
1609
{ return rd? qfrsolve_normform(Q, f, rd): qfisolve_normform(Q, f); }
1610
static GEN
1611
qfbsolve_primitive_i(GEN Q, GEN rd, GEN *Qr, GEN fa, long all)
1612
{
1613
GEN x, W, F = normforms(qfb_disc(Q), fa);
1614
long i, j, l;
1615
if (!F) return NULL;
1616
if (!*Qr) *Qr = qfbredsl2(Q, rd);
1617
l = lg(F); W = all? cgetg(l, t_VEC): NULL;
1618
for (j = i = 1; i < l; i++)
1619
if ((x = qfsolve_normform(*Qr, gel(F,i), rd)))
1620
{
1621
if (!all) return x;
1622
gel(W,j++) = x;
1623
}
1624
if (j == 1) return NULL;
1625
setlg(W,j); return W;
1626
}
1627
1628
static GEN
1629
qfb_initrd(GEN Q) { GEN d = qfb_disc(Q); return signe(d) > 0? sqrti(d): NULL; }
1630
static GEN
1631
qfbsolve_primitive(GEN Q, GEN fa, long all)
1632
{
1633
GEN x, Qr = NULL, rdQ = qfb_initrd(Q);
1634
x = qfbsolve_primitive_i(Q, rdQ, &Qr, fa, all);
1635
if (!x) return cgetg(1, t_VEC);
1636
return x;
1637
}
1638
1639
/* f / g^2 */
1640
static GEN
1641
famat_divsqr(GEN f, GEN g)
1642
{ return famat_reduce(famat_div_shallow(f, famat_pows_shallow(g,2))); }
1643
static GEN
1644
qfbsolve_all(GEN Q, GEN n, long all)
1645
{
1646
GEN W, Qr = NULL, fa = factorint(n, 0), rdQ = qfb_initrd(Q);
1647
GEN D = divisors_factored(mkmat2(gel(fa,1), gshift(gel(fa,2),-1)));
1648
long i, j, l = lg(D);
1649
W = all? cgetg(l, t_VEC): NULL;
1650
for (i = j = 1; i < l; i++)
1651
{
1652
GEN w, d = gel(D,i), FA = i == 1? fa: famat_divsqr(fa, gel(d,2));
1653
if ((w = qfbsolve_primitive_i(Q, rdQ, &Qr, FA, all)))
1654
{
1655
if (i != 1) w = RgV_Rg_mul(w, gel(d,1));
1656
if (!all) return w;
1657
gel(W,j++) = w;
1658
}
1659
}
1660
if (j == 1) return cgetg(1, t_VEC);
1661
setlg(W,j); return shallowconcat1(W);
1662
}
1663
1664
GEN
1665
qfbsolve(GEN Q, GEN n, long fl)
1666
{
1667
pari_sp av = avma;
1668
if (typ(Q) != t_QFB) pari_err_TYPE("qfbsolve",Q);
1669
if (fl < 0 || fl > 3) pari_err_FLAG("qfbsolve");
1670
return gerepilecopy(av, (fl & 2)? qfbsolve_all(Q, n, fl & 1)
1671
: qfbsolve_primitive(Q, n, fl & 1));
1672
}
1673
1674
/* 1 if there exists x,y such that x^2 + dy^2 = p [prime], 0 otherwise */
1675
long
1676
cornacchia(GEN d, GEN p, GEN *px, GEN *py)
1677
{
1678
pari_sp av = avma;
1679
GEN a, b, c, r;
1680
1681
if (typ(d) != t_INT) pari_err_TYPE("cornacchia", d);
1682
if (typ(p) != t_INT) pari_err_TYPE("cornacchia", p);
1683
if (signe(d) <= 0) pari_err_DOMAIN("cornacchia", "d","<=",gen_0,d);
1684
*px = *py = gen_0;
1685
b = subii(p, d);
1686
if (signe(b) < 0) return 0;
1687
if (signe(b) == 0) { *py = gen_1; return gc_long(av,1); }
1688
b = Fp_sqrt(b, p); /* sqrt(-d) */
1689
if (!b) return gc_long(av,0);
1690
b = gmael(halfgcdii(p, b), 2, 2);
1691
a = subii(p, sqri(b));
1692
c = dvmdii(a, d, &r);
1693
if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);
1694
set_avma(av);
1695
*px = icopy(b);
1696
*py = icopy(c); return 1;
1697
}
1698
1699
static GEN
1700
lastqi(GEN Q)
1701
{
1702
GEN s = gcoeff(Q,1,1), q = gcoeff(Q,1,2), p = absi_shallow(gcoeff(Q,2,2));
1703
if (signe(q)==0) return gen_0;
1704
if (signe(s)==0) return p;
1705
if (is_pm1(q)) return subiu(p,1);
1706
return divii(p, absi_shallow(q));
1707
}
1708
1709
static long
1710
cornacchia2_helper(long av, GEN d, GEN p, GEN b, GEN px4, GEN *px, GEN *py)
1711
{
1712
GEN M, Q, V, a, c, r;
1713
if (!signe(b)) { /* d = p,2p,3p,4p */
1714
set_avma(av);
1715
if (absequalii(d, px4)){ *py = gen_1; return 1; }
1716
if (absequalii(d, p)) { *py = gen_2; return 1; }
1717
return 0;
1718
}
1719
if (mod2(b) != mod2(d)) b = subii(p,b);
1720
M = halfgcdii(shifti(p,1), b); Q = gel(M,1); V = gel(M, 2);
1721
b = addii(mulii(gel(V,1), lastqi(Q)),gel(V,2));
1722
if (cmpii(sqri(b),px4) > 0) b = gel(V,1);
1723
if (cmpii(sqri(b),px4) > 0) b = gel(V,2);
1724
a = subii(px4, sqri(b));
1725
c = dvmdii(a, d, &r);
1726
if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);
1727
set_avma(av);
1728
*px = icopy(b);
1729
*py = icopy(c); return 1;
1730
}
1731
1732
/* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */
1733
long
1734
cornacchia2(GEN d, GEN p, GEN *px, GEN *py)
1735
{
1736
pari_sp av = avma;
1737
GEN b, px4;
1738
long k;
1739
1740
if (typ(d) != t_INT) pari_err_TYPE("cornacchia2", d);
1741
if (typ(p) != t_INT) pari_err_TYPE("cornacchia2", p);
1742
if (signe(d) <= 0) pari_err_DOMAIN("cornacchia2", "d","<=",gen_0,d);
1743
*px = *py = gen_0;
1744
k = mod4(d);
1745
if (k == 1 || k == 2) pari_err_DOMAIN("cornacchia2","-d mod 4", ">",gen_1,d);
1746
px4 = shifti(p,2);
1747
if (abscmpii(px4, d) < 0) return gc_long(av,0);
1748
if (absequaliu(p, 2))
1749
{
1750
set_avma(av);
1751
switch (itou_or_0(d)) {
1752
case 4: *px = gen_2; break;
1753
case 7: *px = gen_1; break;
1754
default: return 0;
1755
}
1756
*py = gen_1; return 1;
1757
}
1758
b = Fp_sqrt(negi(d), p);
1759
if (!b) return gc_long(av,0);
1760
return cornacchia2_helper(av, d, p, b, px4, px, py);
1761
}
1762
1763
/* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */
1764
long
1765
cornacchia2_sqrt(GEN d, GEN p, GEN b, GEN *px, GEN *py)
1766
{
1767
pari_sp av = avma;
1768
GEN px4;
1769
*px = *py = gen_0;
1770
px4 = shifti(p,2);
1771
if (abscmpii(px4, d) < 0) return gc_long(av,0);
1772
return cornacchia2_helper(av, d, p, b, px4, px, py);
1773
}
1774
1775