use std::marker::PhantomData; use std::thread; use poulpy_core::layouts::{ Base2K, Dnum, Dsize, GGSWInfos, GGSWPreparedFactory, GLWEInfos, LWEInfos, Rank, TorusPrecision, prepared::GGSWPrepared, }; use poulpy_core::layouts::{ GGLWEInfos, GGLWEPreparedToRef, GGSW, GGSWLayout, GGSWPreparedToMut, GGSWPreparedToRef, GLWEAutomorphismKeyHelper, GetGaloisElement, LWE, }; use poulpy_core::{GLWECopy, GLWEDecrypt, GLWEPacking, LWEFromGLWE}; use poulpy_core::{GGSWEncryptSk, ScratchTakeCore, layouts::GLWESecretPreparedToRef}; use poulpy_hal::api::{ModuleLogN, ScratchAvailable, ScratchFromBytes}; use poulpy_hal::layouts::{Backend, Data, DataRef, Module}; use poulpy_hal::{ api::ModuleN, layouts::{DataMut, Scratch}, source::Source, }; use crate::tfhe::bdd_arithmetic::{BDDKey, BDDKeyHelper, BDDKeyInfos, BDDKeyPrepared, BDDKeyPreparedFactory, FheUint, ToBits}; use crate::tfhe::bdd_arithmetic::{Cmux, FromBits, ScratchTakeBDD, UnsignedInteger}; use crate::tfhe::blind_rotation::BlindRotationAlgo; use crate::tfhe::circuit_bootstrapping::{CircuitBootstrappingKeyInfos, CirtuitBootstrappingExecute}; /// A prepared FHE ciphertext encrypting the bits of an [UnsignedInteger]. pub struct FheUintPrepared { pub(crate) bits: Vec>, pub(crate) _phantom: PhantomData, } impl FheUintPreparedFactory for Module where Self: Sized + GGSWPreparedFactory {} pub trait GetGGSWBit: Sync { fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE>; } impl GetGGSWBit for FheUintPrepared { fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE> { assert!(bit <= self.bits.len()); self.bits[bit].to_ref() } } pub trait GetGGSWBitMut { fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE>; } impl GetGGSWBitMut for FheUintPrepared { fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE> { assert!(bit <= self.bits.len()); self.bits[bit].to_mut() } } pub trait FheUintPreparedFactory where Self: Sized + GGSWPreparedFactory, { fn alloc_fhe_uint_prepared( &self, base2k: Base2K, k: TorusPrecision, dnum: Dnum, dsize: Dsize, rank: Rank, ) -> FheUintPrepared, T, BE> { FheUintPrepared { bits: (0..T::BITS) .map(|_| GGSWPrepared::alloc(self, base2k, k, dnum, dsize, rank)) .collect(), _phantom: PhantomData, } } fn alloc_fhe_uint_prepared_from_infos(&self, infos: &A) -> FheUintPrepared, T, BE> where A: GGSWInfos, { self.alloc_fhe_uint_prepared( infos.base2k(), infos.k(), infos.dnum(), infos.dsize(), infos.rank(), ) } } impl FheUintPrepared, T, BE> { pub fn alloc_from_infos(module: &M, infos: &A) -> Self where A: GGSWInfos, M: FheUintPreparedFactory, { module.alloc_fhe_uint_prepared_from_infos(infos) } pub fn alloc(module: &M, base2k: Base2K, k: TorusPrecision, dnum: Dnum, dsize: Dsize, rank: Rank) -> Self where M: FheUintPreparedFactory, { module.alloc_fhe_uint_prepared(base2k, k, dnum, dsize, rank) } } impl FheUintPreparedEncryptSk for Module where Self: Sized + ModuleN + GGSWEncryptSk + GGSWPreparedFactory { } pub trait FheUintPreparedEncryptSk where Self: Sized + ModuleN + GGSWEncryptSk + GGSWPreparedFactory, { fn fhe_uint_prepared_encrypt_sk( &self, res: &mut FheUintPrepared, value: T, sk: &S, source_xa: &mut Source, source_xe: &mut Source, scratch: &mut Scratch, ) where DM: DataMut, S: GLWESecretPreparedToRef + GLWEInfos, Scratch: ScratchTakeCore, { use poulpy_hal::{api::ScratchTakeBasic, layouts::ZnxZero}; assert!(self.n().is_multiple_of(T::BITS as usize)); assert_eq!(res.n(), self.n() as u32); assert_eq!(sk.n(), self.n() as u32); let (mut tmp_ggsw, scratch_1) = scratch.take_ggsw(res); let (mut pt, scratch_2) = scratch_1.take_scalar_znx(self.n(), 1); pt.zero(); for i in 0..T::BITS as usize { use poulpy_hal::layouts::ZnxViewMut; pt.at_mut(0, 0)[0] = value.bit(i) as i64; tmp_ggsw.encrypt_sk(self, &pt, sk, source_xa, source_xe, scratch_2); res.bits[i].prepare(self, &tmp_ggsw, scratch_2); } } } impl FheUintPrepared { pub fn encrypt_sk( &mut self, module: &M, value: T, sk: &S, source_xa: &mut Source, source_xe: &mut Source, scratch: &mut Scratch, ) where S: GLWESecretPreparedToRef + GLWEInfos, M: FheUintPreparedEncryptSk, Scratch: ScratchTakeCore, { module.fhe_uint_prepared_encrypt_sk(self, value, sk, source_xa, source_xe, scratch); } } impl FheUintPrepared { pub fn decrypt(&self, module: &M, sk: &S, keys: &H, scratch: &mut Scratch) -> T where M: ModuleLogN + GLWEDecrypt + Cmux + GLWEPacking + GLWECopy, S: GLWESecretPreparedToRef + GLWEInfos, Scratch: ScratchTakeCore, K: GGLWEPreparedToRef + GetGaloisElement + GGLWEInfos, H: GLWEAutomorphismKeyHelper, { let (mut tmp, scratch_1) = scratch.take_fhe_uint(self); tmp.from_fhe_uint_prepared(module, self, keys, scratch_1); tmp.decrypt(module, sk, scratch_1) } } impl LWEInfos for FheUintPrepared { fn base2k(&self) -> poulpy_core::layouts::Base2K { self.bits[0].base2k() } fn k(&self) -> poulpy_core::layouts::TorusPrecision { self.bits[0].k() } fn n(&self) -> poulpy_core::layouts::Degree { self.bits[0].n() } } impl GLWEInfos for FheUintPrepared { fn rank(&self) -> poulpy_core::layouts::Rank { self.bits[0].rank() } } impl GGSWInfos for FheUintPrepared { fn dsize(&self) -> poulpy_core::layouts::Dsize { self.bits[0].dsize() } fn dnum(&self) -> poulpy_core::layouts::Dnum { self.bits[0].dnum() } } impl BDDKeyPrepared { pub fn prepare(&mut self, module: &M, other: &BDDKey, scratch: &mut Scratch) where DR: DataRef, M: BDDKeyPreparedFactory, Scratch: ScratchTakeCore, { module.prepare_bdd_key(self, other, scratch); } } pub trait FheUintPrepare { fn fhe_uint_prepare_tmp_bytes( &self, block_size: usize, extension_factor: usize, res_infos: &R, bits_infos: &A, bdd_infos: &B, ) -> usize where R: GGSWInfos, A: GLWEInfos, B: BDDKeyInfos; fn fhe_uint_prepare( &self, res: &mut FheUintPrepared, bits: &FheUint, key: &K, scratch: &mut Scratch, ) where DM: DataMut, DB: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, Scratch: ScratchFromBytes, { self.fhe_uint_prepare_custom(res, bits, 0, T::BITS as usize, key, scratch); } fn fhe_uint_prepare_custom( &self, res: &mut FheUintPrepared, bits: &FheUint, bit_start: usize, bit_count: usize, key: &K, scratch: &mut Scratch, ) where DM: DataMut, DB: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, { self.fhe_uint_prepare_custom_multi_thread(1, res, bits, bit_start, bit_count, key, scratch) } #[allow(clippy::too_many_arguments)] fn fhe_uint_prepare_custom_multi_thread( &self, threads: usize, res: &mut FheUintPrepared, bits: &FheUint, bit_start: usize, bit_count: usize, key: &K, scratch: &mut Scratch, ) where DM: DataMut, DB: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos; } impl FheUintPrepare for Module where Self: LWEFromGLWE + CirtuitBootstrappingExecute + GGSWPreparedFactory, Scratch: ScratchTakeCore, { fn fhe_uint_prepare_tmp_bytes( &self, block_size: usize, extension_factor: usize, res_infos: &R, bits_infos: &A, bdd_infos: &B, ) -> usize where R: GGSWInfos, A: GLWEInfos, B: BDDKeyInfos, { self.circuit_bootstrapping_execute_tmp_bytes( block_size, extension_factor, res_infos, &bdd_infos.cbt_infos(), ) + GGSW::bytes_of_from_infos(res_infos) + LWE::bytes_of_from_infos(bits_infos) } fn fhe_uint_prepare_custom_multi_thread( &self, threads: usize, res: &mut FheUintPrepared, bits: &FheUint, bit_start: usize, bit_count: usize, key: &K, scratch: &mut Scratch, ) where DM: DataMut, DB: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, { let bit_end = bit_start + bit_count; let (cbt, ks) = key.get_cbt_key(); assert!(bit_end <= T::BITS as usize); let scratch_thread_size = self.fhe_uint_prepare_tmp_bytes(cbt.block_size(), 1, res, bits, key); assert!(scratch.available() >= threads * scratch_thread_size); let chunk_size: usize = bit_count.div_ceil(threads); let (mut scratches, _) = scratch.split_mut(threads, scratch_thread_size); let ggsw_infos: &GGSWLayout = &res.ggsw_layout(); thread::scope(|scope| { for (thread_index, (scratch_thread, res_bits_chunk)) in scratches .iter_mut() .zip(res.bits[bit_start..bit_end].chunks_mut(chunk_size)) .enumerate() { let start: usize = bit_start + thread_index * chunk_size; scope.spawn(move || { let (mut tmp_ggsw, scratch_1) = scratch_thread.take_ggsw(ggsw_infos); let (mut tmp_lwe, scratch_2) = scratch_1.take_lwe(bits); for (local_bit, dst) in res_bits_chunk.iter_mut().enumerate() { bits.get_bit_lwe(self, start + local_bit, &mut tmp_lwe, ks, scratch_2); cbt.execute_to_constant(self, &mut tmp_ggsw, &tmp_lwe, 1, 1, scratch_2); dst.prepare(self, &tmp_ggsw, scratch_2); } }); } }); for i in 0..bit_start { res.bits[i].zero(self); } for i in bit_end..T::BITS as usize { res.bits[i].zero(self); } } } impl FheUintPrepared { pub fn prepare(&mut self, module: &M, other: &FheUint, key: &K, scratch: &mut Scratch) where BRA: BlindRotationAlgo, O: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, M: FheUintPrepare, Scratch: ScratchTakeCore, { module.fhe_uint_prepare(self, other, key, scratch); } pub fn prepare_custom( &mut self, module: &M, other: &FheUint, bit_start: usize, bit_end: usize, key: &K, scratch: &mut Scratch, ) where BRA: BlindRotationAlgo, O: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, M: FheUintPrepare, Scratch: ScratchTakeCore, { module.fhe_uint_prepare_custom(self, other, bit_start, bit_end, key, scratch); } #[allow(clippy::too_many_arguments)] pub fn prepare_custom_multi_thread( &mut self, threads: usize, module: &M, other: &FheUint, bit_start: usize, bit_end: usize, key: &K, scratch: &mut Scratch, ) where BRA: BlindRotationAlgo, O: DataRef, DK: DataRef, K: BDDKeyHelper + BDDKeyInfos, M: FheUintPrepare, Scratch: ScratchTakeCore, { module.fhe_uint_prepare_custom_multi_thread(threads, self, other, bit_start, bit_end, key, scratch); } }