diff --git a/tfhe/src/integer/server_key/radix/add.rs b/tfhe/src/integer/server_key/radix/add.rs index adddde58db..15efc98037 100644 --- a/tfhe/src/integer/server_key/radix/add.rs +++ b/tfhe/src/integer/server_key/radix/add.rs @@ -109,10 +109,19 @@ impl ServerKey { where T: IntegerRadixCiphertext, { - for (ct_left_i, ct_right_i) in ct_left.blocks().iter().zip(ct_right.blocks().iter()) { - if !self.key.is_add_possible(ct_left_i, ct_right_i) { + // Assumes message_modulus and carry_modulus matches between pairs of block + let mut preceding_block_carry = 0; + for (left_block, right_block) in ct_left.blocks().iter().zip(ct_right.blocks().iter()) { + let degree_after_add = left_block.degree.0 + right_block.degree.0; + + // Also need to take into account preceding_carry + if (degree_after_add + preceding_block_carry) + >= (left_block.message_modulus.0 * left_block.carry_modulus.0) + { + // We would exceed the block 'capacity' return false; } + preceding_block_carry = degree_after_add / left_block.message_modulus.0; } true } diff --git a/tfhe/src/integer/server_key/radix/neg.rs b/tfhe/src/integer/server_key/radix/neg.rs index eaff27c2f4..3f5fd66404 100644 --- a/tfhe/src/integer/server_key/radix/neg.rs +++ b/tfhe/src/integer/server_key/radix/neg.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::prelude::misc::divide_ceil; use crate::integer::ciphertext::IntegerRadixCiphertext; use crate::integer::server_key::CheckError; use crate::integer::server_key::CheckError::CarryFull; @@ -100,26 +101,35 @@ impl ServerKey { where T: IntegerRadixCiphertext, { - for i in 0..ctxt.blocks().len() { + let mut preceding_block_carry = 0; + let mut preceding_scaled_z = 0; + for block in ctxt.blocks().iter() { + let msg_mod = block.message_modulus.0; + let carry_mod = block.carry_modulus.0; + let total_modulus = msg_mod * carry_mod; + // z = ceil( degree / 2^p ) x 2^p - let msg_mod = self.key.message_modulus.0; - let mut z = (ctxt.blocks()[i].degree.0 + msg_mod - 1) / msg_mod; + let mut z = divide_ceil(block.degree.0, msg_mod); z = z.wrapping_mul(msg_mod); + // In the actual operation, preceding_scaled_z is added to the ciphertext + // before doing lwe_ciphertext_opposite: + // i.e the code does -(ciphertext + preceding_scaled_z) + z + // here we do -ciphertext -preceding_scaled_z + z + // which is easier to express degree + let block_degree_after_negation = z - preceding_scaled_z; - // z will be the new degree of ctxt.blocks[i] - if z > self.key.max_degree.0 { + if block_degree_after_negation >= (total_modulus) { return false; } - let z_b = z / msg_mod; - - if i < ctxt.blocks().len() - 1 - && !self - .key - .is_scalar_add_possible(&ctxt.blocks()[i + 1], z_b as u8) - { + // We want to be able to the negated block and the carry from preceding negated + // block to make sure carry propagation would be correct. + if (block_degree_after_negation + preceding_block_carry) >= (total_modulus) { return false; } + + preceding_block_carry = block_degree_after_negation / msg_mod; + preceding_scaled_z = z / msg_mod; } true } diff --git a/tfhe/src/integer/server_key/radix/sub.rs b/tfhe/src/integer/server_key/radix/sub.rs index 21cfe8e5be..78eed38194 100644 --- a/tfhe/src/integer/server_key/radix/sub.rs +++ b/tfhe/src/integer/server_key/radix/sub.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::algorithms::misc::divide_ceil; use crate::integer::ciphertext::IntegerRadixCiphertext; use crate::integer::server_key::CheckError; use crate::integer::server_key::CheckError::CarryFull; @@ -110,10 +111,39 @@ impl ServerKey { where T: IntegerRadixCiphertext, { - for (ct_left_i, ct_right_i) in ctxt_left.blocks().iter().zip(ctxt_right.blocks().iter()) { - if !self.key.is_sub_possible(ct_left_i, ct_right_i) { + let mut preceding_block_carry = 0; + let mut preceding_scaled_z = 0; + for (left_block, right_block) in ctxt_left.blocks().iter().zip(ctxt_right.blocks().iter()) { + // Assumes message_modulus and carry_modulus matches between pairs of block + let msg_mod = left_block.message_modulus.0; + let carry_mod = left_block.carry_modulus.0; + let total_modulus = msg_mod * carry_mod; + + // z = ceil( degree / 2^p ) x 2^p + let mut z = divide_ceil(right_block.degree.0, msg_mod); + z = z.wrapping_mul(msg_mod); + // In the actual operation, preceding_scaled_z is added to the ciphertext + // before doing lwe_ciphertext_opposite: + // i.e the code does -(ciphertext + preceding_scaled_z) + z + // here we do -ciphertext -preceding_scaled_z + z + // which is easier to express degree + let right_block_degree_after_negation = z - preceding_scaled_z; + + if right_block_degree_after_negation >= (total_modulus) { return false; } + + let degree_after_add = left_block.degree.0 + right_block_degree_after_negation; + + // We want to be able to add the the left block, the negated right block + // and we also want to be able to add the carry from preceding block addition + // to make sure carry propagation would be correct. + if (degree_after_add + preceding_block_carry) >= (total_modulus) { + return false; + } + + preceding_block_carry = degree_after_add / msg_mod; + preceding_scaled_z = z / msg_mod; } true }