void TruncToZZ(ZZ& z, const RR& a) { if (a.e >= 0) LeftShift(z, a.x, a.e); else RightShift(z, a.x, -a.e); }
// This procedure assumes that k*(2^e +1) > deg(poly) > k*(2^e -1), // and that babyStep contains >= k + (deg(poly) mod k) powers static void degPowerOfTwo(Ctxt& ret, const ZZX& poly, long k, DynamicCtxtPowers& babyStep, DynamicCtxtPowers& giantStep) { if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval simplePolyEval(ret, poly, babyStep); return; } long n = deg(poly)/k; // We assume n=2^e or n=2^e -1 n = 1L << NextPowerOfTwo(n); // round up to n=2^e ZZX r = trunc(poly, (n-1)*k); // degree <= k(2^e-1)-1 ZZX q = RightShift(poly, (n-1)*k); // 0 < degree < 2k SetCoeff(r, (n-1)*k); // monic, degree == k(2^e-1) q -= 1; PatersonStockmeyer(ret, r, k, n/2, 0, babyStep, giantStep); Ctxt tmp(ret.getPubKey(), ret.getPtxtSpace()); simplePolyEval(tmp, q, babyStep); // evaluate q // multiply by X^{k(n-1)} with minimum depth for (long i=1; i<n; i*=2) { tmp.multiplyBy(giantStep.getPower(i)); } ret += tmp; }
static void normalize1(RR& z, const ZZ& y_x, long y_e, long prec, long residual) { long len = NumBits(y_x); if (len > prec) { long correction = ZZ_RoundCorrection(y_x, len - prec, residual); RightShift(z.x, y_x, len - prec); if (correction) add(z.x, z.x, correction); z.e = y_e + len - prec; } else if (len == 0) { clear(z.x); z.e = 0; } else { z.x = y_x; z.e = y_e; } if (!IsOdd(z.x)) z.e += MakeOdd(z.x); if (z.e >= NTL_OVFBND) ResourceError("RR: overflow"); if (z.e <= -NTL_OVFBND) ResourceError("RR: underflow"); }
void LeftShift(zz_pX& x, const zz_pX& a, long n) { if (IsZero(a)) { clear(x); return; } if (n < 0) { if (n < -NTL_MAX_LONG) clear(x); else RightShift(x, a, -n); return; } if (NTL_OVERFLOW(n, 1, 0)) Error("overflow in LeftShift"); long m = a.rep.length(); x.rep.SetLength(m+n); long i; for (i = m-1; i >= 0; i--) x.rep[i+n] = a.rep[i]; for (i = 0; i < n; i++) clear(x.rep[i]); }
long CRT(vec_ZZ& gg, ZZ& a, const vec_zz_p& G) { long n = gg.length(); if (G.length() != n) Error("CRT: vector length mismatch"); long p = zz_p::modulus(); ZZ new_a; mul(new_a, a, p); long a_inv; a_inv = rem(a, p); a_inv = InvMod(a_inv, p); long p1; p1 = p >> 1; ZZ a1; RightShift(a1, a, 1); long p_odd = (p & 1); long modified = 0; long h; ZZ g; long i; for (i = 0; i < n; i++) { if (!CRTInRange(gg[i], a)) { modified = 1; rem(g, gg[i], a); if (g > a1) sub(g, g, a); } else g = gg[i]; h = rem(g, p); h = SubMod(rep(G[i]), h, p); h = MulMod(h, a_inv, p); if (h > p1) h = h - p; if (h != 0) { modified = 1; if (!p_odd && g > 0 && (h == p1)) MulSubFrom(g, a, h); else MulAddTo(g, a, h); } gg[i] = g; } a = new_a; return modified; }
void CeilToZZ(ZZ& z, const RR& a) { if (a.e >= 0) LeftShift(z, a.x, a.e); else { long sgn = sign(a.x); RightShift(z, a.x, -a.e); if (sgn > 0) add(z, z, 1); } }
void conv(ZZ& z, const RR& a) { if (a.e >= 0) LeftShift(z, a.x, a.e); else { long sgn = sign(a.x); RightShift(z, a.x, -a.e); if (sgn < 0) sub(z, z, 1); } }
void trunc(RR& z, const RR& a) { NTL_TLS_LOCAL(RR, t); if (a.e >= 0) xcopy(z, a); else { RightShift(t.x, a.x, -a.e); t.e = 0; xcopy(z, t); } }
void trunc(RR& z, const RR& a) { static RR t; if (a.e >= 0) xcopy(z, a); else { RightShift(t.x, a.x, -a.e); t.e = 0; xcopy(z, t); } }
void ceil(RR& z, const RR& a) { static RR t; if (a.e >= 0) xcopy(z, a); else { RightShift(t.x, a.x, -a.e); if (sign(a.x) > 0) add(t.x, t.x, 1); t.e = 0; xcopy(z, t); } }
void ceil(RR& z, const RR& a) { NTL_TLS_LOCAL(RR, t); if (a.e >= 0) xcopy(z, a); else { RightShift(t.x, a.x, -a.e); if (sign(a.x) > 0) add(t.x, t.x, 1); t.e = 0; xcopy(z, t); } }
static void recursivePolyEval(Ctxt& ret, const ZZX& poly, long k, DynamicCtxtPowers& babyStep, DynamicCtxtPowers& giantStep) { if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval simplePolyEval(ret, poly, babyStep); return; } long delta = deg(poly) % k; // deg(poly) mod k long n = divc(deg(poly),k); // ceil( deg(poly)/k ) long t = 1L<<(NextPowerOfTwo(n)); // t >= n, so t*k >= deg(poly) // Special case for deg(poly) = k * 2^e +delta if (n==t) { degPowerOfTwo(ret, poly, k, babyStep, giantStep); return; } // When deg(poly) = k*(2^e -1) we use the Paterson-Stockmeyer recursion if (n == t-1 && delta==0) { PatersonStockmeyer(ret, poly, k, t/2, delta, babyStep, giantStep); return; } t = t/2; // In any other case we have kt < deg(poly) < k(2t-1). We then set // u = deg(poly) - k*(t-1) and poly = q*X^u + r with deg(r)<u // and recurse on poly = (q-1)*X^u + (X^u+r) long u = deg(poly) - k*(t-1); ZZX r = trunc(poly, u); // degree <= u-1 ZZX q = RightShift(poly, u); // degree == k*(t-1) q -= 1; SetCoeff(r, u); // degree == u PatersonStockmeyer(ret, q, k, t/2, 0, babyStep, giantStep); Ctxt tmp = giantStep.getPower(u/k); if (delta!=0) { // if u is not divisible by k then compute it tmp.multiplyBy(babyStep.getPower(delta)); } ret.multiplyBy(tmp); recursivePolyEval(tmp, r, k, babyStep, giantStep); ret += tmp; }
NTL_START_IMPL // This implements a variation of an algorithm in // [P. Domich, R. Kannan and L. Trotter, Math. Oper. Research 12:50-59, 1987]. // I started with the description in Henri Cohen's book, but had to modify // that because Cohen does not actually keep the numbers reduced modulo // the determinant, which leads to larger than necessary numbers. // This modifiaction was put in place in v3.9b. static void EuclUpdate(vec_ZZ& u, vec_ZZ& v, const ZZ& a, const ZZ& b, const ZZ& c, const ZZ& d, const ZZ& M) { long m = u.length(); long i; ZZ M1; RightShift(M1, M, 1); ZZ t1, t2, t3; for (i = 1; i <= m; i++) { mul(t1, u(i), a); mul(t2, v(i), b); add(t1, t1, t2); rem(t1, t1, M); if (t1 > M1) sub(t1, t1, M); t3 = t1; mul(t1, u(i), c); mul(t2, v(i), d); add(t1, t1, t2); rem(t1, t1, M); if (t1 > M1) sub(t1, t1, M); u(i) = t3; v(i) = t1; } }
// This procedure assumes that k*(2^e +1) > deg(poly) > k*(2^e -1), // and that babyStep contains k+ (deg(poly) mod k) powers static long degPowerOfTwo(const ZZX& poly, long k, DynamicPtxtPowers& babyStep, DynamicPtxtPowers& giantStep, long mod, long& recursiveDepth) { if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval long ret = simplePolyEval(poly, babyStep, mod); recursiveDepth = babyStep.getDepth(deg(poly)); return ret; } long subDepth1 =0, subDepth2=0; long n = deg(poly)/k; // We assume n=2^e or n=2^e -1 n = 1L << NextPowerOfTwo(n); // round up to n=2^e ZZX r = trunc(poly, (n-1)*k); // degree <= k(2^e-1)-1 ZZX q = RightShift(poly, (n-1)*k); // 0 < degree < 2k SetCoeff(r, (n-1)*k); // monic, degree == k(2^e-1) q -= 1; if (verbose) cerr << ", recursing on "<<r<<" + X^"<<(n-1)*k<<"*"<<q<<endl; long ret = PatersonStockmeyer(r, k, n/2, 0, babyStep, giantStep, mod, subDepth2); if (verbose) cerr << " PatersonStockmeyer("<<r<<") returns "<<ret << ", depth="<<subDepth2<<endl; long tmp = simplePolyEval(q, babyStep, mod); // evaluate q subDepth1 = babyStep.getDepth(deg(q)); if (verbose) cerr << " simplePolyEval("<<q<<") returns "<<tmp << ", depth="<<subDepth1<<endl; // multiply by X^{k(n-1)} with minimum depth for (long i=1; i<n; i*=2) { tmp = MulMod(tmp, giantStep.getPower(i), mod); nMults++; subDepth1 = max(subDepth1, giantStep.getDepth(i)) +1; if (verbose) cerr << " after mult by giantStep.getPower("<<i<< ")=" << giantStep.getPower(i)<<" of depth="<< giantStep.getDepth(i) << ", ret="<<tmp<<" and depth is "<<subDepth1<<endl; } totalDepth = max(subDepth1, subDepth2); return AddMod(ret, tmp, mod); // return q * X^{k(n-1)} + r }
// The recursive procedure in the Paterson-Stockmeyer // polynomial-evaluation algorithm from SIAM J. on Computing, 1973. // This procedure assumes that poly is monic, deg(poly)=k*(2t-1)+delta // with t=2^e, and that babyStep contains >= k+delta powers static void PatersonStockmeyer(Ctxt& ret, const ZZX& poly, long k, long t, long delta, DynamicCtxtPowers& babyStep, DynamicCtxtPowers& giantStep) { if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval simplePolyEval(ret, poly, babyStep); return; } ZZX r = trunc(poly, k*t); // degree <= k*2^e-1 ZZX q = RightShift(poly, k*t); // degree == k(2^e-1) +delta const ZZ p = to_ZZ(babyStep[0].getPtxtSpace()); const ZZ& coef = coeff(r,deg(q)); SetCoeff(r, deg(q), coef-1); // r' = r - X^{deg(q)} ZZX c,s; DivRem(c,s,r,q); // r' = c*q + s // deg(s)<deg(q), and if c!= 0 then deg(c)<k-delta assert(deg(s)<deg(q)); assert(IsZero(c) || deg(c)<k-delta); SetCoeff(s,deg(q)); // s' = s + X^{deg(q)}, deg(s)==deg(q) // reduce the coefficients modulo p for (long i=0; i<=deg(c); i++) rem(c[i],c[i], p); c.normalize(); for (long i=0; i<=deg(s); i++) rem(s[i],s[i], p); s.normalize(); // Evaluate recursively poly = (c+X^{kt})*q + s' PatersonStockmeyer(ret, q, k, t/2, delta, babyStep, giantStep); Ctxt tmp(ret.getPubKey(), ret.getPtxtSpace()); simplePolyEval(tmp, c, babyStep); tmp += giantStep.getPower(t); ret.multiplyBy(tmp); PatersonStockmeyer(tmp, s, k, t/2, delta, babyStep, giantStep); ret += tmp; }
void FindRoot(ZZ_pE& root, const ZZ_pEX& ff) // finds a root of ff. // assumes that ff is monic and splits into distinct linear factors { ZZ_pEXModulus F; ZZ_pEX h, h1, f; ZZ_pEX r; f = ff; if (!IsOne(LeadCoeff(f))) LogicError("FindRoot: bad args"); if (deg(f) == 0) LogicError("FindRoot: bad args"); while (deg(f) > 1) { build(F, f); random(r, deg(F)); if (IsOdd(ZZ_pE::cardinality())) { PowerMod(h, r, RightShift(ZZ_pE::cardinality(), 1), F); sub(h, h, 1); } else { AbsTraceMap(h, r, 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; } } negate(root, ConstTerm(f)); }
static void RecFindRoots(vec_ZZ_pE& x, const ZZ_pEX& f) { if (deg(f) == 0) return; if (deg(f) == 1) { long k = x.length(); x.SetLength(k+1); negate(x[k], ConstTerm(f)); return; } ZZ_pEX h; ZZ_pEX r; { ZZ_pEXModulus F; build(F, f); do { random(r, deg(F)); if (IsOdd(ZZ_pE::cardinality())) { PowerMod(h, r, RightShift(ZZ_pE::cardinality(), 1), F); sub(h, h, 1); } else { AbsTraceMap(h, r, F); } GCD(h, h, f); } while (deg(h) <= 0 || deg(h) == deg(f)); } RecFindRoots(x, h); div(h, f, h); RecFindRoots(x, h); }
void FindRoot(ZZ_p& root, const ZZ_pX& ff) // finds a root of ff. // assumes that ff is monic and splits into distinct linear factors { ZZ_pXModulus F; ZZ_pX h, h1, f; ZZ_p r; ZZ p1; f = ff; if (!IsOne(LeadCoeff(f))) Error("FindRoot: bad args"); if (deg(f) == 0) Error("FindRoot: bad args"); RightShift(p1, ZZ_p::modulus(), 1); h1 = 1; while (deg(f) > 1) { build(F, f); random(r); PowerXPlusAMod(h, r, p1, F); sub(h, h, h1); 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; } } negate(root, ConstTerm(f)); }
static void RecFindRoots(vec_ZZ_p& x, const ZZ_pX& f) { if (deg(f) == 0) return; if (deg(f) == 1) { long k = x.length(); x.SetLength(k+1); negate(x[k], ConstTerm(f)); return; } ZZ_pX h; ZZ_p r; ZZ p1; RightShift(p1, ZZ_p::modulus(), 1); { ZZ_pXModulus F; build(F, f); do { random(r); PowerXPlusAMod(h, r, p1, F); add(h, h, -1); GCD(h, h, f); } while (deg(h) <= 0 || deg(h) == deg(f)); } RecFindRoots(x, h); div(h, f, h); RecFindRoots(x, h); }
static CYTHON_INLINE struct ZZX* ZZX_right_shift(struct ZZX* x, long n) { struct ZZX* y = new ZZX(); RightShift(*y, *x, n); return y; }
long CRT(mat_ZZ& gg, ZZ& a, const mat_zz_p& G) { long n = gg.NumRows(); long m = gg.NumCols(); if (G.NumRows() != n || G.NumCols() != m) Error("CRT: dimension mismatch"); long p = zz_p::modulus(); ZZ new_a; mul(new_a, a, p); long a_inv; a_inv = rem(a, p); a_inv = InvMod(a_inv, p); long p1; p1 = p >> 1; ZZ a1; RightShift(a1, a, 1); long p_odd = (p & 1); long modified = 0; long h; ZZ g; long i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { if (!CRTInRange(gg[i][j], a)) { modified = 1; rem(g, gg[i][j], a); if (g > a1) sub(g, g, a); } else g = gg[i][j]; h = rem(g, p); h = SubMod(rep(G[i][j]), h, p); h = MulMod(h, a_inv, p); if (h > p1) h = h - p; if (h != 0) { modified = 1; if (!p_odd && g > 0 && (h == p1)) MulSubFrom(g, a, h); else MulAddTo(g, a, h); } gg[i][j] = g; } } a = new_a; return modified; }
void HalfGCD(zz_pXMatrix& M_out, const zz_pX& U, const zz_pX& V, long d_red) { if (IsZero(V) || deg(V) <= deg(U) - d_red) { set(M_out(0,0)); clear(M_out(0,1)); clear(M_out(1,0)); set(M_out(1,1)); return; } long n = deg(U) - 2*d_red + 2; if (n < 0) n = 0; zz_pX U1, V1; RightShift(U1, U, n); RightShift(V1, V, n); if (d_red <= NTL_zz_pX_HalfGCD_CROSSOVER) { IterHalfGCD(M_out, U1, V1, d_red); return; } long d1 = (d_red + 1)/2; if (d1 < 1) d1 = 1; if (d1 >= d_red) d1 = d_red - 1; zz_pXMatrix M1; HalfGCD(M1, U1, V1, d1); mul(U1, V1, M1); long d2 = deg(V1) - deg(U) + n + d_red; if (IsZero(V1) || d2 <= 0) { M_out = M1; return; } zz_pX Q; zz_pXMatrix M2; DivRem(Q, U1, U1, V1); swap(U1, V1); HalfGCD(M2, U1, V1, d2); zz_pX t(INIT_SIZE, deg(M1(1,1))+deg(Q)+1); mul(t, Q, M1(1,0)); sub(t, M1(0,0), t); swap(M1(0,0), M1(1,0)); swap(M1(1,0), t); t.kill(); t.SetMaxLength(deg(M1(1,1))+deg(Q)+1); mul(t, Q, M1(1,1)); sub(t, M1(0,1), t); swap(M1(0,1), M1(1,1)); swap(M1(1,1), t); t.kill(); mul(M_out, M2, M1); }
// This procedure assumes that poly is monic and that babyStep contains // at least k+delta powers, where delta = deg(poly) mod k static long recursivePolyEval(const ZZX& poly, long k, DynamicPtxtPowers& babyStep, DynamicPtxtPowers& giantStep, long mod, long& recursiveDepth) { if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval long ret = simplePolyEval(poly, babyStep, mod); recursiveDepth = babyStep.getDepth(deg(poly)); return ret; } if (verbose) cerr << "recursivePolyEval("<<poly<<")\n"; long delta = deg(poly) % k; // deg(poly) mod k long n = divc(deg(poly),k); // ceil( deg(poly)/k ) long t = 1L<<(NextPowerOfTwo(n)); // t >= n, so t*k >= deg(poly) // Special case for deg(poly) = k * 2^e +delta if (n==t) return degPowerOfTwo(poly, k, babyStep, giantStep, mod, recursiveDepth); // When deg(poly) = k*(2^e -1) we use the Paterson-Stockmeyer recursion if (n == t-1 && delta==0) return PatersonStockmeyer(poly, k, t/2, delta, babyStep, giantStep, mod, recursiveDepth); t = t/2; // In any other case we have kt < deg(poly) < k(2t-1). We then set // u = deg(poly) - k*(t-1) and poly = q*X^u + r with deg(r)<u // and recurse on poly = (q-1)*X^u + (X^u+r) long u = deg(poly) - k*(t-1); ZZX r = trunc(poly, u); // degree <= u-1 ZZX q = RightShift(poly, u); // degree == k*(t-1) q -= 1; SetCoeff(r, u); // degree == u long ret, tmp; long subDepth1=0, subDepth2=0; if (verbose) cerr << " {deg(poly)="<<deg(poly)<<"<k*(2t-1)="<<k*(2*t-1) << "} recursing on "<<r<<" + X^"<<u<<"*"<<q<<endl; ret = PatersonStockmeyer(q, k, t/2, 0, babyStep, giantStep, mod, subDepth1); if (verbose) { cerr << " PatersonStockmeyer("<<q<<") returns "<<ret<<", depth="<<subDepth1<<endl; if (ret != polyEvalMod(q,babyStep[0], mod)) { cerr << " @@1st recursive call failed, q="<<q << ", ret="<<ret<<"!=" << polyEvalMod(q,babyStep[0], mod)<<endl; exit(0); } } tmp = giantStep.getPower(u/k); subDepth2 = giantStep.getDepth(u/k); if (delta!=0) { // if u is not divisible by k then compute it if (verbose) cerr <<" multiplying by X^"<<u <<"=giantStep.getPower("<<(u/k)<<")*babyStep.getPower("<<delta<<")=" << giantStep.getPower(u/k)<<"*"<<babyStep.getPower(delta) << "="<<tmp<<endl; tmp = MulMod(tmp, babyStep.getPower(delta), mod); nMults++; subDepth2++; } ret = MulMod(ret, tmp, mod); nMults ++; subDepth1 = max(subDepth1, subDepth2)+1; if (verbose) cerr << " after mult by X^{k*"<<u<<"+"<<delta<<"}, depth="<< subDepth1<<endl; tmp = recursivePolyEval(r, k, babyStep, giantStep, mod, subDepth2); if (verbose) cerr << " recursivePolyEval("<<r<<") returns "<<tmp<<", depth="<<subDepth2<<endl; if (tmp != polyEvalMod(r,babyStep[0], mod)) { cerr << " @@2nd recursive call failed, r="<<r << ", ret="<<tmp<<"!=" << polyEvalMod(r,babyStep[0], mod)<<endl; exit(0); } recursiveDepth = max(subDepth1, subDepth2); return AddMod(ret, tmp, mod); }
void ResHalfGCD(ZZ_pXMatrix& M_out, const ZZ_pX& U, const ZZ_pX& V, long d_red, vec_ZZ_p& cvec, vec_long& dvec) { if (IsZero(V) || deg(V) <= deg(U) - d_red) { set(M_out(0,0)); clear(M_out(0,1)); clear(M_out(1,0)); set(M_out(1,1)); return; } long n = deg(U) - 2*d_red + 2; if (n < 0) n = 0; ZZ_pX U1, V1; RightShift(U1, U, n); RightShift(V1, V, n); if (d_red <= NTL_ZZ_pX_HalfGCD_CROSSOVER) { ResIterHalfGCD(M_out, U1, V1, d_red, cvec, dvec); return; } long d1 = (d_red + 1)/2; if (d1 < 1) d1 = 1; if (d1 >= d_red) d1 = d_red - 1; ZZ_pXMatrix M1; ResHalfGCD(M1, U1, V1, d1, cvec, dvec); mul(U1, V1, M1); long d2 = deg(V1) - deg(U) + n + d_red; if (IsZero(V1) || d2 <= 0) { M_out = M1; return; } ZZ_pX Q; ZZ_pXMatrix M2; append(cvec, LeadCoeff(V1)); append(dvec, dvec[dvec.length()-1]-deg(U1)+deg(V1)); DivRem(Q, U1, U1, V1); swap(U1, V1); ResHalfGCD(M2, U1, V1, d2, cvec, dvec); ZZ_pX t(INIT_SIZE, deg(M1(1,1))+deg(Q)+1); mul(t, Q, M1(1,0)); sub(t, M1(0,0), t); swap(M1(0,0), M1(1,0)); swap(M1(1,0), t); t.kill(); t.SetMaxLength(deg(M1(1,1))+deg(Q)+1); mul(t, Q, M1(1,1)); sub(t, M1(0,1), t); swap(M1(0,1), M1(1,1)); swap(M1(1,1), t); t.kill(); mul(M_out, M2, M1); }
// This procedure assumes that poly is monic, deg(poly)=k*(2t-1)+delta // with t=2^e, and that babyStep contains k+delta powers static long PatersonStockmeyer(const ZZX& poly, long k, long t, long delta, DynamicPtxtPowers& babyStep, DynamicPtxtPowers& giantStep, long mod, long& recursiveDepth) { if (verbose) cerr << "PatersonStockmeyer("<<poly<<"), k="<<k<<", t="<<t<<endl; if (deg(poly)<=babyStep.size()) { // Edge condition, use simple eval long ret = simplePolyEval(poly, babyStep, mod); recursiveDepth = babyStep.getDepth(deg(poly)); return ret; } long subDepth1=0, subDepth2=0; long ret, tmp; ZZX r = trunc(poly, k*t); // degree <= k*2^e-1 ZZX q = RightShift(poly, k*t); // degree == k(2^e-1) +delta if (verbose) cerr << " r ="<<r<< ", q="<<q; const ZZ& coef = coeff(r,deg(q)); SetCoeff(r, deg(q), coef-1); // r' = r - X^{deg(q)} if (verbose) cerr << ", r'="<<r; ZZX c,s; DivRem(c,s,r,q); // r' = c*q + s // deg(s)<deg(q), and if c!= 0 then deg(c)<k-delta if (verbose) cerr << ", c="<<c<< ", s ="<<s<<endl; assert(deg(s)<deg(q)); assert(IsZero(c) || deg(c)<k-delta); SetCoeff(s,deg(q)); // s' = s + X^{deg(q)}, deg(s)==deg(q) // reduce the coefficients modulo mod for (long i=0; i<=deg(c); i++) rem(c[i],c[i], to_ZZ(mod)); c.normalize(); for (long i=0; i<=deg(s); i++) rem(s[i],s[i], to_ZZ(mod)); s.normalize(); if (verbose) cerr << " {t==n+1} recursing on "<<s<<" + (X^"<<t*k<<"+"<<c<<")*"<<q<<endl; // Evaluate recursively poly = (c+X^{kt})*q + s' tmp = simplePolyEval(c, babyStep, mod); tmp = AddMod(tmp, giantStep.getPower(t), mod); subDepth1 = max(babyStep.getDepth(deg(c)), giantStep.getDepth(t)); ret = PatersonStockmeyer(q, k, t/2, delta, babyStep, giantStep, mod, subDepth2); if (verbose) { cerr << " PatersonStockmeyer("<<q<<") returns "<<ret << ", depth="<<subDepth2<<endl; if (ret != polyEvalMod(q,babyStep[0], mod)) { cerr << " **1st recursive call failed, q="<<q<<endl; exit(0); } } ret = MulMod(ret, tmp, mod); nMults++; subDepth1 = max(subDepth1, subDepth2)+1; tmp = PatersonStockmeyer(s, k, t/2, delta, babyStep, giantStep, mod, subDepth2); if (verbose) { cerr << " PatersonStockmeyer("<<s<<") returns "<<tmp << ", depth="<<subDepth2<<endl; if (tmp != polyEvalMod(s,babyStep[0], mod)) { cerr << " **2nd recursive call failed, s="<<s<<endl; exit(0); } } ret = AddMod(ret,tmp,mod); recursiveDepth = max(subDepth1, subDepth2); return ret; }