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