// computes ctxt^{2^d-1} using a method that takes // O(log d) automorphisms and multiplications void fastPower(Ctxt& ctxt, long d) { assert(ctxt.getPtxtSpace()==2); if (d <= 1) return; Ctxt orig = ctxt; long k = NumBits(d); long e = 1; for (long i = k-2; i >= 0; i--) { Ctxt tmp1 = ctxt; tmp1.smartAutomorph(1L << e); ctxt.multiplyBy(tmp1); e = 2*e; if (bit(d, i)) { ctxt.smartAutomorph(2); ctxt.multiplyBy(orig); e += 1; } } }
// 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; }
// Main entry point: Evaluate an encrypted polynomial on an encrypted input // return in ret = sum_i poly[i] * x^i void polyEval(Ctxt& ret, const Vec<Ctxt>& poly, const Ctxt& x) { if (poly.length()<=1) { // Some special cases if (poly.length()==0) ret.clear(); // empty polynomial else ret = poly[0]; // constant polynomial return; } long deg = poly.length()-1; long logD = NextPowerOfTwo(divc(poly.length(),3)); long d = 1L << logD; // We have d <= deg(poly) < 3d assert(d <= deg && deg < 3*d); Vec<Ctxt> powers(INIT_SIZE, logD+1, x); if (logD>0) { powers[1].square(); for (long i=2; i<=logD; i++) { // powers[i] = x^{2^i} powers[i] = powers[i-1]; powers[i].square(); } } // Compute in three parts p0(X) + ( p1(X) + p2(X)*X^d )*X^d Ctxt tmp(ZeroCtxtLike, ret); recursivePolyEval(ret, &poly[d], min(d,poly.length()-d), powers); // p1(X) if (poly.length() > 2*d) { // p2 is not empty recursivePolyEval(tmp, &poly[2*d], poly.length()-2*d, powers); // p2(X) tmp.multiplyBy(powers[logD]); ret += tmp; } ret.multiplyBy(powers[logD]); // ( p1(X) + p2(X)*X^d )*X^d recursivePolyEval(tmp, &poly[0], d, powers); // p0(X) ret += tmp; }