diff --git a/tfhe/src/high_level_api/compact_list.rs b/tfhe/src/high_level_api/compact_list.rs index facb80d251..836888cd8a 100644 --- a/tfhe/src/high_level_api/compact_list.rs +++ b/tfhe/src/high_level_api/compact_list.rs @@ -110,7 +110,10 @@ impl CompactCiphertextList { self.inner .expand( IntegerCompactCiphertextListUnpackingMode::UnpackIfNecessary(sks.key.pbs_key()), - IntegerCompactCiphertextListCastingMode::NoCasting, + sks.cpk_casting_key().map_or( + IntegerCompactCiphertextListCastingMode::NoCasting, + IntegerCompactCiphertextListCastingMode::CastIfNecessary, + ), ) .map(|inner| CompactCiphertextListExpander { inner, @@ -549,6 +552,68 @@ mod tests { } } + #[test] + fn test_compact_list_with_casting() { + use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let config = crate::ConfigBuilder::with_custom_parameters( + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + ) + .use_dedicated_compact_public_key_parameters(( + PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + )) + .build(); + + let ck = crate::ClientKey::generate(config); + let sk = crate::ServerKey::new(&ck); + let pk = crate::CompactPublicKey::new(&ck); + + let compact_list = CompactCiphertextList::builder(&pk) + .push(17u32) + .push(-1i64) + .push(false) + .push(true) + .push_with_num_bits(3u8, 2) + .unwrap() + .build_packed(); + + let serialized = bincode::serialize(&compact_list).unwrap(); + let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap(); + let expander = compact_list.expand_with_key(&sk).unwrap(); + + { + let a: FheUint32 = expander.get(0).unwrap().unwrap(); + let b: FheInt64 = expander.get(1).unwrap().unwrap(); + let c: FheBool = expander.get(2).unwrap().unwrap(); + let d: FheBool = expander.get(3).unwrap().unwrap(); + let e: FheUint2 = expander.get(4).unwrap().unwrap(); + + let a: u32 = a.decrypt(&ck); + assert_eq!(a, 17); + let b: i64 = b.decrypt(&ck); + assert_eq!(b, -1); + let c = c.decrypt(&ck); + assert!(!c); + let d = d.decrypt(&ck); + assert!(d); + let e: u8 = e.decrypt(&ck); + assert_eq!(e, 3); + + assert!(expander.get::(5).is_none()); + } + + { + // Incorrect type + assert!(expander.get::(0).unwrap().is_err()); + + // Correct type but wrong number of bits + assert!(expander.get::(0).unwrap().is_err()); + } + } + #[cfg(feature = "zk-pok")] #[test] fn test_proven_compact_list() { diff --git a/tfhe/src/shortint/ciphertext/compact_list.rs b/tfhe/src/shortint/ciphertext/compact_list.rs index 0b350ded1e..d932837b7f 100644 --- a/tfhe/src/shortint/ciphertext/compact_list.rs +++ b/tfhe/src/shortint/ciphertext/compact_list.rs @@ -59,6 +59,13 @@ impl ParameterSetConformant for CompactCiphertextList { } impl CompactCiphertextList { + /// Expand a [`CompactCiphertextList`] to a `Vec` of [`Ciphertext`]. + /// + /// The function takes a [`ShortintCompactCiphertextListCastingMode`] to indicate whether a + /// keyswitch should be applied during expansion. + /// + /// This is useful when using separate parameters for the public key used to encrypt the + /// [`CompactCiphertextList`] allowing to keyswitch to the computation params during expansion. pub fn expand( &self, casting_mode: ShortintCompactCiphertextListCastingMode<'_>,