tfhe/high_level_api/
compact_list.rs

1use crate::backward_compatibility::compact_list::CompactCiphertextListVersions;
2use crate::conformance::ParameterSetConformant;
3use crate::core_crypto::commons::math::random::{Deserialize, Serialize};
4use crate::core_crypto::prelude::Numeric;
5use crate::high_level_api::global_state;
6use crate::high_level_api::keys::InternalServerKeyRef;
7use crate::high_level_api::traits::Tagged;
8use crate::integer::block_decomposition::DecomposableInto;
9use crate::integer::ciphertext::{Compactable, DataKind};
10use crate::integer::encryption::KnowsMessageModulus;
11use crate::integer::parameters::{
12    CompactCiphertextListConformanceParams, IntegerCompactCiphertextListExpansionMode,
13};
14use crate::named::Named;
15use crate::prelude::CiphertextList;
16use crate::shortint::MessageModulus;
17use crate::HlExpandable;
18use tfhe_versionable::Versionize;
19#[cfg(feature = "zk-pok")]
20pub use zk::ProvenCompactCiphertextList;
21
22#[cfg(feature = "gpu")]
23use crate::high_level_api::global_state::with_cuda_internal_keys;
24
25#[cfg(feature = "zk-pok")]
26use crate::zk::{CompactPkeCrs, ZkComputeLoad};
27use crate::{CompactPublicKey, Tag};
28
29#[cfg(feature = "strings")]
30use super::ClearString;
31
32use crate::high_level_api::global_state::device_of_internal_keys;
33#[cfg(feature = "gpu")]
34use crate::integer::gpu::ciphertext::compact_list::CudaFlattenedVecCompactCiphertextList;
35#[cfg(feature = "gpu")]
36use crate::integer::gpu::key_switching_key::CudaKeySwitchingKey;
37use serde::Serializer;
38use tfhe_versionable::{Unversionize, UnversionizeError, VersionizeOwned};
39
40impl crate::FheTypes {
41    pub(crate) fn from_data_kind(
42        data_kind: DataKind,
43        message_modulus: MessageModulus,
44    ) -> Option<Self> {
45        Some(match data_kind {
46            DataKind::Unsigned(n) => {
47                let num_bits_per_block = message_modulus.0.ilog2() as usize;
48                let num_bits = n.get() * num_bits_per_block;
49                match num_bits {
50                    2 => Self::Uint2,
51                    4 => Self::Uint4,
52                    6 => Self::Uint6,
53                    8 => Self::Uint8,
54                    10 => Self::Uint10,
55                    12 => Self::Uint12,
56                    14 => Self::Uint14,
57                    16 => Self::Uint16,
58                    24 => Self::Uint24,
59                    32 => Self::Uint32,
60                    40 => Self::Uint40,
61                    48 => Self::Uint48,
62                    56 => Self::Uint56,
63                    64 => Self::Uint64,
64                    72 => Self::Uint72,
65                    80 => Self::Uint80,
66                    88 => Self::Uint88,
67                    96 => Self::Uint96,
68                    104 => Self::Uint104,
69                    112 => Self::Uint112,
70                    120 => Self::Uint120,
71                    128 => Self::Uint128,
72                    136 => Self::Uint136,
73                    144 => Self::Uint144,
74                    152 => Self::Uint152,
75                    160 => Self::Uint160,
76                    168 => Self::Uint168,
77                    176 => Self::Uint176,
78                    184 => Self::Uint184,
79                    192 => Self::Uint192,
80                    200 => Self::Uint200,
81                    208 => Self::Uint208,
82                    216 => Self::Uint216,
83                    224 => Self::Uint224,
84                    232 => Self::Uint232,
85                    240 => Self::Uint240,
86                    248 => Self::Uint248,
87                    256 => Self::Uint256,
88                    512 => Self::Uint512,
89                    1024 => Self::Uint1024,
90                    2048 => Self::Uint2048,
91                    _ => return None,
92                }
93            }
94            DataKind::Signed(n) => {
95                let num_bits_per_block = message_modulus.0.ilog2() as usize;
96                let num_bits = n.get() * num_bits_per_block;
97                match num_bits {
98                    2 => Self::Int2,
99                    4 => Self::Int4,
100                    6 => Self::Int6,
101                    8 => Self::Int8,
102                    10 => Self::Int10,
103                    12 => Self::Int12,
104                    14 => Self::Int14,
105                    16 => Self::Int16,
106                    24 => Self::Int24,
107                    32 => Self::Int32,
108                    40 => Self::Int40,
109                    48 => Self::Int48,
110                    56 => Self::Int56,
111                    64 => Self::Int64,
112                    72 => Self::Int72,
113                    80 => Self::Int80,
114                    88 => Self::Int88,
115                    96 => Self::Int96,
116                    104 => Self::Int104,
117                    112 => Self::Int112,
118                    120 => Self::Int120,
119                    128 => Self::Int128,
120                    136 => Self::Int136,
121                    144 => Self::Int144,
122                    152 => Self::Int152,
123                    160 => Self::Int160,
124                    168 => Self::Int168,
125                    176 => Self::Int176,
126                    184 => Self::Int184,
127                    192 => Self::Int192,
128                    200 => Self::Int200,
129                    208 => Self::Int208,
130                    216 => Self::Int216,
131                    224 => Self::Int224,
132                    232 => Self::Int232,
133                    240 => Self::Int240,
134                    248 => Self::Int248,
135                    256 => Self::Int256,
136                    512 => Self::Int512,
137                    1024 => Self::Int1024,
138                    2048 => Self::Int2048,
139                    _ => return None,
140                }
141            }
142            DataKind::Boolean => Self::Bool,
143            DataKind::String { .. } => Self::AsciiString,
144        })
145    }
146}
147
148pub enum InnerCompactCiphertextList {
149    Cpu(crate::integer::ciphertext::CompactCiphertextList),
150    #[cfg(feature = "gpu")]
151    // A InnerCompactCiphertextList is a CudaFlattenedVecCompactCiphertextList initialized as a
152    // vector of a single compact list
153    Cuda(crate::integer::gpu::ciphertext::compact_list::CudaFlattenedVecCompactCiphertextList),
154}
155
156impl Clone for InnerCompactCiphertextList {
157    fn clone(&self) -> Self {
158        match self {
159            Self::Cpu(inner) => Self::Cpu(inner.clone()),
160            #[cfg(feature = "gpu")]
161            Self::Cuda(inner) => with_cuda_internal_keys(|keys| {
162                let streams = &keys.streams;
163                Self::Cuda(inner.duplicate(streams))
164            }),
165        }
166    }
167}
168
169impl serde::Serialize for InnerCompactCiphertextList {
170    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171    where
172        S: Serializer,
173    {
174        self.on_cpu().serialize(serializer)
175    }
176}
177
178impl InnerCompactCiphertextList {
179    pub(crate) fn on_cpu(&self) -> crate::integer::ciphertext::CompactCiphertextList {
180        match self {
181            Self::Cpu(inner) => inner.clone(),
182            #[cfg(feature = "gpu")]
183            Self::Cuda(inner) => with_cuda_internal_keys(|keys| {
184                let streams = &keys.streams;
185                inner.to_integer_compact_ciphertext_list(streams).unwrap()
186            }),
187        }
188    }
189    #[allow(clippy::unnecessary_wraps)] // Method can return an error if hpu is enabled
190    fn move_to_device(&mut self, device: crate::Device) -> Result<(), crate::Error> {
191        let new_value = match (&self, device) {
192            (Self::Cpu(_), crate::Device::Cpu) => None,
193            #[cfg(feature = "gpu")]
194            (Self::Cuda(cuda_ct), crate::Device::CudaGpu) => with_cuda_internal_keys(|keys| {
195                let streams = &keys.streams;
196                if cuda_ct.gpu_indexes() == streams.gpu_indexes() {
197                    None
198                } else {
199                    Some(Self::Cuda(cuda_ct.duplicate(streams)))
200                }
201            }),
202            #[cfg(feature = "gpu")]
203            (Self::Cuda(cuda_ct), crate::Device::Cpu) => with_cuda_internal_keys(|keys| {
204                let streams = &keys.streams;
205                Some(Self::Cpu(
206                    cuda_ct.to_integer_compact_ciphertext_list(streams).unwrap(),
207                ))
208            }),
209            #[cfg(feature = "gpu")]
210            (Self::Cpu(cpu_ct), crate::Device::CudaGpu) => {
211                let cuda_ct = with_cuda_internal_keys(|keys| {
212                    let streams = &keys.streams;
213                    CudaFlattenedVecCompactCiphertextList::from_integer_compact_ciphertext_list(
214                        cpu_ct, streams,
215                    )
216                });
217                Some(Self::Cuda(cuda_ct))
218            }
219            #[cfg(feature = "hpu")]
220            (Self::Cpu(_), crate::Device::Hpu) => {
221                return Err(crate::error!("HPU device does not support compact list"));
222            }
223            #[cfg(all(feature = "hpu", feature = "gpu"))]
224            (Self::Cuda(_), crate::Device::Hpu) => {
225                return Err(crate::error!("HPU device does not support compact list"));
226            }
227        };
228
229        if let Some(v) = new_value {
230            *self = v;
231        }
232        Ok(())
233    }
234}
235
236impl<'de> serde::Deserialize<'de> for InnerCompactCiphertextList {
237    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
238    where
239        D: serde::Deserializer<'de>,
240    {
241        let mut new = crate::integer::ciphertext::CompactCiphertextList::deserialize(deserializer)
242            .map(Self::Cpu)?;
243
244        if let Some(device) = device_of_internal_keys() {
245            new.move_to_device(device)
246                .map_err(serde::de::Error::custom)?;
247        }
248
249        Ok(new)
250    }
251}
252
253impl Versionize for InnerCompactCiphertextList {
254    type Versioned<'vers> =
255        <crate::integer::ciphertext::CompactCiphertextList as VersionizeOwned>::VersionedOwned;
256    fn versionize(&self) -> Self::Versioned<'_> {
257        self.on_cpu().versionize_owned()
258    }
259}
260impl VersionizeOwned for InnerCompactCiphertextList {
261    type VersionedOwned =
262        <crate::integer::ciphertext::CompactCiphertextList as VersionizeOwned>::VersionedOwned;
263    fn versionize_owned(self) -> Self::VersionedOwned {
264        self.on_cpu().versionize_owned()
265    }
266}
267
268impl Unversionize for InnerCompactCiphertextList {
269    fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
270        Ok(Self::Cpu(
271            crate::integer::ciphertext::CompactCiphertextList::unversionize(versioned)?,
272        ))
273    }
274}
275
276#[derive(Clone, Serialize, Deserialize, Versionize)]
277#[versionize(CompactCiphertextListVersions)]
278pub struct CompactCiphertextList {
279    pub(crate) inner: InnerCompactCiphertextList,
280    pub(crate) tag: Tag,
281}
282
283impl Named for CompactCiphertextList {
284    const NAME: &'static str = "high_level_api::CompactCiphertextList";
285}
286
287impl CompactCiphertextList {
288    pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
289        CompactCiphertextListBuilder::new(pk)
290    }
291
292    pub fn len(&self) -> usize {
293        match &self.inner {
294            InnerCompactCiphertextList::Cpu(inner) => inner.len(),
295            #[cfg(feature = "gpu")]
296            InnerCompactCiphertextList::Cuda(inner) => inner.lwe_ciphertext_count.0,
297        }
298    }
299
300    pub fn is_empty(&self) -> bool {
301        self.len() == 0
302    }
303
304    pub fn get_kind_of(&self, index: usize) -> Option<crate::FheTypes> {
305        match &self.inner {
306            InnerCompactCiphertextList::Cpu(inner) => {
307                inner.get_kind_of(index).and_then(|data_kind| {
308                    crate::FheTypes::from_data_kind(data_kind, inner.ct_list.message_modulus)
309                })
310            }
311            #[cfg(feature = "gpu")]
312            InnerCompactCiphertextList::Cuda(inner) => {
313                inner.get_kind_of(index).and_then(|data_kind| {
314                    crate::FheTypes::from_data_kind(data_kind, inner.message_modulus)
315                })
316            }
317        }
318    }
319
320    pub fn expand_with_key<'a>(
321        &self,
322        sks: impl Into<InternalServerKeyRef<'a>>,
323    ) -> crate::Result<CompactCiphertextListExpander> {
324        let sks = sks.into();
325        match (&self.inner, sks) {
326            (InnerCompactCiphertextList::Cpu(cpu_inner), InternalServerKeyRef::Cpu(cpu_key)) => {
327                // CPU data, CPU key case
328                cpu_inner
329                    .expand(cpu_key.integer_compact_ciphertext_list_expansion_mode())
330                    .map(|inner| CompactCiphertextListExpander {
331                        inner: InnerCompactCiphertextListExpander::Cpu(inner),
332                        tag: self.tag.clone(),
333                    })
334            }
335            #[cfg(feature = "gpu")]
336            (InnerCompactCiphertextList::Cpu(cpu_inner), InternalServerKeyRef::Cuda(cuda_key)) => {
337                // CPU data, CUDA key case
338                // We copy data to GPU and then expand it
339                let streams = &cuda_key.streams;
340                let gpu_inner =
341                    CudaFlattenedVecCompactCiphertextList::from_integer_compact_ciphertext_list(
342                        cpu_inner, streams,
343                    );
344
345                let ksk = CudaKeySwitchingKey {
346                    key_switching_key_material: cuda_key
347                        .key
348                        .cpk_key_switching_key_material
349                        .as_ref()
350                        .unwrap(),
351                    dest_server_key: &cuda_key.key.key,
352                };
353                let expander = gpu_inner.expand(&ksk, streams)?;
354
355                Ok(CompactCiphertextListExpander {
356                    inner: InnerCompactCiphertextListExpander::Cuda(expander),
357                    tag: self.tag.clone(),
358                })
359            }
360            #[cfg(feature = "gpu")]
361            (InnerCompactCiphertextList::Cuda(gpu_inner), InternalServerKeyRef::Cpu(cpu_key)) => {
362                // CUDA data, CPU key case
363                // We copy data to CPU and then expand it
364                let cpu_inner = with_cuda_internal_keys(|cuda_key| {
365                    let streams = &cuda_key.streams;
366                    gpu_inner.to_integer_compact_ciphertext_list(streams)
367                })?;
368
369                cpu_inner
370                    .expand(cpu_key.integer_compact_ciphertext_list_expansion_mode())
371                    .map(|inner| CompactCiphertextListExpander {
372                        inner: InnerCompactCiphertextListExpander::Cpu(inner),
373                        tag: self.tag.clone(),
374                    })
375            }
376            #[cfg(feature = "gpu")]
377            (InnerCompactCiphertextList::Cuda(gpu_inner), InternalServerKeyRef::Cuda(cuda_key)) => {
378                // CUDA data, CUDA key case
379                assert!(
380                    cuda_key.key.cpk_key_switching_key_material.is_some(),
381                    "cpk_key_switching_key_material must not be None"
382                );
383
384                let ksk = CudaKeySwitchingKey {
385                    key_switching_key_material: cuda_key
386                        .key
387                        .cpk_key_switching_key_material
388                        .as_ref()
389                        .unwrap(),
390                    dest_server_key: &cuda_key.key.key,
391                };
392                let streams = &cuda_key.streams;
393                let expander = gpu_inner.expand(&ksk, streams)?;
394
395                Ok(CompactCiphertextListExpander {
396                    inner: InnerCompactCiphertextListExpander::Cuda(expander),
397                    tag: self.tag.clone(),
398                })
399            }
400            #[cfg(feature = "hpu")]
401            (InnerCompactCiphertextList::Cpu(_), InternalServerKeyRef::Hpu(_)) => Err(
402                crate::Error::new("Expand not supported for HPU".to_string()),
403            ),
404            #[cfg(all(feature = "hpu", feature = "gpu"))]
405            (InnerCompactCiphertextList::Cuda(_), InternalServerKeyRef::Hpu(_)) => Err(
406                crate::Error::new("Expand not supported for HPU".to_string()),
407            ),
408        }
409    }
410
411    pub fn expand(&self) -> crate::Result<CompactCiphertextListExpander> {
412        // For WASM
413        #[allow(irrefutable_let_patterns)]
414        if let InnerCompactCiphertextList::Cpu(inner) = &self.inner {
415            if !inner.is_packed() && !inner.needs_casting() {
416                // No ServerKey required, short-circuit to avoid the global state call
417                return Ok(CompactCiphertextListExpander {
418                    inner: InnerCompactCiphertextListExpander::Cpu(inner.expand(
419                        IntegerCompactCiphertextListExpansionMode::NoCastingAndNoUnpacking,
420                    )?),
421                    tag: self.tag.clone(),
422                });
423            }
424        }
425
426        global_state::try_with_internal_keys(|maybe_keys| {
427            maybe_keys.map_or_else(
428                || Err(crate::high_level_api::errors::UninitializedServerKey.into()),
429                |internal_key| self.expand_with_key(internal_key),
430            )
431        })
432    }
433}
434
435impl Tagged for CompactCiphertextList {
436    fn tag(&self) -> &Tag {
437        &self.tag
438    }
439
440    fn tag_mut(&mut self) -> &mut Tag {
441        &mut self.tag
442    }
443}
444
445impl ParameterSetConformant for CompactCiphertextList {
446    type ParameterSet = CompactCiphertextListConformanceParams;
447
448    fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
449        let Self { inner, tag: _ } = self;
450        inner.on_cpu().is_conformant(parameter_set)
451    }
452}
453
454#[cfg(feature = "zk-pok")]
455mod zk {
456    use super::*;
457    use crate::backward_compatibility::compact_list::ProvenCompactCiphertextListVersions;
458    use crate::conformance::ParameterSetConformant;
459    use crate::high_level_api::global_state::device_of_internal_keys;
460    use crate::high_level_api::keys::InternalServerKey;
461    use crate::integer::ciphertext::IntegerProvenCompactCiphertextListConformanceParams;
462    #[cfg(feature = "gpu")]
463    use crate::integer::gpu::key_switching_key::CudaKeySwitchingKey;
464    #[cfg(feature = "gpu")]
465    use crate::integer::gpu::zk::CudaProvenCompactCiphertextList;
466    use serde::Serializer;
467
468    pub enum InnerProvenCompactCiphertextList {
469        Cpu(crate::integer::ciphertext::ProvenCompactCiphertextList),
470        #[cfg(feature = "gpu")]
471        Cuda(crate::integer::gpu::zk::CudaProvenCompactCiphertextList),
472    }
473
474    impl Clone for InnerProvenCompactCiphertextList {
475        fn clone(&self) -> Self {
476            match self {
477                Self::Cpu(inner) => Self::Cpu(inner.clone()),
478                #[cfg(feature = "gpu")]
479                Self::Cuda(inner) => with_cuda_internal_keys(|keys| {
480                    let streams = &keys.streams;
481                    Self::Cuda(inner.duplicate(streams))
482                }),
483            }
484        }
485    }
486
487    #[derive(Clone, Serialize, Deserialize, Versionize)]
488    #[versionize(ProvenCompactCiphertextListVersions)]
489    pub struct ProvenCompactCiphertextList {
490        pub(crate) inner: InnerProvenCompactCiphertextList,
491        pub(crate) tag: Tag,
492    }
493
494    impl InnerProvenCompactCiphertextList {
495        pub(crate) fn on_cpu(&self) -> &crate::integer::ciphertext::ProvenCompactCiphertextList {
496            match self {
497                Self::Cpu(inner) => inner,
498                #[cfg(feature = "gpu")]
499                Self::Cuda(inner) => &inner.h_proved_lists,
500            }
501        }
502
503        #[allow(clippy::unnecessary_wraps)] // Method can return an error if hpu is enabled
504        fn move_to_device(&mut self, device: crate::Device) -> Result<(), crate::Error> {
505            let new_value = match (&self, device) {
506                (Self::Cpu(_), crate::Device::Cpu) => None,
507                #[cfg(feature = "gpu")]
508                (Self::Cuda(cuda_ct), crate::Device::CudaGpu) => with_cuda_internal_keys(|keys| {
509                    let streams = &keys.streams;
510                    if cuda_ct.gpu_indexes() == streams.gpu_indexes() {
511                        None
512                    } else {
513                        Some(Self::Cuda(cuda_ct.duplicate(streams)))
514                    }
515                }),
516                #[cfg(feature = "gpu")]
517                (Self::Cuda(cuda_ct), crate::Device::Cpu) => {
518                    let cpu_ct = cuda_ct.h_proved_lists.clone();
519                    Some(Self::Cpu(cpu_ct))
520                }
521                #[cfg(feature = "gpu")]
522                (Self::Cpu(cpu_ct), crate::Device::CudaGpu) => {
523                    let cuda_ct = with_cuda_internal_keys(|keys| {
524                        let streams = &keys.streams;
525                        CudaProvenCompactCiphertextList::from_proven_compact_ciphertext_list(
526                            cpu_ct, streams,
527                        )
528                    });
529                    Some(Self::Cuda(cuda_ct))
530                }
531                #[cfg(feature = "hpu")]
532                (_, crate::Device::Hpu) => {
533                    return Err(crate::error!(
534                        "Hpu does not support ProvenCompactCiphertextList"
535                    ))
536                }
537            };
538
539            if let Some(v) = new_value {
540                *self = v;
541            }
542            Ok(())
543        }
544    }
545
546    impl serde::Serialize for InnerProvenCompactCiphertextList {
547        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
548        where
549            S: Serializer,
550        {
551            self.on_cpu().serialize(serializer)
552        }
553    }
554
555    impl<'de> serde::Deserialize<'de> for InnerProvenCompactCiphertextList {
556        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
557        where
558            D: serde::Deserializer<'de>,
559        {
560            let mut new =
561                crate::integer::ciphertext::ProvenCompactCiphertextList::deserialize(deserializer)
562                    .map(Self::Cpu)?;
563
564            if let Some(device) = device_of_internal_keys() {
565                new.move_to_device(device)
566                    .map_err(serde::de::Error::custom)?;
567            }
568
569            Ok(new)
570        }
571    }
572    use tfhe_versionable::{Unversionize, UnversionizeError, VersionizeOwned};
573    impl Versionize for InnerProvenCompactCiphertextList {
574        type Versioned<'vers> =
575        <crate::integer::ciphertext::ProvenCompactCiphertextList as VersionizeOwned>::VersionedOwned;
576        fn versionize(&self) -> Self::Versioned<'_> {
577            self.on_cpu().clone().versionize_owned()
578        }
579    }
580    impl VersionizeOwned for InnerProvenCompactCiphertextList {
581        type VersionedOwned =
582        <crate::integer::ciphertext::ProvenCompactCiphertextList as VersionizeOwned>::VersionedOwned;
583        fn versionize_owned(self) -> Self::VersionedOwned {
584            self.on_cpu().clone().versionize_owned()
585        }
586    }
587
588    impl Unversionize for InnerProvenCompactCiphertextList {
589        fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
590            Ok(Self::Cpu(
591                crate::integer::ciphertext::ProvenCompactCiphertextList::unversionize(versioned)?,
592            ))
593        }
594    }
595
596    impl Tagged for ProvenCompactCiphertextList {
597        fn tag(&self) -> &Tag {
598            &self.tag
599        }
600
601        fn tag_mut(&mut self) -> &mut Tag {
602            &mut self.tag
603        }
604    }
605    impl Named for ProvenCompactCiphertextList {
606        const NAME: &'static str = "high_level_api::ProvenCompactCiphertextList";
607    }
608
609    impl ProvenCompactCiphertextList {
610        pub fn builder(pk: &CompactPublicKey) -> CompactCiphertextListBuilder {
611            CompactCiphertextListBuilder::new(pk)
612        }
613
614        pub fn len(&self) -> usize {
615            self.inner.on_cpu().len()
616        }
617
618        pub fn is_empty(&self) -> bool {
619            self.len() == 0
620        }
621
622        pub fn get_kind_of(&self, index: usize) -> Option<crate::FheTypes> {
623            let inner_cpu = self.inner.on_cpu();
624            inner_cpu.get_kind_of(index).and_then(|data_kind| {
625                crate::FheTypes::from_data_kind(data_kind, inner_cpu.ct_list.message_modulus())
626            })
627        }
628
629        pub fn verify(
630            &self,
631            crs: &CompactPkeCrs,
632            pk: &CompactPublicKey,
633            metadata: &[u8],
634        ) -> crate::zk::ZkVerificationOutcome {
635            self.inner.on_cpu().verify(crs, &pk.key.key, metadata)
636        }
637
638        pub fn verify_and_expand(
639            &self,
640            crs: &CompactPkeCrs,
641            pk: &CompactPublicKey,
642            metadata: &[u8],
643        ) -> crate::Result<CompactCiphertextListExpander> {
644            #[allow(irrefutable_let_patterns)]
645            if let InnerProvenCompactCiphertextList::Cpu(inner) = &self.inner {
646                // For WASM
647                if !inner.is_packed() && !inner.needs_casting() {
648                    let expander = inner.verify_and_expand(
649                        crs,
650                        &pk.key.key,
651                        metadata,
652                        IntegerCompactCiphertextListExpansionMode::NoCastingAndNoUnpacking,
653                    )?;
654                    // No ServerKey required, short circuit to avoid the global state call
655                    return Ok(CompactCiphertextListExpander {
656                        inner: InnerCompactCiphertextListExpander::Cpu(expander),
657                        tag: self.tag.clone(),
658                    });
659                }
660            }
661
662            global_state::try_with_internal_keys(|maybe_keys| match maybe_keys {
663                None => Err(crate::high_level_api::errors::UninitializedServerKey.into()),
664                Some(InternalServerKey::Cpu(cpu_key)) => match &self.inner {
665                    InnerProvenCompactCiphertextList::Cpu(inner) => inner
666                        .verify_and_expand(
667                            crs,
668                            &pk.key.key,
669                            metadata,
670                            cpu_key.integer_compact_ciphertext_list_expansion_mode(),
671                        )
672                        .map(|expander| CompactCiphertextListExpander {
673                            inner: InnerCompactCiphertextListExpander::Cpu(expander),
674                            tag: self.tag.clone(),
675                        }),
676                    #[cfg(feature = "gpu")]
677                    InnerProvenCompactCiphertextList::Cuda(inner) => inner
678                        .h_proved_lists
679                        .verify_and_expand(
680                            crs,
681                            &pk.key.key,
682                            metadata,
683                            cpu_key.integer_compact_ciphertext_list_expansion_mode(),
684                        )
685                        .map(|expander| CompactCiphertextListExpander {
686                            inner: InnerCompactCiphertextListExpander::Cpu(expander),
687                            tag: self.tag.clone(),
688                        }),
689                },
690                #[cfg(feature = "gpu")]
691                Some(InternalServerKey::Cuda(gpu_key)) => match &self.inner {
692                    InnerProvenCompactCiphertextList::Cuda(inner) => {
693                        let streams = &gpu_key.streams;
694                        let ksk = CudaKeySwitchingKey {
695                            key_switching_key_material: gpu_key
696                                .key
697                                .cpk_key_switching_key_material
698                                .as_ref()
699                                .unwrap(),
700                            dest_server_key: &gpu_key.key.key,
701                        };
702                        let expander =
703                            inner.verify_and_expand(crs, &pk.key.key, metadata, &ksk, streams)?;
704
705                        Ok(CompactCiphertextListExpander {
706                            inner: InnerCompactCiphertextListExpander::Cuda(expander),
707                            tag: self.tag.clone(),
708                        })
709                    }
710                    InnerProvenCompactCiphertextList::Cpu(cpu_inner) => {
711                        with_cuda_internal_keys(|keys| {
712                            let streams = &keys.streams;
713                            let gpu_proven_ct = CudaProvenCompactCiphertextList::from_proven_compact_ciphertext_list(
714                                    cpu_inner, streams,
715                                );
716                            let ksk = CudaKeySwitchingKey {
717                                key_switching_key_material: gpu_key
718                                    .key
719                                    .cpk_key_switching_key_material
720                                    .as_ref()
721                                    .unwrap(),
722                                dest_server_key: &gpu_key.key.key,
723                            };
724                            let expander = gpu_proven_ct.verify_and_expand(
725                                crs,
726                                &pk.key.key,
727                                metadata,
728                                &ksk,
729                                streams,
730                            )?;
731
732                            Ok(CompactCiphertextListExpander {
733                                inner: InnerCompactCiphertextListExpander::Cuda(expander),
734                                tag: self.tag.clone(),
735                            })
736                        })
737                    }
738                },
739                #[cfg(feature = "hpu")]
740                Some(InternalServerKey::Hpu(_)) => Err(crate::error!(
741                    "Hpu does not support ProvenCompactCiphertextList"
742                )),
743            })
744        }
745
746        #[doc(hidden)]
747        /// This function allows to expand a ciphertext without verifying the associated proof.
748        ///
749        /// If you are here you were probably looking for it: use at your own risks.
750        pub fn expand_without_verification(&self) -> crate::Result<CompactCiphertextListExpander> {
751            #[allow(irrefutable_let_patterns)]
752            if let InnerProvenCompactCiphertextList::Cpu(inner) = &self.inner {
753                // For WASM
754                if !inner.is_packed() && !inner.needs_casting() {
755                    // No ServerKey required, short circuit to avoid the global state call
756                    return Ok(CompactCiphertextListExpander {
757                        inner: InnerCompactCiphertextListExpander::Cpu(
758                            inner.expand_without_verification(
759                                IntegerCompactCiphertextListExpansionMode::NoCastingAndNoUnpacking,
760                            )?,
761                        ),
762                        tag: self.tag.clone(),
763                    });
764                }
765            }
766
767            global_state::try_with_internal_keys(|maybe_keys| {
768                match maybe_keys {
769                None => Err(crate::high_level_api::errors::UninitializedServerKey.into()),
770                    Some(InternalServerKey::Cpu(cpu_key)) => match &self.inner {
771                        InnerProvenCompactCiphertextList::Cpu(inner) => inner
772                            .expand_without_verification(
773                                cpu_key.integer_compact_ciphertext_list_expansion_mode(),
774                            )
775                            .map(|expander| CompactCiphertextListExpander {
776                                inner: InnerCompactCiphertextListExpander::Cpu(expander),
777                                tag: self.tag.clone(),
778                            }),
779                        #[cfg(feature = "gpu")]
780                        InnerProvenCompactCiphertextList::Cuda(_) => {
781                            Err(crate::Error::new("Tried expanding a ProvenCompactCiphertextList on the GPU, but the set ServerKey is a ServerKey".to_string()))
782                        }
783                    },
784                    #[cfg(feature = "gpu")]
785                    Some(InternalServerKey::Cuda(gpu_key)) => match &self.inner {
786                        InnerProvenCompactCiphertextList::Cuda(inner) => {
787                                let streams = &gpu_key.streams;
788                                let ksk = CudaKeySwitchingKey {
789                                    key_switching_key_material: gpu_key
790                                        .key
791                                        .cpk_key_switching_key_material
792                                        .as_ref()
793                                        .unwrap(),
794                                    dest_server_key: &gpu_key.key.key,
795                                };
796                                let expander = inner.expand_without_verification(&ksk, streams)?;
797
798                                Ok(CompactCiphertextListExpander {
799                                    inner: InnerCompactCiphertextListExpander::Cuda(expander),
800                                    tag: self.tag.clone(),
801                                })
802                        }
803                        InnerProvenCompactCiphertextList::Cpu(inner) => {
804                            with_cuda_internal_keys(|keys| {
805                               let streams = &keys.streams;
806                               let gpu_proven_ct = CudaProvenCompactCiphertextList::from_proven_compact_ciphertext_list(
807                                    inner, streams,
808                                );
809                                let ksk = CudaKeySwitchingKey {
810                                    key_switching_key_material: gpu_key
811                                        .key
812                                        .cpk_key_switching_key_material
813                                        .as_ref()
814                                        .unwrap(),
815                                    dest_server_key: &gpu_key.key.key,
816                                };
817                                let expander = gpu_proven_ct.expand_without_verification(&ksk, streams)?;
818
819                                Ok(CompactCiphertextListExpander {
820                                    inner: InnerCompactCiphertextListExpander::Cuda(expander),
821                                    tag: self.tag.clone(),
822                                })
823                            })
824                        }
825                    },
826                #[cfg(feature = "hpu")]
827                Some(InternalServerKey::Hpu(_)) => Err(crate::error!("Hpu does not support ProvenCompactCiphertextList")),
828                }
829            })
830        }
831    }
832
833    impl ParameterSetConformant for ProvenCompactCiphertextList {
834        type ParameterSet = IntegerProvenCompactCiphertextListConformanceParams;
835
836        fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
837            self.inner.on_cpu().is_conformant(parameter_set)
838        }
839    }
840
841    #[cfg(test)]
842    mod test {
843        use super::*;
844        use crate::integer::ciphertext::IntegerProvenCompactCiphertextListConformanceParams;
845        use crate::shortint::parameters::*;
846
847        use rand::{thread_rng, Rng};
848
849        #[test]
850        fn conformance_zk_compact_ciphertext_list() {
851            let mut rng = thread_rng();
852
853            let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
854
855            let cpk_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
856
857            let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
858
859            let config = crate::ConfigBuilder::with_custom_parameters(params)
860                .use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
861
862            let client_key = crate::ClientKey::generate(config.clone());
863
864            let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap();
865            let public_key = crate::CompactPublicKey::try_new(&client_key).unwrap();
866
867            let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's'];
868
869            let clear_a = rng.gen::<u64>();
870            let clear_b = rng.gen::<bool>();
871
872            let proven_compact_list = crate::ProvenCompactCiphertextList::builder(&public_key)
873                .push(clear_a)
874                .push(clear_b)
875                .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
876                .unwrap();
877
878            let params =
879                IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters(
880                    cpk_params, &crs,
881                );
882
883            assert!(proven_compact_list.is_conformant(&params));
884        }
885    }
886}
887
888pub enum InnerCompactCiphertextListExpander {
889    Cpu(crate::integer::ciphertext::CompactCiphertextListExpander),
890    #[cfg(feature = "gpu")]
891    Cuda(crate::integer::gpu::ciphertext::compact_list::CudaCompactCiphertextListExpander),
892}
893
894pub struct CompactCiphertextListExpander {
895    pub inner: InnerCompactCiphertextListExpander,
896    tag: Tag,
897}
898
899impl CiphertextList for CompactCiphertextListExpander {
900    fn len(&self) -> usize {
901        match &self.inner {
902            InnerCompactCiphertextListExpander::Cpu(inner) => inner.len(),
903            #[cfg(feature = "gpu")]
904            InnerCompactCiphertextListExpander::Cuda(inner) => inner.len(),
905        }
906    }
907
908    fn is_empty(&self) -> bool {
909        self.len() == 0
910    }
911
912    fn get_kind_of(&self, index: usize) -> Option<crate::FheTypes> {
913        match &self.inner {
914            InnerCompactCiphertextListExpander::Cpu(inner) => {
915                inner.get_kind_of(index).and_then(|data_kind| {
916                    crate::FheTypes::from_data_kind(data_kind, inner.message_modulus())
917                })
918            }
919            #[cfg(feature = "gpu")]
920            InnerCompactCiphertextListExpander::Cuda(inner) => {
921                inner.get_kind_of(index).and_then(|data_kind| {
922                    crate::FheTypes::from_data_kind(data_kind, inner.message_modulus(index)?)
923                })
924            }
925        }
926    }
927
928    fn get<T>(&self, index: usize) -> crate::Result<Option<T>>
929    where
930        T: HlExpandable + Tagged,
931    {
932        let mut expanded = match &self.inner {
933            InnerCompactCiphertextListExpander::Cpu(inner) => inner.get::<T>(index),
934            #[cfg(feature = "gpu")]
935            InnerCompactCiphertextListExpander::Cuda(inner) => with_cuda_internal_keys(|keys| {
936                let streams = &keys.streams;
937                inner.get::<T>(index, streams)
938            }),
939        };
940
941        if let Ok(Some(inner)) = &mut expanded {
942            inner.tag_mut().set_data(self.tag.data());
943        }
944        expanded
945    }
946    #[cfg(feature = "gpu")]
947    fn get_decompression_size_on_gpu(&self, index: usize) -> crate::Result<Option<u64>> {
948        {
949            match &self.inner {
950                InnerCompactCiphertextListExpander::Cpu(_) => Ok(Some(0)),
951                InnerCompactCiphertextListExpander::Cuda(inner) => {
952                    Ok(with_cuda_internal_keys(|keys| {
953                        let streams = &keys.streams;
954                        inner.get_decompression_size_on_gpu(index, streams)
955                    }))
956                }
957            }
958        }
959    }
960}
961
962fn num_bits_to_strict_num_blocks(
963    num_bits: usize,
964    message_modulus: MessageModulus,
965) -> crate::Result<usize> {
966    let bits_per_block = message_modulus.0.ilog2();
967    if !(num_bits as u32).is_multiple_of(bits_per_block) {
968        let message = format!("Number of bits must be a multiple of the parameter's MessageModulus.ilog2 ({bits_per_block} here)");
969        return Err(crate::Error::new(message));
970    }
971    Ok(num_bits.div_ceil(bits_per_block as usize))
972}
973
974pub trait HlCompactable: Compactable {}
975
976impl HlCompactable for bool {}
977
978impl<T> HlCompactable for T where
979    T: Numeric + DecomposableInto<u64> + std::ops::Shl<usize, Output = T>
980{
981}
982
983pub struct CompactCiphertextListBuilder {
984    inner: crate::integer::ciphertext::CompactCiphertextListBuilder,
985    tag: Tag,
986}
987
988impl CompactCiphertextListBuilder {
989    pub fn new(pk: &CompactPublicKey) -> Self {
990        Self {
991            inner: crate::integer::ciphertext::CompactCiphertextListBuilder::new(&pk.key.key),
992            tag: pk.tag.clone(),
993        }
994    }
995
996    pub fn push<T>(&mut self, value: T) -> &mut Self
997    where
998        T: HlCompactable,
999    {
1000        self.inner.push(value);
1001        self
1002    }
1003
1004    pub fn extend<T>(&mut self, values: impl Iterator<Item = T>) -> &mut Self
1005    where
1006        T: HlCompactable,
1007    {
1008        self.inner.extend(values);
1009        self
1010    }
1011
1012    pub fn push_with_num_bits<T>(&mut self, number: T, num_bits: usize) -> crate::Result<&mut Self>
1013    where
1014        T: HlCompactable + Numeric,
1015    {
1016        let num_blocks =
1017            num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
1018        self.inner.push_with_num_blocks(number, num_blocks);
1019        Ok(self)
1020    }
1021
1022    pub fn extend_with_num_bits<T>(
1023        &mut self,
1024        values: impl Iterator<Item = T>,
1025        num_bits: usize,
1026    ) -> crate::Result<&mut Self>
1027    where
1028        T: HlCompactable + Numeric,
1029    {
1030        let num_blocks =
1031            num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
1032        self.inner.extend_with_num_blocks(values, num_blocks);
1033        Ok(self)
1034    }
1035
1036    pub fn build(&self) -> CompactCiphertextList {
1037        CompactCiphertextList {
1038            inner: crate::high_level_api::compact_list::InnerCompactCiphertextList::Cpu(
1039                self.inner.build(),
1040            ),
1041            tag: self.tag.clone(),
1042        }
1043    }
1044
1045    pub fn build_packed(&self) -> CompactCiphertextList {
1046        self.inner
1047            .build_packed()
1048            .map(|list| CompactCiphertextList {
1049                inner: crate::high_level_api::compact_list::InnerCompactCiphertextList::Cpu(list),
1050                tag: self.tag.clone(),
1051            })
1052            .expect("Internal error, invalid parameters should not have been allowed")
1053    }
1054    #[cfg(feature = "zk-pok")]
1055    pub fn build_with_proof_packed(
1056        &self,
1057        crs: &CompactPkeCrs,
1058        metadata: &[u8],
1059        compute_load: ZkComputeLoad,
1060    ) -> crate::Result<ProvenCompactCiphertextList> {
1061        self.inner
1062            .build_with_proof_packed(crs, metadata, compute_load)
1063            .map(|proved_list| ProvenCompactCiphertextList {
1064                inner:
1065                    crate::high_level_api::compact_list::zk::InnerProvenCompactCiphertextList::Cpu(
1066                        proved_list,
1067                    ),
1068                tag: self.tag.clone(),
1069            })
1070    }
1071}
1072
1073#[cfg(feature = "strings")]
1074impl CompactCiphertextListBuilder {
1075    pub fn push_string(&mut self, string: &ClearString) -> &mut Self {
1076        self.push(string)
1077    }
1078
1079    pub fn push_string_with_padding(
1080        &mut self,
1081        clear_string: &ClearString,
1082        padding_count: u32,
1083    ) -> &mut Self {
1084        self.inner
1085            .push_string_with_padding(clear_string, padding_count);
1086        self
1087    }
1088
1089    pub fn push_string_with_fixed_size(
1090        &mut self,
1091        clear_string: &ClearString,
1092        size: u32,
1093    ) -> &mut Self {
1094        self.inner.push_string_with_fixed_size(clear_string, size);
1095        self
1096    }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101    use super::*;
1102    use crate::prelude::*;
1103    use crate::shortint::parameters::*;
1104    use crate::{set_server_key, FheBool, FheInt64, FheUint16, FheUint2, FheUint32};
1105
1106    #[cfg(feature = "gpu")]
1107    use crate::CompressedServerKey;
1108
1109    #[test]
1110    fn test_compact_list() {
1111        let config = crate::ConfigBuilder::default().build();
1112
1113        let ck = crate::ClientKey::generate(config);
1114        let sk = crate::ServerKey::new(&ck);
1115        let pk = crate::CompactPublicKey::new(&ck);
1116
1117        set_server_key(sk);
1118
1119        let compact_list = CompactCiphertextList::builder(&pk)
1120            .push(17u32)
1121            .push(-1i64)
1122            .push(false)
1123            .push(true)
1124            .push_with_num_bits(3u8, 2)
1125            .unwrap()
1126            .build_packed();
1127
1128        let serialized = bincode::serialize(&compact_list).unwrap();
1129        let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1130        let expander = compact_list.expand().unwrap();
1131
1132        {
1133            let a: FheUint32 = expander.get(0).unwrap().unwrap();
1134            let b: FheInt64 = expander.get(1).unwrap().unwrap();
1135            let c: FheBool = expander.get(2).unwrap().unwrap();
1136            let d: FheBool = expander.get(3).unwrap().unwrap();
1137            let e: FheUint2 = expander.get(4).unwrap().unwrap();
1138
1139            let a: u32 = a.decrypt(&ck);
1140            assert_eq!(a, 17);
1141            let b: i64 = b.decrypt(&ck);
1142            assert_eq!(b, -1);
1143            let c = c.decrypt(&ck);
1144            assert!(!c);
1145            let d = d.decrypt(&ck);
1146            assert!(d);
1147            let e: u8 = e.decrypt(&ck);
1148            assert_eq!(e, 3);
1149
1150            assert!(expander.get::<FheBool>(5).unwrap().is_none());
1151        }
1152
1153        {
1154            // Incorrect type
1155            assert!(expander.get::<FheInt64>(0).is_err());
1156
1157            // Correct type but wrong number of bits
1158            assert!(expander.get::<FheUint16>(0).is_err());
1159        }
1160    }
1161
1162    #[test]
1163    fn test_empty_list() {
1164        let config = crate::ConfigBuilder::default().build();
1165
1166        let ck = crate::ClientKey::generate(config);
1167        let sk = crate::ServerKey::new(&ck);
1168        let pk = crate::CompactPublicKey::new(&ck);
1169
1170        set_server_key(sk);
1171
1172        let compact_list = CompactCiphertextList::builder(&pk).build_packed();
1173
1174        let expander = compact_list.expand().unwrap();
1175
1176        assert!(expander.get::<FheBool>(0).unwrap().is_none());
1177    }
1178
1179    #[cfg(feature = "gpu")]
1180    #[test]
1181    fn test_gpu_compact_list() {
1182        for i in [0, 1] {
1183            let config = if i == 0 {
1184                crate::ConfigBuilder::with_custom_parameters(
1185                    PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1186                )
1187                .use_dedicated_compact_public_key_parameters((
1188                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1189                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1190                ))
1191                .build()
1192            } else if i == 1 {
1193                crate::ConfigBuilder::with_custom_parameters(
1194                    PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1195                )
1196                .use_dedicated_compact_public_key_parameters((
1197                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1198                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1199                ))
1200                .build()
1201            } else {
1202                panic!("Unexpected parameter set")
1203            };
1204
1205            let ck = crate::ClientKey::generate(config);
1206            let compressed_server_key = CompressedServerKey::new(&ck);
1207            let gpu_sk = compressed_server_key.decompress_to_gpu();
1208            let pk = crate::CompactPublicKey::new(&ck);
1209
1210            set_server_key(gpu_sk);
1211
1212            let compact_list = CompactCiphertextList::builder(&pk)
1213                .push(17u32)
1214                .push(-1i64)
1215                .push(false)
1216                .push(true)
1217                .push_with_num_bits(3u8, 2)
1218                .unwrap()
1219                .build_packed();
1220
1221            let serialized = bincode::serialize(&compact_list).unwrap();
1222            let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1223            let expander = compact_list.expand().unwrap();
1224
1225            {
1226                let a: FheUint32 = expander.get(0).unwrap().unwrap();
1227                let b: FheInt64 = expander.get(1).unwrap().unwrap();
1228                let c: FheBool = expander.get(2).unwrap().unwrap();
1229                let d: FheBool = expander.get(3).unwrap().unwrap();
1230                let e: FheUint2 = expander.get(4).unwrap().unwrap();
1231
1232                let a: u32 = a.decrypt(&ck);
1233                assert_eq!(a, 17);
1234                let b: i64 = b.decrypt(&ck);
1235                assert_eq!(b, -1);
1236                let c = c.decrypt(&ck);
1237                assert!(!c);
1238                let d = d.decrypt(&ck);
1239                assert!(d);
1240                let e: u8 = e.decrypt(&ck);
1241                assert_eq!(e, 3);
1242
1243                assert!(expander.get::<FheBool>(5).unwrap().is_none());
1244            }
1245
1246            {
1247                // Incorrect type
1248                assert!(expander.get::<FheInt64>(0).is_err());
1249
1250                // Correct type but wrong number of bits
1251                assert!(expander.get::<FheUint16>(0).is_err());
1252            }
1253        }
1254    }
1255
1256    #[cfg(feature = "extended-types")]
1257    #[test]
1258    fn test_compact_list_extended_types() {
1259        let config = crate::ConfigBuilder::default().build();
1260
1261        let ck = crate::ClientKey::generate(config);
1262        let sk = crate::ServerKey::new(&ck);
1263        let pk = crate::CompactPublicKey::new(&ck);
1264
1265        set_server_key(sk);
1266
1267        let compact_list = CompactCiphertextList::builder(&pk)
1268            .push_with_num_bits(-17i64, 40)
1269            .unwrap()
1270            .push_with_num_bits(3u8, 24)
1271            .unwrap()
1272            .build_packed();
1273
1274        let serialized = bincode::serialize(&compact_list).unwrap();
1275        let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1276        let expander = compact_list.expand().unwrap();
1277
1278        {
1279            let a: crate::FheInt40 = expander.get(0).unwrap().unwrap();
1280            let b: crate::FheUint24 = expander.get(1).unwrap().unwrap();
1281
1282            let a: i64 = a.decrypt(&ck);
1283            assert_eq!(a, -17);
1284            let b: u8 = b.decrypt(&ck);
1285            assert_eq!(b, 3);
1286        }
1287
1288        {
1289            // Incorrect type
1290            assert!(expander.get::<FheUint32>(0).is_err());
1291
1292            // Correct type but wrong number of bits
1293            assert!(expander.get::<FheInt64>(0).is_err());
1294        }
1295    }
1296
1297    #[cfg(feature = "extended-types")]
1298    #[cfg(feature = "gpu")]
1299    #[test]
1300    fn test_gpu_compact_list_extended_types() {
1301        for i in [0, 1] {
1302            let config = if i == 0 {
1303                crate::ConfigBuilder::with_custom_parameters(
1304                    PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1305                )
1306                .use_dedicated_compact_public_key_parameters((
1307                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1308                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1309                ))
1310                .build()
1311            } else if i == 1 {
1312                crate::ConfigBuilder::with_custom_parameters(
1313                    PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1314                )
1315                .use_dedicated_compact_public_key_parameters((
1316                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1317                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1318                ))
1319                .build()
1320            } else {
1321                panic!("Unexpected parameter set")
1322            };
1323
1324            let ck = crate::ClientKey::generate(config);
1325            let compressed_server_key = CompressedServerKey::new(&ck);
1326            let gpu_sk = compressed_server_key.decompress_to_gpu();
1327            let pk = crate::CompactPublicKey::new(&ck);
1328
1329            set_server_key(gpu_sk);
1330
1331            let compact_list = CompactCiphertextList::builder(&pk)
1332                .push_with_num_bits(-17i64, 40)
1333                .unwrap()
1334                .push_with_num_bits(3u8, 24)
1335                .unwrap()
1336                .build_packed();
1337
1338            let serialized = bincode::serialize(&compact_list).unwrap();
1339            let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1340            let expander = compact_list.expand().unwrap();
1341
1342            {
1343                let a: crate::FheInt40 = expander.get(0).unwrap().unwrap();
1344                let b: crate::FheUint24 = expander.get(1).unwrap().unwrap();
1345
1346                let a: i64 = a.decrypt(&ck);
1347                assert_eq!(a, -17);
1348                let b: u8 = b.decrypt(&ck);
1349                assert_eq!(b, 3);
1350            }
1351
1352            {
1353                // Incorrect type
1354                assert!(expander.get::<FheUint32>(0).is_err());
1355
1356                // Correct type but wrong number of bits
1357                assert!(expander.get::<FheInt64>(0).is_err());
1358            }
1359        }
1360    }
1361
1362    #[test]
1363    fn test_compact_list_with_casting() {
1364        for compute_param in [
1365            PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(),
1366            PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128.into(),
1367        ] {
1368            test_compact_list_with_casting_inner(compute_param);
1369        }
1370    }
1371
1372    fn test_compact_list_with_casting_inner(params: AtomicPatternParameters) {
1373        let config = crate::ConfigBuilder::with_custom_parameters(params)
1374            .use_dedicated_compact_public_key_parameters((
1375                PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1376                PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1377            ))
1378            .build();
1379
1380        let ck = crate::ClientKey::generate(config);
1381        let sk = crate::ServerKey::new(&ck);
1382        let pk = crate::CompactPublicKey::new(&ck);
1383
1384        let compact_list = CompactCiphertextList::builder(&pk)
1385            .push(17u32)
1386            .push(-1i64)
1387            .push(false)
1388            .push(true)
1389            .push_with_num_bits(3u8, 2)
1390            .unwrap()
1391            .build_packed();
1392
1393        let serialized = bincode::serialize(&compact_list).unwrap();
1394        let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1395        let expander = compact_list.expand_with_key(&sk).unwrap();
1396
1397        {
1398            let a: FheUint32 = expander.get(0).unwrap().unwrap();
1399            let b: FheInt64 = expander.get(1).unwrap().unwrap();
1400            let c: FheBool = expander.get(2).unwrap().unwrap();
1401            let d: FheBool = expander.get(3).unwrap().unwrap();
1402            let e: FheUint2 = expander.get(4).unwrap().unwrap();
1403
1404            let a: u32 = a.decrypt(&ck);
1405            assert_eq!(a, 17);
1406            let b: i64 = b.decrypt(&ck);
1407            assert_eq!(b, -1);
1408            let c = c.decrypt(&ck);
1409            assert!(!c);
1410            let d = d.decrypt(&ck);
1411            assert!(d);
1412            let e: u8 = e.decrypt(&ck);
1413            assert_eq!(e, 3);
1414
1415            assert!(expander.get::<FheBool>(5).unwrap().is_none());
1416        }
1417
1418        {
1419            // Incorrect type
1420            assert!(expander.get::<FheInt64>(0).is_err());
1421
1422            // Correct type but wrong number of bits
1423            assert!(expander.get::<FheUint16>(0).is_err());
1424        }
1425    }
1426
1427    #[cfg(feature = "zk-pok")]
1428    #[test]
1429    fn test_proven_compact_list() {
1430        for compute_param in [
1431            PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(),
1432            PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128.into(),
1433        ] {
1434            test_proven_compact_list_inner(compute_param);
1435        }
1436    }
1437
1438    #[cfg(feature = "zk-pok")]
1439    fn test_proven_compact_list_inner(params: AtomicPatternParameters) {
1440        let config = crate::ConfigBuilder::with_custom_parameters(params)
1441            .use_dedicated_compact_public_key_parameters((
1442                PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1443                PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1444            ))
1445            .build();
1446
1447        let ck = crate::ClientKey::generate(config);
1448        let pk = crate::CompactPublicKey::new(&ck);
1449        let sks = crate::ServerKey::new(&ck);
1450
1451        set_server_key(sks);
1452
1453        // Intentionally low so that we test when multiple lists and proofs are needed
1454        let crs = CompactPkeCrs::from_config(config, 32).unwrap();
1455
1456        let metadata = [b'h', b'l', b'a', b'p', b'i'];
1457
1458        let compact_list = ProvenCompactCiphertextList::builder(&pk)
1459            .push(17u32)
1460            .push(-1i64)
1461            .push(false)
1462            .push_with_num_bits(3u32, 2)
1463            .unwrap()
1464            .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
1465            .unwrap();
1466
1467        let serialized = bincode::serialize(&compact_list).unwrap();
1468        let compact_list: ProvenCompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1469        let expander = compact_list
1470            .verify_and_expand(&crs, &pk, &metadata)
1471            .unwrap();
1472
1473        {
1474            let a: FheUint32 = expander.get(0).unwrap().unwrap();
1475            let b: FheInt64 = expander.get(1).unwrap().unwrap();
1476            let c: FheBool = expander.get(2).unwrap().unwrap();
1477            let d: FheUint2 = expander.get(3).unwrap().unwrap();
1478
1479            let a: u32 = a.decrypt(&ck);
1480            assert_eq!(a, 17);
1481            let b: i64 = b.decrypt(&ck);
1482            assert_eq!(b, -1);
1483            let c = c.decrypt(&ck);
1484            assert!(!c);
1485            let d: u8 = d.decrypt(&ck);
1486            assert_eq!(d, 3);
1487
1488            assert!(expander.get::<FheBool>(4).unwrap().is_none());
1489        }
1490
1491        {
1492            // Incorrect type
1493            assert!(expander.get::<FheInt64>(0).is_err());
1494
1495            // Correct type but wrong number of bits
1496            assert!(expander.get::<FheUint16>(0).is_err());
1497        }
1498
1499        let unverified_expander = compact_list.expand_without_verification().unwrap();
1500
1501        {
1502            let a: FheUint32 = unverified_expander.get(0).unwrap().unwrap();
1503            let b: FheInt64 = unverified_expander.get(1).unwrap().unwrap();
1504            let c: FheBool = unverified_expander.get(2).unwrap().unwrap();
1505            let d: FheUint2 = unverified_expander.get(3).unwrap().unwrap();
1506
1507            let a: u32 = a.decrypt(&ck);
1508            assert_eq!(a, 17);
1509            let b: i64 = b.decrypt(&ck);
1510            assert_eq!(b, -1);
1511            let c = c.decrypt(&ck);
1512            assert!(!c);
1513            let d: u8 = d.decrypt(&ck);
1514            assert_eq!(d, 3);
1515
1516            assert!(unverified_expander.get::<FheBool>(4).unwrap().is_none());
1517        }
1518    }
1519
1520    #[cfg(feature = "zk-pok")]
1521    #[test]
1522    fn test_empty_proven_list() {
1523        let config = crate::ConfigBuilder::with_custom_parameters(
1524            PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1525        )
1526        .use_dedicated_compact_public_key_parameters((
1527            PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1528            PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1529        ))
1530        .build();
1531
1532        let ck = crate::ClientKey::generate(config);
1533        let sk = crate::ServerKey::new(&ck);
1534        let pk = crate::CompactPublicKey::new(&ck);
1535
1536        set_server_key(sk);
1537
1538        let crs = CompactPkeCrs::from_config(config, 32).unwrap();
1539
1540        let metadata = [b'h', b'l', b'a', b'p', b'i'];
1541
1542        let compact_list = CompactCiphertextList::builder(&pk)
1543            .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
1544            .unwrap();
1545
1546        let expander = compact_list
1547            .verify_and_expand(&crs, &pk, &metadata)
1548            .unwrap();
1549
1550        assert!(expander.get::<FheBool>(0).unwrap().is_none());
1551    }
1552
1553    #[cfg(all(feature = "zk-pok", feature = "gpu"))]
1554    #[test]
1555    fn test_gpu_proven_compact_list() {
1556        for i in [0, 1] {
1557            let config = if i == 0 {
1558                crate::ConfigBuilder::with_custom_parameters(
1559                    PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1560                )
1561                .use_dedicated_compact_public_key_parameters((
1562                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1563                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1564                ))
1565                .build()
1566            } else if i == 1 {
1567                crate::ConfigBuilder::with_custom_parameters(
1568                    PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1569                )
1570                .use_dedicated_compact_public_key_parameters((
1571                    PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1572                    PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1573                ))
1574                .build()
1575            } else {
1576                panic!("Unexpected parameter set")
1577            };
1578
1579            let ck = crate::ClientKey::generate(config);
1580            let compressed_server_key = CompressedServerKey::new(&ck);
1581            let gpu_sk = compressed_server_key.decompress_to_gpu();
1582            let pk = crate::CompactPublicKey::new(&ck);
1583
1584            set_server_key(gpu_sk);
1585
1586            // Intentionally low so that we test when multiple lists and proofs are needed
1587            let crs = CompactPkeCrs::from_config(config, 32).unwrap();
1588
1589            let metadata = [b'h', b'l', b'a', b'p', b'i'];
1590
1591            let compact_list = ProvenCompactCiphertextList::builder(&pk)
1592                .push(17u32)
1593                .push(-1i64)
1594                .push(false)
1595                .push_with_num_bits(3u32, 2)
1596                .unwrap()
1597                .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)
1598                .unwrap();
1599
1600            let serialized = bincode::serialize(&compact_list).unwrap();
1601            let compact_list: ProvenCompactCiphertextList =
1602                bincode::deserialize(&serialized).unwrap();
1603            let expander = compact_list
1604                .verify_and_expand(&crs, &pk, &metadata)
1605                .unwrap();
1606
1607            {
1608                let a: FheUint32 = expander.get(0).unwrap().unwrap();
1609                let b: FheInt64 = expander.get(1).unwrap().unwrap();
1610                let c: FheBool = expander.get(2).unwrap().unwrap();
1611                let d: FheUint2 = expander.get(3).unwrap().unwrap();
1612
1613                let a: u32 = a.decrypt(&ck);
1614                assert_eq!(a, 17);
1615                let b: i64 = b.decrypt(&ck);
1616                assert_eq!(b, -1);
1617                let c = c.decrypt(&ck);
1618                assert!(!c);
1619                let d: u8 = d.decrypt(&ck);
1620                assert_eq!(d, 3);
1621
1622                assert!(expander.get::<FheBool>(4).unwrap().is_none());
1623            }
1624
1625            {
1626                // Incorrect type
1627                assert!(expander.get::<FheInt64>(0).is_err());
1628
1629                // Correct type but wrong number of bits
1630                assert!(expander.get::<FheUint16>(0).is_err());
1631            }
1632
1633            let unverified_expander = compact_list.expand_without_verification().unwrap();
1634
1635            {
1636                let a: FheUint32 = unverified_expander.get(0).unwrap().unwrap();
1637                let b: FheInt64 = unverified_expander.get(1).unwrap().unwrap();
1638                let c: FheBool = unverified_expander.get(2).unwrap().unwrap();
1639                let d: FheUint2 = unverified_expander.get(3).unwrap().unwrap();
1640
1641                let a: u32 = a.decrypt(&ck);
1642                assert_eq!(a, 17);
1643                let b: i64 = b.decrypt(&ck);
1644                assert_eq!(b, -1);
1645                let c = c.decrypt(&ck);
1646                assert!(!c);
1647                let d: u8 = d.decrypt(&ck);
1648                assert_eq!(d, 3);
1649
1650                assert!(unverified_expander.get::<FheBool>(4).unwrap().is_none());
1651            }
1652        }
1653    }
1654
1655    #[cfg(feature = "strings")]
1656    #[test]
1657    fn test_compact_list_with_string_and_casting() {
1658        for compute_param in [
1659            PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(),
1660            PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128.into(),
1661        ] {
1662            test_compact_list_with_string_and_casting_inner(compute_param);
1663        }
1664    }
1665
1666    #[cfg(feature = "strings")]
1667    fn test_compact_list_with_string_and_casting_inner(params: AtomicPatternParameters) {
1668        use crate::FheAsciiString;
1669
1670        let config = crate::ConfigBuilder::with_custom_parameters(params)
1671            .use_dedicated_compact_public_key_parameters((
1672                PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1673                PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
1674            ))
1675            .build();
1676
1677        let ck = crate::ClientKey::generate(config);
1678        let sk = crate::ServerKey::new(&ck);
1679        let pk = crate::CompactPublicKey::new(&ck);
1680
1681        let string1 = ClearString::new("The quick brown fox".to_string());
1682        let string2 = ClearString::new("jumps over the lazy dog".to_string());
1683
1684        let compact_list = CompactCiphertextList::builder(&pk)
1685            .push(17u32)
1686            .push(true)
1687            .push(&string1)
1688            .push_string_with_fixed_size(&string2, 55)
1689            .build_packed();
1690
1691        let serialized = bincode::serialize(&compact_list).unwrap();
1692        let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
1693        let expander = compact_list.expand_with_key(&sk).unwrap();
1694
1695        {
1696            let a: FheUint32 = expander.get(0).unwrap().unwrap();
1697            let b: FheBool = expander.get(1).unwrap().unwrap();
1698            let c: FheAsciiString = expander.get(2).unwrap().unwrap();
1699            let d: FheAsciiString = expander.get(3).unwrap().unwrap();
1700
1701            assert_eq!(expander.get_kind_of(0), Some(crate::FheTypes::Uint32));
1702            assert_eq!(expander.get_kind_of(1), Some(crate::FheTypes::Bool));
1703            assert_eq!(expander.get_kind_of(2), Some(crate::FheTypes::AsciiString));
1704            assert_eq!(expander.get_kind_of(3), Some(crate::FheTypes::AsciiString));
1705
1706            let a: u32 = a.decrypt(&ck);
1707            assert_eq!(a, 17);
1708            let b: bool = b.decrypt(&ck);
1709            assert!(b);
1710            let c = c.decrypt(&ck);
1711            assert_eq!(&c, string1.str());
1712            let d = d.decrypt(&ck);
1713            assert_eq!(&d, string2.str());
1714
1715            assert!(expander.get::<FheBool>(4).unwrap().is_none());
1716        }
1717
1718        {
1719            // Incorrect type
1720            assert!(expander.get::<FheInt64>(0).is_err());
1721
1722            // Correct type but wrong number of bits
1723            assert!(expander.get::<FheAsciiString>(0).is_err());
1724        }
1725    }
1726}