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

form-dev / form / 18588042037

17 Oct 2025 09:07AM UTC coverage: 56.759% (-0.02%) from 56.779%
18588042037

Pull #729

github

web-flow
Merge b79cc4cda into 2aba64bb5
Pull Request #729: flint: perf: use mpoly for sparse univariate computations

6 of 7 new or added lines in 1 file covered. (85.71%)

19 existing lines in 2 files now uncovered.

46739 of 82347 relevant lines covered (56.76%)

5694590.73 hits per line

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

87.18
/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's univariate poly has a dense representation. For sufficiently sparse polynomials it is
42
 * faster to use mpoly instead, which is sparse. For a density <= this threshold we switch, where
43
 * the density defined as is "number of terms" / "maximum degree".
44
 */
45
#define UNIVARIATE_DENSITY_THR 0.02f
46

47
/*
48
        #[ flint::cleanup :
49
*/
50
void flint::cleanup(void) {
2,760✔
51
        flint_cleanup();
2,760✔
52
}
2,760✔
53
/*
54
        #] flint::cleanup :
55
        #[ flint::cleanup_master :
56
*/
57
void flint::cleanup_master(void) {
1,200✔
58
        flint_cleanup_master();
1,200✔
59
}
1,200✔
60
/*
61
        #] flint::cleanup_master :
62

63
        #[ flint::divmod_mpoly :
64
*/
65
WORD* flint::divmod_mpoly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
4,353✔
66
        const WORD must_fit_term, const var_map_t &var_map) {
67

68
        flint::mpoly_ctx ctx(var_map.size());
4,353✔
69
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
4,353✔
70

71
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
4,353✔
72
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
4,353✔
73

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

89

90
        flint::fmpz scale;
4,353✔
91
        flint::mpoly div(ctx.d), rem(ctx.d);
4,353✔
92

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

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

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

103

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

117

118
        // Write out the result
119
        write = true;
4,353✔
120
        if ( return_rem ) {
4,353✔
121
                (uint64_t)flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
2,532✔
122
                        rem.d, var_map, ctx.d, scale.d);
123
        }
124
        else {
125
                (uint64_t)flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
1,821✔
126
                        div.d, var_map, ctx.d, scale.d);
127
        }
128

129
        return res;
4,353✔
130
}
4,353✔
131
/*
132
        #] flint::divmod_mpoly :
133
        #[ flint::divmod_poly :
134
*/
135
WORD* flint::divmod_poly(PHEAD const WORD *a, const WORD *b, const bool return_rem,
11,022✔
136
        const WORD must_fit_term, const var_map_t &var_map) {
137

138
        flint::poly pa, pb, denpa, denpb;
11,022✔
139

140
        flint::from_argument_poly(pa.d, denpa.d, a, false);
11,022✔
141
        flint::from_argument_poly(pb.d, denpb.d, b, false);
11,022✔
142

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

158
        flint::fmpz scale;
11,022✔
159
        uint64_t scale_power = 0;
11,022✔
160
        flint::poly div, rem;
11,022✔
161

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

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

167
        // The quotient must be multiplied by the denominator of the divisor
168
        fmpz_poly_mul(div.d, div.d, denpb.d);
11,022✔
169

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

176

177
        // Determine the size of the output by passing write = false.
178
        const bool with_arghead = false;
11,022✔
179
        bool write = false;
11,022✔
180
        const uint64_t prev_size = 0;
11,022✔
181
        const uint64_t out_size = return_rem ?
11,022✔
182
                (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
5,646✔
183
                        rem.d, var_map, scale.d)
184
                :
185
                (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead, must_fit_term, write, prev_size,
5,376✔
186
                        div.d, var_map, scale.d)
11,022✔
187
                ;
188
        WORD* res = (WORD *)Malloc1(sizeof(WORD)*out_size, "flint::divrem_poly");
11,022✔
189

190

191
        // Write out the result
192
        write = true;
11,022✔
193
        if ( return_rem ) {
11,022✔
194
                (uint64_t)flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
5,646✔
195
                        rem.d, var_map, scale.d);
196
        }
197
        else {
198
                (uint64_t)flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size,
5,376✔
199
                        div.d, var_map, scale.d);
200
        }
201

202
        return res;
