mod modulus_switch_compression;
pub(crate) mod test_add;
pub(crate) mod test_aes;
pub(crate) mod test_aes256;
pub(crate) mod test_bitwise_op;
mod test_block_rotate;
mod test_block_shift;
pub(crate) mod test_cmux;
pub(crate) mod test_comparison;
mod test_count_zeros_ones;
pub(crate) mod test_div_mod;
pub(crate) mod test_ilog2;
#[cfg(feature = "gpu")]
pub(crate) mod test_kreyvium;
pub(crate) mod test_kv_store;
pub(crate) mod test_mul;
pub(crate) mod test_neg;
pub(crate) mod test_oprf;
pub(crate) mod test_rotate;
pub(crate) mod test_scalar_add;
pub(crate) mod test_scalar_bitwise_op;
pub(crate) mod test_scalar_comparison;
pub(crate) mod test_scalar_div_mod;
mod test_scalar_dot_prod;
pub(crate) mod test_scalar_mul;
pub(crate) mod test_scalar_rotate;
pub(crate) mod test_scalar_shift;
pub(crate) mod test_scalar_sub;
pub(crate) mod test_shift;
pub(crate) mod test_slice;
pub(crate) mod test_sub;
pub(crate) mod test_sum;
#[cfg(feature = "gpu")]
pub(crate) mod test_trivium;
pub(crate) mod test_vector_comparisons;
pub(crate) mod test_vector_find;
use super::tests_cases_unsigned::*;
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::prelude::UnsignedInteger;
use crate::integer::keycache::KEY_CACHE;
use crate::integer::oprf::OprfServerKey;
use crate::integer::server_key::radix_parallel::tests_long_run::OpSequenceFunctionExecutor;
use crate::integer::tests::create_parameterized_test;
use crate::integer::{IntegerKeyKind, RadixCiphertext, RadixClientKey, ServerKey};
use crate::shortint::ciphertext::MaxDegree;
#[cfg(tarpaulin)]
use crate::shortint::parameters::coverage_parameters::*;
use crate::shortint::parameters::test_params::*;
use crate::shortint::parameters::*;
use crate::CompressedServerKey;
use rand::prelude::ThreadRng;
use rand::Rng;
use std::sync::Arc;
use tfhe_csprng::generators::DefaultRandomGenerator;
#[cfg(not(tarpaulin))]
pub(crate) const NB_CTXT: usize = 4;
#[cfg(tarpaulin)]
pub(crate) const NB_CTXT: usize = 2;
#[cfg(not(tarpaulin))]
pub(crate) const MAX_VEC_LEN: usize = 25;
#[cfg(tarpaulin)]
pub(crate) const MAX_VEC_LEN: usize = 5;
pub(crate) const MAX_NB_CTXT: usize = 8;
pub(crate) const fn nb_unchecked_tests_for_params(params: AtomicPatternParameters) -> usize {
nb_tests_for_params(params)
}
pub(crate) const fn nb_tests_for_params(params: AtomicPatternParameters) -> usize {
let full_modulus = params.message_modulus().0 * params.carry_modulus().0;
if cfg!(tarpaulin) {
1
} else {
if full_modulus >= 1 << 8 {
return 5;
}
if full_modulus >= 1 << 6 {
return 15;
}
30
}
}
pub(crate) const fn nb_tests_smaller_for_params(params: AtomicPatternParameters) -> usize {
let full_modulus = params.message_modulus().0 * params.carry_modulus().0;
if cfg!(tarpaulin) {
1
} else {
if full_modulus >= 1 << 8 {
return 2;
}
if full_modulus >= 1 << 6 {
return 5;
}
10
}
}
pub(crate) fn random_non_zero_value(rng: &mut ThreadRng, modulus: u64) -> u64 {
rng.gen_range(1..modulus)
}
pub(crate) fn rotate_left_helper(value: u64, n: u32, actual_bit_size: u32) -> u64 {
let n = n % actual_bit_size;
let mask = 1u64.wrapping_shl(actual_bit_size) - 1;
let shifted_mask = mask.wrapping_shl(n) & !mask;
let rotated = value.rotate_left(n);
(rotated & mask) | ((rotated & shifted_mask) >> actual_bit_size)
}
pub(crate) fn block_rotate_left_helper(
value: u64,
n: u32,
num_blocks: u32,
bits_per_block: u32,
) -> u64 {
let mut max_num_bits_that_tell_shift = num_blocks.ilog2();
if !num_blocks.is_power_of_two() {
max_num_bits_that_tell_shift += 1;
}
let n = n % (1 << max_num_bits_that_tell_shift);
rotate_right_helper(value, n * bits_per_block, num_blocks * bits_per_block)
}
pub(crate) fn rotate_right_helper(value: u64, n: u32, actual_bit_size: u32) -> u64 {
let n = n % actual_bit_size;
let mask = 1u64.wrapping_shl(actual_bit_size) - 1;
let shifted_mask = mask.rotate_right(n) & !mask;
let rotated = value.rotate_right(n);
(rotated & mask) | ((rotated & shifted_mask) >> (u64::BITS - actual_bit_size))
}
pub(crate) fn block_rotate_right_helper(
value: u64,
n: u32,
num_blocks: u32,
bits_per_block: u32,
) -> u64 {
let mut max_num_bits_that_tell_shift = num_blocks.ilog2();
if !num_blocks.is_power_of_two() {
max_num_bits_that_tell_shift += 1;
}
let n = n % (1 << max_num_bits_that_tell_shift);
rotate_left_helper(value, n * bits_per_block, num_blocks * bits_per_block)
}
pub(crate) fn block_shift_right_helper(
value: u64,
n: u32,
num_blocks: u32,
bits_per_block: u32,
) -> u64 {
let mut max_num_bits_that_tell_shift = num_blocks.ilog2();
if !num_blocks.is_power_of_two() {
max_num_bits_that_tell_shift += 1;
}
let n = n % (1 << max_num_bits_that_tell_shift);
value.checked_shl(n * bits_per_block).unwrap() % (1u64 << (bits_per_block * num_blocks))
}
pub(crate) fn block_shift_left_helper(
value: u64,
n: u32,
num_blocks: u32,
bits_per_block: u32,
) -> u64 {
let mut max_num_bits_that_tell_shift = num_blocks.ilog2();
if !num_blocks.is_power_of_two() {
max_num_bits_that_tell_shift += 1;
}
let n = n % (1 << max_num_bits_that_tell_shift);
value.checked_shr(n * bits_per_block).unwrap()
}
pub(crate) fn overflowing_sub_under_modulus<T: UnsignedInteger>(
lhs: T,
rhs: T,
modulus: T,
) -> (T, bool) {
let (result, overflowed) = lhs.overflowing_sub(rhs);
(result % modulus, overflowed)
}
pub(crate) fn overflowing_add_under_modulus<T: UnsignedInteger>(
lhs: T,
rhs: T,
modulus: T,
) -> (T, bool) {
let (result, overflowed) = lhs.overflowing_add(rhs);
(result % modulus, overflowed || result >= modulus)
}
pub(crate) fn overflowing_sum_slice_under_modulus(elems: &[u64], modulus: u64) -> (u64, bool) {
let mut s = 0u64;
let mut o = false;
for e in elems.iter().copied() {
let (curr_s, curr_o) = overflowing_add_under_modulus(s, e, modulus);
s = curr_s;
o |= curr_o;
}
(s, o)
}
pub(crate) fn overflowing_mul_under_modulus(a: u64, b: u64, modulus: u64) -> (u64, bool) {
let (mut result, mut overflow) = a.overflowing_mul(b);
overflow |= result >= modulus;
result %= modulus;
(result, overflow)
}
pub(crate) fn unsigned_modulus(block_modulus: MessageModulus, num_blocks: u32) -> u64 {
block_modulus
.0
.checked_pow(num_blocks)
.expect("Modulus exceed u64::MAX")
}
pub(crate) fn unsigned_modulus_u128(block_modulus: MessageModulus, num_blocks: u32) -> u128 {
(block_modulus.0 as u128)
.checked_pow(num_blocks)
.expect("Modulus exceed u128::MAX")
}
#[track_caller]
fn panic_if_any_block_values_exceeds_its_degree<C>(ct: &RadixCiphertext, cks: &C)
where
C: AsRef<crate::integer::ClientKey>,
{
let cks = cks.as_ref();
for (i, block) in ct.blocks.iter().enumerate() {
let block_value = cks.key.decrypt_message_and_carry(block);
assert!(
block_value <= block.degree.get(),
"Block at index {i} has a value {block_value} that exceeds its degree ({:?})",
block.degree
);
}
}
#[track_caller]
fn panic_if_any_block_info_exceeds_max_degree_or_noise(
ct: &RadixCiphertext,
max_degree: MaxDegree,
max_noise_level: MaxNoiseLevel,
) {
if ct.blocks.is_empty() {
return;
}
let first_block = &ct.blocks[0];
let first_block_max_degree =
MaxDegree::from_msg_carry_modulus(first_block.message_modulus, first_block.carry_modulus);
assert!(
first_block_max_degree.validate(first_block.degree).is_ok(),
"Block at index 0 has a degree {:?} that exceeds max degree ({first_block_max_degree:?})",
first_block.degree
);
assert!(
max_noise_level.validate(first_block.noise_level()).is_ok(),
"Block at index 0 has a noise level {:?} that exceeds max noise level ({max_noise_level:?})",
first_block.degree
);
for (i, block) in ct.blocks.iter().enumerate().skip(1) {
assert!(
max_degree.validate(block.degree).is_ok(),
"Block at index {i} has a degree {:?} that exceeds max degree ({max_degree:?})",
block.degree
);
assert!(
max_noise_level.validate(block.noise_level()).is_ok(),
"Block at index {i} has a noise level {:?} that exceeds max noise level ({max_noise_level:?})",
block.degree
);
}
}
#[track_caller]
fn panic_if_any_block_is_not_clean<C>(ct: &RadixCiphertext, cks: &C)
where
C: AsRef<crate::integer::ClientKey>,
{
let cks = cks.as_ref();
let max_degree_acceptable = cks.key.parameters().message_modulus().0 - 1;
let num_blocks = ct.blocks.len();
for (i, block) in ct.blocks.iter().enumerate() {
assert_eq!(
block.noise_level(),
NoiseLevel::NOMINAL,
"Block at index {i} / {num_blocks} has a non nominal noise level: {:?}",
block.noise_level()
);
assert!(
block.degree.get() <= max_degree_acceptable,
"Block at index {i} / {num_blocks} has a degree {:?} that exceeds the maximum ({}) for a clean block",
block.degree,
max_degree_acceptable
);
let block_value = cks.key.decrypt_message_and_carry(block);
assert!(
block_value <= block.degree.get(),
"Block at index {i} has a value {block_value} that exceeds its degree ({:?})",
block.degree
);
}
}
#[track_caller]
fn panic_if_any_block_is_not_clean_or_trivial<C>(ct: &RadixCiphertext, cks: &C)
where
C: AsRef<crate::integer::ClientKey>,
{
let cks = cks.as_ref();
let max_degree_acceptable = cks.key.parameters().message_modulus().0 - 1;
for (i, block) in ct.blocks.iter().enumerate() {
if block.is_trivial() {
continue;
}
assert_eq!(
block.noise_level(),
NoiseLevel::NOMINAL,
"Block at index {i} has a non nominal noise level: {:?}",
block.noise_level()
);
assert!(
block.degree.get() <= max_degree_acceptable,
"Block at index {i} has a degree {:?} that exceeds the maximum ({}) for a clean block",
block.degree,
max_degree_acceptable
);
let block_value = cks.key.decrypt_message_and_carry(block);
assert!(
block_value <= block.degree.get(),
"Block at index {i} has a value {block_value} that exceeds its degree ({:?})",
block.degree
);
}
}
struct ExpectedValues<T> {
values: Vec<T>,
}
type ExpectedNoiseLevels = ExpectedValues<NoiseLevel>;
type ExpectedDegrees = ExpectedValues<Degree>;
impl<T> ExpectedValues<T> {
fn new(init: T, len: usize) -> Self
where
T: Clone,
{
Self {
values: vec![init; len],
}
}
fn set_with(&mut self, iter: impl Iterator<Item = T>) {
let mut self_iter = self.values.iter_mut();
self_iter
.by_ref()
.zip(iter)
.for_each(|(old_value, new_value)| {
*old_value = new_value;
});
assert!(
self_iter.next().is_none(),
"Did not update all expected values"
);
}
}
impl ExpectedNoiseLevels {
#[track_caller]
fn panic_if_any_is_not_equal(&self, ct: &RadixCiphertext) {
assert_eq!(self.values.len(), ct.blocks.len());
for (i, (block, expected_noise)) in ct
.blocks
.iter()
.zip(self.values.iter().copied())
.enumerate()
{
assert_eq!(
block.noise_level(),
expected_noise,
"Block at index {i} has noise level {:?}, but {expected_noise:?} was expected",
block.noise_level()
);
}
}
}
impl ExpectedDegrees {
#[track_caller]
fn panic_if_any_is_not_equal(&self, ct: &RadixCiphertext) {
assert_eq!(self.values.len(), ct.blocks.len());
for (i, (block, expected_degree)) in ct
.blocks
.iter()
.zip(self.values.iter().copied())
.enumerate()
{
assert_eq!(
block.degree, expected_degree,
"Block at index {i} has degree {:?}, but {expected_degree:?} was expected",
block.degree
);
}
}
#[track_caller]
fn panic_if_any_is_greater(&self, ct: &RadixCiphertext) {
assert_eq!(self.values.len(), ct.blocks.len());
for (i, (block, expected_degree)) in ct
.blocks
.iter()
.zip(self.values.iter().copied())
.enumerate()
{
assert!(
block.degree <= expected_degree,
"Block at index {i} has degree {:?}, but something less or equal (<=) than {expected_degree:?} was expected",
block.degree
);
}
}
}
create_parameterized_test!(integer_trim_radix_msb_blocks_handles_dirty_inputs);
create_parameterized_test!(
integer_full_propagate {
coverage => {
COVERAGE_PARAM_MESSAGE_2_CARRY_2_KS_PBS,
COVERAGE_PARAM_MESSAGE_2_CARRY_3_KS_PBS, COVERAGE_PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS,
},
no_coverage => {
TEST_PARAM_MESSAGE_1_CARRY_1_KS_PBS_GAUSSIAN_2M128,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
TEST_PARAM_MESSAGE_2_CARRY_3_KS_PBS_GAUSSIAN_2M128, TEST_PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128,
TEST_PARAM_MESSAGE_4_CARRY_4_KS_PBS_GAUSSIAN_2M64,
TEST_PARAM_MULTI_BIT_GROUP_2_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64,
TEST_PARAM_MULTI_BIT_GROUP_2_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M64,
TEST_PARAM_MULTI_BIT_GROUP_3_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64,
TEST_PARAM_MULTI_BIT_GROUP_3_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M64,
}
}
);
pub(crate) struct CpuFunctionExecutor<F> {
pub(crate) sks: Option<Arc<ServerKey>>,
pub(crate) func: F,
}
impl<F> CpuFunctionExecutor<F> {
pub(crate) fn new(func: F) -> Self {
Self { sks: None, func }
}
}
pub(crate) trait NotTuple {}
impl<T> NotTuple for &crate::integer::ciphertext::BaseRadixCiphertext<T> {}
impl<T> NotTuple for &crate::integer::ciphertext::BaseSignedRadixCiphertext<T> {}
impl<T> NotTuple for &mut crate::integer::ciphertext::BaseRadixCiphertext<T> {}
impl<T> NotTuple for &mut crate::integer::ciphertext::BaseSignedRadixCiphertext<T> {}
impl<T> NotTuple for &Vec<T> {}
impl NotTuple for &crate::integer::ciphertext::BooleanBlock {}
impl<F, I1, O> FunctionExecutor<I1, O> for CpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1) -> O,
I1: NotTuple,
{
fn setup(&mut self, _cks: &RadixClientKey, sks: Arc<ServerKey>) {
self.sks = Some(sks);
}
fn execute(&mut self, input: I1) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input)
}
}
impl<F, I1, I2, O> FunctionExecutor<(I1, I2), O> for CpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2) -> O,
{
fn setup(&mut self, _cks: &RadixClientKey, sks: Arc<ServerKey>) {
self.sks = Some(sks);
}
fn execute(&mut self, input: (I1, I2)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1)
}
}
impl<F, I1, I2, I3, O> FunctionExecutor<(I1, I2, I3), O> for CpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2, I3) -> O,
{
fn setup(&mut self, _cks: &RadixClientKey, sks: Arc<ServerKey>) {
self.sks = Some(sks);
}
fn execute(&mut self, input: (I1, I2, I3)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1, input.2)
}
}
impl<F, I1, I2, I3, I4, O> FunctionExecutor<(I1, I2, I3, I4), O> for CpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2, I3, I4) -> O,
{
fn setup(&mut self, _cks: &RadixClientKey, sks: Arc<ServerKey>) {
self.sks = Some(sks);
}
fn execute(&mut self, input: (I1, I2, I3, I4)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1, input.2, input.3)
}
}
pub(crate) struct OpSequenceCpuFunctionExecutor<F> {
pub(crate) sks: Option<Arc<ServerKey>>,
pub(crate) func: F,
}
impl<F> OpSequenceCpuFunctionExecutor<F> {
pub(crate) fn new(func: F) -> Self {
Self { sks: None, func }
}
pub(crate) fn setup_from_cpu_keys(&mut self, sks: &CompressedServerKey) {
let (isks, _, _, _, _, _, _, _, _) = sks.decompress().into_raw_parts();
self.sks = Some(Arc::new(isks));
}
}
impl<F, I1, O> OpSequenceFunctionExecutor<I1, O> for OpSequenceCpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1) -> O,
I1: NotTuple,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
let (isks, _, _, _, _, _, _, _, _) = sks.decompress().into_raw_parts();
self.sks = Some(Arc::new(isks));
}
fn execute(&mut self, input: I1) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input)
}
}
impl<F, I1, I2, O> OpSequenceFunctionExecutor<(I1, I2), O> for OpSequenceCpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1)
}
}
impl<F, I1, I2, I3, O> OpSequenceFunctionExecutor<(I1, I2, I3), O>
for OpSequenceCpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2, I3) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2, I3)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1, input.2)
}
}
impl<F, I1, I2, I3, I4, O> OpSequenceFunctionExecutor<(I1, I2, I3, I4), O>
for OpSequenceCpuFunctionExecutor<F>
where
F: Fn(&ServerKey, I1, I2, I3, I4) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2, I3, I4)) -> O {
let sks = self.sks.as_ref().expect("setup was not properly called");
(self.func)(sks, input.0, input.1, input.2, input.3)
}
}
pub(crate) struct CpuOprfExecutor<F> {
pub(crate) sks: Option<(OprfServerKey, ServerKey)>,
pub(crate) func: F,
}
impl<F> CpuOprfExecutor<F> {
pub(crate) fn new(func: F) -> Self {
Self { sks: None, func }
}
pub(crate) fn setup_from_cpu_keys(&mut self, sks: &CompressedServerKey) {
let (isks, _, _, _, _, _, _, oprf_key, _) = sks.decompress().into_raw_parts();
let oprf_key = oprf_key.expect("OprfServerKey is required for the CpuOprfExecutor");
self.sks = Some((oprf_key, isks));
}
}
impl<F, I1, I2, O> OpSequenceFunctionExecutor<(I1, I2), O> for CpuOprfExecutor<F>
where
F: Fn(&OprfServerKey, I1, I2, &ServerKey) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2)) -> O {
let (oprf_key, sks) = self.sks.as_ref().expect("setup was not properly called");
(self.func)(oprf_key, input.0, input.1, sks)
}
}
impl<F, I1, I2, I3, O> OpSequenceFunctionExecutor<(I1, I2, I3), O> for CpuOprfExecutor<F>
where
F: Fn(&OprfServerKey, I1, I2, I3, &ServerKey) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2, I3)) -> O {
let (oprf_key, sks) = self.sks.as_ref().expect("setup was not properly called");
(self.func)(oprf_key, input.0, input.1, input.2, sks)
}
}
impl<F, I1, I2, I3, I4, O> OpSequenceFunctionExecutor<(I1, I2, I3, I4), O> for CpuOprfExecutor<F>
where
F: Fn(&OprfServerKey, I1, I2, I3, I4, &ServerKey) -> O,
{
fn setup(
&mut self,
_cks: &RadixClientKey,
sks: &CompressedServerKey,
_seeder: &mut DeterministicSeeder<DefaultRandomGenerator>,
) {
self.setup_from_cpu_keys(sks);
}
fn execute(&mut self, input: (I1, I2, I3, I4)) -> O {
let (oprf_key, sks) = self.sks.as_ref().expect("setup was not properly called");
(self.func)(oprf_key, input.0, input.1, input.2, input.3, sks)
}
}
#[test]
#[cfg(not(tarpaulin))]
fn test_non_regression_clone_from() {
let (client_key, server_key) =
KEY_CACHE.get_from_params(PARAM_MESSAGE_2_CARRY_2, IntegerKeyKind::Radix);
let num_block: usize = 4;
let a: u8 = 248;
let b: u8 = 249;
let c: u8 = 250;
let d: u8 = 251;
let enc_a = client_key.encrypt_radix(a, num_block);
let enc_b = client_key.encrypt_radix(b, num_block);
let enc_c = client_key.encrypt_radix(c, num_block);
let enc_d = client_key.encrypt_radix(d, num_block);
let (mut q1, mut r1) = server_key.div_rem_parallelized(&enc_b, &enc_a);
let (mut q2, mut r2) = server_key.div_rem_parallelized(&enc_d, &enc_c);
assert_eq!(client_key.decrypt_radix::<u8>(&r1), 1);
assert_eq!(client_key.decrypt_radix::<u8>(&r2), 1);
assert_eq!(client_key.decrypt_radix::<u8>(&q1), 1);
assert_eq!(client_key.decrypt_radix::<u8>(&q2), 1);
let r1r2 = server_key.smart_mul_parallelized(&mut r1, &mut r2);
assert_eq!(client_key.decrypt_radix::<u8>(&r1r2), 1);
let q1q2 = server_key.smart_mul_parallelized(&mut q1, &mut q2);
assert_eq!(client_key.decrypt_radix::<u8>(&q1q2), 1);
}
fn integer_trim_radix_msb_blocks_handles_dirty_inputs<P>(param: P)
where
P: Into<TestParameters>,
{
let param = param.into();
let (client_key, server_key) = crate::integer::gen_keys_radix(param, NB_CTXT);
let modulus = param
.message_modulus()
.0
.checked_pow(NB_CTXT as u32)
.expect("modulus of ciphertext exceed u64::MAX");
let num_bits = param.message_modulus().0.ilog2() * NB_CTXT as u32;
let msg1 = 1u64 << (num_bits - 1);
let msg2 = 1u64 << (num_bits - 1);
let mut ct_1 = client_key.encrypt(msg1);
let mut ct_2 = client_key.encrypt(msg2);
server_key.extend_radix_with_trivial_zero_blocks_msb_assign(&mut ct_1, NB_CTXT);
server_key.extend_radix_with_trivial_zero_blocks_msb_assign(&mut ct_2, NB_CTXT);
let mut ct_3 = server_key.unchecked_add_parallelized(&ct_1, &ct_2);
let output: u64 = client_key.decrypt(&ct_3);
#[allow(clippy::suspicious_operation_groupings)]
{
assert_eq!(output, (msg2 + msg1) % (modulus * modulus));
}
assert_ne!(output, (msg2 + msg1) % (modulus));
server_key.trim_radix_blocks_msb_assign(&mut ct_3, NB_CTXT);
let output: u64 = client_key.decrypt(&ct_3);
assert_eq!(output, (msg2 + msg1) % (modulus));
server_key.extend_radix_with_trivial_zero_blocks_msb_assign(&mut ct_3, NB_CTXT);
let output: u64 = client_key.decrypt(&ct_3);
assert_eq!(output, (msg2 + msg1) % (modulus));
}
fn integer_full_propagate<P>(param: P)
where
P: Into<TestParameters>,
{
let executor = CpuFunctionExecutor::new(&ServerKey::full_propagate_parallelized);
full_propagate_test(param, executor);
}