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