Skip to main content

relp_num/rational/big/
with_small_rational.rs

1//! # Interactions with fixed size ratios
2use crate::NonZeroUbig;
3use crate::rational::{Rational16, Rational32, Rational64, Rational8};
4use crate::rational::big::Big;
5use crate::sign::Negateable;
6use crate::sign::Sign;
7use crate::Ubig;
8
9macro_rules! define_interations {
10    ($small:ident, $module_name:ident) => {
11        mod $module_name {
12            use super::*;
13
14            mod creation {
15                use super::*;
16
17                impl<const S: usize> From<$small> for Big<S> {
18                    #[inline]
19                    fn from(value: $small) -> Self {
20                        Self {
21                            sign: value.sign,
22                            numerator: Ubig::new(value.numerator as usize),
23                            denominator: unsafe {
24                                // SAFETY: Input denominator is nonzero
25                                NonZeroUbig::new_unchecked(value.denominator as usize)
26                            },
27                        }
28                    }
29                }
30
31                impl<const S: usize> From<&$small> for Big<S> {
32                    #[inline]
33                    fn from(value: &$small) -> Self {
34                        Self {
35                            sign: value.sign,
36                            numerator: Ubig::new(value.numerator as usize),
37                            denominator: unsafe {
38                                // SAFETY: Input denominator is nonzero
39                                NonZeroUbig::new_unchecked(value.denominator as usize)
40                            },
41                        }
42                    }
43                }
44            }
45
46            mod compare {
47                use super::*;
48
49                impl<const S: usize> PartialEq<$small> for Big<S> {
50                    #[inline]
51                    fn eq(&self, other: &$small) -> bool {
52                        // Compare first with the denominator, we don't have to do a bounds check
53                        // and the probability that its equal is small
54                        *self.denominator.first() == other.denominator as usize &&
55                            match self.sign {
56                                Sign::Zero => other.sign == Sign::Zero,
57                                Sign::Positive | Sign::Negative => {
58                                    self.sign == other.sign &&
59                                    self.numerator[0] == other.numerator as usize &&
60                                    self.numerator.len() == 1 &&
61                                    self.denominator.len() == 1
62                                }
63                            }
64                    }
65                }
66            }
67
68            mod field {
69                use super::*;
70                use crate::sign::Sign;
71
72                mod add {
73                    use super::*;
74                    use std::ops::{Add, AddAssign};
75                    use crate::rational::big::ops::building_blocks::{add_small, sub_small, SignChange};
76
77                    impl<const S: usize> Add<$small> for Big<S> {
78                        type Output = Self;
79
80                        #[inline]
81                        fn add(mut self, rhs: $small) -> Self::Output {
82                            AddAssign::add_assign(&mut self, &rhs);
83                            self
84                        }
85                    }
86
87                    impl<const S: usize> Add<&$small> for Big<S> {
88                        type Output = Self;
89
90                        #[inline]
91                        fn add(mut self, rhs: &$small) -> Self::Output {
92                            AddAssign::add_assign(&mut self, rhs);
93                            self
94                        }
95                    }
96
97                    impl<const S: usize> Add<Option<&$small>> for Big<S> {
98                        type Output = Self;
99
100                        #[inline]
101                        fn add(self, rhs: Option<&$small>) -> Self::Output {
102                            match rhs {
103                                None => self,
104                                Some(rhs) => Add::add(self, rhs),
105                            }
106                        }
107                    }
108
109                    impl<const S: usize> Add<&$small> for &Big<S> {
110                        type Output = Big<S>;
111
112                        #[inline]
113                        fn add(self, rhs: &$small) -> Self::Output {
114                            // TODO(PERFORMANCE): Make sure that this is just as efficient as a native algorithm.
115                            let mut cloned = self.clone();
116                            AddAssign::add_assign(&mut cloned, rhs);
117                            cloned
118                        }
119                    }
120
121                    impl<const S: usize> Add<Option<&$small>> for &Big<S> {
122                        type Output = Big<S>;
123
124                        #[inline]
125                        fn add(self, rhs: Option<&$small>) -> Self::Output {
126                            // TODO(PERFORMANCE): Make sure that this is just as efficient as a native algorithm.
127                            let copy = self.clone();
128                            match rhs {
129                                None => copy,
130                                Some(rhs) => Add::add(copy, rhs),
131                            }
132                        }
133                    }
134
135                    impl<const S: usize> AddAssign<$small> for Big<S> {
136                        #[inline]
137                        fn add_assign(&mut self, rhs: $small) {
138                            AddAssign::add_assign(self, &rhs);
139                        }
140                    }
141
142                    impl<const S: usize> AddAssign<&$small> for Big<S> {
143                        #[inline]
144                        fn add_assign(&mut self, rhs: &$small) {
145                            match (self.sign, rhs.sign) {
146                                (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
147                                    unsafe {
148                                        // SAFETY: The numbers are non zero
149                                        add_small(
150                                            self.numerator.inner_mut(),
151                                            self.denominator.inner_mut(),
152                                            rhs.numerator as usize,
153                                            rhs.denominator as usize,
154                                        );
155                                    }
156                                },
157                                (Sign::Negative, Sign::Positive) | (Sign::Positive, Sign::Negative) => {
158                                    unsafe {
159                                        // SAFETY: The numbers are non zero
160                                        let sign_change = sub_small(
161                                            self.numerator.inner_mut(),
162                                            self.denominator.inner_mut(),
163                                            rhs.numerator as usize,
164                                            rhs.denominator as usize,
165                                        );
166                                        match sign_change {
167                                            SignChange::None => {}
168                                            SignChange::Flip => self.sign.negate(),
169                                            SignChange::Zero => self.sign = Sign::Zero,
170                                        }
171                                    }
172                                }
173                                (_, Sign::Zero) => {}
174                                (Sign::Zero, _) => {
175                                    self.sign = rhs.sign;
176                                    debug_assert_eq!(self.numerator.len(), 0);
177                                    unsafe {
178                                        // SAFETY: Value was empty before and rhs.numerator is non zero
179                                        self.numerator.inner_mut().push(rhs.numerator as usize);
180                                    }
181                                    debug_assert_eq!(self.denominator.len(), 1);
182                                    debug_assert_eq!(self.denominator[0], 1);
183                                    unsafe {
184                                        // SAFETY: Value was one before and rhs.denominator is non zero
185                                        *self.denominator.first_mut() = rhs.denominator as usize;
186                                    }
187                                }
188                            }
189                        }
190                    }
191                }
192
193                mod sub {
194                    use super::*;
195                    use crate::rational::big::ops::building_blocks::{add_small, sub_small, SignChange};
196                    use std::ops::{Sub, SubAssign};
197
198                    impl<const S: usize> Sub<$small> for Big<S> {
199                        type Output = Self;
200
201                        #[inline]
202                        fn sub(mut self, rhs: $small) -> Self::Output {
203                            SubAssign::sub_assign(&mut self, &rhs);
204                            self
205                        }
206                    }
207
208                    impl<const S: usize> Sub<&$small> for Big<S> {
209                        type Output = Self;
210
211                        #[inline]
212                        fn sub(mut self, rhs: &$small) -> Self::Output {
213                            SubAssign::sub_assign(&mut self, rhs);
214                            self
215                        }
216                    }
217
218                    impl<const S: usize> Sub<Option<&$small>> for Big<S> {
219                        type Output = Self;
220
221                        #[inline]
222                        fn sub(self, rhs: Option<&$small>) -> Self::Output {
223                            match rhs {
224                                None => self,
225                                Some(rhs) => Sub::sub(self, rhs),
226                            }
227                        }
228                    }
229
230                    impl<const S: usize> Sub<&$small> for &Big<S> {
231                        type Output = Big<S>;
232
233                        #[inline]
234                        fn sub(self, rhs: &$small) -> Self::Output {
235                            // TODO(PERFORMANCE): Make sure that this is just as efficient as a native algorithm.
236                            let mut cloned = self.clone();
237                            SubAssign::sub_assign(&mut cloned, rhs);
238                            cloned
239                        }
240                    }
241
242                    impl<const S: usize> Sub<Option<&$small>> for &Big<S> {
243                        type Output = Big<S>;
244
245                        #[inline]
246                        fn sub(self, rhs: Option<&$small>) -> Self::Output {
247                            // TODO(PERFORMANCE): Make sure that this is just as efficient as a native algorithm.
248                            let copy = self.clone();
249                            match rhs {
250                                None => copy,
251                                Some(rhs) => Sub::sub(copy, rhs),
252                            }
253                        }
254                    }
255
256                    impl<const S: usize> SubAssign<$small> for Big<S> {
257                        #[inline]
258                        fn sub_assign(&mut self, rhs: $small) {
259                            SubAssign::sub_assign(self, &rhs);
260                        }
261                    }
262
263                    impl<const S: usize> SubAssign<&$small> for Big<S> {
264                        #[inline]
265                        fn sub_assign(&mut self, rhs: &$small) {
266                            match (self.sign, rhs.sign) {
267                                (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
268                                    unsafe {
269                                        // SAFETY: The numbers are non zero
270                                        let sign_change = sub_small(
271                                            self.numerator.inner_mut(),
272                                            self.denominator.inner_mut(),
273                                            rhs.numerator as usize,
274                                            rhs.denominator as usize,
275                                        );
276                                        match sign_change {
277                                            SignChange::None => {}
278                                            SignChange::Flip => self.sign.negate(),
279                                            SignChange::Zero => self.sign = Sign::Zero,
280                                        }
281                                    }
282                                }
283                                (Sign::Negative, Sign::Positive) | (Sign::Positive, Sign::Negative) => {
284                                    unsafe {
285                                        // SAFETY: The numbers are non zero
286                                        add_small(
287                                            self.numerator.inner_mut(),
288                                            self.denominator.inner_mut(),
289                                            rhs.numerator as usize,
290                                            rhs.denominator as usize,
291                                        );
292                                    }
293                                }
294                                (_, Sign::Zero) => {}
295                                (Sign::Zero, _) => {
296                                    self.sign = -rhs.sign;
297                                    debug_assert_eq!(self.numerator.len(), 0);
298                                    unsafe {
299                                        // SAFETY: The rhs numerator is not zero
300                                        self.numerator.inner_mut().push(rhs.numerator as usize)
301                                    };
302                                    debug_assert_eq!(self.denominator.len(), 1);
303                                    debug_assert_eq!(self.denominator[0], 1);
304                                    unsafe {
305                                        // SAFETY: The rhs denominator is not zero
306                                        *self.denominator.first_mut() = rhs.denominator as usize;
307                                    }
308                                }
309                            }
310                        }
311                    }
312                }
313
314                mod mul {
315                    use super::*;
316                    use std::ops::{Mul, MulAssign};
317                    use crate::rational::big::ops::building_blocks::mul_small;
318
319                    impl<const S: usize> Mul<$small> for Big<S> {
320                        type Output = Self;
321
322                        #[inline]
323                        fn mul(self, rhs: $small) -> Self::Output {
324                            Mul::mul(self, &rhs)
325                        }
326                    }
327
328                    impl<const S: usize> Mul<&$small> for Big<S> {
329                        type Output = Self;
330
331                        #[inline]
332                        fn mul(mut self, rhs: &$small) -> Self::Output {
333                            MulAssign::mul_assign(&mut self, rhs);
334                            self
335                        }
336                    }
337
338                    impl<const S: usize> Mul<&$small> for &Big<S> {
339                        type Output = Big<S>;
340
341                        #[inline]
342                        fn mul(self, rhs: &$small) -> Self::Output {
343                            // TODO(PERFORMANCE): Make sure that this is just as efficient as a native algorithm.
344                            self.clone() * rhs
345                        }
346                    }
347
348                    impl<const S: usize> Mul<Option<&$small>> for Big<S> {
349                        type Output = Big<S>;
350
351                        #[inline]
352                        fn mul(mut self, rhs: Option<&$small>) -> Self::Output {
353                            match rhs {
354                                None => {
355                                    <Self as num_traits::Zero>::set_zero(&mut self);
356                                    self
357                                },
358                                Some(rhs) => Mul::mul(self, rhs),
359                            }
360                        }
361                    }
362
363                    impl<const S: usize> Mul<Option<&$small>> for &Big<S> {
364                        type Output = Big<S>;
365
366                        #[inline]
367                        fn mul(self, rhs: Option<&$small>) -> Self::Output {
368                            match rhs {
369                                None => <Self::Output as num_traits::Zero>::zero(),
370                                Some(rhs) => Mul::mul(self, rhs),
371                            }
372                        }
373                    }
374
375                    impl<const S: usize> MulAssign<$small> for Big<S> {
376                        #[inline]
377                        fn mul_assign(&mut self, rhs: $small) {
378                            MulAssign::mul_assign(self, &rhs);
379                        }
380                    }
381
382                    impl<const S: usize> MulAssign<&$small> for Big<S> {
383                        #[inline]
384                        fn mul_assign(&mut self, rhs: &$small) {
385                            match (self.sign, rhs.sign) {
386                                (Sign::Positive | Sign::Negative, Sign::Positive | Sign::Negative) => {
387                                    self.sign *= rhs.sign;
388                                    unsafe {
389                                        mul_small(
390                                            self.numerator.inner_mut(),
391                                            self.denominator.inner_mut(),
392                                            rhs.numerator as usize,
393                                            rhs.denominator as usize,
394                                        )
395                                    }
396                                }
397                                (Sign::Positive | Sign::Negative, Sign::Zero) => {
398                                    <Self as num_traits::Zero>::set_zero(self);
399                                }
400                                (Sign::Zero, _) => {}
401                            }
402                        }
403                    }
404                }
405
406                mod div {
407                    use super::*;
408                    use std::ops::{Div, DivAssign};
409                    use crate::rational::big::ops::building_blocks::mul_small;
410
411                    impl<const S: usize> Div<$small> for Big<S> {
412                        type Output = Big<S>;
413
414                        #[inline]
415                        fn div(mut self, rhs: $small) -> Self::Output {
416                            DivAssign::div_assign(&mut self, rhs);
417                            self
418                        }
419                    }
420
421                    impl<const S: usize> Div<&$small> for Big<S> {
422                        type Output = Big<S>;
423
424                        #[inline]
425                        fn div(mut self, rhs: &$small) -> Self::Output {
426                            DivAssign::div_assign(&mut self, rhs);
427                            self
428                        }
429                    }
430
431                    impl<const S: usize> DivAssign<$small> for Big<S> {
432                        #[inline]
433                        fn div_assign(&mut self, rhs: $small) {
434                            DivAssign::div_assign(self, &rhs);
435                        }
436                    }
437
438                    // The sign of a quotient is the product of the signs, so `*` is correct here.
439                    #[allow(clippy::suspicious_op_assign_impl)]
440                    impl<const S: usize> DivAssign<&$small> for Big<S> {
441                        #[inline]
442                        fn div_assign(&mut self, rhs: &$small) {
443                            match (self.sign, rhs.sign) {
444                                (Sign::Positive | Sign::Negative, Sign::Positive | Sign::Negative) => {
445                                    self.sign *= rhs.sign;
446                                    unsafe {
447                                        mul_small(
448                                            self.numerator.inner_mut(),
449                                            self.denominator.inner_mut(),
450                                            rhs.denominator as usize,
451                                            rhs.numerator as usize,
452                                        );
453                                    }
454                                }
455                                (_, Sign::Zero) => panic!("attempt to divide by zero"),
456                                (Sign::Zero, _) => {}
457                            }
458                        }
459                    }
460                }
461            }
462        }
463    }
464}
465
466define_interations!(Rational8, rational8);
467define_interations!(Rational16, rational16);
468define_interations!(Rational32, rational32);
469define_interations!(Rational64, rational64);
470
471#[cfg(test)]
472mod test {
473    use num_traits::One;
474    use smallvec::smallvec;
475
476    use crate::{R16, R32, R64, R8, Rational64, RationalBig, RB, Sign, Ubig};
477    use crate::integer::big::NonZeroUbig;
478
479    #[test]
480    fn test_eq() {
481        assert_eq!(RB!(0), R64!(0));
482        assert_eq!(RB!(-2), R64!(-2));
483        assert_eq!(RB!(854984, 6868468468), R64!(854984, 6868468468));
484        assert_eq!(RB!(-989888, 4968468421), R64!(-989888, 4968468421));
485        assert_ne!(RB!(668468498646846546846546898997987_u128), R8!(1));
486        assert_ne!(RB!(668468498646846546846546898997987.4385484_f64), R8!(1, 2));
487    }
488
489    #[test]
490    fn test_add_sub() {
491        assert_eq!(RB!(0) + R8!(0), RB!(0));
492        assert_eq!(RB!(89) + R8!(1), RB!(90));
493        assert_eq!(RB!(89) - R16!(1), RB!(88));
494
495        assert_eq!(RB!(2, 3) + R16!(4, 9), RB!(10, 9));
496        assert_eq!(RB!(989, 141) + R8!(1, 141), RB!(990, 141));
497        assert_eq!(RB!(-84, 3) + R8!(1, 3), RB!(-83, 3));
498        assert_eq!(RB!(1, 2) + R8!(1, 2), RB!(1));
499        assert_eq!(RB!(-1, 2) + R8!(1, 2), RB!(0));
500        assert_eq!(RB!(-1, 2) + R8!(1), RB!(1, 2));
501        assert_eq!(RB!(7, 2) + R8!(5, 2), RB!(6));
502        assert_eq!(RB!(-7, 2) + R8!(5, 2), RB!(-1));
503        assert_eq!(RB!(-7, 2) - R8!(5, 2), RB!(-6));
504        assert_eq!(
505            RationalBig {
506                sign: Sign::Positive,
507                numerator: unsafe { Ubig::from_inner_unchecked(smallvec![1, 1]) },
508                denominator: unsafe { NonZeroUbig::from_inner_unchecked(smallvec![2]) },
509            } + R8!(1, 2),
510            RationalBig {
511                sign: Sign::Positive,
512                numerator: unsafe { Ubig::from_inner_unchecked(smallvec![(1 << 63) + 1]) },
513                denominator: NonZeroUbig::one(),
514            },
515        );
516
517        assert_eq!(RB!(2, 3) + R8!(8, 1), RB!(8 * 3 + 2, 3));
518        assert_eq!(RB!(8) + R8!(16, 3), RB!(8 * 3 + 16, 3));
519        assert_eq!(RB!(3, 5) + R16!(2, 5), RB!(1));
520
521        assert_eq!(RB!(5) - R32!(7), RB!(-2));
522        assert_eq!(
523            RationalBig {
524                sign: Sign::Positive,
525                numerator: unsafe { Ubig::from_inner_unchecked(smallvec![1, 1]) },
526                denominator: unsafe { NonZeroUbig::from_inner_unchecked(smallvec![2]) },
527            } - R8!(1, 2),
528            RationalBig {
529                sign: Sign::Positive,
530                numerator: unsafe { Ubig::from_inner_unchecked(smallvec![1 << 63]) },
531                denominator: NonZeroUbig::one(),
532            },
533        );
534        assert_eq!(RB!(5, 2) - R8!(2), RB!(1, 2));
535        assert_eq!(RB!(3) - R8!(1, 2), RB!(5, 2));
536        assert_eq!(RB!(16, 3) - R8!(11, 4), RB!(16 * 4 - 11 * 3, 12));
537
538        assert_eq!(RB!(0) + R8!(1, 2), RB!(1, 2));
539        assert_eq!(RB!(0) - R8!(3, 4), RB!(-3, 4));
540        assert_eq!(RB!(-10, 4) + R8!(-10, 8), RB!(-15, 4));
541        assert_eq!(RB!(-10, 4) + R64!(1, 4), RB!(-9, 4));
542        assert_eq!(RB!(-10, 8) + R64!(-10, 9), RB!(-5 * 17, 3 * 3 * 2 * 2));
543        assert_eq!(RB!(-5, 4) + R64!(-5, 4), RB!(-5, 2));
544        assert_eq!(RB!(-7, 6) + R64!(-7, 6), RB!(-7, 3));
545
546        let limit = 10;
547        for a in -limit..limit {
548            for b in 1..limit {
549                for c in -limit..limit {
550                    for d in 1..limit {
551                        assert_eq!(RB!(a, b as u64) + R64!(c, d as u64), RB!(a * d + c * b, b as u64 * d as u64), "{} / {} - {} / {}", a, b, c, d);
552                    }
553                }
554            }
555        }
556    }
557
558    #[test]
559    fn test_mul_div() {
560        assert_eq!(RB!(0) * R8!(18), RB!(0));
561        assert_eq!(RB!(1) * R8!(6), RB!(6));
562        assert_eq!(RB!(-1) * R64!(65488846), -RB!(65488846));
563        assert_eq!(RB!(7, 11) * R8!(11, 7), RB!(1));
564        assert_eq!(RB!(7, 11) * R16!(22, 7), RB!(2));
565        assert_eq!(RB!(14, 33) * R32!(3, 2), RB!(7, 11));
566        assert_eq!(RB!(4, 3) * R64!(0), RB!(0));
567
568        let limit = 10;
569        for a in -limit..limit {
570            for b in 1..limit {
571                for c in -limit..limit {
572                    for d in 1..limit {
573                        assert_eq!(RB!(a, b as u64) * R64!(c, d as u64), RB!(a * c, (b * d) as u64), "{} / {} * {} / {}", a, b, c, d);
574                    }
575                }
576            }
577        }
578    }
579
580    /// The sign has to take part in the comparison.
581    ///
582    /// It used to be dropped for non-zero values, so `-2` compared equal to `2`.
583    #[test]
584    fn eq_small_respects_sign() {
585        assert_eq!(RB!(2) == R64!(2), true);
586        assert_eq!(RB!(-2) == R64!(2), false);
587        assert_eq!(RB!(2) == R64!(-2), false);
588        assert_eq!(RB!(-2) == R64!(-2), true);
589        assert_eq!(RB!(1, 2) == R8!(1, 2), true);
590        assert_eq!(RB!(-1, 2) == R8!(1, 2), false);
591        assert_eq!(RB!(1, 2) == R8!(-1, 2), false);
592        assert_eq!(RB!(-1, 2) == R8!(-1, 2), true);
593        assert_eq!(RB!(0) == R32!(0), true);
594
595        // Equality has to agree with the ordering it is paired with.
596        for numerator in -4_i64..=4 {
597            for denominator in 1_u64..=4 {
598                let small = Rational64::new(numerator, denominator).unwrap();
599                let big = RationalBig::from(small);
600                for other in -4_i64..=4 {
601                    let other = Rational64::new(other, denominator).unwrap();
602                    #[allow(clippy::cmp_owned, reason = "comparing after conversion is what is being tested")]
603                    let converted = big == RationalBig::from(other);
604                    assert_eq!(
605                        big == other,
606                        converted,
607                        "{numerator}/{denominator} vs {other}",
608                    );
609                }
610            }
611        }
612    }
613
614    /// Dividing zero by zero has to fail the same way any other division by zero does.
615    #[test]
616    #[should_panic(expected = "attempt to divide by zero")]
617    fn div_zero_by_zero_panics() {
618        let _ = RB!(0) / R8!(0);
619    }
620}