qfall_math/integer_mod_q/ntt_polynomial_ring_zq/arithmetic/
mul.rs1use crate::{
12 integer::Z,
13 integer_mod_q::NTTPolynomialRingZq,
14 macros::arithmetics::{
15 arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
16 arithmetic_trait_mixed_borrowed_owned,
17 },
18 traits::CompareBase,
19};
20use flint_sys::fmpz_mod::fmpz_mod_mul;
21use std::ops::{Mul, MulAssign};
22
23impl Mul for &NTTPolynomialRingZq {
24 type Output = NTTPolynomialRingZq;
25
26 fn mul(self, other: Self) -> Self::Output {
50 if !self.compare_base(other) {
51 panic!("{}", self.call_compare_base_error(other).unwrap());
52 }
53 let binding = &self.modulus.get_q_as_modulus();
54 let mod_q = binding.get_fmpz_mod_ctx_struct();
55
56 let mut res = Vec::with_capacity(self.poly.capacity());
57 for i in 0..self.poly.len() {
58 let mut z_i = Z::ZERO;
59 unsafe {
60 fmpz_mod_mul(
61 &mut z_i.value,
62 &self.poly[i].value,
63 &other.poly[i].value,
64 mod_q,
65 );
66 }
67 res.push(z_i)
68 }
69 Self::Output {
70 poly: res,
71 modulus: self.modulus.clone(),
72 }
73 }
74}
75
76arithmetic_trait_borrowed_to_owned!(
77 Mul,
78 mul,
79 NTTPolynomialRingZq,
80 NTTPolynomialRingZq,
81 NTTPolynomialRingZq
82);
83arithmetic_trait_mixed_borrowed_owned!(
84 Mul,
85 mul,
86 NTTPolynomialRingZq,
87 NTTPolynomialRingZq,
88 NTTPolynomialRingZq
89);
90
91impl MulAssign<&NTTPolynomialRingZq> for NTTPolynomialRingZq {
92 fn mul_assign(&mut self, other: &NTTPolynomialRingZq) {
115 if !self.compare_base(other) {
116 panic!("{}", self.call_compare_base_error(other).unwrap());
117 }
118 let binding = &self.modulus.get_q_as_modulus();
119 let mod_q = binding.get_fmpz_mod_ctx_struct();
120
121 for i in 0..self.poly.len() {
122 unsafe {
123 fmpz_mod_mul(
124 &mut self.poly[i].value,
125 &self.poly[i].value,
126 &other.poly[i].value,
127 mod_q,
128 );
129 }
130 }
131 }
132}
133
134arithmetic_assign_trait_borrowed_to_owned!(
135 MulAssign,
136 mul_assign,
137 NTTPolynomialRingZq,
138 NTTPolynomialRingZq
139);
140
141#[cfg(test)]
142mod test_mul {
143 use crate::{
144 integer_mod_q::{
145 ModulusPolynomialRingZq, NTTPolynomialRingZq, PolyOverZq, PolynomialRingZq,
146 },
147 traits::SetCoefficient,
148 };
149 use std::{ops::Mul, str::FromStr};
150
151 #[test]
154 fn test_dilithium_params() {
155 let n = 256;
156 let modulus = 2_i64.pow(23) - 2_i64.pow(13) + 1;
157
158 let mut mod_poly = PolyOverZq::from(modulus);
159 mod_poly.set_coeff(0, 1).unwrap();
160 mod_poly.set_coeff(n, 1).unwrap();
161
162 let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
163
164 polynomial_modulus.set_ntt_unchecked(1753);
165
166 let p1 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
167 let p2 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
168
169 let ntt1 = NTTPolynomialRingZq::from(&p1);
170 let ntt2 = NTTPolynomialRingZq::from(&p2);
171
172 let ntt_res = ntt1.mul(ntt2);
173
174 assert_eq!(&p1 * &p2, PolynomialRingZq::from(ntt_res))
175 }
176
177 #[test]
180 fn test_hawk1024_params() {
181 let n = 1024;
182 let modulus = 12289;
183
184 let mut mod_poly = PolyOverZq::from(modulus);
185 mod_poly.set_coeff(0, 1).unwrap();
186 mod_poly.set_coeff(n, 1).unwrap();
187
188 let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
189
190 polynomial_modulus.set_ntt_unchecked(1945);
191
192 let p1 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
193 let p2 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
194
195 let ntt1 = NTTPolynomialRingZq::from(&p1);
196 let ntt2 = NTTPolynomialRingZq::from(&p2);
197
198 let ntt_res = ntt1.mul(&ntt2);
199
200 assert_eq!(&p1 * &p2, PolynomialRingZq::from(ntt_res))
201 }
202
203 #[test]
205 #[should_panic]
206 fn different_moduli() {
207 let mut modulus0 = ModulusPolynomialRingZq::from_str("5 1 0 0 0 1 mod 257").unwrap();
208 modulus0.set_ntt_unchecked(64);
209 let mut modulus1 = ModulusPolynomialRingZq::from_str("6 1 0 0 0 0 1 mod 257").unwrap();
210 modulus1.set_ntt_unchecked(64);
211
212 let a = NTTPolynomialRingZq::sample_uniform(&modulus0);
213 let b = NTTPolynomialRingZq::sample_uniform(&modulus1);
214
215 let _ = a * b;
216 }
217}
218
219#[cfg(test)]
220mod test_mul_assign {
221 use crate::{
222 integer_mod_q::{
223 ModulusPolynomialRingZq, NTTPolynomialRingZq, PolyOverZq, PolynomialRingZq,
224 },
225 traits::SetCoefficient,
226 };
227 use std::{ops::MulAssign, str::FromStr};
228
229 #[test]
232 fn test_dilithium_params() {
233 let n = 256;
234 let modulus = 2_i64.pow(23) - 2_i64.pow(13) + 1;
235
236 let mut mod_poly = PolyOverZq::from(modulus);
237 mod_poly.set_coeff(0, 1).unwrap();
238 mod_poly.set_coeff(n, 1).unwrap();
239
240 let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
241
242 polynomial_modulus.set_ntt_unchecked(1753);
243
244 let p1 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
245 let p2 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
246
247 let mut ntt1 = NTTPolynomialRingZq::from(&p1);
248 let ntt2 = NTTPolynomialRingZq::from(&p2);
249
250 ntt1.mul_assign(ntt2);
251
252 assert_eq!(&p1 * &p2, PolynomialRingZq::from(ntt1))
253 }
254
255 #[test]
258 fn test_hawk1024_params() {
259 let n = 1024;
260 let modulus = 12289;
261
262 let mut mod_poly = PolyOverZq::from(modulus);
263 mod_poly.set_coeff(0, 1).unwrap();
264 mod_poly.set_coeff(n, 1).unwrap();
265
266 let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
267
268 polynomial_modulus.set_ntt_unchecked(1945);
269
270 let p1 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
271 let p2 = PolynomialRingZq::sample_uniform(&polynomial_modulus);
272
273 let mut ntt1 = NTTPolynomialRingZq::from(&p1);
274 let ntt2 = NTTPolynomialRingZq::from(&p2);
275
276 ntt1.mul_assign(ntt2);
277
278 assert_eq!(&p1 * &p2, PolynomialRingZq::from(ntt1))
279 }
280
281 #[test]
283 #[should_panic]
284 fn different_moduli() {
285 let mut modulus0 = ModulusPolynomialRingZq::from_str("5 1 0 0 0 1 mod 257").unwrap();
286 modulus0.set_ntt_unchecked(64);
287 let mut modulus1 = ModulusPolynomialRingZq::from_str("6 1 0 0 0 0 1 mod 257").unwrap();
288 modulus1.set_ntt_unchecked(64);
289
290 let mut a = NTTPolynomialRingZq::sample_uniform(&modulus0);
291 let b = NTTPolynomialRingZq::sample_uniform(&modulus1);
292
293 a *= b;
294 }
295}