qfall_math/integer_mod_q/poly_over_zq/arithmetic/
add.rs

1// Copyright © 2023 Phil Milewski, Marcel Luca Schmidt
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! Implementation of the [`Add`] trait for [`PolyOverZq`] values.
10
11use super::super::PolyOverZq;
12use crate::{
13    error::MathError,
14    integer::PolyOverZ,
15    macros::arithmetics::{
16        arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
17        arithmetic_trait_mixed_borrowed_owned, arithmetic_trait_reverse,
18    },
19    traits::CompareBase,
20};
21use flint_sys::fmpz_mod_poly::fmpz_mod_poly_add;
22use std::{
23    ops::{Add, AddAssign},
24    str::FromStr,
25};
26
27impl AddAssign<&PolyOverZq> for PolyOverZq {
28    /// Computes the addition of `self` and `other` reusing
29    /// the memory of `self`.
30    /// [`AddAssign`] can be used on [`PolyOverZq`] in combination with
31    /// [`PolyOverZq`] and [`PolyOverZ`].
32    ///
33    /// Parameters:
34    /// - `other`: specifies the polynomial to add to `self`
35    ///
36    /// Returns the sum of both polynomials modulo `q` as a [`PolyOverZq`].
37    ///
38    /// # Examples
39    /// ```
40    /// use qfall_math::{integer_mod_q::PolyOverZq, integer::PolyOverZ};
41    /// use std::str::FromStr;
42    ///
43    /// let mut a = PolyOverZq::from_str("3  1 2 3 mod 7").unwrap();
44    /// let b = PolyOverZq::from_str("5  1 2 -3 0 4 mod 7").unwrap();
45    /// let c = PolyOverZ::from_str("4  -1 2 5 3").unwrap();
46    ///
47    /// a += &b;
48    /// a += b;
49    /// a += &c;
50    /// a += c;
51    /// ```
52    ///
53    /// # Panics ...
54    /// - if the moduli of both [`PolyOverZq`] mismatch.
55    fn add_assign(&mut self, other: &Self) {
56        if !self.compare_base(other) {
57            panic!("{}", self.call_compare_base_error(other).unwrap());
58        }
59
60        unsafe {
61            fmpz_mod_poly_add(
62                &mut self.poly,
63                &self.poly,
64                &other.poly,
65                self.modulus.get_fmpz_mod_ctx_struct(),
66            )
67        };
68    }
69}
70impl AddAssign<&PolyOverZ> for PolyOverZq {
71    /// Documentation at [`PolyOverZq::add_assign`].
72    fn add_assign(&mut self, other: &PolyOverZ) {
73        let other = PolyOverZq::from((other, self.get_mod()));
74
75        self.add_assign(&other);
76    }
77}
78
79arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, PolyOverZq, PolyOverZq);
80arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, PolyOverZq, PolyOverZ);
81
82impl Add for &PolyOverZq {
83    type Output = PolyOverZq;
84    /// Implements the [`Add`] trait for two [`PolyOverZq`] values.
85    /// [`Add`] is implemented for any combination of [`PolyOverZq`] and borrowed [`PolyOverZq`].
86    ///
87    /// Parameters:
88    /// - `other`: specifies the polynomial to add to `self`
89    ///
90    /// Returns the sum of both polynomials as a [`PolyOverZq`].
91    ///
92    /// # Examples
93    /// ```
94    /// use qfall_math::integer_mod_q::PolyOverZq;
95    /// use std::str::FromStr;
96    ///
97    /// let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
98    /// let b: PolyOverZq = PolyOverZq::from_str("3  5 1 1 mod 7").unwrap();
99    ///
100    /// let c: PolyOverZq = &a + &b;
101    /// let d: PolyOverZq = a + b;
102    /// let e: PolyOverZq = &c + d;
103    /// let f: PolyOverZq = c + &e;
104    /// ```
105    ///
106    /// # Panics ...
107    /// - if the moduli of both [`PolyOverZq`] mismatch.
108    fn add(self, other: Self) -> Self::Output {
109        self.add_safe(other).unwrap()
110    }
111}
112
113arithmetic_trait_borrowed_to_owned!(Add, add, PolyOverZq, PolyOverZq, PolyOverZq);
114arithmetic_trait_mixed_borrowed_owned!(Add, add, PolyOverZq, PolyOverZq, PolyOverZq);
115
116impl Add<&PolyOverZ> for &PolyOverZq {
117    type Output = PolyOverZq;
118    /// Implements the [`Add`] trait for [`PolyOverZq`] and [`PolyOverZ`].
119    /// [`Add`] is implemented for any combination of owned and borrowed values.
120    ///
121    /// Parameters:
122    /// - `other`: specifies the polynomial to add to `self`
123    ///
124    /// Returns the addition of both polynomials as a [`PolyOverZq`].
125    ///
126    /// # Examples
127    /// ```
128    /// use qfall_math::integer_mod_q::PolyOverZq;
129    /// use qfall_math::integer::PolyOverZ;
130    /// use std::str::FromStr;
131    ///
132    /// let a = PolyOverZq::from_str("4  -1 0 1 1 mod 17").unwrap();
133    /// let b = PolyOverZ::from_str("4  2 0 3 1").unwrap();
134    ///
135    /// let c: PolyOverZq = &a + &b;
136    /// ```
137    fn add(self, other: &PolyOverZ) -> Self::Output {
138        let mut out = PolyOverZq::from(&self.modulus);
139        unsafe {
140            fmpz_mod_poly_add(
141                &mut out.poly,
142                &self.poly,
143                &PolyOverZq::from((other, &self.modulus)).poly,
144                self.modulus.get_fmpz_mod_ctx_struct(),
145            );
146        }
147        out
148    }
149}
150
151arithmetic_trait_reverse!(Add, add, PolyOverZ, PolyOverZq, PolyOverZq);
152
153arithmetic_trait_borrowed_to_owned!(Add, add, PolyOverZq, PolyOverZ, PolyOverZq);
154arithmetic_trait_borrowed_to_owned!(Add, add, PolyOverZ, PolyOverZq, PolyOverZq);
155arithmetic_trait_mixed_borrowed_owned!(Add, add, PolyOverZq, PolyOverZ, PolyOverZq);
156arithmetic_trait_mixed_borrowed_owned!(Add, add, PolyOverZ, PolyOverZq, PolyOverZq);
157
158impl PolyOverZq {
159    /// Implements addition for two [`PolyOverZq`] values.
160    ///
161    /// Parameters:
162    /// - `other`: specifies the polynomial to add to `self`
163    ///
164    /// Returns the sum of both polynomials as a [`PolyOverZq`] or an error if the moduli
165    /// mismatch.
166    ///
167    /// # Examples
168    /// ```
169    /// use qfall_math::integer_mod_q::PolyOverZq;
170    /// use std::str::FromStr;
171    ///
172    /// let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
173    /// let b: PolyOverZq = PolyOverZq::from_str("3  5 1 1 mod 7").unwrap();
174    ///
175    /// let c: PolyOverZq = a.add_safe(&b).unwrap();
176    /// ```
177    /// # Errors and Failures
178    /// - Returns a [`MathError`] of type [`MathError::MismatchingModulus`] if the moduli of
179    ///   both [`PolyOverZq`] mismatch.
180    pub fn add_safe(&self, other: &Self) -> Result<PolyOverZq, MathError> {
181        if !self.compare_base(other) {
182            return Err(self.call_compare_base_error(other).unwrap());
183        }
184        let mut out = PolyOverZq::from_str(&format!("0 mod {}", self.modulus)).unwrap();
185        unsafe {
186            fmpz_mod_poly_add(
187                &mut out.poly,
188                &self.poly,
189                &other.poly,
190                self.modulus.get_fmpz_mod_ctx_struct(),
191            );
192        }
193        Ok(out)
194    }
195}
196
197#[cfg(test)]
198mod test_add_assign {
199    use super::PolyOverZq;
200    use crate::integer::PolyOverZ;
201    use std::str::FromStr;
202
203    /// Ensure that `add_assign` works for small numbers.
204    #[test]
205    fn correct_small() {
206        let mut a = PolyOverZq::from_str("3  6 2 -3 mod 7").unwrap();
207        let b = PolyOverZq::from_str("5  1 2 5 1 2 mod 7").unwrap();
208        let cmp = PolyOverZq::from_str("5  0 4 2 1 2 mod 7").unwrap();
209
210        a += b;
211
212        assert_eq!(cmp, a);
213    }
214
215    /// Ensure that `add_assign` works for large numbers.
216    #[test]
217    fn correct_large() {
218        let mut a = PolyOverZq::from_str(&format!(
219            "3  {} {} {} mod {}",
220            u32::MAX,
221            i32::MIN,
222            i32::MAX,
223            u64::MAX
224        ))
225        .unwrap();
226        let b = PolyOverZq::from_str(&format!("2  {} {} mod {}", u32::MAX, i32::MAX, u64::MAX))
227            .unwrap();
228        let cmp = PolyOverZq::from_str(&format!(
229            "3  {} -1 {} mod {}",
230            u64::from(u32::MAX) * 2,
231            i32::MAX,
232            u64::MAX
233        ))
234        .unwrap();
235
236        a += b;
237
238        assert_eq!(cmp, a);
239    }
240
241    /// Ensure that `add_assign` is available for all types.
242    #[test]
243    fn availability() {
244        let mut a = PolyOverZq::from_str("3  1 2 -3 mod 5").unwrap();
245        let b = PolyOverZq::from_str("3  -1 -2 3 mod 5").unwrap();
246        let c = PolyOverZ::from_str("2  -2 2").unwrap();
247
248        a += &b;
249        a += b;
250        a += &c;
251        a += c;
252    }
253
254    /// Ensures that mismatching moduli result in a panic.
255    #[test]
256    #[should_panic]
257    fn mismatching_moduli() {
258        let mut a: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
259        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 8").unwrap();
260
261        a += b;
262    }
263}
264
265#[cfg(test)]
266mod test_add {
267    use super::PolyOverZq;
268    use std::str::FromStr;
269
270    /// Testing addition for two [`PolyOverZq`]
271    #[test]
272    fn add() {
273        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
274        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
275        let c: PolyOverZq = a + b;
276        assert_eq!(c, PolyOverZq::from_str("3  4 1 2 mod 7").unwrap());
277    }
278
279    /// Testing addition for two borrowed [`PolyOverZq`]
280    #[test]
281    fn add_borrow() {
282        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
283        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
284        let c: PolyOverZq = &a + &b;
285        assert_eq!(c, PolyOverZq::from_str("3  4 1 2 mod 7").unwrap());
286    }
287
288    /// Testing addition for borrowed [`PolyOverZq`] and [`PolyOverZq`]
289    #[test]
290    fn add_first_borrowed() {
291        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
292        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
293        let c: PolyOverZq = &a + b;
294        assert_eq!(c, PolyOverZq::from_str("3  4 1 2 mod 7").unwrap());
295    }
296
297    /// Testing addition for [`PolyOverZq`] and borrowed [`PolyOverZq`]
298    #[test]
299    fn add_second_borrowed() {
300        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
301        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
302        let c: PolyOverZq = a + &b;
303        assert_eq!(c, PolyOverZq::from_str("3  4 1 2 mod 7").unwrap());
304    }
305
306    /// Testing addition of [`PolyOverZq`] is reducing the polynomial
307    #[test]
308    fn add_reduce() {
309        let a: PolyOverZq = PolyOverZq::from_str("3  2 4 1 mod 7").unwrap();
310        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 6 mod 7").unwrap();
311        let c: PolyOverZq = a + b;
312        assert_eq!(c, PolyOverZq::from_str("2  4 1 mod 7").unwrap());
313    }
314
315    /// Testing addition for large [`PolyOverZq`]
316    #[test]
317    fn add_large_numbers() {
318        let a: PolyOverZq = PolyOverZq::from_str(&format!(
319            "3  -{} 4 {} mod {}",
320            u64::MAX,
321            i64::MIN,
322            u64::MAX - 58
323        ))
324        .unwrap();
325        let b: PolyOverZq = PolyOverZq::from_str(&format!(
326            "3  {} 4 {} mod {}",
327            i64::MIN,
328            i64::MIN,
329            u64::MAX - 58
330        ))
331        .unwrap();
332        let c: PolyOverZq = a + b;
333        assert!(
334            c == PolyOverZq::from_str(&format!(
335                "3  -{} 8 {} mod {}",
336                i128::from(i64::MAX) + 59,
337                -59,
338                u64::MAX - 58
339            ))
340            .unwrap()
341        );
342    }
343
344    /// Testing addition for [`PolyOverZq`] with different moduli does not work
345    #[test]
346    #[should_panic]
347    fn add_mismatching_modulus() {
348        let a: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
349        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 8").unwrap();
350        let _c: PolyOverZq = a + b;
351    }
352
353    /// Testing whether add_safe throws an error for mismatching moduli
354    #[test]
355    fn add_safe_is_err() {
356        let a: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 7").unwrap();
357        let b: PolyOverZq = PolyOverZq::from_str("3  -5 4 1 mod 11").unwrap();
358        assert!(&a.add_safe(&b).is_err());
359    }
360}
361
362#[cfg(test)]
363mod test_mul_poly_over_z {
364    use super::PolyOverZq;
365    use crate::integer::PolyOverZ;
366    use std::str::FromStr;
367
368    /// Checks if polynomial addition works fine for both borrowed
369    #[test]
370    fn borrowed_correctness() {
371        let poly_1 = PolyOverZq::from_str(&format!("1  {} mod {}", i64::MAX, u64::MAX)).unwrap();
372        let poly_2 = PolyOverZ::from_str("2  1 2").unwrap();
373        let poly_cmp =
374            PolyOverZq::from_str(&format!("2  {} 2 mod {}", i64::MAX as u64 + 1, u64::MAX))
375                .unwrap();
376
377        let poly_1 = &poly_1 + &poly_2;
378
379        assert_eq!(poly_cmp, poly_1);
380    }
381
382    /// Checks if addition works fine for different types
383    #[test]
384    fn availability() {
385        let poly = PolyOverZq::from_str("3  1 2 3 mod 17").unwrap();
386        let z = PolyOverZ::from(2);
387
388        _ = poly.clone() + z.clone();
389        _ = z.clone() + poly.clone();
390        _ = &poly + &z;
391        _ = &z + &poly;
392        _ = &poly + z.clone();
393        _ = z.clone() + &poly;
394        _ = &z + poly.clone();
395        _ = poly.clone() + &z;
396    }
397}