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