arnaucube
arnaucube/poulpyPublic Self-hosted repository
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    GLWECopy, GLWEDecrypt, GLWEEncryptSk, GLWEPacking, GLWERotate, LWEFromGLWE, ScratchTakeCore,
  3    layouts::{
  4        Base2K, Degree, GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWEPlaintextLayout, GLWESecretPreparedToRef, GLWEToMut,
  5        GLWEToRef, 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    // pub fn copy_byte<D0, D1, BRA, M, BE: Backend>(
174    // &mut self,
175    // module: &M,
176    // byte_self: usize,
177    // byte_a: usize,
178    // a: &FheUint<D1, T>,
179    // keys: &BDDKeyPrepared<D0, BRA, BE>,
180    // scratch: &mut Scratch<BE>,
181    // ) where
182    // D0: DataRef,
183    // D1: DataRef,
184    // BRA: BlindRotationAlgo,
185    // M:ModuleLogN + GLWERotate<BE> + GLWETrace<BE> + GLWESub + GLWEAdd,
186    // Scratch<BE>: ScratchTakeBDD<T, BE>,
187    // {
188    // let (mut tmp_fhe_uint_byte, scratch_1) = scratch.take_fhe_uint(a);
189    //
190    //
191    // let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
192    // module.glwe_rotate(-((T::bit_index(byte_a << 3) << log_gap) as i64), tmp_fhe_uint_byte, self);
193    // module.glwe_trace_inplace(&mut tmp_fhe_uint_byte, module.log_n() - 3, module.log_n(),&keys.cbt.atk, scratch);
194    //
195    // let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
196    // let rot: i64 = (T::bit_index(byte_self << 3) << log_gap) as i64;
197    //
198    // Move starting byte index to first coefficient
199    // module.glwe_rotate_inplace(-rot, &mut self.bits, scratch);
200    //
201    // Stores this byte (everything else zeroed) into tmp_trace
202    // let (mut tmp_trace, scratch_1) = scratch.take_glwe(self);
203    // module.glwe_trace(
204    // &mut tmp_trace,
205    // module.log_n() - 3,
206    // module.log_n(),
207    // self,
208    // &keys.cbt.atk,
209    // scratch_1,
210    // );
211    //
212    // Subtracts the byte
213    // module.glwe_sub_inplace(&mut self.bits, &tmp_trace);
214    //
215    // module.glwe_add_inplace(&mut self.bits, &tmp_fhe_uint_byte);
216    //
217    // Moves back into the original position
218    // module.glwe_rotate_inplace(-rot, &mut self.bits, scratch);
219    //
220    // }
221}
222
223impl<D: DataMut, T: UnsignedInteger> GLWEToMut for FheUint<D, T> {
224    fn to_mut(&mut self) -> GLWE<&mut [u8]> {
225        self.bits.to_mut()
226    }
227}
228
229pub trait ScratchTakeBDD<T: UnsignedInteger, BE: Backend>
230where
231    Self: ScratchTakeCore<BE>,
232{
233    fn take_fhe_uint<A>(&mut self, infos: &A) -> (FheUint<&mut [u8], T>, &mut Self)
234    where
235        A: GLWEInfos,
236    {
237        let (glwe, scratch) = self.take_glwe(infos);
238        (
239            FheUint {
240                bits: glwe,
241                _phantom: PhantomData,
242            },
243            scratch,
244        )
245    }
246}
247
248impl<T: UnsignedInteger, BE: Backend> ScratchTakeBDD<T, BE> for Scratch<BE> where Self: ScratchTakeCore<BE> {}
249
250impl<D: DataRef, T: UnsignedInteger> FheUint<D, T> {
251    pub fn get_bit<R, K, M, BE: Backend>(&self, module: &M, bit: usize, res: &mut R, ks: &K, scratch: &mut Scratch<BE>)
252    where
253        R: LWEToMut,
254        K: GGLWEPreparedToRef<BE> + GGLWEInfos,
255        M: ModuleLogN + LWEFromGLWE<BE> + GLWERotate<BE>,
256        Scratch<BE>: ScratchTakeCore<BE>,
257    {
258        let log_gap: usize = module.log_n() - T::LOG_BITS as usize;
259        res.to_mut()
260            .from_glwe(module, self, T::bit_index(bit) << log_gap, ks, scratch);
261    }
262}
263
264impl<D: DataRef, T: UnsignedInteger> GLWEToRef for FheUint<D, T> {
265    fn to_ref(&self) -> GLWE<&[u8]> {
266        self.bits.to_ref()
267    }
268}