1 /*
2 Copyright (C) 2011 William Hart
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 <gmp.h>
13 #include "flint.h"
14 #include "fmpz.h"
15 #include "fmpz_vec.h"
16 #include "fmpz_poly.h"
17 #include "mpn_extras.h"
18
19 /*
20 Divide (arrayg, limbsg) by the positive value gc inplace and
21 return the number of limbs written
22 */
flint_mpn_tdiv_q_fmpz_inplace(mp_ptr arrayg,mp_size_t limbsg,fmpz_t gc)23 mp_size_t flint_mpn_tdiv_q_fmpz_inplace(mp_ptr arrayg, mp_size_t limbsg, fmpz_t gc)
24 {
25 if (fmpz_size(gc) == 1)
26 {
27 mpn_divmod_1(arrayg, arrayg, limbsg, fmpz_get_ui(gc));
28 return limbsg - (arrayg[limbsg - 1] == 0);
29 }
30 else
31 {
32 mp_size_t tlimbs;
33 __mpz_struct * mpz_ptr = COEFF_TO_PTR(*gc);
34
35 mp_ptr temp = flint_malloc(limbsg*sizeof(mp_limb_t));
36 flint_mpn_copyi(temp, arrayg, limbsg);
37
38 mpn_tdiv_q(arrayg, temp, limbsg, mpz_ptr->_mp_d, mpz_ptr->_mp_size);
39 tlimbs = limbsg - mpz_ptr->_mp_size + 1;
40 tlimbs -= (arrayg[tlimbs - 1] == 0);
41
42 flint_free(temp);
43 return tlimbs;
44 }
45 }
46
47 /*
48 Returns 1 if sign * (G, glen) * (Q, qlen) = (P, len), else returns 0.
49 Temp requires space for glen + qlen - 1 coefficients
50 */
multiplies_out(fmpz * P,slong len,const fmpz * Q,slong qlen,const fmpz * G,slong glen,slong sign,fmpz * temp)51 int multiplies_out(fmpz * P, slong len, const fmpz * Q, slong qlen,
52 const fmpz * G, slong glen, slong sign, fmpz * temp)
53 {
54 int divides = 0;
55
56 /* multiply out */
57 if (qlen >= glen)
58 _fmpz_poly_mul(temp, Q, qlen, G, glen);
59 else
60 _fmpz_poly_mul(temp, G, glen, Q, qlen);
61 if (sign < WORD(0)) _fmpz_vec_neg(temp, temp, glen + qlen - 1);
62
63 /* check if quotient really was exact */
64 divides = (glen + qlen - 1 == len && _fmpz_vec_equal(temp, P, len));
65
66 return divides;
67 }
68
69 /* Assumes len1 != 0 != len2 */
70 int
_fmpz_poly_gcd_heuristic(fmpz * res,const fmpz * poly1,slong len1,const fmpz * poly2,slong len2)71 _fmpz_poly_gcd_heuristic(fmpz * res, const fmpz * poly1, slong len1,
72 const fmpz * poly2, slong len2)
73 {
74 slong bits1, bits2, max_bits, pack_bits, bound_bits, bits_G, bits_Q;
75 ulong limbs1, limbs2, limbsg, pack_limbs, qlimbs, qlimbs2;
76 ulong log_glen, log_length;
77 slong sign1, sign2, glen, qlen, qlen2;
78 fmpz_t ac, bc, d, gc;
79 fmpz * A, * B, * G, * Q, * t;
80 mp_ptr array1, array2, arrayg, q, temp;
81 int divides;
82
83 fmpz_init(ac);
84 fmpz_init(bc);
85 fmpz_init(d);
86
87 /* compute gcd of content of poly1 and poly2 */
88 _fmpz_poly_content(ac, poly1, len1);
89 _fmpz_poly_content(bc, poly2, len2);
90 fmpz_gcd(d, ac, bc);
91
92 /* special case, one of the polys is a constant */
93 if (len2 == 1) /* if len1 == 1 then so does len2 */
94 {
95 fmpz_set(res, d);
96
97 fmpz_clear(ac);
98 fmpz_clear(bc);
99 fmpz_clear(d);
100
101 return 1;
102 }
103
104 /* divide poly1 and poly2 by their content */
105 A = _fmpz_vec_init(len1);
106 B = _fmpz_vec_init(len2);
107 _fmpz_vec_scalar_divexact_fmpz(A, poly1, len1, ac);
108 _fmpz_vec_scalar_divexact_fmpz(B, poly2, len2, bc);
109 fmpz_clear(ac);
110 fmpz_clear(bc);
111
112 /* special case, one of the polys is length 2 */
113 if (len2 == 2) /* if len1 == 2 then so does len2 */
114 {
115 Q = _fmpz_vec_init(len1 - len2 + 1);
116 if (_fmpz_poly_divides(Q, A, len1, B, 2))
117 {
118 _fmpz_vec_scalar_mul_fmpz(res, B, 2, d);
119 if (fmpz_sgn(res + 1) < 0)
120 _fmpz_vec_neg(res, res, 2);
121 }
122 else
123 {
124 fmpz_set(res, d);
125 fmpz_zero(res + 1);
126 }
127
128 fmpz_clear(d);
129 _fmpz_vec_clear(A, len1);
130 _fmpz_vec_clear(B, len2);
131 _fmpz_vec_clear(Q, len1 - len2 + 1);
132
133 return 1;
134 }
135
136 /*
137 Determine how many bits (pack_bits) to pack into. The bound
138 bound_bits ensures that if G | A and G | B with G primitive
139 then G is the gcd of A and B. The bound is taken from
140 http://arxiv.org/abs/cs/0206032v1
141 */
142 bits1 = FLINT_ABS(_fmpz_vec_max_bits(A, len1));
143 bits2 = FLINT_ABS(_fmpz_vec_max_bits(B, len2));
144 /*
145 always extra bit for signs whether polys are signed or not, since we don't
146 know if any purported gcds/quotients will be signed
147 */
148 max_bits = FLINT_MAX(bits1, bits2) + 1;
149
150 /*
151 the +6 is chosen heuristically for performance; the theorem
152 is satisfied with +3 (including a bit for signs)
153 */
154 bound_bits = FLINT_MIN(bits1, bits2) + 6;
155 pack_bits = FLINT_MAX(bound_bits, max_bits); /* need to pack original polys */
156 pack_limbs = (pack_bits - 1)/FLINT_BITS + 1;
157
158 if (pack_bits >= 32) /* pack into multiples of limbs if >= 32 bits */
159 pack_bits = FLINT_BITS*pack_limbs;
160
161 /* allocate space to pack into */
162 limbs1 = (pack_bits*len1 - 1)/FLINT_BITS + 1;
163 limbs2 = (pack_bits*len2 - 1)/FLINT_BITS + 1;
164 array1 = flint_calloc(limbs1, sizeof(mp_limb_t));
165 array2 = flint_calloc(limbs2, sizeof(mp_limb_t));
166 arrayg = flint_calloc(limbs2, sizeof(mp_limb_t));
167
168 /* pack first poly and normalise */
169 sign1 = (slong) fmpz_sgn(A + len1 - 1);
170 _fmpz_poly_bit_pack(array1, A, len1, pack_bits, sign1);
171 while (array1[limbs1 - 1] == 0) limbs1--;
172
173 /* pack second poly and normalise */
174 sign2 = (slong) fmpz_sgn(B + len2 - 1);
175 _fmpz_poly_bit_pack(array2, B, len2, pack_bits, sign2);
176 while (array2[limbs2 - 1] == 0) limbs2--;
177
178 /* compute integer GCD */
179 limbsg = flint_mpn_gcd_full(arrayg, array1, limbs1, array2, limbs2);
180
181 /*
182 Make space for unpacked gcd. May have one extra coeff due to
183 1 0 -x being packed as 0 -1 -x.
184 */
185 glen = FLINT_MIN((limbsg*FLINT_BITS)/pack_bits + 1, len2);
186 G = _fmpz_vec_init(glen);
187
188 /*
189 clear bits after g in arrayg so they are not inadvertently
190 pulled into G after bit unpacking
191 */
192 flint_mpn_zero(arrayg + limbsg, limbs2-limbsg);
193
194 /* unpack gcd */
195 _fmpz_poly_bit_unpack(G, glen, arrayg, pack_bits, 0);
196 while (G[glen - 1] == 0) glen--;
197
198 /* divide by any content */
199 fmpz_init(gc);
200 _fmpz_poly_content(gc, G, glen);
201
202 if (!fmpz_is_one(gc))
203 limbsg = flint_mpn_tdiv_q_fmpz_inplace(arrayg, limbsg, gc);
204
205 /* make space for quotient and remainder of both polys by gcd */
206 qlimbs = limbs1 - limbsg + 1;
207 qlen = FLINT_MIN(len1, (qlimbs*FLINT_BITS)/pack_bits + 1);
208 qlimbs2 = limbs2 - limbsg + 1;
209 qlen2 = FLINT_MIN(len2, (qlimbs2*FLINT_BITS)/pack_bits + 1);
210 qlimbs = (FLINT_MAX(qlen, qlen2)*pack_bits - 1)/FLINT_BITS + 1;
211 q = flint_calloc(qlimbs, sizeof(mp_limb_t));
212 temp = flint_malloc(limbsg*sizeof(mp_limb_t));
213 divides = 0;
214
215 if (flint_mpn_divides(q, array1, limbs1, arrayg, limbsg, temp))
216 {
217 /* unpack quotient of first poly by gcd */
218 Q = _fmpz_vec_init(len1);
219 t = _fmpz_vec_init(len1 + glen);
220 _fmpz_poly_bit_unpack(Q, qlen, q, pack_bits, 0);
221 while (Q[qlen - 1] == 0) qlen--;
222
223 /* divide by content */
224 _fmpz_vec_scalar_divexact_fmpz(G, G, glen, gc);
225
226 /* check if we really need to multiply out to check for exact quotient */
227 bits_G = FLINT_ABS(_fmpz_vec_max_bits(G, glen));
228 bits_Q = FLINT_ABS(_fmpz_vec_max_bits(Q, qlen));
229 log_glen = FLINT_BIT_COUNT(glen);
230 log_length = FLINT_MIN(log_glen, FLINT_BIT_COUNT(qlen));
231
232 /* allow one bit for signs */
233 divides = (bits_G + bits_Q + log_length < pack_bits);
234
235 if (!divides) /* need to multiply out to check exact quotient */
236 divides = multiplies_out(A, len1, Q, qlen, G, glen, sign1, t);
237
238 if (divides) /* quotient really was exact */
239 {
240 divides = 0;
241 flint_mpn_zero(q, qlimbs);
242
243 if (flint_mpn_divides(q, array2, limbs2, arrayg, limbsg, temp))
244 {
245 /* unpack quotient of second poly by gcd */
246 _fmpz_poly_bit_unpack(Q, qlen2, q, pack_bits, 0);
247 while (Q[qlen2 - 1] == 0) qlen2--;
248
249 /* check if we really need to multiply out to check for exact quotient */
250 bits_Q = FLINT_ABS(_fmpz_vec_max_bits(Q, qlen2));
251 log_length = FLINT_MIN(log_glen, FLINT_BIT_COUNT(qlen2));
252
253 /* allow one bit for signs */
254 divides = (bits_G + bits_Q + log_length < pack_bits);
255
256 if (!divides) /* we need to multiply out */
257 divides = multiplies_out(B, len2, Q, qlen2, G, glen, sign1, t);
258 }
259 }
260
261 _fmpz_vec_clear(t, len1 + glen);
262 _fmpz_vec_clear(Q, len1);
263 }
264
265 flint_free(q);
266 flint_free(temp);
267 flint_free(arrayg);
268 flint_free(array1);
269 flint_free(array2);
270 fmpz_clear(gc);
271
272 _fmpz_vec_clear(A, len1);
273 _fmpz_vec_clear(B, len2);
274
275 /* we found the gcd, so multiply by content */
276 if (divides)
277 {
278 _fmpz_vec_zero(res + glen, len2 - glen);
279 _fmpz_vec_scalar_mul_fmpz(res, G, glen, d);
280 }
281
282 fmpz_clear(d);
283 _fmpz_vec_clear(G, glen);
284
285 return divides;
286 }
287
288 int
fmpz_poly_gcd_heuristic(fmpz_poly_t res,const fmpz_poly_t poly1,const fmpz_poly_t poly2)289 fmpz_poly_gcd_heuristic(fmpz_poly_t res, const fmpz_poly_t poly1,
290 const fmpz_poly_t poly2)
291 {
292 if (poly1->length < poly2->length)
293 {
294 return fmpz_poly_gcd_heuristic(res, poly2, poly1);
295 }
296 else /* len1 >= len2 >= 0 */
297 {
298 const slong len1 = poly1->length;
299 const slong len2 = poly2->length;
300 int done = 1; /* len1 = 0 or len2 = 0 need done = 1 */
301
302 if (len1 == 0) /* len1 = len2 = 0 */
303 {
304 fmpz_poly_zero(res);
305 }
306 else if (len2 == 0) /* len1 > len2 = 0 */
307 {
308 if (fmpz_sgn(poly1->coeffs + (len1 - 1)) > 0)
309 fmpz_poly_set(res, poly1);
310 else
311 fmpz_poly_neg(res, poly1);
312 }
313 else /* len1 >= len2 >= 1 */
314 {
315 /* underscore function automatically takes care of aliasing */
316
317 fmpz_poly_fit_length(res, len2);
318
319 done = _fmpz_poly_gcd_heuristic(res->coeffs, poly1->coeffs, len1,
320 poly2->coeffs, len2);
321
322 if (done)
323 {
324 _fmpz_poly_set_length(res, len2);
325 _fmpz_poly_normalise(res);
326 }
327 }
328
329 return done;
330 }
331 }
332