11,022✔
203
}
11,022✔
204
/*
205
        #] flint::divmod_poly :
206

207
        #[ flint::factorize_mpoly :
208
*/
209
WORD* flint::factorize_mpoly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
45✔
210
        const bool is_fun_arg, const var_map_t &var_map) {
211

212
        flint::mpoly_ctx ctx(var_map.size());
45✔
213
        flint::mpoly arg(ctx.d), den(ctx.d), base(ctx.d);
45✔
214

215
        flint::from_argument_mpoly(arg.d, den.d, argin, with_arghead, var_map, ctx.d);
45✔
216
        // The denominator must be 1:
217
        if ( fmpz_mpoly_is_one(den.d, ctx.d) != 1 ) {
45✔
218
                MLOCK(ErrorMessageLock);
×
219
                MesPrint("flint::factorize_mpoly error: den != 1");
×
220
                MUNLOCK(ErrorMessageLock);
×
221
                Terminate(-1);
×
222
        }
223

224

225
        // Now we can factor the mpoly:
226
        flint::mpoly_factor arg_fac(ctx.d);
45✔
227
        fmpz_mpoly_factor(arg_fac.d, arg.d, ctx.d);
45✔
228
        const int64_t num_factors = fmpz_mpoly_factor_length(arg_fac.d, ctx.d);
45✔
229

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

241
        // Construct the output. If argout is not NULL, we write the result there.
242
        // Otherwise, allocate memory.
243
        // The output is zero-terminated list of factors. If with_arghead, each has an arghead which
244
        // contains its size. Otherwise, the factors are zero separated.
245

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

249
        // Initially 1, for the final trailing 0.
250
        uint64_t output_size = 1;
45✔
251

252
        // For finding the highest symbol, in FORM's lexicographic ordering
253
        var_map_t var_map_inv;
45✔
254
        for ( auto x: var_map ) {
396✔
255
                var_map_inv[x.second] = x.first;
351✔
256
        }
257

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

261
        for ( int64_t i = 0; i < num_factors; i++ ) {
159✔
262
                fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
114✔
263
                const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
114✔
264

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

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

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

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

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

307
        // Now make the allocation if necessary:
308
        if ( argout == NULL ) {
45✔
309
                argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_mpoly");
33✔
310
        }
311

312

313
        // And now comes the actual output:
314
        WORD* old_argout = argout;
45✔
315

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

334
        for ( int64_t i = 0; i < num_factors; i++ ) {
159✔
335
                fmpz_mpoly_factor_get_base(base.d, arg_fac.d, i, ctx.d);
114✔
336
                const int64_t exponent = fmpz_mpoly_factor_get_exp_si(arg_fac.d, i, ctx.d);
114✔
337

338
                if ( base_sign[i] == -1 ) {
114✔
339
                        fmpz_mpoly_neg(base.d, base.d, ctx.d);
36✔
340
                }
341

342
                const bool write = true;
343
                for ( int64_t j = 0; j < exponent; j++ ) {
237✔
344
                        argout += flint::to_argument_mpoly(BHEAD argout, with_arghead, is_fun_arg, write,
123✔
345
                                argout-old_argout, base.d, var_map, ctx.d);
123✔
346
                }
347
        }
348
        // Final trailing zero to denote the end of the factors.
349
        *argout++ = 0;
45✔
350

351
        return old_argout;
90✔
352
}
45✔
353
/*
354
        #] flint::factorize_mpoly :
355
        #[ flint::factorize_poly :
356
*/
357
WORD* flint::factorize_poly(PHEAD const WORD *argin, WORD *argout, const bool with_arghead,
×
358
        const bool is_fun_arg, const var_map_t &var_map) {
359

360
        flint::poly arg, den;
×
361

362
        flint::from_argument_poly(arg.d, den.d, argin, with_arghead);
×
363
        // The denominator must be 1:
364
        if ( fmpz_poly_is_one(den.d) != 1 ) {
×
365
                MLOCK(ErrorMessageLock);
×
366
                MesPrint("flint::factorize_poly error: den != 1");
×
367
                MUNLOCK(ErrorMessageLock);
×
368
                Terminate(-1);
×
369
        }
370

371

372
        // Now we can factor the poly:
373
        flint::poly_factor arg_fac;
×
374
        fmpz_poly_factor(arg_fac.d, arg.d);
×
375
        // fmpz_poly_factor_t lacks some convenience functions which fmpz_mpoly_factor_t has.
376
        // I have worked out how to get the factors by looking at how fmpz_poly_factor_print works.
377
        const long num_factors = (arg_fac.d)->num;
×
378

379

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

390
                for ( long i = 0; i < num_factors; i++ ) {
×
391
                        fmpz_poly_struct* base = (arg_fac.d)->p + i;
×
392
                        
393
                        const long exponent = (arg_fac.d)->exp[i];
×
394

395
                        const bool write = false;
×
396
                        for ( long j = 0; j < exponent; j++ ) {
×
397
                                output_size += (uint64_t)flint::to_argument_poly(BHEAD NULL, with_arghead,
×
398
                                        is_fun_arg, write, 0, base, var_map);
399
                        }
400
                }
401

402
                argout = (WORD*)Malloc1(sizeof(WORD)*output_size, "flint::factorize_poly");
×
403
        }
404

405
        WORD* old_argout = argout;
×
406

407
        for ( long i = 0; i < num_factors; i++ ) {
×
408
                fmpz_poly_struct* base = (arg_fac.d)->p + i;
×
409
                
410
                const long exponent = (arg_fac.d)->exp[i];
×
411

412
                const bool write = true;
×
413
                for ( long j = 0; j < exponent; j++ ) {
×
414
                        argout += flint::to_argument_poly(BHEAD argout, with_arghead, is_fun_arg, write,
×
415
                                argout-old_argout, base, var_map);
×
416
                }
417
        }
418
        *argout = 0;
×
419

420
        return old_argout;
×
421
}
×
422
/*
423
        #] flint::factorize_poly :
424

425
        #[ flint::form_sort :
426
*/
427
// Sort terms using form's sorting routines. Uses a custom (faster) compare routine, since here
428
// only symbols can appear.
429
// This is a modified poly_sort from polywrap.cc.
430
void flint::form_sort(PHEAD WORD *terms) {
40,140✔
431

432
        if ( terms[0] < 0 ) {
40,140✔
433
                // Fast notation, there is nothing to do
434
                return;
435
        }
436

437
        const WORD oldsorttype = AR.SortType;
40,140✔
438
        AR.SortType = SORTHIGHFIRST;
40,140✔
439

440
        const WORD in_size = terms[0] - ARGHEAD;
40,140✔
441
        WORD out_size;
40,140✔
442

443
        if ( NewSort(BHEAD0) ) {
40,140✔
444
                Terminate(-1);
×
445
        }
446
        AR.CompareRoutine = (COMPAREDUMMY)(&CompareSymbols);
40,140✔
447

448
        // Make sure the symbols are in the right order within the terms
449
        for ( WORD i = ARGHEAD; i < terms[0]; i += terms[i] ) {
519,858✔
450
                if ( SymbolNormalize(terms+i) < 0 || StoreTerm(BHEAD terms+i) ) {
479,718✔
451
                        AR.SortType = oldsorttype;
×
452
                        AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
×
453
                        LowerSortLevel();
×
454
                        Terminate(-1);
×
455
                }
456
        }
457

458
        if ( ( out_size = EndSort(BHEAD terms+ARGHEAD, 1) ) < 0 ) {
40,140✔
459
                AR.SortType = oldsorttype;
×
460
                AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
×
461
                Terminate(-1);
×
462
        }
463

464
        // Check the final size
465
        if ( in_size != out_size  ) {
40,140✔
466
                MLOCK(ErrorMessageLock);
×
467
                MesPrint("flint::form_sort: error: unexpected sorted arg length change %d->%d", in_size,
×
468
                        out_size);
469
                MUNLOCK(ErrorMessageLock);
×
470
                Terminate(-1);
×
471
        }
472

473
        AR.SortType = oldsorttype;
40,140✔
474
        AR.CompareRoutine = (COMPAREDUMMY)(&Compare1);
40,140✔
475
        terms[1] = 0; // set dirty flag to zero
40,140✔
476
}
477
/*
478
        #] flint::form_sort :
479

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

488
        // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
489
        // Make sure they are 0 to begin.
490
        fmpz_mpoly_set_si(poly, 0, ctx);
104,169✔
491
        fmpz_mpoly_set_si(denpoly, 0, ctx);
104,169✔
492

493
        // First check for "fast notation" arguments:
494
        if ( *args == -SNUMBER ) {
104,169✔
495
                fmpz_mpoly_set_si(poly, *(args+1), ctx);
26,157✔
496
                fmpz_mpoly_set_si(denpoly, 1, ctx);
26,157✔
497
                return 2;
26,157✔
498
        }
499

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

505
                fmpz_mpoly_set_coeff_ui_ui(poly, (ulong)1, (ulong*)exponents.data(), ctx);
249✔
506
                fmpz_mpoly_set_ui(denpoly, (ulong)1, ctx);
249✔
507
                return 2;
249✔
508
        }
249✔
509

510

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

521

522
        // Search for numerical or symbol denominators to create "denpoly".
523
        flint::fmpz den_coeff, tmp;
77,763✔
524
        fmpz_set_si(den_coeff.d, 1);
77,763✔
525
        vector<uint64_t> neg_exponents(var_map.size(), 0);
77,763✔
526

527
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
985,434✔
528
                const WORD* term_stop = term+term[0];
921,894✔
529
                const WORD  coeff_size = (term_stop)[-1];
921,894✔
530
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
921,894✔
531
                const WORD* t = term;
921,894✔
532

533
                t++;
921,894✔
534
                if ( t == symbol_stop ) {
921,894✔
535
                        // Just a number, no symbols
536
                }
537
                else {
538
                        t++; // this entry is SYMBOL
900,963✔
539
                        t++; // this entry just has the size of the symbol array, but we can use symbol_stop
900,963✔
540

541
                        for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
3,547,534✔
542
                                if ( *(s+1) < 0 ) {
2,646,571✔
543
                                        neg_exponents[var_map.at(*s)] =
14,451✔
544
                                                MaX(neg_exponents[var_map.at(*s)], (uint64_t)(-(*(s+1))) );
28,902✔
545
                                }
546
                        }
547
                }
548

549
                // Now check for a denominator in the coefficient:
550
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
921,894✔
551
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
32,454✔
552
                        // Record the LCM of the coefficient denominators:
553
                        fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
32,454✔
554
                }
555

556
                if ( !with_arghead && *term_stop == 0 ) {
921,894✔
557
                        // + 1 for the terminating 0
558
                        arg_size = term_stop - args + 1;
14,223✔
559
                        break;
14,223✔
560
                }
561

562
        }
563
        // Assemble denpoly.
564
        fmpz_mpoly_set_coeff_fmpz_ui(denpoly, den_coeff.d, (ulong*)neg_exponents.data(), ctx);
77,763✔
565

566

567
        // For the term coefficients
568
        flint::fmpz coeff;
77,763✔
569

570
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
985,434✔
571
                const WORD* term_stop = term+term[0];
921,894✔
572
                const WORD  coeff_size = (term_stop)[-1];
921,894✔
573
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
921,894✔
574
                const WORD* t = term;
921,894✔
575

576
                vector<uint64_t> exponents(var_map.size(), 0);
921,894✔
577

578
                t++; // skip over the total size entry
921,894✔
579
                if ( t == symbol_stop ) {
921,894✔
580
                        // Just a number, no symbols
581
                }
582
                else {
583
                        t++; // this entry is SYMBOL
900,963✔
584
                        t++; // this entry just has the size of the symbol array, but we can use symbol_stop
900,963✔
585
                        for ( const WORD* s = t; s < symbol_stop; s += 2 ) {
3,547,534✔
586
                                exponents[var_map.at(*s)] = *(s+1);
2,646,571✔
587
                        }
588
                }
589
                // Now read the coefficient
590
                flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
921,894✔
591

592
                // Multiply by denominator LCM
593
                fmpz_mul(coeff.d, coeff.d, den_coeff.d);
921,894✔
594

595
                // Shift by neg_exponents
596
                for ( size_t i = 0; i < var_map.size(); i++ ) {
3,960,119✔
597
                        exponents[i] += neg_exponents[i];
3,038,225✔
598
                }
599

600
                // Read the denominator if there is one, and divide it out of the coefficient
601
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
921,894✔
602
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
48,468✔
603
                        // By construction, this is an exact division
604
                        fmpz_divexact(coeff.d, coeff.d, tmp.d);
32,454✔
605
                }
606

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

611
                if ( !with_arghead && *term_stop == 0 ) {
921,894✔
612
                        break;
613
                }
614

615
        }
921,894✔
616
        // And now sort the mpoly
617
        fmpz_mpoly_sort_terms(poly, ctx);
77,763✔
618

619
        return arg_size;
77,763✔
620
}
77,763✔
621
/*
622
        #] flint::from_argument_mpoly :
623
        #[ flint::from_argument_poly :
624
*/
625
// Convert a FORM argument (or 0-terminated list of terms: with_arghead == false) to a
626
// (uni-variate) fmpz_poly_t poly. The "denominator" is return in denpoly, and contains the
627
// overall negative-power numeric and symbolic factor.
628
uint64_t flint::from_argument_poly(fmpz_poly_t poly, fmpz_poly_t denpoly, const WORD *args,
5,353,458✔
629
        const bool with_arghead) {
630

631
        // Some callers re-use their poly, denpoly to avoid calling init/clear unnecessarily.
632
        // Make sure they are 0 to begin.
633
        fmpz_poly_set_si(poly, 0);
5,353,458✔
634
        fmpz_poly_set_si(denpoly, 0);
5,353,458✔
635

636
        // First check for "fast notation" arguments:
637
        if ( *args == -SNUMBER ) {
5,353,458✔
638
                fmpz_poly_set_si(poly, *(args+1));
828,686✔
639
                fmpz_poly_set_si(denpoly, 1);
828,686✔
640
                return 2;
828,686✔
641
        }
642
        
643
        if ( *args == -SYMBOL ) {
4,524,772✔
644
                // A "fast notation" SYMBOL has a power and coefficient of 1:
645
                fmpz_poly_set_coeff_si(poly, 1, 1);
29,515✔
646
                fmpz_poly_set_si(denpoly, 1);
29,515✔
647
                return 2;
29,515✔
648
        }
649

650
        // Now we can iterate through the terms of the argument. If we have
651
        // an ARGHEAD, we already know where to terminate. Otherwise we'll have
652
        // to loop until the terminating 0.
653
        const WORD* arg_stop = with_arghead ? args+args[0] : (WORD*)UINT64_MAX;
4,495,257✔
654
        uint64_t arg_size = 0;
4,457,145✔
655
        if ( with_arghead ) {
4,457,145✔
656
                arg_size = args[0];
4,457,145✔
657
                args += ARGHEAD;
4,457,145✔
658
        }
659

660
        // Search for numerical or symbol denominators to create "denpoly".
661
        flint::fmpz den_coeff, tmp;
4,495,257✔
662
        fmpz_set_si(den_coeff.d, 1);
4,495,257✔
663
        uint64_t neg_exponent = 0;
664

665
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
21,004,993✔
666

667
                const WORD* term_stop = term+term[0];
16,547,848✔
668
                const WORD  coeff_size = (term_stop)[-1];
16,547,848✔
669
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
16,547,848✔
670
                const WORD* t = term;
16,547,848✔
671

672
                t++; // skip over the total size entry
16,547,848✔
673
                if ( t == symbol_stop ) {
16,547,848✔
674
                        // Just a number, no symbols
675
                }
676
                else {
677
                        t++; // this entry is SYMBOL
12,538,327✔
678
                        t++; // this entry is the size of the symbol array
12,538,327✔
679
                        t++; // this is the first (and only) symbol code
12,538,327✔
680
                        if ( *t < 0 ) {
12,538,327✔
681
                                neg_exponent = MaX(neg_exponent, (uint64_t)(-(*t)) );
2,115✔
682
                        }
683
                }
684

685
                // Now check for a denominator in the coefficient:
686
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
16,547,848✔
687
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
29,814✔
688
                        // Record the LCM of the coefficient denominators:
689
                        fmpz_lcm(den_coeff.d, den_coeff.d, tmp.d);
29,814✔
690
                }
691

692
                if ( *term_stop == 0 ) {
16,547,848✔
693
                        // + 1 for the terminating 0
694
                        arg_size = term_stop - args + 1;
38,112✔
695
                        break;
38,112✔
696
                }
697
        }
698
        // Assemble denpoly.
699
        fmpz_poly_set_coeff_fmpz(denpoly, neg_exponent, den_coeff.d);
4,495,257✔
700

701

702
        // For the term coefficients
703
        flint::fmpz coeff;
4,495,257✔
704

705
        for ( const WORD* term = args; term < arg_stop; term += term[0] ) {
21,004,993✔
706

707
                const WORD* term_stop = term+term[0];
16,547,848✔
708
                const WORD  coeff_size = (term_stop)[-1];
16,547,848✔
709
                const WORD* symbol_stop = term_stop - ABS(coeff_size);
16,547,848✔
710
                const WORD* t = term;
16,547,848✔
711

712
                uint64_t exponent = 0;
16,547,848✔
713

714
                t++; // skip over the total size entry
16,547,848✔
715
                if ( t == symbol_stop ) {
16,547,848✔
716
                        // Just a number, no symbols
717
                }
718
                else {
719
                        t++; // this entry is SYMBOL
12,538,327✔
720
                        t++; // this entry is the size of the symbol array
12,538,327✔
721
                        t++; // this is the first (and only) symbol code
12,538,327✔
722
                        exponent = *t++;
12,538,327✔
723
                }
724

725
                // Now read the coefficient
726
                flint::fmpz_set_form(coeff.d, (UWORD*)symbol_stop, coeff_size/2);
16,547,848✔
727

728
                // Multiply by denominator LCM
729
                fmpz_mul(coeff.d, coeff.d, den_coeff.d);
16,547,848✔
730

731
                // Shift by neg_exponent
732
                exponent += neg_exponent;
16,547,848✔
733

734
                // Read the denominator if there is one, and divide it out of the coefficient
735
                if ( *(symbol_stop+ABS(coeff_size/2)) != 1 ) {
16,547,848✔
736
                        flint::fmpz_set_form(tmp.d, (UWORD*)(symbol_stop+ABS(coeff_size/2)), ABS(coeff_size/2));
44,334✔
737
                        // By construction, this is an exact division
738
                        fmpz_divexact(coeff.d, coeff.d, tmp.d);
29,814✔
739
                }
740

741
                // Add the term to the poly
742
                fmpz_poly_set_coeff_fmpz(poly, exponent, coeff.d);
16,547,848✔
743

744
                if ( *term_stop == 0 ) {
16,547,848✔
745
                        break;
746
                }
747
        }
