// powVec[d] = p_d^{e_d} // cycVec[d] = Phi_{p_d^{e_d}}(X) mod p void computeCycVec(Vec<zz_pX>& cycVec, const Vec<long>& powVec) { long k = powVec.length(); cycVec.SetLength(k); for (long d = 0; d < k; d++) { ZZX PhimX = Cyclotomic(powVec[d]); cycVec[d] = conv<zz_pX>(PhimX); } }
PowerfulTranslationIndexes::PowerfulTranslationIndexes(const Vec<long>& mv): mvec(mv) // copy the vector of factors { // mvec contains the prime-power factorization of m = \prod_{i=1}^k mi long nfactors = mvec.length(); // = k m = computeProd(mvec); // compute m itself // phivec holds phi(mi) for all factors mi phivec.SetLength(nfactors); for (long i = 0; i < nfactors; i++) phivec[i] = phi_N(mvec[i]); phim = computeProd(phivec); // phi(m) = prod_i phi(mi) computeDivVec(divvec, m, mvec); // divvec[i] = m/mi computeInvVec(invvec, divvec, mvec); // invvec[i] = (m/mi)^{-1} mod mi // Let (i_1,...,i_k) be the representation of i in base // (m/m1,...,m/mk), namely i = i_1 (m/m_1)+...+i_k (m/m_k) mod m. // Then polyToCubeMap[i] is the lexicographic index of the tuple // (i_1,...,i_k) in the cube with dimensions (m_1, ..., m_k). // cubeToPolyMap is the inverse map, polyToCubeMap[cubeToPolyMap[j]]=j. longSig.initSignature(mvec); shortSig.initSignature(phivec); computePowerToCubeMap(polyToCubeMap, cubeToPolyMap, m, mvec, invvec, longSig); // shortSig is a CubeSignature for (phi(m_1),..., phi(m_k)), and longSig // is a CubeSignature for (m_1, ..., m_k). shortToLongMap[i] maps an // index i wrt shortSig to an index i' wrt longSig so that both indexes // correspond to the same tuple (i_1,...,i_k). computeShortToLongMap(shortToLongMap, shortSig, longSig); cycVec.SetLength(nfactors); for (long d = 0; d < nfactors; d++) cycVec[d] = Cyclotomic(mvec[d]); phimX = Cyclotomic(m); }
FFTHelper::FFTHelper(long _m, zz_p x) { m = _m; m_inv = 1/conv<zz_p>(m); root = conv<zz_p>( SqrRootMod( conv<ZZ>(x), conv<ZZ>(zz_p::modulus())) ); // NOTE: the previous line is a pain because NTL does not have // a single-precision variant of SqrRootMod... iroot = 1/root; phim = 0; coprime.SetLength(m); for (long i = 0; i < m; i++) { coprime[i] = (GCD(i, m) == 1); if (coprime[i]) phim++; } build(phimx, conv<zz_pX>( Cyclotomic(m) )); }
PAlgebra::PAlgebra(unsigned long mm, unsigned long pp, const vector<long>& _gens, const vector<long>& _ords ) { assert( ProbPrime(pp) ); assert( (mm % pp) != 0 ); assert( mm < NTL_SP_BOUND ); assert( mm > 1 ); cM = 1.0; // default value for the ring constant m = mm; p = pp; long k = NextPowerOfTwo(m); if (mm == (1UL << k)) pow2 = k; else pow2 = 0; // For dry-run, use a tiny m value for the PAlgebra tables if (isDryRun()) mm = (p==3)? 4 : 3; // Compute the generators for (Z/mZ)^* (defined in NumbTh.cpp) if (_gens.size() == 0 || isDryRun()) ordP = findGenerators(this->gens, this->ords, mm, pp); else { assert(_gens.size() == _ords.size()); gens = _gens; ords = _ords; ordP = multOrd(pp, mm); } nSlots = qGrpOrd(); phiM = ordP * nSlots; // Allocate space for the various arrays T.resize(nSlots); dLogT.resize(nSlots*gens.size()); Tidx.assign(mm,-1); // allocate m slots, initialize them to -1 zmsIdx.assign(mm,-1); // allocate m slots, initialize them to -1 long i, idx; for (i=idx=0; i<(long)mm; i++) if (GCD(i,mm)==1) zmsIdx[i] = idx++; // Now fill the Tidx and dLogT translation tables. We identify an element // t\in T with its representation t = \prod_{i=0}^n gi^{ei} mod m (where // the gi's are the generators in gens[]) , represent t by the vector of // exponents *in reverse order* (en,...,e1,e0), and order these vectors // in lexicographic order. // FIXME: is the comment above about reverse order true? It doesn't // seem like it to me. VJS. // buffer is initialized to all-zero, which represents 1=\prod_i gi^0 vector<unsigned long> buffer(gens.size()); // temporaty holds exponents i = idx = 0; long ctr = 0; do { ctr++; unsigned long t = exponentiate(buffer); for (unsigned long j=0; j<buffer.size(); j++) dLogT[idx++] = buffer[j]; assert(GCD(t,mm) == 1); // sanity check for user-supplied gens assert(Tidx[t] == -1); T[i] = t; // The i'th element in T it t Tidx[t] = i++; // the index of t in T is i // increment buffer by one (in lexigoraphic order) } while (nextExpVector(buffer)); // until we cover all the group assert(ctr == long(nSlots)); // sanity check for user-supplied gens PhimX = Cyclotomic(mm); // compute and store Phi_m(X) // initialize prods array long ndims = gens.size(); prods.resize(ndims+1); prods[ndims] = 1; for (long j = ndims-1; j >= 0; j--) { prods[j] = OrderOf(j) * prods[j+1]; } // pp_factorize(mFactors,mm); // prime-power factorization from NumbTh.cpp }
// Generate the representation of Z_m^* for a given odd integer m // and plaintext base p PAlgebra::PAlgebra(unsigned long mm, unsigned long pp) { m = mm; p = pp; assert( (m&1) == 1 ); assert( ProbPrime(p) ); // replaced by Mahdi after a conversation with Shai // assert( m > p && (m % p) != 0 ); // original line assert( (m % p) != 0 ); // end of replace by Mahdi assert( m < NTL_SP_BOUND ); // Compute the generators for (Z/mZ)^* vector<unsigned long> classes(m); vector<long> orders(m); unsigned long i; for (i=0; i<m; i++) { // initially each element in its own class if (GCD(i,m)!=1) classes[i] = 0; // i is not in (Z/mZ)^* else classes[i] = i; } // Start building a representation of (Z/mZ)^*, first use the generator p conjClasses(classes,p,m); // merge classes that have a factor of 2 // The order of p is the size of the equivalence class of 1 ordP = (unsigned long) count (classes.begin(), classes.end(), 1); // Compute orders in (Z/mZ)^*/<p> while comparing to (Z/mZ)^* long idx, largest; while (true) { compOrder(orders,classes,true,m); idx = argmax(orders); // find the element with largest order largest = orders[idx]; if (largest <= 0) break; // stop comparing to order in (Z/mZ)^* // store generator with same order as in (Z/mZ)^* gens.push_back(idx); ords.push_back(largest); conjClasses(classes,idx,m); // merge classes that have a factor of idx } // Compute orders in (Z/mZ)^*/<p> without comparing to (Z/mZ)^* while (true) { compOrder(orders,classes,false,m); idx = argmax(orders); // find the element with largest order largest = orders[idx]; if (largest <= 0) break; // we have the trivial group, we are done // store generator with different order than (Z/mZ)^* gens.push_back(idx); ords.push_back(-largest); // store with negative sign conjClasses(classes,idx,m); // merge classes that have a factor of idx } nSlots = qGrpOrd(); phiM = ordP * nSlots; // Allocate space for the various arrays T.resize(nSlots); dLogT.resize(nSlots*gens.size()); Tidx.assign(m,-1); // allocate m slots, initialize them to -1 zmsIdx.assign(m,-1); // allocate m slots, initialize them to -1 for (i=idx=0; i<m; i++) if (GCD(i,m)==1) zmsIdx[i] = idx++; // Now fill the Tidx and dLogT translation tables. We identify an element // t\in T with its representation t = \prod_{i=0}^n gi^{ei} mod m (where // the gi's are the generators in gens[]) , represent t by the vector of // exponents *in reverse order* (en,...,e1,e0), and order these vectors // in lexicographic order. // buffer is initialized to all-zero, which represents 1=\prod_i gi^0 vector<unsigned long> buffer(gens.size()); // temporaty holds exponents i = idx = 0; do { unsigned long t = exponentiate(buffer); for (unsigned long j=0; j<buffer.size(); j++) dLogT[idx++] = buffer[j]; T[i] = t; // The i'th element in T it t Tidx[t] = i++; // the index of t in T is i // increment buffer by one (in lexigoraphic order) } while (nextExpVector(buffer)); // until we cover all the group PhimX = Cyclotomic(m); // compute and store Phi_m(X) // initialize prods array long ndims = gens.size(); prods.resize(ndims+1); prods[ndims] = 1; for (long j = ndims-1; j >= 0; j--) { prods[j] = OrderOf(j) * prods[j+1]; } }
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"; }