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