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