• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tueda / form / 16793762962

06 Aug 2025 02:15PM UTC coverage: 52.857% (+2.3%) from 50.539%
16793762962

push

github

jodavies
test: update test with dollar-var moduleoption

Avoid the now-default parallel veto warning in test 615.

43919 of 83091 relevant lines covered (52.86%)

2237235.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.33
/sources/flintinterface.cc
1
/** @file flintinterface.cc
2
 *
3
 *   Contains functions which interface with FLINT functions to perform arithmetic with uni- and
4
 *   multi-variate polynomials and perform factorization.
5
 */
6

7
#ifdef HAVE_CONFIG_H
8
#ifndef CONFIG_H_INCLUDED
9
#define CONFIG_H_INCLUDED
10
#include <config.h>
11
#endif
12
#endif
13

14
extern "C" {
15
#include "form3.h"
16
}
17

18
#include "flintinterface.h"
19

20
/*
21
        #[ Types
22
 * Use fixed-size integer types (int32_t, uint32_t, int64_t, uint64_t) except when we refer
23
 * specifically to FORM term data, when we use WORD. This code has only been tested on 64bit
24
 * systems where WORDs are int32_t, and some functions (fmpz_get_form, fmpz_set_form) require this
25
 * to be the case. Enforce this:
26
 */
27
static_assert(sizeof(WORD) == sizeof(int32_t), "flint interface expects 32bit WORD");
28
static_assert(sizeof(UWORD) == sizeof(uint32_t), "flint interface expects 32bit WORD");
29
static_assert(BITSINWORD == 32, "flint interface expects 32bit WORD");
30
static_assert(sizeof(ulong) == sizeof(uint64_t), "flint interface expects ulong is uint64_t");
31
static_assert(sizeof(slong) == sizeof(int64_t), "flint interface expects slong is int64_t");
32

33
/*
34
 * Flint functions take arguments or return values which may be "slong" or "ulong" in its
35
 * documentation, and these are int64_t and uint64_t respectively.
36
 *
37
        #] Types
38
*/
39

40
/*
41
        #[ flint::cleanup :
42
*/
43
void flint::cleanup(void) {
2,528✔
44
        flint_cleanup();
2,528✔
45
}
2,528✔
46
/*
47
        #] flint::cleanup :
48
        #[ flint::cleanup_master :
49
*/
50
void flint::cleanup_master(void) {
1,095✔
51
        flint_cleanup_master();
1,095✔
52
}
1,095✔
53
/*
54
        #] flint::cleanup_master :
55

56
        #[ flint::divmod_mpoly :
57
*/
58
WORD* flint::divmod_mpoly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
4,353✔
59
        const WORD must_fit_term, const var_map_t &var_map) {
60

61
        flint::mpoly_ctx ctx(var_map.size());
4,353✔
62
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
4,353✔
63

64
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
4,353✔
65
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
4,353✔
66

67
        // The input won't have any symbols with negative powers, but there may be rational
68
        // coefficients. Verify this:
69
        if ( fmpz_mpoly_is_fmpz(denpa.d, ctx.d) != 1 ) {
4,353✔
70
                MLOCK(ErrorMessageLock);
×
71
                MesPrint("flint::divmod_mpoly: error: denpa is non-constant");
×
72
                MUNLOCK(ErrorMessageLock);
×
73
                Terminate(-1);
×
74
        }
75
        if ( fmpz_mpoly_is_fmpz(denpb.d, ctx.d) != 1 ) {
4,353✔
76
                MLOCK(ErrorMessageLock);
×
77
                MesPrint("flint::divmod_mpoly: error: denpb is non-constant");
×
78
                MUNLOCK(ErrorMessageLock);
×
79
                Terminate(-1);
×
80
        }
81

82

83
        flint::fmpz scale;
4,353✔
84
        flint::mpoly div(ctx.d), rem(ctx.d);
4,353✔
85

86
        fmpz_mpoly_quasidivrem(scale.d, div.d, rem.d, pa.d, pb.d, ctx.d);
4,353✔
87

88
        // The quotient must be multiplied by the denominator of the divisor
89
        fmpz_mpoly_mul(div.d, div.d, denpb.d, ctx.d);
4,353✔
90

91
        // The overall denominator of both div and rem is given by scale*denpa. This we will pass to
92
        // to_argument_mpoly's "denscale" argument which performs the division in the result. We have
93
        // already checked denpa is just an fmpz.
94
        fmpz_mul(scale.d, scale.d, fmpz_mpoly_term_coeff_ref(denpa.d, 0, ctx.d));
4,353✔
95

96

97
        // Determine the size of the output by passing write = false.
98
        const bool with_arghead = false;
4,353✔
99
        bool write = false;
4,353✔
100
        const uint64_t prev_size = 0;
4,353✔
101
        const uint64_t out_size = return_rem ?
4,353✔
102
                (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
2,532✔
103
                        rem.d, var_map, ctx.d, scale.d)
104
                :
105
                (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
1,821✔
106
                        div.d, var_map, ctx.d, scale.d)
4,353✔
107
                ;
108
        WORD* res = (WORD *)Malloc1(sizeof(WORD)*out_size, "flint::divrem_mpoly");
4,353✔
109

110

111
        // Write out the result
112
        write = true;
4,353✔
113
        if ( return_rem ) {
4,353✔
114
                (uint64_t)flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
2,532✔
115
                        rem.d, var_map, ctx.d, scale.d);
116
        }
117
        else {
118
                (uint64_t)flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
1,821✔
119
                        div.d, var_map, ctx.d, scale.d);
120
        }
121

122
        return res;
4,353✔
123
}
4,353✔
124
/*
125
        #] flint::divmod_mpoly :
126
        #[ flint::divmod_poly :
127
*/
128
WORD* flint::divmod_poly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
11,022✔
129
        const WORD must_fit_term, const var_map_t &var_map) {
130

131
        flint::poly pa, pb, denpa, denpb;
11,022✔
132

133
        flint::from_argument_poly(pa.d, denpa.d, a, false);
11,022✔
134
        flint::from_argument_poly(pb.d, denpb.d, b, false);
11,022✔
135

136
        // The input won't have any symbols with negative powers, but there may be rational
137
        // coefficients. Verify this:
138
        if ( fmpz_poly_length(denpa.d) != 1 ) {
11,022✔
139
                MLOCK(ErrorMessageLock);
×
140
                MesPrint("flint::divmod_poly: error: denpa is non-constant");
×
141
                MUNLOCK(ErrorMessageLock);
×
142
                Terminate(-1);
×
143
        }
144
        if ( fmpz_poly_length(denpb.d) != 1 ) {
11,022✔
145
                MLOCK(ErrorMessageLock);
×
146
                MesPrint("flint::divmod_poly: error: denpb is non-constant");
×
147
                MUNLOCK(ErrorMessageLock);
×
148
                Terminate(-1);
×
149
        }
150

151
        flint::fmpz scale;
11,022✔
152
        uint64_t scale_power = 0;
11,022✔
153
        flint::poly div, rem;
11,022✔
154

155
        // Get the leading coefficient of pb. fmpz_poly_pseudo_divrem returns only the scaling power.
156
        fmpz_poly_get_coeff_fmpz(scale.d, pb.d, fmpz_poly_degree(pb.d));
11,022✔
157

158
        fmpz_poly_pseudo_divrem(div.d, rem.d, (ulong*)&scale_power, pa.d, pb.d);
11,022✔
159

160
        // The quotient must be multiplied by the denominator of the divisor
161
        fmpz_poly_mul(div.d, div.d, denpb.d);
11,022✔
162

163
        // The overall denominator of both div and rem is given by scale^scale_power*denpa. This we will
164
        // pass to to_argument_poly's "denscale" argument which performs the division in the result. We
165
        // have already checked denpa is just an fmpz.
166
        fmpz_pow_ui(scale.d, scale.d, scale_power);
11,022✔
167
        fmpz_mul(scale.d, scale.d, fmpz_poly_get_coeff_ptr(denpa.d, 0));
11,022✔
168

169

170
        // Determine the size of the output by passing write = false.
171
        const bool with_arghead = false;
11,022✔
172
        bool write = false;
11,022✔
173
        const uint64_t prev_size = 0;
11,022✔
174
        const uint64_t out_size = return_rem ?
11,022✔
175
                (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
5,646✔
176
                        rem.d, var_map, scale.d)
177
                :
178
                (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
5,376✔
179
                        div.d, var_map, scale.d)
11,022✔
180
                ;
181
        WORD* res = (WORD *)Malloc1(sizeof(WORD)*out_size, "flint::divrem_poly");
11,022✔
182

183

184
        // Write out the result
185
        write = true;
11,022✔
186
        if ( return_rem ) {
11,022✔
187
                (uint64_t)flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
5,646✔
188
                        rem.d, var_map, scale.d);
189
        }
190
        else {
191
                (uint64_t)flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
5,376✔
192
                        div.d, var_map, scale.d);
193
        }
194

195
        return res;
