use base2k::{ Backend, FFT64, MatZnxDft, MatZnxDftToRef, Module, ScalarZnxDft, ScalarZnxDftOps, ScalarZnxDftToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxToMut, VecZnxToRef, ZnxZero, }; use sampling::source::Source; use crate::{ elem::Infos, gglwe_ciphertext::GGLWECiphertext, ggsw_ciphertext::GGSWCiphertext, glwe_ciphertext::GLWECiphertext, glwe_plaintext::GLWEPlaintext, keys::SecretKeyFourier, keyswitch_key::GLWESwitchingKey, utils::derive_size, vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace}, }; pub struct GLWECiphertextFourier { pub data: VecZnxDft, pub basek: usize, pub k: usize, } impl GLWECiphertextFourier, B> { pub fn new(module: &Module, log_base2k: usize, log_k: usize, rank: usize) -> Self { Self { data: module.new_vec_znx_dft(rank + 1, derive_size(log_base2k, log_k)), basek: log_base2k, k: log_k, } } } impl Infos for GLWECiphertextFourier { type Inner = VecZnxDft; fn inner(&self) -> &Self::Inner { &self.data } fn basek(&self) -> usize { self.basek } fn k(&self) -> usize { self.k } } impl GLWECiphertextFourier { pub fn rank(&self) -> usize { self.cols() - 1 } } impl VecZnxDftToMut for GLWECiphertextFourier where VecZnxDft: VecZnxDftToMut, { fn to_mut(&mut self) -> VecZnxDft<&mut [u8], B> { self.data.to_mut() } } impl VecZnxDftToRef for GLWECiphertextFourier where VecZnxDft: VecZnxDftToRef, { fn to_ref(&self) -> VecZnxDft<&[u8], B> { self.data.to_ref() } } impl GLWECiphertextFourier, FFT64> { #[allow(dead_code)] pub(crate) fn idft_scratch_space(module: &Module, size: usize) -> usize { module.bytes_of_vec_znx(1, size) + (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes()) } pub fn encrypt_sk_scratch_space(module: &Module, rank: usize, ct_size: usize) -> usize { module.bytes_of_vec_znx(rank + 1, ct_size) + GLWECiphertext::encrypt_sk_scratch_space(module, rank, ct_size) } pub fn decrypt_scratch_space(module: &Module, ct_size: usize) -> usize { (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, ct_size) | (module.bytes_of_vec_znx_big(1, ct_size) + module.vec_znx_idft_tmp_bytes())) + module.bytes_of_vec_znx_big(1, ct_size) } pub fn keyswitch_scratch_space(module: &Module, res_size: usize, lhs: usize, rhs: usize) -> usize { , FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs) } pub fn keyswitch_inplace_scratch_space(module: &Module, res_size: usize, rhs: usize) -> usize { , FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space( module, res_size, rhs, ) } pub fn external_product_scratch_space(module: &Module, res_size: usize, lhs: usize, rhs: usize) -> usize { , FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs) } pub fn external_product_inplace_scratch_space(module: &Module, res_size: usize, rhs: usize) -> usize { , FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space( module, res_size, rhs, ) } } impl GLWECiphertextFourier where VecZnxDft: VecZnxDftToMut + VecZnxDftToRef, { pub fn encrypt_zero_sk( &mut self, module: &Module, sk_dft: &SecretKeyFourier, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, bound: f64, scratch: &mut Scratch, ) where ScalarZnxDft: ScalarZnxDftToRef, { let (vec_znx_tmp, scratch_1) = scratch.tmp_vec_znx(module, self.rank() + 1, self.size()); let mut ct_idft = GLWECiphertext { data: vec_znx_tmp, basek: self.basek, k: self.k, }; ct_idft.encrypt_zero_sk( module, sk_dft, source_xa, source_xe, sigma, bound, scratch_1, ); ct_idft.dft(module, self); } pub fn keyswitch( &mut self, module: &Module, lhs: &GLWECiphertextFourier, rhs: &GLWESwitchingKey, scratch: &mut Scratch, ) where VecZnxDft: VecZnxDftToRef, MatZnxDft: MatZnxDftToRef, { rhs.0.prod_with_glwe_fourier(module, self, lhs, scratch); } pub fn keyswitch_inplace( &mut self, module: &Module, rhs: &GLWESwitchingKey, scratch: &mut Scratch, ) where MatZnxDft: MatZnxDftToRef, { rhs.0.prod_with_glwe_fourier_inplace(module, self, scratch); } pub fn external_product( &mut self, module: &Module, lhs: &GLWECiphertextFourier, rhs: &GGSWCiphertext, scratch: &mut Scratch, ) where VecZnxDft: VecZnxDftToRef, MatZnxDft: MatZnxDftToRef, { rhs.prod_with_glwe_fourier(module, self, lhs, scratch); } pub fn external_product_inplace( &mut self, module: &Module, rhs: &GGSWCiphertext, scratch: &mut Scratch, ) where MatZnxDft: MatZnxDftToRef, { rhs.prod_with_glwe_fourier_inplace(module, self, scratch); } } impl GLWECiphertextFourier where VecZnxDft: VecZnxDftToRef, { pub fn decrypt( &self, module: &Module, pt: &mut GLWEPlaintext, sk_dft: &SecretKeyFourier, scratch: &mut Scratch, ) where VecZnx: VecZnxToMut + VecZnxToRef, ScalarZnxDft: ScalarZnxDftToRef, { #[cfg(debug_assertions)] { assert_eq!(self.rank(), sk_dft.rank()); assert_eq!(self.n(), module.n()); assert_eq!(pt.n(), module.n()); assert_eq!(sk_dft.n(), module.n()); } let cols = self.rank() + 1; let (mut pt_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct pt_big.zero(); { (1..cols).for_each(|i| { let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct module.svp_apply(&mut ci_dft, 0, sk_dft, i - 1, self, i); let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft); module.vec_znx_big_add_inplace(&mut pt_big, 0, &ci_big, 0); }); } { let (mut c0_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, self.size()); // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e) module.vec_znx_idft(&mut c0_big, 0, self, 0, scratch_2); module.vec_znx_big_add_inplace(&mut pt_big, 0, &c0_big, 0); } // pt = norm(BIG(m + e)) module.vec_znx_big_normalize(self.basek(), pt, 0, &mut pt_big, 0, scratch_1); pt.basek = self.basek(); pt.k = pt.k().min(self.k()); } pub(crate) fn idft(&self, module: &Module, res: &mut GLWECiphertext, scratch: &mut Scratch) where GLWECiphertext: VecZnxToMut, { #[cfg(debug_assertions)] { assert_eq!(self.rank(), res.rank()); assert_eq!(self.basek(), res.basek()) } let min_size: usize = self.size().min(res.size()); let (mut res_big, scratch1) = scratch.tmp_vec_znx_big(module, 1, min_size); (0..self.rank() + 1).for_each(|i| { module.vec_znx_idft(&mut res_big, 0, self, i, scratch1); module.vec_znx_big_normalize(self.basek(), res, i, &res_big, 0, scratch1); }); } }