// Add a constant polynomial void Ctxt::addConstant(const ZZ& c) { DoubleCRT dcrt(getContext(), getPrimeSet()); long cc = rem(c, ptxtSpace); // reduce modulo plaintext space dcrt = cc; if (cc > ptxtSpace/2) cc -= ptxtSpace; double size = to_double(cc); addConstant(dcrt, size*size); }
// Find the IndexSet such that modDown to that set of primes makes the // additive term due to rounding into the dominant noise term void Ctxt::findBaseSet(IndexSet& s) const { if (getNoiseVar()<=0.0) { // an empty ciphertext s = context.ctxtPrimes; return; } assert(verifyPrimeSet()); bool halfSize = context.containsSmallPrime(); double addedNoise = log(modSwitchAddedNoiseVar())/2; double curNoise = log(getNoiseVar())/2; double firstNoise = context.logOfPrime(0); // remove special primes, if they are included in this->primeSet s = getPrimeSet(); if (!s.disjointFrom(context.specialPrimes)) { // scale down noise curNoise -= context.logOfProduct(context.specialPrimes); s.remove(context.specialPrimes); } if (curNoise<=2*addedNoise) return; // no need to mod down // if the first prime in half size, begin by removing it if (halfSize && s.contains(0)) { curNoise -= firstNoise; s.remove(0); } // while noise is larger than added term, scale down by the next prime while (curNoise>addedNoise && card(s)>1) { curNoise -= context.logOfPrime(s.last()); s.remove(s.last()); } if (halfSize) { // If noise is still too big, drop last big prime and insert half-size prime if (curNoise>addedNoise) { curNoise = firstNoise; s = IndexSet(0); } // Otherwise check if you can add back the half-size prime else if (curNoise+firstNoise <= addedNoise) { curNoise += firstNoise; s.insert(0); } } if (curNoise>addedNoise && log_of_ratio()>-0.5) cerr << "Ctxt::findBaseSet warning: already at lowest level\n"; }
// Divide a cipehrtext by 2. It is assumed that the ciphertext // encrypts an even polynomial and has plaintext space 2^r for r>1. // As a side-effect, the plaintext space is halved from 2^r to 2^{r-1} // If these assumptions are not met then the result will not be a // valid ciphertext anymore. void Ctxt::divideBy2() { // Special case: if *this is empty then do nothing if (this->isEmpty()) return; assert (ptxtSpace % 2 == 0 && ptxtSpace>2); // multiply all the parts by (productOfPrimes+1)/2 ZZ twoInverse; // set to (Q+1)/2 getContext().productOfPrimes(twoInverse, getPrimeSet()); twoInverse += 1; twoInverse /= 2; for (size_t i=0; i<parts.size(); i++) parts[i] *= twoInverse; noiseVar /= 4; // noise is halved by this operation ptxtSpace /= 2; // and so is the plaintext space }
// Divide a cipehrtext by p, for plaintext space p^r, r>1. It is assumed // that the ciphertext encrypts a polynomial which is zero mod p. If this // is not the case then the result will not be a valid ciphertext anymore. // As a side-effect, the plaintext space is reduced from p^r to p^{r-1}. void Ctxt::divideByP() { // Special case: if *this is empty then do nothing if (this->isEmpty()) return; long p = getContext().zMStar.getP(); assert (ptxtSpace>p); // multiply all the parts by p^{-1} mod Q (Q=productOfPrimes) ZZ pInverse, Q; getContext().productOfPrimes(Q, getPrimeSet()); InvMod(pInverse, conv<ZZ>(p), Q); for (size_t i=0; i<parts.size(); i++) parts[i] *= pInverse; noiseVar /= (p * (double)p); // noise is reduced by a p factor ptxtSpace /= p; // and so is the plaintext space }