748

749
        return arg_size;
4,495,257✔
750
}
4,495,257✔
751
/*
752
        #] flint::from_argument_poly :
753

754
        #[ flint::fmpz_get_form :
755
*/
756
// Write FORM's long integer representation of an fmpz at a, and put the number of WORDs at na.
757
// na carries the sign of the integer.
758
WORD flint::fmpz_get_form(fmpz_t z, WORD *a) {
28,880,054✔
759

760
        WORD na = 0;
28,880,054✔
761
        const int32_t sgn = fmpz_sgn(z);
28,880,054✔
762
        if ( sgn == -1 ) {
28,880,054✔
763
                fmpz_neg(z, z);
5,057,698✔
764
        }
765
        const int64_t nlimbs = fmpz_size(z);
28,880,054✔
766

767
        // This works but is UB?
768
        //fmpz_get_ui_array(reinterpret_cast<uint64_t*>(a), nlimbs, z);
769

770
        // Use fixed-size functions to get limb data where possible. These probably cover most real
771
        // cases. 
772
        if ( nlimbs == 1 ) {
28,880,054✔
773
                const uint64_t limb = fmpz_get_ui(z);
25,973,097✔
774
                a[0] = (WORD)(limb & 0xFFFFFFFF);
25,973,097✔
775
                na++;
25,973,097✔
776
                a[1] = (WORD)(limb >> BITSINWORD);
25,973,097✔
777
                if ( a[1] != 0 ) {
25,973,097✔
778
                        na++;
1,030,841✔
779
                }
780
        }
781
        else if ( nlimbs == 2 ) {
2,906,957✔
782
                uint64_t limb_hi = 0, limb_lo = 0;
874,857✔
783
                fmpz_get_uiui((ulong*)&limb_hi, (ulong*)&limb_lo, z);
874,857✔
784
                a[0] = (WORD)(limb_lo & 0xFFFFFFFF);
874,857✔
785
                        na++;
874,857✔
786
                a[1] = (WORD)(limb_lo >> BITSINWORD);
874,857✔
787
                        na++;
874,857✔
788
                a[2] = (WORD)(limb_hi & 0xFFFFFFFF);
874,857✔
789
                        na++;
874,857✔
790
                a[3] = (WORD)(limb_hi >> BITSINWORD);
874,857✔
791
                if ( a[3] != 0 ) {
874,857✔
792
                        na++;
307,696✔
793
                }
794
        }
795
        else {
796
                vector<uint64_t> limb_data(nlimbs, 0);
2,032,100✔
797
                fmpz_get_ui_array((ulong*)limb_data.data(), (slong)nlimbs, z);
2,032,100✔
798
                for ( long i = 0; i < nlimbs; i++ ) {
16,017,344✔
799
                        a[2*i] = (WORD)(limb_data[i] & 0xFFFFFFFF);
13,985,244✔
800
                        na++;
13,985,244✔
801
                        a[2*i+1] = (WORD)(limb_data[i] >> BITSINWORD);
13,985,244✔
802
                        if ( a[2*i+1] != 0 || i < (nlimbs-1) ) {
13,985,244✔
803
                                // The final limb might fit in a single 32bit WORD. Only
804
                                // increment na if the final WORD is non zero.
805
                                na++;
12,922,731✔
806
                        }
807
                }
808
        }
2,032,100✔
809

810
        // And now put the sign in the number of limbs
811
        if ( sgn == -1 ) {
28,880,054✔
812
                na = -na;
5,057,698✔
813
        }
814

815
        return na;
28,880,054✔
816
}
817
/*
818
        #] flint::fmpz_get_form :
819
        #[ flint::fmpz_set_form :
820
*/
821
// Create an fmpz directly from FORM's long integer representation. fmpz uses 64bit unsigned limbs,
822
// but FORM uses 32bit UWORDs on 64bit architectures so we can't use fmpz_set_ui_array directly.
823
void flint::fmpz_set_form(fmpz_t z, UWORD *a, WORD na) {
19,279,192✔
824

825
        if ( na == 0 ) {
19,279,192✔
826
                fmpz_zero(z);
×
827
                return;
×
828
        }
829

830
        // Negative na represenents a negative number
831
        int32_t sgn = 1;
19,279,192✔
832
        if ( na < 0 ) {
19,279,192✔
833
                sgn = -1;
6,233,480✔
834
                na = -na;
6,233,480✔
835
        }
836

837
        // Remove padding. FORM stores numerators and denominators with equal numbers of limbs but we
838
        // don't need to do this within the fmpz. It is not necessary to do this really, the fmpz
839
        // doesn't add zero limbs unnecessarily, but we might be able to avoid creating the limb_data
840
        // array below.
841
        while ( a[na-1] == 0 ) {
19,280,479✔
842
                na--;
1,287✔
843
        }
844

845
        // If the number fits in fixed-size fmpz_set functions, we don't need to use additional memory
846
        // to convert to uint64_t. These probably cover most real cases.
847
        if ( na == 1 ) {
19,279,192✔
848
                fmpz_set_ui(z, (uint64_t)a[0]);
15,971,787✔
849
        }
850
        else if ( na == 2 ) {
3,307,405✔
851
                fmpz_set_ui(z, (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
1,076,616✔
852
        }
853
        else if ( na == 3 ) {
2,230,789✔
854
                fmpz_set_uiui(z, (uint64_t)a[2], (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
572,453✔
855
        }
856
        else if ( na == 4 ) {
1,658,336✔
857
                fmpz_set_uiui(z, (((uint64_t)a[3])<<BITSINWORD) + (uint64_t)a[2],
295,872✔
858
                        (((uint64_t)a[1])<<BITSINWORD) + (uint64_t)a[0]);
295,872✔
859
        }
860
        else {
861
                const int32_t nlimbs = (na+1)/2;
1,362,464✔
862
                vector<uint64_t> limb_data(nlimbs, 0);
1,362,464✔
863
                for ( int32_t i = 0; i < nlimbs; i++ ) {
8,941,289✔
864
                        if ( 2*i+1 <= na-1 ) {
7,578,825✔
865
                                limb_data[i] = (uint64_t)a[2*i] + (((uint64_t)a[2*i+1])<<BITSINWORD);
6,831,513✔
866
                        }
867
                        else {
868
                                limb_data[i] = (uint64_t)a[2*i];
747,312✔
869
                        }
870
                }
871
                fmpz_set_ui_array(z, (ulong*)limb_data.data(), nlimbs);
1,362,464✔
872
        }
1,362,464✔
873

874
        // Finally set the sign.
875
        if ( sgn == -1 ) {
19,279,192✔
876
                fmpz_neg(z, z);
6,233,480✔
877
        }
878

879
        return;
880
}
881
/*
882
        #] flint::fmpz_set_form :
883

884
        #[ flint::gcd_mpoly :
885
*/
886
// Return a pointer to a buffer containing the GCD of the 0-terminated term lists at a and b.
887
// If must_fit_term, this should be a TermMalloc buffer. Otherwise Malloc1 the buffer.
888
// For multi-variate cases.
889
WORD* flint::gcd_mpoly(PHEAD const WORD *a, const WORD *b, const WORD must_fit_term,
918✔
890
        const var_map_t &var_map) {
891

892
        flint::mpoly_ctx ctx(var_map.size());
918✔
893
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d), gcd(ctx.d);
918✔
894

895
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
918✔
896
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
918✔
897

898
        // denpa, denpb must be 1:
899
        if ( fmpz_mpoly_is_one(denpa.d, ctx.d) != 1 ) {
918✔
900
                MLOCK(ErrorMessageLock);
×
901
                MesPrint("flint::gcd_mpoly: error: denpa != 1");
×
902
                MUNLOCK(ErrorMessageLock);
×
903
                Terminate(-1);
×
904
        }
905
        if ( fmpz_mpoly_is_one(denpb.d, ctx.d) != 1 ) {
918✔
906
                MLOCK(ErrorMessageLock);
×
907
                MesPrint("flint::gcd_mpoly: error: denpb != 1");
×
908
                MUNLOCK(ErrorMessageLock);
×
909
                Terminate(-1);
×
910
        }
911

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

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

948
                fmpz_mpoly_gcd(gcd.d, pa.d, pb.d, ctx.d);
498✔
949
                if ( flip_sign ) {
498✔
950
                        fmpz_mpoly_neg(gcd.d, gcd.d, ctx.d);
69✔
951
                }
952
        }
498✔
953

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

970
        const bool with_arghead = false;
918✔
971
        const bool write = true;
918✔
972
        const uint64_t prev_size = 0;
918✔
973
        flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
918✔
974
                var_map, ctx.d);
975

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

988
        flint::poly pa, pb, denpa, denpb, gcd;
1,518✔
989

990
        flint::from_argument_poly(pa.d, denpa.d, a, false);
1,518✔
991
        flint::from_argument_poly(pb.d, denpb.d, b, false);
1,518✔
992

993
        // denpa, denpb must be 1:
994
        if ( fmpz_poly_is_one(denpa.d) != 1 ) {
1,518✔
995
                MLOCK(ErrorMessageLock);
×
996
                MesPrint("flint::gcd_poly: error: denpa != 1");
×
997
                MUNLOCK(ErrorMessageLock);
×
998
                Terminate(-1);
×
999
        }
1000
        if ( fmpz_poly_is_one(denpb.d) != 1 ) {
1,518✔
1001
                MLOCK(ErrorMessageLock);
×
1002
                MesPrint("flint::gcd_poly: error: denpb != 1");
×
1003
                MUNLOCK(ErrorMessageLock);
×
1004
                Terminate(-1);
×
1005
        }
1006

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

1031
                fmpz_poly_gcd(gcd.d, pa.d, pb.d);
1,044✔
1032
        }
1,044✔
1033

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

1047
                res = (WORD *)Malloc1(sizeof(WORD)*gcd_size, "flint::gcd_poly");
1,518✔
1048
        }
1049

1050
        const bool with_arghead = false;
1,518✔
1051
        const uint64_t prev_size = 0;
1,518✔
1052
        const bool write = true;
1,518✔
1053
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, gcd.d,
1,518✔
1054
                var_map);
1055

1056
        return res;
