1 /*
2 Copyright (C) 2019 Daniel Schultz
3
4 This file is part of FLINT.
5
6 FLINT is free software: you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version. See <http://www.gnu.org/licenses/>.
10 */
11
12 #include "nmod_mpoly.h"
13
14
nmod_mpoly_univar_init(nmod_mpoly_univar_t A,const nmod_mpoly_ctx_t ctx)15 void nmod_mpoly_univar_init(nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx)
16 {
17 A->coeffs = NULL;
18 A->exps = NULL;
19 A->alloc = 0;
20 A->length = 0;
21 }
22
23
nmod_mpoly_univar_clear(nmod_mpoly_univar_t A,const nmod_mpoly_ctx_t ctx)24 void nmod_mpoly_univar_clear(nmod_mpoly_univar_t A, const nmod_mpoly_ctx_t ctx)
25 {
26 slong i;
27 for (i = 0; i < A->alloc; i++)
28 {
29 nmod_mpoly_clear(A->coeffs + i, ctx);
30 fmpz_clear(A->exps + i);
31 }
32
33 if (A->coeffs)
34 flint_free(A->coeffs);
35
36 if (A->exps)
37 flint_free(A->exps);
38 }
39
40
nmod_mpoly_univar_fit_length(nmod_mpoly_univar_t A,slong length,const nmod_mpoly_ctx_t ctx)41 void nmod_mpoly_univar_fit_length(nmod_mpoly_univar_t A,
42 slong length, const nmod_mpoly_ctx_t ctx)
43 {
44 slong i;
45 slong old_alloc = A->alloc;
46 slong new_alloc = FLINT_MAX(length, 2*A->alloc);
47
48 if (length > old_alloc)
49 {
50 if (old_alloc == 0)
51 {
52 A->exps = (fmpz *) flint_malloc(new_alloc*sizeof(fmpz));
53 A->coeffs = (nmod_mpoly_struct *) flint_malloc(
54 new_alloc*sizeof(nmod_mpoly_struct));
55 }
56 else
57 {
58 A->exps = (fmpz *) flint_realloc(A->exps, new_alloc*sizeof(fmpz));
59 A->coeffs = (nmod_mpoly_struct *) flint_realloc(A->coeffs,
60 new_alloc*sizeof(nmod_mpoly_struct));
61 }
62
63 for (i = old_alloc; i < new_alloc; i++)
64 {
65 fmpz_init(A->exps + i);
66 nmod_mpoly_init(A->coeffs + i, ctx);
67 }
68 A->alloc = new_alloc;
69 }
70 }
71
nmod_mpoly_univar_assert_canonical(nmod_mpoly_univar_t A,const nmod_mpoly_ctx_t ctx)72 void nmod_mpoly_univar_assert_canonical(nmod_mpoly_univar_t A,
73 const nmod_mpoly_ctx_t ctx)
74 {
75 slong i;
76
77 for (i = 0; i + 1 < A->length; i++)
78 {
79 if (fmpz_cmp(A->exps + i, A->exps + i + 1) <= 0
80 || fmpz_sgn(A->exps + i) < 0
81 || fmpz_sgn(A->exps + i + 1) < 0)
82 {
83 flint_throw(FLINT_ERROR, "Univariate polynomial exponents out of order");
84 }
85 }
86
87 for (i = 0; i < A->length; i++)
88 nmod_mpoly_assert_canonical(A->coeffs + i, ctx);
89 }
90
nmod_mpoly_univar_print_pretty(const nmod_mpoly_univar_t A,const char ** x,const nmod_mpoly_ctx_t ctx)91 void nmod_mpoly_univar_print_pretty(const nmod_mpoly_univar_t A,
92 const char ** x, const nmod_mpoly_ctx_t ctx)
93 {
94 slong i;
95 if (A->length == 0)
96 flint_printf("0");
97 for (i = 0; i < A->length; i++)
98 {
99 if (i != 0)
100 flint_printf("+");
101 flint_printf("(");
102 nmod_mpoly_print_pretty(A->coeffs + i,x,ctx);
103 flint_printf(")*X^");
104 fmpz_print(A->exps + i);
105 }
106 }
107
_mpoly_rbnode_clear_sp(nmod_mpoly_univar_t A,mpoly_rbtree_t tree,mpoly_rbnode_t node)108 static void _mpoly_rbnode_clear_sp(
109 nmod_mpoly_univar_t A,
110 mpoly_rbtree_t tree,
111 mpoly_rbnode_t node)
112 {
113 mpoly_rbnode_struct * left = node->left;
114
115 if (node->right != tree->null)
116 _mpoly_rbnode_clear_sp(A, tree, node->right);
117
118 FLINT_ASSERT(A->length < A->alloc);
119
120 fmpz_set_si(A->exps + A->length, node->key);
121 nmod_mpoly_swap(A->coeffs + A->length, (nmod_mpoly_struct *)(node->data), NULL);
122 A->length++;
123
124 nmod_mpoly_clear(node->data, NULL);
125 flint_free(node->data);
126 flint_free(node);
127
128 if (left != tree->null)
129 _mpoly_rbnode_clear_sp(A, tree, left);
130 }
131
_mpoly_rbnode_clear_mp(nmod_mpoly_univar_t A,mpoly_rbtree_t tree,mpoly_rbnode_t node)132 static void _mpoly_rbnode_clear_mp(
133 nmod_mpoly_univar_t A,
134 mpoly_rbtree_t tree,
135 mpoly_rbnode_t node)
136 {
137 mpoly_rbnode_struct * left = node->left;
138
139 if (node->right != tree->null)
140 _mpoly_rbnode_clear_mp(A, tree, node->right);
141
142 FLINT_ASSERT(A->length < A->alloc);
143
144 fmpz_swap(A->exps + A->length, (fmpz*)(&node->key));
145 nmod_mpoly_swap(A->coeffs + A->length, (nmod_mpoly_struct *)(node->data), NULL);
146 A->length++;
147
148 fmpz_clear((fmpz*)(&node->key));
149 nmod_mpoly_clear(node->data, NULL);
150 flint_free(node->data);
151 flint_free(node);
152
153 if (left != tree->null)
154 _mpoly_rbnode_clear_mp(A, tree, left);
155 }
156
157
158 /* the coefficients of A should be constructed with the same bits as B */
nmod_mpoly_to_univar(nmod_mpoly_univar_t A,const nmod_mpoly_t B,slong var,const nmod_mpoly_ctx_t ctx)159 void nmod_mpoly_to_univar(nmod_mpoly_univar_t A, const nmod_mpoly_t B,
160 slong var, const nmod_mpoly_ctx_t ctx)
161 {
162 flint_bitcnt_t bits = B->bits;
163 slong N = mpoly_words_per_exp(bits, ctx->minfo);
164 slong shift, off;
165 slong Blen = B->length;
166 const mp_limb_t * Bcoeff = B->coeffs;
167 const ulong * Bexp = B->exps;
168 slong i;
169 int new;
170 ulong * one;
171 mpoly_rbtree_t tree;
172 mpoly_rbnode_struct * node;
173 nmod_mpoly_struct * d;
174 #define LUT_limit (48)
175 nmod_mpoly_struct LUT[LUT_limit];
176 TMP_INIT;
177
178 if (B->length == 0)
179 {
180 A->length = 0;
181 return;
182 }
183
184 TMP_START;
185
186 one = (ulong*) TMP_ALLOC(N*sizeof(ulong));
187
188 mpoly_rbtree_init(tree);
189
190 if (bits <= FLINT_BITS)
191 {
192 ulong mask = (-UWORD(1)) >> (FLINT_BITS - bits);
193 mpoly_gen_monomial_offset_shift_sp(one, &off, &shift,
194 var, bits, ctx->minfo);
195 for (i = 0; i < LUT_limit; i++)
196 nmod_mpoly_init3(LUT + i, 4, bits, ctx);
197
198 /* fill in tree/LUT from B */
199 for (i = 0; i < Blen; i++)
200 {
201 ulong k = (Bexp[N*i + off] >> shift) & mask;
202 if (k < LUT_limit)
203 {
204 d = LUT + k;
205 }
206 else
207 {
208 node = mpoly_rbtree_get(&new, tree, k);
209 if (new)
210 {
211 d = flint_malloc(sizeof(nmod_mpoly_struct));
212 nmod_mpoly_init3(d, 4, bits, ctx);
213 node->data = d;
214 }
215 else
216 {
217 d = node->data;
218 }
219 }
220 nmod_mpoly_fit_length(d, d->length + 1, ctx);
221 d->coeffs[d->length] = Bcoeff[i];
222 mpoly_monomial_msub(d->exps + N*d->length, Bexp + N*i, k, one, N);
223 d->length++;
224 }
225
226 /* clear out tree to A */
227 nmod_mpoly_univar_fit_length(A, tree->size + LUT_limit, ctx);
228 A->length = 0;
229 if (tree->size > 0)
230 _mpoly_rbnode_clear_sp(A, tree, tree->head->left);
231
232 for (i = LUT_limit - 1; i >= 0; i--)
233 {
234 d = LUT + i;
235 if (d->length > 0)
236 {
237 FLINT_ASSERT(A->length < A->alloc);
238 fmpz_set_si(A->exps + A->length, i);
239 nmod_mpoly_swap(A->coeffs + A->length, d, ctx);
240 A->length++;
241 }
242 nmod_mpoly_clear(d, ctx);
243 }
244 }
245 else
246 {
247 fmpz_t k;
248 fmpz_init(k);
249
250 off = mpoly_gen_monomial_offset_mp(one, var, bits, ctx->minfo);
251
252 /* fill in tree from B */
253 for (i = 0; i < Blen; i++)
254 {
255 fmpz_set_ui_array(k, Bexp + N*i + off, bits/FLINT_BITS);
256
257 node = mpoly_rbtree_get_fmpz(&new, tree, k);
258 if (new)
259 {
260 d = flint_malloc(sizeof(nmod_mpoly_struct));
261 nmod_mpoly_init3(d, 4, bits, ctx);
262 node->data = d;
263 }
264 else
265 {
266 d = node->data;
267 }
268 nmod_mpoly_fit_length(d, d->length + 1, ctx);
269 d->coeffs[d->length] = Bcoeff[i];
270 mpoly_monomial_msub_ui_array(d->exps + N*d->length, Bexp + N*i,
271 Bexp + N*i + off, bits/FLINT_BITS, one, N);
272 d->length++;
273 }
274
275 /* clear out tree to A */
276 nmod_mpoly_univar_fit_length(A, tree->size, ctx);
277 A->length = 0;
278 FLINT_ASSERT(tree->size > 0);
279 _mpoly_rbnode_clear_mp(A, tree, tree->head->left);
280
281 fmpz_clear(k);
282 }
283
284 TMP_END;
285 }
286
287
288 /*
289 Currently this function does not work if the coefficients depend on "var".
290 The assertion x->next == NULL would need to be replaced by a loop.
291 Other asserts would need to be removed as well.
292 */
nmod_mpoly_from_univar_bits(nmod_mpoly_t A,flint_bitcnt_t Abits,const nmod_mpoly_univar_t B,slong var,const nmod_mpoly_ctx_t ctx)293 void nmod_mpoly_from_univar_bits(nmod_mpoly_t A, flint_bitcnt_t Abits,
294 const nmod_mpoly_univar_t B, slong var, const nmod_mpoly_ctx_t ctx)
295 {
296 slong N = mpoly_words_per_exp(Abits, ctx->minfo);
297 slong i;
298 slong next_loc, heap_len = 1;
299 ulong * cmpmask;
300 slong total_len;
301 mpoly_heap_s * heap;
302 slong Alen;
303 ulong ** Btexp;
304 ulong * exp;
305 ulong * one;
306 mpoly_heap_t * chain, * x;
307 TMP_INIT;
308
309 if (B->length == 0)
310 {
311 nmod_mpoly_fit_bits(A, Abits, ctx);
312 A->bits = Abits;
313 A->length = 0;
314 return;
315 }
316
317 TMP_START;
318
319 /* pack everything into Abits */
320
321 one = (ulong*) TMP_ALLOC(N*sizeof(ulong));
322 cmpmask = (ulong*) TMP_ALLOC(N*sizeof(ulong));
323 Btexp = (ulong **) TMP_ALLOC(B->length*sizeof(ulong*));
324
325 total_len = 0;
326 for (i = 0; i < B->length; i++)
327 {
328 nmod_mpoly_struct * Bi = B->coeffs + i;
329 total_len += Bi->length;
330 Btexp[i] = Bi->exps;
331 if (Abits != Bi->bits)
332 {
333 Btexp[i] = (ulong *) flint_malloc(N*Bi->length*sizeof(ulong));
334 if (!mpoly_repack_monomials(Btexp[i], Abits,
335 Bi->exps, Bi->bits, Bi->length, ctx->minfo))
336 {
337 FLINT_ASSERT(0 && "repack does not fit");
338 }
339 }
340 }
341
342 nmod_mpoly_fit_length(A, total_len, ctx);
343 nmod_mpoly_fit_bits(A, Abits, ctx);
344 A->bits = Abits;
345
346 next_loc = B->length + 2;
347 heap = (mpoly_heap_s *) TMP_ALLOC((B->length + 1)*sizeof(mpoly_heap_s));
348 exp = (ulong *) TMP_ALLOC(B->length*N*sizeof(ulong));
349 chain = (mpoly_heap_t *) TMP_ALLOC(B->length*sizeof(mpoly_heap_t));
350
351 mpoly_get_cmpmask(cmpmask, N, Abits, ctx->minfo);
352
353 Alen = 0;
354 if (Abits <= FLINT_BITS)
355 {
356 mpoly_gen_monomial_sp(one, var, Abits, ctx->minfo);
357
358 for (i = 0; i < B->length; i++)
359 {
360 FLINT_ASSERT(fmpz_fits_si(B->exps + i));
361 x = chain + i;
362 x->i = i;
363 x->j = 0;
364 x->next = NULL;
365 mpoly_monomial_madd(exp + N*x->i, Btexp[x->i] + N*x->j,
366 fmpz_get_si(B->exps + i), one, N);
367 _mpoly_heap_insert(heap, exp + N*i, x, &next_loc, &heap_len,
368 N, cmpmask);
369 }
370
371 while (heap_len > 1)
372 {
373 FLINT_ASSERT(Alen < A->alloc);
374 mpoly_monomial_set(A->exps + N*Alen, heap[1].exp, N);
375 x = _mpoly_heap_pop(heap, &heap_len, N, cmpmask);
376 A->coeffs[Alen] = (B->coeffs + x->i)->coeffs[x->j];
377 Alen++;
378
379 FLINT_ASSERT(x->next == NULL);
380
381 if (x->j + 1 < (B->coeffs + x->i)->length)
382 {
383 FLINT_ASSERT(fmpz_fits_si(B->exps + x->i));
384 x->j = x->j + 1;
385 x->next = NULL;
386 mpoly_monomial_madd(exp + N*x->i, Btexp[x->i] + N*x->j,
387 fmpz_get_si(B->exps + x->i), one, N);
388 _mpoly_heap_insert(heap, exp + N*x->i, x, &next_loc, &heap_len,
389 N, cmpmask);
390 }
391 }
392 }
393 else
394 {
395 mpoly_gen_monomial_offset_mp(one, var, Abits, ctx->minfo);
396
397 for (i = 0; i < B->length; i++)
398 {
399 x = chain + i;
400 x->i = i;
401 x->j = 0;
402 x->next = NULL;
403 mpoly_monomial_madd_fmpz(exp + N*x->i, Btexp[x->i] + N*x->j,
404 B->exps + i, one, N);
405 _mpoly_heap_insert(heap, exp + N*i, x, &next_loc, &heap_len,
406 N, cmpmask);
407 }
408
409 while (heap_len > 1)
410 {
411 FLINT_ASSERT(Alen < A->alloc);
412 mpoly_monomial_set(A->exps + N*Alen, heap[1].exp, N);
413 x = _mpoly_heap_pop(heap, &heap_len, N, cmpmask);
414 A->coeffs[Alen] = (B->coeffs + x->i)->coeffs[x->j];
415 Alen++;
416
417 FLINT_ASSERT(x->next == NULL);
418
419 if (x->j + 1 < (B->coeffs + x->i)->length)
420 {
421 x->j = x->j + 1;
422 x->next = NULL;
423 mpoly_monomial_madd_fmpz(exp + N*x->i, Btexp[x->i] + N*x->j,
424 B->exps + x->i, one, N);
425 _mpoly_heap_insert(heap, exp + N*x->i, x, &next_loc, &heap_len,
426 N, cmpmask);
427 }
428 }
429 }
430
431 A->length = Alen;
432
433 for (i = 0; i < B->length; i++)
434 {
435 if (Btexp[i] != (B->coeffs + i)->exps)
436 flint_free(Btexp[i]);
437 }
438
439 TMP_END;
440 }
441
nmod_mpoly_from_univar(nmod_mpoly_t A,const nmod_mpoly_univar_t B,slong var,const nmod_mpoly_ctx_t ctx)442 void nmod_mpoly_from_univar(nmod_mpoly_t A, const nmod_mpoly_univar_t B,
443 slong var, const nmod_mpoly_ctx_t ctx)
444 {
445 slong n = ctx->minfo->nfields;
446 flint_bitcnt_t bits;
447 slong i;
448 fmpz * gen_fields, * tmp_fields, * max_fields;
449 TMP_INIT;
450
451 if (B->length == 0)
452 {
453 nmod_mpoly_zero(A, ctx);
454 return;
455 }
456
457 TMP_START;
458
459 /* find bits required to represent result */
460 gen_fields = (fmpz *) TMP_ALLOC(ctx->minfo->nfields*sizeof(fmpz));
461 tmp_fields = (fmpz *) TMP_ALLOC(ctx->minfo->nfields*sizeof(fmpz));
462 max_fields = (fmpz *) TMP_ALLOC(ctx->minfo->nfields*sizeof(fmpz));
463 for (i = 0; i < ctx->minfo->nfields; i++)
464 {
465 fmpz_init(gen_fields + i);
466 fmpz_init(tmp_fields + i);
467 fmpz_init(max_fields + i);
468 }
469
470 mpoly_gen_fields_fmpz(gen_fields, var, ctx->minfo);
471
472 for (i = 0; i < B->length; i++)
473 {
474 nmod_mpoly_struct * Bi = B->coeffs + i;
475 mpoly_max_fields_fmpz(tmp_fields, Bi->exps, Bi->length, Bi->bits, ctx->minfo);
476 _fmpz_vec_scalar_addmul_fmpz(tmp_fields, gen_fields, n, B->exps + i);
477 _fmpz_vec_max_inplace(max_fields, tmp_fields, n);
478 }
479 bits = _fmpz_vec_max_bits(max_fields, n);
480 bits = FLINT_MAX(MPOLY_MIN_BITS, bits + 1);
481 bits = mpoly_fix_bits(bits, ctx->minfo);
482
483 for (i = 0; i < ctx->minfo->nfields; i++)
484 {
485 fmpz_clear(gen_fields + i);
486 fmpz_clear(tmp_fields + i);
487 fmpz_clear(max_fields + i);
488 }
489 TMP_END;
490
491 nmod_mpoly_from_univar_bits(A, bits, B, var, ctx);
492 }
493
494