// factor n into a*b using Pollard Rho method // pre-condition: n>1 inline void PollardRho(ZZ& a, ZZ& b, const ZZ& n, const ZZ& bnd=ZZ::zero()) { ZZ_pBak bak; bak.save(); ZZ_p::init(n); ZZ d; ZZ_p x1; random(x1); ZZ_p x2(x1); ZZ end(IsZero(bnd)?5*SqrRoot(SqrRoot(n)):2*SqrRoot(bnd)); for (; !IsZero(end); --end) { x1 = x1*x1 + 1; x2 = x2*x2 + 1; x2 = x2*x2 + 1; GCD(d,n,rep(x2-x1)); if ((d>1)&&(d<n)) { a=d; b=n/d; return; } } // failure a=1; b=n; }
void main() { int cnt = 0; set<pair<int, int>> S; for (long b = 5; b <= 640; b += 5) { for (long t = 1; t <= 11328; t += 1) { // if ((59 * b / 5 + t) % 5 != 0) continue; for (long h = 41; h <= 3776; h += 41) { long A = 6 * ((59 * b + t * 5) * 41 + 450 * h); long B = (b + t + h) * 41 * 25; long g = GCD(A, B); B /= g, A /= g; if ((59. / 41) * (59. / 41) * B / A > 1.) continue; if (is_sqr(A) && is_sqr(B)) { A = SqrRoot(A), B = SqrRoot(B); if (b * 59 * B % (A * 5) != 0) continue; if (t % A != 0) continue; if (h * 90 * B % (A * 41) != 0) continue; pair<int, int> s = {B, A}; if (!S.count(s)) { S.insert(s); ++cnt; print("#{}: b = {}, t = {}, h = {}, m = {}/{}, ans = {}\n", cnt, b, t, h, B * 59, A * 41, B * 59. / (A * 41.)); } } } } } if (S.count({25, 36})) print("good!\n"); }
void SqrRoot(RR& z, const RR& a) { if (sign(a) < 0) ArithmeticError("RR: attempt to take square root of negative number"); if (IsZero(a)) { clear(z); return; } RR t; ZZ T1, T2; long k; k = 2*RR::prec - NumBits(a.x) + 1; if (k < 0) k = 0; if ((a.e - k) & 1) k++; LeftShift(T1, a.x, k); // since k >= 2*prec - bits(a) + 1, T1 has at least 2*prec+1 bits, // thus T1 >= 2^(2*prec) SqrRoot(t.x, T1); // t.x >= 2^prec thus t.x contains the round bit t.e = (a.e - k)/2; sqr(T2, t.x); // T1-T2 is the (lower part of the) sticky bit normalize(z, t, T2 < T1); }
void Comp3Mod(zz_pX& x1, zz_pX& x2, zz_pX& x3, const zz_pX& g1, const zz_pX& g2, const zz_pX& g3, const zz_pX& h, const zz_pXModulus& F) { long m = SqrRoot(g1.rep.length() + g2.rep.length() + g3.rep.length()); if (m == 0) { clear(x1); clear(x2); clear(x3); return; } zz_pXArgument A; build(A, h, F, m); zz_pX xx1, xx2, xx3; CompMod(xx1, g1, A, F); CompMod(xx2, g2, A, F); CompMod(xx3, g3, A, F); x1 = xx1; x2 = xx2; x3 = xx3; }
bool ProvePrime(const ZZ& _n) { ZZ n(_n); if (n<0) abs(n,n); if (n<=1) return 0; if (n<=1000000) { // n is small so use trial division to check primality long ln = to_long(n); long end = to_long(SqrRoot(n)); PrimeSeq s; for (long p=s.next(); p<=end; p=s.next()) if ((ln%p)==0) return 0; return 1; } // check small primes PrimeSeq s; for (long p=s.next(); p<1000; p=s.next()) if (divide(n,p)) return 0; // obviously, something is missing here! return ProbPrime(n); }
NTL_START_IMPL static long CharPolyBound(const mat_ZZ& a) // This bound is computed via interpolation // through complex roots of unity. { long n = a.NumRows(); long i; ZZ res, t1, t2; set(res); for (i = 0; i < n; i++) { InnerProduct(t1, a[i], a[i]); abs(t2, a[i][i]); mul(t2, t2, 2); add(t2, t2, 1); add(t1, t1, t2); if (t1 > 1) { SqrRoot(t1, t1); add(t1, t1, 1); } mul(res, res, t1); } return NumBits(res); }
static void GenerateBabySteps(GF2EX& h1, const GF2EX& f, const GF2EX& h, long k, long verbose) { double t; if (verbose) { cerr << "generating baby steps..."; t = GetTime(); } GF2EXModulus F; build(F, f); GF2EXArgument H; #if 0 double n2 = sqrt(double(F.n)); double n4 = sqrt(n2); double n34 = n2*n4; long sz = long(ceil(n34/sqrt(sqrt(2.0)))); #else long sz = 2*SqrRoot(F.n); #endif build(H, h, F, sz); h1 = h; long i; long HexOutput = GF2X::HexOutput; GF2X::HexOutput = 1; if (!use_files) { BabyStepFile.kill(); BabyStepFile.SetLength(k-1); } for (i = 1; i <= k-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName(GF2EX_stem, "baby", i)); s << h1 << "\n"; s.close(); } else BabyStepFile(i) = h1; CompMod(h1, h1, H, F); if (verbose) cerr << "+"; } if (verbose) cerr << (GetTime()-t) << "\n"; GF2X::HexOutput = HexOutput; }
long UseComposeFrobenius(long d, long n) { long i; i = 1; while (i <= d) i = i << 1; i = i >> 1; i = i >> 1; long m = 1; long dz; if (n == 2) { dz = 1; } else { while (i) { long m1 = 2*m; if (i & d) m1++; if (m1 >= NTL_BITS_PER_LONG-1 || (1L << m1) >= n) break; m = m1; i = i >> 1; } dz = 1L << m; } long rootn = SqrRoot(n); long cnt = 0; if (i) { cnt += SqrRoot(dz+1); i = i >> 1; } while (i) { cnt += rootn; i = i >> 1; } return 4*cnt <= d; }
void SqrRootPrec(RR& x, const RR& a, long p) { if (p < 1 || NTL_OVERFLOW(p, 1, 0)) Error("SqrRootPrec: bad precsion"); long old_p = RR::prec; RR::prec = p; SqrRoot(x, a); RR::prec = old_p; }
// generate only matrices of the form s(X^{g^i})->s(X), but not all of them. // For a generator g whose order is larger than bound, generate only enough // matrices for the giant-step/baby-step procedures (2*sqrt(ord(g))of them). void addSome1DMatrices(FHESecKey& sKey, long bound, long keyID) { const FHEcontext &context = sKey.getContext(); long m = context.zMStar.getM(); // key-switching matrices for the automorphisms for (long i = 0; i < (long)context.zMStar.numOfGens(); i++) { // For generators of small order, add all the powers if (bound >= (long)context.zMStar.OrderOf(i)) for (long j = 1; j < (long)context.zMStar.OrderOf(i); j++) { long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j // From s(X^val) to s(X) sKey.GenKeySWmatrix(1, val, keyID, keyID); if (!context.zMStar.SameOrd(i)) // also from s(X^{1/val}) to s(X) sKey.GenKeySWmatrix(1, InvMod(val,m), keyID, keyID); } else { // For generators of large order, add only some of the powers long num = SqrRoot(context.zMStar.OrderOf(i)); // floor(ord^{1/2}) if (num*num < (long) context.zMStar.OrderOf(i)) num++; // ceil(ord^{1/2}) // VJS: the above two lines replaces the following inexact calculation // with an exact calculation // long num = ceil(sqrt((double)context.zMStar.OrderOf(i))); for (long j=1; j <= num; j++) { // Add matrices for g^j and g^{j*num} long val1 = PowerMod(context.zMStar.ZmStarGen(i), j, m); // g^j long val2 = PowerMod(context.zMStar.ZmStarGen(i),num*j,m);// g^{j*num} if (j < num) { sKey.GenKeySWmatrix(1, val1, keyID, keyID); sKey.GenKeySWmatrix(1, val2, keyID, keyID); } if (!context.zMStar.SameOrd(i)) { // sKey.GenKeySWmatrix(1, InvMod(val1,m), keyID, keyID); sKey.GenKeySWmatrix(1, InvMod(val2,m), keyID, keyID); } } // VJS: experimantal feature...because the replication code // uses rotations by -1, -2, -4, -8, we add a few // of these as well...only the small ones are important, // and we only need them if SameOrd(i)... // Note: we do indeed get a nontrivial speed-up if (context.zMStar.SameOrd(i)) { for (long k = 1; k <= num; k = 2*k) { long j = context.zMStar.OrderOf(i) - k; long val = PowerMod(context.zMStar.ZmStarGen(i), j, m); // val = g^j sKey.GenKeySWmatrix(1, val, keyID, keyID); } } } } sKey.setKeySwitchMap(); // re-compute the key-switching map }
void SqrRootPrec(RR& x, const RR& a, long p) { if (p < 1) LogicError("SqrRootPrec: bad precsion"); if (NTL_OVERFLOW(p, 1, 0)) ResourceError("SqrRootPrec: bad precsion"); RRPush push; RR::prec = p; SqrRoot(x, a); }
NTL_CLIENT #include "FHE.h" long KSGiantStepSize(long D) { //OLD: assert(D > 0); helib::assertTrue<helib::InvalidArgument>(D > 0l, "Step size must be positive"); long g = SqrRoot(D); if (g*g < D) g++; // g = ceiling(sqrt(D)) return g; }
static void GenerateBabySteps(ZZ_pEX& h1, const ZZ_pEX& f, const ZZ_pEX& h, long k, FileList& flist, long verbose) { double t; if (verbose) { cerr << "generating baby steps..."; t = GetTime(); } ZZ_pEXModulus F; build(F, f); ZZ_pEXArgument H; #if 0 double n2 = sqrt(double(F.n)); double n4 = sqrt(n2); double n34 = n2*n4; long sz = long(ceil(n34/sqrt(sqrt(2.0)))); #else long sz = 2*SqrRoot(F.n); #endif build(H, h, F, sz); h1 = h; long i; if (!use_files) { (*BabyStepFile).SetLength(k-1); } for (i = 1; i <= k-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName("baby", i), flist); s << h1 << "\n"; CloseWrite(s); } else (*BabyStepFile)(i) = h1; CompMod(h1, h1, H, F); if (verbose) cerr << "+"; } if (verbose) cerr << (GetTime()-t) << "\n"; }
static void GenerateGiantSteps(const ZZ_pX& f, const ZZ_pX& h, long l, long verbose) { double t; if (verbose) { cerr << "generating giant steps..."; t = GetTime(); } ZZ_pXModulus F; build(F, f); ZZ_pXArgument H; build(H, h, F, 2*SqrRoot(F.n)); ZZ_pX h1; h1 = h; long i; if (!use_files) { GiantStepFile.kill(); GiantStepFile.SetLength(l); } for (i = 1; i <= l-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName(ZZ_pX_stem, "giant", i)); s << h1 << "\n"; s.close(); } else GiantStepFile(i) = h1; CompMod(h1, h1, H, F); if (verbose) cerr << "+"; } if (use_files) { ofstream s; OpenWrite(s, FileName(ZZ_pX_stem, "giant", i)); s << h1 << "\n"; s.close(); } else GiantStepFile(i) = h1; if (verbose) cerr << (GetTime()-t) << "\n"; }
static void hadamard(ZZ& num_bound, ZZ& den_bound, const mat_ZZ& A, const vec_ZZ& b) { long n = A.NumRows(); if (n == 0) Error("internal error: hadamard with n = 0"); ZZ b_len, min_A_len, prod, t1; InnerProduct(min_A_len, A[0], A[0]); prod = min_A_len; long i; for (i = 1; i < n; i++) { InnerProduct(t1, A[i], A[i]); if (t1 < min_A_len) min_A_len = t1; mul(prod, prod, t1); } if (min_A_len == 0) { num_bound = 0; den_bound = 0; return; } InnerProduct(b_len, b, b); div(t1, prod, min_A_len); mul(t1, t1, b_len); SqrRoot(num_bound, t1); SqrRoot(den_bound, prod); }
void CompMod(zz_pX& x, const zz_pX& g, const zz_pX& h, const zz_pXModulus& F) // x = g(h) mod f { long m = SqrRoot(g.rep.length()); if (m == 0) { clear(x); return; } zz_pXArgument A; build(A, h, F, m); CompMod(x, g, A, F); }
void ProjectPowers(vec_zz_p& x, const vec_zz_p& a, long k, const zz_pX& h, const zz_pXModulus& F) { if (a.length() > F.n || k < 0) Error("ProjectPowers: bad args"); if (k == 0) { x.SetLength(0); return; } long m = SqrRoot(k); zz_pXArgument H; build(H, h, F, m); ProjectPowers(x, a, k, H, F); }
static long DetBound(const mat_ZZ& a) { long n = a.NumRows(); long i; ZZ res, t1; set(res); for (i = 0; i < n; i++) { InnerProduct(t1, a[i], a[i]); if (t1 > 1) { SqrRoot(t1, t1); add(t1, t1, 1); } mul(res, res, t1); } return NumBits(res); }
static void GenerateBabySteps(ZZ_pX& h1, const ZZ_pX& f, const ZZ_pX& h, long k, FileList& flist, long verbose) { double t; if (verbose) { cerr << "generating baby steps..."; t = GetTime(); } ZZ_pXModulus F; build(F, f); ZZ_pXArgument H; build(H, h, F, 2*SqrRoot(F.n)); h1 = h; long i; if (!use_files) { (*BabyStepFile).SetLength(k-1); } for (i = 1; i <= k-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName("baby", i), flist); s << h1 << "\n"; CloseWrite(s); } else (*BabyStepFile)(i) = h1; CompMod(h1, h1, H, F); if (verbose) cerr << "+"; } if (verbose) cerr << (GetTime()-t) << "\n"; }
// ECM primitive void ECM(ZZ& q, const ZZ& n, const ZZ& _bnd, double failure_prob, bool verbose) { ZZ bnd; SqrRoot(bnd,n); if (_bnd>0 && _bnd<bnd) bnd=_bnd; long B1,B2,D; double prob; ECM_parameters(B1,B2,prob,D,NumBits(bnd),NumBits(n)); if (verbose) std::cout<<"ECM: bnd="<<NumBits(bnd)<<"bits B1="<<B1<<" B2="<<B2 <<" D="<<D<<" prob="<<prob<<std::endl; if (failure_prob<=0) { // run "forever" if (verbose) std::cout<<"ECM: expected ncurves="<<(1/prob)<<std::endl; ECM(q,n,NTL_MAX_LONG,B1,B2,D,verbose); } else if (failure_prob>=1) { // run on one curve ECM(q,n,1,B1,B2,D,verbose); } else { // run just the right number of times // failure_prob = (1-prob)^ncurves; double ncurves_d; if (prob<0.1) ncurves_d = to_double(log(failure_prob)/log1p(to_RR(-prob))); else ncurves_d = log(failure_prob)/log(1-prob); long ncurves = ncurves_d<=1 ? 1 : (ncurves_d>=NTL_MAX_LONG ? NTL_MAX_LONG : (long)ncurves_d); if (verbose) std::cout<<"ECM: good_prob="<<prob<<" failure_prob="<<failure_prob <<" ncurves="<<ncurves<<std::endl; ECM(q,n,ncurves,B1,B2,D,verbose); } }
void Comp2Mod(ZZ_pX& x1, ZZ_pX& x2, const ZZ_pX& g1, const ZZ_pX& g2, const ZZ_pX& h, const ZZ_pXModulus& F) { long m = SqrRoot(g1.rep.length() + g2.rep.length()); if (m == 0) { clear(x1); clear(x2); return; } ZZ_pXArgument A; build(A, h, F, m); ZZ_pX xx1, xx2; CompMod(xx1, g1, A, F); CompMod(xx2, g2, A, F); x1 = xx1; x2 = xx2; }
long IsFFTPrime(long n, long& w) { long m, x, y, z; long j, k; if (n <= 1 || n >= NTL_SP_BOUND) return 0; if (n % 2 == 0) return 0; if (n % 3 == 0) return 0; if (n % 5 == 0) return 0; if (n % 7 == 0) return 0; m = n - 1; k = 0; while ((m & 1) == 0) { m = m >> 1; k++; } for (;;) { x = RandomBnd(n); if (x == 0) continue; z = PowerMod(x, m, n); if (z == 1) continue; x = z; j = 0; do { y = z; z = MulMod(y, y, n); j++; } while (j != k && z != 1); if (z != 1 || y != n-1) return 0; if (j == k) break; } /* x^{2^k} = 1 mod n, x^{2^{k-1}} = -1 mod n */ long TrialBound; TrialBound = m >> k; if (TrialBound > 0) { if (!ProbPrime(n, 5)) return 0; /* we have to do trial division by special numbers */ TrialBound = SqrRoot(TrialBound); long a, b; for (a = 1; a <= TrialBound; a++) { b = (a << k) + 1; if (n % b == 0) return 0; } } /* n is an FFT prime */ for (j = NTL_FFTMaxRoot; j < k; j++) x = MulMod(x, x, n); w = x; return 1; }
void main() { R = SqrRoot(m); int cnt = 0; for (long i = 1; m / i > R; ++i) basis[++cnt] = m / i; for (int i = R; i; --i) basis[++cnt] = i; // for (int i = 1; i <= cnt; ++i) { // if (get_index(basis[i]) != i) // print("!!! {} {}\n", get_index(basis[i]), i); // } for (int i = 1; i <= cnt; ++i) { num_primes[i] = basis[i] - 1; } // for (int i = 1; i < cnt; ++i) { // for (int v = basis[i + 1] + 1; v <= basis[i]; ++v) // if (v > R && ProbPrime(v)) // f[basis[i]] += 2; // } // f[1] = 1; // for (int i = cnt - 1; i; --i) { // f[basis[i]] += f[basis[i + 1]]; // // print("init {} = {}\n", basis[i], f[basis[i]]); // } // long ans = 0; // for (int i = 1; i <= m; ++i) { // ans += m / i; // } // print("bf = {}\n", ans); for (long x = 2; x <= R; ++x) { if (ProbPrime(x)) { print("current = {}\n", x); for (int i = 1; i <= cnt; ++i) { long y = basis[i]; if (x * x > y) break; auto a = num_primes[get_index(y / x)]; auto b = num_primes[get_index(x - 1)]; num_primes[i] -= a - b; } } } for (int i = 1; i <= cnt; ++i) { num_primes[i] %= MOD; // print("{} {}\n", basis[i], num_primes[i]); } for (int i = 1; i <= cnt; ++i) { f[i] = 1; // if (basis[i] >= R) { // f[i] += 2 * (num_primes[get_index(basis[i])] - num_primes[R]); // } // print("init {} {}\n", basis[i], f[i]); } for (long p = R; p; --p) { if (ProbPrime(p)) { print("current = {}\n", p); int t = p > n ? 0 : get_power(p, n); for (int e = 0; e <= 60; ++e) g[e] = (e + 1) * (t + 1) + t * (t + 1) / 2; for (int i = 1; i <= cnt; ++i) { long y = basis[i]; if (t == 0 && p * p > y) break; long v = 0; // 2 * (num_primes[get_index(y)] - num_primes[p]); for (int e = 0; y; ++e) { v += real_f(y, p) * g[e] % MOD; y /= p; } if (basis[i] >= p) v -= 2 * (num_primes[i] - num_primes[get_index(p)] + 1) % MOD; f[i] = (v + MOD) % MOD; // print("update {} = {}\n", basis[i], v); } } } print("ans = {}\n", real_f(m, 1)); }
static void GenerateGiantSteps(const ZZ_pEX& f, const ZZ_pEX& h, long l, long verbose) { double t; if (verbose) { cerr << "generating giant steps..."; t = GetTime(); } ZZ_pEXModulus F; build(F, f); ZZ_pEXArgument H; #if 0 double n2 = sqrt(double(F.n)); double n4 = sqrt(n2); double n34 = n2*n4; long sz = long(ceil(n34/sqrt(sqrt(2.0)))); #else long sz = 2*SqrRoot(F.n); #endif build(H, h, F, sz); ZZ_pEX h1; h1 = h; long i; if (!use_files) { GiantStepFile.kill(); GiantStepFile.SetLength(l); } for (i = 1; i <= l-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName(ZZ_pEX_stem, "giant", i)); s << h1 << "\n"; s.close(); } else GiantStepFile(i) = h1; CompMod(h1, h1, H, F); if (verbose) cerr << "+"; } if (use_files) { ofstream s; OpenWrite(s, FileName(ZZ_pEX_stem, "giant", i)); s << h1 << "\n"; s.close(); } else GiantStepFile(i) = h1; if (verbose) cerr << (GetTime()-t) << "\n"; }
// general purpose factoring method void factor(vec_pair_ZZ_long& factors, const ZZ& _n, const ZZ& bnd, double failure_prob, bool verbose) { ZZ n(_n); if (n<=1) { abs(n,n); if (n<=1) { factors.SetLength(0); return; } } // upper bound on size of smallest prime factor ZZ upper_bound; SqrRoot(upper_bound,n); if (bnd>0 && bnd<upper_bound) upper_bound=bnd; // figure out appropriate lower_bound for trial division long B1,B2,D; double prob; ECM_parameters(B1,B2,prob,D,NumBits(upper_bound),NumBits(n)); ZZ lower_bound; conv(lower_bound,max(B2,1<<14)); if (lower_bound>upper_bound) lower_bound=upper_bound; // start factoring with trial division TrialDivision(factors,n,n,to_long(lower_bound)); if (IsOne(n)) return; if (upper_bound<=lower_bound || ProbPrime_notd(n)) { addFactor(factors,n); return; } /* n is composite and smallest prime factor is assumed to be such that * lower_bound < factor <= upper_bound * * Ramp-up to searching for factors of size upper_bound. This is a good * idea in cases where we have no idea what size factors N might have, * but we don't want to spend too much time doing this. */ for(lower_bound<<=4; lower_bound<upper_bound; lower_bound<<=4) { ZZ q; ECM(q,n,lower_bound,1,verbose); // one curve only if (!IsOne(q)) { div(n,n,q); if (n<q) swap(n,q); // q is small factor, n is large factor if (ProbPrime_notd(q)) addFactor(factors,q); else factor_r(factors,q,bnd,failure_prob,verbose); if (ProbPrime_notd(n)) { addFactor(factors,n); return; } // new upper_bound SqrRoot(upper_bound,n); if (bnd>0 && bnd<upper_bound) upper_bound=bnd; } } // search for factors of size bnd factor_r(factors,n,bnd,failure_prob,verbose); }
void DDF(vec_pair_ZZ_pX_long& factors, const ZZ_pX& ff, const ZZ_pX& hh, long verbose) { ZZ_pX f = ff; ZZ_pX h = hh; if (!IsOne(LeadCoeff(f))) Error("DDF: bad args"); factors.SetLength(0); if (deg(f) == 0) return; if (deg(f) == 1) { AddFactor(factors, f, 1, verbose); return; } long CompTableSize = 2*SqrRoot(deg(f)); long GCDTableSize = ZZ_pX_BlockingFactor; ZZ_pXModulus F; build(F, f); ZZ_pXArgument H; build(H, h, F, min(CompTableSize, deg(f))); long i, d, limit, old_n; ZZ_pX g, X; vec_ZZ_pX tbl(INIT_SIZE, GCDTableSize); SetX(X); i = 0; g = h; d = 1; limit = GCDTableSize; while (2*d <= deg(f)) { old_n = deg(f); sub(tbl[i], g, X); i++; if (i == limit) { ProcessTable(f, factors, F, i, tbl, d, verbose); i = 0; } d = d + 1; if (2*d <= deg(f)) { // we need to go further if (deg(f) < old_n) { // f has changed build(F, f); rem(h, h, f); rem(g, g, f); build(H, h, F, min(CompTableSize, deg(f))); } CompMod(g, g, H, F); } } ProcessTable(f, factors, F, i, tbl, d-1, verbose); if (!IsOne(f)) AddFactor(factors, f, deg(f), verbose); }
bool is_sqr(int x) { int t = SqrRoot(x); return x == t * t; }
long IterIrredTest(const ZZ_pEX& f) { if (deg(f) <= 0) return 0; if (deg(f) == 1) return 1; ZZ_pEXModulus F; build(F, f); ZZ_pEX h; FrobeniusMap(h, F); long CompTableSize = 2*SqrRoot(deg(f)); ZZ_pEXArgument H; build(H, h, F, CompTableSize); long i, d, limit, limit_sqr; ZZ_pEX g, X, t, prod; SetX(X); i = 0; g = h; d = 1; limit = 2; limit_sqr = limit*limit; set(prod); while (2*d <= deg(f)) { sub(t, g, X); MulMod(prod, prod, t, F); i++; if (i == limit_sqr) { GCD(t, f, prod); if (!IsOne(t)) return 0; set(prod); limit++; limit_sqr = limit*limit; i = 0; } d = d + 1; if (2*d <= deg(f)) { CompMod(g, g, H, F); } } if (i > 0) { GCD(t, f, prod); if (!IsOne(t)) return 0; } return 1; }