static void LocalCopyReverse(zz_pX& x, const zz_pX& a, long lo, long hi) // x[0..hi-lo] = reverse(a[lo..hi]), with zero fill // input may not alias output { long i, j, n, m; n = hi-lo+1; m = a.rep.length(); x.rep.SetLength(n); const zz_p* ap = a.rep.elts(); zz_p* xp = x.rep.elts(); for (i = 0; i < n; i++) { j = hi-i; if (j < 0 || j >= m) clear(xp[i]); else xp[i] = ap[j]; } x.normalize(); }
static void LocalCyclicReduce(zz_pX& x, const zz_pX& a, long m) // computes x = a mod X^m-1 { long n = deg(a); long i, j; zz_p accum; if (n < m) { x = a; return; } if (&x != &a) x.rep.SetLength(m); for (i = 0; i < m; i++) { accum = a.rep[i]; for (j = i + m; j <= n; j += m) add(accum, accum, a.rep[j]); x.rep[i] = accum; } if (&x == &a) x.rep.SetLength(m); x.normalize(); }
void diff(zz_pX& x, const zz_pX& a) { long n = deg(a); long i; if (n <= 0) { clear(x); return; } if (&x != &a) x.rep.SetLength(n); for (i = 0; i <= n-1; i++) { mul(x.rep[i], a.rep[i+1], i+1); } if (&x == &a) x.rep.SetLength(n); x.normalize(); }
void ShiftSub(zz_pX& U, const zz_pX& V, long n) // assumes input does not alias output { if (IsZero(V)) return; long du = deg(U); long dv = deg(V); long d = max(du, n+dv); U.rep.SetLength(d+1); long i; for (i = du+1; i <= d; i++) clear(U.rep[i]); for (i = 0; i <= dv; i++) sub(U.rep[i+n], U.rep[i+n], V.rep[i]); U.normalize(); }
// This routine recursively reduces each hypercolumn // in dimension d (viewed as a coeff vector) by Phi_{m_d}(X) // If one starts with a cube of dimension (m_1, ..., m_k), // one ends up with a cube that is effectively of dimension // phi(m_1, ..., m_k). Viewed as an element of the ring // F_p[X_1,...,X_k]/(Phi_{m_1}(X_1), ..., Phi_{m_k}(X_k)), // the cube remains unchanged. static void recursiveReduce(const CubeSlice<zz_p>& s, const Vec<zz_pXModulus>& cycVec, long d, zz_pX& tmp1, zz_pX& tmp2) { long numDims = s.getNumDims(); //OLD: assert(numDims > 0); helib::assertTrue(numDims > 0l, "CubeSlice s has negative number of dimensions"); long deg0 = deg(cycVec[d]); long posBnd = s.getProd(1); for (long pos = 0; pos < posBnd; pos++) { getHyperColumn(tmp1.rep, s, pos); tmp1.normalize(); // tmp2 may not be normalized, so clear it first clear(tmp2); rem(tmp2, tmp1, cycVec[d]); // now pad tmp2.rep with zeros to length deg0... // tmp2 may not be normalized long len = tmp2.rep.length(); tmp2.rep.SetLength(deg0); for (long i = len; i < deg0; i++) tmp2.rep[i] = 0; setHyperColumn(tmp2.rep, s, pos); } if (numDims == 1) return; for (long i = 0; i < deg0; i++) recursiveReduce(CubeSlice<zz_p>(s, i), cycVec, d+1, tmp1, tmp2); }
void BluesteinInit(long n, const zz_p& root, zz_pX& powers, Vec<mulmod_precon_t>& powers_aux, fftRep& Rb) { long p = zz_p::modulus(); zz_p one; one=1; powers.SetMaxLength(n); SetCoeff(powers,0,one); for (long i=1; i<n; i++) { long iSqr = MulMod(i, i, 2*n); // i^2 mod 2n SetCoeff(powers,i, power(root,iSqr)); // powers[i] = root^{i^2} } // powers_aux tracks powers powers_aux.SetLength(n); for (long i = 0; i < n; i++) powers_aux[i] = PrepMulModPrecon(rep(powers[i]), p); long k = NextPowerOfTwo(2*n-1); long k2 = 1L << k; // k2 = 2^k Rb.SetSize(k); zz_pX b(INIT_SIZE, k2); zz_p rInv = inv(root); SetCoeff(b,n-1,one); // b[n-1] = 1 for (long i=1; i<n; i++) { long iSqr = MulMod(i, i, 2*n); // i^2 mod 2n zz_p bi = power(rInv,iSqr); SetCoeff(b,n-1+i, bi); // b[n-1+i] = b[n-1-i] = root^{-i^2} SetCoeff(b,n-1-i,bi); } TofftRep(Rb, b, k); }
void recursiveEval(const CubeSlice<zz_p>& s, const Vec< copied_ptr<FFTHelper> >& multiEvalPoints, long d, zz_pX& tmp1, Vec<zz_p>& tmp2) { long numDims = s.getNumDims(); assert(numDims > 0); if (numDims > 1) { long dim0 = s.getDim(0); for (long i = 0; i < dim0; i++) recursiveEval(CubeSlice<zz_p>(s, i), multiEvalPoints, d+1, tmp1, tmp2); } long posBnd = s.getProd(1); for (long pos = 0; pos < posBnd; pos++) { getHyperColumn(tmp1.rep, s, pos); tmp1.normalize(); multiEvalPoints[d]->FFT(tmp1, tmp2); setHyperColumn(tmp2, s, pos); } }
void Cmodulus::iFFT(zz_pX &x, const vec_long& y)const { FHE_TIMER_START; zz_pBak bak; bak.save(); context.restore(); if (zMStar->getPow2()) { // special case when m is a power of 2 long k = zMStar->getPow2(); long phim = (1L << (k-1)); long p = zz_p::modulus(); const zz_p *ipowers_p = (*ipowers).rep.elts(); const mulmod_precon_t *ipowers_aux_p = ipowers_aux.elts(); const long *yp = y.elts(); vec_long& tmp = Cmodulus::getScratch_vec_long(); tmp.SetLength(phim); long *tmp_p = tmp.elts(); #ifdef FHE_OPENCL AltFFTRev1(tmp_p, yp, k-1, *altFFTInfo); #else FFTRev1(tmp_p, yp, k-1, *zz_pInfo->p_info); #endif x.rep.SetLength(phim); zz_p *xp = x.rep.elts(); for (long i = 0; i < phim; i++) xp[i].LoopHole() = MulModPrecon(tmp_p[i], rep(ipowers_p[i]), p, ipowers_aux_p[i]); x.normalize(); return; } zz_p rt; long m = getM(); // convert input to zpx format, initializing only the coeffs i s.t. (i,m)=1 x.rep.SetLength(m); long i,j; for (i=j=0; i<m; i++) if (zMStar->inZmStar(i)) x.rep[i].LoopHole() = y[j++]; // DIRT: y[j] already reduced x.normalize(); conv(rt, rInv); // convert rInv to zp format BluesteinFFT(x, m, rt, *ipowers, ipowers_aux, *iRb); // call the FFT routine // reduce the result mod (Phi_m(X),q) and copy to the output polynomial x { FHE_NTIMER_START(iFFT_division); rem(x, x, *phimx); // out %= (Phi_m(X),q) } // normalize zz_p mm_inv; conv(mm_inv, m_inv); x *= mm_inv; }