void PAlgebraModDerived<type>::genMaskTable() const { if (maskTable.size() > 0) return; RBak bak; bak.save(); restoreContext(); // strip const vector< vector< RX > >& mtab = (vector< vector< RX > >&) maskTable; RX tmp1; mtab.resize(zMStar.numOfGens()); for (long i = 0; i < (long)zMStar.numOfGens(); i++) { // if (i==0 && zMStar.SameOrd(i)) continue;//SHAI: need these masks for shift1D long ord = zMStar.OrderOf(i); mtab[i].resize(ord+1); mtab[i][ord] = 0; for (long j = ord-1; j >= 1; j--) { // initialize mask that is 1 whenever the ith coordinate is at least j // Note: mtab[i][0] = constant 1, mtab[i][ord] = constant 0 mtab[i][j] = mtab[i][j+1]; for (long k = 0; k < (long)zMStar.getNSlots(); k++) { if (zMStar.coordinate(i, k) == j) { div(tmp1, PhimXMod, factors[k]); mul(tmp1, tmp1, crtCoeffs[k]); add(mtab[i][j], mtab[i][j], tmp1); } } } mtab[i][0] = 1; } }
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); } }
template<class type> void EncryptedArrayDerived<type>::buildLinPolyCoeffs(vector<RX>& C, const vector<RX>& L) const { FHE_TIMER_START; RBak bak; bak.save(); restoreContext(); // the NTL context for mod p^r REBak ebak; ebak.save(); restoreContextForG(); // The NTL context for mod G do { typename Lazy< Mat<RE> >::Builder builder(linPolyMatrix); if (!builder()) break; long p = tab.getZMStar().getP(); long r = tab.getR(); Mat<RE> M1; // build d x d matrix, d is taken from the surrent NTL context for G buildLinPolyMatrix(M1, p); Mat<RE> M2; ppInvert(M2, M1, p, r); // invert modulo prime-power p^r UniquePtr< Mat<RE> > ptr; ptr.make(M2); builder.move(ptr); } while (0); Vec<RE> CC, LL; convert(LL, L); mul(CC, LL, *linPolyMatrix); convert(C, CC); }
void EncryptedArrayDerived<type>::encodeUnitSelector(ZZX& ptxt, long i) const { assert(i >= 0 && i < (long)context.zMStar.getNSlots()); RBak bak; bak.save(); tab.restoreContext(); RX res; div(res, tab.getPhimXMod(), tab.getFactors()[i]); mul(res, res, tab.getCrtCoeffs()[i]); conv(ptxt, res); }
// Linearized polynomials. // L describes a linear map M by describing its action on the standard // power basis: M(x^j mod G) = (L[j] mod G), for j = 0..d-1. // The result is a coefficient vector C for the linearized polynomial // representing M: a polynoamial h in Z/(p^r)[X] of degree < d is sent to // // M(h(X) \bmod G)= \sum_{i=0}^{d-1}(C[j] \cdot h(X^{p^j}))\bmod G). template<class type> void EncryptedArrayDerived<type>::buildLinPolyCoeffs(vector<ZZX>& C, const vector<ZZX>& L) const { RBak bak; bak.save(); restoreContext(); vector<RX> CC, LL; convert(LL, L); buildLinPolyCoeffs(CC, LL); convert(C, CC); }
void EncryptedArrayDerived<type>::initNormalBasisMatrix() const { do { typename Lazy< Pair< Mat<R>, Mat<R> > >::Builder builder(normalBasisMatrices); if (!builder()) break; RBak bak; bak.save(); restoreContext(); REBak ebak; ebak.save(); restoreContextForG(); long d = RE::degree(); long p = tab.getZMStar().getP(); long r = tab.getR(); // compute change of basis matrix CB mat_R CB; CB.SetDims(d, d); RE normal_element; RE H; bool got_it = false; H = power(conv<RE>(RX(1, 1)), p); do { NTL::random(normal_element); RE pow; pow = normal_element; VectorCopy(CB[0], rep(pow), d); for (long i = 1; i < d; i++) { pow = eval(rep(pow), H); VectorCopy(CB[i], rep(pow), d); } Mat<ZZ> CB1; conv(CB1, CB); { zz_pBak bak1; bak1.save(); zz_p::init(p); Mat<zz_p> CB2; conv(CB2, CB1); got_it = determinant(CB2) != 0; } } while (!got_it); Mat<R> CBi; ppInvert(CBi, CB, p, r); UniquePtr< Pair< Mat<R>, Mat<R> > > ptr; ptr.make(CB, CBi); builder.move(ptr); } while(0); }
void EncryptedArrayDerived<type>::rotate1D(Ctxt& ctxt, long i, long amt, bool dc) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); assert(i >= 0 && i < (long)al.numOfGens()); // Make sure amt is in the range [1,ord-1] long ord = al.OrderOf(i); amt %= ord; if (amt == 0) return; long signed_amt = amt; if (amt < 0) amt += ord; // DIRT: the above assumes division with remainder // follows C++11 and C99 rules if (al.SameOrd(i)) { // a "native" rotation long val = PowerMod(al.ZmStarGen(i), amt, al.getM()); ctxt.smartAutomorph(val); } else if (dc) { // the "don't care" case...it is presumed that any shifts // "off the end" are zero. For this, we have to use // the "signed" version of amt. long val = PowerMod(al.ZmStarGen(i), signed_amt, al.getM()); ctxt.smartAutomorph(val); } else { // more expensive "non-native" rotation assert(maskTable[i].size() > 0); long val = PowerMod(al.ZmStarGen(i), amt, al.getM()); long ival = PowerMod(al.ZmStarGen(i), amt-ord, al.getM()); const RX& mask = maskTable[i][ord-amt]; DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); Ctxt tmp(ctxt); // a copy of the ciphertext tmp.multByConstant(m1); // only the slots in which m1=1 ctxt -= tmp; // only the slots in which m1=0 ctxt.smartAutomorph(val); // shift left by val tmp.smartAutomorph(ival); // shift right by ord-val ctxt += tmp; // combine the two parts } FHE_TIMER_STOP; }
// Multiply a ciphertext vector by a plaintext dense matrix // and/or build a cache with the multiplication constants void multilpy(Ctxt* ctxt) { RBak bak; bak.save(); ea.getTab().restoreContext(); // idxes describes a genealized diagonal, {(i,idx[i])}_i // initially just the identity, idx[i]==i vector<long> idxes(ea.size()); for (long i = 0; i < ea.size(); i++) idxes[i] = i; // call the recursive procedure to do the actual work rec_mul(ctxt, 0, 0, idxes); if (ctxt!=nullptr && res!=nullptr) *ctxt = *res; // copy the result back to ctxt // "install" the cache (if needed) if (buildCache == cachezzX) mat.installzzxcache(zCache); else if (buildCache == cacheDCRT) mat.installDCRTcache(dCache); }
RandomMatrix(const EncryptedArray& _ea) : ea(_ea) { long n = ea.size(); long d = ea.getDegree(); long bnd = 2*n; // non-zero with probability 1/bnd RBak bak; bak.save(); ea.getContext().alMod.restoreContext(); data.resize(n); for (long i = 0; i < n; i++) { data[i].resize(n); for (long j = 0; j < n; j++) { bool zEntry = (RandomBnd(bnd) > 0); if (zEntry) clear(data[i][j]); else random(data[i][j], d); } } }
void EncryptedArrayDerived<type>::shift1D(Ctxt& ctxt, long i, long k) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); assert(i >= 0 && i < (long)al.numOfGens()); long ord = al.OrderOf(i); if (k <= -ord || k >= ord) { ctxt.multByConstant(to_ZZX(0)); return; } // Make sure amt is in the range [1,ord-1] long amt = k % ord; if (amt == 0) return; if (amt < 0) amt += ord; RX mask = maskTable[i][ord-amt]; long val; if (k < 0) val = PowerMod(al.ZmStarGen(i), amt-ord, al.getM()); else { mask = 1 - mask; val = PowerMod(al.ZmStarGen(i), amt, al.getM()); } DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); ctxt.multByConstant(m1); // zero out slots where mask=0 ctxt.smartAutomorph(val); // shift left by val FHE_TIMER_STOP; }
static void apply(const EncryptedArrayDerived<type>& ea, Ctxt& ctxt, const PlaintextMatrixBaseInterface& mat) { assert(&ea == &mat.getEA().getDerived(type())); assert(&ea.getContext() == &ctxt.getContext()); RBak bak; bak.save(); ea.getTab().restoreContext(); // Get the derived type const PlaintextMatrixInterface<type>& mat1 = dynamic_cast< const PlaintextMatrixInterface<type>& >( mat ); ctxt.cleanUp(); // not sure, but this may be a good idea Ctxt res(ctxt.getPubKey(), ctxt.getPtxtSpace()); // fresh encryption of zero long nslots = ea.size(); long d = ea.getDegree(); RX entry; vector<RX> diag; diag.resize(nslots); // Process the diagonals one at a time for (long i = 0; i < nslots; i++) { // process diagonal i bool zDiag = true; // is this a zero diagonal? long nzLast = -1; // index of last non-zero entry on this diagonal // Compute constants for each entry on this diagonal for (long j = 0; j < nslots; j++) { // process entry j bool zEntry = mat1.get(entry, mcMod(j-i, nslots), j); // callback assert(zEntry || deg(entry) < d); if (!zEntry && IsZero(entry)) zEntry = true; // check for zero if (!zEntry) { // non-zero diagonal entry zDiag = false; // diagonal is non-zero // clear entries between last nonzero entry and this one for (long jj = nzLast+1; jj < j; jj++) clear(diag[jj]); nzLast = j; diag[j] = entry; } } if (zDiag) continue; // zero diagonal, continue // clear trailing zero entries for (long jj = nzLast+1; jj < nslots; jj++) clear(diag[jj]); // Now we have the constants for all the diagonal entries, encode the // diagonal as a single polynomial with these constants in the slots ZZX cpoly; ea.encode(cpoly, diag); // rotate by i, multiply by the polynomial, then add to the result Ctxt shCtxt = ctxt; ea.rotate(shCtxt, i); // rotate by i shCtxt.multByConstant(cpoly); res += shCtxt; } ctxt = res; }
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; }
PAlgebraModDerived<type>::PAlgebraModDerived(const PAlgebra& _zMStar, long _r) : zMStar(_zMStar), r(_r) { long p = zMStar.getP(); long m = zMStar.getM(); // For dry-run, use a tiny m value for the PAlgebra tables if (isDryRun()) m = (p==3)? 4 : 3; assert(r > 0); ZZ BigPPowR = power_ZZ(p, r); assert(BigPPowR.SinglePrecision()); pPowR = to_long(BigPPowR); long nSlots = zMStar.getNSlots(); RBak bak; bak.save(); SetModulus(p); // Compute the factors Ft of Phi_m(X) mod p, for all t \in T RX phimxmod; conv(phimxmod, zMStar.getPhimX()); // Phi_m(X) mod p vec_RX localFactors; EDF(localFactors, phimxmod, zMStar.getOrdP()); // equal-degree factorization RX* first = &localFactors[0]; RX* last = first + localFactors.length(); RX* smallest = min_element(first, last); swap(*first, *smallest); // We make the lexicographically smallest factor have index 0. // The remaining factors are ordered according to their representives. RXModulus F1(localFactors[0]); for (long i=1; i<nSlots; i++) { unsigned long t =zMStar.ith_rep(i); // Ft is minimal polynomial of x^{1/t} mod F1 unsigned long tInv = InvMod(t, m); // tInv = t^{-1} mod m RX X2tInv = PowerXMod(tInv,F1); // X2tInv = X^{1/t} mod F1 IrredPolyMod(localFactors[i], X2tInv, F1); } /* Debugging sanity-check #1: we should have Ft= GCD(F1(X^t),Phi_m(X)) for (i=1; i<nSlots; i++) { unsigned long t = T[i]; RX X2t = PowerXMod(t,phimxmod); // X2t = X^t mod Phi_m(X) RX Ft = GCD(CompMod(F1,X2t,phimxmod),phimxmod); if (Ft != localFactors[i]) { cout << "Ft != F1(X^t) mod Phi_m(X), t=" << t << endl; exit(0); } }*******************************************************************/ if (r == 1) { build(PhimXMod, phimxmod); factors = localFactors; pPowRContext.save(); // Compute the CRT coefficients for the Ft's crtCoeffs.SetLength(nSlots); for (long i=0; i<nSlots; i++) { RX te = phimxmod / factors[i]; // \prod_{j\ne i} Fj te %= factors[i]; // \prod_{j\ne i} Fj mod Fi InvMod(crtCoeffs[i], te, factors[i]); // \prod_{j\ne i} Fj^{-1} mod Fi } } else { PAlgebraLift(zMStar.getPhimX(), localFactors, factors, crtCoeffs, r); RX phimxmod1; conv(phimxmod1, zMStar.getPhimX()); build(PhimXMod, phimxmod1); pPowRContext.save(); } // set factorsOverZZ factorsOverZZ.resize(nSlots); for (long i = 0; i < nSlots; i++) conv(factorsOverZZ[i], factors[i]); genCrtTable(); genMaskTable(); }
void EncryptedArrayDerived<type>::rotate(Ctxt& ctxt, long amt) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); // Simple case: just one generator if (al.numOfGens()==1) { // VJS: bug fix: <= must be == rotate1D(ctxt, 0, amt); return; } // Make sure that amt is in [1,nslots-1] amt %= (long) al.getNSlots(); if (amt == 0) { return; } if (amt < 0) amt += al.getNSlots(); // rotate the ciphertext, one dimension at a time long i = al.numOfGens()-1; long v = al.coordinate(i, amt); RX mask = maskTable[i][v]; Ctxt tmp(ctxt.getPubKey()); const RXModulus& PhimXmod = tab.getPhimXMod(); // optimize for the common case where the last generator has order in // Zm*/(p) different than its order in Zm*. In this case we can combine // the rotate1D relative to this generator with the masking after the // rotation. This saves one mult-by-constant, since we use the same mask // inside rotate1D as in the loop below. if (al.SameOrd(i) || v==0) rotate1D(ctxt, i, v); // no need to optimize else { long ord = al.OrderOf(i); long val = PowerMod(al.ZmStarGen(i), v, al.getM()); long ival = PowerMod(al.ZmStarGen(i), v-ord, al.getM()); DoubleCRT m1(conv<ZZX>(maskTable[i][ord-v]), context, ctxt.getPrimeSet()); tmp = ctxt; // a copy of the ciphertext tmp.multByConstant(m1); // only the slots in which m1=1 ctxt -= tmp; // only the slots in which m1=0 ctxt.smartAutomorph(val); // shift left by val tmp.smartAutomorph(ival); // shift right by ord-val // apply rotation relative to next generator before combining the parts --i; v = al.coordinate(i, amt); rotate1D(ctxt, i, v); rotate1D(tmp, i, v+1); ctxt += tmp; // combine the two parts if (i <= 0) { return; } // no more generators mask = ((mask * (maskTable[i][v] - maskTable[i][v+1])) % PhimXmod) + maskTable[i][v+1]; // update the mask for next iteration } // Handle rotation relative to all the other generators (if any) for (i--; i >= 0; i--) { v = al.coordinate(i, amt); DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); tmp = ctxt; tmp.multByConstant(m1); // only the slots in which mask=1 ctxt -= tmp; // only the slots in which mask=0 rotate1D(tmp, i, v); rotate1D(ctxt, i, v+1); ctxt += tmp; if (i>0) { mask = ((mask * (maskTable[i][v] - maskTable[i][v+1])) % PhimXmod) + maskTable[i][v+1]; // update the mask for next iteration } } FHE_TIMER_STOP; }
void EncryptedArrayDerived<type>::shift(Ctxt& ctxt, long k) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); // Simple case: just one generator if (al.numOfGens()==1) { shift1D(ctxt, 0, k); return; } long nSlots = al.getNSlots(); // Shifting by more than the number of slots gives an all-zero cipehrtext if (k <= -nSlots || k >= nSlots) { ctxt.multByConstant(to_ZZX(0)); return; } // Make sure that amt is in [1,nslots-1] long amt = k % nSlots; if (amt == 0) return; if (amt < 0) amt += nSlots; // rotate the ciphertext, one dimension at a time long i = al.numOfGens()-1; long v = al.coordinate(i, amt); RX mask = maskTable[i][v]; Ctxt tmp(ctxt.getPubKey()); const RXModulus& PhimXmod = tab.getPhimXMod(); rotate1D(ctxt, i, v); for (i--; i >= 0; i--) { v = al.coordinate(i, amt); DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); tmp = ctxt; tmp.multByConstant(m1); // only the slots in which mask=1 ctxt -= tmp; // only the slots in which mask=0 if (i>0) { rotate1D(ctxt, i, v+1); rotate1D(tmp, i, v); ctxt += tmp; // combine the two parts mask = ((mask * (maskTable[i][v] - maskTable[i][v+1])) % PhimXmod) + maskTable[i][v+1]; // update the mask before next iteration } else { // i == 0 if (k < 0) v -= al.OrderOf(0); shift1D(tmp, 0, v); shift1D(ctxt, 0, v+1); ctxt += tmp; } } FHE_TIMER_STOP; }
void EncryptedArrayDerived<type>::mat_mul(Ctxt& ctxt, const PlaintextBlockMatrixBaseInterface& mat) const { FHE_TIMER_START; assert(this == &mat.getEA().getDerived(type())); assert(&context == &ctxt.getContext()); RBak bak; bak.save(); tab.restoreContext(); const PlaintextBlockMatrixInterface<type>& mat1 = dynamic_cast< const PlaintextBlockMatrixInterface<type>& >( mat ); ctxt.cleanUp(); // not sure, but this may be a good idea Ctxt res(ctxt.getPubKey(), ctxt.getPtxtSpace()); // a new ciphertext, encrypting zero long nslots = size(); long d = getDegree(); 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 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; rotate(shCtxt, i); // 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; ZZX cpoly; encode(cpoly, cvec); // FIXME: record the encoded polynomial for future use Ctxt shCtxt1 = shCtxt; shCtxt1.frobeniusAutomorph(k); shCtxt1.multByConstant(cpoly); res += shCtxt1; } } ctxt = res; }
void EncryptedArrayDerived<type>::encode(ZZX& ptxt, const NewPlaintextArray& array) const { RBak bak; bak.save(); tab.restoreContext(); encode(ptxt, array.getData<type>()); }
void EncryptedArrayDerived<type>::decode(NewPlaintextArray& array, const ZZX& ptxt) const { RBak bak; bak.save(); tab.restoreContext(); decode(array.getData<type>(), ptxt); }