use poulpy_hal::{ api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare}, layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat, ZnxInfos}, oep::VmpPMatAllocBytesImpl, }; use crate::layouts::{ Base2K, BuildError, Degree, Digits, GGLWECiphertext, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision, prepared::{Prepare, PrepareAlloc}, }; #[derive(PartialEq, Eq)] pub struct GGLWECiphertextPrepared { pub(crate) data: VmpPMat, pub(crate) k: TorusPrecision, pub(crate) base2k: Base2K, pub(crate) digits: Digits, } impl LWEInfos for GGLWECiphertextPrepared { fn n(&self) -> Degree { Degree(self.data.n() as u32) } fn base2k(&self) -> Base2K { self.base2k } fn k(&self) -> TorusPrecision { self.k } fn size(&self) -> usize { self.data.size() } } impl GLWEInfos for GGLWECiphertextPrepared { fn rank(&self) -> Rank { self.rank_out() } } impl GGLWELayoutInfos for GGLWECiphertextPrepared { fn rank_in(&self) -> Rank { Rank(self.data.cols_in() as u32) } fn rank_out(&self) -> Rank { Rank(self.data.cols_out() as u32 - 1) } fn digits(&self) -> Digits { self.digits } fn rows(&self) -> Rows { Rows(self.data.rows() as u32) } } pub struct GGLWECiphertextPreparedBuilder { data: Option>, base2k: Option, k: Option, digits: Option, } impl GGLWECiphertextPrepared { #[inline] pub fn builder() -> GGLWECiphertextPreparedBuilder { GGLWECiphertextPreparedBuilder { data: None, base2k: None, k: None, digits: None, } } } impl GGLWECiphertextPreparedBuilder, B> { #[inline] pub fn layout(mut self, infos: &A) -> Self where A: GGLWELayoutInfos, B: VmpPMatAllocBytesImpl, { self.data = Some(VmpPMat::alloc( infos.n().into(), infos.rows().into(), infos.rank_in().into(), (infos.rank_out() + 1).into(), infos.size(), )); self.base2k = Some(infos.base2k()); self.k = Some(infos.k()); self.digits = Some(infos.digits()); self } } impl GGLWECiphertextPreparedBuilder { #[inline] pub fn data(mut self, data: VmpPMat) -> Self { self.data = Some(data); self } #[inline] pub fn base2k(mut self, base2k: Base2K) -> Self { self.base2k = Some(base2k); self } #[inline] pub fn k(mut self, k: TorusPrecision) -> Self { self.k = Some(k); self } #[inline] pub fn digits(mut self, digits: Digits) -> Self { self.digits = Some(digits); self } pub fn build(self) -> Result, BuildError> { let data: VmpPMat = self.data.ok_or(BuildError::MissingData)?; let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?; let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?; let digits: Digits = self.digits.ok_or(BuildError::MissingDigits)?; if base2k == 0_u32 { return Err(BuildError::ZeroBase2K); } if digits == 0_u32 { return Err(BuildError::ZeroBase2K); } if k == 0_u32 { return Err(BuildError::ZeroTorusPrecision); } if data.n() == 0 { return Err(BuildError::ZeroDegree); } if data.cols() == 0 { return Err(BuildError::ZeroCols); } if data.size() == 0 { return Err(BuildError::ZeroLimbs); } Ok(GGLWECiphertextPrepared { data, base2k, k, digits, }) } } impl GGLWECiphertextPrepared, B> { pub fn alloc(module: &Module, infos: &A) -> Self where A: GGLWELayoutInfos, Module: VmpPMatAlloc, { debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()"); Self::alloc_with( module, infos.base2k(), infos.k(), infos.rows(), infos.digits(), infos.rank_in(), infos.rank_out(), ) } pub fn alloc_with( module: &Module, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank_in: Rank, rank_out: Rank, ) -> Self where Module: VmpPMatAlloc, { let size: usize = k.0.div_ceil(base2k.0) as usize; debug_assert!( size as u32 > digits.0, "invalid gglwe: ceil(k/base2k): {size} <= digits: {}", digits.0 ); assert!( rows.0 * digits.0 <= size as u32, "invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}", rows.0, digits.0, ); Self { data: module.vmp_pmat_alloc(rows.into(), rank_in.into(), (rank_out + 1).into(), size), k, base2k, digits, } } pub fn alloc_bytes(module: &Module, infos: &A) -> usize where A: GGLWELayoutInfos, Module: VmpPMatAllocBytes, { debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()"); Self::alloc_bytes_with( module, infos.base2k(), infos.k(), infos.rows(), infos.digits(), infos.rank_in(), infos.rank_out(), ) } pub fn alloc_bytes_with( module: &Module, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank_in: Rank, rank_out: Rank, ) -> usize where Module: VmpPMatAllocBytes, { let size: usize = k.0.div_ceil(base2k.0) as usize; debug_assert!( size as u32 > digits.0, "invalid gglwe: ceil(k/base2k): {size} <= digits: {}", digits.0 ); assert!( rows.0 * digits.0 <= size as u32, "invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}", rows.0, digits.0, ); module.vmp_pmat_alloc_bytes(rows.into(), rank_in.into(), (rank_out + 1).into(), size) } } impl Prepare> for GGLWECiphertextPrepared where Module: VmpPrepare, { fn prepare(&mut self, module: &Module, other: &GGLWECiphertext, scratch: &mut Scratch) { module.vmp_prepare(&mut self.data, &other.data, scratch); self.k = other.k; self.base2k = other.base2k; self.digits = other.digits; } } impl PrepareAlloc, B>> for GGLWECiphertext where Module: VmpPMatAlloc + VmpPrepare, { fn prepare_alloc(&self, module: &Module, scratch: &mut Scratch) -> GGLWECiphertextPrepared, B> { let mut atk_prepared: GGLWECiphertextPrepared, B> = GGLWECiphertextPrepared::alloc(module, self); atk_prepared.prepare(module, self, scratch); atk_prepared } }