Skip to main content

relp_num/rational/big/ops/
mul_div.rs

1use std::ops::{Div, DivAssign, Mul, MulAssign};
2
3use num_traits::Zero;
4
5use crate::rational::big::{Big, NonZeroBig};
6use crate::rational::big::ops::building_blocks::mul_assign_fraction_non_zero;
7use crate::Sign;
8
9impl<const S: usize> Mul for Big<S> {
10    type Output = Self;
11
12    #[inline]
13    fn mul(mut self, rhs: Self) -> Self::Output {
14        MulAssign::mul_assign(&mut self, rhs);
15        self
16    }
17}
18
19impl<const S: usize> Mul for NonZeroBig<S> {
20    type Output = Self;
21
22    #[inline]
23    fn mul(mut self, rhs: Self) -> Self::Output {
24        MulAssign::mul_assign(&mut self, rhs);
25        self
26    }
27}
28
29impl<const S: usize> Mul<&Big<S>> for Big<S> {
30    type Output = Self;
31
32    #[inline]
33    fn mul(mut self, rhs: &Big<S>) -> Self::Output {
34        // TODO(PERFORMANCE): Should cloning be avoided?
35        MulAssign::mul_assign(&mut self, rhs.clone());
36        self
37    }
38}
39
40impl<const S: usize> Mul<Big<S>> for &Big<S> {
41    type Output = Big<S>;
42
43    #[inline]
44    fn mul(self, rhs: Big<S>) -> Self::Output {
45        Mul::mul(rhs, self)
46    }
47}
48
49impl<const S: usize> Mul<&Big<S>> for &Big<S> {
50    type Output = Big<S>;
51
52    #[inline]
53    fn mul(self, rhs: &Big<S>) -> Self::Output {
54        let mut x = self.clone();
55        // TODO(PERFORMANCE): Should cloning be avoided?
56        MulAssign::mul_assign(&mut x, rhs.clone());
57        x
58    }
59}
60
61impl<const S: usize> MulAssign for Big<S> {
62    #[inline]
63    fn mul_assign(&mut self, rhs: Self) {
64        match (self.sign, rhs.sign) {
65            (Sign::Positive | Sign::Negative, Sign::Positive | Sign::Negative) => {
66                self.sign *= rhs.sign;
67                unsafe {
68                    // SAFETY: All values are non zero
69                    mul_assign_fraction_non_zero(
70                        self.numerator.inner_mut(),
71                        self.denominator.inner_mut(),
72                        rhs.numerator.inner().clone(),
73                        rhs.denominator.inner().clone(),
74                    );
75                }
76            }
77            (Sign::Positive | Sign::Negative, Sign::Zero) => {
78                self.set_zero();
79            }
80            (Sign::Zero, _) => {}
81        }
82    }
83}
84
85impl<const S: usize> MulAssign for NonZeroBig<S> {
86    #[inline]
87    fn mul_assign(&mut self, rhs: Self) {
88        self.sign *= rhs.sign;
89        unsafe {
90            // SAFETY: All values are non zero
91            mul_assign_fraction_non_zero(
92                self.numerator.inner_mut(),
93                self.denominator.inner_mut(),
94                rhs.numerator.inner().clone(),
95                rhs.denominator.inner().clone(),
96            );
97        }
98    }
99}
100
101impl<const S: usize> Mul<&NonZeroBig<S>> for NonZeroBig<S> {
102    type Output = Self;
103
104    #[inline]
105    fn mul(mut self, rhs: &NonZeroBig<S>) -> Self::Output {
106        // TODO(PERFORMANCE): Should cloning be avoided?
107        MulAssign::mul_assign(&mut self, rhs.clone());
108        self
109    }
110}
111
112impl<const S: usize> Mul for &NonZeroBig<S> {
113    type Output = NonZeroBig<S>;
114
115    #[inline]
116    fn mul(self, rhs: &NonZeroBig<S>) -> Self::Output {
117        let mut x = self.clone();
118        // TODO(PERFORMANCE): Should cloning be avoided?
119        MulAssign::mul_assign(&mut x, rhs.clone());
120        x
121    }
122}
123
124impl<const S: usize> MulAssign<&NonZeroBig<S>> for NonZeroBig<S> {
125    #[inline]
126    fn mul_assign(&mut self, rhs: &Self) {
127        self.sign *= rhs.sign;
128        unsafe {
129            // SAFETY: All values are non zero
130            mul_assign_fraction_non_zero(
131                self.numerator.inner_mut(),
132                self.denominator.inner_mut(),
133                rhs.numerator.inner().clone(),
134                rhs.denominator.inner().clone(),
135            );
136        }
137    }
138}
139
140impl<const S: usize> MulAssign<&Big<S>> for Big<S> {
141    #[inline]
142    fn mul_assign(&mut self, rhs: &Self) {
143        match (self.sign, rhs.sign) {
144            (Sign::Positive | Sign::Negative, Sign::Positive | Sign::Negative) => {
145                self.sign *= rhs.sign;
146                unsafe {
147                    // SAFETY: All values are non zero
148                    mul_assign_fraction_non_zero(
149                        self.numerator.inner_mut(),
150                        self.denominator.inner_mut(),
151                        rhs.numerator.inner().clone(),
152                        rhs.denominator.inner().clone(),
153                    );
154                }
155            }
156            (Sign::Positive | Sign::Negative, Sign::Zero) => {
157                self.set_zero();
158            }
159            (Sign::Zero, _) => {}
160        }
161    }
162}
163impl<const S: usize> Div for Big<S> {
164    type Output = Big<S>;
165
166    #[inline]
167    fn div(mut self, rhs: Big<S>) -> Self::Output {
168        DivAssign::div_assign(&mut self, rhs);
169        self
170    }
171}
172
173impl<const S: usize> Div<&Big<S>> for Big<S> {
174    type Output = Big<S>;
175
176    #[inline]
177    fn div(mut self, rhs: &Big<S>) -> Self::Output {
178        DivAssign::div_assign(&mut self, rhs.clone());
179        self
180    }
181}
182
183impl<const S: usize> Div<Big<S>> for &Big<S> {
184    type Output = Big<S>;
185
186    #[inline]
187    fn div(self, rhs: Big<S>) -> Self::Output {
188        let mut x = self.clone();
189        // TODO(PERFORMANCE): Should cloning be avoided?
190        DivAssign::div_assign(&mut x, rhs);
191        x
192    }
193}
194
195impl<const S: usize> Div for &Big<S> {
196    type Output = Big<S>;
197
198    #[inline]
199    fn div(self, rhs: &Big<S>) -> Self::Output {
200        let mut x = self.clone();
201        // TODO(PERFORMANCE): Should cloning be avoided?
202        DivAssign::div_assign(&mut x, rhs.clone());
203        x
204    }
205}
206
207impl<const S: usize> DivAssign<&Big<S>> for Big<S> {
208    #[inline]
209    fn div_assign(&mut self, rhs: &Big<S>) {
210        DivAssign::div_assign(self, rhs.clone());
211    }
212}
213
214impl<const S: usize> Div<&NonZeroBig<S>> for &NonZeroBig<S> {
215    type Output = NonZeroBig<S>;
216
217    #[inline]
218    fn div(self, rhs: &NonZeroBig<S>) -> Self::Output {
219        let mut x = self.clone();
220        // TODO(PERFORMANCE): Should cloning be avoided?
221        DivAssign::div_assign(&mut x, rhs.clone());
222        x
223    }
224}
225
226// The sign of a quotient is the product of the signs, so `*` is correct here.
227#[allow(clippy::suspicious_op_assign_impl)]
228impl<const S: usize> DivAssign for Big<S> {
229    #[inline]
230    fn div_assign(&mut self, rhs: Self) {
231        match (self.sign, rhs.sign) {
232            (Sign::Positive | Sign::Negative, Sign::Positive | Sign::Negative) => {
233                self.sign *= rhs.sign;
234                unsafe {
235                    // SAFETY: All values are non zero
236                    mul_assign_fraction_non_zero(
237                        self.numerator.inner_mut(),
238                        self.denominator.inner_mut(),
239                        rhs.denominator.into_inner(),
240                        rhs.numerator.into_inner(),
241                    );
242                }
243            }
244            // Matched before the zero numerator arm, so that zero divided by zero panics too,
245            // as it does in every other division impl in the crate.
246            (_, Sign::Zero) => panic!("attempt to divide by zero"),
247            (Sign::Zero, _) => {}
248        }
249    }
250}
251
252// The sign of a quotient is the product of the signs, so `*` is correct here.
253#[allow(clippy::suspicious_op_assign_impl)]
254impl<const S: usize> DivAssign for NonZeroBig<S> {
255    #[inline]
256    fn div_assign(&mut self, rhs: Self) {
257        self.sign *= rhs.sign;
258        unsafe {
259            // SAFETY: All values are non zero
260            mul_assign_fraction_non_zero(
261                self.numerator.inner_mut(),
262                self.denominator.inner_mut(),
263                rhs.denominator.into_inner(),
264                rhs.numerator.into_inner(),
265            );
266        }
267    }
268}
269
270// The sign of a quotient is the product of the signs, so `*` is correct here.
271#[allow(clippy::suspicious_op_assign_impl)]
272impl<const S: usize> DivAssign<&NonZeroBig<S>> for NonZeroBig<S> {
273    #[inline]
274    fn div_assign(&mut self, rhs: &Self) {
275        self.sign *= rhs.sign;
276        unsafe {
277            // SAFETY: All values are non zero
278            mul_assign_fraction_non_zero(
279                self.numerator.inner_mut(),
280                self.denominator.inner_mut(),
281                rhs.denominator.inner().clone(),
282                rhs.numerator.inner().clone(),
283            );
284        }
285    }
286}
287
288
289#[cfg(test)]
290mod test {
291    use crate::RB;
292
293    #[test]
294    fn test_mul() {
295        let mut x = RB!(3, 4);
296        x *= RB!(-5, 6);
297        assert_eq!(x, RB!(-3 * 5, 4 * 6));
298
299        let mut x = RB!(3, 4);
300        x *= RB!(5, 6);
301        assert_eq!(x, RB!(3 * 5, 4 * 6));
302
303        let mut x = RB!(0);
304        x *= &RB!(-5, 6);
305        assert_eq!(x, RB!(0));
306
307        let mut x = RB!(-5, 6);
308        x *= RB!(0);
309        assert_eq!(x, RB!(0));
310    }
311
312    #[test]
313    fn test_div() {
314        let mut x = RB!(3, 4);
315        x /= RB!(-5, 6);
316        assert_eq!(x, RB!(-3 * 6, 5 * 4));
317
318        let mut x = RB!(3, 4);
319        x /= &RB!(5, 6);
320        assert_eq!(x, RB!(3 * 6, 5 * 4));
321
322        let mut x = RB!(0);
323        x /= RB!(-5, 6);
324        assert_eq!(x, RB!(0));
325    }
326
327    #[test]
328    #[should_panic]
329    fn test_div_by_zero() {
330        let mut x = RB!(-5, 6);
331        x /= RB!(0);
332        assert_eq!(x, RB!(-5, 6));
333    }
334
335    /// Dividing zero by zero has to fail the same way any other division by zero does.
336    ///
337    /// The zero numerator arm used to be matched first, which returned zero instead of panicking,
338    /// while every other division impl in the crate panicked.
339    #[test]
340    #[should_panic(expected = "attempt to divide by zero")]
341    fn test_div_zero_by_zero() {
342        let _ = RB!(0) / RB!(0);
343    }
344
345    #[test]
346    #[should_panic(expected = "attempt to divide by zero")]
347    fn test_div_assign_zero_by_zero() {
348        let mut x = RB!(0);
349        x /= RB!(0);
350    }
351}