poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rsRaw
1use std::marker::PhantomData;
2
3use poulpy_core::layouts::{Base2K, GLWE, GLWEInfos, GLWEPlaintextLayout, LWEInfos, Rank, TorusPrecision};
4
5#[cfg(test)]
6use poulpy_core::ScratchTakeCore;
7use poulpy_core::{layouts::prepared::GLWESecretPrepared};
8use poulpy_hal::api::VecZnxBigBytesOf;
9#[cfg(test)]
10use poulpy_hal::api::{
11 VecZnxAddInplace, VecZnxAddNormal, VecZnxFillUniform, VecZnxNormalize, VecZnxSub,
12};
13#[cfg(test)]
14use poulpy_hal::source::Source;
15use poulpy_hal::{
16 api::{
17 VecZnxBigAddInplace, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxDftApply,
18 VecZnxDftBytesOf, VecZnxIdftApplyConsume, VecZnxNormalizeTmpBytes,
19 },
20 layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
21};
22
23use poulpy_hal::api::{SvpApplyDftToDftInplace, VecZnxNormalizeInplace, VecZnxSubInplace};
24
25use crate::tfhe::bdd_arithmetic::{FromBits, ToBits, UnsignedInteger};
26
27/// An FHE ciphertext encrypting the bits of an [UnsignedInteger].
28pub struct FheUintBlocks<D: Data, T: UnsignedInteger> {
29 pub(crate) blocks: Vec<GLWE<D>>,
30 pub(crate) _base: u8,
31 pub(crate) _phantom: PhantomData<T>,
32}
33
34impl<D: DataRef, T: UnsignedInteger> LWEInfos for FheUintBlocks<D, T> {
35 fn base2k(&self) -> poulpy_core::layouts::Base2K {
36 self.blocks[0].base2k()
37 }
38
39 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
40 self.blocks[0].k()
41 }
42
43 fn n(&self) -> poulpy_core::layouts::Degree {
44 self.blocks[0].n()
45 }
46}
47
48impl<D: DataRef, T: UnsignedInteger> GLWEInfos for FheUintBlocks<D, T> {
49 fn rank(&self) -> poulpy_core::layouts::Rank {
50 self.blocks[0].rank()
51 }
52}
53
54impl<T: UnsignedInteger> FheUintBlocks<Vec<u8>, T> {
55 #[allow(dead_code)]
56 pub(crate) fn alloc<A, BE: Backend>(module: &Module<BE>, infos: &A) -> Self
57 where
58 A: GLWEInfos,
59 {
60 Self::alloc_with(module, infos.base2k(), infos.k(), infos.rank())
61 }
62
63 #[allow(dead_code)]
64 pub(crate) fn alloc_with<BE: Backend>(module: &Module<BE>, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
65 Self {
66 blocks: (0..T::WORD_SIZE)
67 .map(|_| GLWE::alloc(module.n().into(), base2k, k, rank))
68 .collect(),
69 _base: 1,
70 _phantom: PhantomData,
71 }
72 }
73}
74
75impl<D: DataMut, T: UnsignedInteger + ToBits> FheUintBlocks<D, T> {
76 #[allow(dead_code)]
77 #[cfg(test)]
78 pub(crate) fn encrypt_sk<S, BE: Backend>(
79 &mut self,
80 module: &Module<BE>,
81 value: T,
82 sk: &GLWESecretPrepared<S, BE>,
83 source_xa: &mut Source,
84 source_xe: &mut Source,
85 scratch: &mut Scratch<BE>,
86 ) where
87 S: DataRef,
88 Module<BE>: VecZnxDftBytesOf
89 + VecZnxBigNormalize<BE>
90 + VecZnxDftApply<BE>
91 + SvpApplyDftToDftInplace<BE>
92 + VecZnxIdftApplyConsume<BE>
93 + VecZnxNormalizeTmpBytes
94 + VecZnxFillUniform
95 + VecZnxSubInplace
96 + VecZnxAddInplace
97 + VecZnxNormalizeInplace<BE>
98 + VecZnxAddNormal
99 + VecZnxNormalize<BE>
100 + VecZnxSub,
101 Scratch<BE>: ScratchTakeCore<BE>,
102 {
103 use poulpy_core::layouts::GLWEPlaintextLayout;
104
105 #[cfg(debug_assertions)]
106 {
107 assert!(module.n().is_multiple_of(T::WORD_SIZE));
108 assert_eq!(self.n(), module.n() as u32);
109 assert_eq!(sk.n(), module.n() as u32);
110 }
111
112 let pt_infos = GLWEPlaintextLayout {
113 n: self.n(),
114 base2k: self.base2k(),
115 k: 1_usize.into(),
116 };
117
118 let (mut pt, scratch_1) = scratch.take_glwe_pt(&pt_infos);
119
120 for i in 0..T::WORD_SIZE {
121 pt.encode_coeff_i64(value.bit(i) as i64, TorusPrecision(1), 0);
122 self.blocks[i].encrypt_sk(&module, &pt, sk, source_xa, source_xe, scratch_1);
123 }
124 }
125}
126
127impl<D: DataRef, T: UnsignedInteger + FromBits + ToBits> FheUintBlocks<D, T> {
128 pub fn decrypt<S: DataRef, BE: Backend>(
129 &self,
130 module: &Module<BE>,
131 sk: &GLWESecretPrepared<S, BE>,
132 scratch: &mut Scratch<BE>,
133 ) -> T
134 where
135 Module<BE>: VecZnxDftApply<BE>
136 + SvpApplyDftToDftInplace<BE>
137 + VecZnxIdftApplyConsume<BE>
138 + VecZnxBigAddInplace<BE>
139 + VecZnxBigAddSmallInplace<BE>
140 + VecZnxBigNormalize<BE>,
141 Scratch<BE>: ScratchTakeCore<BE>,
142 {
143 #[cfg(debug_assertions)]
144 {
145 assert!(module.n().is_multiple_of(T::WORD_SIZE));
146 assert_eq!(self.n(), module.n() as u32);
147 assert_eq!(sk.n(), module.n() as u32);
148 }
149
150 let pt_infos = GLWEPlaintextLayout {
151 n: self.n(),
152 base2k: self.base2k(),
153 k: self.k(),
154 };
155
156 let (mut pt, scratch_1) = scratch.take_glwe_pt(&pt_infos);
157
158 let mut bits: Vec<u8> = vec![0u8; T::WORD_SIZE];
159
160 let base2k: usize = self.base2k().into();
161 let scale: f64 = 4.0 / ((1 << base2k) as f64);
162
163 for (i, bit) in bits.iter_mut().enumerate().take(T::WORD_SIZE) {
164 self.blocks[i].decrypt(module, &mut pt, sk, scratch_1);
165 let value: i64 = pt.decode_coeff_i64(base2k.into(), 0);
166 *bit = ((value as f64) * scale).round() as u8;
167 }
168
169 T::from_bits(&bits)
170 }
171
172 pub fn noise<S: DataRef, BE: Backend>(
173 &self,
174 module: &Module<BE>,
175 sk: &GLWESecretPrepared<S, BE>,
176 want: T,
177 scratch: &mut Scratch<BE>,
178 ) -> Vec<f64>
179 where
180 Module<BE>: VecZnxDftBytesOf
181 + VecZnxBigBytesOf
182 + VecZnxDftApply<BE>
183 + SvpApplyDftToDftInplace<BE>
184 + VecZnxIdftApplyConsume<BE>
185 + VecZnxBigAddInplace<BE>
186 + VecZnxBigAddSmallInplace<BE>
187 + VecZnxBigNormalize<BE>
188 + VecZnxNormalizeTmpBytes
189 + VecZnxSubInplace
190 + VecZnxNormalizeInplace<BE>,
191 Scratch<BE>: ScratchTakeCore<BE>,
192 {
193 #[cfg(debug_assertions)]
194 {
195 assert!(module.n().is_multiple_of(T::WORD_SIZE));
196 assert_eq!(self.n(), module.n() as u32);
197 assert_eq!(sk.n(), module.n() as u32);
198 }
199
200 let pt_infos = GLWEPlaintextLayout {
201 n: self.n(),
202 base2k: self.base2k(),
203 k: 1_usize.into(),
204 };
205
206 let (mut pt_want, scratch_1) = scratch.take_glwe_pt(&pt_infos);
207
208 let mut noise: Vec<f64> = vec![0f64; T::WORD_SIZE];
209
210 for (i, noise_i) in noise.iter_mut().enumerate().take(T::WORD_SIZE) {
211 pt_want.encode_coeff_i64(want.bit(i) as i64, TorusPrecision(2), 0);
212 *noise_i = self.blocks[i].noise(module, sk, &pt_want, scratch_1);
213 }
214
215 noise
216 }
217}