1 #include "cado.h" // IWYU pragma: keep
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <gmp.h>
6 #include "macros.h"  // for ASSERT_ALWAYS, ASSERT
7 #include "params.h"
8 #include "cado_poly.h"
9 
10 /* Be conservative and allocate two polynomials by default. */
cado_poly_init(cado_poly_ptr poly)11 void cado_poly_init(cado_poly_ptr poly)
12 {
13     /* ALL fields are zero upon init, EXCEPT the degree field (which is -1) */
14     memset(poly, 0, sizeof(poly[0]));
15 
16     /* By default allocate 2 polynomials */
17     poly->nb_polys = 2;
18     for(int side = 0 ; side < poly->nb_polys ; side++)
19       mpz_poly_init (poly->pols[side], MAX_DEGREE);
20 
21     mpz_init_set_ui(poly->n, 0);
22 }
23 
cado_poly_clear(cado_poly_ptr poly)24 void cado_poly_clear(cado_poly_ptr poly)
25 {
26     for(int side = 0 ; side < poly->nb_polys ; side++)
27       mpz_poly_clear (poly->pols[side]);
28 
29     mpz_clear(poly->n);
30     memset(poly, 0, sizeof(poly[0]));
31 }
32 
33 /* p <- q */
34 void
cado_poly_set(cado_poly_ptr p,cado_poly_srcptr q)35 cado_poly_set (cado_poly_ptr p, cado_poly_srcptr q)
36 {
37     mpz_set (p->n, q->n);
38     p->skew = q->skew;
39     p->nb_polys = q->nb_polys;
40     for(int side = 0 ; side < q->nb_polys ; side++)
41       mpz_poly_set (p->pols[side], q->pols[side]);
42 }
43 
44 void
cado_poly_swap(cado_poly_ptr p,cado_poly_ptr q)45 cado_poly_swap (cado_poly_ptr p, cado_poly_ptr q)
46 {
47     mpz_swap (p->n, q->n);
48     p->skew = q->skew;
49     p->nb_polys = q->nb_polys;
50     for(int side = 0 ; side < q->nb_polys ; side++)
51       mpz_poly_swap (p->pols[side], q->pols[side]);
52 }
53 
54 // This function is no longer exported
55 #define BUF_MAX 10000
56 
57 // returns 0 on failure, 1 on success.
cado_poly_set_plist(cado_poly_ptr poly,param_list_ptr pl)58 int cado_poly_set_plist(cado_poly_ptr poly, param_list_ptr pl)
59 {
60   int ret = 1;
61 
62   /* Parse skew value. Set to 0.0 to ensure that we get an invalid skewness
63      in case it is not given */
64   poly->skew = 0.0;
65   param_list_parse_double(pl, "skew", &(poly->skew));
66 
67   /* Parse polynomials. Either in line format (poly%d=%Zd,%Zd,...) either given
68    * by coefficients. */
69   for(int i = 0; i < NB_POLYS_MAX; i++)
70   {
71     char tag[15], buf[BUF_MAX];
72     snprintf(tag, sizeof(tag), "poly%d", i);
73     if(param_list_parse_string(pl, tag, buf, BUF_MAX))
74     {
75       if(i >= 2) {
76         mpz_poly_init (poly->pols[i], MAX_DEGREE);
77       }
78       param_list_parse_mpz_poly(pl, tag, poly->pols[i]);
79       ASSERT(poly->pols[i]->deg <= MAX_DEGREE);
80     }
81     else
82     {
83       poly->nb_polys = i;
84       break;
85     }
86   }
87   if (poly->nb_polys == 0) /* No polynomials so far. Try other format. */
88   /* Convention: c or X means side 1 (often algebraic)
89    *             Y means side 0 (often rational) */
90   {
91     poly->nb_polys = 2;
92     /* reading polynomials coefficient by coefficient */
93     for (unsigned int i = 0; i <= MAX_DEGREE; i++)
94     {
95       char tag[4];
96       snprintf(tag, sizeof(tag), "c%d", i);
97       int b = param_list_parse_mpz(pl, tag, poly->pols[1]->coeff[i]);
98       if (!b)
99       {
100         snprintf(tag, sizeof(tag), "X%d", i);
101         param_list_parse_mpz(pl, tag, poly->pols[1]->coeff[i]);
102       }
103       snprintf(tag, sizeof(tag), "Y%d", i);
104       param_list_parse_mpz(pl, tag, poly->pols[0]->coeff[i]);
105     }
106   }
107   /* setting degrees */
108   for(int side = 0 ; side < poly->nb_polys ; side++)
109     mpz_poly_cleandeg(poly->pols[side], MAX_DEGREE);
110 
111   /* Parse value of N. Two keys possible: n or None. Return 0 if not found. */
112   if (!param_list_parse_mpz(pl, "n", poly->n) &&
113       !param_list_parse_mpz(pl, NULL, poly->n))
114   {
115     fprintf (stderr, "Error, no value for N in cado_poly_set_plist\n");
116     ret = 0;
117   }
118 
119   for (int side = 0; side < poly->nb_polys; side++)
120     if (poly->pols[side]->deg < 0)
121     {
122       fprintf (stderr, "Error, polynomial on side %u has degree < 0 in "
123                        "cado_poly_set_plist\n", side);
124       ret = 0;
125     }
126 
127   /* check that N divides the resultant*/
128   ASSERT_ALWAYS(cado_poly_check_mapping(NULL, poly, poly->n));
129 
130   return ret;
131 }
132 
133 // returns 0 on failure, 1 on success.
cado_poly_read_stream(cado_poly_ptr poly,FILE * f)134 int cado_poly_read_stream (cado_poly_ptr poly, FILE * f)
135 {
136   param_list pl;
137   param_list_init (pl);
138   param_list_read_stream (pl, f, 0);
139   int r = cado_poly_set_plist (poly, pl);
140   param_list_clear (pl);
141   return r;
142 }
143 
144 // returns 0 on failure, 1 on success.
cado_poly_read_next_poly_from_stream(cado_poly_ptr poly,FILE * f)145 int cado_poly_read_next_poly_from_stream (cado_poly_ptr poly, FILE * f)
146 {
147   int r;
148   param_list pl;
149   param_list_init (pl);
150   r = param_list_read_stream (pl, f, 1);
151   if (r && pl->size > 0)
152     r = cado_poly_set_plist (poly, pl);
153   else
154     r = 0;
155   param_list_clear (pl);
156   return r;
157 }
158 
159 // returns 0 on failure, 1 on success.
cado_poly_read(cado_poly_ptr poly,const char * filename)160 int cado_poly_read(cado_poly_ptr poly, const char *filename)
161 {
162     FILE *file;
163     int r;
164     file = fopen(filename, "r");
165     if (file == NULL)
166     {
167       fprintf(stderr, "Error, in cado_poly_read: could not open %s\n", filename);
168       return 0;
169     }
170     r = cado_poly_read_stream(poly, file);
171     fclose(file);
172     return r;
173 }
174 
175 /* check that there is a mapping from Z[x] to the target ring (Z/N,
176  * GF(p), GF(p^k)) that factors through each of the polynomials.
177  *
178  * If we have only two sides, rational or not, this is clearly something
179  * that we want, for NFS to make any sort of sense.
180  *
181  * The question of how to generalize this check to multiple polynomials
182  * is not entirely clear.
183  *
184  * If there is a rational side (only one, see cado_poly_get_ratside),
185  * having a resultant with the rational polynomial that is a multiple of
186  * N is actually a transitive relation, so that it suffices to check the
187  * condition with the rational side only.
188  *
189  * If we have multiple nonlinear polynomials, we have to make sure that
190  * there is a compatible mapping. This is done by checking the pseudo-gcd
191  * of the set of polynomials in cado_poly
192  *
193  * => The conclusion of all that is that what we really care about is the
194  * computation of the gcd, and this gcd is returned in the polynomial G.
195  * Note that if G is null, then we don't store it, but we do the check
196  * nevertheless.
197  *
198  * This returns true (1) on success, and false (0) on error, after an
199  * error message is printed to stderr.
200  */
cado_poly_check_mapping(mpz_poly_ptr G,cado_poly_srcptr cpoly,mpz_srcptr N)201 int cado_poly_check_mapping(mpz_poly_ptr G, cado_poly_srcptr cpoly,
202         mpz_srcptr N)
203 {
204     /* scratch space for mpz_poly_pseudogcd_mpz, which destroys its
205      * input.
206      */
207     mpz_poly S[2];
208     mpz_poly_init(S[0], -1);
209     mpz_poly_init(S[1], -1);
210 
211     /* a polynomial for the gcd */
212     mpz_poly G_loc;
213     mpz_poly_init(G_loc, -1);
214     mpz_poly_set_xi(G_loc, -1);
215 
216     /* factors of N, if any. This should never happen */
217     mpz_t factors_of_N, ftmp;
218     mpz_init_set_ui(factors_of_N, 1);
219     mpz_init_set_ui(ftmp, 0);
220 
221     /* A copy of N with all trivial factors removed */
222     mpz_t Nsmall;
223     mpz_init(Nsmall);
224 
225     for(int ret = 0 ; ret == 0; ) {
226         mpz_divexact(Nsmall, N, factors_of_N);
227 
228         /* compute all pseudo-gcds */
229         for(int i = 1 ; i < cpoly->nb_polys ; i++) {
230             mpz_poly_set(S[0], cpoly->pols[0]);
231             mpz_poly_set(S[1], cpoly->pols[i]);
232             ret = mpz_poly_pseudogcd_mpz (S[0], S[1], Nsmall, ftmp);
233             if (ret == 0) {
234                 mpz_mul(factors_of_N, factors_of_N, ftmp);
235                 break;
236             }
237             ret = mpz_poly_pseudogcd_mpz (G_loc, S[0], Nsmall, ftmp);
238             if (ret == 0) {
239                 mpz_mul(factors_of_N, factors_of_N, ftmp);
240                 break;
241             }
242         }
243 
244         /* If a factor was found, start over, otherwise we're done with
245          * this silly "restart if small factors pop up" loop (which will
246          * presumably never be activated anyway...)
247          */
248     }
249 
250     int found_mapping = G_loc->deg >= 1;
251 
252     if (G)
253         mpz_poly_set(G, G_loc);
254 
255     if (mpz_cmp_ui(factors_of_N, 1) != 0) {
256         /* I don't think that there's any reason to have a situation
257          * where N has non-trivial factors. If such a situation arises,
258          * it's easy enough to turn the error below into a warning./
259          */
260         gmp_fprintf(stderr, "Error, non-trivial factors (%Zd) of N were found while checking the poly file. This should not happen.\n", factors_of_N);
261         found_mapping = 0;
262     }
263 
264 
265     mpz_clear(Nsmall);
266     mpz_clear(ftmp);
267     mpz_clear(factors_of_N);
268     mpz_poly_clear(G_loc);
269     mpz_poly_clear(S[0]);
270     mpz_poly_clear(S[1]);
271 
272     if (!found_mapping) {
273         fprintf (stderr, "Error, we found no compatible mapping that factors through the given number fields. Your poly file is most probably wrong.\n");
274     }
275 
276     return found_mapping;
277 }
278 
279 
280 /* If NULL is passed for m, then, just check that N divides the resultant.
281  * Assume there at least two polynomials.
282  */
cado_poly_getm(mpz_ptr m,cado_poly_srcptr cpoly,mpz_srcptr N)283 int cado_poly_getm(mpz_ptr m, cado_poly_srcptr cpoly, mpz_srcptr N)
284 {
285     mpz_poly G;
286     mpz_poly_init(G, -1);
287 
288     ASSERT_ALWAYS(cado_poly_check_mapping(G, cpoly, N));
289 
290     /* It doesn't make sense to ask for m when the mapping goes to a
291      * larger structure. Note that this is not necessarily equivalent to
292      * having no rational side: Two algebraic sides may well have only a
293      * rational mapping in common (case of Joux-Lercier polynomial
294      * selection).
295      */
296 
297     if (m != NULL && mpz_poly_degree(G) != 1) {
298         fprintf(stderr, "Error, cannot determine m given that the common mapping is not linear\n");
299         exit (EXIT_FAILURE);
300     }
301 
302     if (m != NULL) {
303         mpz_t inv;
304         mpz_init(inv);
305         int ret2 = mpz_invert(inv, G->coeff[1], N);
306         // This inversion should always work.
307         // If not, it means that N has a small factor (not sure we want
308         // to be robust against that...). This should have been reported,
309         // at least as a warning, by cado_poly_check_mapping.
310         // Or maybe the polynomial selection was really bogus!
311         ASSERT_ALWAYS(ret2);
312         mpz_mul(inv, inv, G->coeff[0]);
313         mpz_neg(inv, inv);
314         mpz_mod(m, inv, N);
315         mpz_clear(inv);
316       }
317 
318     mpz_poly_clear(G);
319 
320     return 1;
321 }
322 
323 /* Return the rational side or -1 if only algebraic sides */
324 /* Assume that there is at most 1 rational side (renumber.cpp does the same
325  * assumption).
326  *
327  * Note that several distinct rational sides would not really make sense.
328  * A rational side prescribes the mapping from the indeterminate (the X
329  * in a-b*X) to the underlying ring (Z/N, GF(p), ...). So if we have
330  * distinct rational sides, we would have distinct mappings, and I don't
331  * think that we could do something useful in such a situation.
332  */
333 int
cado_poly_get_ratside(cado_poly_srcptr pol)334 cado_poly_get_ratside (cado_poly_srcptr pol)
335 {
336   int number_of_rational_sides = 0;
337   int ratside = -1;
338   for(int side = 0; side < pol->nb_polys; side++)
339     if(pol->pols[side]->deg == 1) {
340         ratside = side;
341         number_of_rational_sides++;
342     }
343   ASSERT_ALWAYS(number_of_rational_sides <= 1);
344   return ratside;
345 }
346 
347 void
cado_poly_fprintf(FILE * fp,cado_poly_srcptr poly,const char * prefix)348 cado_poly_fprintf (FILE *fp, cado_poly_srcptr poly, const char *prefix)
349 {
350   if (prefix)
351     fputs (prefix, fp);
352   gmp_fprintf (fp, "n: %Zd\n", poly->n);
353 
354   if (poly->nb_polys == 2)
355   {
356     mpz_poly_fprintf_cado_format (fp, poly->pols[0], 'Y', prefix);
357     mpz_poly_fprintf_cado_format (fp, poly->pols[1], 'c', prefix);
358   }
359   else
360   {
361     for (int side = 0; side < poly->nb_polys; side++)
362     {
363       if (prefix)
364         fputs (prefix, fp);
365       fprintf (fp, "poly%u=", side);
366       mpz_poly_fprintf_coeffs (fp, poly->pols[side], ',');
367     }
368   }
369 
370   if (prefix)
371     fputs (prefix, fp);
372   fprintf (fp, "skew: %1.3f\n", poly->skew);
373 }
374 
375 /* if exp_E = 0, print E = lognorm + alpha (root-optimized polynomial),
376    otherwise print exp_E */
377 void
cado_poly_fprintf_info(FILE * fp,double lognorm,double exp_E,double alpha,double alpha_proj,unsigned int nrroots,const char * prefix)378 cado_poly_fprintf_info (FILE *fp, double lognorm, double exp_E, double alpha,
379                         double alpha_proj, unsigned int nrroots,
380                         const char *prefix)
381 {
382   if (prefix)
383     fputs (prefix, fp);
384   /* Always print "# " after the prefix and before the info line. */
385   fprintf (fp, "# lognorm %1.2f, %s %1.2f, alpha %1.2f (proj %1.2f),"
386              " %u real root%s\n",
387              lognorm, (exp_E == 0) ? "E" : "exp_E",
388              (exp_E == 0) ? lognorm + alpha : exp_E, alpha, alpha_proj,
389              nrroots, (nrroots <= 1) ? "" : "s");
390 }
391 
392 void
cado_poly_fprintf_MurphyE(FILE * fp,double MurphyE,double bound_f,double bound_g,double area,const char * prefix)393 cado_poly_fprintf_MurphyE (FILE *fp, double MurphyE, double bound_f,
394                            double bound_g, double area, const char *prefix)
395 {
396   if (prefix)
397     fputs (prefix, fp);
398   /* Always print "# " after the prefix and before the MurphyE line. */
399   fprintf (fp, "# MurphyE(Bf=%.3e,Bg=%.3e,area=%.3e)=%.3e\n", bound_f, bound_g,
400                area, MurphyE);
401 }
402