1 /*****************************************************************************
2 *
3 * ECPP - Elliptic Curve Primality Proving
4 *
5 * Copyright (c) 2013-2014 Dana Jacobsen (dana@acm.org).
6 * This is free software; you can redistribute it and/or modify it under
7 * the same terms as the Perl 5 programming language system itself.
8 *
9 * This file is part of the Math::Prime::Util::GMP Perl module. A script
10 * is included to build this as a standalone program (see the README file).
11 *
12 * This is pretty good for numbers less than 800 digits. Over that, it needs
13 * larger discriminant sets. Comparing to other contemporary software:
14 *
15 * - Primo is much faster for inputs over 300 digits. Not open source.
16 * - mpz_aprcl 1.1 (APR-CL). Nearly the same speed to ~600 digits, with
17 * very little speed variation. Faster over 800 digits. No certificate.
18 * - GMP-ECPP is much slower at all sizes, and nearly useless > 300 digits.
19 * - AKS is stupendously slow, even with Bernstein and Voloch improvements.
20 * - François Morain's 10-20 year old work describes optimizations not
21 * present here, but his (very old!) binaries run slower than this code at
22 * all sizes. Not open source.
23 *
24 * A set of fixed discriminants are used, rather than calculating them as
25 * needed. Having a way to calculate values as needed would be a big help.
26 * In the interests of space for the MPU package, I've chosen ~600 values which
27 * compile into about 35k of data. This is about 1/5 of the entire code size
28 * for the MPU package. The github repository includes an expanded set of 5271
29 * discriminants that compile to 2MB. This is recommended if proving 300+
30 * digit numbers is a regular occurrence. There is a set available for download
31 * with almost 15k polys, taking 15.5MB.
32 *
33 * This version uses the FAS "factor all strategy", meaning it first constructs
34 * the entire factor chain, with backtracking if necessary, then will do the
35 * elliptic curve proof as it recurses back.
36 *
37 * If your goal is primality proofs for very large numbers, use Primo. It's
38 * free, it is very fast, it is widely used, it can process batch results,
39 * and it makes independently verifiable certificates (including the verifier
40 * included in this package). MPU's ECPP (this software) is an open source
41 * alternative with many of the same features for "small" numbers of <1000
42 * digits. Improvements are possible since it is open source.
43 *
44 * Another open source alternative if one does not need certificates is the
45 * mpz_aprcl code from David Cleaver. To about 600 digits the speeds are
46 * very similar, but past that this ECPP code starts slowing down.
47 *
48 * Thanks to H. Cohen, R. Crandall & C. Pomerance, and H. Riesel for their
49 * text books. Thanks to the authors of open source software who allow me
50 * to compare and contrast (GMP-ECM, GMP-ECPP). Thanks to the authors of GMP.
51 * Thanks to Schoof, Goldwasser, Kilian, Atkin, Morain, Lenstra, etc. for all
52 * the math and publications. Thanks to Gauss, Euler, et al.
53 *
54 * The ECM code in ecm.c was heavily influenced by early GMP-ECM work by Paul
55 * Zimmermann, as well as all the articles of Montgomery, Bosma, Lentra,
56 * Cohen, and others.
57 */
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <math.h>
63 #include <gmp.h>
64
65 #include "ptypes.h"
66 #include "ecpp.h"
67 #include "gmp_main.h" /* primorial */
68 #include "ecm.h"
69 #include "utility.h"
70 #include "prime_iterator.h"
71 #include "bls75.h"
72 #include "factor.h"
73 #include "primality.h"
74
75 #define MAX_SFACS 1000
76
77 #ifdef USE_LIBECM
78 #include <ecm.h>
79 #endif
80
81 #ifdef USE_APRCL
82 #include "mpz_aprcl.h"
83 #include "mpz_aprcl.c"
84 #endif
85
86 /*********** big primorials and lcm for divisibility tests **********/
87 static int _gcdinit = 0;
88 static mpz_t _gcd_small;
89 static mpz_t _gcd_large;
90
init_ecpp_gcds(UV nsize)91 void init_ecpp_gcds(UV nsize) {
92 if (_gcdinit == 0) {
93 mpz_init(_gcd_small);
94 mpz_init(_gcd_large);
95 _GMP_pn_primorial(_gcd_small, 3000);
96 /* This is never re-adjusted -- first number proved sets the size */
97 nsize *= 20;
98 if (nsize < 20000) nsize = 20000;
99 else if (nsize > 500000) nsize = 500000;
100 _GMP_pn_primorial(_gcd_large, nsize);
101 mpz_divexact(_gcd_large, _gcd_large, _gcd_small);
102 mpz_divexact_ui(_gcd_small, _gcd_small, 2*3*5);
103 _gcdinit = 1;
104 }
105 }
106
destroy_ecpp_gcds(void)107 void destroy_ecpp_gcds(void) {
108 if (!_gcdinit) return;
109 mpz_clear(_gcd_small);
110 mpz_clear(_gcd_large);
111 _gcdinit = 0;
112 }
113
114 /* We could use a function with a prefilter here, but my tests are showing
115 * that adding a Fermat test (ala GMP's is_probab_prime) is slower than going
116 * straight to the base-2 Miller-Rabin test we use in BPSW. */
117 #define is_bpsw_prime(n) _GMP_BPSW(n)
118
check_for_factor(mpz_t f,mpz_t inputn,mpz_t fmin,mpz_t n,int stage,mpz_t * sfacs,int * nsfacs,int degree)119 static int check_for_factor(mpz_t f, mpz_t inputn, mpz_t fmin, mpz_t n, int stage, mpz_t* sfacs, int* nsfacs, int degree)
120 {
121 int success, sfaci;
122 UV B1;
123
124 /* Use this so we don't modify their input value */
125 mpz_set(n, inputn);
126
127 if (mpz_cmp(n, fmin) <= 0) return 0;
128
129 #if 0
130 /* Use this to really encourage n-1 / n+1 proof types */
131 if (degree <= 0) {
132 if (stage == 1) return -1;
133 if (stage == 0) stage = 1;
134 }
135 #endif
136
137 /* Utilize GMP's fast gcd algorithms. Trial to 224737+ with two gcds. */
138 mpz_tdiv_q_2exp(n, n, mpz_scan1(n, 0));
139 while (mpz_divisible_ui_p(n, 3)) mpz_divexact_ui(n, n, 3);
140 while (mpz_divisible_ui_p(n, 5)) mpz_divexact_ui(n, n, 5);
141 if (mpz_cmp(n, fmin) <= 0) return 0;
142 mpz_gcd(f, n, _gcd_small);
143 while (mpz_cmp_ui(f, 1) > 0) {
144 mpz_divexact(n, n, f);
145 mpz_gcd(f, f, n);
146 }
147 if (mpz_cmp(n, fmin) <= 0) return 0;
148 mpz_gcd(f, n, _gcd_large);
149 while (mpz_cmp_ui(f, 1) > 0) {
150 mpz_divexact(n, n, f);
151 mpz_gcd(f, f, n);
152 }
153
154 sfaci = 0;
155 success = 1;
156 while (success) {
157 UV nsize = mpz_sizeinbase(n, 2);
158 const int do_pm1 = 1;
159 const int do_pp1 = 1;
160 const int do_pbr = 0;
161 const int do_ecm = 0;
162
163 if (mpz_cmp(n, fmin) <= 0) return 0;
164 if (is_bpsw_prime(n)) { mpz_set(f, n); return (mpz_cmp(f, fmin) > 0); }
165
166 success = 0;
167 B1 = 300 + 3 * nsize;
168 if (degree <= 2) B1 += nsize; /* D1 & D2 are cheap to prove. */
169 if (degree <= 0) B1 += 2*nsize; /* N-1 and N+1 are really cheap. */
170 if (degree > 20 && stage <= 1) B1 -= nsize; /* Less time on big polys. */
171 if (degree > 40) B1 -= nsize/2; /* Less time on big polys. */
172 if (stage == 0) {
173 /* A relatively small performance hit, makes slightly smaller proofs. */
174 if (nsize < 900 && degree <= 2) B1 *= 1.8;
175 /* We need to try a bit harder for the large sizes :( */
176 if (nsize > 1400) B1 *= 2;
177 if (nsize > 2000) B1 *= 2;
178 if (!success)
179 success = _GMP_pminus1_factor(n, f, 100+B1/8, 100+B1);
180 } else if (stage >= 1) {
181 /* P-1 */
182 if ((!success && do_pm1))
183 success = _GMP_pminus1_factor(n, f, B1, 6*B1);
184 /* Pollard's Rho */
185 if ((!success && do_pbr && nsize < 500))
186 success = _GMP_pbrent_factor(n, f, nsize % 53, 1000-nsize);
187 /* P+1 */
188 if ((!success && do_pp1)) {
189 UV ppB = (nsize < 2000) ? B1/4 : B1/16;
190 success = _GMP_pplus1_factor(n, f, 0, ppB, ppB);
191 }
192 if ((!success && do_ecm))
193 success = _GMP_ecm_factor_projective(n, f, 400, 2000, 1);
194 #ifdef USE_LIBECM
195 /* TODO: LIBECM in other stages */
196 /* Note: this will be substantially slower than our code for small sizes
197 * and the small B1/B2 values we're using. */
198 if (!success && degree <= 2 && nsize > 600) {
199 ecm_params params;
200 ecm_init(params);
201 params->method = ECM_ECM;
202 mpz_set_ui(params->B2, 10*B1);
203 mpz_set_ui(params->sigma, 0);
204 success = ecm_factor(f, n, B1/4, params);
205 ecm_clear(params);
206 if (mpz_cmp(f, n) == 0) success = 0;
207 if (success) { printf("ECM FOUND FACTOR\n"); }
208 }
209 #endif
210 }
211 /* Try any factors found in previous stage 2+ calls */
212 while (!success && sfaci < *nsfacs) {
213 if (mpz_divisible_p(n, sfacs[sfaci])) {
214 mpz_set(f, sfacs[sfaci]);
215 success = 1;
216 }
217 sfaci++;
218 }
219 if (stage > 1 && !success) {
220 if (stage == 2) {
221 /* if (!success) success = _GMP_pbrent_factor(n, f, nsize-1, 8192); */
222 if (!success) success = _GMP_pminus1_factor(n, f, 6*B1, 60*B1);
223 /* p+1 with different initial point and searching farther */
224 if (!success) success = _GMP_pplus1_factor(n, f, 1, B1/2, B1/2);
225 if (!success) success = _GMP_ecm_factor_projective(n, f, 250, 2500, 8);
226 } else if (stage == 3) {
227 if (!success) success = _GMP_pbrent_factor(n, f, nsize+1, 16384);
228 if (!success) success = _GMP_pminus1_factor(n, f, 60*B1, 600*B1);
229 /* p+1 with a third initial point and searching farther */
230 if (!success) success = _GMP_pplus1_factor(n, f, 2, 1*B1, 1*B1);
231 if (!success) success = _GMP_ecm_factor_projective(n, f, B1/4, B1*4, 5);
232 } else if (stage == 4) {
233 if (!success) success = _GMP_pminus1_factor(n, f, 300*B1, 300*20*B1);
234 if (!success) success = _GMP_ecm_factor_projective(n, f, B1/2, B1*8, 4);
235 } else if (stage >= 5) {
236 UV B = B1 * (stage-4) * (stage-4) * (stage-4);
237 if (!success) success = _GMP_ecm_factor_projective(n, f, B, 10*B, 8+stage);
238 }
239 }
240 if (success) {
241 if (mpz_cmp_ui(f, 1) == 0 || mpz_cmp(f, n) == 0) {
242 gmp_printf("factoring %Zd resulted in factor %Zd\n", n, f);
243 croak("internal error in ECPP factoring");
244 }
245 /* Add the factor to the saved factors list */
246 if (stage > 1 && *nsfacs < MAX_SFACS) {
247 /* gmp_printf(" ***** adding factor %Zd ****\n", f); */
248 mpz_init_set(sfacs[*nsfacs], f);
249 nsfacs[0]++;
250 }
251 /* Is the factor f what we want? */
252 if ( mpz_cmp(f, fmin) > 0 && is_bpsw_prime(f) ) return 1;
253 /* Divide out f */
254 mpz_divexact(n, n, f);
255 }
256 }
257 /* n is larger than fmin and not prime */
258 mpz_set(f, n);
259 return -1;
260 }
261
262 /* See:
263 * (1) Kaltofen, Valente, Yui 1989
264 * (2) Valente 1992 (Thesis)
265 * (3) Konstantinou, Stamatiou, and Zaroliagis (CHES 2002)
266 * This code is performing table 1 of reference 3.
267 */
weber_root_to_hilbert_root(mpz_t r,mpz_t N,long D)268 static void weber_root_to_hilbert_root(mpz_t r, mpz_t N, long D)
269 {
270 mpz_t A, t;
271
272 if (D < 0) D = -D;
273 D = ((D % 4) == 0) ? D/4 : D;
274 if ( (D % 8) == 0 )
275 return;
276
277 mpz_init(A); mpz_init(t);
278
279 switch (D % 8) {
280 case 1: if ((D % 3) != 0) mpz_powm_ui(t, r, 12, N);
281 else mpz_powm_ui(t, r, 4, N);
282 mpz_mul_ui(A, t, 64);
283 mpz_sub_ui(t, A, 16);
284 break;
285 case 2:
286 case 6: if ((D % 3) != 0) mpz_powm_ui(t, r, 12, N);
287 else mpz_powm_ui(t, r, 4, N);
288 mpz_mul_ui(A, t, 64);
289 mpz_add_ui(t, A, 16);
290 break;
291 case 5: if ((D % 3) != 0) mpz_powm_ui(t, r, 6, N);
292 else mpz_powm_ui(t, r, 2, N);
293 mpz_mul_ui(A, t, 64);
294 mpz_sub_ui(t, A, 16);
295 break;
296 case 7: if (!mpz_invert(t, r, N)) mpz_set_ui(t, 0);
297 if ((D % 3) != 0) mpz_powm_ui(A, t, 24, N);
298 else mpz_powm_ui(A, t, 8, N);
299 mpz_sub_ui(t, A, 16);
300 break;
301 /* Results in degree 3x Hilbert, so typically not used */
302 case 3: if (!mpz_invert(t, r, N)) mpz_set_ui(t, 0);
303 if ((D % 3) != 0) {
304 mpz_powm_ui(t, t, 24, N);
305 mpz_mul_2exp(A, t, 12);
306 } else {
307 mpz_powm_ui(t, t, 8, N);
308 mpz_mul_2exp(A, t, 4);
309 }
310 mpz_sub_ui(t, A, 16);
311 break;
312 default: break;
313 }
314 /* r = t^3 / A */
315 mpz_powm_ui(t, t, 3, N);
316 if ( ! mpz_divmod(r, t, A, N, r) )
317 mpz_set_ui(r, 0);
318 mpz_clear(A); mpz_clear(t);
319 }
320 /* See:
321 * Konstantinou and Kontogeorgis (2008) https://arxiv.org/abs/0804.1652
322 * http://www.math.leidenuniv.nl/~psh/konto5.pdf
323 */
ramanujan_root_to_hilbert_root(mpz_t r,mpz_t N,long D)324 static void ramanujan_root_to_hilbert_root(mpz_t r, mpz_t N, long D)
325 {
326 mpz_t A, t;
327
328 if (D < 0) D = -D;
329 if (D % 24 != 11) return; /* croak("Bad Ramanujan root D: %ld", D); */
330
331 mpz_init(A); mpz_init(t);
332 if (!mpz_invert(t, r, N)) mpz_set_ui(t, 0);
333 mpz_powm_ui(A, t, 6, N);
334 mpz_mul_ui(A, A, 27);
335 mpz_powm_ui(t, r, 6, N);
336 mpz_sub(A, t, A);
337 mpz_sub_ui(A, A, 6);
338 mpz_powm_ui(r, A, 3, N);
339 /* mpz_powm_ui(t, A, 3, N);
340 * gmp_printf("Converted Ramanujan root %Zd to Hilbert %Zd\n", r, t);
341 * mpz_set(r, t);
342 */
343 mpz_clear(A); mpz_clear(t);
344 }
345
346
find_roots(long D,int poly_index,mpz_t N,mpz_t ** roots,int maxroots)347 static int find_roots(long D, int poly_index, mpz_t N, mpz_t** roots, int maxroots)
348 {
349 mpz_t* T;
350 UV degree;
351 long dT, i, nroots;
352 int poly_type;
353
354 if (D == -3 || D == -4) {
355 *roots = 0;
356 return 1;
357 }
358
359 degree = poly_class_poly_num(poly_index, NULL, &T, &poly_type);
360 if (degree == 0 || (poly_type != 1 && poly_type != 2 && poly_type != 3))
361 return 0;
362
363 dT = degree;
364 polyz_mod(T, T, &dT, N);
365
366 polyz_roots_modp(roots, &nroots, maxroots, T, dT, N);
367 if (nroots == 0) {
368 gmp_printf("N = %Zd\n", N);
369 croak("Failed to find roots for D = %ld\n", D);
370 }
371 for (i = 0; i <= dT; i++)
372 mpz_clear(T[i]);
373 Safefree(T);
374 #if 0
375 if (nroots != dT && get_verbose_level())
376 printf(" found %ld roots of the %ld degree poly\n", nroots, dT);
377 #endif
378
379 /* Convert Weber and Ramanujan roots to Hilbert roots */
380 if (poly_type == 2)
381 for (i = 0; i < nroots; i++)
382 weber_root_to_hilbert_root((*roots)[i], N, D);
383 if (poly_type == 3)
384 for (i = 0; i < nroots; i++)
385 ramanujan_root_to_hilbert_root((*roots)[i], N, D);
386
387 return nroots;
388 }
389
select_curve_params(mpz_t a,mpz_t b,mpz_t g,long D,mpz_t * roots,long i,mpz_t N,mpz_t t)390 static void select_curve_params(mpz_t a, mpz_t b, mpz_t g,
391 long D, mpz_t *roots, long i, mpz_t N, mpz_t t)
392 {
393 int N_is_not_1_congruent_3;
394
395 mpz_set_ui(a, 0);
396 mpz_set_ui(b, 0);
397 if (D == -3) { mpz_set_si(b, -1); }
398 else if (D == -4) { mpz_set_si(a, -1); }
399 else {
400 mpz_sub_ui(t, roots[i], 1728);
401 mpz_mod(t, t, N);
402 /* c = (j * inverse(j-1728)) mod n */
403 if (mpz_divmod(b, roots[i], t, N, b)) {
404 mpz_mul_si(a, b, -3); /* r = -3c */
405 mpz_mul_si(b, b, 2); /* s = 2c */
406 }
407 }
408 mpz_mod(a, a, N);
409 mpz_mod(b, b, N);
410
411 /* g: 1 < g < Ni && (g/Ni) != -1 && (g%3!=1 || cubic non-residue) */
412 N_is_not_1_congruent_3 = ! mpz_congruent_ui_p(N, 1, 3);
413 for ( mpz_set_ui(g, 2); mpz_cmp(g, N) < 0; mpz_add_ui(g, g, 1) ) {
414 if (mpz_jacobi(g, N) != -1)
415 continue;
416 if (N_is_not_1_congruent_3)
417 break;
418 mpz_sub_ui(t, N, 1);
419 mpz_tdiv_q_ui(t, t, 3);
420 mpz_powm(t, g, t, N); /* t = g^((Ni-1)/3) mod Ni */
421 if (mpz_cmp_ui(t, 1) == 0)
422 continue;
423 if (D == -3) {
424 mpz_powm_ui(t, t, 3, N);
425 if (mpz_cmp_ui(t, 1) != 0) /* Additional check when D == -3 */
426 continue;
427 }
428 break;
429 }
430 if (mpz_cmp(g, N) >= 0) /* No g can be found: N is composite */
431 mpz_set_ui(g, 0);
432 }
433
select_point(mpz_t x,mpz_t y,mpz_t a,mpz_t b,mpz_t N,mpz_t t,mpz_t t2)434 static void select_point(mpz_t x, mpz_t y, mpz_t a, mpz_t b, mpz_t N,
435 mpz_t t, mpz_t t2)
436 {
437 mpz_t Q, t3, t4;
438
439 mpz_init(Q); mpz_init(t3); mpz_init(t4);
440 mpz_set_ui(y, 0);
441
442 while (mpz_sgn(y) == 0) {
443 /* select a Q s.t. (Q,N) != -1 */
444 do {
445 do {
446 /* mpz_urandomm(x, *p_randstate, N); */
447 mpz_isaac_urandomb(x, 32); /* May as well make x small */
448 mpz_mod(x, x, N);
449 } while (mpz_sgn(x) == 0);
450 mpz_mul(t, x, x);
451 mpz_add(t, t, a);
452 mpz_mul(t, t, x);
453 mpz_add(t, t, b);
454 mpz_mod(Q, t, N);
455 } while (mpz_jacobi(Q, N) == -1);
456 /* Select Y */
457 sqrtmod_t(y, Q, N, t, t2, t3, t4);
458 /* TODO: if y^2 mod Ni != t, return composite */
459 if (mpz_sgn(y) == 0) croak("y == 0 in point selection\n");
460 }
461 mpz_clear(Q); mpz_clear(t3); mpz_clear(t4);
462 }
463
464 /* Returns 0 (composite), 1 (didn't find a point), 2 (found point) */
ecpp_check_point(mpz_t x,mpz_t y,mpz_t m,mpz_t q,mpz_t a,mpz_t N,mpz_t t,mpz_t t2)465 int ecpp_check_point(mpz_t x, mpz_t y, mpz_t m, mpz_t q, mpz_t a, mpz_t N,
466 mpz_t t, mpz_t t2)
467 {
468 struct ec_affine_point P, P1, P2;
469 int result = 1;
470
471 mpz_init_set(P.x, x); mpz_init_set(P.y, y);
472 mpz_init(P1.x); mpz_init(P1.y);
473 mpz_init(P2.x); mpz_init(P2.y);
474
475 mpz_tdiv_q(t, m, q);
476 if (!ec_affine_multiply(a, t, N, P, &P2, t2)) {
477 mpz_tdiv_q(t, m, q);
478 /* P2 should not be (0,1) */
479 if (!(mpz_cmp_ui(P2.x, 0) == 0 && mpz_cmp_ui(P2.y, 1) == 0)) {
480 mpz_set(t, q);
481 if (!ec_affine_multiply(a, t, N, P2, &P1, t2)) {
482 /* P1 should be (0,1) */
483 if (mpz_cmp_ui(P1.x, 0) == 0 && mpz_cmp_ui(P1.y, 1) == 0) {
484 result = 2;
485 }
486 } else result = 0;
487 }
488 } else result = 0;
489
490 mpz_clear(P.x); mpz_clear(P.y);
491 mpz_clear(P1.x); mpz_clear(P1.y);
492 mpz_clear(P2.x); mpz_clear(P2.y);
493 return result;
494 }
495
update_ab(mpz_t a,mpz_t b,long D,mpz_t g,mpz_t N)496 static void update_ab(mpz_t a, mpz_t b, long D, mpz_t g, mpz_t N)
497 {
498 if (D == -3) { mpz_mul(b, b, g); }
499 else if (D == -4) { mpz_mul(a, a, g); }
500 else {
501 mpz_mul(a, a, g);
502 mpz_mul(a, a, g);
503 mpz_mul(b, b, g);
504 mpz_mul(b, b, g);
505 mpz_mul(b, b, g);
506 }
507 mpz_mod(a, a, N);
508 mpz_mod(b, b, N);
509 }
510
511 /* Once we have found a D and q, this will find a curve and point.
512 * Returns: 0 (composite), 1 (didn't work), 2 (success)
513 * It's debatable what to do with a 1 return.
514 */
find_curve(mpz_t a,mpz_t b,mpz_t x,mpz_t y,long D,int poly_index,mpz_t m,mpz_t q,mpz_t N,int maxroots)515 static int find_curve(mpz_t a, mpz_t b, mpz_t x, mpz_t y,
516 long D, int poly_index, mpz_t m, mpz_t q, mpz_t N, int maxroots)
517 {
518 long nroots, npoints, i, rooti, unity, result;
519 mpz_t g, t, t2;
520 mpz_t* roots = 0;
521
522 /* TODO: A better way to do this, I believe, would be to have the root
523 * finder set up as an iterator. That way we'd get the first root,
524 * try to find a curve, and probably we'd be done. Only if we tried
525 * 10+ points on that root would we get another root. This would
526 * probably be set up as a stack (array) of polynomials plus one
527 * saved root (for when we solve a degree 2 poly).
528 */
529 /* Step 1: Get the roots of the Hilbert class polynomial. */
530 nroots = find_roots(D, poly_index, N, &roots, maxroots);
531 if (nroots == 0)
532 return 1;
533
534 /* Step 2: Loop selecting curves and trying points.
535 * On average it takes about 3 points, but we'll try 100+. */
536
537 mpz_init(g); mpz_init(t); mpz_init(t2);
538 npoints = 0;
539 result = 1;
540 for (rooti = 0; result == 1 && rooti < 50*nroots; rooti++) {
541 /* Given this D and root, select curve a,b */
542 select_curve_params(a, b, g, D, roots, rooti % nroots, N, t);
543 if (mpz_sgn(g) == 0) { result = 0; break; }
544
545 /* See Cohen 5.3.1, page 231 */
546 unity = (D == -3) ? 6 : (D == -4) ? 4 : 2;
547 for (i = 0; result == 1 && i < unity; i++) {
548 if (i > 0)
549 update_ab(a, b, D, g, N);
550 npoints++;
551 select_point(x, y, a, b, N, t, t2);
552 result = ecpp_check_point(x, y, m, q, a, N, t, t2);
553 }
554 }
555 if (npoints > 10 && get_verbose_level() > 0)
556 printf(" # point finding took %ld points\n", npoints);
557
558 if (roots != 0) {
559 for (rooti = 0; rooti < nroots; rooti++)
560 mpz_clear(roots[rooti]);
561 Safefree(roots);
562 }
563 mpz_clear(g); mpz_clear(t); mpz_clear(t2);
564
565 return result;
566 }
567
568 /* Select the 2, 4, or 6 numbers we will try to factor. */
choose_m(mpz_t * mlist,long D,mpz_t u,mpz_t v,mpz_t N,mpz_t t,mpz_t Nplus1)569 static void choose_m(mpz_t* mlist, long D, mpz_t u, mpz_t v, mpz_t N,
570 mpz_t t, mpz_t Nplus1)
571 {
572 int i, j;
573 mpz_add_ui(Nplus1, N, 1);
574
575 mpz_sub(mlist[0], Nplus1, u); /* N+1-u */
576 mpz_add(mlist[1], Nplus1, u); /* N+1+u */
577 for (i = 2; i < 6; i++)
578 mpz_set_ui(mlist[i], 0);
579
580 if (D == -3) {
581 /* If reading Cohen, be sure to see the errata for page 474. */
582 mpz_mul_si(t, v, 3);
583 mpz_add(t, t, u);
584 mpz_tdiv_q_2exp(t, t, 1);
585 mpz_sub(mlist[2], Nplus1, t); /* N+1-(u+3v)/2 */
586 mpz_add(mlist[3], Nplus1, t); /* N+1+(u+3v)/2 */
587 mpz_mul_si(t, v, -3);
588 mpz_add(t, t, u);
589 mpz_tdiv_q_2exp(t, t, 1);
590 mpz_sub(mlist[4], Nplus1, t); /* N+1-(u-3v)/2 */
591 mpz_add(mlist[5], Nplus1, t); /* N+1+(u-3v)/2 */
592 } else if (D == -4) {
593 mpz_mul_ui(t, v, 2);
594 mpz_sub(mlist[2], Nplus1, t); /* N+1-2v */
595 mpz_add(mlist[3], Nplus1, t); /* N+1+2v */
596 }
597 /* m must not be prime */
598 for (i = 0; i < 6; i++)
599 if (mpz_sgn(mlist[i]) && _GMP_is_prob_prime(mlist[i]))
600 mpz_set_ui(mlist[i], 0);
601 /* Sort the m values so we test the smallest first */
602 for (i = 0; i < 5; i++)
603 if (mpz_sgn(mlist[i]))
604 for (j = i+1; j < 6; j++)
605 if (mpz_sgn(mlist[j]) && mpz_cmp(mlist[i],mlist[j]) > 0)
606 mpz_swap( mlist[i], mlist[j] );
607 }
608
609
610
611
612
613 /* This is the "factor all strategy" FAS version, which ends up being a lot
614 * simpler than the FPS code.
615 *
616 * It should have a little more smarts for not repeating work when repeating
617 * steps. This could be complicated trying to save all state, but I think we
618 * could get most of the benefit by keeping a simple list of all factors
619 * found after stage 1, and we just try each of them.
620 */
621
622 #define VERBOSE_PRINT_N(step, ndigits, maxH, factorstage) \
623 if (verbose) { \
624 printf("%*sN[%d] (%d dig)", i, "", step, ndigits); \
625 if (factorstage > 1) printf(" [FS %d]", factorstage); \
626 fflush(stdout); \
627 }
628
629 /* Recursive routine to prove via ECPP */
ecpp_down(int i,mpz_t Ni,int facstage,int * pmaxH,int * dilist,mpz_t * sfacs,int * nsfacs,char ** prooftextptr)630 static int ecpp_down(int i, mpz_t Ni, int facstage, int *pmaxH, int* dilist, mpz_t* sfacs, int* nsfacs, char** prooftextptr)
631 {
632 mpz_t a, b, u, v, m, q, minfactor, sqrtn, mD, t, t2;
633 mpz_t mlist[6];
634 mpz_t qlist[6];
635 UV nm1a;
636 IV np1lp, np1lq;
637 struct ec_affine_point P;
638 int k, dindex, pindex, nidigits, facresult, curveresult, downresult, stage, D;
639 int verbose = get_verbose_level();
640
641 nidigits = mpz_sizeinbase(Ni, 10);
642
643 downresult = _GMP_is_prob_prime(Ni);
644 if (downresult == 0) return 0;
645 if (downresult == 2) {
646 if (mpz_sizeinbase(Ni,2) <= 64) {
647 /* No need to put anything in the proof */
648 if (verbose) printf("%*sN[%d] (%d dig) PRIME\n", i, "", i, nidigits);
649 return 2;
650 }
651 downresult = 1;
652 }
653 if (i == 0 && facstage == 2 && miller_rabin_random(Ni, 2, 0) == 0) {
654 gmp_printf("\n\n**** BPSW counter-example found? ****\n**** N = %Zd ****\n\n", Ni);
655 return 0;
656 }
657
658 VERBOSE_PRINT_N(i, nidigits, *pmaxH, facstage);
659
660 mpz_init(a); mpz_init(b);
661 mpz_init(u); mpz_init(v);
662 mpz_init(m); mpz_init(q);
663 mpz_init(mD); mpz_init(minfactor); mpz_init(sqrtn);
664 mpz_init(t); mpz_init(t2);
665 mpz_init(P.x);mpz_init(P.y);
666 for (k = 0; k < 6; k++) {
667 mpz_init(mlist[k]);
668 mpz_init(qlist[k]);
669 }
670
671 /* Any factors q found must be strictly > minfactor.
672 * See Atkin and Morain, 1992, section 6.4 */
673 mpz_root(minfactor, Ni, 4);
674 mpz_add_ui(minfactor, minfactor, 1);
675 mpz_mul(minfactor, minfactor, minfactor);
676 mpz_sqrt(sqrtn, Ni);
677
678 stage = 0;
679 if (nidigits > 700) stage = 1; /* Too rare to find them */
680 if (i == 0 && facstage > 1) stage = facstage;
681 for ( ; stage <= facstage; stage++) {
682 int next_stage = (stage > 1) ? stage : 1;
683 for (dindex = -1; dindex < 0 || dilist[dindex] != 0; dindex++) {
684 int poly_type; /* just for debugging/verbose */
685 int poly_degree;
686 int allq = (nidigits < 400); /* Do all q values together, or not */
687
688 if (dindex == -1) { /* n-1 and n+1 tests */
689 int nm1_success = 0;
690 int np1_success = 0;
691 const char* ptype = "";
692 mpz_sub_ui(m, Ni, 1);
693 mpz_sub_ui(t2, sqrtn, 1);
694 mpz_tdiv_q_2exp(t2, t2, 1); /* t2 = minfactor */
695 nm1_success = check_for_factor(u, m, t2, t, stage, sfacs, nsfacs, 0);
696 mpz_add_ui(m, Ni, 1);
697 mpz_add_ui(t2, sqrtn, 1);
698 mpz_tdiv_q_2exp(t2, t2, 1); /* t2 = minfactor */
699 np1_success = check_for_factor(v, m, t2, t, stage, sfacs, nsfacs, 0);
700 /* If both successful, pick smallest */
701 if (nm1_success > 0 && np1_success > 0) {
702 if (mpz_cmp(u, v) <= 0) np1_success = 0;
703 else nm1_success = 0;
704 }
705 if (nm1_success > 0) { ptype = "n-1"; mpz_set(q, u); D = 1; }
706 else if (np1_success > 0) { ptype = "n+1"; mpz_set(q, v); D = -1; }
707 else continue;
708 if (verbose) { printf(" %s\n", ptype); fflush(stdout); }
709 downresult = ecpp_down(i+1, q, next_stage, pmaxH, dilist, sfacs, nsfacs, prooftextptr);
710 if (downresult == 0) goto end_down; /* composite */
711 if (downresult == 1) { /* nothing found at this stage */
712 VERBOSE_PRINT_N(i, nidigits, *pmaxH, facstage);
713 continue;
714 }
715 if (verbose)
716 { printf("%*sN[%d] (%d dig) %s", i, "", i, nidigits, ptype); fflush(stdout); }
717 curveresult = (nm1_success > 0)
718 ? _GMP_primality_bls_3(Ni, q, &nm1a)
719 : _GMP_primality_bls_15(Ni, q, &np1lp, &np1lq);
720 if (verbose) { printf(" %d\n", curveresult); fflush(stdout); }
721 if ( ! curveresult ) { /* This ought not happen */
722 if (verbose)
723 gmp_printf("\n Could not prove %s with N = %Zd\n", ptype, Ni);
724 downresult = 1;
725 continue;
726 }
727 goto end_down;
728 }
729
730 pindex = dilist[dindex];
731 if (pindex < 0) continue; /* We marked this for skip */
732 /* Get the values for D, degree, and poly type */
733 poly_degree = poly_class_poly_num(pindex, &D, NULL, &poly_type);
734 if (poly_degree == 0)
735 croak("Unknown value in dilist[%d]: %d\n", dindex, pindex);
736
737 if ( (-D % 4) != 3 && (-D % 16) != 4 && (-D % 16) != 8 )
738 croak("Invalid discriminant '%d' in list\n", D);
739 /* D must also be squarefree in odd divisors, but assume it. */
740 /* Make sure we can get a class polynomial for this D. */
741 if (poly_degree > 16 && stage == 0) {
742 if (verbose) printf(" [1]");
743 break;
744 }
745 /* Make the continue-search vs. backtrack decision */
746 if (*pmaxH > 0 && poly_degree > *pmaxH) break;
747 mpz_set_si(mD, D);
748 /* (D/N) must be 1, and we have to have a u,v solution */
749 if (mpz_jacobi(mD, Ni) != 1)
750 continue;
751 if ( ! modified_cornacchia(u, v, mD, Ni) )
752 continue;
753
754 if (verbose > 1)
755 { printf(" %d", D); fflush(stdout); }
756
757 /* We're going to factor all the values for this discriminant then pick
758 * the smallest. This adds a little time, but it means we go down
759 * faster. This makes smaller proofs, and might even save time. */
760
761 choose_m(mlist, D, u, v, Ni, t, t2);
762 if (allq) {
763 int x, y;
764 /* We have 0 to 6 m values. Try to factor them, put in qlist. */
765 for (k = 0; k < 6; k++) {
766 mpz_set_ui(qlist[k], 0);
767 if (mpz_sgn(mlist[k])) {
768 facresult = check_for_factor(qlist[k], mlist[k], minfactor, t, stage, sfacs, nsfacs, poly_degree);
769 /* -1 = couldn't find, 0 = no big factors, 1 = found */
770 if (facresult <= 0)
771 mpz_set_ui(qlist[k], 0);
772 }
773 }
774 /* Sort any q values by size, so we work on the smallest first */
775 for (x = 0; x < 5; x++)
776 if (mpz_sgn(qlist[x]))
777 for (y = x+1; y < 6; y++)
778 if (mpz_sgn(qlist[y]) && mpz_cmp(qlist[x],qlist[y]) > 0) {
779 mpz_swap( qlist[x], qlist[y] );
780 mpz_swap( mlist[x], mlist[y] );
781 }
782 }
783 /* Try to make a proof with the first (smallest) q value.
784 * Repeat for others if we have to. */
785 for (k = 0; k < 6; k++) {
786 int maxH = *pmaxH;
787 int minH = (nidigits <= 240) ? 7 : (nidigits+39)/40;
788
789 if (allq) {
790 if (mpz_sgn(qlist[k]) == 0) continue;
791 mpz_set(m, mlist[k]);
792 mpz_set(q, qlist[k]);
793 } else {
794 if (mpz_sgn(mlist[k]) == 0) continue;
795 mpz_set(m, mlist[k]);
796 facresult = check_for_factor(q, m, minfactor, t, stage, sfacs, nsfacs, poly_degree);
797 if (facresult <= 0) continue;
798 }
799
800 if (verbose)
801 { printf(" %d (%s %d)\n", D, poly_class_type_name(poly_type), poly_degree); fflush(stdout); }
802 if (maxH == 0) {
803 maxH = minH-1 + poly_degree;
804 if (facstage > 1) /* We worked hard to get here, */
805 maxH = 2*maxH + 10; /* try hard to make use of it. */
806 } else if (maxH > minH && maxH > (poly_degree+2)) {
807 maxH--;
808 }
809 /* Great, now go down. */
810 downresult = ecpp_down(i+1, q, next_stage, &maxH, dilist, sfacs, nsfacs, prooftextptr);
811 /* Nothing found, look at more polys in the future */
812 if (downresult == 1 && *pmaxH > 0) *pmaxH = maxH;
813
814 if (downresult == 0) goto end_down; /* composite */
815 if (downresult == 1) { /* nothing found at this stage */
816 VERBOSE_PRINT_N(i, nidigits, *pmaxH, facstage);
817 continue;
818 }
819
820 /* Awesome, we found the q chain and are in STAGE 2 */
821 if (verbose)
822 { printf("%*sN[%d] (%d dig) %d (%s %d)", i, "", i, nidigits, D, poly_class_type_name(poly_type), poly_degree); fflush(stdout); }
823
824 /* Try with only one or two roots, then 8 if that didn't work. */
825 /* TODO: This should be done using a root iterator in find_curve() */
826 curveresult = find_curve(a, b, P.x, P.y, D, pindex, m, q, Ni, 1);
827 if (curveresult == 1) {
828 if (verbose) { printf(" [redo roots]"); fflush(stdout); }
829 curveresult = find_curve(a, b, P.x, P.y, D, pindex, m, q, Ni, 8);
830 }
831 if (verbose) { printf(" %d\n", curveresult); fflush(stdout); }
832 if (curveresult == 1) {
833 /* Something is wrong. Very likely the class poly coefficients are
834 incorrect. We've wasted lots of time, and need to try again. */
835 dilist[dindex] = -2; /* skip this D value from now on */
836 if (verbose) gmp_printf("\n Invalidated D = %d with N = %Zd\n", D, Ni);
837 downresult = 1;
838 continue;
839 }
840 /* We found it was composite or proved it */
841 goto end_down;
842 } /* k loop for D */
843 } /* D */
844 } /* fac stage */
845 /* Nothing at this level */
846 if (downresult != 1) croak("ECPP internal error: downresult is %d at end\n", downresult);
847 if (verbose) {
848 if (*pmaxH > 0) printf(" (max %d)", *pmaxH);
849 printf(" ---\n");
850 fflush(stdout);
851 }
852 if (*pmaxH > 0) *pmaxH = *pmaxH + 2;
853
854 end_down:
855
856 if (downresult == 2) {
857 if (0 && verbose > 1) {
858 gmp_printf("\n");
859 if (D == 1) {
860 gmp_printf("Type BLS3\nN %Zd\nQ %Zd\nA %"UVuf"\n", Ni, q, nm1a);
861 } else if (D == -1) {
862 gmp_printf("Type BLS15\nN %Zd\nQ %Zd\nLP %"IVdf"\nLQ %"IVdf"\n", Ni, q, np1lp, np1lq);
863 } else {
864 gmp_printf("Type ECPP\nN %Zd\nA %Zd\nB %Zd\nM %Zd\nQ %Zd\nX %Zd\nY %Zd\n", Ni, a, b, m, q, P.x, P.y);
865 }
866 gmp_printf("\n");
867 fflush(stdout);
868 }
869 /* Prepend our proof to anything that exists. */
870 if (prooftextptr != 0) {
871 char *proofstr, *proofptr;
872 int curprooflen = (*prooftextptr == 0) ? 0 : strlen(*prooftextptr);
873
874 if (D == 1) {
875 int myprooflen = 20 + 2*(4 + mpz_sizeinbase(Ni, 10)) + 1*21;
876 New(0, proofstr, myprooflen + curprooflen + 1, char);
877 proofptr = proofstr;
878 proofptr += gmp_sprintf(proofptr, "Type BLS3\nN %Zd\nQ %Zd\n", Ni,q);
879 proofptr += sprintf(proofptr, "A %"UVuf"\n", nm1a);
880 } else if (D == -1) {
881 int myprooflen = 20 + 2*(4 + mpz_sizeinbase(Ni, 10)) + 2*21;
882 New(0, proofstr, myprooflen + curprooflen + 1, char);
883 proofptr = proofstr;
884 /* It seems some testers have a sprintf bug with IVs. Try to handle. */
885 proofptr += gmp_sprintf(proofptr, "Type BLS15\nN %Zd\nQ %Zd\n", Ni,q);
886 proofptr += sprintf(proofptr, "LP %d\nLQ %d\n", (int)np1lp, (int)np1lq);
887 } else {
888 int myprooflen = 20 + 7*(4 + mpz_sizeinbase(Ni, 10)) + 0;
889 New(0, proofstr, myprooflen + curprooflen + 1, char);
890 proofptr = proofstr;
891 mpz_sub_ui(t, Ni, 1);
892 if (mpz_cmp(a, t) == 0) mpz_set_si(a, -1);
893 if (mpz_cmp(b, t) == 0) mpz_set_si(b, -1);
894 proofptr += gmp_sprintf(proofptr, "Type ECPP\nN %Zd\nA %Zd\nB %Zd\nM %Zd\nQ %Zd\nX %Zd\nY %Zd\n", Ni, a, b, m, q, P.x, P.y);
895 }
896 if (*prooftextptr) {
897 proofptr += gmp_sprintf(proofptr, "\n");
898 strcat(proofptr, *prooftextptr);
899 Safefree(*prooftextptr);
900 }
901 *prooftextptr = proofstr;
902 }
903 }
904
905 /* Ni passed BPSW, so it's highly unlikely to be composite */
906 if (downresult == 0) {
907 if (mpz_probab_prime_p(Ni, 2) == 0) {
908 gmp_printf("\n\n**** BPSW counter-example found? ****\n**** N = %Zd ****\n\n", Ni);
909 } else {
910 /* Q was composite, but we don't seem to be. */
911 downresult = 1;
912 }
913 }
914
915 mpz_clear(a); mpz_clear(b);
916 mpz_clear(u); mpz_clear(v);
917 mpz_clear(m); mpz_clear(q);
918 mpz_clear(mD); mpz_clear(minfactor); mpz_clear(sqrtn);
919 mpz_clear(t); mpz_clear(t2);
920 mpz_clear(P.x);mpz_clear(P.y);
921 for (k = 0; k < 6; k++) {
922 mpz_clear(mlist[k]);
923 mpz_clear(qlist[k]);
924 }
925
926 return downresult;
927 }
928
929 /* returns 2 if N is proven prime, 1 if probably prime, 0 if composite */
_GMP_ecpp(mpz_t N,char ** prooftextptr)930 int _GMP_ecpp(mpz_t N, char** prooftextptr)
931 {
932 int* dilist;
933 mpz_t* sfacs;
934 int i, fstage, result, nsfacs;
935 UV nsize = mpz_sizeinbase(N,2);
936
937 /* We must check gcd(N,6), let's check 2*3*5*7*11*13*17*19*23. */
938 if (nsize <= 64 || mpz_gcd_ui(NULL, N, 223092870UL) != 1) {
939 result = _GMP_is_prob_prime(N);
940 if (result != 1) return result;
941 }
942
943 init_ecpp_gcds( nsize );
944
945 if (prooftextptr)
946 *prooftextptr = 0;
947
948 New(0, sfacs, MAX_SFACS, mpz_t);
949 dilist = poly_class_nums();
950 nsfacs = 0;
951 result = 1;
952 for (fstage = 1; fstage < 20; fstage++) {
953 int maxH = 0;
954 if (fstage == 3 && get_verbose_level())
955 gmp_printf("Working hard on: %Zd\n", N);
956 result = ecpp_down(0, N, fstage, &maxH, dilist, sfacs, &nsfacs, prooftextptr);
957 if (result != 1)
958 break;
959 }
960 Safefree(dilist);
961 for (i = 0; i < nsfacs; i++)
962 mpz_clear(sfacs[i]);
963 Safefree(sfacs);
964
965 return result;
966 }
967
968
969 #ifdef STANDALONE_ECPP
dieusage(char * prog)970 static void dieusage(char* prog) {
971 printf("ECPP-DJ version 1.04. Dana Jacobsen, 2014.\n\n");
972 printf("Usage: %s [options] <number or expression>\n\n", prog);
973 printf("Options:\n");
974 printf(" -v set verbose\n");
975 printf(" -V set extra verbose\n");
976 printf(" -q no output other than return code\n");
977 printf(" -c print certificate to stdout (redirect to save to a file)\n");
978 printf(" -bpsw use the extra strong BPSW test (probable prime test)\n");
979 printf(" -nm1 use n-1 proof only (BLS75 theorem 5/7)\n");
980 printf(" -np1 use n+1 proof only (BLS75 theorem 19)\n");
981 printf(" -bls use n-1 / n+1 proof (various BLS75 theorems)\n");
982 printf(" -ecpp use ECPP proof only\n");
983 printf(" -aks use AKS for proof\n");
984 #ifdef USE_APRCL
985 printf(" -aprcl use APR-CL for proof\n");
986 #endif
987 printf(" -help this message\n");
988 printf("\n");
989 printf("Return codes: 0 prime, 1 composite, 2 prp, 3 error\n");
990 exit(3);
991 }
992
993 #include "expr.h"
994 #include "aks.h"
995 #include "bls75.h"
996
main(int argc,char ** argv)997 int main(int argc, char **argv)
998 {
999 mpz_t n;
1000 int isprime, i, do_printcert;
1001 int do_nminus1 = 0;
1002 int do_nplus1 = 0;
1003 int do_bls75 = 0;
1004 int do_aks = 0;
1005 int do_aprcl = 0;
1006 int do_ecpp = 0;
1007 int do_bpsw = 0;
1008 int be_quiet = 0;
1009 int retcode = 3;
1010 char* cert = 0;
1011
1012 if (argc < 2) dieusage(argv[0]);
1013 _GMP_init();
1014 mpz_init(n);
1015 set_verbose_level(0);
1016 do_printcert = 0;
1017
1018 /* Braindead hacky option parsing */
1019 for (i = 1; i < argc; i++) {
1020 if (argv[i][0] == '-') {
1021 if (strcmp(argv[i], "-v") == 0) {
1022 set_verbose_level(1);
1023 } else if (strcmp(argv[i], "-V") == 0) {
1024 set_verbose_level(2);
1025 } else if (strcmp(argv[i], "-q") == 0) {
1026 be_quiet = 1;
1027 set_verbose_level(0);
1028 do_printcert = 0;
1029 } else if (strcmp(argv[i], "-c") == 0) {
1030 do_printcert = 1;
1031 } else if (strcmp(argv[i], "-nm1") == 0) {
1032 do_nminus1 = 1;
1033 } else if (strcmp(argv[i], "-np1") == 0) {
1034 do_nplus1 = 1;
1035 } else if (strcmp(argv[i], "-bls") == 0) {
1036 do_bls75 = 1;
1037 } else if (strcmp(argv[i], "-aks") == 0) {
1038 do_aks = 1;
1039 } else if (strcmp(argv[i], "-ecpp") == 0) {
1040 do_ecpp = 1;
1041 } else if (strcmp(argv[i], "-aprcl") == 0) {
1042 do_aprcl = 1;
1043 } else if (strcmp(argv[i], "-bpsw") == 0) {
1044 do_bpsw = 1;
1045 } else if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0) {
1046 dieusage(argv[0]);
1047 } else {
1048 printf("Unknown option: %s\n\n", argv[i]);
1049 dieusage(argv[0]);
1050 }
1051 continue;
1052 }
1053 /* mpz_set_str(n, argv[i], 10); */
1054 if (mpz_expr(n, 10, argv[i])) croak("Can't parse input: '%s'\n",argv[i]);
1055 if (get_verbose_level() > 1) gmp_printf("N: %Zd\n", n);
1056
1057 isprime = _GMP_is_prob_prime(n);
1058 /* isprime = 2 either because BPSW/M-R passed and it is small, or it
1059 * went through a test like Lucas-Lehmer, Proth, or Lucas-Lehmer-Riesel.
1060 * We don't currently handle those tests, so just look for small values. */
1061 if (isprime == 2 && mpz_sizeinbase(n, 2) > 64) isprime = 1;
1062
1063 if (isprime == 2) {
1064 Newz(0, cert, 20 + mpz_sizeinbase(n, 10), char);
1065 gmp_sprintf(cert, "Type Small\nN %Zd\n", n);
1066 } else if (isprime == 1) {
1067 if (do_bpsw) {
1068 /* Done */
1069 } else if (do_nminus1) {
1070 isprime = _GMP_primality_bls_nm1(n, 100, &cert);
1071 } else if (do_nplus1) {
1072 isprime = _GMP_primality_bls_np1(n, 100, &cert);
1073 } else if (do_bls75) {
1074 isprime = bls75_hybrid(n, 100, &cert);
1075 } else if (do_aks) {
1076 isprime = 2 * is_aks_prime(n);
1077 do_printcert = 0;
1078 } else if (do_aprcl) {
1079 #ifdef USE_APRCL
1080 /* int i; for (i = 0; i < 10000; i++) */
1081 isprime = mpz_aprtcle(n, get_verbose_level());
1082 do_printcert = 0;
1083 #else
1084 croak("Compiled without USE_APRCL. Sorry.");
1085 #endif
1086 } else {
1087 if (!do_ecpp) {
1088 /* Quick n-1 test */
1089 isprime = _GMP_primality_bls_nm1(n, 1, &cert);
1090 }
1091 if (isprime == 1)
1092 isprime = _GMP_ecpp(n, &cert);
1093 }
1094 }
1095
1096 /* printf("(%d digit) ", (int)mpz_sizeinbase(n, 10)); */
1097 if (isprime == 0) {
1098 if (!be_quiet) printf("COMPOSITE\n");
1099 retcode = 1;
1100 } else if (isprime == 1) {
1101 /* This would normally only be from BPSW */
1102 if (!be_quiet) printf("PROBABLY PRIME\n");
1103 retcode = 2;
1104 } else if (isprime == 2) {
1105 if (do_printcert) {
1106 gmp_printf("[MPU - Primality Certificate]\n");
1107 gmp_printf("Version 1.0\n");
1108 gmp_printf("\n");
1109 gmp_printf("Proof for:\n");
1110 gmp_printf("N %Zd\n", n);
1111 gmp_printf("\n");
1112 printf("%s", cert);
1113 } else {
1114 if (!be_quiet) printf("PRIME\n");
1115 }
1116 retcode = 0;
1117 } else {
1118 /* E.g. APRCL returns -1 for error */
1119 croak("Unknown return code, probable error.\n");
1120 }
1121 if (cert != 0) {
1122 Safefree(cert);
1123 cert = 0;
1124 }
1125 }
1126 mpz_clear(n);
1127 _GMP_destroy();
1128 return retcode;
1129 }
1130 #endif
1131