11,022✔
196
}
11,022✔
197
/*
198
        #] flint::divmod_poly :
199

200
        #[ flint::factorize_mpoly :
201
*/
202
WORD* flint::factorize_mpoly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
45✔
203
        const bool is_fun_arg, const var_map_t &var_map) {
204

205
        flint::mpoly_ctx ctx(var_map.size());
45✔
206
        flint::mpoly arg(ctx.d), den(ctx.d), base(ctx.d);
45✔
207

208
        flint::from_argument_mpoly(arg.d, den.d, argin, with_arghead, var_map, ctx.d);
45✔
209
        // The denominator must be 1:
210
        if ( fmpz_mpoly_is_one(den.d, ctx.d) != 1 ) {
45✔
211
                MLOCK(ErrorMessageLock);
×
212
                MesPrint("flint::factorize_mpoly error: den != 1");
×
213
                MUNLOCK(ErrorMessageLock);
×
214
                Terminate(-1);
×
215
        }
216

217

218
        // Now we can factor the mpoly:
219
        flint::mpoly_factor arg_fac(ctx.d);
45✔
220
        fmpz_mpoly_factor(arg_fac.d, arg.d, ctx.d);
45✔
221
        const int64_t num_factors = fmpz_mpoly_factor_length(arg_fac.d, ctx.d);
45✔
222

223
        flint::fmpz overall_constant;
45✔
224
        fmpz_mpoly_factor_get_constant_fmpz(overall_constant.d, arg_fac.d, ctx.d);
45✔
225
        // FORM should always have taken the overall constant out in the content. Thus this overall
226
        // constant factor should be +- 1 here. Verify this:
227
        if ( ! ( fmpz_equal_si(overall_constant.d, 1) || fmpz_equal_si(overall_constant.d, -1) ) ) {
45✔
228
                MLOCK(ErrorMessageLock);
×
229
                MesPrint("flint::factorize_mpoly error: overall constant factor != +-1");
×
230
                MUNLOCK(ErrorMessageLock);
×
231
                Terminate(-1);
×
232
        }
233

234
        // Construct the output. If argout is not NULL, we write the result there.
235
        // Otherwise, allocate memory.
236
        // The output is zero-terminated list of factors. If with_arghead, each has an arghead which
237
        // contains its size. Otherwise, the factors are zero separated.
238

239
        // We only need to determine the size of the output if we are allocating memory, but we need to
240
        // loop through the factors to fix their signs anyway. Do both together in one loop:
241

242
        // Initially 1, for the final trailing 0.
243
        uint64_t output_size = 1;
45✔
244

245
        // For finding the highest symbol, in FORM's lexicographic ordering
246
        var_map_t var_map_inv;
45✔
247
        for ( auto x: var_map ) {
396✔
248
                var_map_inv[x.second] = x.first;
351✔
249
        }
250

251
        // Store whether we should flip the factor sign in the ouput:
252
        vector<int32_t> base_sign(num_factors, 0);
45✔
253

254
        for ( int64_t i = 0; i < num_factors; i++ ) {
159✔
255
                fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
114✔
256
                const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
114✔
257

258
                // poly_factorize makes sure the highest power of the "highest symbol" (in FORM's
259
                // lexicographic ordering) has a positive coefficient. Check this, update overall_constant
260
                // of the factorization if necessary.
261
                // Store the sign per factor, so that we can flip the signs in the output without re-checking
262
                // the individual terms again.
263
                uint32_t max_var = 0; // FORM symbols start at 20, 0 is a good initial value.
114✔
264
                int32_t max_pow = -1;
114✔
265
                vector<int64_t> base_term_exponents(var_map.size(), 0);
114✔
266

267
                for ( int64_t j = 0; j < fmpz_mpoly_length(base.d, ctx.d); j++ ) {
1,008✔
268
                        fmpz_mpoly_get_term_exp_si((slong*)base_term_exponents.data(), base.d, (slong)j, ctx.d);
894✔
269

270
                        for ( size_t k = 0; k < var_map.size(); k++ ) {
19,449✔
271
                                if ( base_term_exponents[k] > 0 && ( var_map_inv.at(k) > max_var ||
18,555✔
272
                                        ( var_map_inv.at(k) == max_var && base_term_exponents[k] > max_pow ) ) ) {
18,357✔
273

274
                                        max_var = var_map_inv.at(k);
213✔
275
                                        max_pow = base_term_exponents[k];
213✔
276
                                        base_sign[i] = fmpz_sgn(fmpz_mpoly_term_coeff_ref(base.d, j, ctx.d));
213✔
277
                                }
278
                        }
279
                }
280
                // If this base's sign will be flipped an odd number of times, there is a contribution to
281
                // the overall sign of the whole factorization:
282
                if ( ( base_sign[i] == -1 ) && ( exponent % 2 == 1 ) ) {
114✔
283
                        fmpz_neg(overall_constant.d, overall_constant.d);
36✔
284
                }
285

286
                // Now determine the output size of the factor, if we are allocating the memory
287
                if ( argout == NULL ) {
114✔
288
                        const bool write = false;
289
                        for ( int64_t j = 0; j < exponent; j++ ) {
168✔
290
                                output_size += (uint64_t)flint::to_argument_mpoly(BHEAD NULL, with_arghead,
87✔
291
                                        is_fun_arg, write, 0, base.d, var_map, ctx.d);
292
                        }
293
                }
294
        }
114✔
295
        if ( fmpz_sgn(overall_constant.d) == -1 && argout == NULL ) {
45✔
296
                // Add space for a fast-notation number or a normal-notation number and zero separator
297
                output_size += with_arghead ? 2 : 4+1;
42✔
298
        }
299

300
        // Now make the allocation if necessary:
301
        if ( argout == NULL ) {
45✔
302
                argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_mpoly");
33✔
303
        }
304

305

306
        // And now comes the actual output:
307
        WORD* old_argout = argout;
45✔
308

309
        // If the overall sign is negative, first write a full-notation -1. It will be absorbed into the
310
        // overall factor in the content by the caller.
311
        if ( fmpz_sgn(overall_constant.d) == -1 ) {
45✔
312
                if ( with_arghead ) {
27✔
313
                        // poly writes in fast notation in this case. Fast notation is expected by the caller, to
314
                        // properly merge it with the overall factor of the content.
315
                        *argout++ = -SNUMBER;
6✔
316
                        *argout++ = -1;
6✔
317
                }
318
                else {
319
                        *argout++ = 4; // term size
21✔
320
                        *argout++ = 1; // numerator
21✔
321
                        *argout++ = 1; // denominator
21✔
322
                        *argout++ = -3; // coeff size, negative number
21✔
323
                        *argout++ = 0; // factor separator
21✔
324
                }
325
        }
326

327
        for ( int64_t i = 0; i < num_factors; i++ ) {
159✔
328
                fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
114✔
329
                const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
114✔
330

331
                if ( base_sign[i] == -1 ) {
114✔
332
                        fmpz_mpoly_neg(base.d, base.d, ctx.d);
36✔
333
                }
334

335
                const bool write = true;
336
                for ( int64_t j = 0; j < exponent; j++ ) {
237✔
337
                        argout += flint::to_argument_mpoly(BHEAD argout, with_arghead, is_fun_arg, write,
123✔
338
                                argout-old_argout, base.d, var_map, ctx.d);
123✔
339
                }
340
        }
341
        // Final trailing zero to denote the end of the factors.
342
        *argout++ = 0;
45✔
343

344
        return old_argout;
90✔
345
}
45✔
346
/*
347
        #] flint::factorize_mpoly :
348
        #[ flint::factorize_poly :
349
*/
350
WORD* flint::factorize_poly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
×
351
        const bool is_fun_arg, const var_map_t &var_map) {
352

353
        flint::poly arg, den;
×
354

355
        flint::from_argument_poly(arg.d, den.d, argin, with_arghead);
×
356
        // The denominator must be 1:
357
        if ( fmpz_poly_is_one(den.d) != 1 ) {
×
358
                MLOCK(ErrorMessageLock);
×
359
                MesPrint("flint::factorize_poly error: den != 1");
×
360
                MUNLOCK(ErrorMessageLock);
×
361
                Terminate(-1);
×
362
        }
363

364

365
        // Now we can factor the poly:
366
        flint::poly_factor arg_fac;
×
367
        fmpz_poly_factor(arg_fac.d, arg.d);
×
368
        // fmpz_poly_factor_t lacks some convenience functions which fmpz_mpoly_factor_t has.
369
        // I have worked out how to get the factors by looking at how fmpz_poly_factor_print works.
370
        const long num_factors = (arg_fac.d)->num;
×
371

372

373
        // Construct the output. If argout is not NULL, we write the result there.
374
        // Otherwise, allocate memory.
375
        // The output is zero-terminated list of factors. If with_arghead, each has an arghead which
376
        // contains its size. Otherwise, the factors are zero separated.
377
        if ( argout == NULL ) {
×
378
                // First we need to determine the size of the output. This is the same procedure as the
379
                // loop below, but we don't write anything in to_argument_poly (write arg: false).
380
                // Initially 1, for the final trailing 0.
381
                uint64_t output_size = 1;
382

383
                for ( long i = 0; i < num_factors; i++ ) {
×
384
                        fmpz_poly_struct* base = (arg_fac.d)->p + i;
×
385
                        
386
                        const long exponent = (arg_fac.d)->exp[i];
×
387

388
                        const bool write = false;
×
389
                        for ( long j = 0; j < exponent; j++ ) {
×
390
                                output_size += (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead,
×
391
                                        is_fun_arg, write, 0, base, var_map);
392
                        }
393
                }
394

395
                argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_poly");
×
396
        }
397

398
        WORD* old_argout = argout;
×
399

400
        for ( long i = 0; i < num_factors; i++ ) {
×
401
                fmpz_poly_struct* base = (arg_fac.d)->p + i;
×
402
                
403
                const long exponent = (arg_fac.d)->exp[i];
×
404

405
                const bool write = true;
×
406
                for ( long j = 0; j < exponent; j++ ) {
×
407
                        argout += flint::to_argument_poly(BHEAD argout, with_arghead, is_fun_arg, write,
×
408
                                argout-old_argout, base, var_map);
×
409
                }
410
        }
411
        *argout = 0;
×
412

413
        return old_argout;
×
414
}
×
415
/*
416
        #] flint::factorize_poly :
417

418
        #[ flint::form_sort :
419
*/
420
// Sort terms using form's sorting routines. Uses a custom (faster) compare routine, since here
421
// only symbols can appear.
422
// This is a modified poly_sort from polywrap.cc.
423
void flint::form_sort(PHEAD WORD *terms) {
40,140✔
424

425
        if ( terms[0] < 0 ) {
40,140✔
426
                // Fast notation, there is nothing to do
427
                return;
428
        }
429

430
        const WORD oldsorttype = AR.SortType;
40,140✔
431
        AR.SortType = SORTHIGHFIRST;
40,140✔
432

433
        const WORD in_size = terms[0] - ARGHEAD;
40,140✔
434
        WORD out_size;
40,140✔
435

436
        if ( NewSort(BHEAD0) ) {
40,140✔
437
                Terminate(-1);
×
438
        }
439
        AR.CompareRoutine = (COMPAREDUMMY)(&CompareSymbols);
40,140✔
440

441
        // Make sure the symbols are in the right order within the terms
442
        for ( WORD i = ARGHEAD; i < terms[0]; i += terms[i] ) {
519,879✔
443
                if ( SymbolNormalize(terms+i) < 0 || StoreTerm(BHEAD terms+i) ) {
479,739✔
444
                        AR.SortType = oldsorttype;
×
445
                        AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
×
446
                        LowerSortLevel();
×
447
                        Terminate(-1);
×
448
                }
449
        }
450

451
        if ( ( out_size = EndSort(BHEAD terms+ARGHEAD, 1) ) < 0 ) {
40,140✔
452
                AR.SortType = oldsorttype;
×
453
                AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
×
454
                Terminate(-1);
×
455
        }
456

457
        // Check the final size
458
        if ( in_size != out_size  ) {
40,140✔
459
                MLOCK(ErrorMessageLock);
×
460
                MesPrint("flint::form_sort: error: unexpected sorted arg length change %d->%d", in_size,
×
461
                        out_size);
462
                MUNLOCK(ErrorMessageLock);
×
463
                Terminate(-1);
×
464
        }
465

466
        AR.SortType = oldsorttype;
40,140✔
467
        AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
40,140✔
468
        terms[1] = 0; // set dirty flag to zero
40,140✔
469
}
470
/*
471
        #] flint::form_sort :
472

473
        #[ flint::from_argument_mpoly :
474
*/
475
// Convert a FORM argument (or 0-terminated list of terms: with_arghead == false) to a
476
// (multi-variate) fmpz_mpoly_t poly. The "denominator" is return in denpoly, and contains the
477
// overall negative-power numeric and symbolic factor.
478
uint64_t flint::from_argument_mpoly(fmpz_mpoly_t poly, fmpz_mpoly_t denpoly, const WORD *args,
104,169✔
479
        const bool with_arghead, const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx) {
480

481
        // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
482
        // Make sure they are 0 to begin.
483
        fmpz_mpoly_set_si(poly, 0, ctx);
104,169✔
484
        fmpz_mpoly_set_si(denpoly, 0, ctx);
104,169✔
485

486
        // First check for "fast notation" arguments:
487
        if ( *args == -SNUMBER ) {
104,169✔
488
                fmpz_mpoly_set_si(poly, *(args+1), ctx);
26,157✔
489
                fmpz_mpoly_set_si(denpoly, 1, ctx);
26,157✔
490
                return 2;
26,157✔
491
        }
492

493
        if ( *args == -SYMBOL ) {
78,012✔
494
                // A "fast notation" SYMBOL has a power and coefficient of 1:
495
                vector<uint64_t> exponents(var_map.size(), 0);
249✔
496
                exponents[var_map.at(*(args+1))] = 1;
249✔
497

498
                fmpz_mpoly_set_coeff_ui_ui(poly, (ulong)1, (ulong*)exponents.data(), ctx);
249✔
499
                fmpz_mpoly_set_ui(denpoly, (ulong)1, ctx);
249✔
500
                return 2;
249✔
501
        }
249✔
502

503

504
        // Now we can iterate through the terms of the argument. If we have
505
        // an ARGHEAD, we already know where to terminate. Otherwise we'll have
506
        // to loop until the terminating 0.
507
        const WORD* arg_stop = with_arghead ? args+args[0] : (WORD*)UINT64_MAX;
77,763✔
508
        uint64_t arg_size = 0;
63,540✔
509
        if ( with_arghead ) {
63,540✔
510
                arg_size = args[0];
63,540✔
511
                args += ARGHEAD;
63,540✔
512
        }
513

514

515
        // Search for numerical or symbol denominators to create "denpoly".
516
        flint::fmpz den_coeff, tmp;
77,763✔
517
        fmpz_set_si(den_coeff.d, 1);
77,763✔
518
        vector<uint64_t> neg_exponents(var_map.size(), 0);
77,763✔
519

520
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
985,455✔
521
                const WORD* term_stop = term+term[0];
921,915✔
522
                const WORD  coeff_size = (term_stop)[-1];
921,915✔
523
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
921,915✔
524
                const WORD* t = term;
921,915✔
525

526
                t++;
921,915✔
527
                if ( t == symbol_stop ) {
921,915✔
528
                        // Just a number, no symbols
529
                }
530
                else {
531
                        t++; // this entry is SYMBOL
900,984✔
532
                        t++; // this entry just has the size of the symbol array, but we can use symbol_stop
900,984✔
533

534
                        for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
3,547,667✔
535
                                if ( *(s+1) < 0 ) {
2,646,683✔
536
                                        neg_exponents[var_map.at(*s)] =
14,451✔
537
                                                MaX(neg_exponents[var_map.at(*s)], (uint64_t)(-(*(s+1))) );
28,902✔
538
                                }
539
                        }
540
                }
541

542
                // Now check for a denominator in the coefficient:
543
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
921,915✔
544
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
32,454✔
545
                        // Record the LCM of the coefficient denominators:
546
                        fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
32,454✔
547
                }
548

549
                if ( !with_arghead && *term_stop == 0 ) {
921,915✔
550
                        // + 1 for the terminating 0
551
                        arg_size = term_stop - args + 1;
14,223✔
552
                        break;
14,223✔
553
                }
554

555
        }
556
        // Assemble denpoly.
557
        fmpz_mpoly_set_coeff_fmpz_ui(denpoly, den_coeff.d, (ulong*)neg_exponents.data(), ctx);
77,763✔
558

559

560
        // For the term coefficients
561
        flint::fmpz coeff;
77,763✔
562

563
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
985,455✔
564
                const WORD* term_stop = term+term[0];
921,915✔
565
                const WORD  coeff_size = (term_stop)[-1];
921,915✔
566
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
921,915✔
567
                const WORD* t = term;
921,915✔
568

569
                vector<uint64_t> exponents(var_map.size(), 0);
921,915✔
570

571
                t++; // skip over the total size entry
921,915✔
572
                if ( t == symbol_stop ) {
921,915✔
573
                        // Just a number, no symbols
574
                }
