carl  24.04
Computer ARithmetic Library
Factorization_univariate.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "Derivative.h"
4 #include "Division.h"
5 #include "GCD_univariate.h"
6 
8 #include "../UnivariatePolynomial.h"
9 
10 namespace carl {
11 
12 // TODO replace with cocoa implementation
13 
14 template<typename Coeff>
15 std::map<uint, UnivariatePolynomial<Coeff>> squareFreeFactorization(const UnivariatePolynomial<Coeff>& p) {
16  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: " << p);
17  std::map<uint,UnivariatePolynomial<Coeff>> result;
18 CLANG_WARNING_DISABLE("-Wtautological-compare")
19  assert(!is_zero(p)); // TODO what if zero?
20  // degree() >= characteristic<Coeff>::value throws a warning in clang...
22 CLANG_WARNING_RESET
23  {
24  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: degree greater than characteristic!");
25  result.emplace(1, p);
26  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: add the factor (" << p << ")^1");
27  }
28  else
29  {
30  assert(!is_constant(p)); // Othewise, the derivative is zero and the next assertion is thrown.
32  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: b = " << b);
35  assert(!is_zero(b));
36  UnivariatePolynomial<Coeff> c = carl::extended_gcd(p, b, s, t); // TODO: use gcd instead
37  typename IntegralType<Coeff>::type numOfCpf = get_num(c.coprime_factor());
38  if(numOfCpf != 1) // TODO: is this maybe only necessary because the extended_gcd returns a polynomial with non-integer coefficients but it shouldn't?
39  {
40  c *= Coeff(numOfCpf);
41  }
42  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: c = " << c);
43  if(is_zero(c))
44  {
45  result.emplace(1, p);
46  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: add the factor (" << p << ")^1");
47  }
48  else
49  {
50  UnivariatePolynomial<Coeff> w = carl::divide(p, c).quotient;
51  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: w = " << w);
52  UnivariatePolynomial<Coeff> y = carl::divide(b, c).quotient;
53  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: y = " << y);
55  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: z = " << z);
56  uint i = 1;
57  while(!is_zero(z))
58  {
59  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: next iteration");
60  UnivariatePolynomial<Coeff> g = carl::extended_gcd(w, z, s, t); // TODO: use gcd instead
61  numOfCpf = get_num(g.coprime_factor());
62  if(numOfCpf != 1) // TODO: is this maybe only necessary because the extended_gcd returns a polynomial with non-integer coefficients but it shouldn't?
63  {
64  g *= Coeff(numOfCpf);
65  }
66  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: g = " << g);
67  assert(result.find(i) == result.end());
68  result.emplace(i, g);
69  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: add the factor (" << g << ")^" << i);
70  ++i;
71  w = carl::divide(w, g).quotient;
72  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: w = " << w);
73  y = carl::divide(z, g).quotient;
74  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: y = " << y);
75  z = y - derivative(w);
76  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: z = " << z);
77  }
78  result.emplace(i, w);
79  CARL_LOG_TRACE("carl.core.upoly", "UnivSSF: add the factor (" << w << ")^" << i);
80  }
81  }
82  return result;
83 }
84 
85 namespace detail {
86 
87 template<typename Coeff, typename Integer>
89  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: " << poly );
91  // Exclude the factor x^i from result.
92  auto cf = poly.coefficients().begin();
93  if(*cf == Coeff(0)) // result is of the form a_n * x^n + ... + a_k * x^k (a>k, k>0)
94  {
95  uint k = 0;
96  while(*cf == Coeff(0))
97  {
98  assert(cf != poly.coefficients().end());
99  ++cf;
100  ++k;
101  }
102  // Take x^k as a factor.
103  auto retVal = linearFactors.emplace(UnivariatePolynomial<Coeff>(poly.main_var(), {Coeff(0), Coeff(1)}), k);
104  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: add the factor (" << retVal.first->first << ")^" << k );
105  if(!retVal.second)
106  {
107  retVal.first->second += k;
108  }
109  // Construct the remainder: result := a_n * x^{n-k} + ... + a_{k-1} * x + a_k
110  std::vector<Coeff> cfs;
111  cfs.reserve(poly.coefficients().size()-k);
112  cfs = std::vector<Coeff>(cf, poly.coefficients().end());
113  result = UnivariatePolynomial<Coeff>(poly.main_var(), std::move(cfs));
114  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: remainder is " << result );
115  }
116  else
117  {
118  result = poly;
119  }
120  // Check whether the polynomial is already a linear factor.
121  if(!result.is_linear_in_main_var())
122  {
123  // Exclude the factor (x-r)^i, with r rational and r!=0, from result.
124  assert(result.coefficients().size() > 1);
125  typename IntegralType<Coeff>::type lc = carl::abs(get_num(result.lcoeff()));
126  typename IntegralType<Coeff>::type tc = carl::abs(get_num(result.coefficients().front()));
127  typename IntegralType<Coeff>::type mi = carl::from_int<typename IntegralType<Coeff>::type>(maxInt);
128  if( maxInt != 0 && (tc > mi || lc > mi) )
129  {
130  return result;
131  }
132  Integer lcAsInt = to_int<Integer>(lc);
133  Integer tcAsInt = to_int<Integer>(tc);
134  Integer halfOfLcAsInt = lcAsInt == 1 ? 1 : lcAsInt/2;
135  Integer halfOfTcAsInt = tcAsInt == 1 ? 1 : tcAsInt/2;
136  std::vector<std::pair<Integer, Integer>> shiftedTcs;
137  bool positive = true;
138  bool tcFactorsFound = false;
139  std::vector<Integer> tcFactors(1, 1); // TODO: store the divisors of some numbers during compilation
140  auto tcFactor = tcFactors.begin();
141  bool lcFactorsFound = false;
142  std::vector<Integer> lcFactors(1, 1); // TODO: store the divisors of some numbers during compilation
143  auto lcFactor = lcFactors.begin();
144  while(true)
145  {
146  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: try rational " << (positive ? "-" : "") << *tcFactor << "/" << *lcFactor);
147  // Check whether the numerator of the rational to consider divides the trailing coefficient of all
148  // zero-preserving shifts {result(x+x_0) | for some found x_0 with result(x_0)!=0 and x_0 integer}
149  auto shiftedTc = shiftedTcs.begin();
150  for(; shiftedTc != shiftedTcs.end(); ++shiftedTc)
151  {
152  // we need to be careful with overflows in the following lines
153  if(maxInt/(*lcFactor) >= shiftedTc->first)
154  {
155  Integer divisor = (*lcFactor) * shiftedTc->first;
156  if( divisor != *tcFactor )
157  {
158  if( !(divisor < 0 && *tcFactor < 0 && maxInt + divisor >= -(*tcFactor)) && !(divisor > 0 && *tcFactor > 0 && maxInt - divisor >= *tcFactor ) )
159  {
160  if( divisor > *tcFactor )
161  {
162  divisor = divisor - *tcFactor;
163  }
164  else
165  {
166  divisor = *tcFactor - divisor;
167  }
168  if(carl::mod(shiftedTc->second, divisor) != 0)
169  {
170  break;
171  }
172  }
173  }
174  }
175  }
176  if(shiftedTc == shiftedTcs.end())
177  {
178  Coeff posRatZero = carl::from_int<typename IntegralType<Coeff>::type>(*tcFactor) / carl::from_int<typename IntegralType<Coeff>::type>(*lcFactor);
179  if (!positive) posRatZero = -posRatZero;
180  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: consider possible non zero rational factor " << posRatZero);
181  Coeff image = result.synthetic_division(posRatZero);
182  if (carl::is_zero(image)) {
183  // Remove all linear factor with the found zero from result.
184  UnivariatePolynomial<Coeff> linearFactor(result.main_var(), {-posRatZero, Coeff(1)});
185  while (carl::is_zero(image)) {
186  auto retVal = linearFactors.emplace(linearFactor, 1);
187  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: add the factor (" << linearFactor << ")^" << 1 );
188  if(!retVal.second)
189  {
190  ++retVal.first->second;
191  }
192  // Check whether result is a linear factor now.
193  if(result.is_linear_in_main_var())
194  {
195  goto LinearFactorRemains;
196  }
197  image = result.synthetic_division(posRatZero);
198  }
199  }
200  else if(is_integer(posRatZero))
201  {
202  // Add a zero-preserving shift.
203  assert(is_integer(image));
204  typename IntegralType<Coeff>::type imageInt = carl::abs(get_num(image));
205  using IntNumberType = typename IntegralType<typename UnderlyingNumberType<Coeff>::type>::type;
206  if( imageInt <= carl::from_int<IntNumberType>(maxInt) )
207  {
208  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: new shift with " << get_num(posRatZero) << " to " << carl::abs(get_num(image)));
209  shiftedTcs.push_back(std::pair<Integer, Integer>(to_int<Integer>(get_num(posRatZero)), to_int<Integer>(carl::abs(get_num(image)))));
210  }
211  }
212  }
213  // Find the next numerator-denominator combination.
214  if(shiftedTc == shiftedTcs.end() && positive)
215  {
216  positive = false;
217  }
218  else
219  {
220  positive = true;
221  if(lcFactorsFound)
222  {
223  ++lcFactor;
224  }
225  else
226  {
227  lcFactors.push_back(lcFactors.back());
228  while(lcFactors.back() <= halfOfLcAsInt)
229  {
230  ++lcFactors.back();
231  if(carl::mod(lcAsInt, lcFactors.back()) == 0)
232  {
233  break;
234  }
235  }
236  if(lcFactors.back() > halfOfLcAsInt)
237  {
238  lcFactors.pop_back();
239  lcFactorsFound = true;
240  lcFactor = lcFactors.end();
241  }
242  else
243  {
244  lcFactor = --(lcFactors.end());
245  }
246  }
247  if(lcFactor == lcFactors.end())
248  {
249  if(tcFactorsFound)
250  {
251  ++tcFactor;
252  }
253  else
254  {
255  tcFactors.push_back(tcFactors.back());
256  while(tcFactors.back() <= halfOfTcAsInt)
257  {
258  ++(tcFactors.back());
259  if(carl::mod(tcAsInt, tcFactors.back()) == 0)
260  {
261  break;
262  }
263  }
264  if(tcFactors.back() > halfOfTcAsInt)
265  {
266  tcFactors.pop_back();
267  tcFactor = tcFactors.end();
268  }
269  else
270  {
271  tcFactor = --(tcFactors.end());
272  }
273  }
274  if(tcFactor == tcFactors.end())
275  {
276  Coeff factor = result.coprime_factor();
277  if (!carl::is_one(factor)) {
278  result *= factor;
279  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: add the factor (" << UnivariatePolynomial<Coeff>(result.main_var(), std::initializer_list<Coeff>({(Coeff)1/factor})) << ")^" << 1 );
280  // Add the constant factor to the factors.
281  if( is_constant(linearFactors.begin()->first) )
282  {
283  factor = Coeff(1) / factor;
284  factor *= linearFactors.begin()->first.lcoeff();
285  linearFactors.erase(linearFactors.begin());
286  }
287  linearFactors.insert(linearFactors.begin(), std::pair<UnivariatePolynomial<Coeff>, uint>(UnivariatePolynomial<Coeff>(result.main_var(), std::initializer_list<Coeff>({factor})), 1));
288  }
289  return result;
290  }
291  lcFactor = lcFactors.begin();
292  }
293  }
294  }
295  assert(false);
296  }
297 LinearFactorRemains:
298  Coeff factor = result.lcoeff();
299  if (!carl::is_one(factor)) {
300  result /= factor;
301  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: add the factor (" << UnivariatePolynomial<Coeff>(result.main_var(), factor) << ")^" << 1 );
302  // Add the constant factor to the factors.
303  if( !linearFactors.empty() && is_constant(linearFactors.begin()->first) )
304  {
305  if( carl::is_zero(linearFactors.begin()->first) )
306  factor = Coeff( 0 );
307  else
308  factor *= linearFactors.begin()->first.lcoeff();
309  linearFactors.erase(linearFactors.begin());
310  }
311  linearFactors.insert(linearFactors.begin(), std::pair<UnivariatePolynomial<Coeff>, uint>(UnivariatePolynomial<Coeff>(result.main_var(), factor), 1));
312  }
313  auto retVal = linearFactors.emplace(result, 1);
314  CARL_LOG_TRACE("carl.core.upoly", "UnivELF: add the factor (" << result << ")^" << 1 );
315  if(!retVal.second)
316  {
317  ++retVal.first->second;
318  }
319  return UnivariatePolynomial<Coeff>(result.main_var(), Coeff(1));
320 }
321 
322 }
323 
324 template<typename Coeff>
326  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: " << p);
327  FactorMap<Coeff> result;
328  if(is_constant(p)) // Constant.
329  {
330  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: add the factor (" << p << ")^" << 1 );
331  result.emplace(p, 1);
332  return result;
333  }
334  // Make the polynomial's coefficients coprime (integral and with gcd 1).
335  UnivariatePolynomial<Coeff> remainingPoly(p.main_var());
336  Coeff factor = p.coprime_factor();
337  if(factor == 1)
338  {
339  remainingPoly = p;
340  }
341  else
342  {
343  // Store the rational factor and make the polynomial's coefficients coprime.
344  CARL_LOG_TRACE("carl.core", "UnivFactor: add the factor (" << UnivariatePolynomial<Coeff>(p.main_var(), constant_one<Coeff>::get() / factor) << ")^" << 1 );
345  result.emplace(UnivariatePolynomial<Coeff>(p.main_var(), constant_one<Coeff>::get() / factor), 1);
346  std::vector<Coeff> remaining;
347  remaining.reserve(p.coefficients().size());
348  for(const Coeff& coeff : p.coefficients())
349  {
350  remaining.push_back(coeff * factor);
351  }
352  remainingPoly = UnivariatePolynomial<Coeff>(p.main_var(), std::move(remaining));
353  }
354  assert(p.coefficients().size() > 1);
355  // Exclude the factors (x-r)^i with r rational.
356  remainingPoly = detail::exclude_linear_factors(remainingPoly, result, static_cast<carl::sint>(INT_MAX));
357  assert(!is_constant(remainingPoly) || remainingPoly.lcoeff() == (Coeff)1);
358  if(!is_constant(remainingPoly))
359  {
360  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: Calculating square-free factorization of " << remainingPoly);
361  // Calculate the square free factorization.
362  auto sff = carl::squareFreeFactorization(remainingPoly);
363 // factor = (Coeff) 1;
364  for(auto expFactorPair = sff.begin(); expFactorPair != sff.end(); ++expFactorPair)
365  {
366 // Coeff cpf = expFactorPair->second.coprime_factor();
367 // if(cpf != (Coeff) 1)
368 // {
369 // factor *= pow(expFactorPair->second.coprime_factor(), expFactorPair->first);
370 // expFactorPair->second /= cpf;
371 // }
372  if(!is_constant(expFactorPair->second) || !carl::is_one(expFactorPair->second.lcoeff()))
373  {
374  auto retVal = result.emplace(expFactorPair->second, expFactorPair->first);
375  CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: add the factor (" << expFactorPair->second << ")^" << expFactorPair->first );
376  if(!retVal.second)
377  {
378  retVal.first->second += expFactorPair->first;
379  }
380  }
381  }
382 // if(factor != (Coeff) 1)
383 // {
384 // CARL_LOG_TRACE("carl.core.upoly", "UnivFactor: add the factor (" << UnivariatePolynomial<Coeff>(main_var(), {factor}) << ")^" << 1 );
385 // // Add the constant factor to the factors.
386 // if( result.begin()->first.is_constant() )
387 // {
388 // factor *= result.begin()->first.lcoeff();
389 // result.erase( result.begin() );
390 // }
391 // result.insert(result.begin(), std::pair<UnivariatePolynomial<Coeff>, unsigned>(UnivariatePolynomial<Coeff>(main_var(), {factor}), 1));
392 // }
393  }
394  return result;
395 }
396 
397 }
A small wrapper that configures logging for carl.
#define CARL_LOG_TRACE(channel, msg)
Definition: carl-logging.h:44
mpz_class Integer
carl is the main namespace for the library.
UnivariatePolynomial< Coeff > extended_gcd(const UnivariatePolynomial< Coeff > &a, const UnivariatePolynomial< Coeff > &b, UnivariatePolynomial< Coeff > &s, UnivariatePolynomial< Coeff > &t)
Calculates the extended greatest common divisor g of two polynomials.
const T & derivative(const T &t, Variable, std::size_t n=1)
Computes the n'th derivative of a number, which is either the number itself (for n = 0) or zero.
Definition: Derivative.h:23
std::uint64_t uint
Definition: numbers.h:16
Interval< Number > abs(const Interval< Number > &_in)
Method which returns the absolute value of the passed number.
Definition: Interval.h:1511
bool is_constant(const ContextPolynomial< Coeff, Ordering, Policies > &p)
cln::cl_I mod(const cln::cl_I &a, const cln::cl_I &b)
Calculate the remainder of the integer division.
Definition: operations.h:445
std::map< uint, UnivariatePolynomial< Coeff > > squareFreeFactorization(const UnivariatePolynomial< Coeff > &p)
bool is_zero(const Interval< Number > &i)
Check if this interval is a point-interval containing 0.
Definition: Interval.h:1453
cln::cl_I get_num(const cln::cl_RA &n)
Extract the numerator from a fraction.
Definition: operations.h:60
std::map< UnivariatePolynomial< Coefficient >, uint > FactorMap
void divide(const cln::cl_I &dividend, const cln::cl_I &divisor, cln::cl_I &quotient, cln::cl_I &remainder)
Definition: operations.h:326
typename UnderlyingNumberType< P >::type Coeff
bool is_integer(const Interval< Number > &n)
Definition: Interval.h:1445
std::int64_t sint
Definition: numbers.h:17
Factors< MultivariatePolynomial< C, O, P > > factorization(const MultivariatePolynomial< C, O, P > &p, bool includeConstants=true)
Try to factorize a multivariate polynomial.
Definition: Factorization.h:31
bool is_one(const Interval< Number > &i)
Check if this interval is a point-interval containing 1.
Definition: Interval.h:1462
UnivariatePolynomial< Coeff > exclude_linear_factors(const UnivariatePolynomial< Coeff > &poly, FactorMap< Coeff > &linearFactors, const Integer &maxInt)
This class represents a univariate polynomial with coefficients of an arbitrary type.
Coefficient coprime_factor() const
Calculates a factor that would make the coefficients of this polynomial coprime integers.
const std::vector< Coefficient > & coefficients() const &
Retrieves the coefficients defining this polynomial.
Variable main_var() const
Retrieves the main variable of this polynomial.
const Coefficient & lcoeff() const
Returns the leading coefficient.
Coefficient synthetic_division(const Coefficient &zeroOfDivisor)
uint degree() const
Get the maximal exponent of the main variable.
Type trait for the characteristic of the given field (template argument).
Definition: typetraits.h:294
Gives the corresponding integral type.
Definition: typetraits.h:310