Program Listing for File rgsw-cryptoparameters.cpp
↰ Return to documentation for file (binfhe/lib/rgsw-cryptoparameters.cpp)
//==================================================================================
// BSD 2-Clause License
//
// Copyright (c) 2014-2026, NJIT, Duality Technologies Inc. and other contributors
//
// All rights reserved.
//
// Author TPOC: contact@openfhe.org
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//==================================================================================
#include "rgsw-cryptoparameters.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
namespace lbcrypto {
const std::vector<NativeInteger>& RingGSWCryptoParams::PrecomputeGPower(uint32_t baseG) {
auto it = m_Gpower_map.find(baseG);
if (it != m_Gpower_map.end())
return it->second;
auto digitsG{DigitsForBase(m_Q, baseG)};
NativeInteger vTemp{1};
std::vector<NativeInteger> tempvec(digitsG);
for (uint32_t i = 0; i < digitsG; ++i) {
tempvec[i] = vTemp;
vTemp = vTemp.ModMulFast(NativeInteger(baseG), m_Q);
}
return m_Gpower_map.emplace(baseG, std::move(tempvec)).first->second;
}
void RingGSWCryptoParams::PreCompute(bool signEval) {
// Computes baseR^i (only for AP bootstrapping)
if (m_method == BINFHE_METHOD::AP) {
auto digitCountR{GetDigitCount(m_q.ConvertToInt(), m_baseR)};
m_digitsR.clear();
m_digitsR.reserve(digitCountR);
BasicInteger value{1};
for (size_t i = 0; i < digitCountR; ++i, value *= m_baseR)
m_digitsR.emplace_back(value);
}
// Computes baseG^i. The context's own base is always precomputed: sign evaluation adds
// the three bases Change_BaseG switches between, it does not replace the base in use.
PrecomputeGPower(m_baseG);
for (const auto& item : m_baseG_map)
PrecomputeGPower(item.first);
if (signEval) {
for (uint32_t baseG : {1 << 14, 1 << 18, 1 << 27})
PrecomputeGPower(baseG);
}
m_Gpower = m_Gpower_map.at(m_baseG);
// Sets the gate constants for supported binary operations
m_gateConst = {
NativeInteger(5) * (m_q >> 3), // OR
NativeInteger(7) * (m_q >> 3), // AND
NativeInteger(1) * (m_q >> 3), // NOR
NativeInteger(3) * (m_q >> 3), // NAND
NativeInteger(6) * (m_q >> 3), // XOR
NativeInteger(2) * (m_q >> 3), // XNOR
NativeInteger(7) * (m_q >> 3), // MAJORITY
NativeInteger(11) * (m_q / 12), // AND3
NativeInteger(7) * (m_q / 12), // OR3
NativeInteger(15) * (m_q >> 4), // AND4
NativeInteger(9) * (m_q >> 4), // OR4
NativeInteger(6) * (m_q >> 3), // XOR_FAST
NativeInteger(2) * (m_q >> 3) // XNOR_FAST
};
// Computes polynomials X^m - 1 that are needed in the accumulator for the
// CGGI bootstrapping
if (m_method == BINFHE_METHOD::GINX)
BuildMonomials();
// expand the base map into one entry per LWE index. Digit counts are computed here,
// once per distinct base, instead of per call in the accumulator.
m_baseGByIndex.clear();
if (!m_baseG_map.empty()) {
uint32_t total{0};
for (const auto& [baseG, count] : m_baseG_map)
total += count;
m_baseGByIndex.reserve(total);
m_teamWidth = (m_digitsG - 1) << 1;
for (const auto& [baseG, count] : m_baseG_map)
m_teamWidth = std::max(m_teamWidth, (DigitsForBase(m_Q, baseG) - 1) << 1);
for (const auto& [baseG, count] : m_baseG_map) {
auto it = m_Gpower_map.find(baseG);
if (it == m_Gpower_map.end())
OPENFHE_THROW("No GPower found for the requested gadget base.");
m_baseGByIndex.insert(
m_baseGByIndex.end(), count,
BaseGParams{baseG, DigitsForBase(m_Q, baseG), GetMSB(baseG) - 1, &it->second, m_teamWidth});
}
}
if (m_method == LMKCDEY) {
constexpr uint32_t gen{5};
m_logGen.clear();
uint32_t M{2 * m_N};
m_logGen.resize(M);
uint32_t gPow{1};
m_logGen[M - gPow] = M; // for -1
for (uint32_t i = 1; i < m_N / 2; ++i) {
gPow = (gPow * gen) % M;
m_logGen[gPow] = i;
m_logGen[M - gPow] = -i;
}
// EvalAcc only ever rotates by gen^k for k in [1, numAutoKeys] and by M - gen
m_autoMap.clear();
auto addMap = [this](uint32_t idx) {
auto& v = m_autoMap[idx];
v.resize(m_N);
PrecomputeAutoMap(m_N, idx, &v);
};
addMap(M - gen);
uint32_t idx{1};
for (uint32_t k = 1; k <= m_numAutoKeys; ++k) {
idx = (idx * gen) % M;
addMap(idx);
}
}
}
#if NATIVEINT != 32
const std::shared_ptr<ILNativeParams32>& RingGSWCryptoParams::GetPolyParams32() {
if (m_polyParams32 == nullptr) {
m_polyParams32 = std::make_shared<ILNativeParams32>(
2 * m_N, NativeInteger32(m_Q.ConvertToInt<uint32_t>()),
NativeInteger32(m_polyParams->GetRootOfUnity().ConvertToInt<uint32_t>()));
}
return m_polyParams32;
}
const std::shared_ptr<const std::vector<NativePoly32>>& RingGSWCryptoParams::GetMonomials32() {
if (m_monomials32 == nullptr) {
const auto& pp = GetPolyParams32();
// X^m - 1 built natively at 32 bits: the NTT mod Q is width-independent, so the table is
// identical to narrowing the 64-bit one, which therefore need not exist
NativeInteger32 Q32(m_Q.ConvertToInt<uint32_t>());
constexpr NativeInteger32 one{1};
auto monomials = std::make_shared<std::vector<NativePoly32>>();
monomials->reserve(2 * m_N);
for (uint32_t i = 0; i < 2 * m_N; ++i) {
NativePoly32 aPoly(pp, Format::COEFFICIENT, true);
aPoly[0].ModSubFastEq(one, Q32); // -1
if (i < m_N)
aPoly[i].ModAddFastEq(one, Q32); // X^m
else
aPoly[i - m_N].ModSubFastEq(one, Q32); // -X^m
aPoly.SetFormat(Format::EVALUATION);
monomials->push_back(std::move(aPoly));
}
m_monomials32 = std::move(monomials);
}
return m_monomials32;
}
const std::shared_ptr<const std::vector<NativeVector32>>& RingGSWCryptoParams::GetMonomialsPrecon32() {
if (m_monomialsPrecon32 == nullptr) {
const auto& monomials = *GetMonomials32();
NativeInteger32 Q32(m_Q.ConvertToInt<uint32_t>());
auto precon = std::make_shared<std::vector<NativeVector32>>();
precon->reserve(monomials.size());
for (const auto& m : monomials) {
const auto& mv = m.GetValues();
NativeVector32 p(mv.GetLength(), Q32);
for (size_t k = 0; k < mv.GetLength(); ++k)
p[k] = mv[k].PrepModMulConst(Q32);
precon->push_back(std::move(p));
}
m_monomialsPrecon32 = std::move(precon);
}
return m_monomialsPrecon32;
}
#endif // NATIVEINT != 32
void RingGSWCryptoParams::BuildMonomials() {
constexpr NativeInteger one{1};
m_monomials.clear();
m_monomials.reserve(2 * m_N);
for (uint32_t i = 0; i < m_N; ++i) {
NativePoly aPoly(m_polyParams, Format::COEFFICIENT, true);
aPoly[0].ModSubFastEq(one, m_Q); // -1
aPoly[i].ModAddFastEq(one, m_Q); // X^m
aPoly.SetFormat(Format::EVALUATION);
m_monomials.push_back(std::move(aPoly));
}
for (uint32_t i = 0; i < m_N; ++i) {
NativePoly aPoly(m_polyParams, Format::COEFFICIENT, true);
aPoly[0].ModSubFastEq(one, m_Q); // -1
aPoly[i].ModSubFastEq(one, m_Q); // -X^m
aPoly.SetFormat(Format::EVALUATION);
m_monomials.push_back(std::move(aPoly));
}
}
}; // namespace lbcrypto