575
                else {
576
                        t++; // this entry is SYMBOL
900,984✔
577
                        t++; // this entry just has the size of the symbol array, but we can use symbol_stop
900,984✔
578
                        for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
3,547,667✔
579
                                exponents[var_map.at(*s)] = *(s+1);
2,646,683✔
580
                        }
581
                }
582
                // Now read the coefficient
583
                flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
921,915✔
584

585
                // Multiply by denominator LCM
586
                fmpz_mul(coeff.d, coeff.d, den_coeff.d);
921,915✔
587

588
                // Shift by neg_exponents
589
                for ( size_t i = 0; i < var_map.size(); i++ ) {
3,960,223✔
590
                        exponents[i] += neg_exponents[i];
3,038,308✔
591
                }
592

593
                // Read the denominator if there is one, and divide it out of the coefficient
594
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
921,915✔
595
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
48,468✔
596
                        // By construction, this is an exact division
597
                        fmpz_divexact(coeff.d, coeff.d, tmp.d);
32,454✔
598
                }
599

600
                // Push the term to the mpoly, remember to sort when finished! This is much faster than using
601
                // fmpz_mpoly_set_coeff_fmpz_ui when the terms arrive in the "wrong order".
602
                fmpz_mpoly_push_term_fmpz_ui(poly, coeff.d, (ulong*)exponents.data(), ctx);
921,915✔
603

604
                if ( !with_arghead && *term_stop == 0 ) {
921,915✔
605
                        break;
606
                }
607

608
        }
921,915✔
609
        // And now sort the mpoly
610
        fmpz_mpoly_sort_terms(poly, ctx);
77,763✔
611

612
        return arg_size;
77,763✔
613
}
77,763✔
614
/*
615
        #] flint::from_argument_mpoly :
616
        #[ flint::from_argument_poly :
617
*/
618
// Convert a FORM argument (or 0-terminated list of terms: with_arghead == false) to a
619
// (uni-variate) fmpz_poly_t poly. The "denominator" is return in denpoly, and contains the
620
// overall negative-power numeric and symbolic factor.
621
uint64_t flint::from_argument_poly(fmpz_poly_t poly, fmpz_poly_t denpoly, const WORD *args,
2,411,350✔
622
        const bool with_arghead) {
623

624
        // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
625
        // Make sure they are 0 to begin.
626
        fmpz_poly_set_si(poly, 0);
2,411,350✔
627
        fmpz_poly_set_si(denpoly, 0);
2,411,350✔
628

629
        // First check for "fast notation" arguments:
630
        if ( *args == -SNUMBER ) {
2,411,350✔
631
                fmpz_poly_set_si(poly, *(args+1));
23,604✔
632
                fmpz_poly_set_si(denpoly, 1);
23,604✔
633
                return 2;
23,604✔
634
        }
635
        
636
        if ( *args == -SYMBOL ) {
2,387,746✔
637
                // A "fast notation" SYMBOL has a power and coefficient of 1:
638
                fmpz_poly_set_coeff_si(poly, 1, 1);
375✔
639
                fmpz_poly_set_si(denpoly, 1);
375✔
640
                return 2;
375✔
641
        }
642

643
        // Now we can iterate through the terms of the argument. If we have
644
        // an ARGHEAD, we already know where to terminate. Otherwise we'll have
645
        // to loop until the terminating 0.
646
        const WORD* arg_stop = with_arghead ? args+args[0] : (WORD*)UINT64_MAX;
2,387,371✔
647
        uint64_t arg_size = 0;
2,349,259✔
648
        if ( with_arghead ) {
2,349,259✔
649
                arg_size = args[0];
2,349,259✔
650
                args += ARGHEAD;
2,349,259✔
651
        }
652

653
        // Search for numerical or symbol denominators to create "denpoly".
654
        flint::fmpz den_coeff, tmp;
2,387,371✔
655
        fmpz_set_si(den_coeff.d, 1);
2,387,371✔
656
        uint64_t neg_exponent = 0;
657

658
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
11,774,969✔
659

660
                const WORD* term_stop = term+term[0];
9,425,710✔
661
                const WORD  coeff_size = (term_stop)[-1];
9,425,710✔
662
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
9,425,710✔
663
                const WORD* t = term;
9,425,710✔
664

665
                t++; // skip over the total size entry
9,425,710✔
666
                if ( t == symbol_stop ) {
9,425,710✔
667
                        // Just a number, no symbols
668
                }
669
                else {
670
                        t++; // this entry is SYMBOL
7,068,447✔
671
                        t++; // this entry is the size of the symbol array
7,068,447✔
672
                        t++; // this is the first (and only) symbol code
7,068,447✔
673
                        if ( *t < 0 ) {
7,068,447✔
674
                                neg_exponent = MaX(neg_exponent, (uint64_t)(-(*t)) );
2,115✔
675
                        }
676
                }
677

678
                // Now check for a denominator in the coefficient:
679
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
9,425,710✔
680
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
29,814✔
681
                        // Record the LCM of the coefficient denominators:
682
                        fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
29,814✔
683
                }
684

685
                if ( *term_stop == 0 ) {
9,425,710✔
686
                        // + 1 for the terminating 0
687
                        arg_size = term_stop - args + 1;
38,112✔
688
                        break;
38,112✔
689
                }
690
        }
691
        // Assemble denpoly.
692
        fmpz_poly_set_coeff_fmpz(denpoly, neg_exponent, den_coeff.d);
2,387,371✔
693

694

695
        // For the term coefficients
696
        flint::fmpz coeff;
2,387,371✔
697

698
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
11,774,969✔
699

700
                const WORD* term_stop = term+term[0];
9,425,710✔
701
                const WORD  coeff_size = (term_stop)[-1];
9,425,710✔
702
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
9,425,710✔
703
                const WORD* t = term;
9,425,710✔
704

705
                uint64_t exponent = 0;
9,425,710✔
706

707
                t++; // skip over the total size entry
9,425,710✔
708
                if ( t == symbol_stop ) {
9,425,710✔
709
                        // Just a number, no symbols
710
                }
711
                else {
712
                        t++; // this entry is SYMBOL
7,068,447✔
713
                        t++; // this entry is the size of the symbol array
7,068,447✔
714
                        t++; // this is the first (and only) symbol code
7,068,447✔
715
                        exponent = *t++;
7,068,447✔
716
                }
717

718
                // Now read the coefficient
719
                flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
9,425,710✔
720

721
                // Multiply by denominator LCM
722
                fmpz_mul(coeff.d, coeff.d, den_coeff.d);
9,425,710✔
723

724
                // Shift by neg_exponent
725
                exponent += neg_exponent;
9,425,710✔
726

727
                // Read the denominator if there is one, and divide it out of the coefficient
728
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
9,425,710✔
729
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
44,334✔
730
                        // By construction, this is an exact division
731
                        fmpz_divexact(coeff.d, coeff.d, tmp.d);
29,814✔
732
                }
733

734
                // Add the term to the poly
735
                fmpz_poly_set_coeff_fmpz(poly, exponent, coeff.d);
9,425,710✔
736

737
                if ( *term_stop == 0 ) {
9,425,710✔
738
                        break;
739
                }
740
        }
741

742
        return arg_size;
