in native/src/seal/ckks.h [635:741]
void decode_internal(const Plaintext &plain, T *destination, MemoryPoolHandle pool)
{
// Verify parameters.
if (!is_valid_for(plain, context_))
{
throw std::invalid_argument("plain is not valid for encryption parameters");
}
if (!plain.is_ntt_form())
{
throw std::invalid_argument("plain is not in NTT form");
}
if (!destination)
{
throw std::invalid_argument("destination cannot be null");
}
if (!pool)
{
throw std::invalid_argument("pool is uninitialized");
}
auto &context_data = *context_.get_context_data(plain.parms_id());
auto &parms = context_data.parms();
std::size_t coeff_modulus_size = parms.coeff_modulus().size();
std::size_t coeff_count = parms.poly_modulus_degree();
std::size_t rns_poly_uint64_count = util::mul_safe(coeff_count, coeff_modulus_size);
auto ntt_tables = context_data.small_ntt_tables();
// Check that scale is positive and not too large
if (plain.scale() <= 0 ||
(static_cast<int>(log2(plain.scale())) >= context_data.total_coeff_modulus_bit_count()))
{
throw std::invalid_argument("scale out of bounds");
}
auto decryption_modulus = context_data.total_coeff_modulus();
auto upper_half_threshold = context_data.upper_half_threshold();
int logn = util::get_power_of_two(coeff_count);
// Quick sanity check
if ((logn < 0) || (coeff_count < SEAL_POLY_MOD_DEGREE_MIN) || (coeff_count > SEAL_POLY_MOD_DEGREE_MAX))
{
throw std::logic_error("invalid parameters");
}
double inv_scale = double(1.0) / plain.scale();
// Create mutable copy of input
auto plain_copy(util::allocate_uint(rns_poly_uint64_count, pool));
util::set_uint(plain.data(), rns_poly_uint64_count, plain_copy.get());
// Transform each polynomial from NTT domain
for (std::size_t i = 0; i < coeff_modulus_size; i++)
{
util::inverse_ntt_negacyclic_harvey(plain_copy.get() + (i * coeff_count), ntt_tables[i]);
}
// CRT-compose the polynomial
context_data.rns_tool()->base_q()->compose_array(plain_copy.get(), coeff_count, pool);
// Create floating-point representations of the multi-precision integer coefficients
double two_pow_64 = std::pow(2.0, 64);
auto res(util::allocate<std::complex<double>>(coeff_count, pool));
for (std::size_t i = 0; i < coeff_count; i++)
{
res[i] = 0.0;
if (util::is_greater_than_or_equal_uint(
plain_copy.get() + (i * coeff_modulus_size), upper_half_threshold, coeff_modulus_size))
{
double scaled_two_pow_64 = inv_scale;
for (std::size_t j = 0; j < coeff_modulus_size; j++, scaled_two_pow_64 *= two_pow_64)
{
if (plain_copy[i * coeff_modulus_size + j] > decryption_modulus[j])
{
auto diff = plain_copy[i * coeff_modulus_size + j] - decryption_modulus[j];
res[i] += diff ? static_cast<double>(diff) * scaled_two_pow_64 : 0.0;
}
else
{
auto diff = decryption_modulus[j] - plain_copy[i * coeff_modulus_size + j];
res[i] -= diff ? static_cast<double>(diff) * scaled_two_pow_64 : 0.0;
}
}
}
else
{
double scaled_two_pow_64 = inv_scale;
for (std::size_t j = 0; j < coeff_modulus_size; j++, scaled_two_pow_64 *= two_pow_64)
{
auto curr_coeff = plain_copy[i * coeff_modulus_size + j];
res[i] += curr_coeff ? static_cast<double>(curr_coeff) * scaled_two_pow_64 : 0.0;
}
}
// Scaling instead incorporated above; this can help in cases
// where otherwise pow(two_pow_64, j) would overflow due to very
// large coeff_modulus_size and very large scale
// res[i] = res_accum * inv_scale;
}
fft_handler_.transform_to_rev(res.get(), logn, root_powers_.get());
for (std::size_t i = 0; i < slots_; i++)
{
destination[i] = from_complex<T>(res[static_cast<std::size_t>(matrix_reps_index_map_[i])]);
}
}