// recrypt all ctxt at level < belowLvl void packedRecrypt(const CtPtrs& array, const std::vector<zzX>& unpackConsts, const EncryptedArray& ea, long belowLvl) { std::vector<Ctxt*> v; for (long i=0; i<array.size(); i++) if ( array.isSet(i) && !array[i]->isEmpty() && array[i]->findBaseLevel()<belowLvl ) v.push_back(array[i]); packedRecrypt(CtPtrs_vectorPt(v), unpackConsts, ea); }
// 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); }
// For an n-size array, compute 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]) void computeAllProducts(/*Output*/CtPtrs& products, /*Index*/const CtPtrs& array, std::vector<zzX>* unpackSlotEncoding) { FHE_TIMER_START; long nBits = array.size(); if (lsize(products)>0) { long nBits2 = NTL::NumBits(lsize(products)-1); // ceil(log_2(size)) if (nBits>nBits2) nBits=nBits2; // ignore extra bits in 'array' } if (nBits<1) return; // do nothing assert(nBits <= 16); // Output cannot be bigger than 2^16 if (lsize(products)==0) // try to set the output size products.resize(1L << nBits, &array); for (long i=0; i<lsize(products); i++) products[i]->clear(); // Check that we have enough levels, try to bootstrap otherwise assert(array.ptr2nonNull() != nullptr); long bpl = array.ptr2nonNull()->getContext().BPL(); if (findMinBitCapacity(array) < (NTL::NumBits(nBits)+1)*bpl) { const Ctxt* ct = array.ptr2nonNull(); // find some non-null Ctxt assert(unpackSlotEncoding!=nullptr); assert(ct!=nullptr); assert(ct->getPubKey().isBootstrappable()); packedRecrypt(array, *unpackSlotEncoding, *(ct->getContext().ea), /*belowLevel=*/nBits +3); } if (findMinBitCapacity(array) < (NTL::NumBits(nBits)+1)*bpl) throw std::logic_error("not enough levels for table lookup"); // Call the recursive function that copmutes the products recursiveProducts(products, CtPtrs_slice(array,0,nBits)); }
// A counterpart of tableLookup. The input is an encrypted table T[] // and an array of encrypted bits I[], holding the binary representation // of an index i into T. This function increments by one the entry T[i]. void tableWriteIn(const CtPtrs& table, const CtPtrs& idx, std::vector<zzX>* unpackSlotEncoding) { FHE_TIMER_START; const Ctxt* ct = table.ptr2nonNull(); // find some non-null Ctxt long size = lsize(table); if (size==0) return; std::vector<Ctxt> products(size, Ctxt(ZeroCtxtLike, *ct)); CtPtrs_vectorCt pWrap(products); // A wrapper // Compute all products of ecnrypted bits =: b_i computeAllProducts(pWrap, idx, unpackSlotEncoding); // incrememnt each entry of T[i] by products[i] NTL_EXEC_RANGE(lsize(table), first, last) for(long i=first; i<last; i++) *table[i] += products[i]; NTL_EXEC_RANGE_END }