/** @file factor.cpp * * Polynomial factorization code (implementation). * * 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. * [GCL] Algorithms for Computer Algebra, * K.O.Geddes, S.R.Czapor, G.Labahn, Springer Verlag, 1992. */ /* * GiNaC Copyright (C) 1999-2008 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define DEBUGFACTOR #ifdef DEBUGFACTOR #include #include using namespace GiNaC; #else #include "factor.h" #include "ex.h" #include "numeric.h" #include "operators.h" #include "inifcns.h" #include "symbol.h" #include "relational.h" #include "power.h" #include "mul.h" #include "normal.h" #include "add.h" #endif #include #include #include #include #include using namespace std; #include using namespace cln; #ifdef DEBUGFACTOR namespace Factor { #else namespace GiNaC { #endif #ifdef DEBUGFACTOR #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 // forward declaration ex factor(const ex& poly, unsigned options); // anonymous namespace to hide all utility functions namespace { typedef vector mvec; #ifdef DEBUGFACTOR ostream& operator<<(ostream& o, const mvec& v) { mvec::const_iterator i = v.begin(), end = v.end(); while ( i != end ) { o << *i++ << " "; } return o; } #endif // def DEBUGFACTOR #ifdef DEBUGFACTOR ostream& operator<<(ostream& o, const vector& v) { vector::const_iterator i = v.begin(), end = v.end(); while ( i != end ) { o << *i++ << endl; } return o; } #endif // def DEBUGFACTOR //////////////////////////////////////////////////////////////////////////////// // modular univariate polynomial code //typedef cl_UP_MI umod; typedef std::vector umodpoly; //typedef vector umodvec; typedef vector upvec; // COPY FROM UPOLY.HPP // CHANGED size_t -> int !!! template static int degree(const T& p) { return p.size() - 1; } template static typename T::value_type lcoeff(const T& p) { return p[p.size() - 1]; } static bool normalize_in_field(umodpoly& a) { if (a.size() == 0) return true; if ( lcoeff(a) == a[0].ring()->one() ) { return true; } const cln::cl_MI lc_1 = recip(lcoeff(a)); for (std::size_t k = a.size(); k-- != 0; ) a[k] = a[k]*lc_1; return false; } template static void canonicalize(T& p, const typename T::size_type hint = std::numeric_limits::max()) { if (p.empty()) return; std::size_t i = p.size() - 1; // Be fast if the polynomial is already canonicalized if (!zerop(p[i])) return; if (hint < p.size()) i = hint; bool is_zero = false; do { if (!zerop(p[i])) { ++i; break; } if (i == 0) { is_zero = true; break; } --i; } while (true); if (is_zero) { p.clear(); return; } p.erase(p.begin() + i, p.end()); } // END COPY FROM UPOLY.HPP static void expt_pos(const umodpoly& a, unsigned int q, umodpoly& b) { throw runtime_error("expt_pos: not implemented!"); // code below is not correct! // b.clear(); // if ( a.empty() ) return; // b.resize(degree(a)*q+1, a[0].ring()->zero()); // cl_MI norm = recip(a[0]); // umodpoly an = a; // for ( size_t i=0; ione(); // for ( size_t i=1; i= sb ) { umodpoly r(sa); int i = 0; for ( ; i= sb ) { umodpoly r(sa); int i = 0; for ( ; izero()); for ( int i=0 ; i<=n; ++i ) { for ( int j=0 ; j<=i; ++j ) { if ( j > degree(a) || (i-j) > degree(b) ) continue; // TODO optimize! c[i] = c[i] + a[j] * b[i-j]; } } canonicalize(c); return c; } static umodpoly operator*(const umodpoly& a, const cl_MI& x) { umodpoly r(a.size()); for ( size_t i=0; i=ldeg; --deg ) { cl_I coeff = the(ex_to(e.coeff(x, deg)).to_cl_N()); ump[deg] = R->canonhom(coeff); } for ( ; deg>=0; --deg ) { ump[deg] = R->zero(); } canonicalize(ump); } static void umodpoly_from_ex(umodpoly& ump, const ex& e, const ex& x, const cl_I& modulus) { umodpoly_from_ex(ump, e, x, find_modint_ring(modulus)); } static ex umod_to_ex(const umodpoly& a, const ex& x) { if ( a.empty() ) return 0; cl_modint_ring R = a[0].ring(); cl_I mod = R->modulus; cl_I halfmod = (mod-1) >> 1; ex e; for ( int i=degree(a); i>=0; --i ) { cl_I n = R->retract(a[i]); if ( n > halfmod ) { e += numeric(n-mod) * pow(x, i); } else { e += numeric(n) * pow(x, i); } } return e; } /** Divides all coefficients of the polynomial a by the integer x. * All coefficients are supposed to be divisible by x. If they are not, the * the cast will raise an exception. * * @param[in,out] a polynomial of which the coefficients will be reduced by x * @param[in] x integer that divides the coefficients */ 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 ) { // cln cannot perform this division in the modular field cl_I c = R->retract(*i); *i = cl_MI(R, the(c / x)); } } /** Calculates remainder of a/b. * Assertion: a and b not empty. * * @param[in] a polynomial dividend * @param[in] b polynomial divisor * @param[out] r polynomial remainder */ static void rem(const umodpoly& a, const umodpoly& b, umodpoly& r) { int k, n; n = degree(b); k = degree(a) - n; r = a; if ( k < 0 ) return; do { cl_MI qk = div(r[n+k], b[n]); if ( !zerop(qk) ) { for ( int i=0; izero()); canonicalize(r); } /** Calculates quotient of a/b. * Assertion: a and b not empty. * * @param[in] a polynomial dividend * @param[in] b polynomial divisor * @param[out] q polynomial quotient */ static void div(const umodpoly& a, const umodpoly& b, umodpoly& q) { int k, n; n = degree(b); k = degree(a) - n; q.clear(); if ( k < 0 ) return; umodpoly r = a; q.resize(k+1, a[0].ring()->zero()); do { cl_MI qk = div(r[n+k], b[n]); if ( !zerop(qk) ) { q[k] = qk; for ( int i=0; izero()); do { cl_MI qk = div(r[n+k], b[n]); if ( !zerop(qk) ) { q[k] = qk; for ( int i=0; izero()); canonicalize(r); canonicalize(q); } /** Calculates the GCD of polynomial a and b. * * @param[in] a polynomial * @param[in] b polynomial * @param[out] c GCD */ static void gcd(const umodpoly& a, const umodpoly& b, umodpoly& c) { if ( degree(a) < degree(b) ) return gcd(b, a, c); c = a; normalize_in_field(c); umodpoly d = b; normalize_in_field(d); umodpoly r; while ( !d.empty() ) { rem(c, d, r); c = d; d = r; } normalize_in_field(c); } /** Calculates the derivative of the polynomial a. * * @param[in] a polynomial of which to take the derivative * @param[out] d result/derivative */ static void deriv(const umodpoly& a, umodpoly& d) { d.clear(); if ( a.size() <= 1 ) return; d.insert(d.begin(), a.begin()+1, a.end()); int max = d.size(); for ( int i=1; ione() ); } static bool equal_one(const umodpoly& a) { return ( a.size() == 1 && a[0] == a[0].ring()->one() ); } /** Returns true if polynomial a is square free. * * @param[in] a polynomial to check * @return true if polynomial is square free, false otherwise */ static bool squarefree(const umodpoly& a) { umodpoly b; deriv(a, b); if ( b.empty() ) { return true; } umodpoly c; gcd(a, b, c); return equal_one(c); } // END modular univariate polynomial code //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // modular matrix class modular_matrix { friend ostream& operator<<(ostream& o, const modular_matrix& m); public: modular_matrix(size_t r_, size_t c_, const cl_MI& init) : r(r_), c(c_) { m.resize(c*r, init); } size_t rowsize() const { return r; } size_t colsize() const { return c; } cl_MI& operator()(size_t row, size_t col) { return m[row*c + col]; } 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; } } mvec::const_iterator row_begin(size_t row) const { return m.begin()+row*c; } mvec::const_iterator row_end(size_t row) const { return m.begin()+row*c+r; } private: size_t r, c; mvec m; }; #ifdef DEBUGFACTOR modular_matrix operator*(const modular_matrix& m1, const modular_matrix& m2) { const unsigned int r = m1.rowsize(); const unsigned int c = m2.colsize(); modular_matrix o(r,c,m1(0,0)); for ( size_t i=0; i::const_iterator i = m.m.begin(), end = m.m.end(); size_t wrap = 1; for ( ; i != end; ++i ) { o << *i << " "; if ( !(wrap++ % m.c) ) { o << endl; } } o << endl; return o; } #endif // def DEBUGFACTOR // END modular matrix //////////////////////////////////////////////////////////////////////////////// static void q_matrix(const umodpoly& a, modular_matrix& Q) { int n = degree(a); unsigned int q = cl_I_to_uint(a[0].ring()->modulus); // fast and buggy // vector r(n, a.R->zero()); // r[0] = a.R->one(); // Q.set_row(0, r); // unsigned int max = (n-1) * q; // for ( size_t m=1; m<=max; ++m ) { // cl_MI rn_1 = r.back(); // for ( size_t i=n-1; i>0; --i ) { // r[i] = r[i-1] - rn_1 * a[i]; // } // r[0] = -rn_1 * a[0]; // if ( (m % q) == 0 ) { // Q.set_row(m/q, r); // } // } // slow and (hopefully) correct cl_MI one = a[0].ring()->one(); cl_MI zero = a[0].ring()->zero(); for ( int i=0; i& basis) { const size_t n = M.rowsize(); const cl_MI one = M(0,0).ring()->one(); for ( size_t i=0; i r ) { M.switch_col(cc, r); } break; } } if ( cc < n ) { M.mul_col(r, recip(M(r,r))); for ( cc=0; ccone()); modular_matrix Q(degree(a), degree(a), R->zero()); q_matrix(a, Q); vector nu; nullspace(Q, nu); const unsigned int k = nu.size(); if ( k == 1 ) { return; } list factors; factors.push_back(a); unsigned int size = 1; unsigned int r = 1; unsigned int q = cl_I_to_uint(R->modulus); list::iterator u = factors.begin(); while ( true ) { for ( unsigned int s=0; s::const_iterator i = factors.begin(), end = factors.end(); while ( i != end ) { if ( degree(*i) ) ++size; ++i; } if ( size == k ) { list::const_iterator i = factors.begin(), end = factors.end(); while ( i != end ) { upv.push_back(*i++); } return; } } } if ( ++r == k ) { r = 1; ++u; } } } static void rem_xq(int q, const umodpoly& b, umodpoly& c) { cl_modint_ring R = b[0].ring(); int n = degree(b); if ( n > q ) { c.resize(q+1, R->zero()); c[q] = R->one(); return; } c.clear(); c.resize(n+1, R->zero()); int k = q-n; c[n] = R->one(); int ofs = 0; do { cl_MI qk = div(c[n-ofs], b[n]); if ( !zerop(qk) ) { for ( int i=1; i<=n; ++i ) { c[n-i+ofs] = c[n-i] - qk * b[n-i]; } ofs = ofs ? 0 : 1; } } while ( k-- ); if ( ofs ) { c.pop_back(); } else { c.erase(c.begin()); } canonicalize(c); } static void distinct_degree_factor(const umodpoly& a_, upvec& result) { umodpoly a = a_; cl_modint_ring R = a[0].ring(); int q = cl_I_to_int(R->modulus); int n = degree(a); size_t nhalf = n/2; size_t i = 1; umodpoly w(1, R->one()); umodpoly x = w; upvec ai; while ( i <= nhalf ) { expt_pos(w, q, w); rem(w, a, w); umodpoly buf; gcd(a, w-x, buf); ai.push_back(buf); if ( unequal_one(ai.back()) ) { div(a, ai.back(), a); rem(w, a, w); } ++i; } result = ai; } static void same_degree_factor(const umodpoly& a, upvec& result) { cl_modint_ring R = a[0].ring(); int deg = degree(a); upvec buf; distinct_degree_factor(a, buf); int degsum = 0; for ( size_t i=0; imodulus); int n = degree(a); cl_N pm = 0.3; int l = cl_I_to_int(ceiling1(the(expt(n, pm)))); upvec h(l+1); umodpoly qk(1, R->one()); h[0] = qk; for ( int i=1; i<=l; ++i ) { expt_pos(h[i-1], q, qk); rem(qk, a, h[i]); } int m = std::ceil(((double)n)/2/l); upvec H(m); int ql = std::pow(q, l); H[0] = h[l]; for ( int i=1; ione()); for ( int i=0; i=0; --j ) { umodpoly g; gcd(f, H[i]-h[j], g); result[l*(i+1)-j-1] = g; div(f, g, f); } } } static void cantor_zassenhaus(const umodpoly& a, upvec& result) { } static void factor_modular(const umodpoly& p, upvec& upv) { //same_degree_factor(p, upv); berlekamp(p, upv); return; } static void exteuclid(const umodpoly& a, const umodpoly& b, umodpoly& g, umodpoly& s, umodpoly& t) { if ( degree(a) < degree(b) ) { exteuclid(b, a, g, t, s); return; } umodpoly one(1, a[0].ring()->one()); umodpoly c = a; normalize_in_field(c); umodpoly d = b; normalize_in_field(d); umodpoly c1 = one; umodpoly c2; umodpoly d1; umodpoly d2 = one; while ( !d.empty() ) { umodpoly q; div(c, d, q); umodpoly r = c - q * d; umodpoly r1 = c1 - q * d1; umodpoly r2 = c2 - q * d2; c = d; c1 = d1; c2 = d2; d = r; d1 = r1; d2 = r2; } g = c; normalize_in_field(g); s = c1; for ( int i=0; i<=degree(s); ++i ) { s[i] = s[i] * recip(a[degree(a)] * c[degree(c)]); } canonicalize(s); s = s * g; t = c2; for ( int i=0; i<=degree(t); ++i ) { t[i] = t[i] * recip(b[degree(b)] * c[degree(c)]); } canonicalize(t); t = t * g; } static ex replace_lc(const ex& poly, const ex& x, const ex& lc) { ex r = expand(poly + (lc - poly.lcoeff(x)) * pow(x, poly.degree(x))); return r; } static ex hensel_univar(const ex& a_, const ex& x, unsigned int p, const umodpoly& u1_, const umodpoly& w1_, const ex& gamma_ = 0) { ex a = a_; const cl_modint_ring& R = u1_[0].ring(); // calc bound B ex maxcoeff; for ( int i=a.degree(x); i>=a.ldegree(x); --i ) { maxcoeff += pow(abs(a.coeff(x, i)),2); } cl_I normmc = ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); cl_I maxdegree = (degree(u1_) > degree(w1_)) ? degree(u1_) : degree(w1_); cl_I B = normmc * expt_pos(cl_I(2), maxdegree); // step 1 ex alpha = a.lcoeff(x); ex gamma = gamma_; if ( gamma == 0 ) { gamma = alpha; } numeric gamma_ui = ex_to(abs(gamma)); a = a * gamma; umodpoly nu1 = u1_; normalize_in_field(nu1); umodpoly nw1 = w1_; normalize_in_field(nw1); ex phi; phi = gamma * umod_to_ex(nu1, x); umodpoly u1; umodpoly_from_ex(u1, phi, x, R); phi = alpha * umod_to_ex(nw1, x); umodpoly w1; umodpoly_from_ex(w1, phi, x, R); // step 2 umodpoly g; umodpoly s; umodpoly t; exteuclid(u1, w1, g, s, t); if ( unequal_one(g) ) { throw logic_error("gcd(u1,w1) != 1"); } // step 3 ex u = replace_lc(umod_to_ex(u1, x), x, gamma); ex w = replace_lc(umod_to_ex(w1, x), x, alpha); ex e = expand(a - u * w); numeric modulus = p; const numeric maxmodulus = 2*numeric(B)*gamma_ui; // step 4 while ( !e.is_zero() && modulus < maxmodulus ) { ex c = e / modulus; phi = expand(umod_to_ex(s, x) * c); umodpoly sigmatilde; umodpoly_from_ex(sigmatilde, phi, x, R); phi = expand(umod_to_ex(t, x) * c); umodpoly tautilde; umodpoly_from_ex(tautilde, phi, x, R); umodpoly r, q; remdiv(sigmatilde, w1, r, q); umodpoly sigma = r; phi = expand(umod_to_ex(tautilde, x) + umod_to_ex(q, x) * umod_to_ex(u1, x)); umodpoly tau; umodpoly_from_ex(tau, phi, x, R); u = expand(u + umod_to_ex(tau, x) * modulus); w = expand(w + umod_to_ex(sigma, x) * modulus); e = expand(a - u * w); modulus = modulus * p; } // step 5 if ( e.is_zero() ) { ex delta = u.content(x); u = u / delta; w = w / gamma * delta; return lst(u, w); } else { return lst(); } } 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); } 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; } return candidate; } vector::const_iterator end = primes.end(); for ( ; it!=end; ++it ) { if ( *it > p ) { return *it; } } throw logic_error("next_prime: should not reach this point!"); } class Partition { public: Partition(size_t n_) : n(n_) { k.resize(n, 1); k[0] = 0; sum = n-1; } int operator[](size_t i) const { return k[i]; } size_t size() const { return n; } size_t size_first() const { return n-sum; } size_t size_second() const { return sum; } #ifdef DEBUGFACTOR void get() const { for ( size_t i=0; i=1; --i ) { if ( k[i] ) { --k[i]; --sum; return sum > 0; } ++k[i]; ++sum; } return false; } private: size_t n, sum; vector k; }; static void split(const upvec& factors, const Partition& part, umodpoly& a, umodpoly& b) { umodpoly one(1, factors.front()[0].ring()->one()); a = one; b = one; for ( size_t i=0; i(prim.lcoeff(x)); upvec factors; while ( trials < 2 ) { while ( true ) { p = next_prime(p); if ( irem(lcoeff, p) != 0 ) { R = find_modint_ring(p); umodpoly modpoly; umodpoly_from_ex(modpoly, prim, x, R); if ( squarefree(modpoly) ) break; } } // do modular factorization umodpoly modpoly; umodpoly_from_ex(modpoly, prim, x, R); upvec trialfactors; factor_modular(modpoly, trialfactors); if ( trialfactors.size() <= 1 ) { // irreducible for sure return poly; } if ( minfactors == 0 || trialfactors.size() < minfactors ) { factors = trialfactors; minfactors = factors.size(); lastp = p; trials = 1; } else { ++trials; } } p = lastp; R = find_modint_ring(p); cl_univpoly_modint_ring UPR = find_univpoly_ring(R); // lift all factor combinations stack tocheck; ModFactors mf; mf.poly = prim; mf.factors = factors; tocheck.push(mf); ex result = 1; while ( tocheck.size() ) { const size_t n = tocheck.top().factors.size(); Partition part(n); while ( true ) { umodpoly a, b; split(tocheck.top().factors, part, a, b); ex answer = hensel_univar(tocheck.top().poly, x, p, a, b); if ( answer != lst() ) { if ( part.size_first() == 1 ) { if ( part.size_second() == 1 ) { result *= answer.op(0) * answer.op(1); tocheck.pop(); break; } result *= answer.op(0); tocheck.top().poly = answer.op(1); 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); upvec multiterm_eea_lift(const upvec& a, const ex& x, unsigned int p, unsigned int k) { const size_t r = a.size(); cl_modint_ring R = find_modint_ring(expt_pos(cl_I(p),k)); upvec q(r-1); q[r-2] = a[r-1]; for ( size_t j=r-2; j>=1; --j ) { q[j-1] = a[j] * q[j]; } umodpoly beta(1, R->one()); upvec s; for ( size_t j=1; j mdarg(2); mdarg[0] = umod_to_ex(q[j-1], x); mdarg[1] = umod_to_ex(a[j-1], x); vector empty; vector exsigma = multivar_diophant(mdarg, x, umod_to_ex(beta, x), empty, 0, p, k); umodpoly sigma1; umodpoly_from_ex(sigma1, exsigma[0], x, R); umodpoly sigma2; umodpoly_from_ex(sigma2, exsigma[1], x, R); beta = sigma1; s.push_back(sigma2); } s.push_back(beta); return s; } /** * Assert: a not empty. */ 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)); } canonicalize(a); } 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); umodpoly amod = a; change_modulus(R, amod); umodpoly bmod = b; change_modulus(R, bmod); umodpoly g; umodpoly smod; umodpoly tmod; exteuclid(amod, bmod, g, smod, tmod); if ( unequal_one(g) ) { throw logic_error("gcd(amod,bmod) != 1"); } cl_modint_ring Rpk = find_modint_ring(expt_pos(cl_I(p),k)); umodpoly s = smod; change_modulus(Rpk, s); umodpoly t = tmod; change_modulus(Rpk, t); cl_I modulus(p); umodpoly one(1, Rpk->one()); for ( size_t j=1; j 2 ) { upvec s = multiterm_eea_lift(a, x, p, k); for ( size_t j=0; j(e) || is_a(e) ) { return e.map(*this); } else if ( is_a(e) ) { numeric mod(R->modulus); numeric halfmod = (mod-1)/2; cl_MI emod = R->canonhom(the(ex_to(e).to_cl_N())); numeric n(R->retract(emod)); if ( n > halfmod ) { return n-mod; } else { return n; } } return e; } }; static ex make_modular(const ex& e, const cl_modint_ring& R) { make_modular_map map(R); return map(e.expand()); } 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 size_t r = a.size(); const size_t nu = I.size() + 1; vector sigma; if ( nu > 1 ) { ex xnu = I.back().x; int alphanu = I.back().evalpoint; ex A = 1; for ( size_t i=0; i b(r); for ( size_t i=0; i anew = a; for ( size_t i=0; i Inew = I; Inew.pop_back(); sigma = multivar_diophant(anew, x, cnew, Inew, d, p, k); ex buf = c; for ( size_t i=0; i(xnu), m).subs(xnu==alphanu) / factorial(m); cm = make_modular(cm, R); if ( !cm.is_zero() ) { vector delta_s = multivar_diophant(anew, x, cm, Inew, d, p, k); ex buf = e; for ( size_t j=0; j(c) ) { nterms = c.nops(); z = c.op(0); } else { nterms = 1; z = c; } for ( size_t i=0; i(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); } modcm = cl_MI(R, poscm); for ( size_t j=0; j 1 ) { z = c.op(i+1); } } } for ( size_t i=0; i& v) { for ( size_t i=0; i& 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)); vector A(nu); A[nu-1] = a; for ( size_t j=nu; j>=2; --j ) { ex x = I[j-2].x; int alpha = I[j-2].evalpoint; A[j-2] = A[j-1].subs(x==alpha); A[j-2] = make_modular(A[j-2], R); } int maxdeg = a.degree(I.front().x); for ( size_t i=1; i maxdeg ) maxdeg = maxdeg2; } const size_t n = u.size(); vector U(n); for ( size_t i=0; i U1 = U; ex monomial = 1; for ( size_t m=0; m newI; for ( size_t i=1; i<=j-2; ++i ) { newI.push_back(I[i-1]); } ex xj = I[j-2].x; int alphaj = I[j-2].evalpoint; size_t deg = A[j-1].degree(xj); for ( size_t k=1; k<=deg; ++k ) { if ( !e.is_zero() ) { monomial *= (xj - alphaj); monomial = expand(monomial); ex dif = e.diff(ex_to(xj), k); ex c = dif.subs(xj==alphaj) / factorial(k); if ( !c.is_zero() ) { vector deltaU = multivar_diophant(U1, x, c, newI, maxdeg, p, cl_I_to_uint(l)); for ( size_t i=0; i(e) ) { result.append(e); return result; } if ( is_a(e) ) { result.append(1); result.append(e.op(0)); result.append(e.op(1)); return result; } if ( is_a(e) || is_a(e) ) { result.append(1); result.append(e); result.append(1); return result; } if ( is_a(e) ) { ex nfac = 1; for ( size_t i=0; i(op) ) { nfac = op; } if ( is_a(op) ) { result.append(op.op(0)); result.append(op.op(1)); } if ( is_a(op) || is_a(op) ) { result.append(op); result.append(1); } } result.prepend(nfac); return result; } throw runtime_error("put_factors_into_lst: bad term."); } #ifdef DEBUGFACTOR ostream& operator<<(ostream& o, const vector& v) { for ( size_t i=0; i& d) { const int k = f.nops()-2; numeric q, r; d[0] = ex_to(f.op(0) * f.op(f.nops()-1)); if ( d[0] == 1 && k == 1 && abs(f.op(1)) != 1 ) { return false; } for ( int i=1; i<=k; ++i ) { q = ex_to(abs(f.op(i))); for ( int j=i-1; j>=0; --j ) { r = d[j]; do { r = gcd(r, q); q = q/r; } while ( r != 1 ); if ( q == 1 ) { return true; } } d[i] = q; } return false; } static bool generate_set(const ex& u, const ex& vn, const exset& syms, const ex& f, const numeric& modulus, vector& a, vector& d) { // computation of d is actually not necessary const ex& x = *syms.begin(); bool trying = true; do { ex u0 = u; ex vna = vn; ex vnatry; exset::const_iterator s = syms.begin(); ++s; for ( size_t i=0; i(x))) != 1 ) { continue; } if ( is_a(vn) ) { trying = false; } else { lst fnum; lst::const_iterator i = ex_to(f).begin(); fnum.append(*i++); bool problem = false; while ( i!=ex_to(f).end() ) { ex fs = *i; if ( !is_a(fs) ) { s = syms.begin(); ++s; for ( size_t j=0; j=p.ldegree(x); --i ) { cont = gcd(cont, p.coeff(x,ex_to(i).to_int())); if ( cont == 1 ) break; } ex pp = expand(normal(p / cont)); if ( !is_a(cont) ) { #ifdef DEBUGFACTOR return ::factor(cont) * ::factor(pp); #else return factor(cont) * factor(pp); #endif } /* factor leading coefficient */ pp = pp.collect(x); ex vn = pp.lcoeff(x); pp = pp.expand(); ex vnlst; if ( is_a(vn) ) { vnlst = lst(vn); } else { #ifdef DEBUGFACTOR ex vnfactors = ::factor(vn); #else ex vnfactors = factor(vn); #endif vnlst = put_factors_into_lst(vnfactors); } const numeric maxtrials = 3; numeric modulus = (vnlst.nops()-1 > 3) ? vnlst.nops()-1 : 3; numeric minimalr = -1; vector a(syms.size()-1, 0); vector d((vnlst.nops()-1)/2+1, 0); while ( true ) { numeric trialcount = 0; ex u, delta; unsigned int prime = 3; size_t factor_count = 0; ex ufac; ex ufaclst; while ( trialcount < maxtrials ) { bool problem = generate_set(pp, vn, syms, vnlst, modulus, a, d); if ( problem ) { ++modulus; continue; } u = pp; s = syms.begin(); ++s; for ( size_t i=0; i(u.lcoeff(x)), prime) != 0 ) { umodpoly modpoly; umodpoly_from_ex(modpoly, u, x, R); if ( squarefree(modpoly) ) break; } prime = next_prime(prime); R = find_modint_ring(prime); } #ifdef DEBUGFACTOR ufac = ::factor(u); #else ufac = factor(u); #endif ufaclst = put_factors_into_lst(ufac); factor_count = (ufaclst.nops()-1)/2; // veto factorization for which gcd(u_i, u_j) != 1 for all i,j upvec tryu; for ( size_t i=0; i<(ufaclst.nops()-1)/2; ++i ) { umodpoly newu; umodpoly_from_ex(newu, ufaclst.op(i*2+1), x, R); tryu.push_back(newu); } bool veto = false; for ( size_t i=0; i factor_count ) { minimalr = factor_count; trialcount = 0; } if ( minimalr <= 1 ) { return poly; } } vector ftilde((vnlst.nops()-1)/2+1); ftilde[0] = ex_to(vnlst.op(0)); for ( size_t i=1; i(ft); } vector used_flag((vnlst.nops()-1)/2+1, false); vector D(factor_count, 1); for ( size_t i=0; i<=factor_count; ++i ) { numeric prefac; if ( i == 0 ) { prefac = ex_to(ufaclst.op(0)); ftilde[0] = ftilde[0] / prefac; vnlst.let_op(0) = vnlst.op(0) / prefac; continue; } else { prefac = ex_to(ufaclst.op(2*(i-1)+1).lcoeff(x)); } for ( size_t j=(vnlst.nops()-1)/2+1; j>0; --j ) { if ( abs(ftilde[j-1]) == 1 ) { used_flag[j-1] = true; continue; } numeric g = gcd(prefac, ftilde[j-1]); if ( g != 1 ) { prefac = prefac / g; numeric count = abs(iquo(g, ftilde[j-1])); used_flag[j-1] = true; if ( i > 0 ) { if ( j == 1 ) { D[i-1] = D[i-1] * pow(vnlst.op(0), count); } else { D[i-1] = D[i-1] * pow(vnlst.op(2*(j-2)+1), count); } } else { ftilde[j-1] = ftilde[j-1] / prefac; break; } ++j; } } } bool some_factor_unused = false; for ( size_t i=0; i C(factor_count); if ( delta == 1 ) { for ( size_t i=0; i epv; s = syms.begin(); ++s; for ( size_t i=0; i=u.ldegree(x); --i ) { maxcoeff += pow(abs(u.coeff(x, i)),2); } cl_I normmc = ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); unsigned int maxdegree = 0; for ( size_t i=0; i (int)maxdegree ) { maxdegree = ufaclst[2*i+1].degree(x); } } cl_I B = normmc * expt_pos(cl_I(2), maxdegree); cl_I l = 1; cl_I pl = prime; while ( pl < B ) { l = l + 1; pl = pl * prime; } upvec uvec; cl_modint_ring R = find_modint_ring(expt_pos(cl_I(prime),l)); for ( size_t i=0; i<(ufaclst.nops()-1)/2; ++i ) { umodpoly newu; umodpoly_from_ex(newu, ufaclst.op(i*2+1), x, R); uvec.push_back(newu); } ex res = hensel_multivar(ufaclst.op(0)*pp, x, epv, prime, l, uvec, C); if ( res != lst() ) { ex result = cont * ufaclst.op(0); for ( size_t i=0; i(e) ) { syms.insert(e); return e; } return e.map(*this); } }; static ex factor_sqrfree(const ex& poly) { // determine all symbols in poly find_symbols_map findsymbols; findsymbols(poly); if ( findsymbols.syms.size() == 0 ) { return poly; } if ( findsymbols.syms.size() == 1 ) { // univariate case const ex& x = *(findsymbols.syms.begin()); if ( poly.ldegree(x) > 0 ) { int ld = poly.ldegree(x); ex res = factor_univariate(expand(poly/pow(x, ld)), x); return res * pow(x,ld); } else { ex res = factor_univariate(poly, x); return res; } } // multivariate case ex res = factor_multivariate(poly, findsymbols.syms); return res; } struct apply_factor_map : public map_function { unsigned options; apply_factor_map(unsigned options_) : options(options_) { } ex operator()(const ex& e) { if ( e.info(info_flags::polynomial) ) { #ifdef DEBUGFACTOR return ::factor(e, options); #else return factor(e, options); #endif } if ( is_a(e) ) { ex s1, s2; for ( size_t i=0; i(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; } } return res; } if ( is_a(sfpoly) ) { return poly; } // case: (polynomial) ex f = factor_sqrfree(sfpoly); return f; } } // namespace GiNaC