poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rsRaw
1use poulpy_core::{
2 GLWEAdd, GLWECopy, GLWEDecrypt, GLWEEncryptSk, GLWEPacking, GLWERotate, GLWESub, GLWETrace, LWEFromGLWE, ScratchTakeCore,
3 layouts::{
4 Base2K, Degree, GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEAutomorphismKeyHelper, GLWEInfos, GLWEPlaintextLayout,
5 GLWESecretPreparedToRef, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos, LWEToMut, Rank, TorusPrecision,
6 },
7};
8use poulpy_hal::{
9 api::ModuleLogN,
10 layouts::{Backend, Data, DataMut, DataRef, Scratch},
11 source::Source,
12};
13use std::{collections::HashMap, marker::PhantomData};
14
15use crate::tfhe::{
16 bdd_arithmetic::{BDDKeyPrepared, FromBits, ToBits, UnsignedInteger},
17 blind_rotation::BlindRotationAlgo,
18};
19
20/// An FHE ciphertext encrypting the bits of an [UnsignedInteger].
21pub struct FheUint<D: Data, T: UnsignedInteger> {
22 pub(crate) bits: GLWE<D>,
23 pub(crate) _phantom: PhantomData<T>,
24}
25
26impl<T: UnsignedInteger> FheUint<Vec<u8>, T> {
27 pub fn alloc_from_infos<A>(infos: &A) -> Self
28 where
29 A: GLWEInfos,
30 {
31 Self::alloc(infos.n(), infos.base2k(), infos.k(), infos.rank())
32 }
33
34 pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
35 Self {
36 bits: GLWE::alloc(n, base2k, k, rank),
37 _phantom: PhantomData,
38 }
39 }
40}
41
42impl<D: DataRef, T: UnsignedInteger> LWEInfos for FheUint<D, T> {
43 fn base2k(&self) -> poulpy_core::layouts::Base2K {
44 self.bits.base2k()
45 }
46
47 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
48 self.bits.k()
49 }
50
51 fn n(&self) -> poulpy_core::layouts::Degree {
52 self.bits.n()
53 }
54}
55
56impl<D: DataRef, T: UnsignedInteger> GLWEInfos for FheUint<D, T> {
57 fn rank(&self) -> poulpy_core::layouts::Rank {
58 self.bits.rank()
59 }
60}
61
62impl<D: DataMut, T: UnsignedInteger + ToBits> FheUint<D, T> {
63 pub fn encrypt_sk<S, M, BE: Backend>(
64 &mut self,
65 module: &M,
66 data: T,
67 sk: &S,
68 source_xa: &mut Source,
69 source_xe: &mut Source,
70 scratch: &mut Scratch<BE>,
71 ) where
72 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
73 M: ModuleLogN + GLWEEncryptSk<BE>,
74 Scratch<BE>: ScratchTakeCore<BE>,
75 {
76 #[cfg(debug_assertions)]
77 {
78 assert!(module.n().is_multiple_of(T::BITS as usize));
79 assert_eq!(self.n(), module.n() as u32);
80 assert_eq!(sk.n(), module.n() as u32);
81 }
82
83 let mut data_bits: Vec<i64> = vec![0i64; module.n()];
84
85 let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
86
87 // Interleaves bytes
88 for i in 0..T::BITS as usize {
89 data_bits[T::bit_index(i) << log_gap] = data.bit(i) as i64
90 }
91
92 let pt_infos = GLWEPlaintextLayout {
93 n: self.n(),
94 base2k: self.base2k(),
95 k: 1_usize.into(),
96 };
97
98 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
99
100 pt.encode_vec_i64(&data_bits, TorusPrecision(2));
101 self.bits
102 .encrypt_sk(module, &pt, sk, source_xa, source_xe, scratch_1);
103 }
104}
105
106impl<D: DataRef, T: UnsignedInteger + FromBits> FheUint<D, T> {
107 pub fn decrypt<S, M, BE: Backend>(&self, module: &M, sk: &S, scratch: &mut Scratch<BE>) -> T
108 where
109 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
110 M: ModuleLogN + GLWEDecrypt<BE>,
111 Scratch<BE>: ScratchTakeCore<BE>,
112 {
113 #[cfg(debug_assertions)]
114 {
115 assert!(module.n().is_multiple_of(T::BITS as usize));
116 assert_eq!(self.n(), module.n() as u32);
117 assert_eq!(sk.n(), module.n() as u32);
118 }
119
120 let pt_infos = GLWEPlaintextLayout {
121 n: self.n(),
122 base2k: self.base2k(),
123 k: 1_usize.into(),
124 };
125
126 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
127
128 self.bits.decrypt(module, &mut pt, sk, scratch_1);
129
130 let mut data_bits: Vec<i64> = vec![0i64; module.n()];
131 pt.decode_vec_i64(&mut data_bits, TorusPrecision(2));
132
133 let mut bits: Vec<u8> = vec![0u8; T::BITS as usize];
134
135 let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
136
137 // Retrives from interleaved bytes
138 for i in 0..T::BITS as usize {
139 bits[i] = data_bits[T::bit_index(i) << log_gap] as u8
140 }
141
142 T::from_bits(&bits)
143 }
144}
145
146impl<D: DataMut, T: UnsignedInteger> FheUint<D, T> {
147 /// Packs Vec<GLWE(bit[i])> into [FheUint].
148 pub fn pack<G, D1, M, BRA: BlindRotationAlgo, BE: Backend>(
149 &mut self,
150 module: &M,
151 mut bits: Vec<G>,
152 key: &BDDKeyPrepared<D1, BRA, BE>,
153 scratch: &mut Scratch<BE>,
154 ) where
155 G: GLWEToMut + GLWEToRef + GLWEInfos,
156 D1: DataRef,
157 M: ModuleLogN + GLWEPacking<BE> + GLWECopy,
158 Scratch<BE>: ScratchTakeCore<BE>,
159 {
160 // Repacks the GLWE ciphertexts bits
161 let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
162 let mut cts: HashMap<usize, &mut G> = HashMap::new();
163 for (i, ct) in bits.iter_mut().enumerate().take(T::BITS as usize) {
164 cts.insert(T::bit_index(i) << log_gap, ct);
165 }
166
167 module.glwe_pack(&mut cts, log_gap, &key.cbt.atk, scratch);
168
169 // And copies the repacked ciphertext on the receiver.
170 module.glwe_copy(&mut self.bits, cts.remove(&0).unwrap());
171 }
172
173 #[allow(clippy::too_many_arguments)]
174 pub fn splice_u16<A, B, H, K, M, BE: Backend>(
175 &mut self,
176 module: &M,
177 dst: usize,
178 src: usize,
179 a: &A,
180 b: &B,
181 keys: &H,
182 scratch: &mut Scratch<BE>,
183 ) where
184 A: GLWEToRef + GLWEInfos,
185 B: GLWEToRef + GLWEInfos,
186 H: GLWEAutomorphismKeyHelper<K, BE>,
187 K: GGLWEPreparedToRef<BE> + GGLWEInfos + GetGaloisElement,
188 M: ModuleLogN + GLWERotate<BE> + GLWETrace<BE> + GLWESub + GLWEAdd + GLWECopy,
189 Scratch<BE>: ScratchTakeBDD<T, BE>,
190 {
191 assert!(dst < (T::BITS >> 4) as usize);
192 assert!(src < (T::BITS >> 4) as usize);
193
194 let (mut tmp, scratch_1) = scratch.take_fhe_uint(self);
195 tmp.splice_u8(module, dst << 1, src << 1, a, b, keys, scratch_1);
196 self.splice_u8(
197 module,
198 (dst << 1) + 1,
199 (src << 1) + 1,
200 &tmp,
201 b,
202 keys,
203 scratch_1,
204 );
205 }
206
207 #[allow(clippy::too_many_arguments)]
208 // Store on the receiver a where the byte_a-th byte of a has been replaced by byte_src2 of src2.
209 pub fn splice_u8<A, B, H, K, M, BE: Backend>(
210 &mut self,
211 module: &M,
212 dst: usize,
213 src: usize,
214 a: &A,
215 b: &B,
216 keys: &H,
217 scratch: &mut Scratch<BE>,
218 ) where
219 A: GLWEToRef + GLWEInfos,
220 B: GLWEToRef + GLWEInfos,
221 H: GLWEAutomorphismKeyHelper<K, BE>,
222 K: GGLWEPreparedToRef<BE> + GGLWEInfos + GetGaloisElement,
223 M: ModuleLogN + GLWERotate<BE> + GLWETrace<BE> + GLWESub + GLWEAdd + GLWECopy,
224 Scratch<BE>: ScratchTakeBDD<T, BE>,
225 {
226 assert!(dst < (T::BITS >> 3) as usize);
227 assert!(src < (T::BITS >> 3) as usize);
228
229 // 1) Zero the byte receiver
230 let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
231 let trace_start = (T::LOG_BITS - T::LOG_BYTES) as usize;
232 let rot: i64 = (T::bit_index(dst << 3) << log_gap) as i64;
233
234 // Move a to self and align byte
235 module.glwe_rotate(-rot, &mut self.bits, a);
236
237 // Stores this byte (everything else zeroed) into tmp_trace
238 let (mut tmp_trace, scratch_1) = scratch.take_glwe(a);
239 module.glwe_trace(
240 &mut tmp_trace,
241 trace_start,
242 module.log_n(),
243 self,
244 keys,
245 scratch_1,
246 );
247
248 // Subtracts to self to zero it
249 module.glwe_sub_inplace(&mut self.bits, &tmp_trace);
250
251 // Isolate the byte to transfer from a
252 let (mut tmp_fhe_uint_byte, scratch_1) = scratch.take_fhe_uint(b);
253
254 // Move a[byte_a] into a[0]
255 module.glwe_rotate(
256 -((T::bit_index(src << 3) << log_gap) as i64),
257 &mut tmp_fhe_uint_byte,
258 b,
259 );
260
261 // Zeroes all other bytes
262 module.glwe_trace_inplace(
263 &mut tmp_fhe_uint_byte,
264 trace_start,
265 module.log_n(),
266 keys,
267 scratch_1,
268 );
269
270 // Add self[0] += a[0]
271 module.glwe_add_inplace(&mut self.bits, &tmp_fhe_uint_byte);
272
273 // Moves back self[0] to self[byte_tg]
274 module.glwe_rotate_inplace(rot, &mut self.bits, scratch);
275 }
276}
277
278impl<D: DataMut, T: UnsignedInteger> GLWEToMut for FheUint<D, T> {
279 fn to_mut(&mut self) -> GLWE<&mut [u8]> {
280 self.bits.to_mut()
281 }
282}
283
284pub trait ScratchTakeBDD<T: UnsignedInteger, BE: Backend>
285where
286 Self: ScratchTakeCore<BE>,
287{
288 fn take_fhe_uint<A>(&mut self, infos: &A) -> (FheUint<&mut [u8], T>, &mut Self)
289 where
290 A: GLWEInfos,
291 {
292 let (glwe, scratch) = self.take_glwe(infos);
293 (
294 FheUint {
295 bits: glwe,
296 _phantom: PhantomData,
297 },
298 scratch,
299 )
300 }
301}
302
303impl<T: UnsignedInteger, BE: Backend> ScratchTakeBDD<T, BE> for Scratch<BE> where Self: ScratchTakeCore<BE> {}
304
305impl<D: DataRef, T: UnsignedInteger> FheUint<D, T> {
306 pub fn get_bit<R, K, M, BE: Backend>(&self, module: &M, bit: usize, res: &mut R, ks: &K, scratch: &mut Scratch<BE>)
307 where
308 R: LWEToMut,
309 K: GGLWEPreparedToRef<BE> + GGLWEInfos,
310 M: ModuleLogN + LWEFromGLWE<BE> + GLWERotate<BE>,
311 Scratch<BE>: ScratchTakeCore<BE>,
312 {
313 let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
314 res.to_mut()
315 .from_glwe(module, self, T::bit_index(bit) << log_gap, ks, scratch);
316 }
317}
318
319impl<D: DataRef, T: UnsignedInteger> GLWEToRef for FheUint<D, T> {
320 fn to_ref(&self) -> GLWE<&[u8]> {
321 self.bits.to_ref()
322 }
323}