void rotate(DoubleCRT& d, long amt, const vector< vector< DoubleCRT > > & maskTable) // rotate d by amt { const FHEcontext& context = d.getContext(); const PAlgebra& al = context.zMstar; // const PAlgebraModTwo& al2 = context.modTwo; long ngens = al.numOfGens(); long nslots = al.NSlots(); amt = amt % nslots; if (amt < 0) amt += nslots; if (amt == 0) return; long i, v; i = ngens-1; v = coordinate(al, i, amt); rotate1D(d, i, v, maskTable); if (i == 0) return; DoubleCRT mask(context, d.getIndexSet()); mask.SetZero(); mask.Add(maskTable[i][v], false); DoubleCRT tmp(context, d.getIndexSet()); for (i--; i >= 0; i--) { v = coordinate(al, i, amt); tmp.SetZero(); tmp += d; tmp *= mask; d -= tmp; rotate1D(d, i, v+1, maskTable); rotate1D(tmp, i, v, maskTable); d += tmp; if (i > 0) { tmp.SetZero(); tmp.Add(maskTable[i][v], false); tmp.Sub(maskTable[i][v+1], false); mask *= tmp; mask.Add(maskTable[i][v+1], false); } } }
void rotate1D(DoubleCRT& d, long i, long amt, const vector< vector< DoubleCRT > > & maskTable) // rotate d in dimension i by amt { const FHEcontext& context = d.getContext(); const PAlgebra& al = context.zMstar; // const PAlgebraModTwo& al2 = context.modTwo; long ngens = al.numOfGens(); // long nslots = al.NSlots(); assert(i >= 0 && i < ngens); long ord = al.OrderOf(i); amt = amt % ord; if (amt < 0) amt += ord; if (amt == 0) return; if (al.SameOrd(i)) { // "native" rotation long val = PowerMod(al.ZmStarGen(i), amt, al.M()); d.automorph(val); } else { // more expensive "non-native" rotation assert(maskTable[i].size() > 0); long val = PowerMod(al.ZmStarGen(i), amt, al.M()); // long ival = InvMod(val, al.M()); long ival = PowerMod(al.ZmStarGen(i), amt-ord, al.M()); const DoubleCRT& m1 = maskTable[i].at(ord-amt); DoubleCRT d1(d); d1.Mul(m1, false); d -= d1; d.automorph(val); d1.automorph(ival); d += d1; } }
// Add/subtract a ciphertext part to a ciphertext. // With negative=true we subtract, otherwise we add. void Ctxt::addPart(const DoubleCRT& part, const SKHandle& handle, bool matchPrimeSet, bool negative) { FHE_TIMER_START; assert (&part.getContext() == &context); if (parts.size()==0) { // inserting 1st part primeSet = part.getIndexSet(); parts.push_back(CtxtPart(part,handle)); if (negative) parts.back().Negate(); // not thread-safe?? } else { // adding to a ciphertext with existing parts if (!(part.getIndexSet() <= primeSet)) { // add to the the prime-set of *this, if needed (this is expensive) if (matchPrimeSet) { IndexSet setDiff = part.getIndexSet() / primeSet; // set minus for (size_t i=0; i<parts.size(); i++) parts[i].addPrimes(setDiff); primeSet.insert(setDiff); } else // this should never happen throw std::logic_error("part has too many primes and matchPrimeSet==false"); } DoubleCRT tmp(context, IndexSet::emptySet()); const DoubleCRT* ptr = ∂ // mod-UP the part if needed IndexSet s = primeSet / part.getIndexSet(); if (!empty(s)) { // if need to mod-UP, do it on a temporary copy tmp = part; tmp.addPrimesAndScale(s); ptr = &tmp; } long j = getPartIndexByHandle(handle); if (j>=0) { // found a matching part, add them up if (negative) parts[j] -= *ptr; else parts[j] += *ptr; } else { // no mathing part found, just append this part parts.push_back(CtxtPart(*ptr,handle)); if (negative) parts.back().Negate(); // not thread-safe?? } } }