stv_rs/arithmetic/
mod.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Module providing traits to abstract over the arithmetic needed for STV, and
16//! various implementations of this arithmetic.
17
18mod approx_rational;
19mod exact;
20mod fixed;
21mod fixed_big;
22mod float64;
23#[cfg(test)]
24mod ratio_i64;
25
26pub use approx_rational::ApproxRational;
27pub use fixed::{FixedDecimal9, Integer64};
28pub use fixed_big::BigFixedDecimal9;
29
30use num::traits::{One, Zero};
31use std::fmt::{Debug, Display};
32use std::iter::{Product, Sum};
33use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
34
35/// Trait representing integer arithmetic.
36///
37/// This is a lighter version of the `Integer` trait provided by the
38/// [`num` crate](https://crates.io/crates/num), here we only consider the
39/// arithmetic operations needed for STV.
40pub trait Integer:
41    Clone
42    + Debug
43    + PartialEq
44    + PartialOrd
45    + Zero
46    + One
47    + Product
48    + Add<Output = Self>
49    + Sub<Output = Self>
50    + Mul<Output = Self>
51where
52    for<'a> &'a Self: IntegerRef<Self>,
53{
54    /// Obtains an integer from a primitive `usize` integer.
55    fn from_usize(i: usize) -> Self;
56
57    /// Returns a set of positive values to use for testing.
58    #[cfg(test)]
59    fn get_positive_test_values() -> Vec<Self>;
60}
61
62/// Trait representing rational numbers w.r.t. an [`Integer`] type. Here we only
63/// consider the arithmetic operations needed for STV.
64pub trait Rational<I>:
65    Clone
66    + Display
67    + Debug
68    + PartialEq
69    + PartialOrd
70    + Zero
71    + One
72    + AddAssign
73    + SubAssign
74    + MulAssign
75    + Sum
76    + Product
77    + Add<Output = Self>
78    + Sub<Output = Self>
79    + Mul<Output = Self>
80    + Mul<I, Output = Self>
81    + Div<I, Output = Self>
82    + for<'a> AddAssign<&'a Self>
83    + for<'a> SubAssign<&'a Self>
84    + for<'a> MulAssign<&'a Self>
85    + for<'a> DivAssign<&'a I>
86    + for<'a> Sum<&'a Self>
87    + for<'a> Add<&'a Self, Output = Self>
88    + for<'a> Sub<&'a Self, Output = Self>
89    + for<'a> Mul<&'a Self, Output = Self>
90where
91    I: Integer,
92    for<'a> &'a I: IntegerRef<I>,
93    for<'a> &'a Self: RationalRef<&'a I, Self>,
94{
95    /// Obtains a number equal to the given integer.
96    fn from_int(i: I) -> Self;
97
98    /// Obtains a number equal to the given integer.
99    fn from_usize(i: usize) -> Self {
100        Self::from_int(I::from_usize(i))
101    }
102
103    /// Obtains a number equal to the ratio between the given numerator and
104    /// denominator.
105    fn ratio_i(num: I, denom: I) -> Self;
106
107    /// Obtains a number equal to the ratio between the given numerator and
108    /// denominator.
109    fn ratio(num: usize, denom: usize) -> Self {
110        Self::ratio_i(I::from_usize(num), I::from_usize(denom))
111    }
112
113    /// Converts a number into its floating-point approximation. This can be
114    /// useful to print approximation of large numbers.
115    fn to_f64(&self) -> f64;
116
117    /// Allows to customize equality assertion to inexact types such as [`f64`].
118    #[track_caller]
119    fn assert_eq(a: Self, b: Self, msg: &str) {
120        assert_eq!(a, b, "{msg}");
121    }
122
123    /// Minimal representable value for non-exact arithmetic.
124    fn epsilon() -> Self;
125
126    /// Whether this type represents exact arithmetic, i.e. [`Self::epsilon()`]
127    /// is zero.
128    fn is_exact() -> bool;
129
130    /// Description of the implemented arithmetic, e.g. "64-bit floating point
131    /// arithmetic".
132    fn description() -> &'static str;
133
134    /// Multiplication, rounding up for types that perform a rounding on this
135    /// operation.
136    fn mul_up(&self, rhs: &Self) -> Self {
137        self * rhs
138    }
139
140    /// Performs a division to obtain a keep factor. The division is rounded up
141    /// for types that cannot compute the result precisely. Additionally,
142    /// types may further round the result, which can be useful with exact
143    /// arithmetic, to avoid complexity explosion of rational numbers.
144    fn div_up_as_keep_factor(&self, rhs: &Self) -> Self;
145
146    /// Returns a set of positive values to use for testing.
147    #[cfg(test)]
148    fn get_positive_test_values() -> Vec<Self>;
149}
150
151/// Helper trait that integer references implement.
152pub trait IntegerRef<Output>:
153    Sized + Add<Self, Output = Output> + Sub<Self, Output = Output> + Mul<Self, Output = Output>
154{
155}
156
157/// Helper trait that rational references implement.
158pub trait RationalRef<RefI, Output>:
159    Sized
160    + Add<Self, Output = Output>
161    + Sub<Self, Output = Output>
162    + Mul<Self, Output = Output>
163    + Mul<RefI, Output = Output>
164    + Div<RefI, Output = Output>
165{
166}
167
168#[cfg(test)]
169pub(crate) mod test {
170    use super::*;
171    use ::test::Bencher;
172    use rand::distributions::{Distribution, Uniform};
173    use rand::seq::SliceRandom;
174    use rand::thread_rng;
175    use std::hint::black_box;
176    use std::marker::PhantomData;
177
178    /// Generates test functions for the given list of integer tests.
179    #[macro_export]
180    macro_rules! integer_tests {
181        ( $typei:ty, ) => {};
182        ( $typei:ty, $case:ident, $( $others:tt )* ) => {
183            #[test]
184            fn $case() {
185                $crate::arithmetic::test::IntegerTests::<$typei>::$case();
186            }
187
188            integer_tests!($typei, $($others)*);
189        };
190        ( $typei:ty, $case:ident => fail($msg:expr), $( $others:tt )* ) => {
191            #[test]
192            #[should_panic(expected = $msg)]
193            fn $case() {
194                $crate::arithmetic::test::IntegerTests::<$typei>::$case();
195            }
196
197            integer_tests!($typei, $($others)*);
198        };
199    }
200
201    /// Generates test functions for the given list of large integer tests.
202    #[macro_export]
203    macro_rules! big_integer_tests {
204        ( $typei:ty, $num_samples:expr, ) => {};
205        ( $typei:ty, $num_samples:expr, $case:ident, $( $others:tt )* ) => {
206            #[test]
207            fn $case() {
208                $crate::arithmetic::test::IntegerTests::<$typei>::$case($num_samples);
209            }
210
211            big_integer_tests!($typei, $num_samples, $($others)*);
212        };
213        ( $typei:ty, $num_samples:expr, $case:ident => fail($msg:expr), $( $others:tt )* ) => {
214            #[test]
215            #[should_panic(expected = $msg)]
216            fn $case() {
217                $crate::arithmetic::test::IntegerTests::<$typei>::$case($num_samples);
218            }
219
220            big_integer_tests!($typei, $num_samples, $($others)*);
221        };
222    }
223
224    /// Generates test functions for the given list of numeric tests
225    /// (parametrized by a pair of integer-rational types).
226    #[macro_export]
227    macro_rules! numeric_tests {
228        ( $typei:ty, $typer:ty, ) => {};
229        ( $typei:ty, $typer:ty, $case:ident, $( $others:tt )* ) => {
230            #[test]
231            fn $case() {
232                $crate::arithmetic::test::NumericTests::<$typei, $typer>::$case();
233            }
234
235            numeric_tests!($typei, $typer, $($others)*);
236        };
237        ( $typei:ty, $typer:ty, $case:ident => fail($msg:expr), $( $others:tt )* ) => {
238            #[test]
239            #[should_panic(expected = $msg)]
240            fn $case() {
241                $crate::arithmetic::test::NumericTests::<$typei, $typer>::$case();
242            }
243
244            numeric_tests!($typei, $typer, $($others)*);
245        };
246    }
247
248    /// Generates test functions for the given list of large numeric tests
249    /// (parametrized by a pair of integer-rational types).
250    #[macro_export]
251    macro_rules! big_numeric_tests {
252        ( $typei:ty, $typer:ty, $num_samples:expr, ) => {};
253        ( $typei:ty, $typer:ty, $num_samples:expr, $case:ident, $( $others:tt )* ) => {
254            #[test]
255            fn $case() {
256                $crate::arithmetic::test::NumericTests::<$typei, $typer>::$case($num_samples);
257            }
258
259            big_numeric_tests!($typei, $typer, $num_samples, $($others)*);
260        };
261        ( $typei:ty, $typer:ty, $num_samples:expr, $case:ident => fail($msg:expr), $( $others:tt )* ) => {
262            #[test]
263            #[should_panic(expected = $msg)]
264            fn $case() {
265                $crate::arithmetic::test::NumericTests::<$typei, $typer>::$case($num_samples);
266            }
267
268            big_numeric_tests!($typei, $typer, $num_samples, $($others)*);
269        };
270    }
271
272    /// Generates benchmark functions for the given list of numeric tests
273    /// (parametrized by a pair of integer-rational types).
274    #[macro_export]
275    macro_rules! numeric_benchmarks {
276        ( $typei:ty, $typer:ty, ) => {};
277        ( $typei:ty, $typer:ty, $case:ident, $( $others:tt )* ) => {
278            #[bench]
279            fn $case(b: &mut ::test::Bencher) {
280                $crate::arithmetic::test::NumericTests::<$typei, $typer>::$case(b);
281            }
282
283            numeric_benchmarks!($typei, $typer, $($others)*);
284        };
285    }
286
287    pub struct IntegerTests<I> {
288        _phantomi: PhantomData<I>,
289    }
290
291    impl<I> IntegerTests<I>
292    where
293        I: Integer + Display,
294        for<'a> &'a I: IntegerRef<I>,
295    {
296        pub fn testi_values_are_positive() {
297            let test_values = I::get_positive_test_values();
298            Self::loopi_check1(&test_values, |a| {
299                assert!(*a > I::zero(), "{a} is not positive");
300            });
301        }
302
303        pub fn testi_is_zero() {
304            let test_values = I::get_positive_test_values();
305            assert!(I::zero().is_zero());
306            assert!(!I::one().is_zero());
307            Self::loopi_check1(&test_values, |a| {
308                assert!(!a.is_zero(), "{a} is zero");
309            });
310        }
311
312        pub fn testi_zero_is_add_neutral() {
313            let test_values = I::get_positive_test_values();
314            Self::loopi_check1(&test_values, |a| {
315                assert_eq!(a + &I::zero(), *a, "a + 0 != a for {a}");
316                assert_eq!(&I::zero() + a, *a, "0 + a != a for {a}");
317                assert_eq!(a - &I::zero(), *a, "a - 0 != a for {a}");
318            })
319        }
320
321        #[allow(clippy::eq_op)]
322        pub fn testi_add_is_commutative() {
323            let test_values = I::get_positive_test_values();
324            Self::loopi_check2(&test_values, |a, b| {
325                assert_eq!(a + b, b + a, "a + b != b + a for {a}, {b}");
326            })
327        }
328
329        pub fn testi_add_is_associative(num_samples: Option<usize>) {
330            let test_values = I::get_positive_test_values();
331            Self::loopi_check3(&test_values, num_samples, |a, b, c| {
332                assert_eq!(
333                    &(a + b) + c,
334                    a + &(b + c),
335                    "(a + b) + c != a + (b + c) for {a}, {b}, {c}"
336                );
337            })
338        }
339
340        pub fn testi_opposite() {
341            let test_values = I::get_positive_test_values();
342            Self::loopi_check1(&test_values, |a| {
343                assert_eq!(
344                    I::zero() - (I::zero() - a.clone()),
345                    *a,
346                    "-(-a) != a for {a}"
347                );
348            });
349        }
350
351        #[allow(clippy::eq_op)]
352        pub fn testi_sub_self() {
353            let test_values = I::get_positive_test_values();
354            Self::loopi_check1(&test_values, |a| {
355                assert_eq!(a - a, I::zero(), "a - a != 0 for {a}");
356            });
357        }
358
359        pub fn testi_add_sub() {
360            let test_values = I::get_positive_test_values();
361            Self::loopi_check2(&test_values, |a, b| {
362                assert_eq!(&(a + b) - b, *a, "(a + b) - b != a for {a}, {b}");
363            });
364        }
365
366        pub fn testi_sub_add() {
367            let test_values = I::get_positive_test_values();
368            Self::loopi_check2(&test_values, |a, b| {
369                assert_eq!(&(a - b) + b, *a, "(a - b) + b != a for {a}, {b}");
370            });
371        }
372
373        pub fn testi_one_is_mul_neutral() {
374            let test_values = I::get_positive_test_values();
375            Self::loopi_check1(&test_values, |a| {
376                assert_eq!(a * &I::one(), *a, "a * 1 != a for {a}");
377                assert_eq!(&I::one() * a, *a, "1 * a != a for {a}");
378            })
379        }
380
381        pub fn testi_mul_is_commutative() {
382            let test_values = I::get_positive_test_values();
383            Self::loopi_check2(&test_values, |a, b| {
384                assert_eq!(a * b, b * a, "a * b != b * a for {a}, {b}");
385            })
386        }
387
388        pub fn testi_mul_is_associative(num_samples: Option<usize>) {
389            let test_values = I::get_positive_test_values();
390            Self::loopi_check3(&test_values, num_samples, |a, b, c| {
391                assert_eq!(
392                    &(a * b) * c,
393                    a * &(b * c),
394                    "(a * b) * c != a * (b * c) for {a}, {b}, {c}"
395                );
396            })
397        }
398
399        pub fn testi_mul_is_distributive(num_samples: Option<usize>) {
400            let test_values = I::get_positive_test_values();
401            Self::loopi_check3(&test_values, num_samples, |a, b, c| {
402                assert_eq!(
403                    a * &(b + c),
404                    &(a * b) + &(a * c),
405                    "a * (b + c) != (a * b) + (a * c) for {a}, {b}, {c}"
406                );
407            })
408        }
409
410        pub fn testi_product(num_samples: Option<usize>) {
411            let test_values = I::get_positive_test_values();
412            Self::loopi_check3(&test_values, num_samples, |a, b, c| {
413                assert_eq!(
414                    [a.clone(), b.clone(), c.clone()].into_iter().product::<I>(),
415                    &(a * b) * c,
416                    "[a, b, c].product() != a * b * c for {a}, {b}, {c}"
417                );
418            });
419        }
420
421        fn loopi_check1(test_values: &[I], f: impl Fn(&I)) {
422            for a in test_values {
423                f(a);
424            }
425        }
426
427        fn loopi_check2(test_values: &[I], f: impl Fn(&I, &I)) {
428            for a in test_values {
429                for b in test_values {
430                    f(a, b);
431                }
432            }
433        }
434
435        fn loopi_check3(test_values: &[I], num_samples: Option<usize>, f: impl Fn(&I, &I, &I)) {
436            match num_samples {
437                None => {
438                    // Exhaustive check.
439                    for a in test_values {
440                        for b in test_values {
441                            for c in test_values {
442                                f(a, b, c);
443                            }
444                        }
445                    }
446                }
447                Some(n) => {
448                    // Randomly sample values rather than conducting an exhaustive O(n^3) search on
449                    // the test values.
450                    let mut rng = thread_rng();
451
452                    for _ in 0..n {
453                        let a = test_values.choose(&mut rng).unwrap();
454                        let b = test_values.choose(&mut rng).unwrap();
455                        let c = test_values.choose(&mut rng).unwrap();
456                        f(a, b, c);
457                    }
458                }
459            }
460        }
461    }
462
463    pub struct NumericTests<I, R> {
464        _phantomi: PhantomData<I>,
465        _phantomr: PhantomData<R>,
466    }
467
468    impl<I, R> NumericTests<I, R>
469    where
470        I: Integer + Display,
471        for<'a> &'a I: IntegerRef<I>,
472        R: Rational<I>,
473        for<'a> &'a R: RationalRef<&'a I, R>,
474    {
475        pub fn test_values_are_positive() {
476            let test_values = R::get_positive_test_values();
477            Self::loop_check1(&test_values, |a| {
478                assert!(*a > R::zero(), "{a} is not positive");
479            });
480        }
481
482        pub fn test_is_exact() {
483            assert!(
484                R::is_exact() == R::epsilon().is_zero(),
485                "epsilon is {} but is_exact() returns {}",
486                R::epsilon(),
487                R::is_exact()
488            );
489        }
490
491        pub fn test_ratio() {
492            Self::loop_check_i1(|a| {
493                assert_eq!(
494                    R::ratio_i(a.clone(), I::from_usize(1)),
495                    R::from_int(a.clone()),
496                    "R::ratio(a, 1) != a for {a}"
497                );
498            });
499        }
500
501        pub fn test_ratio_invert() {
502            Self::loop_check_i1(|a| {
503                if !a.is_zero() {
504                    assert_eq!(
505                        R::ratio_i(I::from_usize(1), a.clone()) * R::from_int(a.clone()),
506                        R::one(),
507                        "R::ratio(1, a) * a != 1 for {a}"
508                    );
509                }
510            });
511        }
512
513        pub fn test_is_zero() {
514            let test_values = R::get_positive_test_values();
515            assert!(R::zero().is_zero());
516            assert!(!R::one().is_zero());
517            Self::loop_check1(&test_values, |a| {
518                assert!(!a.is_zero(), "{a} is zero");
519            });
520        }
521
522        pub fn test_zero_is_add_neutral() {
523            let test_values = R::get_positive_test_values();
524            Self::loop_check1(&test_values, |a| {
525                assert_eq!(a + &R::zero(), *a, "a + 0 != a for {a}");
526                assert_eq!(&R::zero() + a, *a, "0 + a != a for {a}");
527                assert_eq!(a - &R::zero(), *a, "a - 0 != a for {a}");
528            })
529        }
530
531        #[allow(clippy::eq_op)]
532        pub fn test_add_is_commutative() {
533            let test_values = R::get_positive_test_values();
534            Self::loop_check2(&test_values, |a, b| {
535                assert_eq!(a + b, b + a, "a + b != b + a for {a}, {b}");
536            })
537        }
538
539        pub fn test_add_is_associative(num_samples: Option<usize>) {
540            let test_values = R::get_positive_test_values();
541            Self::loop_check3(&test_values, num_samples, |a, b, c| {
542                assert_eq!(
543                    &(a + b) + c,
544                    a + &(b + c),
545                    "(a + b) + c != a + (b + c) for {a}, {b}, {c}"
546                );
547            })
548        }
549
550        pub fn test_opposite() {
551            let test_values = R::get_positive_test_values();
552            Self::loop_check1(&test_values, |a| {
553                assert_eq!(R::zero() - (R::zero() - a), *a, "-(-a) != a for {a}");
554            });
555        }
556
557        #[allow(clippy::eq_op)]
558        pub fn test_sub_self() {
559            let test_values = R::get_positive_test_values();
560            Self::loop_check1(&test_values, |a| {
561                assert_eq!(a - a, R::zero(), "a - a != 0 for {a}");
562            });
563        }
564
565        pub fn test_add_sub() {
566            let test_values = R::get_positive_test_values();
567            Self::loop_check2(&test_values, |a, b| {
568                assert_eq!(&(a + b) - b, *a, "(a + b) - b != a for {a}, {b}");
569            });
570        }
571
572        pub fn test_sub_add() {
573            let test_values = R::get_positive_test_values();
574            Self::loop_check2(&test_values, |a, b| {
575                assert_eq!(&(a - b) + b, *a, "(a - b) + b != a for {a}, {b}");
576            });
577        }
578
579        pub fn test_one_is_mul_neutral() {
580            let test_values = R::get_positive_test_values();
581            Self::loop_check1(&test_values, |a| {
582                assert_eq!(a * &R::one(), *a, "a * 1 != a for {a}");
583                assert_eq!(&R::one() * a, *a, "1 * a != a for {a}");
584            })
585        }
586
587        pub fn test_mul_is_commutative() {
588            let test_values = R::get_positive_test_values();
589            Self::loop_check2(&test_values, |a, b| {
590                assert_eq!(a * b, b * a, "a * b != b * a for {a}, {b}");
591            })
592        }
593
594        pub fn test_mul_is_associative(num_samples: Option<usize>) {
595            let test_values = R::get_positive_test_values();
596            Self::loop_check3(&test_values, num_samples, |a, b, c| {
597                assert_eq!(
598                    &(a * b) * c,
599                    a * &(b * c),
600                    "(a * b) * c != a * (b * c) for {a}, {b}, {c}"
601                );
602            })
603        }
604
605        pub fn test_mul_is_distributive(num_samples: Option<usize>) {
606            let test_values = R::get_positive_test_values();
607            Self::loop_check3(&test_values, num_samples, |a, b, c| {
608                assert_eq!(
609                    a * &(b + c),
610                    &(a * b) + &(a * c),
611                    "a * (b + c) != (a * b) + (a * c) for {a}, {b}, {c}"
612                );
613            })
614        }
615
616        pub fn test_mul_up_is_commutative() {
617            let test_values = R::get_positive_test_values();
618            Self::loop_check2(&test_values, |a, b| {
619                assert_eq!(
620                    R::mul_up(a, b),
621                    R::mul_up(b, a),
622                    "mul_up(a, b) != mul_up(b, a) for {a}, {b}"
623                );
624            })
625        }
626
627        pub fn test_mul_up_integers() {
628            Self::loop_check_i2(|a, b| {
629                assert_eq!(
630                    R::mul_up(&R::from_int(a.clone()), &R::from_int(b.clone())),
631                    R::from_int(a.clone() * b.clone()),
632                    "mul_up(a, b) != a * b for {a}, {b}"
633                );
634            })
635        }
636
637        pub fn test_mul_up_wrt_mul() {
638            let test_values = R::get_positive_test_values();
639            Self::loop_check2(&test_values, |a, b| {
640                let mul = a * b;
641                let mul_up = R::mul_up(a, b);
642                assert!(mul_up >= mul, "mul_up(a, b) < a * b for {a}, {b}");
643                assert!(
644                    mul_up <= mul + R::epsilon(),
645                    "mul_up(a, b) > a * b + epsilon for {a}, {b}"
646                );
647            })
648        }
649
650        pub fn test_one_is_div_up_neutral() {
651            let test_values = R::get_positive_test_values();
652            Self::loop_check1(&test_values, |a| {
653                assert_eq!(
654                    R::div_up_as_keep_factor(a, &R::one()),
655                    *a,
656                    "div_up(a, 1) != a for {a}"
657                );
658            })
659        }
660
661        pub fn test_div_up_self() {
662            let test_values = R::get_positive_test_values();
663            Self::loop_check1(&test_values, |a| {
664                assert_eq!(
665                    R::div_up_as_keep_factor(a, a),
666                    R::one(),
667                    "div_up(a, a) != 1 for {a}"
668                );
669            });
670        }
671
672        pub fn test_mul_div_up() {
673            let test_values = R::get_positive_test_values();
674            Self::loop_check2(&test_values, |a, b| {
675                assert_eq!(
676                    R::div_up_as_keep_factor(&(a * b), b),
677                    *a,
678                    "div_up(a * b, b) != a for {a}, {b}"
679                );
680            });
681        }
682
683        pub fn test_mul_by_int() {
684            let test_values = R::get_positive_test_values();
685            Self::loop_check1_i1(&test_values, |a, b| {
686                assert_eq!(
687                    a * &b,
688                    a * &R::from_int(b.clone()),
689                    "a * int(b) != a * b for {a}, {b}"
690                );
691            })
692        }
693
694        pub fn test_mul_div_by_int() {
695            let test_values = R::get_positive_test_values();
696            Self::loop_check1_i1(&test_values, |a, b| {
697                if !b.is_zero() {
698                    assert_eq!(&(a * &b) / &b, *a, "a * int(b) / int(b) != a for {a}, {b}");
699                }
700            })
701        }
702
703        pub fn test_mul_by_int_is_associative(num_samples: Option<usize>) {
704            let test_values = R::get_positive_test_values();
705            Self::loop_check1_i2(&test_values, num_samples, |a, b, c| {
706                assert_eq!(
707                    &(a * &b) * &c,
708                    a * &(b.clone() * c.clone()),
709                    "(a * b) * c != a * (b * c) for {a}, {b}, {c}"
710                );
711            })
712        }
713
714        pub fn test_mul_by_int_is_distributive(num_samples: Option<usize>) {
715            let test_values = R::get_positive_test_values();
716            Self::loop_check2_i1(&test_values, num_samples, |a: &R, b: &R, c: I| {
717                assert_eq!(
718                    &(a + b) * &c,
719                    &(a * &c) + &(b * &c),
720                    "(a + b) * c != (a * c) + (b * c) for {a}, {b}, {c}"
721                );
722            })
723        }
724
725        pub fn test_div_by_int_is_associative(num_samples: Option<usize>) {
726            let test_values = R::get_positive_test_values();
727            Self::loop_check1_i2(&test_values, num_samples, |a, b, c| {
728                if !b.is_zero() && !c.is_zero() {
729                    assert_eq!(
730                        &(a / &b) / &c,
731                        a / &(b.clone() * c.clone()),
732                        "(a / b) / c != a / (b * c) for {a}, {b}, {c}"
733                    );
734                }
735            })
736        }
737
738        pub fn test_div_by_int_is_distributive(num_samples: Option<usize>) {
739            let test_values = R::get_positive_test_values();
740            Self::loop_check2_i1(&test_values, num_samples, |a: &R, b: &R, c: I| {
741                if !c.is_zero() {
742                    assert_eq!(
743                        &(a + b) / &c,
744                        &(a / &c) + &(b / &c),
745                        "(a + b) / c != (a / c) + (b / c) for {a}, {b}, {c}"
746                    );
747                }
748            })
749        }
750
751        pub fn test_references() {
752            let test_values = R::get_positive_test_values();
753
754            Self::loop_check2(&test_values, |a, b| {
755                assert_eq!(
756                    a + b,
757                    a.clone() + b.clone(),
758                    "&a + &b != a + b for {a}, {b}"
759                );
760                assert_eq!(
761                    a - b,
762                    a.clone() - b.clone(),
763                    "&a - &b != a - b for {a}, {b}"
764                );
765                assert_eq!(
766                    a * b,
767                    a.clone() * b.clone(),
768                    "&a * &b != a * b for {a}, {b}"
769                );
770                assert_eq!(a + b, a.clone() + b, "&a + &b != a + &b for {a}, {b}");
771                assert_eq!(a - b, a.clone() - b, "&a - &b != a - &b for {a}, {b}");
772                assert_eq!(a * b, a.clone() * b, "&a * &b != a * &b for {a}, {b}");
773            });
774
775            Self::loop_check1_i1(&test_values, |a, b| {
776                if !b.is_zero() {
777                    assert_eq!(
778                        a * &b,
779                        a.clone() * b.clone(),
780                        "&a * &b != a * b for {a}, {b}"
781                    );
782                    assert_eq!(
783                        a / &b,
784                        a.clone() / b.clone(),
785                        "&a / &b != a / b for {a}, {b}"
786                    );
787                }
788            });
789        }
790
791        pub fn test_assign() {
792            let test_values = R::get_positive_test_values();
793
794            Self::loop_check2(&test_values, |a, b| {
795                let mut c = a.clone();
796                c += b.clone();
797                assert_eq!(c, a + b, "a += b != a + b for {a}, {b}");
798                let mut c = a.clone();
799                c -= b.clone();
800                assert_eq!(c, a - b, "a -= b != a - b for {a}, {b}");
801                let mut c = a.clone();
802                c *= b.clone();
803                assert_eq!(c, a * b, "a *= b != a * b for {a}, {b}");
804
805                let mut c = a.clone();
806                c += b;
807                assert_eq!(c, a + b, "a += &b != a + b for {a}, {b}");
808                let mut c = a.clone();
809                c -= b;
810                assert_eq!(c, a - b, "a -= &b != a - b for {a}, {b}");
811                let mut c = a.clone();
812                c *= b;
813                assert_eq!(c, a * b, "a *= &b != a * b for {a}, {b}");
814            });
815
816            Self::loop_check1_i1(&test_values, |a, b| {
817                if !b.is_zero() {
818                    let mut c = a.clone();
819                    c /= &b;
820                    assert_eq!(c, a / &b, "a /= &b != a / &b for {a}, {b}");
821                }
822            });
823        }
824
825        pub fn test_sum(num_samples: Option<usize>) {
826            let test_values = R::get_positive_test_values();
827            Self::loop_check3(&test_values, num_samples, |a, b, c| {
828                assert_eq!(
829                    [a.clone(), b.clone(), c.clone()].into_iter().sum::<R>(),
830                    &(a + b) + c,
831                    "[a, b, c].sum() != a + b + c for {a}, {b}, {c}"
832                );
833                assert_eq!(
834                    [a, b, c].into_iter().sum::<R>(),
835                    &(a + b) + c,
836                    "[&a, &b, &c].sum() != a + b + c for {a}, {b}, {c}"
837                );
838            });
839        }
840
841        pub fn test_product(num_samples: Option<usize>) {
842            let test_values = R::get_positive_test_values();
843            Self::loop_check3(&test_values, num_samples, |a, b, c| {
844                assert_eq!(
845                    [a.clone(), b.clone(), c.clone()].into_iter().product::<R>(),
846                    &(a * b) * c,
847                    "[a, b, c].product() != a * b * c for {a}, {b}, {c}"
848                );
849            });
850        }
851
852        pub fn bench_add(bencher: &mut Bencher) {
853            let a = R::zero();
854            let b = R::one();
855            bencher.iter(|| black_box(&a) + black_box(&b));
856        }
857
858        pub fn bench_sub(bencher: &mut Bencher) {
859            let a = R::zero();
860            let b = R::one();
861            bencher.iter(|| black_box(&a) - black_box(&b));
862        }
863
864        pub fn bench_mul(bencher: &mut Bencher) {
865            let a = R::zero();
866            let b = R::one();
867            bencher.iter(|| black_box(&a) * black_box(&b));
868        }
869
870        pub fn bench_div_up(bencher: &mut Bencher) {
871            let a = R::zero();
872            let b = R::one();
873            bencher.iter(|| R::div_up_as_keep_factor(black_box(&a), black_box(&b)));
874        }
875
876        fn loop_check1(test_values: &[R], f: impl Fn(&R)) {
877            for a in test_values {
878                f(a);
879            }
880        }
881
882        fn loop_check_i1(f: impl Fn(I)) {
883            for aa in 0..100 {
884                let a = I::from_usize(aa);
885                f(a);
886            }
887        }
888
889        fn loop_check2(test_values: &[R], f: impl Fn(&R, &R)) {
890            for a in test_values {
891                for b in test_values {
892                    f(a, b);
893                }
894            }
895        }
896
897        fn loop_check1_i1(test_values: &[R], f: impl Fn(&R, I)) {
898            for a in test_values {
899                for bb in 0..100 {
900                    let b = I::from_usize(bb);
901                    f(a, b);
902                }
903            }
904        }
905
906        fn loop_check_i2(f: impl Fn(I, I)) {
907            for aa in 0..100 {
908                for bb in 0..100 {
909                    let a = I::from_usize(aa);
910                    let b = I::from_usize(bb);
911                    f(a, b);
912                }
913            }
914        }
915
916        fn loop_check3(test_values: &[R], num_samples: Option<usize>, f: impl Fn(&R, &R, &R)) {
917            match num_samples {
918                None => {
919                    // Exhaustive check.
920                    for a in test_values {
921                        for b in test_values {
922                            for c in test_values {
923                                f(a, b, c);
924                            }
925                        }
926                    }
927                }
928                Some(n) => {
929                    // Randomly sample values rather than conducting an exhaustive O(n^3) search on
930                    // the test values.
931                    let mut rng = thread_rng();
932
933                    for _ in 0..n {
934                        let a = test_values.choose(&mut rng).unwrap();
935                        let b = test_values.choose(&mut rng).unwrap();
936                        let c = test_values.choose(&mut rng).unwrap();
937                        f(a, b, c);
938                    }
939                }
940            }
941        }
942
943        fn loop_check2_i1(test_values: &[R], num_samples: Option<usize>, f: impl Fn(&R, &R, I)) {
944            match num_samples {
945                None => {
946                    // Exhaustive check.
947                    for a in test_values {
948                        for b in test_values {
949                            for cc in 0..100 {
950                                let c = I::from_usize(cc);
951                                f(a, b, c);
952                            }
953                        }
954                    }
955                }
956                Some(n) => {
957                    // Randomly sample values rather than conducting an exhaustive O(n^3) search on
958                    // the test values.
959                    let integer_distribution = Uniform::from(0..100);
960                    let mut rng = thread_rng();
961
962                    for _ in 0..n {
963                        let a = test_values.choose(&mut rng).unwrap();
964                        let b = test_values.choose(&mut rng).unwrap();
965                        let c = I::from_usize(integer_distribution.sample(&mut rng));
966                        f(a, b, c);
967                    }
968                }
969            }
970        }
971
972        fn loop_check1_i2(test_values: &[R], num_samples: Option<usize>, f: impl Fn(&R, I, I)) {
973            match num_samples {
974                None => {
975                    // Exhaustive check.
976                    for a in test_values {
977                        for bb in 0..100 {
978                            for cc in 0..100 {
979                                let b = I::from_usize(bb);
980                                let c = I::from_usize(cc);
981                                f(a, b, c);
982                            }
983                        }
984                    }
985                }
986                Some(n) => {
987                    // Randomly sample values rather than conducting an exhaustive O(n^3) search on
988                    // the test values.
989                    let integer_distribution = Uniform::from(0..100);
990                    let mut rng = thread_rng();
991
992                    for _ in 0..n {
993                        let a = test_values.choose(&mut rng).unwrap();
994                        let b = I::from_usize(integer_distribution.sample(&mut rng));
995                        let c = I::from_usize(integer_distribution.sample(&mut rng));
996                        f(a, b, c);
997                    }
998                }
999            }
1000        }
1001    }
1002}