FORM v5.0.1-33-gdf7fc94
flintinterface.cc
Go to the documentation of this file.
1
6/* #[ License : */
7/*
8 * Copyright (C) 1984-2026 J.A.M. Vermaseren
9 * When using this file you are requested to refer to the publication
10 * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
11 * This is considered a matter of courtesy as the development was paid
12 * for by FOM the Dutch physics granting agency and we would like to
13 * be able to track its scientific use to convince FOM of its value
14 * for the community.
15 *
16 * This file is part of FORM.
17 *
18 * FORM is free software: you can redistribute it and/or modify it under the
19 * terms of the GNU General Public License as published by the Free Software
20 * Foundation, either version 3 of the License, or (at your option) any later
21 * version.
22 *
23 * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
24 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
26 * details.
27 *
28 * You should have received a copy of the GNU General Public License along
29 * with FORM. If not, see <http://www.gnu.org/licenses/>.
30 */
31/* #] License : */
32
33#ifdef HAVE_CONFIG_H
34#ifndef CONFIG_H_INCLUDED
35#define CONFIG_H_INCLUDED
36#include <config.h>
37#endif
38#endif
39
40extern "C" {
41#include "form3.h"
42}
43
44#include "flintinterface.h"
45
46/*
47 #[ Types
48 * Use fixed-size integer types (int32_t, uint32_t, int64_t, uint64_t) except when we refer
49 * specifically to FORM term data, when we use WORD. This code has only been tested on 64bit
50 * systems where WORDs are int32_t, and some functions (fmpz_get_form, fmpz_set_form) require this
51 * to be the case. Enforce this:
52 */
53static_assert(sizeof(WORD) == sizeof(int32_t), "flint interface expects 32bit WORD");
54static_assert(sizeof(UWORD) == sizeof(uint32_t), "flint interface expects 32bit WORD");
55static_assert(BITSINWORD == 32, "flint interface expects 32bit WORD");
56static_assert(sizeof(ulong) == sizeof(uint64_t), "flint interface expects ulong is uint64_t");
57static_assert(sizeof(slong) == sizeof(int64_t), "flint interface expects slong is int64_t");
58
59/*
60 * Flint functions take arguments or return values which may be "slong" or "ulong" in its
61 * documentation, and these are int64_t and uint64_t respectively.
62 *
63 #] Types
64*/
65
66/*
67 * FLINT's univariate poly has a dense representation. For sufficiently sparse polynomials it is
68 * faster to use mpoly instead, which is sparse. For a density <= this threshold we switch, where
69 * the density defined as is "number of terms" / "maximum degree".
70 */
71#define UNIVARIATE_DENSITY_THR 0.02f
72
73/*
74 #[ flint::cleanup :
75*/
76void flint::cleanup(void) {
77 flint_cleanup();
78}
79/*
80 #] flint::cleanup :
81 #[ flint::cleanup_master :
82*/
83void flint::cleanup_master(void) {
84 flint_cleanup_master();
85}
86/*
87 #] flint::cleanup_master :
88
89 #[ flint::divmod_mpoly :
90*/
91WORD* flint::divmod_mpoly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
92 const WORD must_fit_term, const var_map_t &var_map, const bool sort_vars) {
93
94 flint::mpoly_ctx ctx(var_map.size());
95 flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
96
97 flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
98 flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
99
100 // The input won't have any symbols with negative powers, but there may be rational
101 // coefficients. Verify this:
102 if ( fmpz_mpoly_is_fmpz(denpa.d, ctx.d) != 1 ) {
103 // INTERNAL_ERROR_EXCL_START
104 MLOCK(ErrorMessageLock);
105 MesPrint("!>flint::divmod_mpoly: error: denpa is non-constant");
106 MUNLOCK(ErrorMessageLock);
107 Terminate(-1);
108 // INTERNAL_ERROR_EXCL_STOP
109 }
110 if ( fmpz_mpoly_is_fmpz(denpb.d, ctx.d) != 1 ) {
111 // INTERNAL_ERROR_EXCL_START
112 MLOCK(ErrorMessageLock);
113 MesPrint("!>flint::divmod_mpoly: error: denpb is non-constant");
114 MUNLOCK(ErrorMessageLock);
115 Terminate(-1);
116 // INTERNAL_ERROR_EXCL_STOP
117 }
118
119
120 flint::fmpz scale;
121 flint::mpoly div(ctx.d), rem(ctx.d);
122
123 fmpz_mpoly_quasidivrem(scale.d, div.d, rem.d, pa.d, pb.d, ctx.d);
124
125 // The quotient must be multiplied by the denominator of the divisor
126 fmpz_mpoly_mul(div.d, div.d, denpb.d, ctx.d);
127
128 // The overall denominator of both div and rem is given by scale*denpa. This we will pass to
129 // to_argument_mpoly's "denscale" argument which performs the division in the result. We have
130 // already checked denpa is just an fmpz.
131 fmpz_mul(scale.d, scale.d, fmpz_mpoly_term_coeff_ref(denpa.d, 0, ctx.d));
132
133
134 // Determine the size of the output by passing write = false.
135 const bool with_arghead = false;
136 bool write = false;
137 const uint64_t prev_size = 0;
138 const uint64_t out_size = return_rem ?
139 (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
140 rem.d, var_map, ctx.d, sort_vars, scale.d)
141 :
142 (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
143 div.d, var_map, ctx.d, sort_vars, scale.d)
144 ;
145 WORD* res = (WORD *)Malloc1(sizeof(WORD)*out_size, "flint::divrem_mpoly");
146
147
148 // Write out the result
149 write = true;
150 if ( return_rem ) {
151 flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
152 rem.d, var_map, ctx.d, sort_vars, scale.d);
153 }
154 else {
155 flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
156 div.d, var_map, ctx.d, sort_vars, scale.d);
157 }
158
159 return res;
160}
161/*
162 #] flint::divmod_mpoly :
163 #[ flint::divmod_poly :
164*/
165WORD* flint::divmod_poly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
166 const WORD must_fit_term, const var_map_t &var_map) {
167
168 flint::poly pa, pb, denpa, denpb;
169
170 flint::from_argument_poly(pa.d, denpa.d, a, false);
171 flint::from_argument_poly(pb.d, denpb.d, b, false);
172
173 // The input won't have any symbols with negative powers, but there may be rational
174 // coefficients. Verify this:
175 if ( fmpz_poly_length(denpa.d) != 1 ) {
176 // INTERNAL_ERROR_EXCL_START
177 MLOCK(ErrorMessageLock);
178 MesPrint("!>flint::divmod_poly: error: denpa is non-constant");
179 MUNLOCK(ErrorMessageLock);
180 Terminate(-1);
181 // INTERNAL_ERROR_EXCL_STOP
182 }
183 if ( fmpz_poly_length(denpb.d) != 1 ) {
184 // INTERNAL_ERROR_EXCL_START
185 MLOCK(ErrorMessageLock);
186 MesPrint("!>flint::divmod_poly: error: denpb is non-constant");
187 MUNLOCK(ErrorMessageLock);
188 Terminate(-1);
189 // INTERNAL_ERROR_EXCL_STOP
190 }
191
192 flint::fmpz scale;
193 uint64_t scale_power = 0;
194 flint::poly div, rem;
195
196 // Get the leading coefficient of pb. fmpz_poly_pseudo_divrem returns only the scaling power.
197 fmpz_poly_get_coeff_fmpz(scale.d, pb.d, fmpz_poly_degree(pb.d));
198
199 fmpz_poly_pseudo_divrem(div.d, rem.d, (ulong*)&scale_power, pa.d, pb.d);
200
201 // The quotient must be multiplied by the denominator of the divisor
202 fmpz_poly_mul(div.d, div.d, denpb.d);
203
204 // The overall denominator of both div and rem is given by scale^scale_power*denpa. This we will
205 // pass to to_argument_poly's "denscale" argument which performs the division in the result. We
206 // have already checked denpa is just an fmpz.
207 fmpz_pow_ui(scale.d, scale.d, scale_power);
208 fmpz_mul(scale.d, scale.d, fmpz_poly_get_coeff_ptr(denpa.d, 0));
209
210
211 // Determine the size of the output by passing write = false.
212 const bool with_arghead = false;
213 bool write = false;
214 const uint64_t prev_size = 0;
215 const uint64_t out_size = return_rem ?
216 (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
217 rem.d, var_map, scale.d)
218 :
219 (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
220 div.d, var_map, scale.d)
221 ;
222 WORD* res = (WORD *)Malloc1(sizeof(WORD)*out_size, "flint::divrem_poly");
223
224
225 // Write out the result
226 write = true;
227 if ( return_rem ) {
228 flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
229 rem.d, var_map, scale.d);
230 }
231 else {
232 flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
233 div.d, var_map, scale.d);
234 }
235
236 return res;
237}
238/*
239 #] flint::divmod_poly :
240
241 #[ flint::factorize_mpoly :
242*/
243WORD* flint::factorize_mpoly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
244 const bool is_fun_arg, const var_map_t &var_map, const bool sort_vars) {
245
246 flint::mpoly_ctx ctx(var_map.size());
247 flint::mpoly arg(ctx.d), den(ctx.d), base(ctx.d);
248
249 flint::from_argument_mpoly(arg.d, den.d, argin, with_arghead, var_map, ctx.d);
250 // The denominator must be 1:
251 if ( fmpz_mpoly_is_one(den.d, ctx.d) != 1 ) {
252 // INTERNAL_ERROR_EXCL_START
253 MLOCK(ErrorMessageLock);
254 MesPrint("!>flint::factorize_mpoly error: den != 1");
255 MUNLOCK(ErrorMessageLock);
256 Terminate(-1);
257 // INTERNAL_ERROR_EXCL_STOP
258 }
259
260
261 // Now we can factor the mpoly:
262 flint::mpoly_factor arg_fac(ctx.d);
263 fmpz_mpoly_factor(arg_fac.d, arg.d, ctx.d);
264 const int64_t num_factors = fmpz_mpoly_factor_length(arg_fac.d, ctx.d);
265
266 flint::fmpz overall_constant;
267 fmpz_mpoly_factor_get_constant_fmpz(overall_constant.d, arg_fac.d, ctx.d);
268 // FORM should always have taken the overall constant out in the content. Thus this overall
269 // constant factor should be +- 1 here. Verify this:
270 if ( ! ( fmpz_equal_si(overall_constant.d, 1) || fmpz_equal_si(overall_constant.d, -1) ) ) {
271 // INTERNAL_ERROR_EXCL_START
272 MLOCK(ErrorMessageLock);
273 MesPrint("!>flint::factorize_mpoly error: overall constant factor != +-1");
274 MUNLOCK(ErrorMessageLock);
275 Terminate(-1);
276 // INTERNAL_ERROR_EXCL_STOP
277 }
278
279 // Construct the output. If argout is not NULL, we write the result there.
280 // Otherwise, allocate memory.
281 // The output is zero-terminated list of factors. If with_arghead, each has an arghead which
282 // contains its size. Otherwise, the factors are zero separated.
283
284 // We only need to determine the size of the output if we are allocating memory, but we need to
285 // loop through the factors to fix their signs anyway. Do both together in one loop:
286
287 // Initially 1, for the final trailing 0.
288 uint64_t output_size = 1;
289
290 // For finding the highest symbol, in FORM's lexicographic ordering
291 vector<uint32_t> var_map_inv;
292 var_map_inv.resize(var_map.size());
293 for ( auto x : var_map ) {
294 var_map_inv[x.second] = x.first;
295 }
296
297 // Store whether we should flip the factor sign in the ouput:
298 vector<int32_t> base_sign(num_factors, 0);
299
300 for ( int64_t i = 0; i < num_factors; i++ ) {
301 fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
302 const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
303
304 // poly_factorize makes sure the highest power of the "highest symbol" (in FORM's
305 // lexicographic ordering) has a positive coefficient. Check this, update overall_constant
306 // of the factorization if necessary.
307 // Store the sign per factor, so that we can flip the signs in the output without re-checking
308 // the individual terms again.
309 uint32_t max_var = 0; // FORM symbols start at 20, 0 is a good initial value.
310 int32_t max_pow = -1;
311 vector<int64_t> base_term_exponents(var_map.size(), 0);
312
313 for ( int64_t j = 0; j < fmpz_mpoly_length(base.d, ctx.d); j++ ) {
314 fmpz_mpoly_get_term_exp_si((slong*)base_term_exponents.data(), base.d, (slong)j, ctx.d);
315
316 for ( size_t k = 0; k < var_map.size(); k++ ) {
317 if ( base_term_exponents[k] > 0 && ( var_map_inv[k] > max_var ||
318 ( var_map_inv[k] == max_var && base_term_exponents[k] > max_pow ) ) ) {
319
320 max_var = var_map_inv[k];
321 max_pow = base_term_exponents[k];
322 base_sign[i] = fmpz_sgn(fmpz_mpoly_term_coeff_ref(base.d, j, ctx.d));
323 }
324 }
325 }
326 // If this base's sign will be flipped an odd number of times, there is a contribution to
327 // the overall sign of the whole factorization:
328 if ( ( base_sign[i] == -1 ) && ( exponent % 2 == 1 ) ) {
329 fmpz_neg(overall_constant.d, overall_constant.d);
330 }
331
332 // Now determine the output size of the factor, if we are allocating the memory
333 if ( argout == NULL ) {
334 const bool write = false;
335 for ( int64_t j = 0; j < exponent; j++ ) {
336 output_size += (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead,
337 is_fun_arg, write, 0, base.d, var_map, ctx.d, sort_vars);
338 }
339 }
340 }
341 if ( fmpz_sgn(overall_constant.d) == -1 && argout == NULL ) {
342 // Add space for a fast-notation number or a normal-notation number and zero separator
343 output_size += with_arghead ? 2 : 4+1;
344 }
345
346 // Now make the allocation if necessary:
347 if ( argout == NULL ) {
348 argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_mpoly");
349 }
350
351
352 // And now comes the actual output:
353 WORD* old_argout = argout;
354
355 // If the overall sign is negative, first write a full-notation -1. It will be absorbed into the
356 // overall factor in the content by the caller.
357 if ( fmpz_sgn(overall_constant.d) == -1 ) {
358 if ( with_arghead ) {
359 // poly writes in fast notation in this case. Fast notation is expected by the caller, to
360 // properly merge it with the overall factor of the content.
361 *argout++ = -SNUMBER;
362 *argout++ = -1;
363 }
364 else {
365 *argout++ = 4; // term size
366 *argout++ = 1; // numerator
367 *argout++ = 1; // denominator
368 *argout++ = -3; // coeff size, negative number
369 *argout++ = 0; // factor separator
370 }
371 }
372
373 for ( int64_t i = 0; i < num_factors; i++ ) {
374 fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
375 const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
376
377 if ( base_sign[i] == -1 ) {
378 fmpz_mpoly_neg(base.d, base.d, ctx.d);
379 }
380
381 const bool write = true;
382 for ( int64_t j = 0; j < exponent; j++ ) {
383 argout += flint::to_argument_mpoly(BHEAD argout, with_arghead, is_fun_arg, write,
384 argout-old_argout, base.d, var_map, ctx.d, sort_vars);
385 }
386 }
387 // Final trailing zero to denote the end of the factors.
388 *argout++ = 0;
389
390 return old_argout;
391}
392/*
393 #] flint::factorize_mpoly :
394 #[ flint::factorize_poly :
395*/
396WORD* flint::factorize_poly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
397 const bool is_fun_arg, const var_map_t &var_map) {
398
399 flint::poly arg, den;
400
401 flint::from_argument_poly(arg.d, den.d, argin, with_arghead);
402 // The denominator must be 1:
403 if ( fmpz_poly_is_one(den.d) != 1 ) {
404 // INTERNAL_ERROR_EXCL_START
405 MLOCK(ErrorMessageLock);
406 MesPrint("!>flint::factorize_poly error: den != 1");
407 MUNLOCK(ErrorMessageLock);
408 Terminate(-1);
409 // INTERNAL_ERROR_EXCL_STOP
410 }
411
412
413 // Now we can factor the poly:
414 flint::poly_factor arg_fac;
415 fmpz_poly_factor(arg_fac.d, arg.d);
416 // fmpz_poly_factor_t lacks some convenience functions which fmpz_mpoly_factor_t has.
417 // I have worked out how to get the factors by looking at how fmpz_poly_factor_print works.
418 const long num_factors = (arg_fac.d)->num;
419
420
421 // Construct the output. If argout is not NULL, we write the result there.
422 // Otherwise, allocate memory.
423 // The output is zero-terminated list of factors. If with_arghead, each has an arghead which
424 // contains its size. Otherwise, the factors are zero separated.
425 if ( argout == NULL ) {
426 // First we need to determine the size of the output. This is the same procedure as the
427 // loop below, but we don't write anything in to_argument_poly (write arg: false).
428 // Initially 1, for the final trailing 0.
429 uint64_t output_size = 1;
430
431 // If the overall constant is not 1, make an fmpz_poly out of it and include it. Typically
432 // this does not happen: FORM takes out the overall factor. But we do sometimes have this
433 // case, for eg awkward interactions with "On highfirst;" and non-dirty arguments.
434 if ( ! fmpz_is_one(&(arg_fac.d)->c) ) {
435 flint::poly overall_factor;
436 fmpz_poly_set_fmpz(overall_factor.d, &(arg_fac.d)->c);
437 const bool write = false;
438 output_size += (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, is_fun_arg,
439 write, 0, overall_factor.d, var_map);
440 }
441
442 for ( long i = 0; i < num_factors; i++ ) {
443 fmpz_poly_struct* base = (arg_fac.d)->p + i;
444
445 const long exponent = (arg_fac.d)->exp[i];
446
447 const bool write = false;
448 for ( long j = 0; j < exponent; j++ ) {
449 output_size += (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead,
450 is_fun_arg, write, 0, base, var_map);
451 }
452 }
453
454 argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_poly");
455 }
456
457 WORD* old_argout = argout;
458
459 // If the overall constant is not 1, make an fmpz_poly out of it and include it. Typically
460 // this does not happen: FORM takes out the overall factor. But we do sometimes have this
461 // case, for eg awkward interactions with "On highfirst;" and non-dirty arguments.
462 if ( ! fmpz_is_one(&(arg_fac.d)->c) ) {
463 flint::poly overall_factor;
464 fmpz_poly_set_fmpz(overall_factor.d, &(arg_fac.d)->c);
465 const bool write = true;
466 argout += (uint64_t)flint::to_argument_poly(BHEAD argout, with_arghead, is_fun_arg, write,
467 argout-old_argout, overall_factor.d, var_map);
468 }
469
470 for ( long i = 0; i < num_factors; i++ ) {
471 fmpz_poly_struct* base = (arg_fac.d)->p + i;
472
473 const long exponent = (arg_fac.d)->exp[i];
474
475 const bool write = true;
476 for ( long j = 0; j < exponent; j++ ) {
477 argout += flint::to_argument_poly(BHEAD argout, with_arghead, is_fun_arg, write,
478 argout-old_argout, base, var_map);
479 }
480 }
481 *argout = 0;
482
483 return old_argout;
484}
485/*
486 #] flint::factorize_poly :
487
488 #[ flint::form_sort :
489*/
490// Sort terms using form's sorting routines. Uses a custom (faster) compare routine, since here
491// only symbols can appear.
492// This is a modified poly_sort from polywrap.cc.
493void flint::form_sort(PHEAD WORD *terms) {
494
495 if ( terms[0] < 0 ) {
496 // Fast notation, there is nothing to do
497 return;
498 }
499
500 const WORD oldsorttype = AR.SortType;
501 AR.SortType = SORTHIGHFIRST;
502
503 const WORD in_size = terms[0] - ARGHEAD;
504 WORD out_size;
505
506 if ( NewSort(BHEAD0) ) {
507 Terminate(-1);
508 }
509 AR.CompareRoutine = (COMPAREDUMMY)(&CompareSymbols);
510
511 // Make sure the symbols are in the right order within the terms
512 for ( WORD i = ARGHEAD; i < terms[0]; i += terms[i] ) {
513 if ( SymbolNormalize(terms+i) < 0 || StoreTerm(BHEAD terms+i) ) {
514 AR.SortType = oldsorttype;
515 AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
517 Terminate(-1);
518 }
519 }
520
521 if ( ( out_size = EndSort(BHEAD terms+ARGHEAD, 1) ) < 0 ) {
522 AR.SortType = oldsorttype;
523 AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
524 Terminate(-1);
525 }
526
527 // Check the final size
528 if ( in_size != out_size ) {
529 // INTERNAL_ERROR_EXCL_START
530 MLOCK(ErrorMessageLock);
531 MesPrint("!>flint::form_sort: error: unexpected sorted arg length change %d->%d", in_size,
532 out_size);
533 MUNLOCK(ErrorMessageLock);
534 Terminate(-1);
535 // INTERNAL_ERROR_EXCL_STOP
536 }
537
538 AR.SortType = oldsorttype;
539 AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
540 terms[1] = 0; // set dirty flag to zero
541}
542/*
543 #] flint::form_sort :
544
545 #[ flint::from_argument_mpoly :
546*/
547// Convert a FORM argument (or 0-terminated list of terms: with_arghead == false) to a
548// (multi-variate) fmpz_mpoly_t poly. The "denominator" is return in denpoly, and contains the
549// overall negative-power numeric and symbolic factor.
550uint64_t flint::from_argument_mpoly(fmpz_mpoly_t poly, fmpz_mpoly_t denpoly, const WORD *args,
551 const bool with_arghead, const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx) {
552
553 // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
554 // Make sure they are 0 to begin.
555 fmpz_mpoly_set_si(poly, 0, ctx);
556 fmpz_mpoly_set_si(denpoly, 0, ctx);
557
558 // First check for "fast notation" arguments:
559 if ( *args == -SNUMBER ) {
560 fmpz_mpoly_set_si(poly, *(args+1), ctx);
561 fmpz_mpoly_set_si(denpoly, 1, ctx);
562 return 2;
563 }
564
565 if ( *args == -SYMBOL ) {
566 // A "fast notation" SYMBOL has a power and coefficient of 1:
567 vector<uint64_t> exponents(var_map.size(), 0);
568 exponents[var_map.at(*(args+1))] = 1;
569
570 fmpz_mpoly_set_coeff_ui_ui(poly, (ulong)1, (ulong*)exponents.data(), ctx);
571 fmpz_mpoly_set_ui(denpoly, (ulong)1, ctx);
572 return 2;
573 }
574
575
576 // Now we can iterate through the terms of the argument. If we have
577 // an ARGHEAD, we already know where to terminate. Otherwise we'll have
578 // to loop until the terminating 0.
579 const WORD* arg_stop = with_arghead ? args+args[0] : (WORD*)UINT64_MAX;
580 uint64_t arg_size = 0;
581 if ( with_arghead ) {
582 arg_size = args[0];
583 args += ARGHEAD;
584 }
585
586
587 // Search for numerical or symbol denominators to create "denpoly".
588 flint::fmpz den_coeff, tmp;
589 fmpz_set_si(den_coeff.d, 1);
590 vector<uint64_t> exponents(var_map.size(), 0);
591 vector<uint64_t> neg_exponents(var_map.size(), 0);
592
593 for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
594 const WORD* term_stop = term+term[0];
595 const WORD coeff_size = (term_stop)[-1];
596 const WORD* symbol_stop = term_stop - ABS(coeff_size);
597 const WORD* t = term;
598
599 t++;
600 if ( t == symbol_stop ) {
601 // Just a number, no symbols
602 }
603 else {
604 t++; // this entry is SYMBOL
605 t++; // this entry just has the size of the symbol array, but we can use symbol_stop
606
607 for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
608 if ( *(s+1) < 0 ) {
609 neg_exponents[var_map.at(*s)] =
610 MaX(neg_exponents[var_map.at(*s)], (uint64_t)(-(*(s+1))) );
611 }
612 }
613 }
614
615 // Now check for a denominator in the coefficient:
616 if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
617 flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
618 // Record the LCM of the coefficient denominators:
619 fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
620 }
621
622 if ( !with_arghead && *term_stop == 0 ) {
623 // + 1 for the terminating 0
624 arg_size = term_stop - args + 1;
625 break;
626 }
627
628 }
629 // Assemble denpoly.
630 fmpz_mpoly_set_coeff_fmpz_ui(denpoly, den_coeff.d, (ulong*)neg_exponents.data(), ctx);
631
632
633 // For the term coefficients
634 flint::fmpz coeff;
635
636 for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
637 const WORD* term_stop = term+term[0];
638 const WORD coeff_size = (term_stop)[-1];
639 const WORD* symbol_stop = term_stop - ABS(coeff_size);
640 const WORD* t = term;
641
642 fill(exponents.begin(), exponents.end(), 0);
643
644 t++; // skip over the total size entry
645 if ( t == symbol_stop ) {
646 // Just a number, no symbols
647 }
648 else {
649 t++; // this entry is SYMBOL
650 t++; // this entry just has the size of the symbol array, but we can use symbol_stop
651 for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
652 exponents[var_map.at(*s)] = *(s+1);
653 }
654 }
655 // Now read the coefficient
656 flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
657
658 // Multiply by denominator LCM
659 fmpz_mul(coeff.d, coeff.d, den_coeff.d);
660
661 // Shift by neg_exponents
662 for ( size_t i = 0; i < var_map.size(); i++ ) {
663 exponents[i] += neg_exponents[i];
664 }
665
666 // Read the denominator if there is one, and divide it out of the coefficient
667 if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
668 flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
669 // By construction, this is an exact division
670 fmpz_divexact(coeff.d, coeff.d, tmp.d);
671 }
672
673 // Push the term to the mpoly, remember to sort when finished! This is much faster than using
674 // fmpz_mpoly_set_coeff_fmpz_ui when the terms arrive in the "wrong order".
675 fmpz_mpoly_push_term_fmpz_ui(poly, coeff.d, (ulong*)exponents.data(), ctx);
676
677 if ( !with_arghead && *term_stop == 0 ) {
678 break;
679 }
680
681 }
682 // And now sort the mpoly if necessary:
683 if ( ! fmpz_mpoly_is_canonical(poly, ctx) ) {
684 fmpz_mpoly_sort_terms(poly, ctx);
685 }
686
687 return arg_size;
688}
689/*
690 #] flint::from_argument_mpoly :
691 #[ flint::from_argument_poly :
692*/
693// Convert a FORM argument (or 0-terminated list of terms: with_arghead == false) to a
694// (uni-variate) fmpz_poly_t poly. The "denominator" is return in denpoly, and contains the
695// overall negative-power numeric and symbolic factor.
696uint64_t flint::from_argument_poly(fmpz_poly_t poly, fmpz_poly_t denpoly, const WORD *args,
697 const bool with_arghead) {
698
699 // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
700 // Make sure they are 0 to begin.
701 fmpz_poly_set_si(poly, 0);
702 fmpz_poly_set_si(denpoly, 0);
703
704 // First check for "fast notation" arguments:
705 if ( *args == -SNUMBER ) {
706 fmpz_poly_set_si(poly, *(args+1));
707 fmpz_poly_set_si(denpoly, 1);
708 return 2;
709 }
710
711 if ( *args == -SYMBOL ) {
712 // A "fast notation" SYMBOL has a power and coefficient of 1:
713 fmpz_poly_set_coeff_si(poly, 1, 1);
714 fmpz_poly_set_si(denpoly, 1);
715 return 2;
716 }
717
718 // Now we can iterate through the terms of the argument. If we have
719 // an ARGHEAD, we already know where to terminate. Otherwise we'll have
720 // to loop until the terminating 0.
721 const WORD* arg_stop = with_arghead ? args+args[0] : (WORD*)UINT64_MAX;
722 uint64_t arg_size = 0;
723 if ( with_arghead ) {
724 arg_size = args[0];
725 args += ARGHEAD;
726 }
727
728 // Search for numerical or symbol denominators to create "denpoly".
729 flint::fmpz den_coeff, tmp;
730 fmpz_set_si(den_coeff.d, 1);
731 uint64_t neg_exponent = 0;
732
733 for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
734
735 const WORD* term_stop = term+term[0];
736 const WORD coeff_size = (term_stop)[-1];
737 const WORD* symbol_stop = term_stop - ABS(coeff_size);
738 const WORD* t = term;
739
740 t++; // skip over the total size entry
741 if ( t == symbol_stop ) {
742 // Just a number, no symbols
743 }
744 else {
745 t++; // this entry is SYMBOL
746 t++; // this entry is the size of the symbol array
747 t++; // this is the first (and only) symbol code
748 if ( *t < 0 ) {
749 neg_exponent = MaX(neg_exponent, (uint64_t)(-(*t)) );
750 }
751 }
752
753 // Now check for a denominator in the coefficient:
754 if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
755 flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
756 // Record the LCM of the coefficient denominators:
757 fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
758 }
759
760 if ( *term_stop == 0 ) {
761 // + 1 for the terminating 0
762 arg_size = term_stop - args + 1;
763 break;
764 }
765 }
766 // Assemble denpoly.
767 fmpz_poly_set_coeff_fmpz(denpoly, neg_exponent, den_coeff.d);
768
769
770 // For the term coefficients
771 flint::fmpz coeff;
772
773 for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
774
775 const WORD* term_stop = term+term[0];
776 const WORD coeff_size = (term_stop)[-1];
777 const WORD* symbol_stop = term_stop - ABS(coeff_size);
778 const WORD* t = term;
779
780 uint64_t exponent = 0;
781
782 t++; // skip over the total size entry
783 if ( t == symbol_stop ) {
784 // Just a number, no symbols
785 }
786 else {
787 t++; // this entry is SYMBOL
788 t++; // this entry is the size of the symbol array
789 t++; // this is the first (and only) symbol code
790 exponent = *t++;
791 }
792
793 // Now read the coefficient
794 flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
795
796 // Multiply by denominator LCM
797 fmpz_mul(coeff.d, coeff.d, den_coeff.d);
798
799 // Shift by neg_exponent
800 exponent += neg_exponent;
801
802 // Read the denominator if there is one, and divide it out of the coefficient
803 if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
804 flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
805 // By construction, this is an exact division
806 fmpz_divexact(coeff.d, coeff.d, tmp.d);
807 }
808
809 // Add the term to the poly
810 fmpz_poly_set_coeff_fmpz(poly, exponent, coeff.d);
811
812 if ( *term_stop == 0 ) {
813 break;
814 }
815 }
816
817 return arg_size;
818}
819/*
820 #] flint::from_argument_poly :
821
822 #[ flint::fmpz_get_form :
823*/
824// Write FORM's long integer representation of an fmpz at a, and put the number of WORDs at na.
825// na carries the sign of the integer.
826WORD flint::fmpz_get_form(fmpz_t z, WORD *a) {
827
828 WORD na = 0;
829 const int32_t sgn = fmpz_sgn(z);
830 if ( sgn == -1 ) {
831 fmpz_neg(z, z);
832 }
833 const int64_t nlimbs = fmpz_size(z);
834
835 // This works but is UB?
836 //fmpz_get_ui_array(reinterpret_cast<uint64_t*>(a), nlimbs, z);
837
838 // Use fixed-size functions to get limb data where possible. These probably cover most real
839 // cases.
840 if ( nlimbs == 1 ) {
841 const uint64_t limb = fmpz_get_ui(z);
842 a[0] = (WORD)(limb & 0xFFFFFFFF);
843 na++;
844 a[1] = (WORD)(limb >> BITSINWORD);
845 if ( a[1] != 0 ) {
846 na++;
847 }
848 }
849 else if ( nlimbs == 2 ) {
850 uint64_t limb_hi = 0, limb_lo = 0;
851 fmpz_get_uiui((ulong*)&limb_hi, (ulong*)&limb_lo, z);
852 a[0] = (WORD)(limb_lo & 0xFFFFFFFF);
853 na++;
854 a[1] = (WORD)(limb_lo >> BITSINWORD);
855 na++;
856 a[2] = (WORD)(limb_hi & 0xFFFFFFFF);
857 na++;
858 a[3] = (WORD)(limb_hi >> BITSINWORD);
859 if ( a[3] != 0 ) {
860 na++;
861 }
862 }
863 else {
864 vector<uint64_t> limb_data(nlimbs, 0);
865 fmpz_get_ui_array((ulong*)limb_data.data(), (slong)nlimbs, z);
866 for ( long i = 0; i < nlimbs; i++ ) {
867 a[2*i] = (WORD)(limb_data[i] & 0xFFFFFFFF);
868 na++;
869 a[2*i+1] = (WORD)(limb_data[i] >> BITSINWORD);
870 if ( a[2*i+1] != 0 || i < (nlimbs-1) ) {
871 // The final limb might fit in a single 32bit WORD. Only
872 // increment na if the final WORD is non zero.
873 na++;
874 }
875 }
876 }
877
878 // And now put the sign in the number of limbs
879 if ( sgn == -1 ) {
880 na = -na;
881 }
882
883 return na;
884}
885/*
886 #] flint::fmpz_get_form :
887 #[ flint::fmpz_set_form :
888*/
889// Create an fmpz directly from FORM's long integer representation. fmpz uses 64bit unsigned limbs,
890// but FORM uses 32bit UWORDs on 64bit architectures so we can't use fmpz_set_ui_array directly.
891void flint::fmpz_set_form(fmpz_t z, UWORD *a, WORD na) {
892
893 if ( na == 0 ) {
894 fmpz_zero(z);
895 return;
896 }
897
898 // Negative na represenents a negative number
899 int32_t sgn = 1;
900 if ( na < 0 ) {
901 sgn = -1;
902 na = -na;
903 }
904
905 // Remove padding. FORM stores numerators and denominators with equal numbers of limbs but we
906 // don't need to do this within the fmpz. It is not necessary to do this really, the fmpz
907 // doesn't add zero limbs unnecessarily, but we might be able to avoid creating the limb_data
908 // array below.
909 while ( a[na-1] == 0 ) {
910 na--;
911 }
912
913 // If the number fits in fixed-size fmpz_set functions, we don't need to use additional memory
914 // to convert to uint64_t. These probably cover most real cases.
915 if ( na == 1 ) {
916 fmpz_set_ui(z, (uint64_t)a[0]);
917 }
918 else if ( na == 2 ) {
919 fmpz_set_ui(z, (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
920 }
921 else if ( na == 3 ) {
922 fmpz_set_uiui(z, (uint64_t)a[2], (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
923 }
924 else if ( na == 4 ) {
925 fmpz_set_uiui(z, (((uint64_t)a[3])<<BITSINWORD) + (uint64_t)a[2],
926 (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
927 }
928 else {
929 const int32_t nlimbs = (na+1)/2;
930 vector<uint64_t> limb_data(nlimbs, 0);
931 for ( int32_t i = 0; i < nlimbs; i++ ) {
932 if ( 2*i+1 <= na-1 ) {
933 limb_data[i] = (uint64_t)a[2*i] + (((uint64_t)a[2*i+1])<<BITSINWORD);
934 }
935 else {
936 limb_data[i] = (uint64_t)a[2*i];
937 }
938 }
939 fmpz_set_ui_array(z, (ulong*)limb_data.data(), nlimbs);
940 }
941
942 // Finally set the sign.
943 if ( sgn == -1 ) {
944 fmpz_neg(z, z);
945 }
946
947 return;
948}
949/*
950 #] flint::fmpz_set_form :
951
952 #[ flint::gcd_mpoly :
953*/
954// Return a pointer to a buffer containing the GCD of the 0-terminated term lists at a and b.
955// If must_fit_term, this should be a TermMalloc buffer. Otherwise Malloc1 the buffer.
956// For multi-variate cases.
957WORD* flint::gcd_mpoly(PHEAD const WORD *a, const WORD *b, const WORD must_fit_term,
958 const var_map_t &var_map, const bool sort_vars) {
959
960 flint::mpoly_ctx ctx(var_map.size());
961 flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d), gcd(ctx.d);
962
963 flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
964 flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
965
966 // denpa, denpb must be 1:
967 if ( fmpz_mpoly_is_one(denpa.d, ctx.d) != 1 ) {
968 // INTERNAL_ERROR_EXCL_START
969 MLOCK(ErrorMessageLock);
970 MesPrint("!>flint::gcd_mpoly: error: denpa != 1");
971 MUNLOCK(ErrorMessageLock);
972 Terminate(-1);
973 // INTERNAL_ERROR_EXCL_STOP
974 }
975 if ( fmpz_mpoly_is_one(denpb.d, ctx.d) != 1 ) {
976 // INTERNAL_ERROR_EXCL_START
977 MLOCK(ErrorMessageLock);
978 MesPrint("!>flint::gcd_mpoly: error: denpb != 1");
979 MUNLOCK(ErrorMessageLock);
980 Terminate(-1);
981 // INTERNAL_ERROR_EXCL_STOP
982 }
983
984 // poly returns pa if pa == pb, regardless of the lcoeff sign
985 if ( fmpz_mpoly_equal(pa.d, pb.d, ctx.d) ) {
986 fmpz_mpoly_set(gcd.d, pa.d, ctx.d);
987 }
988 else {
989 // We need some gymnastics to have the same sign conventions as the poly class. It takes the
990 // integer or univar content out of a,b, with the convention that the content sign matches
991 // the lcoeff sign. Since FORM has already taken out the content, we are left with +-1. In
992 // Flint, the content always has a positive sign so here we should find +1. Check this:
993 flint::mpoly tmp(ctx.d);
994 fmpz_mpoly_term_content(tmp.d, pa.d, ctx.d);
995 if ( fmpz_mpoly_is_one(tmp.d, ctx.d) != 1 ) {
996 // INTERNAL_ERROR_EXCL_START
997 MLOCK(ErrorMessageLock);
998 MesPrint("!>flint::gcd_mpoly: error: content of 1st arg != 1");
999 MUNLOCK(ErrorMessageLock);
1000 Terminate(-1);
1001 // INTERNAL_ERROR_EXCL_STOP
1002 }
1003 fmpz_mpoly_term_content(tmp.d, pb.d, ctx.d);
1004 if ( fmpz_mpoly_is_one(tmp.d, ctx.d) != 1 ) {
1005 // INTERNAL_ERROR_EXCL_START
1006 MLOCK(ErrorMessageLock);
1007 MesPrint("!>flint::gcd_mpoly: error: content of 2nd arg != 1");
1008 MUNLOCK(ErrorMessageLock);
1009 Terminate(-1);
1010 // INTERNAL_ERROR_EXCL_STOP
1011 }
1012
1013 // The poly class now divides the content out of a,b so that they have a positive lcoeff.
1014 // Then it multiplies the final gcd (which is given a positive lcoeff also) by
1015 // gcd(cont a, cont b). There it has gcd(1,1) = gcd(-1,1) = gcd(1,-1) = 1, and
1016 // gcd(-1,-1) = -1 (because of the pa==pb early return). So: if both input polys have a
1017 // negative lcoeff, we will flip the sign in the final result.
1018 bool flip_sign = 0;
1019 if ( ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(pa.d, 0, ctx.d)) == -1 ) &&
1020 ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(pb.d, 0, ctx.d)) == -1 ) ) {
1021 flip_sign = 1;
1022 }
1023
1024 fmpz_mpoly_gcd(gcd.d, pa.d, pb.d, ctx.d);
1025 if ( flip_sign ) {
1026 fmpz_mpoly_neg(gcd.d, gcd.d, ctx.d);
1027 }
1028 }
1029
1030 // This is freed by the caller
1031 WORD *res;
1032 if ( must_fit_term ) {
1033 res = TermMalloc("flint::gcd_mpoly");
1034 }
1035 else {
1036 // Determine the size of the GCD by passing write = false.
1037 const bool with_arghead = false;
1038 const bool write = false;
1039 const uint64_t prev_size = 0;
1040 const uint64_t gcd_size = (uint64_t)flint::to_argument_mpoly(BHEAD NULL,
1041 with_arghead, must_fit_term, write, prev_size, gcd.d, var_map, ctx.d, sort_vars);
1042
1043 res = (WORD *)Malloc1(sizeof(WORD)*gcd_size, "flint::gcd_mpoly");
1044 }
1045
1046 const bool with_arghead = false;
1047 const bool write = true;
1048 const uint64_t prev_size = 0;
1049 flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
1050 var_map, ctx.d, sort_vars);
1051
1052 return res;
1053}
1054/*
1055 #] flint::gcd_mpoly :
1056 #[ flint::gcd_poly :
1057*/
1058// Return a pointer to a buffer containing the GCD of the 0-terminated term lists at a and b.
1059// If must_fit_term, this should be a TermMalloc buffer. Otherwise Malloc1 the buffer.
1060// For uni-variate cases.
1061WORD* flint::gcd_poly(PHEAD const WORD *a, const WORD *b, const WORD must_fit_term,
1062 const var_map_t &var_map) {
1063
1064 flint::poly pa, pb, denpa, denpb, gcd;
1065
1066 flint::from_argument_poly(pa.d, denpa.d, a, false);
1067 flint::from_argument_poly(pb.d, denpb.d, b, false);
1068
1069 // denpa, denpb must be 1:
1070 if ( fmpz_poly_is_one(denpa.d) != 1 ) {
1071 // INTERNAL_ERROR_EXCL_START
1072 MLOCK(ErrorMessageLock);
1073 MesPrint("!>flint::gcd_poly: error: denpa != 1");
1074 MUNLOCK(ErrorMessageLock);
1075 Terminate(-1);
1076 // INTERNAL_ERROR_EXCL_STOP
1077 }
1078 if ( fmpz_poly_is_one(denpb.d) != 1 ) {
1079 // INTERNAL_ERROR_EXCL_START
1080 MLOCK(ErrorMessageLock);
1081 MesPrint("!>flint::gcd_poly: error: denpb != 1");
1082 MUNLOCK(ErrorMessageLock);
1083 Terminate(-1);
1084 // INTERNAL_ERROR_EXCL_STOP
1085 }
1086
1087 // poly returns pa if pa == pb, regardless of the lcoeff sign
1088 if ( fmpz_poly_equal(pa.d, pb.d) ) {
1089 fmpz_poly_set(gcd.d, pa.d);
1090 }
1091 else {
1092 // Here, we don't have to make any sign flips like the mpoly case, because poly's
1093 // integer_gcd(1,1) = integer_gcd(-1,1) = integer_gcd(1,-1) = integer_gcd(-1,-1) = +1.
1094 // Still, verify that the content is 1:
1095 flint::fmpz tmp;
1096 fmpz_poly_content(tmp.d, pa.d);
1097 if ( fmpz_is_one(tmp.d) != 1 ) {
1098 // INTERNAL_ERROR_EXCL_START
1099 MLOCK(ErrorMessageLock);
1100 MesPrint("!>flint::gcd_poly: error: content of 1st arg != 1");
1101 MUNLOCK(ErrorMessageLock);
1102 Terminate(-1);
1103 // INTERNAL_ERROR_EXCL_STOP
1104 }
1105 fmpz_poly_content(tmp.d, pb.d);
1106 if ( fmpz_is_one(tmp.d) != 1 ) {
1107 // INTERNAL_ERROR_EXCL_START
1108 MLOCK(ErrorMessageLock);
1109 MesPrint("!>flint::gcd_poly: error: content of 2nd arg != 1");
1110 MUNLOCK(ErrorMessageLock);
1111 Terminate(-1);
1112 // INTERNAL_ERROR_EXCL_STOP
1113 }
1114
1115 fmpz_poly_gcd(gcd.d, pa.d, pb.d);
1116 }
1117
1118 // This is freed by the caller
1119 WORD *res;
1120 if ( must_fit_term ) {
1121 res = TermMalloc("flint::gcd_poly");
1122 }
1123 else {
1124 // Determine the size of the GCD by passing write = false.
1125 const bool with_arghead = false;
1126 const bool write = false;
1127 const uint64_t prev_size = 0;
1128 const uint64_t gcd_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1129 with_arghead, must_fit_term, write, prev_size, gcd.d, var_map);
1130
1131 res = (WORD *)Malloc1(sizeof(WORD)*gcd_size, "flint::gcd_poly");
1132 }
1133
1134 const bool with_arghead = false;
1135 const uint64_t prev_size = 0;
1136 const bool write = true;
1137 flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
1138 var_map);
1139
1140 return res;
1141}
1142/*
1143 #] flint::gcd_poly :
1144
1145 #[ flint::get_variables :
1146*/
1147// Get the list of symbols which appear in the vector of expressions. These are polyratfun
1148// numerators, denominators or expressions from calls to gcd_ etc. Return this list as a map
1149// between indices and symbol codes.
1150// TODO FACTORSYMBOL last?
1151flint::var_map_t flint::get_variables(const vector <WORD *> &es, const bool with_arghead,
1152 const bool sort_vars) {
1153
1154 int32_t num_vars = 0;
1155 // We count the total number of terms to determine "density".
1156 uint32_t num_terms = 0;
1157 // To be used if we sort by highest degree, as the poly code does.
1158 vector<int32_t> degrees;
1159 var_map_t var_map;
1160
1161 // extract all variables
1162 for ( size_t ei = 0; ei < es.size(); ei++ ) {
1163 WORD *e = es[ei];
1164
1165 // fast notation
1166 if ( *e == -SNUMBER ) {
1167 num_terms++;
1168 }
1169 else if ( *e == -SYMBOL ) {
1170 num_terms++;
1171 if ( !var_map.count(e[1]) ) {
1172 var_map[e[1]] = num_vars++;
1173 degrees.push_back(1);
1174 }
1175 }
1176 // JD: Here we need to check for non-symbol/number terms in fast notation.
1177 else if ( *e < 0 ) {
1178 MLOCK(ErrorMessageLock);
1179 MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
1180 MUNLOCK(ErrorMessageLock);
1181 Terminate(1);
1182 }
1183 else {
1184 for ( WORD i = with_arghead ? ARGHEAD:0; with_arghead ? i < e[0]:e[i] != 0; i += e[i] ) {
1185 num_terms++;
1186 const WORD coeff_size = e[i+e[i]-1];
1187 const WORD symbols_size = e[i] - ABS(coeff_size);
1188 if ( e[i+1] != SYMBOL && 1 < symbols_size ) {
1189 MLOCK(ErrorMessageLock);
1190 MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
1191 MUNLOCK(ErrorMessageLock);
1192 Terminate(1);
1193 }
1194
1195 for ( WORD j = i+3; j < i+symbols_size; j += 2 ) {
1196 const WORD symbol = e[j];
1197 const WORD degree = e[j+1];
1198 auto it = var_map.find(symbol);
1199 if ( it == var_map.end() ) {
1200 var_map[symbol] = num_vars++;
1201 degrees.push_back(degree);
1202 }
1203 else {
1204 degrees[it->second] = MaX(degrees[it->second], degree);
1205 }
1206 }
1207 }
1208 }
1209 }
1210
1211 if ( sort_vars ) {
1212 // bubble sort variables in decreasing order of degree
1213 // (this seems better for factorization)
1214 for ( size_t i = 0; i < var_map.size(); i++ ) {
1215 for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
1216 if ( degrees[j] < degrees[j+1] ) {
1217 swap(degrees[j], degrees[j+1]);
1218
1219 // Find the map keys associated with the values we want to swap
1220 uint32_t j0 = 0;
1221 uint32_t j1 = 0;
1222 for ( auto x: var_map ) {
1223 if ( x.second == j ) {
1224 j0 = x.first;
1225 }
1226 else if ( x.second == j+1 ) {
1227 j1 = x.first;
1228 }
1229 }
1230 swap(var_map.at(j0), var_map.at(j1));
1231 }
1232 }
1233 }
1234 }
1235 // Otherwise, sort lexicographically in FORM's ordering
1236 else {
1237 for ( size_t i = 0; i < var_map.size(); i++ ) {
1238 for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
1239 uint32_t j0 = 0;
1240 uint32_t j1 = 0;
1241 for ( auto x: var_map ) {
1242 if ( x.second == j ) {
1243 j0 = x.first;
1244 }
1245 else if ( x.second == j+1 ) {
1246 j1 = x.first;
1247 }
1248 }
1249 if ( j0 > j1 ) {
1250 swap(var_map.at(j0), var_map.at(j1));
1251 }
1252 }
1253 }
1254 }
1255
1256 if ( var_map.size() == 1 ) {
1257 // In the univariate case, if the polynomials are sufficiently sparse force the use of the
1258 // multivariate routines, which use a sparse representation, by adding a dummy map entry.
1259 if ( (float)num_terms <= UNIVARIATE_DENSITY_THR * (float)degrees[0] ) {
1260 // -1 will never be a symbol code. Built-in symbols from 0 to 19, and 20 is the first
1261 // user symbol.
1262 var_map[-1] = num_vars;
1263 }
1264 }
1265
1266 return var_map;
1267}
1268/*
1269 #] flint::get_variables :
1270
1271 #[ flint::inverse_poly :
1272*/
1273WORD* flint::inverse_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1274
1275 flint::poly pa, pb, denpa, denpb;
1276
1277 flint::from_argument_poly(pa.d, denpa.d, a, false);
1278 flint::from_argument_poly(pb.d, denpb.d, b, false);
1279
1280 // fmpz_poly_xgcd is undefined if the content of pa and pb are not 1. Take the content out.
1281 // fmpz_poly_content gives the non-negative content and fmpz_poly_primitive normalizes to a
1282 // non-negative lcoeff, so we need to add the sign to the content if the polys have a negative
1283 // lcoeff. We don't need to keep the content of pb, it is a numerical multiple of the modulus.
1284 flint::fmpz content_a, resultant;
1285 flint::poly inverse, tmp;
1286 fmpz_poly_content(content_a.d, pa.d);
1287 if ( fmpz_sgn(fmpz_poly_lead(pa.d)) == -1 ) {
1288 fmpz_neg(content_a.d, content_a.d);
1289 }
1290 fmpz_poly_primitive_part(pa.d, pa.d);
1291 fmpz_poly_primitive_part(pb.d, pb.d);
1292
1293 // Special cases:
1294 // Possibly strange that we give 1 for inverse_(x1,1) but here we take MMA's convention.
1295 if ( fmpz_poly_is_one(pa.d) && fmpz_poly_is_one(pb.d) ) {
1296 fmpz_poly_one(inverse.d);
1297 fmpz_one(resultant.d);
1298 }
1299 else if ( fmpz_poly_is_one(pb.d) ) {
1300 fmpz_poly_zero(inverse.d);
1301 fmpz_one(resultant.d);
1302 }
1303 else {
1304 // Now use the extended Euclidean algorithm to find inverse, resultant, tmp of the Bezout
1305 // identity: inverse*pa + tmp*pb = resultant. Then inverse/resultant is the multiplicative
1306 // inverse of pa mod pb. We'll divide by resultant in the denscale argument of to_argument_poly.
1307 fmpz_poly_xgcd(resultant.d, inverse.d, tmp.d, pa.d, pb.d);
1308
1309 // If the resultant is zero, the inverse does not exist:
1310 if ( fmpz_is_zero(resultant.d) ) {
1311 MLOCK(ErrorMessageLock);
1312 MesPrint("flint::inverse_poly error: inverse does not exist");
1313 MUNLOCK(ErrorMessageLock);
1314 Terminate(-1);
1315 }
1316 }
1317
1318 // Multiply inverse by denpa. denpb is a numerical multiple of the modulus, so doesn't matter.
1319 // We also need to divide by content_a, which we do in the denscale argument of to_argument_poly
1320 // by multiplying resultant by content_a here.
1321 fmpz_poly_mul(inverse.d, inverse.d, denpa.d);
1322 fmpz_mul(resultant.d, resultant.d, content_a.d);
1323
1324
1325 WORD* res;
1326 // First determine the result size, and malloc. The result should have no arghead. Here we use
1327 // the "scale" argument of to_argument_poly since resultant might not be 1.
1328 const bool with_arghead = false;
1329 bool write = false;
1330 const bool must_fit_term = false;
1331 const uint64_t prev_size = 0;
1332 const uint64_t res_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1333 with_arghead, must_fit_term, write, prev_size, inverse.d, var_map, resultant.d);
1334 res = (WORD*)Malloc1(sizeof(WORD)*res_size, "flint::inverse_poly");
1335
1336 write = true;
1337 flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, inverse.d,
1338 var_map, resultant.d);
1339
1340 return res;
1341}
1342/*
1343 #] flint::inverse_poly :
1344
1345 #[ flint::mul_mpoly :
1346*/
1347// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1348// For multi-variate cases.
1349WORD* flint::mul_mpoly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map,
1350 const bool sort_vars) {
1351
1352 flint::mpoly_ctx ctx(var_map.size());
1353 flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
1354
1355 flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
1356 flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
1357
1358 // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1359 if ( fmpz_mpoly_is_fmpz(denpa.d, ctx.d) != 1 ) {
1360 // INTERNAL_ERROR_EXCL_START
1361 MLOCK(ErrorMessageLock);
1362 MesPrint("!>flint::mul_mpoly: error: denpa is non-constant");
1363 MUNLOCK(ErrorMessageLock);
1364 Terminate(-1);
1365 // INTERNAL_ERROR_EXCL_STOP
1366 }
1367 if ( fmpz_mpoly_is_fmpz(denpb.d, ctx.d) != 1 ) {
1368 // INTERNAL_ERROR_EXCL_START
1369 MLOCK(ErrorMessageLock);
1370 MesPrint("!>flint::mul_mpoly: error: denpb is non-constant");
1371 MUNLOCK(ErrorMessageLock);
1372 Terminate(-1);
1373 // INTERNAL_ERROR_EXCL_STOP
1374 }
1375
1376 // Multiply numerators, store result in pa
1377 fmpz_mpoly_mul(pa.d, pa.d, pb.d, ctx.d);
1378 // Multiply denominators, store result in denpa, and convert to an fmpz:
1379 fmpz_mpoly_mul(denpa.d, denpa.d, denpb.d, ctx.d);
1380 flint::fmpz den;
1381 fmpz_mpoly_get_fmpz(den.d, denpa.d, ctx.d);
1382
1383 WORD* res;
1384 // First determine the result size, and malloc. The result should have no arghead. Here we use
1385 // the "scale" argument of to_argument_mpoly since den might not be 1.
1386 const bool with_arghead = false;
1387 bool write = false;
1388 const bool must_fit_term = false;
1389 const uint64_t prev_size = 0;
1390 const uint64_t mul_size = (uint64_t)flint::to_argument_mpoly(BHEAD NULL,
1391 with_arghead, must_fit_term, write, prev_size, pa.d, var_map, ctx.d, sort_vars, den.d);
1392 res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_mpoly");
1393
1394 write = true;
1395 flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
1396 var_map, ctx.d, sort_vars, den.d);
1397
1398 return res;
1399}
1400/*
1401 #] flint::mul_mpoly :
1402 #[ flint::mul_poly :
1403*/
1404// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1405// For uni-variate cases.
1406WORD* flint::mul_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1407
1408 flint::poly pa, pb, denpa, denpb;
1409
1410 flint::from_argument_poly(pa.d, denpa.d, a, false);
1411 flint::from_argument_poly(pb.d, denpb.d, b, false);
1412
1413 // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1414 if ( fmpz_poly_degree(denpa.d) != 0 ) {
1415 // INTERNAL_ERROR_EXCL_START
1416 MLOCK(ErrorMessageLock);
1417 MesPrint("!>flint::mul_poly: error: denpa is non-constant");
1418 MUNLOCK(ErrorMessageLock);
1419 Terminate(-1);
1420 // INTERNAL_ERROR_EXCL_STOP
1421 }
1422 if ( fmpz_poly_degree(denpb.d) != 0 ) {
1423 // INTERNAL_ERROR_EXCL_START
1424 MLOCK(ErrorMessageLock);
1425 MesPrint("!>flint::mul_poly: error: denpb is non-constant");
1426 MUNLOCK(ErrorMessageLock);
1427 Terminate(-1);
1428 // INTERNAL_ERROR_EXCL_STOP
1429 }
1430
1431 // Multiply numerators, store result in pa
1432 fmpz_poly_mul(pa.d, pa.d, pb.d);
1433 // Multiply denominators, store result in denpa, and convert to an fmpz:
1434 fmpz_poly_mul(denpa.d, denpa.d, denpb.d);
1435 flint::fmpz den;
1436 fmpz_poly_get_coeff_fmpz(den.d, denpa.d, 0);
1437
1438 WORD* res;
1439 // First determine the result size, and malloc. The result should have no arghead. Here we use
1440 // the "scale" argument of to_argument_poly since den might not be 1.
1441 const bool with_arghead = false;
1442 bool write = false;
1443 const bool must_fit_term = false;
1444 const uint64_t prev_size = 0;
1445 const uint64_t mul_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1446 with_arghead, must_fit_term, write, prev_size, pa.d, var_map, den.d);
1447 res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_poly");
1448
1449 write = true;
1450 flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
1451 var_map, den.d);
1452
1453 return res;
1454}
1455/*
1456 #] flint::mul_poly :
1457
1458 #[ flint::ratfun_add_mpoly :
1459*/
1460// Add the multi-variate FORM rational polynomials at t1 and t2. The result is written at out.
1461void flint::ratfun_add_mpoly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
1462 const var_map_t &var_map, const bool sort_vars) {
1463
1464 flint::mpoly_ctx ctx(var_map.size());
1465 flint::mpoly gcd(ctx.d), num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d);
1466
1467 flint::ratfun_read_mpoly(t1, num1.d, den1.d, var_map, ctx.d);
1468 flint::ratfun_read_mpoly(t2, num2.d, den2.d, var_map, ctx.d);
1469
1470 if ( fmpz_mpoly_cmp(den1.d, den2.d, ctx.d) != 0 ) {
1471 fmpz_mpoly_gcd_cofactors(gcd.d, den1.d, den2.d, den1.d, den2.d, ctx.d);
1472
1473 fmpz_mpoly_mul(num1.d, num1.d, den2.d, ctx.d);
1474 fmpz_mpoly_mul(num2.d, num2.d, den1.d, ctx.d);
1475
1476 fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
1477 fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
1478 fmpz_mpoly_mul(den1.d, den1.d, gcd.d, ctx.d);
1479 }
1480 else {
1481 fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
1482 }
1483
1484 // Finally divide out any common factors between the resulting num1, den1:
1485 fmpz_mpoly_gcd_cofactors(gcd.d, num1.d, den1.d, num1.d, den1.d, ctx.d);
1486
1487 flint::util::fix_sign_fmpz_mpoly_ratfun(num1.d, den1.d, ctx.d);
1488
1489 // Result in FORM notation:
1490 *out++ = AR.PolyFun;
1491 WORD* args_size = out++;
1492 WORD* args_flag = out++;
1493 *args_flag = 0; // clean prf
1494 FILLFUN3(out); // Remainder of funhead, if it is larger than 3
1495
1496 const bool with_arghead = true;
1497 const bool must_fit_term = true;
1498 const bool write = true;
1499 // prev_size + 4, to account for final term size and coeff of "1/1"
1500 out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
1501 num1.d, var_map, ctx.d, sort_vars);
1502 out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
1503 den1.d, var_map, ctx.d, sort_vars);
1504
1505 *args_size = out - args_size + 1; // The +1 is to include the function ID
1506 AT.WorkPointer = out;
1507}
1508/*
1509 #] flint::ratfun_add_mpoly :
1510 #[ flint::ratfun_add_poly :
1511*/
1512// Add the uni-variate FORM rational polynomials at t1 and t2. The result is written at out.
1513void flint::ratfun_add_poly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
1514 const var_map_t &var_map) {
1515
1516 flint::poly gcd, num1, den1, num2, den2;
1517
1518 flint::ratfun_read_poly(t1, num1.d, den1.d);
1519 flint::ratfun_read_poly(t2, num2.d, den2.d);
1520
1521 if ( fmpz_poly_equal(den1.d, den2.d) == 0 ) {
1522 flint::util::simplify_fmpz_poly(den1.d, den2.d, gcd.d);
1523
1524 fmpz_poly_mul(num1.d, num1.d, den2.d);
1525 fmpz_poly_mul(num2.d, num2.d, den1.d);
1526
1527 fmpz_poly_add(num1.d, num1.d, num2.d);
1528 fmpz_poly_mul(den1.d, den1.d, den2.d);
1529 fmpz_poly_mul(den1.d, den1.d, gcd.d);
1530 }
1531 else {
1532 fmpz_poly_add(num1.d, num1.d, num2.d);
1533 }
1534
1535 // Finally divide out any common factors between the resulting num1, den1:
1536 flint::util::simplify_fmpz_poly(num1.d, den1.d, gcd.d);
1537
1538 flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
1539
1540 // Result in FORM notation:
1541 *out++ = AR.PolyFun;
1542 WORD* args_size = out++;
1543 WORD* args_flag = out++;
1544 *args_flag = 0; // clean prf
1545 FILLFUN3(out); // Remainder of funhead, if it is larger than 3
1546
1547 const bool with_arghead = true;
1548 const bool must_fit_term = true;
1549 const bool write = true;
1550 // prev_size + 4, to account for final term size and coeff of "1/1"
1551 out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
1552 num1.d, var_map);
1553 out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
1554 den1.d, var_map);
1555
1556 *args_size = out - args_size + 1; // The +1 is to include the function ID
1557 AT.WorkPointer = out;
1558}
1559/*
1560 #] flint::ratfun_add_poly :
1561
1562 #[ flint::ratfun_normalize_mpoly :
1563*/
1564// Multiply and simplify occurrences of the multi-variate FORM rational polynomials found in term.
1565// The final term is written in place, with the rational polynomial at the end.
1566void flint::ratfun_normalize_mpoly(PHEAD WORD *term, const var_map_t &var_map,
1567 const bool sort_vars) {
1568
1569 // The length of the coefficient
1570 const WORD ncoeff = (term + *term)[-1];
1571 // The end of the term data, before the coefficient:
1572 const WORD *tstop = term + *term - ABS(ncoeff);
1573
1574 flint::mpoly_ctx ctx(var_map.size());
1575 flint::mpoly num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d), gcd(ctx.d);
1576
1577 // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1578 flint::fmpz tmpNum, tmpDen;
1579 flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
1580 flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
1581 fmpz_mpoly_set_fmpz(num1.d, tmpNum.d, ctx.d);
1582 fmpz_mpoly_set_fmpz(den1.d, tmpDen.d, ctx.d);
1583
1584 // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1585 // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1586 // go at the end.
1587 WORD* term_size = term;
1588 WORD* s = term + 1;
1589 for ( WORD *t = term + 1; t < tstop; ) {
1590 if ( *t == AR.PolyFun ) {
1591 flint::ratfun_read_mpoly(t, num2.d, den2.d, var_map, ctx.d);
1592
1593 // get gcd of num1,den2 and num2,den1 and then assemble
1594 fmpz_mpoly_gcd_cofactors(gcd.d, num1.d, den2.d, num1.d, den2.d, ctx.d);
1595 fmpz_mpoly_gcd_cofactors(gcd.d, num2.d, den1.d, num2.d, den1.d, ctx.d);
1596
1597 fmpz_mpoly_mul(num1.d, num1.d, num2.d, ctx.d);
1598 fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
1599
1600 t += t[1];
1601 }
1602
1603 else {
1604 // Not a PolyFun, just copy or skip over
1605 WORD i = t[1];
1606 if ( s != t ) { NCOPY(s,t,i); }
1607 else { t += i; s += i; }
1608 }
1609 }
1610
1611 flint::util::fix_sign_fmpz_mpoly_ratfun(num1.d, den1.d, ctx.d);
1612
1613 // Result in FORM notation:
1614 WORD* out = s;
1615 *out++ = AR.PolyFun;
1616 WORD* args_size = out++;
1617 WORD* args_flag = out++;
1618 *args_flag &= ~MUSTCLEANPRF;
1619
1620 const bool with_arghead = true;
1621 const bool must_fit_term = true;
1622 const bool write = true;
1623 out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
1624 num1.d, var_map, ctx.d, sort_vars);
1625 out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
1626 den1.d, var_map, ctx.d, sort_vars);
1627
1628 *args_size = out - args_size + 1; // The +1 is to include the function ID
1629
1630 // +3 for the coefficient of 1/1, which is added after the check
1631 if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
1632 MLOCK(ErrorMessageLock);
1633 MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
1634 MUNLOCK(ErrorMessageLock);
1635 Terminate(-1);
1636 }
1637
1638 *out++ = 1;
1639 *out++ = 1;
1640 *out++ = 3; // the term's coefficient is now 1/1
1641
1642 *term_size = out - term_size;
1643}
1644/*
1645 #] flint::ratfun_normalize_mpoly :
1646 #[ flint::ratfun_normalize_poly :
1647*/
1648// Multiply and simplify occurrences of the uni-variate FORM rational polynomials found in term.
1649// The final term is written in place, with the rational polynomial at the end.
1650void flint::ratfun_normalize_poly(PHEAD WORD *term, const var_map_t &var_map) {
1651
1652 // The length of the coefficient
1653 const WORD ncoeff = (term + *term)[-1];
1654 // The end of the term data, before the coefficient:
1655 const WORD *tstop = term + *term - ABS(ncoeff);
1656
1657 flint::poly num1, den1, num2, den2, gcd;
1658
1659 // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1660 flint::fmpz tmpNum, tmpDen;
1661 flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
1662 flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
1663 fmpz_poly_set_fmpz(num1.d, tmpNum.d);
1664 fmpz_poly_set_fmpz(den1.d, tmpDen.d);
1665
1666 // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1667 // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1668 // go at the end.
1669 WORD* term_size = term;
1670 WORD* s = term + 1;
1671 for ( WORD *t = term + 1; t < tstop; ) {
1672 if ( *t == AR.PolyFun ) {
1673 flint::ratfun_read_poly(t, num2.d, den2.d);
1674
1675 // get gcd of num1,den2 and num2,den1 and then assemble
1676 flint::util::simplify_fmpz_poly(num1.d, den2.d, gcd.d);
1677 flint::util::simplify_fmpz_poly(num2.d, den1.d, gcd.d);
1678
1679 fmpz_poly_mul(num1.d, num1.d, num2.d);
1680 fmpz_poly_mul(den1.d, den1.d, den2.d);
1681
1682 t += t[1];
1683 }
1684
1685 else {
1686 // Not a PolyFun, just copy or skip over
1687 WORD i = t[1];
1688 if ( s != t ) { NCOPY(s,t,i); }
1689 else { t += i; s += i; }
1690 }
1691 }
1692
1693 flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
1694
1695 // Result in FORM notation:
1696 WORD* out = s;
1697 *out++ = AR.PolyFun;
1698 WORD* args_size = out++;
1699 WORD* args_flag = out++;
1700 *args_flag &= ~MUSTCLEANPRF;
1701
1702 const bool with_arghead = true;
1703 const bool must_fit_term = true;
1704 const bool write = true;
1705 out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
1706 num1.d, var_map);
1707 out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
1708 den1.d, var_map);
1709
1710 *args_size = out - args_size + 1; // The +1 is to include the function ID
1711
1712 // +3 for the coefficient of 1/1, which is added after the check
1713 if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
1714 MLOCK(ErrorMessageLock);
1715 MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
1716 MUNLOCK(ErrorMessageLock);
1717 Terminate(-1);
1718 }
1719
1720 *out++ = 1;
1721 *out++ = 1;
1722 *out++ = 3; // the term's coefficient is now 1/1
1723
1724 *term_size = out - term_size;
1725}
1726/*
1727 #] flint::ratfun_normalize_poly :
1728
1729 #[ flint::ratfun_read_mpoly :
1730*/
1731// Read the multi-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1732// denominator.
1733void flint::ratfun_read_mpoly(const WORD *a, fmpz_mpoly_t num, fmpz_mpoly_t den,
1734 const var_map_t &var_map, fmpz_mpoly_ctx_t ctx) {
1735
1736 // The end of the arguments:
1737 const WORD* arg_stop = a+a[1];
1738
1739 const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
1740
1741 a += FUNHEAD;
1742 if ( a >= arg_stop ) {
1743 MLOCK(ErrorMessageLock);
1744 MesPrint("ERROR: PolyRatFun cannot have zero arguments");
1745 MUNLOCK(ErrorMessageLock);
1746 Terminate(-1);
1747 }
1748
1749 // Polys to collect the "den of the num" and "den of the den".
1750 // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1751 flint::mpoly den_num(ctx), den_den(ctx);
1752
1753 // Read the numerator
1754 flint::from_argument_mpoly(num, den_num.d, a, true, var_map, ctx);
1755 NEXTARG(a);
1756
1757 if ( a < arg_stop ) {
1758 // Read the denominator
1759 flint::from_argument_mpoly(den, den_den.d, a, true, var_map, ctx);
1760 NEXTARG(a);
1761 }
1762 else {
1763 // The denominator is 1
1764/* INTERNAL_ERROR_EXCL_START */
1765 MLOCK(ErrorMessageLock);
1766 MesPrint("!>implement this");
1767 MUNLOCK(ErrorMessageLock);
1768 Terminate(-1);
1769/* INTERNAL_ERROR_EXCL_STOP */
1770 }
1771 if ( a < arg_stop ) {
1772 MLOCK(ErrorMessageLock);
1773 MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
1774 MUNLOCK(ErrorMessageLock);
1775 Terminate(-1);
1776 }
1777
1778 // Multiply the num by den_den and den by den_num:
1779 fmpz_mpoly_mul(num, num, den_den.d, ctx);
1780 fmpz_mpoly_mul(den, den, den_num.d, ctx);
1781
1782 if ( must_normalize ) {
1783 flint::mpoly gcd(ctx);
1784 fmpz_mpoly_gcd_cofactors(gcd.d, num, den, num, den, ctx);
1785 }
1786}
1787/*
1788 #] flint::ratfun_read_mpoly :
1789 #[ flint::ratfun_read_poly :
1790*/
1791// Read the uni-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1792// denominator.
1793void flint::ratfun_read_poly(const WORD *a, fmpz_poly_t num, fmpz_poly_t den) {
1794
1795 // The end of the arguments:
1796 const WORD* arg_stop = a+a[1];
1797
1798 const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
1799
1800 a += FUNHEAD;
1801 if ( a >= arg_stop ) {
1802 MLOCK(ErrorMessageLock);
1803 MesPrint("ERROR: PolyRatFun cannot have zero arguments");
1804 MUNLOCK(ErrorMessageLock);
1805 Terminate(-1);
1806 }
1807
1808 // Polys to collect the "den of the num" and "den of the den".
1809 // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1810 flint::poly den_num, den_den;
1811
1812 // Read the numerator
1813 flint::from_argument_poly(num, den_num.d, a, true);
1814 NEXTARG(a);
1815
1816 if ( a < arg_stop ) {
1817 // Read the denominator
1818 flint::from_argument_poly(den, den_den.d, a, true);
1819 NEXTARG(a);
1820 }
1821 else {
1822 // The denominator is 1
1823/* INTERNAL_ERROR_EXCL_START */
1824 MLOCK(ErrorMessageLock);
1825 MesPrint("!>implement this");
1826 MUNLOCK(ErrorMessageLock);
1827 Terminate(-1);
1828/* INTERNAL_ERROR_EXCL_STOP */
1829 }
1830 if ( a < arg_stop ) {
1831 MLOCK(ErrorMessageLock);
1832 MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
1833 MUNLOCK(ErrorMessageLock);
1834 Terminate(-1);
1835 }
1836
1837 // Multiply the num by den_den and den by den_num:
1838 fmpz_poly_mul(num, num, den_den.d);
1839 fmpz_poly_mul(den, den, den_num.d);
1840
1841 if ( must_normalize ) {
1842 flint::poly gcd;
1843 flint::util::simplify_fmpz_poly(num, den, gcd.d);
1844 }
1845}
1846/*
1847 #] flint::ratfun_read_poly :
1848 #[ flint::to_argument_mpoly :
1849*/
1850// Convert a fmpz_mpoly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
1851// If the caller is building an output term, prev_size contains the size of the term so far, to
1852// check that the output fits in AM.MaxTer if must_fit_term.
1853// All coefficients will be divided by denscale (which might just be 1).
1854// If write is false, we never write to out but only track the total would-be size. This lets this
1855// function be repurposed as a "size of FORM notation" function without duplicating the code.
1856#define IFW(x) { if ( write ) {x;} }
1857uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
1858 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
1859 const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const bool sort_vars,
1860 const fmpz_t denscale) {
1861
1862 // out is modified later, keep the pointer at entry
1863 const WORD* out_entry = out;
1864
1865 // Track the total size written. We could do this with pointer differences, but if
1866 // write == false we don't write to or move out to be able to find the size that way.
1867 uint64_t ws = 0;
1868
1869 // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1870 if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
1871 MLOCK(ErrorMessageLock);
1872 MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
1873 MUNLOCK(ErrorMessageLock);
1874 Terminate(-1);
1875 }
1876
1877 // Create the inverse of var_map, so we don't have to search it for each symbol written
1878 vector<uint32_t> var_map_inv;
1879 var_map_inv.resize(var_map.size());
1880 for ( auto x : var_map ) {
1881 var_map_inv[x.second] = x.first;
1882 }
1883
1884 vector<int64_t> exponents(var_map.size());
1885 const int64_t n_terms = fmpz_mpoly_length(poly, ctx);
1886
1887 if ( n_terms == 0 ) {
1888 if ( with_arghead ) {
1889 IFW(*out++ = -SNUMBER); ws++;
1890 IFW(*out++ = 0); ws++;
1891 return ws;
1892 }
1893 else {
1894 IFW(*out++ = 0); ws++;
1895 return ws;
1896 }
1897 }
1898
1899 // For dividing out denscale
1900 flint::fmpz coeff, den, gcd;
1901
1902 // The mpoly might be constant or a single symbol with coeff 1. Use fast notation if possible.
1903 if ( with_arghead && n_terms == 1 ) {
1904
1905 if ( fmpz_mpoly_is_fmpz(poly, ctx) ) {
1906 // The mpoly is constant. Use fast notation if the number is an integer and small enough:
1907
1908 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
1909 fmpz_set(den.d, denscale);
1910 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1911
1912 if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
1913 const int64_t fast_coeff = fmpz_get_si(coeff.d);
1914 // While ">=", could work here, FORM does not use fast notation for INT_MIN
1915 if ( fast_coeff > INT32_MIN && fast_coeff <= INT32_MAX ) {
1916 IFW(*out++ = -SNUMBER); ws++;
1917 IFW(*out++ = (WORD)fast_coeff); ws++;
1918 return ws;
1919 }
1920 }
1921 }
1922
1923 else {
1924 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
1925 fmpz_set(den.d, denscale);
1926 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1927
1928 if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
1929 // The coefficient is one. Now check the symbol powers:
1930
1931 fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, 0, ctx);
1932 int64_t use_fast = 0;
1933 uint32_t fast_symbol = 0;
1934
1935 for ( size_t i = 0; i < var_map.size(); i++ ) {
1936 if ( exponents[i] == 1 ) fast_symbol = var_map_inv[i];
1937 use_fast += exponents[i];
1938 }
1939
1940 // use_fast has collected the total degree. If it is 1, then fast_symbol holds the code
1941 if ( use_fast == 1 ) {
1942 IFW(*out++ = -SYMBOL); ws++;
1943 IFW(*out++ = fast_symbol); ws++;
1944 return ws;
1945 }
1946 }
1947 }
1948 }
1949
1950
1951 WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_mpoly");
1952 WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
1953
1954 WORD* arg_size = 0;
1955 WORD* arg_flag = 0;
1956 if ( with_arghead ) {
1957 IFW(arg_size = out++); ws++; // total arg size
1958 IFW(arg_flag = out++); ws++;
1959 IFW(*arg_flag = 0); // clean argument
1960 }
1961
1962 for ( int64_t i = 0; i < n_terms; i++ ) {
1963
1964 fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, i, ctx);
1965
1966 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, i, ctx);
1967 fmpz_set(den.d, denscale);
1968 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1969
1970 uint32_t num_symbols = 0;
1971 for ( size_t j = 0; j < var_map.size(); j++ ) {
1972 if ( exponents[j] != 0 ) { num_symbols += 1; }
1973 }
1974
1975 // Convert the coefficient, write in temporary space
1976 const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
1977 const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
1978 const WORD coeff_size = [num_size, den_size] () -> WORD {
1979 WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
1980 return size * SGN(num_size) * SGN(den_size);
1981 }();
1982
1983 // Now we have the number of symbols and the coeff size, we can determine the output size.
1984 // Check it fits if necessary: term size, num,den of "coeff_size", +1 for total coeff size
1985 uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
1986 if ( num_symbols ) {
1987 // symbols header, code,power of each symbol:
1988 current_size += 2 + 2*num_symbols;
1989 }
1990 if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
1991 MLOCK(ErrorMessageLock);
1992 MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
1993 MUNLOCK(ErrorMessageLock);
1994 Terminate(-1);
1995 }
1996
1997 WORD* term_size = 0;
1998 IFW(term_size = out++); ws++;
1999 if ( num_symbols ) {
2000 IFW(*out++ = SYMBOL); ws++;
2001 WORD* symbol_size = 0;
2002 IFW(symbol_size = out++); ws++;
2003 IFW(*symbol_size = 2);
2004
2005 for ( size_t j = 0; j < var_map.size(); j++ ) {
2006 if ( exponents[j] != 0 ) {
2007 IFW(*out++ = var_map_inv[j]); ws++;
2008 IFW(*out++ = exponents[j]); ws++;
2009 IFW(*symbol_size += 2);
2010 }
2011 }
2012 }
2013
2014 // Copy numerator
2015 for ( WORD j = 0; j < ABS(num_size); j++ ) {
2016 IFW(*out++ = tmp_coeff[j]); ws++;
2017 }
2018 for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
2019 IFW(*out++ = 0); ws++;
2020 }
2021 // Copy denominator
2022 for ( WORD j = 0; j < ABS(den_size); j++ ) {
2023 IFW(*out++ = tmp_den[j]); ws++;
2024 }
2025 for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
2026 IFW(*out++ = 0); ws++;
2027 }
2028
2029 IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
2030 IFW(if ( coeff_size < 0 ) { *out = -(*out); });
2031 IFW(out++); ws++;
2032
2033 IFW(*term_size = out - term_size);
2034 }
2035
2036 if ( with_arghead ) {
2037 IFW(*arg_size = out - arg_size);
2038 if ( write && sort_vars ) {
2039 // Sort into form highfirst ordering, if we have potentially re-ordered the variables by
2040 // degree, in flint::get_variables. Otherwise, we can rely on FLINT's sorting with
2041 // ORD_LEX and the variables ordered in FORM's lexicographic order.
2042 flint::form_sort(BHEAD (WORD*)(out_entry));
2043 }
2044 }
2045 else {
2046 // with no arghead, we write a terminating zero
2047 IFW(*out++ = 0); ws++;
2048 }
2049
2050 NumberFree(tmp_coeff, "flint::to_argument_mpoly");
2051 NumberFree(tmp_den, "flint::to_argument_mpoly");
2052
2053 return ws;
2054}
2055
2056// If no denscale argument is supplied, just set it to 1 and call the usual function
2057uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
2058 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
2059 const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const bool sort_vars) {
2060
2061 flint::fmpz tmp;
2062 fmpz_set_ui(tmp.d, 1);
2063
2064 uint64_t ret = flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write,
2065 prev_size, poly, var_map, ctx, sort_vars, tmp.d);
2066
2067 return ret;
2068}
2069/*
2070 #] flint::to_argument_mpoly :
2071 #[ flint::to_argument_poly :
2072*/
2073// Convert a fmpz_poly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
2074// If the caller is building an output term, prev_size contains the size of the term so far, to
2075// check that the output fits in AM.MaxTer if must_fit_term.
2076// All coefficients will be divided by denscale (which might just be 1).
2077// If write is false, we never write to out but only track the total would-be size. This lets this
2078// function be repurposed as a "size of FORM notation" function without duplicating the code.
2079uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
2080 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2081 const var_map_t &var_map, const fmpz_t denscale) {
2082
2083 // Track the total size written. We could do this with pointer differences, but if
2084 // write == false we don't write to or move out to be able to find the size that way.
2085 uint64_t ws = 0;
2086
2087 // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
2088 if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
2089 MLOCK(ErrorMessageLock);
2090 MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
2091 MUNLOCK(ErrorMessageLock);
2092 Terminate(-1);
2093 }
2094
2095 // Create the inverse of var_map, so we don't have to search it for each symbol written
2096 vector<uint32_t> var_map_inv;
2097 var_map_inv.resize(var_map.size());
2098 for ( auto x : var_map ) {
2099 var_map_inv[x.second] = x.first;
2100 }
2101
2102 const int64_t n_terms = fmpz_poly_length(poly);
2103
2104 // The poly is zero
2105 if ( n_terms == 0 ) {
2106 if ( with_arghead ) {
2107 IFW(*out++ = -SNUMBER); ws++;
2108 IFW(*out++ = 0); ws++;
2109 return ws;
2110 }
2111 else {
2112 IFW(*out++ = 0); ws++;
2113 return ws;
2114 }
2115 }
2116
2117 // For dividing out denscale
2118 flint::fmpz coeff, den, gcd;
2119
2120 // The poly is constant, use fast notation if the coefficient is integer and small enough
2121 if ( with_arghead && n_terms == 1 ) {
2122
2123 fmpz_poly_get_coeff_fmpz(coeff.d, poly, 0);
2124 fmpz_set(den.d, denscale);
2125 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2126
2127 if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
2128 const long fast_coeff = fmpz_get_si(coeff.d);
2129 // While ">=", could work here, FORM does not use fast notation for INT_MIN
2130 if ( fast_coeff > INT_MIN && fast_coeff <= INT_MAX ) {
2131 IFW(*out++ = -SNUMBER); ws++;
2132 IFW(*out++ = (WORD)fast_coeff); ws++;
2133 return ws;
2134 }
2135 }
2136 }
2137
2138 // The poly might be a single symbol with coeff 1, use fast notation if so.
2139 if ( with_arghead && n_terms == 2 ) {
2140 if ( fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, 0)) ) {
2141 // The constant term is zero
2142
2143 fmpz_poly_get_coeff_fmpz(coeff.d, poly, 1);
2144 fmpz_set(den.d, denscale);
2145 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2146
2147 if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
2148 // Single symbol with coeff 1. Use fast notation:
2149 IFW(*out++ = -SYMBOL); ws++;
2150 IFW(*out++ = var_map_inv[0]); ws++;
2151 return ws;
2152 }
2153 }
2154 }
2155
2156 WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_poly");
2157 WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
2158
2159 WORD* arg_size = 0;
2160 WORD* arg_flag = 0;
2161 if ( with_arghead ) {
2162 IFW(arg_size = out++); ws++; // total arg size
2163 IFW(arg_flag = out++); ws++;
2164 IFW(*arg_flag = 0); // clean argument
2165 }
2166
2167 // In reverse, since we want a "highfirst" output
2168 for ( int64_t i = n_terms-1; i >= 0; i-- ) {
2169
2170 // fmpz_poly is dense, there might be many zero coefficients:
2171 if ( !fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)) ) {
2172
2173 fmpz_poly_get_coeff_fmpz(coeff.d, poly, i);
2174 fmpz_set(den.d, denscale);
2175 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2176
2177 // Convert the coefficient, write in temporary space
2178 const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
2179 const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
2180 const WORD coeff_size = [num_size, den_size] () -> WORD {
2181 WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
2182 return size * SGN(num_size) * SGN(den_size);
2183 }();
2184
2185 // Now we have the coeff size, we can determine the output size
2186 // Check it fits if necessary: symbol code,power, num,den of "coeff_size",
2187 // +1 for total coeff size
2188 uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
2189 if ( i > 0 ) {
2190 // and also symbols header, code,power of the symbol
2191 current_size += 4;
2192 }
2193 if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
2194 MLOCK(ErrorMessageLock);
2195 MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
2196 MUNLOCK(ErrorMessageLock);
2197 Terminate(-1);
2198 }
2199
2200 WORD* term_size = 0;
2201 IFW(term_size = out++); ws++;
2202
2203 if ( i > 0 ) {
2204 IFW(*out++ = SYMBOL); ws++;
2205 IFW(*out++ = 4); ws++; // The symbol array size, it is univariate
2206 IFW(*out++ = var_map_inv[0]); ws++;
2207 IFW(*out++ = i); ws++;
2208 }
2209
2210 // Copy numerator
2211 for ( WORD j = 0; j < ABS(num_size); j++ ) {
2212 IFW(*out++ = tmp_coeff[j]); ws++;
2213 }
2214 for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
2215 IFW(*out++ = 0); ws++;
2216 }
2217 // Copy denominator
2218 for ( WORD j = 0; j < ABS(den_size); j++ ) {
2219 IFW(*out++ = tmp_den[j]); ws++;
2220 }
2221 for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
2222 IFW(*out++ = 0); ws++;
2223 }
2224
2225 IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
2226 IFW(if ( coeff_size < 0 ) { *out = -(*out); });
2227 IFW(out++); ws++;
2228
2229 IFW(*term_size = out - term_size);
2230 }
2231
2232 }
2233
2234 if ( with_arghead ) {
2235 IFW(*arg_size = out - arg_size);
2236 }
2237 else {
2238 // with no arghead, we write a terminating zero
2239 IFW(*out++ = 0); ws++;
2240 }
2241
2242 NumberFree(tmp_coeff, "flint::to_argument_poly");
2243 NumberFree(tmp_den, "flint::to_argument_poly");
2244
2245 return ws;
2246}
2247
2248// If no denscale argument is supplied, just set it to 1 and call the usual function
2249uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
2250 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2251 const var_map_t &var_map) {
2252
2253 flint::fmpz tmp;
2254 fmpz_set_ui(tmp.d, 1);
2255
2256 uint64_t ret = flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, prev_size,
2257 poly, var_map, tmp.d);
2258
2259 return ret;
2260}
2261/*
2262 #] flint::to_argument_poly :
2263*/
2264
2265// Utility functions
2266/*
2267 #[ flint::util::simplify_fmpz :
2268*/
2269// Divide the GCD out of num and den
2270inline void flint::util::simplify_fmpz(fmpz_t num, fmpz_t den, fmpz_t gcd) {
2271 fmpz_gcd(gcd, num, den);
2272 if ( !fmpz_is_one(gcd) ) {
2273 fmpz_divexact(num, num, gcd);
2274 fmpz_divexact(den, den, gcd);
2275 }
2276}
2277/*
2278 #] flint::util::simplify_fmpz :
2279 #[ flint::util::simplify_fmpz_poly :
2280*/
2281// Divide the GCD out of num and den
2282inline void flint::util::simplify_fmpz_poly(fmpz_poly_t num, fmpz_poly_t den, fmpz_poly_t gcd) {
2283 fmpz_poly_gcd(gcd, num, den);
2284 if ( !fmpz_poly_is_one(gcd) ) {
2285#if __FLINT_RELEASE >= 30100
2286 // This should be faster than fmpz_poly_div, see https://github.com/flintlib/flint/pull/1766
2287 fmpz_poly_divexact(num, num, gcd);
2288 fmpz_poly_divexact(den, den, gcd);
2289#else
2290 fmpz_poly_div(num, num, gcd);
2291 fmpz_poly_div(den, den, gcd);
2292#endif
2293 }
2294}
2295/*
2296 #] flint::util::simplify_fmpz_poly :
2297 #[ flint::util::fix_sign_fmpz_mpoly_ratfun :
2298*/
2299inline void flint::util::fix_sign_fmpz_mpoly_ratfun(fmpz_mpoly_t num, fmpz_mpoly_t den,
2300 const fmpz_mpoly_ctx_t ctx) {
2301 // Fix sign to align with poly: the leading denominator term should have a positive coeff
2302 if ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(den, 0, ctx)) == -1 ) {
2303 fmpz_mpoly_neg(num, num, ctx);
2304 fmpz_mpoly_neg(den, den, ctx);
2305 }
2306}
2307/*
2308 #] flint::util::fix_sign_fmpz_mpoly_ratfun :
2309 #[ flint::util::fix_sign_fmpz_poly_ratfun :
2310*/
2311inline void flint::util::fix_sign_fmpz_poly_ratfun(fmpz_poly_t num, fmpz_poly_t den) {
2312 // Fix sign to align with poly: the leading denominator term should have a positive coeff
2313 if ( fmpz_sgn(fmpz_poly_get_coeff_ptr(den, fmpz_poly_degree(den))) == -1 ) {
2314 fmpz_poly_neg(num, num);
2315 fmpz_poly_neg(den, den);
2316 }
2317}
2318/*
2319 #] flint::util::fix_sign_fmpz_poly_ratfun :
2320*/
Definition poly.h:53
LONG EndSort(PHEAD WORD *, int)
Definition sort.c:488
void LowerSortLevel(void)
Definition sort.c:4731
int StoreTerm(PHEAD WORD *)
Definition sort.c:4311
int NewSort(PHEAD0)
Definition sort.c:397
WORD Compare1(PHEAD WORD *, WORD *, WORD)
Definition sort.c:2393
WORD CompareSymbols(PHEAD WORD *, WORD *, WORD)
Definition sort.c:2856
int SymbolNormalize(WORD *)
Definition normal.c:5210