// 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>::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; }
int main(int argc, char *argv[]) { argmap_t argmap; argmap["p"] = "5"; argmap["m"] = "101"; argmap["r"] = "1"; if (!parseArgs(argc, argv, argmap)) usage(argv[0]); long p = atoi(argmap["p"]); long m = atoi(argmap["m"]); long r = atoi(argmap["r"]); cout << "p=" << p << ", m=" << m << ", r=" << r << "\n"; ZZX phimx = Cyclotomic(m); zz_p::init(p); zz_pX phimx_modp = to_zz_pX(phimx); vec_zz_pX factors = SFCanZass(phimx_modp); vec_ZZX FFactors; MultiLift(FFactors, factors, phimx, r); zz_p::init(power_long(p, r)); vec_zz_pX Factors; Factors.SetLength(FFactors.length()); for (long i = 0; i < Factors.length(); i++) conv(Factors[i], FFactors[i]); zz_pX G = Factors[0]; long d = deg(G); cout << "d=" << d << "\n"; zz_pE::init(G); // L selects the even coefficients vec_zz_pE L; L.SetLength(d); for (long j = 0; j < d; j++) { if (j % 2 == 0) L[j] = to_zz_pE(zz_pX(j, 1)); } vec_zz_pE C; buildLinPolyCoeffs(C, L, p, r); zz_pE alpha, beta; random(alpha); applyLinPoly(beta, C, alpha, p); cout << alpha << "\n"; cout << beta << "\n"; }