FORM v5.0.1-23-g7a8f756
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 MLOCK(ErrorMessageLock);
1765 MesPrint("implement this");
1766 MUNLOCK(ErrorMessageLock);
1767 Terminate(-1);
1768 }
1769 if ( a < arg_stop ) {
1770 MLOCK(ErrorMessageLock);
1771 MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
1772 MUNLOCK(ErrorMessageLock);
1773 Terminate(-1);
1774 }
1775
1776 // Multiply the num by den_den and den by den_num:
1777 fmpz_mpoly_mul(num, num, den_den.d, ctx);
1778 fmpz_mpoly_mul(den, den, den_num.d, ctx);
1779
1780 if ( must_normalize ) {
1781 flint::mpoly gcd(ctx);
1782 fmpz_mpoly_gcd_cofactors(gcd.d, num, den, num, den, ctx);
1783 }
1784}
1785/*
1786 #] flint::ratfun_read_mpoly :
1787 #[ flint::ratfun_read_poly :
1788*/
1789// Read the uni-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1790// denominator.
1791void flint::ratfun_read_poly(const WORD *a, fmpz_poly_t num, fmpz_poly_t den) {
1792
1793 // The end of the arguments:
1794 const WORD* arg_stop = a+a[1];
1795
1796 const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
1797
1798 a += FUNHEAD;
1799 if ( a >= arg_stop ) {
1800 MLOCK(ErrorMessageLock);
1801 MesPrint("ERROR: PolyRatFun cannot have zero arguments");
1802 MUNLOCK(ErrorMessageLock);
1803 Terminate(-1);
1804 }
1805
1806 // Polys to collect the "den of the num" and "den of the den".
1807 // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1808 flint::poly den_num, den_den;
1809
1810 // Read the numerator
1811 flint::from_argument_poly(num, den_num.d, a, true);
1812 NEXTARG(a);
1813
1814 if ( a < arg_stop ) {
1815 // Read the denominator
1816 flint::from_argument_poly(den, den_den.d, a, true);
1817 NEXTARG(a);
1818 }
1819 else {
1820 // The denominator is 1
1821 MLOCK(ErrorMessageLock);
1822 MesPrint("implement this");
1823 MUNLOCK(ErrorMessageLock);
1824 Terminate(-1);
1825 }
1826 if ( a < arg_stop ) {
1827 MLOCK(ErrorMessageLock);
1828 MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
1829 MUNLOCK(ErrorMessageLock);
1830 Terminate(-1);
1831 }
1832
1833 // Multiply the num by den_den and den by den_num:
1834 fmpz_poly_mul(num, num, den_den.d);
1835 fmpz_poly_mul(den, den, den_num.d);
1836
1837 if ( must_normalize ) {
1838 flint::poly gcd;
1839 flint::util::simplify_fmpz_poly(num, den, gcd.d);
1840 }
1841}
1842/*
1843 #] flint::ratfun_read_poly :
1844 #[ flint::to_argument_mpoly :
1845*/
1846// Convert a fmpz_mpoly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
1847// If the caller is building an output term, prev_size contains the size of the term so far, to
1848// check that the output fits in AM.MaxTer if must_fit_term.
1849// All coefficients will be divided by denscale (which might just be 1).
1850// If write is false, we never write to out but only track the total would-be size. This lets this
1851// function be repurposed as a "size of FORM notation" function without duplicating the code.
1852#define IFW(x) { if ( write ) {x;} }
1853uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
1854 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
1855 const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const bool sort_vars,
1856 const fmpz_t denscale) {
1857
1858 // out is modified later, keep the pointer at entry
1859 const WORD* out_entry = out;
1860
1861 // Track the total size written. We could do this with pointer differences, but if
1862 // write == false we don't write to or move out to be able to find the size that way.
1863 uint64_t ws = 0;
1864
1865 // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1866 if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
1867 MLOCK(ErrorMessageLock);
1868 MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
1869 MUNLOCK(ErrorMessageLock);
1870 Terminate(-1);
1871 }
1872
1873 // Create the inverse of var_map, so we don't have to search it for each symbol written
1874 vector<uint32_t> var_map_inv;
1875 var_map_inv.resize(var_map.size());
1876 for ( auto x : var_map ) {
1877 var_map_inv[x.second] = x.first;
1878 }
1879
1880 vector<int64_t> exponents(var_map.size());
1881 const int64_t n_terms = fmpz_mpoly_length(poly, ctx);
1882
1883 if ( n_terms == 0 ) {
1884 if ( with_arghead ) {
1885 IFW(*out++ = -SNUMBER); ws++;
1886 IFW(*out++ = 0); ws++;
1887 return ws;
1888 }
1889 else {
1890 IFW(*out++ = 0); ws++;
1891 return ws;
1892 }
1893 }
1894
1895 // For dividing out denscale
1896 flint::fmpz coeff, den, gcd;
1897
1898 // The mpoly might be constant or a single symbol with coeff 1. Use fast notation if possible.
1899 if ( with_arghead && n_terms == 1 ) {
1900
1901 if ( fmpz_mpoly_is_fmpz(poly, ctx) ) {
1902 // The mpoly is constant. Use fast notation if the number is an integer and small enough:
1903
1904 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
1905 fmpz_set(den.d, denscale);
1906 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1907
1908 if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
1909 const int64_t fast_coeff = fmpz_get_si(coeff.d);
1910 // While ">=", could work here, FORM does not use fast notation for INT_MIN
1911 if ( fast_coeff > INT32_MIN && fast_coeff <= INT32_MAX ) {
1912 IFW(*out++ = -SNUMBER); ws++;
1913 IFW(*out++ = (WORD)fast_coeff); ws++;
1914 return ws;
1915 }
1916 }
1917 }
1918
1919 else {
1920 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
1921 fmpz_set(den.d, denscale);
1922 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1923
1924 if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
1925 // The coefficient is one. Now check the symbol powers:
1926
1927 fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, 0, ctx);
1928 int64_t use_fast = 0;
1929 uint32_t fast_symbol = 0;
1930
1931 for ( size_t i = 0; i < var_map.size(); i++ ) {
1932 if ( exponents[i] == 1 ) fast_symbol = var_map_inv[i];
1933 use_fast += exponents[i];
1934 }
1935
1936 // use_fast has collected the total degree. If it is 1, then fast_symbol holds the code
1937 if ( use_fast == 1 ) {
1938 IFW(*out++ = -SYMBOL); ws++;
1939 IFW(*out++ = fast_symbol); ws++;
1940 return ws;
1941 }
1942 }
1943 }
1944 }
1945
1946
1947 WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_mpoly");
1948 WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
1949
1950 WORD* arg_size = 0;
1951 WORD* arg_flag = 0;
1952 if ( with_arghead ) {
1953 IFW(arg_size = out++); ws++; // total arg size
1954 IFW(arg_flag = out++); ws++;
1955 IFW(*arg_flag = 0); // clean argument
1956 }
1957
1958 for ( int64_t i = 0; i < n_terms; i++ ) {
1959
1960 fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, i, ctx);
1961
1962 fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, i, ctx);
1963 fmpz_set(den.d, denscale);
1964 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1965
1966 uint32_t num_symbols = 0;
1967 for ( size_t j = 0; j < var_map.size(); j++ ) {
1968 if ( exponents[j] != 0 ) { num_symbols += 1; }
1969 }
1970
1971 // Convert the coefficient, write in temporary space
1972 const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
1973 const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
1974 const WORD coeff_size = [num_size, den_size] () -> WORD {
1975 WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
1976 return size * SGN(num_size) * SGN(den_size);
1977 }();
1978
1979 // Now we have the number of symbols and the coeff size, we can determine the output size.
1980 // Check it fits if necessary: term size, num,den of "coeff_size", +1 for total coeff size
1981 uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
1982 if ( num_symbols ) {
1983 // symbols header, code,power of each symbol:
1984 current_size += 2 + 2*num_symbols;
1985 }
1986 if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
1987 MLOCK(ErrorMessageLock);
1988 MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
1989 MUNLOCK(ErrorMessageLock);
1990 Terminate(-1);
1991 }
1992
1993 WORD* term_size = 0;
1994 IFW(term_size = out++); ws++;
1995 if ( num_symbols ) {
1996 IFW(*out++ = SYMBOL); ws++;
1997 WORD* symbol_size = 0;
1998 IFW(symbol_size = out++); ws++;
1999 IFW(*symbol_size = 2);
2000
2001 for ( size_t j = 0; j < var_map.size(); j++ ) {
2002 if ( exponents[j] != 0 ) {
2003 IFW(*out++ = var_map_inv[j]); ws++;
2004 IFW(*out++ = exponents[j]); ws++;
2005 IFW(*symbol_size += 2);
2006 }
2007 }
2008 }
2009
2010 // Copy numerator
2011 for ( WORD j = 0; j < ABS(num_size); j++ ) {
2012 IFW(*out++ = tmp_coeff[j]); ws++;
2013 }
2014 for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
2015 IFW(*out++ = 0); ws++;
2016 }
2017 // Copy denominator
2018 for ( WORD j = 0; j < ABS(den_size); j++ ) {
2019 IFW(*out++ = tmp_den[j]); ws++;
2020 }
2021 for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
2022 IFW(*out++ = 0); ws++;
2023 }
2024
2025 IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
2026 IFW(if ( coeff_size < 0 ) { *out = -(*out); });
2027 IFW(out++); ws++;
2028
2029 IFW(*term_size = out - term_size);
2030 }
2031
2032 if ( with_arghead ) {
2033 IFW(*arg_size = out - arg_size);
2034 if ( write && sort_vars ) {
2035 // Sort into form highfirst ordering, if we have potentially re-ordered the variables by
2036 // degree, in flint::get_variables. Otherwise, we can rely on FLINT's sorting with
2037 // ORD_LEX and the variables ordered in FORM's lexicographic order.
2038 flint::form_sort(BHEAD (WORD*)(out_entry));
2039 }
2040 }
2041 else {
2042 // with no arghead, we write a terminating zero
2043 IFW(*out++ = 0); ws++;
2044 }
2045
2046 NumberFree(tmp_coeff, "flint::to_argument_mpoly");
2047 NumberFree(tmp_den, "flint::to_argument_mpoly");
2048
2049 return ws;
2050}
2051
2052// If no denscale argument is supplied, just set it to 1 and call the usual function
2053uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
2054 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
2055 const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const bool sort_vars) {
2056
2057 flint::fmpz tmp;
2058 fmpz_set_ui(tmp.d, 1);
2059
2060 uint64_t ret = flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write,
2061 prev_size, poly, var_map, ctx, sort_vars, tmp.d);
2062
2063 return ret;
2064}
2065/*
2066 #] flint::to_argument_mpoly :
2067 #[ flint::to_argument_poly :
2068*/
2069// Convert a fmpz_poly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
2070// If the caller is building an output term, prev_size contains the size of the term so far, to
2071// check that the output fits in AM.MaxTer if must_fit_term.
2072// All coefficients will be divided by denscale (which might just be 1).
2073// If write is false, we never write to out but only track the total would-be size. This lets this
2074// function be repurposed as a "size of FORM notation" function without duplicating the code.
2075uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
2076 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2077 const var_map_t &var_map, const fmpz_t denscale) {
2078
2079 // Track the total size written. We could do this with pointer differences, but if
2080 // write == false we don't write to or move out to be able to find the size that way.
2081 uint64_t ws = 0;
2082
2083 // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
2084 if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
2085 MLOCK(ErrorMessageLock);
2086 MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
2087 MUNLOCK(ErrorMessageLock);
2088 Terminate(-1);
2089 }
2090
2091 // Create the inverse of var_map, so we don't have to search it for each symbol written
2092 vector<uint32_t> var_map_inv;
2093 var_map_inv.resize(var_map.size());
2094 for ( auto x : var_map ) {
2095 var_map_inv[x.second] = x.first;
2096 }
2097
2098 const int64_t n_terms = fmpz_poly_length(poly);
2099
2100 // The poly is zero
2101 if ( n_terms == 0 ) {
2102 if ( with_arghead ) {
2103 IFW(*out++ = -SNUMBER); ws++;
2104 IFW(*out++ = 0); ws++;
2105 return ws;
2106 }
2107 else {
2108 IFW(*out++ = 0); ws++;
2109 return ws;
2110 }
2111 }
2112
2113 // For dividing out denscale
2114 flint::fmpz coeff, den, gcd;
2115
2116 // The poly is constant, use fast notation if the coefficient is integer and small enough
2117 if ( with_arghead && n_terms == 1 ) {
2118
2119 fmpz_poly_get_coeff_fmpz(coeff.d, poly, 0);
2120 fmpz_set(den.d, denscale);
2121 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2122
2123 if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
2124 const long fast_coeff = fmpz_get_si(coeff.d);
2125 // While ">=", could work here, FORM does not use fast notation for INT_MIN
2126 if ( fast_coeff > INT_MIN && fast_coeff <= INT_MAX ) {
2127 IFW(*out++ = -SNUMBER); ws++;
2128 IFW(*out++ = (WORD)fast_coeff); ws++;
2129 return ws;
2130 }
2131 }
2132 }
2133
2134 // The poly might be a single symbol with coeff 1, use fast notation if so.
2135 if ( with_arghead && n_terms == 2 ) {
2136 if ( fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, 0)) ) {
2137 // The constant term is zero
2138
2139 fmpz_poly_get_coeff_fmpz(coeff.d, poly, 1);
2140 fmpz_set(den.d, denscale);
2141 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2142
2143 if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
2144 // Single symbol with coeff 1. Use fast notation:
2145 IFW(*out++ = -SYMBOL); ws++;
2146 IFW(*out++ = var_map_inv[0]); ws++;
2147 return ws;
2148 }
2149 }
2150 }
2151
2152 WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_poly");
2153 WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
2154
2155 WORD* arg_size = 0;
2156 WORD* arg_flag = 0;
2157 if ( with_arghead ) {
2158 IFW(arg_size = out++); ws++; // total arg size
2159 IFW(arg_flag = out++); ws++;
2160 IFW(*arg_flag = 0); // clean argument
2161 }
2162
2163 // In reverse, since we want a "highfirst" output
2164 for ( int64_t i = n_terms-1; i >= 0; i-- ) {
2165
2166 // fmpz_poly is dense, there might be many zero coefficients:
2167 if ( !fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)) ) {
2168
2169 fmpz_poly_get_coeff_fmpz(coeff.d, poly, i);
2170 fmpz_set(den.d, denscale);
2171 flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
2172
2173 // Convert the coefficient, write in temporary space
2174 const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
2175 const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
2176 const WORD coeff_size = [num_size, den_size] () -> WORD {
2177 WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
2178 return size * SGN(num_size) * SGN(den_size);
2179 }();
2180
2181 // Now we have the coeff size, we can determine the output size
2182 // Check it fits if necessary: symbol code,power, num,den of "coeff_size",
2183 // +1 for total coeff size
2184 uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
2185 if ( i > 0 ) {
2186 // and also symbols header, code,power of the symbol
2187 current_size += 4;
2188 }
2189 if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
2190 MLOCK(ErrorMessageLock);
2191 MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
2192 MUNLOCK(ErrorMessageLock);
2193 Terminate(-1);
2194 }
2195
2196 WORD* term_size = 0;
2197 IFW(term_size = out++); ws++;
2198
2199 if ( i > 0 ) {
2200 IFW(*out++ = SYMBOL); ws++;
2201 IFW(*out++ = 4); ws++; // The symbol array size, it is univariate
2202 IFW(*out++ = var_map_inv[0]); ws++;
2203 IFW(*out++ = i); ws++;
2204 }
2205
2206 // Copy numerator
2207 for ( WORD j = 0; j < ABS(num_size); j++ ) {
2208 IFW(*out++ = tmp_coeff[j]); ws++;
2209 }
2210 for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
2211 IFW(*out++ = 0); ws++;
2212 }
2213 // Copy denominator
2214 for ( WORD j = 0; j < ABS(den_size); j++ ) {
2215 IFW(*out++ = tmp_den[j]); ws++;
2216 }
2217 for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
2218 IFW(*out++ = 0); ws++;
2219 }
2220
2221 IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
2222 IFW(if ( coeff_size < 0 ) { *out = -(*out); });
2223 IFW(out++); ws++;
2224
2225 IFW(*term_size = out - term_size);
2226 }
2227
2228 }
2229
2230 if ( with_arghead ) {
2231 IFW(*arg_size = out - arg_size);
2232 }
2233 else {
2234 // with no arghead, we write a terminating zero
2235 IFW(*out++ = 0); ws++;
2236 }
2237
2238 NumberFree(tmp_coeff, "flint::to_argument_poly");
2239 NumberFree(tmp_den, "flint::to_argument_poly");
2240
2241 return ws;
2242}
2243
2244// If no denscale argument is supplied, just set it to 1 and call the usual function
2245uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
2246 const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2247 const var_map_t &var_map) {
2248
2249 flint::fmpz tmp;
2250 fmpz_set_ui(tmp.d, 1);
2251
2252 uint64_t ret = flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, prev_size,
2253 poly, var_map, tmp.d);
2254
2255 return ret;
2256}
2257/*
2258 #] flint::to_argument_poly :
2259*/
2260
2261// Utility functions
2262/*
2263 #[ flint::util::simplify_fmpz :
2264*/
2265// Divide the GCD out of num and den
2266inline void flint::util::simplify_fmpz(fmpz_t num, fmpz_t den, fmpz_t gcd) {
2267 fmpz_gcd(gcd, num, den);
2268 if ( !fmpz_is_one(gcd) ) {
2269 fmpz_divexact(num, num, gcd);
2270 fmpz_divexact(den, den, gcd);
2271 }
2272}
2273/*
2274 #] flint::util::simplify_fmpz :
2275 #[ flint::util::simplify_fmpz_poly :
2276*/
2277// Divide the GCD out of num and den
2278inline void flint::util::simplify_fmpz_poly(fmpz_poly_t num, fmpz_poly_t den, fmpz_poly_t gcd) {
2279 fmpz_poly_gcd(gcd, num, den);
2280 if ( !fmpz_poly_is_one(gcd) ) {
2281#if __FLINT_RELEASE >= 30100
2282 // This should be faster than fmpz_poly_div, see https://github.com/flintlib/flint/pull/1766
2283 fmpz_poly_divexact(num, num, gcd);
2284 fmpz_poly_divexact(den, den, gcd);
2285#else
2286 fmpz_poly_div(num, num, gcd);
2287 fmpz_poly_div(den, den, gcd);
2288#endif
2289 }
2290}
2291/*
2292 #] flint::util::simplify_fmpz_poly :
2293 #[ flint::util::fix_sign_fmpz_mpoly_ratfun :
2294*/
2295inline void flint::util::fix_sign_fmpz_mpoly_ratfun(fmpz_mpoly_t num, fmpz_mpoly_t den,
2296 const fmpz_mpoly_ctx_t ctx) {
2297 // Fix sign to align with poly: the leading denominator term should have a positive coeff
2298 if ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(den, 0, ctx)) == -1 ) {
2299 fmpz_mpoly_neg(num, num, ctx);
2300 fmpz_mpoly_neg(den, den, ctx);
2301 }
2302}
2303/*
2304 #] flint::util::fix_sign_fmpz_mpoly_ratfun :
2305 #[ flint::util::fix_sign_fmpz_poly_ratfun :
2306*/
2307inline void flint::util::fix_sign_fmpz_poly_ratfun(fmpz_poly_t num, fmpz_poly_t den) {
2308 // Fix sign to align with poly: the leading denominator term should have a positive coeff
2309 if ( fmpz_sgn(fmpz_poly_get_coeff_ptr(den, fmpz_poly_degree(den))) == -1 ) {
2310 fmpz_poly_neg(num, num);
2311 fmpz_poly_neg(den, den);
2312 }
2313}
2314/*
2315 #] flint::util::fix_sign_fmpz_poly_ratfun :
2316*/
Definition poly.h:53
LONG EndSort(PHEAD WORD *, int)
Definition sort.c:486
void LowerSortLevel(void)
Definition sort.c:4703
int StoreTerm(PHEAD WORD *)
Definition sort.c:4285
int NewSort(PHEAD0)
Definition sort.c:395
WORD Compare1(PHEAD WORD *, WORD *, WORD)
Definition sort.c:2375
WORD CompareSymbols(PHEAD WORD *, WORD *, WORD)
Definition sort.c:2838
int SymbolNormalize(WORD *)
Definition normal.c:5206