arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/src/rgsw/mod.rs
src/rgsw/mod.rsRaw
   1use itertools::{izip, Itertools};
   2use num_traits::{PrimInt, Signed, ToPrimitive, Zero};
   3use std::{
   4    clone,
   5    fmt::Debug,
   6    iter,
   7    marker::PhantomData,
   8    ops::{Div, Neg, Sub},
   9};
  10
  11use crate::{
  12    backend::{ArithmeticOps, GetModulus, Modulus, VectorOps},
  13    decomposer::{self, Decomposer, RlweDecomposer},
  14    ntt::{self, Ntt, NttInit},
  15    random::{
  16        DefaultSecureRng, NewWithSeed, RandomElementInModulus, RandomFill,
  17        RandomFillGaussianInModulus, RandomFillUniformInModulus,
  18    },
  19    utils::{fill_random_ternary_secret_with_hamming_weight, ToShoup, TryConvertFrom1, WithLocal},
  20    Matrix, MatrixEntity, MatrixMut, Row, RowEntity, RowMut, Secret,
  21};
  22
  23mod keygen;
  24mod runtime;
  25
  26pub(crate) use keygen::*;
  27pub(crate) use runtime::*;
  28
  29pub struct SeededAutoKey<M, S, Mod>
  30where
  31    M: Matrix,
  32{
  33    data: M,
  34    seed: S,
  35    modulus: Mod,
  36}
  37
  38impl<M: Matrix + MatrixEntity, S, Mod: Modulus<Element = M::MatElement>> SeededAutoKey<M, S, Mod> {
  39    fn empty<D: Decomposer>(ring_size: usize, auto_decomposer: &D, seed: S, modulus: Mod) -> Self {
  40        SeededAutoKey {
  41            data: M::zeros(auto_decomposer.decomposition_count(), ring_size),
  42            seed,
  43            modulus,
  44        }
  45    }
  46}
  47
  48pub struct AutoKeyEvaluationDomain<M: Matrix, Mod, R, N> {
  49    data: M,
  50    _phantom: PhantomData<(R, N)>,
  51    modulus: Mod,
  52}
  53
  54impl<
  55        M: MatrixMut + MatrixEntity,
  56        Mod: Modulus<Element = M::MatElement> + Clone,
  57        R: RandomFillUniformInModulus<[M::MatElement], Mod> + NewWithSeed,
  58        N: NttInit<Mod> + Ntt<Element = M::MatElement>,
  59    > From<&SeededAutoKey<M, R::Seed, Mod>> for AutoKeyEvaluationDomain<M, Mod, R, N>
  60where
  61    <M as Matrix>::R: RowMut,
  62    M::MatElement: Copy,
  63
  64    R::Seed: Clone,
  65{
  66    fn from(value: &SeededAutoKey<M, R::Seed, Mod>) -> Self {
  67        let (d, ring_size) = value.data.dimension();
  68        let mut data = M::zeros(2 * d, ring_size);
  69
  70        // sample RLWE'_A(-s(X^k))
  71        let mut p_rng = R::new_with_seed(value.seed.clone());
  72        data.iter_rows_mut().take(d).for_each(|r| {
  73            RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, r.as_mut());
  74        });
  75
  76        // copy over RLWE'_B(-s(X^k))
  77        izip!(data.iter_rows_mut().skip(d), value.data.iter_rows()).for_each(|(to_r, from_r)| {
  78            to_r.as_mut().copy_from_slice(from_r.as_ref());
  79        });
  80
  81        // send RLWE'(-s(X^k)) polynomials to evaluation domain
  82        let ntt_op = N::new(&value.modulus, ring_size);
  83        data.iter_rows_mut()
  84            .for_each(|r| ntt_op.forward(r.as_mut()));
  85
  86        AutoKeyEvaluationDomain {
  87            data,
  88            _phantom: PhantomData,
  89            modulus: value.modulus.clone(),
  90        }
  91    }
  92}
  93
  94pub struct ShoupAutoKeyEvaluationDomain<M> {
  95    data: M,
  96}
  97
  98impl<M: Matrix + ToShoup<Modulus = M::MatElement>, Mod: Modulus<Element = M::MatElement>, R, N>
  99    From<&AutoKeyEvaluationDomain<M, Mod, R, N>> for ShoupAutoKeyEvaluationDomain<M>
 100{
 101    fn from(value: &AutoKeyEvaluationDomain<M, Mod, R, N>) -> Self {
 102        Self {
 103            data: M::to_shoup(&value.data, value.modulus.q().unwrap()),
 104        }
 105    }
 106}
 107
 108pub struct RgswCiphertext<M: Matrix, Mod> {
 109    /// Rgsw ciphertext polynomials
 110    pub(crate) data: M,
 111    modulus: Mod,
 112    /// Decomposition for RLWE part A
 113    d_a: usize,
 114    /// Decomposition for RLWE part B
 115    d_b: usize,
 116}
 117
 118impl<M: MatrixEntity, Mod: Modulus<Element = M::MatElement>> RgswCiphertext<M, Mod> {
 119    pub(crate) fn empty<D: RlweDecomposer>(
 120        ring_size: usize,
 121        decomposer: &D,
 122        modulus: Mod,
 123    ) -> RgswCiphertext<M, Mod> {
 124        RgswCiphertext {
 125            data: M::zeros(
 126                decomposer.a().decomposition_count() * 2 + decomposer.b().decomposition_count() * 2,
 127                ring_size,
 128            ),
 129            d_a: decomposer.a().decomposition_count(),
 130            d_b: decomposer.b().decomposition_count(),
 131            modulus,
 132        }
 133    }
 134}
 135
 136pub struct SeededRgswCiphertext<M, S, Mod>
 137where
 138    M: Matrix,
 139{
 140    pub(crate) data: M,
 141    seed: S,
 142    modulus: Mod,
 143    /// Decomposition for RLWE part A
 144    d_a: usize,
 145    /// Decomposition for RLWE part B
 146    d_b: usize,
 147}
 148
 149impl<M: Matrix + MatrixEntity, S, Mod> SeededRgswCiphertext<M, S, Mod> {
 150    pub(crate) fn empty<D: RlweDecomposer>(
 151        ring_size: usize,
 152        decomposer: &D,
 153        seed: S,
 154        modulus: Mod,
 155    ) -> SeededRgswCiphertext<M, S, Mod> {
 156        SeededRgswCiphertext {
 157            data: M::zeros(
 158                decomposer.a().decomposition_count() * 2 + decomposer.b().decomposition_count(),
 159                ring_size,
 160            ),
 161            seed,
 162            modulus,
 163            d_a: decomposer.a().decomposition_count(),
 164            d_b: decomposer.b().decomposition_count(),
 165        }
 166    }
 167}
 168
 169impl<M: Debug + Matrix, S: Debug, Mod: Debug> Debug for SeededRgswCiphertext<M, S, Mod>
 170where
 171    M::MatElement: Debug,
 172{
 173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 174        f.debug_struct("SeededRgswCiphertext")
 175            .field("data", &self.data)
 176            .field("seed", &self.seed)
 177            .field("modulus", &self.modulus)
 178            .finish()
 179    }
 180}
 181
 182pub struct RgswCiphertextEvaluationDomain<M, Mod, R, N> {
 183    pub(crate) data: M,
 184    modulus: Mod,
 185    _phantom: PhantomData<(R, N)>,
 186}
 187
 188impl<
 189        M: MatrixMut + MatrixEntity,
 190        Mod: Modulus<Element = M::MatElement> + Clone,
 191        R: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], Mod>,
 192        N: NttInit<Mod> + Ntt<Element = M::MatElement> + Debug,
 193    > From<&SeededRgswCiphertext<M, R::Seed, Mod>> for RgswCiphertextEvaluationDomain<M, Mod, R, N>
 194where
 195    <M as Matrix>::R: RowMut,
 196    M::MatElement: Copy,
 197    R::Seed: Clone,
 198    M: Debug,
 199{
 200    fn from(value: &SeededRgswCiphertext<M, R::Seed, Mod>) -> Self {
 201        let mut data = M::zeros(value.d_a * 2 + value.d_b * 2, value.data.dimension().1);
 202
 203        // copy RLWE'(-sm)
 204        izip!(
 205            data.iter_rows_mut().take(value.d_a * 2),
 206            value.data.iter_rows().take(value.d_a * 2)
 207        )
 208        .for_each(|(to_ri, from_ri)| {
 209            to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 210        });
 211
 212        // sample A polynomials of RLWE'(m) - RLWE'A(m)
 213        // TODO(Jay): Do we want to be generic over RandomGenerator used here? I think
 214        // not.
 215        let mut p_rng = R::new_with_seed(value.seed.clone());
 216        izip!(data.iter_rows_mut().skip(value.d_a * 2).take(value.d_b * 1))
 217            .for_each(|ri| p_rng.random_fill(&value.modulus, ri.as_mut()));
 218
 219        // RLWE'_B(m)
 220        izip!(
 221            data.iter_rows_mut().skip(value.d_a * 2 + value.d_b),
 222            value.data.iter_rows().skip(value.d_a * 2)
 223        )
 224        .for_each(|(to_ri, from_ri)| {
 225            to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 226        });
 227
 228        // Send polynomials to evaluation domain
 229        let ring_size = data.dimension().1;
 230        let nttop = N::new(&value.modulus, ring_size);
 231        data.iter_rows_mut()
 232            .for_each(|ri| nttop.forward(ri.as_mut()));
 233
 234        Self {
 235            data: data,
 236            modulus: value.modulus.clone(),
 237            _phantom: PhantomData,
 238        }
 239    }
 240}
 241
 242impl<
 243        M: MatrixMut + MatrixEntity,
 244        Mod: Modulus<Element = M::MatElement> + Clone,
 245        R,
 246        N: NttInit<Mod> + Ntt<Element = M::MatElement>,
 247    > From<&RgswCiphertext<M, Mod>> for RgswCiphertextEvaluationDomain<M, Mod, R, N>
 248where
 249    <M as Matrix>::R: RowMut,
 250    M::MatElement: Copy,
 251    M: Debug,
 252{
 253    fn from(value: &RgswCiphertext<M, Mod>) -> Self {
 254        let mut data = M::zeros(value.d_a * 2 + value.d_b * 2, value.data.dimension().1);
 255
 256        // copy RLWE'(-sm)
 257        izip!(
 258            data.iter_rows_mut().take(value.d_a * 2),
 259            value.data.iter_rows().take(value.d_a * 2)
 260        )
 261        .for_each(|(to_ri, from_ri)| {
 262            to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 263        });
 264
 265        // copy RLWE'(m)
 266        izip!(
 267            data.iter_rows_mut().skip(value.d_a * 2),
 268            value.data.iter_rows().skip(value.d_a * 2)
 269        )
 270        .for_each(|(to_ri, from_ri)| {
 271            to_ri.as_mut().copy_from_slice(from_ri.as_ref());
 272        });
 273
 274        // Send polynomials to evaluation domain
 275        let ring_size = data.dimension().1;
 276        let nttop = N::new(&value.modulus, ring_size);
 277        data.iter_rows_mut()
 278            .for_each(|ri| nttop.forward(ri.as_mut()));
 279
 280        Self {
 281            data: data,
 282            modulus: value.modulus.clone(),
 283            _phantom: PhantomData,
 284        }
 285    }
 286}
 287
 288impl<M: Debug, Mod: Debug, R, N> Debug for RgswCiphertextEvaluationDomain<M, Mod, R, N> {
 289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 290        f.debug_struct("RgswCiphertextEvaluationDomain")
 291            .field("data", &self.data)
 292            .field("modulus", &self.modulus)
 293            .field("_phantom", &self._phantom)
 294            .finish()
 295    }
 296}
 297
 298impl<M: Matrix, Mod, R, N> Matrix for RgswCiphertextEvaluationDomain<M, Mod, R, N> {
 299    type MatElement = M::MatElement;
 300    type R = M::R;
 301
 302    fn dimension(&self) -> (usize, usize) {
 303        self.data.dimension()
 304    }
 305
 306    fn fits(&self, row: usize, col: usize) -> bool {
 307        self.data.fits(row, col)
 308    }
 309}
 310
 311impl<M: Matrix, Mod, R, N> AsRef<[M::R]> for RgswCiphertextEvaluationDomain<M, Mod, R, N> {
 312    fn as_ref(&self) -> &[M::R] {
 313        self.data.as_ref()
 314    }
 315}
 316
 317pub struct ShoupRgswCiphertextEvaluationDomain<M> {
 318    pub(crate) data: M,
 319}
 320
 321impl<M: Matrix + ToShoup<Modulus = M::MatElement>, Mod: Modulus<Element = M::MatElement>, R, N>
 322    From<&RgswCiphertextEvaluationDomain<M, Mod, R, N>> for ShoupRgswCiphertextEvaluationDomain<M>
 323{
 324    fn from(value: &RgswCiphertextEvaluationDomain<M, Mod, R, N>) -> Self {
 325        Self {
 326            data: M::to_shoup(&value.data, value.modulus.q().unwrap()),
 327        }
 328    }
 329}
 330
 331pub struct SeededRlweCiphertext<R, S, Mod> {
 332    pub(crate) data: R,
 333    pub(crate) seed: S,
 334    pub(crate) modulus: Mod,
 335}
 336
 337impl<R: RowEntity, S, Mod> SeededRlweCiphertext<R, S, Mod> {
 338    pub(crate) fn empty(ring_size: usize, seed: S, modulus: Mod) -> Self {
 339        SeededRlweCiphertext {
 340            data: R::zeros(ring_size),
 341            seed,
 342            modulus,
 343        }
 344    }
 345}
 346
 347pub struct RlweCiphertext<M, Rng> {
 348    pub(crate) data: M,
 349    pub(crate) is_trivial: bool,
 350    pub(crate) _phatom: PhantomData<Rng>,
 351}
 352
 353impl<M, Rng> RlweCiphertext<M, Rng> {
 354    pub(crate) fn new_trivial(data: M) -> Self {
 355        RlweCiphertext {
 356            data,
 357            is_trivial: true,
 358            _phatom: PhantomData,
 359        }
 360    }
 361}
 362
 363impl<M: Matrix, Rng> Matrix for RlweCiphertext<M, Rng> {
 364    type MatElement = M::MatElement;
 365    type R = M::R;
 366
 367    fn dimension(&self) -> (usize, usize) {
 368        self.data.dimension()
 369    }
 370
 371    fn fits(&self, row: usize, col: usize) -> bool {
 372        self.data.fits(row, col)
 373    }
 374}
 375
 376impl<M: MatrixMut, Rng> MatrixMut for RlweCiphertext<M, Rng> where <M as Matrix>::R: RowMut {}
 377
 378impl<M: Matrix, Rng> AsRef<[<M as Matrix>::R]> for RlweCiphertext<M, Rng> {
 379    fn as_ref(&self) -> &[<M as Matrix>::R] {
 380        self.data.as_ref()
 381    }
 382}
 383
 384impl<M: MatrixMut, Rng> AsMut<[<M as Matrix>::R]> for RlweCiphertext<M, Rng>
 385where
 386    <M as Matrix>::R: RowMut,
 387{
 388    fn as_mut(&mut self) -> &mut [<M as Matrix>::R] {
 389        self.data.as_mut()
 390    }
 391}
 392
 393impl<M, Rng> IsTrivial for RlweCiphertext<M, Rng> {
 394    fn is_trivial(&self) -> bool {
 395        self.is_trivial
 396    }
 397    fn set_not_trivial(&mut self) {
 398        self.is_trivial = false;
 399    }
 400}
 401
 402impl<
 403        R: Row,
 404        M: MatrixEntity<R = R, MatElement = R::Element> + MatrixMut,
 405        Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], Mod>,
 406        Mod: Modulus<Element = R::Element>,
 407    > From<&SeededRlweCiphertext<R, Rng::Seed, Mod>> for RlweCiphertext<M, Rng>
 408where
 409    Rng::Seed: Clone,
 410    <M as Matrix>::R: RowMut,
 411    R::Element: Copy,
 412{
 413    fn from(value: &SeededRlweCiphertext<R, Rng::Seed, Mod>) -> Self {
 414        let mut data = M::zeros(2, value.data.as_ref().len());
 415
 416        // sample a
 417        let mut p_rng = Rng::new_with_seed(value.seed.clone());
 418        RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0));
 419
 420        data.get_row_mut(1).copy_from_slice(value.data.as_ref());
 421
 422        RlweCiphertext {
 423            data,
 424            is_trivial: false,
 425            _phatom: PhantomData,
 426        }
 427    }
 428}
 429
 430pub trait IsTrivial {
 431    fn is_trivial(&self) -> bool;
 432    fn set_not_trivial(&mut self);
 433}
 434
 435pub struct SeededRlwePublicKey<Ro: Row, S> {
 436    data: Ro,
 437    seed: S,
 438    modulus: Ro::Element,
 439}
 440
 441impl<Ro: RowEntity, S> SeededRlwePublicKey<Ro, S> {
 442    pub(crate) fn empty(ring_size: usize, seed: S, modulus: Ro::Element) -> Self {
 443        Self {
 444            data: Ro::zeros(ring_size),
 445            seed,
 446            modulus,
 447        }
 448    }
 449}
 450
 451pub struct RlwePublicKey<M, R> {
 452    data: M,
 453    _phantom: PhantomData<R>,
 454}
 455
 456impl<
 457        M: MatrixMut + MatrixEntity,
 458        Rng: NewWithSeed + RandomFillUniformInModulus<[M::MatElement], M::MatElement>,
 459    > From<&SeededRlwePublicKey<M::R, Rng::Seed>> for RlwePublicKey<M, Rng>
 460where
 461    <M as Matrix>::R: RowMut,
 462    M::MatElement: Copy,
 463    Rng::Seed: Copy,
 464{
 465    fn from(value: &SeededRlwePublicKey<M::R, Rng::Seed>) -> Self {
 466        let mut data = M::zeros(2, value.data.as_ref().len());
 467
 468        // sample a
 469        let mut p_rng = Rng::new_with_seed(value.seed);
 470        RandomFillUniformInModulus::random_fill(&mut p_rng, &value.modulus, data.get_row_mut(0));
 471
 472        // copy over b
 473        data.get_row_mut(1).copy_from_slice(value.data.as_ref());
 474
 475        Self {
 476            data,
 477            _phantom: PhantomData,
 478        }
 479    }
 480}
 481
 482#[derive(Clone)]
 483pub struct RlweSecret {
 484    pub(crate) values: Vec<i32>,
 485}
 486
 487impl Secret for RlweSecret {
 488    type Element = i32;
 489    fn values(&self) -> &[Self::Element] {
 490        &self.values
 491    }
 492}
 493
 494impl RlweSecret {
 495    pub fn random(hw: usize, n: usize) -> RlweSecret {
 496        DefaultSecureRng::with_local_mut(|rng| {
 497            let mut out = vec![0i32; n];
 498            fill_random_ternary_secret_with_hamming_weight(&mut out, hw, rng);
 499
 500            RlweSecret { values: out }
 501        })
 502    }
 503}
 504
 505#[cfg(test)]
 506pub(crate) mod tests {
 507    use std::{clone, marker::PhantomData, ops::Mul, vec};
 508
 509    use itertools::{izip, Itertools};
 510    use rand::{thread_rng, Rng};
 511
 512    use crate::{
 513        backend::{GetModulus, ModInit, ModularOpsU64, Modulus, VectorOps},
 514        decomposer::{Decomposer, DefaultDecomposer, RlweDecomposer},
 515        ntt::{Ntt, NttBackendU64, NttInit},
 516        random::{DefaultSecureRng, RandomFillGaussianInModulus, RandomFillUniformInModulus},
 517        rgsw::{
 518            galois_auto_shoup, rlwe_by_rgsw_shoup, ShoupAutoKeyEvaluationDomain,
 519            ShoupRgswCiphertextEvaluationDomain,
 520        },
 521        utils::{generate_prime, negacyclic_mul, tests::Stats, TryConvertFrom1},
 522        Matrix, MatrixMut, Secret,
 523    };
 524
 525    use super::{
 526        keygen::{
 527            decrypt_rlwe, galois_key_gen, gen_rlwe_public_key, generate_auto_map, measure_noise,
 528            public_key_encrypt_rgsw, secret_key_encrypt_rgsw, secret_key_encrypt_rlwe,
 529        },
 530        runtime::{galois_auto, rgsw_by_rgsw_inplace, rlwe_by_rgsw},
 531        AutoKeyEvaluationDomain, RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext,
 532        RlwePublicKey, RlweSecret, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext,
 533        SeededRlwePublicKey,
 534    };
 535
 536    pub(crate) fn _sk_encrypt_rlwe<T: Modulus<Element = u64> + Clone>(
 537        m: &[u64],
 538        s: &[i32],
 539        ntt_op: &NttBackendU64,
 540        mod_op: &ModularOpsU64<T>,
 541    ) -> RlweCiphertext<Vec<Vec<u64>>, DefaultSecureRng> {
 542        let ring_size = m.len();
 543        let q = mod_op.modulus();
 544        assert!(s.len() == ring_size);
 545
 546        let mut rng = DefaultSecureRng::new();
 547        let mut rlwe_seed = [0u8; 32];
 548        rng.fill_bytes(&mut rlwe_seed);
 549        let mut seeded_rlwe_ct =
 550            SeededRlweCiphertext::<_, [u8; 32], _>::empty(ring_size as usize, rlwe_seed, q.clone());
 551        let mut p_rng = DefaultSecureRng::new_seeded(rlwe_seed);
 552        secret_key_encrypt_rlwe(
 553            &m,
 554            &mut seeded_rlwe_ct.data,
 555            s,
 556            mod_op,
 557            ntt_op,
 558            &mut p_rng,
 559            &mut rng,
 560        );
 561
 562        RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_ct)
 563    }
 564
 565    // Encrypt m as RGSW ciphertext RGSW(m) using supplied public key
 566    pub(crate) fn _pk_encrypt_rgsw<T: Modulus<Element = u64> + Clone>(
 567        m: &[u64],
 568        public_key: &RlwePublicKey<Vec<Vec<u64>>, DefaultSecureRng>,
 569        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 570        mod_op: &ModularOpsU64<T>,
 571        ntt_op: &NttBackendU64,
 572    ) -> RgswCiphertext<Vec<Vec<u64>>, T> {
 573        let (_, ring_size) = Matrix::dimension(&public_key.data);
 574        let gadget_vector_a = decomposer.a().gadget_vector();
 575        let gadget_vector_b = decomposer.b().gadget_vector();
 576
 577        let mut rng = DefaultSecureRng::new();
 578
 579        assert!(m.len() == ring_size);
 580
 581        // public key encrypt RGSW(m1)
 582        let mut rgsw_ct = RgswCiphertext::empty(ring_size, decomposer, mod_op.modulus().clone());
 583        public_key_encrypt_rgsw(
 584            &mut rgsw_ct.data,
 585            m,
 586            &public_key.data,
 587            &gadget_vector_a,
 588            &gadget_vector_b,
 589            mod_op,
 590            ntt_op,
 591            &mut rng,
 592        );
 593
 594        rgsw_ct
 595    }
 596
 597    /// Encrypts m as RGSW ciphertext RGSW(m) using supplied secret key. Returns
 598    /// unseeded RGSW ciphertext in coefficient domain
 599    pub(crate) fn _sk_encrypt_rgsw<T: Modulus<Element = u64> + Clone>(
 600        m: &[u64],
 601        s: &[i32],
 602        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 603        mod_op: &ModularOpsU64<T>,
 604        ntt_op: &NttBackendU64,
 605    ) -> SeededRgswCiphertext<Vec<Vec<u64>>, [u8; 32], T> {
 606        let ring_size = s.len();
 607        assert!(m.len() == s.len());
 608
 609        let q = mod_op.modulus();
 610
 611        let gadget_vector_a = decomposer.a().gadget_vector();
 612        let gadget_vector_b = decomposer.b().gadget_vector();
 613
 614        let mut rng = DefaultSecureRng::new();
 615        let mut rgsw_seed = [0u8; 32];
 616        rng.fill_bytes(&mut rgsw_seed);
 617        let mut seeded_rgsw_ct = SeededRgswCiphertext::<Vec<Vec<u64>>, [u8; 32], T>::empty(
 618            ring_size as usize,
 619            decomposer,
 620            rgsw_seed,
 621            q.clone(),
 622        );
 623        let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed);
 624        secret_key_encrypt_rgsw(
 625            &mut seeded_rgsw_ct.data,
 626            m,
 627            &gadget_vector_a,
 628            &gadget_vector_b,
 629            s,
 630            mod_op,
 631            ntt_op,
 632            &mut p_rng,
 633            &mut rng,
 634        );
 635        seeded_rgsw_ct
 636    }
 637
 638    /// Prints noise in RGSW ciphertext RGSW(m).
 639    ///
 640    /// - rgsw_ct: RGSW ciphertext in coefficient domain
 641    pub(crate) fn _measure_noise_rgsw<T: Modulus<Element = u64> + Clone>(
 642        rgsw_ct: &[Vec<u64>],
 643        m: &[u64],
 644        s: &[i32],
 645        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 646        q: &T,
 647    ) {
 648        let gadget_vector_a = decomposer.a().gadget_vector();
 649        let gadget_vector_b = decomposer.b().gadget_vector();
 650        let d_a = gadget_vector_a.len();
 651        let d_b = gadget_vector_b.len();
 652        let ring_size = s.len();
 653        assert!(Matrix::dimension(&rgsw_ct) == (d_a * 2 + d_b * 2, ring_size));
 654        assert!(m.len() == ring_size);
 655
 656        let mod_op = ModularOpsU64::new(q.clone());
 657        let ntt_op = NttBackendU64::new(q, ring_size);
 658
 659        let mul_mod =
 660            |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q.q().unwrap() as u128) as u64;
 661        let s_poly = Vec::<u64>::try_convert_from(s, q);
 662        let mut neg_s = s_poly.clone();
 663        mod_op.elwise_neg_mut(neg_s.as_mut());
 664        let neg_sm0m1 = negacyclic_mul(&neg_s, &m, mul_mod, q.q().unwrap());
 665
 666        // RLWE(\beta^j -s * m)
 667        for j in 0..d_a {
 668            let ideal_m = {
 669                // RLWE(\beta^j -s * m)
 670                let mut beta_neg_sm0m1 = vec![0u64; ring_size as usize];
 671                mod_op.elwise_scalar_mul(beta_neg_sm0m1.as_mut(), &neg_sm0m1, &gadget_vector_a[j]);
 672                beta_neg_sm0m1
 673            };
 674
 675            let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
 676            rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(j));
 677            rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a + j));
 678            let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
 679
 680            println!(r"Noise RLWE(\beta^{j} -sm0m1): {noise}");
 681        }
 682
 683        // RLWE(\beta^j  m)
 684        for j in 0..d_b {
 685            let ideal_m = {
 686                // RLWE(\beta^j  m)
 687                let mut beta_m0m1 = vec![0u64; ring_size as usize];
 688                mod_op.elwise_scalar_mul(beta_m0m1.as_mut(), &m, &gadget_vector_b[j]);
 689                beta_m0m1
 690            };
 691
 692            let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
 693            rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + j));
 694            rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + d_b + j));
 695            let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
 696
 697            println!(r"Noise RLWE(\beta^{j} m0m1): {noise}");
 698        }
 699    }
 700
 701    #[test]
 702    fn rlwe_encrypt_decryption() {
 703        let logq = 50;
 704        let logp = 2;
 705        let ring_size = 1 << 4;
 706        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
 707        let p = 1u64 << logp;
 708
 709        let mut rng = DefaultSecureRng::new();
 710
 711        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 712
 713        // sample m0
 714        let mut m0 = vec![0u64; ring_size as usize];
 715        RandomFillUniformInModulus::<[u64], u64>::random_fill(
 716            &mut rng,
 717            &(1u64 << logp),
 718            m0.as_mut_slice(),
 719        );
 720
 721        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 722        let mod_op = ModularOpsU64::new(q);
 723
 724        // encrypt m0
 725        let encoded_m = m0
 726            .iter()
 727            .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 728            .collect_vec();
 729        let rlwe_in_ct = _sk_encrypt_rlwe(&encoded_m, s.values(), &ntt_op, &mod_op);
 730
 731        let mut encoded_m_back = vec![0u64; ring_size as usize];
 732        decrypt_rlwe(
 733            &rlwe_in_ct,
 734            s.values(),
 735            &mut encoded_m_back,
 736            &ntt_op,
 737            &mod_op,
 738        );
 739        let m_back = encoded_m_back
 740            .iter()
 741            .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
 742            .collect_vec();
 743        assert_eq!(m0, m_back);
 744
 745        // let noise = measure_noise(&rlwe_in_ct, &encoded_m, &ntt_op, &mod_op,
 746        // s.values()); println!("Noise: {noise}");
 747    }
 748
 749    #[test]
 750    fn rlwe_by_rgsw_works() {
 751        let logq = 50;
 752        let logp = 2;
 753        let ring_size = 1 << 9;
 754        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
 755        let p: u64 = 1u64 << logp;
 756
 757        let mut rng = DefaultSecureRng::new_seeded([0u8; 32]);
 758
 759        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 760
 761        let mut m0 = vec![0u64; ring_size as usize];
 762        RandomFillUniformInModulus::<[u64], _>::random_fill(
 763            &mut rng,
 764            &(1u64 << logp),
 765            m0.as_mut_slice(),
 766        );
 767        let mut m1 = vec![0u64; ring_size as usize];
 768        m1[thread_rng().gen_range(0..ring_size) as usize] = 1;
 769
 770        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 771        let mod_op = ModularOpsU64::new(q);
 772        let d_rgsw = 10;
 773        let logb = 5;
 774        let decomposer = (
 775            DefaultDecomposer::new(q, logb, d_rgsw),
 776            DefaultDecomposer::new(q, logb, d_rgsw),
 777        );
 778
 779        // create public key
 780        let mut pk_seed = [0u8; 32];
 781        rng.fill_bytes(&mut pk_seed);
 782        let mut pk_prng = DefaultSecureRng::new_seeded(pk_seed);
 783        let mut seeded_pk =
 784            SeededRlwePublicKey::<Vec<u64>, _>::empty(ring_size as usize, pk_seed, q);
 785        gen_rlwe_public_key(
 786            &mut seeded_pk.data,
 787            s.values(),
 788            &ntt_op,
 789            &mod_op,
 790            &mut pk_prng,
 791            &mut rng,
 792        );
 793        let pk = RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk);
 794
 795        // Encrypt m1 as RGSW(m1)
 796        let rgsw_ct = {
 797            //TODO(Jay): Figure out better way to test secret key and public key variant of
 798            // RGSW ciphertext encryption within the same test
 799
 800            if true {
 801                // Encryption m1 as RGSW(m1) using secret key
 802                let seeded_rgsw_ct =
 803                    _sk_encrypt_rgsw(&m1, s.values(), &decomposer, &mod_op, &ntt_op);
 804                RgswCiphertextEvaluationDomain::<Vec<Vec<u64>>, _,DefaultSecureRng, NttBackendU64>::from(&seeded_rgsw_ct)
 805            } else {
 806                // Encrypt m1 as RGSW(m1) using public key
 807                let rgsw_ct = _pk_encrypt_rgsw(&m1, &pk, &decomposer, &mod_op, &ntt_op);
 808                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
 809                    &rgsw_ct,
 810                )
 811            }
 812        };
 813
 814        // Encrypt m0 as RLWE(m0)
 815        let mut rlwe_in_ct = {
 816            let encoded_m = m0
 817                .iter()
 818                .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 819                .collect_vec();
 820
 821            _sk_encrypt_rlwe(&encoded_m, s.values(), &ntt_op, &mod_op)
 822        };
 823
 824        // RLWE(m0m1) = RLWE(m0) x RGSW(m1)
 825        let mut scratch_space = vec![
 826            vec![0u64; ring_size as usize];
 827            std::cmp::max(
 828                decomposer.a().decomposition_count(),
 829                decomposer.b().decomposition_count()
 830            ) + 2
 831        ];
 832
 833        // rlwe x rgsw with additional RGSW ciphertexts in shoup repr
 834        let rlwe_in_ct_shoup = {
 835            let mut rlwe_in_ct_shoup = RlweCiphertext::<_, DefaultSecureRng> {
 836                data: rlwe_in_ct.data.clone(),
 837                is_trivial: rlwe_in_ct.is_trivial,
 838                _phatom: PhantomData::default(),
 839            };
 840
 841            let rgsw_ct_shoup = ShoupRgswCiphertextEvaluationDomain::from(&rgsw_ct);
 842
 843            rlwe_by_rgsw_shoup(
 844                &mut rlwe_in_ct_shoup,
 845                &rgsw_ct.data,
 846                &rgsw_ct_shoup.data,
 847                &mut scratch_space,
 848                &decomposer,
 849                &ntt_op,
 850                &mod_op,
 851            );
 852
 853            rlwe_in_ct_shoup
 854        };
 855
 856        // rlwe x rgsw normal
 857        {
 858            rlwe_by_rgsw(
 859                &mut rlwe_in_ct,
 860                &rgsw_ct.data,
 861                &mut scratch_space,
 862                &decomposer,
 863                &ntt_op,
 864                &mod_op,
 865            );
 866        }
 867
 868        // output from both functions must be equal
 869        {
 870            assert_eq!(rlwe_in_ct.data, rlwe_in_ct_shoup.data);
 871        }
 872
 873        // Decrypt RLWE(m0m1)
 874        let mut encoded_m0m1_back = vec![0u64; ring_size as usize];
 875        decrypt_rlwe(
 876            &rlwe_in_ct,
 877            s.values(),
 878            &mut encoded_m0m1_back,
 879            &ntt_op,
 880            &mod_op,
 881        );
 882        let m0m1_back = encoded_m0m1_back
 883            .iter()
 884            .map(|v| (((*v as f64 * p as f64) / (q as f64)).round() as u64) % p)
 885            .collect_vec();
 886
 887        let mul_mod = |v0: &u64, v1: &u64| (v0 * v1) % p;
 888        let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, p);
 889
 890        // {
 891        //     // measure noise
 892        //     let encoded_m_ideal = m0m1
 893        //         .iter()
 894        //         .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 895        //         .collect_vec();
 896
 897        //     let noise = measure_noise(&rlwe_in_ct, &encoded_m_ideal, &ntt_op,
 898        // &mod_op, s.values());     println!("Noise RLWE(m0m1)(=
 899        // RLWE(m0)xRGSW(m1)) : {noise}"); }
 900
 901        assert!(
 902            m0m1 == m0m1_back,
 903            "Expected {:?} \n Got {:?}",
 904            m0m1,
 905            m0m1_back
 906        );
 907    }
 908
 909    #[test]
 910    fn galois_auto_works() {
 911        let logq = 55;
 912        let ring_size = 1 << 11;
 913        let q = generate_prime(logq, 2 * ring_size, 1u64 << logq).unwrap();
 914        let logp = 3;
 915        let p = 1u64 << logp;
 916        let d_rgsw = 5;
 917        let logb = 11;
 918
 919        let mut rng = DefaultSecureRng::new();
 920        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 921
 922        let mut m = vec![0u64; ring_size as usize];
 923        RandomFillUniformInModulus::random_fill(&mut rng, &p, m.as_mut_slice());
 924        let encoded_m = m
 925            .iter()
 926            .map(|v| (((*v as f64 * q as f64) / (p as f64)).round() as u64))
 927            .collect_vec();
 928
 929        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 930        let mod_op = ModularOpsU64::new(q);
 931
 932        // RLWE_{s}(m)
 933        let mut seed_rlwe = [0u8; 32];
 934        rng.fill_bytes(&mut seed_rlwe);
 935        let mut seeded_rlwe_m = SeededRlweCiphertext::empty(ring_size as usize, seed_rlwe, q);
 936        let mut p_rng = DefaultSecureRng::new_seeded(seed_rlwe);
 937        secret_key_encrypt_rlwe(
 938            &encoded_m,
 939            &mut seeded_rlwe_m.data,
 940            s.values(),
 941            &mod_op,
 942            &ntt_op,
 943            &mut p_rng,
 944            &mut rng,
 945        );
 946        let mut rlwe_m = RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_m);
 947
 948        let auto_k = -125;
 949
 950        // Generate galois key to key switch from s^k to s
 951        let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
 952        let mut seed_auto = [0u8; 32];
 953        rng.fill_bytes(&mut seed_auto);
 954        let mut seeded_auto_key =
 955            SeededAutoKey::empty(ring_size as usize, &decomposer, seed_auto, q);
 956        let mut p_rng = DefaultSecureRng::new_seeded(seed_auto);
 957        let gadget_vector = decomposer.gadget_vector();
 958        galois_key_gen(
 959            &mut seeded_auto_key.data,
 960            s.values(),
 961            auto_k,
 962            &gadget_vector,
 963            &mod_op,
 964            &ntt_op,
 965            &mut p_rng,
 966            &mut rng,
 967        );
 968        let auto_key =
 969            AutoKeyEvaluationDomain::<Vec<Vec<u64>>, _, DefaultSecureRng, NttBackendU64>::from(
 970                &seeded_auto_key,
 971            );
 972
 973        // Send RLWE_{s}(m) -> RLWE_{s}(m^k)
 974        let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw + 2];
 975        let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size as usize, auto_k);
 976
 977        // galois auto with additional auto key in shoup repr
 978        let rlwe_m_shoup = {
 979            let auto_key_shoup = ShoupAutoKeyEvaluationDomain::from(&auto_key);
 980            let mut rlwe_m_shoup = RlweCiphertext::<_, DefaultSecureRng> {
 981                data: rlwe_m.data.clone(),
 982                is_trivial: rlwe_m.is_trivial,
 983                _phatom: PhantomData::default(),
 984            };
 985            galois_auto_shoup(
 986                &mut rlwe_m_shoup,
 987                &auto_key.data,
 988                &auto_key_shoup.data,
 989                &mut scratch_space,
 990                &auto_map_index,
 991                &auto_map_sign,
 992                &mod_op,
 993                &ntt_op,
 994                &decomposer,
 995            );
 996            rlwe_m_shoup
 997        };
 998
 999        // normal galois auto
