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::{
 538            galois_auto_shoup, rlwe_by_rgsw_shoup, ShoupAutoKeyEvaluationDomain,
 539            ShoupRgswCiphertextEvaluationDomain,
 540        },
 541        utils::{generate_prime, negacyclic_mul, Stats, TryConvertFrom1},
 542        Matrix, Secret,
 543    };
 544
 545    use super::{
 546        keygen::{
 547            decrypt_rlwe, galois_key_gen, gen_rlwe_public_key, generate_auto_map, measure_noise,
 548            public_key_encrypt_rgsw, secret_key_encrypt_rgsw, secret_key_encrypt_rlwe,
 549        },
 550        runtime::{galois_auto, rgsw_by_rgsw_inplace, rlwe_by_rgsw},
 551        AutoKeyEvaluationDomain, RgswCiphertext, RgswCiphertextEvaluationDomain, RlweCiphertext,
 552        RlwePublicKey, RlweSecret, SeededAutoKey, SeededRgswCiphertext, SeededRlweCiphertext,
 553        SeededRlwePublicKey,
 554    };
 555
 556    pub(crate) fn _sk_encrypt_rlwe<T: Modulus<Element = u64> + Clone>(
 557        m: &[u64],
 558        s: &[i32],
 559        ntt_op: &NttBackendU64,
 560        mod_op: &ModularOpsU64<T>,
 561    ) -> RlweCiphertext<Vec<Vec<u64>>, DefaultSecureRng> {
 562        let ring_size = m.len();
 563        let q = mod_op.modulus();
 564        assert!(s.len() == ring_size);
 565
 566        let mut rng = DefaultSecureRng::new();
 567        let mut rlwe_seed = [0u8; 32];
 568        rng.fill_bytes(&mut rlwe_seed);
 569        let mut seeded_rlwe_ct =
 570            SeededRlweCiphertext::<_, [u8; 32], _>::empty(ring_size as usize, rlwe_seed, q.clone());
 571        let mut p_rng = DefaultSecureRng::new_seeded(rlwe_seed);
 572        secret_key_encrypt_rlwe(
 573            &m,
 574            &mut seeded_rlwe_ct.data,
 575            s,
 576            mod_op,
 577            ntt_op,
 578            &mut p_rng,
 579            &mut rng,
 580        );
 581
 582        RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_ct)
 583    }
 584
 585    // Encrypt m as RGSW ciphertext RGSW(m) using supplied public key
 586    pub(crate) fn _pk_encrypt_rgsw<T: Modulus<Element = u64> + Clone>(
 587        m: &[u64],
 588        public_key: &RlwePublicKey<Vec<Vec<u64>>, DefaultSecureRng>,
 589        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 590        mod_op: &ModularOpsU64<T>,
 591        ntt_op: &NttBackendU64,
 592    ) -> RgswCiphertext<Vec<Vec<u64>>, T> {
 593        let (_, ring_size) = Matrix::dimension(&public_key.data);
 594        let gadget_vector_a = decomposer.a().gadget_vector();
 595        let gadget_vector_b = decomposer.b().gadget_vector();
 596
 597        let mut rng = DefaultSecureRng::new();
 598
 599        assert!(m.len() == ring_size);
 600
 601        // public key encrypt RGSW(m1)
 602        let mut rgsw_ct = RgswCiphertext::empty(ring_size, decomposer, mod_op.modulus().clone());
 603        public_key_encrypt_rgsw(
 604            &mut rgsw_ct.data,
 605            m,
 606            &public_key.data,
 607            &gadget_vector_a,
 608            &gadget_vector_b,
 609            mod_op,
 610            ntt_op,
 611            &mut rng,
 612        );
 613
 614        rgsw_ct
 615    }
 616
 617    /// Encrypts m as RGSW ciphertext RGSW(m) using supplied secret key. Returns
 618    /// unseeded RGSW ciphertext in coefficient domain
 619    pub(crate) fn _sk_encrypt_rgsw<T: Modulus<Element = u64> + Clone>(
 620        m: &[u64],
 621        s: &[i32],
 622        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 623        mod_op: &ModularOpsU64<T>,
 624        ntt_op: &NttBackendU64,
 625    ) -> SeededRgswCiphertext<Vec<Vec<u64>>, [u8; 32], T> {
 626        let ring_size = s.len();
 627        assert!(m.len() == s.len());
 628
 629        let q = mod_op.modulus();
 630
 631        let gadget_vector_a = decomposer.a().gadget_vector();
 632        let gadget_vector_b = decomposer.b().gadget_vector();
 633
 634        let mut rng = DefaultSecureRng::new();
 635        let mut rgsw_seed = [0u8; 32];
 636        rng.fill_bytes(&mut rgsw_seed);
 637        let mut seeded_rgsw_ct = SeededRgswCiphertext::<Vec<Vec<u64>>, [u8; 32], T>::empty(
 638            ring_size as usize,
 639            decomposer,
 640            rgsw_seed,
 641            q.clone(),
 642        );
 643        let mut p_rng = DefaultSecureRng::new_seeded(rgsw_seed);
 644        secret_key_encrypt_rgsw(
 645            &mut seeded_rgsw_ct.data,
 646            m,
 647            &gadget_vector_a,
 648            &gadget_vector_b,
 649            s,
 650            mod_op,
 651            ntt_op,
 652            &mut p_rng,
 653            &mut rng,
 654        );
 655        seeded_rgsw_ct
 656    }
 657
 658    /// Prints noise in RGSW ciphertext RGSW(m).
 659    ///
 660    /// - rgsw_ct: RGSW ciphertext in coefficient domain
 661    pub(crate) fn _measure_noise_rgsw<T: Modulus<Element = u64> + Clone>(
 662        rgsw_ct: &[Vec<u64>],
 663        m: &[u64],
 664        s: &[i32],
 665        decomposer: &(DefaultDecomposer<u64>, DefaultDecomposer<u64>),
 666        q: &T,
 667    ) {
 668        let gadget_vector_a = decomposer.a().gadget_vector();
 669        let gadget_vector_b = decomposer.b().gadget_vector();
 670        let d_a = gadget_vector_a.len();
 671        let d_b = gadget_vector_b.len();
 672        let ring_size = s.len();
 673        assert!(Matrix::dimension(&rgsw_ct) == (d_a * 2 + d_b * 2, ring_size));
 674        assert!(m.len() == ring_size);
 675
 676        let mod_op = ModularOpsU64::new(q.clone());
 677        let ntt_op = NttBackendU64::new(q, ring_size);
 678
 679        let mul_mod =
 680            |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q.q().unwrap() as u128) as u64;
 681        let s_poly = Vec::<u64>::try_convert_from(s, q);
 682        let mut neg_s = s_poly.clone();
 683        mod_op.elwise_neg_mut(neg_s.as_mut());
 684        let neg_sm0m1 = negacyclic_mul(&neg_s, &m, mul_mod, q.q().unwrap());
 685
 686        // RLWE(\beta^j -s * m)
 687        for j in 0..d_a {
 688            let ideal_m = {
 689                // RLWE(\beta^j -s * m)
 690                let mut beta_neg_sm0m1 = vec![0u64; ring_size as usize];
 691                mod_op.elwise_scalar_mul(beta_neg_sm0m1.as_mut(), &neg_sm0m1, &gadget_vector_a[j]);
 692                beta_neg_sm0m1
 693            };
 694
 695            let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
 696            rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(j));
 697            rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a + j));
 698            let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
 699
 700            println!(r"Noise RLWE(\beta^{j} -sm0m1): {noise}");
 701        }
 702
 703        // RLWE(\beta^j  m)
 704        for j in 0..d_b {
 705            let ideal_m = {
 706                // RLWE(\beta^j  m)
 707                let mut beta_m0m1 = vec![0u64; ring_size as usize];
 708                mod_op.elwise_scalar_mul(beta_m0m1.as_mut(), &m, &gadget_vector_b[j]);
 709                beta_m0m1
 710            };
 711
 712            let mut rlwe = vec![vec![0u64; ring_size as usize]; 2];
 713            rlwe[0].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + j));
 714            rlwe[1].copy_from_slice(rgsw_ct.get_row_slice(d_a * 2 + d_b + j));
 715            let noise = measure_noise(&rlwe, &ideal_m, &ntt_op, &mod_op, s);
 716
 717            println!(r"Noise RLWE(\beta^{j} m0m1): {noise}");
 718        }
 719    }
 720
 721    #[test]
 722    fn rlwe_encrypt_decryption() {
 723        let logq = 50;
 724        let logp = 2;
 725        let ring_size = 1 << 4;
 726        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
 727        let p = 1u64 << logp;
 728
 729        let mut rng = DefaultSecureRng::new();
 730
 731        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 732
 733        // sample m0
 734        let mut m0 = vec![0u64; ring_size as usize];
 735        RandomFillUniformInModulus::<[u64], u64>::random_fill(
 736            &mut rng,
 737            &(1u64 << logp),
 738            m0.as_mut_slice(),
 739        );
 740
 741        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 742        let mod_op = ModularOpsU64::new(q);
 743
 744        // encrypt m0
 745        let encoded_m = m0
 746            .iter()
 747            .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 748            .collect_vec();
 749        let rlwe_in_ct = _sk_encrypt_rlwe(&encoded_m, s.values(), &ntt_op, &mod_op);
 750
 751        let mut encoded_m_back = vec![0u64; ring_size as usize];
 752        decrypt_rlwe(
 753            &rlwe_in_ct,
 754            s.values(),
 755            &mut encoded_m_back,
 756            &ntt_op,
 757            &mod_op,
 758        );
 759        let m_back = encoded_m_back
 760            .iter()
 761            .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
 762            .collect_vec();
 763        assert_eq!(m0, m_back);
 764
 765        // let noise = measure_noise(&rlwe_in_ct, &encoded_m, &ntt_op, &mod_op,
 766        // s.values()); println!("Noise: {noise}");
 767    }
 768
 769    #[test]
 770    fn rlwe_by_rgsw_works() {
 771        let logq = 50;
 772        let logp = 2;
 773        let ring_size = 1 << 9;
 774        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
 775        let p: u64 = 1u64 << logp;
 776
 777        let mut rng = DefaultSecureRng::new_seeded([0u8; 32]);
 778
 779        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 780
 781        let mut m0 = vec![0u64; ring_size as usize];
 782        RandomFillUniformInModulus::<[u64], _>::random_fill(
 783            &mut rng,
 784            &(1u64 << logp),
 785            m0.as_mut_slice(),
 786        );
 787        let mut m1 = vec![0u64; ring_size as usize];
 788        m1[thread_rng().gen_range(0..ring_size) as usize] = 1;
 789
 790        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 791        let mod_op = ModularOpsU64::new(q);
 792        let d_rgsw = 10;
 793        let logb = 5;
 794        let decomposer = (
 795            DefaultDecomposer::new(q, logb, d_rgsw),
 796            DefaultDecomposer::new(q, logb, d_rgsw),
 797        );
 798
 799        // create public key
 800        let mut pk_seed = [0u8; 32];
 801        rng.fill_bytes(&mut pk_seed);
 802        let mut pk_prng = DefaultSecureRng::new_seeded(pk_seed);
 803        let mut seeded_pk =
 804            SeededRlwePublicKey::<Vec<u64>, _>::empty(ring_size as usize, pk_seed, q);
 805        gen_rlwe_public_key(
 806            &mut seeded_pk.data,
 807            s.values(),
 808            &ntt_op,
 809            &mod_op,
 810            &mut pk_prng,
 811            &mut rng,
 812        );
 813        let pk = RlwePublicKey::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_pk);
 814
 815        // Encrypt m1 as RGSW(m1)
 816        let rgsw_ct = {
 817            //TODO(Jay): Figure out better way to test secret key and public key variant of
 818            // RGSW ciphertext encryption within the same test
 819
 820            if true {
 821                // Encryption m1 as RGSW(m1) using secret key
 822                let seeded_rgsw_ct =
 823                    _sk_encrypt_rgsw(&m1, s.values(), &decomposer, &mod_op, &ntt_op);
 824                RgswCiphertextEvaluationDomain::<Vec<Vec<u64>>, _,DefaultSecureRng, NttBackendU64>::from(&seeded_rgsw_ct)
 825            } else {
 826                // Encrypt m1 as RGSW(m1) using public key
 827                let rgsw_ct = _pk_encrypt_rgsw(&m1, &pk, &decomposer, &mod_op, &ntt_op);
 828                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
 829                    &rgsw_ct,
 830                )
 831            }
 832        };
 833
 834        // Encrypt m0 as RLWE(m0)
 835        let mut rlwe_in_ct = {
 836            let encoded_m = m0
 837                .iter()
 838                .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 839                .collect_vec();
 840
 841            _sk_encrypt_rlwe(&encoded_m, s.values(), &ntt_op, &mod_op)
 842        };
 843
 844        // RLWE(m0m1) = RLWE(m0) x RGSW(m1)
 845        let mut scratch_space = vec![
 846            vec![0u64; ring_size as usize];
 847            std::cmp::max(
 848                decomposer.a().decomposition_count(),
 849                decomposer.b().decomposition_count()
 850            ) + 2
 851        ];
 852
 853        // rlwe x rgsw with additional RGSW ciphertexts in shoup repr
 854        let rlwe_in_ct_shoup = {
 855            let mut rlwe_in_ct_shoup = RlweCiphertext::<_, DefaultSecureRng> {
 856                data: rlwe_in_ct.data.clone(),
 857                is_trivial: rlwe_in_ct.is_trivial,
 858                _phatom: PhantomData::default(),
 859            };
 860
 861            let rgsw_ct_shoup = ShoupRgswCiphertextEvaluationDomain::from(&rgsw_ct);
 862
 863            rlwe_by_rgsw_shoup(
 864                &mut rlwe_in_ct_shoup,
 865                &rgsw_ct.data,
 866                &rgsw_ct_shoup.data,
 867                &mut scratch_space,
 868                &decomposer,
 869                &ntt_op,
 870                &mod_op,
 871            );
 872
 873            rlwe_in_ct_shoup
 874        };
 875
 876        // rlwe x rgsw normal
 877        {
 878            rlwe_by_rgsw(
 879                &mut rlwe_in_ct,
 880                &rgsw_ct.data,
 881                &mut scratch_space,
 882                &decomposer,
 883                &ntt_op,
 884                &mod_op,
 885            );
 886        }
 887
 888        // output from both functions must be equal
 889        {
 890            assert_eq!(rlwe_in_ct.data, rlwe_in_ct_shoup.data);
 891        }
 892
 893        // Decrypt RLWE(m0m1)
 894        let mut encoded_m0m1_back = vec![0u64; ring_size as usize];
 895        decrypt_rlwe(
 896            &rlwe_in_ct,
 897            s.values(),
 898            &mut encoded_m0m1_back,
 899            &ntt_op,
 900            &mod_op,
 901        );
 902        let m0m1_back = encoded_m0m1_back
 903            .iter()
 904            .map(|v| (((*v as f64 * p as f64) / (q as f64)).round() as u64) % p)
 905            .collect_vec();
 906
 907        let mul_mod = |v0: &u64, v1: &u64| (v0 * v1) % p;
 908        let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, p);
 909
 910        // {
 911        //     // measure noise
 912        //     let encoded_m_ideal = m0m1
 913        //         .iter()
 914        //         .map(|v| (((*v as f64) * q as f64) / (p as f64)).round() as u64)
 915        //         .collect_vec();
 916
 917        //     let noise = measure_noise(&rlwe_in_ct, &encoded_m_ideal, &ntt_op,
 918        // &mod_op, s.values());     println!("Noise RLWE(m0m1)(=
 919        // RLWE(m0)xRGSW(m1)) : {noise}"); }
 920
 921        assert!(
 922            m0m1 == m0m1_back,
 923            "Expected {:?} \n Got {:?}",
 924            m0m1,
 925            m0m1_back
 926        );
 927    }
 928
 929    #[test]
 930    fn galois_auto_works() {
 931        let logq = 55;
 932        let ring_size = 1 << 11;
 933        let q = generate_prime(logq, 2 * ring_size, 1u64 << logq).unwrap();
 934        let logp = 3;
 935        let p = 1u64 << logp;
 936        let d_rgsw = 5;
 937        let logb = 11;
 938
 939        let mut rng = DefaultSecureRng::new();
 940        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
 941
 942        let mut m = vec![0u64; ring_size as usize];
 943        RandomFillUniformInModulus::random_fill(&mut rng, &p, m.as_mut_slice());
 944        let encoded_m = m
 945            .iter()
 946            .map(|v| (((*v as f64 * q as f64) / (p as f64)).round() as u64))
 947            .collect_vec();
 948
 949        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
 950        let mod_op = ModularOpsU64::new(q);
 951
 952        // RLWE_{s}(m)
 953        let mut seed_rlwe = [0u8; 32];
 954        rng.fill_bytes(&mut seed_rlwe);
 955        let mut seeded_rlwe_m = SeededRlweCiphertext::empty(ring_size as usize, seed_rlwe, q);
 956        let mut p_rng = DefaultSecureRng::new_seeded(seed_rlwe);
 957        secret_key_encrypt_rlwe(
 958            &encoded_m,
 959            &mut seeded_rlwe_m.data,
 960            s.values(),
 961            &mod_op,
 962            &ntt_op,
 963            &mut p_rng,
 964            &mut rng,
 965        );
 966        let mut rlwe_m = RlweCiphertext::<Vec<Vec<u64>>, DefaultSecureRng>::from(&seeded_rlwe_m);
 967
 968        let auto_k = -125;
 969
 970        // Generate galois key to key switch from s^k to s
 971        let decomposer = DefaultDecomposer::new(q, logb, d_rgsw);
 972        let mut seed_auto = [0u8; 32];
 973        rng.fill_bytes(&mut seed_auto);
 974        let mut seeded_auto_key =
 975            SeededAutoKey::empty(ring_size as usize, &decomposer, seed_auto, q);
 976        let mut p_rng = DefaultSecureRng::new_seeded(seed_auto);
 977        let gadget_vector = decomposer.gadget_vector();
 978        galois_key_gen(
 979            &mut seeded_auto_key.data,
 980            s.values(),
 981            auto_k,
 982            &gadget_vector,
 983            &mod_op,
 984            &ntt_op,
 985            &mut p_rng,
 986            &mut rng,
 987        );
 988        let auto_key =
 989            AutoKeyEvaluationDomain::<Vec<Vec<u64>>, _, DefaultSecureRng, NttBackendU64>::from(
 990                &seeded_auto_key,
 991            );
 992
 993        // Send RLWE_{s}(m) -> RLWE_{s}(m^k)
 994        let mut scratch_space = vec![vec![0u64; ring_size as usize]; d_rgsw + 2];
 995        let (auto_map_index, auto_map_sign) = generate_auto_map(ring_size as usize, auto_k);
 996
 997        // galois auto with additional auto key in shoup repr
 998        let rlwe_m_shoup = {
 999            let auto_key_shoup = ShoupAutoKeyEvaluationDomain::from(&auto_key);
1000            let mut rlwe_m_shoup = RlweCiphertext::<_, DefaultSecureRng> {
1001                data: rlwe_m.data.clone(),
1002                is_trivial: rlwe_m.is_trivial,
1003                _phatom: PhantomData::default(),
1004            };
1005            galois_auto_shoup(
1006                &mut rlwe_m_shoup,
1007                &auto_key.data,
1008                &auto_key_shoup.data,
1009                &mut scratch_space,
1010                &auto_map_index,
1011                &auto_map_sign,
1012                &mod_op,
1013                &ntt_op,
1014                &decomposer,
1015            );
1016            rlwe_m_shoup
1017        };
1018
1019        // normal galois auto
1020        {
1021            galois_auto(
1022                &mut rlwe_m,
1023                &auto_key.data,
1024                &mut scratch_space,
1025                &auto_map_index,
1026                &auto_map_sign,
1027                &mod_op,
1028                &ntt_op,
1029                &decomposer,
1030            );
1031        }
1032
1033        // rlwe out from both functions must be same
1034        assert_eq!(rlwe_m.data, rlwe_m_shoup.data);
1035
1036        let rlwe_m_k = rlwe_m;
1037
1038        // Decrypt RLWE_{s}(m^k) and check
1039        let mut encoded_m_k_back = vec![0u64; ring_size as usize];
1040        decrypt_rlwe(
1041            &rlwe_m_k,
1042            s.values(),
1043            &mut encoded_m_k_back,
1044            &ntt_op,
1045            &mod_op,
1046        );
1047        let m_k_back = encoded_m_k_back
1048            .iter()
1049            .map(|v| (((*v as f64 * p as f64) / q as f64).round() as u64) % p)
1050            .collect_vec();
1051
1052        let mut m_k = vec![0u64; ring_size as usize];
1053        // Send \delta m -> \delta m^k
1054        izip!(m.iter(), auto_map_index.iter(), auto_map_sign.iter()).for_each(
1055            |(v, to_index, sign)| {
1056                if !*sign {
1057                    m_k[*to_index] = (p - *v) % p;
1058                } else {
1059                    m_k[*to_index] = *v;
1060                }
1061            },
1062        );
1063
1064        {
1065            let encoded_m_k = m_k
1066                .iter()
1067                .map(|v| ((*v as f64 * q as f64) / p as f64).round() as u64)
1068                .collect_vec();
1069
1070            let noise = measure_noise(&rlwe_m_k, &encoded_m_k, &ntt_op, &mod_op, s.values());
1071            println!("Ksk noise: {noise}");
1072        }
1073
1074        assert_eq!(m_k_back, m_k);
1075    }
1076
1077    #[test]
1078    fn sk_rgsw_by_rgsw() {
1079        let logq = 60;
1080        let logp = 2;
1081        let ring_size = 1 << 11;
1082        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
1083        let p = 1u64 << logp;
1084        let d_rgsw = 5;
1085        let logb = 12;
1086
1087        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
1088
1089        let mut rng = DefaultSecureRng::new();
1090        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
1091        let mod_op = ModularOpsU64::new(q);
1092        let decomposer = (
1093            DefaultDecomposer::new(q, logb, d_rgsw),
1094            DefaultDecomposer::new(q, logb, d_rgsw),
1095        );
1096
1097        let mul_mod = |a: &u64, b: &u64| ((*a as u128 * *b as u128) % q as u128) as u64;
1098
1099        let mut carry_m = vec![0u64; ring_size as usize];
1100        carry_m[thread_rng().gen_range(0..ring_size) as usize] = 1;
1101
1102        // RGSW(carry_m)
1103        let mut rgsw_carrym = {
1104            let seeded_rgsw = _sk_encrypt_rgsw(&carry_m, s.values(), &decomposer, &mod_op, &ntt_op);
1105            let mut rgsw_eval =
1106                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
1107                    &seeded_rgsw,
1108                );
1109            rgsw_eval
1110                .data
1111                .iter_mut()
1112                .for_each(|ri| ntt_op.backward(ri.as_mut()));
1113            rgsw_eval.data
1114        };
1115
1116        let mut scratch_matrix = vec![
1117            vec![0u64; ring_size as usize];
1118            decomposer.a().decomposition_count() * 2
1119                + decomposer.b().decomposition_count() * 2
1120                + std::cmp::max(
1121                    decomposer.a().decomposition_count(),
1122                    decomposer.b().decomposition_count()
1123                )
1124        ];
1125
1126        _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
1127
1128        for i in 0..2 {
1129            let mut m = vec![0u64; ring_size as usize];
1130            m[thread_rng().gen_range(0..ring_size) as usize] = if (i & 1) == 1 { q - 1 } else { 1 };
1131            let rgsw_m =
1132                RgswCiphertextEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
1133                    &_sk_encrypt_rgsw(&m, s.values(), &decomposer, &mod_op, &ntt_op),
1134                );
1135            rgsw_by_rgsw_inplace(
1136                &mut rgsw_carrym,
1137                &rgsw_m.data,
1138                &decomposer,
1139                &mut scratch_matrix,
1140                &ntt_op,
1141                &mod_op,
1142            );
1143
1144            // measure noise
1145            carry_m = negacyclic_mul(&carry_m, &m, mul_mod, q);
1146            println!("########### Noise RGSW(carrym) in {i}^th loop ###########");
1147            _measure_noise_rgsw(&rgsw_carrym, &carry_m, s.values(), &decomposer, &q);
1148        }
1149        {
1150            // RLWE(m) x RGSW(carry_m)
1151            let mut m = vec![0u64; ring_size as usize];
1152            RandomFillUniformInModulus::random_fill(&mut rng, &q, m.as_mut_slice());
1153            let mut rlwe_ct = _sk_encrypt_rlwe(&m, s.values(), &ntt_op, &mod_op);
1154
1155            // send rgsw to evaluation domain
1156            rgsw_carrym
1157                .iter_mut()
1158                .for_each(|ri| ntt_op.forward(ri.as_mut_slice()));
1159
1160            rlwe_by_rgsw(
1161                &mut rlwe_ct,
1162                &rgsw_carrym,
1163                &mut scratch_matrix,
1164                &decomposer,
1165                &ntt_op,
1166                &mod_op,
1167            );
1168            let m_expected = negacyclic_mul(&carry_m, &m, mul_mod, q);
1169            let noise = measure_noise(&rlwe_ct, &m_expected, &ntt_op, &mod_op, s.values());
1170            println!("RLWE(m) x RGSW(carry_m): {noise}");
1171        }
1172    }
1173
1174    #[test]
1175    fn some_work() {
1176        let logq = 50;
1177        let ring_size = 1 << 11;
1178        let q = generate_prime(logq, ring_size, 1u64 << logq).unwrap();
1179        let d_rgsw = 10;
1180        let logb = 5;
1181        let decomposer = (
1182            DefaultDecomposer::new(q, logb, d_rgsw),
1183            DefaultDecomposer::new(q, logb, d_rgsw),
1184        );
1185
1186        let ntt_op = NttBackendU64::new(&q, ring_size as usize);
1187        let mod_op = ModularOpsU64::new(q);
1188        let mut rng = DefaultSecureRng::new_seeded([0u8; 32]);
1189
1190        let s = RlweSecret::random((ring_size >> 1) as usize, ring_size as usize);
1191
1192        let mut check = Stats { samples: vec![] };
1193
1194        for _ in 0..100 {
1195            let mut m0 = vec![0u64; ring_size as usize];
1196            m0[thread_rng().gen_range(0..ring_size) as usize] = 1;
1197            let mut m1 = vec![0u64; ring_size as usize];
1198            m1[thread_rng().gen_range(0..ring_size) as usize] = 1;
1199
1200            let mut rgsw_ct0 = {
1201                let seeded_rgsw_ct =
1202                    _sk_encrypt_rgsw(&m0, s.values(), &decomposer, &mod_op, &ntt_op);
1203                RgswCiphertextEvaluationDomain::<Vec<Vec<u64>>, _,DefaultSecureRng, NttBackendU64>::from(&seeded_rgsw_ct)
1204            };
1205            let rgsw_ct1 = {
1206                let seeded_rgsw_ct =
1207                    _sk_encrypt_rgsw(&m1, s.values(), &decomposer, &mod_op, &ntt_op);
1208                RgswCiphertextEvaluationDomain::<Vec<Vec<u64>>,_, DefaultSecureRng, NttBackendU64>::from(&seeded_rgsw_ct)
1209            };
1210
1211            // RGSW x RGSW
1212            // send RGSW(m0) to coefficient domain
1213            rgsw_ct0
1214                .data
1215                .iter_mut()
1216                .for_each(|r| ntt_op.backward(r.as_mut_slice()));
1217            let mut scratch_matrix = vec![
1218                vec![0u64; ring_size as usize];
1219                std::cmp::max(
1220                    decomposer.a().decomposition_count(),
1221                    decomposer.b().decomposition_count()
1222                ) + decomposer.a().decomposition_count() * 2
1223                    + decomposer.b().decomposition_count() * 2
1224            ];
1225            rgsw_by_rgsw_inplace(
1226                &mut rgsw_ct0.data,
1227                &rgsw_ct1.data,
1228                &decomposer,
1229                &mut scratch_matrix,
1230                &ntt_op,
1231                &mod_op,
1232            );
1233            let mut rgsw_m0m1 = rgsw_ct0;
1234            // Back to Evaluation for RLWExRGSW
1235            rgsw_m0m1
1236                .data
1237                .iter_mut()
1238                .for_each(|r| ntt_op.forward(r.as_mut_slice()));
1239
1240            // Sample m2, encrypt it as RLWE(m2) and multiply RLWE(m2)xRGSW(m0m1)
1241            let mut m2 = vec![0u64; ring_size as usize];
1242            RandomFillUniformInModulus::random_fill(&mut rng, &q, m2.as_mut_slice());
1243            let mut rlwe_in_ct = { _sk_encrypt_rlwe(&m2, s.values(), &ntt_op, &mod_op) };
1244            let mut scratch_space = vec![
1245                vec![0u64; ring_size as usize];
1246                std::cmp::max(
1247                    decomposer.a().decomposition_count(),
1248                    decomposer.b().decomposition_count()
1249                ) + 2
1250            ];
1251            rlwe_by_rgsw(
1252                &mut rlwe_in_ct,
1253                &rgsw_m0m1.data,
1254                &mut scratch_space,
1255                &decomposer,
1256                &ntt_op,
1257                &mod_op,
1258            );
1259
1260            // Decrypt RLWE(m0m1m2)
1261            let mut m0m1m2_back = vec![0u64; ring_size as usize];
1262            decrypt_rlwe(&rlwe_in_ct, s.values(), &mut m0m1m2_back, &ntt_op, &mod_op);
1263
1264            // Calculate m0m1m2
1265            let mul_mod = |v0: &u64, v1: &u64| ((*v0 as u128 * *v1 as u128) % q as u128) as u64;
1266            let m0m1 = negacyclic_mul(&m0, &m1, mul_mod, q);
1267            let m0m1m2 = negacyclic_mul(&m2, &m0m1, mul_mod, q);
1268
1269            // diff
1270            mod_op.elwise_sub_mut(m0m1m2_back.as_mut_slice(), m0m1m2.as_ref());
1271
1272            check.add_more(&Vec::<i64>::try_convert_from(&m0m1m2_back, &q));
1273        }
1274
1275        println!("Std: {}", check.std_dev().abs().log2());
1276    }
1277}