int main(void) { #ifdef TESTING freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif long long int n, m, a; scanf("%I64d %I64d %I64d ", &n, &m, &a); printf("%I64d\n", divc(n, a) * divc(m, a)); return 0; }
// Use packed bootstrapping, so we can bootstrap all in just one go. void packedRecrypt(const CtPtrs& cPtrs, const std::vector<zzX>& unpackConsts, const EncryptedArray& ea) { FHEPubKey& pKey = (FHEPubKey&)cPtrs[0]->getPubKey(); // Allocate temporary ciphertexts for the recryption int nPacked = divc(cPtrs.size(), ea.getDegree()); // ceil(totoalNum/d) std::vector<Ctxt> cts(nPacked, Ctxt(pKey)); repack(CtPtrs_vectorCt(cts), cPtrs, ea); // pack ciphertexts // cout << "@"<< lsize(cts)<<std::flush; for (Ctxt& c: cts) { // then recrypt them c.reducePtxtSpace(2); // we only have recryption data for binary ctxt #ifdef DEBUG_PRINTOUT ZZX ptxt; decryptAndPrint((cout<<" before recryption "), c, *dbgKey, *dbgEa); dbgKey->Decrypt(ptxt, c); c.DummyEncrypt(ptxt); decryptAndPrint((cout<<" after recryption "), c, *dbgKey, *dbgEa); #else pKey.reCrypt(c); #endif } unpack(cPtrs, CtPtrs_vectorCt(cts), ea, unpackConsts); }
int gcd (const bignum & a, int b) { int c, t; bignum t1; c = divc (t1, a, b); while (c > 0) { t = b % c; b = c; c = t; } return b; }
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; }
// 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; }
// 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 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); }
// Main entry point: Evaluate a cleartext polynomial on an encrypted input void polyEval(Ctxt& ret, ZZX poly, const Ctxt& x, long k) // Note: poly is passed by value, so caller keeps the original { if (deg(poly)<=2) { // nothing to optimize here if (deg(poly)<1) { // A constant ret.clear(); ret.addConstant(coeff(poly, 0)); } else { // A linear or quadratic polynomial DynamicCtxtPowers babyStep(x, deg(poly)); simplePolyEval(ret, poly, babyStep); } return; } // 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 // two consecutive powers of two and choose the one that gives 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; } #ifdef DEBUG_PRINTOUT cerr << " k="<<k; #endif long n = divc(deg(poly),k); // n = ceil(deg(p)/k), deg(p) >= k*n DynamicCtxtPowers babyStep(x, k); const Ctxt& x2k = babyStep.getPower(k); // Special case when deg(p)>k*(2^e -1) if (n==(1L << NextPowerOfTwo(n))) { // n is a power of two DynamicCtxtPowers giantStep(x2k, n/2); degPowerOfTwo(ret, poly, k, babyStep, giantStep); return; } // 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 const ZZ p = to_ZZ(x.getPtxtSpace()); ZZ top = LeadCoeff(poly); 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, top, 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. ZZ extra = ZZ::zero(); // extra!=0 denotes an added term extra*X^{n*k} if (!divisible || nonInvertibe) { // need to add a term top = to_ZZ(1); // new top coefficient is one topInv = top; // also the new inverse is one // set extra = 1 - current-coeff-of-X^{n*k} extra = SubMod(top, coeff(poly,n*k), p); SetCoeff(poly, n*k); // set the top coefficient of X^{n*k} to one } long t = IsZero(extra)? divc(n,2) : n; DynamicCtxtPowers giantStep(x2k, t); if (!IsOne(top)) { poly *= topInv; // Multiply by topInv to make into a monic polynomial for (long i=0; i<=n*k; i++) rem(poly[i], poly[i], p); poly.normalize(); } recursivePolyEval(ret, poly, k, babyStep, giantStep); if (!IsOne(top)) { ret.multByConstant(top); } if (!IsZero(extra)) { // if we added a term, now is the time to subtract back Ctxt topTerm = giantStep.getPower(n); topTerm.multByConstant(extra); ret -= topTerm; } }