static void recursiveInterpolateMod(ZZX& poly, const vec_long& x, vec_long& y, const vec_zz_p& xmod, vec_zz_p& ymod, long p, long p2e) { if (p2e<=1) { // recursion edge condition, mod-1 poly = 0 clear(poly); return; } // convert y input to zz_p for (long j=0; j<y.length(); j++) ymod[j] = to_zz_p(y[j] % p); // a polynomial p_i s.t. p_i(x[j]) = i'th p-base digit of poly(x[j]) zz_pX polyMod; interpolate(polyMod, xmod, ymod); // interpolation modulo p ZZX polyTmp; conv(polyTmp, polyMod); // convert to ZZX // update ytmp by subtracting the new digit, then dividing by p for (long j=0; j<y.length(); j++) { y[j] -= polyEvalMod(polyTmp,x[j],p2e); // mod p^e if (y[j]<0) y[j] += p2e; // if (y[j] % p != 0) { // cerr << "@@error (p2^e="<<p2e<<"): y["<<j<<"] not divisible by "<<p<< endl; // exit(0); // } y[j] /= p; } // maybe it's worth optimizing above by using multi-point evaluation // recursive call to get the solution of poly'(x)=y mod p^{e-1} recursiveInterpolateMod(poly, x, y, xmod, ymod, p, p2e/p); // return poly = p*poly' + polyTmp poly *= p; poly += polyTmp; }
// 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); }
// Note: poly is passed by value, not by reference, so the calling routine // keeps its original polynomial long evalPolyTopLevel(ZZX poly, long x, long p, long k=0) { if (verbose) cerr << "\n* evalPolyTopLevel: p="<<p<<", x="<<x<<", poly="<<poly; if (deg(poly)<=2) { // nothing to optimize here if (deg(poly)<1) return to_long(coeff(poly, 0)); DynamicPtxtPowers babyStep(x, p, deg(poly)); long ret = simplePolyEval(poly, babyStep, p); totalDepth = babyStep.getDepth(deg(poly)); return ret; } // How many baby steps: set k~sqrt(n/2), rounded up/down to a power of two // FIXME: There may be some room for optimization here: it may be possible // to choose k as something other than a power of two and still maintain // optimal depth, in principle we can try all possible values of k between // the two powers of two and choose the one that goves the least number // of multiplies, conditioned on minimum depth. if (k<=0) { long kk = (long) sqrt(deg(poly)/2.0); k = 1L << NextPowerOfTwo(kk); // heuristic: if k>>kk then use a smaler power of two if ((k==16 && deg(poly)>167) || (k>16 && k>(1.44*kk))) k /= 2; } cerr << ", k="<<k; long n = divc(deg(poly),k); // deg(p) = k*n +delta if (verbose) cerr << ", n="<<n<<endl; DynamicPtxtPowers babyStep(x, p, k); long x2k = babyStep.getPower(k); // Special case when deg(p)>k*(2^e -1) if (n==(1L << NextPowerOfTwo(n))) { // n is a power of two DynamicPtxtPowers giantStep(x2k, p, n/2, babyStep.getDepth(k)); if (verbose) cerr << "babyStep="<<babyStep<<", giantStep="<<giantStep<<endl; long ret = degPowerOfTwo(poly, k, babyStep, giantStep, p, totalDepth); if (verbose) { cerr << " degPowerOfTwo("<<poly<<") returns "<<ret<<", depth="<<totalDepth<<endl; if (ret != polyEvalMod(poly,babyStep[0], p)) { cerr << " ## recursive call failed, ret="<<ret<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } // cerr << " babyStep depth=["; // for (long i=0; i<babyStep.size(); i++) // cerr << babyStep.getDepth(i+1)<<" "; // cerr << "]\n"; // cerr << " giantStep depth=["; // for (long i=0; i<giantStep.size(); i++) // cerr<<giantStep.getDepth(i+1)<<" "; // cerr << "]\n"; } return ret; } // If n is not a power of two, ensure that poly is monic and that // its degree is divisible by k, then call the recursive procedure ZZ topInv; // the inverse mod p of the top coefficient of poly (if any) bool divisible = (n*k == deg(poly)); // is the degree divisible by k? long nonInvertibe = InvModStatus(topInv, LeadCoeff(poly), to_ZZ(p)); // 0 if invertible, 1 if not // FIXME: There may be some room for optimization below: instead of // adding a term X^{n*k} we can add X^{n'*k} for some n'>n, so long // as n' is smaller than the next power of two. We could save a few // multiplications since giantStep[n'] may be easier to compute than // giantStep[n] when n' has fewer 1's than n in its binary expansion. long extra = 0; // extra!=0 denotes an added term extra*X^{n*k} if (!divisible || nonInvertibe) { // need to add a term // set extra = 1 - current-coeff-of-X^{n*k} extra = SubMod(1, to_long(coeff(poly,n*k)), p); SetCoeff(poly, n*k); // set the top coefficient of X^{n*k} to one topInv = to_ZZ(1); // inverse of new top coefficient is one } long t = (extra==0)? divc(n,2) : n; DynamicPtxtPowers giantStep(x2k, p, t, babyStep.getDepth(k)); if (verbose) cerr << "babyStep="<<babyStep<<", giantStep="<<giantStep<<endl; long y; // the value to return long subDepth1 =0; if (!IsOne(topInv)) { long top = to_long(poly[n*k]); // record the current top coefficient // cerr << ", top-coeff="<<top; // Multiply by topInv modulo p to make into a monic polynomial poly *= topInv; for (long i=0; i<=n*k; i++) rem(poly[i], poly[i], to_ZZ(p)); poly.normalize(); y = recursivePolyEval(poly, k, babyStep, giantStep, p, subDepth1); if (verbose) { cerr << " recursivePolyEval("<<poly<<") returns "<<y<<", depth="<<subDepth1<<endl; if (y != polyEvalMod(poly,babyStep[0], p)) { cerr << "## recursive call failed, ret="<<y<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } } y = MulMod(y, top, p); // multiply by the original top coefficient } else { y = recursivePolyEval(poly, k, babyStep, giantStep, p, subDepth1); if (verbose) { cerr << " recursivePolyEval("<<poly<<") returns "<<y<<", depth="<<subDepth1<<endl; if (y != polyEvalMod(poly,babyStep[0], p)) { cerr << "## recursive call failed, ret="<<y<<"!=" << polyEvalMod(poly,babyStep[0], p)<<endl; exit(0); } } } if (extra != 0) { // if we added a term, now is the time to subtract back if (verbose) cerr << ", subtracting "<<extra<<"*X^"<<k*n; extra = MulMod(extra, giantStep.getPower(n), p); totalDepth = max(subDepth1, giantStep.getDepth(n)); y = SubMod(y, extra, p); } else totalDepth = subDepth1; if (verbose) cerr << endl; return y; }
// 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; }