1,518✔
1057
}
1,518✔
1058
/*
1059
        #] flint::gcd_poly :
1060

1061
        #[ flint::get_variables :
1062
*/
1063
// Get the list of symbols which appear in the vector of expressions. These are polyratfun
1064
// numerators, denominators or expressions from calls to gcd_ etc. Return this list as a map
1065
// between indices and symbol codes.
1066
// TODO FACTORSYMBOL last?
1067
flint::var_map_t flint::get_variables(const vector <WORD *> &es, const bool with_arghead,
1,504,032✔
1068
        const bool sort_vars) {
1069

1070
        int32_t num_vars = 0;
1,504,032✔
1071
        // We count the total number of terms to determine "density".
1072
        uint32_t num_terms = 0;
1,504,032✔
1073
        // To be used if we sort by highest degree, as the poly code does.
1074
        vector<int32_t> degrees;
1,504,032✔
1075
        var_map_t var_map;
1,504,032✔
1076

1077
        // extract all variables
1078
        for ( size_t ei = 0; ei < es.size(); ei++ ) {
6,961,665✔
1079
                WORD *e = es[ei];
5,457,666✔
1080

1081
                // fast notation
1082
                if ( *e == -SNUMBER ) {
5,457,666✔
1083
                        num_terms++;
854,849✔
1084
                }
1085
                else if ( *e == -SYMBOL ) {
4,602,817✔
1086
                        num_terms++;
29,764✔
1087
                        if ( !var_map.count(e[1]) ) {
29,764✔
1088
                                var_map[e[1]] = num_vars++;
27,777✔
1089
                                degrees.push_back(1);
27,777✔
1090
                        }
1091
                }
1092
                // JD: Here we need to check for non-symbol/number terms in fast notation.
1093
                else if ( *e < 0 ) {
4,573,053✔
1094
                        MLOCK(ErrorMessageLock);
27✔
1095
                        MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
27✔
1096
                        MUNLOCK(ErrorMessageLock);
27✔
1097
                        Terminate(1);
27✔
1098
                }
1099
                else {
1100
                        for ( WORD i = with_arghead ? ARGHEAD:0; with_arghead ? i < e[0]:e[i] != 0; i += e[i] ) {
26,615,794✔
1101
                                num_terms++;
17,469,748✔
1102
                                if ( i+1 < i+e[i]-ABS(e[i+e[i]-1]) && e[i+1] != SYMBOL ) {
17,469,748✔
1103
                                        MLOCK(ErrorMessageLock);
6✔
1104
                                        MesPrint("ERROR: polynomials and polyratfuns must contain symbols only");
6✔
1105
                                        MUNLOCK(ErrorMessageLock);
6✔
1106
                                        Terminate(1);
6✔
1107
                                }
1108

1109
                                for ( WORD j = i+3; j<i+e[i]-ABS(e[i+e[i]-1]); j += 2 ) {
32,654,640✔
1110
                                        if ( !var_map.count(e[j]) ) {
15,184,898✔
1111
                                                var_map[e[j]] = num_vars++;
1,500,661✔
1112
                                                degrees.push_back(e[j+1]);
1,500,661✔
1113
                                        }
1114
                                        else {
1115
                                                degrees[var_map[e[j]]] = MaX(degrees[var_map[e[j]]], e[j+1]);
13,684,237✔
1116
                                        }
1117
                                }
1118
                        }
1119
                }
1120
        }
1121

1122
        if ( sort_vars ) {
1,503,999✔
1123
                // bubble sort variables in decreasing order of degree
1124
                // (this seems better for factorization)
1125
                for ( size_t i = 0; i < var_map.size(); i++ ) {
2,974,768✔
1126
                        for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
1,693,384✔
1127
                                if ( degrees[j] < degrees[j+1] ) {
198,900✔
1128
                                        swap(degrees[j], degrees[j+1]);
28,436✔
1129

1130
                                        // Find the map keys associated with the values we want to swap
1131
                                        uint32_t j0 = 0;
28,436✔
1132
                                        uint32_t j1 = 0;
28,436✔
1133
                                        for ( auto x: var_map ) {
139,738✔
1134
                                                if ( x.second == j ) {
111,302✔
1135
                                                        j0 = x.first;
28,436✔
1136
                                                }
1137
                                                else if ( x.second == j+1 ) {
82,866✔
1138
                                                        j1 = x.first;
28,436✔
1139
                                                }
1140
                                        }
1141
                                        swap(var_map.at(j0), var_map.at(j1));
28,436✔
1142
                                }
1143
                        }
1144
                }
1145
        }
1146
        // Otherwise, sort lexicographically in FORM's ordering
1147
        else {
1148
                for ( size_t i = 0; i < var_map.size(); i++ ) {
57,669✔
1149
                        for ( size_t j = 0; j+1 < var_map.size(); j++ ) {
74,238✔
1150
                                uint32_t j0 = 0;
40,284✔
1151
                                uint32_t j1 = 0;
40,284✔
1152
                                for ( auto x: var_map ) {
213,858✔
1153
                                        if ( x.second == j ) {
173,574✔
1154
                                                j0 = x.first;
40,284✔
1155
                                        }
1156
                                        else if ( x.second == j+1 ) {
133,290✔
1157
                                                j1 = x.first;
40,284✔
1158
                                        }
1159
                                }
1160
                                if ( j0 > j1 ) {
40,284✔
1161
                                        swap(var_map.at(j0), var_map.at(j1));
6,834✔
1162
                                }
1163
                        }
1164
                }
1165
        }
1166

1167
        if ( var_map.size() == 1 ) {
1,503,999✔
1168
                // In the univariate case, if the polynomials are sufficiently sparse force the use of the
1169
                // multivariate routines, which use a sparse representation, by adding a dummy map entry.
1170
                if ( (float)num_terms <= UNIVARIATE_DENSITY_THR * (float)degrees[0] ) {
1,430,728✔
1171
                        // -1 will never be a symbol code. Built-in symbols from 0 to 19, and 20 is the first
1172
                        // user symbol.
NEW
1173
                        var_map[-1] = num_vars;
×
1174
                }
1175
        }
1176

1177
        return var_map;
1,503,999✔
1178
}
1,503,999✔
1179
/*
1180
        #] flint::get_variables :
1181

1182
        #[ flint::inverse_poly :
1183
*/
1184
WORD* flint::inverse_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1,137✔
1185

1186
        flint::poly pa, pb, denpa, denpb;
1,137✔
1187

1188
        flint::from_argument_poly(pa.d, denpa.d, a, false);
1,137✔
1189
        flint::from_argument_poly(pb.d, denpb.d, b, false);
1,137✔
1190

1191
        // fmpz_poly_xgcd is undefined if the content of pa and pb are not 1. Take the content out.
1192
        // fmpz_poly_content gives the non-negative content and fmpz_poly_primitive normalizes to a
1193
        // non-negative lcoeff, so we need to add the sign to the content if the polys have a negative
1194
        // lcoeff. We don't need to keep the content of pb, it is a numerical multiple of the modulus.
1195
        flint::fmpz content_a, resultant;
1,137✔
1196
        flint::poly inverse, tmp;
1,137✔
1197
        fmpz_poly_content(content_a.d, pa.d);
1,137✔
1198
        if ( fmpz_sgn(fmpz_poly_lead(pa.d)) == -1 ) {
1,137✔
1199
                fmpz_neg(content_a.d, content_a.d);
537✔
1200
        }
1201
        fmpz_poly_primitive_part(pa.d, pa.d);
1,137✔
1202
        fmpz_poly_primitive_part(pb.d, pb.d);
1,137✔
1203

1204
        // Special cases:
1205
        // Possibly strange that we give 1 for inverse_(x1,1) but here we take MMA's convention.
1206
        if ( fmpz_poly_is_one(pa.d) && fmpz_poly_is_one(pb.d) ) {
1,371✔
1207
                fmpz_poly_one(inverse.d);
54✔
1208
                fmpz_one(resultant.d);
54✔
1209
        }
1210
        else if ( fmpz_poly_is_one(pb.d) ) {
1,083✔
1211
                fmpz_poly_zero(inverse.d);
45✔
1212
                fmpz_one(resultant.d);
45✔
1213
        }
1214
        else {
1215
                // Now use the extended Euclidean algorithm to find inverse, resultant, tmp of the Bezout
1216
                // identity: inverse*pa + tmp*pb = resultant. Then inverse/resultant is the multiplicative
1217
                // inverse of pa mod pb. We'll divide by resultant in the denscale argument of to_argument_poly.
1218
                fmpz_poly_xgcd(resultant.d, inverse.d, tmp.d, pa.d, pb.d);
1,038✔
1219

1220
                // If the resultant is zero, the inverse does not exist:
1221
                if ( fmpz_is_zero(resultant.d) ) {
1,038✔
1222
                        MLOCK(ErrorMessageLock);
3✔
1223
                        MesPrint("flint::inverse_poly error: inverse does not exist");
3✔
1224
                        MUNLOCK(ErrorMessageLock);
3✔
1225
                        Terminate(-1);
3✔
1226
                }
1227
        }
1228

1229
        // Multiply inverse by denpa. denpb is a numerical multiple of the modulus, so doesn't matter.
1230
        // We also need to divide by content_a, which we do in the denscale argument of to_argument_poly
1231
        // by multiplying resultant by content_a here.
1232
        fmpz_poly_mul(inverse.d, inverse.d, denpa.d);
1,134✔
1233
        fmpz_mul(resultant.d, resultant.d, content_a.d);
1,134✔
1234

1235

1236
        WORD* res;
1,134✔
1237
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1238
        // the "scale" argument of to_argument_poly since resultant might not be 1.
1239
        const bool with_arghead = false;
1,134✔
1240
        bool write = false;
1,134✔
1241
        const bool must_fit_term = false;
1,134✔
1242
        const uint64_t prev_size = 0;
1,134✔
1243
        const uint64_t res_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
1,134✔
1244
                with_arghead, must_fit_term, write, prev_size, inverse.d, var_map, resultant.d);
1245
        res = (WORD*)Malloc1(sizeof(WORD)*res_size, "flint::inverse_poly");
1,134✔
1246

1247
        write = true;
1,134✔
1248
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, inverse.d,
1,134✔
1249
                var_map, resultant.d);
1250

1251
        return res;
1,134✔
1252
}
1,134✔
1253
/*
1254
        #] flint::inverse_poly :
1255

1256
        #[ flint::mul_mpoly :
1257
*/
1258
// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1259
// For multi-variate cases.
1260
WORD* flint::mul_mpoly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
1,824✔
1261

1262
        flint::mpoly_ctx ctx(var_map.size());
1,824✔
1263
        flint::mpoly pa(ctx.d), pb(ctx.d), denpa(ctx.d), denpb(ctx.d);
1,824✔
1264

1265
        flint::from_argument_mpoly(pa.d, denpa.d, a, false, var_map, ctx.d);
1,824✔
1266
        flint::from_argument_mpoly(pb.d, denpb.d, b, false, var_map, ctx.d);
1,824✔
1267

1268
        // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1269
        if ( fmpz_mpoly_is_fmpz(denpa.d, ctx.d) != 1 ) {
1,824✔
1270
                MLOCK(ErrorMessageLock);
×
1271
                MesPrint("flint::mul_mpoly: error: denpa is non-constant");
×
1272
                MUNLOCK(ErrorMessageLock);
×
1273
                Terminate(-1);
×
1274
        }
1275
        if ( fmpz_mpoly_is_fmpz(denpb.d, ctx.d) != 1 ) {
1,824✔
1276
                MLOCK(ErrorMessageLock);
×
1277
                MesPrint("flint::mul_mpoly: error: denpb is non-constant");
×
1278
                MUNLOCK(ErrorMessageLock);
×
1279
                Terminate(-1);
×
1280
        }
1281

1282
        // Multiply numerators, store result in pa
1283
        fmpz_mpoly_mul(pa.d, pa.d, pb.d, ctx.d);
1,824✔
1284
        // Multiply denominators, store result in denpa, and convert to an fmpz:
1285
        fmpz_mpoly_mul(denpa.d, denpa.d, denpb.d, ctx.d);
1,824✔
1286
        flint::fmpz den;
