// 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); }
// A recursive function to compute, for an n-size array, the 2^n products // products[j] = \prod_{i s.t. j_i=1} array[i] // \times \prod_{i s.t. j_i=0}(a-array[i]) // It is assume that 'products' size <= 2^n, else only 1st 2^n entries are set static void recursiveProducts(const CtPtrs& products, const CtPtrs_slice& array) { long nBits = lsize(array); long N = lsize(products); if (nBits==0 || N==0) return; // nothing to do if (N > (1L << nBits)) N = (1L << nBits); else if (N < (1L << (nBits-1))) nBits = NTL::NumBits(N-1); // Ensure nBits <= ceil(log2(N)) if (N<=2) { // edge condition *products[0] = *array[0]; products[0]->negate(); products[0]->addConstant(ZZ(1)); // out[0] = 1-in if (N>1) *products[1] = *array[0]; // out[1] = in } // optimization for n=2: a single multiplication instead of 4 else if (N<=4) { *products[0] = *array[1]; // x1 products[0]->multiplyBy(*array[0]);// x1 x0 *products[1] = *array[0]; // x0 *products[1] -= *products[0]; // x0 - x1 x0 = (1-x1)x0 *products[2] = *array[1]; // x1 *products[2] -= *products[0]; // x1 - x1 x0 = x1(1-x0) if (N>3) *products[3] = *products[0]; // x1 x0 products[0]->addConstant(ZZ(1)); // 1 +x1 x0 *products[0] -= *array[1]; // 1 +x1 x0 -x1 *products[0] -= *array[0] ; // 1 +x1 x0 -x1 -x0 = (1-x1)(1-x0) } else { // split the array into two parts; // first part is highest pow(2) < n, second part is what is left long n1 = 1L << (NTL::NumBits(nBits)-1); // largest power of two <= n if (nBits<=n1) n1 = n1/2; // largest power of two < n long k = 1L << n1; // size of first part long l = 1L << (nBits-n1); // size of second part const Ctxt* ct = array.ptr2nonNull(); // find some non-null Ctxt std::vector<Ctxt> products1(k, Ctxt(ZeroCtxtLike, *ct)); std::vector<Ctxt> products2(l, Ctxt(ZeroCtxtLike, *ct)); // compute first part of the array recursiveProducts(CtPtrs_vectorCt(products1), CtPtrs_slice(array,0, n1)); // recursive call on second part of array recursiveProducts(CtPtrs_vectorCt(products2), CtPtrs_slice(array,n1,nBits-n1)); // multiplication to get all subset products NTL_EXEC_RANGE(lsize(products), first, last) for(long ii=first; ii<last; ii++) { long j = ii / k; long i = ii - j*k; *products[ii] = products1[i]; products[ii]->multiplyBy(products2[j]); } NTL_EXEC_RANGE_END } }