1 /**
2  * Copyright 2015, SRI International.
3  *
4  * This file is part of LibPoly.
5  *
6  * LibPoly is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * LibPoly is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with LibPoly.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "polynomial/factorization.h"
21 
22 #include "polynomial/gcd.h"
23 #include "polynomial/output.h"
24 
25 #include "utils/debug_trace.h"
26 #include "utils/statistics.h"
27 
28 #include <stdlib.h>
29 
STAT_DECLARE(int,coefficient,factor_content_free)30 STAT_DECLARE(int, coefficient, factor_content_free)
31 STAT_DECLARE(int, coefficient, factor_square_free)
32 STAT_DECLARE(int, coefficient, factor_square_free_pp)
33 
34 #define INITIAL_FACTORS_CAPACITY 5
35 
36 void coefficient_factors_construct(coefficient_factors_t* factors) {
37   factors->size = 0;
38   factors->capacity = INITIAL_FACTORS_CAPACITY;
39   factors->factors = malloc(sizeof(coefficient_t)*INITIAL_FACTORS_CAPACITY);
40   factors->multiplicities = malloc(sizeof(size_t)*INITIAL_FACTORS_CAPACITY);
41 }
42 
coefficient_factors_destruct(coefficient_factors_t * factors)43 void coefficient_factors_destruct(coefficient_factors_t* factors) {
44   if (factors->factors) {
45     size_t i;
46     for (i = 0; i < factors->size; ++ i) {
47       coefficient_destruct(factors->factors + i);
48     }
49     free(factors->factors);
50     free(factors->multiplicities);
51   }
52 }
53 
coefficient_factors_add(const lp_polynomial_context_t * ctx,coefficient_factors_t * factors,const coefficient_t * C,size_t multiplicity)54 void coefficient_factors_add(const lp_polynomial_context_t* ctx, coefficient_factors_t* factors, const coefficient_t* C, size_t multiplicity) {
55   if (factors->size == factors->capacity) {
56     factors->capacity *= 2;
57     factors->factors = realloc(factors->factors, sizeof(coefficient_t)*factors->capacity);
58     factors->multiplicities = realloc(factors->multiplicities, sizeof(size_t)*factors->capacity);
59   }
60 
61   factors->multiplicities[factors->size] = multiplicity;
62   coefficient_construct_copy(ctx, factors->factors + factors->size, C);
63   factors->size ++;
64 }
65 
coefficient_factors_print(const lp_polynomial_context_t * ctx,const coefficient_factors_t * factors,FILE * out)66 int coefficient_factors_print(const lp_polynomial_context_t* ctx, const coefficient_factors_t* factors, FILE* out) {
67   int ret = 0;
68 
69   fprintf(out, "[");
70 
71   size_t i;
72   for (i = 0; i < factors->size; ++ i) {
73     if (i) {
74       ret += fprintf(out, ", ");
75     }
76     ret += fprintf(out, "(");
77     ret += coefficient_print(ctx, factors->factors + i, out);
78     ret += fprintf(out, ", %zu)", factors->multiplicities[i]);
79   }
80 
81   fprintf(out, "]");
82 
83   return ret;
84 }
85 
86 /**
87  * We are given a polynomial f and we will return it's square-free factorization
88  *
89  *  f = \prod_{k} f_k^k
90  *
91  * where each f_k is square free and all gcd(f_i, f_j) = 1.
92  *
93  * First, note that if f has a square factor, then the gcd(f, f') != 1. This is
94  * because if f = u^2*v, then f' = 2*u*u'*v + u^2*v, so gcd(f, f') would include
95  * at least u != 1.
96  *
97  * Now,consider f'
98  *
99  *  f' = \sum_{k} k*f_k^{k-1}*f_k'*\prod_{i!=k} f_i^i (if we are in Z_p)
100  *     = \sum_{p \ndiv k} k*f_k^{k-1}*f_k'*\prod_{i!=k}
101  *
102  * It is easy to see that f' is divisible by each f_k^{k-1} for k such that
103  * p \ndiv k, but not f_k^k. It is also divisible by f_k^k for k such that p
104  * \div k. Since these are the only possible factors of f, then
105  *
106  *  P = gcd(f, f') = \prod_{p \ndiv k} f_k^{k-1} * \prod_{p \div k} f_k^k
107  *
108  * With P as above, then
109  *
110  *  L = f/P = \prod_{p \ndiv k} f_k               -> Linear product (can be 1)
111  *
112  * To compute the first factor we then compute (loop start)
113  *
114  *  R = gcd(P, L) = \prod_{k > 1 & p \ndiv k} f_k -> Rest of linear product
115  *  O = L / R                                     -> First factor (output)
116  *
117  * To continue the loop, we set
118  *
119  *  P = P / R     -> Reduced powers of f_k with p \ndiv k, k > 1
120  *  L = R         -> Linear product of f_k with p \ndiv k, k > 1
121  *
122  * And go on with the loop.
123  *
124  * The loop ends when L = 1 and then we know that P = \prod_{p \div k} f_k^k
125  * which we know how to special case (if P != 1).
126  */
coefficient_factor_square_free_pp(const lp_polynomial_context_t * ctx,const coefficient_t * C,coefficient_factors_t * factors)127 void coefficient_factor_square_free_pp(const lp_polynomial_context_t* ctx, const coefficient_t* C, coefficient_factors_t* factors) {
128 
129   STAT_INCR(coefficient, factor_square_free_pp)
130 
131   if (trace_is_enabled("factorization")) {
132     tracef("coefficient_factor_square_free_pp("); coefficient_print(ctx, C, trace_out); tracef(")\n");
133   }
134 
135   assert(C->type == COEFFICIENT_POLYNOMIAL);
136 
137   // Derivative
138   coefficient_t C_d;
139   coefficient_construct(ctx, &C_d);
140   coefficient_derivative(ctx, &C_d, C);
141 
142   if (coefficient_is_zero(ctx, &C_d)) {
143       assert(ctx->K && ctx->K->is_prime);
144       // f' is zero for a non-zero polynomial => f has to be of the form
145       // f = \sum a_k x^(p*d_k) = f_p(x^p) where f_p = \sum a_k x^d_k
146       // we factor f_p and then return f_p(x^p)=(f_p)^p
147       size_t p = (size_t) integer_to_int(&ctx->K->M);
148       coefficient_t C_p;
149       coefficient_construct_copy(ctx, &C_p, C);
150       coefficient_div_degrees(ctx, &C_p, p);
151       size_t i = factors->size;
152       coefficient_factor_square_free_pp(ctx, &C_p, factors);
153       // Adjust the multiplicities
154       for (;i < factors->size; ++ i) {
155         factors->multiplicities[i] *= p;
156       }
157       coefficient_destruct(&C_p);
158     } else {
159 
160       // Degree of the factor
161       size_t k = 1;
162 
163       if (trace_is_enabled("factorization")) {
164         tracef("C = "); coefficient_print(ctx, C, trace_out); tracef("\n");
165         tracef("C' = "); coefficient_print(ctx, &C_d, trace_out); tracef("\n");
166       }
167 
168       // P = GCD(f, f')
169       coefficient_t P;
170       coefficient_construct(ctx, &P);
171       coefficient_gcd(ctx, &P, C, &C_d);
172       if (trace_is_enabled("factorization")) {
173         tracef("P = "); coefficient_print(ctx, &P, trace_out); tracef("\n");
174       }
175       // L = f/P
176       coefficient_t L;
177       coefficient_construct(ctx, &L);
178       coefficient_div(ctx, &L, C, &P);
179       if (trace_is_enabled("factorization")) {
180         tracef("L = "); coefficient_print(ctx, &L, trace_out); tracef("\n");
181       }
182 
183       coefficient_t O, R;
184       coefficient_construct(ctx, &R);
185       coefficient_construct(ctx, &O);
186       while (coefficient_degree(&L) > 0) {
187         // R = gcd(P, L)
188         coefficient_gcd(ctx, &R, &P, &L);
189         if (trace_is_enabled("factorization")) {
190           tracef("R = "); coefficient_print(ctx, &R, trace_out); tracef("\n");
191         }
192         // O = L / R (it can be constant if there is no factor of power k)
193         if (coefficient_cmp(ctx, &L, &R)) {
194           coefficient_div(ctx, &O, &L, &R);
195           if (trace_is_enabled("factorization")) {
196             tracef("O = "); coefficient_print(ctx, &O, trace_out); tracef("\n");
197           }
198           // Record the output
199           coefficient_factors_add(ctx, factors, &O, k);
200         }
201         // P = P / R
202         coefficient_div(ctx, &P, &P, &R);
203         if (trace_is_enabled("factorization")) {
204           tracef("P = "); coefficient_print(ctx, &P, trace_out); tracef("\n");
205         }
206         // L = R
207         coefficient_swap(&L, &R);
208         if (trace_is_enabled("factorization")) {
209           tracef("L = "); coefficient_print(ctx, &L, trace_out); tracef("\n");
210         }
211         // Next degree
212         k = k + 1;
213       }
214 
215       // If P has content, it is a power of p
216       if (coefficient_degree(&P) > 0) {
217         size_t p = integer_to_int(&ctx->K->M);
218         coefficient_div_degrees(ctx, &P, p);
219         size_t i = factors->size;
220         coefficient_factor_square_free_pp(ctx, &P, factors);
221         for (; i < factors->size; ++ i) {
222           factors->multiplicities[i] *= p;
223         }
224       }
225 
226       coefficient_destruct(&P);
227       coefficient_destruct(&L);
228       coefficient_destruct(&O);
229       coefficient_destruct(&R);
230     }
231 
232 
233   coefficient_destruct(&C_d);
234 
235   if (trace_is_enabled("factorization")) {
236     tracef("coefficient_factor_square_free("); coefficient_print(ctx, C, trace_out); tracef(") = ");
237     coefficient_factors_print(ctx, factors, trace_out); tracef("\n");
238   }
239 }
240 
241 static
coefficient_factor_square_free_special(const lp_polynomial_context_t * ctx,const coefficient_t * C,coefficient_factors_t * factors)242 int coefficient_factor_square_free_special(const lp_polynomial_context_t* ctx, const coefficient_t* C, coefficient_factors_t* factors) {
243   // Check if linear
244   if (coefficient_is_linear(C)) {
245     // We only need to take out the get the content and primitive part
246     coefficient_t C_pp, C_cont;
247     coefficient_construct(ctx, &C_pp);
248     coefficient_construct(ctx, &C_cont);
249     coefficient_pp_cont(ctx, &C_pp, &C_cont, C);
250     if (!coefficient_is_one(ctx, &C_cont)) {
251       coefficient_factors_add(ctx, factors, &C_cont, 1);
252     }
253     if (!coefficient_is_one(ctx, &C_pp)) {
254       coefficient_factors_add(ctx, factors, &C_pp, 1);
255     }
256     coefficient_destruct(&C_pp);
257     coefficient_destruct(&C_cont);
258     return 1;
259   }
260   return 0;
261 }
262 
coefficient_factor_square_free(const lp_polynomial_context_t * ctx,const coefficient_t * C,coefficient_factors_t * factors)263 void coefficient_factor_square_free(const lp_polynomial_context_t* ctx, const coefficient_t* C, coefficient_factors_t* factors) {
264 
265   STAT_INCR(coefficient, factor_square_free)
266 
267   if (trace_is_enabled("factorization")) {
268     tracef("coefficient_factor_square_free("); coefficient_print(ctx, C, trace_out); tracef(")\n");
269   }
270 
271   // Check for special cases
272   int special = coefficient_factor_square_free_special(ctx, C, factors);
273   if (special) {
274     return;
275   }
276 
277   // Regular factorization
278   coefficient_t C_pp, C_cont;
279   coefficient_construct(ctx, &C_pp);
280   coefficient_construct(ctx, &C_cont);
281 
282   // Get the content and primitive part
283   coefficient_pp_cont(ctx, &C_pp, &C_cont, C);
284 
285   // Factor the content if not trivial
286   if (coefficient_is_constant(&C_cont)) {
287     if (!coefficient_is_one(ctx, &C_cont)) {
288       coefficient_factors_add(ctx, factors, &C_cont, 1);
289     }
290   } else {
291     coefficient_factor_square_free(ctx, &C_cont, factors);
292   }
293 
294   // Factor the primitive part
295   if (!coefficient_is_constant(&C_pp)) {
296     coefficient_factor_square_free_pp(ctx, &C_pp, factors);
297   }
298 
299   coefficient_destruct(&C_pp);
300   coefficient_destruct(&C_cont);
301 
302   if (trace_is_enabled("factorization")) {
303     tracef("coefficient_factor_square_free("); coefficient_print(ctx, C, trace_out); tracef(") =>");
304     coefficient_factors_print(ctx, factors, trace_out); tracef("\n");
305   }
306 }
307 
coefficient_factor_content_free(const lp_polynomial_context_t * ctx,const coefficient_t * C,coefficient_factors_t * factors)308 void coefficient_factor_content_free(const lp_polynomial_context_t* ctx, const coefficient_t* C, coefficient_factors_t* factors)  {
309 
310   STAT_INCR(coefficient, factor_square_free)
311 
312   if (trace_is_enabled("factorization")) {
313     tracef("coefficient_factor_content_free("); coefficient_print(ctx, C, trace_out); tracef(")\n");
314   }
315 
316   coefficient_t C_pp, C_cont;
317   coefficient_construct(ctx, &C_pp);
318   coefficient_construct(ctx, &C_cont);
319 
320   // Get the content and primitive part
321   coefficient_pp_cont(ctx, &C_pp, &C_cont, C);
322 
323   // Factor the content if not trivial
324   if (!coefficient_is_constant(&C_cont)) {
325     coefficient_factor_content_free(ctx, &C_cont, factors);
326   } else {
327     // Add if not one
328     if (!coefficient_is_one(ctx, &C_cont)) {
329       coefficient_factors_add(ctx, factors, &C_cont, 1);
330     }
331   }
332 
333   // Add the primitive part
334   if (!coefficient_is_one(ctx, &C_pp)) {
335     coefficient_factors_add(ctx, factors, &C_pp, 1);
336   }
337 
338   coefficient_destruct(&C_pp);
339   coefficient_destruct(&C_cont);
340 
341   if (trace_is_enabled("factorization")) {
342     tracef("coefficient_factor_square_free("); coefficient_print(ctx, C, trace_out); tracef(") =>");
343     coefficient_factors_print(ctx, factors, trace_out); tracef("\n");
344   }
345 
346 }
347