static void apply(const EncryptedArrayDerived<type>& ea, std::vector<zzX>& unpackSlotEncoding) { FHE_NTIMER_START(buildUnpackSlotEncoding); RBak bak; bak.save(); ea.restoreContext(); // the NTL context for mod p^r long nslots = ea.size(); // how many slots long d = ea.getDegree(); // size of each slot const Mat<R>& CBi=ea.getNormalBasisMatrixInverse(); // CBi contains a description of the normal-basis inverse transformation std::vector<RX> LM(d); for (long i = 0; i < d; i++) // prepare the linear polynomial LM[i] = CBi[i][0]; std::vector<RX> C; ea.buildLinPolyCoeffs(C, LM); // "build" the linear polynomial unpackSlotEncoding.resize(d); // encode the coefficients for (long j = 0; j < d; j++) { std::vector<RX> v(nslots, C[j]); ea.encode(unpackSlotEncoding[j], v); } }
static void apply(const EncryptedArrayDerived<type>& ea, Ctxt& ctxt, const PlaintextBlockMatrixBaseInterface& mat) { assert(&ea == &mat.getEA().getDerived(type())); assert(&ea.getContext() == &ctxt.getContext()); const PAlgebra& zMStar = ea.getContext().zMStar; long p = zMStar.getP(); long m = zMStar.getM(); const RXModulus& F = ea.getTab().getPhimXMod(); RBak bak; bak.save(); ea.getTab().restoreContext(); const PlaintextBlockMatrixInterface<type>& mat1 = dynamic_cast< const PlaintextBlockMatrixInterface<type>& >( mat ); ctxt.cleanUp(); // not sure, but this may be a good idea long nslots = ea.size(); long d = ea.getDegree(); Vec< shared_ptr<Ctxt> > acc; acc.SetLength(d); for (long k = 0; k < d; k++) acc[k] = shared_ptr<Ctxt>(new Ctxt(ZeroCtxtLike, ctxt)); mat_R entry; entry.SetDims(d, d); vector<RX> entry1; entry1.resize(d); vector< vector<RX> > diag; diag.resize(nslots); for (long j = 0; j < nslots; j++) diag[j].resize(d); for (long i = 0; i < nslots; i++) { // process diagonal i bool zDiag = true; long nzLast = -1; for (long j = 0; j < nslots; j++) { bool zEntry = mat1.get(entry, mcMod(j-i, nslots), j); assert(zEntry || (entry.NumRows() == d && entry.NumCols() == d)); // get(...) returns true if the entry is empty, false otherwise if (!zEntry && IsZero(entry)) zEntry=true; // zero is an empty entry too if (!zEntry) { // non-empty entry zDiag = false; // mark diagonal as non-empty // clear entries between last nonzero entry and this one for (long jj = nzLast+1; jj < j; jj++) { for (long k = 0; k < d; k++) clear(diag[jj][k]); } nzLast = j; // recode entry as a vector of polynomials for (long k = 0; k < d; k++) conv(entry1[k], entry[k]); // compute the lin poly coeffs ea.buildLinPolyCoeffs(diag[j], entry1); } } if (zDiag) continue; // zero diagonal, continue // clear trailing zero entries for (long jj = nzLast+1; jj < nslots; jj++) { for (long k = 0; k < d; k++) clear(diag[jj][k]); } // now diag[j] contains the lin poly coeffs Ctxt shCtxt = ctxt; ea.rotate(shCtxt, i); shCtxt.cleanUp(); RX cpoly1, cpoly2; ZZX cpoly; // apply the linearlized polynomial for (long k = 0; k < d; k++) { // compute the constant bool zConst = true; vector<RX> cvec; cvec.resize(nslots); for (long j = 0; j < nslots; j++) { cvec[j] = diag[j][k]; if (!IsZero(cvec[j])) zConst = false; } if (zConst) continue; ea.encode(cpoly, cvec); conv(cpoly1, cpoly); // apply inverse automorphism to constant plaintextAutomorph(cpoly2, cpoly1, PowerMod(p, mcMod(-k, d), m), zMStar, F); conv(cpoly, cpoly2); Ctxt shCtxt1 = shCtxt; shCtxt1.multByConstant(cpoly); *acc[k] += shCtxt1; } } Ctxt res(ZeroCtxtLike, ctxt); for (long k = 0; k < d; k++) { acc[k]->frobeniusAutomorph(k); res += *acc[k]; } ctxt = res; }