static void compute_a_vals(Vec<ZZ>& a, long p, long e) // computes a[m] = a(m)/m! for m = p..(e-1)(p-1)+1, // as defined by Chen and Han. // a.length() is set to (e-1)(p-1)+2 { ZZ p_to_e = power_ZZ(p, e); ZZ p_to_2e = power_ZZ(p, 2*e); long len = (e-1)*(p-1)+2; ZZ_pPush push(p_to_2e); ZZ_pX x_plus_1_to_p = power(ZZ_pX(INIT_MONO, 1) + 1, p); ZZ_pX denom = InvTrunc(x_plus_1_to_p - ZZ_pX(INIT_MONO, p), len); ZZ_pX poly = MulTrunc(x_plus_1_to_p, denom, len); poly *= p; a.SetLength(len); ZZ m_fac(1); for (long m = 2; m < p; m++) { m_fac = MulMod(m_fac, m, p_to_2e); } for (long m = p; m < len; m++) { m_fac = MulMod(m_fac, m, p_to_2e); ZZ c = rep(coeff(poly, m)); ZZ d = GCD(m_fac, p_to_2e); if (d == 0 || d > p_to_e || c % d != 0) Error("cannot divide"); ZZ m_fac_deflated = (m_fac / d) % p_to_e; ZZ c_deflated = (c / d) % p_to_e; a[m] = MulMod(c_deflated, InvMod(m_fac_deflated, p_to_e), p_to_e); } }
// This computes Chen and Han's magic polynomial G, which // has the property that G(x) = (x mod p) (mod p^e). // Here, (x mod p) is in the interval [0,1] if p == 2, // and otherwise, is in the interval (-p/2, p/2). static void compute_magic_poly(ZZX& poly1, long p, long e) { FHE_TIMER_START; Vec<ZZ> a; compute_a_vals(a, p, e); ZZ p_to_e = power_ZZ(p, e); long len = (e-1)*(p-1)+2; ZZ_pPush push(p_to_e); ZZ_pX poly(0); ZZ_pX term(1); ZZ_pX X(INIT_MONO, 1); poly = 0; term = 1; for (long m = 0; m < p; m++) { term *= (X-m); } for (long m = p; m < len; m++) { poly += term * conv<ZZ_p>(a[m]); term *= (X-m); } // replace poly by poly(X+(p-1)/2) for odd p if (p % 2 == 1) { ZZ_pX poly2(0); for (long i = deg(poly); i >= 0; i--) poly2 = poly2*(X+(p-1)/2) + poly[i]; poly = poly2; } poly = X - poly; poly1 = conv<ZZX>(poly); }
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(); }