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 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)] 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_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 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 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 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 #[allow(irrefutable_let_patterns)]
414 if let InnerCompactCiphertextList::Cpu(inner) = &self.inner {
415 if !inner.is_packed() && !inner.needs_casting() {
416 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)] 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 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 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 pub fn expand_without_verification(&self) -> crate::Result<CompactCiphertextListExpander> {
751 #[allow(irrefutable_let_patterns)]
752 if let InnerProvenCompactCiphertextList::Cpu(inner) = &self.inner {
753 if !inner.is_packed() && !inner.needs_casting() {
755 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(¶ms));
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 assert!(expander.get::<FheInt64>(0).is_err());
1156
1157 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 assert!(expander.get::<FheInt64>(0).is_err());
1249
1250 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 assert!(expander.get::<FheUint32>(0).is_err());
1291
1292 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 assert!(expander.get::<FheUint32>(0).is_err());
1355
1356 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 assert!(expander.get::<FheInt64>(0).is_err());
1421
1422 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 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 assert!(expander.get::<FheInt64>(0).is_err());
1494
1495 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 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 assert!(expander.get::<FheInt64>(0).is_err());
1628
1629 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 assert!(expander.get::<FheInt64>(0).is_err());
1721
1722 assert!(expander.get::<FheAsciiString>(0).is_err());
1724 }
1725 }
1726}