2,387,371✔
743
}
2,387,371✔
744
/*
745
        #] flint::from_argument_poly :
746

747
        #[ flint::fmpz_get_form :
748
*/
749
// Write FORM's long integer representation of an fmpz at a, and put the number of WORDs at na.
750
// na carries the sign of the integer.
751
WORD flint::fmpz_get_form(fmpz_t z, WORD *a) {
19,426,928✔
752

753
        WORD na = 0;
19,426,928✔
754
        const int32_t sgn = fmpz_sgn(z);
19,426,928✔
755
        if ( sgn == -1 ) {
19,426,928✔
756
                fmpz_neg(z, z);
2,739,933✔
757
        }
758
        const int64_t nlimbs = fmpz_size(z);
19,426,928✔
759

760
        // This works but is UB?
761
        //fmpz_get_ui_array(reinterpret_cast<uint64_t*>(a), nlimbs, z);
762

763
        // Use fixed-size functions to get limb data where possible. These probably cover most real
764
        // cases. 
765
        if ( nlimbs == 1 ) {
19,426,928✔
766
                const uint64_t limb = fmpz_get_ui(z);
16,515,405✔
767
                a[0] = (WORD)(limb & 0xFFFFFFFF);
16,515,405✔
768
                na++;
16,515,405✔
769
                a[1] = (WORD)(limb >> BITSINWORD);
16,515,405✔
770
                if ( a[1] != 0 ) {
16,515,405✔
771
                        na++;
1,028,730✔
772
                }
773
        }
774
        else if ( nlimbs == 2 ) {
2,911,523✔
775
                uint64_t limb_hi = 0, limb_lo = 0;
878,310✔
776
                fmpz_get_uiui((ulong*)&limb_hi, (ulong*)&limb_lo, z);
878,310✔
777
                a[0] = (WORD)(limb_lo & 0xFFFFFFFF);
878,310✔
778
                        na++;
878,310✔
779
                a[1] = (WORD)(limb_lo >> BITSINWORD);
878,310✔
780
                        na++;
878,310✔
781
                a[2] = (WORD)(limb_hi & 0xFFFFFFFF);
878,310✔
782
                        na++;
878,310✔
783
                a[3] = (WORD)(limb_hi >> BITSINWORD);
878,310✔
784
                if ( a[3] != 0 ) {
878,310✔
785
                        na++;
308,922✔
786
                }
787
        }
788
        else {
789
                vector<uint64_t> limb_data(nlimbs, 0);
2,033,213✔
790
                fmpz_get_ui_array((ulong*)limb_data.data(), (slong)nlimbs, z);
2,033,213✔
791
                for ( long i = 0; i < nlimbs; i++ ) {
16,021,253✔
792
                        a[2*i] = (WORD)(limb_data[i] & 0xFFFFFFFF);
13,988,040✔
793
                        na++;
13,988,040✔
794
                        a[2*i+1] = (WORD)(limb_data[i] >> BITSINWORD);
13,988,040✔
795
                        if ( a[2*i+1] != 0 || i < (nlimbs-1) ) {
13,988,040✔
796
                                // The final limb might fit in a single 32bit WORD. Only
797
                                // increment na if the final WORD is non zero.
798
                                na++;
12,926,401✔
799
                        }
800
                }
801
        }
2,033,213✔
802

803
        // And now put the sign in the number of limbs
804
        if ( sgn == -1 ) {
19,426,928✔
805
                na = -na;
2,739,933✔
806
        }
807

808
        return na;
19,426,928✔
809
}
810
/*
811
        #] flint::fmpz_get_form :
812
        #[ flint::fmpz_set_form :
813
*/
814
// Create an fmpz directly from FORM's long integer representation. fmpz uses 64bit unsigned limbs,
815
// but FORM uses 32bit UWORDs on 64bit architectures so we can't use fmpz_set_ui_array directly.
816
void flint::fmpz_set_form(fmpz_t z, UWORD *a, WORD na) {
11,373,733✔
817

818
        if ( na == 0 ) {
11,373,733✔
819
                fmpz_zero(z);
×
820
                return;
×
821
        }
822

823
        // Negative na represenents a negative number
824
        int32_t sgn = 1;
11,373,733✔
825
        if ( na < 0 ) {
11,373,733✔
826
                sgn = -1;
2,764,843✔
827
                na = -na;
2,764,843✔
828
        }
829

830
        // Remove padding. FORM stores numerators and denominators with equal numbers of limbs but we
831
        // don't need to do this within the fmpz. It is not necessary to do this really, the fmpz
832
        // doesn't add zero limbs unnecessarily, but we might be able to avoid creating the limb_data
833
        // array below.
834
        while ( a[na-1] == 0 ) {
11,375,020✔
835
                na--;
1,287✔
836
        }
837

838
        // If the number fits in fixed-size fmpz_set functions, we don't need to use additional memory
839
        // to convert to uint64_t. These probably cover most real cases.
840
        if ( na == 1 ) {
11,373,733✔
841
                fmpz_set_ui(z, (uint64_t)a[0]);
8,066,267✔
842
        }
843
        else if ( na == 2 ) {
3,307,466✔
844
                fmpz_set_ui(z, (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
1,074,376✔
845
        }
846
        else if ( na == 3 ) {
2,233,090✔
847
                fmpz_set_uiui(z, (uint64_t)a[2], (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
574,489✔
848
        }
849
        else if ( na == 4 ) {
1,658,601✔
850
                fmpz_set_uiui(z, (((uint64_t)a[3])<<BITSINWORD) + (uint64_t)a[2],
296,924✔
851
                        (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
296,924✔
852
        }
853
        else {
854
                const int32_t nlimbs = (na+1)/2;
1,361,677✔
855
                vector<uint64_t> limb_data(nlimbs, 0);
1,361,677✔
856
                for ( int32_t i = 0; i < nlimbs; i++ ) {
8,934,343✔
857
                        if ( 2*i+1 <= na-1 ) {
7,572,666✔
858
                                limb_data[i] = (uint64_t)a[2*i] + (((uint64_t)a[2*i+1])<<BITSINWORD);
6,827,122✔
859
                        }
860
                        else {
861
                                limb_data[i] = (uint64_t)a[2*i];
745,544✔
862
                        }
863
                }
864
                fmpz_set_ui_array(z, (ulong*)limb_data.data(), nlimbs);
1,361,677✔
865
        }
1,361,677✔
866

867
        // Finally set the sign.
868
        if ( sgn == -1 ) {
11,373,733✔
869
                fmpz_neg(z, z);
2,764,843✔
870
        }
871

872
        return;
873
}
874
/*
875
        #] flint::fmpz_set_form :
876

877
        #[ flint::gcd_mpoly :
878
*/
879
// Return a pointer to a buffer containing the GCD of the 0-terminated term lists at a and b.
880
// If must_fit_term, this should be a TermMalloc buffer. Otherwise Malloc1 the buffer.
881
// For multi-variate cases.
882
WORD* flint::gcd_mpoly(PHEAD const WORD *a, const WORD *b, const WORD must_fit_term,
918✔
883
        const var_map_t &var_map) {
884

885
        flint::mpoly_ctx ctx(var_map.size());
918✔
886
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d), gcd(ctx.d);
918✔
887

888
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
918✔
889
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
918✔
890

891
        // denpa, denpb must be 1:
892
        if ( fmpz_mpoly_is_one(denpa.d, ctx.d) != 1 ) {
918✔
893
                MLOCK(ErrorMessageLock);
×
894
                MesPrint("flint::gcd_mpoly: error: denpa != 1");
×
895
                MUNLOCK(ErrorMessageLock);
×
896
                Terminate(-1);
×
897
        }
898
        if ( fmpz_mpoly_is_one(denpb.d, ctx.d) != 1 ) {
918✔
899
                MLOCK(ErrorMessageLock);
×
900
                MesPrint("flint::gcd_mpoly: error: denpb != 1");
×
901
                MUNLOCK(ErrorMessageLock);
×
902
                Terminate(-1);
×
903
        }
904

905
        // poly returns pa if pa == pb, regardless of the lcoeff sign
906
        if ( fmpz_mpoly_equal(pa.d, pb.d, ctx.d) ) {
918✔
907
                fmpz_mpoly_set(gcd.d, pa.d, ctx.d);
420✔
908
        }
909
        else {
910
                // We need some gymnastics to have the same sign conventions as the poly class. It takes the
911
                // integer or univar content out of a,b, with the convention that the content sign matches
912
                // the lcoeff sign. Since FORM has already taken out the content, we are left with +-1. In
913
                // Flint, the content always has a positive sign so here we should find +1. Check this:
914
                flint::mpoly tmp(ctx.d);
498✔
915
                fmpz_mpoly_term_content(tmp.d, pa.d, ctx.d);
498✔
916
                if ( fmpz_mpoly_is_one(tmp.d, ctx.d) != 1 ) {
498✔
917
                        MLOCK(ErrorMessageLock);
×
918
                        MesPrint("flint::gcd_mpoly: error: content of 1st arg != 1");
×
919
                        MUNLOCK(ErrorMessageLock);
×
920
                        Terminate(-1);
×
921
                }
922
                fmpz_mpoly_term_content(tmp.d, pb.d, ctx.d);
498✔
923
                if ( fmpz_mpoly_is_one(tmp.d, ctx.d) != 1 ) {
498✔
924
                        MLOCK(ErrorMessageLock);
×
925
                        MesPrint("flint::gcd_mpoly: error: content of 2nd arg != 1");
×
926
                        MUNLOCK(ErrorMessageLock);
×
927
                        Terminate(-1);
×
928
                }
929

930
                // The poly class now divides the content out of a,b so that they have a positive lcoeff.
931
                // Then it multiplies the final gcd (which is given a positive lcoeff also) by
932
                // gcd(cont a, cont b). There it has gcd(1,1) = gcd(-1,1) = gcd(1,-1) = 1, and
933
                // gcd(-1,-1) = -1 (because of the pa==pb early return). So: if both input polys have a
934
                // negative lcoeff, we will flip the sign in the final result.
935
                bool flip_sign = 0;
498✔
936
                if ( ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(pa.d, 0, ctx.d)) == -1 ) &&
642✔
937
                        ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(pb.d, 0, ctx.d)) == -1 ) ) {
144✔
938
                        flip_sign = 1;
939
                }
940

941
                fmpz_mpoly_gcd(gcd.d, pa.d, pb.d, ctx.d);
498✔
942
                if ( flip_sign ) {
498✔
943
                        fmpz_mpoly_neg(gcd.d, gcd.d, ctx.d);
69✔
944
                }
945
        }
498✔
946

947
        // This is freed by the caller
948
        WORD *res;
918✔
949
        if ( must_fit_term ) {
918✔
950
                res = TermMalloc("flint::gcd_mpoly");
×
951
        }
952
        else {
953
                // Determine the size of the GCD by passing write = false.
954
                const bool with_arghead = false;
918✔
955
                const bool write = false;
918✔
956
                const uint64_t prev_size = 0;
918✔
957
                const uint64_t gcd_size = (uint64_t)flint::to_argument_mpoly(BHEAD NULL,
918✔
958
                        with_arghead, must_fit_term, write, prev_size, gcd.d, var_map, ctx.d);
959
                
960
                res = (WORD *)Malloc1(sizeof(WORD)*gcd_size, "flint::gcd_mpoly");
918✔
961
        }
962

963
        const bool with_arghead = false;
918✔
964
        const bool write = true;
918✔
965
        const uint64_t prev_size = 0;
918✔
966
        flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
918✔
967
                var_map, ctx.d);
968

969
        return res;
918✔
970
}
918✔
971
/*
972
        #] flint::gcd_mpoly :
973
        #[ flint::gcd_poly :
974
*/
975
// Return a pointer to a buffer containing the GCD of the 0-terminated term lists at a and b.
976
// If must_fit_term, this should be a TermMalloc buffer. Otherwise Malloc1 the buffer.
977
// For uni-variate cases.
978
WORD* flint::gcd_poly(PHEAD const WORD *a, const WORD *b, const WORD must_fit_term,
1,518✔
979
        const var_map_t &var_map) {
980

981
        flint::poly pa, pb, denpa, denpb, gcd;
1,518✔
982

983
        flint::from_argument_poly(pa.d, denpa.d, a, false);
1,518✔
984
        flint::from_argument_poly(pb.d, denpb.d, b, false);
1,518✔
985

986
        // denpa, denpb must be 1:
987
        if ( fmpz_poly_is_one(denpa.d) != 1 ) {
1,518✔
988
                MLOCK(ErrorMessageLock);
×
989
                MesPrint("flint::gcd_poly: error: denpa != 1");
×
990
                MUNLOCK(ErrorMessageLock);
×
991
                Terminate(-1);
×
992
        }
993
        if ( fmpz_poly_is_one(denpb.d) != 1 ) {
1,518✔
994
                MLOCK(ErrorMessageLock);
×
995
                MesPrint("flint::gcd_poly: error: denpb != 1");
×
996
                MUNLOCK(ErrorMessageLock);
×
997
                Terminate(-1);
×
998
        }
999

1000
        // poly returns pa if pa == pb, regardless of the lcoeff sign
1001
        if ( fmpz_poly_equal(pa.d, pb.d) ) {
1,518✔
1002
                fmpz_poly_set(gcd.d, pa.d);
474✔
1003
        }
1004
        else {
1005
                // Here, we don't have to make any sign flips like the mpoly case, because poly's
1006
                // integer_gcd(1,1) = integer_gcd(-1,1) = integer_gcd(1,-1) = integer_gcd(-1,-1) = +1.
1007
                // Still, verify that the content is 1:
1008
                flint::fmpz tmp;
1,044✔
1009
                fmpz_poly_content(tmp.d, pa.d);
1,044✔
1010
                if ( fmpz_is_one(tmp.d) != 1 ) {
1,044✔
1011
                        MLOCK(ErrorMessageLock);
×
1012
                        MesPrint("flint::gcd_poly: error: content of 1st arg != 1");
×
1013
                        MUNLOCK(ErrorMessageLock);
×
1014
                        Terminate(-1);
×
1015
                }
1016
                fmpz_poly_content(tmp.d, pb.d);
1,044✔
1017
                if ( fmpz_is_one(tmp.d) != 1 ) {
1,044✔
1018
                        MLOCK(ErrorMessageLock);
×
1019
                        MesPrint("flint::gcd_poly: error: content of 2nd arg != 1");
×
1020
                        MUNLOCK(ErrorMessageLock);
×
1021
                        Terminate(-1);
×
1022
                }
1023

1024
                fmpz_poly_gcd(gcd.d, pa.d, pb.d);
1,044✔
1025
        }
1,044✔
1026

1027
        // This is freed by the caller
1028
        WORD *res;
1,518✔
1029
        if ( must_fit_term ) {
1,518✔
1030
                res = TermMalloc("flint::gcd_poly");
×
1031
        }
1032
        else {
1033
                // Determine the size of the GCD by passing write = false.
1034
                const bool with_arghead = false;
1,518✔
1035
                const bool write = false;
1,518✔
1036
                const uint64_t prev_size = 0;
1,518✔
1037
                const uint64_t gcd_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1,518✔
1038
                        with_arghead, must_fit_term, write, prev_size, gcd.d, var_map);
1039

1040
                res = (WORD *)Malloc1(sizeof(WORD)*gcd_size, "flint::gcd_poly");
1,518✔
1041
        }
1042

1043
        const bool with_arghead = false;
1,518✔
1044
        const uint64_t prev_size = 0;
1,518✔
1045
        const bool write = true;
1,518✔
1046
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
1,518✔
1047
                var_map);
1048

1049
        return res;
1,518✔
1050
}
1,518✔
1051
/*
1052
        #] flint::gcd_poly :
1053

1054
        #[ flint::get_variables :
1055
*/
1056
// Get the list of symbols which appear in the vector of expressions. These are polyratfun
1057
// numerators, denominators or expressions from calls to gcd_ etc. Return this list as a map
1058
// between indices and symbol codes.
1059
// TODO FACTORSYMBOL last?
1060
flint::var_map_t flint::get_variables(const vector <WORD *> &es, const bool with_arghead,
853,366✔
1061
        const bool sort_vars) {
1062

1063
        int32_t num_vars = 0;
853,366✔
1064
        // To be used if we sort by highest degree, as the polu code does.
1065
        vector<int32_t> degrees;
853,366✔
1066
        var_map_t var_map;
853,366✔
1067

1068
        // extract all variables
1069
        for ( size_t ei = 0; ei < es.size(); ei++ ) {
3,368,891✔
1070
                WORD *e = es[ei];
2,515,558✔
1071

1072
                // fast notation
1073
                if ( *e == -SNUMBER ) {
2,515,558✔
1074
                }
1075
                else if ( *e == -SYMBOL ) {
2,465,791✔
1076
                        if ( !var_map.count(e[1]) ) {
624✔
1077
                                var_map[e[1]] = num_vars++;
196✔
1078
                                degrees.push_back(1);
196✔
1079
                        }
1080
                }
1081
                // JD: Here we need to check for non-symbol/number terms in fast notation.
1082
                else if ( *e < 0 ) {
2,465,167✔
1083
                        MLOCK(ErrorMessageLock);
27✔
1084
                        MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
27✔
1085
                        MUNLOCK(ErrorMessageLock);
27✔
1086
                        Terminate(1);
27✔
1087
                }
1088
                else {
1089
                        for ( WORD i = with_arghead ? ARGHEAD:0; with_arghead ? i < e[0]:e[i] != 0; i += e[i] ) {
15,277,905✔
1090
                                if ( i+1 < i+e[i]-ABS(e[i+e[i]-1]) && e[i+1] != SYMBOL ) {
10,347,631✔
1091
                                        MLOCK(ErrorMessageLock);
6✔
1092
                                        MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
6✔
1093
                                        MUNLOCK(ErrorMessageLock);
6✔
1094
                                        Terminate(1);
6✔
1095
                                }
1096

1097
                                for ( WORD j = i+3; j<i+e[i]-ABS(e[i+e[i]-1]); j += 2 ) {
20,062,755✔
1098
                                        if ( !var_map.count(e[j]) ) {
9,715,130✔
1099
                                                var_map[e[j]] = num_vars++;
908,247✔
1100
                                                degrees.push_back(e[j+1]);
908,247✔
1101
                                        }
1102
                                        else {
1103
                                                degrees[var_map[e[j]]] = MaX(degrees[var_map[e[j]]], e[j+1]);
8,806,883✔
1104
                                        }
1105
                                }
1106
                        }
1107
                }
1108
        }
1109

1110
        if ( sort_vars ) {
853,333✔
1111
                // bubble sort variables in decreasing order of degree
1112
                // (this seems better for factorization)
1113
                for ( size_t i = 0; i < var_map.size(); i++ ) {
1,704,107✔
1114
                        for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
1,073,389✔
1115
                                if ( degrees[j] < degrees[j+1] ) {
198,900✔
1116
                                        swap(degrees[j], degrees[j+1]);
28,437✔
1117

1118
                                        // Find the map keys associated with the values we want to swap
1119
                                        uint32_t j0 = 0;
28,437✔
1120
                                        uint32_t j1 = 0;
28,437✔
1121
                                        for ( auto x: var_map ) {
139,751✔
1122
                                                if ( x.second == j ) {
111,314✔
1123
                                                        j0 = x.first;
28,437✔
1124
                                                }
1125
                                                else if ( x.second == j+1 ) {
82,877✔
1126
                                                        j1 = x.first;
28,437✔
1127
                                                }
1128
                                        }
1129
                                        swap(var_map.at(j0), var_map.at(j1));
28,437✔
1130
                                }
1131
                        }
1132
                }
1133
        }
