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::{
4 GLWEDecrypt, GLWENoise,
5 layouts::{Base2K, GLWE, GLWEInfos, GLWEPlaintextLayout, GLWESecretPreparedToRef, LWEInfos, Rank, TorusPrecision},
6};
7
8use poulpy_core::GLWEEncryptSk;
9use poulpy_core::ScratchTakeCore;
10use poulpy_hal::layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, ZnxZero};
11use poulpy_hal::source::Source;
12
13use crate::tfhe::bdd_arithmetic::{FromBits, ToBits, UnsignedInteger};
14
15/// An FHE ciphertext encrypting the bits of an [UnsignedInteger].
16pub struct FheUintBlocks<D: Data, T: UnsignedInteger> {
17 pub(crate) blocks: Vec<GLWE<D>>,
18 pub(crate) _base: u8,
19 pub(crate) _phantom: PhantomData<T>,
20}
21
22impl<D: DataRef, T: UnsignedInteger> LWEInfos for FheUintBlocks<D, T> {
23 fn base2k(&self) -> poulpy_core::layouts::Base2K {
24 self.blocks[0].base2k()
25 }
26
27 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
28 self.blocks[0].k()
29 }
30
31 fn n(&self) -> poulpy_core::layouts::Degree {
32 self.blocks[0].n()
33 }
34}
35
36impl<D: DataRef, T: UnsignedInteger> GLWEInfos for FheUintBlocks<D, T> {
37 fn rank(&self) -> poulpy_core::layouts::Rank {
38 self.blocks[0].rank()
39 }
40}
41
42impl<D: Data, T: UnsignedInteger> FheUintBlocks<D, T> {
43 pub fn new(blocks: Vec<GLWE<D>>) -> Self {
44 assert_eq!(blocks.len(), T::WORD_SIZE);
45 Self {
46 blocks,
47 _base: 1,
48 _phantom: PhantomData,
49 }
50 }
51}
52
53impl<T: UnsignedInteger> FheUintBlocks<Vec<u8>, T> {
54 pub fn alloc_from_infos<A, BE: Backend>(module: &Module<BE>, infos: &A) -> Self
55 where
56 A: GLWEInfos,
57 {
58 Self::alloc(module, infos.base2k(), infos.k(), infos.rank())
59 }
60
61 pub fn alloc<BE: Backend>(module: &Module<BE>, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
62 Self {
63 blocks: (0..T::WORD_SIZE)
64 .map(|_| GLWE::alloc(module.n().into(), base2k, k, rank))
65 .collect(),
66 _base: 1,
67 _phantom: PhantomData,
68 }
69 }
70}
71
72impl<D: DataMut, T: UnsignedInteger + ToBits> FheUintBlocks<D, T> {
73 pub fn encrypt_sk<S, BE: Backend>(
74 &mut self,
75 module: &Module<BE>,
76 value: T,
77 sk: &S,
78 source_xa: &mut Source,
79 source_xe: &mut Source,
80 scratch: &mut Scratch<BE>,
81 ) where
82 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
83 Module<BE>: GLWEEncryptSk<BE>,
84 Scratch<BE>: ScratchTakeCore<BE>,
85 {
86 use poulpy_core::layouts::GLWEPlaintextLayout;
87 use poulpy_hal::layouts::ZnxZero;
88
89 #[cfg(debug_assertions)]
90 {
91 assert!(module.n().is_multiple_of(T::WORD_SIZE));
92 assert_eq!(self.n(), module.n() as u32);
93 assert_eq!(sk.n(), module.n() as u32);
94 }
95
96 let pt_infos = GLWEPlaintextLayout {
97 n: self.n(),
98 base2k: self.base2k(),
99 k: 1_usize.into(),
100 };
101
102 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
103 pt.data.zero();
104
105 for i in 0..T::WORD_SIZE {
106 pt.encode_coeff_i64(value.bit(i) as i64, TorusPrecision(2), 0);
107 self.blocks[i].encrypt_sk(module, &pt, sk, source_xa, source_xe, scratch_1);
108 }
109 }
110}
111
112impl<D: DataRef, T: UnsignedInteger + FromBits + ToBits> FheUintBlocks<D, T> {
113 pub fn decrypt<S, BE: Backend>(&self, module: &Module<BE>, sk: &S, scratch: &mut Scratch<BE>) -> T
114 where
115 Module<BE>: GLWEDecrypt<BE>,
116 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
117 Scratch<BE>: ScratchTakeCore<BE>,
118 {
119 #[cfg(debug_assertions)]
120 {
121 assert!(module.n().is_multiple_of(T::WORD_SIZE));
122 assert_eq!(self.n(), module.n() as u32);
123 assert_eq!(sk.n(), module.n() as u32);
124 }
125
126 let pt_infos = GLWEPlaintextLayout {
127 n: self.n(),
128 base2k: self.base2k(),
129 k: self.k(),
130 };
131
132 let (mut pt, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
133
134 let mut bits: Vec<u8> = vec![0u8; T::WORD_SIZE];
135
136 let base2k: usize = self.base2k().into();
137 let scale: f64 = 4.0 / ((1 << base2k) as f64);
138
139 for (i, bit) in bits.iter_mut().enumerate().take(T::WORD_SIZE) {
140 self.blocks[i].decrypt(module, &mut pt, sk, scratch_1);
141 let value: i64 = pt.decode_coeff_i64(base2k.into(), 0);
142 *bit = ((value as f64) * scale).round() as u8;
143 }
144
145 T::from_bits(&bits)
146 }
147
148 pub fn noise<S, BE: Backend>(&self, module: &Module<BE>, sk: &S, want: T, scratch: &mut Scratch<BE>) -> Vec<f64>
149 where
150 Module<BE>: GLWENoise<BE>,
151 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
152 Scratch<BE>: ScratchTakeCore<BE>,
153 {
154 #[cfg(debug_assertions)]
155 {
156 assert!(module.n().is_multiple_of(T::WORD_SIZE));
157 assert_eq!(self.n(), module.n() as u32);
158 assert_eq!(sk.n(), module.n() as u32);
159 }
160
161 let pt_infos = GLWEPlaintextLayout {
162 n: self.n(),
163 base2k: self.base2k(),
164 k: 1_usize.into(),
165 };
166
167 let (mut pt_want, scratch_1) = scratch.take_glwe_plaintext(&pt_infos);
168 pt_want.data.zero();
169
170 let mut noise: Vec<f64> = vec![0f64; T::WORD_SIZE];
171
172 for (i, noise_i) in noise.iter_mut().enumerate().take(T::WORD_SIZE) {
173 pt_want.encode_coeff_i64(want.bit(i) as i64, TorusPrecision(2), 0);
174 *noise_i = self.blocks[i].noise(module, sk, &pt_want, scratch_1);
175 }
176
177 noise
178 }
179}