Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite Prime Moduli Sampling and Scaling Factor Calculations (#910 phase 2) #929

Merged
merged 27 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3ed5909
Initial commit: adding composite scaling parameters and scaling tech …
fdiasmor Dec 12, 2024
fb50d79
Initial commit: adding composite scaling parameters and scaling tech …
fdiasmor Dec 12, 2024
d3c1a58
Composite prime generation and adaptive scaling factors.
fdiasmor Dec 16, 2024
e397a73
Propagating composite degree to mod reduce calls.
fdiasmor Dec 17, 2024
3bf393a
Handling overflow for scaling factors over 64 bits on composite scali…
fdiasmor Dec 17, 2024
51db3ca
Bug fix of overflow handling for d > 2 and sc > 64.
fdiasmor Dec 18, 2024
44b206b
Update impl of Rescale and Mod/LevelReduce methods with composite deg…
fdiasmor Dec 18, 2024
3d2c220
Minor bug fix: else case missing.
fdiasmor Dec 19, 2024
a9c9978
Update EvalPoly functions to use composite degree.
fdiasmor Dec 19, 2024
c03c8f9
Update Compress function to raise error.
fdiasmor Dec 19, 2024
470cd11
Update/Add new composite scaling examples.
fdiasmor Dec 19, 2024
6369eaa
Recalculate sizeP and update EstimateLogP interface.
fdiasmor Dec 19, 2024
5d54a9f
Update composite-prime generation function.
fdiasmor Dec 31, 2024
a5a2b9c
Remove comments from prime gen function.
fdiasmor Jan 3, 2025
9a7ad26
Update composite scaling unittests and fix unittest runtime issue.
fdiasmor Dec 25, 2024
c8c76a8
Some review changes.
fdiasmor Jan 8, 2025
4b721d9
Update composite scaling unittests and fix unittest runtime issue.
fdiasmor Dec 25, 2024
cf685de
Disable composite scaling set methods for non-CKKS schemes.
fdiasmor Jan 9, 2025
a5a443a
Move COMPOSITESCALING support error handling.
fdiasmor Jan 10, 2025
b14472d
Disable composite scaling set methods for non-CKKS schemes.
fdiasmor Jan 9, 2025
6dd35c0
Removing additional constructor that accepts composite scaling parame…
fdiasmor Jan 10, 2025
e9cdffa
Merge/rebase review changes from (#928).
fdiasmor Jan 18, 2025
80fe03f
Merge branch 'dev' into 910-composite-prime-gen
fdiasmor Jan 18, 2025
c63d0fe
Review changes for PR 910 phase 2.
fdiasmor Feb 5, 2025
2ec7adf
Review updates and fixes.
fdiasmor Feb 6, 2025
abd4585
Review updates and fixes.
fdiasmor Feb 6, 2025
d3208fa
Review updates and fixes.
fdiasmor Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 21 additions & 41 deletions src/pke/include/cryptocontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@
#include <algorithm>
#include <unordered_map>
#include <set>
#ifdef DEBUG_KEY
#include <iostream>
#endif
#include <iostream>

namespace lbcrypto {

Expand Down Expand Up @@ -1054,6 +1052,20 @@ class CryptoContextImpl : public Serializable {
return params->GetElementParams()->GetRootOfUnity();
}

/**
* GetCompositeDegree: get composite degree of the current scheme crypto context.
* @return integer value corresponding to composite degree
*/
uint32_t GetCompositeDegreeFromCtxt() const {
const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRNS>(params);
if (!cryptoParams) {
std::string errorMsg(std::string("std::dynamic_pointer_cast<CryptoParametersRNS>() failed"));
OPENFHE_THROW(errorMsg);
}

return cryptoParams->GetCompositeDegree();
}

//------------------------------------------------------------------------------
// KEYS GETTERS
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -2342,10 +2354,7 @@ class CryptoContextImpl : public Serializable {
Ciphertext<Element> Rescale(ConstCiphertext<Element> ciphertext) const {
ValidateCiphertext(ciphertext);

const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());

return GetScheme()->ModReduce(ciphertext, cryptoParams->GetCompositeDegree());
return GetScheme()->ModReduce(ciphertext, GetCompositeDegreeFromCtxt());
}

/**
Expand All @@ -2357,10 +2366,7 @@ class CryptoContextImpl : public Serializable {
void RescaleInPlace(Ciphertext<Element>& ciphertext) const {
ValidateCiphertext(ciphertext);

const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());

GetScheme()->ModReduceInPlace(ciphertext, cryptoParams->GetCompositeDegree());
GetScheme()->ModReduceInPlace(ciphertext, GetCompositeDegreeFromCtxt());
}

/**
Expand All @@ -2371,13 +2377,7 @@ class CryptoContextImpl : public Serializable {
Ciphertext<Element> ModReduce(ConstCiphertext<Element> ciphertext) const {
ValidateCiphertext(ciphertext);

if (isCKKS(m_schemeId)) {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());
return GetScheme()->ModReduce(ciphertext, cryptoParams->GetCompositeDegree());
}

return GetScheme()->ModReduce(ciphertext, BASE_NUM_LEVELS_TO_DROP);
return GetScheme()->ModReduce(ciphertext, GetCompositeDegreeFromCtxt());
}

/**
Expand All @@ -2387,14 +2387,7 @@ class CryptoContextImpl : public Serializable {
void ModReduceInPlace(Ciphertext<Element>& ciphertext) const {
ValidateCiphertext(ciphertext);

if (isCKKS(m_schemeId)) {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());
GetScheme()->ModReduceInPlace(ciphertext, cryptoParams->GetCompositeDegree());
}
else {
GetScheme()->ModReduceInPlace(ciphertext, BASE_NUM_LEVELS_TO_DROP);
}
GetScheme()->ModReduceInPlace(ciphertext, GetCompositeDegreeFromCtxt());
}

/**
Expand All @@ -2408,13 +2401,7 @@ class CryptoContextImpl : public Serializable {
size_t levels = 1) const {
ValidateCiphertext(ciphertext);

if (isCKKS(m_schemeId)) {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());
return GetScheme()->LevelReduce(ciphertext, evalKey, levels * cryptoParams->GetCompositeDegree());
}

return GetScheme()->LevelReduce(ciphertext, evalKey, levels);
return GetScheme()->LevelReduce(ciphertext, evalKey, levels * GetCompositeDegreeFromCtxt());
}

/**
Expand All @@ -2429,14 +2416,7 @@ class CryptoContextImpl : public Serializable {
return;
}

if (isCKKS(m_schemeId)) {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertext->GetCryptoContext()->GetCryptoParameters());
GetScheme()->LevelReduceInPlace(ciphertext, evalKey, levels * cryptoParams->GetCompositeDegree());
}
else {
GetScheme()->LevelReduceInPlace(ciphertext, evalKey, levels);
}
GetScheme()->LevelReduceInPlace(ciphertext, evalKey, levels * GetCompositeDegreeFromCtxt());
}
/**
* Compress - Reduces the size of ciphertext modulus to minimize the
Expand Down
3 changes: 3 additions & 0 deletions src/pke/include/scheme/ckksrns/ckksrns-advancedshe.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class AdvancedSHECKKSRNS : public AdvancedSHERNS {
public:
virtual ~AdvancedSHECKKSRNS() {}

Ciphertext<DCRTPoly> EvalMultMany(const std::vector<Ciphertext<DCRTPoly>>& ciphertextVec,
const std::vector<EvalKey<DCRTPoly>>& evalKeyVec) const override;

//------------------------------------------------------------------------------
// LINEAR WEIGHTED SUM
//------------------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions src/pke/include/schemerns/rns-leveledshe.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,15 @@ class LeveledSHERNS : public LeveledSHEBase<DCRTPoly> {

Ciphertext<DCRTPoly> Compress(ConstCiphertext<DCRTPoly> ciphertext, size_t towersLeft) const override;

////////////////////////////////////////
// SHE LEVELED ComposedEvalMult
////////////////////////////////////////

using LeveledSHEBase<DCRTPoly>::ComposedEvalMult;

Ciphertext<DCRTPoly> ComposedEvalMult(ConstCiphertext<DCRTPoly> ciphertext1, ConstCiphertext<DCRTPoly> ciphertext2,
const EvalKey<DCRTPoly> evalKey) const override;

protected:
/////////////////////////////////////
// RNS Core
Expand Down
26 changes: 26 additions & 0 deletions src/pke/lib/scheme/ckksrns/ckksrns-advancedshe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,32 @@ CKKS implementation. See https://eprint.iacr.org/2020/1118 for details.

namespace lbcrypto {

Ciphertext<DCRTPoly> AdvancedSHECKKSRNS::EvalMultMany(const std::vector<Ciphertext<DCRTPoly>>& ciphertextVec,
const std::vector<EvalKey<DCRTPoly>>& evalKeys) const {
if (ciphertextVec.size() < 1)
OPENFHE_THROW("Input ciphertext vector size should be 1 or more");

const size_t inSize = ciphertextVec.size();
const size_t lim = inSize * 2 - 2;
std::vector<Ciphertext<DCRTPoly>> ciphertextMultVec;
ciphertextMultVec.resize(inSize - 1);
size_t ctrIndex = 0;

auto algo = ciphertextVec[0]->GetCryptoContext()->GetScheme();
const auto cc = ciphertextVec[0]->GetCryptoContext();
const auto cryptoParams = std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertextVec[0]->GetCryptoParameters());
uint32_t levelsToDrop = cryptoParams->GetCompositeDegree();

for (size_t i = 0; i < lim; i = i + 2) {
ciphertextMultVec[ctrIndex] = algo->EvalMultAndRelinearize(
i < inSize ? ciphertextVec[i] : ciphertextMultVec[i - inSize],
i + 1 < inSize ? ciphertextVec[i + 1] : ciphertextMultVec[i + 1 - inSize], evalKeys);
algo->ModReduceInPlace(ciphertextMultVec[ctrIndex++], levelsToDrop);
}

return ciphertextMultVec.back();
}

//------------------------------------------------------------------------------
// LINEAR WEIGHTED SUM
//------------------------------------------------------------------------------
Expand Down
84 changes: 37 additions & 47 deletions src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,20 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr<CryptoParamete

// Determine appropriate composite degree automatically if scaling technique set to COMPOSITESCALINGAUTO
cryptoParamsCKKSRNS->ConfigureCompositeDegree(firstModSize);

uint32_t compositeDegree = cryptoParamsCKKSRNS->GetCompositeDegree();
uint32_t registerWordSize = cryptoParamsCKKSRNS->GetRegisterWordSize();

if (scalTech == COMPOSITESCALINGAUTO || scalTech == COMPOSITESCALINGMANUAL) {
if (compositeDegree > 2 && scalingModSize < 55) {
OPENFHE_THROW(
"COMPOSITESCALING Warning: There will probably not be enough prime moduli for composite degree > 2 and scaling factor < 55, prime moduli too small. Prime moduli size must generally be greater than 22, especially for larger multiplicative depth. Try increasing the scaling factor (scalingModSize) or feel free to try it using COMPOSITESCALINGMANUAL at your own risk.");
std::string errorMsg = "COMPOSITESCALING Warning:";
errorMsg +=
"There will not be enough prime moduli for composite degree > 2 and scaling factor < 55 -- prime moduli too small.";
errorMsg +=
"Prime moduli size must generally be greater than 22, especially for larger multiplicative depth.";
errorMsg += "Try increasing the scaling factor (scalingModSize).";
errorMsg += "Also, feel free to use COMPOSITESCALINGMANUAL at your own risk.";
OPENFHE_THROW(errorMsg);
}
else if (compositeDegree == 1 && registerWordSize < 64) {
OPENFHE_THROW(
Expand All @@ -88,8 +94,9 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr<CryptoParamete
}

if (registerWordSize < 24 && scalTech == COMPOSITESCALINGAUTO) {
OPENFHE_THROW(
"Register word size must be greater than or equal to 24 for COMPOSITESCALINGAUTO. Otherwise, try it with COMPOSITESCALINGMANUAL.");
std::string errorMsg = "Register word size must be greater than or equal to 24 for COMPOSITESCALINGAUTO.";
errorMsg += "Otherwise, try it with COMPOSITESCALINGMANUAL.";
OPENFHE_THROW(errorMsg);
}
}

Expand Down Expand Up @@ -166,11 +173,7 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr<CryptoParamete

uint32_t dcrtBits = scalingModSize;

if (scalTech == COMPOSITESCALINGAUTO || scalTech == COMPOSITESCALINGMANUAL) {
numPrimes *= compositeDegree;
std::cout << __FUNCTION__ << "::" << __LINE__ << " numPrimes: " << numPrimes << " qBound: " << qBound
<< std::endl;
}
numPrimes *= compositeDegree;

uint32_t vecSize = (extraModSize == 0) ? numPrimes : numPrimes + 1;
std::vector<NativeInteger> moduliQ(vecSize);
Expand Down Expand Up @@ -205,13 +208,8 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr<CryptoParamete

// Validate the ring dimension found using estimated logQ(P) against actual logQ(P)
if (stdLevel != HEStd_NotSet) {
uint32_t logActualQ = 0;
if (ksTech == HYBRID) {
logActualQ = cryptoParamsCKKSRNS->GetParamsQP()->GetModulus().GetMSB();
}
else {
logActualQ = cryptoParamsCKKSRNS->GetElementParams()->GetModulus().GetMSB();
}
uint32_t logActualQ = (ksTech == HYBRID) ? cryptoParamsCKKSRNS->GetParamsQP()->GetModulus().GetMSB() :
cryptoParamsCKKSRNS->GetElementParams()->GetModulus().GetMSB();

uint32_t nActual = StdLatticeParm::FindRingDim(distType, stdLevel, logActualQ);
if (n < nActual) {
Expand All @@ -234,16 +232,13 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
std::unordered_set<uint64_t> moduliQRecord;

// Sample q0, the first primes in the modulus chain
NativeInteger q;
uint32_t qBitSize = static_cast<double>(dcrtBits) / compositeDegree;
uint32_t remBits = dcrtBits;
uint32_t remBits = dcrtBits;

for (uint32_t d = 1; d <= compositeDegree; ++d) {
double numBits = static_cast<double>(remBits) / (compositeDegree - d + 1);
qBitSize = std::ceil(numBits);
q = FirstPrime<NativeInteger>(qBitSize, cyclOrder);
q = PreviousPrime<NativeInteger>(q, cyclOrder);
while (std::log2(q.ConvertToDouble()) > registerWordSize || std::log2(q.ConvertToDouble()) > numBits ||
uint32_t qBitSize = std::ceil(static_cast<double>(remBits) / (compositeDegree - d + 1));
NativeInteger q = FirstPrime<NativeInteger>(qBitSize, cyclOrder);
q = PreviousPrime<NativeInteger>(q, cyclOrder);
while (std::log2(q.ConvertToDouble()) > registerWordSize || std::log2(q.ConvertToDouble()) > qBitSize ||
moduliQRecord.find(q.ConvertToInt()) != moduliQRecord.end()) {
q = PreviousPrime<NativeInteger>(q, cyclOrder);
}
Expand All @@ -253,17 +248,17 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
remBits -= std::ceil(std::log2(q.ConvertToDouble()));
}

std::vector<NativeInteger> qPrev(std::ceil(static_cast<double>(compositeDegree) / 2));
std::vector<NativeInteger> qNext(compositeDegree - static_cast<uint32_t>(qPrev.size()));

if (numPrimes > 1) {
std::vector<NativeInteger> qPrev(std::ceil(static_cast<double>(compositeDegree) / 2));
std::vector<NativeInteger> qNext(compositeDegree - static_cast<uint32_t>(qPrev.size()));

// Prep to compute initial scaling factor
double sf = moduliQ[numPrimes - 1].ConvertToDouble();
for (uint32_t d = 2; d <= compositeDegree; ++d) {
sf *= moduliQ[numPrimes - d].ConvertToDouble();
}

uint32_t cnt = 1;
bool flag = true;
for (usint i = numPrimes - compositeDegree; i >= 2 * compositeDegree; i -= compositeDegree) {
// Compute initial scaling factor
sf = static_cast<double>(std::pow(sf, 2));
Expand All @@ -273,15 +268,13 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg

auto sf_sqrt = std::pow(sf, 1.0 / compositeDegree);

qBitSize = std::ceil(std::log2(sf_sqrt));

NativeInteger sfInt = std::llround(sf_sqrt);
NativeInteger sfRem = sfInt.Mod(cyclOrder);

double primeProduct = 1.0;
std::unordered_set<uint64_t> qCurrentRecord; // current prime tracker

for (uint32_t step = 0; step < static_cast<uint32_t>(qPrev.size()); ++step) {
for (size_t step = 0; step < qPrev.size(); ++step) {
qPrev[step] = sfInt - sfRem + NativeInteger(1) - NativeInteger(cyclOrder);
do {
try {
Expand All @@ -291,15 +284,14 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
OPENFHE_THROW(
"COMPOSITE SCALING previous prime sampling error. Try increasing scaling factor (scalingModSize).");
}
} while ( // std::log2(qPrev[step].ConvertToDouble()) > qBitSize ||
std::log2(qPrev[step].ConvertToDouble()) > registerWordSize ||
moduliQRecord.find(qPrev[step].ConvertToInt()) != moduliQRecord.end() ||
qCurrentRecord.find(qPrev[step].ConvertToInt()) != qCurrentRecord.end());
} while (std::log2(qPrev[step].ConvertToDouble()) > registerWordSize ||
moduliQRecord.find(qPrev[step].ConvertToInt()) != moduliQRecord.end() ||
qCurrentRecord.find(qPrev[step].ConvertToInt()) != qCurrentRecord.end());
qCurrentRecord.emplace(qPrev[step].ConvertToInt());
primeProduct *= qPrev[step].ConvertToDouble();
}

for (uint32_t step = 0; step < static_cast<uint32_t>(qNext.size()); ++step) {
for (size_t step = 0; step < qNext.size(); ++step) {
qNext[step] = sfInt - sfRem + NativeInteger(1) + NativeInteger(cyclOrder);
do {
try {
Expand All @@ -316,7 +308,7 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
primeProduct *= qNext[step].ConvertToDouble();
}

if (cnt == 0) {
if (flag == false) {
NativeInteger qPrevNext = NativeInteger(qNext[qNext.size() - 1].ConvertToInt());
while (primeProduct > sf) {
do {
Expand Down Expand Up @@ -351,7 +343,7 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
moduliQRecord.emplace(moduliQ[i - d].ConvertToInt());
}

cnt = 1;
flag = true;
}
else {
NativeInteger qNextPrev = NativeInteger(qPrev[qPrev.size() - 1].ConvertToInt());
Expand All @@ -366,10 +358,9 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
OPENFHE_THROW(
"COMPOSITE SCALING next prime sampling error. Try increasing scaling factor (scalingModSize).");
}
} while ( // std::log2(qNextPrev.ConvertToDouble()) > qBitSize ||
std::log2(qNextPrev.ConvertToDouble()) > registerWordSize ||
moduliQRecord.find(qNextPrev.ConvertToInt()) != moduliQRecord.end() ||
qCurrentRecord.find(qNextPrev.ConvertToInt()) != qCurrentRecord.end());
} while (std::log2(qNextPrev.ConvertToDouble()) > registerWordSize ||
moduliQRecord.find(qNextPrev.ConvertToInt()) != moduliQRecord.end() ||
qCurrentRecord.find(qNextPrev.ConvertToInt()) != qCurrentRecord.end());
qCurrentRecord.emplace(qNextPrev.ConvertToInt());

primeProduct /= qPrev[qPrev.size() - 1].ConvertToDouble();
Expand All @@ -390,7 +381,7 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
moduliQRecord.emplace(moduliQ[i - d].ConvertToInt());
}

cnt = 0;
flag = false;
}
} // for loop
} // if numPrimes > 1
Expand All @@ -399,10 +390,9 @@ void ParameterGenerationCKKSRNS::CompositePrimeModuliGen(std::vector<NativeInteg
OPENFHE_THROW("firstModSize must be > scalingModSize.");
}
else {
qBitSize = 0;
remBits = static_cast<uint32_t>(firstModSize);
remBits = static_cast<uint32_t>(firstModSize);
for (uint32_t d = 1; d <= compositeDegree; ++d) {
qBitSize = std::ceil(static_cast<double>(remBits) / (compositeDegree - d + 1));
uint32_t qBitSize = std::ceil(static_cast<double>(remBits) / (compositeDegree - d + 1));
// Find next prime
NativeInteger nextInteger = FirstPrime<NativeInteger>(qBitSize, cyclOrder);
nextInteger = PreviousPrime<NativeInteger>(nextInteger, cyclOrder);
Expand Down
11 changes: 1 addition & 10 deletions src/pke/lib/schemebase/base-advancedshe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,11 @@ Ciphertext<Element> AdvancedSHEBase<Element>::EvalMultMany(const std::vector<Cip

auto algo = ciphertextVec[0]->GetCryptoContext()->GetScheme();

const auto cc = ciphertextVec[0]->GetCryptoContext();

uint32_t levelsToDrop = BASE_NUM_LEVELS_TO_DROP;
if (cc->getSchemeId() == CKKSRNS_SCHEME) {
const auto cryptoParams =
std::dynamic_pointer_cast<CryptoParametersRNS>(ciphertextVec[0]->GetCryptoParameters());
levelsToDrop = cryptoParams->GetCompositeDegree();
}

for (size_t i = 0; i < lim; i = i + 2) {
ciphertextMultVec[ctrIndex] = algo->EvalMultAndRelinearize(
i < inSize ? ciphertextVec[i] : ciphertextMultVec[i - inSize],
i + 1 < inSize ? ciphertextVec[i + 1] : ciphertextMultVec[i + 1 - inSize], evalKeys);
algo->ModReduceInPlace(ciphertextMultVec[ctrIndex++], levelsToDrop);
algo->ModReduceInPlace(ciphertextMultVec[ctrIndex++], BASE_NUM_LEVELS_TO_DROP);
}

return ciphertextMultVec.back();
Expand Down
Loading