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