1#![forbid(missing_docs, unsafe_code)]
23
24mod denom;
25mod denom_array;
26mod denom_sparse;
27mod primes;
28mod util;
29
30pub use denom::{Denom, DenomRef};
31pub use denom_array::{DenomArray, DenomArray24};
32pub use denom_sparse::{DenomSparse6542, DenomSparseU16};
33use num_bigint::{BigInt, BigUint, Sign};
34use num_rational::BigRational;
35use num_traits::{One, Pow, Zero};
36use std::cmp::Ordering;
37use std::fmt::Display;
38use std::iter::{Product, Sum};
39use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
40
41#[derive(Clone, Debug)]
46pub struct SmartBigRational<D> {
47 num: BigInt,
48 denom: D,
49}
50
51impl<D: Denom> SmartBigRational<D> {
52 pub const ZERO: Self = Self {
54 num: BigInt::ZERO,
55 denom: D::ONE,
56 };
57
58 pub const ONE: Self = Self {
60 num: BigInt::ONE,
61 denom: D::ONE,
62 };
63
64 pub fn ratio(num: impl Into<BigInt>, denom: impl Into<D>) -> Self {
79 Self {
80 num: num.into(),
81 denom: denom.into(),
82 }
83 }
84
85 pub fn into_raw(self) -> (BigInt, D) {
87 (self.num, self.denom)
88 }
89
90 pub fn numer(&self) -> &BigInt {
92 &self.num
93 }
94
95 pub fn denom(&self) -> &D {
97 &self.denom
98 }
99
100 pub fn into_big_rational(self) -> BigRational {
102 self.into()
103 }
104
105 pub fn to_big_rational(&self) -> BigRational {
107 self.into()
108 }
109
110 pub fn reduce(&mut self) {
122 self.denom.gcd_reduce(&mut self.num);
123 }
124}
125
126impl<D: Denom> From<BigRational> for SmartBigRational<D> {
127 fn from(value: BigRational) -> Self {
128 let (num, denom) = value.into_raw();
129 let (sign, denom) = denom.into_parts();
130 assert_eq!(sign, Sign::Plus);
131 Self {
132 num,
133 denom: denom.into(),
134 }
135 }
136}
137
138impl<D: Denom> From<&BigRational> for SmartBigRational<D> {
139 fn from(value: &BigRational) -> Self {
140 let denom = value.denom();
141 assert_eq!(denom.sign(), Sign::Plus);
142 Self {
143 num: value.numer().clone(),
144 denom: denom.magnitude().into(),
145 }
146 }
147}
148
149impl<D: Denom> From<BigInt> for SmartBigRational<D> {
150 fn from(value: BigInt) -> Self {
151 Self {
152 num: value,
153 denom: D::ONE,
154 }
155 }
156}
157
158impl<D: Denom> From<SmartBigRational<D>> for BigRational {
159 fn from(value: SmartBigRational<D>) -> BigRational {
160 BigRational::new(value.num, value.denom.to_biguint().into())
161 }
162}
163
164impl<D: Denom> From<&SmartBigRational<D>> for BigRational {
165 fn from(value: &SmartBigRational<D>) -> BigRational {
166 BigRational::new(value.num.clone(), value.denom.to_biguint().into())
167 }
168}
169
170impl<D: Denom> PartialEq for SmartBigRational<D> {
171 fn eq(&self, rhs: &Self) -> bool {
172 self.num.sign() == rhs.num.sign()
173 && self.num.magnitude() * rhs.denom.to_biguint()
174 == rhs.num.magnitude() * self.denom.to_biguint()
175 }
176}
177
178impl<D: Denom> Eq for SmartBigRational<D> {}
179
180impl<D: Denom> PartialOrd for SmartBigRational<D> {
181 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
182 Some(self.cmp(rhs))
183 }
184}
185
186impl<D: Denom> Ord for SmartBigRational<D> {
187 fn cmp(&self, rhs: &Self) -> Ordering {
188 match (self.num.sign(), rhs.num.sign()) {
189 (Sign::Plus, Sign::Plus) => (self.num.magnitude() * rhs.denom.to_biguint())
190 .cmp(&(rhs.num.magnitude() * self.denom.to_biguint())),
191 (Sign::Plus, Sign::NoSign) => Ordering::Greater,
192 (Sign::Plus, Sign::Minus) => Ordering::Greater,
193 (Sign::NoSign, Sign::Plus) => Ordering::Less,
194 (Sign::NoSign, Sign::NoSign) => Ordering::Equal,
195 (Sign::NoSign, Sign::Minus) => Ordering::Greater,
196 (Sign::Minus, Sign::Plus) => Ordering::Less,
197 (Sign::Minus, Sign::NoSign) => Ordering::Less,
198 (Sign::Minus, Sign::Minus) => (rhs.num.magnitude() * self.denom.to_biguint())
199 .cmp(&(self.num.magnitude() * rhs.denom.to_biguint())),
200 }
201 }
202}
203
204impl<D: Denom> Display for SmartBigRational<D> {
205 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
206 Display::fmt(&self.to_big_rational(), f)
207 }
208}
209
210impl<D: Denom> Zero for SmartBigRational<D> {
211 fn zero() -> Self {
212 Self::ZERO
213 }
214
215 fn is_zero(&self) -> bool {
216 self.num.is_zero()
217 }
218}
219
220impl<D: Denom> One for SmartBigRational<D> {
221 fn one() -> Self {
222 Self::ONE
223 }
224}
225
226impl<D: Denom> Neg for SmartBigRational<D> {
227 type Output = Self;
228
229 fn neg(self) -> Self {
230 Self {
231 num: -self.num,
232 denom: self.denom,
233 }
234 }
235}
236
237impl<D: Denom> Neg for &SmartBigRational<D> {
238 type Output = SmartBigRational<D>;
239
240 fn neg(self) -> SmartBigRational<D> {
241 SmartBigRational {
242 num: -&self.num,
243 denom: self.denom.clone(),
244 }
245 }
246}
247
248impl<D: Denom> Pow<u32> for SmartBigRational<D> {
249 type Output = Self;
250
251 fn pow(self, rhs: u32) -> Self {
252 Self {
253 num: self.num.pow(rhs),
254 denom: self.denom.pow(rhs),
255 }
256 }
257}
258
259impl<D: Denom> Pow<u32> for &SmartBigRational<D>
260where
261 for<'a> &'a D: DenomRef<D>,
262{
263 type Output = SmartBigRational<D>;
264
265 fn pow(self, rhs: u32) -> SmartBigRational<D> {
266 SmartBigRational {
267 num: Pow::pow(&self.num, rhs),
268 denom: Pow::pow(&self.denom, rhs),
269 }
270 }
271}
272
273impl<D: Denom> Add for SmartBigRational<D> {
274 type Output = Self;
275
276 fn add(mut self, mut rhs: Self) -> Self {
277 let denom = D::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
278 Self {
279 num: self.num + rhs.num,
280 denom,
281 }
282 }
283}
284
285impl<D: Denom> Add<&Self> for SmartBigRational<D> {
286 type Output = Self;
287
288 fn add(mut self, rhs: &Self) -> Self {
289 let mut rhs_num = rhs.num.clone();
290 let denom = D::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
291 Self {
292 num: self.num + rhs_num,
293 denom,
294 }
295 }
296}
297
298impl<D: Denom> Add for &SmartBigRational<D> {
299 type Output = SmartBigRational<D>;
300
301 fn add(self, rhs: Self) -> SmartBigRational<D> {
302 let mut num = self.num.clone();
303 let mut rhs_num = rhs.num.clone();
304 let denom = D::normalize(&mut num, &mut rhs_num, &self.denom, &rhs.denom);
305 SmartBigRational {
306 num: num + rhs_num,
307 denom,
308 }
309 }
310}
311
312impl<D: Denom> Add<SmartBigRational<D>> for &SmartBigRational<D> {
313 type Output = SmartBigRational<D>;
314
315 fn add(self, mut rhs: SmartBigRational<D>) -> SmartBigRational<D> {
316 let mut num = self.num.clone();
317 let denom = D::normalize(&mut num, &mut rhs.num, &self.denom, &rhs.denom);
318 SmartBigRational {
319 num: num + rhs.num,
320 denom,
321 }
322 }
323}
324
325impl<D: Denom> AddAssign for SmartBigRational<D> {
326 fn add_assign(&mut self, mut rhs: Self) {
327 self.denom = D::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
328 self.num += rhs.num;
329 }
330}
331
332impl<D: Denom> AddAssign<&Self> for SmartBigRational<D> {
333 fn add_assign(&mut self, rhs: &Self) {
334 let mut rhs_num = rhs.num.clone();
335 self.denom = D::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
336 self.num += rhs_num;
337 }
338}
339
340impl<D: Denom> Add<BigInt> for SmartBigRational<D>
341where
342 BigInt: for<'a> MulAssign<&'a D>,
343{
344 type Output = Self;
345
346 fn add(self, mut rhs: BigInt) -> Self {
347 rhs *= &self.denom;
348 Self {
349 num: self.num + rhs,
350 denom: self.denom,
351 }
352 }
353}
354
355impl<D: Denom> Add<&BigInt> for SmartBigRational<D>
356where
357 for<'a> &'a D: DenomRef<D>,
358{
359 type Output = Self;
360
361 fn add(self, rhs: &BigInt) -> Self {
362 Self {
363 num: self.num + &self.denom * rhs,
364 denom: self.denom,
365 }
366 }
367}
368
369impl<D: Denom> Add<BigInt> for &SmartBigRational<D>
370where
371 BigInt: for<'a> MulAssign<&'a D>,
372{
373 type Output = SmartBigRational<D>;
374
375 fn add(self, mut rhs: BigInt) -> SmartBigRational<D> {
376 rhs *= &self.denom;
377 SmartBigRational {
378 num: &self.num + rhs,
379 denom: self.denom.clone(),
380 }
381 }
382}
383
384impl<D: Denom> Add<&BigInt> for &SmartBigRational<D>
385where
386 for<'a> &'a D: DenomRef<D>,
387{
388 type Output = SmartBigRational<D>;
389
390 fn add(self, rhs: &BigInt) -> SmartBigRational<D> {
391 SmartBigRational {
392 num: &self.num + &self.denom * rhs,
393 denom: self.denom.clone(),
394 }
395 }
396}
397
398impl<D: Denom> AddAssign<BigInt> for SmartBigRational<D>
399where
400 BigInt: for<'a> MulAssign<&'a D>,
401{
402 fn add_assign(&mut self, mut rhs: BigInt) {
403 rhs *= &self.denom;
404 self.num += rhs;
405 }
406}
407
408impl<D: Denom> AddAssign<&BigInt> for SmartBigRational<D>
409where
410 for<'a> &'a D: DenomRef<D>,
411{
412 fn add_assign(&mut self, rhs: &BigInt) {
413 self.num += &self.denom * rhs;
414 }
415}
416
417impl<D: Denom> Sub for SmartBigRational<D> {
418 type Output = Self;
419
420 fn sub(mut self, mut rhs: Self) -> Self {
421 let denom = D::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
422 Self {
423 num: self.num - rhs.num,
424 denom,
425 }
426 }
427}
428
429impl<D: Denom> Sub<&Self> for SmartBigRational<D> {
430 type Output = Self;
431
432 fn sub(mut self, rhs: &Self) -> Self {
433 let mut rhs_num = rhs.num.clone();
434 let denom = D::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
435 Self {
436 num: self.num - rhs_num,
437 denom,
438 }
439 }
440}
441
442impl<D: Denom> Sub for &SmartBigRational<D> {
443 type Output = SmartBigRational<D>;
444
445 fn sub(self, rhs: Self) -> SmartBigRational<D> {
446 let mut num = self.num.clone();
447 let mut rhs_num = rhs.num.clone();
448 let denom = D::normalize(&mut num, &mut rhs_num, &self.denom, &rhs.denom);
449 SmartBigRational {
450 num: num - rhs_num,
451 denom,
452 }
453 }
454}
455
456impl<D: Denom> Sub<SmartBigRational<D>> for &SmartBigRational<D> {
457 type Output = SmartBigRational<D>;
458
459 fn sub(self, mut rhs: SmartBigRational<D>) -> SmartBigRational<D> {
460 let mut num = self.num.clone();
461 let denom = D::normalize(&mut num, &mut rhs.num, &self.denom, &rhs.denom);
462 SmartBigRational {
463 num: num - rhs.num,
464 denom,
465 }
466 }
467}
468
469impl<D: Denom> SubAssign for SmartBigRational<D> {
470 fn sub_assign(&mut self, mut rhs: Self) {
471 self.denom = D::normalize(&mut self.num, &mut rhs.num, &self.denom, &rhs.denom);
472 self.num -= rhs.num;
473 }
474}
475
476impl<D: Denom> SubAssign<&Self> for SmartBigRational<D> {
477 fn sub_assign(&mut self, rhs: &Self) {
478 let mut rhs_num = rhs.num.clone();
479 self.denom = D::normalize(&mut self.num, &mut rhs_num, &self.denom, &rhs.denom);
480 self.num -= rhs_num;
481 }
482}
483
484impl<D: Denom> Sub<BigInt> for SmartBigRational<D>
485where
486 BigInt: for<'a> MulAssign<&'a D>,
487{
488 type Output = Self;
489
490 fn sub(self, mut rhs: BigInt) -> Self {
491 rhs *= &self.denom;
492 Self {
493 num: self.num - rhs,
494 denom: self.denom,
495 }
496 }
497}
498
499impl<D: Denom> Sub<&BigInt> for SmartBigRational<D>
500where
501 for<'a> &'a D: DenomRef<D>,
502{
503 type Output = Self;
504
505 fn sub(self, rhs: &BigInt) -> Self {
506 Self {
507 num: self.num - &self.denom * rhs,
508 denom: self.denom,
509 }
510 }
511}
512
513impl<D: Denom> Sub<BigInt> for &SmartBigRational<D>
514where
515 BigInt: for<'a> MulAssign<&'a D>,
516{
517 type Output = SmartBigRational<D>;
518
519 fn sub(self, mut rhs: BigInt) -> SmartBigRational<D> {
520 rhs *= &self.denom;
521 SmartBigRational {
522 num: &self.num - rhs,
523 denom: self.denom.clone(),
524 }
525 }
526}
527
528impl<D: Denom> Sub<&BigInt> for &SmartBigRational<D>
529where
530 for<'a> &'a D: DenomRef<D>,
531{
532 type Output = SmartBigRational<D>;
533
534 fn sub(self, rhs: &BigInt) -> SmartBigRational<D> {
535 SmartBigRational {
536 num: &self.num - &self.denom * rhs,
537 denom: self.denom.clone(),
538 }
539 }
540}
541
542impl<D: Denom> SubAssign<BigInt> for SmartBigRational<D>
543where
544 BigInt: for<'a> MulAssign<&'a D>,
545{
546 fn sub_assign(&mut self, mut rhs: BigInt) {
547 rhs *= &self.denom;
548 self.num -= rhs;
549 }
550}
551
552impl<D: Denom> SubAssign<&BigInt> for SmartBigRational<D>
553where
554 for<'a> &'a D: DenomRef<D>,
555{
556 fn sub_assign(&mut self, rhs: &BigInt) {
557 self.num -= &self.denom * rhs;
558 }
559}
560
561impl<D: Denom> Mul for SmartBigRational<D> {
562 type Output = Self;
563
564 fn mul(self, rhs: Self) -> Self {
565 Self {
566 num: self.num * rhs.num,
567 denom: self.denom * rhs.denom,
568 }
569 }
570}
571
572impl<D: Denom> Mul<&Self> for SmartBigRational<D> {
573 type Output = Self;
574
575 fn mul(self, rhs: &Self) -> Self {
576 Self {
577 num: self.num * &rhs.num,
578 denom: self.denom * &rhs.denom,
579 }
580 }
581}
582
583impl<D: Denom> Mul for &SmartBigRational<D>
584where
585 for<'a> &'a D: DenomRef<D>,
586{
587 type Output = SmartBigRational<D>;
588
589 fn mul(self, rhs: Self) -> SmartBigRational<D> {
590 SmartBigRational {
591 num: &self.num * &rhs.num,
592 denom: &self.denom * &rhs.denom,
593 }
594 }
595}
596
597impl<D: Denom> Mul<SmartBigRational<D>> for &SmartBigRational<D>
598where
599 for<'a> &'a D: DenomRef<D>,
600{
601 type Output = SmartBigRational<D>;
602
603 fn mul(self, rhs: SmartBigRational<D>) -> SmartBigRational<D> {
604 SmartBigRational {
605 num: &self.num * rhs.num,
606 denom: &self.denom * rhs.denom,
607 }
608 }
609}
610
611impl<D: Denom> MulAssign for SmartBigRational<D> {
612 fn mul_assign(&mut self, rhs: Self) {
613 self.num *= rhs.num;
614 self.denom *= rhs.denom;
615 }
616}
617
618impl<D: Denom> MulAssign<&Self> for SmartBigRational<D> {
619 fn mul_assign(&mut self, rhs: &Self) {
620 self.num *= &rhs.num;
621 self.denom *= &rhs.denom;
622 }
623}
624
625impl<D: Denom> Mul<BigInt> for SmartBigRational<D> {
626 type Output = Self;
627
628 fn mul(self, rhs: BigInt) -> Self {
629 Self {
630 num: self.num * rhs,
631 denom: self.denom,
632 }
633 }
634}
635
636impl<D: Denom> Mul<&BigInt> for SmartBigRational<D> {
637 type Output = Self;
638
639 fn mul(self, rhs: &BigInt) -> Self {
640 Self {
641 num: self.num * rhs,
642 denom: self.denom,
643 }
644 }
645}
646
647impl<D: Denom> Mul<BigInt> for &SmartBigRational<D> {
648 type Output = SmartBigRational<D>;
649
650 fn mul(self, rhs: BigInt) -> SmartBigRational<D> {
651 SmartBigRational {
652 num: &self.num * rhs,
653 denom: self.denom.clone(),
654 }
655 }
656}
657
658impl<D: Denom> Mul<&BigInt> for &SmartBigRational<D> {
659 type Output = SmartBigRational<D>;
660
661 fn mul(self, rhs: &BigInt) -> SmartBigRational<D> {
662 SmartBigRational {
663 num: &self.num * rhs,
664 denom: self.denom.clone(),
665 }
666 }
667}
668
669impl<D: Denom> MulAssign<BigInt> for SmartBigRational<D> {
670 fn mul_assign(&mut self, rhs: BigInt) {
671 self.num *= rhs;
672 }
673}
674
675impl<D: Denom> MulAssign<&BigInt> for SmartBigRational<D> {
676 fn mul_assign(&mut self, rhs: &BigInt) {
677 self.num *= rhs;
678 }
679}
680
681impl<D: Denom> Div for SmartBigRational<D> {
682 type Output = Self;
683
684 fn div(self, rhs: Self) -> Self {
685 let (rhs_sign, rhs_num) = rhs.num.into_parts();
686 let rhs_denom = BigInt::from_biguint(rhs_sign, rhs.denom.into());
687 Self {
688 num: self.num * rhs_denom,
689 denom: self.denom * D::from(rhs_num),
690 }
691 }
692}
693
694impl<D: Denom> Div<&Self> for SmartBigRational<D>
695where
696 for<'a> &'a D: DenomRef<D>,
697{
698 type Output = Self;
699
700 fn div(self, rhs: &Self) -> Self {
701 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), (&rhs.denom).into());
702 Self {
703 num: self.num * rhs_denom,
704 denom: self.denom * D::from(rhs.num.magnitude()),
705 }
706 }
707}
708
709impl<D: Denom> Div for &SmartBigRational<D>
710where
711 for<'a> &'a D: DenomRef<D>,
712{
713 type Output = SmartBigRational<D>;
714
715 fn div(self, rhs: Self) -> SmartBigRational<D> {
716 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), (&rhs.denom).into());
717 SmartBigRational {
718 num: &self.num * rhs_denom,
719 denom: &self.denom * D::from(rhs.num.magnitude()),
720 }
721 }
722}
723
724impl<D: Denom> Div<SmartBigRational<D>> for &SmartBigRational<D>
725where
726 for<'a> &'a D: DenomRef<D>,
727{
728 type Output = SmartBigRational<D>;
729
730 fn div(self, rhs: SmartBigRational<D>) -> SmartBigRational<D> {
731 let (rhs_sign, rhs_num) = rhs.num.into_parts();
732 let rhs_denom = BigInt::from_biguint(rhs_sign, rhs.denom.into());
733 SmartBigRational {
734 num: &self.num * rhs_denom,
735 denom: &self.denom * D::from(rhs_num),
736 }
737 }
738}
739
740impl<D: Denom> DivAssign for SmartBigRational<D> {
741 fn div_assign(&mut self, rhs: Self) {
742 let (rhs_sign, rhs_num) = rhs.num.into_parts();
743 let rhs_denom = BigInt::from_biguint(rhs_sign, rhs.denom.into());
744 self.num *= rhs_denom;
745 self.denom *= D::from(rhs_num);
746 }
747}
748
749impl<D: Denom> DivAssign<&Self> for SmartBigRational<D>
750where
751 for<'a> &'a D: DenomRef<D>,
752{
753 fn div_assign(&mut self, rhs: &Self) {
754 let rhs_denom = BigInt::from_biguint(rhs.num.sign(), (&rhs.denom).into());
755 self.num *= rhs_denom;
756 self.denom *= D::from(rhs.num.magnitude());
757 }
758}
759
760impl<D: Denom> Div<BigUint> for SmartBigRational<D> {
761 type Output = Self;
762
763 #[expect(clippy::suspicious_arithmetic_impl)]
764 fn div(self, rhs: BigUint) -> Self {
765 Self {
766 num: self.num,
767 denom: self.denom * D::from(rhs),
768 }
769 }
770}
771
772impl<D: Denom> Div<&BigUint> for SmartBigRational<D> {
773 type Output = Self;
774
775 #[expect(clippy::suspicious_arithmetic_impl)]
776 fn div(self, rhs: &BigUint) -> Self {
777 Self {
778 num: self.num,
779 denom: self.denom * D::from(rhs),
780 }
781 }
782}
783
784impl<D: Denom> Div<BigUint> for &SmartBigRational<D>
785where
786 for<'a> &'a D: DenomRef<D>,
787{
788 type Output = SmartBigRational<D>;
789
790 #[expect(clippy::suspicious_arithmetic_impl)]
791 fn div(self, rhs: BigUint) -> SmartBigRational<D> {
792 SmartBigRational {
793 num: self.num.clone(),
794 denom: &self.denom * D::from(rhs),
795 }
796 }
797}
798
799impl<D: Denom> Div<&BigUint> for &SmartBigRational<D>
800where
801 for<'a> &'a D: DenomRef<D>,
802{
803 type Output = SmartBigRational<D>;
804
805 #[expect(clippy::suspicious_arithmetic_impl)]
806 fn div(self, rhs: &BigUint) -> SmartBigRational<D> {
807 SmartBigRational {
808 num: self.num.clone(),
809 denom: &self.denom * D::from(rhs),
810 }
811 }
812}
813
814impl<D: Denom> DivAssign<BigUint> for SmartBigRational<D> {
815 #[expect(clippy::suspicious_op_assign_impl)]
816 fn div_assign(&mut self, rhs: BigUint) {
817 self.denom *= D::from(rhs);
818 }
819}
820
821impl<D: Denom> DivAssign<&BigUint> for SmartBigRational<D> {
822 #[expect(clippy::suspicious_op_assign_impl)]
823 fn div_assign(&mut self, rhs: &BigUint) {
824 self.denom *= D::from(rhs);
825 }
826}
827
828impl<D: Denom> Sum for SmartBigRational<D> {
829 fn sum<I>(iter: I) -> Self
830 where
831 I: Iterator<Item = Self>,
832 {
833 iter.fold(Self::ZERO, |acc, x| acc + x)
834 }
835}
836
837impl<'a, D: Denom> Sum<&'a Self> for SmartBigRational<D> {
838 fn sum<I>(iter: I) -> Self
839 where
840 I: Iterator<Item = &'a Self>,
841 {
842 iter.fold(Self::ZERO, |acc, x| acc + x)
843 }
844}
845
846impl<D: Denom> Product for SmartBigRational<D> {
847 fn product<I>(iter: I) -> Self
848 where
849 I: Iterator<Item = Self>,
850 {
851 iter.fold(Self::ONE, |acc, x| acc * x)
852 }
853}
854
855impl<'a, D: Denom> Product<&'a Self> for SmartBigRational<D> {
856 fn product<I>(iter: I) -> Self
857 where
858 I: Iterator<Item = &'a Self>,
859 {
860 iter.fold(Self::ONE, |acc, x| acc * x)
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use primes::ODD_PRIMES;
868 use rand::seq::IndexedRandom;
869 use std::fmt::Debug;
870
871 fn get_positive_test_values<D: Denom>() -> Vec<SmartBigRational<D>> {
872 let mut result = Vec::new();
873 for i in 0..=30 {
874 result.push(SmartBigRational::ratio(1 << i, 1u32));
875 }
876 for i in 0..=30 {
877 result.push(SmartBigRational::ratio(1, 1u32 << i));
878 }
879 for i in 0..=30 {
880 result.push(SmartBigRational::ratio(0x7FFF_FFFF - (1 << i), 1u32));
881 }
882 for i in 0..=30 {
883 result.push(SmartBigRational::ratio(1, 0x7FFF_FFFF - (1u32 << i)));
884 }
885 for &p in ODD_PRIMES.iter().take(30) {
886 result.push(SmartBigRational::ratio(1, p));
887 }
888 let prime_product = (0..=30)
889 .map(|i| ODD_PRIMES[i])
890 .fold(BigUint::ONE, |acc, x| acc * x);
891 result.push(SmartBigRational::ratio(1, prime_product));
892 result
893 }
894
895 fn loop_check1<T>(test_values: &[T], f: impl Fn(&T)) {
896 for a in test_values {
897 f(a);
898 }
899 }
900
901 fn loop_check2<T>(test_values: &[T], f: impl Fn(&T, &T)) {
902 for a in test_values {
903 for b in test_values {
904 f(a, b);
905 }
906 }
907 }
908
909 fn loop_check3<T>(test_values: &[T], num_samples: Option<usize>, f: impl Fn(&T, &T, &T)) {
910 match num_samples {
911 None => {
912 for a in test_values {
914 for b in test_values {
915 for c in test_values {
916 f(a, b, c);
917 }
918 }
919 }
920 }
921 Some(n) => {
922 let mut rng = rand::rng();
925
926 for _ in 0..n {
927 let a = test_values.choose(&mut rng).unwrap();
928 let b = test_values.choose(&mut rng).unwrap();
929 let c = test_values.choose(&mut rng).unwrap();
930 f(a, b, c);
931 }
932 }
933 }
934 }
935
936 macro_rules! tests {
937 (
938 $mod:ident,
939 $denom:ty,
940 $( $case:ident ,)*
941 ) => {
942 mod $mod {
943 use super::*;
944
945 $(
946 #[test]
947 fn $case() {
948 $crate::tests::$case::<$denom>();
949 }
950 )*
951 }
952 };
953 }
954
955 macro_rules! all_tests {
956 (
957 $mod:ident,
958 $denom:ty
959 ) => {
960 tests!(
961 $mod,
962 $denom,
963 test_is_zero,
964 test_zero_is_add_neutral,
965 test_add_is_commutative,
966 test_add_is_associative,
967 test_opposite,
968 test_sub_self,
969 test_add_sub,
970 test_sub_add,
971 test_one_is_mul_neutral,
972 test_mul_is_commutative,
973 test_mul_is_associative,
974 test_mul_is_distributive,
975 test_one_is_div_neutral,
976 test_div_self,
977 test_mul_div,
978 test_sum,
979 test_product,
980 );
981 };
982 }
983
984 all_tests!(denom_array24, DenomArray<24>);
985 all_tests!(denom_array200, DenomArray<200>);
986 all_tests!(denom_sparse24, DenomSparseU16<24, 8>);
987 all_tests!(denom_sparse6542, DenomSparseU16<6542, 8>);
988
989 fn test_is_zero<D: Denom + Debug>()
990 where
991 for<'a> &'a D: DenomRef<D>,
992 {
993 let test_values = get_positive_test_values::<D>();
994 assert!(SmartBigRational::<DenomArray24>::ZERO.is_zero());
995 assert!(!SmartBigRational::<DenomArray24>::ONE.is_zero());
996 loop_check1(&test_values, |a| {
997 assert!(!a.is_zero(), "{a} is zero");
998 });
999 }
1000
1001 fn test_zero_is_add_neutral<D: Denom + Debug>()
1002 where
1003 for<'a> &'a D: DenomRef<D>,
1004 {
1005 let test_values = get_positive_test_values::<D>();
1006 loop_check1(&test_values, |a| {
1007 assert_eq!(&(a + SmartBigRational::ZERO), a, "a + 0 != a for {a}");
1008 assert_eq!(&(SmartBigRational::ZERO + a), a, "0 + a != a for {a}");
1009 assert_eq!(&(a - SmartBigRational::ZERO), a, "a - 0 != a for {a}");
1010 })
1011 }
1012
1013 fn test_add_is_commutative<D: Denom + Debug>()
1014 where
1015 for<'a> &'a D: DenomRef<D>,
1016 {
1017 let test_values = get_positive_test_values::<D>();
1018 loop_check2(&test_values, |a, b| {
1019 assert_eq!(a + b, b + a, "a + b != b + a for {a}, {b}");
1020 })
1021 }
1022
1023 fn test_add_is_associative<D: Denom + Debug>()
1024 where
1025 for<'a> &'a D: DenomRef<D>,
1026 {
1027 let test_values = get_positive_test_values::<D>();
1028 loop_check3(&test_values, None, |a, b, c| {
1029 assert_eq!(
1030 (a + b) + c,
1031 a + (b + c),
1032 "(a + b) + c != a + (b + c) for {a}, {b}, {c}"
1033 );
1034 })
1035 }
1036
1037 fn test_opposite<D: Denom + Debug>()
1038 where
1039 for<'a> &'a D: DenomRef<D>,
1040 {
1041 let test_values = get_positive_test_values::<D>();
1042 loop_check1(&test_values, |a| {
1043 assert_eq!(&-(-a), a, "-(-a) != a for {a}");
1044 assert_eq!(
1045 &(SmartBigRational::ZERO - (SmartBigRational::ZERO - a)),
1046 a,
1047 "0 - (0 - a) != a for {a}"
1048 );
1049 });
1050 }
1051
1052 #[expect(clippy::eq_op)]
1053 fn test_sub_self<D: Denom + Debug>()
1054 where
1055 for<'a> &'a D: DenomRef<D>,
1056 {
1057 let test_values = get_positive_test_values::<D>();
1058 loop_check1(&test_values, |a| {
1059 assert_eq!(a - a, SmartBigRational::ZERO, "a - a != 0 for {a}");
1060 });
1061 }
1062
1063 fn test_add_sub<D: Denom + Debug>()
1064 where
1065 for<'a> &'a D: DenomRef<D>,
1066 {
1067 let test_values = get_positive_test_values::<D>();
1068 loop_check2(&test_values, |a, b| {
1069 assert_eq!(&((a + b) - b), a, "(a + b) - b != a for {a}, {b}");
1070 });
1071 }
1072
1073 fn test_sub_add<D: Denom + Debug>()
1074 where
1075 for<'a> &'a D: DenomRef<D>,
1076 {
1077 let test_values = get_positive_test_values::<D>();
1078 loop_check2(&test_values, |a, b| {
1079 assert_eq!(&((a - b) + b), a, "(a - b) + b != a for {a}, {b}");
1080 });
1081 }
1082
1083 fn test_one_is_mul_neutral<D: Denom + Debug>()
1084 where
1085 for<'a> &'a D: DenomRef<D>,
1086 {
1087 let test_values = get_positive_test_values::<D>();
1088 loop_check1(&test_values, |a| {
1089 assert_eq!(&(a * SmartBigRational::ONE), a, "a * 1 != a for {a}");
1090 assert_eq!(&(SmartBigRational::ONE * a), a, "1 * a != a for {a}");
1091 })
1092 }
1093
1094 fn test_mul_is_commutative<D: Denom + Debug>()
1095 where
1096 for<'a> &'a D: DenomRef<D>,
1097 {
1098 let test_values = get_positive_test_values::<D>();
1099 loop_check2(&test_values, |a, b| {
1100 assert_eq!(a * b, b * a, "a * b != b * a for {a}, {b}");
1101 })
1102 }
1103
1104 fn test_mul_is_associative<D: Denom + Debug>()
1105 where
1106 for<'a> &'a D: DenomRef<D>,
1107 {
1108 let test_values = get_positive_test_values::<D>();
1109 loop_check3(&test_values, None, |a, b, c| {
1110 assert_eq!(
1111 (a * b) * c,
1112 a * (b * c),
1113 "(a * b) * c != a * (b * c) for {a}, {b}, {c}"
1114 );
1115 })
1116 }
1117
1118 fn test_mul_is_distributive<D: Denom + Debug>()
1119 where
1120 for<'a> &'a D: DenomRef<D>,
1121 {
1122 let test_values = get_positive_test_values::<D>();
1123 loop_check3(&test_values, None, |a, b, c| {
1124 assert_eq!(
1125 a * (b + c),
1126 (a * b) + (a * c),
1127 "a * (b + c) != (a * b) + (a * c) for {a}, {b}, {c}"
1128 );
1129 })
1130 }
1131
1132 fn test_one_is_div_neutral<D: Denom + Debug>()
1133 where
1134 for<'a> &'a D: DenomRef<D>,
1135 {
1136 let test_values = get_positive_test_values::<D>();
1137 loop_check1(&test_values, |a| {
1138 assert_eq!(&(a / SmartBigRational::ONE), a, "a / 1 != a for {a}");
1139 })
1140 }
1141
1142 #[expect(clippy::eq_op)]
1143 fn test_div_self<D: Denom + Debug>()
1144 where
1145 for<'a> &'a D: DenomRef<D>,
1146 {
1147 let test_values = get_positive_test_values::<D>();
1148 loop_check1(&test_values, |a| {
1149 assert_eq!(a / a, SmartBigRational::ONE, "a / a != 1 for {a}");
1150 });
1151 }
1152
1153 fn test_mul_div<D: Denom + Debug>()
1154 where
1155 for<'a> &'a D: DenomRef<D>,
1156 {
1157 let test_values = get_positive_test_values::<D>();
1158 loop_check2(&test_values, |a, b| {
1159 assert_eq!(&((a * b) / b), a, "(a * b) / b != a for {a}, {b}");
1160 });
1161 }
1162
1163 fn test_sum<D: Denom + Debug>()
1164 where
1165 for<'a> &'a D: DenomRef<D>,
1166 {
1167 let test_values = get_positive_test_values::<D>();
1168 let mut expected = SmartBigRational::ZERO;
1169 for x in &test_values {
1170 expected += x;
1171 }
1172 assert_eq!(
1173 test_values.iter().sum::<SmartBigRational<_>>(),
1174 expected,
1175 "[x, ..., y].sum() != x + ... + y for {test_values:?}"
1176 );
1177 }
1178
1179 fn test_product<D: Denom + Debug>()
1180 where
1181 for<'a> &'a D: DenomRef<D>,
1182 {
1183 let test_values = get_positive_test_values::<D>();
1184 let mut expected = SmartBigRational::ONE;
1185 for x in &test_values {
1186 expected *= x;
1187 }
1188 assert_eq!(
1189 test_values.iter().product::<SmartBigRational<_>>(),
1190 expected,
1191 "[x, ..., y].product() != x * ... * y for {test_values:?}"
1192 );
1193 }
1194}