1,824✔
1287
        fmpz_mpoly_get_fmpz(den.d, denpa.d, ctx.d);
1,824✔
1288

1289
        WORD* res;
1,824✔
1290
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1291
        // the "scale" argument of to_argument_mpoly since den might not be 1.
1292
        const bool with_arghead = false;
1,824✔
1293
        bool write = false;
1,824✔
1294
        const bool must_fit_term = false;
1,824✔
1295
        const uint64_t prev_size = 0;
1,824✔
1296
        const uint64_t mul_size = (uint64_t)flint::to_argument_mpoly(BHEAD NULL,
1,824✔
1297
                with_arghead, must_fit_term, write, prev_size, pa.d, var_map, ctx.d, den.d);
1298
        res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_mpoly");
1,824✔
1299

1300
        write = true;
1,824✔
1301
        flint::to_argument_mpoly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
1,824✔
1302
                var_map, ctx.d, den.d);
1303

1304
        return res;
1,824✔
1305
}
1,824✔
1306
/*
1307
        #] flint::mul_mpoly :
1308
        #[ flint::mul_poly :
1309
*/
1310
// Return a pointer to a buffer containing the product of the 0-terminated term lists at a and b.
1311
// For uni-variate cases.
1312
WORD* flint::mul_poly(PHEAD const WORD *a, const WORD *b, const var_map_t &var_map) {
5,379✔
1313

1314
        flint::poly pa, pb, denpa, denpb;
5,379✔
1315

1316
        flint::from_argument_poly(pa.d, denpa.d, a, false);
5,379✔
1317
        flint::from_argument_poly(pb.d, denpb.d, b, false);
5,379✔
1318

1319
        // denpa, denpb must be integers. Negative symbol powers have been converted to extra symbols.
1320
        if ( fmpz_poly_degree(denpa.d) != 0 ) {
5,379✔
1321
                MLOCK(ErrorMessageLock);
×
1322
                MesPrint("flint::mul_poly: error: denpa is non-constant");
×
1323
                MUNLOCK(ErrorMessageLock);
×
1324
                Terminate(-1);
×
1325
        }
1326
        if ( fmpz_poly_degree(denpb.d) != 0 ) {
5,379✔
1327
                MLOCK(ErrorMessageLock);
×
1328
                MesPrint("flint::mul_poly: error: denpb is non-constant");
×
1329
                MUNLOCK(ErrorMessageLock);
×
1330
                Terminate(-1);
×
1331
        }
1332

1333
        // Multiply numerators, store result in pa
1334
        fmpz_poly_mul(pa.d, pa.d, pb.d);
5,379✔
1335
        // Multiply denominators, store result in denpa, and convert to an fmpz:
1336
        fmpz_poly_mul(denpa.d, denpa.d, denpb.d);
5,379✔
1337
        flint::fmpz den;
5,379✔
1338
        fmpz_poly_get_coeff_fmpz(den.d, denpa.d, 0);
5,379✔
1339

1340
        WORD* res;
5,379✔
1341
        // First determine the result size, and malloc. The result should have no arghead. Here we use
1342
        // the "scale" argument of to_argument_poly since den might not be 1.
1343
        const bool with_arghead = false;
5,379✔
1344
        bool write = false;
5,379✔
1345
        const bool must_fit_term = false;
5,379✔
1346
        const uint64_t prev_size = 0;
5,379✔
1347
        const uint64_t mul_size = (uint64_t)flint::to_argument_poly(BHEAD NULL,
5,379✔
1348
                with_arghead, must_fit_term, write, prev_size, pa.d, var_map, den.d);
1349
        res = (WORD*)Malloc1(sizeof(WORD)*mul_size, "flint::mul_poly");
5,379✔
1350

1351
        write = true;
5,379✔
1352
        flint::to_argument_poly(BHEAD res, with_arghead, must_fit_term, write, prev_size, pa.d,
5,379✔
1353
                var_map, den.d);
1354

1355
        return res;
5,379✔
1356
}
5,379✔
1357
/*
1358
        #] flint::mul_poly :
1359

1360
        #[ flint::ratfun_add_mpoly :
1361
*/
1362
// Add the multi-variate FORM rational polynomials at t1 and t2. The result is written at out.
1363
void flint::ratfun_add_mpoly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
6,402✔
1364
        const var_map_t &var_map) {
1365

1366
        flint::mpoly_ctx ctx(var_map.size());
6,402✔
1367
        flint::mpoly gcd(ctx.d), num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d);
6,402✔
1368

1369
        flint::ratfun_read_mpoly(t1, num1.d, den1.d, var_map, ctx.d);
6,402✔
1370
        flint::ratfun_read_mpoly(t2, num2.d, den2.d, var_map, ctx.d);
6,402✔
1371

1372
        if ( fmpz_mpoly_cmp(den1.d, den2.d, ctx.d) != 0 ) {
6,402✔
1373
                fmpz_mpoly_gcd_cofactors(gcd.d, den1.d, den2.d, den1.d, den2.d, ctx.d);
3,413✔
1374

1375
                fmpz_mpoly_mul(num1.d, num1.d, den2.d, ctx.d);
3,413✔
1376
                fmpz_mpoly_mul(num2.d, num2.d, den1.d, ctx.d);
3,413✔
1377

1378
                fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
3,413✔
1379
                fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
3,413✔
1380
                fmpz_mpoly_mul(den1.d, den1.d, gcd.d,  ctx.d);
3,413✔
1381
        }
1382
        else {
1383
                fmpz_mpoly_add(num1.d, num1.d, num2.d, ctx.d);
2,989✔
1384
        }
1385

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

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

1391
        // Result in FORM notation:
1392
        *out++ = AR.PolyFun;
6,402✔
1393
        WORD* args_size = out++;
6,402✔
1394
        WORD* args_flag = out++;
6,402✔
1395
        *args_flag = 0; // clean prf
6,402✔
1396
        FILLFUN3(out); // Remainder of funhead, if it is larger than 3
6,402✔
1397

1398
        const bool with_arghead = true;
6,402✔
1399
        const bool must_fit_term = true;
6,402✔
1400
        const bool write = true;
6,402✔
1401
        // prev_size + 4, to account for final term size and coeff of "1/1"
1402
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
6,402✔
1403
                num1.d, var_map, ctx.d);
1404
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
6,402✔
1405
                den1.d, var_map, ctx.d);
1406

1407
        *args_size = out - args_size + 1; // The +1 is to include the function ID
6,402✔
1408
        AT.WorkPointer = out;
6,402✔
1409
}
6,402✔
1410
/*
1411
        #] flint::ratfun_add_mpoly :
1412
        #[ flint::ratfun_add_poly :
1413
*/
1414
// Add the uni-variate FORM rational polynomials at t1 and t2. The result is written at out.
1415
void flint::ratfun_add_poly(PHEAD const WORD *t1, const WORD *t2, WORD *out,
628,943✔
1416
        const var_map_t &var_map) {
1417

1418
        flint::poly gcd, num1, den1, num2, den2;
628,943✔
1419

1420
        flint::ratfun_read_poly(t1, num1.d, den1.d);
628,943✔
1421
        flint::ratfun_read_poly(t2, num2.d, den2.d);
628,943✔
1422

1423
        if ( fmpz_poly_equal(den1.d, den2.d) == 0 ) {
628,943✔
1424
                flint::util::simplify_fmpz_poly(den1.d, den2.d, gcd.d);
453,834✔
1425

1426
                fmpz_poly_mul(num1.d, num1.d, den2.d);
453,834✔
1427
                fmpz_poly_mul(num2.d, num2.d, den1.d);
453,834✔
1428

1429
                fmpz_poly_add(num1.d, num1.d, num2.d);
453,834✔
1430
                fmpz_poly_mul(den1.d, den1.d, den2.d);
453,834✔
1431
                fmpz_poly_mul(den1.d, den1.d, gcd.d);
453,834✔
1432
        }
1433
        else {
1434
                fmpz_poly_add(num1.d, num1.d, num2.d);
175,109✔
1435
        }
1436

1437
        // Finally divide out any common factors between the resulting num1, den1:
1438
        flint::util::simplify_fmpz_poly(num1.d, den1.d, gcd.d);
628,943✔
1439

1440
        flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
628,942✔
1441

1442
        // Result in FORM notation:
1443
        *out++ = AR.PolyFun;
628,942✔
1444
        WORD* args_size = out++;
628,942✔
1445
        WORD* args_flag = out++;
628,942✔
1446
        *args_flag = 0; // clean prf
628,942✔
1447
        FILLFUN3(out); // Remainder of funhead, if it is larger than 3
628,942✔
1448

1449
        const bool with_arghead = true;
628,942✔
1450
        const bool must_fit_term = true;
628,942✔
1451
        const bool write = true;
628,942✔
1452
        // prev_size + 4, to account for final term size and coeff of "1/1"
1453
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
628,942✔
1454
                num1.d, var_map);
1455
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-args_size+4,
628,942✔
1456
                den1.d, var_map);
1457

1458
        *args_size = out - args_size + 1; // The +1 is to include the function ID
628,942✔
1459
        AT.WorkPointer = out;
628,942✔
1460
}
628,942✔
1461
/*
1462
        #] flint::ratfun_add_poly :
1463

1464
        #[ flint::ratfun_normalize_mpoly :
1465
*/
1466
// Multiply and simplify occurrences of the multi-variate FORM rational polynomials found in term.
1467
// The final term is written in place, with the rational polynomial at the end.
1468
void flint::ratfun_normalize_mpoly(PHEAD WORD *term, const var_map_t &var_map) {
21,936✔
1469

1470
        // The length of the coefficient
1471
        const WORD ncoeff = (term + *term)[-1];
21,936✔
1472
        // The end of the term data, before the coefficient:
1473
        const WORD *tstop = term + *term - ABS(ncoeff);
21,936✔
1474

1475
        flint::mpoly_ctx ctx(var_map.size());
21,936✔
1476
        flint::mpoly num1(ctx.d), den1(ctx.d), num2(ctx.d), den2(ctx.d), gcd(ctx.d);
21,936✔
1477

1478
        // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1479
        flint::fmpz tmpNum, tmpDen;
21,936✔
1480
        flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
21,936✔
1481
        flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
23,208✔
1482
        fmpz_mpoly_set_fmpz(num1.d, tmpNum.d, ctx.d);
21,936✔
1483
        fmpz_mpoly_set_fmpz(den1.d, tmpDen.d, ctx.d);
21,936✔
1484

1485
        // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1486
        // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1487
        // go at the end.
1488
        WORD* term_size = term;
21,936✔
1489
        WORD* s = term + 1;
21,936✔
1490
        for ( WORD *t = term + 1; t < tstop; ) {
66,315✔
1491
                if ( *t == AR.PolyFun ) {
44,382✔
1492
                        flint::ratfun_read_mpoly(t, num2.d, den2.d, var_map, ctx.d);
32,163✔
1493

1494
                        // get gcd of num1,den2 and num2,den1 and then assemble
1495
                        fmpz_mpoly_gcd_cofactors(gcd.d, num1.d, den2.d, num1.d, den2.d, ctx.d);
32,160✔
1496
                        fmpz_mpoly_gcd_cofactors(gcd.d, num2.d, den1.d, num2.d, den1.d, ctx.d);
32,160✔
1497

1498
                        fmpz_mpoly_mul(num1.d, num1.d, num2.d, ctx.d);
32,160✔
1499
                        fmpz_mpoly_mul(den1.d, den1.d, den2.d, ctx.d);
32,160✔
1500

1501
                        t += t[1];
32,160✔
1502
                }
1503

1504
                else {
1505
                        // Not a PolyFun, just copy or skip over
1506
                        WORD i = t[1];
12,219✔
1507
                        if ( s != t ) { NCOPY(s,t,i); }
82,296✔
1508
                        else { t += i; s += i; }
3,678✔
1509
                }
1510
        }
1511

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

1514
        // Result in FORM notation:
1515
        WORD* out = s;
21,933✔
1516
        *out++ = AR.PolyFun;
21,933✔
1517
        WORD* args_size = out++;
21,933✔
1518
        WORD* args_flag = out++;
21,933✔
1519
        *args_flag &= ~MUSTCLEANPRF;
21,933✔
1520

1521
        const bool with_arghead = true;
21,933✔
1522
        const bool must_fit_term = true;
21,933✔
1523
        const bool write = true;
21,933✔
1524
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
21,933✔
1525
                num1.d, var_map, ctx.d);
