Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(integer): improve {overflowing_}scalar_add/sub
Browse files Browse the repository at this point in the history
tmontaigu committed Jul 29, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent f937524 commit 300cc68
Showing 10 changed files with 1,155 additions and 569 deletions.
10 changes: 5 additions & 5 deletions tfhe/src/high_level_api/integers/signed/overflowing_ops.rs
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ where
impl<Id, Clear> OverflowingAdd<Clear> for &FheInt<Id>
where
Id: FheIntId,
Clear: SignedNumeric + DecomposableInto<u64>,
Clear: SignedNumeric + DecomposableInto<u64> + DecomposableInto<u8>,
{
type Output = FheInt<Id>;

@@ -155,7 +155,7 @@ where
impl<Id, Clear> OverflowingAdd<Clear> for FheInt<Id>
where
Id: FheIntId,
Clear: SignedNumeric + DecomposableInto<u64>,
Clear: SignedNumeric + DecomposableInto<u64> + DecomposableInto<u8>,
{
type Output = Self;

@@ -192,7 +192,7 @@ where
impl<Id, Clear> OverflowingAdd<&FheInt<Id>> for Clear
where
Id: FheIntId,
Clear: SignedNumeric + DecomposableInto<u64>,
Clear: SignedNumeric + DecomposableInto<u64> + DecomposableInto<u8>,
{
type Output = FheInt<Id>;

@@ -318,7 +318,7 @@ where
impl<Id, Clear> OverflowingSub<Clear> for &FheInt<Id>
where
Id: FheIntId,
Clear: SignedNumeric + DecomposableInto<u64>,
Clear: SignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = Clear>,
{
type Output = FheInt<Id>;

@@ -369,7 +369,7 @@ where
impl<Id, Clear> OverflowingSub<Clear> for FheInt<Id>
where
Id: FheIntId,
Clear: SignedNumeric + DecomposableInto<u64>,
Clear: SignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = Clear>,
{
type Output = Self;

4 changes: 2 additions & 2 deletions tfhe/src/high_level_api/integers/unsigned/overflowing_ops.rs
Original file line number Diff line number Diff line change
@@ -323,7 +323,7 @@ where
impl<Id, Clear> OverflowingSub<Clear> for &FheUint<Id>
where
Id: FheUintId,
Clear: UnsignedNumeric + DecomposableInto<u8>,
Clear: UnsignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = Clear>,
{
type Output = FheUint<Id>;

@@ -371,7 +371,7 @@ where
impl<Id, Clear> OverflowingSub<Clear> for FheUint<Id>
where
Id: FheUintId,
Clear: UnsignedNumeric + DecomposableInto<u8>,
Clear: UnsignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = Clear>,
{
type Output = Self;

375 changes: 195 additions & 180 deletions tfhe/src/integer/server_key/radix_parallel/add.rs

Large diffs are not rendered by default.

716 changes: 646 additions & 70 deletions tfhe/src/integer/server_key/radix_parallel/scalar_add.rs

Large diffs are not rendered by default.

133 changes: 55 additions & 78 deletions tfhe/src/integer/server_key/radix_parallel/scalar_sub.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::core_crypto::prelude::{SignedNumeric, UnsignedNumeric};
use crate::integer::block_decomposition::{BlockDecomposer, DecomposableInto};
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::ciphertext::IntegerRadixCiphertext;
use crate::integer::server_key::radix::scalar_sub::TwosComplementNegation;
use crate::integer::{BooleanBlock, RadixCiphertext, ServerKey, SignedRadixCiphertext};
@@ -105,51 +105,71 @@ impl ServerKey {
self.full_propagate_parallelized(ct);
};

let Some(decomposer) = self.create_negated_block_decomposer(scalar) else {
// subtraction by zero
if Scalar::ZERO == scalar {
return;
};
}

let blocks = decomposer
.take(ct.blocks().len())
.map(|v| self.key.create_trivial(u64::from(v)))
.collect::<Vec<_>>();
let rhs = T::from_blocks(blocks);
const INPUT_CARRY: bool = false;
const COMPUTE_OVERFLOW: bool = false;

self.add_assign_with_carry_parallelized(ct, &rhs, None);
self.scalar_add_assign_with_carry_parallelized(
ct,
scalar.twos_complement_negation(),
INPUT_CARRY,
COMPUTE_OVERFLOW,
);
}

pub fn unsigned_overflowing_scalar_sub_assign_parallelized<T>(
pub fn overflowing_scalar_sub_assign_parallelized<T, Scalar>(
&self,
lhs: &mut RadixCiphertext,
scalar: T,
lhs: &mut T,
scalar: Scalar,
) -> BooleanBlock
where
T: UnsignedNumeric + DecomposableInto<u8>,
T: IntegerRadixCiphertext,
Scalar: std::ops::Not<Output = Scalar> + DecomposableInto<u8>,
{
if !lhs.block_carries_are_empty() {
self.full_propagate_parallelized(lhs);
}

let mut scalar_decomposer =
BlockDecomposer::new(scalar, self.message_modulus().0.ilog2()).iter_as::<u8>();
const INPUT_CARRY: bool = true;
const COMPUTE_OVERFLOW: bool = true;

lhs.blocks
.iter_mut() // Not worth to parallelize
.zip(scalar_decomposer.by_ref())
.for_each(|(lhs_block, rhs_scalar)| {
self.key
.unchecked_scalar_sub_assign_with_correcting_term(lhs_block, rhs_scalar)
});
let overflowed = self.unsigned_overflowing_propagate_subtraction_borrow(lhs);
let flipped_scalar = !scalar;

let is_there_any_non_zero_scalar_blocks_left = scalar_decomposer.any(|x| x != 0);
if is_there_any_non_zero_scalar_blocks_left {
// Scalar has more blocks so subtraction counts as overflowing
BooleanBlock::new_unchecked(self.key.create_trivial(1))
} else {
overflowed
}
self.scalar_add_assign_with_carry_parallelized(
lhs,
flipped_scalar,
INPUT_CARRY,
COMPUTE_OVERFLOW,
)
.expect("overflow computation was requested")
}

pub fn overflowing_scalar_sub_parallelized<T, Scalar>(
&self,
lhs: &T,
scalar: Scalar,
) -> (T, BooleanBlock)
where
T: IntegerRadixCiphertext,
Scalar: std::ops::Not<Output = Scalar> + DecomposableInto<u8>,
{
let mut result = lhs.clone();
let overflowed = self.overflowing_scalar_sub_assign_parallelized(&mut result, scalar);
(result, overflowed)
}

pub fn unsigned_overflowing_scalar_sub_assign_parallelized<T>(
&self,
lhs: &mut RadixCiphertext,
scalar: T,
) -> BooleanBlock
where
T: UnsignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = T>,
{
self.overflowing_scalar_sub_assign_parallelized(lhs, scalar)
}

pub fn unsigned_overflowing_scalar_sub_parallelized<T>(
@@ -158,12 +178,9 @@ impl ServerKey {
scalar: T,
) -> (RadixCiphertext, BooleanBlock)
where
T: UnsignedNumeric + DecomposableInto<u8>,
T: UnsignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = T>,
{
let mut result = lhs.clone();
let overflowed =
self.unsigned_overflowing_scalar_sub_assign_parallelized(&mut result, scalar);
(result, overflowed)
self.overflowing_scalar_sub_parallelized(lhs, scalar)
}

pub fn signed_overflowing_scalar_sub_parallelized<Scalar>(
@@ -172,48 +189,8 @@ impl ServerKey {
scalar: Scalar,
) -> (SignedRadixCiphertext, BooleanBlock)
where
Scalar: SignedNumeric + DecomposableInto<u64>,
Scalar: SignedNumeric + DecomposableInto<u8> + std::ops::Not<Output = Scalar>,
{
// In this implementation, we cannot simply call
// signed_overflowing_scalar_add_parallelized(lhs, -scalar)
// because in the case scalar == -modulus (i.e the minimum value of the ciphertext type)
// then -rhs will still be -modulus, however, the overflow detection of the add
// will be invalid

let mut tmp_lhs;
let lhs = if lhs.block_carries_are_empty() {
lhs
} else {
tmp_lhs = lhs.clone();
self.full_propagate_parallelized(&mut tmp_lhs);
&tmp_lhs
};

// To keep the code simple we transform the scalar into a trivial
// performances wise this won't have much impact as all the cost is
// in the carry propagation
let trivial: SignedRadixCiphertext = self.create_trivial_radix(scalar, lhs.blocks.len());
let (result, overflowed) = self.signed_overflowing_sub_parallelized(lhs, &trivial);

let mut extra_scalar_block_iter =
BlockDecomposer::new(scalar, self.key.message_modulus.0.ilog2())
.iter_as::<u64>()
.skip(lhs.blocks.len());

let extra_blocks_have_correct_value = if scalar < Scalar::ZERO {
extra_scalar_block_iter.all(|block| block == (self.message_modulus().0 as u64 - 1))
} else {
extra_scalar_block_iter.all(|block| block == 0)
};

if extra_blocks_have_correct_value {
(result, overflowed)
} else {
// Scalar has more blocks so addition counts as overflowing
(
result,
BooleanBlock::new_unchecked(self.key.create_trivial(1)),
)
}
self.overflowing_scalar_sub_parallelized(lhs, scalar)
}
}
220 changes: 114 additions & 106 deletions tfhe/src/integer/server_key/radix_parallel/tests_cases_unsigned.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::tests_unsigned::{
nb_tests_for_params, nb_tests_smaller_for_params, overflowing_add_under_modulus,
overflowing_mul_under_modulus, overflowing_sub_under_modulus, random_non_zero_value,
rotate_left_helper, rotate_right_helper,
rotate_left_helper, rotate_right_helper, MAX_NB_CTXT,
};
use crate::integer::block_decomposition::BlockDecomposer;
use crate::integer::ciphertext::boolean_value::BooleanBlock;
@@ -1861,39 +1861,43 @@ where
sks.set_deterministic_pbs_execution(true);
let sks = Arc::new(sks);

// message_modulus^vec_length
let modulus = cks.parameters().message_modulus().0.pow(NB_CTXT as u32) as u64;

let mut rng = rand::thread_rng();

executor.setup(&cks, sks);

let mut clear;

for _ in 0..nb_tests_smaller {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
let cks: crate::integer::ClientKey = cks.into();

let ctxt_0 = cks.encrypt(clear_0);
let mut clear;

let mut ct_res = executor.execute((&ctxt_0, clear_1));
assert!(ct_res.block_carries_are_empty());
for num_blocks in 1..MAX_NB_CTXT {
// message_modulus^vec_length
let modulus = cks.parameters().message_modulus().0.pow(num_blocks as u32) as u64;

clear = (clear_0 + clear_1) % modulus;
for _ in 0..nb_tests_smaller {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;

let dec_res: u64 = cks.decrypt(&ct_res);
assert_eq!(clear, dec_res);
let ctxt_0 = cks.encrypt_radix(clear_0, num_blocks);

// Add multiple times to raise the degree
for _ in 0..nb_tests_smaller {
let tmp = executor.execute((&ct_res, clear_1));
ct_res = executor.execute((&ct_res, clear_1));
let mut ct_res = executor.execute((&ctxt_0, clear_1));
assert!(ct_res.block_carries_are_empty());
assert_eq!(ct_res, tmp);
clear = clear.wrapping_add(clear_1) % modulus;

let dec_res: u64 = cks.decrypt(&ct_res);
clear = (clear_0 + clear_1) % modulus;

let dec_res: u64 = cks.decrypt_radix(&ct_res);
assert_eq!(clear, dec_res);

// Add multiple times to raise the degree
for _ in 0..nb_tests_smaller {
let tmp = executor.execute((&ct_res, clear_1));
ct_res = executor.execute((&ct_res, clear_1));
assert!(ct_res.block_carries_are_empty());
assert_eq!(ct_res, tmp);
clear = clear.wrapping_add(clear_1) % modulus;

let dec_res: u64 = cks.decrypt_radix(&ct_res);
assert_eq!(clear, dec_res);
}
}
}
}
@@ -1913,133 +1917,137 @@ where

let mut rng = rand::thread_rng();

// message_modulus^vec_length
let modulus = cks.parameters().message_modulus().0.pow(NB_CTXT as u32) as u64;

executor.setup(&cks, sks.clone());

for _ in 0..nb_tests_smaller {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
let cks: crate::integer::ClientKey = cks.into();

let ctxt_0 = cks.encrypt(clear_0);

let (ct_res, result_overflowed) = executor.execute((&ctxt_0, clear_1));
let (tmp_ct, tmp_o) = executor.execute((&ctxt_0, clear_1));
assert!(ct_res.block_carries_are_empty());
assert_eq!(ct_res, tmp_ct, "Failed determinism check");
assert_eq!(tmp_o, result_overflowed, "Failed determinism check");

let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_0, clear_1, modulus);

let decrypted_result: u64 = cks.decrypt(&ct_res);
let decrypted_overflowed = cks.decrypt_bool(&result_overflowed);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
);
assert_eq!(
decrypted_overflowed,
expected_overflowed,
"Invalid overflow flag result for overflowing_add for ({clear_0} + {clear_1}) % {modulus} \
expected overflow flag {expected_overflowed}, got {decrypted_overflowed}"
);
assert_eq!(result_overflowed.0.degree.get(), 1);
assert_eq!(result_overflowed.0.noise_level(), NoiseLevel::NOMINAL);
for num_blocks in 1..MAX_NB_CTXT {
// message_modulus^vec_length
let modulus = cks.parameters().message_modulus().0.pow(num_blocks as u32) as u64;

for _ in 0..nb_tests_smaller {
// Add non zero scalar to have non clean ciphertexts
let clear_2 = random_non_zero_value(&mut rng, modulus);
let clear_rhs = random_non_zero_value(&mut rng, modulus);
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;

let ctxt_0 = sks.unchecked_scalar_add(&ctxt_0, clear_2);
let (clear_lhs, _) = overflowing_add_under_modulus(clear_0, clear_2, modulus);
let d0: u64 = cks.decrypt(&ctxt_0);
assert_eq!(d0, clear_lhs, "Failed sanity decryption check");
let ctxt_0 = cks.encrypt_radix(clear_0, num_blocks);

let (ct_res, result_overflowed) = executor.execute((&ctxt_0, clear_rhs));
let (ct_res, result_overflowed) = executor.execute((&ctxt_0, clear_1));
let (tmp_ct, tmp_o) = executor.execute((&ctxt_0, clear_1));
assert!(ct_res.block_carries_are_empty());
assert_eq!(ct_res, tmp_ct, "Failed determinism check");
assert_eq!(tmp_o, result_overflowed, "Failed determinism check");

let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_lhs, clear_rhs, modulus);
overflowing_add_under_modulus(clear_0, clear_1, modulus);

let decrypted_result: u64 = cks.decrypt(&ct_res);
let decrypted_result: u64 = cks.decrypt_radix(&ct_res);
let decrypted_overflowed = cks.decrypt_bool(&result_overflowed);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_lhs} + {clear_rhs}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
);
assert_eq!(
decrypted_overflowed,
expected_overflowed,
"Invalid overflow flag result for overflowing_add for ({clear_0} + {clear_1}) % {modulus} \
expected overflow flag {expected_overflowed}, got {decrypted_overflowed}"
);
assert_eq!(result_overflowed.0.degree.get(), 1);
assert_eq!(result_overflowed.0.noise_level(), NoiseLevel::NOMINAL);

for _ in 0..nb_tests_smaller {
// Add non zero scalar to have non clean ciphertexts
let clear_2 = random_non_zero_value(&mut rng, modulus);
let clear_rhs = random_non_zero_value(&mut rng, modulus);

let ctxt_0 = sks.unchecked_scalar_add(&ctxt_0, clear_2);
let (clear_lhs, _) = overflowing_add_under_modulus(clear_0, clear_2, modulus);
let d0: u64 = cks.decrypt_radix(&ctxt_0);
assert_eq!(d0, clear_lhs, "Failed sanity decryption check");

let (ct_res, result_overflowed) = executor.execute((&ctxt_0, clear_rhs));
assert!(ct_res.block_carries_are_empty());
let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_lhs, clear_rhs, modulus);

let decrypted_result: u64 = cks.decrypt_radix(&ct_res);
let decrypted_overflowed = cks.decrypt_bool(&result_overflowed);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_lhs} + {clear_rhs}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
);
assert_eq!(
decrypted_overflowed,
expected_overflowed,
"Invalid overflow flag result for overflowing_add, for ({clear_lhs} + {clear_rhs}) % {modulus} \
expected overflow flag {expected_overflowed}, got {decrypted_overflowed}"
);
assert_eq!(result_overflowed.0.degree.get(), 1);
assert_eq!(result_overflowed.0.noise_level(), NoiseLevel::NOMINAL);
assert_eq!(result_overflowed.0.degree.get(), 1);
assert_eq!(result_overflowed.0.noise_level(), NoiseLevel::NOMINAL);
}
}
}

// Test with trivial inputs
for _ in 0..4 {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;
// Test with trivial inputs
for _ in 0..4 {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen::<u64>() % modulus;

let a: RadixCiphertext = sks.create_trivial_radix(clear_0, NB_CTXT);
let a: RadixCiphertext = sks.create_trivial_radix(clear_0, num_blocks);

let (encrypted_result, encrypted_overflow) = executor.execute((&a, clear_1));
let (encrypted_result, encrypted_overflow) = executor.execute((&a, clear_1));

let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_0, clear_1, modulus);
let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_0, clear_1, modulus);

let decrypted_result: u64 = cks.decrypt(&encrypted_result);
let decrypted_overflowed = cks.decrypt_bool(&encrypted_overflow);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
let decrypted_result: u64 = cks.decrypt_radix(&encrypted_result);
let decrypted_overflowed = cks.decrypt_bool(&encrypted_overflow);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
);
assert_eq!(
);
assert_eq!(
decrypted_overflowed,
expected_overflowed,
"Invalid overflow flag result for overflowing_add, for ({clear_0} + {clear_1}) % {modulus} \
expected overflow flag {expected_overflowed}, got {decrypted_overflowed}"
);
assert_eq!(encrypted_overflow.0.degree.get(), 1);
#[cfg(not(feature = "gpu"))]
assert_eq!(encrypted_overflow.0.noise_level(), NoiseLevel::ZERO);
}
assert_eq!(encrypted_overflow.0.degree.get(), 1);
#[cfg(not(feature = "gpu"))]
assert_eq!(encrypted_overflow.0.noise_level(), NoiseLevel::ZERO);
}

