1 /*
2     Copyright (C) 2020 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 "fmpq_mpoly.h"
13 
14 
fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A,const fmpq_mpoly_ctx_t ctx)15 void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx)
16 {
17     A->coeffs = NULL;
18     A->exps = NULL;
19     A->alloc = 0;
20     A->length = 0;
21 }
22 
23 
fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A,const fmpq_mpoly_ctx_t ctx)24 void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx)
25 {
26     slong i;
27     for (i = 0; i < A->alloc; i++)
28     {
29         fmpq_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 
fmpq_mpoly_univar_fit_length(fmpq_mpoly_univar_t A,slong length,const fmpq_mpoly_ctx_t ctx)41 void fmpq_mpoly_univar_fit_length(fmpq_mpoly_univar_t A,
42                                       slong length, const fmpq_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 = (fmpq_mpoly_struct *) flint_malloc(
54                                           new_alloc*sizeof(fmpq_mpoly_struct));
55         }
56         else
57         {
58             A->exps = (fmpz *) flint_realloc(A->exps, new_alloc*sizeof(fmpz));
59             A->coeffs = (fmpq_mpoly_struct *) flint_realloc(A->coeffs,
60                                           new_alloc*sizeof(fmpq_mpoly_struct));
61         }
62 
63         for (i = old_alloc; i < new_alloc; i++)
64         {
65             fmpz_init(A->exps + i);
66             fmpq_mpoly_init(A->coeffs + i, ctx);
67         }
68         A->alloc = new_alloc;
69     }
70 }
71 
72 
fmpq_mpoly_univar_assert_canonical(fmpq_mpoly_univar_t A,const fmpq_mpoly_ctx_t ctx)73 void fmpq_mpoly_univar_assert_canonical(fmpq_mpoly_univar_t A,
74                                                     const fmpq_mpoly_ctx_t ctx)
75 {
76     slong i;
77 
78     for (i = 0; i + 1 < A->length; i++)
79     {
80         if (fmpz_cmp(A->exps + i, A->exps + i + 1) <= 0
81             || fmpz_sgn(A->exps + i) < 0
82             || fmpz_sgn(A->exps + i + 1) < 0)
83         {
84             flint_throw(FLINT_ERROR, "Univariate polynomial exponents out of order");
85         }
86     }
87 
88     for (i = 0; i < A->length; i++)
89         fmpq_mpoly_assert_canonical(A->coeffs + i, ctx);
90 }
91 
92 
fmpq_mpoly_univar_print_pretty(const fmpq_mpoly_univar_t A,const char ** x,const fmpq_mpoly_ctx_t ctx)93 void fmpq_mpoly_univar_print_pretty(const fmpq_mpoly_univar_t A,
94                                    const char ** x, const fmpq_mpoly_ctx_t ctx)
95 {
96     slong i;
97     if (A->length == 0)
98         flint_printf("0");
99     for (i = 0; i < A->length; i++)
100     {
101         if (i != 0)
102             flint_printf("+");
103         flint_printf("(");
104         fmpq_mpoly_print_pretty(A->coeffs + i,x,ctx);
105         flint_printf(")*X^");
106         fmpz_print(A->exps + i);
107     }
108 }
109 
110 
fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A,const fmpq_mpoly_t B,slong var,const fmpq_mpoly_ctx_t ctx)111 void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B,
112                                          slong var, const fmpq_mpoly_ctx_t ctx)
113 {
114     slong i;
115     fmpz_mpoly_univar_t Z;
116     fmpz_mpoly_univar_init(Z, ctx->zctx);
117     fmpz_mpoly_to_univar(Z, B->zpoly, var, ctx->zctx);
118     fmpq_mpoly_univar_fit_length(A, Z->length, ctx);
119     A->length = Z->length;
120     for (i = Z->length - 1; i >= 0; i--)
121     {
122         fmpz_swap(A->exps + i, Z->exps + i);
123         fmpz_mpoly_swap(A->coeffs[i].zpoly, Z->coeffs + i, ctx->zctx);
124         fmpq_set(A->coeffs[i].content, B->content);
125         fmpq_mpoly_reduce(A->coeffs + i, ctx);
126     }
127     fmpz_mpoly_univar_clear(Z, ctx->zctx);
128 }
129 
130 /*
131     Currently this function does not work if the coefficients depend on "var".
132     The assertion x->next == NULL would need to be replaced by a loop.
133     Other asserts would need to be removed as well.
134 */
fmpq_mpoly_from_univar_bits(fmpq_mpoly_t A,flint_bitcnt_t Abits,const fmpq_mpoly_univar_t B,slong var,const fmpq_mpoly_ctx_t ctx)135 void fmpq_mpoly_from_univar_bits(fmpq_mpoly_t A, flint_bitcnt_t Abits,
136             const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx)
137 {
138     slong N = mpoly_words_per_exp(Abits, ctx->zctx->minfo);
139     slong i;
140     slong next_loc, heap_len = 1;
141     ulong * cmpmask;
142     slong total_len;
143     mpoly_heap_s * heap;
144     slong Alen;
145     ulong ** Btexp;
146     fmpz * Bscales;
147     fmpz_t t;
148     ulong * exp;
149     ulong * one;
150     mpoly_heap_t * chain, * x;
151     TMP_INIT;
152 
153     if (B->length == 0)
154     {
155         fmpz_mpoly_fit_bits(A->zpoly, Abits, ctx->zctx);
156         A->zpoly->bits = Abits;
157         _fmpz_mpoly_set_length(A->zpoly, 0, ctx->zctx);
158         fmpq_zero(A->content);
159         return;
160     }
161 
162     TMP_START;
163 
164     /* pack everything into Abits */
165 
166     one = (ulong*) TMP_ALLOC(N*sizeof(ulong));
167     cmpmask = (ulong*) TMP_ALLOC(N*sizeof(ulong));
168     Btexp = (ulong **) TMP_ALLOC(B->length*sizeof(ulong*));
169     Bscales = (fmpz *) TMP_ALLOC(B->length*sizeof(ulong*));
170 
171     total_len = 0;
172     fmpq_zero(A->content);
173     for (i = 0; i < B->length; i++)
174     {
175         fmpz_mpoly_struct * Bi;
176 
177         fmpz_init(Bscales + i);
178 
179         fmpq_gcd(A->content, A->content, B->coeffs[i].content);
180 
181         Bi = B->coeffs[i].zpoly;
182         total_len += Bi->length;
183         Btexp[i] = Bi->exps;
184         if (Abits != Bi->bits)
185         {
186             Btexp[i] = (ulong *) flint_malloc(N*Bi->length*sizeof(ulong));
187             if (!mpoly_repack_monomials(Btexp[i], Abits,
188                              Bi->exps, Bi->bits, Bi->length, ctx->zctx->minfo))
189             {
190                 FLINT_ASSERT(0 && "repack does not fit");
191             }
192         }
193     }
194 
195     fmpz_init(t);
196     if (!fmpq_is_zero(A->content))
197     {
198         for (i = 0; i < B->length; i++)
199         {
200             _fmpq_div(Bscales + i, t, fmpq_numref(B->coeffs[i].content),
201                                       fmpq_denref(B->coeffs[i].content),
202                                       fmpq_numref(A->content),
203                                       fmpq_denref(A->content));
204             FLINT_ASSERT(fmpz_is_one(t));
205         }
206     }
207     fmpz_clear(t);
208 
209     fmpz_mpoly_fit_length(A->zpoly, total_len, ctx->zctx);
210     fmpz_mpoly_fit_bits(A->zpoly, Abits, ctx->zctx);
211     A->zpoly->bits = Abits;
212 
213     next_loc = B->length + 2;
214     heap = (mpoly_heap_s *) TMP_ALLOC((B->length + 1)*sizeof(mpoly_heap_s));
215     exp = (ulong *) TMP_ALLOC(B->length*N*sizeof(ulong));
216     chain = (mpoly_heap_t *) TMP_ALLOC(B->length*sizeof(mpoly_heap_t));
217 
218     mpoly_get_cmpmask(cmpmask, N, Abits, ctx->zctx->minfo);
219 
220     Alen = 0;
221     if (Abits <= FLINT_BITS)
222     {
223         mpoly_gen_monomial_sp(one, var, Abits, ctx->zctx->minfo);
224 
225         for (i = 0; i < B->length; i++)
226         {
227             FLINT_ASSERT(fmpz_fits_si(B->exps + i));
228             x = chain + i;
229             x->i = i;
230             x->j = 0;
231             x->next = NULL;
232             mpoly_monomial_madd(exp + N*x->i, Btexp[x->i] + N*x->j,
233                                              fmpz_get_si(B->exps + i), one, N);
234             _mpoly_heap_insert(heap, exp + N*i, x, &next_loc, &heap_len,
235                                                                    N, cmpmask);
236         }
237 
238         while (heap_len > 1)
239         {
240             FLINT_ASSERT(Alen < A->zpoly->alloc);
241             mpoly_monomial_set(A->zpoly->exps + N*Alen, heap[1].exp, N);
242             x = _mpoly_heap_pop(heap, &heap_len, N, cmpmask);
243             fmpz_mul(A->zpoly->coeffs + Alen, Bscales + x->i,
244                                      (B->coeffs + x->i)->zpoly->coeffs + x->j);
245             Alen++;
246 
247             FLINT_ASSERT(x->next == NULL);
248 
249             if (x->j + 1 < (B->coeffs + x->i)->zpoly->length)
250             {
251                 FLINT_ASSERT(fmpz_fits_si(B->exps + x->i));
252                 x->j = x->j + 1;
253                 x->next = NULL;
254                 mpoly_monomial_madd(exp + N*x->i, Btexp[x->i] + N*x->j,
255                                           fmpz_get_si(B->exps + x->i), one, N);
256                 _mpoly_heap_insert(heap, exp + N*x->i, x, &next_loc, &heap_len,
257                                                                    N, cmpmask);
258             }
259         }
260     }
261     else
262     {
263         mpoly_gen_monomial_offset_mp(one, var, Abits, ctx->zctx->minfo);
264 
265         for (i = 0; i < B->length; i++)
266         {
267             x = chain + i;
268             x->i = i;
269             x->j = 0;
270             x->next = NULL;
271             mpoly_monomial_madd_fmpz(exp + N*x->i, Btexp[x->i] + N*x->j,
272                                                           B->exps + i, one, N);
273             _mpoly_heap_insert(heap, exp + N*i, x, &next_loc, &heap_len,
274                                                                    N, cmpmask);
275         }
276 
277         while (heap_len > 1)
278         {
279             FLINT_ASSERT(Alen < A->zpoly->alloc);
280             mpoly_monomial_set(A->zpoly->exps + N*Alen, heap[1].exp, N);
281             x = _mpoly_heap_pop(heap, &heap_len, N, cmpmask);
282             fmpz_mul(A->zpoly->coeffs + Alen, Bscales + x->i,
283                                      (B->coeffs + x->i)->zpoly->coeffs + x->j);
284             Alen++;
285 
286             FLINT_ASSERT(x->next == NULL);
287 
288             if (x->j + 1 < (B->coeffs + x->i)->zpoly->length)
289             {
290                 x->j = x->j + 1;
291                 x->next = NULL;
292                 mpoly_monomial_madd_fmpz(exp + N*x->i, Btexp[x->i] + N*x->j,
293                                                        B->exps + x->i, one, N);
294                 _mpoly_heap_insert(heap, exp + N*x->i, x, &next_loc, &heap_len,
295                                                                    N, cmpmask);
296             }
297         }
298     }
299 
300     for (i = 0; i < B->length; i++)
301     {
302         fmpz_clear(Bscales + i);
303         if (Btexp[i] != (B->coeffs + i)->zpoly->exps)
304             flint_free(Btexp[i]);
305     }
306     TMP_END;
307 
308     _fmpz_mpoly_set_length(A->zpoly, Alen, ctx->zctx);
309     fmpq_mpoly_reduce(A, ctx);
310 }
311 
312 
fmpq_mpoly_from_univar(fmpq_mpoly_t A,const fmpq_mpoly_univar_t B,slong var,const fmpq_mpoly_ctx_t ctx)313 void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B,
314                                          slong var, const fmpq_mpoly_ctx_t ctx)
315 {
316     slong n = ctx->zctx->minfo->nfields;
317     flint_bitcnt_t bits;
318     slong i;
319     fmpz * gen_fields, * tmp_fields, * max_fields;
320     TMP_INIT;
321 
322     if (B->length == 0)
323     {
324         fmpq_mpoly_zero(A, ctx);
325         return;
326     }
327 
328     TMP_START;
329 
330     /* find bits required to represent result */
331     gen_fields = (fmpz *) TMP_ALLOC(ctx->zctx->minfo->nfields*sizeof(fmpz));
332     tmp_fields = (fmpz *) TMP_ALLOC(ctx->zctx->minfo->nfields*sizeof(fmpz));
333     max_fields = (fmpz *) TMP_ALLOC(ctx->zctx->minfo->nfields*sizeof(fmpz));
334     for (i = 0; i < ctx->zctx->minfo->nfields; i++)
335     {
336         fmpz_init(gen_fields + i);
337         fmpz_init(tmp_fields + i);
338         fmpz_init(max_fields + i);
339     }
340 
341     mpoly_gen_fields_fmpz(gen_fields, var, ctx->zctx->minfo);
342 
343     for (i = 0; i < B->length; i++)
344     {
345         fmpz_mpoly_struct * Bi = B->coeffs[i].zpoly;
346         mpoly_max_fields_fmpz(tmp_fields, Bi->exps, Bi->length, Bi->bits, ctx->zctx->minfo);
347         _fmpz_vec_scalar_addmul_fmpz(tmp_fields, gen_fields, n, B->exps + i);
348         _fmpz_vec_max_inplace(max_fields, tmp_fields, n);
349     }
350     bits = _fmpz_vec_max_bits(max_fields, n);
351     bits = FLINT_MAX(MPOLY_MIN_BITS, bits + 1);
352     bits = mpoly_fix_bits(bits, ctx->zctx->minfo);
353 
354     for (i = 0; i < ctx->zctx->minfo->nfields; i++)
355     {
356         fmpz_clear(gen_fields + i);
357         fmpz_clear(tmp_fields + i);
358         fmpz_clear(max_fields + i);
359     }
360     TMP_END;
361 
362     fmpq_mpoly_from_univar_bits(A, bits, B, var, ctx);
363 }
364 
365