poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rsRaw
1use itertools::Itertools;
2use poulpy_core::{
3 GLWECopy, GLWEDecrypt, GLWEEncryptSk, GLWEPacking, GLWERotate, LWEFromGLWE, ScratchTakeCore,
4 layouts::{
5 Base2K, Degree, GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWEPlaintextLayout, GLWESecretPreparedToRef, GLWEToRef,
6 LWEInfos, LWEToMut, Rank, TorusPrecision, prepared::GLWEAutomorphismKeyPrepared,
7 },
8};
9use poulpy_hal::{
10 api::ModuleN,
11 layouts::{Backend, Data, DataMut, DataRef, Scratch},
12 source::Source,
13};
14use std::{collections::HashMap, marker::PhantomData};
15
16use crate::tfhe::bdd_arithmetic::{FromBits, ToBits, UnsignedInteger};
17
18/// An FHE ciphertext encrypting the bits of an [UnsignedInteger].
19pub struct FheUint<D: Data, T: UnsignedInteger> {
20 pub(crate) bits: GLWE<D>,
21 pub(crate) _phantom: PhantomData<T>,
22}
23
24impl<T: UnsignedInteger> FheUint<Vec<u8>, T> {
25 pub fn alloc_from_infos<A>(infos: &A) -> Self
26 where
27 A: GLWEInfos,
28 {
29 Self::alloc(infos.n(), infos.base2k(), infos.k(), infos.rank())
30 }
31
32 pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
33 Self {
34 bits: GLWE::alloc(n, base2k, k, rank),
35 _phantom: PhantomData,
36 }
37 }
38}
39
40impl<D: DataRef, T: UnsignedInteger> LWEInfos for FheUint<D, T> {
41 fn base2k(&self) -> poulpy_core::layouts::Base2K {
42 self.bits.base2k()
43 }
44
45 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
46 self.bits.k()
47 }
48
49 fn n(&self) -> poulpy_core::layouts::Degree {
50 self.bits.n()
51 }
52}
53
54impl<D: DataRef, T: UnsignedInteger> GLWEInfos for FheUint<D, T> {
55 fn rank(&self) -> poulpy_core::layouts::Rank {
56 self.bits.rank()
57 }
58}
59
60impl<D: DataMut, T: UnsignedInteger + ToBits> FheUint<D, T> {
61 pub fn encrypt_sk<S, M, BE: Backend>(
62 &mut self,
63 module: &M,
64 data: T,
65 sk: &S,
66 source_xa: &mut Source,
67 source_xe: &mut Source,
68 scratch: &mut Scratch<BE>,
69 ) where
70 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
71 M: ModuleN + GLWEEncryptSk<BE>,
72 Scratch<BE>: ScratchTakeCore<BE>,
73 {
74 #[cfg(debug_assertions)]
75 {
76 assert!(module.n().is_multiple_of(T::WORD_SIZE));
77 assert_eq!(self.n(), module.n() as u32);
78 assert_eq!(sk.n(), module.n() as u32);
79 }
80
81 let gap: usize = module.n() / T::WORD_SIZE;
82
83 let mut data_bits: Vec<i64> = vec![0i64; module.n()];
84
85 for i in 0..T::WORD_SIZE {
86 data_bits[i * gap] = data.bit(i) as i64
87 }
88
89 let pt_infos = GLWEPlaintextLayout {
90 n: self.n(),
91 base2k: self.base2k(),
92 k: 1_usize.into(),
93 };
94
95 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
96
97 pt.encode_vec_i64(&data_bits, TorusPrecision(2));
98 self.bits
99 .encrypt_sk(module, &pt, sk, source_xa, source_xe, scratch_1);
100 }
101}
102
103impl<D: DataRef, T: UnsignedInteger + FromBits> FheUint<D, T> {
104 pub fn decrypt<S, M, BE: Backend>(&self, module: &M, sk: &S, scratch: &mut Scratch<BE>) -> T
105 where
106 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
107 M: GLWEDecrypt<BE>,
108 Scratch<BE>: ScratchTakeCore<BE>,
109 {
110 #[cfg(debug_assertions)]
111 {
112 assert!(module.n().is_multiple_of(T::WORD_SIZE));
113 assert_eq!(self.n(), module.n() as u32);
114 assert_eq!(sk.n(), module.n() as u32);
115 }
116
117 let gap: usize = module.n() / T::WORD_SIZE;
118
119 let pt_infos = GLWEPlaintextLayout {
120 n: self.n(),
121 base2k: self.base2k(),
122 k: 1_usize.into(),
123 };
124
125 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
126
127 self.bits.decrypt(module, &mut pt, sk, scratch_1);
128
129 let mut data: Vec<i64> = vec![0i64; module.n()];
130
131 pt.decode_vec_i64(&mut data, TorusPrecision(2));
132
133 let bits: Vec<u8> = data.iter().step_by(gap).map(|c| *c as u8).collect_vec();
134 T::from_bits(&bits)
135 }
136}
137
138impl<D: DataMut, T: UnsignedInteger> FheUint<D, T> {
139 #[allow(dead_code)]
140 pub(crate) fn pack<D1, ATK, M, BE: Backend>(
141 &mut self,
142 module: &M,
143 mut tmp_res: Vec<GLWE<D1>>,
144 auto_keys: &HashMap<i64, GLWEAutomorphismKeyPrepared<ATK, BE>>,
145 scratch: &mut Scratch<BE>,
146 ) where
147 D1: DataMut,
148 ATK: DataRef,
149 M: GLWEPacking<BE> + GLWECopy,
150 Scratch<BE>: ScratchTakeCore<BE>,
151 {
152 // Repacks the GLWE ciphertexts bits
153 let gap: usize = module.n() / T::WORD_SIZE;
154 let log_gap: usize = (usize::BITS - (gap - 1).leading_zeros()) as usize;
155 let mut cts: HashMap<usize, &mut GLWE<D1>> = HashMap::new();
156 for (i, ct) in tmp_res.iter_mut().enumerate().take(T::WORD_SIZE) {
157 cts.insert(i * gap, ct);
158 }
159
160 module.glwe_pack(&mut cts, log_gap, auto_keys, scratch);
161
162 // And copies the repacked ciphertext on the receiver.
163 module.glwe_copy(&mut self.bits, cts.remove(&0).unwrap());
164 }
165}
166
167impl<D: DataRef, T: UnsignedInteger> FheUint<D, T> {
168 pub fn get_bit<L, K, M, BE: Backend>(&self, module: &M, bit: usize, res: &mut L, ks: &K, scratch: &mut Scratch<BE>)
169 where
170 L: LWEToMut,
171 K: GGLWEPreparedToRef<BE> + GGLWEInfos,
172 M: LWEFromGLWE<BE> + GLWERotate<BE>,
173 Scratch<BE>: ScratchTakeCore<BE>,
174 {
175 let gap: usize = module.n() / T::WORD_SIZE;
176 res.to_mut().from_glwe(module, self, bit * gap, ks, scratch);
177 }
178}
179
180impl<D: DataRef, T: UnsignedInteger> GLWEToRef for FheUint<D, T> {
181 fn to_ref(&self) -> GLWE<&[u8]> {
182 self.bits.to_ref()
183 }
184}