// Test with scalar that is bigger than ciphertext modulus
for _ in 0..2 {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen_range(modulus..=u64::MAX);
// Test with scalar that is bigger than ciphertext modulus
for _ in 0..2 {
let clear_0 = rng.gen::<u64>() % modulus;
let clear_1 = rng.gen_range(modulus..=u64::MAX);

let a: RadixCiphertext = cks.encrypt(clear_0);
let a: RadixCiphertext = cks.encrypt_radix(clear_0, num_blocks);

let (encrypted_result, encrypted_overflow) = executor.execute((&a, clear_1));
let (encrypted_result, encrypted_overflow) = executor.execute((&a, clear_1));

let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_0, clear_1, modulus);
let (expected_result, expected_overflowed) =
overflowing_add_under_modulus(clear_0, clear_1, modulus);

let decrypted_result: u64 = cks.decrypt(&encrypted_result);
let decrypted_overflowed = cks.decrypt_bool(&encrypted_overflow);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
let decrypted_result: u64 = cks.decrypt_radix(&encrypted_result);
let decrypted_overflowed = cks.decrypt_bool(&encrypted_overflow);
assert_eq!(
decrypted_result, expected_result,
"Invalid result for add, for ({clear_0} + {clear_1}) % {modulus} \
expected {expected_result}, got {decrypted_result}"
);
assert_eq!(
);
assert_eq!(
decrypted_overflowed,
expected_overflowed,
"Invalid overflow flag result for overflowing_add, for ({clear_0} + {clear_1}) % {modulus} \
expected overflow flag {expected_overflowed}, got {decrypted_overflowed}"
);
assert!(decrypted_overflowed); // Actually we know its an overflow case
assert_eq!(encrypted_overflow.0.degree.get(), 1);
assert_eq!(encrypted_overflow.0.noise_level(), NoiseLevel::ZERO);
assert!(decrypted_overflowed); // Actually we know its an overflow case
assert_eq!(encrypted_overflow.0.degree.get(), 1);
assert_eq!(encrypted_overflow.0.noise_level(), NoiseLevel::ZERO);
}
}
}

Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ pub(crate) mod test_vector_comparisons;
use crate::integer::keycache::KEY_CACHE;
use crate::integer::server_key::radix_parallel::tests_cases_unsigned::FunctionExecutor;
use crate::integer::server_key::radix_parallel::tests_unsigned::{
nb_tests_for_params, nb_tests_smaller_for_params, CpuFunctionExecutor, NB_CTXT,
nb_tests_for_params, nb_tests_smaller_for_params, CpuFunctionExecutor, MAX_NB_CTXT, NB_CTXT,
};
use crate::integer::tests::create_parametrized_test;
use crate::integer::{

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -43,6 +43,8 @@ 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: PBSParameters) -> usize {
nb_tests_for_params(params)
}
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ use super::{
nb_tests_for_params, nb_tests_smaller_for_params, overflowing_add_under_modulus,
panic_if_any_block_info_exceeds_max_degree_or_noise, panic_if_any_block_is_not_clean,
panic_if_any_block_values_exceeds_its_degree, random_non_zero_value, unsigned_modulus,
CpuFunctionExecutor, ExpectedDegrees, ExpectedNoiseLevels, NB_CTXT,
CpuFunctionExecutor, ExpectedDegrees, ExpectedNoiseLevels, MAX_NB_CTXT, NB_CTXT,
};
use crate::integer::keycache::KEY_CACHE;
use crate::integer::server_key::radix_parallel::tests_cases_unsigned::FunctionExecutor;
@@ -37,8 +37,6 @@ create_parametrized_test!(integer_advanced_add_assign_with_carry_at_least_4_bits
});
create_parametrized_test!(integer_advanced_add_assign_with_carry_sequential);

const MAX_NB_CTXT: usize = 8;

fn integer_unchecked_add<P>(param: P)
where
P: Into<PBSParameters>,

0 comments on commit 300cc68

Please sign in to comment.