1526
        out += flint::to_argument_mpoly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
21,930✔
1527
                den1.d, var_map, ctx.d);
1528

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

1531
        // +3 for the coefficient of 1/1, which is added after the check
1532
        if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
21,927✔
1533
                MLOCK(ErrorMessageLock);
3✔
1534
                MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
3✔
1535
                MUNLOCK(ErrorMessageLock);
3✔
1536
                Terminate(-1);
3✔
1537
        }
1538

1539
        *out++ = 1;
21,924✔
1540
        *out++ = 1;
21,924✔
1541
        *out++ = 3; // the term's coefficient is now 1/1
21,924✔
1542

1543
        *term_size = out - term_size;
21,924✔
1544
}
21,924✔
1545
/*
1546
        #] flint::ratfun_normalize_mpoly :
1547
        #[ flint::ratfun_normalize_poly :
1548
*/
1549
// Multiply and simplify occurrences of the uni-variate FORM rational polynomials found in term.
1550
// The final term is written in place, with the rational polynomial at the end.
1551
void flint::ratfun_normalize_poly(PHEAD WORD *term, const var_map_t &var_map) {
820,521✔
1552

1553
        // The length of the coefficient
1554
        const WORD ncoeff = (term + *term)[-1];
820,521✔
1555
        // The end of the term data, before the coefficient:
1556
        const WORD *tstop = term + *term - ABS(ncoeff);
820,521✔
1557

1558
        flint::poly num1, den1, num2, den2, gcd;
820,521✔
1559

1560
        // Start with "trivial" polynomials, and multiply in the num and den of the prf which appear.
1561
        flint::fmpz tmpNum, tmpDen;
820,521✔
1562
        flint::fmpz_set_form(tmpNum.d, (UWORD*)tstop, ncoeff/2);
820,521✔
1563
        flint::fmpz_set_form(tmpDen.d, (UWORD*)tstop+ABS(ncoeff/2), ABS(ncoeff/2));
868,296✔
1564
        fmpz_poly_set_fmpz(num1.d, tmpNum.d);
820,521✔
1565
        fmpz_poly_set_fmpz(den1.d, tmpDen.d);
820,521✔
1566

1567
        // Loop over the occurrences of PolyFun in the term, and multiply in to num1, den1.
1568
        // s tracks where we are writing the non-PolyFun term data. The final PolyFun will
1569
        // go at the end.
1570
        WORD* term_size = term;
820,521✔
1571
        WORD* s = term + 1;
820,521✔
1572
        for ( WORD *t = term + 1; t < tstop; ) {
3,559,332✔
1573
                if ( *t == AR.PolyFun ) {
2,738,817✔
1574
                        flint::ratfun_read_poly(t, num2.d, den2.d);
1,399,788✔
1575

1576
                        // get gcd of num1,den2 and num2,den1 and then assemble
1577
                        flint::util::simplify_fmpz_poly(num1.d, den2.d, gcd.d);
1,399,782✔
1578
                        flint::util::simplify_fmpz_poly(num2.d, den1.d, gcd.d);
1,399,782✔
1579

1580
                        fmpz_poly_mul(num1.d, num1.d, num2.d);
1,399,782✔
1581
                        fmpz_poly_mul(den1.d, den1.d, den2.d);
1,399,782✔
1582

1583
                        t += t[1];
1,399,782✔
1584
                }
1585

1586
                else {
1587
                        // Not a PolyFun, just copy or skip over
1588
                        WORD i = t[1];
1,339,029✔
1589
                        if ( s != t ) { NCOPY(s,t,i); }
10,185,948✔
1590
                        else { t += i; s += i; }
841,479✔
1591
                }
1592
        }
1593

1594
        flint::util::fix_sign_fmpz_poly_ratfun(num1.d, den1.d);
820,515✔
1595

1596
        // Result in FORM notation:
1597
        WORD* out = s;
820,515✔
1598
        *out++ = AR.PolyFun;
820,515✔
1599
        WORD* args_size = out++;
820,515✔
1600
        WORD* args_flag = out++;
820,515✔
1601
        *args_flag &= ~MUSTCLEANPRF;
820,515✔
1602

1603
        const bool with_arghead = true;
820,515✔
1604
        const bool must_fit_term = true;
820,515✔
1605
        const bool write = true;
820,515✔
1606
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
820,515✔
1607
                num1.d, var_map);
1608
        out += flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, out-term_size,
820,512✔
1609
                den1.d, var_map);
1610

1611
        *args_size = out - args_size + 1; // The +1 is to include the function ID
820,509✔
1612

1613
        // +3 for the coefficient of 1/1, which is added after the check
1614
        if ( sizeof(WORD)*(out-term_size+3) > (size_t)AM.MaxTer ) {
820,509✔
1615
                MLOCK(ErrorMessageLock);
3✔
1616
                MesPrint("flint::ratfun_normalize: output exceeds MaxTermSize");
3✔
1617
                MUNLOCK(ErrorMessageLock);
3✔
1618
                Terminate(-1);
3✔
1619
        }
1620

1621
        *out++ = 1;
820,506✔
1622
        *out++ = 1;
820,506✔
1623
        *out++ = 3; // the term's coefficient is now 1/1
820,506✔
1624

1625
        *term_size = out - term_size;
820,506✔
1626
}
820,506✔
1627
/*
1628
        #] flint::ratfun_normalize_poly :
1629

1630
        #[ flint::ratfun_read_mpoly :
1631
*/
1632
// Read the multi-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1633
// denominator.
1634
void flint::ratfun_read_mpoly(const WORD *a, fmpz_mpoly_t num, fmpz_mpoly_t den,
44,967✔
1635
        const var_map_t &var_map, fmpz_mpoly_ctx_t ctx) {
1636

1637
        // The end of the arguments:
1638
        const WORD* arg_stop = a+a[1];
44,967✔
1639

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

1642
        a += FUNHEAD;
44,967✔
1643
        if ( a >= arg_stop ) {
44,967✔
1644
                MLOCK(ErrorMessageLock);
×
1645
                MesPrint("ERROR: PolyRatFun cannot have zero arguments");
×
1646
                MUNLOCK(ErrorMessageLock);
×
1647
                Terminate(-1);
×
1648
        }
1649

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

1654
        // Read the numerator
1655
        flint::from_argument_mpoly(num, den_num.d, a, true, var_map, ctx);
44,967✔
1656
        NEXTARG(a);
44,967✔
1657

1658
        if ( a < arg_stop ) {
44,967✔
1659
                // Read the denominator
1660
                flint::from_argument_mpoly(den, den_den.d, a, true, var_map, ctx);
44,967✔
1661
                NEXTARG(a);
44,967✔
1662
        }
1663
        else {
1664
                // The denominator is 1
1665
                MLOCK(ErrorMessageLock);
×
1666
                MesPrint("implement this");
×
1667
                MUNLOCK(ErrorMessageLock);
×
1668
                Terminate(-1);
×
1669
        }
1670
        if ( a < arg_stop ) {
44,967✔
1671
                MLOCK(ErrorMessageLock);
3✔
1672
                MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
3✔
1673
                MUNLOCK(ErrorMessageLock);
3✔
1674
                Terminate(-1);
3✔
1675
        }
1676

1677
        // Multiply the num by den_den and den by den_num:
1678
        fmpz_mpoly_mul(num, num, den_den.d, ctx);
44,964✔
1679
        fmpz_mpoly_mul(den, den, den_num.d, ctx);
44,964✔
1680

1681
        if ( must_normalize ) {
44,964✔
1682
                flint::mpoly gcd(ctx);
22,176✔
1683
                fmpz_mpoly_gcd_cofactors(gcd.d, num, den, num, den, ctx);
22,176✔
1684
        }
22,176✔
1685
}
44,964✔
1686
/*
1687
        #] flint::ratfun_read_mpoly :
1688
        #[ flint::ratfun_read_poly :
1689
*/
1690
// Read the uni-variate FORM rational polynomial at a and create fmpz_mpoly_t numerator and
1691
// denominator.
1692
void flint::ratfun_read_poly(const WORD *a, fmpz_poly_t num, fmpz_poly_t den) {
2,657,672✔
1693

1694
        // The end of the arguments:
1695
        const WORD* arg_stop = a+a[1];
2,657,672✔
1696

1697
        const bool must_normalize = (a[2] & MUSTCLEANPRF) != 0;
2,657,672✔
1698

1699
        a += FUNHEAD;
2,657,672✔
1700
        if ( a >= arg_stop ) {
2,657,672✔
1701
                MLOCK(ErrorMessageLock);
3✔
1702
                MesPrint("ERROR: PolyRatFun cannot have zero arguments");
3✔
1703
                MUNLOCK(ErrorMessageLock);
3✔
1704
                Terminate(-1);
3✔
1705
        }
1706

1707
        // Polys to collect the "den of the num" and "den of the den".
1708
        // Input can arrive like this when enabling the PolyRatFun or moving things into it.
1709
        flint::poly den_num, den_den;
2,657,669✔
1710

1711
        // Read the numerator
1712
        flint::from_argument_poly(num, den_num.d, a, true);
2,657,669✔
1713
        NEXTARG(a);
2,657,669✔
1714

1715
        if ( a < arg_stop ) {
2,657,669✔
1716
                // Read the denominator
1717
                flint::from_argument_poly(den, den_den.d, a, true);
2,657,669✔
1718
                NEXTARG(a);
2,657,669✔
1719
        }
1720
        else {
1721
                // The denominator is 1
1722
                MLOCK(ErrorMessageLock);
×
1723
                MesPrint("implement this");
×
1724
                MUNLOCK(ErrorMessageLock);
×
1725
                Terminate(-1);
×
1726
        }
1727
        if ( a < arg_stop ) {
2,657,669✔
1728
                MLOCK(ErrorMessageLock);
3✔
1729
                MesPrint("ERROR: PolyRatFun cannot have more than two arguments");
3✔
1730
                MUNLOCK(ErrorMessageLock);
3✔
1731
                Terminate(-1);
3✔
1732
        }
1733

1734
        // Multiply the num by den_den and den by den_num:
1735
        fmpz_poly_mul(num, num, den_den.d);
2,657,666✔
1736
        fmpz_poly_mul(den, den, den_num.d);
2,657,666✔
1737

1738
        if ( must_normalize ) {
2,657,666✔
1739
                flint::poly gcd;
805,092✔
1740
                flint::util::simplify_fmpz_poly(num, den, gcd.d);
805,092✔
1741
        }
805,092✔
1742
}
2,657,666✔
1743
/*
1744
        #] flint::ratfun_read_poly :
1745
        #[ flint::to_argument_mpoly :
1746
*/
1747
// Convert a fmpz_mpoly_t to a FORM argument (or 0-terminated list of terms: with_arghead==false).
1748
// If the caller is building an output term, prev_size contains the size of the term so far, to
1749
// check that the output fits in AM.MaxTer if must_fit_term.
1750
// All coefficients will be divided by denscale (which might just be 1).
1751
// If write is false, we never write to out but only track the total would-be size. This lets this
1752
// function be repurposed as a "size of FORM notation" function without duplicating the code.
1753
#define IFW(x) { if ( write ) {x;} }
1754
uint64_t flint::to_argument_mpoly(PHEAD WORD *out, const bool with_arghead,
71,067✔
1755
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_mpoly_t poly,
1756
        const var_map_t &var_map, const fmpz_mpoly_ctx_t ctx, const fmpz_t denscale) {
1757

1758
        // out is modified later, keep the pointer at entry
1759
        const WORD* out_entry = out;
71,067✔
1760

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

1765
        // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1766
        if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
71,067✔
1767
                MLOCK(ErrorMessageLock);
×
1768
                MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
×
1769
                MUNLOCK(ErrorMessageLock);
×
1770
                Terminate(-1);
×
1771
        }
