use num_traits::{ConstZero, FromPrimitive, PrimInt}; use crate::{backend::Modulus, decomposer::Decomposer}; #[derive(Clone, PartialEq)] pub(crate) struct BoolParameters { rlwe_q: CiphertextModulus, lwe_q: CiphertextModulus, br_q: usize, rlwe_n: PolynomialSize, lwe_n: LweDimension, lwe_decomposer_base: DecompostionLogBase, lwe_decomposer_count: DecompositionCount, rlrg_decomposer_base: DecompostionLogBase, /// RLWE x RGSW decomposition count for (part A, part B) rlrg_decomposer_count: (DecompositionCount, DecompositionCount), rgrg_decomposer_base: DecompostionLogBase, /// RGSW x RGSW decomposition count for (part A, part B) rgrg_decomposer_count: (DecompositionCount, DecompositionCount), auto_decomposer_base: DecompostionLogBase, auto_decomposer_count: DecompositionCount, g: usize, w: usize, } impl BoolParameters { pub(crate) fn rlwe_q(&self) -> &CiphertextModulus { &self.rlwe_q } pub(crate) fn lwe_q(&self) -> &CiphertextModulus { &self.lwe_q } pub(crate) fn br_q(&self) -> &usize { &self.br_q } pub(crate) fn rlwe_n(&self) -> &PolynomialSize { &self.rlwe_n } pub(crate) fn lwe_n(&self) -> &LweDimension { &self.lwe_n } pub(crate) fn g(&self) -> usize { self.g } pub(crate) fn w(&self) -> usize { self.w } pub(crate) fn rlwe_rgsw_decomposition_base(&self) -> DecompostionLogBase { self.rlrg_decomposer_base } pub(crate) fn rlwe_rgsw_decomposition_count(&self) -> (DecompositionCount, DecompositionCount) { self.rlrg_decomposer_count } pub(crate) fn rgsw_rgsw_decomposition_base(&self) -> DecompostionLogBase { self.rgrg_decomposer_base } pub(crate) fn rgsw_rgsw_decomposition_count(&self) -> (DecompositionCount, DecompositionCount) { self.rgrg_decomposer_count } pub(crate) fn auto_decomposition_base(&self) -> DecompostionLogBase { self.auto_decomposer_base } pub(crate) fn auto_decomposition_count(&self) -> DecompositionCount { self.auto_decomposer_count } pub(crate) fn lwe_decomposition_base(&self) -> DecompostionLogBase { self.lwe_decomposer_base } pub(crate) fn lwe_decomposition_count(&self) -> DecompositionCount { self.lwe_decomposer_count } pub(crate) fn rgsw_rgsw_decomposer>(&self) -> (D, D) where El: Copy, { ( // A D::new( self.rlwe_q.0, self.rgrg_decomposer_base.0, self.rgrg_decomposer_count.0 .0, ), // B D::new( self.rlwe_q.0, self.rgrg_decomposer_base.0, self.rgrg_decomposer_count.1 .0, ), ) } pub(crate) fn auto_decomposer>(&self) -> D where El: Copy, { D::new( self.rlwe_q.0, self.auto_decomposer_base.0, self.auto_decomposer_count.0, ) } pub(crate) fn lwe_decomposer>(&self) -> D where El: Copy, { D::new( self.lwe_q.0, self.lwe_decomposer_base.0, self.lwe_decomposer_count.0, ) } pub(crate) fn rlwe_rgsw_decomposer>(&self) -> (D, D) where El: Copy, { ( // A D::new( self.rlwe_q.0, self.rlrg_decomposer_base.0, self.rlrg_decomposer_count.0 .0, ), // B D::new( self.rlwe_q.0, self.rlrg_decomposer_base.0, self.rlrg_decomposer_count.1 .0, ), ) } /// Returns dlogs of `g` for which auto keys are required as /// per the parameter. Given that autos are required for [-g, g, g^2, ..., /// g^w] function returns the following [0, 1, 2, ..., w] where `w` is /// the window size. Note that although g^0 = 1, we use 0 for -g. pub(crate) fn auto_element_dlogs(&self) -> Vec { let mut els = vec![0]; (1..self.w + 1).into_iter().for_each(|e| { els.push(e); }); els } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct DecompostionLogBase(pub(crate) usize); impl AsRef for DecompostionLogBase { fn as_ref(&self) -> &usize { &self.0 } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct DecompositionCount(pub(crate) usize); impl AsRef for DecompositionCount { fn as_ref(&self) -> &usize { &self.0 } } #[derive(Clone, Copy, PartialEq)] pub(crate) struct LweDimension(pub(crate) usize); #[derive(Clone, Copy, PartialEq)] pub(crate) struct PolynomialSize(pub(crate) usize); #[derive(Clone, Copy, PartialEq, Debug)] /// T equals modulus when modulus is non-native. Otherwise T equals 0. bool is /// true when modulus is native, false otherwise. pub(crate) struct CiphertextModulus(T, bool); impl CiphertextModulus { const fn new_native() -> Self { // T::zero is stored only for convenience. It has no use when modulus // is native. That is, either u128,u64,u32,u16 Self(T::ZERO, true) } const fn new_non_native(q: T) -> Self { Self(q, false) } } impl CiphertextModulus where T: PrimInt, { pub(crate) fn _bits() -> usize { std::mem::size_of::() as usize * 8 } fn _native(&self) -> bool { self.1 } fn _half_q(&self) -> T { if self._native() { T::one() << (Self::_bits() - 1) } else { self.0 >> 1 } } fn _q(&self) -> Option { if self._native() { None } else { Some(self.0) } } } impl Modulus for CiphertextModulus where T: PrimInt + FromPrimitive, { type Element = T; fn is_native(&self) -> bool { self._native() } fn largest_unsigned_value(&self) -> Self::Element { if self._native() { T::max_value() } else { self.0 - T::one() } } fn neg_one(&self) -> Self::Element { if self._native() { T::max_value() } else { self.0 - T::one() } } // fn signed_max(&self) -> Self::Element {} // fn signed_min(&self) -> Self::Element {} fn smallest_unsigned_value(&self) -> Self::Element { T::zero() } fn map_element_to_i64(&self, v: &Self::Element) -> i64 { if *v > self._half_q() { -((self.largest_unsigned_value() - *v) + T::one()) .to_i64() .unwrap() } else { v.to_i64().unwrap() } } fn map_element_from_f64(&self, v: f64) -> Self::Element { let v = v.round(); if v < 0.0 { self.largest_unsigned_value() - T::from_f64(v.abs()).unwrap() + T::one() } else { T::from_f64(v.abs()).unwrap() } } fn map_element_from_i64(&self, v: i64) -> Self::Element { if v < 0 { self.largest_unsigned_value() - T::from_i64(v.abs()).unwrap() + T::one() } else { T::from_i64(v.abs()).unwrap() } } fn q(&self) -> Option { self._q() } fn q_as_f64(&self) -> Option { if self._native() { Some(T::max_value().to_f64().unwrap() + 1.0) } else { self.0.to_f64() } } } pub(crate) const SP_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(268369921u64), lwe_q: CiphertextModulus::new_non_native(1 << 16), br_q: 1 << 10, rlwe_n: PolynomialSize(1 << 10), lwe_n: LweDimension(500), lwe_decomposer_base: DecompostionLogBase(4), lwe_decomposer_count: DecompositionCount(4), rlrg_decomposer_base: DecompostionLogBase(7), rlrg_decomposer_count: (DecompositionCount(4), DecompositionCount(4)), rgrg_decomposer_base: DecompostionLogBase(7), rgrg_decomposer_count: (DecompositionCount(4), DecompositionCount(4)), auto_decomposer_base: DecompostionLogBase(7), auto_decomposer_count: DecompositionCount(4), g: 5, w: 5, }; pub(crate) const MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(1152921504606830593), lwe_q: CiphertextModulus::new_non_native(1 << 20), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(500), lwe_decomposer_base: DecompostionLogBase(4), lwe_decomposer_count: DecompositionCount(5), rlrg_decomposer_base: DecompostionLogBase(12), rlrg_decomposer_count: (DecompositionCount(5), DecompositionCount(5)), rgrg_decomposer_base: DecompostionLogBase(12), rgrg_decomposer_count: (DecompositionCount(5), DecompositionCount(5)), auto_decomposer_base: DecompostionLogBase(12), auto_decomposer_count: DecompositionCount(5), g: 5, w: 10, }; pub(crate) const SMALL_MP_BOOL_PARAMS: BoolParameters = BoolParameters:: { rlwe_q: CiphertextModulus::new_non_native(36028797018820609), lwe_q: CiphertextModulus::new_non_native(1 << 20), br_q: 1 << 11, rlwe_n: PolynomialSize(1 << 11), lwe_n: LweDimension(600), lwe_decomposer_base: DecompostionLogBase(4), lwe_decomposer_count: DecompositionCount(5), rlrg_decomposer_base: DecompostionLogBase(11), rlrg_decomposer_count: (DecompositionCount(2), DecompositionCount(1)), rgrg_decomposer_base: DecompostionLogBase(11), rgrg_decomposer_count: (DecompositionCount(5), DecompositionCount(4)), auto_decomposer_base: DecompostionLogBase(11), auto_decomposer_count: DecompositionCount(2), g: 5, w: 10, }; #[cfg(test)] mod tests { use crate::utils::generate_prime; #[test] fn find_prime() { let bits = 55; let ring_size = 1 << 15; let prime = generate_prime(bits, ring_size * 2, 1 << bits).unwrap(); dbg!(prime); } }