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