1772

1773
        // Create the inverse of var_map, so we don't have to search it for each symbol written
1774
        var_map_t var_map_inv;
71,067✔
1775
        for ( auto x: var_map ) {
267,042✔
1776
                var_map_inv[x.second] = x.first;
195,975✔
1777
        }
1778

1779
        vector<int64_t> exponents(var_map.size());
71,067✔
1780
        const int64_t n_terms = fmpz_mpoly_length(poly, ctx);
71,067✔
1781

1782
        if ( n_terms == 0 ) {
71,067✔
1783
                if ( with_arghead ) {
4,911✔
1784
                        IFW(*out++ = -SNUMBER); ws++;
2,421✔
1785
                        IFW(*out++ = 0); ws++;
2,421✔
1786
                        return ws;
2,421✔
1787
                }
1788
                else {
1789
                        IFW(*out++ = 0); ws++;
2,490✔
1790
                        return ws;
2,490✔
1791
                }
1792
        }
1793

1794
        // For dividing out denscale
1795
        flint::fmpz coeff, den, gcd;
66,156✔
1796

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

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

1803
                        fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
13,953✔
1804
                        fmpz_set(den.d, denscale);
13,953✔
1805
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
13,953✔
1806

1807
                        if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
13,953✔
1808
                                const int64_t fast_coeff = fmpz_get_si(coeff.d);
13,953✔
1809
                                // While ">=", could work here, FORM does not use fast notation for INT_MIN
1810
                                if ( fast_coeff > INT32_MIN && fast_coeff <= INT32_MAX ) {
13,953✔
1811
                                        IFW(*out++ = -SNUMBER); ws++;
13,953✔
1812
                                        IFW(*out++ = (WORD)fast_coeff); ws++;
13,953✔
1813
                                        return ws;
13,953✔
1814
                                }
1815
                        }
1816
                }
1817

1818
                else {
1819
                        fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, 0, ctx);
5,156✔
1820
                        fmpz_set(den.d, denscale);
5,156✔
1821
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
5,156✔
1822

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

1826
                                fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, 0, ctx);
954✔
1827
                                int64_t use_fast = 0;
1828
                                uint32_t fast_symbol = 0;
1829

1830
                                for ( size_t i = 0; i < var_map.size(); i++ ) {
3,078✔
1831
                                        if ( exponents[i] == 1 ) fast_symbol = var_map_inv[i];
2,124✔
1832
                                        use_fast += exponents[i];
2,124✔
1833
                                }
1834

1835
                                // use_fast has collected the total degree. If it is 1, then fast_symbol holds the code
1836
                                if ( use_fast == 1 ) {
954✔
1837
                                        IFW(*out++ = -SYMBOL); ws++;
183✔
1838
                                        IFW(*out++ = fast_symbol); ws++;
183✔
1839
                                        return ws;
183✔
1840
                                }
1841
                        }
1842
                }
1843
        }
1844

1845

1846
        WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_mpoly");
52,020✔
1847
        WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
52,020✔
1848

1849
        WORD* arg_size = 0;
52,020✔
1850
        WORD* arg_flag = 0;
52,020✔
1851
        if ( with_arghead ) {
52,020✔
1852
                IFW(arg_size = out++); ws++; // total arg size
40,146✔
1853
                IFW(arg_flag = out++); ws++;
40,146✔
1854
                IFW(*arg_flag = 0); // clean argument
40,146✔
1855
        }
1856

1857
        for ( int64_t i = 0; i < n_terms; i++ ) {
1,271,817✔
1858

1859
                fmpz_mpoly_get_term_exp_si((slong*)exponents.data(), poly, i, ctx);
1,219,803✔
1860

1861
                fmpz_mpoly_get_term_coeff_fmpz(coeff.d, poly, i, ctx);
1,219,803✔
1862
                fmpz_set(den.d, denscale);
1,219,803✔
1863
                flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
1,219,803✔
1864

1865
                uint32_t num_symbols = 0;
1866
                for ( size_t j = 0; j < var_map.size(); j++ ) {
5,394,050✔
1867
                        if ( exponents[j] != 0 ) { num_symbols += 1; }
4,174,247✔
1868
                }
1869

1870
                // Convert the coefficient, write in temporary space
1871
                const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
1,219,803✔
1872
                const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
1,219,803✔
1873
                const WORD coeff_size = [num_size, den_size] () -> WORD {
2,439,606✔
1874
                        WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
1,219,803✔
1875
                        return size * SGN(num_size) * SGN(den_size);
1,219,803✔
1876
                }();
1,219,803✔
1877

1878
                // Now we have the number of symbols and the coeff size, we can determine the output size.
1879
                // Check it fits if necessary: term size, num,den of "coeff_size", +1 for total coeff size
1880
                uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
1,219,803✔
1881
                if ( num_symbols ) {
1,219,803✔
1882
                        // symbols header, code,power of each symbol:
1883
                        current_size += 2 + 2*num_symbols;
1,208,667✔
1884
                }
1885
                if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
1,219,803✔
1886
                        MLOCK(ErrorMessageLock);
6✔
1887
                        MesPrint("flint::to_argument_mpoly: output exceeds MaxTermSize");
6✔
1888
                        MUNLOCK(ErrorMessageLock);
6✔
1889
                        Terminate(-1);
6✔
1890
                }
1891

1892
                WORD* term_size = 0;
1,219,797✔
1893
                IFW(term_size = out++); ws++;
1,219,797✔
1894
                if ( num_symbols ) {
1,219,797✔
1895
                        IFW(*out++ = SYMBOL); ws++;
1,208,661✔
1896
                        WORD* symbol_size = 0;
1,208,661✔
1897
                        IFW(symbol_size = out++); ws++;
1,208,661✔
1898
                        IFW(*symbol_size = 2);
1,208,661✔
1899

1900
                        for ( size_t j = 0; j < var_map.size(); j++ ) {
5,355,233✔
1901
                                if ( exponents[j] != 0 ) {
4,146,572✔
1902
                                        IFW(*out++ = var_map_inv[j]); ws++;
3,869,875✔
1903
                                        IFW(*out++ = exponents[j]); ws++;
3,869,875✔
1904
                                        IFW(*symbol_size += 2);
3,869,875✔
1905
                                }
1906
                        }
1907
                }
1908

1909
                // Copy numerator
1910
                for ( WORD j = 0; j < ABS(num_size); j++ ) {
2,576,760✔
1911
                        IFW(*out++ = tmp_coeff[j]); ws++;
1,356,963✔
1912
                }
1913
                for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
1,228,467✔
1914
                        IFW(*out++ = 0); ws++;
8,670✔
1915
                }
1916
                // Copy denominator
1917
                for ( WORD j = 0; j < ABS(den_size); j++ ) {
2,472,090✔
1918
                        IFW(*out++ = tmp_den[j]); ws++;
1,252,293✔
1919
                }
1920
                for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
1,333,137✔
1921
                        IFW(*out++ = 0); ws++;
113,340✔
1922
                }
1923

1924
                IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
1,219,797✔
1925
                IFW(if ( coeff_size < 0 ) { *out = -(*out); });
849,801✔
1926
                IFW(out++); ws++;
849,801✔
1927

1928
                IFW(*term_size = out - term_size);
1,219,797✔
1929
        }
1930

1931
        if ( with_arghead ) {
52,014✔
1932
                IFW(*arg_size = out - arg_size);
40,140✔
1933
                if ( write ) {
40,140✔
1934
                        // Sort into form highfirst ordering
1935
                        flint::form_sort(BHEAD (WORD*)(out_entry));
40,140✔
1936
                }
1937
        }
1938
        else {
1939
                // with no arghead, we write a terminating zero
1940
                IFW(*out++ = 0); ws++;
11,874✔
1941
        }
1942

1943
        NumberFree(tmp_coeff, "flint::to_argument_mpoly");
52,014✔
1944
        NumberFree(tmp_den, "flint::to_argument_mpoly");
52,014✔
1945

1946
        return ws;
52,014✔
1947
}
71,061✔
1948

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

1954
        flint::fmpz tmp;
58,713✔
1955
        fmpz_set_ui(tmp.d, 1);
58,713✔
1956

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

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

1976
        // Track the total size written. We could do this with pointer differences, but if
1977
        // write == false we don't write to or move out to be able to find the size that way.
1978
        uint64_t ws = 0;
2,937,011✔
1979

1980
        // Check there is at least space for ARGHEAD WORDs (the arghead or fast-notation number/symbol)
1981
        if ( write && must_fit_term && (sizeof(WORD)*(prev_size + ARGHEAD) > (size_t)AM.MaxTer) ) {
2,937,011✔
1982
                MLOCK(ErrorMessageLock);
×
1983
                MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
×
1984
                MUNLOCK(ErrorMessageLock);
×
1985
                Terminate(-1);
×
1986
        }
1987

1988
        // Create the inverse of var_map, so we don't have to search it for each symbol written
1989
        var_map_t var_map_inv;
2,937,011✔
1990
        for ( auto x: var_map ) {
5,798,448✔
1991
                var_map_inv[x.second] = x.first;
2,861,437✔
1992
        }
1993

1994
        const int64_t n_terms = fmpz_poly_length(poly);
2,937,011✔
1995

1996
        // The poly is zero
1997
        if ( n_terms == 0 ) {
2,937,011✔
1998
                if ( with_arghead ) {
11,219✔
1999
                        IFW(*out++ = -SNUMBER); ws++;
6,245✔
2000
                        IFW(*out++ = 0); ws++;
6,245✔
2001
                        return ws;
6,245✔
2002
                }
2003
                else {
2004
                        IFW(*out++ = 0); ws++;
4,974✔
2005
                        return ws;
4,974✔
2006
                }
2007
        }
2008

2009
        // For dividing out denscale
2010
        flint::fmpz coeff, den, gcd;
2,925,792✔
2011

2012
        // The poly is constant, use fast notation if the coefficient is integer and small enough
