poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/word.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/word.rsRaw
1use itertools::Itertools;
2use poulpy_core::{
3 layouts::{
4 prepared::{GLWEAutomorphismKeyPrepared, GLWESecretPrepared}, GLWEInfos, GLWEPlaintextLayout, LWEInfos, TorusPrecision, GLWE
5 }, ScratchTakeCore,
6};
7use poulpy_hal::{
8 api::{
9 ScratchAvailable, SvpApplyDftToDftInplace, VecZnxAddInplace, VecZnxAddNormal,
10 VecZnxAddScalarInplace, VecZnxAutomorphismInplace, VecZnxBigAddInplace, VecZnxBigAddSmallInplace,
11 VecZnxBigAutomorphismInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallNegateInplace, VecZnxCopy,
12 VecZnxDftApply, VecZnxDftBytesOf, VecZnxDftCopy, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA,
13 VecZnxNegateInplace, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace,
14 VecZnxRshInplace, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing, VmpApplyDftToDft, VmpApplyDftToDftAdd,
15 VmpApplyDftToDftTmpBytes,
16 },
17 layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
18 source::Source,
19};
20use std::{collections::HashMap, marker::PhantomData};
21
22use crate::tfhe::bdd_arithmetic::{FromBits, ToBits, UnsignedInteger};
23
24/// A FHE ciphertext encrypting a [UnsignedInteger].
25pub struct FheUintWord<D: Data, T: UnsignedInteger>(pub(crate) GLWE<D>, pub(crate) PhantomData<T>);
26
27impl<D: DataMut, T: UnsignedInteger> FheUintWord<D, T> {
28 #[allow(dead_code)]
29 fn post_process<ATK, BE: Backend>(
30 &mut self,
31 module: &Module<BE>,
32 mut tmp_res: Vec<GLWE<&mut [u8]>>,
33 auto_keys: &HashMap<i64, GLWEAutomorphismKeyPrepared<ATK, BE>>,
34 scratch: &mut Scratch<BE>,
35 ) where
36 ATK: DataRef,
37 Module<BE>: VecZnxSub
38 + VecZnxCopy
39 + VecZnxNegateInplace
40 + VecZnxDftBytesOf
41 + VecZnxAddInplace
42 + VmpApplyDftToDftTmpBytes
43 + VecZnxNormalizeTmpBytes
44 + VecZnxDftApply<BE>
45 + VmpApplyDftToDft<BE>
46 + VmpApplyDftToDftAdd<BE>
47 + VecZnxIdftApplyConsume<BE>
48 + VecZnxBigNormalize<BE>
49 + VecZnxNormalize<BE>
50 + VecZnxRotateInplace<BE>
51 + VecZnxNormalizeInplace<BE>
52 + VecZnxSwitchRing
53 + VecZnxBigAutomorphismInplace<BE>
54 + VecZnxRshInplace<BE>
55 + VecZnxDftCopy<BE>
56 + VecZnxIdftApplyTmpA<BE>
57 + VecZnxSubInplace
58 + VecZnxBigNormalizeTmpBytes
59 + VecZnxBigAddSmallInplace<BE>
60 + VecZnxAutomorphismInplace<BE>
61 + VecZnxBigSubSmallNegateInplace<BE>
62 + VecZnxRotate,
63 Scratch<BE>: ScratchTakeCore<BE>,
64 {
65 // Repacks the GLWE ciphertexts bits
66 let gap: usize = module.n() / T::WORD_SIZE;
67 let log_gap: usize = (usize::BITS - (gap - 1).leading_zeros()) as usize;
68 let mut cts: HashMap<usize, &mut GLWE<&mut [u8]>> = HashMap::new();
69 for (i, ct) in tmp_res.iter_mut().enumerate().take(T::WORD_SIZE) {
70 cts.insert(i * gap, ct);
71 }
72 glwe_packing(module, &mut cts, log_gap, auto_keys, scratch);
73
74 // And copies the repacked ciphertext on the receiver.
75 self.0.copy(module, cts.remove(&0).unwrap())
76 }
77}
78
79impl<D: DataRef, T: UnsignedInteger> LWEInfos for FheUintWord<D, T> {
80 fn base2k(&self) -> poulpy_core::layouts::Base2K {
81 self.0.base2k()
82 }
83
84 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
85 self.0.k()
86 }
87
88 fn n(&self) -> poulpy_core::layouts::Degree {
89 self.0.n()
90 }
91}
92
93impl<D: DataRef, T: UnsignedInteger> GLWEInfos for FheUintWord<D, T> {
94 fn rank(&self) -> poulpy_core::layouts::Rank {
95 self.0.rank()
96 }
97}
98
99impl<D: DataMut, T: UnsignedInteger + ToBits> FheUintWord<D, T> {
100 pub fn encrypt_sk<S: DataRef, BE: Backend>(
101 &mut self,
102 module: &Module<BE>,
103 data: T,
104 sk: &GLWESecretPrepared<S, BE>,
105 source_xa: &mut Source,
106 source_xe: &mut Source,
107 scratch: &mut Scratch<BE>,
108 ) where
109 Module<BE>: VecZnxAddScalarInplace
110 + VecZnxDftBytesOf
111 + VecZnxBigNormalize<BE>
112 + VecZnxDftApply<BE>
113 + SvpApplyDftToDftInplace<BE>
114 + VecZnxIdftApplyConsume<BE>
115 + VecZnxNormalizeTmpBytes
116 + VecZnxFillUniform
117 + VecZnxSubInplace
118 + VecZnxAddInplace
119 + VecZnxNormalizeInplace<BE>
120 + VecZnxAddNormal
121 + VecZnxNormalize<BE>
122 + VecZnxSub,
123 Scratch<BE>: ScratchTakeCore<BE>,
124 {
125 #[cfg(debug_assertions)]
126 {
127 assert!(module.n().is_multiple_of(T::WORD_SIZE));
128 assert_eq!(self.n(), module.n() as u32);
129 assert_eq!(sk.n(), module.n() as u32);
130 }
131
132 let gap: usize = module.n() / T::WORD_SIZE;
133
134 let mut data_bits: Vec<i64> = vec![0i64; module.n()];
135
136 for i in 0..T::WORD_SIZE {
137 data_bits[i * gap] = data.bit(i) as i64
138 }
139
140 let pt_infos = GLWEPlaintextLayout {
141 n: self.n(),
142 base2k: self.base2k(),
143 k: 1_usize.into(),
144 };
145
146 let (mut pt, scratch_1) = scratch.take_glwe_pt(&pt_infos);
147
148 pt.encode_vec_i64(&data_bits, TorusPrecision(1));
149 self.0
150 .encrypt_sk(module, &pt, sk, source_xa, source_xe, scratch_1);
151 }
152}
153
154impl<D: DataRef, T: UnsignedInteger + FromBits> FheUintWord<D, T> {
155 pub fn decrypt<S: DataRef, BE: Backend>(
156 &self,
157 module: &Module<BE>,
158 sk: &GLWESecretPrepared<S, BE>,
159 scratch: &mut Scratch<BE>,
160 ) -> T
161 where
162 Module<BE>: VecZnxDftApply<BE>
163 + SvpApplyDftToDftInplace<BE>
164 + VecZnxIdftApplyConsume<BE>
165 + VecZnxBigAddInplace<BE>
166 + VecZnxBigAddSmallInplace<BE>
167 + VecZnxBigNormalize<BE>,
168 Scratch<BE>: ScratchTakeCore<BE>,
169 {
170 #[cfg(debug_assertions)]
171 {
172 assert!(module.n().is_multiple_of(T::WORD_SIZE));
173 assert_eq!(self.n(), module.n() as u32);
174 assert_eq!(sk.n(), module.n() as u32);
175 }
176
177 let gap: usize = module.n() / T::WORD_SIZE;
178
179 let pt_infos = GLWEPlaintextLayout {
180 n: self.n(),
181 base2k: self.base2k(),
182 k: 1_usize.into(),
183 };
184
185 let (mut pt, scratch_1) = scratch.take_glwe_pt(&pt_infos);
186
187 self.0.decrypt(module, &mut pt, sk, scratch_1);
188
189 let mut data: Vec<i64> = vec![0i64; module.n()];
190
191 pt.decode_vec_i64(&mut data, TorusPrecision(1));
192
193 let bits: Vec<u8> = data.iter().step_by(gap).map(|c| *c as u8).collect_vec();
194 T::from_bits(&bits)
195 }
196}