1134
        // Otherwise, sort lexicographically in FORM's ordering
1135
        else {
1136
                for ( size_t i = 0; i < var_map.size(); i++ ) {
57,669✔
1137
                        for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
74,238✔
1138
                                uint32_t j0 = 0;
40,284✔
1139
                                uint32_t j1 = 0;
40,284✔
1140
                                for ( auto x: var_map ) {
213,858✔
1141
                                        if ( x.second == j ) {
173,574✔
1142
                                                j0 = x.first;
40,284✔
1143
                                        }
1144
                                        else if ( x.second == j+1 ) {
133,290✔
1145
                                                j1 = x.first;
40,284✔
1146
                                        }
1147
                                }
1148
                                if ( j0 > j1 ) {
40,284✔
1149
                                        swap(var_map.at(j0), var_map.at(j1));
6,834✔
1150
                                }
1151
                        }
1152
                }
1153
        }
1154

1155
        return var_map;
853,333✔
1156
}
853,333✔
1157
/*
1158
        #] flint::get_variables :
1159

1160
        #[ flint::inverse_poly :
1161
*/
1162
WORD* flint::inverse_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1,137✔
1163

1164
        flint::poly pa, pb, denpa, denpb;
1,137✔
1165

1166
        flint::from_argument_poly(pa.d, denpa.d, a, false);
1,137✔
1167
        flint::from_argument_poly(pb.d, denpb.d, b, false);
1,137✔
1168

1169
        // fmpz_poly_xgcd is undefined if the content of pa and pb are not 1. Take the content out.
1170
        // fmpz_poly_content gives the non-negative content and fmpz_poly_primitive normalizes to a
1171
        // non-negative lcoeff, so we need to add the sign to the content if the polys have a negative
1172
        // lcoeff. We don't need to keep the content of pb, it is a numerical multiple of the modulus.
1173
        flint::fmpz content_a, resultant;
1,137✔
1174
        flint::poly inverse, tmp;
1,137✔
1175
        fmpz_poly_content(content_a.d, pa.d);
1,137✔
1176
        if ( fmpz_sgn(fmpz_poly_lead(pa.d)) == -1 ) {
1,137✔
1177
                fmpz_neg(content_a.d, content_a.d);
537✔
1178
        }
1179
        fmpz_poly_primitive_part(pa.d, pa.d);
1,137✔
1180
        fmpz_poly_primitive_part(pb.d, pb.d);
1,137✔
1181

1182
        // Special cases:
1183
        // Possibly strange that we give 1 for inverse_(x1,1) but here we take MMA's convention.
1184
        if ( fmpz_poly_is_one(pa.d) && fmpz_poly_is_one(pb.d) ) {
1,371✔
1185
                fmpz_poly_one(inverse.d);
54✔
1186
                fmpz_one(resultant.d);
54✔
1187
        }
1188
        else if ( fmpz_poly_is_one(pb.d) ) {
1,083✔
1189
                fmpz_poly_zero(inverse.d);
45✔
1190
                fmpz_one(resultant.d);
45✔
1191
        }
1192
        else {
1193
                // Now use the extended Euclidean algorithm to find inverse, resultant, tmp of the Bezout
1194
                // identity: inverse*pa + tmp*pb = resultant. Then inverse/resultant is the multiplicative
1195
                // inverse of pa mod pb. We'll divide by resultant in the denscale argument of to_argument_poly.
1196
                fmpz_poly_xgcd(resultant.d, inverse.d, tmp.d, pa.d, pb.d);
1,038✔
1197

1198
                // If the resultant is zero, the inverse does not exist:
1199
                if ( fmpz_is_zero(resultant.d) ) {
1,038✔
1200
                        MLOCK(ErrorMessageLock);
3✔
1201
                        MesPrint("flint::inverse_poly error: inverse does not exist");
3✔
1202
                        MUNLOCK(ErrorMessageLock);
3✔
1203
                        Terminate(-1);
3✔
1204
                }
1205
        }
1206

1207
        // Multiply inverse by denpa. denpb is a numerical multiple of the modulus, so doesn't matter.
1208
        // We also need to divide by content_a, which we do in the denscale argument of to_argument_poly
1209
        // by multiplying resultant by content_a here.
1210
        fmpz_poly_mul(inverse.d, inverse.d, denpa.d);
1,134✔
1211
        fmpz_mul(resultant.d, resultant.d, content_a.d);
1,134✔
1212

1213

1214
        WORD* res;
1,134✔
1215
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1216
        // the "scale" argument of to_argument_poly since resultant might not be 1.
1217
        const bool with_arghead = false;
1,134✔
1218
        bool write = false;
1,134✔
1219
        const bool must_fit_term = false;
1,134✔
1220
        const uint64_t prev_size = 0;
1,134✔
1221
        const uint64_t res_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1,134✔
1222
                with_arghead, must_fit_term, write, prev_size, inverse.d, var_map, resultant.d);
1223
        res = (WORD*)Malloc1(sizeof(WORD)*res_size, "flint::inverse_poly");
1,134✔
1224

1225
        write = true;
1,134✔
1226
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, inverse.d,
1,134✔
1227
                var_map, resultant.d);
1228

1229
        return res;
1,134✔
1230
}
1,134✔
1231
/*
1232
        #] flint::inverse_poly :
1233

1234
        #[ flint::mul_mpoly :
1235
*/
1236
// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1237
// For multi-variate cases.
1238
WORD* flint::mul_mpoly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1,824✔
1239

1240
        flint::mpoly_ctx ctx(var_map.size());
1,824✔
1241
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
1,824✔
1242

1243
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
1,824✔
1244
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
1,824✔
1245

1246
        // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1247
        if ( fmpz_mpoly_is_fmpz(denpa.d, ctx.d) != 1 ) {
1,824✔
1248
                MLOCK(ErrorMessageLock);
×
1249
                MesPrint("flint::mul_mpoly: error: denpa is non-constant");
×
1250
                MUNLOCK(ErrorMessageLock);
×
1251
                Terminate(-1);
×
1252
        }
1253
        if ( fmpz_mpoly_is_fmpz(denpb.d, ctx.d) != 1 ) {
1,824✔
1254
                MLOCK(ErrorMessageLock);
×
1255
                MesPrint("flint::mul_mpoly: error: denpb is non-constant");
×
1256
                MUNLOCK(ErrorMessageLock);
×
1257
                Terminate(-1);
×
1258
        }
1259

1260
        // Multiply numerators, store result in pa
1261
        fmpz_mpoly_mul(pa.d, pa.d, pb.d, ctx.d);
1,824✔
1262
        // Multiply denominators, store result in denpa, and convert to an fmpz:
1263
        fmpz_mpoly_mul(denpa.d, denpa.d, denpb.d, ctx.d);
1,824✔
1264
        flint::fmpz den;
1,824✔
1265
        fmpz_mpoly_get_fmpz(den.d, denpa.d, ctx.d);
1,824✔
1266

1267
        WORD* res;
1,824✔
1268
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1269
        // the "scale" argument of to_argument_mpoly since den might not be 1.
1270
        const bool with_arghead = false;
1,824✔
1271
        bool write = false;
1,824✔
1272
        const bool must_fit_term = false;
1,824✔
1273
        const uint64_t prev_size = 0;
1,824✔
1274
        const uint64_t mul_size = (uint64_t)flint::to_argument_mpoly(BHEAD NULL,
1,824✔
1275
                with_arghead, must_fit_term, write, prev_size, pa.d, var_map, ctx.d, den.d);
1276
        res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_mpoly");
1,824✔
1277

1278
        write = true;
1,824✔
1279
        flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
1,824✔
1280
                var_map, ctx.d, den.d);
1281

1282
        return res;
1,824✔
1283
}
1,824✔
1284
/*
1285
        #] flint::mul_mpoly :
1286
        #[ flint::mul_poly :
1287
*/
1288
// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1289
// For uni-variate cases.
1290
WORD* flint::mul_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
5,379✔
1291

1292
        flint::poly pa, pb, denpa, denpb;
5,379✔
1293

1294
        flint::from_argument_poly(pa.d, denpa.d, a, false);
5,379✔
1295
        flint::from_argument_poly(pb.d, denpb.d, b, false);
5,379✔
1296

1297
        // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1298
        if ( fmpz_poly_degree(denpa.d) != 0 ) {
5,379✔
1299
                MLOCK(ErrorMessageLock);
×
1300
                MesPrint("flint::mul_poly: error: denpa is non-constant");
×
1301
                MUNLOCK(ErrorMessageLock);
×
1302
                Terminate(-1);
×
1303
        }
1304
        if ( fmpz_poly_degree(denpb.d) != 0 ) {
5,379✔
1305
                MLOCK(ErrorMessageLock);
×
1306
                MesPrint("flint::mul_poly: error: denpb is non-constant");
×
1307
                MUNLOCK(ErrorMessageLock);
×
1308
                Terminate(-1);
×
1309
        }
1310

1311
        // Multiply numerators, store result in pa
1312
        fmpz_poly_mul(pa.d, pa.d, pb.d);
5,379✔
1313
        // Multiply denominators, store result in denpa, and convert to an fmpz:
1314
        fmpz_poly_mul(denpa.d, denpa.d, denpb.d);
5,379✔
1315
        flint::fmpz den;
5,379✔
1316
        fmpz_poly_get_coeff_fmpz(den.d, denpa.d, 0);
5,379✔
1317

1318
        WORD* res;
5,379✔
1319
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1320
        // the "scale" argument of to_argument_poly since den might not be 1.
1321
        const bool with_arghead = false;
5,379✔
1322
        bool write = false;
5,379✔
1323
        const bool must_fit_term = false;
5,379✔
1324
        const uint64_t prev_size = 0;
5,379✔
1325
        const uint64_t mul_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
5,379✔
1326
                with_arghead, must_fit_term, write, prev_size, pa.d, var_map, den.d);
1327
        res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_poly");
5,379✔
1328

1329
        write = true;
5,379✔
1330
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
5,379✔
1331
                var_map, den.d);
1332

1333
        return res;
5,379✔
1334
}
5,379✔
1335
/*
1336
        #] flint::mul_poly :
1337

1338
        #[ flint::ratfun_add_mpoly :
1339
*/
1340
// Add the multi-variate FORM rational polynomials at t1 and t2. The result is written at out.
1341
void flint::ratfun_add_mpoly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
6,402✔
1342
        const var_map_t &var_map) {
1343

1344
        flint::mpoly_ctx ctx(var_map.size());
6,402✔
1345
        flint::mpoly gcd(ctx.d), num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d);
6,402✔
1346

1347
        flint::ratfun_read_mpoly(t1, num1.d, den1.d, var_map, ctx.d);
6,402✔
1348
        flint::ratfun_read_mpoly(t2, num2.d, den2.d, var_map, ctx.d);
6,402✔
1349

1350
        if ( fmpz_mpoly_cmp(den1.d, den2.d, ctx.d) != 0 ) {
6,402✔
1351
                fmpz_mpoly_gcd_cofactors(gcd.d, den1.d, den2.d, den1.d, den2.d, ctx.d);
3,413✔
1352

1353
                fmpz_mpoly_mul(num1.d, num1.d, den2.d, ctx.d);
3,413✔
1354
                fmpz_mpoly_mul(num2.d, num2.d, den1.d, ctx.d);
3,413✔
1355

1356
                fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
3,413✔
1357
                fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
3,413✔
1358
                fmpz_mpoly_mul(den1.d, den1.d, gcd.d,  ctx.d);
3,413✔
1359
        }
1360
        else {
1361
                fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
2,989✔
1362
        }
1363

1364
        // Finally divide out any common factors between the resulting num1, den1:
1365
        fmpz_mpoly_gcd_cofactors(gcd.d, num1.d, den1.d, num1.d, den1.d, ctx.d);
6,402✔
1366

1367
        flint::util::fix_sign_fmpz_mpoly_ratfun(num1.d, den1.d, ctx.d);
6,402✔
1368

1369
        // Result in FORM notation:
1370
        *out++ = AR.PolyFun;
6,402✔
1371
        WORD* args_size = out++;
6,402✔
1372
        WORD* args_flag = out++;
6,402✔
1373
        *args_flag = 0; // clean prf
6,402✔
1374
        FILLFUN3(out); // Remainder of funhead, if it is larger than 3
6,402✔
1375

1376
        const bool with_arghead = true;
6,402✔
1377
        const bool must_fit_term = true;
6,402✔
1378
        const bool write = true;
6,402✔
1379
        // prev_size + 4, to account for final term size and coeff of "1/1"
1380
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
6,402✔
1381
                num1.d, var_map, ctx.d);