1000        {
1001            galois_auto(
1002                &mut rlwe_m,
1003                &auto_key.data,
1004                &mut scratch_space,
1005                &auto_map_index,
1006                &auto_map_sign,
1007                &mod_op,
1008                &ntt_op,
1009                &decomposer,
1010            );
1011        }
1012
1013        // rlwe out from both functions must be same
1014        assert_eq!(rlwe_m.data, rlwe_m_shoup.data);
1015
1016        let rlwe_m_k = rlwe_m;
1017
1018        // Decrypt RLWE_{s}(m^k) and check
1019        let mut encoded_m_k_back = vec![0u64; ring_size as usize];
1020        decrypt_rlwe(
1021            &rlwe_m_k,
1022            s.values(),
1023            &mut encoded_m_k_back,
1024            &ntt_op,
1025            &mod_op,
1026        );
1027        let m_k_back = encoded_m_k_back
1028            .iter()
1029            .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
1030            .collect_vec();
1031
1032        let mut m_k = vec![0u64; ring_size as usize];
1033        // Send \delta m -> \delta m^k
1034        izip!(m.iter(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
1035            |(v, to_index, sign)| {
1036                if !*sign {
1037                    m_k[*to_index] = (p - *v) % p;
1038                } else {
1039                    m_k[*to_index] = *v;
1040                }
1041            },
1042        );
1043
1044        {
1045            let encoded_m_k = m_k
1046                .iter()
1047                .map(|v| ((*v as f64 * q as f64) / p as f64).round() as u64)
1048                .collect_vec();
1049
1050            let noise = measure_noise(&rlwe_m_k, &encoded_m_k, &ntt_op, &mod_op, s.values());
1051            println!("Ksk noise: {noise}");
1052        }
1053
1054        assert_eq!(m_k_back, m_k);
1055    }
1056
1057    #[test]
1058    fn sk_rgsw_by_rgsw() {
1059        let logq = 60;
1060        let logp = 2;
1061        let ring_size = 1 << 11;
1062        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
1063        let p = 1u64 << logp;
1064        let d_rgsw = 12;
1065        let logb = 5;
1066
1067        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
1068
1069        let mut rng = DefaultSecureRng::new();
1070        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
1071        let mod_op = ModularOpsU64::new(q);
1072        let decomposer = (
1073            DefaultDecomposer::new(q, logb, d_rgsw),
1074            DefaultDecomposer::new(q, logb, d_rgsw),
1075        );
1076
1077        let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
1078
1079        let mut carry_m = vec![0u64; ring_size as usize];
1080        carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
1081
1082        // RGSW(carry_m)
1083        let mut rgsw_carrym = {
1084            let seeded_rgsw = _sk_encrypt_rgsw(&carry_m, s.values(), &decomposer, &mod_op, &ntt_op);
1085            let mut rgsw_eval =
1086                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
1087                    &seeded_rgsw,
1088                );
1089            rgsw_eval
1090                .data
1091                .iter_mut()
1092                .for_each(|ri| ntt_op.backward(ri.as_mut()));
1093            rgsw_eval.data
1094        };
1095
1096        let mut scratch_matrix = vec![
1097            vec![0u64; ring_size as usize];
1098            decomposer.a().decomposition_count() * 2
1099                + decomposer.b().decomposition_count() * 2
1100                + std::cmp::max(
1101                    decomposer.a().decomposition_count(),
1102                    decomposer.b().decomposition_count()
1103                )
1104        ];
1105
1106        _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
1107
1108        for i in 0..2 {
1109            let mut m = vec![0u64; ring_size as usize];
1110            m[thread_rng().gen_range(0..ring_size) as usize] = if (i & 1) == 1 { q - 1 } else { 1 };
1111            let rgsw_m =
1112                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
1113                    &_sk_encrypt_rgsw(&m, s.values(), &decomposer, &mod_op, &ntt_op),
1114                );
1115            rgsw_by_rgsw_inplace(
1116                &mut rgsw_carrym,
1117                decomposer.a().decomposition_count(),
1118                decomposer.b().decomposition_count(),
1119                &rgsw_m.data,
1120                &decomposer,
1121                &mut scratch_matrix,
1122                &ntt_op,
1123                &mod_op,
1124            );
1125
1126            // measure noise
1127            carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q);
1128            println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
1129            _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
1130        }
1131        {
1132            // RLWE(m) x RGSW(carry_m)
1133            let mut m = vec![0u64; ring_size as usize];
1134            RandomFillUniformInModulus::random_fill(&mut rng, &q, m.as_mut_slice());
1135            let mut rlwe_ct = _sk_encrypt_rlwe(&m, s.values(), &ntt_op, &mod_op);
1136
1137            // send rgsw to evaluation domain
1138            rgsw_carrym
1139                .iter_mut()
1140                .for_each(|ri| ntt_op.forward(ri.as_mut_slice()));
1141
1142            rlwe_by_rgsw(
1143                &mut rlwe_ct,
1144                &rgsw_carrym,
1145                &mut scratch_matrix,
1146                &decomposer,
1147                &ntt_op,
1148                &mod_op,
1149            );
1150            let m_expected = negacyclic_mul(&carry_m, &m, mul_mod, q);
1151            let noise = measure_noise(&rlwe_ct, &m_expected, &ntt_op, &mod_op, s.values());
1152            println!("RLWE(m) x RGSW(carry_m): {noise}");
1153        }
1154    }
1155
1156    #[test]
1157    fn some_work() {
1158        let logq = 55;
1159        let ring_size = 1 << 11;
1160        let q = generate_prime(logq, ring_size as u64, 1u64 << logq).unwrap();
1161        let d = 2;
1162        let logb = 12;
1163        let decomposer = DefaultDecomposer::new(q, logb, d);
1164
1165        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
1166        let mod_op = ModularOpsU64::new(q);
1167        let mut rng = DefaultSecureRng::new();
1168
1169        let mut stats = Stats::new();
1170
1171        for _ in 0..10 {
1172            let mut a = vec![0u64; ring_size];
1173            RandomFillUniformInModulus::random_fill(&mut rng, &q, a.as_mut());
1174            let mut m = vec![0u64; ring_size];
1175            RandomFillGaussianInModulus::random_fill(&mut rng, &q, m.as_mut());
1176
1177            let mut sk = vec![0u64; ring_size];
1178            RandomFillGaussianInModulus::random_fill(&mut rng, &q, sk.as_mut());
1179            let mut sk_eval = sk.clone();
1180            ntt_op.forward(sk_eval.as_mut_slice());
1181
1182            let gadget_vector = decomposer.gadget_vector();
1183
1184            // ksk (beta e)
1185            let mut ksk_part_b = vec![vec![0u64; ring_size]; decomposer.decomposition_count()];
1186            let mut ksk_part_a = vec![vec![0u64; ring_size]; decomposer.decomposition_count()];
1187            izip!(
1188                ksk_part_b.iter_rows_mut(),
1189                ksk_part_a.iter_rows_mut(),
1190                gadget_vector.iter()
1191            )
1192            .for_each(|(part_b, part_a, beta)| {
1193                RandomFillUniformInModulus::random_fill(&mut rng, &q, part_a.as_mut());
1194
1195                // a * s
1196                let mut tmp = part_a.to_vec();
1197                ntt_op.forward(tmp.as_mut());
1198                mod_op.elwise_mul_mut(tmp.as_mut(), sk_eval.as_ref());
1199                ntt_op.backward(tmp.as_mut());
1200
1201                // a*s + e + beta m
1202                RandomFillGaussianInModulus::random_fill(&mut rng, &q, part_b.as_mut());
1203                // println!("E: {:?}", &part_b);
1204                // a*s + e
1205                mod_op.elwise_add_mut(part_b.as_mut_slice(), tmp.as_ref());
1206                // a*s + e + beta m
1207                let mut tmp = m.to_vec();
1208                mod_op.elwise_scalar_mul_mut(tmp.as_mut_slice(), beta);
1209                mod_op.elwise_add_mut(part_b.as_mut_slice(), tmp.as_ref());
1210            });
1211
1212            // decompose a
1213            let mut decomposed_a = vec![vec![0u64; ring_size]; decomposer.decomposition_count()];
1214            a.iter().enumerate().for_each(|(ri, el)| {
1215                decomposer
1216                    .decompose_iter(el)
1217                    .into_iter()
1218                    .enumerate()
1219                    .for_each(|(j, d_el)| {
1220                        decomposed_a[j][ri] = d_el;
1221                    });
1222            });
1223
1224            // println!("Last limb");
1225
1226            // decomp_a * ksk(beta m)
1227            ksk_part_b
1228                .iter_mut()
1229                .for_each(|r| ntt_op.forward(r.as_mut_slice()));
1230            ksk_part_a
1231                .iter_mut()
1232                .for_each(|r| ntt_op.forward(r.as_mut_slice()));
1233            decomposed_a
1234                .iter_mut()
1235                .for_each(|r| ntt_op.forward(r.as_mut_slice()));
1236            let mut out = vec![vec![0u64; ring_size]; 2];
1237            izip!(decomposed_a.iter(), ksk_part_b.iter(), ksk_part_a.iter()).for_each(
1238                |(d_a, part_b, part_a)| {
1239                    // out_a += d_a * part_a
1240                    let mut d_a_clone = d_a.clone();
1241                    mod_op.elwise_mul_mut(d_a_clone.as_mut_slice(), part_a.as_ref());
1242                    mod_op.elwise_add_mut(out[0].as_mut_slice(), d_a_clone.as_ref());
1243
1244                    // out_b += d_a * part_b
1245                    let mut d_a_clone = d_a.clone();
1246                    mod_op.elwise_mul_mut(d_a_clone.as_mut_slice(), part_b.as_ref());
1247                    mod_op.elwise_add_mut(out[1].as_mut_slice(), d_a_clone.as_ref());
1248                },
1249            );
1250            out.iter_mut()
1251                .for_each(|r| ntt_op.backward(r.as_mut_slice()));
1252
1253            let out_back = {
1254                // decrypt
1255                // a*s
1256                ntt_op.forward(out[0].as_mut());
1257                mod_op.elwise_mul_mut(out[0].as_mut(), sk_eval.as_ref());
1258                ntt_op.backward(out[0].as_mut());
1259
1260                // b - a*s
1261                let tmp = (out[0]).clone();
1262                mod_op.elwise_sub_mut(out[1].as_mut(), tmp.as_ref());
1263                out.remove(1)
1264            };
1265
1266            let out_expected = {
1267                let mut a_clone = a.clone();
1268                let mut m_clone = m.clone();
1269
1270                ntt_op.forward(a_clone.as_mut_slice());
1271                ntt_op.forward(m_clone.as_mut_slice());
1272
1273                mod_op.elwise_mul_mut(a_clone.as_mut_slice(), m_clone.as_mut_slice());
1274                ntt_op.backward(a_clone.as_mut_slice());
1275                a_clone
1276            };
1277
1278            let mut diff = out_expected;
1279            mod_op.elwise_sub_mut(diff.as_mut_slice(), out_back.as_ref());
1280            stats.add_more(&Vec::<i64>::try_convert_from(diff.as_ref(), &q));
1281        }
1282
1283        println!("Std: {}", stats.std_dev().abs().log2());
1284    }
1285}