2013
        if ( with_arghead && n_terms == 1 ) {
2,925,792✔
2014

2015
                fmpz_poly_get_coeff_fmpz(coeff.d, poly, 0);
284,668✔
2016
                fmpz_set(den.d, denscale);
284,668✔
2017
                flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
284,668✔
2018

2019
                if ( fmpz_is_one(den.d) && fmpz_fits_si(coeff.d) ) {
284,668✔
2020
                        const long fast_coeff = fmpz_get_si(coeff.d);
284,668✔
2021
                        // While ">=", could work here, FORM does not use fast notation for INT_MIN
2022
                        if ( fast_coeff > INT_MIN && fast_coeff <= INT_MAX ) {
284,668✔
2023
                                IFW(*out++ = -SNUMBER); ws++;
284,668✔
2024
                                IFW(*out++ = (WORD)fast_coeff); ws++;
284,668✔
2025
                                return ws;
284,668✔
2026
                        }
2027
                }
2028
        }
2029

2030
        // The poly might be a single symbol with coeff 1, use fast notation if so.
2031
        if ( with_arghead && n_terms == 2 ) {
2,641,124✔
2032
                if ( fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, 0)) ) {
1,029,211✔
2033
                        // The constant term is zero
2034

2035
                        fmpz_poly_get_coeff_fmpz(coeff.d, poly, 1);
13,682✔
2036
                        fmpz_set(den.d, denscale);
13,682✔
2037
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
13,682✔
2038

2039
                        if ( fmpz_is_one(coeff.d) && fmpz_is_one(den.d) ) {
13,682✔
2040
                                // Single symbol with coeff 1. Use fast notation:
2041
                                IFW(*out++ = -SYMBOL); ws++;
1,135✔
2042
                                IFW(*out++ = var_map_inv[0]); ws++;
1,135✔
2043
                                return ws;
1,135✔
2044
                        }
2045
                }
2046
        }
2047

2048
        WORD *tmp_coeff = (WORD *)NumberMalloc("flint::to_argument_poly");
2,639,989✔
2049
        WORD *tmp_den = (WORD *)NumberMalloc("flint::to_argument_mpoly");
2,639,989✔
2050

2051
        WORD* arg_size = 0;
2,639,989✔
2052
        WORD* arg_flag = 0;
2,639,989✔
2053
        if ( with_arghead ) {
2,639,989✔
2054
                IFW(arg_size = out++); ws++; // total arg size
2,606,857✔
2055
                IFW(arg_flag = out++); ws++;
2,606,857✔
2056
                IFW(*arg_flag = 0); // clean argument
2,606,857✔
2057
        }
2058

2059
        // In reverse, since we want a "highfirst" output
2060
        for ( int64_t i = n_terms-1; i >= 0; i-- ) {
16,645,114✔
2061

2062
                // fmpz_poly is dense, there might be many zero coefficients:
2063
                if ( !fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)) ) {
14,005,131✔
2064

2065
                        fmpz_poly_get_coeff_fmpz(coeff.d, poly, i);
13,220,189✔
2066
                        fmpz_set(den.d, denscale);
13,220,189✔
2067
                        flint::util::simplify_fmpz(coeff.d, den.d, gcd.d);
13,220,189✔
2068

2069
                        // Convert the coefficient, write in temporary space
2070
                        const WORD num_size = flint::fmpz_get_form(coeff.d, tmp_coeff);
13,220,189✔
2071
                        const WORD den_size = flint::fmpz_get_form(den.d, tmp_den);
13,220,189✔
2072
                        const WORD coeff_size = [num_size, den_size] () -> WORD {
26,440,381✔
2073
                                WORD size = ABS(num_size) > ABS(den_size) ? ABS(num_size) : ABS(den_size);
13,220,192✔
2074
                                return size * SGN(num_size) * SGN(den_size);
13,220,192✔
2075
                        }();
13,220,189✔
2076

2077
                        // Now we have the coeff size, we can determine the output size
2078
                        // Check it fits if necessary: symbol code,power, num,den of "coeff_size",
2079
                        // +1 for total coeff size
2080
                        uint64_t current_size = prev_size + ws + 1 + 2*ABS(coeff_size) + 1;
13,220,189✔
2081
                        if ( i > 0 ) {
13,220,189✔
2082
                                // and also symbols header, code,power of the symbol
2083
                                current_size += 4;
10,897,429✔
2084
                        }
2085
                        if ( write && must_fit_term && (sizeof(WORD)*current_size > (size_t)AM.MaxTer) ) {
13,220,189✔
2086
                                MLOCK(ErrorMessageLock);
6✔
2087
                                MesPrint("flint::to_argument_poly: output exceeds MaxTermSize");
6✔
2088
                                MUNLOCK(ErrorMessageLock);
6✔
2089
                                Terminate(-1);
6✔
2090
                        }
2091

2092
                        WORD* term_size = 0;
13,220,183✔
2093
                        IFW(term_size = out++); ws++;
13,220,183✔
2094

2095
                        if ( i > 0 ) {
13,220,183✔
2096
                                IFW(*out++ = SYMBOL); ws++;
10,897,426✔
2097
                                IFW(*out++ = 4); ws++; // The symbol array size, it is univariate
10,897,426✔
2098
                                IFW(*out++ = var_map_inv[0]); ws++;
10,897,426✔
2099
                                IFW(*out++ = i); ws++;
10,897,426✔
2100
                        }
2101

2102
                        // Copy numerator
2103
                        for ( WORD j = 0; j < ABS(num_size); j++ ) {
54,228,722✔
2104
                                IFW(*out++ = tmp_coeff[j]); ws++;
41,008,539✔
2105
                        }
2106
                        for ( WORD j = ABS(num_size); j < ABS(coeff_size); j++ ) {
13,220,195✔
2107
                                IFW(*out++ = 0); ws++;
12✔
2108
                        }
2109
                        // Copy denominator
2110
                        for ( WORD j = 0; j < ABS(den_size); j++ ) {
26,446,468✔
2111
                                IFW(*out++ = tmp_den[j]); ws++;
13,226,285✔
2112
                        }
2113
                        for ( WORD j = ABS(den_size); j < ABS(coeff_size); j++ ) {
41,002,449✔
2114
                                IFW(*out++ = 0); ws++;
27,782,266✔
2115
                        }
2116

2117
                        IFW(*out = 2*ABS(coeff_size) + 1); // the size of the coefficient
13,220,183✔
2118
                        IFW(if ( coeff_size < 0 ) { *out = -(*out); });
13,066,145✔
2119
                        IFW(out++); ws++;
13,066,145✔
2120

2121
                        IFW(*term_size = out - term_size);
13,220,183✔
2122
                }
2123

2124
        }
2125

2126
        if ( with_arghead ) {
2,639,983✔
2127
                IFW(*arg_size = out - arg_size);
2,606,851✔
2128
        }
2129
        else {
2130
                // with no arghead, we write a terminating zero
2131
                IFW(*out++ = 0); ws++;
33,132✔
2132
        }
2133

2134
        NumberFree(tmp_coeff, "flint::to_argument_poly");
2,639,983✔
2135
        NumberFree(tmp_den, "flint::to_argument_poly");
2,639,983✔
2136

2137
        return ws;
2,639,983✔
2138
}
5,862,791✔
2139

2140
// If no denscale argument is supplied, just set it to 1 and call the usual function
2141
uint64_t flint::to_argument_poly(PHEAD WORD *out, const bool with_arghead,
2,901,941✔
2142
        const bool must_fit_term, const bool write, const uint64_t prev_size, const fmpz_poly_t poly,
2143
        const var_map_t &var_map) {
2144

2145
        flint::fmpz tmp;
2,901,941✔
2146
        fmpz_set_ui(tmp.d, 1);
2,901,941✔
2147

2148
        uint64_t ret = flint::to_argument_poly(BHEAD out, with_arghead, must_fit_term, write, prev_size,
2,901,941✔
2149
                poly, var_map, tmp.d);
2150

2151
        return ret;
2,901,935✔
2152
}
2,901,935✔
2153
/*
2154
        #] flint::to_argument_poly :
2155
*/
2156

2157
// Utility functions
2158
/*
2159
        #[ flint::util::simplify_fmpz :
2160
*/
2161
// Divide the GCD out of num and den
2162
inline void flint::util::simplify_fmpz(fmpz_t num, fmpz_t den, fmpz_t gcd) {
14,757,449✔
2163
        fmpz_gcd(gcd, num, den);
14,757,449✔
2164
        if ( !fmpz_is_one(gcd) ) {
14,757,449✔
2165
                fmpz_divexact(num, num, gcd);
110,328✔
2166
                fmpz_divexact(den, den, gcd);
110,328✔
2167
        }
2168
}
14,757,449✔
2169
/*
2170
        #] flint::util::simplify_fmpz :
2171
        #[ flint::util::simplify_fmpz_poly :
2172
*/
2173
// Divide the GCD out of num and den
2174
inline void flint::util::simplify_fmpz_poly(fmpz_poly_t num, fmpz_poly_t den, fmpz_poly_t gcd) {
4,687,425✔
2175
        fmpz_poly_gcd(gcd, num, den);
4,687,425✔
2176
        if ( !fmpz_poly_is_one(gcd) ) {
4,687,425✔
2177
#if __FLINT_RELEASE >= 30100
2178
                // This should be faster than fmpz_poly_div, see https://github.com/flintlib/flint/pull/1766
2179
                fmpz_poly_divexact(num, num, gcd);
365,805✔
2180
                fmpz_poly_divexact(den, den, gcd);
365,805✔
2181
#else
2182
                fmpz_poly_div(num, num, gcd);
2183
                fmpz_poly_div(den, den, gcd);
2184
#endif
2185
        }
2186
}
4,687,425✔
2187
/*
2188
        #] flint::util::simplify_fmpz_poly :
2189
        #[ flint::util::fix_sign_fmpz_mpoly_ratfun :
2190
*/
2191
inline void flint::util::fix_sign_fmpz_mpoly_ratfun(fmpz_mpoly_t num, fmpz_mpoly_t den,
28,335✔
2192
        const fmpz_mpoly_ctx_t ctx) {
2193
        // Fix sign to align with poly: the leading denominator term should have a positive coeff
2194
        if ( fmpz_sgn(fmpz_mpoly_term_coeff_ref(den, 0, ctx)) == -1 ) {
28,335✔
2195
                fmpz_mpoly_neg(num, num, ctx);
4,665✔
2196
                fmpz_mpoly_neg(den, den, ctx);
4,665✔
2197
        }
2198
}
28,335✔
2199
/*
2200
        #] flint::util::fix_sign_fmpz_mpoly_ratfun :
2201
        #[ flint::util::fix_sign_fmpz_poly_ratfun :
2202
*/
2203
inline void flint::util::fix_sign_fmpz_poly_ratfun(fmpz_poly_t num, fmpz_poly_t den) {
1,449,454✔
2204
        // Fix sign to align with poly: the leading denominator term should have a positive coeff
2205
        if ( fmpz_sgn(fmpz_poly_get_coeff_ptr(den, fmpz_poly_degree(den))) == -1 ) {
1,449,454✔
2206
                fmpz_poly_neg(num, num);
38,349✔
2207
                fmpz_poly_neg(den, den);
38,349✔
2208
        }
2209
}
1,449,454✔
2210
/*
2211
        #] flint::util::fix_sign_fmpz_poly_ratfun :
2212
*/
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