1382
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
6,402✔
1383
                den1.d, var_map, ctx.d);
1384

1385
        *args_size = out - args_size + 1; // The +1 is to include the function ID
6,402✔
1386
        AT.WorkPointer = out;
6,402✔
1387
}
6,402✔
1388
/*
1389
        #] flint::ratfun_add_mpoly :
1390
        #[ flint::ratfun_add_poly :
1391
*/
1392
// Add the uni-variate FORM rational polynomials at t1 and t2. The result is written at out.
1393
void flint::ratfun_add_poly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
369,949✔
1394
        const var_map_t &var_map) {
1395

1396
        flint::poly gcd, num1, den1, num2, den2;
369,949✔
1397

1398
        flint::ratfun_read_poly(t1, num1.d, den1.d);
369,949✔
1399
        flint::ratfun_read_poly(t2, num2.d, den2.d);
369,949✔
1400

1401
        if ( fmpz_poly_equal(den1.d, den2.d) == 0 ) {
369,949✔
1402
                flint::util::simplify_fmpz_poly(den1.d, den2.d, gcd.d);
363,772✔
1403

1404
                fmpz_poly_mul(num1.d, num1.d, den2.d);
363,772✔
1405
                fmpz_poly_mul(num2.d, num2.d, den1.d);
363,772✔
1406

1407
                fmpz_poly_add(num1.d, num1.d, num2.d);
363,772✔
1408
                fmpz_poly_mul(den1.d, den1.d, den2.d);
363,772✔
1409
                fmpz_poly_mul(den1.d, den1.d, gcd.d);
363,772✔
1410
        }
1411
        else {
1412
                fmpz_poly_add(num1.d, num1.d, num2.d);
6,177✔
1413
        }
1414

1415
        // Finally divide out any common factors between the resulting num1, den1:
1416
        flint::util::simplify_fmpz_poly(num1.d, den1.d, gcd.d);
369,949✔
1417

1418
        flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
369,949✔
1419

1420
        // Result in FORM notation:
1421
        *out++ = AR.PolyFun;
369,949✔
1422
        WORD* args_size = out++;
369,949✔
1423
        WORD* args_flag = out++;
369,949✔
1424
        *args_flag = 0; // clean prf
369,949✔
1425
        FILLFUN3(out); // Remainder of funhead, if it is larger than 3
369,949✔
1426

1427
        const bool with_arghead = true;
369,949✔
1428
        const bool must_fit_term = true;
369,949✔
1429
        const bool write = true;
369,949✔
1430
        // prev_size + 4, to account for final term size and coeff of "1/1"
1431
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
369,949✔
1432
                num1.d, var_map);
1433
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
369,949✔
1434
                den1.d, var_map);
1435

1436
        *args_size = out - args_size + 1; // The +1 is to include the function ID
369,949✔
1437
        AT.WorkPointer = out;
369,949✔
1438
}
369,949✔
1439
/*
1440
        #] flint::ratfun_add_poly :
1441

1442
        #[ flint::ratfun_normalize_mpoly :
1443
*/
1444
// Multiply and simplify occurrences of the multi-variate FORM rational polynomials found in term.
1445
// The final term is written in place, with the rational polynomial at the end.
1446
void flint::ratfun_normalize_mpoly(PHEAD WORD *term, const var_map_t &var_map) {
21,936✔
1447

1448
        // The length of the coefficient
1449
        const WORD ncoeff = (term + *term)[-1];
21,936✔
1450
        // The end of the term data, before the coefficient:
1451
        const WORD *tstop = term + *term - ABS(ncoeff);
21,936✔
1452

1453
        flint::mpoly_ctx ctx(var_map.size());
21,936✔
1454
        flint::mpoly num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d), gcd(ctx.d);
21,936✔
1455

1456
        // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1457
        flint::fmpz tmpNum, tmpDen;
21,936✔
1458
        flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
21,936✔
1459
        flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
23,208✔
1460
        fmpz_mpoly_set_fmpz(num1.d, tmpNum.d, ctx.d);
21,936✔
1461
        fmpz_mpoly_set_fmpz(den1.d, tmpDen.d, ctx.d);
21,936✔
1462

1463
        // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1464
        // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1465
        // go at the end.
1466
        WORD* term_size = term;
21,936✔
1467
        WORD* s = term + 1;
21,936✔
1468
        for ( WORD *t = term + 1; t < tstop; ) {
66,315✔
1469
                if ( *t == AR.PolyFun ) {
44,382✔
1470
                        flint::ratfun_read_mpoly(t, num2.d, den2.d, var_map, ctx.d);
32,163✔
1471

1472
                        // get gcd of num1,den2 and num2,den1 and then assemble
1473
                        fmpz_mpoly_gcd_cofactors(gcd.d, num1.d, den2.d, num1.d, den2.d, ctx.d);
32,160✔
1474
                        fmpz_mpoly_gcd_cofactors(gcd.d, num2.d, den1.d, num2.d, den1.d, ctx.d);
32,160✔
1475

1476
                        fmpz_mpoly_mul(num1.d, num1.d, num2.d, ctx.d);
32,160✔
1477
                        fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
32,160✔
1478

1479
                        t += t[1];
32,160✔
1480
                }
1481

1482
                else {
1483
                        // Not a PolyFun, just copy or skip over
1484
                        WORD i = t[1];
12,219✔
1485
                        if ( s != t ) { NCOPY(s,t,i); }
82,296✔
1486
                        else { t += i; s += i; }
3,678✔
1487
                }
1488
        }
1489

1490
        flint::util::fix_sign_fmpz_mpoly_ratfun(num1.d, den1.d, ctx.d);
21,933✔
1491

1492
        // Result in FORM notation:
1493
        WORD* out = s;
21,933✔
1494
        *out++ = AR.PolyFun;
21,933✔
1495
        WORD* args_size = out++;
21,933✔
1496
        WORD* args_flag = out++;
21,933✔
1497
        *args_flag &= ~MUSTCLEANPRF;
21,933✔
1498

1499
        const bool with_arghead = true;
21,933✔
1500
        const bool must_fit_term = true;
21,933✔
1501
        const bool write = true;
21,933✔
1502
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
21,933✔
1503
                num1.d, var_map, ctx.d);
1504
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
21,930✔
1505
                den1.d, var_map, ctx.d);
1506

1507
        *args_size = out - args_size + 1; // The +1 is to include the function ID
21,927✔
1508

1509
        // +3 for the coefficient of 1/1, which is added after the check
1510
        if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
21,927✔
1511
                MLOCK(ErrorMessageLock);
3✔
1512
                MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
3✔
1513
                MUNLOCK(ErrorMessageLock);
3✔
1514
                Terminate(-1);
3✔
1515
        }
1516

1517
        *out++ = 1;
21,924✔
1518
        *out++ = 1;
21,924✔
1519
        *out++ = 3; // the term's coefficient is now 1/1
21,924✔
1520

1521
        *term_size = out - term_size;
21,924✔
1522
}
21,924✔
1523
/*
1524
        #] flint::ratfun_normalize_mpoly :
1525
        #[ flint::ratfun_normalize_poly :
1526
*/
1527
// Multiply and simplify occurrences of the uni-variate FORM rational polynomials found in term.
1528
// The final term is written in place, with the rational polynomial at the end.
1529
void flint::ratfun_normalize_poly(PHEAD WORD *term, const var_map_t &var_map) {
428,850✔
1530

1531
        // The length of the coefficient
1532
        const WORD ncoeff = (term + *term)[-1];
428,850✔
1533
        // The end of the term data, before the coefficient:
1534
        const WORD *tstop = term + *term - ABS(ncoeff);
428,850✔
1535

1536
        flint::poly num1, den1, num2, den2, gcd;
428,850✔
1537

1538
        // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1539
        flint::fmpz tmpNum, tmpDen;
428,850✔
1540
        flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
428,850✔
1541
        flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
429,222✔
1542
        fmpz_poly_set_fmpz(num1.d, tmpNum.d);
428,850✔
1543
        fmpz_poly_set_fmpz(den1.d, tmpDen.d);
428,850✔
1544

1545
        // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1546
        // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1547
        // go at the end.
1548
        WORD* term_size = term;
428,850✔
1549
        WORD* s = term + 1;
428,850✔
1550
        for ( WORD *t = term + 1; t < tstop; ) {
1,292,169✔
1551
                if ( *t == AR.PolyFun ) {
863,325✔
1552
                        flint::ratfun_read_poly(t, num2.d, den2.d);
446,724✔
1553

1554
                        // get gcd of num1,den2 and num2,den1 and then assemble
1555
                        flint::util::simplify_fmpz_poly(num1.d, den2.d, gcd.d);
446,718✔
1556
                        flint::util::simplify_fmpz_poly(num2.d, den1.d, gcd.d);
446,718✔
1557

1558
                        fmpz_poly_mul(num1.d, num1.d, num2.d);
446,718✔
1559
                        fmpz_poly_mul(den1.d, den1.d, den2.d);
446,718✔
1560

1561
                        t += t[1];
446,718✔
1562
                }
1563

1564
                else {
1565
                        // Not a PolyFun, just copy or skip over
1566
                        WORD i = t[1];
416,601✔
1567
                        if ( s != t ) { NCOPY(s,t,i); }
425,742✔
1568
                        else { t += i; s += i; }
415,116✔
1569
                }
1570
        }
1571

1572
        flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
428,844✔
1573

1574
        // Result in FORM notation:
1575
        WORD* out = s;
428,844✔
1576
        *out++ = AR.PolyFun;
428,844✔
1577
        WORD* args_size = out++;
428,844✔
1578
        WORD* args_flag = out++;
428,844✔
1579
        *args_flag &= ~MUSTCLEANPRF;
428,844✔
1580

1581
        const bool with_arghead = true;
428,844✔
1582
        const bool must_fit_term = true;
428,844✔
1583
        const bool write = true;
428,844✔
1584
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
428,844✔
1585
                num1.d, var_map);
1586
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
428,841✔
1587
                den1.d, var_map);
1588

1589
        *args_size = out - args_size + 1; // The +1 is to include the function ID
428,838✔
1590

1591
        // +3 for the coefficient of 1/1, which is added after the check
1592
        if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
428,838✔
1593
                MLOCK(ErrorMessageLock);
3✔
1594
                MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
3✔
1595
                MUNLOCK(ErrorMessageLock);
3✔
1596
                Terminate(-1);
3✔
1597
        }
1598

1599
        *out++ = 1;
428,835✔
1600
        *out++ = 1;
428,835✔
1601
        *out++ = 3; // the term's coefficient is now 1/1
428,835✔
1602

1603
        *term_size = out - term_size;
