1#![forbid(missing_docs, unsafe_code)]
23
24mod denom;
25
26pub use denom::Denom;
27use num_bigint::{BigInt, BigUint, Sign};
28use num_rational::BigRational;
29use num_traits::{One, Pow, Zero};
30use std::cmp::Ordering;
31use std::fmt::Display;
32use std::iter::{Product, Sum};
33use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
34
35#[derive(Clone, Debug)]
40pub struct SmartBigRational {
41 num: BigInt,
42 denom: Denom,
43}
44
45impl SmartBigRational {
46 pub const ZERO: Self = Self {
48 num: BigInt::ZERO,
49 denom: Denom::ONE,
50 };
51
52 pub const ONE: Self = Self {
54 num: BigInt::ONE,
55 denom: Denom::ONE,
56 };
57
58 pub fn ratio(num: impl Into<BigInt>, denom: impl Into<Denom>) -> Self {
73 Self {
74 num: num.into(),
75 denom: denom.into(),
76 }
77 }
78
79 pub fn into_big_rational(self) -> BigRational {
81 self.into()
82 }
83
84 pub fn to_big_rational(&self) -> BigRational {
86 self.into()
87 }
88
89 pub fn reduce(&mut self) {
101 self.denom.gcd_reduce(&mut self.num);
102 }
103}
104
105impl From<BigRational> for SmartBigRational {
106 fn from(value: BigRational) -> SmartBigRational {
107 let (num, denom) = value.into_raw();
108 let (sign, denom) = denom.into_parts();
109 assert_eq!(sign, Sign::Plus);
110 SmartBigRational {
111 num,
112 denom: denom.into(),
113 }
114 }
115}
116
117impl From<&BigRational> for SmartBigRational {
118 fn from(value: &BigRational) -> SmartBigRational {
119 let denom = value.denom();
120 assert_eq!(denom.sign(), Sign::Plus);
121 SmartBigRational {
122 num: value.numer().clone(),
123 denom: denom.magnitude().into(),
124 }
125 }
126}
127
128impl From<BigInt> for SmartBigRational {
129 fn from(value: BigInt) -> SmartBigRational {
130 SmartBigRational {
131 num: value,
132 denom: Denom::ONE,
133 }
134 }
135}
136
137impl From<SmartBigRational> for BigRational {
138 fn from(value: SmartBigRational) -> BigRational {
139 BigRational::new(value.num, value.denom.to_biguint().into())
140 }
141}
142
143impl From<&SmartBigRational> for BigRational {
144 fn from(value: &SmartBigRational) -> BigRational {
145 BigRational::new(value.num.clone(), value.denom.to_biguint().into())
146 }
147}
148
149impl PartialEq for SmartBigRational {
150 fn eq(&self, rhs: &Self) -> bool {
151 self.num.sign() == rhs.num.sign()
152 && self.num.magnitude() * rhs.denom.to_biguint()
153 == rhs.num.magnitude() * self.denom.to_biguint()
154 }
155}
156
157impl Eq for SmartBigRational {}
158
159impl PartialOrd for SmartBigRational {
160 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
161 Some(self.cmp(rhs))
162 }
163}
164
165impl Ord for SmartBigRational {
166 fn cmp(&self, rhs: &Self) -> Ordering {
167 match (self.num.sign(), rhs.num.sign()) {
168 (Sign::Plus, Sign::Plus) => (self.num.magnitude() * rhs.denom.to_biguint())
169 .cmp(&(rhs.num.magnitude() * self.denom.to_biguint())),
170 (Sign::Plus, Sign::NoSign) => Ordering::Greater,
171 (Sign::Plus, Sign::Minus) => Ordering::Greater,
172 (Sign::NoSign, Sign::Plus) => Ordering::Less,
173 (Sign::NoSign, Sign::NoSign) => Ordering::Equal,
174 (Sign::NoSign, Sign::Minus) => Ordering::Greater,
175 (Sign::Minus, Sign::Plus) => Ordering::Less,
176 (Sign::Minus, Sign::NoSign) => Ordering::Less,
177 (Sign::Minus, Sign::Minus) => (rhs.num.magnitude() * self.denom.to_biguint())
178 .cmp(&(self.num.magnitude() * rhs.denom.to_biguint())),
179 }
180 }
181}
182
183impl Display for SmartBigRational {
184 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
185 Display::fmt(&self.to_big_rational(), f)
186 }
187}
188
189impl Zero for SmartBigRational {
190 fn zero() -> Self {
191 Self::ZERO
192 }
193
194 fn is_zero(&self) -> bool {
195 self.num.is_zero()
196 }
197}
198
199impl One for SmartBigRational {
200 fn one() -> Self {
201 Self::ONE
202 }
203}
204
205impl Neg for SmartBigRational {
206 type Output = Self;
207
208 fn neg(self) -> Self {
209 SmartBigRational {
210 num: -self.num,
211 denom: self.denom,
212 }
213 }
214}
215
216impl Neg for &SmartBigRational {
217 type Output = SmartBigRational;
218
219 fn neg(self) -> SmartBigRational {
220 SmartBigRational {
221 num: -&self.num,
222 denom: self.denom.clone(),
223 }
224 }
225}
226
227impl Pow<u32> for SmartBigRational {
228 type Output = Self;
229
230 fn pow(self, rhs: u32) -> Self {
231 SmartBigRational {
232 num: self.num.pow(rhs),
233 denom: self.denom.pow(rhs),
234 }
235 }
236}
237
238impl Pow<u32> for &SmartBigRational {
239 type Output = SmartBigRational;
240
241 fn pow(self, rhs: u32) -> SmartBigRational {
242 SmartBigRational {
243 num: Pow::pow(&self.num, rhs),
244 denom: Pow::pow(&self.denom, rhs),
245 }
246 }
247}
248
249impl Add for SmartBigRational {
250 type Output = Self;
251
252 fn add(mut self, mut rhs: Self) -> Self {
253 let denom = Denom::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
254 SmartBigRational {
255 num: self.num + rhs.num,
256 denom,
257 }
258 }
259}
260
261impl Add<&SmartBigRational> for SmartBigRational {
262 type Output = Self;
263
264 fn add(mut self, rhs: &SmartBigRational) -> SmartBigRational {
265 let mut rhs_num = rhs.num.clone();
266 let denom = Denom::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
267 SmartBigRational {
268 num: self.num + rhs_num,
269 denom,
270 }
271 }
272}
273
274impl Add for &SmartBigRational {
275 type Output = SmartBigRational;
276
277 fn add(self, rhs: Self) -> SmartBigRational {
278 let mut num = self.num.clone();
279 let mut rhs_num = rhs.num.clone();
280 let denom = Denom::normalize(&mut num, &mut rhs_num, &self.denom, &rhs.denom);
281 SmartBigRational {
282 num: num + rhs_num,
283 denom,
284 }
285 }
286}
287
288impl Add<SmartBigRational> for &SmartBigRational {
289 type Output = SmartBigRational;
290
291 fn add(self, mut rhs: SmartBigRational) -> SmartBigRational {
292 let mut num = self.num.clone();
293 let denom = Denom::normalize(&mut num, &mut rhs.num, &self.denom, &rhs.denom);
294 SmartBigRational {
295 num: num + rhs.num,
296 denom,
297 }
298 }
299}
300
301impl AddAssign for SmartBigRational {
302 fn add_assign(&mut self, mut rhs: Self) {
303 self.denom = Denom::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
304 self.num += rhs.num;
305 }
306}
307
308impl AddAssign<&SmartBigRational> for SmartBigRational {
309 fn add_assign(&mut self, rhs: &SmartBigRational) {
310 let mut rhs_num = rhs.num.clone();
311 self.denom = Denom::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
312 self.num += rhs_num;
313 }
314}
315
316impl Add<BigInt> for SmartBigRational {
317 type Output = Self;
318
319 fn add(self, mut rhs: BigInt) -> Self {
320 rhs *= &self.denom;
321 SmartBigRational {
322 num: self.num + rhs,
323 denom: self.denom,
324 }
325 }
326}
327
328impl Add<&BigInt> for SmartBigRational {
329 type Output = Self;
330
331 fn add(self, rhs: &BigInt) -> Self {
332 SmartBigRational {
333 num: self.num + rhs * &self.denom,
334 denom: self.denom,
335 }
336 }
337}
338
339impl Add<BigInt> for &SmartBigRational {
340 type Output = SmartBigRational;
341
342 fn add(self, mut rhs: BigInt) -> SmartBigRational {
343 rhs *= &self.denom;
344 SmartBigRational {
345 num: &self.num + rhs,
346 denom: self.denom.clone(),
347 }
348 }
349}
350
351impl Add<&BigInt> for &SmartBigRational {
352 type Output = SmartBigRational;
353
354 fn add(self, rhs: &BigInt) -> SmartBigRational {
355 SmartBigRational {
356 num: &self.num + rhs * &self.denom,
357 denom: self.denom.clone(),
358 }
359 }
360}
361
362impl AddAssign<BigInt> for SmartBigRational {
363 fn add_assign(&mut self, mut rhs: BigInt) {
364 rhs *= &self.denom;
365 self.num += rhs;
366 }
367}
368
369impl AddAssign<&BigInt> for SmartBigRational {
370 fn add_assign(&mut self, rhs: &BigInt) {
371 self.num += rhs * &self.denom;
372 }
373}
374
375impl Sub for SmartBigRational {
376 type Output = Self;
377
378 fn sub(mut self, mut rhs: Self) -> Self {
379 let denom = Denom::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
380 SmartBigRational {
381 num: self.num - rhs.num,
382 denom,
383 }
384 }
385}
386
387impl Sub<&SmartBigRational> for SmartBigRational {
388 type Output = Self;
389
390 fn sub(mut self, rhs: &SmartBigRational) -> Self {
391 let mut rhs_num = rhs.num.clone();
392 let denom = Denom::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
393 SmartBigRational {
394 num: self.num - rhs_num,
395 denom,
396 }
397 }
398}
399
400impl Sub for &SmartBigRational {
401 type Output = SmartBigRational;
402
403 fn sub(self, rhs: Self) -> SmartBigRational {
404 let mut num = self.num.clone();
405 let mut rhs_num = rhs.num.clone();
406 let denom = Denom::normalize(&mut num, &mut rhs_num, &self.denom, &rhs.denom);
407 SmartBigRational {
408 num: num - rhs_num,
409 denom,
410 }
411 }
412}
413
414impl Sub<SmartBigRational> for &SmartBigRational {
415 type Output = SmartBigRational;
416
417 fn sub(self, mut rhs: SmartBigRational) -> SmartBigRational {
418 let mut num = self.num.clone();
419 let denom = Denom::normalize(&mut num, &mut rhs.num, &self.denom, &rhs.denom);
420 SmartBigRational {
421 num: num - rhs.num,
422 denom,
423 }
424 }
425}
426
427impl SubAssign for SmartBigRational {
428 fn sub_assign(&mut self, mut rhs: Self) {
429 self.denom = Denom::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
430 self.num -= rhs.num;
431 }
432}
433
434impl SubAssign<&SmartBigRational> for SmartBigRational {
435 fn sub_assign(&mut self, rhs: &SmartBigRational) {
436 let mut rhs_num = rhs.num.clone();
437 self.denom = Denom::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
438 self.num -= rhs_num;
439 }
440}
441
442impl Sub<BigInt> for SmartBigRational {
443 type Output = Self;
444
445 fn sub(self, mut rhs: BigInt) -> Self {
446 rhs *= &self.denom;
447 SmartBigRational {
448 num: self.num - rhs,
449 denom: self.denom,
450 }
451 }
452}
453
454impl Sub<&BigInt> for SmartBigRational {
455 type Output = Self;
456
457 fn sub(self, rhs: &BigInt) -> Self {
458 SmartBigRational {
459 num: self.num - rhs * &self.denom,
460 denom: self.denom,
461 }
462 }
463}
464
465impl Sub<BigInt> for &SmartBigRational {
466 type Output = SmartBigRational;
467
468 fn sub(self, mut rhs: BigInt) -> SmartBigRational {
469 rhs *= &self.denom;
470 SmartBigRational {
471 num: &self.num - rhs,
472 denom: self.denom.clone(),
473 }
474 }
475}
476
477impl Sub<&BigInt> for &SmartBigRational {
478 type Output = SmartBigRational;
479
480 fn sub(self, rhs: &BigInt) -> SmartBigRational {
481 SmartBigRational {
482 num: &self.num - rhs * &self.denom,
483 denom: self.denom.clone(),
484 }
485 }
486}
487
488impl SubAssign<BigInt> for SmartBigRational {
489 fn sub_assign(&mut self, mut rhs: BigInt) {
490 rhs *= &self.denom;
491 self.num -= rhs;
492 }
493}
494
495impl SubAssign<&BigInt> for SmartBigRational {
496 fn sub_assign(&mut self, rhs: &BigInt) {
497 self.num -= rhs * &self.denom;
498 }
499}
500
501impl Mul for SmartBigRational {
502 type Output = Self;
503
504 fn mul(self, rhs: Self) -> Self {
505 SmartBigRational {
506 num: self.num * rhs.num,
507 denom: self.denom * rhs.denom,
508 }
509 }
510}
511
512impl Mul<&SmartBigRational> for SmartBigRational {
513 type Output = Self;
514
515 fn mul(self, rhs: &SmartBigRational) -> Self {
516 SmartBigRational {
517 num: self.num * &rhs.num,
518 denom: self.denom * &rhs.denom,
519 }
520 }
521}
522
523impl Mul for &SmartBigRational {
524 type Output = SmartBigRational;
525
526 fn mul(self, rhs: Self) -> SmartBigRational {
527 SmartBigRational {
528 num: &self.num * &rhs.num,
529 denom: &self.denom * &rhs.denom,
530 }
531 }
532}
533
534impl Mul<SmartBigRational> for &SmartBigRational {
535 type Output = SmartBigRational;
536
537 fn mul(self, rhs: SmartBigRational) -> SmartBigRational {
538 SmartBigRational {
539 num: &self.num * rhs.num,
540 denom: &self.denom * rhs.denom,
541 }
542 }
543}
544
545impl MulAssign for SmartBigRational {
546 fn mul_assign(&mut self, rhs: Self) {
547 self.num *= rhs.num;
548 self.denom *= rhs.denom;
549 }
550}
551
552impl MulAssign<&SmartBigRational> for SmartBigRational {
553 fn mul_assign(&mut self, rhs: &SmartBigRational) {
554 self.num *= &rhs.num;
555 self.denom *= &rhs.denom;
556 }
557}
558
559impl Mul<BigInt> for SmartBigRational {
560 type Output = Self;
561
562 fn mul(self, rhs: BigInt) -> Self {
563 SmartBigRational {
564 num: self.num * rhs,
565 denom: self.denom,
566 }
567 }
568}
569
570impl Mul<&BigInt> for SmartBigRational {
571 type Output = Self;
572
573 fn mul(self, rhs: &BigInt) -> Self {
574 SmartBigRational {
575 num: self.num * rhs,
576 denom: self.denom,
577 }
578 }
579}
580
581impl Mul<BigInt> for &SmartBigRational {
582 type Output = SmartBigRational;
583
584 fn mul(self, rhs: BigInt) -> SmartBigRational {
585 SmartBigRational {
586 num: &self.num * rhs,
587 denom: self.denom.clone(),
588 }
589 }
590}
591
592impl Mul<&BigInt> for &SmartBigRational {
593 type Output = SmartBigRational;
594
595 fn mul(self, rhs: &BigInt) -> SmartBigRational {
596 SmartBigRational {
597 num: &self.num * rhs,
598 denom: self.denom.clone(),
599 }
600 }
601}
602
603impl MulAssign<BigInt> for SmartBigRational {
604 fn mul_assign(&mut self, rhs: BigInt) {
605 self.num *= rhs;
606 }
607}
608
609impl MulAssign<&BigInt> for SmartBigRational {
610 fn mul_assign(&mut self, rhs: &BigInt) {
611 self.num *= rhs;
612 }
613}
614
615impl Div for SmartBigRational {
616 type Output = Self;
617
618 fn div(self, rhs: Self) -> Self {
619 let (rhs_sign, rhs_num) = rhs.num.into_parts();
620 let rhs_denom = BigInt::from_biguint(rhs_sign, BigUint::from(rhs.denom));
621 SmartBigRational {
622 num: self.num * rhs_denom,
623 denom: self.denom * Denom::from(rhs_num),
624 }
625 }
626}
627
628impl Div<&SmartBigRational> for SmartBigRational {
629 type Output = Self;
630
631 fn div(self, rhs: &SmartBigRational) -> Self {
632 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), BigUint::from(&rhs.denom));
633 SmartBigRational {
634 num: self.num * rhs_denom,
635 denom: self.denom * Denom::from(rhs.num.magnitude()),
636 }
637 }
638}
639
640impl Div for &SmartBigRational {
641 type Output = SmartBigRational;
642
643 fn div(self, rhs: Self) -> SmartBigRational {
644 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), BigUint::from(&rhs.denom));
645 SmartBigRational {
646 num: &self.num * rhs_denom,
647 denom: &self.denom * Denom::from(rhs.num.magnitude()),
648 }
649 }
650}
651
652impl Div<SmartBigRational> for &SmartBigRational {
653 type Output = SmartBigRational;
654
655 fn div(self, rhs: SmartBigRational) -> SmartBigRational {
656 let (rhs_sign, rhs_num) = rhs.num.into_parts();
657 let rhs_denom = BigInt::from_biguint(rhs_sign, BigUint::from(rhs.denom));
658 SmartBigRational {
659 num: &self.num * rhs_denom,
660 denom: &self.denom * Denom::from(rhs_num),
661 }
662 }
663}
664
665impl DivAssign for SmartBigRational {
666 fn div_assign(&mut self, rhs: Self) {
667 let (rhs_sign, rhs_num) = rhs.num.into_parts();
668 let rhs_denom = BigInt::from_biguint(rhs_sign, BigUint::from(rhs.denom));
669 self.num *= rhs_denom;
670 self.denom *= Denom::from(rhs_num);
671 }
672}
673
674impl DivAssign<&SmartBigRational> for SmartBigRational {
675 fn div_assign(&mut self, rhs: &SmartBigRational) {
676 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), BigUint::from(&rhs.denom));
677 self.num *= rhs_denom;
678 self.denom *= Denom::from(rhs.num.magnitude());
679 }
680}
681
682impl Div<BigUint> for SmartBigRational {
683 type Output = Self;
684
685 #[expect(clippy::suspicious_arithmetic_impl)]
686 fn div(self, rhs: BigUint) -> Self {
687 SmartBigRational {
688 num: self.num,
689 denom: self.denom * Denom::from(rhs),
690 }
691 }
692}
693
694impl Div<&BigUint> for SmartBigRational {
695 type Output = Self;
696
697 #[expect(clippy::suspicious_arithmetic_impl)]
698 fn div(self, rhs: &BigUint) -> Self {
699 SmartBigRational {
700 num: self.num,
701 denom: self.denom * Denom::from(rhs),
702 }
703 }
704}
705
706impl Div<BigUint> for &SmartBigRational {
707 type Output = SmartBigRational;
708
709 #[expect(clippy::suspicious_arithmetic_impl)]
710 fn div(self, rhs: BigUint) -> SmartBigRational {
711 SmartBigRational {
712 num: self.num.clone(),
713 denom: &self.denom * Denom::from(rhs),
714 }
715 }
716}
717
718impl Div<&BigUint> for &SmartBigRational {
719 type Output = SmartBigRational;
720
721 #[expect(clippy::suspicious_arithmetic_impl)]
722 fn div(self, rhs: &BigUint) -> SmartBigRational {
723 SmartBigRational {
724 num: self.num.clone(),
725 denom: &self.denom * Denom::from(rhs),
726 }
727 }
728}
729
730impl DivAssign<BigUint> for SmartBigRational {
731 #[expect(clippy::suspicious_op_assign_impl)]
732 fn div_assign(&mut self, rhs: BigUint) {
733 self.denom *= Denom::from(rhs);
734 }
735}
736
737impl DivAssign<&BigUint> for SmartBigRational {
738 #[expect(clippy::suspicious_op_assign_impl)]
739 fn div_assign(&mut self, rhs: &BigUint) {
740 self.denom *= Denom::from(rhs);
741 }
742}
743
744impl Sum for SmartBigRational {
745 fn sum<I>(iter: I) -> Self
746 where
747 I: Iterator<Item = Self>,
748 {
749 iter.fold(Self::zero(), |acc, x| acc + x)
750 }
751}
752
753impl<'a> Sum<&'a SmartBigRational> for SmartBigRational {
754 fn sum<I>(iter: I) -> Self
755 where
756 I: Iterator<Item = &'a SmartBigRational>,
757 {
758 iter.fold(Self::zero(), |acc, x| acc + x)
759 }
760}
761
762impl Product for SmartBigRational {
763 fn product<I>(iter: I) -> Self
764 where
765 I: Iterator<Item = Self>,
766 {
767 iter.fold(Self::one(), |acc, x| acc * x)
768 }
769}
770
771impl<'a> Product<&'a SmartBigRational> for SmartBigRational {
772 fn product<I>(iter: I) -> Self
773 where
774 I: Iterator<Item = &'a SmartBigRational>,
775 {
776 iter.fold(Self::one(), |acc, x| acc * x)
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783 use rand::seq::IndexedRandom;
784
785 fn get_positive_test_values() -> Vec<SmartBigRational> {
786 let mut result = Vec::new();
787 for i in 0..=30 {
788 result.push(SmartBigRational::ratio(1 << i, 1u32));
789 }
790 for i in 0..=30 {
791 result.push(SmartBigRational::ratio(1, 1u32 << i));
792 }
793 for i in 0..=30 {
794 result.push(SmartBigRational::ratio(0x7FFF_FFFF - (1 << i), 1u32));
795 }
796 for i in 0..=30 {
797 result.push(SmartBigRational::ratio(1, 0x7FFF_FFFF - (1u32 << i)));
798 }
799 result
800 }
801
802 fn loop_check1<T>(test_values: &[T], f: impl Fn(&T)) {
803 for a in test_values {
804 f(a);
805 }
806 }
807
808 fn loop_check2<T>(test_values: &[T], f: impl Fn(&T, &T)) {
809 for a in test_values {
810 for b in test_values {
811 f(a, b);
812 }
813 }
814 }
815
816 fn loop_check3<T>(test_values: &[T], num_samples: Option<usize>, f: impl Fn(&T, &T, &T)) {
817 match num_samples {
818 None => {
819 for a in test_values {
821 for b in test_values {
822 for c in test_values {
823 f(a, b, c);
824 }
825 }
826 }
827 }
828 Some(n) => {
829 let mut rng = rand::rng();
832
833 for _ in 0..n {
834 let a = test_values.choose(&mut rng).unwrap();
835 let b = test_values.choose(&mut rng).unwrap();
836 let c = test_values.choose(&mut rng).unwrap();
837 f(a, b, c);
838 }
839 }
840 }
841 }
842
843 #[test]
844 fn test_is_zero() {
845 let test_values = get_positive_test_values();
846 assert!(SmartBigRational::ZERO.is_zero());
847 assert!(!SmartBigRational::ONE.is_zero());
848 loop_check1(&test_values, |a| {
849 assert!(!a.is_zero(), "{a} is zero");
850 });
851 }
852
853 #[test]
854 fn test_zero_is_add_neutral() {
855 let test_values = get_positive_test_values();
856 loop_check1(&test_values, |a| {
857 assert_eq!(&(a + SmartBigRational::ZERO), a, "a + 0 != a for {a}");
858 assert_eq!(&(SmartBigRational::ZERO + a), a, "0 + a != a for {a}");
859 assert_eq!(&(a - SmartBigRational::ZERO), a, "a - 0 != a for {a}");
860 })
861 }
862
863 #[test]
864 fn test_add_is_commutative() {
865 let test_values = get_positive_test_values();
866 loop_check2(&test_values, |a, b| {
867 assert_eq!(a + b, b + a, "a + b != b + a for {a}, {b}");
868 })
869 }
870
871 #[test]
872 fn test_add_is_associative() {
873 let test_values = get_positive_test_values();
874 loop_check3(&test_values, None, |a, b, c| {
875 assert_eq!(
876 (a + b) + c,
877 a + (b + c),
878 "(a + b) + c != a + (b + c) for {a}, {b}, {c}"
879 );
880 })
881 }
882
883 #[test]
884 fn test_opposite() {
885 let test_values = get_positive_test_values();
886 loop_check1(&test_values, |a| {
887 assert_eq!(&-(-a), a, "-(-a) != a for {a}");
888 assert_eq!(
889 &(SmartBigRational::ZERO - (SmartBigRational::ZERO - a)),
890 a,
891 "0 - (0 - a) != a for {a}"
892 );
893 });
894 }
895
896 #[test]
897 fn test_sub_self() {
898 let test_values = get_positive_test_values();
899 loop_check1(&test_values, |a| {
900 assert_eq!(a - a, SmartBigRational::ZERO, "a - a != 0 for {a}");
901 });
902 }
903
904 #[test]
905 fn test_add_sub() {
906 let test_values = get_positive_test_values();
907 loop_check2(&test_values, |a, b| {
908 assert_eq!(&((a + b) - b), a, "(a + b) - b != a for {a}, {b}");
909 });
910 }
911
912 #[test]
913 fn test_sub_add() {
914 let test_values = get_positive_test_values();
915 loop_check2(&test_values, |a, b| {
916 assert_eq!(&((a - b) + b), a, "(a - b) + b != a for {a}, {b}");
917 });
918 }
919
920 #[test]
921 fn test_one_is_mul_neutral() {
922 let test_values = get_positive_test_values();
923 loop_check1(&test_values, |a| {
924 assert_eq!(&(a * SmartBigRational::ONE), a, "a * 1 != a for {a}");
925 assert_eq!(&(SmartBigRational::ONE * a), a, "1 * a != a for {a}");
926 })
927 }
928
929 #[test]
930 fn test_mul_is_commutative() {
931 let test_values = get_positive_test_values();
932 loop_check2(&test_values, |a, b| {
933 assert_eq!(a * b, b * a, "a * b != b * a for {a}, {b}");
934 })
935 }
936
937 #[test]
938 fn test_mul_is_associative() {
939 let test_values = get_positive_test_values();
940 loop_check3(&test_values, None, |a, b, c| {
941 assert_eq!(
942 (a * b) * c,
943 a * (b * c),
944 "(a * b) * c != a * (b * c) for {a}, {b}, {c}"
945 );
946 })
947 }
948
949 #[test]
950 fn test_mul_is_distributive() {
951 let test_values = get_positive_test_values();
952 loop_check3(&test_values, None, |a, b, c| {
953 assert_eq!(
954 a * (b + c),
955 (a * b) + (a * c),
956 "a * (b + c) != (a * b) + (a * c) for {a}, {b}, {c}"
957 );
958 })
959 }
960
961 #[test]
962 fn test_one_is_div_neutral() {
963 let test_values = get_positive_test_values();
964 loop_check1(&test_values, |a| {
965 assert_eq!(&(a / SmartBigRational::ONE), a, "a / 1 != a for {a}");
966 })
967 }
968
969 #[test]
970 fn test_div_self() {
971 let test_values = get_positive_test_values();
972 loop_check1(&test_values, |a| {
973 assert_eq!(a / a, SmartBigRational::ONE, "a / a != 1 for {a}");
974 });
975 }
976
977 #[test]
978 fn test_mul_div() {
979 let test_values = get_positive_test_values();
980 loop_check2(&test_values, |a, b| {
981 assert_eq!(&((a * b) / b), a, "(a * b) / b != a for {a}, {b}");
982 });
983 }
984}