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