+ exset::const_iterator s;
+ const ex& x = *syms.begin();
+
+ /* make polynomial primitive */
+ ex p = poly.expand().collect(x);
+ ex cont = p.lcoeff(x);
+ for ( numeric i=p.degree(x)-1; i>=p.ldegree(x); --i ) {
+ cont = gcd(cont, p.coeff(x,ex_to<numeric>(i).to_int()));
+ if ( cont == 1 ) break;
+ }
+ ex pp = expand(normal(p / cont));
+ if ( !is_a<numeric>(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<numeric>(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<numeric> a(syms.size()-1, 0);
+ vector<numeric> 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<a.size(); ++i ) {
+ u = u.subs(*s == a[i]);
+ ++s;
+ }
+ delta = u.content(x);
+
+ // determine proper prime
+ prime = 3;
+ cl_modint_ring R = find_modint_ring(prime);
+ while ( true ) {
+ if ( irem(ex_to<numeric>(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<tryu.size()-1; ++i ) {
+ for ( size_t j=i+1; j<tryu.size(); ++j ) {
+ umodpoly tryg;
+ gcd(tryu[i], tryu[j], tryg);
+ if ( unequal_one(tryg) ) {
+ veto = true;
+ goto escape_quickly;
+ }
+ }
+ }
+ escape_quickly: ;
+ if ( veto ) {
+ continue;
+ }
+
+ if ( factor_count <= 1 ) {
+ return poly;
+ }
+
+ if ( minimalr < 0 ) {
+ minimalr = factor_count;
+ }
+ else if ( minimalr == factor_count ) {
+ ++trialcount;
+ ++modulus;
+ }
+ else if ( minimalr > factor_count ) {
+ minimalr = factor_count;
+ trialcount = 0;
+ }
+ if ( minimalr <= 1 ) {
+ return poly;
+ }
+ }
+
+ vector<numeric> ftilde((vnlst.nops()-1)/2+1);
+ ftilde[0] = ex_to<numeric>(vnlst.op(0));
+ for ( size_t i=1; i<ftilde.size(); ++i ) {
+ ex ft = vnlst.op((i-1)*2+1);
+ s = syms.begin();
+ ++s;
+ for ( size_t j=0; j<a.size(); ++j ) {
+ ft = ft.subs(*s == a[j]);
+ ++s;
+ }
+ ftilde[i] = ex_to<numeric>(ft);
+ }
+
+ vector<bool> used_flag((vnlst.nops()-1)/2+1, false);
+ vector<ex> D(factor_count, 1);
+ for ( size_t i=0; i<=factor_count; ++i ) {
+ numeric prefac;
+ if ( i == 0 ) {
+ prefac = ex_to<numeric>(ufaclst.op(0));
+ ftilde[0] = ftilde[0] / prefac;
+ vnlst.let_op(0) = vnlst.op(0) / prefac;
+ continue;
+ }
+ else {
+ prefac = ex_to<numeric>(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<used_flag.size(); ++i ) {
+ if ( !used_flag[i] ) {
+ some_factor_unused = true;
+ break;
+ }
+ }
+ if ( some_factor_unused ) {
+ continue;
+ }
+
+ vector<ex> C(factor_count);
+ if ( delta == 1 ) {
+ for ( size_t i=0; i<D.size(); ++i ) {
+ ex Dtilde = D[i];
+ s = syms.begin();
+ ++s;
+ for ( size_t j=0; j<a.size(); ++j ) {
+ Dtilde = Dtilde.subs(*s == a[j]);
+ ++s;
+ }
+ C[i] = D[i] * (ufaclst.op(2*i+1).lcoeff(x) / Dtilde);
+ }
+ }
+ else {
+ for ( size_t i=0; i<D.size(); ++i ) {
+ ex Dtilde = D[i];
+ s = syms.begin();
+ ++s;
+ for ( size_t j=0; j<a.size(); ++j ) {
+ Dtilde = Dtilde.subs(*s == a[j]);
+ ++s;
+ }
+ ex ui;
+ if ( i == 0 ) {
+ ui = ufaclst.op(0);
+ }
+ else {
+ ui = ufaclst.op(2*(i-1)+1);
+ }
+ while ( true ) {
+ ex d = gcd(ui.lcoeff(x), Dtilde);
+ C[i] = D[i] * ( ui.lcoeff(x) / d );
+ ui = ui * ( Dtilde[i] / d );
+ delta = delta / ( Dtilde[i] / d );
+ if ( delta == 1 ) break;
+ ui = delta * ui;
+ C[i] = delta * C[i];
+ pp = pp * pow(delta, D.size()-1);
+ }
+ }
+ }
+
+ EvalPoint ep;
+ vector<EvalPoint> epv;
+ s = syms.begin();
+ ++s;
+ for ( size_t i=0; i<a.size(); ++i ) {
+ ep.x = *s++;
+ ep.evalpoint = a[i].to_int();
+ epv.push_back(ep);
+ }
+
+ // calc bound B
+ ex maxcoeff;
+ for ( int i=u.degree(x); i>=u.ldegree(x); --i ) {
+ maxcoeff += pow(abs(u.coeff(x, i)),2);
+ }
+ cl_I normmc = ceiling1(the<cl_R>(cln::sqrt(ex_to<numeric>(maxcoeff).to_cl_N())));
+ unsigned int maxdegree = 0;
+ for ( size_t i=0; i<factor_count; ++i ) {
+ if ( ufaclst[2*i+1].degree(x) > (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<res.nops(); ++i ) {
+ result *= res.op(i).content(x) * res.op(i).unit(x);
+ result *= res.op(i).primpart(x);
+ }
+ return result;
+ }
+ }