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}