void buildModChain(FHEcontext &context, long nLevels, long nDgts) { #ifdef NO_HALF_SIZE_PRIME long nPrimes = nLevels; #else long nPrimes = (nLevels+1)/2; // The first prime should be of half the size. The code below tries to find // a prime q0 of this size where q0-1 is divisible by 2^k * m for some k>1. // Then if the plaintext space is a power of two it tries to choose the // second prime q1 so that q0*q1 = 1 mod ptxtSpace. All the other primes are // chosen so that qi-1 is divisible by 2^k * m for as large k as possible. long twoM; if (ALT_CRT) twoM = 2; else twoM = 2 * context.zMStar.getM(); long bound = (1L << (context.bitsPerLevel-1)); while (twoM < bound/(2*context.bitsPerLevel)) twoM *= 2; // divisible by 2^k * m for a larger k bound = bound - (bound % twoM) +1; // = 1 mod 2m long q0 = context.AddPrime(bound, twoM, false, !ALT_CRT); // add next prime to chain assert(q0 != 0); nPrimes--; #endif // Choose the next primes as large as possible if (nPrimes>0) AddPrimesByNumber(context, nPrimes); // calculate the size of the digits if (nDgts > nPrimes) nDgts = nPrimes; // sanity checks if (nDgts <= 0) nDgts = 1; context.digits.resize(nDgts); // allocate space IndexSet s1; double sizeSoFar = 0.0; double maxDigitSize = 0.0; if (nDgts>1) { // we break ciphetext into a few digits when key-switching double dsize = context.logOfProduct(context.ctxtPrimes)/nDgts; // estimate double target = dsize-(context.bitsPerLevel/3.0); long idx = context.ctxtPrimes.first(); for (long i=0; i<nDgts-1; i++) { // compute next digit IndexSet s; while (idx <= context.ctxtPrimes.last() && (empty(s)||sizeSoFar<target)) { s.insert(idx); sizeSoFar += log((double)context.ithPrime(idx)); idx = context.ctxtPrimes.next(idx); } assert (!empty(s)); context.digits[i] = s; s1.insert(s); double thisDigitSize = context.logOfProduct(s); if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize; target += dsize; } IndexSet s = context.ctxtPrimes / s1; // all the remaining primes if (!empty(s)) { context.digits[nDgts-1] = s; double thisDigitSize = context.logOfProduct(s); if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize; } else { // If last digit is empty, remove it nDgts--; context.digits.resize(nDgts); } } else { maxDigitSize = context.logOfProduct(context.ctxtPrimes); context.digits[0] = context.ctxtPrimes; } // Add primes to the chain for the P factor of key-switching long p2r = (context.rcData.alMod)? context.rcData.alMod->getPPowR() : context.alMod.getPPowR(); double sizeOfSpecialPrimes = maxDigitSize + log(nDgts/32.0)/2 + log(context.stdev *2) + log((double)p2r); AddPrimesBySize(context, sizeOfSpecialPrimes, true); }
void buildModChain(FHEcontext &context, long nLevels, long nDgts,long extraBits) { #ifdef NO_HALF_SIZE_PRIME long nPrimes = nLevels; #else long nPrimes = (nLevels+1)/2; // The first prime should be of half the size. The code below tries to find // a prime q0 of this size where q0-1 is divisible by 2^k * m for some k>1. long twoM = 2 * context.zMStar.getM(); long bound = (1L << (context.bitsPerLevel-1)); while (twoM < bound/(2*context.bitsPerLevel)) twoM *= 2; // divisible by 2^k * m for a larger k bound = bound - (bound % twoM) +1; // = 1 mod 2m long q0 = context.AddPrime(bound, twoM, false); // add next prime to chain assert(q0 != 0); nPrimes--; #endif // Choose the next primes as large as possible if (nPrimes>0) AddPrimesByNumber(context, nPrimes); // calculate the size of the digits if (nDgts > nPrimes) nDgts = nPrimes; // sanity checks if (nDgts <= 0) nDgts = 1; context.digits.resize(nDgts); // allocate space IndexSet s1; double sizeSoFar = 0.0; double maxDigitSize = 0.0; if (nDgts>1) { // we break ciphetext into a few digits when key-switching double dsize = context.logOfProduct(context.ctxtPrimes)/nDgts; // estimate // A hack: we break the current digit after the total size of all digits // so far "almost reaches" the next multiple of dsize, upto 1/3 of a level double target = dsize-(context.bitsPerLevel/3.0); long idx = context.ctxtPrimes.first(); for (long i=0; i<nDgts-1; i++) { // set all digits but the last IndexSet s; while (idx <= context.ctxtPrimes.last() && (empty(s)||sizeSoFar<target)) { s.insert(idx); sizeSoFar += log((double)context.ithPrime(idx)); idx = context.ctxtPrimes.next(idx); } assert (!empty(s)); context.digits[i] = s; s1.insert(s); double thisDigitSize = context.logOfProduct(s); if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize; target += dsize; } // The ctxt primes that are left (if any) form the last digit IndexSet s = context.ctxtPrimes / s1; if (!empty(s)) { context.digits[nDgts-1] = s; double thisDigitSize = context.logOfProduct(s); if (maxDigitSize < thisDigitSize) maxDigitSize = thisDigitSize; } else { // If last digit is empty, remove it nDgts--; context.digits.resize(nDgts); } } else { // only one digit maxDigitSize = context.logOfProduct(context.ctxtPrimes); context.digits[0] = context.ctxtPrimes; } // Add special primes to the chain for the P factor of key-switching long p2r = context.alMod.getPPowR(); double sizeOfSpecialPrimes = maxDigitSize + log(nDgts) + log(context.stdev *2) + log((double)p2r) + (extraBits*log(2.0)); AddPrimesBySize(context, sizeOfSpecialPrimes, true); }