qfall_math/rational/q/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 [`Q`] values.
10
11use super::super::Q;
12use crate::{
13    integer::Z,
14    macros::arithmetics::{
15        arithmetic_assign_between_types, arithmetic_assign_trait_borrowed_to_owned,
16        arithmetic_between_types, arithmetic_trait_borrowed_to_owned,
17        arithmetic_trait_mixed_borrowed_owned,
18    },
19};
20use flint_sys::fmpq::{fmpq_add, fmpq_add_fmpz, fmpq_add_si, fmpq_add_ui};
21use std::ops::{Add, AddAssign};
22
23impl AddAssign<&Q> for Q {
24    /// Computes the addition of `self` and `other` reusing
25    /// the memory of `self`.
26    /// [`AddAssign`] can be used on [`Q`] in combination with
27    /// [`Q`], [`Z`], [`f64`], [`f32`], [`i64`], [`i32`], [`i16`], [`i8`], [`u64`], [`u32`], [`u16`] and [`u8`].
28    ///
29    /// Parameters:
30    ///  - `other`: specifies the value to add to `self`
31    ///
32    /// Returns the sum of both rationals as a [`Q`].
33    ///
34    /// # Examples
35    /// ```
36    /// use qfall_math::{rational::Q, integer::Z};
37    ///
38    /// let mut a: Q = Q::from(42);
39    /// let b: Q = Q::from((-42, 2));
40    /// let c: Z = Z::from(5);
41    ///
42    /// a += &b;
43    /// a += b;
44    /// a += 5;
45    /// a += c;
46    /// a += 5.0;
47    /// ```
48    fn add_assign(&mut self, other: &Self) {
49        unsafe { fmpq_add(&mut self.value, &self.value, &other.value) };
50    }
51}
52impl AddAssign<&Z> for Q {
53    /// Documentation at [`Q::add_assign`].
54    fn add_assign(&mut self, other: &Z) {
55        unsafe { fmpq_add_fmpz(&mut self.value, &self.value, &other.value) };
56    }
57}
58impl AddAssign<i64> for Q {
59    /// Documentation at [`Q::add_assign`].
60    fn add_assign(&mut self, other: i64) {
61        unsafe { fmpq_add_si(&mut self.value, &self.value, other) };
62    }
63}
64impl AddAssign<u64> for Q {
65    /// Documentation at [`Q::add_assign`].
66    fn add_assign(&mut self, other: u64) {
67        unsafe { fmpq_add_ui(&mut self.value, &self.value, other) };
68    }
69}
70impl AddAssign<f64> for Q {
71    /// Documentation at [`Q::add_assign`].
72    fn add_assign(&mut self, other: f64) {
73        let other = Q::from(other);
74
75        unsafe { fmpq_add(&mut self.value, &self.value, &other.value) };
76    }
77}
78
79arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, Q, Q);
80arithmetic_assign_trait_borrowed_to_owned!(AddAssign, add_assign, Q, Z);
81arithmetic_assign_between_types!(AddAssign, add_assign, Q, i64, i32 i16 i8);
82arithmetic_assign_between_types!(AddAssign, add_assign, Q, u64, u32 u16 u8);
83arithmetic_assign_between_types!(AddAssign, add_assign, Q, f64, f32);
84
85impl Add for &Q {
86    type Output = Q;
87
88    /// Implements the [`Add`] trait for two [`Q`] values.
89    /// [`Add`] is implemented for any combination of [`Q`] and borrowed [`Q`].
90    ///
91    /// Parameters:
92    ///  - `other`: specifies the value to add to `self`
93    ///
94    /// Returns the sum of both rationals as a [`Q`].
95    ///
96    /// # Examples
97    /// ```
98    /// use qfall_math::rational::Q;
99    /// use std::str::FromStr;
100    ///
101    /// let a: Q = Q::from(42);
102    /// let b: Q = Q::from((-42, 2));
103    ///
104    /// let c: Q = &a + &b;
105    /// let d: Q = a + b;
106    /// let e: Q = &c + d;
107    /// let f: Q = c + &e;
108    /// ```
109    fn add(self, other: Self) -> Self::Output {
110        let mut out = Q::default();
111        unsafe {
112            fmpq_add(&mut out.value, &self.value, &other.value);
113        }
114        out
115    }
116}
117
118arithmetic_trait_borrowed_to_owned!(Add, add, Q, Q, Q);
119arithmetic_trait_mixed_borrowed_owned!(Add, add, Q, Q, Q);
120arithmetic_between_types!(Add, add, Q, Q, i64 i32 i16 i8 u64 u32 u16 u8 f32 f64);
121
122impl Add<&Z> for &Q {
123    type Output = Q;
124
125    /// Implements the [`Add`] trait for [`Q`] and [`Z`] values.
126    /// [`Add`] is implemented for any combination of owned and borrowed values.
127    ///
128    /// Parameters:
129    ///  - `other`: specifies the value to add to `self`
130    ///
131    /// Returns the sum of both numbers as a [`Q`].
132    ///
133    /// # Examples
134    /// ```
135    /// use qfall_math::rational::Q;
136    /// use qfall_math::integer::Z;
137    /// use std::str::FromStr;
138    ///
139    /// let a: Q = Q::from((42, 19));
140    /// let b: Z = Z::from(-42);
141    ///
142    /// let c: Q = &a + &b;
143    /// let d: Q = a + b;
144    /// let e: Q = &c + Z::from(42);
145    /// let f: Q = c + &Z::from(42);
146    /// ```
147    fn add(self, other: &Z) -> Self::Output {
148        let mut out = Q::default();
149        unsafe {
150            fmpq_add_fmpz(&mut out.value, &self.value, &other.value);
151        }
152        out
153    }
154}
155
156arithmetic_trait_borrowed_to_owned!(Add, add, Q, Z, Q);
157arithmetic_trait_mixed_borrowed_owned!(Add, add, Q, Z, Q);
158
159#[cfg(test)]
160mod test_add_assign {
161    use crate::{integer::Z, rational::Q};
162
163    /// Ensure that `add_assign` works for small numbers.
164    #[test]
165    fn correct_small() {
166        let mut a = Q::MINUS_ONE;
167        let b = Q::MINUS_ONE;
168        let c = Q::ONE;
169        let d = Q::from((1, 2));
170
171        a += &b;
172        assert_eq!(-2, a);
173        a += &c;
174        assert_eq!(-1, a);
175        a += &c;
176        assert_eq!(0, a);
177        a += &c;
178        assert_eq!(1, a);
179        a += &c;
180        assert_eq!(2, a);
181        a += 2 * b;
182        assert_eq!(0, a);
183        a += d;
184        assert_eq!(Q::from((1, 2)), a);
185    }
186
187    /// Ensure that `add_assign` works for large numbers.
188    #[test]
189    fn correct_large() {
190        let mut a = Q::from(i64::MAX);
191        let b = Q::from(i64::MIN);
192        let c = Q::from(u64::MAX);
193
194        a += b;
195        assert_eq!(-1, a);
196        a += c;
197        assert_eq!(u64::MAX - 1, a);
198    }
199
200    /// Ensure that `add_assign` is available for all types.
201    #[test]
202    fn availability() {
203        let mut a = Q::from((1, 2));
204        let b = Q::from((4, 5));
205        let c = Z::ONE;
206
207        a += &b;
208        a += b;
209        a += &c;
210        a += c;
211        a += 0.5_f64;
212        a += 0.5_f32;
213        a += 1_u8;
214        a += 1_u16;
215        a += 1_u32;
216        a += 1_u64;
217        a += 1_i8;
218        a += 1_i16;
219        a += 1_i32;
220        a += 1_i64;
221    }
222}
223
224#[cfg(test)]
225mod test_add {
226    use super::Q;
227    use std::str::FromStr;
228
229    /// Testing addition for two [`Q`]
230    #[test]
231    fn add() {
232        let a: Q = Q::from(42);
233        let b: Q = Q::from((42, 2));
234        let c: Q = a + b;
235        assert_eq!(c, Q::from(63));
236    }
237
238    /// Testing addition for two borrowed [`Q`]
239    #[test]
240    fn add_borrow() {
241        let a: Q = Q::from(42);
242        let b: Q = Q::from((42, 2));
243        let c: Q = &a + &b;
244        assert_eq!(c, Q::from(63));
245    }
246
247    /// Testing addition for borrowed [`Q`] and [`Q`]
248    #[test]
249    fn add_first_borrowed() {
250        let a: Q = Q::from((42, 5));
251        let b: Q = Q::from((42, 10));
252        let c: Q = &a + b;
253        assert_eq!(c, Q::from((63, 5)));
254    }
255
256    /// Testing addition for [`Q`] and borrowed [`Q`]
257    #[test]
258    fn add_second_borrowed() {
259        let a: Q = Q::from(42);
260        let b: Q = Q::from((42, 2));
261        let c: Q = a + &b;
262        assert_eq!(c, Q::from(63));
263    }
264
265    /// Testing addition for large numerators and divisors
266    #[test]
267    fn add_large() {
268        let a: Q = Q::from(i64::MAX);
269        let b: Q = Q::from(u64::MAX - 1);
270        let c: Q = Q::from((1, i32::MAX));
271        let d: Q = Q::from((1, u32::MAX));
272        let e: Q = &a + &a;
273        let f: Q = c + d;
274        assert_eq!(e, b);
275        assert_eq!(
276            f,
277            Q::from_str(&format!(
278                "{}/{}",
279                u64::from(u32::MAX) + u64::from((u32::MAX - 1) / 2),
280                u64::from(u32::MAX) * u64::from((u32::MAX - 1) / 2)
281            ))
282            .unwrap()
283        );
284    }
285}
286
287#[cfg(test)]
288mod test_add_between_q_and_z {
289    use crate::integer::Z;
290    use crate::rational::Q;
291
292    /// Testing addition for [`Q`] and [`Z`]
293    #[test]
294    fn add() {
295        let a: Q = Q::from((5, 7));
296        let b: Z = Z::from(4);
297        let c: Q = a + b;
298        assert_eq!(c, Q::from((33, 7)));
299    }
300
301    /// Testing addition for both borrowed [`Q`] and [`Z`]
302    #[test]
303    fn add_borrow() {
304        let a: Q = Q::from((5, 7));
305        let b: Z = Z::from(4);
306        let c: Q = &a + &b;
307        assert_eq!(c, Q::from((33, 7)));
308    }
309
310    /// Testing addition for borrowed [`Q`] and [`Z`]
311    #[test]
312    fn add_first_borrowed() {
313        let a: Q = Q::from((5, 7));
314        let b: Z = Z::from(4);
315        let c: Q = &a + b;
316        assert_eq!(c, Q::from((33, 7)));
317    }
318
319    /// Testing addition for [`Q`] and borrowed [`Z`]
320    #[test]
321    fn add_second_borrowed() {
322        let a: Q = Q::from((5, 7));
323        let b: Z = Z::from(4);
324        let c: Q = a + &b;
325        assert_eq!(c, Q::from((33, 7)));
326    }
327
328    /// Testing addition for large numbers
329    #[test]
330    fn add_large_numbers() {
331        let a: Q = Q::from((u64::MAX, 2));
332        let b: Q = Q::from((1, u64::MAX));
333        let c: Z = Z::from(u64::MAX);
334        let d: Q = a + &c;
335        let e: Q = b + c;
336        assert_eq!(d, Q::from(u64::MAX) + Q::from((u64::MAX, 2)));
337        assert_eq!(e, Q::from((1, u64::MAX)) + Q::from(u64::MAX));
338    }
339}
340
341#[cfg(test)]
342mod test_add_between_types {
343
344    use crate::rational::Q;
345
346    /// Testing addition between different types
347    #[test]
348    #[allow(clippy::op_ref)]
349    fn add() {
350        let a: Q = Q::from(42);
351        let b: u64 = 1;
352        let c: u32 = 1;
353        let d: u16 = 1;
354        let e: u8 = 1;
355        let f: i64 = 1;
356        let g: i32 = 1;
357        let h: i16 = 1;
358        let i: i8 = 1;
359        let j: f32 = 0.3;
360        let k: f64 = 0.3;
361        let _: Q = &a + &b;
362        let _: Q = &a + &c;
363        let _: Q = &a + &d;
364        let _: Q = &a + &e;
365        let _: Q = &a + &f;
366        let _: Q = &a + &g;
367        let _: Q = &a + &h;
368        let _: Q = &a + &i;
369        let _: Q = &a + &j;
370        let _: Q = &a + &k;
371
372        let _: Q = &b + &a;
373        let _: Q = &c + &a;
374        let _: Q = &d + &a;
375        let _: Q = &e + &a;
376        let _: Q = &f + &a;
377        let _: Q = &g + &a;
378        let _: Q = &h + &a;
379        let _: Q = &i + &a;
380        let _: Q = &j + &a;
381        let _: Q = &k + &a;
382
383        let _: Q = &a + b;
384        let _: Q = &a + c;
385        let _: Q = &a + d;
386        let _: Q = &a + e;
387        let _: Q = &a + f;
388        let _: Q = &a + g;
389        let _: Q = &a + h;
390        let _: Q = &a + i;
391        let _: Q = &a + j;
392        let _: Q = &a + k;
393
394        let _: Q = &b + Q::from(42);
395        let _: Q = &c + Q::from(42);
396        let _: Q = &d + Q::from(42);
397        let _: Q = &e + Q::from(42);
398        let _: Q = &f + Q::from(42);
399        let _: Q = &g + Q::from(42);
400        let _: Q = &h + Q::from(42);
401        let _: Q = &i + Q::from(42);
402        let _: Q = &j + Q::from(42);
403        let _: Q = &k + Q::from(42);
404
405        let _: Q = Q::from(42) + &b;
406        let _: Q = Q::from(42) + &c;
407        let _: Q = Q::from(42) + &d;
408        let _: Q = Q::from(42) + &e;
409        let _: Q = Q::from(42) + &f;
410        let _: Q = Q::from(42) + &g;
411        let _: Q = Q::from(42) + &h;
412        let _: Q = Q::from(42) + &i;
413        let _: Q = Q::from(42) + &j;
414        let _: Q = Q::from(42) + &k;
415
416        let _: Q = b + &a;
417        let _: Q = c + &a;
418        let _: Q = d + &a;
419        let _: Q = e + &a;
420        let _: Q = f + &a;
421        let _: Q = g + &a;
422        let _: Q = h + &a;
423        let _: Q = i + &a;
424        let _: Q = j + &a;
425        let _: Q = k + &a;
426
427        let _: Q = Q::from(42) + b;
428        let _: Q = Q::from(42) + c;
429        let _: Q = Q::from(42) + d;
430        let _: Q = Q::from(42) + e;
431        let _: Q = Q::from(42) + f;
432        let _: Q = Q::from(42) + g;
433        let _: Q = Q::from(42) + h;
434        let _: Q = Q::from(42) + i;
435        let _: Q = Q::from(42) + j;
436        let _: Q = Q::from(42) + k;
437
438        let _: Q = b + Q::from(42);
439        let _: Q = c + Q::from(42);
440        let _: Q = d + Q::from(42);
441        let _: Q = e + Q::from(42);
442        let _: Q = f + Q::from(42);
443        let _: Q = g + Q::from(42);
444        let _: Q = h + Q::from(42);
445        let _: Q = i + Q::from(42);
446        let _: Q = j + Q::from(42);
447        let _: Q = k + Q::from(42);
448    }
449}