Skip to main content

malachite_base/num/arithmetic/
mod_product.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2010 William Hart
6//
7//      Copyright © 2021 Fredrik Johansson
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::num::arithmetic::traits::ModProduct;
16use crate::num::basic::unsigneds::PrimitiveUnsigned;
17
18// The data for multiplying modulo `m` is computed once and reused for every factor. Once the
19// partial product is zero it stays zero, so the factors after the one that made it zero are not
20// examined. Starting from 1 reduced modulo `m` makes the product of no numbers correct even when
21// `m` is 1.
22fn mod_product<T: PrimitiveUnsigned, I: Iterator<Item = T>>(xs: I, m: T) -> T {
23    assert_ne!(m, T::ZERO, "m cannot be zero");
24    let data = T::precompute_mod_mul_data(&m);
25    let mut product = if m == T::ONE { T::ZERO } else { T::ONE };
26    for x in xs {
27        assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
28        product.mod_mul_precomputed_assign(x, m, &data);
29        if product == T::ZERO {
30            break;
31        }
32    }
33    product
34}
35
36macro_rules! impl_mod_product {
37    ($t:ident) => {
38        impl ModProduct for $t {
39            /// Multiplies together all the numbers in an iterator, modulo another number $m$. Every
40            /// number must be already reduced modulo $m$.
41            ///
42            /// The product of no numbers is 1 reduced modulo $m$, which is 0 when $m = 1$.
43            ///
44            /// $f((x_i)_ {i=0}^{n-1}, m) = y$, where $y < m$ and $\prod_ {i=0}^{n-1} x_i \equiv y
45            /// \mod m$.
46            ///
47            /// # Worst-case complexity
48            /// $T(n) = O(n)$
49            ///
50            /// $M(n) = O(1)$
51            ///
52            /// where $T$ is time, $M$ is additional memory, and $n$ is `xs.count()`.
53            ///
54            /// # Panics
55            /// Panics if `m` is zero or if any number is greater than or equal to `m`. As soon as
56            /// the partial product is zero, the remaining numbers are not examined, so an unreduced
57            /// number after that point is not detected.
58            ///
59            /// # Examples
60            /// See [here](super::mod_product#mod_product).
61            #[inline]
62            fn mod_product<I: Iterator<Item = $t>>(xs: I, m: $t) -> $t {
63                mod_product(xs, m)
64            }
65        }
66
67        impl<'a> ModProduct<&'a $t, $t> for $t {
68            /// Multiplies together all the numbers in an iterator of references, modulo another
69            /// number $m$. Every number must be already reduced modulo $m$.
70            ///
71            /// The product of no numbers is 1 reduced modulo $m$, which is 0 when $m = 1$.
72            ///
73            /// $f((x_i)_ {i=0}^{n-1}, m) = y$, where $y < m$ and $\prod_ {i=0}^{n-1} x_i \equiv y
74            /// \mod m$.
75            ///
76            /// # Worst-case complexity
77            /// $T(n) = O(n)$
78            ///
79            /// $M(n) = O(1)$
80            ///
81            /// where $T$ is time, $M$ is additional memory, and $n$ is `xs.count()`.
82            ///
83            /// # Panics
84            /// Panics if `m` is zero or if any number is greater than or equal to `m`. As soon as
85            /// the partial product is zero, the remaining numbers are not examined, so an unreduced
86            /// number after that point is not detected.
87            ///
88            /// # Examples
89            /// See [here](super::mod_product#mod_product).
90            #[inline]
91            fn mod_product<I: Iterator<Item = &'a $t>>(xs: I, m: $t) -> $t {
92                mod_product(xs.copied(), m)
93            }
94        }
95    };
96}
97apply_to_unsigneds!(impl_mod_product);