tfhe/high_level_api/integers/unsigned/
base.rs

1use tfhe_versionable::Versionize;
2
3use super::inner::RadixCiphertext;
4use crate::backward_compatibility::integers::FheUintVersions;
5use crate::conformance::ParameterSetConformant;
6use crate::core_crypto::prelude::{CastFrom, UnsignedInteger, UnsignedNumeric};
7#[cfg(feature = "gpu")]
8use crate::high_level_api::global_state::with_thread_local_cuda_streams;
9use crate::high_level_api::integers::signed::{FheInt, FheIntId};
10use crate::high_level_api::integers::IntegerId;
11use crate::high_level_api::keys::InternalServerKey;
12use crate::high_level_api::traits::{FheWait, Tagged};
13use crate::high_level_api::{global_state, Device};
14use crate::integer::block_decomposition::{DecomposableInto, RecomposableFrom};
15#[cfg(feature = "gpu")]
16use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext;
17use crate::integer::parameters::RadixCiphertextConformanceParams;
18use crate::integer::server_key::MatchValues;
19use crate::named::Named;
20use crate::prelude::CastInto;
21use crate::shortint::ciphertext::NotTrivialCiphertextError;
22use crate::shortint::PBSParameters;
23#[cfg(feature = "gpu")]
24use crate::GpuIndex;
25use crate::{FheBool, ServerKey, Tag};
26use std::marker::PhantomData;
27
28#[cfg(feature = "hpu")]
29use crate::high_level_api::traits::{FheHpu, HpuHandle};
30#[cfg(feature = "hpu")]
31use tfhe_hpu_backend::prelude::*;
32
33#[derive(Debug)]
34pub enum GenericIntegerBlockError {
35    NumberOfBlocks(usize, usize),
36    CarryModulus(crate::shortint::CarryModulus, crate::shortint::CarryModulus),
37    MessageModulus(
38        crate::shortint::MessageModulus,
39        crate::shortint::MessageModulus,
40    ),
41}
42
43impl std::fmt::Display for GenericIntegerBlockError {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
45        match self {
46            Self::NumberOfBlocks(correct, incorrect) => write!(
47                f,
48                "Wrong number of blocks for creating 
49                    a GenericInteger: should have been {correct}, but
50                    was {incorrect} instead"
51            ),
52            Self::CarryModulus(correct, incorrect) => write!(
53                f,
54                "Wrong carry modulus for creating 
55                    a GenericInteger: should have been {correct:?}, but
56                    was {incorrect:?} instead"
57            ),
58            Self::MessageModulus(correct, incorrect) => write!(
59                f,
60                "Wrong message modulus for creating 
61                    a GenericInteger: should have been {correct:?}, but
62                    was {incorrect:?} instead"
63            ),
64        }
65    }
66}
67
68pub trait FheUintId: IntegerId {}
69
70/// A Generic FHE unsigned integer
71///
72/// This struct is generic over some Id, as its the Id
73/// that controls how many bit they represent.
74///
75/// You will need to use one of this type specialization (e.g., [FheUint8], [FheUint12],
76/// [FheUint16]).
77///
78/// Its the type that overloads the operators (`+`, `-`, `*`),
79/// since the `FheUint` type is not `Copy` the operators are also overloaded
80/// to work with references.
81///
82/// [FheUint8]: crate::high_level_api::FheUint8
83/// [FheUint12]: crate::high_level_api::FheUint12
84/// [FheUint16]: crate::high_level_api::FheUint16
85#[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)]
86#[versionize(FheUintVersions)]
87pub struct FheUint<Id: FheUintId> {
88    pub(in crate::high_level_api) ciphertext: RadixCiphertext,
89    pub(in crate::high_level_api) id: Id,
90    pub(crate) tag: Tag,
91}
92
93#[derive(Copy, Clone)]
94pub struct FheUintConformanceParams<Id: FheUintId> {
95    pub(crate) params: RadixCiphertextConformanceParams,
96    pub(crate) id: PhantomData<Id>,
97}
98
99impl<Id: FheUintId, P: Into<PBSParameters>> From<P> for FheUintConformanceParams<Id> {
100    fn from(params: P) -> Self {
101        let params = params.into();
102        Self {
103            params: RadixCiphertextConformanceParams {
104                shortint_params: params.to_shortint_conformance_param(),
105                num_blocks_per_integer: Id::num_blocks(params.message_modulus()),
106            },
107            id: PhantomData,
108        }
109    }
110}
111
112impl<Id: FheUintId> From<&ServerKey> for FheUintConformanceParams<Id> {
113    fn from(sks: &ServerKey) -> Self {
114        Self {
115            params: RadixCiphertextConformanceParams {
116                shortint_params: sks.key.pbs_key().key.conformance_params(),
117                num_blocks_per_integer: Id::num_blocks(sks.key.pbs_key().message_modulus()),
118            },
119            id: PhantomData,
120        }
121    }
122}
123
124impl<Id: FheUintId> ParameterSetConformant for FheUint<Id> {
125    type ParameterSet = FheUintConformanceParams<Id>;
126
127    fn is_conformant(&self, params: &FheUintConformanceParams<Id>) -> bool {
128        let Self {
129            ciphertext,
130            id: _,
131            tag: _,
132        } = self;
133
134        ciphertext.on_cpu().is_conformant(&params.params)
135    }
136}
137
138impl<Id: FheUintId> Named for FheUint<Id> {
139    const NAME: &'static str = "high_level_api::FheUint";
140}
141
142impl<Id> Tagged for FheUint<Id>
143where
144    Id: FheUintId,
145{
146    fn tag(&self) -> &Tag {
147        &self.tag
148    }
149
150    fn tag_mut(&mut self) -> &mut Tag {
151        &mut self.tag
152    }
153}
154
155impl<Id> FheWait for FheUint<Id>
156where
157    Id: FheUintId,
158{
159    fn wait(&self) {
160        self.ciphertext.wait()
161    }
162}
163
164#[cfg(feature = "hpu")]
165impl<Id> FheHpu for FheUint<Id>
166where
167    Id: FheUintId,
168{
169    fn iop_exec(iop: &hpu_asm::AsmIOpcode, src: HpuHandle<&Self>) -> HpuHandle<Self> {
170        use crate::integer::hpu::ciphertext::HpuRadixCiphertext;
171        global_state::with_thread_local_hpu_device(|device| {
172            let mut srcs = Vec::new();
173            for n in src.native.iter() {
174                srcs.push(n.ciphertext.on_hpu(device).clone());
175            }
176            for b in src.boolean.iter() {
177                srcs.push(b.ciphertext.on_hpu(device).clone());
178            }
179
180            let (opcode, proto) = {
181                (
182                    iop.opcode(),
183                    &iop.format().expect("Unspecified IOP format").proto,
184                )
185            };
186            // These clones are cheap are they are just Arc
187            let hpu_res = HpuRadixCiphertext::exec(proto, opcode, &srcs, &src.imm);
188            HpuHandle {
189                native: hpu_res
190                    .iter()
191                    .filter(|x| !x.0.is_boolean())
192                    .map(|x| Self::new(x.clone(), device.tag.clone()))
193                    .collect::<Vec<_>>(),
194                boolean: hpu_res
195                    .iter()
196                    .filter(|x| x.0.is_boolean())
197                    .map(|x| FheBool::new(x.clone(), device.tag.clone()))
198                    .collect::<Vec<_>>(),
199                imm: Vec::new(),
200            }
201        })
202    }
203}
204
205impl<Id> FheUint<Id>
206where
207    Id: FheUintId,
208{
209    pub(in crate::high_level_api) fn new<T>(ciphertext: T, tag: Tag) -> Self
210    where
211        T: Into<RadixCiphertext>,
212    {
213        Self {
214            ciphertext: ciphertext.into(),
215            id: Id::default(),
216            tag,
217        }
218    }
219
220    pub fn into_raw_parts(self) -> (crate::integer::RadixCiphertext, Id, Tag) {
221        let Self {
222            ciphertext,
223            id,
224            tag,
225        } = self;
226
227        let ciphertext = ciphertext.into_cpu();
228
229        (ciphertext, id, tag)
230    }
231
232    pub fn from_raw_parts(ciphertext: crate::integer::RadixCiphertext, id: Id, tag: Tag) -> Self {
233        Self {
234            ciphertext: RadixCiphertext::Cpu(ciphertext),
235            id,
236            tag,
237        }
238    }
239
240    pub fn num_bits() -> usize {
241        Id::num_bits()
242    }
243
244    pub(in crate::high_level_api) fn move_to_device_of_server_key_if_set(&mut self) {
245        self.ciphertext.move_to_device_of_server_key_if_set();
246    }
247
248    /// Returns the device where the ciphertext is currently on
249    pub fn current_device(&self) -> Device {
250        self.ciphertext.current_device()
251    }
252
253    /// Moves (in-place) the ciphertext to the desired device.
254    ///
255    /// Does nothing if the ciphertext is already in the desired device
256    pub fn move_to_device(&mut self, device: Device) {
257        self.ciphertext.move_to_device(device)
258    }
259
260    /// Moves (in-place) the ciphertext to the device of the current
261    /// thread-local server key
262    ///
263    /// Does nothing if the ciphertext is already in the desired device
264    /// or if no server key is set
265    pub fn move_to_current_device(&mut self) {
266        self.ciphertext.move_to_device_of_server_key_if_set();
267    }
268
269    /// Returns the indexes of the GPUs where the ciphertext lives
270    ///
271    /// If the ciphertext is on another deive (e.g CPU) then the returned
272    /// slice is empty
273    #[cfg(feature = "gpu")]
274    pub fn gpu_indexes(&self) -> &[GpuIndex] {
275        #[allow(clippy::match_wildcard_for_single_variants)]
276        match &self.ciphertext {
277            RadixCiphertext::Cuda(cuda_ct) => cuda_ct.gpu_indexes(),
278            _ => &[],
279        }
280    }
281    /// Returns a FheBool that encrypts `true` if the value is even
282    ///
283    /// # Example
284    ///
285    /// ```rust
286    /// use tfhe::prelude::*;
287    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
288    ///
289    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
290    /// set_server_key(server_key);
291    ///
292    /// let a = FheUint16::encrypt(32u16, &client_key);
293    ///
294    /// let result = a.is_even();
295    /// let decrypted = result.decrypt(&client_key);
296    /// assert!(decrypted);
297    /// ```
298    pub fn is_even(&self) -> FheBool {
299        global_state::with_internal_keys(|key| match key {
300            InternalServerKey::Cpu(cpu_key) => {
301                let result = cpu_key
302                    .pbs_key()
303                    .is_even_parallelized(&*self.ciphertext.on_cpu());
304                FheBool::new(result, cpu_key.tag.clone())
305            }
306            #[cfg(feature = "gpu")]
307            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
308                let result = cuda_key
309                    .key
310                    .key
311                    .is_even(&*self.ciphertext.on_gpu(streams), streams);
312                FheBool::new(result, cuda_key.tag.clone())
313            }),
314            #[cfg(feature = "hpu")]
315            InternalServerKey::Hpu(_device) => {
316                panic!("Hpu does not support this operation yet.")
317            }
318        })
319    }
320
321    /// Returns a FheBool that encrypts `true` if the value is odd
322    ///
323    /// # Example
324    ///
325    /// ```rust
326    /// use tfhe::prelude::*;
327    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
328    ///
329    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
330    /// set_server_key(server_key);
331    ///
332    /// let a = FheUint16::encrypt(4393u16, &client_key);
333    ///
334    /// let result = a.is_odd();
335    /// let decrypted = result.decrypt(&client_key);
336    /// assert!(decrypted);
337    /// ```
338    pub fn is_odd(&self) -> FheBool {
339        global_state::with_internal_keys(|key| match key {
340            InternalServerKey::Cpu(cpu_key) => {
341                let result = cpu_key
342                    .pbs_key()
343                    .is_odd_parallelized(&*self.ciphertext.on_cpu());
344                FheBool::new(result, cpu_key.tag.clone())
345            }
346            #[cfg(feature = "gpu")]
347            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
348                let result = cuda_key
349                    .key
350                    .key
351                    .is_odd(&*self.ciphertext.on_gpu(streams), streams);
352                FheBool::new(result, cuda_key.tag.clone())
353            }),
354            #[cfg(feature = "hpu")]
355            InternalServerKey::Hpu(_device) => {
356                panic!("Hpu does not support this operation yet.")
357            }
358        })
359    }
360
361    /// Tries to decrypt a trivial ciphertext
362    ///
363    /// Trivial ciphertexts are ciphertexts which are not encrypted
364    /// meaning they can be decrypted by any key, or even without a key.
365    ///
366    /// For debugging it can be useful to use trivial ciphertext to speed up
367    /// execution, and use [Self::try_decrypt_trivial] to decrypt temporary values
368    /// and debug.
369    ///
370    /// # Example
371    ///
372    /// ```rust
373    /// use tfhe::prelude::*;
374    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
375    ///
376    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
377    /// set_server_key(server_key);
378    ///
379    /// // This is not a trivial ciphertext as we use a client key to encrypt.
380    /// let non_trivial = FheUint16::encrypt(1u16, &client_key);
381    /// // This is a trivial ciphertext
382    /// let trivial = FheUint16::encrypt_trivial(2u16);
383    ///
384    /// // We can trivial decrypt
385    /// let result: Result<u16, _> = trivial.try_decrypt_trivial();
386    /// assert_eq!(result, Ok(2));
387    ///
388    /// // We cannot trivial decrypt
389    /// let result: Result<u16, _> = non_trivial.try_decrypt_trivial();
390    /// assert!(result.is_err());
391    /// ```
392    pub fn try_decrypt_trivial<Clear>(&self) -> Result<Clear, NotTrivialCiphertextError>
393    where
394        Clear: UnsignedNumeric + RecomposableFrom<u64>,
395    {
396        self.ciphertext.on_cpu().decrypt_trivial()
397    }
398
399    /// Returns true if the ciphertext is a trivial encryption
400    ///
401    /// # Example
402    ///
403    /// ```rust
404    /// use tfhe::prelude::*;
405    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
406    ///
407    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
408    /// set_server_key(server_key);
409    ///
410    /// let non_trivial = FheUint16::encrypt(1u16, &client_key);
411    /// assert!(!non_trivial.is_trivial());
412    ///
413    /// let trivial = FheUint16::encrypt_trivial(2u16);
414    /// assert!(trivial.is_trivial());
415    /// ```
416    pub fn is_trivial(&self) -> bool {
417        self.ciphertext.on_cpu().is_trivial()
418    }
419
420    /// Sums multiple ciphertexts together.
421    ///
422    /// This is much more efficient than manually calling the `+` operator, thus
423    /// using sum should always be preferred.
424    ///
425    /// # Example
426    ///
427    /// ```rust
428    /// use tfhe::prelude::*;
429    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
430    ///
431    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
432    /// set_server_key(server_key);
433    ///
434    /// let a = FheUint16::encrypt(1u16, &client_key);
435    /// let b = FheUint16::encrypt(2u16, &client_key);
436    /// let c = FheUint16::encrypt(3u16, &client_key);
437    ///
438    /// let result = FheUint16::sum([&a, &b, &c]);
439    /// let decrypted: u16 = result.decrypt(&client_key);
440    /// assert_eq!(decrypted, 1u16 + 2 + 3);
441    ///
442    /// // Or
443    /// let result = [&a, &b, &c].into_iter().sum::<FheUint16>();
444    /// let decrypted: u16 = result.decrypt(&client_key);
445    /// assert_eq!(decrypted, 1u16 + 2 + 3);
446    /// ```
447    pub fn sum<'a, C>(collection: C) -> Self
448    where
449        C: AsRef<[&'a Self]>,
450    {
451        collection.as_ref().iter().copied().sum()
452    }
453
454    /// Returns the number of leading zeros in the binary representation of self.
455    ///
456    /// # Example
457    ///
458    /// ```rust
459    /// use tfhe::prelude::*;
460    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
461    ///
462    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
463    /// set_server_key(server_key);
464    ///
465    /// let a = FheUint16::encrypt(0b00111111_11111111u16, &client_key);
466    ///
467    /// let result = a.leading_zeros();
468    /// let decrypted: u16 = result.decrypt(&client_key);
469    /// assert_eq!(decrypted, 2);
470    /// ```
471    pub fn leading_zeros(&self) -> super::FheUint32 {
472        global_state::with_internal_keys(|key| match key {
473            InternalServerKey::Cpu(cpu_key) => {
474                let result = cpu_key
475                    .pbs_key()
476                    .leading_zeros_parallelized(&*self.ciphertext.on_cpu());
477                let result = cpu_key.pbs_key().cast_to_unsigned(
478                    result,
479                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
480                );
481                super::FheUint32::new(result, cpu_key.tag.clone())
482            }
483            #[cfg(feature = "gpu")]
484            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
485                let result = cuda_key
486                    .key
487                    .key
488                    .leading_zeros(&*self.ciphertext.on_gpu(streams), streams);
489                let result = cuda_key.key.key.cast_to_unsigned(
490                    result,
491                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
492                    streams,
493                );
494                super::FheUint32::new(result, cuda_key.tag.clone())
495            }),
496            #[cfg(feature = "hpu")]
497            InternalServerKey::Hpu(_device) => {
498                panic!("Hpu does not support this operation yet.")
499            }
500        })
501    }
502
503    /// Returns the number of leading ones in the binary representation of self.
504    ///
505    /// # Example
506    ///
507    /// ```rust
508    /// use tfhe::prelude::*;
509    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
510    ///
511    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
512    /// set_server_key(server_key);
513    ///
514    /// let a = FheUint16::encrypt(0b11000000_00000000u16, &client_key);
515    ///
516    /// let result = a.leading_ones();
517    /// let decrypted: u16 = result.decrypt(&client_key);
518    /// assert_eq!(decrypted, 2);
519    /// ```
520    pub fn leading_ones(&self) -> super::FheUint32 {
521        global_state::with_internal_keys(|key| match key {
522            InternalServerKey::Cpu(cpu_key) => {
523                let result = cpu_key
524                    .pbs_key()
525                    .leading_ones_parallelized(&*self.ciphertext.on_cpu());
526                let result = cpu_key.pbs_key().cast_to_unsigned(
527                    result,
528                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
529                );
530                super::FheUint32::new(result, cpu_key.tag.clone())
531            }
532            #[cfg(feature = "gpu")]
533            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
534                let result = cuda_key
535                    .key
536                    .key
537                    .leading_ones(&*self.ciphertext.on_gpu(streams), streams);
538                let result = cuda_key.key.key.cast_to_unsigned(
539                    result,
540                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
541                    streams,
542                );
543                super::FheUint32::new(result, cuda_key.tag.clone())
544            }),
545            #[cfg(feature = "hpu")]
546            InternalServerKey::Hpu(_device) => {
547                panic!("Hpu does not support this operation yet.")
548            }
549        })
550    }
551
552    /// Returns the number of trailing zeros in the binary representation of self.
553    ///
554    /// # Example
555    ///
556    /// ```rust
557    /// use tfhe::prelude::*;
558    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
559    ///
560    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
561    /// set_server_key(server_key);
562    ///
563    /// let a = FheUint16::encrypt(0b0000000_0101000u16, &client_key);
564    ///
565    /// let result = a.trailing_zeros();
566    /// let decrypted: u16 = result.decrypt(&client_key);
567    /// assert_eq!(decrypted, 3);
568    /// ```
569    pub fn trailing_zeros(&self) -> super::FheUint32 {
570        global_state::with_internal_keys(|key| match key {
571            InternalServerKey::Cpu(cpu_key) => {
572                let result = cpu_key
573                    .pbs_key()
574                    .trailing_zeros_parallelized(&*self.ciphertext.on_cpu());
575                let result = cpu_key.pbs_key().cast_to_unsigned(
576                    result,
577                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
578                );
579                super::FheUint32::new(result, cpu_key.tag.clone())
580            }
581            #[cfg(feature = "gpu")]
582            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
583                let result = cuda_key
584                    .key
585                    .key
586                    .trailing_zeros(&*self.ciphertext.on_gpu(streams), streams);
587                let result = cuda_key.key.key.cast_to_unsigned(
588                    result,
589                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
590                    streams,
591                );
592                super::FheUint32::new(result, cuda_key.tag.clone())
593            }),
594            #[cfg(feature = "hpu")]
595            InternalServerKey::Hpu(_device) => {
596                panic!("Hpu does not support this operation yet.")
597            }
598        })
599    }
600
601    /// Returns the number of trailing ones in the binary representation of self.
602    ///
603    /// # Example
604    ///
605    /// ```rust
606    /// use tfhe::prelude::*;
607    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
608    ///
609    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
610    /// set_server_key(server_key);
611    ///
612    /// let a = FheUint16::encrypt(0b0000000_0110111u16, &client_key);
613    ///
614    /// let result = a.trailing_ones();
615    /// let decrypted: u16 = result.decrypt(&client_key);
616    /// assert_eq!(decrypted, 3);
617    /// ```
618    pub fn trailing_ones(&self) -> super::FheUint32 {
619        global_state::with_internal_keys(|key| match key {
620            InternalServerKey::Cpu(cpu_key) => {
621                let result = cpu_key
622                    .pbs_key()
623                    .trailing_ones_parallelized(&*self.ciphertext.on_cpu());
624                let result = cpu_key.pbs_key().cast_to_unsigned(
625                    result,
626                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
627                );
628                super::FheUint32::new(result, cpu_key.tag.clone())
629            }
630            #[cfg(feature = "gpu")]
631            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
632                let result = cuda_key
633                    .key
634                    .key
635                    .trailing_ones(&*self.ciphertext.on_gpu(streams), streams);
636                let result = cuda_key.key.key.cast_to_unsigned(
637                    result,
638                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
639                    streams,
640                );
641                super::FheUint32::new(result, cuda_key.tag.clone())
642            }),
643            #[cfg(feature = "hpu")]
644            InternalServerKey::Hpu(_device) => {
645                panic!("Hpu does not support this operation yet.")
646            }
647        })
648    }
649
650    /// Returns the number of ones in the binary representation of self.
651    ///
652    /// # Example
653    ///
654    /// ```rust
655    /// use tfhe::prelude::*;
656    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheBool, FheUint16};
657    ///
658    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
659    /// set_server_key(server_key);
660    ///
661    /// let clear_a = 0b0000000_0110111u16;
662    /// let a = FheUint16::encrypt(clear_a, &client_key);
663    ///
664    /// let result = a.count_ones();
665    /// let decrypted: u32 = result.decrypt(&client_key);
666    /// assert_eq!(decrypted, clear_a.count_ones());
667    /// ```
668    pub fn count_ones(&self) -> super::FheUint32 {
669        global_state::with_internal_keys(|key| match key {
670            InternalServerKey::Cpu(cpu_key) => {
671                let result = cpu_key
672                    .pbs_key()
673                    .count_ones_parallelized(&*self.ciphertext.on_cpu());
674                let result = cpu_key.pbs_key().cast_to_unsigned(
675                    result,
676                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
677                );
678                super::FheUint32::new(result, cpu_key.tag.clone())
679            }
680            #[cfg(feature = "gpu")]
681            InternalServerKey::Cuda(_) => {
682                panic!("Cuda devices do not support count_ones yet");
683            }
684            #[cfg(feature = "hpu")]
685            InternalServerKey::Hpu(_device) => {
686                panic!("Hpu does not support this operation yet.")
687            }
688        })
689    }
690
691    /// Returns the number of zeros in the binary representation of self.
692    ///
693    /// # Example
694    ///
695    /// ```rust
696    /// use tfhe::prelude::*;
697    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheBool, FheUint16};
698    ///
699    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
700    /// set_server_key(server_key);
701    ///
702    /// let clear_a = 0b0000000_0110111u16;
703    /// let a = FheUint16::encrypt(clear_a, &client_key);
704    ///
705    /// let result = a.count_zeros();
706    /// let decrypted: u32 = result.decrypt(&client_key);
707    /// assert_eq!(decrypted, clear_a.count_zeros());
708    /// ```
709    pub fn count_zeros(&self) -> super::FheUint32 {
710        global_state::with_internal_keys(|key| match key {
711            InternalServerKey::Cpu(cpu_key) => {
712                let result = cpu_key
713                    .pbs_key()
714                    .count_zeros_parallelized(&*self.ciphertext.on_cpu());
715                let result = cpu_key.pbs_key().cast_to_unsigned(
716                    result,
717                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
718                );
719                super::FheUint32::new(result, cpu_key.tag.clone())
720            }
721            #[cfg(feature = "gpu")]
722            InternalServerKey::Cuda(_) => {
723                panic!("Cuda devices do not support count_zeros yet");
724            }
725            #[cfg(feature = "hpu")]
726            InternalServerKey::Hpu(_device) => {
727                panic!("Hpu does not support this operation yet.")
728            }
729        })
730    }
731
732    /// Returns the base 2 logarithm of the number, rounded down.
733    ///
734    /// Result has no meaning if self encrypts 0. See [Self::checked_ilog2]
735    ///
736    /// # Example
737    ///
738    /// ```rust
739    /// use tfhe::prelude::*;
740    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
741    ///
742    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
743    /// set_server_key(server_key);
744    ///
745    /// let a = FheUint16::encrypt(2u16, &client_key);
746    ///
747    /// let result = a.ilog2();
748    /// let decrypted: u16 = result.decrypt(&client_key);
749    /// assert_eq!(decrypted, 1);
750    /// ```
751    pub fn ilog2(&self) -> super::FheUint32 {
752        global_state::with_internal_keys(|key| match key {
753            InternalServerKey::Cpu(cpu_key) => {
754                let result = cpu_key
755                    .pbs_key()
756                    .ilog2_parallelized(&*self.ciphertext.on_cpu());
757                let result = cpu_key.pbs_key().cast_to_unsigned(
758                    result,
759                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
760                );
761                super::FheUint32::new(result, cpu_key.tag.clone())
762            }
763            #[cfg(feature = "gpu")]
764            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
765                let result = cuda_key
766                    .key
767                    .key
768                    .ilog2(&*self.ciphertext.on_gpu(streams), streams);
769                let result = cuda_key.key.key.cast_to_unsigned(
770                    result,
771                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
772                    streams,
773                );
774                super::FheUint32::new(result, cuda_key.tag.clone())
775            }),
776            #[cfg(feature = "hpu")]
777            InternalServerKey::Hpu(_device) => {
778                panic!("Hpu does not support this operation yet.")
779            }
780        })
781    }
782
783    /// Returns the base 2 logarithm of the number, rounded down.
784    ///
785    /// Also returns a boolean flag that is true if the result is valid (i.e self was > 0)
786    ///
787    /// # Example
788    ///
789    /// ```rust
790    /// use tfhe::prelude::*;
791    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16};
792    ///
793    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
794    /// set_server_key(server_key);
795    ///
796    /// let a = FheUint16::encrypt(0u16, &client_key);
797    ///
798    /// let (result, is_ok) = a.checked_ilog2();
799    ///
800    /// let is_ok = is_ok.decrypt(&client_key);
801    /// assert!(!is_ok);
802    ///
803    /// let decrypted: u16 = result.decrypt(&client_key);
804    /// assert_eq!(decrypted, 63); // result is meaningless
805    /// ```
806    pub fn checked_ilog2(&self) -> (super::FheUint32, FheBool) {
807        global_state::with_internal_keys(|key| match key {
808            InternalServerKey::Cpu(cpu_key) => {
809                let (result, is_ok) = cpu_key
810                    .pbs_key()
811                    .checked_ilog2_parallelized(&*self.ciphertext.on_cpu());
812                let result = cpu_key.pbs_key().cast_to_unsigned(
813                    result,
814                    super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()),
815                );
816                (
817                    super::FheUint32::new(result, cpu_key.tag.clone()),
818                    FheBool::new(is_ok, cpu_key.tag.clone()),
819                )
820            }
821            #[cfg(feature = "gpu")]
822            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
823                let (result, is_ok) = cuda_key
824                    .key
825                    .key
826                    .checked_ilog2(&*self.ciphertext.on_gpu(streams), streams);
827                let result = cuda_key.key.key.cast_to_unsigned(
828                    result,
829                    super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus),
830                    streams,
831                );
832                (
833                    super::FheUint32::new(result, cuda_key.tag.clone()),
834                    FheBool::new(is_ok, cuda_key.tag.clone()),
835                )
836            }),
837            #[cfg(feature = "hpu")]
838            InternalServerKey::Hpu(_device) => {
839                panic!("Hpu does not support this operation yet.")
840            }
841        })
842    }
843
844    /// `match` an input value to an output value
845    ///
846    /// - Input values are not required to span all possible values that `self` could hold. And the
847    ///   output type can be different.
848    ///
849    /// Returns a FheBool that encrypts `true` if the input `self`
850    /// matched one of the possible inputs
851    ///
852    /// # Example
853    ///
854    /// ```rust
855    /// use tfhe::prelude::*;
856    /// use tfhe::{
857    ///     generate_keys, set_server_key, ConfigBuilder, FheUint16, FheUint8, MatchValues,
858    /// };
859    ///
860    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
861    /// set_server_key(server_key);
862    ///
863    /// let a = FheUint16::encrypt(17u16, &client_key);
864    ///
865    /// let match_values = MatchValues::new(vec![
866    ///     (0u16, 3u16),
867    ///     (1u16, 3u16),
868    ///     (2u16, 3u16),
869    ///     (17u16, 25u16),
870    /// ])
871    /// .unwrap();
872    /// let (result, matched): (FheUint8, _) = a.match_value(&match_values)
873    ///     .unwrap(); // All possible output values fit in a u8
874    ///
875    /// let matched = matched.decrypt(&client_key);
876    /// assert!(matched);
877    ///
878    /// let decrypted: u16 = result.decrypt(&client_key);
879    /// assert_eq!(decrypted, 25u16)
880    /// ```
881    pub fn match_value<Clear, OutId>(
882        &self,
883        matches: &MatchValues<Clear>,
884    ) -> crate::Result<(FheUint<OutId>, FheBool)>
885    where
886        Clear: UnsignedInteger + DecomposableInto<u64> + CastInto<usize>,
887        OutId: FheUintId,
888    {
889        global_state::with_internal_keys(|key| match key {
890            InternalServerKey::Cpu(cpu_key) => {
891                let (result, matched) = cpu_key
892                    .pbs_key()
893                    .match_value_parallelized(&self.ciphertext.on_cpu(), matches);
894                let target_num_blocks = OutId::num_blocks(cpu_key.message_modulus());
895                if target_num_blocks >= result.blocks.len() {
896                    let result = cpu_key
897                        .pbs_key()
898                        .cast_to_unsigned(result, target_num_blocks);
899                    Ok((
900                        FheUint::new(result, cpu_key.tag.clone()),
901                        FheBool::new(matched, cpu_key.tag.clone()),
902                    ))
903                } else {
904                    Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string()))
905                }
906            }
907            #[cfg(feature = "gpu")]
908            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
909                let (result, matched) = cuda_key.key.key.match_value(
910                    &self.ciphertext.on_gpu(streams),
911                    matches,
912                    streams,
913                );
914                let target_num_blocks = OutId::num_blocks(cuda_key.key.key.message_modulus);
915                if target_num_blocks >= result.ciphertext.d_blocks.lwe_ciphertext_count().0 {
916                    Ok((
917                        FheUint::new(result, cuda_key.tag.clone()),
918                        FheBool::new(matched, cuda_key.tag.clone()),
919                    ))
920                } else {
921                    Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string()))
922                }
923            }),
924            #[cfg(feature = "hpu")]
925            InternalServerKey::Hpu(_device) => {
926                panic!("Hpu does not support this operation yet.")
927            }
928        })
929    }
930
931    /// `match` an input value to an output value
932    ///
933    /// - Input values are not required to span all possible values that `self` could hold. And the
934    ///   output type can be different.
935    ///
936    /// If none of the input matched the `self` then, `self` will encrypt the
937    /// value given to `or_value`
938    /// # Example
939    ///
940    /// ```rust
941    /// use tfhe::prelude::*;
942    /// use tfhe::{
943    ///     generate_keys, set_server_key, ConfigBuilder, FheUint16, FheUint8, MatchValues,
944    /// };
945    ///
946    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
947    /// set_server_key(server_key);
948    ///
949    /// let a = FheUint16::encrypt(17u16, &client_key);
950    ///
951    /// let match_values = MatchValues::new(vec![
952    ///     (0u16, 3u16), // map 0 to 3
953    ///     (1u16, 234u16),
954    ///     (2u16, 123u16),
955    /// ])
956    /// .unwrap();
957    /// let result: FheUint8 = a.match_value_or(&match_values, 25u16)
958    ///     .unwrap(); // All possible output values fit on a u8
959    ///
960    /// let decrypted: u16 = result.decrypt(&client_key);
961    /// assert_eq!(decrypted, 25u16)
962    /// ```
963    pub fn match_value_or<Clear, OutId>(
964        &self,
965        matches: &MatchValues<Clear>,
966        or_value: Clear,
967    ) -> crate::Result<FheUint<OutId>>
968    where
969        Clear: UnsignedInteger + DecomposableInto<u64> + CastInto<usize>,
970        OutId: FheUintId,
971    {
972        global_state::with_internal_keys(|key| match key {
973            InternalServerKey::Cpu(cpu_key) => {
974                let result = cpu_key.pbs_key().match_value_or_parallelized(
975                    &self.ciphertext.on_cpu(),
976                    matches,
977                    or_value,
978                );
979                let target_num_blocks = OutId::num_blocks(cpu_key.message_modulus());
980                if target_num_blocks >= result.blocks.len() {
981                    let result = cpu_key
982                        .pbs_key()
983                        .cast_to_unsigned(result, target_num_blocks);
984                    Ok(FheUint::new(result, cpu_key.tag.clone()))
985                } else {
986                    Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string()))
987                }
988            }
989            #[cfg(feature = "gpu")]
990            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
991                let result = cuda_key.key.key.match_value_or(
992                    &self.ciphertext.on_gpu(streams),
993                    matches,
994                    or_value,
995                    streams,
996                );
997                let target_num_blocks = OutId::num_blocks(cuda_key.key.key.message_modulus);
998                if target_num_blocks >= result.ciphertext.d_blocks.lwe_ciphertext_count().0 {
999                    Ok(FheUint::new(result, cuda_key.tag.clone()))
1000                } else {
1001                    Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string()))
1002                }
1003            }),
1004            #[cfg(feature = "hpu")]
1005            InternalServerKey::Hpu(_device) => {
1006                panic!("Hpu does not support this operation yet.")
1007            }
1008        })
1009    }
1010
1011    /// Reverse the bit of the unsigned integer
1012    ///
1013    /// # Example
1014    ///
1015    /// ```rust
1016    /// use tfhe::prelude::*;
1017    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
1018    ///
1019    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
1020    /// set_server_key(server_key);
1021    ///
1022    /// let msg = 0b10110100_u8;
1023    ///
1024    /// let a = FheUint8::encrypt(msg, &client_key);
1025    ///
1026    /// let result: FheUint8 = a.reverse_bits();
1027    ///
1028    /// let decrypted: u8 = result.decrypt(&client_key);
1029    /// assert_eq!(decrypted, msg.reverse_bits());
1030    /// ```
1031    pub fn reverse_bits(&self) -> Self {
1032        global_state::with_internal_keys(|key| match key {
1033            InternalServerKey::Cpu(cpu_key) => {
1034                let sk = &cpu_key.pbs_key();
1035
1036                let ct = self.ciphertext.on_cpu();
1037
1038                Self::new(sk.reverse_bits_parallelized(&*ct), cpu_key.tag.clone())
1039            }
1040            #[cfg(feature = "gpu")]
1041            InternalServerKey::Cuda(_) => {
1042                panic!("Cuda devices do not support reverse yet");
1043            }
1044            #[cfg(feature = "hpu")]
1045            InternalServerKey::Hpu(_device) => {
1046                panic!("Hpu does not support this operation yet.")
1047            }
1048        })
1049    }
1050
1051    /// Creates a FheUint that encrypts either of two values depending
1052    /// on an encrypted condition
1053    ///
1054    /// # Example
1055    ///
1056    /// ```rust
1057    /// use tfhe::prelude::*;
1058    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheBool, FheUint32};
1059    ///
1060    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
1061    /// set_server_key(server_key);
1062    ///
1063    /// let cond = FheBool::encrypt(true, &client_key);
1064    ///
1065    /// let result = FheUint32::if_then_else(&cond, u32::MAX, u32::MIN);
1066    /// let decrypted: u32 = result.decrypt(&client_key);
1067    /// assert_eq!(decrypted, u32::MAX);
1068    ///
1069    /// let result = FheUint32::if_then_else(&!cond, u32::MAX, u32::MIN);
1070    /// let decrypted: u32 = result.decrypt(&client_key);
1071    /// assert_eq!(decrypted, u32::MIN);
1072    /// ```
1073    pub fn if_then_else<Clear>(condition: &FheBool, true_value: Clear, false_value: Clear) -> Self
1074    where
1075        Clear: UnsignedNumeric + DecomposableInto<u64>,
1076    {
1077        global_state::with_internal_keys(|key| match key {
1078            InternalServerKey::Cpu(cpu_key) => {
1079                let sk = cpu_key.pbs_key();
1080
1081                let result: crate::integer::RadixCiphertext = sk.scalar_if_then_else_parallelized(
1082                    &condition.ciphertext.on_cpu(),
1083                    true_value,
1084                    false_value,
1085                    Id::num_blocks(sk.message_modulus()),
1086                );
1087
1088                Self::new(result, cpu_key.tag.clone())
1089            }
1090            #[cfg(feature = "gpu")]
1091            InternalServerKey::Cuda(_) => {
1092                panic!("Cuda devices do not support if_then_else yet");
1093            }
1094            #[cfg(feature = "hpu")]
1095            InternalServerKey::Hpu(_) => {
1096                panic!("Hpu does not support this operation yet.");
1097            }
1098        })
1099    }
1100
1101    /// Same as [Self::if_then_else] but with a different name
1102    pub fn select<Clear>(condition: &FheBool, true_value: Clear, false_value: Clear) -> Self
1103    where
1104        Clear: UnsignedNumeric + DecomposableInto<u64>,
1105    {
1106        Self::if_then_else(condition, true_value, false_value)
1107    }
1108
1109    /// Same as [Self::if_then_else] but with a different name
1110    pub fn cmux<Clear>(condition: &FheBool, true_value: Clear, false_value: Clear) -> Self
1111    where
1112        Clear: UnsignedNumeric + DecomposableInto<u64>,
1113    {
1114        Self::if_then_else(condition, true_value, false_value)
1115    }
1116}
1117
1118impl<Id> TryFrom<crate::integer::RadixCiphertext> for FheUint<Id>
1119where
1120    Id: FheUintId,
1121{
1122    type Error = GenericIntegerBlockError;
1123
1124    fn try_from(other: crate::integer::RadixCiphertext) -> Result<Self, GenericIntegerBlockError> {
1125        // Get correct carry modulus and message modulus from ServerKey
1126        let (correct_carry_mod, correct_message_mod) =
1127            global_state::with_internal_keys(|sks| match sks {
1128                InternalServerKey::Cpu(sks) => (
1129                    sks.pbs_key().key.carry_modulus,
1130                    sks.pbs_key().key.message_modulus,
1131                ),
1132                #[cfg(feature = "gpu")]
1133                InternalServerKey::Cuda(cuda_key) => (
1134                    cuda_key.key.key.carry_modulus,
1135                    cuda_key.key.key.message_modulus,
1136                ),
1137                #[cfg(feature = "hpu")]
1138                InternalServerKey::Hpu(_device) => {
1139                    panic!("Hpu does not support this operation yet.")
1140                }
1141            });
1142
1143        // Check number of blocks
1144        let expected_num_blocks = Id::num_blocks(correct_message_mod);
1145        if other.blocks.len() != expected_num_blocks {
1146            return Err(GenericIntegerBlockError::NumberOfBlocks(
1147                expected_num_blocks,
1148                other.blocks.len(),
1149            ));
1150        }
1151
1152        // For each block, check that carry modulus and message modulus are valid
1153        for block in &other.blocks {
1154            let (input_carry_mod, input_message_mod) = (block.carry_modulus, block.message_modulus);
1155
1156            if input_carry_mod != correct_carry_mod {
1157                return Err(GenericIntegerBlockError::CarryModulus(
1158                    correct_carry_mod,
1159                    input_carry_mod,
1160                ));
1161            } else if input_message_mod != correct_message_mod {
1162                return Err(GenericIntegerBlockError::MessageModulus(
1163                    correct_message_mod,
1164                    input_message_mod,
1165                ));
1166            }
1167        }
1168
1169        let mut ciphertext = Self::new(other, Tag::default());
1170        ciphertext.move_to_device_of_server_key_if_set();
1171        Ok(ciphertext)
1172    }
1173}
1174
1175impl<Id, T> TryFrom<Vec<T>> for FheUint<Id>
1176where
1177    Id: FheUintId,
1178    crate::integer::RadixCiphertext: From<Vec<T>>,
1179{
1180    type Error = GenericIntegerBlockError;
1181    fn try_from(blocks: Vec<T>) -> Result<Self, GenericIntegerBlockError> {
1182        let ciphertext = crate::integer::RadixCiphertext::from(blocks);
1183        Self::try_from(ciphertext)
1184    }
1185}
1186
1187impl<FromId, IntoId> CastFrom<FheInt<FromId>> for FheUint<IntoId>
1188where
1189    FromId: FheIntId,
1190    IntoId: FheUintId,
1191{
1192    /// Cast a FheInt to an FheUint
1193    ///
1194    /// # Example
1195    ///
1196    /// ```rust
1197    /// use tfhe::prelude::*;
1198    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt32, FheUint16};
1199    ///
1200    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
1201    /// set_server_key(server_key);
1202    ///
1203    /// let a = FheInt32::encrypt(i32::MIN, &client_key);
1204    /// let b = FheUint16::cast_from(a);
1205    ///
1206    /// let decrypted: u16 = b.decrypt(&client_key);
1207    /// assert_eq!(decrypted, i32::MIN as u16);
1208    /// ```
1209    fn cast_from(input: FheInt<FromId>) -> Self {
1210        global_state::with_internal_keys(|keys| match keys {
1211            InternalServerKey::Cpu(cpu_key) => {
1212                let casted = cpu_key.pbs_key().cast_to_unsigned(
1213                    input.ciphertext.into_cpu(),
1214                    IntoId::num_blocks(cpu_key.message_modulus()),
1215                );
1216                Self::new(casted, cpu_key.tag.clone())
1217            }
1218            #[cfg(feature = "gpu")]
1219            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
1220                let casted = cuda_key.key.key.cast_to_unsigned(
1221                    input.ciphertext.into_gpu(streams),
1222                    IntoId::num_blocks(cuda_key.message_modulus()),
1223                    streams,
1224                );
1225                Self::new(casted, cuda_key.tag.clone())
1226            }),
1227            #[cfg(feature = "hpu")]
1228            InternalServerKey::Hpu(_device) => {
1229                panic!("Hpu does not support this operation yet.")
1230            }
1231        })
1232    }
1233}
1234
1235impl<FromId, IntoId> CastFrom<FheUint<FromId>> for FheUint<IntoId>
1236where
1237    FromId: FheUintId,
1238    IntoId: FheUintId,
1239{
1240    /// Cast FheUint to another FheUint
1241    ///
1242    /// # Example
1243    ///
1244    /// ```rust
1245    /// use tfhe::prelude::*;
1246    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint16, FheUint32};
1247    ///
1248    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
1249    /// set_server_key(server_key);
1250    ///
1251    /// let a = FheUint32::encrypt(u32::MAX, &client_key);
1252    /// let b = FheUint16::cast_from(a);
1253    ///
1254    /// let decrypted: u16 = b.decrypt(&client_key);
1255    /// assert_eq!(decrypted, u32::MAX as u16);
1256    /// ```
1257    fn cast_from(input: FheUint<FromId>) -> Self {
1258        global_state::with_internal_keys(|keys| match keys {
1259            InternalServerKey::Cpu(cpu_key) => {
1260                let casted = cpu_key.pbs_key().cast_to_unsigned(
1261                    input.ciphertext.on_cpu().to_owned(),
1262                    IntoId::num_blocks(cpu_key.message_modulus()),
1263                );
1264                Self::new(casted, cpu_key.tag.clone())
1265            }
1266            #[cfg(feature = "gpu")]
1267            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
1268                let casted = cuda_key.key.key.cast_to_unsigned(
1269                    input.ciphertext.into_gpu(streams),
1270                    IntoId::num_blocks(cuda_key.message_modulus()),
1271                    streams,
1272                );
1273                Self::new(casted, cuda_key.tag.clone())
1274            }),
1275            #[cfg(feature = "hpu")]
1276            InternalServerKey::Hpu(_device) => {
1277                panic!("Hpu does not support this operation yet.")
1278            }
1279        })
1280    }
1281}
1282
1283impl<Id> CastFrom<FheBool> for FheUint<Id>
1284where
1285    Id: FheUintId,
1286{
1287    /// Cast a boolean ciphertext to an unsigned ciphertext
1288    ///
1289    /// # Example
1290    ///
1291    /// ```rust
1292    /// use tfhe::prelude::*;
1293    /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheBool, FheUint16};
1294    ///
1295    /// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
1296    /// set_server_key(server_key);
1297    ///
1298    /// let a = FheBool::encrypt(true, &client_key);
1299    /// let b = FheUint16::cast_from(a);
1300    ///
1301    /// let decrypted: u16 = b.decrypt(&client_key);
1302    /// assert_eq!(decrypted, u16::from(true));
1303    /// ```
1304    fn cast_from(input: FheBool) -> Self {
1305        global_state::with_internal_keys(|key| match key {
1306            InternalServerKey::Cpu(cpu_key) => {
1307                let ciphertext: crate::integer::RadixCiphertext = input
1308                    .ciphertext
1309                    .on_cpu()
1310                    .into_owned()
1311                    .into_radix(Id::num_blocks(cpu_key.message_modulus()), cpu_key.pbs_key());
1312                Self::new(ciphertext, cpu_key.tag.clone())
1313            }
1314            #[cfg(feature = "gpu")]
1315            InternalServerKey::Cuda(cuda_key) => with_thread_local_cuda_streams(|streams| {
1316                let inner = cuda_key.key.key.cast_to_unsigned(
1317                    input.ciphertext.into_gpu(streams).0,
1318                    Id::num_blocks(cuda_key.message_modulus()),
1319                    streams,
1320                );
1321                Self::new(inner, cuda_key.tag.clone())
1322            }),
1323            #[cfg(feature = "hpu")]
1324            InternalServerKey::Hpu(_device) => {
1325                panic!("Hpu does not support this operation yet.")
1326            }
1327        })
1328    }
1329}
1330
1331#[cfg(test)]
1332mod test {
1333    use super::*;
1334    use crate::core_crypto::prelude::UnsignedInteger;
1335    use crate::prelude::*;
1336    use crate::shortint::parameters::{AtomicPatternKind, PARAM_MESSAGE_2_CARRY_2_KS_PBS};
1337    use crate::shortint::{CiphertextModulus, PBSOrder};
1338    use crate::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
1339    use rand::{thread_rng, Rng};
1340
1341    type IndexedParameterAccessor<Ct, T> = dyn Fn(usize, &mut Ct) -> &mut T;
1342
1343    type IndexedParameterModifier<'a, Ct> = dyn Fn(usize, &mut Ct) + 'a;
1344
1345    fn change_parameters<Ct, T: UnsignedInteger>(
1346        func: &IndexedParameterAccessor<Ct, T>,
1347    ) -> [Box<IndexedParameterModifier<'_, Ct>>; 3] {
1348        [
1349            Box::new(|i, ct| *func(i, ct) = T::ZERO),
1350            Box::new(|i, ct| *func(i, ct) = func(i, ct).wrapping_add(T::ONE)),
1351            Box::new(|i, ct| *func(i, ct) = func(i, ct).wrapping_sub(T::ONE)),
1352        ]
1353    }
1354
1355    #[test]
1356    fn test_invalid_generic_integer() {
1357        type Ct = FheUint8;
1358
1359        let config = ConfigBuilder::default().build();
1360
1361        let (client_key, _server_key) = generate_keys(config);
1362
1363        let ct = FheUint8::try_encrypt(0_u64, &client_key).unwrap();
1364
1365        assert!(ct.is_conformant(&FheUintConformanceParams::from(
1366            PARAM_MESSAGE_2_CARRY_2_KS_PBS,
1367        )));
1368
1369        let breaker_lists = [
1370            change_parameters(&|i, ct: &mut Ct| {
1371                &mut ct.ciphertext.as_cpu_mut().blocks[i].message_modulus.0
1372            }),
1373            change_parameters(&|i, ct: &mut Ct| {
1374                &mut ct.ciphertext.as_cpu_mut().blocks[i].carry_modulus.0
1375            }),
1376            change_parameters(&|i, ct: &mut Ct| {
1377                ct.ciphertext.as_cpu_mut().blocks[i].degree.as_mut()
1378            }),
1379        ];
1380
1381        for breaker_list in breaker_lists {
1382            for breaker in breaker_list {
1383                for i in 0..ct.ciphertext.on_cpu().blocks.len() {
1384                    let mut ct_clone = ct.clone();
1385
1386                    breaker(i, &mut ct_clone);
1387
1388                    assert!(!ct_clone.is_conformant(&FheUintConformanceParams::from(
1389                        PARAM_MESSAGE_2_CARRY_2_KS_PBS,
1390                    )));
1391                }
1392            }
1393        }
1394        let breakers2: Vec<&IndexedParameterModifier<'_, Ct>> = vec![
1395            &|i, ct: &mut Ct| {
1396                *ct.ciphertext.as_cpu_mut().blocks[i]
1397                    .ct
1398                    .get_mut_ciphertext_modulus() =
1399                    CiphertextModulus::try_new_power_of_2(1).unwrap();
1400            },
1401            &|i, ct: &mut Ct| {
1402                *ct.ciphertext.as_cpu_mut().blocks[i]
1403                    .ct
1404                    .get_mut_ciphertext_modulus() = CiphertextModulus::try_new(3).unwrap();
1405            },
1406            &|_i, ct: &mut Ct| {
1407                ct.ciphertext.as_cpu_mut().blocks.pop();
1408            },
1409            &|i, ct: &mut Ct| {
1410                let cloned_block = ct.ciphertext.on_cpu().blocks[i].clone();
1411                ct.ciphertext.as_cpu_mut().blocks.push(cloned_block);
1412            },
1413            &|i, ct: &mut Ct| {
1414                ct.ciphertext.as_cpu_mut().blocks[i].atomic_pattern =
1415                    AtomicPatternKind::Standard(PBSOrder::BootstrapKeyswitch);
1416            },
1417        ];
1418
1419        for breaker in breakers2 {
1420            for i in 0..ct.ciphertext.on_cpu().blocks.len() {
1421                let mut ct_clone = ct.clone();
1422
1423                breaker(i, &mut ct_clone);
1424
1425                assert!(!ct_clone.is_conformant(&FheUintConformanceParams::from(
1426                    PARAM_MESSAGE_2_CARRY_2_KS_PBS,
1427                )));
1428            }
1429        }
1430    }
1431
1432    #[test]
1433    fn test_valid_generic_integer() {
1434        let config = ConfigBuilder::default().build();
1435
1436        let (client_key, server_key) = generate_keys(config);
1437
1438        set_server_key(server_key);
1439
1440        let ct = FheUint8::try_encrypt(0_u64, &client_key).unwrap();
1441
1442        assert!(ct.is_conformant(&FheUintConformanceParams::from(
1443            PARAM_MESSAGE_2_CARRY_2_KS_PBS,
1444        )));
1445
1446        let mut rng = thread_rng();
1447
1448        let num_blocks = ct.ciphertext.on_cpu().blocks.len();
1449
1450        for _ in 0..10 {
1451            let mut ct_clone = ct.clone();
1452
1453            for i in 0..num_blocks {
1454                ct_clone.ciphertext.as_cpu_mut().blocks[i]
1455                    .ct
1456                    .as_mut()
1457                    .fill_with(|| rng.gen::<u64>());
1458            }
1459
1460            assert!(ct.is_conformant(&FheUintConformanceParams::from(
1461                PARAM_MESSAGE_2_CARRY_2_KS_PBS,
1462            )));
1463
1464            ct_clone += &ct_clone.clone();
1465        }
1466    }
1467}