#include #include #include #include #include #include NTL_START_IMPL static void IterSqr(GF2E& c, const GF2E& a, long n) { GF2E res; long i; res = a; for (i = 0; i < n; i++) sqr(res, res); c = res; } void SquareFreeDecomp(vec_pair_GF2EX_long& u, const GF2EX& ff) { GF2EX f = ff; if (!IsOne(LeadCoeff(f))) Error("SquareFreeDecomp: bad args"); GF2EX r, t, v, tmp1; long m, j, finished, done; u.SetLength(0); if (deg(f) == 0) return; m = 1; finished = 0; do { j = 1; diff(tmp1, f); GCD(r, f, tmp1); div(t, f, r); if (deg(t) > 0) { done = 0; do { GCD(v, r, t); div(tmp1, t, v); if (deg(tmp1) > 0) append(u, cons(tmp1, j*m)); if (deg(v) > 0) { div(r, r, v); t = v; j++; } else done = 1; } while (!done); if (deg(r) == 0) finished = 1; } if (!finished) { /* r is a square */ long k, d; d = deg(r)/2; f.rep.SetLength(d+1); for (k = 0; k <= d; k++) IterSqr(f.rep[k], r.rep[k*2], GF2E::degree()-1); m = m*2; } } while (!finished); } static void NullSpace(long& r, vec_long& D, vec_GF2XVec& M, long verbose) { long k, l, n; long i, j; long pos; GF2X t1, t2; GF2X *x, *y; const GF2XModulus& p = GF2E::modulus(); n = M.length(); D.SetLength(n); for (j = 0; j < n; j++) D[j] = -1; r = 0; l = 0; for (k = 0; k < n; k++) { if (verbose && k % 10 == 0) cerr << "+"; pos = -1; for (i = l; i < n; i++) { rem(t1, M[i][k], p); M[i][k] = t1; if (pos == -1 && !IsZero(t1)) pos = i; } if (pos != -1) { swap(M[pos], M[l]); // make M[l, k] == -1 mod p, and make row l reduced InvMod(t1, M[l][k], p); for (j = k+1; j < n; j++) { rem(t2, M[l][j], p); MulMod(M[l][j], t2, t1, p); } for (i = l+1; i < n; i++) { // M[i] = M[i] + M[l]*M[i,k] t1 = M[i][k]; // this is already reduced x = M[i].elts() + (k+1); y = M[l].elts() + (k+1); for (j = k+1; j < n; j++, x++, y++) { // *x = *x + (*y)*t1 mul(t2, *y, t1); add(*x, *x, t2); } } D[k] = l; // variable k is defined by row l l++; } else { r++; } } } static void BuildMatrix(vec_GF2XVec& M, long n, const GF2EX& g, const GF2EXModulus& F, long verbose) { long i, j, m; GF2EX h; M.SetLength(n); for (i = 0; i < n; i++) M[i].SetSize(n, 2*GF2E::WordLength()); set(h); for (j = 0; j < n; j++) { if (verbose && j % 10 == 0) cerr << "+"; m = deg(h); for (i = 0; i < n; i++) { if (i <= m) M[i][j] = rep(h.rep[i]); else clear(M[i][j]); } if (j < n-1) MulMod(h, h, g, F); } for (i = 0; i < n; i++) add(M[i][i], M[i][i], 1); } static void TraceMap(GF2EX& h, const GF2EX& a, const GF2EXModulus& F) // one could consider making a version based on modular composition, // as in ComposeFrobeniusMap... { GF2EX res, tmp; res = a; tmp = a; long i; for (i = 0; i < GF2E::degree()-1; i++) { SqrMod(tmp, tmp, F); add(res, res, tmp); } h = res; } void PlainFrobeniusMap(GF2EX& h, const GF2EXModulus& F) { GF2EX res; SetX(res); long i; for (i = 0; i < GF2E::degree(); i++) SqrMod(res, res, F); h = res; } 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 ComposeFrobeniusMap(GF2EX& y, const GF2EXModulus& F) { long d = GF2E::degree(); long n = deg(F); long i; i = 1; while (i <= d) i = i << 1; i = i >> 1; GF2EX z(INIT_SIZE, n), z1(INIT_SIZE, n); i = i >> 1; long m = 1; if (n == 2) { SetX(z); SqrMod(z, z, F); } 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; } clear(z); SetCoeff(z, 1L << m); } while (i) { z1 = z; long j, k, dz; dz = deg(z); for (j = 0; j <= dz; j++) for (k = 0; k < m; k++) sqr(z1.rep[j], z1.rep[j]); CompMod(z, z1, z, F); m = 2*m; if (d & i) { SqrMod(z, z, F); m++; } i = i >> 1; } y = z; } void FrobeniusMap(GF2EX& h, const GF2EXModulus& F) { long n = deg(F); long d = GF2E::degree(); if (n == 1) { h = ConstTerm(F); return; } if (UseComposeFrobenius(d, n)) ComposeFrobeniusMap(h, F); else PlainFrobeniusMap(h, F); } static void RecFindRoots(vec_GF2E& x, const GF2EX& f) { if (deg(f) == 0) return; if (deg(f) == 1) { long k = x.length(); x.SetLength(k+1); x[k] = ConstTerm(f); return; } GF2EX h; GF2E r; { GF2EXModulus F; build(F, f); do { random(r); clear(h); SetCoeff(h, 1, r); TraceMap(h, h, F); GCD(h, h, f); } while (deg(h) <= 0 || deg(h) == deg(f)); } RecFindRoots(x, h); div(h, f, h); RecFindRoots(x, h); } void FindRoots(vec_GF2E& x, const GF2EX& ff) { GF2EX f = ff; if (!IsOne(LeadCoeff(f))) Error("FindRoots: bad args"); x.SetMaxLength(deg(f)); x.SetLength(0); RecFindRoots(x, f); } static void RandomBasisElt(GF2EX& g, const vec_long& D, const vec_GF2XVec& M) { static GF2X t1, t2; long n = D.length(); long i, j, s; g.rep.SetLength(n); vec_GF2E& v = g.rep; for (j = n-1; j >= 0; j--) { if (D[j] == -1) random(v[j]); else { i = D[j]; // v[j] = sum_{s=j+1}^{n-1} v[s]*M[i,s] clear(t1); for (s = j+1; s < n; s++) { mul(t2, rep(v[s]), M[i][s]); add(t1, t1, t2); } conv(v[j], t1); } } g.normalize(); } static void split(GF2EX& f1, GF2EX& g1, GF2EX& f2, GF2EX& g2, const GF2EX& f, const GF2EX& g, const vec_GF2E& roots, long lo, long mid) { long r = mid-lo+1; GF2EXModulus F; build(F, f); vec_GF2E lroots(INIT_SIZE, r); long i; for (i = 0; i < r; i++) lroots[i] = roots[lo+i]; GF2EX h, a, d; BuildFromRoots(h, lroots); CompMod(a, h, g, F); GCD(f1, a, f); div(f2, f, f1); rem(g1, g, f1); rem(g2, g, f2); } static void RecFindFactors(vec_GF2EX& factors, const GF2EX& f, const GF2EX& g, const vec_GF2E& roots, long lo, long hi) { long r = hi-lo+1; if (r == 0) return; if (r == 1) { append(factors, f); return; } GF2EX f1, g1, f2, g2; long mid = (lo+hi)/2; split(f1, g1, f2, g2, f, g, roots, lo, mid); RecFindFactors(factors, f1, g1, roots, lo, mid); RecFindFactors(factors, f2, g2, roots, mid+1, hi); } static void FindFactors(vec_GF2EX& factors, const GF2EX& f, const GF2EX& g, const vec_GF2E& roots) { long r = roots.length(); factors.SetMaxLength(r); factors.SetLength(0); RecFindFactors(factors, f, g, roots, 0, r-1); } #if 0 static void IterFindFactors(vec_GF2EX& factors, const GF2EX& f, const GF2EX& g, const vec_GF2E& roots) { long r = roots.length(); long i; GF2EX h; factors.SetLength(r); for (i = 0; i < r; i++) { add(h, g, roots[i]); GCD(factors[i], f, h); } } #endif void SFBerlekamp(vec_GF2EX& factors, const GF2EX& ff, long verbose) { GF2EX f = ff; if (!IsOne(LeadCoeff(f))) Error("SFBerlekamp: bad args"); if (deg(f) == 0) { factors.SetLength(0); return; } if (deg(f) == 1) { factors.SetLength(1); factors[0] = f; return; } double t; long n = deg(f); GF2EXModulus F; build(F, f); GF2EX g, h; if (verbose) { cerr << "computing X^p..."; t = GetTime(); } FrobeniusMap(g, F); if (verbose) { cerr << (GetTime()-t) << "\n"; } vec_long D; long r; vec_GF2XVec M; if (verbose) { cerr << "building matrix..."; t = GetTime(); } BuildMatrix(M, n, g, F, verbose); if (verbose) { cerr << (GetTime()-t) << "\n"; } if (verbose) { cerr << "diagonalizing..."; t = GetTime(); } NullSpace(r, D, M, verbose); if (verbose) { cerr << (GetTime()-t) << "\n"; } if (verbose) cerr << "number of factors = " << r << "\n"; if (r == 1) { factors.SetLength(1); factors[0] = f; return; } if (verbose) { cerr << "factor extraction..."; t = GetTime(); } vec_GF2E roots; RandomBasisElt(g, D, M); MinPolyMod(h, g, F, r); if (deg(h) == r) M.kill(); FindRoots(roots, h); FindFactors(factors, f, g, roots); GF2EX g1; vec_GF2EX S, S1; long i; while (factors.length() < r) { if (verbose) cerr << "+"; RandomBasisElt(g, D, M); S.kill(); for (i = 0; i < factors.length(); i++) { const GF2EX& f = factors[i]; if (deg(f) == 1) { append(S, f); continue; } build(F, f); rem(g1, g, F); if (deg(g1) <= 0) { append(S, f); continue; } MinPolyMod(h, g1, F, min(deg(f), r-factors.length()+1)); FindRoots(roots, h); S1.kill(); FindFactors(S1, f, g1, roots); append(S, S1); } swap(factors, S); } if (verbose) { cerr << (GetTime()-t) << "\n"; } if (verbose) { cerr << "degrees:"; long i; for (i = 0; i < factors.length(); i++) cerr << " " << deg(factors[i]); cerr << "\n"; } } void berlekamp(vec_pair_GF2EX_long& factors, const GF2EX& f, long verbose) { double t; vec_pair_GF2EX_long sfd; vec_GF2EX x; if (!IsOne(LeadCoeff(f))) Error("berlekamp: bad args"); if (verbose) { cerr << "square-free decomposition..."; t = GetTime(); } SquareFreeDecomp(sfd, f); if (verbose) cerr << (GetTime()-t) << "\n"; factors.SetLength(0); long i, j; for (i = 0; i < sfd.length(); i++) { if (verbose) { cerr << "factoring multiplicity " << sfd[i].b << ", deg = " << deg(sfd[i].a) << "\n"; } SFBerlekamp(x, sfd[i].a, verbose); for (j = 0; j < x.length(); j++) append(factors, cons(x[j], sfd[i].b)); } } static void AddFactor(vec_pair_GF2EX_long& factors, const GF2EX& g, long d, long verbose) { if (verbose) cerr << "degree=" << d << ", number=" << deg(g)/d << "\n"; append(factors, cons(g, d)); } static void ProcessTable(GF2EX& f, vec_pair_GF2EX_long& factors, const GF2EXModulus& F, long limit, const vec_GF2EX& tbl, long d, long verbose) { if (limit == 0) return; if (verbose) cerr << "+"; GF2EX t1; if (limit == 1) { GCD(t1, f, tbl[0]); if (deg(t1) > 0) { AddFactor(factors, t1, d, verbose); div(f, f, t1); } return; } long i; t1 = tbl[0]; for (i = 1; i < limit; i++) MulMod(t1, t1, tbl[i], F); GCD(t1, f, t1); if (deg(t1) == 0) return; div(f, f, t1); GF2EX t2; i = 0; d = d - limit + 1; while (2*d <= deg(t1)) { GCD(t2, tbl[i], t1); if (deg(t2) > 0) { AddFactor(factors, t2, d, verbose); div(t1, t1, t2); } i++; d++; } if (deg(t1) > 0) AddFactor(factors, t1, deg(t1), verbose); } void TraceMap(GF2EX& w, const GF2EX& a, long d, const GF2EXModulus& F, const GF2EX& b) { if (d < 0) Error("TraceMap: bad args"); GF2EX y, z, t; z = b; y = a; clear(w); while (d) { if (d == 1) { if (IsZero(w)) w = y; else { CompMod(w, w, z, F); add(w, w, y); } } else if ((d & 1) == 0) { Comp2Mod(z, t, z, y, z, F); add(y, t, y); } else if (IsZero(w)) { w = y; Comp2Mod(z, t, z, y, z, F); add(y, t, y); } else { Comp3Mod(z, t, w, z, y, w, z, F); add(w, w, y); add(y, t, y); } d = d >> 1; } } void PowerCompose(GF2EX& y, const GF2EX& h, long q, const GF2EXModulus& F) { if (q < 0) Error("powerCompose: bad args"); GF2EX z(INIT_SIZE, F.n); long sw; z = h; SetX(y); while (q) { sw = 0; if (q > 1) sw = 2; if (q & 1) { if (IsX(y)) y = z; else sw = sw | 1; } switch (sw) { case 0: break; case 1: CompMod(y, y, z, F); break; case 2: CompMod(z, z, z, F); break; case 3: Comp2Mod(y, z, y, z, z, F); break; } q = q >> 1; } } long ProbIrredTest(const GF2EX& f, long iter) { long n = deg(f); if (n <= 0) return 0; if (n == 1) return 1; GF2EXModulus F; build(F, f); GF2EX b, r, s; FrobeniusMap(b, F); long all_zero = 1; long i; for (i = 0; i < iter; i++) { random(r, n); TraceMap(s, r, n, F, b); all_zero = all_zero && IsZero(s); if (deg(s) > 0) return 0; } if (!all_zero || (n & 1)) return 1; PowerCompose(s, b, n/2, F); return !IsX(s); } long GF2EX_BlockingFactor = 10; void DDF(vec_pair_GF2EX_long& factors, const GF2EX& ff, const GF2EX& hh, long verbose) { GF2EX f = ff; GF2EX 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 = GF2EX_BlockingFactor; GF2EXModulus F; build(F, f); GF2EXArgument H; build(H, h, F, min(CompTableSize, deg(f))); long i, d, limit, old_n; GF2EX g, X; vec_GF2EX tbl(INIT_SIZE, GCDTableSize); SetX(X); i = 0; g = h; d = 1; limit = GCDTableSize; while (2*d <= deg(f)) { old_n = deg(f); add(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); } void RootEDF(vec_GF2EX& factors, const GF2EX& f, long verbose) { vec_GF2E roots; double t; if (verbose) { cerr << "finding roots..."; t = GetTime(); } FindRoots(roots, f); if (verbose) { cerr << (GetTime()-t) << "\n"; } long r = roots.length(); factors.SetLength(r); for (long j = 0; j < r; j++) { SetX(factors[j]); add(factors[j], factors[j], roots[j]); } } static void EDFSplit(vec_GF2EX& v, const GF2EX& f, const GF2EX& b, long d) { GF2EX a, g, h; GF2EXModulus F; vec_GF2E roots; build(F, f); long n = F.n; long r = n/d; random(a, n); TraceMap(g, a, d, F, b); MinPolyMod(h, g, F, r); FindRoots(roots, h); FindFactors(v, f, g, roots); } static void RecEDF(vec_GF2EX& factors, const GF2EX& f, const GF2EX& b, long d, long verbose) { vec_GF2EX v; long i; GF2EX bb; if (verbose) cerr << "+"; EDFSplit(v, f, b, d); for (i = 0; i < v.length(); i++) { if (deg(v[i]) == d) { append(factors, v[i]); } else { GF2EX bb; rem(bb, b, v[i]); RecEDF(factors, v[i], bb, d, verbose); } } } void EDF(vec_GF2EX& factors, const GF2EX& ff, const GF2EX& bb, long d, long verbose) { GF2EX f = ff; GF2EX b = bb; if (!IsOne(LeadCoeff(f))) Error("EDF: bad args"); long n = deg(f); long r = n/d; if (r == 0) { factors.SetLength(0); return; } if (r == 1) { factors.SetLength(1); factors[0] = f; return; } if (d == 1) { RootEDF(factors, f, verbose); return; } double t; if (verbose) { cerr << "computing EDF(" << d << "," << r << ")..."; t = GetTime(); } factors.SetLength(0); RecEDF(factors, f, b, d, verbose); if (verbose) cerr << (GetTime()-t) << "\n"; } void SFCanZass(vec_GF2EX& factors, const GF2EX& ff, long verbose) { GF2EX f = ff; if (!IsOne(LeadCoeff(f))) Error("SFCanZass: bad args"); if (deg(f) == 0) { factors.SetLength(0); return; } if (deg(f) == 1) { factors.SetLength(1); factors[0] = f; return; } factors.SetLength(0); double t; GF2EXModulus F; build(F, f); GF2EX h; if (verbose) { cerr << "computing X^p..."; t = GetTime(); } FrobeniusMap(h, F); if (verbose) { cerr << (GetTime()-t) << "\n"; } vec_pair_GF2EX_long u; if (verbose) { cerr << "computing DDF..."; t = GetTime(); } NewDDF(u, f, h, verbose); if (verbose) { t = GetTime()-t; cerr << "DDF time: " << t << "\n"; } GF2EX hh; vec_GF2EX v; long i; for (i = 0; i < u.length(); i++) { const GF2EX& g = u[i].a; long d = u[i].b; long r = deg(g)/d; if (r == 1) { // g is already irreducible append(factors, g); } else { // must perform EDF if (d == 1) { // root finding RootEDF(v, g, verbose); append(factors, v); } else { // general case rem(hh, h, g); EDF(v, g, hh, d, verbose); append(factors, v); } } } } void CanZass(vec_pair_GF2EX_long& factors, const GF2EX& f, long verbose) { if (!IsOne(LeadCoeff(f))) Error("CanZass: bad args"); double t; vec_pair_GF2EX_long sfd; vec_GF2EX x; if (verbose) { cerr << "square-free decomposition..."; t = GetTime(); } SquareFreeDecomp(sfd, f); if (verbose) cerr << (GetTime()-t) << "\n"; factors.SetLength(0); long i, j; for (i = 0; i < sfd.length(); i++) { if (verbose) { cerr << "factoring multiplicity " << sfd[i].b << ", deg = " << deg(sfd[i].a) << "\n"; } SFCanZass(x, sfd[i].a, verbose); for (j = 0; j < x.length(); j++) append(factors, cons(x[j], sfd[i].b)); } } void mul(GF2EX& f, const vec_pair_GF2EX_long& v) { long i, j, n; n = 0; for (i = 0; i < v.length(); i++) n += v[i].b*deg(v[i].a); GF2EX g(INIT_SIZE, n+1); set(g); for (i = 0; i < v.length(); i++) for (j = 0; j < v[i].b; j++) { mul(g, g, v[i].a); } f = g; } static long BaseCase(const GF2EX& h, long q, long a, const GF2EXModulus& F) { long b, e; GF2EX lh(INIT_SIZE, F.n); lh = h; b = 1; e = 0; while (e < a-1 && !IsX(lh)) { e++; b *= q; PowerCompose(lh, lh, q, F); } if (!IsX(lh)) b *= q; return b; } static void TandemPowerCompose(GF2EX& y1, GF2EX& y2, const GF2EX& h, long q1, long q2, const GF2EXModulus& F) { GF2EX z(INIT_SIZE, F.n); long sw; z = h; SetX(y1); SetX(y2); while (q1 || q2) { sw = 0; if (q1 > 1 || q2 > 1) sw = 4; if (q1 & 1) { if (IsX(y1)) y1 = z; else sw = sw | 2; } if (q2 & 1) { if (IsX(y2)) y2 = z; else sw = sw | 1; } switch (sw) { case 0: break; case 1: CompMod(y2, y2, z, F); break; case 2: CompMod(y1, y1, z, F); break; case 3: Comp2Mod(y1, y2, y1, y2, z, F); break; case 4: CompMod(z, z, z, F); break; case 5: Comp2Mod(z, y2, z, y2, z, F); break; case 6: Comp2Mod(z, y1, z, y1, z, F); break; case 7: Comp3Mod(z, y1, y2, z, y1, y2, z, F); break; } q1 = q1 >> 1; q2 = q2 >> 1; } } static long RecComputeDegree(long u, const GF2EX& h, const GF2EXModulus& F, FacVec& fvec) { if (IsX(h)) return 1; if (fvec[u].link == -1) return BaseCase(h, fvec[u].q, fvec[u].a, F); GF2EX h1, h2; long q1, q2, r1, r2; q1 = fvec[fvec[u].link].val; q2 = fvec[fvec[u].link+1].val; TandemPowerCompose(h1, h2, h, q1, q2, F); r1 = RecComputeDegree(fvec[u].link, h2, F, fvec); r2 = RecComputeDegree(fvec[u].link+1, h1, F, fvec); return r1*r2; } long RecComputeDegree(const GF2EX& h, const GF2EXModulus& F) // f = F.f is assumed to be an "equal degree" polynomial // h = X^p mod f // the common degree of the irreducible factors of f is computed { if (F.n == 1 || IsX(h)) return 1; FacVec fvec; FactorInt(fvec, F.n); return RecComputeDegree(fvec.length()-1, h, F, fvec); } void FindRoot(GF2E& root, const GF2EX& ff) // finds a root of ff. // assumes that ff is monic and splits into distinct linear factors { GF2EXModulus F; GF2EX h, h1, f; GF2E r; f = ff; if (!IsOne(LeadCoeff(f))) Error("FindRoot: bad args"); if (deg(f) == 0) Error("FindRoot: bad args"); while (deg(f) > 1) { build(F, f); random(r); clear(h); SetCoeff(h, 1, r); TraceMap(h, h, F); GCD(h, h, f); if (deg(h) > 0 && deg(h) < deg(f)) { if (deg(h) > deg(f)/2) div(f, f, h); else f = h; } } root = ConstTerm(f); } static long power(long a, long e) { long i, res; res = 1; for (i = 1; i <= e; i++) res = res * a; return res; } static long IrredBaseCase(const GF2EX& h, long q, long a, const GF2EXModulus& F) { long e; GF2EX X, s, d; e = power(q, a-1); PowerCompose(s, h, e, F); SetX(X); add(s, s, X); GCD(d, F.f, s); return IsOne(d); } static long RecIrredTest(long u, const GF2EX& h, const GF2EXModulus& F, const FacVec& fvec) { long q1, q2; GF2EX h1, h2; if (IsX(h)) return 0; if (fvec[u].link == -1) { return IrredBaseCase(h, fvec[u].q, fvec[u].a, F); } q1 = fvec[fvec[u].link].val; q2 = fvec[fvec[u].link+1].val; TandemPowerCompose(h1, h2, h, q1, q2, F); return RecIrredTest(fvec[u].link, h2, F, fvec) && RecIrredTest(fvec[u].link+1, h1, F, fvec); } long DetIrredTest(const GF2EX& f) { if (deg(f) <= 0) return 0; if (deg(f) == 1) return 1; GF2EXModulus F; build(F, f); GF2EX h; FrobeniusMap(h, F); GF2EX s; PowerCompose(s, h, F.n, F); if (!IsX(s)) return 0; FacVec fvec; FactorInt(fvec, F.n); return RecIrredTest(fvec.length()-1, h, F, fvec); } long IterIrredTest(const GF2EX& f) { if (deg(f) <= 0) return 0; if (deg(f) == 1) return 1; GF2EXModulus F; build(F, f); GF2EX h; FrobeniusMap(h, F); long CompTableSize = 2*SqrRoot(deg(f)); GF2EXArgument H; build(H, h, F, CompTableSize); long i, d, limit, limit_sqr; GF2EX 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)) { add(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; } static void MulByXPlusY(vec_GF2EX& h, const GF2EX& f, const GF2EX& g) // h represents the bivariate polynomial h[0] + h[1]*Y + ... + h[n-1]*Y^k, // where the h[i]'s are polynomials in X, each of degree < deg(f), // and k < deg(g). // h is replaced by the bivariate polynomial h*(X+Y) (mod f(X), g(Y)). { long n = deg(g); long k = h.length()-1; if (k < 0) return; if (k < n-1) { h.SetLength(k+2); h[k+1] = h[k]; for (long i = k; i >= 1; i--) { MulByXMod(h[i], h[i], f); add(h[i], h[i], h[i-1]); } MulByXMod(h[0], h[0], f); } else { GF2EX b, t; b = h[n-1]; for (long i = n-1; i >= 1; i--) { mul(t, b, g.rep[i]); MulByXMod(h[i], h[i], f); add(h[i], h[i], h[i-1]); add(h[i], h[i], t); } mul(t, b, g.rep[0]); MulByXMod(h[0], h[0], f); add(h[0], h[0], t); } // normalize k = h.length()-1; while (k >= 0 && IsZero(h[k])) k--; h.SetLength(k+1); } static void IrredCombine(GF2EX& x, const GF2EX& f, const GF2EX& g) { if (deg(f) < deg(g)) { IrredCombine(x, g, f); return; } // deg(f) >= deg(g)...not necessary, but maybe a little more // time & space efficient long df = deg(f); long dg = deg(g); long m = df*dg; vec_GF2EX h(INIT_SIZE, dg); long i; for (i = 0; i < dg; i++) h[i].SetMaxLength(df); h.SetLength(1); set(h[0]); vec_GF2E a; a.SetLength(2*m); for (i = 0; i < 2*m; i++) { a[i] = ConstTerm(h[0]); if (i < 2*m-1) MulByXPlusY(h, f, g); } MinPolySeq(x, a, m); } static void BuildPrimePowerIrred(GF2EX& f, long q, long e) { long n = power(q, e); do { random(f, n); SetCoeff(f, n); } while (!IterIrredTest(f)); } static void RecBuildIrred(GF2EX& f, long u, const FacVec& fvec) { if (fvec[u].link == -1) BuildPrimePowerIrred(f, fvec[u].q, fvec[u].a); else { GF2EX g, h; RecBuildIrred(g, fvec[u].link, fvec); RecBuildIrred(h, fvec[u].link+1, fvec); IrredCombine(f, g, h); } } void BuildIrred(GF2EX& f, long n) { if (n <= 0) Error("BuildIrred: n must be positive"); if (NTL_OVERFLOW(n, 1, 0)) Error("overflow in BuildIrred"); if (n == 1) { SetX(f); return; } FacVec fvec; FactorInt(fvec, n); RecBuildIrred(f, fvec.length()-1, fvec); } #if 0 void BuildIrred(GF2EX& f, long n) { if (n <= 0) Error("BuildIrred: n must be positive"); if (NTL_OVERFLOW(n, 1, 0)) Error("overflow in BuildIrred"); if (n == 1) { SetX(f); return; } GF2EX g; do { random(g, n); SetCoeff(g, n); } while (!IterIrredTest(g)); f = g; } #endif void BuildRandomIrred(GF2EX& f, const GF2EX& g) { GF2EXModulus G; GF2EX h, ff; build(G, g); do { random(h, deg(g)); IrredPolyMod(ff, h, G); } while (deg(ff) < deg(g)); f = ff; } /************* NEW DDF ****************/ long GF2EX_GCDTableSize = 4; char GF2EX_stem[256] = ""; double GF2EXFileThresh = 256; static vec_GF2EX BabyStepFile; static vec_GF2EX GiantStepFile; static long use_files; static double CalcTableSize(long n, long k) { double sz = GF2E::storage(); sz = sz * n; sz = sz + NTL_VECTOR_HEADER_SIZE + sizeof(vec_GF2E); sz = sz * k; sz = sz/1024; return sz; } 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; } static void GenerateGiantSteps(const GF2EX& f, const GF2EX& h, long l, long verbose) { double t; if (verbose) { cerr << "generating giant 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); GF2EX h1; h1 = h; long i; long HexOutput = GF2X::HexOutput; GF2X::HexOutput = 1; if (!use_files) { GiantStepFile.kill(); GiantStepFile.SetLength(l); } for (i = 1; i <= l-1; i++) { if (use_files) { ofstream s; OpenWrite(s, FileName(GF2EX_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(GF2EX_stem, "giant", i)); s << h1 << "\n"; s.close(); } else GiantStepFile(i) = h1; if (verbose) cerr << (GetTime()-t) << "\n"; GF2X::HexOutput = HexOutput; } static void FileCleanup(long k, long l) { if (use_files) { long i; for (i = 1; i <= k-1; i++) remove(FileName(GF2EX_stem, "baby", i)); for (i = 1; i <= l; i++) remove(FileName(GF2EX_stem, "giant", i)); } else { BabyStepFile.kill(); GiantStepFile.kill(); } } static void NewAddFactor(vec_pair_GF2EX_long& u, const GF2EX& g, long m, long verbose) { long len = u.length(); u.SetLength(len+1); u[len].a = g; u[len].b = m; if (verbose) { cerr << "split " << m << " " << deg(g) << "\n"; } } static void NewProcessTable(vec_pair_GF2EX_long& u, GF2EX& f, const GF2EXModulus& F, vec_GF2EX& buf, long size, long StartInterval, long IntervalLength, long verbose) { if (size == 0) return; GF2EX& g = buf[size-1]; long i; for (i = 0; i < size-1; i++) MulMod(g, g, buf[i], F); GCD(g, f, g); if (deg(g) == 0) return; div(f, f, g); long d = (StartInterval-1)*IntervalLength + 1; i = 0; long interval = StartInterval; while (i < size-1 && 2*d <= deg(g)) { GCD(buf[i], buf[i], g); if (deg(buf[i]) > 0) { NewAddFactor(u, buf[i], interval, verbose); div(g, g, buf[i]); } i++; interval++; d += IntervalLength; } if (deg(g) > 0) { if (i == size-1) NewAddFactor(u, g, interval, verbose); else NewAddFactor(u, g, (deg(g)+IntervalLength-1)/IntervalLength, verbose); } } static void FetchGiantStep(GF2EX& g, long gs, const GF2EXModulus& F) { if (use_files) { ifstream s; OpenRead(s, FileName(GF2EX_stem, "giant", gs)); s >> g; s.close(); } else g = GiantStepFile(gs); rem(g, g, F); } static void FetchBabySteps(vec_GF2EX& v, long k) { v.SetLength(k); SetX(v[0]); long i; for (i = 1; i <= k-1; i++) { if (use_files) { ifstream s; OpenRead(s, FileName(GF2EX_stem, "baby", i)); s >> v[i]; s.close(); } else v[i] = BabyStepFile(i); } } static void GiantRefine(vec_pair_GF2EX_long& u, const GF2EX& ff, long k, long l, long verbose) { double t; if (verbose) { cerr << "giant refine..."; t = GetTime(); } u.SetLength(0); vec_GF2EX BabyStep; FetchBabySteps(BabyStep, k); vec_GF2EX buf(INIT_SIZE, GF2EX_GCDTableSize); GF2EX f; f = ff; GF2EXModulus F; build(F, f); GF2EX g; GF2EX h; long size = 0; long first_gs; long d = 1; while (2*d <= deg(f)) { long old_n = deg(f); long gs = (d+k-1)/k; long bs = gs*k - d; if (bs == k-1) { size++; if (size == 1) first_gs = gs; FetchGiantStep(g, gs, F); add(buf[size-1], g, BabyStep[bs]); } else { add(h, g, BabyStep[bs]); MulMod(buf[size-1], buf[size-1], h, F); } if (verbose && bs == 0) cerr << "+"; if (size == GF2EX_GCDTableSize && bs == 0) { NewProcessTable(u, f, F, buf, size, first_gs, k, verbose); if (verbose) cerr << "*"; size = 0; } d++; if (2*d <= deg(f) && deg(f) < old_n) { build(F, f); long i; for (i = 1; i <= k-1; i++) rem(BabyStep[i], BabyStep[i], F); } } if (size > 0) { NewProcessTable(u, f, F, buf, size, first_gs, k, verbose); if (verbose) cerr << "*"; } if (deg(f) > 0) NewAddFactor(u, f, 0, verbose); if (verbose) { t = GetTime()-t; cerr << "giant refine time: " << t << "\n"; } } static void IntervalRefine(vec_pair_GF2EX_long& factors, const GF2EX& ff, long k, long gs, const vec_GF2EX& BabyStep, long verbose) { vec_GF2EX buf(INIT_SIZE, GF2EX_GCDTableSize); GF2EX f; f = ff; GF2EXModulus F; build(F, f); GF2EX g; FetchGiantStep(g, gs, F); long size = 0; long first_d; long d = (gs-1)*k + 1; long bs = k-1; while (bs >= 0 && 2*d <= deg(f)) { long old_n = deg(f); if (size == 0) first_d = d; rem(buf[size], BabyStep[bs], F); add(buf[size], buf[size], g); size++; if (size == GF2EX_GCDTableSize) { NewProcessTable(factors, f, F, buf, size, first_d, 1, verbose); size = 0; } d++; bs--; if (bs >= 0 && 2*d <= deg(f) && deg(f) < old_n) { build(F, f); rem(g, g, F); } } NewProcessTable(factors, f, F, buf, size, first_d, 1, verbose); if (deg(f) > 0) NewAddFactor(factors, f, deg(f), verbose); } static void BabyRefine(vec_pair_GF2EX_long& factors, const vec_pair_GF2EX_long& u, long k, long l, long verbose) { double t; if (verbose) { cerr << "baby refine..."; t = GetTime(); } factors.SetLength(0); vec_GF2EX BabyStep; long i; for (i = 0; i < u.length(); i++) { const GF2EX& g = u[i].a; long gs = u[i].b; if (gs == 0 || 2*((gs-1)*k+1) > deg(g)) NewAddFactor(factors, g, deg(g), verbose); else { if (BabyStep.length() == 0) FetchBabySteps(BabyStep, k); IntervalRefine(factors, g, k, gs, BabyStep, verbose); } } if (verbose) { t = GetTime()-t; cerr << "baby refine time: " << t << "\n"; } } void NewDDF(vec_pair_GF2EX_long& factors, const GF2EX& f, const GF2EX& h, long verbose) { if (!IsOne(LeadCoeff(f))) Error("NewDDF: bad args"); if (deg(f) == 0) { factors.SetLength(0); return; } if (deg(f) == 1) { factors.SetLength(0); append(factors, cons(f, 1)); return; } if (!GF2EX_stem[0]) sprintf(GF2EX_stem, "ddf-%ld", RandomBnd(10000)); long B = deg(f)/2; long k = SqrRoot(B); long l = (B+k-1)/k; GF2EX h1; if (CalcTableSize(deg(f), k + l - 1) > GF2EXFileThresh) use_files = 1; else use_files = 0; GenerateBabySteps(h1, f, h, k, verbose); GenerateGiantSteps(f, h1, l, verbose); vec_pair_GF2EX_long u; GiantRefine(u, f, k, l, verbose); BabyRefine(factors, u, k, l, verbose); FileCleanup(k, l); } long IterComputeDegree(const GF2EX& h, const GF2EXModulus& F) { long n = deg(F); if (n == 1 || IsX(h)) return 1; long B = n/2; long k = SqrRoot(B); long l = (B+k-1)/k; GF2EXArgument H; #if 0 double n2 = sqrt(double(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); GF2EX h1; h1 = h; vec_GF2EX baby; baby.SetLength(k); SetX(baby[0]); long i; for (i = 1; i <= k-1; i++) { baby[i] = h1; CompMod(h1, h1, H, F); if (IsX(h1)) return i+1; } build(H, h1, F, sz); long j; for (j = 2; j <= l; j++) { CompMod(h1, h1, H, F); for (i = k-1; i >= 0; i--) { if (h1 == baby[i]) return j*k-i; } } return n; } NTL_END_IMPL