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