428,835✔
1604
}
428,835✔
1605
/*
1606
        #] flint::ratfun_normalize_poly :
1607

1608
        #[ flint::ratfun_read_mpoly :
1609
*/
1610
// Read the multi-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1611
// denominator.
1612
void flint::ratfun_read_mpoly(const WORD *a, fmpz_mpoly_t num, fmpz_mpoly_t den,
44,967✔
1613
        const var_map_t &var_map, fmpz_mpoly_ctx_t ctx) {
1614

1615
        // The end of the arguments:
1616
        const WORD* arg_stop = a+a[1];
44,967✔
1617

1618
        const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
44,967✔
1619

1620
        a += FUNHEAD;
44,967✔
1621
        if ( a >= arg_stop ) {
44,967✔
1622
                MLOCK(ErrorMessageLock);
×
1623
                MesPrint("ERROR: PolyRatFun cannot have zero arguments");
×
1624
                MUNLOCK(ErrorMessageLock);
×
1625
                Terminate(-1);
×
1626
        }
1627

1628
        // Polys to collect the "den of the num" and "den of the den".
1629
        // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1630
        flint::mpoly den_num(ctx), den_den(ctx);
44,967✔
1631

1632
        // Read the numerator
1633
        flint::from_argument_mpoly(num, den_num.d, a, true, var_map, ctx);
44,967✔
1634
        NEXTARG(a);
44,967✔
1635

1636
        if ( a < arg_stop ) {
44,967✔
1637
                // Read the denominator
1638
                flint::from_argument_mpoly(den, den_den.d, a, true, var_map, ctx);
44,967✔
1639
                NEXTARG(a);
44,967✔
1640
        }
1641
        else {
1642
                // The denominator is 1
1643
                MLOCK(ErrorMessageLock);
×
1644
                MesPrint("implement this");
×
1645
                MUNLOCK(ErrorMessageLock);
×
1646
                Terminate(-1);
×
1647
        }
1648
        if ( a < arg_stop ) {
44,967✔
1649
                MLOCK(ErrorMessageLock);
3✔
1650
                MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
3✔
1651
                MUNLOCK(ErrorMessageLock);
3✔
1652
                Terminate(-1);
3✔
1653
        }
1654

1655
        // Multiply the num by den_den and den by den_num:
1656
        fmpz_mpoly_mul(num, num, den_den.d, ctx);
44,964✔
1657
        fmpz_mpoly_mul(den, den, den_num.d, ctx);
44,964✔
1658

1659
        if ( must_normalize ) {
44,964✔
1660
                flint::mpoly gcd(ctx);
22,176✔
1661
                fmpz_mpoly_gcd_cofactors(gcd.d, num, den, num, den, ctx);
22,176✔
1662
        }
22,176✔
1663
}
44,964✔
1664
/*
1665
        #] flint::ratfun_read_mpoly :
1666
        #[ flint::ratfun_read_poly :
1667
*/
1668
// Read the uni-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1669
// denominator.
1670
void flint::ratfun_read_poly(const WORD *a, fmpz_poly_t num, fmpz_poly_t den) {
1,186,622✔
1671

1672
        // The end of the arguments:
1673
        const WORD* arg_stop = a+a[1];
1,186,622✔
1674

1675
        const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
1,186,622✔
1676

1677
        a += FUNHEAD;
1,186,622✔
1678
        if ( a >= arg_stop ) {
1,186,622✔
1679
                MLOCK(ErrorMessageLock);
3✔
1680
                MesPrint("ERROR: PolyRatFun cannot have zero arguments");
3✔
1681
                MUNLOCK(ErrorMessageLock);
3✔
1682
                Terminate(-1);
3✔
1683
        }
1684

1685
        // Polys to collect the "den of the num" and "den of the den".
1686
        // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1687
        flint::poly den_num, den_den;
1,186,619✔
1688

1689
        // Read the numerator
1690
        flint::from_argument_poly(num, den_num.d, a, true);
1,186,619✔
1691
        NEXTARG(a);
1,186,619✔
1692

1693
        if ( a < arg_stop ) {
1,186,619✔
1694
                // Read the denominator
1695
                flint::from_argument_poly(den, den_den.d, a, true);
1,186,619✔
1696
                NEXTARG(a);
1,186,619✔
1697
        }
1698
        else {
1699
                // The denominator is 1
1700
                MLOCK(ErrorMessageLock);
×
1701
                MesPrint("implement this");
×
1702
                MUNLOCK(ErrorMessageLock);
×
1703
                Terminate(-1);
×
1704
        }
1705
        if ( a < arg_stop ) {
1,186,619✔
1706
                MLOCK(ErrorMessageLock);
3✔
1707
                MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
3✔
1708
                MUNLOCK(ErrorMessageLock);
3✔
1709
                Terminate(-1);
3✔
1710
        }
1711

1712
        // Multiply the num by den_den and den by den_num:
1713
        fmpz_poly_mul(num, num, den_den.d);
1,186,616✔
1714
        fmpz_poly_mul(den, den, den_num.d);
1,186,616✔
1715

1716
        if ( must_normalize ) {
1,186,616✔
1717
                flint::poly gcd;
435,546✔
1718
                flint::util::simplify_fmpz_poly(num, den, gcd.d);
435,546✔
1719
        }
435,546✔
1720
}
1,186,616✔
1721
/*
1722
        #] flint::ratfun_read_poly :
1723

1724
        #[ flint::startup_init :
1725
*/
1726
// The purpose of this function is it work around threading issues in flint, reported in
1727
// https://github.com/flintlib/flint/issues/1652 and fixed in
1728
// https://github.com/flintlib/flint/pull/1658 . This implies versions prior to 3.1.0,
1729
// but at least 3.0.0 (when gr was introduced).
1730
void flint::startup_init(void) {
1,095✔
1731

1732
#if __FLINT_RELEASE >= 30000
1733
        // Here we initialize some gr contexts so that their method tables are populated. Crashes have
1734
        // otherwise been observed due to these two contexts in particular.
1735
        fmpz_t dummy_fmpz;
1,095✔
1736
        fmpz_init(dummy_fmpz);
1,095✔
1737
        fmpz_set_si(dummy_fmpz, 19);
1,095✔
1738

1739
        gr_ctx_t dummy_gr_ctx_fmpz_mod;
1,095✔
1740
        gr_ctx_init_fmpz_mod(dummy_gr_ctx_fmpz_mod, dummy_fmpz);
1,095✔
1741
        gr_ctx_clear(dummy_gr_ctx_fmpz_mod);
1,095✔
1742

1743
        gr_ctx_t dummy_gr_ctx_nmod;
1,095✔
1744
        gr_ctx_init_nmod(dummy_gr_ctx_nmod, 19);
1,095✔
1745
        gr_ctx_clear(dummy_gr_ctx_nmod);
1,095✔
1746

1747
        fmpz_clear(dummy_fmpz);
1,095✔
1748
#endif
1749
}
1,095✔
1750
/*
1751
        #] flint::startup_init :
1752

1753
        #[ flint::to_argument_mpoly :
1754
*/
1755
// Convert a fmpz_mpoly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
1756
// If the caller is building an output term, prev_size contains the size of the term so far, to
1757
// check that the output fits in AM.MaxTer if must_fit_term.
1758
// All coefficients will be divided by denscale (which might just be 1).
1759
// If write is false, we never write to out but only track the total would-be size. This lets this
1760
// function be repurposed as a "size of FORM notation" function without duplicating the code.
1761
#define IFW(x) { if ( write ) {x;} }
1762
uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
71,067✔
1763
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
1764
        const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const fmpz_t denscale) {
1765

1766
        // out is modified later, keep the pointer at entry
1767
        const WORD* out_entry = out;
71,067✔
1768

1769
        // Track the total size written. We could do this with pointer differences, but if
1770
        // write == false we don't write to or move out to be able to find the size that way.
1771
        uint64_t ws = 0;
71,067✔
1772

1773
        // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1774
        if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
71,067✔
1775
                MLOCK(ErrorMessageLock);
×
1776
                MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
×
1777
                MUNLOCK(ErrorMessageLock);
×
1778
                Terminate(-1);
×
1779
        }
1780

1781
        // Create the inverse of var_map, so we don't have to search it for each symbol written
1782
        var_map_t var_map_inv;
71,067✔
1783
        for ( auto x: var_map ) {
267,042✔
1784
                var_map_inv[x.second] = x.first;
195,975✔
1785
        }
1786

1787
        vector<int64_t> exponents(var_map.size());
71,067✔
1788
        const int64_t n_terms = fmpz_mpoly_length(poly, ctx);
71,067✔
1789

1790
        if ( n_terms == 0 ) {
71,067✔
1791
                if ( with_arghead ) {
4,911✔
1792
                        IFW(*out++ = -SNUMBER); ws++;
2,421✔
1793
                        IFW(*out++ = 0); ws++;
2,421✔
1794
                        return ws;
2,421✔
1795
                }
1796
                else {
1797
                        IFW(*out++ = 0); ws++;
2,490✔
1798
                        return ws;
2,490✔
1799
                }
1800
        }
1801

1802
        // For dividing out denscale
1803
        flint::fmpz coeff, den, gcd;
66,156✔
1804

1805
        // The mpoly might be constant or a single symbol with coeff 1. Use fast notation if possible.
1806
        if ( with_arghead && n_terms == 1 ) {
66,156✔
1807

1808
                if ( fmpz_mpoly_is_fmpz(poly, ctx) ) {
19,109✔
1809
                        // The mpoly is constant. Use fast notation if the number is an integer and small enough:
1810

1811
                        fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
13,953✔
1812
                        fmpz_set(den.d, denscale);
13,953✔
1813
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
13,953✔
1814

1815
                        if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
13,953✔
1816
                                const int64_t fast_coeff = fmpz_get_si(coeff.d);
13,953✔
1817
                                // While ">=", could work here, FORM does not use fast notation for INT_MIN
1818
                                if ( fast_coeff > INT32_MIN && fast_coeff <= INT32_MAX ) {
13,953✔
1819
                                        IFW(*out++ = -SNUMBER); ws++;
13,953✔
1820
                                        IFW(*out++ = (WORD)fast_coeff); ws++;
13,953✔
1821
                                        return ws;
13,953✔
1822
                                }
1823
                        }
1824
                }
1825

1826
                else {
1827
                        fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
5,156✔
1828
                        fmpz_set(den.d, denscale);
5,156✔
1829
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
5,156✔
1830

1831
                        if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
5,156✔
1832
                                // The coefficient is one. Now check the symbol powers:
1833

1834
                                fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, 0, ctx);
954✔
1835
                                int64_t use_fast = 0;
1836
                                uint32_t fast_symbol = 0;
1837

1838
                                for ( size_t i = 0; i < var_map.size(); i++ ) {
3,078✔
1839
                                        if ( exponents[i] == 1 ) fast_symbol = var_map_inv[i];
2,124✔
1840
                                        use_fast += exponents[i];
2,124✔
1841
                                }
1842

1843
                                // use_fast has collected the total degree. If it is 1, then fast_symbol holds the code
1844
                                if ( use_fast == 1 ) {
954✔
1845
                                        IFW(*out++ = -SYMBOL); ws++;
183✔
1846
                                        IFW(*out++ = fast_symbol); ws++;
183✔
1847
                                        return ws;
183✔
1848
                                }
1849
                        }
1850
                }
1851
        }
1852

1853

1854
        WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_mpoly");
52,020✔
1855
        WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
52,020✔
1856

1857
        WORD* arg_size = 0;
52,020✔
1858
        WORD* arg_flag = 0;
52,020✔
1859
        if ( with_arghead ) {
52,020✔
1860
                IFW(arg_size = out++); ws++; // total arg size
40,146✔
1861
                IFW(arg_flag = out++); ws++;
40,146✔
1862
                IFW(*arg_flag = 0); // clean argument
40,146✔
1863
        }
1864

1865
        for ( int64_t i = 0; i < n_terms; i++ ) {
1,271,838✔
1866

1867
                fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, i, ctx);
1,219,824✔
1868

1869
                fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, i, ctx);
1,219,824✔
1870
                fmpz_set(den.d, denscale);
1,219,824✔
1871
                flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1,219,824✔
1872

1873
                uint32_t num_symbols = 0;
1874
                for ( size_t j = 0; j < var_map.size(); j++ ) {
5,394,154✔
1875
                        if ( exponents[j] != 0 ) { num_symbols += 1; }
4,174,330✔
1876
                }
1877

1878
                // Convert the coefficient, write in temporary space
1879
                const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
1,219,824✔
1880
                const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
1,219,824✔
1881
                const WORD coeff_size = [num_size, den_size] () -> WORD {
2,439,648✔
1882
                        WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
1,219,824✔
1883
                        return size * SGN(num_size) * SGN(den_size);
1,219,824✔
1884
                }();
1,219,824✔
1885

1886
                // Now we have the number of symbols and the coeff size, we can determine the output size.
1887
                // Check it fits if necessary: term size, num,den of "coeff_size", +1 for total coeff size
1888
                uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
1,219,824✔
1889
                if ( num_symbols ) {
1,219,824✔
1890
                        // symbols header, code,power of each symbol:
1891
                        current_size += 2 + 2*num_symbols;
1,208,688✔
1892
                }
1893
                if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
1,219,824✔
1894
                        MLOCK(ErrorMessageLock);
6✔
1895
                        MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
6✔
1896
                        MUNLOCK(ErrorMessageLock);
6✔
1897
                        Terminate(-1);
6✔
1898
                }
1899

1900
                WORD* term_size = 0;
1,219,818✔
1901
                IFW(term_size = out++); ws++;
1,219,818✔
1902
                if ( num_symbols ) {
1,219,818✔
1903
                        IFW(*out++ = SYMBOL); ws++;
1,208,682✔
1904
                        WORD* symbol_size = 0;
1,208,682✔
1905
                        IFW(symbol_size = out++); ws++;
1,208,682✔
1906
                        IFW(*symbol_size = 2);
1,208,682✔
1907

1908
                        for ( size_t j = 0; j < var_map.size(); j++ ) {
5,355,337✔
1909
                                if ( exponents[j] != 0 ) {
4,146,655✔
1910
                                        IFW(*out++ = var_map_inv[j]); ws++;
3,869,987✔
1911
                                        IFW(*out++ = exponents[j]); ws++;
3,869,987✔
1912
                                        IFW(*symbol_size += 2);
3,869,987✔
1913
                                }
1914
                        }
1915
                }
1916

1917
                // Copy numerator
1918
                for ( WORD j = 0; j < ABS(num_size); j++ ) {
2,576,802✔
1919
                        IFW(*out++ = tmp_coeff[j]); ws++;
1,356,984✔
1920
                }
1921
                for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
1,228,488✔
1922
                        IFW(*out++ = 0); ws++;
8,670✔
1923
                }
1924
                // Copy denominator
1925
                for ( WORD j = 0; j < ABS(den_size); j++ ) {
2,472,132✔
1926
                        IFW(*out++ = tmp_den[j]); ws++;
1,252,314✔
1927
                }
1928
                for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
1,333,158✔
1929
                        IFW(*out++ = 0); ws++;
113,340✔
1930
                }
1931

1932
                IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
1,219,818✔
1933
                IFW(if ( coeff_size < 0 ) { *out = -(*out); });
849,822✔
1934
                IFW(out++); ws++;
849,822✔
1935

1936
                IFW(*term_size = out - term_size);
1,219,818✔
1937
        }
1938

1939
        if ( with_arghead ) {
52,014✔
1940
                IFW(*arg_size = out - arg_size);
40,140✔
1941
                if ( write ) {
40,140✔
1942
                        // Sort into form highfirst ordering
1943
                        flint::form_sort(BHEAD (WORD*)(out_entry));
40,140✔
1944
                }
1945
        }
