X-Git-Url: https://ginac.de/ginac.git//ginac.git?a=blobdiff_plain;f=ginac%2Ffactor.cpp;h=2b77d1b0110f3e327216a11a20510b901a6b93ee;hb=709e61093f43462b5816f859a32a31cc00758da8;hp=fb73897218db06ed31b40f32bf52aabca7e33acf;hpb=2b0ad5c381dc081cc4066b0c5f939f5169ad9ab3;p=ginac.git diff --git a/ginac/factor.cpp b/ginac/factor.cpp index fb738972..2b77d1b0 100644 --- a/ginac/factor.cpp +++ b/ginac/factor.cpp @@ -1,16 +1,39 @@ /** @file factor.cpp * - * Polynomial factorization code (implementation). + * Polynomial factorization (implementation). + * + * The interface function factor() at the end of this file is defined in the + * GiNaC namespace. All other utility functions and classes are defined in an + * additional anonymous namespace. + * + * Factorization starts by doing a square free factorization and making the + * coefficients integer. Then, depending on the number of free variables it + * proceeds either in dedicated univariate or multivariate factorization code. + * + * Univariate factorization does a modular factorization via Berlekamp's + * algorithm and distinct degree factorization. Hensel lifting is used at the + * end. + * + * Multivariate factorization uses the univariate factorization (applying a + * evaluation homomorphism first) and Hensel lifting raises the answer to the + * multivariate domain. The Hensel lifting code is completely distinct from the + * code used by the univariate factorization. * * Algorithms used can be found in - * [W1] An Improved Multivariate Polynomial Factoring Algorithm, - * P.S.Wang, Mathematics of Computation, Vol. 32, No. 144 (1978) 1215--1231. + * [Wan] An Improved Multivariate Polynomial Factoring Algorithm, + * P.S.Wang, + * Mathematics of Computation, Vol. 32, No. 144 (1978) 1215--1231. * [GCL] Algorithms for Computer Algebra, - * K.O.Geddes, S.R.Czapor, G.Labahn, Springer Verlag, 1992. + * K.O.Geddes, S.R.Czapor, G.Labahn, + * Springer Verlag, 1992. + * [Mig] Some Useful Bounds, + * M.Mignotte, + * In "Computer Algebra, Symbolic and Algebraic Computation" (B.Buchberger et al., eds.), + * pp. 259-263, Springer-Verlag, New York, 1982. */ /* - * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany + * GiNaC Copyright (C) 1999-2019 Johannes Gutenberg University Mainz, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,10 +66,10 @@ #include "add.h" #include -#include #include #include #include +#include #ifdef DEBUGFACTOR #include #endif @@ -61,53 +84,57 @@ namespace GiNaC { #define DCOUT(str) cout << #str << endl #define DCOUTVAR(var) cout << #var << ": " << var << endl #define DCOUT2(str,var) cout << #str << ": " << var << endl -#else -#define DCOUT(str) -#define DCOUTVAR(var) -#define DCOUT2(str,var) -#endif - -// anonymous namespace to hide all utility functions -namespace { - -typedef vector mvec; -#ifdef DEBUGFACTOR ostream& operator<<(ostream& o, const vector& v) { - vector::const_iterator i = v.begin(), end = v.end(); + auto i = v.begin(), end = v.end(); while ( i != end ) { - o << *i++ << " "; + o << *i << " "; + ++i; } return o; } -ostream& operator<<(ostream& o, const vector& v) +static ostream& operator<<(ostream& o, const vector& v) { - vector::const_iterator i = v.begin(), end = v.end(); + auto i = v.begin(), end = v.end(); while ( i != end ) { o << *i << "[" << i-v.begin() << "]" << " "; ++i; } return o; } -ostream& operator<<(ostream& o, const vector& v) +static ostream& operator<<(ostream& o, const vector& v) { - vector::const_iterator i = v.begin(), end = v.end(); + auto i = v.begin(), end = v.end(); while ( i != end ) { o << *i << "[" << i-v.begin() << "]" << " "; ++i; } return o; } -ostream& operator<<(ostream& o, const vector< vector >& v) +ostream& operator<<(ostream& o, const vector& v) +{ + for ( size_t i=0; i>& v) { - vector< vector >::const_iterator i = v.begin(), end = v.end(); + auto i = v.begin(), end = v.end(); while ( i != end ) { o << i-v.begin() << ": " << *i << endl; ++i; } return o; } -#endif +#else +#define DCOUT(str) +#define DCOUTVAR(var) +#define DCOUT2(str,var) +#endif // def DEBUGFACTOR + +// anonymous namespace to hide all utility functions +namespace { //////////////////////////////////////////////////////////////////////////////// // modular univariate polynomial code @@ -192,8 +219,32 @@ static void expt_pos(umodpoly& a, unsigned int q) } } +template struct enable_if +{ + typedef T type; +}; + +template struct enable_if { /* empty */ }; + +template struct uvar_poly_p +{ + static const bool value = false; +}; + +template<> struct uvar_poly_p +{ + static const bool value = true; +}; + +template<> struct uvar_poly_p +{ + static const bool value = true; +}; + template -static T operator+(const T& a, const T& b) +// Don't define this for anything but univariate polynomials. +static typename enable_if::value, T>::type +operator+(const T& a, const T& b) { int sa = a.size(); int sb = b.size(); @@ -224,7 +275,11 @@ static T operator+(const T& a, const T& b) } template -static T operator-(const T& a, const T& b) +// Don't define this for anything but univariate polynomials. Otherwise +// overload resolution might fail (this actually happens when compiling +// GiNaC with g++ 3.4). +static typename enable_if::value, T>::type +operator-(const T& a, const T& b) { int sa = a.size(); int sb = b.size(); @@ -444,11 +499,10 @@ static void reduce_coeff(umodpoly& a, const cl_I& x) if ( a.empty() ) return; cl_modint_ring R = a[0].ring(); - umodpoly::iterator i = a.begin(), end = a.end(); - for ( ; i!=end; ++i ) { + for (auto & i : a) { // cln cannot perform this division in the modular field - cl_I c = R->retract(*i); - *i = cl_MI(R, the(c / x)); + cl_I c = R->retract(i); + i = cl_MI(R, the(c / x)); } } @@ -621,9 +675,13 @@ static bool squarefree(const umodpoly& a) //////////////////////////////////////////////////////////////////////////////// // modular matrix +typedef vector mvec; + class modular_matrix { +#ifdef DEBUGFACTOR friend ostream& operator<<(ostream& o, const modular_matrix& m); +#endif public: modular_matrix(size_t r_, size_t c_, const cl_MI& init) : r(r_), c(c_) { @@ -635,90 +693,75 @@ public: cl_MI operator()(size_t row, size_t col) const { return m[row*c + col]; } void mul_col(size_t col, const cl_MI x) { - mvec::iterator i = m.begin() + col; for ( size_t rc=0; rc::iterator i = m.begin() + row*c; for ( size_t cc=0; cc::iterator i1 = m.begin() + row1*c; - vector::iterator i2 = m.begin() + row2*c; for ( size_t cc=0; cc::iterator i1 = m.begin() + row1*c; - vector::iterator i2 = m.begin() + row2*c; for ( size_t cc=0; cc& newrow) { - mvec::iterator i1 = m.begin() + row*c; - mvec::const_iterator i2 = newrow.begin(), end = newrow.end(); - for ( ; i2 != end; ++i1, ++i2 ) { - *i1 = *i2; + for (std::size_t i2 = 0; i2 < newrow.size(); ++i2) { + std::size_t i1 = row*c + i2; + m[i1] = newrow[i2]; } } mvec::const_iterator row_begin(size_t row) const { return m.begin()+row*c; } @@ -770,6 +813,11 @@ ostream& operator<<(ostream& o, const modular_matrix& m) // END modular matrix //////////////////////////////////////////////////////////////////////////////// +/** Calculates the Q matrix for a polynomial. Used by Berlekamp's algorithm. + * + * @param[in] a_ modular polynomial + * @param[out] Q Q matrix + */ static void q_matrix(const umodpoly& a_, modular_matrix& Q) { umodpoly a = a_; @@ -793,6 +841,11 @@ static void q_matrix(const umodpoly& a_, modular_matrix& Q) } } +/** Determine the nullspace of a matrix M-1. + * + * @param[in,out] M matrix, will be modified + * @param[out] basis calculated nullspace of M-1 + */ static void nullspace(modular_matrix& M, vector& basis) { const size_t n = M.rowsize(); @@ -837,11 +890,20 @@ static void nullspace(modular_matrix& M, vector& basis) } } +/** Berlekamp's modular factorization. + * + * The implementation follows the algorithm in chapter 8 of [GCL]. + * + * @param[in] a modular polynomial + * @param[out] upv vector containing modular factors. if upv was not empty the + * new elements are added at the end + */ static void berlekamp(const umodpoly& a, upvec& upv) { cl_modint_ring R = a[0].ring(); umodpoly one(1, R->one()); + // find nullspace of Q matrix modular_matrix Q(degree(a), degree(a), R->zero()); q_matrix(a, Q); vector nu; @@ -849,17 +911,18 @@ static void berlekamp(const umodpoly& a, upvec& upv) const unsigned int k = nu.size(); if ( k == 1 ) { + // irreducible return; } - list factors; - factors.push_back(a); + list factors = {a}; unsigned int size = 1; unsigned int r = 1; unsigned int q = cl_I_to_uint(R->modulus); list::iterator u = factors.begin(); + // calculate all gcd's while ( true ) { for ( unsigned int s=0; s::const_iterator i = factors.begin(), end = factors.end(); - while ( i != end ) { - if ( degree(*i) ) ++size; - ++i; + for (auto & i : factors) { + if (degree(i)) + ++size; } if ( size == k ) { - list::const_iterator i = factors.begin(), end = factors.end(); - while ( i != end ) { - upv.push_back(*i++); + for (auto & i : factors) { + upv.push_back(i); } return; } @@ -899,6 +959,16 @@ static void berlekamp(const umodpoly& a, upvec& upv) } } +// modular square free factorization is not used at the moment so we deactivate +// the code +#if 0 + +/** Calculates a^(1/prime). + * + * @param[in] a polynomial + * @param[in] prime prime number -> exponent 1/prime + * @param[in] ap resulting polynomial + */ static void expt_1_over_p(const umodpoly& a, unsigned int prime, umodpoly& ap) { size_t newdeg = degree(a)/prime; @@ -909,6 +979,12 @@ static void expt_1_over_p(const umodpoly& a, unsigned int prime, umodpoly& ap) } } +/** Modular square free factorization. + * + * @param[in] a polynomial + * @param[out] factors modular factors + * @param[out] mult corresponding multiplicities (exponents) + */ static void modsqrfree(const umodpoly& a, upvec& factors, vector& mult) { const unsigned int prime = cl_I_to_uint(a[0].ring()->modulus); @@ -942,8 +1018,7 @@ static void modsqrfree(const umodpoly& a, upvec& factors, vector& mult) mult[i] *= prime; } } - } - else { + } else { umodpoly ap; expt_1_over_p(a, prime, ap); size_t previ = mult.size(); @@ -954,6 +1029,18 @@ static void modsqrfree(const umodpoly& a, upvec& factors, vector& mult) } } +#endif // deactivation of square free factorization + +/** Distinct degree factorization (DDF). + * + * The implementation follows the algorithm in chapter 8 of [GCL]. + * + * @param[in] a_ modular polynomial + * @param[out] degrees vector containing the degrees of the factors of the + * corresponding polynomials in ddfactors. + * @param[out] ddfactors vector containing polynomials which factors have the + * degree given in degrees. + */ static void distinct_degree_factor(const umodpoly& a_, vector& degrees, upvec& ddfactors) { umodpoly a = a_; @@ -995,6 +1082,16 @@ static void distinct_degree_factor(const umodpoly& a_, vector& degrees, upv } } +/** Modular same degree factorization. + * Same degree factorization is a kind of misnomer. It performs distinct degree + * factorization, but instead of using the Cantor-Zassenhaus algorithm it + * (sub-optimally) uses Berlekamp's algorithm for the factors of the same + * degree. + * + * @param[in] a modular polynomial + * @param[out] upv vector containing modular factors. if upv was not empty the + * new elements are added at the end + */ static void same_degree_factor(const umodpoly& a, upvec& upv) { cl_modint_ring R = a[0].ring(); @@ -1006,15 +1103,25 @@ static void same_degree_factor(const umodpoly& a, upvec& upv) for ( size_t i=0; i maxcoeff ) ? B : maxcoeff; } +/** Calculates the bound for the modulus. + * See [Mig]. + */ static inline cl_I calc_bound(const upoly& a, int maxdeg) { cl_I maxcoeff = 0; @@ -1110,6 +1227,18 @@ static inline cl_I calc_bound(const upoly& a, int maxdeg) return ( B > maxcoeff ) ? B : maxcoeff; } +/** Hensel lifting as used by factor_univariate(). + * + * The implementation follows the algorithm in chapter 6 of [GCL]. + * + * @param[in] a_ primitive univariate polynomials + * @param[in] p prime number that does not divide lcoeff(a) + * @param[in] u1_ modular factor of a (mod p) + * @param[in] w1_ modular factor of a (mod p), relatively prime to u1_, + * fulfilling u1_*w1_ == a mod p + * @param[out] u lifted factor + * @param[out] w lifted factor, u*w = a + */ static void hensel_univar(const upoly& a_, unsigned int p, const umodpoly& u1_, const umodpoly& w1_, upoly& u, upoly& w) { upoly a = a_; @@ -1180,45 +1309,52 @@ static void hensel_univar(const upoly& a_, unsigned int p, const umodpoly& u1_, if ( alpha != 1 ) { w = w / alpha; } - } - else { + } else { u.clear(); } } +/** Returns a new prime number. + * + * @param[in] p prime number + * @return next prime number after p + */ static unsigned int next_prime(unsigned int p) { static vector primes; - if ( primes.size() == 0 ) { - primes.push_back(3); primes.push_back(5); primes.push_back(7); + if (primes.empty()) { + primes = {3, 5, 7}; } - vector::const_iterator it = primes.begin(); if ( p >= primes.back() ) { unsigned int candidate = primes.back() + 2; while ( true ) { size_t n = primes.size()/2; for ( size_t i=0; i p ) break; + if (candidate > p) + break; } return candidate; } - vector::const_iterator end = primes.end(); - for ( ; it!=end; ++it ) { - if ( *it > p ) { - return *it; + for (auto & it : primes) { + if ( it > p ) { + return it; } } throw logic_error("next_prime: should not reach this point!"); } +/** Manages the splitting a vector of of modular factors into two partitions. + */ class factor_partition { public: + /** Takes the vector of modular factors and initializes the first partition */ factor_partition(const upvec& factors_) : factors(factors_) { n = factors.size(); @@ -1234,9 +1370,8 @@ public: size_t size() const { return n; } size_t size_left() const { return n-len; } size_t size_right() const { return len; } -#ifdef DEBUGFACTOR - void get() const { DCOUTVAR(k); } -#endif + /** Initializes the next partition. + Returns true, if there is one, false otherwise. */ bool next() { if ( last == n-1 ) { @@ -1266,15 +1401,16 @@ public: if ( len > n/2 ) return false; fill(k.begin(), k.begin()+len, 1); fill(k.begin()+len+1, k.end(), 0); - } - else { + } else { k[last++] = 0; k[last] = 1; } split(); return true; } + /** Get first partition */ umodpoly& left() { return lr[0]; } + /** Get second partition */ umodpoly& right() { return lr[1]; } private: void split_cached() @@ -1288,8 +1424,7 @@ private: if ( d ) { if ( cache[pos].size() >= d ) { lr[group] = lr[group] * cache[pos][d-1]; - } - else { + } else { if ( cache[pos].size() == 0 ) { cache[pos].push_back(factors[pos] * factors[pos+1]); } @@ -1303,8 +1438,7 @@ private: } lr[group] = lr[group] * cache[pos].back(); } - } - else { + } else { lr[group] = lr[group] * factors[pos]; } } while ( i < n ); @@ -1315,8 +1449,7 @@ private: lr[1] = one; if ( n > 6 ) { split_cached(); - } - else { + } else { for ( size_t i=0; i > cache; + vector> cache; upvec factors; umodpoly one; size_t n; @@ -1333,12 +1466,25 @@ private: vector k; }; +/** Contains a pair of univariate polynomial and its modular factors. + * Used by factor_univariate(). + */ struct ModFactors { upoly poly; upvec factors; }; +/** Univariate polynomial factorization. + * + * Modular factorization is tried for several primes to minimize the number of + * modular factors. Then, Hensel lifting is performed. + * + * @param[in] poly expanded square free univariate polynomial + * @param[in] x symbol + * @param[in,out] prime prime number to start trying modular factorization with, + * output value is the prime number actually used + */ static ex factor_univariate(const ex& poly, const ex& x, unsigned int& prime) { ex unit, cont, prim_ex; @@ -1352,7 +1498,17 @@ static ex factor_univariate(const ex& poly, const ex& x, unsigned int& prime) cl_modint_ring R; unsigned int trials = 0; unsigned int minfactors = 0; - cl_I lc = lcoeff(prim) * the(ex_to(cont).to_cl_N()); + + const numeric& cont_n = ex_to(cont); + cl_I i_cont; + if (cont_n.is_integer()) { + i_cont = the(cont_n.to_cl_N()); + } else { + // poly \in Q[x] => poly = q ipoly, ipoly \in Z[x], q \in Q + // factor(poly) \equiv q factor(ipoly) + i_cont = cl_I(1); + } + cl_I lc = lcoeff(prim)*i_cont; upvec factors; while ( trials < 2 ) { umodpoly modpoly; @@ -1378,8 +1534,7 @@ static ex factor_univariate(const ex& poly, const ex& x, unsigned int& prime) minfactors = trialfactors.size(); lastp = prime; trials = 1; - } - else { + } else { ++trials; } } @@ -1398,8 +1553,10 @@ static ex factor_univariate(const ex& poly, const ex& x, unsigned int& prime) const size_t n = tocheck.top().factors.size(); factor_partition part(tocheck.top().factors); while ( true ) { + // call Hensel lifting hensel_univar(tocheck.top().poly, prime, part.left(), part.right(), f1, f2); if ( !f1.empty() ) { + // successful, update the stack and the result if ( part.size_left() == 1 ) { if ( part.size_right() == 1 ) { result *= upoly_to_ex(f1, x) * upoly_to_ex(f2, x); @@ -1431,15 +1588,13 @@ static ex factor_univariate(const ex& poly, const ex& x, unsigned int& prime) } } break; - } - else { + } else { upvec newfactors1(part.size_left()), newfactors2(part.size_right()); - upvec::iterator i1 = newfactors1.begin(), i2 = newfactors2.begin(); + auto i1 = newfactors1.begin(), i2 = newfactors2.begin(); for ( size_t i=0; i==). + */ struct EvalPoint { ex x; int evalpoint; }; +#ifdef DEBUGFACTOR +ostream& operator<<(ostream& o, const vector& v) +{ + for ( size_t i=0; i multivar_diophant(const vector& a_, const ex& x, const ex& c, const vector& I, unsigned int d, unsigned int p, unsigned int k); +/** Utility function for multivariate Hensel lifting. + * + * Solves the equation + * s_1*b_1 + ... + s_r*b_r == 1 mod p^k + * with deg(s_i) < deg(a_i) + * and with given b_1 = a_1 * ... * a_{i-1} * a_{i+1} * ... * a_r + * + * The implementation follows the algorithm in chapter 6 of [GCL]. + * + * @param[in] a vector of modular univariate polynomials + * @param[in] x symbol + * @param[in] p prime number + * @param[in] k p^k is modulus + * @return vector of polynomials (s_i) + */ static upvec multiterm_eea_lift(const upvec& a, const ex& x, unsigned int p, unsigned int k) { const size_t r = a.size(); @@ -1508,20 +1695,35 @@ static upvec multiterm_eea_lift(const upvec& a, const ex& x, unsigned int p, uns return s; } -/** - * Assert: a not empty. +/** Changes the modulus of a modular polynomial. Used by eea_lift(). + * + * @param[in] R new modular ring + * @param[in,out] a polynomial to change (in situ) */ static void change_modulus(const cl_modint_ring& R, umodpoly& a) { if ( a.empty() ) return; cl_modint_ring oldR = a[0].ring(); - umodpoly::iterator i = a.begin(), end = a.end(); - for ( ; i!=end; ++i ) { - *i = R->canonhom(oldR->retract(*i)); + for (auto & i : a) { + i = R->canonhom(oldR->retract(i)); } canonicalize(a); } +/** Utility function for multivariate Hensel lifting. + * + * Solves s*a + t*b == 1 mod p^k given a,b. + * + * The implementation follows the algorithm in chapter 6 of [GCL]. + * + * @param[in] a polynomial + * @param[in] b polynomial + * @param[in] x symbol + * @param[in] p prime number + * @param[in] k p^k is modulus + * @param[out] s_ output polynomial + * @param[out] t_ output polynomial + */ static void eea_lift(const umodpoly& a, const umodpoly& b, const ex& x, unsigned int p, unsigned int k, umodpoly& s_, umodpoly& t_) { cl_modint_ring R = find_modint_ring(p); @@ -1565,6 +1767,21 @@ static void eea_lift(const umodpoly& a, const umodpoly& b, const ex& x, unsigned s_ = s; t_ = t; } +/** Utility function for multivariate Hensel lifting. + * + * Solves the equation + * s_1*b_1 + ... + s_r*b_r == x^m mod p^k + * with given b_1 = a_1 * ... * a_{i-1} * a_{i+1} * ... * a_r + * + * The implementation follows the algorithm in chapter 6 of [GCL]. + * + * @param a vector with univariate polynomials mod p^k + * @param x symbol + * @param m exponent of x^m in the equation to solve + * @param p prime number + * @param k p^k is modulus + * @return vector of polynomials (s_i) + */ static upvec univar_diophant(const upvec& a, const ex& x, unsigned int m, unsigned int p, unsigned int k) { cl_modint_ring R = find_modint_ring(expt_pos(cl_I(p),k)); @@ -1579,8 +1796,7 @@ static upvec univar_diophant(const upvec& a, const ex& x, unsigned int m, unsign rem(bmod, a[j], buf); result.push_back(buf); } - } - else { + } else { umodpoly s, t; eea_lift(a[1], a[0], x, p, k, s, t); umodpoly bmod = umodpoly_to_umodpoly(s, R, m); @@ -1595,10 +1811,14 @@ static upvec univar_diophant(const upvec& a, const ex& x, unsigned int m, unsign return result; } +/** Map used by function make_modular(). + * Finds every coefficient in a polynomial and replaces it by is value in the + * given modular ring R (symmetric representation). + */ struct make_modular_map : public map_function { cl_modint_ring R; make_modular_map(const cl_modint_ring& R_) : R(R_) { } - ex operator()(const ex& e) + ex operator()(const ex& e) override { if ( is_a(e) || is_a(e) ) { return e.map(*this); @@ -1610,8 +1830,7 @@ struct make_modular_map : public map_function { numeric n(R->retract(emod)); if ( n > halfmod ) { return n-mod; - } - else { + } else { return n; } } @@ -1619,18 +1838,43 @@ struct make_modular_map : public map_function { } }; +/** Helps mimicking modular multivariate polynomial arithmetic. + * + * @param e expression of which to make the coefficients equal to their value + * in the modular ring R (symmetric representation) + * @param R modular ring + * @return resulting expression + */ static ex make_modular(const ex& e, const cl_modint_ring& R) { make_modular_map map(R); return map(e.expand()); } +/** Utility function for multivariate Hensel lifting. + * + * Returns the polynomials s_i that fulfill + * s_1*b_1 + ... + s_r*b_r == c mod + * with given b_1 = a_1 * ... * a_{i-1} * a_{i+1} * ... * a_r + * + * The implementation follows the algorithm in chapter 6 of [GCL]. + * + * @param a_ vector of multivariate factors mod p^k + * @param x symbol (equiv. x_1 in [GCL]) + * @param c polynomial mod p^k + * @param I vector of evaluation points + * @param d maximum total degree of result + * @param p prime number + * @param k p^k is modulus + * @return vector of polynomials (s_i) + */ static vector multivar_diophant(const vector& a_, const ex& x, const ex& c, const vector& I, unsigned int d, unsigned int p, unsigned int k) { vector a = a_; - const cl_modint_ring R = find_modint_ring(expt_pos(cl_I(p),k)); + const cl_I modulus = expt_pos(cl_I(p),k); + const cl_modint_ring R = find_modint_ring(modulus); const size_t r = a.size(); const size_t nu = I.size() + 1; @@ -1680,8 +1924,7 @@ static vector multivar_diophant(const vector& a_, const ex& x, const ex& e = make_modular(buf, R); } } - } - else { + } else { upvec amod; for ( size_t i=0; i multivar_diophant(const vector& a_, const ex& x, const ex& if ( is_a(c) ) { nterms = c.nops(); z = c.op(0); - } - else { + } else { nterms = 1; z = c; } @@ -1705,16 +1947,13 @@ static vector multivar_diophant(const vector& a_, const ex& x, const ex& cl_I cm = the(ex_to(z.lcoeff(x)).to_cl_N()); upvec delta_s = univar_diophant(amod, x, m, p, k); cl_MI modcm; - cl_I poscm = cm; - while ( poscm < 0 ) { - poscm = poscm + expt_pos(cl_I(p),k); - } + cl_I poscm = plusp(cm) ? cm : mod(cm, modulus); modcm = cl_MI(R, poscm); for ( size_t j=0; j 1 ) { + if ( nterms > 1 && i+1 != nterms ) { z = c.op(i+1); } } @@ -1727,17 +1966,24 @@ static vector multivar_diophant(const vector& a_, const ex& x, const ex& return sigma; } -#ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const vector& v) -{ - for ( size_t i=0; i& I, unsigned int p, const cl_I& l, const upvec& u, const vector& lcU) +/** Multivariate Hensel lifting. + * The implementation follows the algorithm in chapter 6 of [GCL]. + * Since we don't have a data type for modular multivariate polynomials, the + * respective operations are done in a GiNaC::ex and the function + * make_modular() is then called to make the coefficient modular p^l. + * + * @param a multivariate polynomial primitive in x + * @param x symbol (equiv. x_1 in [GCL]) + * @param I vector of evaluation points (x_2==a_2,x_3==a_3,...) + * @param p prime number (should not divide lcoeff(a mod I)) + * @param l p^l is the modulus of the lifted univariate field + * @param u vector of modular (mod p^l) factors of a mod I + * @param lcU correct leading coefficient of the univariate factors of a mod I + * @return list GiNaC::lst with lifted factors (multivariate factors of a), + * empty if Hensel lifting did not succeed + */ +static ex hensel_multivar(const ex& a, const ex& x, const vector& I, + unsigned int p, const cl_I& l, const upvec& u, const vector& lcU) { const size_t nu = I.size() + 1; const cl_modint_ring R = find_modint_ring(expt_pos(cl_I(p),l)); @@ -1821,22 +2067,19 @@ static ex hensel_multivar(const ex& a, const ex& x, const vector& I, acand *= U[i]; } if ( expand(a-acand).is_zero() ) { - lst res; - for ( size_t i=0; i {7,x,y+1}. The first + * element of the list is always the numeric coefficient. + */ static ex put_factors_into_lst(const ex& e) { lst result; - if ( is_a(e) ) { result.append(e); return result; @@ -1847,8 +2090,9 @@ static ex put_factors_into_lst(const ex& e) return result; } if ( is_a(e) || is_a(e) ) { - result.append(1); - result.append(e); + ex icont(e.integer_content()); + result.append(icont); + result.append(e/icont); return result; } if ( is_a(e) ) { @@ -1871,20 +2115,11 @@ static ex put_factors_into_lst(const ex& e) throw runtime_error("put_factors_into_lst: bad term."); } -#ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const vector& v) -{ - for ( size_t i=0; i(x))); if ( !is_a(g) ) { continue; } if ( !is_a(vn) ) { - /* ... and for which the evaluated factors have each an unique prime factor */ + // ... and for which the evaluated factors have each an unique prime factor lst fnum = f; fnum.let_op(0) = fnum.op(0) * u0.content(x); for ( size_t i=1; i=p.ldegree(x); --i ) { - cont = gcd(cont, p.coeff(x,i)); - if ( cont == 1 ) break; - } - ex pp = expand(normal(p / cont)); + // make polynomial primitive + ex unit, cont, pp; + poly.unitcontprim(x, unit, cont, pp); if ( !is_a(cont) ) { return factor_sqrfree(cont) * factor_sqrfree(pp); } - /* factor leading coefficient */ + // factor leading coefficient ex vn = pp.collect(x).lcoeff(x); ex vnlst; if ( is_a(vn) ) { - vnlst = lst(vn); + vnlst = lst{vn}; } else { ex vnfactors = factor(vn); @@ -2012,7 +2254,7 @@ static ex factor_multivariate(const ex& poly, const exset& syms) numeric modulus = (vnlst.nops() > 3) ? vnlst.nops() : 3; vector a(syms.size()-1, 0); - /* try now to factorize until we are successful */ + // try now to factorize until we are successful while ( true ) { unsigned int trialcount = 0; @@ -2022,10 +2264,10 @@ static ex factor_multivariate(const ex& poly, const exset& syms) ex u, delta; ex ufac, ufaclst; - /* try several evaluation points to reduce the number of modular factors */ + // try several evaluation points to reduce the number of factors while ( trialcount < maxtrials ) { - /* generate a set of valid evaluation points */ + // generate a set of valid evaluation points generate_set(pp, vn, syms, ex_to(vnlst), modulus, u, a); ufac = factor_univariate(u, x, prime); @@ -2034,19 +2276,19 @@ static ex factor_multivariate(const ex& poly, const exset& syms) delta = ufaclst.op(0); if ( factor_count <= 1 ) { - /* irreducible */ + // irreducible return poly; } if ( min_factor_count < 0 ) { - /* first time here */ + // first time here min_factor_count = factor_count; } else if ( min_factor_count == factor_count ) { - /* one less to try */ + // one less to try ++trialcount; } else if ( min_factor_count > factor_count ) { - /* new minimum, reset trial counter */ + // new minimum, reset trial counter min_factor_count = factor_count; trialcount = 0; } @@ -2055,11 +2297,15 @@ static ex factor_multivariate(const ex& poly, const exset& syms) // determine true leading coefficients for the Hensel lifting vector C(factor_count); if ( is_a(vn) ) { + // easy case for ( size_t i=1; i ftilde(vnlst.nops()-1); for ( size_t i=0; i(ft); } - + // calculate D and C vector used_flag(ftilde.size(), false); vector D(factor_count, 1); if ( delta == 1 ) { @@ -2090,8 +2336,7 @@ static ex factor_multivariate(const ex& poly, const exset& syms) } C[i] = D[i] * prefac; } - } - else { + } else { for ( int i=0; i(ufaclst.op(i+1).lcoeff(x)); for ( int j=ftilde.size()-1; j>=0; --j ) { @@ -2115,7 +2360,7 @@ static ex factor_multivariate(const ex& poly, const exset& syms) C[i] = D[i] * prefac; } } - + // check if something went wrong bool some_factor_unused = false; for ( size_t i=0; i epv; s = syms.begin(); @@ -2157,15 +2405,18 @@ static ex factor_multivariate(const ex& poly, const exset& syms) l = l + 1; pl = pl * prime; } + + // set up modular factors (mod p^l) cl_modint_ring R = find_modint_ring(expt_pos(cl_I(prime),l)); upvec modfactors(ufaclst.nops()-1); for ( size_t i=1; i(e) ) { syms.insert(e); @@ -2187,6 +2440,9 @@ struct find_symbols_map : public map_function { } }; +/** Factorizes a polynomial that is square free. It calls either the univariate + * or the multivariate factorization functions. + */ static ex factor_sqrfree(const ex& poly) { // determine all symbols in poly @@ -2200,11 +2456,11 @@ static ex factor_sqrfree(const ex& poly) // univariate case const ex& x = *(findsymbols.syms.begin()); if ( poly.ldegree(x) > 0 ) { + // pull out direct factors int ld = poly.ldegree(x); ex res = factor_univariate(expand(poly/pow(x, ld)), x); return res * pow(x,ld); - } - else { + } else { ex res = factor_univariate(poly, x); return res; } @@ -2215,10 +2471,13 @@ static ex factor_sqrfree(const ex& poly) return res; } +/** Map used by factor() when factor_options::all is given to access all + * subexpressions and to call factor() on them. + */ struct apply_factor_map : public map_function { unsigned options; apply_factor_map(unsigned options_) : options(options_) { } - ex operator()(const ex& e) + ex operator()(const ex& e) override { if ( e.info(info_flags::polynomial) ) { return factor(e, options); @@ -2228,22 +2487,51 @@ struct apply_factor_map : public map_function { for ( size_t i=0; i void +factor_iter(const ex &e, F yield) +{ + if (is_a(e)) { + for (const auto &f : e) { + if (is_a(f)) { + yield(f.op(0), f.op(1)); + } else { + yield(f, ex(1)); + } + } + } else { + if (is_a(e)) { + yield(e.op(0), e.op(1)); + } else { + yield(e, ex(1)); + } + } +} -ex factor(const ex& poly, unsigned options) +/** This function factorizes a polynomial. It checks the arguments, + * tries a square free factorization, and then calls factor_sqrfree + * to do the hard work. + * + * This function expands its argument, so for polynomials with + * explicit factors it's better to call it on each one separately + * (or use factor() which does just that). + */ +static ex factor1(const ex& poly, unsigned options) { // check arguments if ( !poly.info(info_flags::polynomial) ) { @@ -2262,56 +2550,43 @@ ex factor(const ex& poly, unsigned options) return poly; } lst syms; - exset::const_iterator i=findsymbols.syms.begin(), end=findsymbols.syms.end(); - for ( ; i!=end; ++i ) { - syms.append(*i); + for (auto & i : findsymbols.syms ) { + syms.append(i); } // make poly square free ex sfpoly = sqrfree(poly.expand(), syms); // factorize the square free components - if ( is_a(sfpoly) ) { - // case: (polynomial)^exponent - const ex& base = sfpoly.op(0); - if ( !is_a(base) ) { - // simple case: (monomial)^exponent - return sfpoly; - } - ex f = factor_sqrfree(base); - return pow(f, sfpoly.op(1)); - } - if ( is_a(sfpoly) ) { - // case: multiple factors - ex res = 1; - for ( size_t i=0; i(t) ) { - const ex& base = t.op(0); - if ( !is_a(base) ) { - res *= t; - } - else { - ex f = factor_sqrfree(base); - res *= pow(f, t.op(1)); - } - } - else if ( is_a(t) ) { - ex f = factor_sqrfree(t); - res *= f; - } - else { - res *= t; + ex res = 1; + factor_iter(sfpoly, + [&](const ex &f, const ex &k) { + if ( is_a(f) ) { + res *= pow(factor_sqrfree(f), k); + } else { + // simple case: (monomial)^exponent + res *= pow(f, k); } - } - return res; - } - if ( is_a(sfpoly) ) { - return poly; - } - // case: (polynomial) - ex f = factor_sqrfree(sfpoly); - return f; + }); + return res; +} + +} // anonymous namespace + +/** Interface function to the outside world. It uses factor1() + * on each of the explicitly present factors of poly. + */ +ex factor(const ex& poly, unsigned options) +{ + ex result = 1; + factor_iter(poly, + [&](const ex &f1, const ex &k1) { + factor_iter(factor1(f1, options), + [&](const ex &f2, const ex &k2) { + result *= pow(f2, k1*k2); + }); + }); + return result; } } // namespace GiNaC