void EncryptedArrayDerived<type>::shift1D(Ctxt& ctxt, long i, long k) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); assert(i >= 0 && i < (long)al.numOfGens()); long ord = al.OrderOf(i); if (k <= -ord || k >= ord) { ctxt.multByConstant(to_ZZX(0)); return; } // Make sure amt is in the range [1,ord-1] long amt = k % ord; if (amt == 0) return; if (amt < 0) amt += ord; RX mask = maskTable[i][ord-amt]; long val; if (k < 0) val = PowerMod(al.ZmStarGen(i), amt-ord, al.getM()); else { mask = 1 - mask; val = PowerMod(al.ZmStarGen(i), amt, al.getM()); } DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); ctxt.multByConstant(m1); // zero out slots where mask=0 ctxt.smartAutomorph(val); // shift left by val FHE_TIMER_STOP; }
// computes ctxt^{2^d-1} using a method that takes // O(log d) automorphisms and multiplications void fastPower(Ctxt& ctxt, long d) { assert(ctxt.getPtxtSpace()==2); if (d <= 1) return; Ctxt orig = ctxt; long k = NumBits(d); long e = 1; for (long i = k-2; i >= 0; i--) { Ctxt tmp1 = ctxt; tmp1.smartAutomorph(1L << e); ctxt.multiplyBy(tmp1); e = 2*e; if (bit(d, i)) { ctxt.smartAutomorph(2); ctxt.multiplyBy(orig); e += 1; } } }
void EncryptedArrayDerived<type>::rotate(Ctxt& ctxt, long amt) const { FHE_TIMER_START; const PAlgebra& al = context.zMStar; const vector< vector< RX > >& maskTable = tab.getMaskTable(); RBak bak; bak.save(); tab.restoreContext(); assert(&context == &ctxt.getContext()); // Simple case: just one generator if (al.numOfGens()==1) { // VJS: bug fix: <= must be == rotate1D(ctxt, 0, amt); return; } // Make sure that amt is in [1,nslots-1] amt %= (long) al.getNSlots(); if (amt == 0) { return; } if (amt < 0) amt += al.getNSlots(); // rotate the ciphertext, one dimension at a time long i = al.numOfGens()-1; long v = al.coordinate(i, amt); RX mask = maskTable[i][v]; Ctxt tmp(ctxt.getPubKey()); const RXModulus& PhimXmod = tab.getPhimXMod(); // optimize for the common case where the last generator has order in // Zm*/(p) different than its order in Zm*. In this case we can combine // the rotate1D relative to this generator with the masking after the // rotation. This saves one mult-by-constant, since we use the same mask // inside rotate1D as in the loop below. if (al.SameOrd(i) || v==0) rotate1D(ctxt, i, v); // no need to optimize else { long ord = al.OrderOf(i); long val = PowerMod(al.ZmStarGen(i), v, al.getM()); long ival = PowerMod(al.ZmStarGen(i), v-ord, al.getM()); DoubleCRT m1(conv<ZZX>(maskTable[i][ord-v]), context, ctxt.getPrimeSet()); tmp = ctxt; // a copy of the ciphertext tmp.multByConstant(m1); // only the slots in which m1=1 ctxt -= tmp; // only the slots in which m1=0 ctxt.smartAutomorph(val); // shift left by val tmp.smartAutomorph(ival); // shift right by ord-val // apply rotation relative to next generator before combining the parts --i; v = al.coordinate(i, amt); rotate1D(ctxt, i, v); rotate1D(tmp, i, v+1); ctxt += tmp; // combine the two parts if (i <= 0) { return; } // no more generators mask = ((mask * (maskTable[i][v] - maskTable[i][v+1])) % PhimXmod) + maskTable[i][v+1]; // update the mask for next iteration } // Handle rotation relative to all the other generators (if any) for (i--; i >= 0; i--) { v = al.coordinate(i, amt); DoubleCRT m1(conv<ZZX>(mask), context, ctxt.getPrimeSet()); tmp = ctxt; tmp.multByConstant(m1); // only the slots in which mask=1 ctxt -= tmp; // only the slots in which mask=0 rotate1D(tmp, i, v); rotate1D(ctxt, i, v+1); ctxt += tmp; if (i>0) { mask = ((mask * (maskTable[i][v] - maskTable[i][v+1])) % PhimXmod) + maskTable[i][v+1]; // update the mask for next iteration } } FHE_TIMER_STOP; }