poulpy/math/tests/rescaling_rns.rs
math/tests/rescaling_rns.rsRaw
1use itertools::izip;
2use math::num_bigint::Div;
3use math::poly::{Poly, PolyRNS};
4use math::ring::RingRNS;
5use num_bigint::BigInt;
6use sampling::source::Source;
7
8#[test]
9fn rescaling_rns_u64() {
10 let n = 1 << 10;
11 let moduli: Vec<u64> = vec![
12 0x1fffffffffc80001u64,
13 0x1fffffffffe00001u64,
14 0x1fffffffffb40001,
15 0x1fffffffff500001,
16 ];
17 let ring_rns: RingRNS<u64> = RingRNS::new(n, moduli);
18
19 sub_test("test_div_by_last_modulus::<ROUND:false, NTT:false>", || {
20 test_div_by_last_modulus::<false, false>(&ring_rns)
21 });
22 sub_test("test_div_by_last_modulus::<ROUND:false, NTT:true>", || {
23 test_div_by_last_modulus::<false, true>(&ring_rns)
24 });
25 sub_test("test_div_by_last_modulus::<ROUND:true, NTT:false>", || {
26 test_div_by_last_modulus::<true, false>(&ring_rns)
27 });
28 sub_test("test_div_by_last_modulus::<ROUND:true, NTT:true>", || {
29 test_div_by_last_modulus::<true, true>(&ring_rns)
30 });
31 sub_test(
32 "test_div_by_last_modulus_inplace::<ROUND:false, NTT:false>",
33 || test_div_by_last_modulus_inplace::<false, false>(&ring_rns),
34 );
35 sub_test(
36 "test_div_by_last_modulus_inplace::<ROUND:false, NTT:true>",
37 || test_div_by_last_modulus_inplace::<false, true>(&ring_rns),
38 );
39 sub_test(
40 "test_div_by_last_modulus_inplace::<ROUND:true, NTT:true>",
41 || test_div_by_last_modulus_inplace::<true, true>(&ring_rns),
42 );
43 sub_test(
44 "test_div_by_last_modulus_inplace::<ROUND:true, NTT:false>",
45 || test_div_by_last_modulus_inplace::<true, false>(&ring_rns),
46 );
47 sub_test("test_div_by_last_moduli::<ROUND:false, NTT:false>", || {
48 test_div_by_last_moduli::<false, false>(&ring_rns)
49 });
50 sub_test("test_div_by_last_moduli::<ROUND:false, NTT:true>", || {
51 test_div_by_last_moduli::<false, true>(&ring_rns)
52 });
53 sub_test("test_div_by_last_moduli::<ROUND:true, NTT:false>", || {
54 test_div_by_last_moduli::<true, false>(&ring_rns)
55 });
56 sub_test("test_div_by_last_moduli::<ROUND:true, NTT:true>", || {
57 test_div_by_last_moduli::<true, true>(&ring_rns)
58 });
59 sub_test(
60 "test_div_by_last_moduli_inplace::<ROUND:false, NTT:false>",
61 || test_div_by_last_moduli_inplace::<false, false>(&ring_rns),
62 );
63 sub_test(
64 "test_div_by_last_moduli_inplace::<ROUND:false, NTT:true>",
65 || test_div_by_last_moduli_inplace::<false, true>(&ring_rns),
66 );
67 sub_test(
68 "test_div_by_last_moduli_inplace::<ROUND:true, NTT:false>",
69 || test_div_by_last_moduli_inplace::<true, false>(&ring_rns),
70 );
71 sub_test(
72 "test_div_by_last_moduli_inplace::<ROUND:true, NTT:true>",
73 || test_div_by_last_moduli_inplace::<true, true>(&ring_rns),
74 );
75}
76
77fn sub_test<F: FnOnce()>(name: &str, f: F) {
78 println!("Running {}", name);
79 f();
80}
81
82fn test_div_by_last_modulus<const ROUND: bool, const NTT: bool>(ring_rns: &RingRNS<u64>) {
83 let seed: [u8; 32] = [0; 32];
84 let mut source: Source = Source::new(seed);
85
86 let mut a: PolyRNS<u64> = ring_rns.new_polyrns();
87 let mut buf: [Poly<u64>; 2] = [ring_rns.new_poly(), ring_rns.new_poly()];
88 let mut c: PolyRNS<u64> = ring_rns.at_level(ring_rns.level() - 1).new_polyrns();
89
90 // Allocates a random PolyRNS
91 ring_rns.fill_uniform(&mut source, &mut a);
92
93 // Maps PolyRNS to [BigInt]
94 let mut coeffs_a: Vec<BigInt> = (0..a.n()).map(|i| BigInt::from(i)).collect();
95 ring_rns
96 .at_level(a.level())
97 .to_bigint_inplace(&a, 1, &mut coeffs_a);
98
99 // Performs c = intt(ntt(a) / q_level)
100 if NTT {
101 ring_rns.ntt_inplace::<false>(&mut a);
102 }
103
104 ring_rns.div_by_last_modulus::<ROUND, NTT>(&a, &mut buf, &mut c);
105
106 if NTT {
107 ring_rns.at_level(c.level()).intt_inplace::<false>(&mut c);
108 }
109
110 // Exports c to coeffs_c
111 let mut coeffs_c = vec![BigInt::from(0); c.n()];
112 ring_rns
113 .at_level(c.level())
114 .to_bigint_inplace(&c, 1, &mut coeffs_c);
115
116 // Performs floor division on a
117 let scalar_big = BigInt::from(ring_rns.0[ring_rns.level()].modulus.q);
118 coeffs_a.iter_mut().for_each(|a| {
119 if ROUND {
120 *a = a.div_round(&scalar_big);
121 } else {
122 *a = a.div_floor(&scalar_big);
123 }
124 });
125
126 izip!(coeffs_a, coeffs_c).for_each(|(a, b)| assert_eq!(a, b));
127}
128
129fn test_div_by_last_modulus_inplace<const ROUND: bool, const NTT: bool>(ring_rns: &RingRNS<u64>) {
130 let seed: [u8; 32] = [0; 32];
131 let mut source: Source = Source::new(seed);
132
133 let mut a: PolyRNS<u64> = ring_rns.new_polyrns();
134 let mut buf: [Poly<u64>; 2] = [ring_rns.new_poly(), ring_rns.new_poly()];
135
136 // Allocates a random PolyRNS
137 ring_rns.fill_uniform(&mut source, &mut a);
138
139 // Maps PolyRNS to [BigInt]
140 let mut coeffs_a: Vec<BigInt> = (0..a.n()).map(|i| BigInt::from(i)).collect();
141 ring_rns
142 .at_level(a.level())
143 .to_bigint_inplace(&a, 1, &mut coeffs_a);
144
145 // Performs c = intt(ntt(a) / q_level)
146 if NTT {
147 ring_rns.ntt_inplace::<false>(&mut a);
148 }
149
150 ring_rns.div_by_last_modulus_inplace::<ROUND, NTT>(&mut buf, &mut a);
151
152 if NTT {
153 ring_rns
154 .at_level(a.level() - 1)
155 .intt_inplace::<false>(&mut a);
156 }
157
158 // Exports c to coeffs_c
159 let mut coeffs_c = vec![BigInt::from(0); a.n()];
160 ring_rns
161 .at_level(a.level() - 1)
162 .to_bigint_inplace(&a, 1, &mut coeffs_c);
163
164 // Performs floor division on a
165 let scalar_big = BigInt::from(ring_rns.0[ring_rns.level()].modulus.q);
166 coeffs_a.iter_mut().for_each(|a| {
167 if ROUND {
168 *a = a.div_round(&scalar_big);
169 } else {
170 *a = a.div_floor(&scalar_big);
171 }
172 });
173
174 izip!(coeffs_a, coeffs_c).for_each(|(a, b)| assert_eq!(a, b));
175}
176
177fn test_div_by_last_moduli<const ROUND: bool, const NTT: bool>(ring_rns: &RingRNS<u64>) {
178 let seed: [u8; 32] = [0; 32];
179 let mut source: Source = Source::new(seed);
180
181 let nb_moduli_dropped: usize = ring_rns.level();
182
183 let mut a: PolyRNS<u64> = ring_rns.new_polyrns();
184 let mut buf0: [Poly<u64>; 2] = [ring_rns.new_poly(), ring_rns.new_poly()];
185 let mut buf1: PolyRNS<u64> = ring_rns.new_polyrns();
186 let mut c: PolyRNS<u64> = ring_rns
187 .at_level(ring_rns.level() - nb_moduli_dropped)
188 .new_polyrns();
189
190 // Allocates a random PolyRNS
191 ring_rns.fill_uniform(&mut source, &mut a);
192
193 // Maps PolyRNS to [BigInt]
194 let mut coeffs_a: Vec<BigInt> = (0..a.n()).map(|i| BigInt::from(i)).collect();
195 ring_rns
196 .at_level(a.level())
197 .to_bigint_inplace(&a, 1, &mut coeffs_a);
198
199 // Performs c = intt(ntt(a) / q_level)
200 if NTT {
201 ring_rns.ntt_inplace::<false>(&mut a);
202 }
203
204 ring_rns.div_by_last_moduli::<ROUND, NTT>(nb_moduli_dropped, &a, &mut buf0, &mut buf1, &mut c);
205
206 if NTT {
207 ring_rns.at_level(c.level()).intt_inplace::<false>(&mut c);
208 }
209
210 // Exports c to coeffs_c
211 let mut coeffs_c = vec![BigInt::from(0); a.n()];
212 ring_rns
213 .at_level(c.level())
214 .to_bigint_inplace(&c, 1, &mut coeffs_c);
215
216 // Performs floor division on a
217 let mut scalar_big = BigInt::from(1);
218 (0..nb_moduli_dropped)
219 .for_each(|i| scalar_big *= BigInt::from(ring_rns.0[ring_rns.level() - i].modulus.q));
220 coeffs_a.iter_mut().for_each(|a| {
221 if ROUND {
222 *a = a.div_round(&scalar_big);
223 } else {
224 *a = a.div_floor(&scalar_big);
225 }
226 });
227
228 izip!(coeffs_a, coeffs_c).for_each(|(a, b)| assert_eq!(a, b));
229}
230
231fn test_div_by_last_moduli_inplace<const ROUND: bool, const NTT: bool>(ring_rns: &RingRNS<u64>) {
232 let seed: [u8; 32] = [0; 32];
233 let mut source: Source = Source::new(seed);
234
235 let nb_moduli_dropped: usize = ring_rns.level();
236
237 let mut a: PolyRNS<u64> = ring_rns.new_polyrns();
238 let mut buf0: [Poly<u64>; 2] = [ring_rns.new_poly(), ring_rns.new_poly()];
239 let mut buf1: PolyRNS<u64> = ring_rns.new_polyrns();
240
241 // Allocates a random PolyRNS
242 ring_rns.fill_uniform(&mut source, &mut a);
243
244 // Maps PolyRNS to [BigInt]
245 let mut coeffs_a: Vec<BigInt> = (0..a.n()).map(|i| BigInt::from(i)).collect();
246 ring_rns
247 .at_level(a.level())
248 .to_bigint_inplace(&a, 1, &mut coeffs_a);
249
250 // Performs c = intt(ntt(a) / q_level)
251 if NTT {
252 ring_rns.ntt_inplace::<false>(&mut a);
253 }
254
255 ring_rns.div_by_last_moduli_inplace::<ROUND, NTT>(
256 nb_moduli_dropped,
257 &mut buf0,
258 &mut buf1,
259 &mut a,
260 );
261
262 if NTT {
263 ring_rns
264 .at_level(a.level() - nb_moduli_dropped)
265 .intt_inplace::<false>(&mut a);
266 }
267
268 // Exports c to coeffs_c
269 let mut coeffs_c = vec![BigInt::from(0); a.n()];
270 ring_rns
271 .at_level(a.level() - nb_moduli_dropped)
272 .to_bigint_inplace(&a, 1, &mut coeffs_c);
273
274 // Performs floor division on a
275 let mut scalar_big = BigInt::from(1);
276 (0..nb_moduli_dropped)
277 .for_each(|i| scalar_big *= BigInt::from(ring_rns.0[ring_rns.level() - i].modulus.q));
278 coeffs_a.iter_mut().for_each(|a| {
279 if ROUND {
280 *a = a.div_round(&scalar_big);
281 } else {
282 *a = a.div_floor(&scalar_big);
283 }
284 });
285
286 izip!(coeffs_a, coeffs_c).for_each(|(a, b)| assert_eq!(a, b));
287}