qfall_math/integer/poly_over_z/arithmetic/
add.rs

1// Copyright © 2023 Phil Milewski
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 [`PolyOverZ`] values.
10
11use super::super::PolyOverZ;
12use crate::integer::Z;
13use crate::macros::arithmetics::{
14    arithmetic_assign_trait_borrowed_to_owned, arithmetic_trait_borrowed_to_owned,
15    arithmetic_trait_mixed_borrowed_owned,
16};
17use flint_sys::{fmpz::fmpz_add, fmpz_poly::fmpz_poly_add};
18use std::ops::{Add, AddAssign};
19
20impl AddAssign<&PolyOverZ> for PolyOverZ {
21    /// Computes the addition of `self` and `other` reusing
22    /// the memory of `self`.
23    ///
24    /// Parameters:
25    /// - `other`: specifies the polynomial to add to `self`
26    ///
27    /// Returns the sum of both polynomials as a [`PolyOverZ`].
28    ///
29    /// # Examples
30    /// ```
31    /// use qfall_math::integer::PolyOverZ;
32    /// use std::str::FromStr;
33    ///
34    /// let mut a = PolyOverZ::from_str("3  1 2 -3").unwrap();
35    /// let b = PolyOverZ::from_str("5  1 2 -3 0 8").unwrap();
36    ///
37    /// a += &b;
38    /// a += b;
39    /// ```
40    fn add_assign(&mut self, other: &Self) {
41        unsafe { fmpz_poly_add(&mut self.poly, &self.poly, &other.poly) };
42    }
43}
44
45arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, PolyOverZ, PolyOverZ);
46
47impl Add for &PolyOverZ {
48    type Output = PolyOverZ;
49    /// Implements the [`Add`] trait for two [`PolyOverZ`] values.
50    /// [`Add`] is implemented for any combination of [`PolyOverZ`] and borrowed [`PolyOverZ`].
51    ///
52    /// Parameters:
53    /// - `other`: specifies the value to add to `self`
54    ///
55    /// Returns the sum of both polynomials as a [`PolyOverZ`].
56    ///
57    /// # Examples
58    /// ```
59    /// use qfall_math::integer::PolyOverZ;
60    /// use std::str::FromStr;
61    ///
62    /// let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
63    /// let b: PolyOverZ = PolyOverZ::from_str("5  1 2 -3 0 8").unwrap();
64    ///
65    /// let c: PolyOverZ = &a + &b;
66    /// let d: PolyOverZ = a + b;
67    /// let e: PolyOverZ = &c + d;
68    /// let f: PolyOverZ = c + &e;
69    /// ```
70    fn add(self, other: Self) -> Self::Output {
71        let mut out = PolyOverZ::default();
72        unsafe {
73            fmpz_poly_add(&mut out.poly, &self.poly, &other.poly);
74        }
75        out
76    }
77}
78
79arithmetic_trait_borrowed_to_owned!(Add, add, PolyOverZ, PolyOverZ, PolyOverZ);
80arithmetic_trait_mixed_borrowed_owned!(Add, add, PolyOverZ, PolyOverZ, PolyOverZ);
81
82impl Add<&Z> for &PolyOverZ {
83    type Output = PolyOverZ;
84    /// Implements the [`Add`] trait for [`PolyOverZ`] and [`Z`] values.
85    /// [`Add`] is implemented for any combination of owned and borrowed values.
86    ///
87    /// Parameters:
88    ///  - `other`: specifies the [`Z`] to add to `self`
89    ///
90    /// Returns the sum of both as a [`PolyOverZ`].
91    ///
92    /// # Examples
93    /// ```
94    /// use qfall_math::integer::PolyOverZ;
95    /// use qfall_math::integer::Z;
96    /// use std::str::FromStr;
97    ///
98    /// let b: PolyOverZ = PolyOverZ::from_str("4  5 1 2 3").unwrap();
99    /// let a: Z = Z::from(42);
100    ///
101    /// let c: PolyOverZ = &a + &b;
102    /// let d: PolyOverZ = a + b;
103    /// let e: PolyOverZ = d + &Z::from(42);
104    /// let f: PolyOverZ = &e + Z::from(42);
105    /// ```
106    fn add(self, other: &Z) -> Self::Output {
107        // check if the first coefficient of the polynomial is initiated and
108        // can be addressed
109        if self.is_zero() {
110            PolyOverZ::from(other)
111        } else {
112            let out = self.clone();
113            unsafe {
114                fmpz_add(out.poly.coeffs, &other.value, out.poly.coeffs);
115            }
116            out
117        }
118    }
119}
120
121arithmetic_trait_borrowed_to_owned!(Add, add, PolyOverZ, Z, PolyOverZ);
122arithmetic_trait_mixed_borrowed_owned!(Add, add, PolyOverZ, Z, PolyOverZ);
123
124#[cfg(test)]
125mod test_add_assign {
126    use super::PolyOverZ;
127    use std::str::FromStr;
128
129    /// Ensure that `add_assign` works for small numbers.
130    #[test]
131    fn correct_small() {
132        let mut a = PolyOverZ::from_str("3  -1 2 -3").unwrap();
133        let b = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
134        let cmp = PolyOverZ::from_str("5  0 4 2 1 2").unwrap();
135
136        a += b;
137
138        assert_eq!(cmp, a);
139    }
140
141    /// Ensure that `add_assign` works for large numbers.
142    #[test]
143    fn correct_large() {
144        let mut a =
145            PolyOverZ::from_str(&format!("3  {} {} {}", u32::MAX, i32::MIN, i32::MAX)).unwrap();
146        let b = PolyOverZ::from_str(&format!("2  {} {}", u32::MAX, i32::MAX)).unwrap();
147        let cmp = PolyOverZ::from_str(&format!("3  {} -1 {}", u64::from(u32::MAX) * 2, i32::MAX))
148            .unwrap();
149
150        a += b;
151
152        assert_eq!(cmp, a);
153    }
154
155    /// Ensure that `add_assign` is available for all types.
156    #[test]
157    fn availability() {
158        let mut a = PolyOverZ::from_str("3  1 2 -3").unwrap();
159        let b = PolyOverZ::from_str("3  -1 -2 3").unwrap();
160
161        a += &b;
162        a += b;
163    }
164}
165
166#[cfg(test)]
167mod test_add {
168    use super::PolyOverZ;
169    use std::str::FromStr;
170
171    /// Testing addition for two [`PolyOverZ`]
172    #[test]
173    fn add() {
174        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
175        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
176        let c: PolyOverZ = a + b;
177        assert_eq!(c, PolyOverZ::from_str("5  2 4 2 1 2").unwrap());
178    }
179
180    /// Testing addition for two borrowed [`PolyOverZ`]
181    #[test]
182    fn add_borrow() {
183        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
184        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
185        let c: PolyOverZ = &a + &b;
186        assert_eq!(c, PolyOverZ::from_str("5  2 4 2 1 2").unwrap());
187    }
188
189    /// Testing addition for borrowed [`PolyOverZ`] and [`PolyOverZ`]
190    #[test]
191    fn add_first_borrowed() {
192        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
193        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
194        let c: PolyOverZ = &a + b;
195        assert_eq!(c, PolyOverZ::from_str("5  2 4 2 1 2").unwrap());
196    }
197
198    /// Testing addition for [`PolyOverZ`] and borrowed P[`PolyOverZ`]
199    #[test]
200    fn add_second_borrowed() {
201        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
202        let b: PolyOverZ = PolyOverZ::from_str("5  1 2 5 1 2").unwrap();
203        let c: PolyOverZ = a + &b;
204        assert_eq!(c, PolyOverZ::from_str("5  2 4 2 1 2").unwrap());
205    }
206
207    /// Testing addition with eliminating coefficients
208    #[test]
209    fn add_eliminating_coefficients() {
210        let a: PolyOverZ = PolyOverZ::from_str("3  1 2 -3").unwrap();
211        let b: PolyOverZ = PolyOverZ::from_str("3  -1 -2 3").unwrap();
212        let c: PolyOverZ = a + b;
213        assert_eq!(c, PolyOverZ::default());
214    }
215
216    /// Testing addition for large [`PolyOverZ`]
217    #[test]
218    fn add_large_numbers() {
219        let a: PolyOverZ =
220            PolyOverZ::from_str(&format!("3  {} {} {}", u32::MAX, i32::MIN, i32::MAX)).unwrap();
221        let b: PolyOverZ = PolyOverZ::from_str(&format!("2  {} {}", u32::MAX, i32::MAX)).unwrap();
222        let c: PolyOverZ = a + b;
223        assert_eq!(
224            c,
225            PolyOverZ::from_str(&format!("3  {} -1 {}", u64::from(u32::MAX) * 2, i32::MAX))
226                .unwrap()
227        );
228    }
229}
230
231#[cfg(test)]
232mod test_add_between_z_and_poly_over_z {
233    use crate::integer::PolyOverZ;
234    use crate::integer::Z;
235    use std::str::FromStr;
236
237    /// Testing addition for [`Z`] and [`PolyOverZ`]
238    #[test]
239    fn add() {
240        let a: PolyOverZ = PolyOverZ::from_str("4  1 1 2 3").unwrap();
241        let b: Z = Z::from(9);
242        let c: PolyOverZ = a + b;
243        assert_eq!(c, PolyOverZ::from_str("4  10 1 2 3").unwrap());
244    }
245
246    /// Testing addition for both borrowed [`Z`] and [`PolyOverZ`]
247    #[test]
248    fn add_borrow() {
249        let a: PolyOverZ = PolyOverZ::from_str("4  1 1 2 3").unwrap();
250        let b: Z = Z::from(9);
251        let c: PolyOverZ = &a + &b;
252        assert_eq!(c, PolyOverZ::from_str("4  10 1 2 3").unwrap());
253    }
254
255    /// Testing addition for borrowed [`Z`] and [`PolyOverZ`]
256    #[test]
257    fn add_first_borrowed() {
258        let a: PolyOverZ = PolyOverZ::from_str("4  1 1 2 3").unwrap();
259        let b: Z = Z::from(9);
260        let c: PolyOverZ = &a + b;
261        assert_eq!(c, PolyOverZ::from_str("4  10 1 2 3").unwrap());
262    }
263
264    /// Testing addition for [`Z`] and borrowed [`PolyOverZ`]
265    #[test]
266    fn add_second_borrowed() {
267        let a: PolyOverZ = PolyOverZ::from_str("4  1 1 2 3").unwrap();
268        let b: Z = Z::from(9);
269        let c: PolyOverZ = a + &b;
270        assert_eq!(c, PolyOverZ::from_str("4  10 1 2 3").unwrap());
271    }
272
273    /// Testing addition for large numbers
274    #[test]
275    fn add_large_numbers() {
276        let a: PolyOverZ =
277            PolyOverZ::from_str(&format!("3  {} {} {}", i64::MAX, u64::MAX, i32::MAX)).unwrap();
278        let b: Z = Z::from(i64::MAX);
279        let c: PolyOverZ = a + b;
280        assert_eq!(
281            c,
282            PolyOverZ::from_str(&format!("3  {} {} {}", u64::MAX - 1, u64::MAX, i32::MAX)).unwrap()
283        );
284    }
285
286    /// Testing addition for an empty polynomial and a zero [`Z`]
287    #[test]
288    fn add_zero() {
289        let a: PolyOverZ = PolyOverZ::default();
290        let b: Z = Z::from(15);
291        let c: PolyOverZ = a + b;
292        assert_eq!(c, PolyOverZ::from_str("1  15").unwrap());
293
294        let d: PolyOverZ = PolyOverZ::from_str("1  15").unwrap();
295        let e: Z = Z::ZERO;
296        let f: PolyOverZ = d + e;
297        assert_eq!(f, PolyOverZ::from_str("1  15").unwrap());
298    }
299}