1 /* Copyright (C) 2014 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
15 #include "pari.h"
16 #include "paripriv.h"
17
18 #define dbg_mode() if (DEBUGLEVEL >= 2)
19 #define dbg_mode1() if (DEBUGLEVEL >= 3)
20
21 #define ANSI_RED "\x1b[31m"
22 #define ANSI_GREEN "\x1b[32m"
23 #define ANSI_YELLOW "\x1b[33m"
24 #define ANSI_BLUE "\x1b[34m"
25 #define ANSI_MAGENTA "\x1b[35m"
26 #define ANSI_CYAN "\x1b[36m"
27 #define ANSI_WHITE "\x1b[37m"
28
29 #define ANSI_BRIGHT_RED "\x1b[31;1m"
30 #define ANSI_BRIGHT_GREEN "\x1b[32;1m"
31 #define ANSI_BRIGHT_YELLOW "\x1b[33;1m"
32 #define ANSI_BRIGHT_BLUE "\x1b[34;1m"
33 #define ANSI_BRIGHT_MAGENTA "\x1b[35;1m"
34 #define ANSI_BRIGHT_CYAN "\x1b[36;1m"
35 #define ANSI_BRIGHT_WHITE "\x1b[37;1m"
36
37 #define ANSI_RESET "\x1b[0m"
38
39 /* THINGS THAT DON'T BELONG */
40
41 /* Assume that x is a vector such that
42 f(x) = 1 for x <= k
43 f(x) = 0 otherwise.
44 Return k.
45 */
46 static long
zv_binsearch0(void * E,long (* f)(void * E,GEN x),GEN x)47 zv_binsearch0(void *E, long (*f)(void* E, GEN x), GEN x)
48 {
49 long lo = 1;
50 long hi = lg(x)-1;
51 while (lo < hi) {
52 long mi = lo + (hi - lo)/2 + 1;
53 if (f(E,gel(x,mi))) lo = mi;
54 else hi = mi - 1;
55 }
56 if (f(E,gel(x,lo))) return lo;
57 return 0;
58 }
59
60 INLINE long
time_record(GEN * X0,const char * Xx,long t)61 time_record(GEN* X0, const char* Xx, long t)
62 {
63 long i = Xx[0]-'A'+1, j = Xx[1]-'1'+1;
64 umael3(*X0, 1, i, j) += t;
65 umael3(*X0, 2, i, j) ++; return t;
66 }
67
68 INLINE long
timer_record(GEN * X0,const char * Xx,pari_timer * ti)69 timer_record(GEN* X0, const char* Xx, pari_timer* ti)
70 { return time_record(X0, Xx, timer_delay(ti)); }
71
72 INLINE long
FpJ_is_inf(GEN P)73 FpJ_is_inf(GEN P) { return signe(gel(P, 3)) == 0; }
74
75 /*****************************************************************/
76
77 /* D < 0 fundamental: return the number of units in Q(sqrt(D)) */
78 INLINE long
D_get_wD(long D)79 D_get_wD(long D)
80 {
81 if (D == -4) return 4;
82 if (D == -3) return 6;
83 return 2;
84 }
85
86 /* Dinfo contains information related to the discirminant
87 * D: the discriminant (D < 0)
88 * h: the class number associated to D
89 * bi: the "best invariant" for computing polclass(D, bi)
90 * pd: the degree of polclass; equal to h except when bi is a double
91 * eta-quotient w_p,q with p|D and q|D, where pd = h/2.
92 * Dfac: the prime factorization of D; we have D = q0 q1* q2* ... qn*
93 * where q0 = 1, -4, 8, -8, qi* = 1 mod 4 and |qi| is a prime.
94 * The factorization is a vecsmall listing the indices of the qi* as
95 * they appear in the primelist (q0 = 1 is omitted) */
96 INLINE long
Dinfo_get_D(GEN Dinfo)97 Dinfo_get_D(GEN Dinfo) { return gel(Dinfo, 1)[1]; }
98 INLINE long
Dinfo_get_h(GEN Dinfo)99 Dinfo_get_h(GEN Dinfo) { return gel(Dinfo, 1)[2]; }
100 INLINE long
Dinfo_get_bi(GEN Dinfo)101 Dinfo_get_bi(GEN Dinfo) { return gel(Dinfo, 1)[3]; }
102 INLINE ulong
Dinfo_get_pd(GEN Dinfo)103 Dinfo_get_pd(GEN Dinfo) { return umael(Dinfo, 1, 4); }
104 INLINE GEN
Dinfo_get_Dfac(GEN Dinfo)105 Dinfo_get_Dfac(GEN Dinfo) { return gel(Dinfo, 2); }
106
107 /* primelist and indexlist
108 *
109 * primelist begins with 8, -4, -8; subsequent elements are the p^* for
110 * successive odd primes <= maxsqrt, by increasing absolute value
111 *
112 * i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
113 * ---------+----+----+----+----+----+----+----+----+----+-----+-----+
114 * i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
115 * Plist[i] | 8 | -4 | -8 | -3 | 5 | -7 |-11 | 13 | 17 | -19 | -23 |
116 * p | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 |
117 * ---------+----+----+----+----+----+----+----+----+----+-----+-----+*/
118
119 /* primelist containing primes whose absolute value is at most maxsqrt */
120 static GEN
ecpp_primelist_init(long maxsqrt)121 ecpp_primelist_init(long maxsqrt)
122 {
123 GEN P = cgetg(maxsqrt, t_VECSMALL);
124 forprime_t T;
125 long p, j = 4;
126 u_forprime_init(&T, 3, maxsqrt);
127 P[1] = 8; P[2] = -4; P[3] = -8;
128 while((p = u_forprime_next(&T))) P[j++] = ((p & 3UL) == 1)? p: -p;
129 setlg(P,j); return P;
130 }
131
132 static GEN
Dfac_to_p(GEN x,GEN P)133 Dfac_to_p(GEN x, GEN P) { pari_APPLY_long(uel(P,x[i])); }
134 static GEN
Dfac_to_roots(GEN x,GEN P)135 Dfac_to_roots(GEN x, GEN P) { pari_APPLY_type(t_VEC, gel(P,x[i])); }
136
137 #if 0
138 INLINE ulong
139 ecpp_param_get_maxsqrt(GEN param) { return umael3(param, 1, 1, 1); }
140 INLINE ulong
141 ecpp_param_get_maxdisc(GEN param) { return umael3(param, 1, 1, 2); }
142 #endif
143 INLINE ulong
ecpp_param_get_maxpcdg(GEN param)144 ecpp_param_get_maxpcdg(GEN param) { return umael3(param, 1, 1, 3); }
145 INLINE GEN
ecpp_param_get_primelist(GEN param)146 ecpp_param_get_primelist(GEN param) { return gmael(param, 1, 2); }
147 INLINE GEN
ecpp_param_get_disclist(GEN param)148 ecpp_param_get_disclist(GEN param) { return gmael(param, 1, 3); }
149 INLINE GEN
ecpp_param_get_primeord(GEN param)150 ecpp_param_get_primeord(GEN param) { return gmael(param, 1, 4); }
151 INLINE GEN
ecpp_param_get_primorial_vec(GEN param)152 ecpp_param_get_primorial_vec(GEN param) { return gel(param, 2); }
153 INLINE GEN
ecpp_param_get_tune(GEN param)154 ecpp_param_get_tune(GEN param) { return gel(param, 3); }
155
156 /* Input: x, 20 <= x <= 35
157 * Output: a vector whose ith entry is the product of all primes below 2^x */
158 static GEN
primorial_vec(ulong x)159 primorial_vec(ulong x)
160 {
161 pari_sp av = avma;
162 long i, y = x-19;
163 GEN v = primes_upto_zv(1UL << x), w = cgetg(y+1, t_VEC);
164 /* ind[i]th prime number is the largest prime <= 2^(20+i) */
165 long ind[] = { 0, 82025L, 155611L, 295947L, 564163L, 1077871L, 2063689L,
166 3957809L, 7603553L, 14630843L, 28192750L, 54400028L,
167 105097565L, 203280221L, 393615806L, 762939111L, 1480206279L};
168 gel(w,1) = zv_prod_Z(vecslice(v,1,ind[1]));
169 for (i = 2; i <= y; i++)
170 {
171 pari_sp av2 = avma;
172 GEN q = mulii(gel(w,i-1), zv_prod_Z(vecslice(v,ind[i-1]+1,ind[i])));
173 gel(w,i) = gerepileuptoint(av2, q);
174 }
175 return gerepileupto(av, w);
176 }
177
178 /* NDmqg contains
179 N, as in the theorem in ??ecpp
180 Dinfo, as discussed in the comment about Dinfo
181 m, as in the theorem in ??ecpp
182 q, as in the theorem in ??ecpp
183 g, a quadratic nonresidue modulo N
184 sqrt, a list of squareroots
185 */
186 INLINE GEN
NDmqg_get_N(GEN x)187 NDmqg_get_N(GEN x) { return gel(x,1); }
188 INLINE GEN
NDmqg_get_Dinfo(GEN x)189 NDmqg_get_Dinfo(GEN x) { return gel(x,2); }
190 INLINE GEN
NDmqg_get_m(GEN x)191 NDmqg_get_m(GEN x) { return gel(x,3); }
192 INLINE GEN
NDmqg_get_q(GEN x)193 NDmqg_get_q(GEN x) { return gel(x,4); }
194 INLINE GEN
NDmqg_get_g(GEN x)195 NDmqg_get_g(GEN x) { return gel(x,5); }
196 INLINE GEN
NDmqg_get_sqrt(GEN x)197 NDmqg_get_sqrt(GEN x) { return gel(x,6); }
198
199 /* COMPARATOR FUNCTIONS */
200
201 static int
sort_disclist(void * data,GEN x,GEN y)202 sort_disclist(void *data, GEN x, GEN y)
203 {
204 long d1, h1, g1, o1, pd1, hf1, wD1, d2, h2, g2, o2, pd2, hf2, wD2;
205 (void)data;
206 d1 = Dinfo_get_D(x); wD1 = D_get_wD(d1);
207 d2 = Dinfo_get_D(y); wD2 = D_get_wD(d2);
208 /* higher number of units means more elliptic curves to try */
209 if (wD1 != wD2) return wD2 > wD1 ? 1 : -1;
210 /* lower polclass degree is better: faster computation of roots modulo N */
211 pd1 = Dinfo_get_pd(x); /* degree of polclass */
212 pd2 = Dinfo_get_pd(y);
213 if (pd1 != pd2) return pd1 > pd2 ? 1 : -1;
214 g1 = lg(Dinfo_get_Dfac(x))-1; /* genus number */
215 h1 = Dinfo_get_h(x); /* class number */
216 o1 = h1 >> (g1-1); /* odd class number */
217 g2 = lg(Dinfo_get_Dfac(y))-1;
218 h2 = Dinfo_get_h(y);
219 o2 = h2 >> (g2-1);
220 if (o1 != o2) return g1 > g2 ? 1 : -1;
221 /* lower class number is better: higher probability of succesful cornacchia */
222 if (h1 != h2) return h1 > h2 ? 1 : -1;
223 /* higher height factor is better: polclass would have lower coefficients */
224 hf1 = modinv_height_factor(Dinfo_get_bi(x)); /* height factor for best inv. */
225 hf2 = modinv_height_factor(Dinfo_get_bi(y));
226 if (hf1 != hf2) return hf2 > hf1 ? 1 : -1;
227 /* "higher" discriminant is better since its absolute value is lower */
228 if (d1 != d2) return d2 > d1 ? 1 : -1;
229 return 0;
230 }
231
232 INLINE long
NDmqg_get_D(GEN x)233 NDmqg_get_D(GEN x) { return Dinfo_get_D(NDmqg_get_Dinfo(x)); }
234 static int
sort_NDmq_by_D(void * data,GEN x,GEN y)235 sort_NDmq_by_D(void *data, GEN x, GEN y)
236 {
237 long d1 = NDmqg_get_D(x);
238 long d2 = NDmqg_get_D(y);
239 (void)data; return d2 > d1 ? 1 : -1;
240 }
241
242 static int
sort_Dmq_by_q(void * data,GEN x,GEN y)243 sort_Dmq_by_q(void *data, GEN x, GEN y)
244 { (void)data; return cmpii(gel(x,3), gel(y,3)); }
245
246 INLINE long
Dmq_get_D(GEN Dmq)247 Dmq_get_D(GEN Dmq) { return Dinfo_get_D(gel(Dmq,1)); }
248 INLINE long
Dmq_get_h(GEN Dmq)249 Dmq_get_h(GEN Dmq) { return Dinfo_get_h(gel(Dmq,1)); }
250 static int
sort_Dmq_by_cnum(void * data,GEN x,GEN y)251 sort_Dmq_by_cnum(void *data, GEN x, GEN y)
252 {
253 ulong h1 = Dmq_get_h(x);
254 ulong h2 = Dmq_get_h(y);
255 if (h1 != h2) return h1 > h2 ? 1 : -1;
256 return sort_Dmq_by_q(data, x, y);
257 }
258
259 /* return H s.t if -maxD <= D < 0 is fundamental then H[(-D)>>1] is the
260 * ordinary class number of Q(sqrt(D)); junk at other entries. */
261 static GEN
allh(ulong maxD)262 allh(ulong maxD)
263 {
264 ulong a, A = usqrt(maxD/3), maxD2 = maxD >> 1;
265 GEN H = zero_zv(maxD2);
266 for (a = 1; a <= A; a++)
267 {
268 ulong a2 = a << 1, aa = a*a, aa4 = aa << 2, b, c;
269 { /* b = 0 */
270 ulong D = aa << 1;
271 for (c = a; D <= maxD2; c++) { H[D]++; D += a2; }
272 }
273 for (b = 1; b < a; b++)
274 {
275 ulong B = b*b, D = (aa4 - B) >> 1;
276 if (D > maxD2) continue;
277 H[D]++; D += a2; /* c = a */
278 for (c = a+1; D <= maxD2; c++) { H[D] += 2; D += a2; }
279 }
280 { /* b = a */
281 ulong D = (aa4 - aa) >> 1;
282 for (c = a; D <= maxD2; c++) { H[D]++; D += a2; }
283 }
284 }
285 return H;
286 }
287
288 static GEN
mkDinfo(GEN c,long D,long h)289 mkDinfo(GEN c, long D, long h)
290 {
291 long bi, pd, p1, p2;
292 bi = disc_best_modinv(D);
293 pd = (modinv_degree(&p1,&p2,bi) && (-D)%p1==0 && (-D)%p2==0)? h/2: h;
294 gel(c,1) = mkvecsmall4(D, h, bi, pd); return c;
295 }
296
297 static GEN
ecpp_disclist_init(ulong maxdisc,GEN primelist)298 ecpp_disclist_init(ulong maxdisc, GEN primelist)
299 {
300 pari_sp av = avma;
301 long t, ip, u, lp, lmerge;
302 GEN merge, ev, od, Harr = allh(maxdisc); /* table of class numbers*/
303 long lenv = maxdisc/4; /* max length of od/ev */
304 long N; /* maximum number of positive prime factors */
305
306 /* tuning paramaters blatantly copied from vecfactoru */
307 if (maxdisc < 510510UL) N = 7;
308 else if (maxdisc < 9699690UL) N = 8;
309 else if (maxdisc < 223092870UL) N = 9;
310 #ifdef LONG_IS_64BIT
311 else if (maxdisc < 6469693230UL) N = 10;
312 else if (maxdisc < 200560490130UL) N = 11;
313 else if (maxdisc < 7420738134810UL) N = 12;
314 else if (maxdisc < 304250263527210UL) N = 13;
315 else N = 16; /* don't bother */
316 #else
317 else N = 10;
318 #endif
319
320 /* od[t] attached to discriminant 1-4*t, ev[t] attached to -4*t */
321 od = cgetg(lenv + 1, t_VEC); /* contains 'long' at first: save memory */
322 ev = cgetg(lenv + 1, t_VEC);
323 /* first pass: squarefree sieve and restrict to maxsqrt-smooth disc */
324 for (t = 1; t <= lenv; t++)
325 {
326 od[t] = 1;
327 switch(t&7)
328 {
329 case 0: case 4: ev[t] = 0; break;
330 case 2: ev[t] =-8; break;
331 case 6: ev[t] = 8; break;
332 default:ev[t] =-4; break;
333 }
334 }
335 lp = lg(primelist);
336 for (ip = 4; ip < lp; ip++) /* skip 8,-4,-8 */
337 { /* sieve by squares of primes */
338 long s, q = primelist[ip], p = labs(q);
339 s = (q == p)? 3: 1;
340 for (t = (s*p+1)>>2; t <= lenv; t += p, s += 4)
341 {
342 long c = od[t];
343 if (c) { if (s%p == 0) od[t] = 0; else od[t] = c*q; }
344 }
345 s = 1;
346 for (t = p; t <= lenv; t += p, s++)
347 {
348 long c = ev[t];
349 if (c) { if (s%p == 0) ev[t] = 0; else ev[t] = c*q; }
350 }
351 }
352 for (u = 0, t = 1; t <= lenv; t++)
353 { /* restrict to maxsqrt-smooth disc */
354 if (od[t] != -4*t+1) od[t] = 0; else u++;
355 if (ev[t] != -4*t) ev[t] = 0; else u++;
356 }
357
358 /* second pass: sieve by primes and record factorization */
359 for (t = 1; t <= lenv; t++)
360 {
361 if (od[t]) gel(od,t) = mkvec2(NULL, vecsmalltrunc_init(N));
362 if (ev[t])
363 {
364 GEN F = vecsmalltrunc_init(N);
365 long id;
366 switch(t&7)
367 {
368 case 2: id = 3; break;
369 case 6: id = 1; break;
370 default:id = 2; break;
371 }
372 vecsmalltrunc_append(F, id);
373 gel(ev,t) = mkvec2(NULL, F);
374 }
375 }
376 lp = lg(primelist);
377 for (ip = 4; ip < lp; ip++) /* skip 8,-4,-8 */
378 {
379 long s, q = primelist[ip], p = labs(q);
380 s = (q == p)? 3: 1;
381 for (t = (s*p+1)>>2; t <= lenv; t += p, s += 4)
382 {
383 GEN c = gel(od,t);
384 if (c) vecsmalltrunc_append(gel(c,2), ip);
385 }
386 s = 1;
387 for (t = p; t <= lenv; t += p, s++)
388 {
389 GEN c = gel(ev,t);
390 if (c) vecsmalltrunc_append(gel(c,2), ip);
391 }
392 }
393 /* merging the two arrays */
394 merge = cgetg(u+1, t_VEC); lmerge = 0;
395 for (t = 1; t <= lenv; t++)
396 {
397 GEN c;
398 c = gel(od,t); if (c) gel(merge, ++lmerge) = mkDinfo(c,1-4*t, Harr[2*t-1]);
399 c = gel(ev,t); if (c) gel(merge, ++lmerge) = mkDinfo(c, -4*t, Harr[2*t]);
400 }
401 setlg(merge, lmerge);
402 gen_sort_inplace(merge, NULL, &sort_disclist, NULL);
403 return gerepilecopy(av, merge);
404 }
405
406 static GEN
ecpp_primeord_init(GEN primelist,GEN disclist)407 ecpp_primeord_init(GEN primelist, GEN disclist)
408 {
409 long i, k=1, lgdisclist = lg(disclist), lprimelist = lg(primelist);
410 GEN primeord = zero_Flv(lprimelist-1);
411 for (i=1;i < lgdisclist; i++)
412 {
413 GEN Dinfo = gel(disclist, i), Dfac = Dinfo_get_Dfac(Dinfo);
414 long j, l = lg(Dfac);
415 for (j = 1; j < l; j++)
416 {
417 long ip = Dfac[j];
418 if (primeord[ip]==0)
419 primeord[ip]=k++;
420 }
421 }
422 return perm_inv(primeord);
423 }
424
425 /* Input: a vector tune whose components are [maxsqrt,maxpcdg,tdivexp,expiN]
426 * Output: vector param of precomputations
427 * let x = be a component of tune then
428 * param[1][1] = [maxsqrt, maxdisc, maxpcdg]
429 * param[1][2] = primelist = Vecsmall of primes
430 * param[1][3] = disclist = vector of Dinfos, sorted by quality
431 * param[2] = primorial_vec
432 * param[3] = tune */
433 static GEN
ecpp_param_set(GEN tune,GEN x)434 ecpp_param_set(GEN tune, GEN x)
435 {
436 pari_sp av = avma;
437 ulong maxsqrt = uel(x,1), maxpcdg = uel(x,2), tdivexp = uel(x,3);
438 ulong maxdisc = maxsqrt * maxsqrt;
439 GEN T = mkvecsmall3(maxsqrt, maxdisc, maxpcdg);
440 GEN Plist = ecpp_primelist_init(maxsqrt);
441 GEN Dlist = ecpp_disclist_init(maxdisc, Plist);
442 GEN Olist = ecpp_primeord_init(Plist, Dlist);
443 GEN primorial = primorial_vec(tdivexp);
444 return gerepilecopy(av, mkvec3(mkvec4(T,Plist,Dlist,Olist), primorial, tune));
445 }
446
447 /* cert contains [N, t, s, a4, [x, y]] as documented in ??ecpp; the following
448 * information can be obtained:
449 * D = squarefreepart(t^2-4N)
450 * m = (N+1-t), q = m/s, a6 = y^3 - x^2 - a4*x, J */
451 INLINE GEN
cert_get_N(GEN x)452 cert_get_N(GEN x) { return gel(x,1); }
453 INLINE GEN
cert_get_t(GEN x)454 cert_get_t(GEN x) { return gel(x,2); }
455 INLINE GEN
cert_get_D(GEN x)456 cert_get_D(GEN x)
457 {
458 GEN N = cert_get_N(x), t = cert_get_t(x);
459 return coredisc(subii(sqri(t), shifti(N, 2)));
460 }
461 INLINE GEN
cert_get_m(GEN x)462 cert_get_m(GEN x)
463 {
464 GEN N = cert_get_N(x), t = cert_get_t(x);
465 return subii(addiu(N, 1), t);
466 }
467 INLINE GEN
cert_get_s(GEN x)468 cert_get_s(GEN x) { return gel(x,3); }
469 INLINE GEN
cert_get_q(GEN x)470 cert_get_q(GEN x) { return diviiexact(cert_get_m(x), cert_get_s(x)); }
471 INLINE GEN
cert_get_a4(GEN x)472 cert_get_a4(GEN x) { return gel(x,4); }
473 INLINE GEN
cert_get_P(GEN x)474 cert_get_P(GEN x) { return gel(x,5); }
475 INLINE GEN
cert_get_x(GEN x)476 cert_get_x(GEN x) { return gel(cert_get_P(x), 1); }
477 INLINE GEN
cert_get_a6(GEN z)478 cert_get_a6(GEN z)
479 {
480 GEN N = cert_get_N(z), a = cert_get_a4(z), P = cert_get_P(z);
481 GEN x = gel(P,1), xx = Fp_sqr(x, N);
482 GEN y = gel(P,2), yy = Fp_sqr(y, N);
483 return Fp_sub(yy, Fp_mul(x, Fp_add(xx,a,N), N), N);
484 }
485 INLINE GEN
cert_get_E(GEN x)486 cert_get_E(GEN x) { return mkvec2(cert_get_a4(x), cert_get_a6(x)); }
487 INLINE GEN
cert_get_J(GEN x)488 cert_get_J(GEN x)
489 {
490 GEN a = cert_get_a4(x), b = cert_get_a6(x), N = cert_get_N(x);
491 return Fp_ellj(a, b, N);
492 }
493
494 /* Given J, N, set A = 3*J*(1728-J) mod N, B = 2*J*(1728-J)^2 mod N */
495 static void
Fp_ellfromj(GEN j,GEN N,GEN * A,GEN * B)496 Fp_ellfromj(GEN j, GEN N, GEN *A, GEN *B)
497 {
498 GEN k, jk;
499 j = Fp_red(j, N);
500 if (isintzero(j)) { *A = gen_0; *B = gen_1; return; }
501 if (absequalui(umodui(1728,N), j)) { *A = gen_1; *B = gen_0; return; }
502 k = Fp_sub(utoi(1728), j, N);
503 jk = Fp_mul(j, k, N);
504 *A = Fp_mulu(jk, 3, N);
505 *B = Fp_mulu(Fp_mul(j, Fp_sqr(k,N), N), 2, N);
506 }
507
508 /* "Twist factor". Does not cover J = 0, 1728 */
509 static GEN
cert_get_lambda(GEN z)510 cert_get_lambda(GEN z)
511 {
512 GEN N, J, a, b, A, B;
513 J = cert_get_J(z);
514 N = cert_get_N(z);
515 a = cert_get_a4(z);
516 b = cert_get_a6(z);
517 Fp_ellfromj(J, N, &A, &B);
518 return Fp_div(Fp_mul(a,B,N), Fp_mul(b,A,N), N);
519 }
520
521 /* Solves for T such that if
522 [A, B] = [3*J*(1728-J), 2*J*(1728-J)^2]
523 and you let
524 L = T^3 + A*T + B, A = A*L^2, B = B*L^3
525 then
526 x == TL and y == L^2
527 */
528 static GEN
cert_get_T(GEN z)529 cert_get_T(GEN z)
530 {
531 GEN N = cert_get_N(z), x = cert_get_x(z);
532 GEN l = cert_get_lambda(z); /* l = 1/L */
533 return Fp_mul(x, l, N);
534 }
535
536 /* database for all invariants, stolen from Hamish's code */
537 static GEN
polmodular_db_init_allinv(void)538 polmodular_db_init_allinv(void)
539 {
540 const long DEFAULT_MODPOL_DB_LEN = 32;
541 GEN a, b = cgetg(39+1, t_VEC);
542 long i;
543 for (i = 1; i < 40; i++) gel(b,i) = zerovec_block(DEFAULT_MODPOL_DB_LEN);
544 a = zerovec_block(DEFAULT_MODPOL_DB_LEN);
545 return mkvec2(a, b);
546 }
547
548 /* Given D and a database of modular polynomials, return polclass(D, inv) */
549 static GEN
D_polclass(long D,long inv,GEN * db)550 D_polclass(long D, long inv, GEN *db)
551 {
552 GEN HD, t = mkvec2(gel(*db, 1), inv == 0? gen_0: gmael(*db, 2, inv));
553 HD = polclass0(D, inv, 0, &t);
554 gel(*db, 1) = gel(t,1);
555 if (inv != 0) gmael(*db, 2, inv) = gel(t,2);
556 return HD;
557 }
558
559 static GEN
NqE_check(GEN N,GEN q,GEN a,GEN b,GEN s)560 NqE_check(GEN N, GEN q, GEN a, GEN b, GEN s)
561 {
562 GEN mP, sP, P = random_FpE(a, b, N);
563 GEN PJ = mkvec3(gel(P,1), gel(P,2), gen_1);
564 sP = FpJ_mul(PJ, s, a, N); if (FpJ_is_inf(sP)) return NULL;
565 mP = FpJ_mul(sP, q, a, N); return FpJ_is_inf(mP)? mkvec2(a, P): NULL;
566 }
567
568 /* Find an elliptic curve E modulo N and a point P on E which corresponds to
569 * the ith element of the certificate; uses N, D, m, q, J from previous steps.
570 * All computations are modulo N unless stated otherwise */
571
572 /* g is a quadratic and cubic nonresidue modulo N */
573 static GEN
j0_find_g(GEN N)574 j0_find_g(GEN N)
575 {
576 GEN n = diviuexact(subiu(N, 1), 3);
577 for(;;)
578 {
579 GEN g = randomi(N);
580 if (kronecker(g, N) == -1 && !equali1(Fp_pow(g, n, N))) return g;
581 }
582 }
583
584 static GEN
find_EP(GEN N,long D,GEN q,GEN g,GEN J,GEN s)585 find_EP(GEN N, long D, GEN q, GEN g, GEN J, GEN s)
586 {
587 GEN A0, B0; Fp_ellfromj(J, N, &A0, &B0);
588 for(;;)
589 { /* expect one iteration: not worth saving the A's and B's */
590 GEN gg, v, A = A0, B = B0;
591 long i;
592 if ((v = NqE_check(N, q, A, B, s))) return v;
593 switch (D_get_wD(D))
594 {
595 case 2:
596 gg = Fp_sqr(g, N);
597 A = Fp_mul(A, gg, N);
598 B = Fp_mul(Fp_mul(B, gg, N), g, N);
599 if ((v = NqE_check(N, q, A, B, s))) return v;
600 break;
601 case 4:
602 for (i = 1; i < 4; i++)
603 {
604 A = Fp_mul(A, g, N);
605 if ((v = NqE_check(N, q, A, B, s))) return v;
606 }
607 break;
608 default: /* 6 */
609 B = Fp_mul(B, g, N);
610 if ((v = NqE_check(N, q, A, B, s))) return v;
611 g = j0_find_g(N);
612 for (i = 1; i < 6; i++)
613 {
614 B = Fp_mul(B, g, N);
615 if (i == 3) continue;
616 if ((v = NqE_check(N, q, A, B, s))) return v;
617 }
618 break;
619 }
620 }
621 }
622
623 /* Convert the disc. factorisation of a genus field to the
624 * disc. factorisation of its real part. */
625 static GEN
realgenusfield(GEN Dfac,GEN sq,GEN p)626 realgenusfield(GEN Dfac, GEN sq, GEN p)
627 {
628 long i, j, l = lg(Dfac), dn, n = 0;
629 GEN sn, s = gen_0, R = cgetg(l-1, t_VECSMALL);
630 for (i = 1; i < l; i++)
631 if (Dfac[i] < 0) { n = i; break; }
632 if (n == 0) pari_err_BUG("realgenusfield");
633 dn = Dfac[n]; sn = gel(sq, n);
634 for (j = i = 1; i < l; i++)
635 if (i != n)
636 {
637 long d = Dfac[i];
638 GEN si = gel(sq,i);
639 R[j++] = d > 0 ? d : d * dn;
640 s = Fp_add(s, d > 0? si: Fp_mul(si, sn, p), p);
641 }
642 return mkvec2(R, s);
643 }
644
645 static GEN
FpX_classtower_oneroot(GEN P,GEN Dfac,GEN sq,GEN p)646 FpX_classtower_oneroot(GEN P, GEN Dfac, GEN sq, GEN p)
647 {
648 pari_sp av = avma;
649 GEN C;
650 if (degpol(P) > 1)
651 {
652 GEN N = NULL, V = realgenusfield(Dfac, sq, p), v = gel(V,1), R = gel(V,2);
653 long i, l = lg(v);
654 for (i = 1; i < l; i++)
655 {
656 GEN Q = deg2pol_shallow(gen_1, gen_0, stoi(-v[i]), 1);
657 if (!N) N = Q;
658 else
659 {
660 GEN cm = polcompositum0(N,Q,3);
661 N = gel(cm,1); P = gsubst(P, 1, gel(cm,2));
662 }
663 P = liftpol_shallow(gmael(nffactor(N,P),1,1));
664 }
665 if (N)
666 P = FpXY_evalx(Q_primpart(P), R, p);
667 }
668 C = FpX_oneroot_split(P, p);
669 return gerepileupto(av, C);
670 }
671
672 GEN
ecpp_step2_worker(GEN S,GEN HD,GEN primelist)673 ecpp_step2_worker(GEN S, GEN HD, GEN primelist)
674 {
675 pari_sp av = avma;
676 pari_timer ti;
677 GEN J, t, s, EP, rt, res;
678 GEN N = NDmqg_get_N(S), Dinfo = NDmqg_get_Dinfo(S);
679 GEN m = NDmqg_get_m(S), q = NDmqg_get_q(S);
680 GEN g = NDmqg_get_g(S), sq = NDmqg_get_sqrt(S);
681 long D = Dinfo_get_D(Dinfo), inv = Dinfo_get_bi(Dinfo);
682 GEN Dfacp = Dfac_to_p(Dinfo_get_Dfac(Dinfo), primelist);
683 long C2 = 0, C3 = 0, D1 = 0;
684 GEN c = getrand();
685 setrand(gen_1); /* for reproducibility */
686 /* C2: Find a root modulo N of polclass(D,inv) */
687 dbg_mode() timer_start(&ti);
688 rt = FpX_classtower_oneroot(HD, Dfacp, sq, N);
689 dbg_mode() C2 = timer_delay(&ti);
690 /* C3: Convert root from previous step into the appropriate j-invariant */
691 J = Fp_modinv_to_j(rt, inv, N); /* root of polclass(D) */
692 dbg_mode() C3 = timer_delay(&ti);
693 /* D1: Find an elliptic curve E with a point P satisfying the theorem */
694 s = diviiexact(m, q);
695 EP = find_EP(N, D, q, g, J, s);
696 dbg_mode() D1 = timer_delay(&ti);
697
698 /* D2: Compute for t and s */
699 t = subii(addiu(N, 1), m); /* t = N+1-m */
700 res = mkvec2(mkvec5(N, t, s, gel(EP,1), gel(EP,2)),mkvecsmall3(C2,C3,D1));
701 setrand(c);
702 return gerepilecopy(av, res);
703 }
704
705 /* This uses [N, D, m, q] from step 1 to find the appropriate j-invariants
706 * to be used in step 3. Step 2 is divided into substeps 2a, 2b, 2c */
707 static GEN
ecpp_step2(GEN step1,GEN * X0,GEN primelist)708 ecpp_step2(GEN step1, GEN *X0, GEN primelist)
709 {
710 long j, Dprev = 0;
711 pari_timer ti;
712 GEN perm = gen_indexsort(step1, NULL, &sort_NDmq_by_D);
713 GEN step2 = cgetg(lg(step1), t_VEC);
714 GEN HD = NULL, db = polmodular_db_init_allinv();
715 long ls = lg(step2), pending = 0;
716 GEN worker;
717 struct pari_mt pt;
718 GEN Hi = cgetg(ls, t_VEC);
719 for (j = 1; j < ls; j++)
720 {
721 long i = uel(perm, j);
722 GEN S = gel(step1, i);
723 GEN Dinfo = NDmqg_get_Dinfo(S);
724 long D = Dinfo_get_D(Dinfo), inv = Dinfo_get_bi(Dinfo);
725 /* C1: Find the appropriate class polynomial modulo N */
726 dbg_mode() timer_start(&ti);
727 if (D != Dprev) HD = D_polclass(D, inv, &db);
728 dbg_mode() {
729 GEN N = NDmqg_get_N(S);
730 long tt = timer_record(X0, "C1", &ti);
731 err_printf(ANSI_BRIGHT_GREEN "\n[ %3d | %4ld bits]" ANSI_RESET, i, expi(N));
732 err_printf(ANSI_GREEN " D = %8ld poldeg = %4ld" ANSI_RESET, D, degpol(HD));
733 if (D == Dprev) err_printf(" %6ld", tt);
734 else err_printf(ANSI_BRIGHT_WHITE " %6ld" ANSI_RESET, tt);
735 }
736 gel(Hi, i) = HD;
737 }
738 gunclone_deep(db);
739 worker = snm_closure(is_entry("_ecpp_step2_worker"),mkvec(primelist));
740 mt_queue_start_lim(&pt, worker, ls-1);
741 for (j = 1; j < ls || pending; j++)
742 {
743 long jj;
744 GEN S = gel(step1, j), HD = gel(Hi, j), done;
745 mt_queue_submit(&pt, j, j < ls ? mkvec2(S, HD) : NULL);
746 done = mt_queue_get(&pt, &jj, &pending);
747 if (!done) continue;
748 dbg_mode() {
749 GEN T = gel(done,2);
750 GEN N = NDmqg_get_N(gel(step1, jj));
751 err_printf(ANSI_BRIGHT_GREEN "\n[ %3d | %4ld bits]" ANSI_RESET, jj, expi(N));
752 err_printf(" %6ld", time_record(X0, "C2", T[1]));
753 err_printf(" %6ld", time_record(X0, "C3", T[2]));
754 err_printf(" %6ld", time_record(X0, "D1", T[3]));
755 }
756 gel(step2, jj) = gel(done,1);
757 }
758 mt_queue_end(&pt);
759 return step2;
760 }
761 /* end of functions for step 2 */
762
763 GEN
ecpp_sqrt_worker(GEN p,GEN g,GEN N)764 ecpp_sqrt_worker(GEN p, GEN g, GEN N)
765 {
766 GEN r = Fp_sqrt_i(p, g, N);
767 return r ? r: gen_0;
768 }
769
770 static void
ecpp_parsqrt(GEN N,GEN param,GEN kroP,GEN sqrtlist,GEN g,long nb,long * nbsqrt)771 ecpp_parsqrt(GEN N, GEN param, GEN kroP, GEN sqrtlist, GEN g, long nb, long *nbsqrt)
772 {
773 GEN primelist = ecpp_param_get_primelist(param);
774 GEN ordinv = ecpp_param_get_primeord(param);
775 long i, k, l = lg(ordinv), n = *nbsqrt+1;
776 GEN worker = snm_closure(is_entry("_ecpp_sqrt_worker"), mkvec2(g, N));
777 GEN W = cgetg(nb+1,t_VEC), V;
778 for (i=n, k=1; k<=nb && i<l; i++)
779 {
780 long pi = ordinv[i];
781 long s = kroP[pi];
782 if (s > 1) kroP[pi] = s = krosi(primelist[pi], N); /* update cache */
783 if (kroP[pi] > 0)
784 gel(W,k++) = stoi(primelist[pi]);
785 }
786 *nbsqrt=i-1;
787 setlg(W,k);
788 V = gen_parapply(worker, W);
789 for (i=n, k=1; k<=nb && i<l; i++)
790 {
791 long pi = ordinv[i];
792 if (kroP[pi] > 0)
793 {
794 dbg_mode() err_printf(ANSI_MAGENTA "S" ANSI_RESET);
795 if (!signe(gel(V,k)))
796 pari_err_BUG("D_find_discsqrt"); /* possible if N composite */
797 gel(sqrtlist,pi) = gel(V,k++);
798 }
799 }
800 }
801
802 /* start of functions for step 1 */
803
804 /* This finds the square root of D modulo N [given by Dfac]: find the square
805 * root modulo N of each prime p dividing D and multiply them out */
806 static GEN
D_find_discsqrt(GEN N,GEN param,GEN Dfac,GEN kroP,GEN sqrtlist,GEN g,long * nbsqrt)807 D_find_discsqrt(GEN N, GEN param, GEN Dfac, GEN kroP, GEN sqrtlist, GEN g, long *nbsqrt)
808 {
809 GEN s = NULL, sj;
810 long i, l = lg(Dfac);
811 for (i = 1; i < l; i++)
812 {
813 long j = Dfac[i];
814 while(!signe(gel(sqrtlist,j)))
815 ecpp_parsqrt(N, param, kroP, sqrtlist, g, mt_nbthreads(), nbsqrt);
816 sj = gel(sqrtlist,j);
817 s = s? Fp_mul(s, sj, N): sj;
818 }
819 return s;/* != NULL */
820 }
821
822 /* Given a solution U, V to U^2 + |D|V^2 = 4N, this find all the possible
823 cardinalities of the elliptic curves over the finite field F_N
824 whose endomorphism ring is isomorphic to the maximal order
825 in the imaginary quadratic number field K = Q(sqrt(D)). ???
826 */
827 static GEN
NUV_find_m(GEN Dinfo,GEN N,GEN U,GEN V,long wD)828 NUV_find_m(GEN Dinfo, GEN N, GEN U, GEN V, long wD)
829 {
830 GEN m, Nplus1 = addiu(N, 1), u = U, mvec = cgetg(wD+1, t_VEC);
831 long i;
832 for (i = 0; i < wD; i++)
833 {
834 if (odd(i)) m = subii(Nplus1, u);
835 else
836 {
837 if (wD == 4 && i==2) u = shifti(V, 1);
838 else if (wD == 6 && i==2) u = shifti(submuliu(U, V, 3), -1);
839 else if (wD == 6 && i==4) u = shifti(addmuliu(U, V, 3), -1);
840 m = addii(Nplus1, u);
841 }
842 gel(mvec, i+1) = mkvec2(Dinfo, m);
843 }
844 return mvec;
845 }
846
847 /* Populates Dmbatch with Dmvec's -- whose components are of the form [D,m],
848 where m is a cardinalities of an elliptic curve with endomorphism ring
849 isomorphic to the maximal order of imaginary quadratic K = Q(sqrt(D)).
850 It returns 0 if:
851 * Any of the p* dividing D is not a quadratic residue mod N
852 * Cornacchia cannot find a solution U^2 + |D|V^2 = 4N.
853 Otherwise, it returns the number of cardinalities added to Dmbatch.
854 Finally, sqrtlist and g help compute the square root modulo N of D.
855 */
856 static long
D_collectcards(GEN N,GEN param,GEN * X0,GEN Dinfo,GEN sqrtlist,GEN g,GEN Dmbatch,GEN kroP,long * nbsqrt)857 D_collectcards(GEN N, GEN param, GEN* X0, GEN Dinfo, GEN sqrtlist, GEN g, GEN Dmbatch, GEN kroP, long *nbsqrt)
858 {
859 long i, l, corn_succ, wD, D = Dinfo_get_D(Dinfo);
860 GEN U, V, sqrtofDmodN, Dfac = Dinfo_get_Dfac(Dinfo);
861 GEN primelist = ecpp_param_get_primelist(param);
862 pari_timer ti;
863
864 /* A1: Check (p*|N) = 1 for all primes dividing D */
865 l = lg(Dfac);
866 for (i = 1; i < l; i++)
867 {
868 long j = Dfac[i], s = kroP[j];
869 if (s > 1) kroP[j] = s = krosi(primelist[j], N); /* update cache */
870 if (s == 0) return -1; /* N is composite */
871 if (s < 0) return 0;
872 }
873 /* A3: Get square root of D mod N */
874 dbg_mode() timer_start(&ti);
875 sqrtofDmodN = D_find_discsqrt(N, param, Dfac, kroP, sqrtlist, g, nbsqrt);
876 dbg_mode() timer_record(X0, "A3", &ti);
877 /* A5: Use square root with Cornacchia to solve U^2 + |D|V^2 = 4N */
878 dbg_mode() timer_start(&ti);
879 corn_succ = cornacchia2_sqrt( utoi(labs(D)), N, sqrtofDmodN, &U, &V);
880 dbg_mode() timer_record(X0, "A5", &ti);
881 if (!corn_succ) {
882 dbg_mode1() err_printf(ANSI_YELLOW "c" ANSI_RESET);
883 return 0;
884 }
885 /* A6: Collect the w(D) cardinalities of E/(F_N) with CM by D */
886 dbg_mode() err_printf(ANSI_BRIGHT_YELLOW "D" ANSI_RESET);
887 wD = D_get_wD(D);
888 vectrunc_append_batch(Dmbatch, NUV_find_m(Dinfo,N,U,V,wD));
889 return wD;
890 }
891
892 /* Compute (S(16N, 2) + S(4096N, 4) + 4)\4 + 1, where S is the nth root
893 * rounded down. This is at most one more than (N^1/4 + 1)^2. */
894 static GEN
ecpp_qlo(GEN N)895 ecpp_qlo(GEN N)
896 {
897 GEN a = sqrtnint(shifti(N, 4), 2);
898 GEN b = sqrtnint(shifti(N, 12), 4);
899 return addiu(shifti(addii(a, b), -2), 2);
900 }
901
902 static long
lessthan_qlo(void * E,GEN Dmq)903 lessthan_qlo(void* E, GEN Dmq) { return (cmpii(gel(Dmq,3), (GEN)E) < 0); }
904 static long
gained_bits(void * E,GEN Dmq)905 gained_bits(void* E, GEN Dmq) { return (expi(gel(Dmq,3)) <= (long)E); }
906
907 /* Input: Dmqvec
908 * Output: Dmqvec such that q satisfies (N^1/4 + 1)^2 < q < N/2 */
909 static GEN
Dmqvec_slice(GEN N,GEN Dmqvec)910 Dmqvec_slice(GEN N, GEN Dmqvec)
911 {
912 long lo, hi;
913
914 gen_sort_inplace(Dmqvec, NULL, &sort_Dmq_by_q, NULL); /* sort wrt q */
915 lo = zv_binsearch0((void*)ecpp_qlo(N), &lessthan_qlo, Dmqvec); lo++;
916 if (lo >= lg(Dmqvec)) return NULL;
917
918 hi = zv_binsearch0((void*)(expi(N)-1), &gained_bits, Dmqvec);
919 if (hi == 0) return NULL;
920
921 return vecslice(Dmqvec, lo, hi);
922 }
923
924 /* Given a Dmvec of [D,m]'s, simultaneously remove all prime factors of each
925 * m less then BOUND_PRIMORIAL. This implements Franke 2004: Proving the
926 * Primality of Very Large Numbers with fastECPP */
927 static GEN
Dmvec_batchfactor(GEN Dmvec,GEN primorial)928 Dmvec_batchfactor(GEN Dmvec, GEN primorial)
929 {
930 long i, l = lg(Dmvec);
931 GEN leaf, v = cgetg(l, t_VEC);
932 for (i = 1; i < l; i++)
933 { /* cheaply remove powers of 2 */
934 GEN m = gmael(Dmvec, i, 2);
935 long v2 = vali(m);
936 gel(v,i) = v2? shifti(m,-v2): m;
937 }
938 leaf = Z_ZV_mod_tree(primorial, v, ZV_producttree(v));
939 /* Go through each leaf and remove small factors. */
940 for (i = 1; i < l; i++)
941 {
942 GEN q = gel(v,i), Dm = gel(Dmvec,i), L = gel(leaf,i);
943 while (!equali1(L)) { L = gcdii(q, L); q = diviiexact(q, L); }
944 gel(v,i) = mkvec3(gel(Dm,1), gel(Dm,2), q);
945 }
946 return v;
947 }
948
949 /* return good parameters [maxsqrt, maxpcdg, tdivexp] for ecpp(N) */
950 static GEN
tunevec(long expiN,GEN param)951 tunevec(long expiN, GEN param)
952 {
953 GEN T = ecpp_param_get_tune(param);
954 long i, n = lg(T)-1;
955 for (i = 1; i < n; i++) { GEN x = gel(T,i); if (expiN <= x[4]) return x; }
956 return gel(T,n);
957 }
958 static long
tunevec_tdivbd(long expiN,GEN param)959 tunevec_tdivbd(long expiN, GEN param) { return uel(tunevec(expiN, param), 3); }
960 static long
tunevec_batchsize(long expiN,GEN param)961 tunevec_batchsize(long expiN, GEN param)
962 {
963 long t, b = 28 - tunevec_tdivbd(expiN, param);
964 if (b < 0) return expiN;
965 t = expiN >> b; return t < 1? 1: t;
966 }
967
968 static GEN
Dmbatch_factor_Dmqvec(GEN N,long expiN,GEN * X0,GEN Dmbatch,GEN param)969 Dmbatch_factor_Dmqvec(GEN N, long expiN, GEN* X0, GEN Dmbatch, GEN param)
970 {
971 pari_sp av = avma;
972 pari_timer ti;
973 GEN Dmqvec, primorial_vec = ecpp_param_get_primorial_vec(param);
974 GEN primorial = gel(primorial_vec, tunevec_tdivbd(expiN, param)-19);
975
976 /* B1: Factor by batch */
977 dbg_mode() timer_start(&ti);
978 Dmqvec = Dmvec_batchfactor(Dmbatch, primorial);
979 dbg_mode() timer_record(X0, "B1", &ti);
980
981 /* B2: For each batch, remove cardinalities lower than (N^(1/4)+1)^2
982 * and cardinalities in which we didn't win enough bits */
983 Dmqvec = Dmqvec_slice(N, Dmqvec);
984 if (!Dmqvec) return gc_NULL(av); /* nothing is left */
985 return gerepilecopy(av, Dmqvec);
986 }
987
988 GEN
ecpp_ispsp_worker(GEN N)989 ecpp_ispsp_worker(GEN N)
990 { return mkvecsmall(BPSW_psp_nosmalldiv(N)); }
991
992 static GEN
mkNDmqg(GEN z,GEN N,GEN Dmq,GEN g,GEN sqrtlist)993 mkNDmqg(GEN z, GEN N, GEN Dmq, GEN g, GEN sqrtlist)
994 {
995 GEN Dinfo = gel(Dmq,1), sq = Dfac_to_roots(Dinfo_get_Dfac(Dinfo),sqrtlist);
996 GEN NDmqg = mkcol6(N, Dinfo, gel(Dmq,2), gel(Dmq,3), g, sq);
997 return mkvec2(NDmqg, z);
998 }
999 /* expi(N) > 64. Return [ NDmqg, N_downrun(q) ]; NDmqg is a vector of the form
1000 * [N,D,m,q,g,sqrt]. For successive D, find a square root of D mod N (g is a
1001 * quadratic nonresidue), solve U^2 + |D|V^2 = 4N, then find candidates for m.
1002 * When enough m's, batch-factor them to produce the q's. If one of the q's is
1003 * pseudoprime, recursive call with N = q. May return gen_0 at toplevel
1004 * => N has a small prime divisor */
1005 static GEN
N_downrun(GEN N,GEN param,GEN worker,GEN * X0,long * depth,long persevere)1006 N_downrun(GEN N, GEN param, GEN worker, GEN *X0, long *depth, long persevere)
1007 {
1008 pari_timer T, ti;
1009 long lgdisclist, lprimelist, nbsqrt = 0, t, i, j, expiN = expi(N);
1010 long persevere_next = 0, FAIL = 0;
1011 ulong maxpcdg;
1012 GEN primelist, disclist, sqrtlist, g, Dmbatch, kroP;
1013
1014 dbg_mode() timer_start(&T);
1015 (*depth)++; /* we're going down the tree. */
1016 maxpcdg = ecpp_param_get_maxpcdg(param);
1017 primelist = ecpp_param_get_primelist(param);
1018 disclist = ecpp_param_get_disclist(param);
1019 lgdisclist = lg(disclist);
1020 t = tunevec_batchsize(expiN, param);
1021
1022 /* Precomputation for taking square roots, g needed for Fp_sqrt_i */
1023 g = Fp_2gener(N); if (!g) return gen_0; /* Composite if this happens. */
1024
1025 /* Initialize sqrtlist for this N. */
1026 lprimelist = lg(primelist);
1027 sqrtlist = zerovec(lprimelist-1);
1028
1029 /* A2: Check (p*|N) = 1 for all p */
1030 dbg_mode() timer_start(&ti);
1031 /* kroP[i] will contain (primelist[i] | N) */
1032 kroP = const_vecsmall(lprimelist-1, 2/*sentinel*/);
1033 switch(mod8(N))
1034 { /* primelist[1,2,3] = [8,-4,-8]; N is odd */
1035 case 1: kroP[1] = kroP[2] = kroP[3] = 1; break;
1036 case 3: kroP[1] = -1; kroP[2] =-1; kroP[3] = 1; break;
1037 case 5: kroP[1] = -1; kroP[2] = 1; kroP[3] =-1; break;
1038 case 7: kroP[1] = 1; kroP[2] =-1; kroP[3] =-1; break;
1039 }
1040 for(i=4; i<lprimelist; i++)
1041 kroP[i]= krosi(primelist[i],N);
1042 dbg_mode() timer_record(X0, "A2", &ti);
1043
1044 /* Print the start of this iteration. */
1045 dbg_mode() {
1046 char o = persevere? '<': '[';
1047 char c = persevere? '>': ']';
1048 err_printf(ANSI_BRIGHT_CYAN "\n%c %3d | %4ld bits%c "
1049 ANSI_RESET, o, *depth, expiN, c);
1050 }
1051 /* Initialize Dmbatch, populated with candidate cardinalities in Phase I
1052 * (until #Dmbatch >= t); its elements will be factored on Phase II */
1053 Dmbatch = vectrunc_init(t+7);
1054
1055 /* Number of cardinalities so far; should always be equal to lg(Dmbatch)-1. */
1056 /* i determines which discriminant we are considering. */
1057 i = 1;
1058 while (!FAIL)
1059 {
1060 pari_timer F;
1061 long lgDmqlist, last_i = i, numcard = 0; /* #Dmbatch */
1062 GEN Dmqlist;
1063
1064 /* Dmbatch begins "empty", but keep the allocated memory. */
1065 setlg(Dmbatch, 1);
1066
1067 /* PHASE I: Go through the D's and search for candidate m's.
1068 * Fill up Dmbatch until there are at least t elements. */
1069 while (i < lgdisclist )
1070 {
1071 GEN Dinfo = gel(disclist, i);
1072 long n;
1073 if (!persevere && Dinfo_get_pd(Dinfo) > maxpcdg) { FAIL = 1; break; }
1074 n = D_collectcards(N,param, X0, Dinfo, sqrtlist, g, Dmbatch, kroP, &nbsqrt);
1075 if (n < 0) return gen_0;
1076 last_i = i++;
1077 numcard += n; if (numcard >= t) break;
1078 }
1079
1080 /* We have exhausted disclist and there are no card. to be factored */
1081 if (numcard <= 0 && (FAIL || i >= lgdisclist)) break;
1082
1083 /* PHASE II: Find the corresponding q's for the m's found. Use Dmbatch */
1084 /* Find coresponding q of the candidate m's. */
1085 dbg_mode() timer_start(&F);
1086 Dmqlist = Dmbatch_factor_Dmqvec(N, expiN, X0, Dmbatch, param);
1087 if (Dmqlist == NULL) continue; /* none left => next discriminant. */
1088
1089 /* If we are cheating by adding class numbers, sort by class number */
1090 if (Dinfo_get_pd(gel(disclist, last_i)) > maxpcdg)
1091 gen_sort_inplace(Dmqlist, NULL, &sort_Dmq_by_cnum, NULL);
1092
1093 /* Check pseudoprimality before going down. */
1094 lgDmqlist = lg(Dmqlist);
1095 for (j = 1; j < lgDmqlist; j++)
1096 {
1097 GEN z, Dmq, q;
1098 struct pari_mt pt;
1099 pari_timer ti2;
1100 long running, stop = 0, pending = 0;
1101 mt_queue_start_lim(&pt, worker, lgDmqlist-j);
1102 dbg_mode() timer_start(&ti2);
1103 while ((running = (!stop && j < lgDmqlist)) || pending)
1104 {
1105 long jj;
1106 GEN done;
1107 mt_queue_submit(&pt, j, running ? mkvec(gmael(Dmqlist,j,3)) : NULL);
1108 done = mt_queue_get(&pt, &jj, &pending);
1109 if (done)
1110 {
1111 if (done[1] == 0)
1112 { dbg_mode() err_printf(ANSI_WHITE "." ANSI_RESET); }
1113 else if (!stop || jj < stop)
1114 stop = jj;
1115 }
1116 j++;
1117 }
1118 mt_queue_end(&pt);
1119 dbg_mode() timer_record(X0, "B3", &ti2);
1120 if (!stop && j >= lgDmqlist) break;
1121 j = stop; Dmq = gel(Dmqlist,j); q = gel(Dmq,3);
1122
1123 dbg_mode() {
1124 err_printf(ANSI_BRIGHT_RED "\n %5ld bits " ANSI_RESET,
1125 expi(q)-expiN);
1126 err_printf(" D = %ld", Dmq_get_D(Dmq));
1127 err_printf(ANSI_BRIGHT_BLUE " h = %ld" ANSI_RESET, Dmq_get_h(Dmq));
1128 }
1129 /* q is pseudoprime */
1130 if (expi(q) < 64) z = gen_1; /* q is prime; sentinel */
1131 else
1132 {
1133 z = N_downrun(q, param, worker, X0, depth, persevere_next);
1134 if (!z) {
1135 dbg_mode() { char o = persevere? '<': '[', c = persevere? '>': ']';
1136 err_printf(ANSI_CYAN "\n%c %3d | %4ld bits%c "
1137 ANSI_RESET, o, *depth, expiN, c); }
1138 continue; /* downrun failed */
1139 }
1140 }
1141 return mkNDmqg(z, N, Dmq, g, sqrtlist); /* SUCCESS */
1142 }
1143 if (i >= lgdisclist) break; /* discriminants exhausted: FAIL */
1144 if (Dinfo_get_pd(gel(disclist, last_i)) > maxpcdg)
1145 {
1146 dbg_mode() err_printf(ANSI_BRIGHT_RED " !" ANSI_RESET);
1147 persevere_next = 1;
1148 }
1149 }
1150 /* FAILED: Out of discriminants. */
1151 umael(*X0, 3, 1)++; /* FAILS++ */
1152 dbg_mode() err_printf(ANSI_BRIGHT_RED " X" ANSI_RESET);
1153 (*depth)--; return NULL;
1154 }
1155
1156 /* x: the output of the (recursive) downrun function; return a vector
1157 * whose components are [N, D, m, q, g] */
1158 static GEN
ecpp_flattencert(GEN x,long depth)1159 ecpp_flattencert(GEN x, long depth)
1160 {
1161 long i, l = depth+1;
1162 GEN ret = cgetg(l, t_VEC);
1163 if (typ(x) != t_VEC) return gen_0;
1164 for (i = 1; i < l; i++) { gel(ret, i) = gel(x,1); x = gel(x,2); }
1165 return ret;
1166 }
1167
1168 /* Call the first downrun then unravel the skeleton of the certificate.
1169 * Assume expi(N) < 64. This returns one of the following:
1170 * - a vector of the form [N, D, m, q, y]
1171 * - gen_0 (if N is composite)
1172 * - NULL (if FAIL) */
1173 static GEN
ecpp_step1(GEN N,GEN param,GEN * X0)1174 ecpp_step1(GEN N, GEN param, GEN* X0)
1175 {
1176 pari_sp av = avma;
1177 long depth = 0;
1178 GEN worker = strtofunction("_ecpp_ispsp_worker");
1179 GEN downrun = N_downrun(N, param, worker, X0, &depth, 1);
1180 if (downrun == NULL) return gc_NULL(av);
1181 return gerepilecopy(av, ecpp_flattencert(downrun, depth));
1182 }
1183
1184 /* The input is an integer N.
1185 It uses the precomputation step0 done in ecpp_step0. */
1186 static GEN
ecpp0(GEN N,GEN param)1187 ecpp0(GEN N, GEN param)
1188 {
1189
1190 GEN step1, answer, Tv, Cv, X0;
1191 pari_sp av = avma;
1192 long i, j;
1193
1194 /* Check if N is pseudoprime to begin with. */
1195 if (!BPSW_psp(N)) return gen_0;
1196
1197 /* Check if we should even prove it. */
1198 if (expi(N) < 64) return N;
1199
1200 /* Timers and Counters */
1201 Tv = mkvec4(zero_zv(5), zero_zv(3), zero_zv(3), zero_zv(1));
1202 Cv = mkvec4(zero_zv(5), zero_zv(3), zero_zv(3), zero_zv(1));
1203 X0 = mkvec3(Tv, Cv, zero_zv(1));
1204
1205 step1 = ecpp_step1(N, param, &X0);
1206 if (step1 == NULL) return gc_NULL(av);
1207 if (typ(step1) != t_VEC) { set_avma(av); return gen_0; }
1208
1209 answer = ecpp_step2(step1, &X0, ecpp_param_get_primelist(param));
1210
1211 dbg_mode() {
1212 for (i = 1; i < lg(Tv); i++)
1213 {
1214 GEN v = gel(Tv, i);
1215 long l = lg(v);
1216 for (j = 1; j < l; j++)
1217 {
1218 long t = umael3(X0,1, i,j), n = umael3(X0,2, i,j);
1219 if (!t) continue;
1220 err_printf("\n %c%ld: %16ld %16ld %16.3f", 'A'+i-1,j, t,n, (double)t/n);
1221 }
1222 }
1223 err_printf("\n" ANSI_BRIGHT_RED "\nFAILS: %16ld" ANSI_RESET "\n", umael(X0, 3, 1));
1224 }
1225 return gerepilecopy(av, answer);
1226 }
1227
1228 static const long ecpp_tune[][4]=
1229 {
1230 { 100, 2, 20, 500 },
1231 { 350, 14, 21, 1000 },
1232 { 450, 18, 21, 1500 },
1233 { 750, 22, 22, 2000 },
1234 { 900, 26, 23, 2500 },
1235 { 1000, 32, 23, 3000 },
1236 { 1200, 38, 24, 3500 },
1237 { 1400, 44, 24, 4000 },
1238 { 1600, 50, 24, 4500 },
1239 { 1800, 56, 25, 5000 },
1240 { 2000, 62, 25, 5500 },
1241 { 2200, 68, 26, 6000 },
1242 { 2400, 74, 26, 6500 },
1243 { 2600, 80, 26, 7000 },
1244 { 2800, 86, 26, 7500 },
1245 { 3000, 92, 27, 8000 },
1246 { 3200, 98, 27, 8500 },
1247 { 3400, 104, 28, 9000 },
1248 { 3600, 110, 28, 9500 },
1249 { 3800, 116, 29, 10000 },
1250 { 4000, 122, 29, 0 }
1251 };
1252
1253 /* assume N BPSW-pseudoprime */
1254 GEN
ecpp(GEN N)1255 ecpp(GEN N)
1256 {
1257 long expiN, i, tunelen;
1258 GEN tune;
1259
1260 /* Check if we should even prove it. */
1261 expiN = expi(N);
1262 if (expiN < 64) return N;
1263
1264 tunelen = (expiN+499)/500;
1265 tune = cgetg(tunelen+1, t_VEC);
1266 for (i = 1; i <= tunelen && ecpp_tune[i-1][3]; i++)
1267 gel(tune,i) = mkvecsmall4(ecpp_tune[i-1][0], ecpp_tune[i-1][1],
1268 ecpp_tune[i-1][2], ecpp_tune[i-1][3]);
1269 for (; i <= tunelen; i++) gel(tune,i) = mkvecsmall4(200*(i-1),6*i-4,30,500*i);
1270 for(;;)
1271 {
1272 GEN C, param, x = gel(tune, tunelen);
1273 pari_timer T;
1274 dbg_mode() timer_start(&T);
1275 param = ecpp_param_set(tune, x);
1276 dbg_mode() {
1277 err_printf(ANSI_BRIGHT_WHITE "\n%Ps" ANSI_RESET, x);
1278 err_printf(ANSI_WHITE " %8ld" ANSI_RESET, timer_delay(&T));
1279 }
1280 if ((C = ecpp0(N, param))) return C;
1281 x[1] *= 2;
1282 x[2] *= 2;
1283 x[3] = minss(x[3]+1, 30);
1284 }
1285 }
1286 long
isprimeECPP(GEN N)1287 isprimeECPP(GEN N)
1288 {
1289 pari_sp av = avma;
1290 if (!BPSW_psp(N)) return 0;
1291 return gc_long(av, !isintzero(ecpp(N)));
1292 }
1293
1294 /* PARI ECPP Certificate -> Human-readable format */
1295 static GEN
cert_out(GEN x)1296 cert_out(GEN x)
1297 {
1298 long i, l = lg(x);
1299 pari_str str;
1300 str_init(&str, 1);
1301 if (typ(x) == t_INT)
1302 {
1303 str_printf(&str, "%Ps is prime.\nIndeed, ispseudoprime(%Ps) = 1 and %Ps < 2^64.\n", x, x, x);
1304 return strtoGENstr(str.string);
1305 }
1306 for (i = 1; i < l; i++)
1307 {
1308 GEN xi = gel(x, i);
1309 str_printf(&str, "[%ld]\n", i);
1310 str_printf(&str, " N = %Ps\n", cert_get_N(xi));
1311 str_printf(&str, " t = %Ps\n", cert_get_t(xi));
1312 str_printf(&str, " s = %Ps\n", cert_get_s(xi));
1313 str_printf(&str, "a = %Ps\n", cert_get_a4(xi));
1314 str_printf(&str, "D = %Ps\n", cert_get_D(xi));
1315 str_printf(&str, "m = %Ps\n", cert_get_m(xi));
1316 str_printf(&str, "q = %Ps\n", cert_get_q(xi));
1317 str_printf(&str, "E = %Ps\n", cert_get_E(xi));
1318 str_printf(&str, "P = %Ps\n", cert_get_P(xi));
1319 }
1320 return strtoGENstr(str.string);
1321 }
1322
1323 /* PARI ECPP Certificate -> Magma Certificate
1324 * [* [*
1325 * N, |D|, -1, m,
1326 * [* a, b *],
1327 * [* x, y, 1 *],
1328 * [* [* q, 1 *] *]
1329 * *], ... *] */
1330 static GEN
magma_out(GEN x)1331 magma_out(GEN x)
1332 {
1333 long i, n = lg(x)-1;
1334 pari_str ret;
1335 str_init(&ret, 1);
1336 if (typ(x) == t_INT)
1337 {
1338 str_printf(&ret, "Operation not supported.");
1339 return strtoGENstr(ret.string);
1340 }
1341 str_printf(&ret, "[* ");
1342 for (i = 1; i<=n; i++)
1343 {
1344 GEN xi = gel(x,i), E = cert_get_E(xi), P = cert_get_P(xi);
1345 str_printf(&ret, "[* %Ps, %Ps, -1, %Ps, ",
1346 cert_get_N(xi), negi(cert_get_D(xi)), cert_get_m(xi));
1347 str_printf(&ret, "[* %Ps, %Ps *], ", gel(E,1), gel(E,2));
1348 str_printf(&ret, "[* %Ps, %Ps, 1 *], ", gel(P,1), gel(P,2));
1349 str_printf(&ret, "[* [* %Ps, 1 *] *] *]", cert_get_q(xi));
1350 if (i != n) str_printf(&ret, ", ");
1351 }
1352 str_printf(&ret, " *]");
1353 return strtoGENstr(ret.string);
1354 }
1355
1356 /* Prints: label=(sign)hexvalue\n
1357 * where sign is + or -
1358 * hexvalue is value in hex, of the form: 0xABC123 */
1359 static void
primo_printval(pari_str * ret,const char * label,GEN value)1360 primo_printval(pari_str *ret, const char* label, GEN value)
1361 {
1362 str_printf(ret, label);
1363 if (signe(value) >= 0) str_printf(ret, "=0x%P0X\n", value);
1364 else str_printf(ret, "=-0x%P0X\n", negi(value));
1365 }
1366
1367 /* Input: PARI ECPP Certificate / Output: PRIMO Certificate
1368 *
1369 * Let Y^2 = X^3 + aX + b be the elliptic curve over N with the point (x,y)
1370 * as described in the PARI certificate.
1371 *
1372 * If J != 0, 1728, PRIMO asks for [J,T], where T = a/A * B/b * x,
1373 * A = 3J(1728-J) and B = 2J(1728-J)^2.
1374 *
1375 * If J=0 or J=1728, PRIMO asks for [A,B,T]; we let A=a and B=b => T = x */
1376 static GEN
primo_out(GEN x)1377 primo_out(GEN x)
1378 {
1379 long i, l = (typ(x) == t_INT) ? 1 : lg(x);
1380 pari_str ret;
1381 str_init(&ret, 1);
1382 str_printf(&ret, "[PRIMO - Primality Certificate]\nFormat=4\n");
1383 str_printf(&ret, "TestCount=%ld\n\n[Comments]\n", l-1);
1384 str_printf(&ret, "Generated by %s\n",paricfg_version);
1385 str_printf(&ret, "https://pari.math.u-bordeaux.fr/\n\n[Candidate]\n");
1386 if (typ(x) == t_INT)
1387 {
1388 str_printf(&ret, "N=0x%P0X\n", x);
1389 return strtoGENstr(ret.string);
1390 }
1391 str_printf(&ret, "N=0x%P0X\n\n", cert_get_N(gel(x,1)));
1392 for (i = 1; i < l; i++)
1393 {
1394 GEN a4, a6, N, Nover2, xi = gel(x,i);
1395 long Ais0, Bis0;
1396 str_printf(&ret, "[%ld]\n", i);
1397 N = cert_get_N(xi);
1398 Nover2 = shifti(N, -1);
1399 primo_printval(&ret, "S", cert_get_s(xi));
1400 primo_printval(&ret, "W", cert_get_t(xi));
1401 a4 = cert_get_a4(xi); Ais0 = isintzero(a4);
1402 a6 = cert_get_a6(xi); Bis0 = isintzero(a6);
1403 if (!Ais0 && !Bis0) { /* J != 0, 1728 */
1404 primo_printval(&ret, "J", Fp_center_i(cert_get_J(xi), N, Nover2));
1405 primo_printval(&ret, "T", cert_get_T(xi));
1406 } else {
1407 if (!Ais0) a4 = Fp_center_i(a4, N, Nover2);
1408 if (!Bis0) a6 = Fp_center_i(a6, N, Nover2);
1409 primo_printval(&ret, "A", a4);
1410 primo_printval(&ret, "B", a6);
1411 primo_printval(&ret, "T", cert_get_x(xi));
1412 }
1413 str_printf(&ret, "\n");
1414 }
1415 return strtoGENstr(ret.string);
1416 }
1417
1418 /* return 1 if q > (N^1/4 + 1)^2, 0 otherwise */
1419 static long
Nq_isvalid(GEN N,GEN q)1420 Nq_isvalid(GEN N, GEN q)
1421 {
1422 GEN c = subii(sqri(subiu(q,1)), N);
1423 if (signe(c) <= 0) return 0;
1424 /* (q-1)^2 > N; check that (N - (q-1)^2)^2 > 16Nq */
1425 return (cmpii(sqri(c), shifti(mulii(N,q), 4)) > 0);
1426 }
1427
1428 GEN
primecertisvalid_ecpp_worker(GEN certi)1429 primecertisvalid_ecpp_worker(GEN certi)
1430 {
1431 GEN N, W, mP, sP, r, m, s, a, P, q;
1432 if (lg(certi)-1 != 5) return gen_0;
1433
1434 N = cert_get_N(certi);
1435 if (typ(N) != t_INT || signe(N) <= 0) return gen_0;
1436 switch(umodiu(N,6))
1437 {
1438 case 1: case 5: break; /* Check if N is not divisible by 2 or 3 */
1439 default: return gen_0;
1440 }
1441
1442 W = cert_get_t(certi);
1443 if (typ(W) != t_INT) return gen_0;
1444 /* Check if W^2 < 4N (Hasse interval) */
1445 if (!(cmpii(sqri(W), shifti(N,2)) < 0)) return gen_0;
1446
1447 s = cert_get_s(certi);
1448 if (typ(s) != t_INT || signe(s) < 0) return gen_0;
1449
1450 m = cert_get_m(certi);
1451 q = dvmdii(m, s, &r);
1452 /* Check m%s == 0 */
1453 if (!isintzero(r)) return gen_0;
1454 /* Check q > (N^(1/4) + 1)^2 */
1455 if (!Nq_isvalid(N, q)) return gen_0;
1456
1457 a = cert_get_a4(certi);
1458 if (typ(a) != t_INT) return gen_0;
1459
1460 P = cert_get_P(certi);
1461 if (typ(P) != t_VEC || lg(P) != 3) return gen_0;
1462 P = FpE_to_FpJ(P);
1463
1464 /* Check mP == 0 */
1465 mP = FpJ_mul(P, m, a, N);
1466 if (!FpJ_is_inf(mP)) return gen_0;
1467
1468 /* Check sP != 0 and third component is coprime to N */
1469 sP = FpJ_mul(P, s, a, N);
1470 if (!isint1(gcdii(gel(sP, 3), N))) return gen_0;
1471 return q;
1472 }
1473
1474 /* return 1 if 'cert' is a valid PARI ECPP certificate */
1475 static long
ecppisvalid_i(GEN cert)1476 ecppisvalid_i(GEN cert)
1477 {
1478 const long trustbits = 64;/* a pseudoprime < 2^trustbits is prime */
1479 long i, lgcert = lg(cert);
1480 GEN ql, q = gen_0, worker, check;
1481
1482 if (typ(cert) == t_INT)
1483 {
1484 if (expi(cert) >= trustbits) return 0; /* >= 2^trustbits */
1485 return BPSW_psp(cert);
1486 }
1487 if (typ(cert) != t_VEC || lgcert <= 1) return 0;
1488 ql = gel(cert, lgcert-1); if (lg(ql)-1 != 5) return 0;
1489 ql = cert_get_q(ql);
1490 if (expi(ql) >= trustbits || !BPSW_psp(ql)) return 0;
1491 worker = strtofunction("_primecertisvalid_ecpp_worker");
1492 check = gen_parapply(worker, cert);
1493 for (i = 1; i < lgcert; i++)
1494 {
1495 GEN certi = gel(cert, i);
1496 GEN qq = gel(check,i), N = cert_get_N(certi);
1497 if (isintzero(qq)) return 0;
1498 if (i > 1 && !equalii(N, q)) return 0;
1499 q = qq;
1500 }
1501 return 1;
1502 }
1503 long
ecppisvalid(GEN cert)1504 ecppisvalid(GEN cert)
1505 { pari_sp av = avma; return gc_long(av, ecppisvalid_i(cert)); }
1506
1507 GEN
ecppexport(GEN cert,long flag)1508 ecppexport(GEN cert, long flag)
1509 {
1510 switch(flag)
1511 {
1512 case 0: return cert_out(cert);
1513 case 1: return primo_out(cert);
1514 case 2: return magma_out(cert);
1515 }
1516 pari_err_FLAG("primecertexport");
1517 return NULL;/*LCOV_EXCL_LINE*/
1518 }
1519