1946
        else {
1947
                // with no arghead, we write a terminating zero
1948
                IFW(*out++ = 0); ws++;
11,874✔
1949
        }
1950

1951
        NumberFree(tmp_coeff, "flint::to_argument_mpoly");
52,014✔
1952
        NumberFree(tmp_den, "flint::to_argument_mpoly");
52,014✔
1953

1954
        return ws;
52,014✔
1955
}
71,061✔
1956

1957
// If no denscale argument is supplied, just set it to 1 and call the usual function
1958
uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
58,713✔
1959
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
1960
        const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx) {
1961

1962
        flint::fmpz tmp;
58,713✔
1963
        fmpz_set_ui(tmp.d, 1);
58,713✔
1964

1965
        uint64_t ret = flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write,
58,713✔
1966
                prev_size, poly, var_map, ctx, tmp.d);
1967

1968
        return ret;
58,707✔
1969
}
58,707✔
1970
/*
1971
        #] flint::to_argument_mpoly :
1972
        #[ flint::to_argument_poly :
1973
*/
1974
// Convert a fmpz_poly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
1975
// If the caller is building an output term, prev_size contains the size of the term so far, to
1976
// check that the output fits in AM.MaxTer if must_fit_term.
1977
// All coefficients will be divided by denscale (which might just be 1).
1978
// If write is false, we never write to out but only track the total would-be size. This lets this
1979
// function be repurposed as a "size of FORM notation" function without duplicating the code.
1980
uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
1,635,689✔
1981
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
1982
        const var_map_t &var_map, const fmpz_t denscale) {
1983

1984
        // Track the total size written. We could do this with pointer differences, but if
1985
        // write == false we don't write to or move out to be able to find the size that way.
1986
        uint64_t ws = 0;
1,635,689✔
1987

1988
        // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1989
        if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
1,635,689✔
1990
                MLOCK(ErrorMessageLock);
×
1991
                MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
×
1992
                MUNLOCK(ErrorMessageLock);
×
1993
                Terminate(-1);
×
1994
        }
1995

1996
        // Create the inverse of var_map, so we don't have to search it for each symbol written
1997
        var_map_t var_map_inv;
1,635,689✔
1998
        for ( auto x: var_map ) {
3,257,146✔
1999
                var_map_inv[x.second] = x.first;
1,621,457✔
2000
        }
2001

2002
        const int64_t n_terms = fmpz_poly_length(poly);
1,635,689✔
2003

2004
        // The poly is zero
2005
        if ( n_terms == 0 ) {
1,635,689✔
2006
                if ( with_arghead ) {
5,886✔
2007
                        IFW(*out++ = -SNUMBER); ws++;
912✔
2008
                        IFW(*out++ = 0); ws++;
912✔
2009
                        return ws;
912✔
2010
                }
2011
                else {
2012
                        IFW(*out++ = 0); ws++;
4,974✔
2013
                        return ws;
4,974✔
2014
                }
2015
        }
2016

2017
        // For dividing out denscale
2018
        flint::fmpz coeff, den, gcd;
1,629,803✔
2019

2020
        // The poly is constant, use fast notation if the coefficient is integer and small enough
2021
        if ( with_arghead && n_terms == 1 ) {
1,629,803✔
2022

2023
                fmpz_poly_get_coeff_fmpz(coeff.d, poly, 0);
21,603✔
2024
                fmpz_set(den.d, denscale);
21,603✔
2025
                flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
21,603✔
2026

2027
                if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
21,603✔
2028
                        const long fast_coeff = fmpz_get_si(coeff.d);
21,603✔
2029
                        // While ">=", could work here, FORM does not use fast notation for INT_MIN
2030
                        if ( fast_coeff > INT_MIN && fast_coeff <= INT_MAX ) {
21,603✔
2031
                                IFW(*out++ = -SNUMBER); ws++;
21,603✔
2032
                                IFW(*out++ = (WORD)fast_coeff); ws++;
21,603✔
2033
                                return ws;
21,603✔
2034
                        }
2035
                }
2036
        }
2037

2038
        // The poly might be a single symbol with coeff 1, use fast notation if so.
2039
        if ( with_arghead && n_terms == 2 ) {
1,608,200✔
2040
                if ( fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, 0)) ) {
811,698✔
2041
                        // The constant term is zero
2042

2043
                        fmpz_poly_get_coeff_fmpz(coeff.d, poly, 1);
1,053✔
2044
                        fmpz_set(den.d, denscale);
1,053✔
2045
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1,053✔
2046

2047
                        if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
1,053✔
2048
                                // Single symbol with coeff 1. Use fast notation:
2049
                                IFW(*out++ = -SYMBOL); ws++;
366✔
2050
                                IFW(*out++ = var_map_inv[0]); ws++;
366✔
2051
                                return ws;
366✔
2052
                        }
2053
                }
2054
        }
2055

2056
        WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_poly");
1,607,834✔
2057
        WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
1,607,834✔
2058

2059
        WORD* arg_size = 0;
1,607,834✔
2060
        WORD* arg_flag = 0;
1,607,834✔
2061
        if ( with_arghead ) {
1,607,834✔
2062
                IFW(arg_size = out++); ws++; // total arg size
1,574,702✔
2063
                IFW(arg_flag = out++); ws++;
1,574,702✔
2064
                IFW(*arg_flag = 0); // clean argument
1,574,702✔
2065
        }
2066

2067
        // In reverse, since we want a "highfirst" output
2068
        for ( int64_t i = n_terms-1; i >= 0; i-- ) {
10,445,666✔
2069

2070
                // fmpz_poly is dense, there might be many zero coefficients:
2071
                if ( !fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)) ) {
8,837,838✔
2072

2073
                        fmpz_poly_get_coeff_fmpz(coeff.d, poly, i);
8,493,640✔
2074
                        fmpz_set(den.d, denscale);
8,493,640✔
2075
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
8,493,640✔
2076

2077
                        // Convert the coefficient, write in temporary space
2078
                        const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
8,493,640✔
2079
                        const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
8,493,640✔
2080
                        const WORD coeff_size = [num_size, den_size] () -> WORD {
16,987,280✔
2081
                                WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
8,493,640✔
2082
                                return size * SGN(num_size) * SGN(den_size);
8,493,640✔
2083
                        }();
8,493,640✔
2084

2085
                        // Now we have the coeff size, we can determine the output size
2086
                        // Check it fits if necessary: symbol code,power, num,den of "coeff_size",
2087
                        // +1 for total coeff size
2088
                        uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
8,493,640✔
2089
                        if ( i > 0 ) {
8,493,640✔
2090
                                // and also symbols header, code,power of the symbol
2091
                                current_size += 4;
6,907,031✔
2092
                        }
2093
                        if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
8,493,640✔
2094
                                MLOCK(ErrorMessageLock);
6✔
2095
                                MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
6✔
2096
                                MUNLOCK(ErrorMessageLock);
6✔
2097
                                Terminate(-1);
6✔
2098
                        }
2099

2100
                        WORD* term_size = 0;
8,493,634✔
2101
                        IFW(term_size = out++); ws++;
8,493,634✔
2102

2103
                        if ( i > 0 ) {
8,493,634✔
2104
                                IFW(*out++ = SYMBOL); ws++;
6,907,028✔
2105
                                IFW(*out++ = 4); ws++; // The symbol array size, it is univariate
6,907,028✔
2106
                                IFW(*out++ = var_map_inv[0]); ws++;
6,907,028✔
2107
                                IFW(*out++ = i); ws++;
6,907,028✔
2108
                        }
2109

2110
                        // Copy numerator
2111
                        for ( WORD j = 0; j < ABS(num_size); j++ ) {
44,787,004✔
2112
                                IFW(*out++ = tmp_coeff[j]); ws++;
36,293,370✔
2113
                        }
2114
                        for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
8,493,646✔
2115
                                IFW(*out++ = 0); ws++;
12✔
2116
                        }
2117
                        // Copy denominator
2118
                        for ( WORD j = 0; j < ABS(den_size); j++ ) {
16,993,370✔
2119
                                IFW(*out++ = tmp_den[j]); ws++;
8,499,736✔
2120
                        }
2121
                        for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
36,287,280✔
2122
                                IFW(*out++ = 0); ws++;
27,793,646✔
2123
                        }
2124

2125
                        IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
8,493,634✔
2126
                        IFW(if ( coeff_size < 0 ) { *out = -(*out); });
8,339,596✔
2127
                        IFW(out++); ws++;
8,339,596✔
2128

2129
                        IFW(*term_size = out - term_size);
8,493,634✔
2130
                }
2131

2132
        }
2133

2134
        if ( with_arghead ) {
1,607,828✔
2135
                IFW(*arg_size = out - arg_size);
1,574,696✔
2136
        }
2137
        else {
2138
                // with no arghead, we write a terminating zero
2139
                IFW(*out++ = 0); ws++;
33,132✔
2140
        }
2141

2142
        NumberFree(tmp_coeff, "flint::to_argument_poly");
1,607,828✔
2143
        NumberFree(tmp_den, "flint::to_argument_poly");
1,607,828✔
2144

2145
        return ws;
1,607,828✔
2146
}
3,265,480✔
2147

2148
// If no denscale argument is supplied, just set it to 1 and call the usual function
2149
uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
1,600,619✔
2150
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2151
        const var_map_t &var_map) {
2152

2153
        flint::fmpz tmp;
1,600,619✔
2154
        fmpz_set_ui(tmp.d, 1);
1,600,619✔
2155

2156
        uint64_t ret = flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, prev_size,
1,600,619✔
2157
                poly, var_map, tmp.d);
2158

2159
        return ret;
1,600,613✔
2160
}
1,600,613✔
2161
/*
2162
        #] flint::to_argument_poly :
2163
*/
2164

2165
// Utility functions
2166
/*
2167
        #[ flint::util::simplify_fmpz :
2168
*/
2169
// Divide the GCD out of num and den
2170
inline void flint::util::simplify_fmpz(fmpz_t num, fmpz_t den, fmpz_t gcd) {
9,755,229✔
2171
        fmpz_gcd(gcd, num, den);
9,755,229✔
2172
        if ( !fmpz_is_one(gcd) ) {
9,755,229✔
2173
                fmpz_divexact(num, num, gcd);
110,328✔
2174
                fmpz_divexact(den, den, gcd);
110,328✔
2175
        }
2176
}
9,755,229✔
2177
/*
2178
        #] flint::util::simplify_fmpz :
2179
        #[ flint::util::simplify_fmpz_poly :
2180
*/
2181
// Divide the GCD out of num and den
2182
inline void flint::util::simplify_fmpz_poly(fmpz_poly_t num, fmpz_poly_t den, fmpz_poly_t gcd) {
2,062,703✔
2183
        fmpz_poly_gcd(gcd, num, den);
2,062,703✔
2184
        if ( !fmpz_poly_is_one(gcd) ) {
2,062,703✔
2185
#if __FLINT_RELEASE >= 30100
2186
                // This should be faster than fmpz_poly_div, see https://github.com/flintlib/flint/pull/1766
2187
                fmpz_poly_divexact(num, num, gcd);
2188
                fmpz_poly_divexact(den, den, gcd);
2189
#else
2190
                fmpz_poly_div(num, num, gcd);
15,177✔
2191
                fmpz_poly_div(den, den, gcd);
15,177✔
2192
#endif
2193
        }
2194
}
2,062,703✔
2195
/*
2196
        #] flint::util::simplify_fmpz_poly :
2197
        #[ flint::util::fix_sign_fmpz_mpoly_ratfun :
2198
*/
2199
inline void flint::util::fix_sign_fmpz_mpoly_ratfun(fmpz_mpoly_t num, fmpz_mpoly_t den,
28,335✔
2200
        const fmpz_mpoly_ctx_t ctx) {
2201
        // Fix sign to align with poly: the leading denominator term should have a positive coeff
2202
        if ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(den, 0, ctx)) == -1 ) {
28,335✔
2203
                fmpz_mpoly_neg(num, num, ctx);
4,666✔
2204
                fmpz_mpoly_neg(den, den, ctx);
4,666✔
2205
        }
2206
}
28,335✔
2207
/*
2208
        #] flint::util::fix_sign_fmpz_mpoly_ratfun :
2209
        #[ flint::util::fix_sign_fmpz_poly_ratfun :
2210
*/
2211
inline void flint::util::fix_sign_fmpz_poly_ratfun(fmpz_poly_t num, fmpz_poly_t den) {
798,793✔
2212
        // Fix sign to align with poly: the leading denominator term should have a positive coeff
2213
        if ( fmpz_sgn(fmpz_poly_get_coeff_ptr(den, fmpz_poly_degree(den))) == -1 ) {
798,793✔
2214
                fmpz_poly_neg(num, num);
5,589✔
2215
                fmpz_poly_neg(den, den);
5,589✔
2216
        }
2217
}
798,793✔
2218
/*
2219
        #] flint::util::fix_sign_fmpz_poly_ratfun :
2220
*/
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc