X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=blobdiff_plain;f=ginac%2Ffactor.cpp;h=204010be3750294640434dc28da698f9f452db69;hp=3a012405ffc2934ae9d91358261a039101343e2e;hb=edc92b7a463993da62357fb4afad053e8c6d0771;hpb=114449ae6f2cd3151d9b8342c570db021a9e892c diff --git a/ginac/factor.cpp b/ginac/factor.cpp index 3a012405..204010be 100644 --- a/ginac/factor.cpp +++ b/ginac/factor.cpp @@ -1,7 +1,12 @@ /** @file factor.cpp * - * Polynomial factorization routines. - * Only univariate at the moment and completely non-optimized! + * 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. */ /* @@ -22,6 +27,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +//#define DEBUGFACTOR + #include "factor.h" #include "ex.h" @@ -36,589 +43,589 @@ #include "add.h" #include +#include +#include #include #include +#ifdef DEBUGFACTOR +#include +#endif using namespace std; #include using namespace cln; -//#define DEBUGFACTOR - -#ifdef DEBUGFACTOR -#include -#endif // def DEBUGFACTOR - namespace GiNaC { +#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 + +// anonymous namespace to hide all utility functions namespace { -typedef vector Vec; -typedef vector VecVec; - +typedef vector mvec; #ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const Vec& v) +ostream& operator<<(ostream& o, const vector& v) { - Vec::const_iterator i = v.begin(), end = v.end(); + vector::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 VecVec& v) +ostream& operator<<(ostream& o, const vector& v) { - VecVec::const_iterator i = v.begin(), end = v.end(); + vector::const_iterator i = v.begin(), end = v.end(); while ( i != end ) { - o << *i++ << endl; + o << *i << "[" << i-v.begin() << "]" << " "; + ++i; } return o; } -#endif // def DEBUGFACTOR - -struct Term -{ - cl_MI c; // coefficient - unsigned int exp; // exponent >=0 -}; - -#ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const Term& t) +ostream& operator<<(ostream& o, const vector& v) { - if ( t.exp ) { - o << "(" << t.c << ")x^" << t.exp; + vector::const_iterator i = v.begin(), end = v.end(); + while ( i != end ) { + o << *i << "[" << i-v.begin() << "]" << " "; + ++i; } - else { - o << "(" << t.c << ")"; + return o; +} +ostream& operator<<(ostream& o, const vector< vector >& v) +{ + vector< vector >::const_iterator i = v.begin(), end = v.end(); + while ( i != end ) { + o << i-v.begin() << ": " << *i << endl; + ++i; } return o; } -#endif // def DEBUGFACTOR +#endif -struct UniPoly +//////////////////////////////////////////////////////////////////////////////// +// modular univariate polynomial code + +typedef std::vector umodpoly; +typedef std::vector upoly; +typedef vector upvec; + +// COPY FROM UPOLY.HPP + +// CHANGED size_t -> int !!! +template static int degree(const T& p) { - cl_modint_ring R; - list terms; // highest exponent first - - UniPoly(const cl_modint_ring& ring) : R(ring) { } - UniPoly(const cl_modint_ring& ring, const ex& poly, const ex& x) : R(ring) - { - // assert: poly is in Z[x] - Term t; - for ( int i=poly.degree(x); i>=poly.ldegree(x); --i ) { - int coeff = ex_to(poly.coeff(x,i)).to_int(); - if ( coeff ) { - t.c = R->canonhom(coeff); - if ( !zerop(t.c) ) { - t.exp = i; - terms.push_back(t); - } - } - } - } - UniPoly(const cl_modint_ring& ring, const Vec& v) : R(ring) - { - Term t; - for ( unsigned int i=0; i 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; } - unsigned int degree() const - { - if ( terms.size() ) { - return terms.front().exp; + + 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; } - else { - return 0; + if (i == 0) { + is_zero = true; + break; } + --i; + } while (true); + + if (is_zero) { + p.clear(); + return; } - bool zero() const { return (terms.size() == 0); } - const cl_MI operator[](unsigned int deg) const - { - list::const_iterator i = terms.begin(), end = terms.end(); - for ( ; i != end; ++i ) { - if ( i->exp == deg ) { - return i->c; - } - if ( i->exp < deg ) { - break; - } - } - return R->zero(); + + p.erase(p.begin() + i, p.end()); +} + +// END COPY FROM UPOLY.HPP + +static void expt_pos(umodpoly& a, unsigned int q) +{ + if ( a.empty() ) return; + cl_MI zero = a[0].ring()->zero(); + int deg = degree(a); + a.resize(degree(a)*q+1, zero); + for ( int i=deg; i>0; --i ) { + a[i*q] = a[i]; + a[i] = zero; } - void set(unsigned int deg, const cl_MI& c) - { - list::iterator i = terms.begin(), end = terms.end(); - while ( i != end ) { - if ( i->exp == deg ) { - if ( !zerop(c) ) { - i->c = c; - } - else { - terms.erase(i); - } - return; - } - if ( i->exp < deg ) { - break; - } - ++i; +} + +template +static T operator+(const T& a, const T& b) +{ + int sa = a.size(); + int sb = b.size(); + if ( sa >= sb ) { + T r(sa); + int i = 0; + for ( ; i::const_iterator i = terms.begin(), end = terms.end(); - if ( symmetric ) { - numeric mod(R->modulus); - numeric halfmod = (mod-1)/2; - for ( ; i != end; ++i ) { - numeric n(R->retract(i->c)); - if ( n > halfmod ) { - r += pow(x, i->exp) * (n-mod); - } - else { - r += pow(x, i->exp) * n; - } - } + else { + T r(sb); + int i = 0; + for ( ; iexp) * numeric(R->retract(i->c)); - } + for ( ; ione() ) { - list::iterator i = terms.begin(), end = terms.end(); - cl_MI cont = i->c; - i->c = R->one(); - while ( ++i != end ) { - i->c = div(i->c, cont); - if ( zerop(i->c) ) { - terms.erase(i); - } - } - } - } - } - cl_MI unit() const - { - return terms.front().c; - } - void divide(const cl_MI& x) - { - list::iterator i = terms.begin(), end = terms.end(); - for ( ; i != end; ++i ) { - i->c = div(i->c, x); - if ( zerop(i->c) ) { - terms.erase(i); - } +} + +template +static T operator-(const T& a, const T& b) +{ + int sa = a.size(); + int sb = b.size(); + if ( sa >= sb ) { + T r(sa); + int i = 0; + for ( ; i::iterator i = terms.begin(), end = terms.end(); - while ( i != end ) { - if ( i->exp > 0 ) { - // assert: i->exp is multiple of prime - i->exp /= prime; - } - ++i; + for ( ; i::const_iterator i = terms.begin(), end = terms.end(); - while ( i != end ) { - if ( i->exp ) { - cl_MI newc = i->c * i->exp; - if ( !zerop(newc) ) { - Term t; - t.c = newc; - t.exp = i->exp-1; - d.terms.push_back(t); - } - } - ++i; + else { + T r(sb); + int i = 0; + for ( ; i::const_iterator i1 = terms.begin(), end = terms.end(); - list::const_iterator i2 = o.terms.begin(); - while ( i1 != end ) { - if ( i1->exp != i2->exp ) { - return i1->exp < i2->exp; - } - if ( i1->c != i2->c ) { - return R->retract(i1->c) < R->retract(i2->c); - } - ++i1; ++i2; + for ( ; i::const_iterator i1 = terms.begin(), end = terms.end(); - list::const_iterator i2 = o.terms.begin(); - while ( i1 != end ) { - if ( i1->exp != i2->exp ) { - return false; - } - if ( i1->c != i2->c ) { - return false; - } - ++i1; ++i2; +} + +static upoly operator*(const upoly& a, const upoly& b) +{ + upoly c; + if ( a.empty() || b.empty() ) return c; + + int n = degree(a) + degree(b); + c.resize(n+1, 0); + for ( int i=0 ; i<=n; ++i ) { + for ( int j=0 ; j<=i; ++j ) { + if ( j > degree(a) || (i-j) > degree(b) ) continue; + c[i] = c[i] + a[j] * b[i-j]; } - return true; - } - bool operator!=(const UniPoly& o) const - { - bool res = !(*this == o); - return res; } -}; + canonicalize(c); + return c; +} -static UniPoly operator*(const UniPoly& a, const UniPoly& b) +static umodpoly operator*(const umodpoly& a, const umodpoly& b) { - unsigned int n = a.degree()+b.degree(); - UniPoly c(a.R); - Term t; - for ( unsigned int i=0 ; i<=n; ++i ) { - t.c = a.R->zero(); - for ( unsigned int j=0 ; j<=i; ++j ) { - t.c = t.c + a[j] * b[i-j]; - } - if ( !zerop(t.c) ) { - t.exp = i; - c.terms.push_front(t); + umodpoly c; + if ( a.empty() || b.empty() ) return c; + + int n = degree(a) + degree(b); + c.resize(n+1, a[0].ring()->zero()); + for ( int i=0 ; i<=n; ++i ) { + for ( int j=0 ; j<=i; ++j ) { + if ( j > degree(a) || (i-j) > degree(b) ) continue; + c[i] = c[i] + a[j] * b[i-j]; } } + canonicalize(c); return c; } -static UniPoly operator-(const UniPoly& a, const UniPoly& b) +static upoly operator*(const upoly& a, const cl_I& x) { - list::const_iterator ia = a.terms.begin(), aend = a.terms.end(); - list::const_iterator ib = b.terms.begin(), bend = b.terms.end(); - UniPoly c(a.R); - while ( ia != aend && ib != bend ) { - if ( ia->exp > ib->exp ) { - c.terms.push_back(*ia); - ++ia; - } - else if ( ia->exp < ib->exp ) { - c.terms.push_back(*ib); - c.terms.back().c = -c.terms.back().c; - ++ib; - } - else { - Term t; - t.exp = ia->exp; - t.c = ia->c - ib->c; - if ( !zerop(t.c) ) { - c.terms.push_back(t); - } - ++ia; ++ib; - } + if ( zerop(x) ) { + upoly r; + return r; } - while ( ia != aend ) { - c.terms.push_back(*ia); - ++ia; + upoly r(a.size()); + for ( size_t i=0; i::const_iterator ia = a.terms.begin(), aend = a.terms.end(); - UniPoly c(a.R); - while ( ia != aend ) { - c.terms.push_back(*ia); - c.terms.back().c = -c.terms.back().c; - ++ia; + umodpoly r(a.size()); + for ( size_t i=0; i::const_iterator i = t.terms.begin(), end = t.terms.end(); - if ( i == end ) { - o << "0"; - return o; + // assert: e is in Z[x] + int deg = e.degree(x); + up.resize(deg+1); + int ldeg = e.ldegree(x); + for ( ; deg>=ldeg; --deg ) { + up[deg] = the(ex_to(e.coeff(x, deg)).to_cl_N()); } - for ( ; i != end; ) { - o << *i++; - if ( i != end ) { - o << " + "; - } + for ( ; deg>=0; --deg ) { + up[deg] = 0; } - return o; + canonicalize(up); } -#endif // def DEBUGFACTOR -#ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const list& t) +static void umodpoly_from_upoly(umodpoly& ump, const upoly& e, const cl_modint_ring& R) { - list::const_iterator i = t.begin(), end = t.end(); - o << "{" << endl; - for ( ; i != end; ) { - o << *i++ << endl; + int deg = degree(e); + ump.resize(deg+1); + for ( ; deg>=0; --deg ) { + ump[deg] = R->canonhom(e[deg]); } - o << "}" << endl; - return o; + canonicalize(ump); } -#endif // def DEBUGFACTOR -typedef vector UniPolyVec; +static void umodpoly_from_ex(umodpoly& ump, const ex& e, const ex& x, const cl_modint_ring& R) +{ + // assert: e is in Z[x] + int deg = e.degree(x); + ump.resize(deg+1); + int ldeg = e.ldegree(x); + for ( ; deg>=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); +} -struct UniFactor +static void umodpoly_from_ex(umodpoly& ump, const ex& e, const ex& x, const cl_I& modulus) { - UniPoly p; - unsigned int exp; + umodpoly_from_ex(ump, e, x, find_modint_ring(modulus)); +} - UniFactor(const cl_modint_ring& ring) : p(ring) { } - UniFactor(const UniPoly& p_, unsigned int exp_) : p(p_), exp(exp_) { } - bool operator<(const UniFactor& o) const - { - return p < o.p; +static ex upoly_to_ex(const upoly& a, const ex& x) +{ + if ( a.empty() ) return 0; + ex e; + for ( int i=degree(a); i>=0; --i ) { + e += numeric(a[i]) * pow(x, i); } -}; + return e; +} -struct UniFactorVec +static ex umodpoly_to_ex(const umodpoly& a, const ex& x) { - vector factors; - - void unique() - { - sort(factors.begin(), factors.end()); - if ( factors.size() > 1 ) { - vector::iterator i = factors.begin(); - vector::const_iterator cmp = factors.begin()+1; - vector::iterator end = factors.end(); - while ( cmp != end ) { - if ( i->p != cmp->p ) { - ++i; - ++cmp; - } - else { - i->exp += cmp->exp; - ++cmp; - } - } - if ( i != end-1 ) { - factors.erase(i+1, end); - } + 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; +} -#ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const UniFactorVec& ufv) +static upoly umodpoly_to_upoly(const umodpoly& a) { - for ( size_t i=0; imodulus; + cl_I halfmod = (mod-1) >> 1; + for ( int i=degree(a); i>=0; --i ) { + cl_I n = R->retract(a[i]); + if ( n > halfmod ) { + e[i] = n-mod; + } else { + e[i] = n; } - o << "[ " << ufv.factors[i].p << " ]^" << ufv.factors[i].exp << endl; } - return o; + return e; } -#endif // def DEBUGFACTOR -static void rem(const UniPoly& a_, const UniPoly& b, UniPoly& c) +static umodpoly umodpoly_to_umodpoly(const umodpoly& a, const cl_modint_ring& R, unsigned int m) { - if ( a_.degree() < b.degree() ) { - c = a_; - return; - } + umodpoly e; + if ( a.empty() ) return e; + cl_modint_ring oldR = a[0].ring(); + size_t sa = a.size(); + e.resize(sa+m, R->zero()); + for ( int i=0; icanonhom(oldR->retract(a[i])); + } + canonicalize(e); + return e; +} - unsigned int k, n; - n = b.degree(); - k = a_.degree() - n; +/** 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; - if ( n == 0 ) { - c.terms.clear(); - 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)); } +} - c = a_; - Term termbuf; - - while ( true ) { - cl_MI qk = div(c[n+k], b[n]); +/** 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) ) { - unsigned int j; - for ( unsigned int i=0; i::iterator i = c.terms.begin(), end = c.terms.end(); - while ( i != end ) { - if ( i->exp <= n-1 ) { - break; - } - ++i; - } - c.terms.erase(c.terms.begin(), i); + } while ( k-- ); + + fill(r.begin()+n, r.end(), a[0].ring()->zero()); + canonicalize(r); } -static void div(const UniPoly& a_, const UniPoly& b, UniPoly& q) +/** 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) { - if ( a_.degree() < b.degree() ) { - q.terms.clear(); - return; - } - - unsigned int k, n; - n = b.degree(); - k = a_.degree() - n; + 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) ) { - Term t; - t.c = qk; - t.exp = k; - q.terms.push_back(t); - unsigned int j; - for ( unsigned int i=0; izero()); + canonicalize(r); + canonicalize(q); } -static void gcd(const UniPoly& a, const UniPoly& b, UniPoly& c) +/** 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) { - c = a; - c.unit_normal(); - UniPoly d = b; - d.unit_normal(); - - if ( c.degree() < d.degree() ) { - gcd(b, a, c); - return; - } + if ( degree(a) < degree(b) ) return gcd(b, a, c); - while ( !d.zero() ) { - UniPoly r(a.R); + 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; } - c.unit_normal(); + normalize_in_field(c); } -static bool is_one(const UniPoly& w) +/** 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) { - if ( w.terms.size() == 1 && w[0] == w.R->one() ) { - return true; + d.clear(); + if ( a.size() <= 1 ) return; + + d.insert(d.begin(), a.begin()+1, a.end()); + int max = d.size(); + for ( int i=1; imodulus); - c.reduce_exponents(prime); - unsigned int pos = fvec.factors.size(); - sqrfree_main(c, fvec); - for ( unsigned int p=pos; pmodulus); - UniPoly amod = a; - amod.reduce_exponents(prime); - unsigned int pos = fvec.factors.size(); - sqrfree_main(amod, fvec); - for ( unsigned int p=pos; pone() ); } -static void squarefree(const UniPoly& a, UniFactorVec& fvec) +static bool equal_one(const umodpoly& a) { - sqrfree_main(a, fvec); - fvec.unique(); + return ( a.size() == 1 && a[0] == a[0].ring()->one() ); } -class Matrix +/** 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) { - friend ostream& operator<<(ostream& o, const Matrix& m); -public: - Matrix(size_t r_, size_t c_, const cl_MI& init) : r(r_), c(c_) - { - m.resize(c*r, init); + 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; } @@ -626,7 +633,7 @@ 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) { - Vec::iterator i = m.begin() + col; + 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) { - Vec::iterator i1 = m.begin() + row*c; - Vec::const_iterator i2 = newrow.begin(), end = newrow.end(); + mvec::iterator i1 = m.begin() + row*c; + mvec::const_iterator i2 = newrow.begin(), end = newrow.end(); for ( ; i2 != end; ++i1, ++i2 ) { *i1 = *i2; } } - Vec::const_iterator row_begin(size_t row) const { return m.begin()+row*c; } - Vec::const_iterator row_end(size_t row) const { return m.begin()+row*c+r; } + 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; - Vec m; + mvec m; }; #ifdef DEBUGFACTOR -ostream& operator<<(ostream& o, const Matrix& m) +modular_matrix operator*(const modular_matrix& m1, const modular_matrix& m2) { - vector::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; + 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; iretract(m(i,j)) << ","; + } + o << R->retract(m(i,m.colsize()-1)) << "}"; + if ( i != m.rowsize()-1 ) { + o << ","; + } + } + o << "}"; return o; } #endif // def DEBUGFACTOR -static void q_matrix(const UniPoly& a, Matrix& Q) +// END modular matrix +//////////////////////////////////////////////////////////////////////////////// + +static void q_matrix(const umodpoly& a_, modular_matrix& Q) { - unsigned int n = a.degree(); - unsigned int q = cl_I_to_uint(a.R->modulus); - vector r(n, a.R->zero()); - r[0] = a.R->one(); + umodpoly a = a_; + normalize_in_field(a); + + int n = degree(a); + unsigned int q = cl_I_to_uint(a[0].ring()->modulus); + umodpoly r(n, a[0].ring()->zero()); + r[0] = a[0].ring()->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[i] = r[i-1] - (rn_1 * a[i]); } r[0] = -rn_1 * a[0]; if ( (m % q) == 0 ) { @@ -715,7 +791,7 @@ static void q_matrix(const UniPoly& a, Matrix& Q) } } -static void nullspace(Matrix& M, vector& basis) +static void nullspace(modular_matrix& M, vector& basis) { const size_t n = M.rowsize(); const cl_MI one = M(0,0).ring()->one(); @@ -753,58 +829,65 @@ static void nullspace(Matrix& M, vector& basis) } for ( size_t i=0; izero()); + cl_modint_ring R = a[0].ring(); + umodpoly one(1, R->one()); + + modular_matrix Q(degree(a), degree(a), R->zero()); q_matrix(a, Q); - VecVec nu; + vector nu; nullspace(Q, nu); + const unsigned int k = nu.size(); if ( k == 1 ) { return; } - list factors; + list factors; factors.push_back(a); unsigned int size = 1; unsigned int r = 1; - unsigned int q = cl_I_to_uint(a.R->modulus); + unsigned int q = cl_I_to_uint(R->modulus); - list::iterator u = factors.begin(); + 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(); + list::const_iterator i = factors.begin(), end = factors.end(); while ( i != end ) { upv.push_back(*i++); } return; } - if ( u->degree() < nur.degree() ) { - break; - } } } if ( ++r == k ) { @@ -814,121 +897,299 @@ static void berlekamp(const UniPoly& a, UniPolyVec& upv) } } -static void factor_modular(const UniPoly& p, UniPolyVec& upv) +static void expt_1_over_p(const umodpoly& a, unsigned int prime, umodpoly& ap) { - berlekamp(p, upv); - return; + size_t newdeg = degree(a)/prime; + ap.resize(newdeg+1); + ap[0] = a[0]; + for ( size_t i=1; i<=newdeg; ++i ) { + ap[i] = a[i*prime]; + } } -static void exteuclid(const UniPoly& a, const UniPoly& b, UniPoly& g, UniPoly& s, UniPoly& t) +static void modsqrfree(const umodpoly& a, upvec& factors, vector& mult) { - if ( a.degree() < b.degree() ) { - exteuclid(b, a, g, t, s); + const unsigned int prime = cl_I_to_uint(a[0].ring()->modulus); + int i = 1; + umodpoly b; + deriv(a, b); + if ( b.size() ) { + umodpoly c; + gcd(a, b, c); + umodpoly w; + div(a, c, w); + while ( unequal_one(w) ) { + umodpoly y; + gcd(w, c, y); + umodpoly z; + div(w, y, z); + factors.push_back(z); + mult.push_back(i); + ++i; + w = y; + umodpoly buf; + div(c, y, buf); + c = buf; + } + if ( unequal_one(c) ) { + umodpoly cp; + expt_1_over_p(c, prime, cp); + size_t previ = mult.size(); + modsqrfree(cp, factors, mult); + for ( size_t i=previ; i& degrees, upvec& ddfactors) +{ + umodpoly a = a_; + + cl_modint_ring R = a[0].ring(); + int q = cl_I_to_int(R->modulus); + int nhalf = degree(a)/2; + + int i = 1; + umodpoly w(2); + w[0] = R->zero(); + w[1] = R->one(); + umodpoly x = w; + + bool nontrivial = false; + while ( i <= nhalf ) { + expt_pos(w, q); + umodpoly buf; + rem(w, a, buf); + w = buf; + umodpoly wx = w - x; + gcd(a, wx, buf); + if ( unequal_one(buf) ) { + degrees.push_back(i); + ddfactors.push_back(buf); + } + if ( unequal_one(buf) ) { + umodpoly buf2; + div(a, buf, buf2); + a = buf2; + nhalf = degree(a)/2; + rem(w, a, buf); + w = buf; + } + ++i; + } + if ( unequal_one(a) ) { + degrees.push_back(degree(a)); + ddfactors.push_back(a); + } +} + +static void same_degree_factor(const umodpoly& a, upvec& upv) +{ + cl_modint_ring R = a[0].ring(); + int deg = degree(a); + + vector degrees; + upvec ddfactors; + distinct_degree_factor(a, degrees, ddfactors); + + for ( size_t i=0; i mult; + modsqrfree(p, factors, mult); + +#define USE_SAME_DEGREE_FACTOR +#ifdef USE_SAME_DEGREE_FACTOR + for ( size_t i=0; i0; --j ) { + upv.insert(upv.end(), upvbuf.begin(), upvbuf.end()); + } + } +#else + for ( size_t i=0; i0; --j ) { + upv.push_back(factors[i]); + } + } + } +#endif +} + +/** Calculates polynomials s and t such that a*s+b*t==1. + * Assertion: a and b are relatively prime and not zero. + * + * @param[in] a polynomial + * @param[in] b polynomial + * @param[out] s polynomial + * @param[out] t polynomial + */ +static void exteuclid(const umodpoly& a, const umodpoly& b, umodpoly& s, umodpoly& t) +{ + if ( degree(a) < degree(b) ) { + exteuclid(b, a, t, s); return; } - UniPoly c1(a.R), c2(a.R), d1(a.R), d2(a.R), q(a.R), r(a.R), r1(a.R), r2(a.R); - UniPoly c = a; c.unit_normal(); - UniPoly d = b; d.unit_normal(); - c1.set(0, a.R->one()); - d2.set(0, a.R->one()); - while ( !d.zero() ) { - q.terms.clear(); + + umodpoly one(1, a[0].ring()->one()); + umodpoly c = a; normalize_in_field(c); + umodpoly d = b; normalize_in_field(d); + s = one; + t.clear(); + umodpoly d1; + umodpoly d2 = one; + umodpoly q; + while ( true ) { div(c, d, q); - r = c - q * d; - r1 = c1 - q * d1; - r2 = c2 - q * d2; + umodpoly r = c - q * d; + umodpoly r1 = s - q * d1; + umodpoly r2 = t - q * d2; c = d; - c1 = d1; - c2 = d2; + s = d1; + t = d2; + if ( r.empty() ) break; d = r; d1 = r1; d2 = r2; } - g = c; g.unit_normal(); - s = c1; - s.divide(a.unit()); - s.divide(c.unit()); - t = c2; - t.divide(b.unit()); - t.divide(c.unit()); + cl_MI fac = recip(lcoeff(a) * lcoeff(c)); + umodpoly::iterator i = s.begin(), end = s.end(); + for ( ; i!=end; ++i ) { + *i = *i * fac; + } + canonicalize(s); + fac = recip(lcoeff(b) * lcoeff(c)); + i = t.begin(), end = t.end(); + for ( ; i!=end; ++i ) { + *i = *i * fac; + } + canonicalize(t); } -static ex replace_lc(const ex& poly, const ex& x, const ex& lc) +static upoly replace_lc(const upoly& poly, const cl_I& lc) { - ex r = expand(poly + (lc - poly.lcoeff(x)) * pow(x, poly.degree(x))); + if ( poly.empty() ) return poly; + upoly r = poly; + r.back() = lc; return r; } -static ex hensel_univar(const ex& a_, const ex& x, unsigned int p, const UniPoly& u1_, const UniPoly& w1_, const ex& gamma_ = 0) +static void hensel_univar(const upoly& a_, unsigned int p, const umodpoly& u1_, const umodpoly& w1_, upoly& u, upoly& w) { - ex a = a_; - const cl_modint_ring& R = u1_.R; + upoly 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_R maxcoeff = 0; + for ( int i=degree(a); i>=0; --i ) { + maxcoeff = maxcoeff + square(abs(a[i])); } - cl_I normmc = ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); - unsigned int maxdegree = (u1_.degree() > w1_.degree()) ? u1_.degree() : w1_.degree(); - unsigned int B = cl_I_to_uint(normmc * expt_pos(cl_I(2), maxdegree)); + cl_I normmc = ceiling1(the(cln::sqrt(maxcoeff))); + 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; - } - unsigned int gamma_ui = ex_to(abs(gamma)).to_int(); - a = a * gamma; - UniPoly nu1 = u1_; - nu1.unit_normal(); - UniPoly nw1 = w1_; - nw1.unit_normal(); - ex phi; - phi = expand(gamma * nu1.to_ex(x)); - UniPoly u1(R, phi, x); - phi = expand(alpha * nw1.to_ex(x)); - UniPoly w1(R, phi, x); + cl_I alpha = lcoeff(a); + a = a * alpha; + umodpoly nu1 = u1_; + normalize_in_field(nu1); + umodpoly nw1 = w1_; + normalize_in_field(nw1); + upoly phi; + phi = umodpoly_to_upoly(nu1) * alpha; + umodpoly u1; + umodpoly_from_upoly(u1, phi, R); + phi = umodpoly_to_upoly(nw1) * alpha; + umodpoly w1; + umodpoly_from_upoly(w1, phi, R); // step 2 - UniPoly s(R), t(R), g(R); - exteuclid(u1, w1, g, s, t); + umodpoly s; + umodpoly t; + exteuclid(u1, w1, s, t); // step 3 - ex u = replace_lc(u1.to_ex(x), x, gamma); - ex w = replace_lc(w1.to_ex(x), x, alpha); - ex e = expand(a - u * w); - unsigned int modulus = p; + u = replace_lc(umodpoly_to_upoly(u1), alpha); + w = replace_lc(umodpoly_to_upoly(w1), alpha); + upoly e = a - u * w; + cl_I modulus = p; + const cl_I maxmodulus = 2*B*abs(alpha); // step 4 - while ( !e.is_zero() && modulus < 2*B*gamma_ui ) { - ex c = e / modulus; - phi = expand(s.to_ex(x)*c); - UniPoly sigmatilde(R, phi, x); - phi = expand(t.to_ex(x)*c); - UniPoly tautilde(R, phi, x); - UniPoly q(R), r(R); - div(sigmatilde, w1, q); - rem(sigmatilde, w1, r); - UniPoly sigma = r; - phi = expand(tautilde.to_ex(x) + q.to_ex(x) * u1.to_ex(x)); - UniPoly tau(R, phi, x); - u = expand(u + tau.to_ex(x) * modulus); - w = expand(w + sigma.to_ex(x) * modulus); - e = expand(a - u * w); + while ( !e.empty() && modulus < maxmodulus ) { + // ad-hoc divisablity check + for ( size_t k=0; kone()); + split(); } 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 { DCOUTVAR(k); } +#endif bool next() { for ( size_t i=n-1; i>=1; --i ) { if ( k[i] ) { --k[i]; --sum; - return sum > 0; + if ( sum > 0 ) { + split(); + return true; + } + else { + return false; + } } ++k[i]; ++sum; } return false; } + void split() + { + left = one; + right = one; + for ( size_t i=0; i k; }; -static void split(const UniPolyVec& factors, const Partition& part, UniPoly& a, UniPoly& b) -{ - a.set(0, a.R->one()); - b.set(0, a.R->one()); - for ( size_t i=0; i(prim.lcoeff(x)), p) != 0 ) { - UniPoly modpoly(R, prim, x); - UniFactorVec sqrfree_ufv; - squarefree(modpoly, sqrfree_ufv); - if ( sqrfree_ufv.factors.size() == 1 ) break; - } - p = next_prime(p); - R = find_modint_ring(p); - } - - // do modular factorization - UniPoly modpoly(R, prim, x); - UniPolyVec factors; - factor_modular(modpoly, factors); - if ( factors.size() <= 1 ) { - // irreducible for sure - return poly; + // determine proper prime and minimize number of modular factors + unsigned int p = 3, lastp = 3; + cl_modint_ring R; + unsigned int trials = 0; + unsigned int minfactors = 0; + cl_I lc = lcoeff(prim); + upvec factors; + while ( trials < 2 ) { + umodpoly modpoly; + while ( true ) { + p = next_prime(p); + if ( !zerop(rem(lc, p)) ) { + R = find_modint_ring(p); + umodpoly_from_upoly(modpoly, prim, R); + if ( squarefree(modpoly) ) break; + } + } + + // do modular factorization + 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); // lift all factor combinations stack tocheck; @@ -1047,24 +1341,22 @@ static ex factor_univariate(const ex& poly, const ex& x) mf.poly = prim; mf.factors = factors; tocheck.push(mf); + upoly f1, f2; ex result = 1; while ( tocheck.size() ) { const size_t n = tocheck.top().factors.size(); - Partition part(n); + factor_partition part(tocheck.top().factors); while ( true ) { - UniPoly a(R), b(R); - split(tocheck.top().factors, part, a, b); - - ex answer = hensel_univar(tocheck.top().poly, x, p, a, b); - if ( answer != lst() ) { + hensel_univar(tocheck.top().poly, p, part.left, part.right, f1, f2); + if ( !f1.empty() ) { if ( part.size_first() == 1 ) { if ( part.size_second() == 1 ) { - result *= answer.op(0) * answer.op(1); + result *= upoly_to_ex(f1, x) * upoly_to_ex(f2, x); tocheck.pop(); break; } - result *= answer.op(0); - tocheck.top().poly = answer.op(1); + result *= upoly_to_ex(f1, x); + tocheck.top().poly = f2; 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] = umodpoly_to_ex(q[j-1], x); + mdarg[1] = umodpoly_to_ex(a[j-1], x); + vector empty; + vector exsigma = multivar_diophant(mdarg, x, umodpoly_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 smod; + umodpoly tmod; + exteuclid(amod, bmod, smod, tmod); + + 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) ) { + return factor(cont) * factor(pp); + } + + /* 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 { + ex vnfactors = factor(vn); + 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); + } + + ufac = factor(u); + 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 0 ) { int ld = poly.ldegree(x); @@ -1155,16 +2222,53 @@ static ex factor_sqrfree(const ex& poly) } } - // multivariate case not yet implemented! - throw runtime_error("multivariate case not yet implemented!"); + // 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) ) { + return factor(e, options); + } + if ( is_a(e) ) { + ex s1, s2; + for ( size_t i=0; i(sfpoly) ) { + // case: multiple factors ex res = 1; for ( size_t i=0; i(sfpoly) ) { + return poly; + } // case: (polynomial) ex f = factor_sqrfree(sfpoly); return f; } } // namespace GiNaC + +#ifdef DEBUGFACTOR +#include "test.h" +#endif